Persist section renumbering

This commit is contained in:
2026-07-25 21:30:29 +02:00
parent abcb468807
commit c45afd0981
24 changed files with 317 additions and 638 deletions
+27 -19
View File
@@ -33,6 +33,9 @@ import {
import {
buildCircuitSectionReorderAssignments,
} from "../utils/circuit-section-reorder-command";
import {
buildCircuitSectionRenumberAssignments,
} from "../utils/circuit-section-renumber-command";
import type {
CellKey,
CellKind,
@@ -57,9 +60,8 @@ import {
moveCircuitDeviceRowsToNewCircuitCommand,
reorderCircuitSectionCommand,
reorderCircuitSectionsCommand,
renumberCircuitSection,
renumberCircuitSectionCommand,
redoProjectCommand,
updateSectionEquipmentIdentifiers,
updateCircuitById,
updateCircuitDeviceRowById,
undoProjectCommand,
@@ -2592,7 +2594,8 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
await handleDeleteCircuit(intent.circuitId);
}
// Explicit renumber action. Undo restores previous BMKs via safe section identifier update.
// Explicit renumber action through project history. Sorting and device rows
// remain unchanged; the server handles collision-safe identifier swaps.
async function handleRenumberSection(sectionId: string) {
if (!confirm("Diesen Bereich neu nummerieren? Nur die Stromkreise in diesem Bereich werden geändert.")) {
return;
@@ -2601,26 +2604,31 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
if (!section) {
return;
}
const before = section.circuits.map((circuit) => ({
id: circuit.id,
equipmentIdentifier: circuit.equipmentIdentifier,
}));
const assignments = buildCircuitSectionRenumberAssignments(
data?.sections ?? [],
sectionId
);
if (assignments.length === 0) {
return;
}
const label = "Bereich neu nummerieren";
await runCommand({
label: "Bereich neu nummerieren",
label,
redo: async () => {
await renumberCircuitSection(sectionId);
const result = await renumberCircuitSectionCommand(
projectId,
getExpectedProjectRevision(),
sectionId,
assignments,
label
);
applyProjectCommandResult(result);
return null;
},
undo: async () => {
// Restore prior BMKs through safe bulk endpoint to avoid transient unique collisions.
await updateSectionEquipmentIdentifiers(
sectionId,
before.map((entry) => ({
circuitId: entry.id,
equipmentIdentifier: entry.equipmentIdentifier,
}))
);
return null;
undo: async () => null,
persistent: {
undoIntent: () => null,
redoIntent: () => null,
},
});
}
+18 -12
View File
@@ -42,6 +42,9 @@ import type {
import type {
CircuitSectionsReorderSection,
} from "../../domain/models/circuit-sections-reorder-project-command.model";
import type {
CircuitSectionRenumberAssignment,
} from "../../domain/models/circuit-section-renumber-project-command.model";
async function request<T>(url: string, init?: RequestInit): Promise<T> {
const response = await fetch(url, {
@@ -319,12 +322,6 @@ export function moveCircuitDeviceRowsToNewCircuitCommand(
);
}
export function renumberCircuitSection(sectionId: string) {
return request(`/api/circuit-sections/${sectionId}/renumber`, {
method: "POST",
});
}
export function reorderCircuitSectionCommand(
projectId: string,
expectedRevision: number,
@@ -362,14 +359,23 @@ export function reorderCircuitSectionsCommand(
);
}
export function updateSectionEquipmentIdentifiers(
export function renumberCircuitSectionCommand(
projectId: string,
expectedRevision: number,
sectionId: string,
identifiers: Array<{ circuitId: string; equipmentIdentifier: string }>
assignments: CircuitSectionRenumberAssignment[],
description: string
) {
return request(`/api/circuit-sections/${sectionId}/equipment-identifiers`, {
method: "PATCH",
body: JSON.stringify({ identifiers }),
});
return executeProjectCommand(
projectId,
expectedRevision,
{
schemaVersion: 1,
type: "circuit.renumber-section",
payload: { sectionId, assignments },
},
description
);
}
export function listFloors(projectId: string) {
@@ -0,0 +1,54 @@
import type {
CircuitSectionRenumberAssignment,
} from "../../domain/models/circuit-section-renumber-project-command.model.js";
import type { CircuitTreeSectionDto } from "../types.js";
export function buildCircuitSectionRenumberAssignments(
sections: CircuitTreeSectionDto[],
sectionId: string
): CircuitSectionRenumberAssignment[] {
const targetSection = sections.find(
(section) => section.id === sectionId
);
if (!targetSection) {
throw new Error("Der Bereich wurde nicht gefunden.");
}
const identifiersOutsideSection = new Set(
sections
.filter((section) => section.id !== sectionId)
.flatMap((section) =>
section.circuits.map(
(circuit) => circuit.equipmentIdentifier
)
)
);
const assignments: CircuitSectionRenumberAssignment[] = [];
let suffix = 1;
for (const circuit of targetSection.circuits) {
let targetEquipmentIdentifier = `${targetSection.prefix}${suffix}`;
while (
identifiersOutsideSection.has(targetEquipmentIdentifier)
) {
suffix += 1;
targetEquipmentIdentifier = `${targetSection.prefix}${suffix}`;
}
assignments.push({
circuitId: circuit.id,
expectedEquipmentIdentifier: circuit.equipmentIdentifier,
targetEquipmentIdentifier,
});
suffix += 1;
}
if (
assignments.every(
(assignment) =>
assignment.expectedEquipmentIdentifier ===
assignment.targetEquipmentIdentifier
)
) {
return [];
}
return assignments;
}