From e8e0022359127fb0d0082baded12d343f11e48a6 Mon Sep 17 00:00:00 2001 From: bes-r <27369668+bes-r@users.noreply.github.com> Date: Fri, 12 Jun 2026 23:18:50 +0200 Subject: [PATCH] Rooster gesplitst in weergave + Roostermaker, printmodule afgebouwd (subtiele Afdrukken-knop met print-CSS) --- README.md | 6 +++ ROADMAP.md | 16 ++++++++ backend/core/apps.py | 3 +- backend/modules/printing/apps.py | 11 +++--- frontend/src/App.jsx | 6 ++- frontend/src/pages.jsx | 63 +++++++++++++++++++++----------- frontend/src/styles.css | 14 +++++++ 7 files changed, 89 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index d47fb53..41e2253 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,12 @@ rustiger is). De zijbalk groepeert items onder inklapbare submenu's (Personen, Groepen) en heeft onderaan een subtiel **Instellingen**-blok met Gebruikers en Modulebeheer. +Er zijn twee rooster-schermen: **Rooster** (alleen-lezen weergave, om te bekijken +en af te drukken) en **Roostermaker** (de aanpasser met drag-and-drop, tabs en +conflictdetectie). De module **Printen/Exporteren** voegt — als die aanstaat — een +subtiele **Afdrukken**-knop toe aan beide; die drukt het weekrooster +printvriendelijk af (of bewaart als PDF via de printdialoog). + Optionele module **Leerplein**: koppelt subgroepen aan groepen en bundelt groepen, subgroepen én losse leerlingen onder een overkoepelend leerplein. Optionele module **Stage**: interne en externe stages van leerlingen — beide met diff --git a/ROADMAP.md b/ROADMAP.md index eaa47a7..c57c7e6 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -48,6 +48,22 @@ volgt. (de betreffende blokken vervallen voor die leerling). - [ ] Stage instelbaar **per blok, per dagdeel of per dag**. +## Module: Urenoverzicht + +- [ ] **Module die berekent hoeveel uur een groep per vak krijgt.** +- [ ] Optie om dit **per leerling** te tonen, **inclusief stage-uren**. +- [ ] **Geschiedenis** van dit overzicht **per leerling** tonen (verloop over de + tijd). +- [ ] **Vrije dagen tellen niet mee** in deze urenberekening. + +## Roostermaker — vrije dagen & blok vervallen + +- [ ] **Vrije/vakantie-/studiedagen kunnen niet gebruikt worden** in de + Roostermaker (geen blokken plannen op die dagen). +- [ ] De actie **"Deze dag laten vervallen"** subtieler wegwerken **en de tekst + corrigeren** — het is per *blok*, niet per dag. +- [ ] Mogelijkheid om **een hele dag op vervallen** te zetten (i.p.v. per blok). + ## Later - [ ] **Module: automatisch rooster genereren.** De gebruiker geeft vereisten/ diff --git a/backend/core/apps.py b/backend/core/apps.py index 4174825..2143c42 100644 --- a/backend/core/apps.py +++ b/backend/core/apps.py @@ -27,8 +27,9 @@ class CoreConfig(AppConfig): category="Kern", core=True, menu_items=( - # Hoofdniveau (Schooljaar zit nu als tab in het Rooster) + # Hoofdniveau: Rooster = weergave, Roostermaker = aanpasser. MenuItem("Rooster", "/rooster", icon="calendar", order=10), + MenuItem("Roostermaker", "/roostermaker", icon="calendar", order=12), # Submenu Personen MenuItem("Personen", "/personen", icon="users", order=20, group="Personen"), MenuItem("Functies", "/functies", icon="badge", order=25, group="Personen"), diff --git a/backend/modules/printing/apps.py b/backend/modules/printing/apps.py index 845575d..70bbfb2 100644 --- a/backend/modules/printing/apps.py +++ b/backend/modules/printing/apps.py @@ -15,15 +15,16 @@ class PrintingConfig(AppConfig): key="printing", name="Printen / Exporteren", description=( - "Roosters afdrukken of als PDF exporteren, per groep, " - "leerling of leerkracht." + "Voegt een subtiele 'Afdrukken'-knop toe aan het Rooster en de " + "Roostermaker, die het weekrooster printvriendelijk afdrukt " + "(of als PDF bewaart via de printdialoog)." ), version="0.1.0", category="Uitvoer", core=False, default_enabled=True, - menu_items=( - MenuItem("Afdrukken", "/afdrukken", icon="printer", order=200), - ), + # Geen eigen menu-item: de knop verschijnt in de rooster-schermen + # zodra deze module aanstaat. + menu_items=(), ) ) diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 4629472..8b6f9ed 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -3,7 +3,8 @@ import { getMe, login, logout, getModules, setModuleState } from "./api.js"; import Logo from "./Logo.jsx"; import { GroepenPage, PersonenPage, SubgroepenPage, FunctiesPage, - ActiviteitenPage, LocatiesPage, TijdslotenPage, SchooljaarPage, RoosterPage, + ActiviteitenPage, LocatiesPage, TijdslotenPage, SchooljaarPage, + RoosterWeergavePage, RoostermakerPage, AfwezigheidPage, LeerpleinPage, StagePage, } from "./pages.jsx"; @@ -16,7 +17,8 @@ const ICONS = { // Koppel een menu-pad aan een echt scherm. Onbekende paden tonen een // placeholder (worden in een latere fase ingevuld). const PAGES = { - "/rooster": RoosterPage, + "/rooster": RoosterWeergavePage, + "/roostermaker": RoostermakerPage, "/personen": PersonenPage, "/functies": FunctiesPage, "/groepen": GroepenPage, diff --git a/frontend/src/pages.jsx b/frontend/src/pages.jsx index a78ef7f..5a9d60d 100644 --- a/frontend/src/pages.jsx +++ b/frontend/src/pages.jsx @@ -4,6 +4,7 @@ import { createResource, updateResource, deleteResource, + getModules, } from "./api.js"; const ROLLEN = [ @@ -1013,9 +1014,12 @@ function maandagVan(d) { const x = new Date(d); x.setHours(0, 0, 0, 0); const wd function plusDagen(d, n) { const x = new Date(d); x.setDate(x.getDate() + n); return x; } /* ---------------------------------- Rooster ------------------------------- */ -// Weekweergave met effectief rooster (vrije dagen, afwezigheid, uitzonderingen) -// en drag-and-drop (muis + touch) om een blok naar een ander tijdslot te slepen. -export function RoosterPage() { +// Eén component met twee modi: +// - mode="view" -> Rooster (alleen-lezen weergave, om te bekijken/printen) +// - mode="edit" -> Roostermaker (volledige bewerking + drag-and-drop) +export function RoosterScherm({ mode }) { + const edit = mode === "edit"; + const [printAan, setPrintAan] = useState(false); const [schooljaren, setSchooljaren] = useState([]); const [schooljaar, setSchooljaar] = useState(""); const [tijdsloten, setTijdsloten] = useState([]); @@ -1052,6 +1056,8 @@ export function RoosterPage() { listResource("subgroepen").then(setSubgroepen).catch(() => {}); listResource("personen", "?rol=leerling").then(setLeerlingen).catch(() => {}); listResource("personen").then((ps) => setMedewerkers(ps.filter((p) => p.rol !== "leerling"))).catch(() => {}); + // Afdrukken alleen tonen als de print-module aanstaat. + getModules().then((ms) => setPrintAan(ms.some((m) => m.key === "printing" && m.enabled))).catch(() => {}); }, []); const weekDatums = [0, 1, 2, 3, 4].map((i) => plusDagen(maandag, i)); @@ -1140,12 +1146,16 @@ export function RoosterPage() { const persoonAfwezig = (id, iso) => afwezig.some((a) => a.persoon === id && a.start_datum <= iso && a.eind_datum >= iso); return ( -
-

Rooster

-

Sleep een blok naar een ander tijdslot om het te verplaatsen. Vrije dagen, afwezigheid en uitzonderingen worden meegenomen.

+
+

{edit ? "Roostermaker" : "Rooster"}

+

+ {edit + ? "Sleep een blok naar een ander tijdslot om het te verplaatsen. Vrije dagen, afwezigheid en uitzonderingen worden meegenomen." + : "Bekijk het weekrooster. Vrije dagen, afwezigheid en uitzonderingen zijn verwerkt."} +

-
+
)} {kal &&
Vervalt — {kal.type_label}
} - {!kal && uit && uit.vervalt &&
Vervalt {uit.opmerking ? `(${uit.opmerking})` : ""} ·
} + {!kal && uit && uit.vervalt &&
Vervalt {uit.opmerking ? `(${uit.opmerking})` : ""}{edit && <> · }
} {!kal && uit && !uit.vervalt && uit.vervanger_naam &&
↪ Vervanger: {uit.vervanger_naam}
} - {!kal && !uit && ( - + {edit && !kal && !uit && ( + )}
); @@ -1291,7 +1306,11 @@ export function RoosterPage() { )} - {ghost &&
{ghost.label}
} + {edit && ghost &&
{ghost.label}
}
); } + +// Twee schermen op basis van dezelfde component. +export function RoosterWeergavePage() { return ; } +export function RoostermakerPage() { return ; } diff --git a/frontend/src/styles.css b/frontend/src/styles.css index 91d34a0..523d2b4 100644 --- a/frontend/src/styles.css +++ b/frontend/src/styles.css @@ -147,6 +147,20 @@ input[type="color"] { padding: 2px; width: 44px; min-width: 44px; height: 38px; .drag-ghost { position: fixed; z-index: 1000; pointer-events: none; background: var(--brand); color: #fff; padding: 6px 12px; border-radius: 8px; font-size: 13px; font-weight: 600; box-shadow: 0 6px 18px rgba(0,0,0,.25); } +/* --- Afdrukken --- */ +.print-knop { margin-left: auto; background: #fff; } +@media print { + .sidebar, .no-print, .tabbar, .rooster-balk, .banner, + .rb-greep, .rb-x, .rb-verval, .print-knop, .drag-ghost { display: none !important; } + body { background: #fff; } + .app { display: block; } + .content { padding: 0; } + .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; } +} + /* --- Onderkant zijbalk: subtiel Instellingen-blok --- */ .sidebar-foot { margin-top: auto; padding-top: 10px; border-top: 1px solid var(--line); } .nav-group-head.subtiel { color: var(--muted); opacity: .8; }