"use client"; /* 1H — Shelf check. Kill the "have you got any size 12s?" message before it is sent. * * Words, never counts. Wards see In stock / Low / None on shelf; the number stays in the linen * room. That is partly the product rule and partly honesty — the word here is as fresh as the last * stocktake of that garment, which is what the one closing line says and why no row implies a live * figure. * * Every row is a way on. A size that is not there leads to the waitlist; a size that is leads to * the request screen with the garment and size already chosen. A row that did neither left somebody * who had just found their size with nothing to tap. */ import Link from "next/link"; import { useMemo, useState } from "react"; import { MBody, MRule, MSection, MTop, inputStyle } from "@/components/m"; import { DIVIDER, INK, Kicker, N600, N700, StockTag } from "@/components/staffui"; import { fmtDate } from "@/lib/compute"; type Size = { size: string; si: number; word: string; countedOn: string }; type Item = { id: string; item: string; type: string; gender: string; sizes: Size[]; recorded: string }; export default function ShelfScreen({ items }: { items: Item[] }) { const [q, setQ] = useState(""); const shown = useMemo(() => { const needle = q.trim().toLowerCase(); if (!needle) return items; return items.filter((i) => i.item.toLowerCase().includes(needle) || i.type.toLowerCase().includes(needle) || i.sizes.some((s) => String(s.size).toLowerCase() === needle)); }, [items, q]); // The most recent stocktake anywhere in what is on screen: one date, in one line, at the foot. const counted = shown.flatMap((i) => i.sizes).map((s) => s.countedOn).filter(Boolean).sort().reverse()[0] || ""; return ( <> {/* A detail screen: a chevron, and no tab bar. The bar belongs to the five tab roots, and on this screen it marked none of them as current — five unselected tabs and no aria-current tell a screen reader the app is on no tab at all, and eat 66px of a shelf list. */}
Wards see words, not counts {/* Named, not just placeheld: the placeholder is gone as soon as anyone types, and a box that then announces itself as "edit, blank" is a box nobody can come back to. */} setQ(e.target.value)} placeholder="Search garments or a size" aria-label="Search garments or a size" autoComplete="off" style={{ ...inputStyle, width: "100%", marginTop: 12 }} />
{shown.length === 0 && (
Nothing matches “{q}”.
)} {shown.map((it) => (
{it.sizes.map((s) => { const mine = !!it.recorded && String(it.recorded) === String(s.size); const none = s.word === "none"; const href = none ? `/my/waitlist?item=${encodeURIComponent(it.id)}&si=${s.si}` : `/my/request?item=${encodeURIComponent(it.id)}&si=${s.si}`; return ( {s.size}{mine ? " · your size" : ""} ); })}
))}

Availability comes from the last linen-room stocktake{counted ? ` — most recently ${fmtDate(counted)}` : ""}.

); }