"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 geloescht 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 ausgewaehlt"}

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

{isLoading ? "Laedt" : "Bereit"}

Summen nach Verteilung

{totalsByBoard.map((item) => ( ))} {!totalsByBoard.length ? ( ) : null}
Verteilung Verbraucher Installierte Leistung [kW] Berechnete Leistung [kW]
{item.boardName} {item.consumerCount} {formatNumber(item.installedPowerKw)} {formatNumber(item.demandPowerKw)}
Noch keine Verbraucher fuer 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}
Verbraucher Verteilung Kategorie Anzahl Leistung je Stueck [kW] Installierte Leistung [kW] GZF Berechnete Leistung [kW] Strom [A] Bemerkung Aktionen
{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.
); }