"use client"; /* Sign for a picked request, then Done. A request still "accepted" is moved to "picking" first (the queue only lets a picked request be collected); request.collected writes the issue rows, the signature and, when asked, the slip to the wearer's staff app. */ import { useEffect, useState } from "react"; import { useParams } from "next/navigation"; import { useDerived, useSnap } from "@/lib/client"; import { key } from "@/lib/compute"; import { plural } from "@/lib/today"; import { PICK_STATUSES } from "@/lib/workcount"; import { useRequests, type RequestRow } from "@/components/requests/RequestList"; import { useBasket } from "@/components/MBasket"; import SignFlow from "@/components/SignFlow"; import { MBody, MButton, MEmpty, MError, MHead, MKick, MRule, MTop } from "@/components/m"; import { garmentSize, personMeta, segment } from "@/components/m/work/util"; export default function RequestSign() { const id = segment(useParams<{ id: string }>().id); const { s, mutate } = useSnap(); const { byId, staffById } = useDerived(); const { data, error, reload } = useRequests(); const basket = useBasket(); // Held once loaded: the hand-over refreshes the queue, and the Done screen must not turn into // "moved on" when this request leaves it. const [held, setHeld] = useState<{ r: RequestRow; ready: boolean } | null>(null); const [seen, setSeen] = useState(false); const stored = basket.picked(id); useEffect(() => { if (held || !data) return; const r = data.requests.find((x) => x.id === id); if (r && PICK_STATUSES.has(r.status) && r.bag.length) { const ready = r.status === "ready" || r.bag.every((l) => (stored[l.id] ?? 0) >= l.qty); setHeld({ r, ready }); } setSeen(true); }, [data, id, held, stored]); const shell = (body: React.ReactNode) => ( <> {body} ); if (!held) { if (!data && error) return shell(<> void reload()} />); if (!data || !seen) return shell(Loading); return shell(<>); } const { r } = held; if (!held.ready) { return shell(<>); } const st = staffById[r.staffId]; const total = r.bag.reduce((t, l) => t + l.qty, 0); return ( } lines={r.bag.map((l) => ({ key: l.id, name: garmentSize(byId[l.itemId], l.item, l.size), qty: l.qty }))} signerName={r.staffName} slip={{ available: !!st?.selfEmail }} barLabel={`Hand over ${plural(total, "item")}`} spentHref={`/m/request/${r.id}`} commit={async ({ sigId, slip }) => { // Accepted cannot go straight to collected. If the pick screen already moved it, this // refusal is expected and collected below answers for the real state. if (r.status === "accepted") await mutate("request.pick", { id: r.id }); const c = await mutate("request.collected", { id: r.id, sigId, slip }); if (!c.ok) { void reload(); return { ok: false, error: c.error }; } basket.clear("picked", r.id); void reload(); return { ok: true, done: { head: "Request handed over", sub: `${r.staffName} · ${plural(total, "item")}${slip ? " · slip sent" : ""}`, shelfKeys: r.bag.map((l) => key(l.itemId, l.si)), next: "work", }, }; }} /> ); }