From 066fe232505b8215918dba747063813734646c14 Mon Sep 17 00:00:00 2001 From: bes-r <27369668+bes-r@users.noreply.github.com> Date: Thu, 18 Jun 2026 23:32:30 +0200 Subject: [PATCH] =?UTF-8?q?uniforme=20vak-/klasweergave:=20klas-kop=20met?= =?UTF-8?q?=20vak=20eronder,=20=C3=A9=C3=A9n=20kaartontwerp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/pages.jsx | 121 ++++++++++++++++++++-------------------- frontend/src/styles.css | 19 ++++++- 2 files changed, 77 insertions(+), 63 deletions(-) diff --git a/frontend/src/pages.jsx b/frontend/src/pages.jsx index 803ae3f..1f02ac2 100644 --- a/frontend/src/pages.jsx +++ b/frontend/src/pages.jsx @@ -2303,33 +2303,39 @@ export function RoosterScherm({ mode }) { const editorCtx = { activiteiten, locaties, lokalen, groepen, subgroepen, leerlingen, medewerkers, tijdsloten }; const isOpenBlok = (b) => edit && bewerkBlok && bewerkBlok.id === b.id; const toggleOpenBlok = (b) => setBewerkBlok((huidig) => (huidig && huidig.id === b.id ? null : b)); - function renderRoosterBlok(b, iso, kal) { + // Eén uniform vak-kaartje, gebruikt in álle situaties (los vak of vak per + // subgroep) zodat de weergave overal hetzelfde oogt. + function renderVakKaart(b, iso, kal) { const uit = uitzOp(b.id, iso); const vervallen = !!kal || (uit && uit.vervalt); return (
toggleOpenBlok(b) : undefined} title={edit ? "Klik om te bewerken" : undefined} > -
+
{edit && startDrag(e, b)} onClick={(e) => e.stopPropagation()} title="Sleep naar ander tijdslot">⠿} - {b.activiteit_pictogram && }{b.activiteit_icoon || vakIcoon(b.activiteit_naam)} {b.activiteit_naam} + {b.activiteit_pictogram && } + {b.activiteit_icoon || vakIcoon(b.activiteit_naam)} {b.activiteit_naam} {edit && } {edit && }
-
Doel: {b.doel_naam} ({b.doel_type})
- {b.locatie_naam &&
Locatie: {b.locatie_naam}
} - {b.lokaal_naam &&
Lokaal: {b.lokaal_naam}
} - {b.begeleiders_namen.length > 0 && ( -
Begeleiding: {b.begeleiders_namen.map((x, idx) => ( - {idx > 0 ? ", " : ""}{x.naam}{rolAfk(x.id) ? ` (${rolAfk(x.id)})` : ""} - ))}
+ {(b.locatie_naam || b.lokaal_naam || b.begeleiders_namen.length > 0) && ( +
+ {b.locatie_naam && 📍 {b.locatie_naam}} + {b.lokaal_naam && 🚪 {b.lokaal_naam}} + {b.begeleiders_namen.length > 0 && ( + 👤 {b.begeleiders_namen.map((x, idx) => ( + {idx > 0 ? ", " : ""}{x.naam}{rolAfk(x.id) ? ` (${rolAfk(x.id)})` : ""} + ))} + )} +
)} {stageNoot(b)} - {kal &&
Vervalt - {kal.type_label}
} + {kal &&
Vervalt — {kal.type_label}
} {!kal && uit && uit.vervalt &&
Vervalt {uit.opmerking ? `(${uit.opmerking})` : ""}{edit && }
} {!kal && uit && !uit.vervalt && uit.vervanger_naam &&
Vervanger: {uit.vervanger_naam}
} {edit && !kal && !uit && ( @@ -2338,33 +2344,15 @@ export function RoosterScherm({ mode }) {
); } - function renderCompactBlok(b, iso, kal) { - const uit = uitzOp(b.id, iso); - const vervallen = !!kal || (uit && uit.vervalt); - return ( -
toggleOpenBlok(b) : undefined} - title={edit ? "Klik om te bewerken" : undefined} - > -
- {edit && startDrag(e, b)} onClick={(e) => e.stopPropagation()} title="Sleep naar ander tijdslot">⠿} - {b.activiteit_pictogram && }{b.activiteit_icoon || vakIcoon(b.activiteit_naam)} {b.activiteit_naam} - {edit && } - {edit && } -
-
- {b.locatie_naam && {b.locatie_naam}} - {b.lokaal_naam && {b.lokaal_naam}} - {b.begeleiders_namen.length > 0 && {b.begeleiders_namen.map((x) => x.naam).join(", ")}} - {vervallen && Vervalt} -
- {stageNoot(b)} -
- ); - } + // Klas-/doelgroepkop boven de vakken: altijd duidelijk in beeld. + const doelIcoon = (t) => (t === "leerling" ? "🧒" : t === "leerplein" ? "🏫" : "👥"); + const klasKop = (naam, sub, count, icoon) => ( +
+ {icoon} +
{naam}{sub}
+ {count != null && {count}} +
+ ); function renderSlotBlokken(slotBlokken, iso, kal) { const losse = []; const perGroep = new Map(); @@ -2374,27 +2362,38 @@ export function RoosterScherm({ mode }) { if (!perGroep.has(gid)) perGroep.set(gid, []); perGroep.get(gid).push(b); }); - const out = losse.map((b) => renderRoosterBlok(b, iso, kal)); + const out = []; + + // Losse doelen (individuele leerling e.d.): per doel een klas-kop + vakken. + const losPerDoel = new Map(); + losse.forEach((b) => { + const k = `${b.doel_type}:${b.doel_naam}`; + if (!losPerDoel.has(k)) losPerDoel.set(k, []); + losPerDoel.get(k).push(b); + }); + losPerDoel.forEach((blks, k) => { + const eerste = blks[0]; + out.push( +
+ {klasKop(eerste.doel_naam, eerste.doel_type, blks.length > 1 ? blks.length : null, doelIcoon(eerste.doel_type))} +
{blks.map((b) => renderVakKaart(b, iso, kal))}
+
+ ); + }); + if (scopeType === "leerplein" && scopeId && perGroep.size > 0) { const leerplein = leerpleinen.find((l) => l.id === Number(scopeId)); const kolommen = [...perGroep.entries()]; out.push(
-
- -
- {leerplein ? leerplein.naam : BOVENGROEP_LABEL} - {BOVENGROEP_LABEL} → groepen -
- {kolommen.length} -
+ {klasKop(leerplein ? leerplein.naam : BOVENGROEP_LABEL, `${BOVENGROEP_LABEL} → groepen`, kolommen.length, "🏫")}
{kolommen.map(([gid, groepBlokken]) => { const groep = groepen.find((g) => g.id === gid); return (
{groep ? groep.naam : "Groep"}
-
{groepBlokken.map((b) => renderCompactBlok(b, iso, kal))}
+
{groepBlokken.map((b) => renderVakKaart(b, iso, kal))}
); })} @@ -2403,31 +2402,31 @@ export function RoosterScherm({ mode }) { ); return out; } + perGroep.forEach((groepBlokken, gid) => { + const groep = groepen.find((g) => g.id === gid); + const groepNaam = groep ? groep.naam : "Groep"; const heeftSplitsing = groepBlokken.some((b) => b.subgroep); if (!heeftSplitsing) { - groepBlokken.forEach((b) => out.push(renderRoosterBlok(b, iso, kal))); + out.push( +
+ {klasKop(groepNaam, "groep", groepBlokken.length > 1 ? groepBlokken.length : null, "👥")} +
{groepBlokken.map((b) => renderVakKaart(b, iso, kal))}
+
+ ); return; } - const groep = groepen.find((g) => g.id === gid); const heleGroep = groepBlokken.filter((b) => b.groep === gid); const subIds = [...new Set(groepBlokken.filter((b) => b.subgroep).map((b) => b.subgroep))]; const kolomAantal = subIds.length + (heleGroep.length > 0 ? 1 : 0); out.push(
-
- -
- {groep ? groep.naam : "Groep"} - Groep → subgroepen -
- {kolomAantal} -
+ {klasKop(groepNaam, "groep → subgroepen", kolomAantal, "👥")}
{heleGroep.length > 0 && (
Hele groep
-
{heleGroep.map((b) => renderCompactBlok(b, iso, kal))}
+
{heleGroep.map((b) => renderVakKaart(b, iso, kal))}
)} {subIds.map((sid) => { @@ -2436,7 +2435,7 @@ export function RoosterScherm({ mode }) { return (
{sub ? sub.naam : "Subgroep"}
-
{blocks.map((b) => renderCompactBlok(b, iso, kal))}
+
{blocks.map((b) => renderVakKaart(b, iso, kal))}
); })} diff --git a/frontend/src/styles.css b/frontend/src/styles.css index 23658ef..9862ec1 100644 --- a/frontend/src/styles.css +++ b/frontend/src/styles.css @@ -219,6 +219,20 @@ input[type="color"] { padding: 2px; width: 44px; min-width: 44px; height: 38px; .split-vak-kop { display: flex; align-items: center; gap: 5px; } .split-vak-kop strong { min-width: 0; flex: 1; font-size: 12px; } .split-vak-meta { display: flex; flex-direction: column; gap: 1px; color: var(--muted); font-size: 11px; margin-top: 2px; } + +/* Eén uniform vak-kaartje (overal gelijk) + klas-kop-container */ +.blok-splitsing.enkel { background: linear-gradient(180deg, #eff6ff 0, #f8fbff 40px, #fff 100%); } +.blok-splitsing.enkel .split-header { margin-bottom: 8px; } +.vak-kaart { position: relative; border: 1px solid var(--line); border-left: 4px solid var(--brand); border-radius: 7px; background: #fff; padding: 7px 8px; cursor: default; box-shadow: 0 1px 0 rgba(31,41,55,.03); touch-action: none; } +.vak-kaart.klikbaar { cursor: pointer; } +.vak-kaart.klikbaar:hover, .vak-kaart.open { box-shadow: 0 0 0 2px var(--brand-soft); } +.vak-kaart.vervallen { opacity: .55; } +.vak-kaart.vervallen .vk-naam { text-decoration: line-through; } +.vk-kop { display: flex; align-items: center; gap: 5px; } +.vk-naam { min-width: 0; flex: 1; font-size: 13px; overflow: hidden; text-overflow: ellipsis; } +.vk-meta { display: flex; flex-wrap: wrap; gap: 4px 6px; margin-top: 5px; } +.vk-tag { display: inline-flex; align-items: center; gap: 3px; background: var(--bg); color: var(--muted); border-radius: 999px; padding: 2px 8px; font-size: 11px; line-height: 1.5; max-width: 100%; } +@media print { .blok-splitsing.enkel { background: #fff; } } .klas-strip { display: flex; flex-wrap: wrap; gap: 6px; flex: 1 1 auto; min-width: 0; } /* --- Blok-bewerker --- */ @@ -365,7 +379,8 @@ input[type="color"] { padding: 2px; width: 44px; min-width: 44px; height: 38px; /* Stage in het rooster + stagelocatie-beheer */ .rb-stage { color: #b45309; font-weight: 600; } .rooster-blok.vervallen .rb-stage, -.split-vak.vervallen .rb-stage { opacity: .7; } +.split-vak.vervallen .rb-stage, +.vak-kaart.vervallen .rb-stage { opacity: .7; } .subsectie { margin-top: 28px; padding-top: 18px; border-top: 1px solid var(--line); } .subsectie h2 { margin: 0 0 4px; font-size: 18px; } .veld-kol { display: flex; flex-direction: column; gap: 4px; flex: 1; min-width: 120px; } @@ -535,4 +550,4 @@ input[type="color"] { padding: 2px; width: 44px; min-width: 44px; height: 38px; .conflict-lijst li { padding: 4px 0 4px 16px; position: relative; color: #7f1d1d; font-size: 14px; } .conflict-lijst li::before { content: "•"; position: absolute; left: 2px; } .conflict-lijst li.waarschuwing { color: #92400e; } -.rooster-blok.in-conflict, .split-vak.in-conflict { outline: 2px solid #ef4444; outline-offset: 1px; box-shadow: 0 0 0 4px rgba(239,68,68,.15); } +.rooster-blok.in-conflict, .split-vak.in-conflict, .vak-kaart.in-conflict { outline: 2px solid #ef4444; outline-offset: 1px; box-shadow: 0 0 0 4px rgba(239,68,68,.15); }