From 04c299e3f2300b15f0a6d5bd226a4bae483dfe1c Mon Sep 17 00:00:00 2001 From: Julian Appel Date: Thu, 23 Jul 2026 19:23:53 +0200 Subject: [PATCH] Remove legacy consumer UI --- docs/circuit-list-editor-known-limitations.md | 2 +- docs/circuit-list-editor-migration.md | 5 +- .../[circuitListId]/tree-edit/page.tsx | 3 - .../[circuitListId]/tree/page.tsx | 4 +- .../[projectId]/circuit-lists/page.tsx | 1669 +---------------- src/app/projects/[projectId]/page.tsx | 11 - src/frontend/components/README.md | 4 - .../components/power-balance-workspace.tsx | 666 ------- src/frontend/types.ts | 77 - src/frontend/utils/api.ts | 25 - src/shared/types/power.types.ts | 39 - 11 files changed, 14 insertions(+), 2491 deletions(-) delete mode 100644 src/frontend/components/README.md delete mode 100644 src/frontend/components/power-balance-workspace.tsx delete mode 100644 src/shared/types/power.types.ts diff --git a/docs/circuit-list-editor-known-limitations.md b/docs/circuit-list-editor-known-limitations.md index 52d615f..b698006 100644 --- a/docs/circuit-list-editor-known-limitations.md +++ b/docs/circuit-list-editor-known-limitations.md @@ -10,4 +10,4 @@ - No full norm-compliant voltage-drop and protection-dimensioning calculation flow yet. - Sorting is view-only until users explicitly apply sorted order. - Cross-section circuit drag-reorder is intentionally blocked. -- Legacy consumer and circuit-first paths coexist; migration is transitional and still requires operational discipline. +- The legacy consumer UI has been removed after complete migration; its server endpoints and retained database records are still pending final cleanup. diff --git a/docs/circuit-list-editor-migration.md b/docs/circuit-list-editor-migration.md index 12fda5b..a6a29a4 100644 --- a/docs/circuit-list-editor-migration.md +++ b/docs/circuit-list-editor-migration.md @@ -46,7 +46,8 @@ Run in this order for local database workflows: The migration command finishes with a cutover verification across the complete database. It exits with an error while any legacy consumer lacks a migration mapping, including consumers that cannot be migrated because they have no circuit -list assignment. Do not remove or disable the legacy path until this check passes. +list assignment. The legacy multi-list UI was removed only after this check passed; +old `/projects/:projectId/circuit-lists` bookmarks redirect to the project page. ## Validation Checks @@ -77,6 +78,6 @@ Actions: Do not delete legacy consumers yet. -- legacy endpoints/views remain enabled only until the cutover verification passes +- legacy write endpoints remain temporarily available for the next server cutover step - migration trace tables reference old/new mapping - keeping legacy rows allows comparison and rollback validation during transition diff --git a/src/app/projects/[projectId]/circuit-lists/[circuitListId]/tree-edit/page.tsx b/src/app/projects/[projectId]/circuit-lists/[circuitListId]/tree-edit/page.tsx index 1027ec6..4c4ce86 100644 --- a/src/app/projects/[projectId]/circuit-lists/[circuitListId]/tree-edit/page.tsx +++ b/src/app/projects/[projectId]/circuit-lists/[circuitListId]/tree-edit/page.tsx @@ -24,9 +24,6 @@ export default function CircuitTreeEditPage() { Zum Projekt - - Bisherige Listenansicht - diff --git a/src/app/projects/[projectId]/circuit-lists/[circuitListId]/tree/page.tsx b/src/app/projects/[projectId]/circuit-lists/[circuitListId]/tree/page.tsx index 600cf69..8cfea27 100644 --- a/src/app/projects/[projectId]/circuit-lists/[circuitListId]/tree/page.tsx +++ b/src/app/projects/[projectId]/circuit-lists/[circuitListId]/tree/page.tsx @@ -25,10 +25,10 @@ export default function CircuitTreePreviewPage() { Editor öffnen - Zurück zur bisherigen Listenansicht + Zum Projekt diff --git a/src/app/projects/[projectId]/circuit-lists/page.tsx b/src/app/projects/[projectId]/circuit-lists/page.tsx index 60d880f..d83a3c5 100644 --- a/src/app/projects/[projectId]/circuit-lists/page.tsx +++ b/src/app/projects/[projectId]/circuit-lists/page.tsx @@ -1,1665 +1,12 @@ -"use client"; +import { redirect } from "next/navigation"; -import Link from "next/link"; -import { useParams, useSearchParams } from "next/navigation"; -import { FormEvent, useEffect, useMemo, useState } from "react"; -import { - createConsumer, - deleteConsumer, - getProject, - listConsumers, - listDistributionBoards, - listProjectDevices, - listRooms, - updateConsumer, -} from "../../../../frontend/utils/api"; -import type { - ConsumerWithCalculatedValues, - CreateConsumerInput, - DistributionBoardDto, - ProjectDeviceDto, - ProjectDto, - RoomDto, -} from "../../../../frontend/types"; -import { - cableCrossSectionOptions, - cableTypeOptions, - consumerGroupOptions, - deviceTypeOptions, - phaseTypeOptions, - protectionCharacteristicOptions, - protectionTypeOptions, - tradeOrCostGroupOptions, -} from "../../../../shared/constants/consumer-option-lists"; - -interface SlotState { - boardId: string; - selectedConsumerIds: string[]; +interface CircuitListsRedirectPageProps { + params: Promise<{ projectId: string }>; } -interface QuickCreateFormState { - name: string; - roomId: string; - quantity: string; - installedPowerPerUnitKw: string; - demandFactor: string; - totalPowerKw: string; - addCount: string; +export default async function CircuitListsRedirectPage({ + params, +}: CircuitListsRedirectPageProps) { + const { projectId } = await params; + redirect(`/projects/${projectId}`); } - -type SortField = "name" | "circuitNumber" | "quantity" | "installedPowerPerUnitKw" | "demandFactor" | "demandPowerKw"; -type SortDirection = "asc" | "desc"; - -interface ListFilterState { - query: string; - sortField: SortField; - sortDirection: SortDirection; -} - -interface BulkEditState { - quantity: string; - installedPowerPerUnitKw: string; - demandFactor: string; -} - -type ColumnKey = - | "selection" - | "circuitNumber" - | "description" - | "name" - | "projectDevice" - | "deviceLink" - | "room" - | "floor" - | "category" - | "deviceType" - | "phaseType" - | "tradeOrCostGroup" - | "group" - | "protectionType" - | "protectionRatedCurrent" - | "protectionCharacteristic" - | "cableType" - | "cableCrossSection" - | "comment" - | "quantity" - | "installedPowerPerUnitKw" - | "installedPowerKw" - | "demandFactor" - | "demandPowerKw" - | "voltageV" - | "phaseCount" - | "powerFactor" - | "currentA" - | "note" - | "actions"; - -const defaultSlots: SlotState[] = [ - { boardId: "", selectedConsumerIds: [] }, - { boardId: "", selectedConsumerIds: [] }, - { boardId: "", selectedConsumerIds: [] }, -]; - -const defaultCreateForm: QuickCreateFormState = { - name: "", - roomId: "", - quantity: "1", - installedPowerPerUnitKw: "0.10", - demandFactor: "1.00", - totalPowerKw: "0.10", - addCount: "1", -}; - -const defaultFilterState: ListFilterState = { - query: "", - sortField: "name", - sortDirection: "asc", -}; - -const defaultBulkEditState: BulkEditState = { - quantity: "", - installedPowerPerUnitKw: "", - demandFactor: "", -}; - -const allColumns: Array<{ key: ColumnKey; label: string }> = [ - { key: "selection", label: "Auswahl" }, - { key: "circuitNumber", label: "Stromkreis-Nr." }, - { key: "description", label: "Beschreibung" }, - { key: "name", label: "Verbraucher" }, - { key: "projectDevice", label: "Projektgerät" }, - { key: "deviceLink", label: "Verknüpfung" }, - { key: "room", label: "Raum" }, - { key: "floor", label: "Etage" }, - { key: "category", label: "Kategorie" }, - { key: "deviceType", label: "Geräteart" }, - { key: "phaseType", label: "Phasenart" }, - { key: "tradeOrCostGroup", label: "Kostengruppe" }, - { key: "group", label: "Gruppe" }, - { key: "protectionType", label: "Schutzart" }, - { key: "protectionRatedCurrent", label: "Schutznennstrom [A]" }, - { key: "protectionCharacteristic", label: "Schutzkennlinie" }, - { key: "cableType", label: "Kabeltyp" }, - { key: "cableCrossSection", label: "Querschnitt" }, - { key: "comment", label: "Kommentar" }, - { key: "quantity", label: "Anzahl" }, - { key: "installedPowerPerUnitKw", label: "Einzelleistung [kW]" }, - { key: "installedPowerKw", label: "Installierte Leistung [kW]" }, - { key: "demandFactor", label: "Gleichzeitigkeitsfaktor" }, - { key: "demandPowerKw", label: "Gesamtleistung [kW]" }, - { key: "voltageV", label: "Spannung [V]" }, - { key: "phaseCount", label: "Phasen" }, - { key: "powerFactor", label: "cos f" }, - { key: "currentA", label: "Strom [A]" }, - { key: "note", label: "Bemerkung" }, - { key: "actions", label: "Aktionen" }, -]; - -const defaultVisibleColumns: ColumnKey[] = [ - "selection", - "circuitNumber", - "description", - "name", - "projectDevice", - "deviceLink", - "room", - "quantity", - "installedPowerPerUnitKw", - "demandFactor", - "demandPowerKw", - "actions", -]; - -function formatNumber(value: number | undefined, digits = 2) { - if (value === undefined || Number.isNaN(value)) { - return "-"; - } - return value.toFixed(digits); -} - -function roomLabel(room: RoomDto) { - return `${room.roomNumber} - ${room.roomName}`; -} - -export default function CircuitListsPage() { - const params = useParams<{ projectId: string }>(); - const searchParams = useSearchParams(); - const [projectId, setProjectId] = useState(""); - const [project, setProject] = useState(null); - const [boards, setBoards] = useState([]); - const [rooms, setRooms] = useState([]); - const [projectDevices, setProjectDevices] = useState([]); - const [consumers, setConsumers] = useState([]); - const [slots, setSlots] = useState(defaultSlots); - const [activeListCount, setActiveListCount] = useState(1); - const [newDeviceTemplateId, setNewDeviceTemplateId] = useState>({ - 0: "", - 1: "", - 2: "", - }); - const [quickCreateForms, setQuickCreateForms] = useState>({ - 0: { ...defaultCreateForm }, - 1: { ...defaultCreateForm }, - 2: { ...defaultCreateForm }, - }); - const [visibleColumns, setVisibleColumns] = useState(defaultVisibleColumns); - const [listFilters, setListFilters] = useState>({ - 0: { ...defaultFilterState }, - 1: { ...defaultFilterState }, - 2: { ...defaultFilterState }, - }); - const [bulkEditForms, setBulkEditForms] = useState>({ - 0: { ...defaultBulkEditState }, - 1: { ...defaultBulkEditState }, - 2: { ...defaultBulkEditState }, - }); - const [showColumnManager, setShowColumnManager] = useState(false); - const [error, setError] = useState(null); - const [isSaving, setIsSaving] = useState(false); - - useEffect(() => { - setProjectId(params.projectId); - }, [params.projectId]); - - useEffect(() => { - if (!projectId) { - return; - } - Promise.all([ - getProject(projectId), - listDistributionBoards(projectId), - listRooms(projectId), - listProjectDevices(projectId), - listConsumers(projectId), - ]) - .then(([loadedProject, distributionBoards, loadedRooms, loadedProjectDevices, loadedConsumers]) => { - const initialBoardId = searchParams.get("boardId") ?? distributionBoards[0]?.id ?? ""; - setProject(loadedProject); - setBoards(distributionBoards); - setRooms(loadedRooms); - setProjectDevices(loadedProjectDevices); - setConsumers(loadedConsumers); - setSlots((current) => [ - { ...current[0], boardId: current[0].boardId || initialBoardId }, - current[1], - current[2], - ]); - setActiveListCount(1); - setError(null); - }) - .catch((err: unknown) => - setError(err instanceof Error ? err.message : "Stromkreislisten konnten nicht geladen werden.") - ); - }, [projectId, searchParams]); - - const boardById = useMemo(() => new Map(boards.map((board) => [board.id, board])), [boards]); - const slotColumnClass = - activeListCount === 1 ? "col-12" : activeListCount === 2 ? "col-12 col-xxl-6" : "col-12 col-xxl-4"; - - function consumersForBoard(boardId: string) { - return consumers.filter((item) => item.distributionBoardId === boardId); - } - - function updateListFilter(slotIndex: number, patch: Partial) { - setListFilters((current) => ({ - ...current, - [slotIndex]: { ...current[slotIndex], ...patch }, - })); - } - - function updateBulkEditForm(slotIndex: number, patch: Partial) { - setBulkEditForms((current) => ({ - ...current, - [slotIndex]: { ...current[slotIndex], ...patch }, - })); - } - - function getComparableSortValue(consumer: ConsumerWithCalculatedValues, sortField: SortField): number | string { - switch (sortField) { - case "name": - return (consumer.name ?? "").toLocaleLowerCase("de-DE"); - case "circuitNumber": - return (consumer.circuitNumber ?? "").toLocaleLowerCase("de-DE"); - case "quantity": - return consumer.quantity ?? 0; - case "installedPowerPerUnitKw": - return consumer.installedPowerPerUnitKw ?? 0; - case "demandFactor": - return consumer.demandFactor ?? 0; - case "demandPowerKw": - return consumer.demandPowerKw ?? 0; - default: - return ""; - } - } - - function setSlotBoard(slotIndex: number, boardId: string) { - setSlots((current) => - current.map((slot, index) => (index === slotIndex ? { boardId, selectedConsumerIds: [] } : slot)) - ); - } - - function addList() { - setActiveListCount((current) => Math.min(3, current + 1)); - } - - function removeList() { - setActiveListCount((current) => { - const next = Math.max(1, current - 1); - setSlots((slotState) => - slotState.map((slot, index) => (index >= next ? { boardId: "", selectedConsumerIds: [] } : slot)) - ); - return next; - }); - } - - function toggleConsumerSelection(slotIndex: number, consumerId: string) { - setSlots((current) => - current.map((slot, index) => { - if (index !== slotIndex) { - return slot; - } - const exists = slot.selectedConsumerIds.includes(consumerId); - return { - ...slot, - selectedConsumerIds: exists - ? slot.selectedConsumerIds.filter((id) => id !== consumerId) - : [...slot.selectedConsumerIds, consumerId], - }; - }) - ); - } - - function updateQuickCreateForm(slotIndex: number, patch: Partial) { - setQuickCreateForms((current) => { - const next = { ...current[slotIndex], ...patch }; - const quantity = Number(next.quantity); - const unit = Number(next.installedPowerPerUnitKw); - const demandFactor = Number(next.demandFactor); - if (!Number.isNaN(quantity) && !Number.isNaN(unit) && !Number.isNaN(demandFactor)) { - next.totalPowerKw = String(quantity * unit * demandFactor); - } - return { ...current, [slotIndex]: next }; - }); - } - - function updateTotalPower(slotIndex: number, totalPowerKw: string) { - setQuickCreateForms((current) => { - const next = { ...current[slotIndex], totalPowerKw }; - const quantity = Number(next.quantity); - const demandFactor = Number(next.demandFactor); - const total = Number(totalPowerKw); - if ( - !Number.isNaN(quantity) && - !Number.isNaN(demandFactor) && - !Number.isNaN(total) && - quantity > 0 && - demandFactor > 0 - ) { - next.installedPowerPerUnitKw = String(total / (quantity * demandFactor)); - } - return { ...current, [slotIndex]: next }; - }); - } - - async function reloadConsumers() { - if (!projectId) { - return; - } - setConsumers(await listConsumers(projectId)); - } - - async function handleCreateManualConsumer(slotIndex: number, event: FormEvent) { - event.preventDefault(); - const slot = slots[slotIndex]; - const form = quickCreateForms[slotIndex]; - const name = form.name.trim(); - if (!projectId || !slot.boardId || !name) { - return; - } - - const payload: CreateConsumerInput = { - projectId, - distributionBoardId: slot.boardId, - isLinkedToDevice: false, - roomId: form.roomId || undefined, - description: name, - name, - quantity: Number(form.quantity), - installedPowerPerUnitKw: Number(form.installedPowerPerUnitKw), - demandFactor: Number(form.demandFactor), - phaseCount: 1, - powerFactor: 1, - }; - - setIsSaving(true); - setError(null); - try { - await createConsumer(payload); - setQuickCreateForms((current) => ({ - ...current, - [slotIndex]: { ...defaultCreateForm, roomId: current[slotIndex].roomId }, - })); - await reloadConsumers(); - } catch (err) { - setError(err instanceof Error ? err.message : "Verbraucher konnte nicht erstellt werden."); - } finally { - setIsSaving(false); - } - } - - async function handleCreateFromProjectDevice(slotIndex: number) { - const slot = slots[slotIndex]; - const form = quickCreateForms[slotIndex]; - const templateId = newDeviceTemplateId[slotIndex]; - const projectDevice = projectDevices.find((item) => item.id === templateId); - if (!projectId || !slot.boardId || !projectDevice) { - return; - } - const addCountRaw = Number(form.addCount); - const addCount = Number.isFinite(addCountRaw) ? Math.max(1, Math.floor(addCountRaw)) : 1; - - const payload: CreateConsumerInput = { - projectId, - distributionBoardId: slot.boardId, - projectDeviceId: projectDevice.id, - isLinkedToDevice: true, - roomId: form.roomId || undefined, - description: projectDevice.displayName, - name: projectDevice.displayName, - category: projectDevice.category ?? undefined, - quantity: projectDevice.quantity, - installedPowerPerUnitKw: projectDevice.installedPowerPerUnitKw, - demandFactor: projectDevice.demandFactor, - phaseCount: projectDevice.phaseCount ?? 1, - powerFactor: projectDevice.powerFactor ?? 1, - note: projectDevice.note ?? undefined, - }; - - setIsSaving(true); - setError(null); - try { - await Promise.all(Array.from({ length: addCount }, () => createConsumer(payload))); - await reloadConsumers(); - } catch (err) { - setError(err instanceof Error ? err.message : "Verbraucher konnte nicht aus Projektgerät erstellt werden."); - } finally { - setIsSaving(false); - } - } - - async function handleCopyToSlot(sourceConsumer: ConsumerWithCalculatedValues, targetSlotIndex: number) { - const targetBoardId = slots[targetSlotIndex].boardId; - if (!projectId || !targetBoardId) { - return; - } - - const payload: CreateConsumerInput = { - projectId, - distributionBoardId: targetBoardId, - projectDeviceId: sourceConsumer.projectDeviceId ?? undefined, - isLinkedToDevice: sourceConsumer.isLinkedToDevice, - circuitNumber: sourceConsumer.circuitNumber, - description: sourceConsumer.description, - roomId: sourceConsumer.roomId ?? undefined, - name: sourceConsumer.name, - category: sourceConsumer.category, - deviceType: sourceConsumer.deviceType, - phaseType: sourceConsumer.phaseType, - tradeOrCostGroup: sourceConsumer.tradeOrCostGroup, - group: sourceConsumer.group, - protectionType: sourceConsumer.protectionType, - protectionRatedCurrent: sourceConsumer.protectionRatedCurrent, - protectionCharacteristic: sourceConsumer.protectionCharacteristic, - cableType: sourceConsumer.cableType, - cableCrossSection: sourceConsumer.cableCrossSection, - comment: sourceConsumer.comment, - quantity: sourceConsumer.quantity, - installedPowerPerUnitKw: sourceConsumer.installedPowerPerUnitKw, - demandFactor: sourceConsumer.demandFactor, - voltageV: sourceConsumer.voltageV, - phaseCount: sourceConsumer.phaseCount, - powerFactor: sourceConsumer.powerFactor, - note: sourceConsumer.note, - }; - - setIsSaving(true); - setError(null); - try { - await createConsumer(payload); - await reloadConsumers(); - } catch (err) { - setError(err instanceof Error ? err.message : "Kopieren in Ziel-Stromkreisliste fehlgeschlagen."); - } finally { - setIsSaving(false); - } - } - - async function handleDuplicateInSameList(sourceConsumer: ConsumerWithCalculatedValues) { - if (!projectId || !sourceConsumer.distributionBoardId) { - return; - } - - const payload: CreateConsumerInput = { - projectId, - distributionBoardId: sourceConsumer.distributionBoardId, - projectDeviceId: sourceConsumer.projectDeviceId ?? undefined, - isLinkedToDevice: sourceConsumer.isLinkedToDevice, - circuitNumber: sourceConsumer.circuitNumber, - description: sourceConsumer.description, - roomId: sourceConsumer.roomId ?? undefined, - name: sourceConsumer.name, - category: sourceConsumer.category, - deviceType: sourceConsumer.deviceType, - phaseType: sourceConsumer.phaseType, - tradeOrCostGroup: sourceConsumer.tradeOrCostGroup, - group: sourceConsumer.group, - protectionType: sourceConsumer.protectionType, - protectionRatedCurrent: sourceConsumer.protectionRatedCurrent, - protectionCharacteristic: sourceConsumer.protectionCharacteristic, - cableType: sourceConsumer.cableType, - cableCrossSection: sourceConsumer.cableCrossSection, - comment: sourceConsumer.comment, - quantity: sourceConsumer.quantity, - installedPowerPerUnitKw: sourceConsumer.installedPowerPerUnitKw, - demandFactor: sourceConsumer.demandFactor, - voltageV: sourceConsumer.voltageV, - phaseCount: sourceConsumer.phaseCount, - powerFactor: sourceConsumer.powerFactor, - note: sourceConsumer.note, - }; - - setIsSaving(true); - setError(null); - try { - await createConsumer(payload); - await reloadConsumers(); - } catch (err) { - setError(err instanceof Error ? err.message : "Duplizieren in derselben Liste fehlgeschlagen."); - } finally { - setIsSaving(false); - } - } - - async function handleCopySelectionToSlot(sourceSlotIndex: number, targetSlotIndex: number) { - const sourceSlot = slots[sourceSlotIndex]; - const targetBoardId = slots[targetSlotIndex].boardId; - if (!projectId || !targetBoardId || !sourceSlot.selectedConsumerIds.length) { - return; - } - - const selected = consumers.filter((consumer) => sourceSlot.selectedConsumerIds.includes(consumer.id)); - if (!selected.length) { - return; - } - - setIsSaving(true); - setError(null); - try { - await Promise.all( - selected.map((sourceConsumer) => - createConsumer({ - projectId, - distributionBoardId: targetBoardId, - projectDeviceId: sourceConsumer.projectDeviceId ?? undefined, - isLinkedToDevice: sourceConsumer.isLinkedToDevice, - circuitNumber: sourceConsumer.circuitNumber, - description: sourceConsumer.description, - roomId: sourceConsumer.roomId ?? undefined, - name: sourceConsumer.name, - category: sourceConsumer.category, - deviceType: sourceConsumer.deviceType, - phaseType: sourceConsumer.phaseType, - tradeOrCostGroup: sourceConsumer.tradeOrCostGroup, - group: sourceConsumer.group, - protectionType: sourceConsumer.protectionType, - protectionRatedCurrent: sourceConsumer.protectionRatedCurrent, - protectionCharacteristic: sourceConsumer.protectionCharacteristic, - cableType: sourceConsumer.cableType, - cableCrossSection: sourceConsumer.cableCrossSection, - comment: sourceConsumer.comment, - quantity: sourceConsumer.quantity, - installedPowerPerUnitKw: sourceConsumer.installedPowerPerUnitKw, - demandFactor: sourceConsumer.demandFactor, - voltageV: sourceConsumer.voltageV, - phaseCount: sourceConsumer.phaseCount, - powerFactor: sourceConsumer.powerFactor, - note: sourceConsumer.note, - }) - ) - ); - await reloadConsumers(); - setSlots((current) => - current.map((slot, index) => (index === sourceSlotIndex ? { ...slot, selectedConsumerIds: [] } : slot)) - ); - } catch (err) { - setError(err instanceof Error ? err.message : "Sammel-Kopieren fehlgeschlagen."); - } finally { - setIsSaving(false); - } - } - - async function handleBulkEditSelection(slotIndex: number) { - const slot = slots[slotIndex]; - if (!slot.selectedConsumerIds.length) { - return; - } - - const form = bulkEditForms[slotIndex]; - const patch: Partial = {}; - const quantity = form.quantity.trim(); - const installedPowerPerUnitKw = form.installedPowerPerUnitKw.trim(); - const demandFactor = form.demandFactor.trim(); - - if (quantity) { - const value = Number(quantity); - if (!Number.isNaN(value) && value >= 0) { - patch.quantity = value; - } - } - if (installedPowerPerUnitKw) { - const value = Number(installedPowerPerUnitKw); - if (!Number.isNaN(value) && value >= 0) { - patch.installedPowerPerUnitKw = value; - } - } - if (demandFactor) { - const value = Number(demandFactor); - if (!Number.isNaN(value) && value >= 0 && value <= 1) { - patch.demandFactor = value; - } - } - - if (!Object.keys(patch).length) { - return; - } - - const selected = consumers.filter((consumer) => slot.selectedConsumerIds.includes(consumer.id)); - if (!selected.length) { - return; - } - - setIsSaving(true); - setError(null); - try { - await Promise.all(selected.map((consumer) => handleInlineUpdateFields(consumer, patch))); - await reloadConsumers(); - setBulkEditForms((current) => ({ ...current, [slotIndex]: { ...defaultBulkEditState } })); - } catch (err) { - setError(err instanceof Error ? err.message : "Sammeländerung fehlgeschlagen."); - } finally { - setIsSaving(false); - } - } - - async function handleInlineUpdateFields( - consumer: ConsumerWithCalculatedValues, - patch: Partial - ) { - const payload = { - projectId, - distributionBoardId: - patch.distributionBoardId !== undefined - ? patch.distributionBoardId - : consumer.distributionBoardId ?? undefined, - circuitListId: patch.circuitListId !== undefined ? patch.circuitListId : consumer.circuitListId ?? undefined, - projectDeviceId: - patch.projectDeviceId !== undefined ? patch.projectDeviceId : consumer.projectDeviceId ?? undefined, - isLinkedToDevice: - patch.isLinkedToDevice !== undefined ? patch.isLinkedToDevice : consumer.isLinkedToDevice ?? false, - circuitNumber: patch.circuitNumber !== undefined ? patch.circuitNumber : consumer.circuitNumber, - description: patch.description !== undefined ? patch.description : consumer.description, - roomId: patch.roomId !== undefined ? patch.roomId : consumer.roomId ?? undefined, - name: patch.name ?? consumer.name, - category: patch.category !== undefined ? patch.category : consumer.category ?? undefined, - deviceType: patch.deviceType !== undefined ? patch.deviceType : consumer.deviceType, - phaseType: patch.phaseType !== undefined ? patch.phaseType : consumer.phaseType, - tradeOrCostGroup: - patch.tradeOrCostGroup !== undefined ? patch.tradeOrCostGroup : consumer.tradeOrCostGroup, - group: patch.group !== undefined ? patch.group : consumer.group, - protectionType: patch.protectionType !== undefined ? patch.protectionType : consumer.protectionType, - protectionRatedCurrent: - patch.protectionRatedCurrent !== undefined - ? patch.protectionRatedCurrent - : consumer.protectionRatedCurrent, - protectionCharacteristic: - patch.protectionCharacteristic !== undefined - ? patch.protectionCharacteristic - : consumer.protectionCharacteristic, - cableType: patch.cableType !== undefined ? patch.cableType : consumer.cableType, - cableCrossSection: - patch.cableCrossSection !== undefined ? patch.cableCrossSection : consumer.cableCrossSection, - comment: patch.comment !== undefined ? patch.comment : consumer.comment, - quantity: patch.quantity ?? consumer.quantity, - installedPowerPerUnitKw: patch.installedPowerPerUnitKw ?? consumer.installedPowerPerUnitKw, - demandFactor: patch.demandFactor ?? consumer.demandFactor, - voltageV: patch.voltageV !== undefined ? patch.voltageV : consumer.voltageV, - phaseCount: patch.phaseCount !== undefined ? patch.phaseCount : consumer.phaseCount, - powerFactor: patch.powerFactor !== undefined ? patch.powerFactor : consumer.powerFactor, - note: patch.note !== undefined ? patch.note : consumer.note, - }; - try { - const updated = await updateConsumer(consumer.id, payload); - setConsumers((current) => current.map((item) => (item.id === consumer.id ? updated : item))); - } catch (err) { - setError(err instanceof Error ? err.message : "Aktualisierung fehlgeschlagen."); - } - } - - async function handleDeleteConsumer(consumerId: string) { - setIsSaving(true); - setError(null); - try { - await deleteConsumer(consumerId); - await reloadConsumers(); - } catch (err) { - setError(err instanceof Error ? err.message : "Verbraucher konnte nicht gelöscht werden."); - } finally { - setIsSaving(false); - } - } - - function toggleColumn(column: ColumnKey) { - if (column === "selection" || column === "actions") { - return; - } - setVisibleColumns((current) => - current.includes(column) ? current.filter((item) => item !== column) : [...current, column] - ); - } - - function moveColumn(column: ColumnKey, direction: -1 | 1) { - setVisibleColumns((current) => { - const index = current.indexOf(column); - if (index < 0) { - return current; - } - const nextIndex = index + direction; - if (nextIndex < 0 || nextIndex >= current.length) { - return current; - } - const clone = [...current]; - const [item] = clone.splice(index, 1); - clone.splice(nextIndex, 0, item); - return clone; - }); - } - - const renderedColumns = allColumns.filter((column) => visibleColumns.includes(column.key)); - - return ( -
-
-
-

Stromkreislisten

-

Projekt: {project?.name ?? projectId}

-
-
- - - - - Zurück zu Verteilern - -
-
- -

Aktiv geöffnet: {activeListCount} von maximal 3 Listen.

- - {error ?
{error}
: null} - - {showColumnManager ? ( -
-
Spaltenauswahl und Reihenfolge
-
-
- {allColumns - .filter((column) => column.key !== "selection" && column.key !== "actions") - .map((column) => ( -
-
- -
- - -
-
-
- ))} -
- - Standardmäßig sind cos f, Phasen und Strom ausgeblendet. Du kannst sie hier jederzeit einblenden. - -
-
- ) : null} - -
- {slots.slice(0, activeListCount).map((slot, slotIndex) => { - const currentFilter = listFilters[slotIndex]; - const bulkEditForm = bulkEditForms[slotIndex]; - const baseConsumersForSlot = slot.boardId ? consumersForBoard(slot.boardId) : []; - const query = currentFilter.query.trim().toLocaleLowerCase("de-DE"); - const filteredConsumersForSlot = query - ? baseConsumersForSlot.filter((consumer) => { - const searchable = [ - consumer.circuitNumber ?? "", - consumer.description ?? "", - consumer.name ?? "", - consumer.category ?? "", - consumer.note ?? "", - consumer.roomNumber ?? "", - consumer.roomName ?? "", - consumer.floorName ?? "", - ] - .join(" ") - .toLocaleLowerCase("de-DE"); - return searchable.includes(query); - }) - : baseConsumersForSlot; - const listConsumersForSlot = [...filteredConsumersForSlot].sort((a, b) => { - const aValue = getComparableSortValue(a, currentFilter.sortField); - const bValue = getComparableSortValue(b, currentFilter.sortField); - let result = 0; - if (typeof aValue === "number" && typeof bValue === "number") { - result = aValue - bValue; - } else { - result = String(aValue).localeCompare(String(bValue), "de-DE", { sensitivity: "base" }); - } - return currentFilter.sortDirection === "asc" ? result : result * -1; - }); - const quickForm = quickCreateForms[slotIndex]; - - return ( -
-
-
- Liste {slotIndex + 1} - {listConsumersForSlot.length} -
-
- - - -
handleCreateManualConsumer(slotIndex, event)}> -
- updateQuickCreateForm(slotIndex, { name: event.target.value })} - /> -
-
- -
-
- updateQuickCreateForm(slotIndex, { quantity: event.target.value })} - placeholder="Anzahl" - /> -
-
- - updateQuickCreateForm(slotIndex, { installedPowerPerUnitKw: event.target.value }) - } - placeholder="Einzelleistung" - /> -
-
- updateQuickCreateForm(slotIndex, { demandFactor: event.target.value })} - placeholder="Gleichzeitigkeitsfaktor" - /> -
-
- updateTotalPower(slotIndex, event.target.value)} - placeholder="Gesamtleistung" - /> -
-
- -
-
- -
- - updateQuickCreateForm(slotIndex, { addCount: event.target.value })} - title="Anzahl Einträge" - style={{ maxWidth: "8rem" }} - /> - -
- -
-
-
-
- updateListFilter(slotIndex, { query: event.target.value })} - /> -
-
- -
-
- -
-
- updateBulkEditForm(slotIndex, { quantity: event.target.value })} - /> -
-
- - updateBulkEditForm(slotIndex, { installedPowerPerUnitKw: event.target.value }) - } - /> -
-
- updateBulkEditForm(slotIndex, { demandFactor: event.target.value })} - /> -
-
-
- - Gefiltert: {listConsumersForSlot.length} von {baseConsumersForSlot.length} - - -
-
- - - - {renderedColumns.map((column) => ( - - ))} - - - - {listConsumersForSlot.map((consumer) => ( - - {renderedColumns.map((column) => { - switch (column.key) { - case "selection": - return ( - - ); - case "circuitNumber": - return ( - - ); - case "description": - return ( - - ); - case "name": - return ( - - ); - case "projectDevice": - return ( - - ); - case "deviceLink": - return ( - - ); - case "room": - return ( - - ); - case "floor": - return ; - case "category": - return ( - - ); - case "deviceType": - return ( - - ); - case "phaseType": - return ( - - ); - case "tradeOrCostGroup": - return ( - - ); - case "group": - return ( - - ); - case "protectionType": - return ( - - ); - case "protectionRatedCurrent": - return ( - - ); - case "protectionCharacteristic": - return ( - - ); - case "cableType": - return ( - - ); - case "cableCrossSection": - return ( - - ); - case "comment": - return ( - - ); - case "quantity": - return ( - - ); - case "installedPowerPerUnitKw": - return ( - - ); - case "installedPowerKw": - return ; - case "demandFactor": - return ( - - ); - case "demandPowerKw": - return ; - case "voltageV": - return ; - case "phaseCount": - return ( - - ); - case "powerFactor": - return ( - - ); - case "currentA": - return ; - case "note": - return ( - - ); - case "actions": - return ( - - ); - default: - return null; - } - })} - - ))} - {!listConsumersForSlot.length ? ( - - - - ) : null} - -
{column.label}
- toggleConsumerSelection(slotIndex, consumer.id)} - /> - - - event.target.value !== (consumer.circuitNumber ?? "") - ? handleInlineUpdateFields(consumer, { - circuitNumber: event.target.value || undefined, - }) - : undefined - } - /> - - - event.target.value !== (consumer.description ?? consumer.name) - ? handleInlineUpdateFields(consumer, { - description: event.target.value || undefined, - }) - : undefined - } - /> - - - event.target.value !== consumer.name - ? handleInlineUpdateFields(consumer, { name: event.target.value }) - : undefined - } - /> - - - - - - - {consumer.floorName ?? "-"} - - event.target.value !== (consumer.category ?? "") - ? handleInlineUpdateFields(consumer, { - category: event.target.value || undefined, - }) - : undefined - } - /> - - - - - - - - - - - - { - const raw = event.target.value.trim(); - const value = raw ? Number(raw) : undefined; - if (raw === "" && consumer.protectionRatedCurrent !== undefined) { - void handleInlineUpdateFields(consumer, { - protectionRatedCurrent: undefined, - }); - } else if ( - value !== undefined && - !Number.isNaN(value) && - value !== consumer.protectionRatedCurrent - ) { - void handleInlineUpdateFields(consumer, { protectionRatedCurrent: value }); - } - }} - /> - - - - - - - - - event.target.value !== (consumer.comment ?? "") - ? handleInlineUpdateFields(consumer, { - comment: event.target.value || undefined, - }) - : undefined - } - /> - - { - const value = Number(event.target.value); - if (!Number.isNaN(value) && value !== consumer.quantity) { - void handleInlineUpdateFields(consumer, { quantity: value }); - } - }} - /> - - { - const value = Number(event.target.value); - if (!Number.isNaN(value) && value !== consumer.installedPowerPerUnitKw) { - void handleInlineUpdateFields(consumer, { installedPowerPerUnitKw: value }); - } - }} - /> - {formatNumber(consumer.installedPowerKw)} - { - const value = Number(event.target.value); - if (!Number.isNaN(value) && value !== consumer.demandFactor) { - void handleInlineUpdateFields(consumer, { demandFactor: value }); - } - }} - /> - {formatNumber(consumer.demandPowerKw)}{formatNumber(consumer.effectiveVoltageV, 0)} - - - { - const raw = event.target.value.trim(); - const value = raw ? Number(raw) : undefined; - if (raw === "" && consumer.powerFactor !== undefined) { - void handleInlineUpdateFields(consumer, { powerFactor: undefined }); - } else if (!Number.isNaN(value) && value !== consumer.powerFactor) { - void handleInlineUpdateFields(consumer, { powerFactor: value }); - } - }} - /> - {consumer.currentA?.toFixed(2) ?? "-"} - - event.target.value !== (consumer.note ?? "") - ? handleInlineUpdateFields(consumer, { note: event.target.value || undefined }) - : undefined - } - /> - -
- {slots.slice(0, activeListCount).map((_, targetSlotIndex) => ( - - ))} - - -
-
- Keine Einträge für {slot.boardId ? boardById.get(slot.boardId)?.name : "diese Liste"}. -
-
- -
- Ausgewählt: {slot.selectedConsumerIds.length} -
- {slots.slice(0, activeListCount).map((_, targetSlotIndex) => ( - - ))} -
-
-
-
-
- ); - })} -
-
- ); -} - - diff --git a/src/app/projects/[projectId]/page.tsx b/src/app/projects/[projectId]/page.tsx index 15af332..9f35996 100644 --- a/src/app/projects/[projectId]/page.tsx +++ b/src/app/projects/[projectId]/page.tsx @@ -607,12 +607,6 @@ export default function ProjectDetailPage() { Keine Stromkreisliste vorhanden )} - - Bisherige Ansicht - @@ -1124,11 +1118,6 @@ export default function ProjectDetailPage() { ) : null} -
- - 3 parallele Stromkreislisten öffnen - -
); } diff --git a/src/frontend/components/README.md b/src/frontend/components/README.md deleted file mode 100644 index 318bc76..0000000 --- a/src/frontend/components/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# Components Placeholder - -Reusable UI components for project, distribution board, and consumer tables. - diff --git a/src/frontend/components/power-balance-workspace.tsx b/src/frontend/components/power-balance-workspace.tsx deleted file mode 100644 index 3a246d1..0000000 --- a/src/frontend/components/power-balance-workspace.tsx +++ /dev/null @@ -1,666 +0,0 @@ -"use client"; - -import { Activity, FolderPlus, Pencil, Plus, RefreshCw, Save, Trash2, X, Zap } from "lucide-react"; -import { FormEvent, useEffect, useMemo, useState } from "react"; -import { - createConsumer, - createDistributionBoard, - createProject, - deleteConsumer, - listConsumers, - listDistributionBoards, - listProjects, - updateConsumer, -} from "../utils/api"; -import type { - ConsumerWithCalculatedValues, - CreateConsumerInput, - DistributionBoardDto, - ProjectDto, - UpdateConsumerInput, -} from "../types"; - -const initialConsumerForm = { - name: "", - category: "", - quantity: "1", - installedPowerPerUnitKw: "0.1", - demandFactor: "1", - voltageV: "230", - phaseCount: "1", - powerFactor: "1", - note: "", -}; - -interface EditConsumerForm { - name: string; - category: string; - distributionBoardId: string; - quantity: string; - installedPowerPerUnitKw: string; - demandFactor: string; - voltageV: string; - phaseCount: "1" | "3"; - powerFactor: string; - note: string; -} - -type ConsumerForm = typeof initialConsumerForm; - -function toOptionalNumber(value: string) { - if (value.trim() === "") { - return undefined; - } - return Number(value); -} - -function formatNumber(value: number | undefined, digits = 2) { - if (value === undefined || Number.isNaN(value)) { - return "-"; - } - return new Intl.NumberFormat("de-DE", { - maximumFractionDigits: digits, - minimumFractionDigits: digits, - }).format(value); -} - -function createEditForm(consumer: ConsumerWithCalculatedValues): EditConsumerForm { - return { - name: consumer.name, - category: consumer.category ?? "", - distributionBoardId: consumer.distributionBoardId ?? "", - quantity: String(consumer.quantity), - installedPowerPerUnitKw: String(consumer.installedPowerPerUnitKw), - demandFactor: String(consumer.demandFactor), - voltageV: consumer.voltageV !== undefined ? String(consumer.voltageV) : "", - phaseCount: consumer.phaseCount === 3 ? "3" : "1", - powerFactor: consumer.powerFactor !== undefined ? String(consumer.powerFactor) : "", - note: consumer.note ?? "", - }; -} - -export function PowerBalanceWorkspace() { - const [projects, setProjects] = useState([]); - const [selectedProjectId, setSelectedProjectId] = useState(""); - const [distributionBoards, setDistributionBoards] = useState([]); - const [selectedBoardId, setSelectedBoardId] = useState(""); - const [consumers, setConsumers] = useState([]); - const [projectName, setProjectName] = useState(""); - const [boardName, setBoardName] = useState(""); - const [consumerForm, setConsumerForm] = useState(initialConsumerForm); - const [editingConsumerId, setEditingConsumerId] = useState(null); - const [editConsumerForm, setEditConsumerForm] = useState(null); - const [isLoading, setIsLoading] = useState(true); - const [isSaving, setIsSaving] = useState(false); - const [error, setError] = useState(null); - - const selectedProject = projects.find((project) => project.id === selectedProjectId); - const selectedBoard = distributionBoards.find((board) => board.id === selectedBoardId); - const boardNames = new Map(distributionBoards.map((board) => [board.id, board.name])); - const visibleConsumers = selectedBoardId - ? consumers.filter((consumer) => consumer.distributionBoardId === selectedBoardId) - : consumers; - - const totals = useMemo( - () => - visibleConsumers.reduce( - (sum, consumer) => ({ - installedPowerKw: sum.installedPowerKw + consumer.installedPowerKw, - demandPowerKw: sum.demandPowerKw + consumer.demandPowerKw, - }), - { installedPowerKw: 0, demandPowerKw: 0 } - ), - [visibleConsumers] - ); - const totalsByBoard = useMemo(() => { - const bucket = new Map(); - for (const board of distributionBoards) { - bucket.set(board.id, { consumerCount: 0, installedPowerKw: 0, demandPowerKw: 0 }); - } - bucket.set("__unassigned__", { consumerCount: 0, installedPowerKw: 0, demandPowerKw: 0 }); - - for (const consumer of consumers) { - const key = consumer.distributionBoardId ?? "__unassigned__"; - const entry = bucket.get(key); - if (!entry) { - continue; - } - entry.consumerCount += 1; - entry.installedPowerKw += consumer.installedPowerKw; - entry.demandPowerKw += consumer.demandPowerKw; - } - - return [ - ...distributionBoards.map((board) => ({ - key: board.id, - boardName: board.name, - ...bucket.get(board.id)!, - })), - { - key: "__unassigned__", - boardName: "Ohne Verteilung", - ...bucket.get("__unassigned__")!, - }, - ].filter((item) => item.consumerCount > 0); - }, [consumers, distributionBoards]); - const projectTotals = useMemo( - () => - consumers.reduce( - (sum, consumer) => ({ - consumerCount: sum.consumerCount + 1, - installedPowerKw: sum.installedPowerKw + consumer.installedPowerKw, - demandPowerKw: sum.demandPowerKw + consumer.demandPowerKw, - }), - { consumerCount: 0, installedPowerKw: 0, demandPowerKw: 0 } - ), - [consumers] - ); - - async function refreshProjects() { - setError(null); - const loadedProjects = await listProjects(); - setProjects(loadedProjects); - setSelectedProjectId((current) => current || loadedProjects[0]?.id || ""); - } - - async function refreshConsumers(projectId: string) { - if (!projectId) { - setConsumers([]); - return; - } - setError(null); - setConsumers(await listConsumers(projectId)); - } - - async function refreshDistributionBoards(projectId: string) { - if (!projectId) { - setDistributionBoards([]); - setSelectedBoardId(""); - return; - } - - setError(null); - const boards = await listDistributionBoards(projectId); - setDistributionBoards(boards); - setSelectedBoardId((current) => (boards.some((board) => board.id === current) ? current : boards[0]?.id || "")); - } - - useEffect(() => { - refreshProjects() - .catch((err: unknown) => setError(err instanceof Error ? err.message : "Projekte konnten nicht geladen werden.")) - .finally(() => setIsLoading(false)); - }, []); - - useEffect(() => { - setEditingConsumerId(null); - setEditConsumerForm(null); - refreshDistributionBoards(selectedProjectId).catch((err: unknown) => - setError(err instanceof Error ? err.message : "Verteilungen konnten nicht geladen werden.") - ); - refreshConsumers(selectedProjectId).catch((err: unknown) => - setError(err instanceof Error ? err.message : "Verbraucher konnten nicht geladen werden.") - ); - }, [selectedProjectId]); - - async function handleCreateProject(event: FormEvent) { - event.preventDefault(); - const name = projectName.trim(); - if (!name) { - return; - } - - setIsSaving(true); - setError(null); - try { - const project = await createProject(name); - setProjects((current) => [...current, project]); - setSelectedProjectId(project.id); - setProjectName(""); - } catch (err) { - setError(err instanceof Error ? err.message : "Projekt konnte nicht angelegt werden."); - } finally { - setIsSaving(false); - } - } - - async function handleCreateDistributionBoard(event: FormEvent) { - event.preventDefault(); - const name = boardName.trim(); - if (!selectedProjectId || !name) { - return; - } - - setIsSaving(true); - setError(null); - try { - const board = await createDistributionBoard(selectedProjectId, name); - setDistributionBoards((current) => [...current, board]); - setSelectedBoardId(board.id); - setBoardName(""); - } catch (err) { - setError(err instanceof Error ? err.message : "Verteilung konnte nicht angelegt werden."); - } finally { - setIsSaving(false); - } - } - - async function handleCreateConsumer(event: FormEvent) { - event.preventDefault(); - if (!selectedProjectId || !consumerForm.name.trim()) { - return; - } - - const input: CreateConsumerInput = { - projectId: selectedProjectId, - distributionBoardId: selectedBoardId || undefined, - name: consumerForm.name.trim(), - category: consumerForm.category.trim() || undefined, - quantity: Number(consumerForm.quantity), - installedPowerPerUnitKw: Number(consumerForm.installedPowerPerUnitKw), - demandFactor: Number(consumerForm.demandFactor), - voltageV: toOptionalNumber(consumerForm.voltageV), - phaseCount: consumerForm.phaseCount === "3" ? 3 : 1, - powerFactor: toOptionalNumber(consumerForm.powerFactor), - note: consumerForm.note.trim() || undefined, - }; - - setIsSaving(true); - setError(null); - try { - const created = await createConsumer(input); - setConsumers((current) => [...current, created]); - setConsumerForm(initialConsumerForm); - } catch (err) { - setError(err instanceof Error ? err.message : "Verbraucher konnte nicht angelegt werden."); - } finally { - setIsSaving(false); - } - } - - async function handleSaveConsumer(consumerId: string) { - if (!selectedProjectId || !editConsumerForm) { - return; - } - - const input: UpdateConsumerInput = { - projectId: selectedProjectId, - distributionBoardId: editConsumerForm.distributionBoardId || undefined, - name: editConsumerForm.name.trim(), - category: editConsumerForm.category.trim() || undefined, - quantity: Number(editConsumerForm.quantity), - installedPowerPerUnitKw: Number(editConsumerForm.installedPowerPerUnitKw), - demandFactor: Number(editConsumerForm.demandFactor), - voltageV: toOptionalNumber(editConsumerForm.voltageV), - phaseCount: editConsumerForm.phaseCount === "3" ? 3 : 1, - powerFactor: toOptionalNumber(editConsumerForm.powerFactor), - note: editConsumerForm.note.trim() || undefined, - }; - - setIsSaving(true); - setError(null); - try { - const updated = await updateConsumer(consumerId, input); - setConsumers((current) => current.map((item) => (item.id === consumerId ? updated : item))); - setEditingConsumerId(null); - setEditConsumerForm(null); - } catch (err) { - setError(err instanceof Error ? err.message : "Verbraucher konnte nicht gespeichert werden."); - } finally { - setIsSaving(false); - } - } - - async function handleDeleteConsumer(consumerId: string) { - setIsSaving(true); - setError(null); - try { - await deleteConsumer(consumerId); - setConsumers((current) => current.filter((item) => item.id !== consumerId)); - if (editingConsumerId === consumerId) { - setEditingConsumerId(null); - setEditConsumerForm(null); - } - } catch (err) { - setError(err instanceof Error ? err.message : "Verbraucher konnte nicht gelöscht werden."); - } finally { - setIsSaving(false); - } - } - - function startEditConsumer(consumer: ConsumerWithCalculatedValues) { - setEditingConsumerId(consumer.id); - setEditConsumerForm(createEditForm(consumer)); - } - - function cancelEditConsumer() { - setEditingConsumerId(null); - setEditConsumerForm(null); - } - - function updateConsumerForm(field: keyof ConsumerForm, value: string) { - setConsumerForm((current) => ({ ...current, [field]: value })); - } - - function updateEditConsumerForm(field: keyof EditConsumerForm, value: string) { - setEditConsumerForm((current) => (current ? { ...current, [field]: value } : current)); - } - - return ( -
-
-
-

TGA / ELT Planung

-

Leistungsbilanz

-
- -
- - {error ?

{error}

: null} - -
-
- - - -
- -
- - - -
- -
-
- Verbraucher - {visibleConsumers.length} -
-
- Installierte Leistung - {formatNumber(totals.installedPowerKw)} kW -
-
- Berechnete Leistung - {formatNumber(totals.demandPowerKw)} kW -
-
-
- -
-
- - - - - - - - - - -
-
- -
-
-
-

Aktuelles Projekt

-

{selectedProject?.name || "Noch kein Projekt ausgewählt"}

-

{selectedBoard ? `Verteilung: ${selectedBoard.name}` : "Alle Verteilungen"}

-
-
- - {isLoading ? "Lädt" : "Bereit"} -
-
- -
-

Summen nach Verteilung

-
- - - - - - - - - - - {totalsByBoard.map((item) => ( - - - - - - - ))} - {!totalsByBoard.length ? ( - - - - ) : null} - - - - - - - -
VerteilungVerbraucherInstallierte Leistung [kW]Berechnete Leistung [kW]
{item.boardName}{item.consumerCount}{formatNumber(item.installedPowerKw)}{formatNumber(item.demandPowerKw)}
- Noch keine Verbraucher für eine Summenbildung vorhanden. -
Projekt gesamt{projectTotals.consumerCount}{formatNumber(projectTotals.installedPowerKw)}{formatNumber(projectTotals.demandPowerKw)}
-
-
- -
- - - - - - - - - - - - - - - - - - {visibleConsumers.map((consumer) => { - const isEditing = editingConsumerId === consumer.id && editConsumerForm; - return ( - - - - - - - - - - - - - - ); - })} - {!visibleConsumers.length ? ( - - - - ) : null} - -
VerbraucherVerteilungKategorieAnzahlLeistung je Stück [kW]Installierte Leistung [kW]GZFBerechnete Leistung [kW]Strom [A]BemerkungAktionen
- - {isEditing ? ( - updateEditConsumerForm("name", event.target.value)} /> - ) : ( - consumer.name - )} - - {isEditing ? ( - - ) : consumer.distributionBoardId ? ( - boardNames.get(consumer.distributionBoardId) || "-" - ) : ( - "-" - )} - - {isEditing ? ( - updateEditConsumerForm("category", event.target.value)} /> - ) : ( - consumer.category || "-" - )} - - {isEditing ? ( - updateEditConsumerForm("quantity", event.target.value)} /> - ) : ( - consumer.quantity - )} - - {isEditing ? ( - updateEditConsumerForm("installedPowerPerUnitKw", event.target.value)} - /> - ) : ( - formatNumber(consumer.installedPowerPerUnitKw) - )} - {formatNumber(consumer.installedPowerKw)} - {isEditing ? ( - updateEditConsumerForm("demandFactor", event.target.value)} /> - ) : ( - formatNumber(consumer.demandFactor) - )} - {formatNumber(consumer.demandPowerKw)} - {isEditing ? ( -
- updateEditConsumerForm("voltageV", event.target.value)} /> - - updateEditConsumerForm("powerFactor", event.target.value)} /> -
- ) : ( - formatNumber(consumer.currentA) - )} -
- {isEditing ? ( - updateEditConsumerForm("note", event.target.value)} /> - ) : ( - consumer.note || "-" - )} - -
- {isEditing ? ( - <> - - - - ) : ( - - )} - -
-
- Lege eine Verteilung an oder erfasse den ersten Verbraucher. -
-
-
-
- ); -} diff --git a/src/frontend/types.ts b/src/frontend/types.ts index 4829e17..2395e99 100644 --- a/src/frontend/types.ts +++ b/src/frontend/types.ts @@ -5,45 +5,6 @@ export interface ProjectDto { threePhaseVoltageV: number; } -export interface ConsumerWithCalculatedValues { - id: string; - projectId: string; - distributionBoardId?: string | null; - circuitListId?: string | null; - projectDeviceId?: string | null; - isLinkedToDevice?: boolean; - roomId?: string | null; - roomNumber?: string; - roomName?: string; - floorId?: string; - floorName?: string; - circuitNumber?: string; - description?: string; - name: string; - category?: string; - deviceType?: string; - phaseType?: string; - tradeOrCostGroup?: string; - group?: string; - protectionType?: string; - protectionRatedCurrent?: number; - protectionCharacteristic?: string; - cableType?: string; - cableCrossSection?: string; - comment?: string; - quantity: number; - installedPowerPerUnitKw: number; - demandFactor: number; - voltageV?: number; - phaseCount?: 1 | 3; - powerFactor?: number; - note?: string; - installedPowerKw: number; - demandPowerKw: number; - effectiveVoltageV?: number; - currentA?: number; -} - export interface DistributionBoardDto { id: string; projectId: string; @@ -101,47 +62,9 @@ export interface ProjectDeviceDto { totalPower: number; cosPhi: number | null; remark: string | null; - // Legacy aliases retained until the old Consumer editor is removed. - installedPowerPerUnitKw: number; - demandFactor: number; voltageV: number | null; - phaseCount: 1 | 3 | null; - powerFactor: number | null; - note: string | null; } -export interface CreateConsumerInput { - projectId: string; - distributionBoardId?: string; - circuitListId?: string; - projectDeviceId?: string; - isLinkedToDevice?: boolean; - roomId?: string; - circuitNumber?: string; - description?: string; - name: string; - category?: string; - deviceType?: string; - phaseType?: string; - tradeOrCostGroup?: string; - group?: string; - protectionType?: string; - protectionRatedCurrent?: number; - protectionCharacteristic?: string; - cableType?: string; - cableCrossSection?: string; - comment?: string; - quantity: number; - installedPowerPerUnitKw: number; - demandFactor: number; - voltageV?: number; - phaseCount?: 1 | 3; - powerFactor?: number; - note?: string; -} - -export interface UpdateConsumerInput extends CreateConsumerInput {} - export interface CreateFloorInput { name: string; } diff --git a/src/frontend/utils/api.ts b/src/frontend/utils/api.ts index fca4a8d..185a462 100644 --- a/src/frontend/utils/api.ts +++ b/src/frontend/utils/api.ts @@ -3,8 +3,6 @@ import type { CreateFloorInput, CreateProjectDeviceInput, CreateRoomInput, - ConsumerWithCalculatedValues, - CreateConsumerInput, CreateGlobalDeviceInput, DistributionBoardDto, FloorDto, @@ -16,7 +14,6 @@ import type { ProjectDeviceSyncResultDto, ProjectDto, RoomDto, - UpdateConsumerInput, CircuitTreeResponseDto, CircuitTreeCircuitDto, CircuitTreeDeviceRowDto, @@ -229,28 +226,6 @@ export function createRoom(projectId: string, input: CreateRoomInput) { }); } -export function listConsumers(projectId: string) { - return request(`/api/consumers/projects/${projectId}`); -} - -export function createConsumer(input: CreateConsumerInput) { - return request("/api/consumers", { - method: "POST", - body: JSON.stringify(input), - }); -} - -export function updateConsumer(consumerId: string, input: UpdateConsumerInput) { - return request(`/api/consumers/${consumerId}`, { - method: "PUT", - body: JSON.stringify(input), - }); -} - -export function deleteConsumer(consumerId: string) { - return request(`/api/consumers/${consumerId}`, { method: "DELETE" }); -} - export function listGlobalDevices() { return request("/api/global-devices"); } diff --git a/src/shared/types/power.types.ts b/src/shared/types/power.types.ts deleted file mode 100644 index 9b3ce4b..0000000 --- a/src/shared/types/power.types.ts +++ /dev/null @@ -1,39 +0,0 @@ -export interface ProjectDto { - id: string; - name: string; -} - -export interface DistributionBoardDto { - id: string; - projectId: string; - name: string; -} - -export interface ConsumerDto { - id: string; - projectId: string; - distributionBoardId: string | null; - circuitListId: string | null; - roomId: string | null; - circuitNumber: string | null; - description: string | null; - name: string; - category: string | null; - deviceType: string | null; - phaseType: string | null; - tradeOrCostGroup: string | null; - group: string | null; - protectionType: string | null; - protectionRatedCurrent: number | null; - protectionCharacteristic: string | null; - cableType: string | null; - cableCrossSection: string | null; - comment: string | null; - quantity: number; - installedPowerPerUnitKw: number; - demandFactor: number; - voltageV: number | null; - phaseCount: 1 | 3 | null; - powerFactor: number | null; - note: string | null; -}