Menu-opschoning: Schooljaar als Rooster-tab, subtiel Instellingen-blok (Gebruikers + Modulebeheer), hint verwijderd

This commit is contained in:
bes-r 2026-06-12 23:06:47 +02:00
parent b3ce841efd
commit 3318ae601e
7 changed files with 54 additions and 29 deletions

View file

@ -12,9 +12,11 @@ Fase 3 (afwezigheid, uitzonderingen, effectief weekrooster) en Fase 4
Het Rooster-scherm is een **weekweergave met drag-and-drop** (muis én touch) en Het Rooster-scherm is een **weekweergave met drag-and-drop** (muis én touch) en
een **touch-vriendelijk** ontwerp; vrije dagen, afwezigheid en uitzonderingen een **touch-vriendelijk** ontwerp; vrije dagen, afwezigheid en uitzonderingen
worden automatisch verwerkt. Het Rooster heeft tabs **Weekrooster / Conflicten / worden automatisch verwerkt. Het Rooster heeft tabs **Weekrooster / Conflicten /
Instellingen** (tijdsloten, activiteiten en locaties beheer je daar, zodat het Schooljaar / Instellingen** (tijdsloten, activiteiten en locaties beheer je daar,
menu rustiger is). De zijbalk groepeert items onder inklapbare submenu's en het schooljaar + de kalender voer je in onder de Schooljaar-tab, zodat het menu
(Personen, Groepen). rustiger is). De zijbalk groepeert items onder inklapbare submenu's (Personen,
Groepen) en heeft onderaan een subtiel **Instellingen**-blok met Gebruikers en
Modulebeheer.
Optionele module **Leerplein**: koppelt subgroepen aan groepen en bundelt Optionele module **Leerplein**: koppelt subgroepen aan groepen en bundelt
groepen, subgroepen én losse leerlingen onder een overkoepelend leerplein. groepen, subgroepen én losse leerlingen onder een overkoepelend leerplein.

View file

@ -28,12 +28,12 @@ volgt.
bijvoorbeeld **per leerplein een eigen overzicht** in plaats van alles door bijvoorbeeld **per leerplein een eigen overzicht** in plaats van alles door
elkaar. elkaar.
- [ ] **Snelmenu** om per klas of per leerplein direct het rooster te tonen. - [ ] **Snelmenu** om per klas of per leerplein direct het rooster te tonen.
- [ ] **Schooljaar uit het hoofdmenu** halen en **onder Rooster** plaatsen, met - [x] **Schooljaar uit het hoofdmenu** halen en **onder Rooster** plaatsen
een **snelle invoer** van een schooljaar. (tab "Schooljaar") met snelle invoer + kalenderbeheer.
- [ ] **Instellingenmenu** (subtiel weggewerkt) waaronder o.a. **Gebruikers** en - [x] **Instellingenmenu** (subtiel onderaan de zijbalk) waaronder **Gebruikers**
**Modulebeheer** verdwijnen. en **Modulebeheer** verdwijnen.
- [ ] De hint **"Menu's verschijnen alleen voor ingeschakelde modules."** uit de - [x] De hint **"Menu's verschijnen alleen voor ingeschakelde modules."** uit de
zijbalk verwijderen. zijbalk verwijderd.
## Vakken ## Vakken

View file

@ -27,9 +27,8 @@ class CoreConfig(AppConfig):
category="Kern", category="Kern",
core=True, core=True,
menu_items=( menu_items=(
# Hoofdniveau # Hoofdniveau (Schooljaar zit nu als tab in het Rooster)
MenuItem("Rooster", "/rooster", icon="calendar", order=10), MenuItem("Rooster", "/rooster", icon="calendar", order=10),
MenuItem("Schooljaar", "/schooljaar", icon="calendar", order=15),
# Submenu Personen # Submenu Personen
MenuItem("Personen", "/personen", icon="users", order=20, group="Personen"), MenuItem("Personen", "/personen", icon="users", order=20, group="Personen"),
MenuItem("Functies", "/functies", icon="badge", order=25, group="Personen"), MenuItem("Functies", "/functies", icon="badge", order=25, group="Personen"),

View file

@ -24,7 +24,7 @@ class AccountsConfig(AppConfig):
core=False, core=False,
default_enabled=False, default_enabled=False,
menu_items=( menu_items=(
MenuItem("Gebruikers", "/gebruikers", icon="key", order=300), MenuItem("Gebruikers", "/gebruikers", icon="key", order=300, group="Instellingen"),
), ),
) )
) )

View file

@ -10,7 +10,7 @@ import {
const ICONS = { const ICONS = {
calendar: "📅", users: "👥", layers: "🗂️", printer: "🖨️", calendar: "📅", users: "👥", layers: "🗂️", printer: "🖨️",
key: "🔑", puzzle: "🧩", badge: "🪪", book: "📚", pin: "📍", clock: "🕘", key: "🔑", puzzle: "🧩", badge: "🪪", book: "📚", pin: "📍", clock: "🕘",
away: "🌴", away: "🌴", gear: "⚙️",
}; };
// Koppel een menu-pad aan een echt scherm. Onbekende paden tonen een // Koppel een menu-pad aan een echt scherm. Onbekende paden tonen een
@ -134,23 +134,26 @@ function Shell({ user, onLogout }) {
const [openGroups, setOpenGroups] = useState({}); const [openGroups, setOpenGroups] = useState({});
// Bouw de navigatie op uit de menu-items die ACTIEVE modules aanbieden. // Bouw de navigatie op uit de menu-items die ACTIEVE modules aanbieden.
// Items met een `group` komen onder een inklapbaar submenu. // Items met een `group` komen onder een inklapbaar submenu. De groep
const { topItems, groups, alleItems } = useMemo(() => { // "Instellingen" wordt apart, subtiel onderaan, getoond.
const { topItems, groups, instellingenItems, alleItems } = useMemo(() => {
const items = []; const items = [];
for (const m of modules) { for (const m of modules) {
if (!m.enabled) continue; if (!m.enabled) continue;
for (const it of m.menu_items) items.push({ ...it, module: m.key }); for (const it of m.menu_items) items.push({ ...it, module: m.key });
} }
if (user.is_staff) { if (user.is_staff) {
items.push({ label: "Modulebeheer", path: "/modules", icon: "puzzle", order: 9999, group: "" }); items.push({ label: "Modulebeheer", path: "/modules", icon: "gear", order: 9999, group: "Instellingen" });
} }
items.sort((a, b) => a.order - b.order); items.sort((a, b) => a.order - b.order);
const top = items.filter((i) => !i.group); const top = items.filter((i) => !i.group);
const grouped = {}; const grouped = {};
const settings = [];
for (const i of items) { for (const i of items) {
if (i.group) (grouped[i.group] = grouped[i.group] || []).push(i); if (i.group === "Instellingen") settings.push(i);
else if (i.group) (grouped[i.group] = grouped[i.group] || []).push(i);
} }
return { topItems: top, groups: grouped, alleItems: items }; return { topItems: top, groups: grouped, instellingenItems: settings, alleItems: items };
}, [modules, user.is_staff]); }, [modules, user.is_staff]);
function NavKnop({ it, indent }) { function NavKnop({ it, indent }) {
@ -189,11 +192,21 @@ function Shell({ user, onLogout }) {
); );
})} })}
</nav> </nav>
<div className="sidebar-foot">
{instellingenItems.length > 0 && (
<div className="nav-group">
<button className="nav-group-head subtiel" onClick={() => setOpenGroups((o) => ({ ...o, Instellingen: !o.Instellingen }))}>
<span> Instellingen</span>
<span className="nav-chev">{openGroups.Instellingen ? "▾" : "▸"}</span>
</button>
{openGroups.Instellingen && instellingenItems.map((it) => <NavKnop key={it.path} it={it} indent />)}
</div>
)}
<div className="sidebar-user"> <div className="sidebar-user">
<span className="muted small">👤 {user.username}</span> <span className="muted small">👤 {user.username}</span>
<button className="link" onClick={doLogout}>uitloggen</button> <button className="link" onClick={doLogout}>uitloggen</button>
</div> </div>
<div className="hint">Menu's verschijnen alleen voor ingeschakelde modules.</div> </div>
</aside> </aside>
<main className="content"> <main className="content">

View file

@ -1163,13 +1163,12 @@ export function RoosterPage() {
{schooljaar && tab === "week" && <button className="btn primary" onClick={() => setToonForm((v) => !v)}>{toonForm ? "Sluiten" : "+ Blok"}</button>} {schooljaar && tab === "week" && <button className="btn primary" onClick={() => setToonForm((v) => !v)}>{toonForm ? "Sluiten" : "+ Blok"}</button>}
</div> </div>
{schooljaar && (
<div className="tabbar"> <div className="tabbar">
<button className={tab === "week" ? "tab on" : "tab"} onClick={() => setTab("week")}>Weekrooster</button> <button className={tab === "week" ? "tab on" : "tab"} onClick={() => setTab("week")}>Weekrooster</button>
<button className={tab === "conflicten" ? "tab on" : "tab"} onClick={() => setTab("conflicten")}>Conflicten</button> <button className={tab === "conflicten" ? "tab on" : "tab"} onClick={() => setTab("conflicten")}>Conflicten</button>
<button className={tab === "schooljaar" ? "tab on" : "tab"} onClick={() => setTab("schooljaar")}>Schooljaar</button>
<button className={tab === "instellingen" ? "tab on" : "tab"} onClick={() => setTab("instellingen")}>Instellingen</button> <button className={tab === "instellingen" ? "tab on" : "tab"} onClick={() => setTab("instellingen")}>Instellingen</button>
</div> </div>
)}
{schooljaar && tab === "week" && toonForm && ( {schooljaar && tab === "week" && toonForm && (
<form className="form-card" onSubmit={opslaan}> <form className="form-card" onSubmit={opslaan}>
@ -1276,6 +1275,13 @@ export function RoosterPage() {
{schooljaar && tab === "conflicten" && <Conflicten schooljaar={schooljaar} />} {schooljaar && tab === "conflicten" && <Conflicten schooljaar={schooljaar} />}
{tab === "schooljaar" && (
<div className="instellingen">
<p className="muted">Maak hier snel een schooljaar aan en beheer de kalender (studiedagen, vrije dagen, vakanties).</p>
<SchooljaarPage />
</div>
)}
{schooljaar && tab === "instellingen" && ( {schooljaar && tab === "instellingen" && (
<div className="instellingen"> <div className="instellingen">
<p className="muted">Bouwstenen van het rooster beheer ze hier zonder het Rooster te verlaten.</p> <p className="muted">Bouwstenen van het rooster beheer ze hier zonder het Rooster te verlaten.</p>

View file

@ -147,6 +147,11 @@ 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); } .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); }
/* --- 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; }
.nav-group-head.subtiel:hover { opacity: 1; }
/* --- Submenu's in de zijbalk --- */ /* --- Submenu's in de zijbalk --- */
.nav-group { margin-top: 2px; } .nav-group { margin-top: 2px; }
.nav-group-head { display: flex; justify-content: space-between; align-items: center; width: 100%; background: transparent; border: 0; cursor: pointer; padding: 10px 10px 4px; font-size: 11px; text-transform: uppercase; letter-spacing: .5px; color: var(--muted); font-weight: 700; } .nav-group-head { display: flex; justify-content: space-between; align-items: center; width: 100%; background: transparent; border: 0; cursor: pointer; padding: 10px 10px 4px; font-size: 11px; text-transform: uppercase; letter-spacing: .5px; color: var(--muted); font-weight: 700; }