Verfijn rooster naar minimalistische opmaak (v0.2.01 beta)
Some checks are pending
CI / backend (push) Waiting to run
CI / frontend (push) Waiting to run

Roostermaker en roosterweergave hebben een rustiger, moderne opmaak
gekregen zonder informatie te verliezen.

- Minder geneste kaders: kolom, tijdvak, splitsing en vakkaart stapelden
  vier randen; nu dragen alleen de dagkolom en de vakkaart een rand en
  scheiden de rest met witruimte en haarlijnen.
- Vakkaart groeit mee met de inhoud in plaats van een vaste hoogte, zodat
  lokaal, begeleiding en opmerkingen niet meer afgekapt worden.
- Meta-regel (locatie, lokaal, begeleiders) zonder pil-achtergronden;
  lange vaknamen lopen door over twee regels met tooltip.
- Bewerkknoppen (vervallen, bewerken, verwijderen) staan gebundeld
  rechtsboven op de kaart en verschijnen bij hover, zodat de vaknaam de
  volle breedte houdt; op aanraakschermen blijven ze zichtbaar.
- Tijdvak toont nu begin- en eindtijd; lege tijdvakken in de weergave
  krijgen een subtiele markering.
- Dagkop is compacter en markeert "vandaag"; dagacties verschijnen bij
  hover. Diagonale arcering bij vrije dagen vervangen door een getinte
  kolom met dagtype-label.
- Subgroep-vertakking en doelgroepkop in neutrale haarlijnen in plaats
  van blauwe kaders.
- Conflicten met een rode rand in plaats van een harde binnenschaduw;
  bij volledig gekleurde vakken blijft de vakkleur staan met rode ring.
- Toolbar, weeknavigatie, detailkeuze en scopebalk strakker uitgelijnd.
- Afdrukken meegenomen: geen sticky dagkop, neutrale randen, en bij
  "vakken in kleur" kleuren lokaal en begeleiding mee.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FAQ4Np13v8fwbTtKHKnwDz
This commit is contained in:
Ramon 2026-08-13 09:16:22 +02:00
parent c5e19eca6a
commit 4f0bc009bb
5 changed files with 213 additions and 14 deletions

View file

@ -4,4 +4,10 @@ De oorspronkelijke vormgeving staat ongewijzigd in `src/styles.css` en is vastge
De moderne vormgeving wordt afzonderlijk geladen uit `src/modern.css`. Verwijder de import van `./modern.css` uit `src/main.jsx` om tijdelijk terug te keren.
Versie vóór modernisering: `v0.1.0`. Moderne dev-versie: `v0.2.00 beta`.
Versie vóór modernisering: `v0.1.0`. Moderne dev-versie: `v0.2.01 beta`.
## v0.2.01 beta — rooster
De roostermaker en de roosterweergave hebben een rustiger, minimalistische
opmaak gekregen (onderaan in `src/modern.css`, sectie "Roostermaker &
roosterweergave"). De legacy-opmaak in `src/styles.css` is ongewijzigd.

View file

@ -1,12 +1,12 @@
{
"name": "roosterwijs-frontend",
"version": "0.2.0-beta",
"version": "0.2.1-beta",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "roosterwijs-frontend",
"version": "0.2.0-beta",
"version": "0.2.1-beta",
"dependencies": {
"react": "^18.3.1",
"react-dom": "^18.3.1"

View file

@ -1,7 +1,7 @@
{
"name": "roosterwijs-frontend",
"private": true,
"version": "0.2.0-beta",
"version": "0.2.1-beta",
"type": "module",
"scripts": {
"dev": "vite",

View file

@ -35,7 +35,7 @@ button,a,input,select{transition:border-color .15s,background-color .15s,color .
.cog-btn{width:100%;min-height:38px;justify-content:flex-start;font-size:15px;padding:0}
.cog-btn::after{content:"Beheer en instellingen";margin-left:8px;font-size:13px}
.cog-dropdown{position:static;min-width:0;margin:4px 0 0;box-shadow:none}
.topbar::after{content:"v0.2.00 beta";order:4;margin:10px 8px 0;color:#98a2b3;font-size:10px}
.topbar::after{content:"v0.2.01 beta";order:4;margin:10px 8px 0;color:#98a2b3;font-size:10px}
.content{margin-left:var(--sidebar);width:calc(100% - var(--sidebar));max-width:1680px;padding:32px 38px 48px}
.content-wide{padding:24px 28px 42px}
h1{font-size:clamp(24px,2.4vw,30px);line-height:1.2;letter-spacing:-.025em;font-weight:750}
@ -73,3 +73,188 @@ input:focus,select:focus,textarea:focus{outline:3px solid rgba(36,87,214,.13);bo
}
@media print{.content,.content-wide{margin:0;width:100%;padding:0}}
@media(prefers-reduced-motion:reduce){*,*::before,*::after{transition-duration:.01ms!important;animation-duration:.01ms!important}}
/* ===================================================================
Roostermaker & roosterweergave rustig, minimalistisch, met detail.
Uitgangspunt: minder geneste kaders. Vroeger stapelden kolom tijdvak
splitsing vakkaart vier randen op elkaar; nu dragen alleen de
dagkolom en de vakkaart een rand, de rest scheidt met witruimte en
haarlijnen. Vakkleur en conflictsignalen blijven leidend.
=================================================================== */
:root{--rw-hair:#eef1f7;--rw-rand:#e6eaf2}
/* --- Kop, weeknavigatie en weergaveopties ------------------------- */
.rooster-kop{align-items:flex-start;margin-bottom:16px}
.rooster-scherm .page-subtitle{font-size:13px;color:var(--muted)}
.rooster-toolbar{gap:12px 16px;padding:10px 12px;border-radius:var(--radius)}
.rooster-scherm .veld{font-size:12px;font-weight:650;color:#98a2b3;letter-spacing:.01em}
.week-nav{gap:2px;padding:3px;background:#f4f6fb;border:1px solid var(--rw-rand);border-radius:10px}
.week-nav .btn.icon{width:32px;min-height:32px;padding:0;border:0;background:transparent;color:var(--muted);font-size:17px;border-radius:7px}
.week-nav .btn.icon:hover{background:#fff;border:0;color:var(--brand);box-shadow:0 1px 2px rgba(23,32,51,.1)}
.week-label{min-width:152px;font-size:13px;font-weight:650;color:var(--ink);font-variant-numeric:tabular-nums}
.week-nav .link.small{margin-left:4px;padding:0 8px;font-size:12px;font-weight:600}
.weergave-opties{gap:5px}
.icon-toggle{width:36px;height:36px;border-radius:9px;font-size:15px}
.icon-toggle:hover{background:#f5f7fb}
.icon-toggle.on{background:var(--brand-soft);color:var(--brand);border-color:#c8d5f8}
.segmented{padding:2px;gap:2px;background:#f4f6fb;border-color:var(--rw-rand);border-radius:10px;overflow:visible}
.detail-keuze .seg{padding:6px 11px;border:0;border-radius:7px;background:transparent;color:var(--muted);font-size:12px;font-weight:650}
.detail-keuze .seg:hover{color:var(--ink)}
.detail-keuze .seg.on{background:#fff;color:var(--ink);box-shadow:0 1px 2px rgba(23,32,51,.1)}
.sneltoets-hint{margin:-2px 0 10px;color:#a4adbd;font-size:11px}
/* --- Scopebalk: filteren op klas, leerplein of persoon -------------
Geen eigen paneel: de chips dragen zelf al hun vorm, dus alleen lucht. */
.rooster-scherm .scope-balk{gap:4px 20px;margin:10px 0 0;padding:0 2px;background:transparent;border:0;box-shadow:none}
.scope-label{font-size:10px;letter-spacing:.1em;color:#a4adbd}
.scope-balk .chip{min-height:30px;padding:5px 11px;border-radius:8px;font-size:12px;color:#475467}
.scope-balk .chip:hover{background:#f5f7fb;border-color:#ced6e2}
.scope-balk .chip.on{background:var(--brand-soft);border-color:#b9c9f5;color:var(--brand);font-weight:700}
.scope-persoon{min-height:36px;padding:6px 10px;font-size:13px}
.scope-aantal{font-size:12px}
/* --- Tabbalk (alleen roostermaker) -------------------------------- */
.tabbar{gap:2px;margin:16px 0;border-bottom-color:var(--rw-rand)}
.tab{padding:9px 13px;border-bottom-width:2px;border-radius:8px 8px 0 0;font-size:13.5px;font-weight:600}
.tab:hover{background:#f5f7fb}
.tab.on{background:transparent;border-bottom-color:var(--brand)}
/* --- Weekraster: dagkolommen -------------------------------------- */
.rooster-grid{gap:12px;margin-top:16px}
.rooster-kolom{padding:0 0 6px;background:#fff;border:1px solid var(--rw-rand);border-radius:var(--radius);min-height:120px}
.rooster-kolom.vrij{background:#fcfcfd}
.rooster-dag{position:sticky;top:0;z-index:3;display:flex;align-items:baseline;flex-wrap:wrap;gap:2px 7px;min-height:0;margin:0;padding:11px 74px 10px 13px;background:#fff;border-bottom:1px solid var(--rw-hair);border-radius:calc(var(--radius) - 1px) calc(var(--radius) - 1px) 0 0;font-size:13px;font-weight:700;letter-spacing:-.01em;color:var(--ink)}
.dag-datum{font-size:12px;font-weight:600;color:#98a2b3;font-variant-numeric:tabular-nums}
.rooster-dag .tag{flex:0 0 100%;margin-top:5px;font-size:11px}
/* Vandaag: rustig gemarkeerd, niet schreeuwerig. */
.rooster-kolom.vandaag{border-color:#c4d3f8}
.rooster-kolom.vandaag .rooster-dag{background:var(--brand-soft);border-bottom-color:#d3dffb;color:var(--brand)}
.rooster-kolom.vandaag .dag-datum{color:var(--brand);opacity:.72}
/* Dagacties (vervallen/leegmaken/eenmalig) blijven uit beeld tot je erbij bent. */
.dag-acties{top:8px;right:8px;gap:1px;background:transparent;opacity:0;transition:opacity .15s}
.rooster-kolom:hover .dag-acties,.dag-acties:focus-within{opacity:1}
.dag-acties .icon-btn{width:24px;height:24px;border-radius:7px;font-size:12px}
/* --- Tijdvakken: scheiden met een haarlijn, geen doos-in-doos ------ */
.slot-cel{margin:0;padding:10px 12px 12px;background:transparent;border:0;border-top:1px solid var(--rw-hair);border-radius:0}
.rooster-dag+.slot-cel{border-top:0}
.slot-cel.hover{background:var(--brand-soft);box-shadow:inset 0 0 0 1px #c8d5f8}
.slot-tijd{display:flex;align-items:baseline;gap:7px;max-width:100%;margin-bottom:7px;padding:0;background:transparent;border:0;border-radius:0;font-size:11px;font-weight:700;overflow:visible}
.st-tijd{color:#475467;letter-spacing:.02em;font-variant-numeric:tabular-nums;white-space:nowrap}
.st-naam{min-width:0;color:#a4adbd;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.slot-leeg{color:#d3d9e4;font-size:12px;line-height:1}
.slot-add{margin-top:8px;padding:6px;border:1px dashed #dfe4ee;border-radius:9px;color:#a4adbd;font-size:12px;font-weight:600;opacity:0}
.rooster-edit .slot-add{padding:6px;opacity:0}
.slot-cel:hover .slot-add,.rooster-edit .slot-cel:hover .slot-add,.slot-add:focus-visible{opacity:1}
.slot-add:hover{border-color:#b9c9f5;background:var(--brand-soft);color:var(--brand)}
/* Vrije dag: de getinte kolom en het dagtype-label zijn het signaal
de oude diagonale arcering was alleen maar onrustig. */
.rooster-kolom.vrij .slot-cel{opacity:.5}
.slot-cel[data-vrij]{background:transparent}
/* --- Doelgroep-kop boven de vakken (klas / leerling / leerplein) --- */
.blok-splitsing,.blok-splitsing.enkel{margin:0 0 12px;padding:0;background:transparent;border:0;border-radius:0}
.slot-cel .blok-splitsing:last-of-type{margin-bottom:0}
.split-header{gap:6px;margin-bottom:7px;padding:0 0 5px;background:transparent;border:0;border-bottom:1px solid var(--rw-hair);border-radius:0}
.split-icon{width:16px;height:16px;background:transparent;color:inherit;font-size:12px;filter:grayscale(1);opacity:.7}
.split-title strong{font-size:12px;font-weight:700;color:var(--ink)}
.split-title span{font-size:10px;color:#a4adbd;letter-spacing:.02em}
.split-count{min-width:19px;height:19px;background:#f1f4fa;color:#667085;border-radius:6px;font-size:11px;font-weight:700}
/* Vertakking naar subgroepen: neutrale haarlijnen in plaats van blauwe balken. */
.split-kolommen{gap:10px;margin-left:5px;padding:8px 0 0 13px;border-left:1px solid var(--rw-rand)}
.split-kolommen::before{left:-1px;height:8px;border-left:1px solid var(--rw-rand)}
.split-kolom::before{top:11px;left:-13px;width:13px;border-top:1px solid var(--rw-rand)}
.split-node-kop{margin-bottom:5px;padding:0 0 3px;background:transparent;border:0;color:#667085;font-size:11px;font-weight:700}
.split-vakken{gap:7px;min-width:0}
.split-vakken.meerdere-vakken{gap:7px;padding-left:0;border-left:0}
.split-vakken.meerdere-vakken .vak-kaart{border-radius:10px}
/* --- De vakkaart: het enige echte kaartje in het rooster ---------- */
/* min-width:0 is nodig: als grid-item zou de kaart anders niet onder zijn
min-content-breedte krimpen en over de kolomrand heen lopen. */
.vak-kaart{height:auto;min-height:0;min-width:0;gap:5px;padding:9px 11px;border:1px solid var(--rw-rand);border-left:3px solid var(--brand);border-radius:10px;box-shadow:none;overflow:visible}
.vak-kaart.klikbaar:hover{border-color:#ced6e2;border-left-color:var(--vk-kleur);box-shadow:0 4px 14px rgba(23,32,51,.09)}
.vak-kaart.open{border-color:var(--brand);border-left-color:var(--brand);box-shadow:0 0 0 3px rgba(36,87,214,.13)}
.vak-kaart.vervallen{opacity:1;background:#fbfbfc;border-color:var(--rw-hair);border-left-color:#d3d9e4}
.vak-kaart.vervallen .vk-naam,.vak-kaart.vervallen .vk-meta{color:#98a2b3}
.vk-kop{gap:6px;align-items:flex-start}
/* Lange vaknamen mogen over twee regels: in smalle subgroepkolommen is
afkappen op één regel te veel informatieverlies. */
.vk-naam{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;font-size:13px;font-weight:650;letter-spacing:-.01em;line-height:1.32;color:var(--ink);white-space:normal;overflow-wrap:break-word;overflow:hidden}
.detail-compact .vk-naam{-webkit-line-clamp:1}
/* Meta zonder pil-achtergronden: het icoon groepeert al, witruimte scheidt. */
.vk-meta{align-items:center;gap:3px 10px;margin-top:1px;overflow:visible}
.vk-tag{gap:4px;padding:0;background:transparent;border-radius:0;color:#667085;font-size:11px;font-weight:550;line-height:1.45}
.vk-opmerking{margin-top:3px;padding-top:0;color:#98a2b3;font-size:11px}
.rb-stage{color:var(--warn);font-size:11px}
.rb-status,.rb-verv{font-size:11px}
.rb-status{color:var(--danger)}
.rb-verv{color:var(--on)}
/* Bewerkacties zweven rechtsboven en verschijnen bij hover: zo blijft de
volle kaartbreedte voor de vaknaam, ook in smalle subgroepkolommen. */
.vk-acties{position:absolute;top:6px;right:6px;z-index:2;display:inline-flex;gap:1px;padding:2px;background:#fff;border:1px solid var(--rw-rand);border-radius:8px;box-shadow:0 2px 8px rgba(23,32,51,.13);opacity:0;pointer-events:none;transition:opacity .12s}
.vak-kaart:hover .vk-acties,.vak-kaart:focus-within .vk-acties,.vak-kaart.open .vk-acties{opacity:1;pointer-events:auto}
.vk-acties .icon-btn{width:24px;height:24px;border-radius:6px;font-size:13px}
.rb-greep{padding:0 1px;font-size:14px;opacity:0;transition:opacity .12s}
.vak-kaart:hover .rb-greep,.vak-kaart.open .rb-greep{opacity:.6}
.rb-status .icon-btn{opacity:1}
/* Volledig gekleurde variant blijft leesbaar zonder extra randen. */
.vak-kaart.kleur-vlak{border-color:transparent;border-left-color:rgba(0,0,0,.16);box-shadow:inset 0 0 0 1px rgba(0,0,0,.05)}
.vak-kaart.kleur-vlak .vk-tag,.vak-kaart.kleur-vlak .vk-opmerking,.vak-kaart.kleur-vlak .rb-stage{color:inherit;opacity:.85}
.vak-kaart.kleur-vlak.vervallen{opacity:.45}
.vak-kaart.kleur-vlak.vervallen .vk-naam,.vak-kaart.kleur-vlak.vervallen .vk-meta{color:inherit}
/* --- Detailniveaus ------------------------------------------------ */
.rooster-view.details-compact .vak-kaart,.detail-uitgebreid .vak-kaart{height:auto;min-height:0}
.rooster-view.details-compact .vak-kaart{padding:7px 11px}
.rooster-view.details-compact .blok-splitsing{background:transparent;border:0}
.rooster-view.details-compact .split-header{background:transparent;margin-bottom:6px}
.details-compact .split-vakken,.details-compact .split-kolommen{gap:6px}
.detail-uitgebreid .vak-kaart{padding:10px 12px}
/* --- Conflicten: duidelijk, maar zonder rode schreeuw -------------- */
.conflict-banner{margin-bottom:16px;border-color:#f3c2c7;border-radius:var(--radius)}
.conflict-kop{padding:11px 14px;color:#a4303f;font-weight:600}
.conflict-lijst li{font-size:13px;color:#8f3540}
.rooster-blok.in-conflict,.split-vak.in-conflict,.vak-kaart.in-conflict{box-shadow:none;background:#fffafa;border-color:#eda9b0;border-left-color:var(--danger)}
.rooster-view .vak-kaart.in-conflict::after{content:"⚠";position:absolute;top:8px;right:9px;color:var(--danger);font-size:11px;line-height:1}
.rooster-view .vak-kaart.in-conflict .vk-naam{padding-right:14px}
/* Bij een volledig gekleurd vak blijft de vakkleur staan; het conflict
wordt dan met een rode ring gemarkeerd in plaats van met een vlak. */
.vak-kaart.kleur-vlak.in-conflict{background:var(--vk-kleur);border-color:var(--danger);border-left-color:var(--danger);box-shadow:0 0 0 2px rgba(200,62,77,.4)}
.rooster-view .vak-kaart.kleur-vlak.in-conflict::after{color:inherit}
/* --- Sleep-etiket + modaal ---------------------------------------- */
.drag-ghost{padding:7px 13px;border-radius:9px;font-size:12.5px;box-shadow:0 10px 26px rgba(23,32,51,.28)}
.modal{border-top-width:1px;border-color:var(--line);border-radius:16px;box-shadow:0 26px 70px rgba(23,32,51,.22)}
.modal-kop h2{font-size:17px;letter-spacing:-.02em}
.modal-editor{background:#fbfcfe;border-color:var(--rw-rand);border-radius:12px}
.editor-detail summary{font-size:12px;letter-spacing:.01em}
/* --- "Dagen onder elkaar": tijdvakken krijgen daar wél een kader --- */
.rooster-grid.lijst{gap:14px}
.rooster-grid.lijst .rooster-kolom{padding:0 12px 12px;gap:10px}
.rooster-grid.lijst .rooster-dag{margin:0 -12px 2px}
.rooster-grid.lijst .slot-cel{padding:9px 11px 11px;border:1px solid var(--rw-hair);border-radius:11px}
/* --- Aanraakschermen: verborgen acties altijd tonen ----------------
Zonder hover moet alles bereikbaar zijn; de actieknoppen gaan dan
terug in de regel in plaats van over de vaknaam heen te zweven. */
@media(hover:none){
.rb-greep,.dag-acties,.slot-add,.rooster-edit .slot-add{opacity:1}
.vk-acties{position:static;margin-left:auto;padding:0;background:transparent;border:0;box-shadow:none;opacity:1;pointer-events:auto}
}
@media(max-width:900px){
.rooster-toolbar{gap:10px}.week-label{min-width:0;flex:1}
.rooster-kolom{min-height:0}
}
@media print{
.rooster-kolom,.rooster-grid.lijst .slot-cel{border-color:#bbb}
.rooster-dag{position:static;border-bottom-color:#bbb;padding-right:13px}
.rooster-kolom.vandaag,.rooster-kolom.vandaag .rooster-dag{border-color:#bbb;background:#fff;color:#000}
.slot-cel{border-top-color:#ddd}
.vak-kaart{break-inside:avoid;box-shadow:none}
.slot-leeg,.slot-add{display:none!important}
/* Bij "vakken in kleur" moeten ook lokaal/begeleiding meekleuren. */
body.print-kleur .vak-kaart .vk-tag,body.print-kleur .vak-kaart .vk-opmerking,body.print-kleur .vak-kaart .rb-stage{color:inherit!important}
}

View file

@ -2151,6 +2151,7 @@ export function RoosterScherm({ mode }) {
const weekDatums = [0, 1, 2, 3, 4].map((i) => plusDagen(maandag, i));
const vanISO = fmtISO(maandag);
const totISO = fmtISO(plusDagen(maandag, 6));
const vandaagISO = fmtISO(new Date());
async function laadBlokken() {
if (!schooljaar) { setBlokken([]); return; }
@ -2417,9 +2418,14 @@ export function RoosterScherm({ mode }) {
<div className="vk-kop">
{edit && <span className="rb-greep" onPointerDown={(e) => startDrag(e, b)} onClick={(e) => e.stopPropagation()} title="Sleep naar ander tijdslot"></span>}
{b.activiteit_pictogram && <img src={b.activiteit_pictogram} alt="" className="rb-picto" />}
<strong className="vk-naam">{b.activiteit_icoon || vakIcoon(b.activiteit_naam)} {b.activiteit_naam}</strong>
{edit && <button type="button" className="icon-btn" onClick={(e) => { e.stopPropagation(); toggleOpenBlok(b); }} title="Bewerken"></button>}
{edit && <button type="button" className="icon-btn danger rb-x" onClick={(e) => { e.stopPropagation(); verwijder(b.id); }} title="Blok verwijderen">×</button>}
<strong className="vk-naam" title={b.activiteit_naam}>{b.activiteit_icoon || vakIcoon(b.activiteit_naam)} {b.activiteit_naam}</strong>
{edit && (
<span className="vk-acties no-print">
{!kal && !uit && <button type="button" className="icon-btn rb-verval" onClick={(e) => { e.stopPropagation(); laatVervallen(b, iso); }} title="Dit vak laten vervallen"></button>}
<button type="button" className="icon-btn" onClick={(e) => { e.stopPropagation(); toggleOpenBlok(b); }} title="Bewerken"></button>
<button type="button" className="icon-btn danger rb-x" onClick={(e) => { e.stopPropagation(); verwijder(b.id); }} title="Blok verwijderen">×</button>
</span>
)}
</div>
{(b.locatie_naam || b.lokaal_naam || b.begeleiders_namen.length > 0) && (
<div className="vk-meta">
@ -2437,9 +2443,6 @@ export function RoosterScherm({ mode }) {
{kal && <div className="small rb-status">Vervalt {kal.type_label}</div>}
{!kal && uit && uit.vervalt && <div className="small rb-status">Vervalt {uit.opmerking ? `(${uit.opmerking})` : ""}{edit && <button type="button" className="icon-btn" title="Herstellen" onClick={(e) => { e.stopPropagation(); herstel(uit.id); }}></button>}</div>}
{!kal && uit && !uit.vervalt && uit.vervanger_naam && <div className="small rb-verv">Vervanger: {uit.vervanger_naam}</div>}
{edit && !kal && !uit && (
<button type="button" className="icon-btn danger rb-verval no-print" title="Dit vak laten vervallen" onClick={(e) => { e.stopPropagation(); laatVervallen(b, iso); }}></button>
)}
</div>
);
}
@ -2783,9 +2786,10 @@ export function RoosterScherm({ mode }) {
const kal = kalenderOp(iso);
const slots = tijdsloten.filter((t) => t.dag === d.value).sort((a, b) => a.volgorde - b.volgorde);
return (
<div className={kal ? "rooster-kolom vrij" : "rooster-kolom"} key={d.value}>
<div className={"rooster-kolom" + (kal ? " vrij" : "") + (iso === vandaagISO ? " vandaag" : "")} key={d.value}>
<div className="rooster-dag">
{d.label} <span className="muted">{fmtDM(datum)}</span>
<span className="dag-naam">{d.label}</span>
<span className="dag-datum">{fmtDM(datum)}</span>
{kal && <div className={`tag dag-${kal.type}`}>{kal.type_label}{kal.omschrijving ? `: ${kal.omschrijving}` : ""}</div>}
{edit && (
<span className="dag-acties no-print">
@ -2811,8 +2815,12 @@ export function RoosterScherm({ mode }) {
data-vrij={kal ? "1" : undefined}
key={t.id}
>
<div className="slot-tijd">{t.start_tijd?.slice(0, 5)} {t.naam}</div>
<div className="slot-tijd">
<span className="st-tijd">{t.start_tijd?.slice(0, 5)}{t.eind_tijd ? `${t.eind_tijd.slice(0, 5)}` : ""}</span>
{t.naam && <span className="st-naam">{t.naam}</span>}
</div>
{renderSlotBlokken(slotBlokken, iso, kal)}
{!edit && slotBlokken.length === 0 && <div className="slot-leeg"></div>}
{edit && !kal && (
<button type="button" className="slot-add no-print" title="Vak toevoegen in dit tijdvak" onClick={() => openNieuwBlokVoorSlot(t.id)}> vak</button>
)}