Renumber circuit groups explicitly
This commit is contained in:
@@ -52,8 +52,10 @@ import {
|
||||
buildCircuitSectionRenumberAssignments,
|
||||
} from "../utils/circuit-section-renumber-command";
|
||||
import {
|
||||
buildCircuitGroupRenumberPlan,
|
||||
buildCircuitGroupReorderAssignments,
|
||||
buildNewCircuitGroupSnapshot,
|
||||
canRenumberCircuitGroups,
|
||||
canDeleteCircuitGroup,
|
||||
renameCircuitGroupSnapshot,
|
||||
toCircuitGroupSnapshot,
|
||||
@@ -97,6 +99,7 @@ import {
|
||||
reorderCircuitSectionCommand,
|
||||
reorderCircuitSectionsCommand,
|
||||
reorderCircuitGroupsCommand,
|
||||
renumberCircuitGroupsCommand,
|
||||
renumberCircuitSectionCommand,
|
||||
redoProjectCommand,
|
||||
updateCircuitById,
|
||||
@@ -126,7 +129,10 @@ import type {
|
||||
import { DistributionBoardComponentModal } from "./distribution-board-component-modal";
|
||||
import { CircuitGroupModal } from "./circuit-group-modal";
|
||||
import { CircuitProtectionModal } from "./circuit-protection-modal";
|
||||
import type { CircuitGroupCategory } from "../../shared/constants/circuit-group";
|
||||
import {
|
||||
circuitGroupCategoryLabels,
|
||||
type CircuitGroupCategory,
|
||||
} from "../../shared/constants/circuit-group";
|
||||
|
||||
type SaveDirection = "stay" | "next" | "prev";
|
||||
type StartEditMode = "selectExisting" | "replaceWithTypedChar";
|
||||
@@ -1267,6 +1273,44 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
||||
});
|
||||
}
|
||||
|
||||
async function handleRenumberCircuitGroups(
|
||||
category: CircuitGroupCategory
|
||||
) {
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
let plan: ReturnType<typeof buildCircuitGroupRenumberPlan>;
|
||||
try {
|
||||
plan = buildCircuitGroupRenumberPlan(data.sections, category);
|
||||
} catch (err) {
|
||||
setError(normalizeUiError(err));
|
||||
return;
|
||||
}
|
||||
if (!plan) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
!confirm(
|
||||
`Alle Gruppen der Kategorie „${circuitGroupCategoryLabels[category]}“ gemäß ihrer aktuellen Reihenfolge neu nummerieren? Gruppennummern, Präfixe und zugehörige BMK werden dabei gemeinsam geändert.`
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
await runCommand({
|
||||
label: "Stromkreisgruppen neu nummerieren",
|
||||
redo: async () => {
|
||||
const result = await renumberCircuitGroupsCommand(
|
||||
projectId,
|
||||
getExpectedProjectRevision(),
|
||||
circuitListId,
|
||||
plan
|
||||
);
|
||||
applyProjectCommandResult(result);
|
||||
return null;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function handleSaveCircuitProtection(
|
||||
protection: CircuitTreeProtectionDeviceDto
|
||||
) {
|
||||
@@ -3543,6 +3587,32 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
||||
>
|
||||
Gruppe nach unten
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
tabIndex={-1}
|
||||
disabled={
|
||||
hasActiveSortOrFilter ||
|
||||
!section.category ||
|
||||
!canRenumberCircuitGroups(
|
||||
data.sections,
|
||||
section.category
|
||||
)
|
||||
}
|
||||
onClick={() =>
|
||||
section.category
|
||||
? void handleRenumberCircuitGroups(
|
||||
section.category
|
||||
)
|
||||
: undefined
|
||||
}
|
||||
title={
|
||||
hasActiveSortOrFilter
|
||||
? "Vor der Gruppenneunummerierung Sortierung und Filter zurücksetzen."
|
||||
: "Alle Gruppen dieser Kategorie gemäß ihrer Reihenfolge neu nummerieren"
|
||||
}
|
||||
>
|
||||
Gruppen-BMK neu nummerieren
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
tabIndex={-1}
|
||||
|
||||
@@ -62,6 +62,9 @@ import type {
|
||||
import type {
|
||||
CircuitProtectionSnapshot,
|
||||
} from "../../domain/models/circuit-protection-project-command.model";
|
||||
import type {
|
||||
CircuitGroupRenumberPlan,
|
||||
} from "../../domain/services/circuit-group-renumbering";
|
||||
|
||||
async function request<T>(url: string, init?: RequestInit): Promise<T> {
|
||||
const response = await fetch(url, {
|
||||
@@ -452,6 +455,24 @@ export function reorderCircuitGroupsCommand(
|
||||
);
|
||||
}
|
||||
|
||||
export function renumberCircuitGroupsCommand(
|
||||
projectId: string,
|
||||
expectedRevision: number,
|
||||
circuitListId: string,
|
||||
plan: CircuitGroupRenumberPlan
|
||||
) {
|
||||
return executeProjectCommand(
|
||||
projectId,
|
||||
expectedRevision,
|
||||
{
|
||||
schemaVersion: 1,
|
||||
type: "circuit-group.renumber",
|
||||
payload: { circuitListId, groups: plan.groups },
|
||||
},
|
||||
"Stromkreisgruppen neu nummerieren"
|
||||
);
|
||||
}
|
||||
|
||||
export function updateCircuitProtectionCommand(
|
||||
projectId: string,
|
||||
expectedRevision: number,
|
||||
|
||||
@@ -10,6 +10,9 @@ import type { CircuitTreeSectionDto } from "../types";
|
||||
import type {
|
||||
CircuitGroupReorderAssignment,
|
||||
} from "../../domain/models/circuit-group-structure-project-command.model";
|
||||
import type {
|
||||
CircuitGroupRenumberPlan,
|
||||
} from "../../domain/services/circuit-group-renumbering";
|
||||
|
||||
export function toCircuitGroupSnapshot(
|
||||
section: CircuitTreeSectionDto,
|
||||
@@ -115,6 +118,75 @@ export function buildCircuitGroupReorderAssignments(
|
||||
}));
|
||||
}
|
||||
|
||||
export function buildCircuitGroupRenumberPlan(
|
||||
sections: readonly CircuitTreeSectionDto[],
|
||||
category: CircuitGroupCategory
|
||||
): CircuitGroupRenumberPlan | null {
|
||||
const groups = getOrderedCategoryGroups(sections, category);
|
||||
if (!canRenumberCircuitGroups(sections, category)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
groups: groups.map((group, index) => {
|
||||
const targetGroupNumber = index + 1;
|
||||
const expectedPrefix = formatGroupPrefix(
|
||||
category,
|
||||
group.groupNumber
|
||||
);
|
||||
if (group.prefix !== expectedPrefix) {
|
||||
throw new Error(
|
||||
`Das Präfix der Gruppe „${group.displayName}“ passt nicht zu ihrer Gruppennummer.`
|
||||
);
|
||||
}
|
||||
return {
|
||||
groupId: group.id,
|
||||
category,
|
||||
expectedGroupNumber: group.groupNumber,
|
||||
targetGroupNumber,
|
||||
expectedPrefix,
|
||||
targetPrefix: formatGroupPrefix(category, targetGroupNumber),
|
||||
circuits: group.circuits.map((circuit) => {
|
||||
const circuitNumber = parseCircuitNumber(
|
||||
circuit.equipmentIdentifier,
|
||||
category,
|
||||
group.groupNumber
|
||||
);
|
||||
return {
|
||||
circuitId: circuit.id,
|
||||
expectedEquipmentIdentifier: circuit.equipmentIdentifier,
|
||||
targetEquipmentIdentifier: formatCircuitIdentifier(
|
||||
category,
|
||||
targetGroupNumber,
|
||||
circuitNumber
|
||||
),
|
||||
};
|
||||
}),
|
||||
components: group.components.map((component) => ({
|
||||
componentId: component.id,
|
||||
expectedEquipmentIdentifier: component.equipmentIdentifier,
|
||||
targetEquipmentIdentifier: formatGroupComponentIdentifier(
|
||||
category,
|
||||
targetGroupNumber,
|
||||
component.role
|
||||
),
|
||||
})),
|
||||
};
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
export function canRenumberCircuitGroups(
|
||||
sections: readonly CircuitTreeSectionDto[],
|
||||
category: CircuitGroupCategory
|
||||
) {
|
||||
const groups = getOrderedCategoryGroups(sections, category);
|
||||
return (
|
||||
groups.length > 0 &&
|
||||
groups.some((group, index) => group.groupNumber !== index + 1)
|
||||
);
|
||||
}
|
||||
|
||||
function getNewGroupSortOrder(
|
||||
category: CircuitGroupCategory,
|
||||
sections: readonly (CircuitTreeSectionDto & {
|
||||
@@ -141,3 +213,77 @@ function getNewGroupSortOrder(
|
||||
? previous.sortOrder + (next.sortOrder - previous.sortOrder) / 2
|
||||
: previous.sortOrder + 10;
|
||||
}
|
||||
|
||||
function formatGroupPrefix(
|
||||
category: CircuitGroupCategory,
|
||||
groupNumber: number
|
||||
) {
|
||||
return `-${circuitGroupCategoryNumbers[category]}F${groupNumber}.`;
|
||||
}
|
||||
|
||||
function getOrderedCategoryGroups(
|
||||
sections: readonly CircuitTreeSectionDto[],
|
||||
category: CircuitGroupCategory
|
||||
) {
|
||||
return sections
|
||||
.filter(
|
||||
(section): section is CircuitTreeSectionDto & {
|
||||
category: CircuitGroupCategory;
|
||||
groupNumber: number;
|
||||
} =>
|
||||
section.category === category &&
|
||||
Number.isInteger(section.groupNumber) &&
|
||||
(section.groupNumber ?? 0) > 0
|
||||
)
|
||||
.sort(
|
||||
(left, right) =>
|
||||
left.sortOrder - right.sortOrder ||
|
||||
left.id.localeCompare(right.id)
|
||||
);
|
||||
}
|
||||
|
||||
function formatCircuitIdentifier(
|
||||
category: CircuitGroupCategory,
|
||||
groupNumber: number,
|
||||
circuitNumber: number
|
||||
) {
|
||||
return `${formatGroupPrefix(category, groupNumber)}${circuitNumber}`;
|
||||
}
|
||||
|
||||
function parseCircuitNumber(
|
||||
equipmentIdentifier: string,
|
||||
category: CircuitGroupCategory,
|
||||
groupNumber: number
|
||||
) {
|
||||
const prefix = formatGroupPrefix(category, groupNumber);
|
||||
const suffix = equipmentIdentifier.startsWith(prefix)
|
||||
? equipmentIdentifier.slice(prefix.length)
|
||||
: "";
|
||||
const circuitNumber = Number(suffix);
|
||||
if (
|
||||
!/^[1-9]\d*$/.test(suffix) ||
|
||||
!Number.isSafeInteger(circuitNumber)
|
||||
) {
|
||||
throw new Error(
|
||||
`Das Stromkreis-BMK „${equipmentIdentifier}“ passt nicht zu seiner Gruppe.`
|
||||
);
|
||||
}
|
||||
return circuitNumber;
|
||||
}
|
||||
|
||||
function formatGroupComponentIdentifier(
|
||||
category: CircuitGroupCategory,
|
||||
groupNumber: number,
|
||||
role: CircuitTreeSectionDto["components"][number]["role"]
|
||||
) {
|
||||
const categoryNumber = circuitGroupCategoryNumbers[category];
|
||||
if (role === "group_upstream_protection") {
|
||||
return `-${categoryNumber}F${groupNumber}.0`;
|
||||
}
|
||||
if (role === "group_residual_current_protection") {
|
||||
return `-${categoryNumber}Q${groupNumber}.0`;
|
||||
}
|
||||
throw new Error(
|
||||
"Die Stromkreisgruppe enthält eine nicht unterstützte Verteilerkomponente."
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user