"use client";
/* Home — today. Four figures, what’s just happened, and a way into a count. */
import Link from "next/link";
import { useMemo } from "react";
import { useDerived, useSnap } from "@/lib/client";
import { countsAsIssued, daysBetween, label, longLabel, onhand, touched, reorderAt, staffName, variantName } from "@/lib/compute";
import { INK, IconRight, MBody, MNav, MRow, MRule, MSection, MTopBrand } from "@/components/m";
function Stat({ n, l, hot }: { n: string; l: string; hot?: boolean }) {
return (
{n}
{l}
);
}
export default function MHome() {
const { s } = useSnap();
const { L, byId, staffById, variants } = useDerived();
const d = useMemo(() => {
const today = s.today;
let issued = 0, returned = 0;
for (const i of s.issues) {
if (i.date === today) issued += i.qty;
if (i.returned?.date === today) returned += i.qty;
}
const low = variants.filter((v) => touched(s, L, v.key) && onhand(s, L, v.key) <= reorderAt(s, v.key));
const lastCount = s.stocktakes.find((t) => t.mode !== "preloved");
const since = lastCount ? daysBetween(lastCount.date, today) : null;
// Recent activity, newest first. One line per person per day per kind — four garments handed
// to the same nurse in one go is one thing that happened, not four.
const grouped: Record = {};
for (const i of s.issues.slice(-120)) {
const add = (kind: "Issued" | "Returned", at: string) => {
const k = `${i.staffId}|${kind}|${at}`;
(grouped[k] ||= { staffId: i.staffId, kind, at, qty: 0 }).qty += i.qty;
};
add("Issued", i.date);
if (i.returned) add("Returned", i.returned.date);
}
const recent = Object.values(grouped)
.sort((a, b) => (a.at < b.at ? 1 : a.at > b.at ? -1 : 0))
.slice(0, 3)
.map((g) => ({
title: `${staffName(staffById[g.staffId], "Staff")} — ${g.qty} item${g.qty === 1 ? "" : "s"}`,
sub: `${g.kind} · ${g.at === today ? "today" : g.at}`,
mark: "ink" as const, href: `/m/person/${g.staffId}`, at: g.at,
})) as { title: string; sub: string; mark: "ink" | "accent"; href?: string; at: string }[];
// A line AT its reorder level is in `low` on purpose (reorder now, not once it's short), but
// "Below par · 3 of 3" reads as a contradiction on the phone, so name the two states apart.
for (const v of low.slice(0, 2)) {
const oh = onhand(s, L, v.key), par = reorderAt(s, v.key);
recent.push({ title: `${variantName(byId[v.itemId], v.size)}`, sub: `${oh < par ? "Below par" : "At par"} · ${oh} of ${par}`, mark: "accent", href: `/m/stock`, at: "" });
}
return { issued, returned, low: low.length, since, recent };
}, [s, L, byId, staffById, variants]);
const fac = [s.settings.facility, s.settings.location].filter(Boolean).join(" · ");
const dateLine = new Date(+s.today.slice(0, 4), +s.today.slice(5, 7) - 1, +s.today.slice(8, 10))
.toLocaleDateString("en-AU", { weekday: "long", day: "numeric", month: "long" });
return (
<>
{dateLine}
Today
0} />
30} />
{d.recent.length === 0
?
Nothing has moved yet today.
: d.recent.map((r, i) => (
{r.sub}} />
))}
Start a countIssue to someoneDeliveries, pickups, rounds and more