"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 (
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) => }
);
}