Manage distribution board components

This commit is contained in:
Julian Appel 2026-07-31 07:29:25 +02:00
parent 31589875b5
commit 756e307bd8
9 changed files with 877 additions and 8 deletions

View file

@ -52,6 +52,9 @@ import type {
import type {
CircuitSectionRenumberAssignment,
} from "../../domain/models/circuit-section-renumber-project-command.model";
import type {
DistributionBoardComponentSnapshot,
} from "../../domain/models/distribution-board-component-structure-project-command.model";
async function request<T>(url: string, init?: RequestInit): Promise<T> {
const response = await fetch(url, {
@ -320,6 +323,58 @@ export function getCircuitTree(projectId: string, circuitListId: string) {
return request<CircuitTreeResponseDto>(`/api/projects/${projectId}/circuit-lists/${circuitListId}/tree`);
}
export function insertDistributionBoardComponentCommand(
projectId: string,
expectedRevision: number,
snapshot: DistributionBoardComponentSnapshot
) {
return executeProjectCommand(
projectId,
expectedRevision,
{
schemaVersion: 1,
type: "distribution-board-component.insert",
payload: { snapshot },
},
"Verteilerkomponente hinzufügen"
);
}
export function updateDistributionBoardComponentCommand(
projectId: string,
expectedRevision: number,
expected: DistributionBoardComponentSnapshot,
target: DistributionBoardComponentSnapshot
) {
return executeProjectCommand(
projectId,
expectedRevision,
{
schemaVersion: 1,
type: "distribution-board-component.update",
payload: { expected, target },
},
"Verteilerkomponente bearbeiten"
);
}
export function deleteDistributionBoardComponentCommand(
projectId: string,
expectedRevision: number,
snapshot: DistributionBoardComponentSnapshot
) {
return executeProjectCommand(
projectId,
expectedRevision,
{
schemaVersion: 1,
type: "distribution-board-component.delete",
payload: { snapshot },
},
"Verteilerkomponente entfernen"
);
}
export function updateCircuitById(
projectId: string,
expectedRevision: number,

View file

@ -0,0 +1,139 @@
import type {
DistributionBoardComponentSnapshot,
} from "../../domain/models/distribution-board-component-structure-project-command.model";
import type {
CircuitTreeComponentDto,
CircuitTreeProtectionDeviceDto,
CircuitTreeSectionDto,
} from "../types";
import { circuitGroupCategoryNumbers } from "../../shared/constants/circuit-group";
export type MutableDistributionBoardComponentRole =
| "group_upstream_protection"
| "group_residual_current_protection"
| "auxiliary";
export interface DistributionBoardComponentEditorValues {
equipmentIdentifier: string;
name: string;
protectionDevice?: CircuitTreeProtectionDeviceDto;
}
export function toDistributionBoardComponentSnapshot(
component: CircuitTreeComponentDto
): DistributionBoardComponentSnapshot {
if (
component.role !== "group_upstream_protection" &&
component.role !== "group_residual_current_protection" &&
component.role !== "auxiliary"
) {
throw new Error("Feste Verteilerkomponenten können hier nicht bearbeitet werden.");
}
return {
component: {
id: component.id,
circuitListId: component.circuitListId,
sectionId: component.sectionId ?? null,
equipmentIdentifier: component.equipmentIdentifier,
name: component.name,
role: component.role,
placement: component.placement,
sortOrder: component.sortOrder,
},
protectionDevice: component.protectionDevice
? {
componentId: component.id,
type: component.protectionDevice.type,
ratedCurrentA: component.protectionDevice.ratedCurrentA,
fuseUtilizationCategory:
component.protectionDevice.fuseUtilizationCategory ?? null,
tripCharacteristic:
component.protectionDevice.tripCharacteristic ?? null,
rcdType: component.protectionDevice.rcdType ?? null,
ratedResidualCurrentMa:
component.protectionDevice.ratedResidualCurrentMa ?? null,
}
: null,
};
}
export function buildDistributionBoardComponentSnapshot(input: {
id: string;
circuitListId: string;
role: MutableDistributionBoardComponentRole;
sectionId?: string;
sortOrder: number;
values: DistributionBoardComponentEditorValues;
}): DistributionBoardComponentSnapshot {
const groupComponent = input.role !== "auxiliary";
if (groupComponent && !input.sectionId) {
throw new Error("Für den Gruppenschutz fehlt die Stromkreisgruppe.");
}
if (groupComponent && !input.values.protectionDevice) {
throw new Error("Für den Gruppenschutz fehlt die Schutzgerätekonfiguration.");
}
return {
component: {
id: input.id,
circuitListId: input.circuitListId,
sectionId: groupComponent ? input.sectionId! : null,
equipmentIdentifier: input.values.equipmentIdentifier.trim(),
name: input.values.name.trim(),
role: input.role,
placement: groupComponent ? "group" : "footer",
sortOrder: input.sortOrder,
},
protectionDevice:
groupComponent && input.values.protectionDevice
? {
componentId: input.id,
type: input.values.protectionDevice.type,
ratedCurrentA: input.values.protectionDevice.ratedCurrentA,
fuseUtilizationCategory:
input.values.protectionDevice.fuseUtilizationCategory ?? null,
tripCharacteristic:
input.values.protectionDevice.tripCharacteristic ?? null,
rcdType: input.values.protectionDevice.rcdType ?? null,
ratedResidualCurrentMa:
input.values.protectionDevice.ratedResidualCurrentMa ?? null,
}
: null,
};
}
export function updateDistributionBoardComponentSnapshot(
expected: DistributionBoardComponentSnapshot,
values: DistributionBoardComponentEditorValues
): DistributionBoardComponentSnapshot {
return buildDistributionBoardComponentSnapshot({
id: expected.component.id,
circuitListId: expected.component.circuitListId,
role: expected.component.role,
sectionId: expected.component.sectionId ?? undefined,
sortOrder: expected.component.sortOrder,
values,
});
}
export function getSuggestedGroupComponentIdentifier(
section: CircuitTreeSectionDto,
role:
| "group_upstream_protection"
| "group_residual_current_protection"
): string {
if (!section.category || !section.groupNumber) {
return "";
}
const categoryNumber = circuitGroupCategoryNumbers[section.category];
const deviceLetter =
role === "group_upstream_protection" ? "F" : "Q";
return `-${categoryNumber}${deviceLetter}${section.groupNumber}.0`;
}
export function getNextComponentSortOrder(
components: readonly { sortOrder: number }[]
): number {
return components.length === 0
? 10
: Math.max(...components.map((component) => component.sortOrder)) + 10;
}