Skip to content
Snippets Groups Projects
Verified Commit aa789863 authored by Dorian Koch's avatar Dorian Koch
Browse files

More changelog improvements, closes #12

parent cdfd1821
No related branches found
No related tags found
No related merge requests found
Pipeline #6421 failed
...@@ -7,7 +7,262 @@ import { useFilteredDataView, PagingNavigation, FilterInput } from "@/components ...@@ -7,7 +7,262 @@ import { useFilteredDataView, PagingNavigation, FilterInput } from "@/components
import { DateTime } from "luxon"; import { DateTime } from "luxon";
import { showError, ErrorPage } from "@/misc/ErrorHandlers"; import { showError, ErrorPage } from "@/misc/ErrorHandlers";
import type { GetChangelogResponse, int } from "@/api/api_v1_types"; import type { changelog_entry, GetChangelogResponse, int } from "@/api/api_v1_types";
import { useEffect, useState } from "react";
function ValToStr({ val }: { val: any }) {
const [expand, setExpand] = useState(false);
if (val === null) {
return <span className="fst-italic text-muted">null</span>;
}
if (val === undefined) {
return <span className="fst-italic text-muted">undefined</span>;
}
const stringifiedVal = JSON.stringify(val);
if (stringifiedVal.length > 100) {
if (!expand) {
return (
<span>
{stringifiedVal.substring(0, 100)}
<button
type="button"
className="btn btn-link p-0 align-baseline text-decoration-underline"
onClick={(e) => {
setExpand(true);
}}
>
...
</button>
</span>
);
}
return (
<span>
{stringifiedVal} <br />
<button
type="button"
className="btn btn-secondary btn-sm"
onClick={(e) => {
setExpand(false);
}}
>
Hide
</button>
</span>
);
}
return stringifiedVal;
}
function Modification({
i,
filterLevel,
formattedDate,
resolveUser,
}: {
i: changelog_entry;
filterLevel: (lvl: number) => () => void;
formattedDate: string;
resolveUser: (id: number) => string;
}) {
const reloadFunc = useReloadBoundary();
const api = useBackendContext();
const undo = () => {
if (i.type !== "modification") {
alert("TODO: Not implemented");
return;
}
if (!confirm("Wirklich zurücksetzen?")) {
return;
}
let updates: { [key: string]: any } = {};
updates[i.field_description!.id] = i.old_value;
api.updateOMObject(i.object_type!, i.object_id!, {
updates: updates,
expected_current_values: {}, //TODO have something here?
})
.then(reloadFunc)
.catch((e) => {
showError(e, "Exception while reverting change");
});
};
return (
<tr key={i.id}>
<td>{formattedDate}</td>
<td>{resolveUser(i.modifying_user_id)}</td>
<td>{i.type}</td>
<td>
<button
type="button"
className="btn btn-link p-0 align-baseline"
onClick={filterLevel(1)}
>
{`${i.object_type}`}
</button>
.
<button
type="button"
className="btn btn-link p-0 align-baseline"
onClick={filterLevel(2)}
>
{`${i.object_id}`}
</button>
.
<button
type="button"
className="btn btn-link p-0 align-baseline"
onClick={filterLevel(3)}
>{`${i.field_description!.id}`}</button>
{` (${i.field_description?.type})`}
</td>
<td>
<ValToStr val={i.old_value} />
</td>
<td>
<ValToStr val={i.new_value} />
</td>
<td>
<button className="btn btn-warning" onClick={undo}>
revert to old
</button>
</td>
</tr>
);
}
function DeletionChange({
i,
filterLevel,
formattedDate,
resolveUser,
}: {
i: changelog_entry;
filterLevel: (lvl: number) => () => void;
formattedDate: string;
resolveUser: (id: number) => string;
}) {
const reloadFunc = useReloadBoundary();
const api = useBackendContext();
const undelete = () => {
if (i.type !== "deletion_change" || i.is_now_deleted === false) {
alert("invalid action!");
return;
}
if (!confirm("Wirklich wiederherstellen?")) {
return;
}
api.resurrectOMObject(i.object_type!, i.object_id!)
.then(reloadFunc)
.catch((e) => {
showError(e, "Exception while reverting change");
});
};
return (
<tr key={i.id}>
<td>{formattedDate}</td>
<td>{resolveUser(i.modifying_user_id)}</td>
<td>{i.type}</td>
<td>
<button
type="button"
className="btn btn-link p-0 align-baseline"
onClick={filterLevel(1)}
>
{`${i.object_type}`}
</button>
.
<button
type="button"
className="btn btn-link p-0 align-baseline"
onClick={filterLevel(2)}
>
{`${i.object_id}`}
</button>
</td>
{i.is_now_deleted ? (
<>
<td>
<span className="fst-italic text-muted">not deleted</span>
</td>
<td>
<span className="fst-italic text-danger">deleted</span>
</td>
</>
) : (
<>
<td>
<span className="fst-italic text-danger">deleted</span>
</td>
<td>
<span className="fst-italic text-muted">not deleted</span>
</td>
</>
)}
<td>
{i.is_now_deleted === true && (
<button className="btn btn-warning" onClick={undelete}>
undelete
</button>
)}
</td>
</tr>
);
}
function Creation({
i,
filterLevel,
formattedDate,
resolveUser,
}: {
i: changelog_entry;
filterLevel: (lvl: number) => () => void;
formattedDate: string;
resolveUser: (id: number) => string;
}) {
const reloadFunc = useReloadBoundary();
const api = useBackendContext();
return (
<tr key={i.id}>
<td>{formattedDate}</td>
<td>{resolveUser(i.modifying_user_id)}</td>
<td>{i.type}</td>
<td>
<button
type="button"
className="btn btn-link p-0 align-baseline"
onClick={filterLevel(1)}
>
{`${i.object_type}`}
</button>
.
<button
type="button"
className="btn btn-link p-0 align-baseline"
onClick={filterLevel(2)}
>
{`${i.object_id}`}
</button>
{typeof i.variant === "string" && ` (variant:${i.variant})`}
</td>
<td>
<ValToStr val={i.old_value} />
</td>
<td>
<ValToStr val={i.new_value} />
</td>
<td></td>
</tr>
);
}
function ChangelogList({ function ChangelogList({
changelog, changelog,
...@@ -21,8 +276,25 @@ function ChangelogList({ ...@@ -21,8 +276,25 @@ function ChangelogList({
updateNow: boolean, updateNow: boolean,
) => void; ) => void;
}) { }) {
const reloadFunc = useReloadBoundary();
const api = useBackendContext(); const api = useBackendContext();
const [usersData, setUsersData] = useState<{ [key: number]: string }>({});
useEffect(() => {
api.getUsers().then((data) => {
let users: { [key: number]: string } = {};
data.users.forEach((u) => {
users[u.id] = u.name;
});
setUsersData(users);
});
}, [api]);
const resolveUser = (id: number) => {
if (usersData[id]) {
return `${usersData[id]} (${id})`;
}
return id + "";
};
return ( return (
<> <>
...@@ -76,79 +348,38 @@ function ChangelogList({ ...@@ -76,79 +348,38 @@ function ChangelogList({
}; };
}; };
let pfad = <></>; let pfad = <></>;
const formattedDate = DateTime.fromISO(i.change_time).toFormat(
"yyyy-MM-dd HH:mm:ss",
);
switch (i.type) { switch (i.type) {
case "modification": case "modification":
pfad = ( return (
<> <Modification
<button i={i}
type="button" filterLevel={filterLevel}
className="btn btn-link p-0 align-baseline" formattedDate={formattedDate}
onClick={filterLevel(1)} resolveUser={resolveUser}
> />
{`${i.object_type}`}
</button>
.
<button
type="button"
className="btn btn-link p-0 align-baseline"
onClick={filterLevel(2)}
>
{`${i.object_id}`}
</button>
.
<button
type="button"
className="btn btn-link p-0 align-baseline"
onClick={filterLevel(3)}
>{`${i.field_description!.id}`}</button>
{` (${i.field_description?.type})`}
</>
); );
break;
case "creation": case "creation":
pfad = ( return (
<> <Creation
<button i={i}
type="button" filterLevel={filterLevel}
className="btn btn-link p-0 align-baseline" formattedDate={formattedDate}
onClick={filterLevel(1)} resolveUser={resolveUser}
> />
{`${i.object_type}`}
</button>
.
<button
type="button"
className="btn btn-link p-0 align-baseline"
onClick={filterLevel(2)}
>
{`${i.object_id}`}
</button>
{typeof i.variant === "string" &&
` (variant:${i.variant})`}
</>
); );
break;
case "deletion_change": case "deletion_change":
pfad = ( return (
<> <DeletionChange
<button i={i}
type="button" filterLevel={filterLevel}
className="btn btn-link p-0 align-baseline" formattedDate={formattedDate}
onClick={filterLevel(1)} resolveUser={resolveUser}
> />
{`${i.object_type}`}
</button>
.
<button
type="button"
className="btn btn-link p-0 align-baseline"
onClick={filterLevel(2)}
>
{`${i.object_id}`}
</button>
</>
); );
break;
case "unknown": case "unknown":
pfad = ( pfad = (
<>{`${i.unknown_type}.${i.unknown_field}, id=${i.object_id}`}</> <>{`${i.unknown_type}.${i.unknown_field}, id=${i.object_id}`}</>
...@@ -157,53 +388,20 @@ function ChangelogList({ ...@@ -157,53 +388,20 @@ function ChangelogList({
default: default:
pfad = <>{i.type}</>; pfad = <>{i.type}</>;
} }
const valToStr = (val: any) => {
if (val === null) {
return <span className="fst-italic text-muted">null</span>;
}
if (val === undefined) {
return <span className="fst-italic text-muted">undefined</span>;
}
return JSON.stringify(val);
};
const formattedDate = DateTime.fromISO(i.change_time).toFormat(
"yyyy-MM-dd HH:mm:ss",
);
const undo = () => {
if (i.type !== "modification") {
alert("TODO: Not implemented");
return;
}
if (!confirm("Wirklich zurücksetzen?")) {
return;
}
let updates: { [key: string]: any } = {};
updates[i.field_description!.id] = i.old_value;
api.updateOMObject(i.object_type!, i.object_id!, {
updates: updates,
expected_current_values: {}, //TODO have something here?
})
.then(reloadFunc)
.catch((e) => {
showError(e, "Exception while reverting change");
});
};
return ( return (
<tr key={i.id}> <tr key={i.id}>
<td>{formattedDate}</td> <td>{formattedDate}</td>
<td>{i.modifying_user_id}</td> <td>{resolveUser(i.modifying_user_id)}</td>
<td>{i.type}</td> <td>{i.type}</td>
<td>{pfad}</td> <td>{pfad}</td>
<td>{valToStr(i.old_value)}</td>
<td>{valToStr(i.new_value)}</td>
<td> <td>
{i.type === "modification" && ( <ValToStr val={i.old_value} />
<button className="btn btn-warning" onClick={undo}> </td>
revert to old <td>
</button> <ValToStr val={i.new_value} />
)}
</td> </td>
<td></td>
</tr> </tr>
); );
})} })}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment