"use client"; /* 1A — Home. * * Answer "is anything waiting for me?" in one glance, then get out of the way. One live thing at * the top — the request furthest along — and everything else is a shortcut. There is no list of * orders here on purpose: that is what the Orders tab is for, and a home screen that tried to be * both would be neither. */ import { useState } from "react"; import { MBody, MTopBrand } from "@/components/m"; import { Banner, DarkCard, DarkRow, EdgeRow, IdentityBlock, Kicker, N600, Notice, QuickGrid, SecondaryBar, } from "@/components/staffui"; import StaffNav from "@/components/staffnav"; import { statusText } from "@/lib/staffreq"; import type { ReqRow } from "@/lib/staffdata"; type Data = { name: string; num: string; ward: string; facility: string; hasManager: boolean; wardDesk: boolean; holding: number; live: ReqRow | null; openCount: number; notice: string; /** Open requests this person raised for somebody else — never their own. */ raisedOpen: ReqRow[]; }; const ic = (d: React.ReactNode) => ( {d} ); export default function HomeScreen({ data, approvals, roundBags, kitCheckDue, canRaiseForTeam }: { data: Data; approvals: number; roundBags: number; kitCheckDue: string | null; /** Does anybody name this person as their manager? If so they may raise for them. */ canRaiseForTeam: boolean; }) { const [leaving, setLeaving] = useState(false); const live = data.live; const st = live ? statusText(live) : null; return ( <> { setLeaving(true); await fetch("/api/staff/logout", { method: "POST", headers: { "content-type": "application/json" }, body: "{}", }).catch(() => {}); // A full navigation: the cookie has just been cleared and every screen behind it is // server-rendered. window.location.replace("/my/signin"); }} style={{ background: "none", border: "1px solid rgba(243,242,242,0.4)", color: "var(--color-neutral-400)", font: "inherit", fontSize: 11, fontWeight: 800, letterSpacing: "0.08em", textTransform: "uppercase", padding: "6px 10px", borderRadius: 0, cursor: leaving ? "wait" : "pointer", flex: "0 0 auto", minHeight: 30, }} >{leaving ? "…" : "Sign out"} } /> {/* A manager with people waiting on them sees it before anything of their own. The queue is the one thing in this app where somebody else is blocked until they act. */} {approvals > 0 && ( { window.location.href = "/my/approvals"; }} /> )}
{live && st ? ( /* One request covers as many garments as the person needed, so the card leads with the * summary — "5 garments · Tunic, Trousers, Fleece" — and the list itself is a tap away * on the order. This screen answers "is anything waiting for me?" and then gets out of * the way; a home screen that unpacked every request would be the Orders tab. * * The decision rides alongside the status because on a split one the status word is a * half-truth: "Approved — with linen room" over an ask where the fleece was knocked * back has somebody expecting three garments in a bag that holds two. */ 1 ? live.decision : "", st.note].filter(Boolean).join(" · ")} > {live.collectCode && } ) : ( 0 ? "Nothing on the way" : "Nothing yet"} title={data.holding > 0 ? `${data.holding} garment${data.holding === 1 ? "" : "s"} with you` : "No uniform on your record"} meta={ data.hasManager ? "Ask for something and it goes to your manager first." : "Your manager isn’t recorded yet — the linen room has to set who approves your requests before you can ask for anything." } /> )}
Quick actions
) }, { label: "Swap a size", href: "/my/request?swap=1", icon: ic(<>) }, { label: "Report damage", href: "/my/damage", icon: ic(<>) }, { label: "What’s on the shelf", href: "/my/shelf", icon: ic(<>) }, ]} /> {/* The desk’s own work, only for the person on it: signing the trolley in. The round is listed by ward, so a clerk whose ward was never filled in has no round to open — /my/round refuses a blank ward, and signing is fenced the same way on the server. The card still appears, because the desk work is real and hiding it would tell her nothing; it just stops being a link and names the missing ward. Reading "nothing on the round right now" and tapping through to a not-found page was the worst of both: indistinguishable from a quiet day, and no clue the linen room had to fix anything. */} {data.wardDesk && (
{data.ward ? ( 0 ? `${roundBags} bag${roundBags === 1 ? "" : "s"} to sign` : "Ward round"} meta={roundBags > 0 ? "Arriving on your ward today." : "Nothing on the round for your ward right now."} /> ) : ( )}
)} {/* Somebody who will not type a request themselves asks the person who approves it, which is the one door left for raising on another person's behalf. The server sends anything a manager raises up a level, which is why the card can say so plainly — approving your own raise is the one thing this must never let happen. */} {canRaiseForTeam && (
)} {/* What they have raised for other people, as one row rather than a list. Every list in this app starts from the wearer, so until Orders grew a Raised tab a request somebody typed in for a colleague vanished the moment it was sent — and the raise screen promises they will see the outcome. Home is not the place for the list itself (that rule is the whole shape of this screen), but it is the place to say the requests exist and where they went. */} {data.raisedOpen.length > 0 && (
Raised by you
{data.raisedOpen.length === 1 ? `One request open for ${data.raisedOpen[0].subjectName}` : `${data.raisedOpen.length} requests open for other people`}
Not yours to collect — this is where they got to.
)} {/* An open cycle they haven’t finished. Not a banner — this isn’t urgent, it’s a chore with a deadline, and dressing it as an alert would devalue the ones that are. The title is word for word the heading on the screen it opens, and it no longer asks about a locker. Wearers take their uniform home and wash it themselves; there is no locker to stand in front of, so the only answerable question is what they still have, wherever it happens to be that day. */} {kitCheckDue && (
)} {data.notice &&
{data.notice}
} {/* Sitting under everything else because it is the least often needed thing here — but it is the only way a wearer can end a session on a phone they no longer have, so it has to be somewhere they can find without asking. The label names privacy too: the policy and the answer about deleting an account live behind this row, and somebody looking for either would never guess that "your sign-in" was where they were kept. */}
); }