"use client"; import Link from "next/link"; import { useEffect, useState } from "react"; import { useSnap } from "@/lib/client"; /* The first-run checklist. Six things a new room does once, each read from the records rather than * remembered: a tick appears because the staff register has a row in it, not because somebody * clicked "done". Every row links to the screen that does it. The panel goes on its own once all * six are ticked, or once the room is 60 days old with three or more ticked, and an admin can put * it away with Dismiss (Facility.checklistDismissed). Today renders it through useSetupSteps(). */ const DAY = 86_400_000; export type SetupStep = { label: string; done: boolean; href: string; cta: string }; /** The steps, the tally and whether the checklist should show. `welcome` comes from ?welcome=1. */ export function useSetupSteps(): { steps: SetupStep[]; done: number; finished: boolean; visible: boolean; dismiss: () => Promise } { const { s, mutate } = useSnap(); const [hidden, setHidden] = useState(false); const [welcome, setWelcome] = useState(false); useEffect(() => { if (new URLSearchParams(window.location.search).get("welcome") === "1") setWelcome(true); }, []); const admin = s.session.role === "Admin"; const steps: SetupStep[] = [ { label: "Add staff, or import the register", done: s.staff.length > 0, href: "/app/settings?tab=data", cta: "Settings › Data" }, { label: "Add garments", done: s.catalog.length > 0, href: "/app/settings?tab=data", cta: "Settings › Data" }, { label: "Set reorder levels", done: Object.values(s.stock).some((x) => x.reorder !== null && x.reorder !== undefined), href: "/app/stock", cta: "Stock" }, { label: "Record opening stock", done: s.moves.length > 0 || Object.values(s.stock).some((x) => x.opening > 0), href: "/app/stock", cta: "Stock" }, { label: "Issue a garment", done: s.issues.length > 0, href: "/app/counter", cta: "Counter" }, { label: "Bind a barcode or print labels", done: Object.keys(s.barcodes).length > 0, href: "/app/stock", cta: "Stock" }, ]; const done = steps.filter((x) => x.done).length; const ageDays = Math.floor((Date.now() - new Date(s.createdAt).getTime()) / DAY); const finished = done === steps.length || (ageDays >= 60 && done >= 3); const visible = !hidden && !s.settings.checklistDismissed && (!finished || welcome); async function dismiss() { setHidden(true); if (admin) await mutate("settings.checklist", { dismissed: true }); } return { steps, done, finished, visible, dismiss }; } export default function Checklist({ welcome, onDismissWelcome }: { welcome: boolean; onDismissWelcome: () => void }) { const { s } = useSnap(); const admin = s.session.role === "Admin"; const { steps, done, finished, visible, dismiss: hide } = useSetupSteps(); const [gone, setGone] = useState(false); const show = !gone && (visible || (welcome && !s.settings.checklistDismissed)); if (!show) return null; async function dismiss() { setGone(true); onDismissWelcome(); await hide(); } return (
{welcome ? "Welcome to ThreadCount" : "Getting set up"}
{done} of {steps.length}
{steps.map((st) => (
{st.done ? "✓" : "○"}
{st.label}
{!st.done && {st.cta}}
))}
{(admin || welcome) && (
)}
); }