diff --git a/ROADMAP.md b/ROADMAP.md index 78ba794..9ace35e 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -74,9 +74,9 @@ volgt. - [ ] **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). +- [x] De actie heet nu **"dit vak laten vervallen"** (per blok, juiste tekst). +- [x] Een **hele dag laten vervallen** kan direct in het weekrooster (knop in de + dagkop; maakt een vrije dag aan, met "weer lesdag" om te herstellen). ## Later diff --git a/backend/core/apps.py b/backend/core/apps.py index 2143c42..231f0d4 100644 --- a/backend/core/apps.py +++ b/backend/core/apps.py @@ -30,9 +30,8 @@ class CoreConfig(AppConfig): # Hoofdniveau: Rooster = weergave, Roostermaker = aanpasser. MenuItem("Rooster", "/rooster", icon="calendar", order=10), MenuItem("Roostermaker", "/roostermaker", icon="calendar", order=12), - # Submenu Personen + # Submenu Personen (functies zitten als tab in het Personen-scherm) MenuItem("Personen", "/personen", icon="users", order=20, group="Personen"), - MenuItem("Functies", "/functies", icon="badge", order=25, group="Personen"), MenuItem("Afwezigheid", "/afwezigheid", icon="away", order=28, group="Personen"), # Submenu Groepen MenuItem("Groepen", "/groepen", icon="layers", order=30, group="Groepen"), diff --git a/frontend/src/pages.jsx b/frontend/src/pages.jsx index 151c4cf..13ee5b1 100644 --- a/frontend/src/pages.jsx +++ b/frontend/src/pages.jsx @@ -271,6 +271,7 @@ export function PersonenPage() { const [form, setForm] = useState(leegLeerling); const [editId, setEditId] = useState(null); const [filter, setFilter] = useState(""); + const [hoofdtab, setHoofdtab] = useState("personen"); function laadHulplijsten() { listResource("groepen").then(setGroepen).catch(() => {}); @@ -354,9 +355,26 @@ export function PersonenPage() { : filter === "medewerker" ? items.filter((p) => p.rol !== "leerling") : items; + if (hoofdtab === "functies") { + return ( +
+

Personen & functies

+
+ + +
+ +
+ ); + } + return (
-

Personen

+

Personen & functies

+
+ + +

Personeel en leerlingen staan los van elkaar. Kies wat je wilt toevoegen.

@@ -1224,7 +1242,19 @@ function BlokBewerker({ blok, ctx, onSluit, onKlaar }) { }); const [kopieDoelen, setKopieDoelen] = useState([]); const [error, setError] = useState(null); - const doelOpties = f.doel_type === "groep" ? groepen : f.doel_type === "subgroep" ? subgroepen : leerlingen; + const [subs, setSubs] = useState(subgroepen); + const [nieuweSub, setNieuweSub] = useState(""); + const doelOpties = f.doel_type === "groep" ? groepen : f.doel_type === "subgroep" ? subs : leerlingen; + + async function maakSubgroep() { + if (!nieuweSub.trim()) return; + try { + const s = await createResource("subgroepen", { naam: nieuweSub.trim(), groep: f.doel_type === "groep" && f.doel ? f.doel : null }); + setSubs([...subs, s]); + setF({ ...f, doel_type: "subgroep", doel: s.id }); + setNieuweSub(""); + } catch (e) { setError(e.message); } + } const toggleBeg = (id) => setF((s) => s.begeleiders.includes(id) ? { ...s, begeleiders: s.begeleiders.filter((x) => x !== id) } : { ...s, begeleiders: [...s.begeleiders, id] }); const toggleKopie = (id) => setKopieDoelen((d) => d.includes(id) ? d.filter((x) => x !== id) : [...d, id]); const doelPayload = () => ({ @@ -1273,6 +1303,12 @@ function BlokBewerker({ blok, ctx, onSluit, onKlaar }) { {doelOpties.map((o) => )} + {f.doel_type === "subgroep" && ( +
+ setNieuweSub(e.target.value)} /> + +
+ )}
Begeleiders:
@@ -1528,6 +1564,14 @@ export function RoosterScherm({ mode }) { async function herstel(uitId) { try { await deleteResource("blok-uitzonderingen", uitId); await laadWeek(); } catch (err) { setError(err.message); } } + async function dagVervallen(iso) { + try { await createResource("kalenderdagen", { schooljaar, datum: iso, type: "vrije_dag", omschrijving: "vervallen" }); await laadBlokken(); } + catch (err) { setError(err.message); } + } + async function dagHerstel(kalId) { + try { await deleteResource("kalenderdagen", kalId); await laadBlokken(); } + catch (err) { setError(err.message); } + } // Hulpfuncties voor het effectieve rooster. const kalenderOp = (iso) => kalender.find((k) => k.datum === iso); @@ -1590,7 +1634,6 @@ export function RoosterScherm({ mode }) { {edit && (
- @@ -1650,6 +1693,10 @@ export function RoosterScherm({ mode }) {
{d.label} {fmtDM(datum)} {kal &&
{kal.type_label}{kal.omschrijving ? `: ${kal.omschrijving}` : ""}
} + {edit && (kal + ? + : + )}
{slots.length === 0 &&
} {slots.map((t) => { @@ -1688,7 +1735,7 @@ export function RoosterScherm({ mode }) { {!kal && uit && uit.vervalt &&
Vervalt {uit.opmerking ? `(${uit.opmerking})` : ""}{edit && <> · }
} {!kal && uit && !uit.vervalt && uit.vervanger_naam &&
↪ Vervanger: {uit.vervanger_naam}
} {edit && !kal && !uit && ( - + )}
); @@ -1702,17 +1749,6 @@ export function RoosterScherm({ mode }) {
)} - {edit && tab === "indeling" && ( - { try { setSubgroepen(await listResource("subgroepen")); } catch (e) { setError(e.message); } }} - /> - )} - {schooljaar && tab === "conflicten" && } {tab === "schooljaar" && ( diff --git a/frontend/src/styles.css b/frontend/src/styles.css index 2cf03da..0fc660a 100644 --- a/frontend/src/styles.css +++ b/frontend/src/styles.css @@ -147,6 +147,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 { display: block; margin-top: 4px; font-size: 11px; } + /* --- Groepsindeling --- */ .indeling { margin-top: 12px; } .indeling-hoofd { background: var(--brand); color: #fff; border-radius: 10px; padding: 12px 16px; font-weight: 700; font-size: 16px; text-align: center; }