"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.
: (
| Garment | Size | Qty | Issued | Signed | {onReturn && Action | }
{open.map((i) => {
const it = byId[i.itemId];
return (
| {label(it)}{i.preloved ? · pre-loved : null} |
{sizeOf(it, i.si)} |
{i.qty} |
{dayMonth(i.date)} |
|
{onReturn && (
|
)}
);
})}
)}
{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}
);
})}
);
}