Persist device row moves

This commit is contained in:
Julian Appel 2026-07-25 21:09:02 +02:00
parent 2eba4ea75e
commit 08a2775a88
25 changed files with 391 additions and 1678 deletions

View file

@ -33,6 +33,9 @@ import type {
import type {
CircuitSnapshot,
} from "../../domain/models/circuit-structure-project-command.model";
import type {
CircuitDeviceRowMoveAssignment,
} from "../../domain/models/circuit-device-row-move-project-command.model";
async function request<T>(url: string, init?: RequestInit): Promise<T> {
const response = await fetch(url, {
@ -212,12 +215,6 @@ export function deleteCircuitCommand(
);
}
export function deleteCircuitById(circuitId: string) {
return request<void>(`/api/circuits/${circuitId}`, {
method: "DELETE",
});
}
export function getNextCircuitIdentifier(sectionId: string) {
return request<{ sectionId: string; nextIdentifier: string }>(
`/api/circuit-sections/${sectionId}/next-identifier`
@ -275,28 +272,44 @@ export function deleteCircuitDeviceRowCommand(
);
}
export function moveCircuitDeviceRowById(
rowId: string,
input: { targetCircuitId?: string; targetSectionId?: string; createNewCircuit?: boolean }
export function moveCircuitDeviceRowsCommand(
projectId: string,
expectedRevision: number,
moves: CircuitDeviceRowMoveAssignment[],
description: string
) {
return request(`/api/circuit-device-rows/${rowId}/move`, {
method: "PATCH",
body: JSON.stringify(input),
});
return executeProjectCommand(
projectId,
expectedRevision,
{
schemaVersion: 1,
type: "circuit-device-row.move",
payload: { moves },
},
description
);
}
export function moveCircuitDeviceRowsBulk(input: {
rowIds: string[];
targetCircuitId?: string;
targetSectionId?: string;
createNewCircuit?: boolean;
}) {
return request<{ movedRowIds: string[]; targetCircuitId: string; createdCircuitId?: string }>(
"/api/circuit-device-rows/move-bulk",
export function moveCircuitDeviceRowsToNewCircuitCommand(
projectId: string,
expectedRevision: number,
targetCircuit: CircuitSnapshot,
moves: CircuitDeviceRowMoveAssignment[],
description: string
) {
return executeProjectCommand(
projectId,
expectedRevision,
{
method: "PATCH",
body: JSON.stringify(input),
}
schemaVersion: 1,
type: "circuit-device-row.move-with-new-circuit",
payload: {
targetCircuitAction: "create",
targetCircuit,
moves,
},
},
description
);
}

View file

@ -0,0 +1,66 @@
import type {
CircuitTreeCircuitDto,
CircuitTreeDeviceRowDto,
} from "../types.js";
import type {
CircuitDeviceRowMoveAssignment,
} from "../../domain/models/circuit-device-row-move-project-command.model.js";
export function buildCircuitDeviceRowMoveAssignments(input: {
circuits: CircuitTreeCircuitDto[];
rowIds: string[];
targetCircuitId: string;
targetDeviceRows: CircuitTreeDeviceRowDto[];
}): CircuitDeviceRowMoveAssignment[] {
const { circuits, rowIds, targetCircuitId, targetDeviceRows } =
input;
if (new Set(rowIds).size !== rowIds.length) {
throw new Error("Gerätezeilen dürfen nicht mehrfach ausgewählt sein.");
}
const sourceByRowId = new Map<
string,
{ circuitId: string; sortOrder: number }
>();
for (const circuit of circuits) {
for (const row of circuit.deviceRows) {
sourceByRowId.set(row.id, {
circuitId: circuit.id,
sortOrder: row.sortOrder,
});
}
}
const movedRowIds = new Set(rowIds);
const retainedTargetRows = targetDeviceRows.filter(
(row) => !movedRowIds.has(row.id)
);
const lastTargetSortOrder = retainedTargetRows.reduce(
(highest, row) => Math.max(highest, row.sortOrder),
0
);
return rowIds.flatMap((rowId, index) => {
const source = sourceByRowId.get(rowId);
if (!source) {
throw new Error("Eine ausgewählte Gerätezeile wurde nicht gefunden.");
}
const targetSortOrder =
lastTargetSortOrder + (index + 1) * 10;
if (
source.circuitId === targetCircuitId &&
source.sortOrder === targetSortOrder
) {
return [];
}
return [
{
rowId,
expectedCircuitId: source.circuitId,
expectedSortOrder: source.sortOrder,
targetCircuitId,
targetSortOrder,
},
];
});
}