640 lines
21 KiB
TypeScript
640 lines
21 KiB
TypeScript
"use client";
|
||
|
||
import type { ReactNode } from "react";
|
||
import type {
|
||
CircuitTreeCircuitDto,
|
||
CircuitTreeComponentDto,
|
||
CircuitTreeDeviceRowDto,
|
||
OfficeProfileDto,
|
||
ProjectDto,
|
||
ProjectSnapshotMetadataDto,
|
||
} from "../types";
|
||
import {
|
||
formatCircuitCable,
|
||
formatCurrent,
|
||
formatFactor,
|
||
formatFeedingReference,
|
||
formatKilowatt,
|
||
formatProtectionDevice,
|
||
formatUnitPower,
|
||
type PrintBoardHierarchyEntry,
|
||
} from "./print-document-model";
|
||
|
||
export interface PrintDocumentProps {
|
||
project: ProjectDto;
|
||
office: OfficeProfileDto;
|
||
entries: PrintBoardHierarchyEntry[];
|
||
namedSnapshots: ProjectSnapshotMetadataDto[];
|
||
logoSrc: string | null;
|
||
printedAt: Date;
|
||
}
|
||
|
||
/** Shows a value or the orange "[ergänzen]" marker for missing data. */
|
||
function Value({ value, multiline = false }: { value: string | null; multiline?: boolean }) {
|
||
if (!value) {
|
||
return <Placeholder />;
|
||
}
|
||
return multiline ? <span className="pd-multiline">{value}</span> : <>{value}</>;
|
||
}
|
||
|
||
const dateFormat = new Intl.DateTimeFormat("de-DE", {
|
||
day: "2-digit",
|
||
month: "2-digit",
|
||
year: "numeric",
|
||
});
|
||
|
||
function Placeholder({ label = "ergänzen" }: { label?: string }) {
|
||
return <span className="pd-placeholder">[{label}]</span>;
|
||
}
|
||
|
||
function Open() {
|
||
return <span className="pd-open">offen</span>;
|
||
}
|
||
|
||
function Logo({ src, className }: { src: string | null; className: string }) {
|
||
return src ? (
|
||
// eslint-disable-next-line @next/next/no-img-element
|
||
<img alt="Logo" className={`pd-logo ${className}`} src={src} />
|
||
) : (
|
||
<span className={`pd-logo-missing ${className}`}>Logo</span>
|
||
);
|
||
}
|
||
|
||
function SheetFooter({ project, stand }: { project: ProjectDto; stand: string }) {
|
||
return (
|
||
<div className="pd-sheet-footer" aria-hidden="true">
|
||
<span>{project.name} · Leistungsbilanz</span>
|
||
<span>
|
||
Stand {stand} · Datenstand Rev. {project.currentRevision}
|
||
</span>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
function TitleBlock({
|
||
kicker,
|
||
number,
|
||
name,
|
||
rows,
|
||
project,
|
||
stand,
|
||
logoSrc,
|
||
}: {
|
||
kicker: string;
|
||
number?: string | null;
|
||
name: string;
|
||
rows: Array<[ReactNode, ReactNode]>;
|
||
project: ProjectDto;
|
||
stand: string;
|
||
logoSrc: string | null;
|
||
}) {
|
||
return (
|
||
<div className="pd-titleblock">
|
||
<div className="pd-tb-logo">
|
||
<Logo className="pd-tb-logo-image" src={logoSrc} />
|
||
</div>
|
||
<div className="pd-tb-main">
|
||
<div className="pd-tb-kicker">{kicker}</div>
|
||
<div className="pd-tb-title">
|
||
{number ? <span className="pd-tb-number">{number}</span> : null}
|
||
<span className="pd-tb-name">{name}</span>
|
||
</div>
|
||
</div>
|
||
<dl className="pd-tb-grid">
|
||
{rows.map(([label, value], index) => (
|
||
<div key={index}>
|
||
<dt>{label}</dt>
|
||
<dd>{value}</dd>
|
||
</div>
|
||
))}
|
||
</dl>
|
||
<dl className="pd-tb-grid pd-tb-meta">
|
||
<div>
|
||
<dt>Projekt</dt>
|
||
<dd>{project.name}</dd>
|
||
</div>
|
||
<div>
|
||
<dt>Projekt-Nr.</dt>
|
||
<dd>{project.internalProjectNumber ?? "–"}</dd>
|
||
</div>
|
||
<div>
|
||
<dt>Stand</dt>
|
||
<dd>{stand}</dd>
|
||
</div>
|
||
<div>
|
||
<dt>Datenstand</dt>
|
||
<dd>Rev. {project.currentRevision}</dd>
|
||
</div>
|
||
</dl>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
function boardLabel(entry: PrintBoardHierarchyEntry) {
|
||
const { board } = entry.summary;
|
||
return board.boardNumber ? `${board.boardNumber} – ${board.name}` : board.name;
|
||
}
|
||
|
||
function CoverSheet({
|
||
project,
|
||
office,
|
||
entries,
|
||
namedSnapshots,
|
||
logoSrc,
|
||
stand,
|
||
}: Omit<PrintDocumentProps, "printedAt"> & { stand: string }) {
|
||
return (
|
||
<section className="pd-sheet pd-portrait pd-cover">
|
||
<div className="pd-cover-head">
|
||
<div className="pd-cover-kicker">Projektdokumentation Elektrotechnik</div>
|
||
<Logo className="pd-cover-logo" src={logoSrc} />
|
||
</div>
|
||
<div className="pd-cover-title">
|
||
<h1>Leistungsbilanz</h1>
|
||
<p>Verteilerübersicht und Verteilerpläne</p>
|
||
</div>
|
||
|
||
<table className="pd-kv">
|
||
<caption>Projekt</caption>
|
||
<tbody>
|
||
<tr><th>Bauvorhaben</th><td>{project.name}</td></tr>
|
||
<tr><th>Anschrift</th><td><Value multiline value={project.siteAddress} /></td></tr>
|
||
<tr><th>Bauherr</th><td><Value value={project.buildingOwner} /></td></tr>
|
||
<tr><th>Projektnummer intern</th><td><Value value={project.internalProjectNumber} /></td></tr>
|
||
<tr><th>Projektnummer extern</th><td><Value value={project.externalProjectNumber} /></td></tr>
|
||
<tr><th>Leistungsphase</th><td><Value value={project.workStage} /></td></tr>
|
||
<tr><th>Gewerk</th><td>KG 440 – Starkstromanlagen</td></tr>
|
||
<tr><th>Beschreibung</th><td>{project.description ?? "–"}</td></tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<div className="pd-cover-cols">
|
||
<table className="pd-kv">
|
||
<caption>Planverfasser</caption>
|
||
<tbody>
|
||
<tr><th>Büro</th><td><Value value={office.officeName} /></td></tr>
|
||
<tr><th>Anschrift</th><td><Value multiline value={office.officeAddress} /></td></tr>
|
||
<tr><th>Projektleitung</th><td><Value value={project.projectLead} /></td></tr>
|
||
<tr><th>Bearbeitung</th><td><Value value={project.projectEditor} /></td></tr>
|
||
</tbody>
|
||
</table>
|
||
<table className="pd-kv">
|
||
<caption>Dokument</caption>
|
||
<tbody>
|
||
<tr><th>Dokumentart</th><td>Leistungsbilanz</td></tr>
|
||
<tr><th>Stand</th><td>{stand}</td></tr>
|
||
<tr><th>Datenstand</th><td>Revision {project.currentRevision}</td></tr>
|
||
<tr><th>Verteiler</th><td>{entries.length}</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<table className="pd-grid pd-block">
|
||
<caption>Planstände</caption>
|
||
<thead>
|
||
<tr><th className="pd-col-index">Index</th><th>Datum</th><th>Änderung</th><th>Bearbeitung</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
{namedSnapshots.length ? (
|
||
namedSnapshots.map((snapshot, index) => (
|
||
<tr key={snapshot.id}>
|
||
<td>{String.fromCharCode(97 + (index % 26))}</td>
|
||
<td>{dateFormat.format(new Date(snapshot.createdAtIso))}</td>
|
||
<td>{snapshot.name}</td>
|
||
{/* Snapshots do not record an editor yet; the project's
|
||
current editor is shown for every entry. */}
|
||
<td><Value value={project.projectEditor} /></td>
|
||
</tr>
|
||
))
|
||
) : (
|
||
<tr><td colSpan={4}>–</td></tr>
|
||
)}
|
||
</tbody>
|
||
</table>
|
||
|
||
<table className="pd-grid pd-block">
|
||
<caption>Inhalt</caption>
|
||
<tbody>
|
||
<tr><td className="pd-col-index">1</td><td>Verteilerübersicht und Leistungssummen</td></tr>
|
||
{entries.map((entry, index) => (
|
||
<tr key={entry.summary.board.id}>
|
||
<td className="pd-col-index">2.{index + 1}</td>
|
||
<td>Verteilerplan {boardLabel(entry)}</td>
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
</table>
|
||
<SheetFooter project={project} stand={stand} />
|
||
</section>
|
||
);
|
||
}
|
||
|
||
function StructureNode({
|
||
entry,
|
||
entries,
|
||
}: {
|
||
entry: PrintBoardHierarchyEntry;
|
||
entries: PrintBoardHierarchyEntry[];
|
||
}) {
|
||
const children = entries.filter(
|
||
(candidate) => candidate.parentBoardId === entry.summary.board.id
|
||
);
|
||
const { board } = entry.summary;
|
||
return (
|
||
<li>
|
||
<div className="pd-node">
|
||
<strong>{board.boardNumber ?? board.name}</strong>
|
||
{board.boardNumber ? <span>{board.name}</span> : null}
|
||
<em>{formatKilowatt(entry.summary.demandPowerKw)} kW</em>
|
||
</div>
|
||
{children.length ? (
|
||
<ul>
|
||
{children.map((child) => (
|
||
<StructureNode entries={entries} entry={child} key={child.summary.board.id} />
|
||
))}
|
||
</ul>
|
||
) : null}
|
||
</li>
|
||
);
|
||
}
|
||
|
||
function OverviewSheet({
|
||
project,
|
||
entries,
|
||
logoSrc,
|
||
stand,
|
||
}: {
|
||
project: ProjectDto;
|
||
entries: PrintBoardHierarchyEntry[];
|
||
logoSrc: string | null;
|
||
stand: string;
|
||
}) {
|
||
const roots = entries.filter((entry) => entry.parentBoardId === null);
|
||
// Root boards may still carry an external feed description such as
|
||
// "HAK / Zähleranlage"; logical board links determine the actual tree.
|
||
const feedGroups = new Map<string, PrintBoardHierarchyEntry[]>();
|
||
for (const root of roots) {
|
||
const feed = root.summary.board.fedFrom ?? "";
|
||
feedGroups.set(feed, [...(feedGroups.get(feed) ?? []), root]);
|
||
}
|
||
const sum =(pick: (entry: PrintBoardHierarchyEntry) => number) =>
|
||
entries.reduce((total, entry) => total + pick(entry), 0);
|
||
return (
|
||
<section className="pd-sheet pd-landscape">
|
||
<TitleBlock
|
||
kicker="Übersicht"
|
||
logoSrc={logoSrc}
|
||
name="Verteilerübersicht und Leistungssummen"
|
||
project={project}
|
||
rows={[
|
||
["Verteiler", String(entries.length)],
|
||
["Netz", `${project.threePhaseVoltageV} / ${project.singlePhaseVoltageV} V`],
|
||
]}
|
||
stand={stand}
|
||
/>
|
||
<div className="pd-overview">
|
||
<div className="pd-structure">
|
||
<h2>Versorgungsstruktur</h2>
|
||
<ul className="pd-tree">
|
||
{[...feedGroups].map(([feed, feedRoots]) => {
|
||
const nodes = feedRoots.map((root) => (
|
||
<StructureNode entries={entries} entry={root} key={root.summary.board.id} />
|
||
));
|
||
return feed ? (
|
||
<li key={`feed:${feed}`}>
|
||
<div className="pd-node pd-feed">
|
||
<strong>Einspeisung</strong>
|
||
<span>{feed}</span>
|
||
</div>
|
||
<ul>{nodes}</ul>
|
||
</li>
|
||
) : (
|
||
nodes
|
||
);
|
||
})}
|
||
</ul>
|
||
</div>
|
||
<div>
|
||
<table className="pd-grid pd-plan">
|
||
<thead>
|
||
<tr>
|
||
<th>Nr.</th><th>Bezeichnung</th><th>Örtlichkeit</th><th>Etage</th><th>Netz</th><th>Gespeist von</th>
|
||
<th className="pd-r">SK</th>
|
||
<th className="pd-r">P<sub>inst</sub><br />[kW]</th>
|
||
<th className="pd-r">P vor Vert.-GZF<br />[kW]</th>
|
||
<th className="pd-r">GZF<br />Vert.</th>
|
||
<th className="pd-r">P<sub>gl</sub> inkl. UV<br />[kW]</th>
|
||
<th className="pd-r">I<sub>B</sub><br />[A]</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{entries.map((entry) => {
|
||
const { board } = entry.summary;
|
||
return (
|
||
<tr key={board.id}>
|
||
<td style={{ paddingLeft: `${1.6 + entry.depth * 3.5}mm` }}>
|
||
{entry.depth ? "└ " : ""}
|
||
<strong>{board.boardNumber ?? "–"}</strong>
|
||
</td>
|
||
<td>{board.name}</td>
|
||
<td>{board.location ?? "–"}</td>
|
||
<td>{entry.summary.floorName ?? "–"}</td>
|
||
<td>{board.supplyType ?? "–"}</td>
|
||
<td>{formatFeedingReference(entry.summary)}</td>
|
||
<td className="pd-r">{entry.summary.circuitCount}</td>
|
||
<td className="pd-r">{formatKilowatt(entry.summary.installedPowerKw)}</td>
|
||
<td className="pd-r">{formatKilowatt(entry.summary.powerBeforeBoardSimultaneityKw)}</td>
|
||
<td className="pd-r">{formatFactor(entry.summary.simultaneityFactor)}</td>
|
||
<td className="pd-r"><strong>{formatKilowatt(entry.summary.demandPowerKw)}</strong></td>
|
||
<td className="pd-r">{formatCurrent(entry.summary.designCurrentA)}</td>
|
||
</tr>
|
||
);
|
||
})}
|
||
</tbody>
|
||
<tfoot>
|
||
<tr>
|
||
<td colSpan={7}>Summe der Einspeisungen</td>
|
||
<td className="pd-r">{formatKilowatt(sum((entry) => entry.summary.installedPowerKw))}</td>
|
||
<td className="pd-r">{formatKilowatt(sum((entry) =>
|
||
entry.parentBoardId === null
|
||
? entry.summary.powerBeforeBoardSimultaneityKw
|
||
: 0
|
||
))}</td>
|
||
<td />
|
||
<td className="pd-r">{formatKilowatt(sum((entry) =>
|
||
entry.parentBoardId === null ? entry.summary.demandPowerKw : 0
|
||
))}</td>
|
||
<td />
|
||
</tr>
|
||
</tfoot>
|
||
</table>
|
||
<p className="pd-note">
|
||
Die Struktur folgt den logischen Verknüpfungen. Eine Unterverteilung
|
||
wird im gewählten Abgang mit ihrer Leistung nach eigenem
|
||
Verteiler-GZF berücksichtigt. Ohne gewählten Abgang erscheint sie
|
||
nur in der Hierarchie.
|
||
</p>
|
||
<p className="pd-legend">
|
||
P<sub>inst</sub> = lokale Geräte ohne GZF · P vor Vert.-GZF =
|
||
Geräte-GZF einschließlich gespeister Unterverteilungen · P<sub>gl</sub> =
|
||
zusätzlich mit Verteiler-GZF · I<sub>B</sub> aus S und der Phasenart
|
||
des speisenden Abgangs
|
||
</p>
|
||
</div>
|
||
</div>
|
||
<SheetFooter project={project} stand={stand} />
|
||
</section>
|
||
);
|
||
}
|
||
|
||
const planColumnCount = 11;
|
||
|
||
function roomLabel(row: CircuitTreeDeviceRowDto) {
|
||
return row.roomNumberSnapshot
|
||
? `${row.roomNumberSnapshot} ${row.roomNameSnapshot ?? ""}`.trim()
|
||
: "–";
|
||
}
|
||
|
||
function ComponentRow({ component }: { component: CircuitTreeComponentDto }) {
|
||
return (
|
||
<tr className="pd-component">
|
||
<td className="pd-bmk">{component.equipmentIdentifier}</td>
|
||
<td colSpan={6}>{component.name}</td>
|
||
<td>{formatProtectionDevice(component.protectionDevice) ?? "–"}</td>
|
||
<td colSpan={3} />
|
||
</tr>
|
||
);
|
||
}
|
||
|
||
function CircuitRows({ circuit }: { circuit: CircuitTreeCircuitDto }) {
|
||
const rows = circuit.deviceRows;
|
||
const downstreamBoards = circuit.downstreamBoardLoads ?? [];
|
||
const circuitCells = (
|
||
<>
|
||
<td>{formatProtectionDevice(circuit.protectionDevice) ?? <Open />}</td>
|
||
<td>{formatCircuitCable(circuit) ?? (circuit.isReserve ? "–" : <Open />)}</td>
|
||
<td className="pd-r">
|
||
{circuit.cableLength ?? (circuit.isReserve ? "–" : <Open />)}
|
||
</td>
|
||
<td>{circuit.remark ?? ""}</td>
|
||
</>
|
||
);
|
||
if (rows.length === 0 && downstreamBoards.length === 0) {
|
||
return (
|
||
<tr className="pd-circuit pd-reserve">
|
||
<td className="pd-bmk">{circuit.equipmentIdentifier}</td>
|
||
<td>{circuit.displayName ?? "Reserve"}</td>
|
||
<td>–</td>
|
||
<td /><td /><td />
|
||
<td className="pd-r">–</td>
|
||
{circuitCells}
|
||
</tr>
|
||
);
|
||
}
|
||
if (rows.length === 1 && downstreamBoards.length === 0) {
|
||
const row = rows[0];
|
||
const title = circuit.displayName ?? row.displayName;
|
||
return (
|
||
<tr className="pd-circuit">
|
||
<td className="pd-bmk">{circuit.equipmentIdentifier}</td>
|
||
<td>
|
||
{title}
|
||
{title !== row.displayName ? (
|
||
<span className="pd-sub"> · {row.displayName}</span>
|
||
) : null}
|
||
</td>
|
||
<td>{roomLabel(row)}</td>
|
||
<td className="pd-r">{row.quantity}</td>
|
||
<td className="pd-r">{formatUnitPower(row.powerPerUnit)}</td>
|
||
<td className="pd-r">{formatFactor(row.simultaneityFactor)}</td>
|
||
<td className="pd-r"><strong>{formatKilowatt(circuit.circuitTotalPower)}</strong></td>
|
||
{circuitCells}
|
||
</tr>
|
||
);
|
||
}
|
||
return (
|
||
<>
|
||
<tr className="pd-circuit">
|
||
<td className="pd-bmk">{circuit.equipmentIdentifier}</td>
|
||
<td>{circuit.displayName ?? ""}</td>
|
||
<td /><td /><td /><td />
|
||
<td className="pd-r"><strong>{formatKilowatt(circuit.circuitTotalPower)}</strong></td>
|
||
{circuitCells}
|
||
</tr>
|
||
{rows.map((row) => (
|
||
<tr className="pd-device" key={row.id}>
|
||
<td />
|
||
<td>{row.displayName}</td>
|
||
<td>{roomLabel(row)}</td>
|
||
<td className="pd-r">{row.quantity}</td>
|
||
<td className="pd-r">{formatUnitPower(row.powerPerUnit)}</td>
|
||
<td className="pd-r">{formatFactor(row.simultaneityFactor)}</td>
|
||
<td className="pd-r">{formatKilowatt(row.rowTotalPower)}</td>
|
||
<td colSpan={4} />
|
||
</tr>
|
||
))}
|
||
{downstreamBoards.map((load) => (
|
||
<tr className="pd-device" key={`downstream:${load.distributionBoardId}`}>
|
||
<td />
|
||
<td>Unterverteilung {load.boardNumber ?? load.name}</td>
|
||
<td>–</td>
|
||
<td /><td /><td />
|
||
<td className="pd-r">{formatKilowatt(load.activePowerKw)}</td>
|
||
<td colSpan={4}>Automatisch aus der verknüpften Verteilung</td>
|
||
</tr>
|
||
))}
|
||
</>
|
||
);
|
||
}
|
||
|
||
function BoardPlanSheet({
|
||
entry,
|
||
project,
|
||
logoSrc,
|
||
stand,
|
||
}: {
|
||
entry: PrintBoardHierarchyEntry;
|
||
project: ProjectDto;
|
||
logoSrc: string | null;
|
||
stand: string;
|
||
}) {
|
||
const { summary } = entry;
|
||
const { board, tree } = summary;
|
||
return (
|
||
<section className="pd-sheet pd-landscape">
|
||
<table className="pd-grid pd-plan">
|
||
{/* The title block lives in <thead> so it repeats on every printed page. */}
|
||
<thead>
|
||
<tr>
|
||
<th className="pd-tb-cell" colSpan={planColumnCount}>
|
||
<TitleBlock
|
||
kicker="Verteilerplan"
|
||
logoSrc={logoSrc}
|
||
name={board.name}
|
||
number={board.boardNumber}
|
||
project={project}
|
||
rows={[
|
||
["Örtlichkeit", [board.location, summary.floorName].filter(Boolean).join(" · ") || "–"],
|
||
["Netzart", board.supplyType ?? "–"],
|
||
["Gespeist von", formatFeedingReference(summary)],
|
||
[
|
||
<>P<sub>gl</sub></>,
|
||
<>
|
||
{formatKilowatt(summary.demandPowerKw)} kW · I<sub>B</sub>{" "}
|
||
{formatCurrent(summary.designCurrentA)} A
|
||
</>,
|
||
],
|
||
]}
|
||
stand={stand}
|
||
/>
|
||
</th>
|
||
</tr>
|
||
<tr className="pd-head">
|
||
<th>SK-Nr.</th><th>Bezeichnung</th><th>Raum</th>
|
||
<th className="pd-r">Anz.</th>
|
||
<th className="pd-r">P/Stk.<br />[kW]</th>
|
||
<th className="pd-r">GZF</th>
|
||
<th className="pd-r">P<br />[kW]</th>
|
||
<th>Schutz</th><th>Kabel</th>
|
||
<th className="pd-r">Länge<br />[m]</th>
|
||
<th>Bemerkung</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{tree.headerComponents.map((component) => (
|
||
<ComponentRow component={component} key={component.id} />
|
||
))}
|
||
{tree.sections.map((section) => (
|
||
<SectionRows key={section.id} section={section} />
|
||
))}
|
||
{tree.footerComponents.map((component) => (
|
||
<ComponentRow component={component} key={component.id} />
|
||
))}
|
||
</tbody>
|
||
<tfoot>
|
||
<tr>
|
||
<td />
|
||
<td colSpan={5}>
|
||
Summe Verteiler (P<sub>inst</sub> {formatKilowatt(summary.installedPowerKw)} kW)
|
||
</td>
|
||
<td className="pd-r">{formatKilowatt(summary.powerBeforeBoardSimultaneityKw)}</td>
|
||
<td colSpan={4} />
|
||
</tr>
|
||
<tr>
|
||
<td />
|
||
<td colSpan={5}>Gleichzeitigkeitsfaktor Verteiler</td>
|
||
<td className="pd-r">{formatFactor(summary.simultaneityFactor)}</td>
|
||
<td colSpan={4} />
|
||
</tr>
|
||
<tr className="pd-grand">
|
||
<td />
|
||
<td colSpan={5}>Gleichzeitige Leistung P<sub>gl</sub></td>
|
||
<td className="pd-r">{formatKilowatt(summary.demandPowerKw)}</td>
|
||
<td colSpan={4}>I<sub>B</sub> ≈ {formatCurrent(summary.designCurrentA)} A</td>
|
||
</tr>
|
||
</tfoot>
|
||
</table>
|
||
<SheetFooter project={project} stand={stand} />
|
||
</section>
|
||
);
|
||
}
|
||
|
||
function SectionRows({
|
||
section,
|
||
}: {
|
||
section: PrintBoardHierarchyEntry["summary"]["tree"]["sections"][number];
|
||
}) {
|
||
return (
|
||
<>
|
||
<tr className="pd-group">
|
||
<td className="pd-bmk">{section.prefix}</td>
|
||
<td colSpan={planColumnCount - 1}>{section.displayName}</td>
|
||
</tr>
|
||
{section.components.map((component) => (
|
||
<ComponentRow component={component} key={component.id} />
|
||
))}
|
||
{section.circuits.map((circuit) => (
|
||
<CircuitRows circuit={circuit} key={circuit.id} />
|
||
))}
|
||
<tr className="pd-subtotal">
|
||
<td />
|
||
<td colSpan={5}>Summe {section.displayName}</td>
|
||
<td className="pd-r">{formatKilowatt(section.sectionTotalPower)}</td>
|
||
<td colSpan={4} />
|
||
</tr>
|
||
</>
|
||
);
|
||
}
|
||
|
||
export function PrintDocument({
|
||
project,
|
||
office,
|
||
entries,
|
||
namedSnapshots,
|
||
logoSrc,
|
||
printedAt,
|
||
}: PrintDocumentProps) {
|
||
const stand = dateFormat.format(printedAt);
|
||
return (
|
||
<div className="pd-pages">
|
||
<CoverSheet
|
||
entries={entries}
|
||
logoSrc={logoSrc}
|
||
namedSnapshots={namedSnapshots}
|
||
office={office}
|
||
project={project}
|
||
stand={stand}
|
||
/>
|
||
<OverviewSheet entries={entries} logoSrc={logoSrc} project={project} stand={stand} />
|
||
{entries.map((entry) => (
|
||
<BoardPlanSheet
|
||
entry={entry}
|
||
key={entry.summary.board.id}
|
||
logoSrc={logoSrc}
|
||
project={project}
|
||
stand={stand}
|
||
/>
|
||
))}
|
||
</div>
|
||
);
|
||
}
|