From e06c7d91e2fff2c764cf19b03a7cb88e73ce90ed Mon Sep 17 00:00:00 2001 From: Grovy311 Date: Sat, 26 Sep 2026 17:48:37 +0200 Subject: [PATCH 1/3] Print the rough power balance in the kbp design The print of the rough power balance was plain grey. It now follows the kbp design of the print view: a title block with office logo, stand, project, number, owner, address, work stage, date, editor and office; header row in kbp blue, cost-group rows tinted with a lilac rule, bold subtotals, the total including reserve as a blue band, colored deviations in the comparison, Arial, exact print colors, and a footer with project, stand and "Seite x von y" on every page. Co-Authored-By: Claude Opus 5.5 --- src/app/globals.css | 210 ++++++++++++++++-- .../grobbilanz/[balanceId]/page.tsx | 2 + .../estimate-print-header.tsx | 104 +++++++++ 3 files changed, 292 insertions(+), 24 deletions(-) create mode 100644 src/frontend/estimate-balance/estimate-print-header.tsx diff --git a/src/app/globals.css b/src/app/globals.css index b3f526a..4a0adc8 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -1965,14 +1965,23 @@ button.structure-entry-main:focus-visible { display: none; } +/* Druck im kbp-CD der Seitenansicht (src/frontend/print/print-document.css): + gleiche Farben, Schrift, Titelblock und Fußzeile. Die Fußzeile mit + Seitenzahlen setzt EstimatePrintHeader als @page-Regel mit Projektdaten. */ +.estimate-print-titleblock { + display: none; +} + @page estimate-balance { size: A4 landscape; - margin: 10mm; + margin: 10mm 10mm 13mm; } @media print { body:has(.estimate-print-root) .sidebar, .estimate-print-root .estimate-no-print, + .estimate-print-root .estimate-page-header, + .estimate-print-root .estimate-header-fields, .estimate-print-root .alert, .estimate-print-root input, .estimate-print-root select, @@ -1989,28 +1998,122 @@ button.structure-entry-main:focus-visible { } .estimate-print-root { + --ep-blue: #476088; + --ep-lilac: #9198c4; + --ep-line: #d6dae5; + --ep-tint: #f5f6fa; + --ep-text: #2b2b2b; + --ep-muted: #6b7280; + --ep-accent: #c9782a; page: estimate-balance; padding: 0 !important; max-width: none; - color: #000; + color: var(--ep-text); + font-family: Arial, "Helvetica Neue", Helvetica, sans-serif; + color-scheme: light; + } + + .estimate-print-root * { + -webkit-print-color-adjust: exact; + print-color-adjust: exact; } .estimate-print-root .estimate-print-value { display: inline; } - .estimate-print-root .page-header { + /* Titelblock wie der Plankopf der Seitenansicht */ + .estimate-print-titleblock { + display: grid; + grid-template-columns: 30mm 1fr auto auto; margin-bottom: 4mm; + border: 1.5px solid var(--ep-blue); + background: #fff; + font-size: 8.5pt; + break-inside: avoid; } - .estimate-print-root .page-header h1 { - font-size: 14pt; - color: #000; + .estimate-print-titleblock > * { + padding: 2.5mm 3.5mm; + border-left: 1px solid var(--ep-line); } + .estimate-print-titleblock > :first-child { + border-left: none; + } + + .estimate-print-logo { + display: flex; + align-items: center; + justify-content: center; + min-height: 16mm; + color: var(--ep-blue); + font-weight: bold; + text-align: center; + } + + .estimate-print-logo img { + max-width: 100%; + max-height: 10mm; + } + + .estimate-print-kicker { + color: var(--ep-muted); + font-size: 7pt; + letter-spacing: 0.05em; + text-transform: uppercase; + } + + .estimate-print-heading { + display: flex; + align-items: baseline; + flex-wrap: wrap; + gap: 1mm 4mm; + margin-top: 1mm; + } + + .estimate-print-stand { + color: var(--ep-blue); + font-size: 16pt; + font-weight: bold; + } + + .estimate-print-project { + font-size: 12pt; + font-weight: bold; + } + + .estimate-print-meta { + display: grid; + grid-template-columns: auto auto; + gap: 0.8mm 4mm; + align-content: center; + margin: 0; + font-size: 7.5pt; + } + + .estimate-print-meta div { + display: contents; + } + + .estimate-print-meta dt { + color: var(--ep-muted); + font-weight: normal; + } + + .estimate-print-meta dd { + margin: 0; + font-weight: bold; + } + + .estimate-print-meta.is-tinted { + background: var(--ep-tint); + } + + /* Karten und Ränder der Bildschirmansicht entfallen */ .estimate-print-root .card { border: 0; - box-shadow: none; + box-shadow: none !important; background: transparent; } @@ -2026,54 +2129,117 @@ button.structure-entry-main:focus-visible { break-inside: avoid; } + .estimate-print-root .estimate-comparison-card .card-header { + margin-bottom: 2mm; + color: var(--ep-blue); + font-size: 10pt; + font-weight: bold; + } + .estimate-sheet-wrap { overflow: visible; } + /* Tabelle */ .estimate-sheet { min-width: 0; table-layout: auto; - font-size: 7.5pt; + font-size: 7.4pt; + line-height: 1.2; background: #fff; - color: #000; + color: var(--ep-text); } .estimate-sheet th, .estimate-sheet td { - border-color: #999; - padding: 1px 3px; + border: 1px solid var(--ep-line) !important; + padding: 0.7mm 1.5mm; + background: #fff; + color: var(--ep-text); } .estimate-sheet thead { display: table-header-group; } - .estimate-sheet thead th, - .estimate-total-row td { - background: #e8e8e8 !important; + .estimate-sheet thead th { + background: var(--ep-blue) !important; + border-color: var(--ep-blue) !important; + color: #fff !important; + font-weight: bold; + vertical-align: bottom; + } + + .estimate-sheet thead .estimate-th-hint { + color: #dfe3f0 !important; } .estimate-group-title-row td { - background: #d9d9d9 !important; + background: var(--ep-tint) !important; + border-top: 1.5px solid var(--ep-lilac) !important; + font-weight: bold; + } + + .estimate-group-title-row .estimate-cost-group-cell { + color: var(--ep-blue) !important; + } + + .estimate-group-title-row .estimate-group-editor { + color: var(--ep-muted); + font-weight: normal; } .estimate-subtotal-row td { - background: #f2f2f2 !important; + border-bottom: 1.5px solid var(--ep-line) !important; + font-weight: bold; + } + + .estimate-subtotal-row .estimate-total-label { + color: var(--ep-muted); + } + + .estimate-total-row td { + background: var(--ep-tint) !important; + font-weight: bold; + } + + .estimate-total-row.is-strong td { + background: var(--ep-blue) !important; + border-color: var(--ep-blue) !important; + color: #fff !important; + } + + .estimate-total-row .estimate-total-hint { + color: var(--ep-muted); + font-weight: normal; + } + + .estimate-total-row.is-strong .estimate-total-hint { + color: #dfe3f0; + } + + .estimate-comparison .is-over { + color: var(--ep-accent) !important; + font-weight: bold; + } + + .estimate-comparison .is-under { + color: var(--ep-blue) !important; + font-weight: bold; } .estimate-group-title-row, .estimate-subtotal-row, - .estimate-total-row { + .estimate-total-row, + .estimate-row { break-inside: avoid; - -webkit-print-color-adjust: exact; - print-color-adjust: exact; } .estimate-group-title-row { break-after: avoid; } - /* Totals print once at the end, not repeated on every page. */ + /* Summen einmal am Ende, nicht auf jeder Seite */ .estimate-sheet tfoot { display: table-row-group; break-inside: avoid; @@ -2087,8 +2253,4 @@ button.structure-entry-main:focus-visible { .estimate-gzf-input { justify-content: flex-end; } - - .estimate-header-fields { - margin-bottom: 3mm; - } } diff --git a/src/app/projects/[projectId]/grobbilanz/[balanceId]/page.tsx b/src/app/projects/[projectId]/grobbilanz/[balanceId]/page.tsx index 2aa7f5e..584f5cf 100644 --- a/src/app/projects/[projectId]/grobbilanz/[balanceId]/page.tsx +++ b/src/app/projects/[projectId]/grobbilanz/[balanceId]/page.tsx @@ -48,6 +48,7 @@ import { } from "../../../../../frontend/estimate-balance/estimate-balance-sheet"; import { EstimateComparisonTable } from "../../../../../frontend/estimate-balance/estimate-comparison-table"; import { DraftInput } from "../../../../../frontend/estimate-balance/estimate-draft-input"; +import { EstimatePrintHeader } from "../../../../../frontend/estimate-balance/estimate-print-header"; import { EstimateGroupModal, type EstimateGroupFormValues, @@ -461,6 +462,7 @@ export default function EstimateBalanceEditorPage() { return (
+ {project && draft ? : null}
\r\n]/g, " ")); +} + +/** + * Print-only title block and page footer of the rough power balance in the + * kbp corporate design of the print view (Seitenansicht): logo, blue title, + * meta data, "Seite x von y". Hidden on screen. + */ +export function EstimatePrintHeader({ project, balance }: EstimatePrintHeaderProps) { + const [office, setOffice] = useState(null); + const [hasLogo, setHasLogo] = useState(false); + const [logoVersion, setLogoVersion] = useState(""); + + useEffect(() => { + getDocumentBranding() + .then((branding) => { + setOffice(branding.office); + setHasLogo(Boolean(branding.logo)); + setLogoVersion(branding.logo ? branding.logo.updatedAtIso : ""); + }) + .catch(() => { + setOffice(null); + setHasLogo(false); + }); + }, []); + + const stand = [balance.name, balance.workStage].filter(Boolean).join(" · "); + const date = balance.dateIso ? formatIsoDateGerman(balance.dateIso) : null; + const footer = ` + @page estimate-balance { + size: A4 landscape; + margin: 10mm 10mm 13mm; + @bottom-left { content: ${cssString(`${project.name} · Grobe Leistungsbilanz`)}; font: 7.5pt Arial, sans-serif; color: #6b7280; } + @bottom-center { content: ${cssString(`Stand ${stand}${date ? ` · ${date}` : ""}`)}; font: 7.5pt Arial, sans-serif; color: #6b7280; } + @bottom-right { content: "Seite " counter(page) " von " counter(pages); font: 7.5pt Arial, sans-serif; color: #6b7280; } + }`; + + return ( + <> + + + + ); +} From 72189d23a50b12ade4672bd6901d2c78626710f2 Mon Sep 17 00:00:00 2001 From: Grovy311 Date: Sat, 26 Sep 2026 17:56:53 +0200 Subject: [PATCH 2/3] Offer supply suggestions, drop the circuit column, center cells MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Versorgung field suggests ELT and MSR (plus values already used in the balance) through a datalist and still accepts free text. The circuit number column is not needed in early design phases; it is removed from the data model, the regenerated migration 0010, the editor and the print. All cells are centered except "Verbraucher / Flächen", group titles and total labels. Co-Authored-By: Claude Opus 5.5 --- docs/estimate-balance.md | 2 +- src/app/globals.css | 33 +++++++++++++++- src/db/migrations/0010_estimate_balances.sql | 1 - src/db/migrations/meta/0010_snapshot.json | 9 +---- src/db/migrations/meta/_journal.json | 2 +- src/db/schema/estimate-balances.ts | 1 - .../domain/estimate-balance-command.model.ts | 2 - .../estimate-balance-document.ts | 3 -- .../estimate-balance-sheet.tsx | 39 ++++++++++++------- .../estimate-balance/estimate-balance.ts | 4 +- tests/estimate-balance-frontend.test.ts | 5 +-- tests/estimate-balance.test.ts | 1 - 12 files changed, 60 insertions(+), 42 deletions(-) diff --git a/docs/estimate-balance.md b/docs/estimate-balance.md index 4e43cec..afe4780 100644 --- a/docs/estimate-balance.md +++ b/docs/estimate-balance.md @@ -34,7 +34,7 @@ das Frontend sie direkt nutzt (Turbopack löst die `.js`-Suffixe der API nicht a |---|---| | `estimate_balances` | ein **Stand** je Projekt (z. B. Vorplanung, Entwurfsplanung): Name, LPH, Datum, Reserve AV/SV in %, Sortierung | | `estimate_groups` | Kostengruppe (`"410"` … `"480"`, `""` für „Allgemein“), Titel, „bearbeitet von“, Sortierung | -| `estimate_rows` | Versorgung, Stromkreis-Nr. (Freitext), Verbraucher/Fläche, Anzahl bzw. Fläche, Einheit (`piece` = St., `area` = m²), Anschluss (fest/Steckdose), Einzelleistung in W bzw. W/m², GLF, cos φ, SV ja/nein, Anmerkung | +| `estimate_rows` | Versorgung (Auswahl ELT/MSR oder Freitext), Verbraucher/Fläche, Anzahl bzw. Fläche, Einheit (`piece` = St., `area` = m²), Anschluss (fest/Steckdose), Einzelleistung in W bzw. W/m², GLF, cos φ, SV ja/nein, Anmerkung | Neue Stände entstehen aus der Vorlage KG 400 (410 Sanitär, 420 Wärme, 430 Luft, 434 Kälte, 440 Starkstrom, 450 Schwachstrom, 460 Förder, 470 Nutzungsspezifisch, diff --git a/src/app/globals.css b/src/app/globals.css index 4a0adc8..4f0bcb7 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -1731,7 +1731,6 @@ button.structure-entry-main:focus-visible { .estimate-col-kg { width: 3rem; } .estimate-col-supply { width: 4rem; } -.estimate-col-circuit { width: 4.6rem; } .estimate-col-name { width: auto; } .estimate-col-quantity { width: 4.8rem; } .estimate-col-unit { width: 3.8rem; } @@ -1965,6 +1964,36 @@ button.structure-entry-main:focus-visible { display: none; } +/* Ausrichtung: alle Zellen zentriert, nur „Verbraucher / Flächen“, die + Gruppentitel und die Summenbeschriftungen stehen links bzw. rechts. */ +.estimate-sheet td, +.estimate-sheet td.estimate-number, +.estimate-sheet .estimate-cell-input, +.estimate-sheet .estimate-cell-input.is-numeric { + text-align: center; +} + +.estimate-sheet .estimate-cell-select { + text-align: center; + text-align-last: center; +} + +.estimate-sheet .estimate-unit-input, +.estimate-sheet .estimate-gzf-input { + justify-content: center; +} + +.estimate-sheet td.estimate-name-cell, +.estimate-sheet td.estimate-name-cell .estimate-cell-input, +.estimate-sheet .estimate-group-title-row td:not(.estimate-cost-group-cell), +.estimate-comparison td:nth-child(2) { + text-align: left; +} + +.estimate-sheet td.estimate-total-label { + text-align: right; +} + /* Druck im kbp-CD der Seitenansicht (src/frontend/print/print-document.css): gleiche Farben, Schrift, Titelblock und Fußzeile. Die Fußzeile mit Seitenzahlen setzt EstimatePrintHeader als @page-Regel mit Projektdaten. */ @@ -2251,6 +2280,6 @@ button.structure-entry-main:focus-visible { .estimate-unit-input, .estimate-gzf-input { - justify-content: flex-end; + justify-content: center; } } diff --git a/src/db/migrations/0010_estimate_balances.sql b/src/db/migrations/0010_estimate_balances.sql index 645ccbe..bfa9dd3 100644 --- a/src/db/migrations/0010_estimate_balances.sql +++ b/src/db/migrations/0010_estimate_balances.sql @@ -27,7 +27,6 @@ CREATE TABLE `estimate_rows` ( `group_id` text NOT NULL, `sort_order` integer DEFAULT 0 NOT NULL, `supply` text, - `circuit_ref` text, `name` text DEFAULT '' NOT NULL, `quantity` real DEFAULT 0 NOT NULL, `unit` text DEFAULT 'piece' NOT NULL, diff --git a/src/db/migrations/meta/0010_snapshot.json b/src/db/migrations/meta/0010_snapshot.json index 869d504..d58518a 100644 --- a/src/db/migrations/meta/0010_snapshot.json +++ b/src/db/migrations/meta/0010_snapshot.json @@ -1,7 +1,7 @@ { "version": "6", "dialect": "sqlite", - "id": "6df688bd-a78a-46a1-ba7a-4068119bf8bd", + "id": "5376ae9f-67c7-4603-b3d2-8527136436f9", "prevId": "2cd9adef-2f08-4635-b3d9-ecf71f624c7f", "tables": { "buildings": { @@ -1322,13 +1322,6 @@ "notNull": false, "autoincrement": false }, - "circuit_ref": { - "name": "circuit_ref", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, "name": { "name": "name", "type": "text", diff --git a/src/db/migrations/meta/_journal.json b/src/db/migrations/meta/_journal.json index 209a0a2..f2c5dba 100644 --- a/src/db/migrations/meta/_journal.json +++ b/src/db/migrations/meta/_journal.json @@ -75,7 +75,7 @@ { "idx": 10, "version": "6", - "when": 1790425784626, + "when": 1790438092269, "tag": "0010_estimate_balances", "breakpoints": true } diff --git a/src/db/schema/estimate-balances.ts b/src/db/schema/estimate-balances.ts index 81420f1..4425a03 100644 --- a/src/db/schema/estimate-balances.ts +++ b/src/db/schema/estimate-balances.ts @@ -44,7 +44,6 @@ export const estimateRows = sqliteTable( .references(() => estimateGroups.id, { onDelete: "cascade" }), sortOrder: integer("sort_order").notNull().default(0), supply: text("supply"), - circuitRef: text("circuit_ref"), name: text("name").notNull().default(""), quantity: real("quantity").notNull().default(0), unit: text("unit", { enum: ["piece", "area"] }).notNull().default("piece"), diff --git a/src/estimate-balance/domain/estimate-balance-command.model.ts b/src/estimate-balance/domain/estimate-balance-command.model.ts index 44263e4..f1b0da8 100644 --- a/src/estimate-balance/domain/estimate-balance-command.model.ts +++ b/src/estimate-balance/domain/estimate-balance-command.model.ts @@ -28,7 +28,6 @@ export const estimateRowSnapshotSchema = z id: idSchema, sortOrder: z.number().int().min(0), supply: nullableText(50), - circuitRef: nullableText(50), name: trimmed(200), quantity: finite.min(0), unit: z.enum(["piece", "area"]), @@ -158,7 +157,6 @@ export function canonicalEstimateBalance(balance: EstimateBalanceSnapshot): stri id: row.id, sortOrder: row.sortOrder, supply: row.supply, - circuitRef: row.circuitRef, name: row.name, quantity: row.quantity, unit: row.unit, diff --git a/src/frontend/estimate-balance/estimate-balance-document.ts b/src/frontend/estimate-balance/estimate-balance-document.ts index 0049dc2..e2726d1 100644 --- a/src/frontend/estimate-balance/estimate-balance-document.ts +++ b/src/frontend/estimate-balance/estimate-balance-document.ts @@ -18,7 +18,6 @@ export const estimateTextLimits = { groupTitle: 200, groupEditor: 100, supply: 50, - circuitRef: 50, rowName: 200, remark: 500, } as const; @@ -34,7 +33,6 @@ export function createEstimateRow( id: createId(), sortOrder, supply: null, - circuitRef: null, name: "", quantity: 1, unit: "piece", @@ -257,7 +255,6 @@ export function toSaveableEstimateBalance( id: row.id, sortOrder: row.sortOrder, supply: optionalText(row.supply), - circuitRef: optionalText(row.circuitRef), name: requiredText(row.name), quantity: row.quantity, unit: row.unit, diff --git a/src/frontend/estimate-balance/estimate-balance-sheet.tsx b/src/frontend/estimate-balance/estimate-balance-sheet.tsx index debba7f..4703397 100644 --- a/src/frontend/estimate-balance/estimate-balance-sheet.tsx +++ b/src/frontend/estimate-balance/estimate-balance-sheet.tsx @@ -50,8 +50,12 @@ interface EstimateBalanceSheetProps { } // Leading columns up to cos φ; the power columns follow. -const LEADING_COLUMN_COUNT = 10; -const COLUMN_COUNT = 17; +const LEADING_COLUMN_COUNT = 9; +const COLUMN_COUNT = 16; + +/** Suggested supply values; free text stays possible. */ +const SUPPLY_SUGGESTIONS = ["ELT", "MSR"]; +const SUPPLY_LIST_ID = "estimate-supply-options"; function NumberInput({ ariaLabel, @@ -85,9 +89,17 @@ function NumberInput({ ); } +function supplyOptions(balance: EstimateBalanceSnapshot): string[] { + const used = balance.groups.flatMap((group) => + group.rows.map((row) => row.supply?.trim() ?? "") + ); + return [...new Set([...SUPPLY_SUGGESTIONS, ...used.filter(Boolean)])]; +} + function TextInput({ ariaLabel, disabled, + list, maxLength, onChange, placeholder, @@ -95,6 +107,7 @@ function TextInput({ }: { ariaLabel: string; disabled: boolean; + list?: string; maxLength: number; onChange: (value: string) => void; placeholder?: string; @@ -106,6 +119,7 @@ function TextInput({ aria-label={ariaLabel} className="estimate-cell-input" disabled={disabled} + list={list} maxLength={maxLength} onChange={(event) => onChange(event.target.value)} placeholder={placeholder} @@ -189,22 +203,14 @@ function EstimateRowLine({ patch({ supply })} placeholder="ELT" value={row.supply} /> - - patch({ circuitRef })} - value={row.circuitRef} - /> - - + + + {supplyOptions(balance).map((supply) => ( + - @@ -409,9 +419,8 @@ export function EstimateBalanceSheet({ -
KG Versorgung - ELT/ISP … + ELT/MSR … Stromkreis-Nr. Verbraucher / Flächen Anzahl / Fläche diff --git a/src/shared/estimate-balance/estimate-balance.ts b/src/shared/estimate-balance/estimate-balance.ts index 1a7b5a5..63d3753 100644 --- a/src/shared/estimate-balance/estimate-balance.ts +++ b/src/shared/estimate-balance/estimate-balance.ts @@ -12,10 +12,8 @@ export type EstimateRowConnection = "fixed" | "socket"; export interface EstimateRowSnapshot { id: string; sortOrder: number; - /** Versorgung, e.g. "ELT" or "ISP". */ + /** Versorgung, e.g. "ELT" or "MSR" (free text). */ supply: string | null; - /** Optional circuit reference, free text. */ - circuitRef: string | null; /** Verbraucher / Fläche. May be empty while a row is being typed. */ name: string; /** Anzahl (unit "piece") or area in m² (unit "area"). */ diff --git a/tests/estimate-balance-frontend.test.ts b/tests/estimate-balance-frontend.test.ts index fa9368d..ed5f4c7 100644 --- a/tests/estimate-balance-frontend.test.ts +++ b/tests/estimate-balance-frontend.test.ts @@ -165,7 +165,6 @@ describe("estimate balance document editing", () => { id: "row-x", sortOrder: 3, supply: null, - circuitRef: null, name: "", quantity: 1, unit: "piece", @@ -273,7 +272,6 @@ describe("estimate balance saving", () => { balance = updateEstimateRow(balance, "group-410", "row-a", { name: " Hebeanlage ", supply: " ELT ", - circuitRef: "", remark: " ", }); const saveable = toSaveableEstimateBalance(balance); @@ -286,10 +284,9 @@ describe("estimate balance saving", () => { { name: saveable.groups[0]!.rows[0]!.name, supply: saveable.groups[0]!.rows[0]!.supply, - circuitRef: saveable.groups[0]!.rows[0]!.circuitRef, remark: saveable.groups[0]!.rows[0]!.remark, }, - { name: "Hebeanlage", supply: "ELT", circuitRef: null, remark: null } + { name: "Hebeanlage", supply: "ELT", remark: null } ); }); diff --git a/tests/estimate-balance.test.ts b/tests/estimate-balance.test.ts index 7d294da..1463fe4 100644 --- a/tests/estimate-balance.test.ts +++ b/tests/estimate-balance.test.ts @@ -52,7 +52,6 @@ function row( id: id(), sortOrder: 0, supply: "ELT", - circuitRef: null, name, quantity, unit: "piece", From 7b2419b347470807a6a5a9a6bc395fe9ba51009b Mon Sep 17 00:00:00 2001 From: Grovy311 Date: Sat, 26 Sep 2026 18:00:57 +0200 Subject: [PATCH 3/3] Polish the printed rough power balance The printed page kept the dark app background in the page margins, the consumer column wrapped while the empty remark column took the space, an empty "bearbeitet von" label was printed, and numbers were printed as typed (11200). Print margins are white now, the consumer column is wider, empty editors are omitted and numbers print with thousands separators (11.200) like the template. Co-Authored-By: Claude Opus 5.5 --- src/app/globals.css | 28 ++++++++++++++++++- .../estimate-balance-sheet.tsx | 10 ++++++- .../estimate-balance/estimate-draft-input.tsx | 7 ++++- 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/src/app/globals.css b/src/app/globals.css index 4f0bcb7..dbf49fb 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -2018,10 +2018,36 @@ button.structure-entry-main:focus-visible { display: none !important; } - body:has(.estimate-print-root) { + html:has(.estimate-print-root), + body:has(.estimate-print-root), + body:has(.estimate-print-root) .app-shell, + body:has(.estimate-print-root) .app-shell-content { background: #fff !important; } + /* Der Dunkelmodus setzt color-scheme: dark; Chrome färbt damit auch den + Seitenrand dunkel. Im Druck gilt immer das helle Schema. */ + html:has(.estimate-print-root) { + color-scheme: light !important; + } + + .estimate-print-root .estimate-group-editor.is-empty { + display: none; + } + + /* Verbraucher bekommt den Platz; Anmerkungen bleiben schmal. */ + .estimate-sheet .estimate-col-name { + width: 52mm; + } + + .estimate-sheet .estimate-col-remark { + width: 24mm; + } + + .estimate-sheet .estimate-col-supply { + width: 13mm; + } + body:has(.estimate-print-root) .app-shell { display: block; } diff --git a/src/frontend/estimate-balance/estimate-balance-sheet.tsx b/src/frontend/estimate-balance/estimate-balance-sheet.tsx index 4703397..07dc19c 100644 --- a/src/frontend/estimate-balance/estimate-balance-sheet.tsx +++ b/src/frontend/estimate-balance/estimate-balance-sheet.tsx @@ -31,6 +31,7 @@ import { formatKw, parseGermanDecimal, type DecimalRange, + formatGermanNumber, } from "./estimate-balance-format"; import { DraftInput } from "./estimate-draft-input"; @@ -81,6 +82,7 @@ function NumberInput({ disabled={disabled} format={(current: number) => formatDecimalInput(current)} inputMode="decimal" + printFormat={(current: number) => formatGermanNumber(current, 0, 3)} onCommit={onCommit} onValidityChange={(isValid) => onValidityChange(fieldKey, isValid)} parse={(text) => parseGermanDecimal(text, range)} @@ -461,7 +463,13 @@ export function EstimateBalanceSheet({
{group.title} -