/* ================================================================== Admin → Personnel ------------------------------------------------------------------ Manage non-teaching support staff (drivers, sweepers, guards, …) and the duties they perform. A person can hold several duties. Creates a support_staff user + login credentials in one go (the temp password is shown once, to hand over). ================================================================== */ const { Icon: PsIcon } = window.AppUI; const PERSONNEL_DUTIES = [ "driver", "sweeper", "marketing", "electrician", "plumber", "guard", "gateman", "cook", "helper", ]; const dutyLabel = (d) => d.charAt(0).toUpperCase() + d.slice(1); // Multi-select duty chips. const DutyChips = ({ value, onChange }) => { const toggle = (d) => { const set = new Set(value); set.has(d) ? set.delete(d) : set.add(d); onChange(Array.from(set)); }; return (
{PERSONNEL_DUTIES.map((d) => { const on = value.includes(d); return ( ); })}
); }; const PersonnelScreen = () => { const [list, setList] = React.useState([]); const [loading, setLoading] = React.useState(true); const [err, setErr] = React.useState(null); const [adding, setAdding] = React.useState(false); const [form, setForm] = React.useState({ name: "", phone: "", email: "", password: "", duties: [] }); const [flash, setFlash] = React.useState(null); // { name, username, temp_password } const [editId, setEditId] = React.useState(null); // row being duty-edited const [editDuties, setEditDuties] = React.useState([]); const load = React.useCallback(async () => { setLoading(true); setErr(null); try { setList((await window.AppApi.get("/admin/personnel")).personnel || []); } catch (e) { setErr(e.message); } finally { setLoading(false); } }, []); React.useEffect(() => { load(); }, [load]); async function create(e) { e.preventDefault(); setErr(null); if (!form.name.trim() || !form.phone.trim()) { setErr("Name and phone are required."); return; } try { const out = await window.AppApi.post("/admin/personnel", { name: form.name.trim(), phone: form.phone.trim(), email: form.email.trim() || undefined, password: form.password.trim() || undefined, duties: form.duties, }); setFlash({ name: out.name, username: out.username, temp_password: out.temp_password }); setForm({ name: "", phone: "", email: "", password: "", duties: [] }); setAdding(false); load(); } catch (e) { setErr(e.message.includes("phone already") ? "That phone is already registered." : e.message.includes("email already") ? "That email is already registered." : e.message); } } async function resetPw(p) { try { const out = await window.AppApi.post(`/admin/personnel/${p.id}/reset-password`, {}); setFlash({ name: p.name, username: p.username, temp_password: out.temp_password }); } catch (e) { setErr(e.message); } } async function toggleActive(p) { try { if (p.active) await window.AppApi.del(`/admin/personnel/${p.id}`); else await window.AppApi.patch(`/admin/personnel/${p.id}`, { active: true }); load(); } catch (e) { setErr(e.message); } } function startEditDuties(p) { setEditId(p.id); setEditDuties(p.duties || []); } async function saveDuties(p) { try { await window.AppApi.patch(`/admin/personnel/${p.id}`, { duties: editDuties }); setEditId(null); load(); } catch (e) { setErr(e.message); } } return (
Admin · support staff

Personnel

Support staff and their duties, with a login for each.

{!adding && }
{/* One-time credentials flash */} {flash && (
Login for {flash.name}
Username: {flash.username}  ·  Temp password: {flash.temp_password}
Shown once — hand these to the staffer. They sign in with the username (or phone) + this password.
)} {/* Add form */} {adding && (
DUTIES (pick one or more)
setForm({ ...form, duties: d })} />
{err &&
{err}
}
)} {err && !adding &&
{err}
} {/* List */} {loading ?
Loading…
: list.length === 0 ? (
No personnel yet — add your first support-staff member above.
) : (
{list.map((p) => ( ))}
Name Login Duties Actions
{p.name}
{p.phone &&
{p.phone}
}
{p.username || "—"} {editId === p.id ? (
{" "}
) : (
{(p.duties || []).length === 0 && —} {(p.duties || []).map((d) => ( {dutyLabel(d)} ))}
)}
{" "}
)}
); }; window.PersonnelScreen = PersonnelScreen;