forked from jappel/leistungsbilanz-ts
Merge branch 'main' into feature/user-management
This commit is contained in:
commit
73579d1e79
22 changed files with 1141 additions and 54 deletions
|
|
@ -1766,7 +1766,7 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
|||
const currentDisplay =
|
||||
mode === "replaceWithTypedChar" && cell.cellKey !== "phaseType"
|
||||
? typedChar ?? ""
|
||||
: String(visibleCell.value ?? "");
|
||||
: String(visibleCell.editValue ?? visibleCell.value ?? "");
|
||||
focusTokenRef.current += 1;
|
||||
setEditingCell({
|
||||
...cell,
|
||||
|
|
@ -4077,7 +4077,9 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
|||
key={row.rowKey}
|
||||
className={`${
|
||||
row.rowType === "circuitSummary"
|
||||
? "summary-row"
|
||||
? row.downstreamBoardLoad
|
||||
? "feeder-circuit-row"
|
||||
: "summary-row"
|
||||
: row.rowType === "deviceRow"
|
||||
? "device-row"
|
||||
: row.rowType === "downstreamBoardLoad"
|
||||
|
|
@ -4708,11 +4710,19 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
|||
tabIndex={-1}
|
||||
/>
|
||||
<IconButton
|
||||
disabled={isSaving}
|
||||
disabled={
|
||||
isSaving ||
|
||||
(row.circuit.downstreamBoardLoads?.length ?? 0) > 0
|
||||
}
|
||||
icon={Trash2}
|
||||
label="Stromkreis löschen"
|
||||
onClick={() => void handleDeleteCircuit(row.circuit!.id)}
|
||||
tabIndex={-1}
|
||||
title={
|
||||
(row.circuit.downstreamBoardLoads?.length ?? 0) > 0
|
||||
? "Speist eine Unterverteilung – zuerst deren Einspeisung ändern"
|
||||
: undefined
|
||||
}
|
||||
variant="danger"
|
||||
/>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -431,6 +431,25 @@ function CircuitRows({ circuit }: { circuit: CircuitTreeCircuitDto }) {
|
|||
</tr>
|
||||
);
|
||||
}
|
||||
if (rows.length === 0 && downstreamBoards.length === 1) {
|
||||
const boardLabel =
|
||||
downstreamBoards[0].boardNumber ?? downstreamBoards[0].name;
|
||||
return (
|
||||
<tr className="pd-circuit">
|
||||
<td className="pd-bmk">{circuit.equipmentIdentifier}</td>
|
||||
<td>
|
||||
{boardLabel}
|
||||
{circuit.displayName ? (
|
||||
<span className="pd-sub"> · {circuit.displayName}</span>
|
||||
) : null}
|
||||
</td>
|
||||
<td>–</td>
|
||||
<td /><td /><td />
|
||||
<td className="pd-r"><strong>{formatKilowatt(circuit.circuitTotalPower)}</strong></td>
|
||||
{circuitCells}
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
if (rows.length === 1 && downstreamBoards.length === 0) {
|
||||
const row = rows[0];
|
||||
const title = circuit.displayName ?? row.displayName;
|
||||
|
|
|
|||
|
|
@ -366,20 +366,22 @@ export function createDistributionBoard(
|
|||
);
|
||||
}
|
||||
|
||||
export type UpdateDistributionBoardInput = {
|
||||
floorId: string | null;
|
||||
supplyType: NonNullable<DistributionBoardDto["supplyType"]>;
|
||||
simultaneityFactor: number;
|
||||
} & Partial<DistributionBoardInfoDto> &
|
||||
Partial<
|
||||
Pick<
|
||||
DistributionBoardDto,
|
||||
"feedingDistributionBoardId" | "feedingCircuitId"
|
||||
>
|
||||
>;
|
||||
|
||||
export function updateDistributionBoard(
|
||||
projectId: string,
|
||||
distributionBoardId: string,
|
||||
input: {
|
||||
floorId: string | null;
|
||||
supplyType: NonNullable<DistributionBoardDto["supplyType"]>;
|
||||
simultaneityFactor: number;
|
||||
} & Partial<DistributionBoardInfoDto> &
|
||||
Partial<
|
||||
Pick<
|
||||
DistributionBoardDto,
|
||||
"feedingDistributionBoardId" | "feedingCircuitId"
|
||||
>
|
||||
>,
|
||||
input: UpdateDistributionBoardInput,
|
||||
expectedRevision: number
|
||||
) {
|
||||
return request<DistributionBoardCommandResultDto>(
|
||||
|
|
@ -391,6 +393,41 @@ export function updateDistributionBoard(
|
|||
);
|
||||
}
|
||||
|
||||
export function createFeederCircuitAndUpdateDistributionBoard(
|
||||
projectId: string,
|
||||
distributionBoardId: string,
|
||||
input: UpdateDistributionBoardInput & {
|
||||
feedingDistributionBoardId: string;
|
||||
feedingCircuitId: string;
|
||||
},
|
||||
circuit: CircuitSnapshot,
|
||||
expectedRevision: number
|
||||
) {
|
||||
return executeProjectCommand(
|
||||
projectId,
|
||||
expectedRevision,
|
||||
{
|
||||
schemaVersion: 1,
|
||||
type: "distribution-board.create-feeder-circuit",
|
||||
payload: {
|
||||
circuit,
|
||||
boardUpdate: {
|
||||
schemaVersion: 2,
|
||||
type: "distribution-board.update",
|
||||
payload: {
|
||||
distributionBoardId,
|
||||
changes: Object.entries(input).map(([field, value]) => ({
|
||||
field,
|
||||
value,
|
||||
})),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"Einspeiseabgang anlegen und Verteilung verknüpfen"
|
||||
);
|
||||
}
|
||||
|
||||
export const documentLogoUrl = "/api/document-branding/logo";
|
||||
|
||||
export function getDocumentBranding() {
|
||||
|
|
|
|||
|
|
@ -617,7 +617,11 @@ export function getCellKind(rowType: RowType, key: CellKey): CellKind {
|
|||
if (rowType === "deviceRow") return deviceFieldKeys.has(key) ? "deviceField" : "readonly";
|
||||
if (rowType === "downstreamBoardLoad") return "readonly";
|
||||
if (rowType === "circuitSummary") return circuitFieldKeys.has(key) ? "circuitField" : "readonly";
|
||||
if (rowType === "reserveCircuit" || rowType === "circuitCompact") {
|
||||
if (rowType === "reserveCircuit") {
|
||||
if (circuitFieldKeys.has(key)) return "circuitField";
|
||||
if (deviceFieldKeys.has(key)) return "deviceField";
|
||||
}
|
||||
if (rowType === "circuitCompact") {
|
||||
if (deviceFieldKeys.has(key)) return "deviceField";
|
||||
if (circuitFieldKeys.has(key)) return "circuitField";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ export interface VisibleGridCell {
|
|||
editable: boolean;
|
||||
kind: CellKind;
|
||||
value: GridValue;
|
||||
/** Separate persisted value when the displayed value is derived. */
|
||||
editValue?: GridValue;
|
||||
}
|
||||
|
||||
export interface VisibleGridRow {
|
||||
|
|
@ -144,6 +146,7 @@ function makeVisibleGridRow(
|
|||
const kind = getCellKind(rowType, column.key);
|
||||
const editable = kind === "circuitField" || kind === "deviceField";
|
||||
let value: GridValue;
|
||||
let editValue: GridValue | undefined;
|
||||
|
||||
if (rowType === "downstreamBoardLoad" && downstreamBoardLoad) {
|
||||
value = column.key === "displayName"
|
||||
|
|
@ -155,6 +158,35 @@ function makeVisibleGridRow(
|
|||
: column.key === "remark"
|
||||
? "Automatisch aus der verknüpften Verteilung"
|
||||
: undefined;
|
||||
} else if (
|
||||
rowType === "circuitSummary" &&
|
||||
circuit &&
|
||||
downstreamBoardLoad &&
|
||||
circuit.deviceRows.length === 0
|
||||
) {
|
||||
if (column.key === "displayName") {
|
||||
const boardLabel =
|
||||
downstreamBoardLoad.boardNumber ?? downstreamBoardLoad.name;
|
||||
value = circuit.displayName
|
||||
? `${boardLabel} · ${circuit.displayName}`
|
||||
: boardLabel;
|
||||
editValue = circuit.displayName ?? "";
|
||||
} else if (column.key === "cosPhi") {
|
||||
value = circuit.circuitCosPhi;
|
||||
} else if (
|
||||
kind === "computed" &&
|
||||
circuitOnlyColumns.has(column.key)
|
||||
) {
|
||||
value = getCircuitValue(circuit, column.key);
|
||||
} else if (kind === "circuitField") {
|
||||
value = getCircuitValue(circuit, column.key);
|
||||
} else if (column.key === "rowTotalPower") {
|
||||
value = circuit.circuitTotalPower;
|
||||
} else if (column.key === "circuitTotalPower") {
|
||||
value = circuit.circuitTotalPower;
|
||||
} else if (column.key === "voltage") {
|
||||
value = circuit.voltage;
|
||||
}
|
||||
} else if (rowType === "placeholder") {
|
||||
value = column.key === "equipmentIdentifier" ? "-frei-" : undefined;
|
||||
} else if (kind === "deviceField" && device) {
|
||||
|
|
@ -178,7 +210,7 @@ function makeVisibleGridRow(
|
|||
value = circuit.voltage;
|
||||
}
|
||||
|
||||
return { cellKey: column.key, editable, kind, value };
|
||||
return { cellKey: column.key, editable, kind, value, editValue };
|
||||
});
|
||||
|
||||
return {
|
||||
|
|
@ -247,6 +279,19 @@ export function buildVisibleGridRowsWithStructure(
|
|||
const downstreamLoads = circuit.downstreamBoardLoads ?? [];
|
||||
if (circuit.deviceRows.length === 0 && downstreamLoads.length === 0) {
|
||||
rows.push(makeVisibleGridRow("reserveCircuit", section.id, circuit));
|
||||
} else if (
|
||||
circuit.deviceRows.length === 0 &&
|
||||
downstreamLoads.length === 1
|
||||
) {
|
||||
rows.push(
|
||||
makeVisibleGridRow(
|
||||
"circuitSummary",
|
||||
section.id,
|
||||
circuit,
|
||||
undefined,
|
||||
downstreamLoads[0]
|
||||
)
|
||||
);
|
||||
} else if (circuit.deviceRows.length === 1 && downstreamLoads.length === 0) {
|
||||
rows.push(makeVisibleGridRow("circuitCompact", section.id, circuit, circuit.deviceRows[0]));
|
||||
} else {
|
||||
|
|
|
|||
68
src/frontend/utils/distribution-board-feeder-circuit.ts
Normal file
68
src/frontend/utils/distribution-board-feeder-circuit.ts
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
import type { CircuitSnapshot } from "../../domain/models/circuit-structure-project-command.model.js";
|
||||
import type {
|
||||
CircuitTreeResponseDto,
|
||||
CircuitTreeSectionDto,
|
||||
} from "../types";
|
||||
import { buildCircuitInsertSnapshot } from "./circuit-structure-command";
|
||||
|
||||
export function getDefaultFeederCircuitSection(
|
||||
tree: CircuitTreeResponseDto | undefined
|
||||
) {
|
||||
if (!tree) return undefined;
|
||||
return (
|
||||
tree.sections.find((section) => section.category === "three_phase") ??
|
||||
tree.sections.find((section) => section.category !== undefined)
|
||||
);
|
||||
}
|
||||
|
||||
export function getNextFeederCircuitIdentifier(
|
||||
section: CircuitTreeSectionDto
|
||||
) {
|
||||
let highestSuffix = 0;
|
||||
for (const circuit of section.circuits) {
|
||||
if (!circuit.equipmentIdentifier.startsWith(section.prefix)) continue;
|
||||
const suffix = circuit.equipmentIdentifier.slice(section.prefix.length);
|
||||
if (!/^\d+$/.test(suffix)) continue;
|
||||
highestSuffix = Math.max(highestSuffix, Number(suffix));
|
||||
}
|
||||
return `${section.prefix}${highestSuffix + 1}`;
|
||||
}
|
||||
|
||||
export function buildFeederCircuitSnapshot(input: {
|
||||
id: string;
|
||||
tree: CircuitTreeResponseDto;
|
||||
sectionId: string;
|
||||
displayName: string;
|
||||
}): CircuitSnapshot {
|
||||
const section = input.tree.sections.find(
|
||||
(candidate) => candidate.id === input.sectionId
|
||||
);
|
||||
if (!section || !section.category) {
|
||||
throw new Error("Die gewählte Stromkreisgruppe ist nicht verfügbar.");
|
||||
}
|
||||
const snapshot = buildCircuitInsertSnapshot({
|
||||
id: input.id,
|
||||
circuitListId: input.tree.circuitListId,
|
||||
category: section.category,
|
||||
deviceRows: [],
|
||||
values: {
|
||||
sectionId: section.id,
|
||||
equipmentIdentifier: getNextFeederCircuitIdentifier(section),
|
||||
displayName: input.displayName.trim() || undefined,
|
||||
sortOrder:
|
||||
section.circuits.length === 0
|
||||
? 10
|
||||
: Math.max(...section.circuits.map((circuit) => circuit.sortOrder)) +
|
||||
10,
|
||||
voltage:
|
||||
section.category === "three_phase"
|
||||
? input.tree.threePhaseVoltageV
|
||||
: input.tree.singlePhaseVoltageV,
|
||||
isReserve: true,
|
||||
},
|
||||
});
|
||||
// A distribution-board feeder is not a generic final circuit. Its protection
|
||||
// must be selected from the calculated load and cable, so no 16 A group
|
||||
// default is persisted here.
|
||||
return { ...snapshot, protectionDevice: null };
|
||||
}
|
||||
|
|
@ -32,6 +32,10 @@ const commandTypeLabels: Record<string, string> = {
|
|||
"project.update-settings": "Projekteinstellungen bearbeitet",
|
||||
"distribution-board.insert": "Verteilung angelegt",
|
||||
"distribution-board.update": "Verteilung bearbeitet",
|
||||
"distribution-board.create-feeder-circuit":
|
||||
"Einspeiseabgang angelegt und verknüpft",
|
||||
"distribution-board.remove-feeder-circuit":
|
||||
"Einspeiseabgang und Verknüpfung entfernt",
|
||||
"distribution-board.delete": "Verteilung entfernt",
|
||||
"distribution-board.insert-subtree": "Verteilung vollständig eingefügt",
|
||||
"distribution-board.delete-subtree": "Verteilung vollständig entfernt",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue