From 1b559dd2c6c7fed70ed478b1e80d1f22191b3f8f Mon Sep 17 00:00:00 2001 From: bes-r <27369668+bes-r@users.noreply.github.com> Date: Fri, 19 Jun 2026 23:45:06 +0200 Subject: [PATCH] printopties-dialog: bereik, indeling, kleur, details, papierformaat & orientatie --- ROADMAP.md | 6 +++ frontend/src/pages.jsx | 98 ++++++++++++++++++++++++++++++++++++++--- frontend/src/styles.css | 23 +++++++++- 3 files changed, 119 insertions(+), 8 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index 45e9f81..6aa9ca6 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -40,6 +40,12 @@ volgt. ## Fase 6 — Weergave & beheer +- [x] **Printopties-dialog** (🖨 in Rooster/Roostermaker): kies wát (huidige + selectie / alle klassen) en hóé je afdrukt — indeling (kolommen / dagen + onder elkaar), vakken in kleur, begeleiding/lokaal tonen, optionele + koptekst — plus **papierformaat** (A4/A3/A5/Letter) en oriëntatie + (liggend/staand). Toegepast via body-classes + dynamische `@page`. + - [x] **Vakkleur als vlak** (toggle 🎨, onthouden per browser): vak volledig in de activiteitskleur i.p.v. een streepje, met automatisch leesbare tekstkleur (zwart/wit op basis van helderheid). Werkt in Rooster/Roostermaker en diff --git a/frontend/src/pages.jsx b/frontend/src/pages.jsx index 019db4a..b57d748 100644 --- a/frontend/src/pages.jsx +++ b/frontend/src/pages.jsx @@ -2072,6 +2072,12 @@ export function RoosterScherm({ mode }) { const setWeergave = (v) => { setWeergaveState(v); try { localStorage.setItem("rw_weergave", v); } catch { /* niet beschikbaar */ } }; const [vakkleurVlak, setVakkleurState] = useState(() => { try { return localStorage.getItem("rw_vakkleur") === "vlak"; } catch { return false; } }); const setVakkleur = (v) => { setVakkleurState(v); try { localStorage.setItem("rw_vakkleur", v ? "vlak" : "streep"); } catch { /* niet beschikbaar */ } }; + // Printopties + const [printOpen, setPrintOpen] = useState(false); + const [printPending, setPrintPending] = useState(false); + const [printAllesActief, setPrintAllesActief] = useState(false); + const [printKop, setPrintKop] = useState(""); + const [printOpts, setPrintOpts] = useState({ bereik: "huidig", indeling: "kolom", kleur: false, begeleiding: true, lokaal: true, papier: "A4", orientatie: "landscape" }); useEffect(() => { listResource("schooljaren").then((j) => { setSchooljaren(j); if (j.length) setSchooljaar((s) => s || String(j[0].id)); }).catch((e) => setError(e.message)); @@ -2096,7 +2102,9 @@ export function RoosterScherm({ mode }) { // Scope-context + gefilterde blokken (voor weergave/afdrukken). const scopeCtx = { leerlingen, subgroepen, leerpleinen }; const scope = { type: scopeType, id: scopeId ? Number(scopeId) : "" }; - const zichtbareBlokken = blokken.filter((b) => blokInScope(b, scope, scopeCtx)); + // Bij printen "alle klassen" tijdelijk de scope negeren. + const effScope = printAllesActief ? { type: "alles", id: "" } : scope; + const zichtbareBlokken = blokken.filter((b) => blokInScope(b, effScope, scopeCtx)); // Conflicten over álle blokken (niet alleen de zichtbare scope), zodat de // melding compleet is en de betrokken blokken in het rooster omlijnd worden. const conflictInfo = berekenRoosterConflicten(blokken, scopeCtx, tijdsloten); @@ -2194,6 +2202,34 @@ export function RoosterScherm({ mode }) { setBewerkBlok(null); setToonForm(true); } + // Print: pas de gekozen opties toe via body-classes + @page, dan afdrukken. + function zetPaginaStijl(papier, orientatie) { + let el = document.getElementById("rw-print-page"); + if (!el) { el = document.createElement("style"); el.id = "rw-print-page"; document.head.appendChild(el); } + el.textContent = `@page { size: ${papier} ${orientatie}; margin: 10mm; }`; + } + function startPrint() { + const b = document.body.classList; + b.toggle("print-lijst", printOpts.indeling === "lijst"); + b.toggle("print-kolom", printOpts.indeling === "kolom"); + b.toggle("print-kleur", !!printOpts.kleur); + b.toggle("print-geen-begeleiding", !printOpts.begeleiding); + b.toggle("print-geen-lokaal", !printOpts.lokaal); + zetPaginaStijl(printOpts.papier, printOpts.orientatie); + setPrintKop(printOpts.kop || ""); + if (printOpts.bereik === "alles") setPrintAllesActief(true); + setPrintOpen(false); + setPrintPending(true); + } + useEffect(() => { + if (!printPending) return; + const id = setTimeout(() => { + window.print(); + setPrintPending(false); + setPrintAllesActief(false); + }, 120); + return () => clearTimeout(id); + }, [printPending]); // Direct vanuit een tijdvak in het rooster een vak toevoegen: tijdslot vast, // en het doel alvast op de actieve scope (klas/leerling) zetten. function openNieuwBlokVoorSlot(tijdslotId) { @@ -2338,7 +2374,7 @@ export function RoosterScherm({ mode }) {
toggleOpenBlok(b) : undefined} title={edit ? "Klik om te bewerken" : undefined} > @@ -2351,10 +2387,10 @@ export function RoosterScherm({ mode }) {
{(b.locatie_naam || b.lokaal_naam || b.begeleiders_namen.length > 0) && (
- {b.locatie_naam && 📍 {b.locatie_naam}} - {b.lokaal_naam && 🚪 {b.lokaal_naam}} + {b.locatie_naam && 📍 {b.locatie_naam}} + {b.lokaal_naam && 🚪 {b.lokaal_naam}} {b.begeleiders_namen.length > 0 && ( - 👤 {b.begeleiders_namen.map((x, idx) => ( + 👤 {b.begeleiders_namen.map((x, idx) => ( {idx > 0 ? ", " : ""}{x.naam}{rolAfk(x.id) ? ` (${rolAfk(x.id)})` : ""} ))} )} @@ -2474,6 +2510,7 @@ export function RoosterScherm({ mode }) { return (
+ {printKop &&
{printKop}
}

{edit ? "Roostermaker" : "Rooster"}

{edit @@ -2501,7 +2538,7 @@ export function RoosterScherm({ mode }) {

- {printAan && } + {printAan && }
)}
@@ -2772,6 +2809,55 @@ export function RoosterScherm({ mode }) { /> )} {edit && ghost &&
{ghost.label}
} + + {printOpen && ( +
setPrintOpen(false)}> +
e.stopPropagation()}> +
+

Afdrukken

Kies wat en hoe je afdrukt.
+ +
+
+ + + + +
+ +
+ + + +
+
+ + +
+
+
+ )} ); } diff --git a/frontend/src/styles.css b/frontend/src/styles.css index 91f68c0..9669b71 100644 --- a/frontend/src/styles.css +++ b/frontend/src/styles.css @@ -326,10 +326,26 @@ input[type="color"] { padding: 2px; width: 44px; min-width: 44px; height: 38px; body { background: #fff; } .app { display: block; } .content { padding: 0; } + .print-only { display: block; } .rooster-scherm h1 { margin: 0 0 8px; } .rooster-grid { grid-template-columns: repeat(5, 1fr); gap: 4px; } .rooster-kolom { border-color: #999; } - .rooster-blok { break-inside: avoid; } + .rooster-blok, .vak-kaart, .blok-splitsing { break-inside: avoid; } + + /* Indeling-keuze uit de printdialog */ + body.print-kolom .rooster-grid { display: grid !important; grid-template-columns: repeat(5, 1fr) !important; } + body.print-lijst .rooster-grid { display: flex !important; flex-direction: column !important; } + body.print-lijst .rooster-kolom { display: flex; flex-wrap: wrap; gap: 6px; } + body.print-lijst .rooster-dag { flex: 0 0 100%; } + body.print-lijst .slot-cel { flex: 1 1 200px; max-width: 320px; } + + /* Details verbergen volgens keuze */ + body.print-geen-begeleiding .vk-tag.t-beg { display: none; } + body.print-geen-lokaal .vk-tag.t-lok, body.print-geen-lokaal .vk-tag.t-loc { display: none; } + + /* Vakken in kleur afdrukken */ + body.print-kleur .vak-kaart { background: var(--vk-kleur) !important; color: var(--vk-tekst) !important; -webkit-print-color-adjust: exact; print-color-adjust: exact; } + body.print-kleur .vak-kaart .vk-naam, body.print-kleur .vak-kaart .muted { color: inherit !important; } } /* --- Onderkant zijbalk: subtiel Instellingen-blok --- */ @@ -560,8 +576,11 @@ input[type="color"] { padding: 2px; width: 44px; min-width: 44px; height: 38px; .wm-add { width: 100%; border: 1px dashed var(--line); background: transparent; color: var(--muted); border-radius: 7px; padding: 4px; cursor: pointer; font: inherit; font-size: 13px; opacity: .6; } .wm-cel:hover .wm-add { opacity: 1; } .wm-add:hover { border-color: var(--brand); color: var(--brand); background: var(--brand-soft); } -/* Vak volledig in de vakkleur (tekstkleur wordt inline op contrast gezet) */ +/* Vak volledig in de vakkleur (kleur + leesbare tekstkleur via CSS-variabelen) */ +.vak-kaart.kleur-vlak { background: var(--vk-kleur); color: var(--vk-tekst); } .vak-kaart.kleur-vlak .vk-naam { color: inherit; } +.print-only { display: none; } +.print-kop { font-size: 16px; font-weight: 700; margin: 0 0 8px; } .vak-kaart.kleur-vlak .rb-greep, .vak-kaart.kleur-vlak .icon-btn { color: inherit; opacity: .85; } .vak-kaart.kleur-vlak .rb-status, .vak-kaart.kleur-vlak .rb-verv { color: inherit; } .wm-vak.kleur-vlak .wm-vak-naam { color: inherit; }