Personen+Functies samengevoegd (tab), Indeling-tab weg + subgroep aanmaken vanuit blok, 'dit vak laten vervallen' + hele dag laten vervallen in weekrooster
This commit is contained in:
parent
8841fd5698
commit
95a04f3ca1
4 changed files with 57 additions and 20 deletions
|
|
@ -74,9 +74,9 @@ volgt.
|
||||||
|
|
||||||
- [ ] **Vrije/vakantie-/studiedagen kunnen niet gebruikt worden** in de
|
- [ ] **Vrije/vakantie-/studiedagen kunnen niet gebruikt worden** in de
|
||||||
Roostermaker (geen blokken plannen op die dagen).
|
Roostermaker (geen blokken plannen op die dagen).
|
||||||
- [ ] De actie **"Deze dag laten vervallen"** subtieler wegwerken **en de tekst
|
- [x] De actie heet nu **"dit vak laten vervallen"** (per blok, juiste tekst).
|
||||||
corrigeren** — het is per *blok*, niet per dag.
|
- [x] Een **hele dag laten vervallen** kan direct in het weekrooster (knop in de
|
||||||
- [ ] Mogelijkheid om **een hele dag op vervallen** te zetten (i.p.v. per blok).
|
dagkop; maakt een vrije dag aan, met "weer lesdag" om te herstellen).
|
||||||
|
|
||||||
## Later
|
## Later
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,9 +30,8 @@ class CoreConfig(AppConfig):
|
||||||
# Hoofdniveau: Rooster = weergave, Roostermaker = aanpasser.
|
# Hoofdniveau: Rooster = weergave, Roostermaker = aanpasser.
|
||||||
MenuItem("Rooster", "/rooster", icon="calendar", order=10),
|
MenuItem("Rooster", "/rooster", icon="calendar", order=10),
|
||||||
MenuItem("Roostermaker", "/roostermaker", icon="calendar", order=12),
|
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("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"),
|
MenuItem("Afwezigheid", "/afwezigheid", icon="away", order=28, group="Personen"),
|
||||||
# Submenu Groepen
|
# Submenu Groepen
|
||||||
MenuItem("Groepen", "/groepen", icon="layers", order=30, group="Groepen"),
|
MenuItem("Groepen", "/groepen", icon="layers", order=30, group="Groepen"),
|
||||||
|
|
|
||||||
|
|
@ -271,6 +271,7 @@ export function PersonenPage() {
|
||||||
const [form, setForm] = useState(leegLeerling);
|
const [form, setForm] = useState(leegLeerling);
|
||||||
const [editId, setEditId] = useState(null);
|
const [editId, setEditId] = useState(null);
|
||||||
const [filter, setFilter] = useState("");
|
const [filter, setFilter] = useState("");
|
||||||
|
const [hoofdtab, setHoofdtab] = useState("personen");
|
||||||
|
|
||||||
function laadHulplijsten() {
|
function laadHulplijsten() {
|
||||||
listResource("groepen").then(setGroepen).catch(() => {});
|
listResource("groepen").then(setGroepen).catch(() => {});
|
||||||
|
|
@ -354,9 +355,26 @@ export function PersonenPage() {
|
||||||
: filter === "medewerker" ? items.filter((p) => p.rol !== "leerling")
|
: filter === "medewerker" ? items.filter((p) => p.rol !== "leerling")
|
||||||
: items;
|
: items;
|
||||||
|
|
||||||
|
if (hoofdtab === "functies") {
|
||||||
|
return (
|
||||||
|
<section>
|
||||||
|
<h1>Personen & functies</h1>
|
||||||
|
<div className="tabbar">
|
||||||
|
<button className="tab" onClick={() => setHoofdtab("personen")}>Personen</button>
|
||||||
|
<button className="tab on" onClick={() => setHoofdtab("functies")}>Functies</button>
|
||||||
|
</div>
|
||||||
|
<FunctiesPage />
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section>
|
<section>
|
||||||
<h1>Personen</h1>
|
<h1>Personen & functies</h1>
|
||||||
|
<div className="tabbar">
|
||||||
|
<button className="tab on" onClick={() => setHoofdtab("personen")}>Personen</button>
|
||||||
|
<button className="tab" onClick={() => setHoofdtab("functies")}>Functies</button>
|
||||||
|
</div>
|
||||||
<p className="muted">Personeel en leerlingen staan los van elkaar. Kies wat je wilt toevoegen.</p>
|
<p className="muted">Personeel en leerlingen staan los van elkaar. Kies wat je wilt toevoegen.</p>
|
||||||
<Foutmelding msg={error} />
|
<Foutmelding msg={error} />
|
||||||
|
|
||||||
|
|
@ -1224,7 +1242,19 @@ function BlokBewerker({ blok, ctx, onSluit, onKlaar }) {
|
||||||
});
|
});
|
||||||
const [kopieDoelen, setKopieDoelen] = useState([]);
|
const [kopieDoelen, setKopieDoelen] = useState([]);
|
||||||
const [error, setError] = useState(null);
|
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 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 toggleKopie = (id) => setKopieDoelen((d) => d.includes(id) ? d.filter((x) => x !== id) : [...d, id]);
|
||||||
const doelPayload = () => ({
|
const doelPayload = () => ({
|
||||||
|
|
@ -1273,6 +1303,12 @@ function BlokBewerker({ blok, ctx, onSluit, onKlaar }) {
|
||||||
{doelOpties.map((o) => <option key={o.id} value={o.id}>{o.naam || o.volledige_naam}</option>)}
|
{doelOpties.map((o) => <option key={o.id} value={o.id}>{o.naam || o.volledige_naam}</option>)}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
{f.doel_type === "subgroep" && (
|
||||||
|
<div className="form-row">
|
||||||
|
<input placeholder="Nieuwe subgroep" value={nieuweSub} onChange={(e) => setNieuweSub(e.target.value)} />
|
||||||
|
<button type="button" className="btn" onClick={maakSubgroep}>+ Subgroep aanmaken</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div className="leden-keuze">
|
<div className="leden-keuze">
|
||||||
<div className="muted small">Begeleiders:</div>
|
<div className="muted small">Begeleiders:</div>
|
||||||
<div className="chips">
|
<div className="chips">
|
||||||
|
|
@ -1528,6 +1564,14 @@ export function RoosterScherm({ mode }) {
|
||||||
async function herstel(uitId) {
|
async function herstel(uitId) {
|
||||||
try { await deleteResource("blok-uitzonderingen", uitId); await laadWeek(); } catch (err) { setError(err.message); }
|
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.
|
// Hulpfuncties voor het effectieve rooster.
|
||||||
const kalenderOp = (iso) => kalender.find((k) => k.datum === iso);
|
const kalenderOp = (iso) => kalender.find((k) => k.datum === iso);
|
||||||
|
|
@ -1590,7 +1634,6 @@ export function RoosterScherm({ mode }) {
|
||||||
{edit && (
|
{edit && (
|
||||||
<div className="tabbar no-print">
|
<div className="tabbar no-print">
|
||||||
<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 === "indeling" ? "tab on" : "tab"} onClick={() => setTab("indeling")}>Indeling</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 === "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>
|
||||||
|
|
@ -1650,6 +1693,10 @@ export function RoosterScherm({ mode }) {
|
||||||
<div className="rooster-dag">
|
<div className="rooster-dag">
|
||||||
{d.label} <span className="muted">{fmtDM(datum)}</span>
|
{d.label} <span className="muted">{fmtDM(datum)}</span>
|
||||||
{kal && <div className={`tag dag-${kal.type}`}>{kal.type_label}{kal.omschrijving ? `: ${kal.omschrijving}` : ""}</div>}
|
{kal && <div className={`tag dag-${kal.type}`}>{kal.type_label}{kal.omschrijving ? `: ${kal.omschrijving}` : ""}</div>}
|
||||||
|
{edit && (kal
|
||||||
|
? <button className="link small no-print dag-actie" onClick={() => dagHerstel(kal.id)}>weer lesdag</button>
|
||||||
|
: <button className="link danger small no-print dag-actie" onClick={() => dagVervallen(iso)}>dag laten vervallen</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{slots.length === 0 && <div className="muted small">—</div>}
|
{slots.length === 0 && <div className="muted small">—</div>}
|
||||||
{slots.map((t) => {
|
{slots.map((t) => {
|
||||||
|
|
@ -1688,7 +1735,7 @@ export function RoosterScherm({ mode }) {
|
||||||
{!kal && uit && uit.vervalt && <div className="small rb-status">Vervalt {uit.opmerking ? `(${uit.opmerking})` : ""}{edit && <> · <button className="link" onClick={(e) => { e.stopPropagation(); herstel(uit.id); }}>herstel</button></>}</div>}
|
{!kal && uit && uit.vervalt && <div className="small rb-status">Vervalt {uit.opmerking ? `(${uit.opmerking})` : ""}{edit && <> · <button className="link" onClick={(e) => { e.stopPropagation(); herstel(uit.id); }}>herstel</button></>}</div>}
|
||||||
{!kal && uit && !uit.vervalt && uit.vervanger_naam && <div className="small rb-verv">↪ Vervanger: {uit.vervanger_naam}</div>}
|
{!kal && uit && !uit.vervalt && uit.vervanger_naam && <div className="small rb-verv">↪ Vervanger: {uit.vervanger_naam}</div>}
|
||||||
{edit && !kal && !uit && (
|
{edit && !kal && !uit && (
|
||||||
<button className="link danger small rb-verval no-print" onClick={(e) => { e.stopPropagation(); laatVervallen(b, iso); }}>deze dag laten vervallen</button>
|
<button className="link danger small rb-verval no-print" onClick={(e) => { e.stopPropagation(); laatVervallen(b, iso); }}>dit vak laten vervallen</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
@ -1702,17 +1749,6 @@ export function RoosterScherm({ mode }) {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{edit && tab === "indeling" && (
|
|
||||||
<Groepsindeling
|
|
||||||
groepen={groepen}
|
|
||||||
leerpleinen={leerpleinen}
|
|
||||||
subgroepen={subgroepen}
|
|
||||||
medewerkers={medewerkers}
|
|
||||||
blokken={blokken}
|
|
||||||
onWijzig={async () => { try { setSubgroepen(await listResource("subgroepen")); } catch (e) { setError(e.message); } }}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{schooljaar && tab === "conflicten" && <Conflicten schooljaar={schooljaar} />}
|
{schooljaar && tab === "conflicten" && <Conflicten schooljaar={schooljaar} />}
|
||||||
|
|
||||||
{tab === "schooljaar" && (
|
{tab === "schooljaar" && (
|
||||||
|
|
|
||||||
|
|
@ -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); }
|
.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 --- */
|
/* --- Groepsindeling --- */
|
||||||
.indeling { margin-top: 12px; }
|
.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; }
|
.indeling-hoofd { background: var(--brand); color: #fff; border-radius: 10px; padding: 12px 16px; font-weight: 700; font-size: 16px; text-align: center; }
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue