Compare commits
2 commits
c845588e5e
...
4459f60019
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4459f60019 | ||
|
|
958ace1536 |
3 changed files with 122 additions and 59 deletions
|
|
@ -23,6 +23,22 @@ const GROEP_ICON = { Rooster: "calendar", Organisatie: "users", Overzicht: "cloc
|
|||
const GROEPSINDELING_PADEN = new Set(["/groepsindeling", "/structuur", "/leerplein", "/groepen", "/subgroepen"]);
|
||||
const GROEPSINDELING_ITEM = { label: "Groepsindeling", path: "/groepsindeling", icon: "layers", order: 24, group: "Organisatie" };
|
||||
const normaliseerPad = (path) => (GROEPSINDELING_PADEN.has(path) ? "/groepsindeling" : path);
|
||||
const ROLE_NAV = {
|
||||
roostermaker: new Set(["/rooster", "/perioderooster", "/roostermaker", "/vervanging", "/afwezigheid", "/auto-rooster", "/vak-wizard"]),
|
||||
directie: null,
|
||||
zorgcoordinator: new Set(["/rooster", "/groepsindeling", "/personen", "/afwezigheid", "/stage", "/uren", "/portaal"]),
|
||||
stagebegeleider: new Set(["/rooster", "/stage", "/afwezigheid", "/portaal"]),
|
||||
medewerker: new Set(["/rooster", "/groepsindeling", "/afwezigheid", "/stage", "/uren", "/portaal"]),
|
||||
kijker: new Set(["/rooster", "/uren", "/portaal"]),
|
||||
ouder: new Set(["/portaal", "/ouderrooster"]),
|
||||
leerling: new Set(["/portaal", "/leerlingrooster"]),
|
||||
};
|
||||
function magMenuZien(user, item) {
|
||||
if (user.is_staff || user.role === "beheerder" || user.role === "directie") return true;
|
||||
const allowed = ROLE_NAV[user.role];
|
||||
if (!allowed) return true;
|
||||
return allowed.has(normaliseerPad(item.path));
|
||||
}
|
||||
|
||||
// Koppel een menu-pad aan een echt scherm. Onbekende paden tonen een
|
||||
// placeholder (worden in een latere fase ingevuld).
|
||||
|
|
@ -261,16 +277,19 @@ function Shell({ user, onLogout }) {
|
|||
if (isBeheerder) {
|
||||
voegToe({ label: "Modulebeheer", path: "/modules", icon: "gear", order: 9999, group: "Instellingen" });
|
||||
}
|
||||
const items = [...perPad.values()];
|
||||
const items = [...perPad.values()].filter((item) => magMenuZien(user, item));
|
||||
items.sort((a, b) => a.order - b.order);
|
||||
return {
|
||||
tabs: items.filter((i) => i.group !== "Instellingen"),
|
||||
instellingenTabs: items.filter((i) => i.group === "Instellingen"),
|
||||
alleItems: items,
|
||||
};
|
||||
}, [modules, user.is_staff, user.role]);
|
||||
}, [modules, user]);
|
||||
|
||||
const activePad = normaliseerPad(active);
|
||||
const eerstePad = alleItems[0]?.path || "/rooster";
|
||||
const activePad = normaliseerPad(
|
||||
[...alleItems].some((item) => normaliseerPad(item.path) === normaliseerPad(active)) ? active : eerstePad
|
||||
);
|
||||
const Page = PAGES[activePad];
|
||||
|
||||
// Groepeer de hoofdtabs: groepen met meerdere items worden een dropdown,
|
||||
|
|
@ -391,7 +410,7 @@ function Shell({ user, onLogout }) {
|
|||
</nav>
|
||||
</header>
|
||||
|
||||
<main className={activePad === "/roostermaker" ? "content content-wide" : "content"}>
|
||||
<main className={activePad === "/roostermaker" || activePad === "/rooster" ? "content content-wide" : "content"}>
|
||||
{error && <div className="banner error">⚠ {error}</div>}
|
||||
{loading ? (
|
||||
<p>Laden…</p>
|
||||
|
|
|
|||
|
|
@ -2087,6 +2087,8 @@ 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 */ } };
|
||||
const [detailsOpen, setDetailsOpenState] = useState(() => { try { return localStorage.getItem("rw_rooster_details") === "1"; } catch { return false; } });
|
||||
const setDetailsOpen = (v) => { setDetailsOpenState(v); try { localStorage.setItem("rw_rooster_details", v ? "1" : "0"); } catch { /* niet beschikbaar */ } };
|
||||
// Printopties
|
||||
const [printOpen, setPrintOpen] = useState(false);
|
||||
const [printPending, setPrintPending] = useState(false);
|
||||
|
|
@ -2409,7 +2411,7 @@ export function RoosterScherm({ mode }) {
|
|||
{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>}
|
||||
</div>
|
||||
{(b.locatie_naam || b.lokaal_naam || b.begeleiders_namen.length > 0) && (
|
||||
{(edit || detailsOpen) && (b.locatie_naam || b.lokaal_naam || b.begeleiders_namen.length > 0) && (
|
||||
<div className="vk-meta">
|
||||
{b.locatie_naam && <span className="vk-tag t-loc">📍 {b.locatie_naam}</span>}
|
||||
{b.lokaal_naam && <span className="vk-tag t-lok">🚪 {b.lokaal_naam}</span>}
|
||||
|
|
@ -2420,7 +2422,7 @@ export function RoosterScherm({ mode }) {
|
|||
)}
|
||||
</div>
|
||||
)}
|
||||
{stageNoot(b)}
|
||||
{(edit || detailsOpen) && stageNoot(b)}
|
||||
{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>}
|
||||
|
|
@ -2462,7 +2464,7 @@ export function RoosterScherm({ mode }) {
|
|||
out.push(
|
||||
<div className="blok-splitsing enkel" key={`los-${k}`}>
|
||||
{klasKop(eerste.doel_naam, eerste.doel_type, blks.length > 1 ? blks.length : null, doelIcoon(eerste.doel_type))}
|
||||
<div className="split-vakken">{blks.map((b) => renderVakKaart(b, iso, kal))}</div>
|
||||
<div className={blks.length > 1 ? "split-vakken meerdere-vakken" : "split-vakken"}>{blks.map((b) => renderVakKaart(b, iso, kal))}</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
|
@ -2479,7 +2481,7 @@ export function RoosterScherm({ mode }) {
|
|||
return (
|
||||
<div className="split-kolom" key={gid}>
|
||||
<div className="split-kop split-node-kop">{groep ? groep.naam : "Groep"}</div>
|
||||
<div className="split-vakken">{groepBlokken.map((b) => renderVakKaart(b, iso, kal))}</div>
|
||||
<div className={groepBlokken.length > 1 ? "split-vakken meerdere-vakken" : "split-vakken"}>{groepBlokken.map((b) => renderVakKaart(b, iso, kal))}</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
|
@ -2497,7 +2499,7 @@ export function RoosterScherm({ mode }) {
|
|||
out.push(
|
||||
<div className="blok-splitsing enkel" key={`groep-${gid}`}>
|
||||
{klasKop(groepNaam, "groep", groepBlokken.length > 1 ? groepBlokken.length : null, "👥")}
|
||||
<div className="split-vakken">{groepBlokken.map((b) => renderVakKaart(b, iso, kal))}</div>
|
||||
<div className={groepBlokken.length > 1 ? "split-vakken meerdere-vakken" : "split-vakken"}>{groepBlokken.map((b) => renderVakKaart(b, iso, kal))}</div>
|
||||
</div>
|
||||
);
|
||||
return;
|
||||
|
|
@ -2512,7 +2514,7 @@ export function RoosterScherm({ mode }) {
|
|||
{heleGroep.length > 0 && (
|
||||
<div className="split-kolom">
|
||||
<div className="split-kop split-node-kop">Hele groep</div>
|
||||
<div className="split-vakken">{heleGroep.map((b) => renderVakKaart(b, iso, kal))}</div>
|
||||
<div className={heleGroep.length > 1 ? "split-vakken meerdere-vakken" : "split-vakken"}>{heleGroep.map((b) => renderVakKaart(b, iso, kal))}</div>
|
||||
</div>
|
||||
)}
|
||||
{subIds.map((sid) => {
|
||||
|
|
@ -2521,7 +2523,7 @@ export function RoosterScherm({ mode }) {
|
|||
return (
|
||||
<div className="split-kolom" key={sid}>
|
||||
<div className="split-kop split-node-kop">{sub ? sub.naam : "Subgroep"}</div>
|
||||
<div className="split-vakken">{blocks.map((b) => renderVakKaart(b, iso, kal))}</div>
|
||||
<div className={blocks.length > 1 ? "split-vakken meerdere-vakken" : "split-vakken"}>{blocks.map((b) => renderVakKaart(b, iso, kal))}</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
|
@ -2533,7 +2535,7 @@ export function RoosterScherm({ mode }) {
|
|||
}
|
||||
|
||||
return (
|
||||
<section className="rooster-scherm">
|
||||
<section className={"rooster-scherm " + (edit ? "rooster-edit " : "rooster-view ") + (detailsOpen ? "details-open" : "details-compact")}>
|
||||
<div className="print-only print-kop">
|
||||
<RoosterLogo print />
|
||||
<div className="print-kop-tekst">
|
||||
|
|
@ -2541,18 +2543,19 @@ export function RoosterScherm({ mode }) {
|
|||
<span>{(schooljaren.find((j) => String(j.id) === String(schooljaar)) || {}).naam || ""}{schooljaar ? " · " : ""}{schooljaar ? weekTitel(maandag) : ""} · afgedrukt {fmtDM(new Date())}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="rooster-kop">
|
||||
<h1>{edit ? "Roostermaker" : "Rooster"}</h1>
|
||||
<div className="rooster-kop app-page-kop">
|
||||
<div>
|
||||
<h1>{edit ? "Roostermaker" : "Rooster"}</h1>
|
||||
<div className="page-subtitle no-print">
|
||||
{schooljaar ? `${weekTitel(maandag)} · ${zichtbareBlokken.length} blokken zichtbaar` : "Kies een schooljaar"}
|
||||
{conflictInfo.meldingen.length > 0 ? ` · ${conflictInfo.meldingen.length} conflicten` : ""}
|
||||
</div>
|
||||
</div>
|
||||
<RoosterLogo />
|
||||
</div>
|
||||
<p className="muted no-print">
|
||||
{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."}
|
||||
</p>
|
||||
<Foutmelding msg={error} />
|
||||
|
||||
<div className="rooster-balk no-print">
|
||||
<div className="rooster-balk rooster-toolbar no-print">
|
||||
<label className="veld">Schooljaar
|
||||
<select value={schooljaar} onChange={(e) => setSchooljaar(e.target.value)}>
|
||||
<option value="">— kies —</option>
|
||||
|
|
@ -2571,6 +2574,7 @@ export function RoosterScherm({ mode }) {
|
|||
<div className="weergave-opties no-print">
|
||||
<button type="button" className={weergave === "lijst" ? "icon-toggle on" : "icon-toggle"} aria-pressed={weergave === "lijst"} title={weergave === "kolom" ? "Dagen onder elkaar tonen" : "Dagen als kolommen tonen"} onClick={() => setWeergave(weergave === "kolom" ? "lijst" : "kolom")}>{weergave === "kolom" ? "☰" : "▦"}</button>
|
||||
<button type="button" className={vakkleurVlak ? "icon-toggle on" : "icon-toggle"} aria-pressed={vakkleurVlak} title={vakkleurVlak ? "Vakkleur als streepje tonen" : "Vak volledig in vakkleur tonen"} onClick={() => setVakkleur(!vakkleurVlak)}>🎨</button>
|
||||
<button type="button" className={detailsOpen ? "icon-toggle on" : "icon-toggle"} aria-pressed={detailsOpen} title={detailsOpen ? "Minder details tonen" : "Meer details tonen"} onClick={() => setDetailsOpen(!detailsOpen)}>ⓘ</button>
|
||||
{printAan && <button type="button" className="icon-toggle" title="Afdrukken (met opties)" onClick={() => setPrintOpen(true)}>🖨</button>}
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -2638,9 +2642,7 @@ export function RoosterScherm({ mode }) {
|
|||
<button className={tab === "instellingen" ? "tab on" : "tab"} title="Instellingen" onClick={() => setTab("instellingen")}><span className="ico">⚙</span><span className="tab-tekst">Instellingen</span></button>
|
||||
</div>
|
||||
)}
|
||||
{edit && tab === "week" && (
|
||||
<p className="muted small no-print sneltoets-hint">Sneltoetsen: <b>N</b> nieuw blok · <b>←</b>/<b>→</b> week · <b>T</b> deze week · <b>Esc</b> sluiten</p>
|
||||
)}
|
||||
{edit && tab === "week" && <div className="muted small no-print sneltoets-hint">N nieuw · pijltjes week · T vandaag · Esc sluiten</div>}
|
||||
|
||||
{edit && schooljaar && tab === "week" && toonForm && (
|
||||
<div className="modal-overlay no-print" onClick={sluitNieuwBlok}>
|
||||
|
|
@ -2770,13 +2772,18 @@ export function RoosterScherm({ mode }) {
|
|||
<div className="rooster-dag">
|
||||
{d.label} <span className="muted">{fmtDM(datum)}</span>
|
||||
{kal && <div className={`tag dag-${kal.type}`}>{kal.type_label}{kal.omschrijving ? `: ${kal.omschrijving}` : ""}</div>}
|
||||
{edit && (kal
|
||||
? <button type="button" className="icon-btn no-print dag-actie" title="Weer lesdag maken" onClick={() => dagHerstel(kal.id)}>↺</button>
|
||||
: <>
|
||||
<button type="button" className="icon-btn danger no-print dag-actie" title="Hele dag laten vervallen (vrije dag)" onClick={() => dagVervallen(iso)}>⊘</button>
|
||||
<button type="button" className="icon-btn no-print dag-actie" title="Dag leegmaken (alleen deze dag)" onClick={() => leegDag(iso)}>🧹</button>
|
||||
<button type="button" className="icon-btn no-print dag-actie" title="Eenmalig blok op deze dag toevoegen" onClick={() => openEenmalig(iso)}>➕</button>
|
||||
</>
|
||||
{edit && (
|
||||
<span className="dag-acties no-print">
|
||||
{kal ? (
|
||||
<button type="button" className="icon-btn" title="Weer lesdag maken" onClick={() => dagHerstel(kal.id)}>↺</button>
|
||||
) : (
|
||||
<>
|
||||
<button type="button" className="icon-btn danger" title="Hele dag laten vervallen" onClick={() => dagVervallen(iso)}>⊘</button>
|
||||
<button type="button" className="icon-btn" title="Dag leegmaken" onClick={() => leegDag(iso)}>⌫</button>
|
||||
<button type="button" className="icon-btn" title="Eenmalig blok toevoegen" onClick={() => openEenmalig(iso)}>+</button>
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{slots.length === 0 && <div className="muted small">—</div>}
|
||||
|
|
|
|||
|
|
@ -16,13 +16,13 @@ body { margin: 0; font-family: system-ui, "Segoe UI", Roboto, sans-serif; color:
|
|||
|
||||
.topbar {
|
||||
position: sticky; top: 0; z-index: 50;
|
||||
background: rgba(255,255,255,.96); backdrop-filter: blur(10px);
|
||||
background: rgba(255,255,255,.97); backdrop-filter: blur(10px);
|
||||
border-bottom: 1px solid var(--line);
|
||||
padding: 10px 24px 8px;
|
||||
padding: 8px 24px 7px;
|
||||
}
|
||||
.topbar-main { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 8px; }
|
||||
.topbar-main { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 6px; }
|
||||
.topbar-user { display: flex; align-items: center; gap: 10px; white-space: nowrap; }
|
||||
.top-tabs { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
|
||||
.top-tabs { display: flex; align-items: center; gap: 4px; flex-wrap: nowrap; overflow-x: auto; scrollbar-width: thin; }
|
||||
.top-tabs-settings { display: flex; align-items: center; gap: 6px; margin-left: auto; padding-left: 10px; border-left: 1px solid var(--line); }
|
||||
|
||||
/* Instellingen als cog-menu (popover via hover of klik) */
|
||||
|
|
@ -36,10 +36,10 @@ body { margin: 0; font-family: system-ui, "Segoe UI", Roboto, sans-serif; color:
|
|||
.cog-item.active { background: var(--brand-soft); color: var(--brand); font-weight: 600; }
|
||||
.top-tab {
|
||||
display: inline-flex; align-items: center; gap: 8px;
|
||||
min-height: 38px; flex: 0 0 auto;
|
||||
min-height: 34px; flex: 0 0 auto;
|
||||
border: 1px solid transparent; border-radius: 8px;
|
||||
background: transparent; color: var(--ink);
|
||||
padding: 8px 12px; font: inherit; font-size: 14px; cursor: pointer;
|
||||
padding: 6px 10px; font: inherit; font-size: 13px; cursor: pointer;
|
||||
}
|
||||
.top-tab:hover { background: var(--bg); border-color: var(--line); }
|
||||
.top-tab.active { background: var(--brand); color: #fff; border-color: var(--brand); font-weight: 700; }
|
||||
|
|
@ -59,8 +59,8 @@ body { margin: 0; font-family: system-ui, "Segoe UI", Roboto, sans-serif; color:
|
|||
.ico { width: 18px; text-align: center; }
|
||||
.hint { margin-top: auto; font-size: 12px; color: var(--muted); padding: 12px 8px 0; }
|
||||
|
||||
.content { width: 100%; padding: 22px 24px 32px; }
|
||||
.content-wide { padding-left: 18px; padding-right: 18px; }
|
||||
.content { width: 100%; padding: 20px 24px 32px; }
|
||||
.content-wide { padding: 14px 14px 28px; }
|
||||
h1 { margin: 0 0 6px; font-size: 24px; }
|
||||
.muted { color: var(--muted); }
|
||||
|
||||
|
|
@ -147,14 +147,14 @@ input[type="color"] { padding: 2px; width: 44px; min-width: 44px; height: 38px;
|
|||
.tag.dag-vakantie { background: #dcfce7; color: #166534; }
|
||||
|
||||
.rooster-scherm { width: 100%; }
|
||||
.rooster-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 10px; margin-top: 18px; width: 100%; }
|
||||
.rooster-grid { display: grid; grid-template-columns: repeat(5, minmax(180px, 1fr)); gap: 8px; margin-top: 10px; width: 100%; }
|
||||
/* "Dagen onder elkaar": elke dag een volledige band, tijdvakken naast elkaar; de week scrollt verticaal mee. */
|
||||
.rooster-grid.lijst { display: flex; flex-direction: column; gap: 12px; }
|
||||
.rooster-grid.lijst .rooster-kolom { display: flex; flex-wrap: wrap; gap: 8px; align-content: flex-start; }
|
||||
.rooster-grid.lijst .rooster-dag { flex: 0 0 100%; text-align: left; padding-right: 32px; margin-bottom: 4px; }
|
||||
.rooster-grid.lijst .slot-cel { flex: 1 1 210px; max-width: 320px; margin-bottom: 0; }
|
||||
.rooster-kolom { background: var(--panel); border: 1px solid var(--line); border-radius: 10px; padding: 8px; min-height: 80px; }
|
||||
.rooster-dag { position: relative; font-weight: 600; font-size: 13px; text-align: center; padding: 6px 32px 6px 0; border-bottom: 1px solid var(--line); margin-bottom: 8px; min-height: 36px; }
|
||||
.rooster-kolom { background: var(--panel); border: 1px solid var(--line); border-radius: 8px; padding: 6px; min-height: 80px; }
|
||||
.rooster-dag { position: sticky; top: 92px; z-index: 3; background: var(--panel); font-weight: 700; font-size: 12px; text-align: left; padding: 5px 76px 7px 4px; border-bottom: 1px solid var(--line); margin-bottom: 6px; min-height: 34px; }
|
||||
.rooster-blok { border: 1px solid var(--line); border-left: 4px solid var(--brand); border-radius: 8px; padding: 8px; margin-bottom: 8px; background: #fff; }
|
||||
.rooster-blok.open { box-shadow: 0 0 0 2px var(--brand-soft); }
|
||||
.rb-top { display: flex; justify-content: space-between; align-items: center; gap: 6px; }
|
||||
|
|
@ -163,17 +163,18 @@ input[type="color"] { padding: 2px; width: 44px; min-width: 44px; height: 38px;
|
|||
@media (max-width: 900px) { .rooster-grid { grid-template-columns: 1fr; } }
|
||||
|
||||
/* --- Fase 3: weekweergave, drag-and-drop, touch --- */
|
||||
.rooster-balk { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; margin: 16px 0; }
|
||||
.rooster-balk { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin: 10px 0; }
|
||||
.rooster-toolbar { background: var(--panel); border: 1px solid var(--line); border-radius: 8px; padding: 8px 10px; }
|
||||
.week-nav { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
||||
.week-label { font-weight: 700; min-width: 150px; text-align: center; }
|
||||
.week-label { font-weight: 700; min-width: 138px; text-align: center; font-size: 13px; }
|
||||
.btn.icon { padding: 6px 12px; font-size: 18px; line-height: 1; }
|
||||
|
||||
.rooster-kolom.vrij { background: repeating-linear-gradient(45deg, #fafafa, #fafafa 8px, #f1f1f4 8px, #f1f1f4 16px); }
|
||||
.rooster-dag .tag { display: inline-block; margin-top: 4px; }
|
||||
|
||||
.slot-cel { border: 1px dashed transparent; border-radius: 8px; padding: 4px; margin-bottom: 8px; transition: background .1s, border-color .1s; }
|
||||
.slot-cel { border: 1px dashed transparent; border-radius: 7px; padding: 3px; margin-bottom: 6px; transition: background .1s, border-color .1s; }
|
||||
.slot-cel.hover { border-color: var(--brand); background: var(--brand-soft); }
|
||||
.slot-tijd { font-size: 11px; color: var(--muted); padding: 2px 2px 4px; }
|
||||
.slot-tijd { font-size: 10px; color: var(--muted); padding: 1px 2px 3px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
/* Snel een vak toevoegen direct in een tijdvak */
|
||||
.slot-add { width: 100%; margin-top: 4px; border: 1px dashed var(--line); background: transparent; color: var(--muted); border-radius: 8px; padding: 5px; font: inherit; font-size: 12px; cursor: pointer; opacity: .55; transition: opacity .1s, border-color .1s, color .1s, background .1s; }
|
||||
.slot-cel:hover .slot-add, .slot-add:focus-visible { opacity: 1; }
|
||||
|
|
@ -204,7 +205,8 @@ 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); }
|
||||
|
||||
.dag-actie { position: absolute; right: 0; top: 3px; margin: 0; }
|
||||
.dag-acties { position: absolute; right: 2px; top: 3px; display: inline-flex; gap: 2px; background: rgba(255,255,255,.88); border-radius: 6px; }
|
||||
.dag-acties .icon-btn { width: 22px; height: 22px; font-size: 12px; }
|
||||
.sneltoets-hint { margin: -4px 0 12px; }
|
||||
|
||||
/* --- Groepsindeling --- */
|
||||
|
|
@ -233,6 +235,14 @@ input[type="color"] { padding: 2px; width: 44px; min-width: 44px; height: 38px;
|
|||
.split-kop { font-size: 11px; font-weight: 700; color: var(--muted); margin: 0 0 4px; }
|
||||
.split-node-kop { display: inline-flex; max-width: 100%; align-items: center; border: 1px solid #bfdbfe; border-radius: 6px; background: #fff; color: var(--brand); padding: 4px 7px; margin-bottom: 6px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.split-vakken { display: grid; gap: 6px; }
|
||||
.split-vakken.meerdere-vakken {
|
||||
gap: 5px;
|
||||
padding-left: 7px;
|
||||
border-left: 2px solid var(--line);
|
||||
}
|
||||
.split-vakken.meerdere-vakken .vak-kaart {
|
||||
border-radius: 6px;
|
||||
}
|
||||
.split-vak { position: relative; border: 1px solid var(--line); border-left: 4px solid var(--brand); border-radius: 6px; background: #fff; padding: 6px 6px 5px; margin-bottom: 6px; cursor: pointer; box-shadow: 0 1px 0 rgba(31,41,55,.03); }
|
||||
.split-vak.open { box-shadow: 0 0 0 2px var(--brand-soft); }
|
||||
.split-vak.vervallen { opacity: .55; }
|
||||
|
|
@ -243,15 +253,15 @@ input[type="color"] { padding: 2px; width: 44px; min-width: 44px; height: 38px;
|
|||
/* 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 { position: relative; border: 1px solid var(--line); border-left: 4px solid var(--brand); border-radius: 7px; background: #fff; padding: 6px 7px; 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%; }
|
||||
.vk-naam { min-width: 0; flex: 1; font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.vk-meta { display: flex; flex-wrap: wrap; gap: 3px 5px; margin-top: 5px; }
|
||||
.vk-tag { display: inline-flex; align-items: center; gap: 3px; background: var(--bg); color: var(--muted); border-radius: 999px; padding: 1px 6px; font-size: 10px; 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; }
|
||||
|
||||
|
|
@ -309,14 +319,8 @@ input[type="color"] { padding: 2px; width: 44px; min-width: 44px; height: 38px;
|
|||
.modal-hr { border: 0; border-top: 1px solid var(--line); margin: 16px 0 12px; }
|
||||
.rooster-blok.klikbaar { cursor: pointer; }
|
||||
.rooster-blok.klikbaar:hover { box-shadow: 0 0 0 2px var(--brand-soft); }
|
||||
.scope-balk { margin-top: -6px; flex-direction: column; align-items: stretch; gap: 10px; background: var(--panel); border: 1px solid var(--line); border-radius: 12px; padding: 12px 14px; }
|
||||
.scope-rij { display: flex; align-items: center; gap: 10px; }
|
||||
.scope-label { color: var(--muted); font-size: 13px; font-weight: 600; flex: 0 0 auto; width: 104px; text-align: right; white-space: nowrap; }
|
||||
.klas-strip .chip { white-space: nowrap; flex: 0 0 auto; min-width: 62px; display: inline-flex; align-items: center; justify-content: center; min-height: 34px; }
|
||||
.klas-strip .chip.chip-plein { min-width: 96px; }
|
||||
.scope-persoon { flex: 0 0 auto; max-width: 300px; min-height: 34px; }
|
||||
.scope-persoon { flex: 0 0 auto; min-height: 34px; }
|
||||
.scope-persoon.on { border-color: var(--brand); color: var(--brand); font-weight: 600; }
|
||||
.scope-aantal { flex: 0 0 auto; white-space: nowrap; }
|
||||
|
||||
/* --- Afdrukken --- */
|
||||
.print-knop { margin-left: auto; background: #fff; }
|
||||
|
|
@ -576,7 +580,10 @@ input[type="color"] { padding: 2px; width: 44px; min-width: 44px; height: 38px;
|
|||
.wm-vak-kop { display: flex; align-items: center; gap: 4px; }
|
||||
.wm-vak-naam { flex: 1; min-width: 0; font-size: 12px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; }
|
||||
.wm-vak-meta { color: var(--muted); font-size: 11px; margin-top: 2px; }
|
||||
.wm-vak.in-conflict { outline: 2px solid #ef4444; outline-offset: 1px; box-shadow: 0 0 0 4px rgba(239,68,68,.15); }
|
||||
.wm-vak.in-conflict {
|
||||
outline: 0;
|
||||
box-shadow: inset 0 0 0 2px #ef4444, 0 1px 0 rgba(31,41,55,.03);
|
||||
}
|
||||
.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); }
|
||||
|
|
@ -593,6 +600,31 @@ input[type="color"] { padding: 2px; width: 44px; min-width: 44px; height: 38px;
|
|||
/* Schoollogo in de roosterweergave */
|
||||
.rooster-kop { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
|
||||
.rooster-logo { max-height: 40px; max-width: 150px; object-fit: contain; }
|
||||
.app-page-kop { margin-bottom: 8px; }
|
||||
.page-subtitle { color: var(--muted); font-size: 13px; margin-top: 2px; }
|
||||
.rooster-scherm h1 { margin-bottom: 0; }
|
||||
.rooster-scherm .veld { gap: 8px; }
|
||||
.rooster-scherm select { min-width: 0; }
|
||||
.scope-balk { align-items: stretch; background: transparent; border: 0; padding: 0; margin-top: 6px; }
|
||||
.scope-rij { display: flex; align-items: center; gap: 8px; min-width: 0; flex: 1 1 360px; }
|
||||
.scope-label { flex: none; font-size: 11px; font-weight: 800; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }
|
||||
.scope-balk .klas-strip { display: flex; flex-wrap: nowrap; gap: 6px; overflow-x: auto; padding: 2px 2px 7px; scrollbar-width: thin; }
|
||||
.scope-balk .chip { flex: 0 0 auto; padding: 4px 9px; font-size: 12px; }
|
||||
.scope-persoon { max-width: 320px; }
|
||||
.scope-aantal { white-space: nowrap; }
|
||||
.rooster-edit .rooster-grid { grid-template-columns: repeat(5, minmax(210px, 1fr)); }
|
||||
.rooster-view.details-compact .vak-kaart { padding-top: 7px; padding-bottom: 7px; }
|
||||
.rooster-view.details-compact .blok-splitsing { background: #fff; border-color: var(--line); }
|
||||
.rooster-view.details-compact .split-header { background: #fafbff; margin-bottom: 6px; }
|
||||
.details-compact .split-kolommen { gap: 6px; }
|
||||
.details-compact .split-vakken { gap: 5px; }
|
||||
.details-compact .rb-picto { height: 18px; width: 18px; }
|
||||
.details-compact .split-title span { display: none; }
|
||||
.rooster-edit .vak-kaart { background: #fff; }
|
||||
.rooster-edit .slot-add { opacity: .35; padding: 4px; }
|
||||
.rooster-edit .slot-cel:hover .slot-add { opacity: 1; }
|
||||
.rooster-edit .rooster-kolom { background: #fbfcff; }
|
||||
.sneltoets-hint { margin: -2px 0 8px; text-align: right; }
|
||||
|
||||
/* Huisstijl-pagina: logo-upload + voorbeeld op geruite achtergrond */
|
||||
.huisstijl .logo-vak { display: flex; flex-wrap: wrap; gap: 16px; align-items: center; margin: 10px 0; }
|
||||
|
|
@ -657,4 +689,9 @@ 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, .vak-kaart.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: 0;
|
||||
box-shadow: inset 0 0 0 2px #ef4444, 0 1px 0 rgba(31,41,55,.03);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue