/* ==================================================================
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 && (
)}
{err && !adding &&
{err}
}
{/* List */}
{loading ?
Loading…
: list.length === 0 ? (
No personnel yet — add your first support-staff member above.
) : (
| Name |
Login |
Duties |
Actions |
{list.map((p) => (
|
{p.name}
{p.phone && {p.phone} }
|
{p.username || "—"} |
{editId === p.id ? (
{" "}
) : (
{(p.duties || []).length === 0 && —}
{(p.duties || []).map((d) => (
{dutyLabel(d)}
))}
)}
|
{" "}
|
))}
)}
);
};
window.PersonnelScreen = PersonnelScreen;