"use client"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { useState } from "react"; import { useSnap } from "@/lib/client"; import { ErrorLine, Field, LiveRegion } from "@/components/ui"; import { Panel, Tag } from "@/components/portal"; import { FTE_OPTIONS, UNIFORM_STYLES, ccOf, entOf, fmtDate, isNursing, type StaffRec } from "@/lib/compute"; import ManagerBox from "./ManagerBox"; import ReportsBox from "./ReportsBox"; import SelfService from "./SelfService"; import { fullName, type Act } from "./shared"; export default function DetailsTab({ st, act, edit, base }: { st: StaffRec; act: Act; edit: boolean; base: string }) { const { s, isAdmin, mutate } = useSnap(); const [offMsg, setOffMsg] = useState(""); const reports = s.staff.filter((x) => x.managerId === st.id && !x.inactive) .sort((a, b) => `${a.last} ${a.first}`.localeCompare(`${b.last} ${b.first}`)); const hasHistory = s.issues.some((i) => i.staffId === st.id) || s.orders.some((o) => o.staffId === st.id); /* Taking somebody off the register also closes the requests still waiting on approval (staff.patch, same transaction). Nobody is emailed, so the count is said out loud here. */ async function deactivate() { setOffMsg(""); const r = await mutate<{ closedRequests?: number }>("staff.patch", { id: st.id, inactive: true }); if (!r.ok) { setOffMsg(r.error); return; } const n = r.result?.closedRequests ?? 0; if (n) setOffMsg(`${n} ${n === 1 ? "request was" : "requests were"} waiting on approval and ${n === 1 ? "has" : "have"} been closed.`); } return (
{edit ? : }
Requests from {st.dept} : undefined}>
{isAdmin ? ( ) : (
{st.wardDesk ? {st.dept ? `Ward desk · ${st.dept}` : "Ward desk"} : "Not on a ward desk."}
)}
{isAdmin && (
{!st.inactive ? : } {!hasHistory && ( )}
)}
); } function ReadDetails({ st, act, isAdmin, base }: { st: StaffRec; act: Act; isAdmin: boolean; base: string }) { const { s } = useSnap(); const of = entOf(s, st), limited = Number.isFinite(of); const fte = st.fte || ""; const Row = ({ k, children }: { k: string; children: React.ReactNode }) => (
{k}{children}
); return ( Edit details : undefined}>
{st.phone ? {st.phone} : "–"} {st.dept || "–"} {ccOf(s, st) || "–"} {st.ccOverride ? "override" : "from ward"} {st.top || "–"} / {st.pants || "–"} {st.uniformStyle || "Not set (every style)"} {st.start ? fmtDate(st.start) : "–"} {limited ? <>{of} garments : "Not measured"}
Combined FTE {isAdmin ? ( ) : {fte || "Not recorded"}}
); } function EditDetails({ st, base }: { st: StaffRec; base: string }) { const { s, mutate } = useSnap(); const router = useRouter(); const [f, setF] = useState({ first: st.first, last: st.last, phone: st.phone, top: st.top, pants: st.pants, ent: st.ent === null ? "" : String(st.ent), group: st.group, dept: st.dept, ccOverride: st.ccOverride, start: st.start, uniformStyle: st.uniformStyle }); const [err, setErr] = useState(""); const [busy, setBusy] = useState(false); const ccCodes = [...new Set(s.depts.map((d) => d.cc).filter(Boolean))]; const nursing = isNursing(s, { ...st, group: f.group }); const close = () => router.replace(`${base}?tab=details`, { scroll: false }); async function save() { if (!f.first.trim() || !f.last.trim()) return; setBusy(true); setErr(""); const r = await mutate("staff.save", { id: st.id, num: st.num, ...f, ent: f.ent === "" ? null : parseInt(f.ent, 10) || 0, notes: st.notes }); setBusy(false); if (!r.ok) { setErr(r.error); return; } close(); } return ( Staff no. {st.num} can't change }>
{([["first", "First name"], ["last", "Last name"], ["phone", "Phone"], ["top", "Top size"], ["pants", "Pants size"]] as const).map(([k, lbl]) => ( {(c) => setF({ ...f, [k]: e.target.value })} />} ))} {(c) => ( )} {(c) => nursing ? : setF({ ...f, ent: e.target.value.replace(/[^0-9]/g, "") })} />} {(c) => } {(c) => } {(c) => setF({ ...f, start: e.target.value })} />} {(c) => }
); }