Expose project version history

This commit is contained in:
Julian Appel 2026-07-25 22:56:51 +02:00
parent 384a5769ab
commit d00ae30bda
13 changed files with 759 additions and 10 deletions

View file

@ -12,6 +12,9 @@ import type {
ProjectDeviceCommandResultDto,
ProjectDeviceDto,
ProjectHistoryStateDto,
ProjectRevisionPageDto,
ProjectSnapshotMetadataDto,
ProjectSnapshotRestoreResultDto,
ProjectDeviceSyncCommandResultDto,
ProjectDeviceSyncPreviewDto,
ProjectDto,
@ -82,6 +85,62 @@ export function getProjectHistory(projectId: string) {
);
}
export function listProjectRevisions(
projectId: string,
input: { limit?: number; beforeRevision?: number } = {}
) {
const query = new URLSearchParams();
if (input.limit !== undefined) {
query.set("limit", String(input.limit));
}
if (input.beforeRevision !== undefined) {
query.set("beforeRevision", String(input.beforeRevision));
}
const suffix = query.size ? `?${query.toString()}` : "";
return request<ProjectRevisionPageDto>(
`/api/projects/${projectId}/history/revisions${suffix}`
);
}
export function listProjectSnapshots(projectId: string) {
return request<ProjectSnapshotMetadataDto[]>(
`/api/projects/${projectId}/snapshots`
);
}
export function createNamedProjectSnapshot(
projectId: string,
expectedRevision: number,
name: string,
description?: string
) {
return request<ProjectSnapshotMetadataDto>(
`/api/projects/${projectId}/snapshots`,
{
method: "POST",
body: JSON.stringify({
expectedRevision,
name,
...(description ? { description } : {}),
}),
}
);
}
export function restoreProjectSnapshot(
projectId: string,
snapshotId: string,
expectedRevision: number
) {
return request<ProjectSnapshotRestoreResultDto>(
`/api/projects/${projectId}/snapshots/${snapshotId}/restore`,
{
method: "POST",
body: JSON.stringify({ expectedRevision }),
}
);
}
export function executeProjectCommand(
projectId: string,
expectedRevision: number,

View file

@ -0,0 +1,63 @@
import type {
ProjectRevisionSourceDto,
ProjectRevisionSummaryDto,
} from "../types";
const sourceLabels: Record<ProjectRevisionSourceDto, string> = {
user: "Bearbeitung",
undo: "Rückgängig",
redo: "Wiederholt",
restore: "Wiederherstellung",
migration: "Migration",
};
const commandTypeLabels: Record<string, string> = {
"circuit.update": "Stromkreis bearbeitet",
"circuit.insert": "Stromkreis angelegt",
"circuit.delete": "Stromkreis gelöscht",
"circuit-device-row.update": "Gerätezeile bearbeitet",
"circuit-device-row.insert": "Gerätezeile angelegt",
"circuit-device-row.delete": "Gerätezeile gelöscht",
"circuit-device-row.move": "Gerätezeile verschoben",
"circuit-device-row.move-with-new-circuit":
"Gerätezeile in neuen Stromkreis verschoben",
"circuit.reorder-section": "Stromkreise sortiert",
"circuit.reorder-sections": "Stromkreise bereichsübergreifend sortiert",
"circuit.renumber-section": "Bereich neu nummeriert",
"project-device.update": "Projektgerät bearbeitet",
"project-device.insert": "Projektgerät angelegt",
"project-device.delete": "Projektgerät gelöscht",
"project-device.sync-rows": "Projektgerät-Verknüpfungen geändert",
"project.restore-state": "Projektstand wiederhergestellt",
};
export function getProjectRevisionSourceLabel(
source: ProjectRevisionSourceDto
) {
return sourceLabels[source];
}
export function getProjectRevisionDescription(
revision: ProjectRevisionSummaryDto
) {
return (
revision.description?.trim() ||
commandTypeLabels[revision.commandType] ||
revision.commandType
);
}
export function mergeProjectRevisionPages(
current: ProjectRevisionSummaryDto[],
next: ProjectRevisionSummaryDto[]
) {
const byId = new Map(
[...current, ...next].map((revision) => [
revision.revisionId,
revision,
])
);
return [...byId.values()].sort(
(left, right) => right.revisionNumber - left.revisionNumber
);
}