"use client"; /* 2D — Ward round manifest. What the desk sees when the trolley arrives. * * Unclaimed bags from previous rounds sit **above** today's work. They are the linen room's * biggest waste — a bag signed for on the ward and never collected is a garment out of stock and * off the record — so the screen refuses to bury them under whatever arrived this morning. * * Anyone on the ward can sign, and whoever does is named on the requester's order. That is the * whole audit story: a missing bag has a name against it. */ import { useState } from "react"; import { MBody, MError, MRule, MTop } from "@/components/m"; import { ACCENT_300, CompactAction, DoneRow, EdgeRow, GROUND, INK, Kicker, LineList, N300, N400, N600, N700, SecondaryBar } from "@/components/staffui"; import { useStaff } from "@/lib/staffclient"; import { statusText } from "@/lib/staffreq"; import type { ReqLine, ReqRow } from "@/lib/staffdata"; import { formatInZone } from "@/lib/compute"; type Bag = { id: string; code: string; subjectName: string; /** What is actually in the bag: the approved lines, and nothing the manager knocked back. */ lines: ReqLine[]; summary: string; garments: number; lineCount: number; status: string; signerName: string | null; signedAt: string | null; claimedAt: string | null; since: string; }; export default function RoundScreen({ ward, toSign, unclaimed, signedToday, raised }: { ward: string; toSign: Bag[]; unclaimed: Bag[]; signedToday: Bag[]; /** Open requests this person raised for someone else, however they came to raise them. */ raised: ReqRow[]; }) { const { mutate, busy, me } = useStaff(); /* `signedAt` arrives as a UTC instant, and both of the places it is shown used to read it as * local text — slicing the first ten characters for the date, and formatting with no zone for the * time. The ward round happens in the morning, which is exactly when the UTC date is still * yesterday's, so the desk was routinely told a bag it signed for an hour ago went out the day * before. The facility's zone answers both. */ const tz = me.tz; const [err, setErr] = useState(""); async function sign(id: string) { const r = await mutate("round.sign", { id }); if (!r.ok) setErr(r.error); } async function claim(id: string) { const r = await mutate("round.claim", { id }); if (!r.ok) { setErr(r.error); return; } window.location.reload(); } return ( <> {ward}} />
{toSign.length}
{toSign.length === 1 ? "bag to sign" : "bags to sign"}
{toSign.length === 0 ? "nothing waiting" : "arriving today"}
{signedToday.length > 0 && (
{signedToday.length} signed
)}
setErr("")} /> {unclaimed.length > 0 && ( <>
Unclaimed from earlier rounds
{unclaimed.map((b) => (
{b.subjectName}
{b.summary} · {b.code}
signed by {b.signerName === me.name ? "you" : b.signerName} {b.signedAt ? ` ${formatInZone(b.signedAt, tz)}` : ""}
{ window.location.assign(`/my/orders/${b.id}/messages`); }} /> {/* The desk’s own way out of this list. Nudging only works if the requester eventually opens the app; often the bag went days ago and the person who knows that is the clerk standing where it used to be. */} claim(b.id)} />
))}
)}
Arriving today
{toSign.length === 0 && signedToday.length === 0 ? (
Nothing on the round for {ward || "your ward"} right now.
) : ( <> {toSign.map((b) => (
{b.subjectName}
{b.summary} · {b.code}
sign(b.id)} />
{/* Signing is a signature: whoever puts their name to a bag of four garments should be able to see the four before they do, not a count. Nothing declined is listed — a knocked-back garment never reaches the trolley. */} {b.lineCount > 1 && (
)}
))} {signedToday.map((b) => (
{b.subjectName}
signed {b.signedAt ? formatInZone(b.signedAt, tz, { hour: "2-digit", minute: "2-digit", hour12: false }) : ""} by {b.signerName}
))} )} {/* What this person raised in somebody else's name, still on its way. The bags above are only the ones arriving today; a request typed in on Tuesday and approved on Thursday is invisible there until it turns up on a trolley, so without this the only way to find out where it had got to was to ring the linen room. */} {raised.length > 0 && ( <>
Raised by you · still open
{raised.map((r) => { const st = statusText(r, { mine: false, first: r.subjectName?.split(" ")[0] }); return (
{r.subjectName} {r.code}
{r.summary}
{[st.label, st.note].filter(Boolean).join(" · ")}
); })}
)}

Anyone on the ward can sign. Whoever does appears on the requester’s order, so a missing bag has a name against it.

{toSign.length > 1 && (
{/* Bulk signing is allowed but not encouraged — a secondary action, never the red one. */} { for (const b of toSign) { const r = await mutate("round.sign", { id: b.id }); if (!r.ok) { setErr(r.error); return; } } }} />
)} ); }