"use client"; /* Return, Hand in and Swap a size: the counter's other three modes. */ import { useMemo, useState } from "react"; import { useDerived, useSnap } from "@/lib/client"; import { Panel, QtyStepper, Tag } from "@/components/portal"; import { printHandInReceipt } from "@/components/dialogs"; import { key, label, onhand, plOf, type IssueRec, type StaffRec } from "@/lib/compute"; import { dayMonth, openIssuesOf, owedLinesOf, plural, sizeOf } from "./lib"; import styles from "./counter.module.css"; /** Every open issue line, ungrouped, with the signed toggle; Return when `onReturn` is given. */ export function HoldingPanel({ st, onReturn, onError }: { st: StaffRec; onReturn?: (i: IssueRec) => void; onError: (e: string) => void }) { const { s, mutate } = useSnap(); const { byId } = useDerived(); const open = useMemo(() => openIssuesOf(s, st.id), [s, st.id]); const owed = useMemo(() => owedLinesOf(s, st.id), [s, st.id]); const qty = open.reduce((t, i) => t + i.qty, 0); async function sign(i: IssueRec) { const r = await mutate("issue.receipt", { id: i.id, receipt: !i.receipt }); if (!r.ok) onError(r.error); } return ( {open.length === 0 ?
Nothing out.
: (
{onReturn && } {open.map((i) => { const it = byId[i.itemId]; return ( {onReturn && ( )} ); })}
GarmentSizeQtyIssuedSignedAction
{label(it)}{i.preloved ? · pre-loved : null} {sizeOf(it, i.si)} {i.qty} {dayMonth(i.date)}
)} {owed.length > 0 && ( <>
On order or waiting
{owed.map((o) => ( ))}
{label(byId[o.itemId])} {o.size || "–"} {o.qty} {o.where}
)}
); } export function ReturnedToday({ st }: { st: StaffRec }) { const { s } = useSnap(); const { byId } = useDerived(); const rows = s.issues.filter((i) => i.staffId === st.id && i.returned?.date === s.today); return ( t + i.qty, 0), "garment", "garments") : undefined}> {rows.length === 0 ?
Nothing returned today.
: rows.map((i) => { const it = byId[i.itemId]; const cond = i.returned!.cond; return (
{label(it)} · {sizeOf(it, i.si)} ×{i.qty}
{cond.replace("Returned - ", "")} {i.returned!.photoId && Photo}
); })}
); } export function HandInPanel({ st, onRecord }: { st: StaffRec; onRecord: () => void }) { const { s } = useSnap(); const { byId } = useDerived(); const list = s.handins.filter((h) => h.staffId === st.id).sort((a, b) => (a.date < b.date ? 1 : a.date > b.date ? -1 : 0)); return (
Hand-ins
{list.length === 0 ?
No hand-ins on file.
: list.map((h) => { const good = h.lines.filter((l) => l.cond === "Good").reduce((t, l) => t + l.qty, 0); const rag = h.lines.filter((l) => l.cond === "Rag").reduce((t, l) => t + l.qty, 0); return (
{dayMonth(h.date)}
{good} to pre-loved · {rag} rag
{h.credit && Credited}
); })}
); } function SwapRow({ issue, onDone, onError }: { issue: IssueRec; onDone: (msg: string) => void; onError: (e: string) => void }) { const { s, mutate } = useSnap(); const { L, byId } = useDerived(); const it = byId[issue.itemId]; const [si, setSi] = useState(-1); const [qty, setQty] = useState(1); const [busy, setBusy] = useState(false); const n = Math.min(Math.max(1, qty), issue.qty); if (!it) return null; async function swap() { if (si < 0 || busy) return; setBusy(true); const r = await mutate<{ size: string; qty: number }>("issue.exchange", { id: issue.id, si, qty: n }); setBusy(false); if (!r.ok) { onError(r.error); return; } setSi(-1); setQty(1); onDone(`Swapped ${label(it)} ${sizeOf(it, issue.si)} for ${r.result.size} ×${r.result.qty}.`); } return (
{label(it)} · {sizeOf(it, issue.si)} ×{issue.qty}
{issue.qty > 1 && }
); } export function SwapPanel({ st, onDone, onError }: { st: StaffRec; onDone: (msg: string) => void; onError: (e: string) => void }) { const { s } = useSnap(); const open = useMemo(() => openIssuesOf(s, st.id), [s, st.id]); return ( t + i.qty, 0), "garment", "garments")}> {open.length === 0 ?
Nothing out.
: open.map((i) => )}
); } /** Best effort: today's good returns with a new issue of the same garment, another size, today. */ export function SwappedToday({ st }: { st: StaffRec }) { const { s } = useSnap(); const { byId } = useDerived(); const mine = s.issues.filter((i) => i.staffId === st.id); const rows = mine .filter((i) => i.returned?.date === s.today && i.returned.cond === "Returned - Good") .flatMap((old) => { const nu = mine.find((n) => n.date === s.today && n.itemId === old.itemId && n.si !== old.si && n.qty === old.qty); return nu ? [{ old, nu }] : []; }); return ( {rows.length === 0 ?
Nothing swapped today.
: rows.map(({ old, nu }) => { const it = byId[old.itemId]; return (
{label(it)} · {sizeOf(it, old.si)}{sizeOf(it, nu.si)}
×{old.qty}
); })}
); }