Files
alpenwerk-hr/app/(app)/orgchart/print/page.tsx
Maximilian Stubhan 3151f32404 Print the org chart as an org chart, and ask first what to print
The chart on screen is an infinite canvas: you zoom in, drag around, and
look at one corner at a time. Paper has none of that. Printing the canvas
means scaling 850 people onto one sheet, which yields boxes two
millimetres wide — technically the whole company, practically nothing.

So the print view is rebuilt rather than shrunk, and it does two things
the canvas cannot.

It asks before it prints. Depth (bereiche, abteilungen, teams, or teams
with every name) and which divisions, each one selectable. Whoever needs
Produktion for a meeting gets two sheets instead of forty, and the page
count is on the button before anything reaches the printer.

And it draws the hierarchy as a hierarchy: boxes joined by connecting
lines, not a column of cards. Superior and subordinate are the entire
point of an org chart; a tidy list of the same units simply does not say
it. The lines come from borders on pseudo-elements, so the PDF keeps
them as vectors and they stay sharp when someone zooms in. Header
shading gets weaker with each level down, which survives the black-and-
white printer that most of these end up on.

Overview sheet first, then one sheet per selected division, each
carrying its own heading and headcount so page seven is still readable
on its own. A4 or A3, landscape.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 22:03:54 +02:00

44 lines
1.8 KiB
TypeScript

import { PrintChart } from "@/components/orgchart/PrintChart";
import type { OrgUnitNode } from "@/components/orgchart/types";
import { currentUserId } from "@/lib/auth/session";
import { withUser } from "@/lib/db";
import { todayIso } from "@/lib/format";
import { loadOrgAsOf } from "@/lib/orgchart-data";
import { buildPrintModel } from "@/lib/orgchart-print";
import { parseIsoDateParam } from "@/lib/reports";
export const metadata = { title: "Organigramm drucken" };
type SearchParams = { asOf?: string };
// Eigene Route statt eines Druckstils auf der Organigramm-Seite.
//
// Die interaktive Ansicht ist eine Leinwand mit Zoom und Verschiebung; was
// davon auf Papier landet, hängt vom Zufallsstand der Ansicht ab. Hier wird
// stattdessen aus denselben Daten eine Seitenfolge gebaut, und der Stichtag
// wird aus der Adresse übernommen — wer im Organigramm einen Stichtag
// eingestellt hat, druckt genau den.
export default async function OrgChartPrintPage({ searchParams }: { searchParams: Promise<SearchParams> }) {
const params = await searchParams;
const today = todayIso();
const asOf = parseIsoDateParam(params.asOf) ?? today;
const { org, units } = await withUser(await currentUserId(), async (tx) => {
const [org, units] = await Promise.all([
loadOrgAsOf(tx, asOf),
tx
.selectFrom("org_units")
.select(["id", "org_number", "name", "parent_id", "unit_type"])
.where((eb) => eb.or([eb("valid_to", "is", null), eb("valid_to", ">", asOf)]))
.where("valid_from", "<=", asOf)
.orderBy("org_number")
.execute(),
]);
return { org, units };
});
const model = buildPrintModel(units as OrgUnitNode[], org.employees, org.vacancies);
return <PrintChart model={model} asOf={asOf} today={today} />;
}