← back to index

V0.3 — Bulk Archive products + template-only listLOCAL

Gate 1. Narong's Telegram, 2026-05-29: bulk archive on the admin Products page, refused while any POS session is open. Mid-Gate-1 pivot: Products page becomes template-only (one row per product_template); archive at template level only; variants get their own page (queued as V0.4). No migration — cafe.product_template.is_active already exists.

Summary

Status
10/10 local · typecheck clean · stopped for Gate 1 approval
Files
5 modified · ~700 LOC net · no migration · no schema · no backend
Surfaces
/cafe/products — template-only list, search bar (name + every variant SKU), Filters dropdown (Archived + Hidden URL toggles), checkbox column + select-all, sticky "N selected · Actions ▾" bar, Archive/Unarchive confirm dialog with inline session-blocker error.

10/10 local checks

countOpenSessionsForTenant exported from lib/db/cafe_sessions.ts with state='open' predicate
listProductTemplatesForAdmin + ProductTemplateRow exported with includeHidden + includeArchived opts
bulkArchive/UnarchiveProductTemplates DAOs present (single tenant-scoped UPDATE; sets is_active false/true)
Old H4-Y listProductsForAdminJoined + JoinedProductRow fully removed (no dead exports linger)
bulkArchiveProductsAction wired with countOpenSessionsForTenant session guard + "Cannot archive" error text
bulkUnarchiveProductsAction wired without session guard (unarchive can't disrupt a shift)
Products page passes includeArchived to DAO via ?archived=1 searchParam, mirrors ?hidden=1
Client wires search input + Filters dropdown + bulk bar + confirm dialog with all 11 testids present
DAO round-trip against local Postgres: create 3 templates → archive 2 → default list omits archived → includeArchived:true brings back → unarchive restores → variant count + min/max price aggregates correct
Open-session guard: countOpenSessionsForTenant reflects state='open' rows (synthetic INSERT lifts count by 1, DELETE restores)