Two parallel asks bundled into one arc on 2026-05-08: NIX-OS-86 dashboard / reports rework per spec §13 (Ranking of POS, Top Products under it, Recent Orders trimmed, Sales Trend stays daily) plus a net-new manager sidebar (spec §10.1, "Show multiple Points of Sales") rendered as a right-side toggle drawer with live polling. Both surfaces share the same per-config summary shape (Cash + Bank + Sold today), so R7.1 builds the data once and R7.3 / R7.6 consume it. Bank Balance is computed from cafe.order_payments non-cash rows on the live session — cafe-master tenants get the real value, Odoo-master path returns 0 placeholder until a non-cafe-master Pro tenant exists.
test-r7-prod.mjs, exercised on both get-coffee (Pro / cafe-master since 2026-05-08 R6.6) and lumiere-coffee (Starter). Six R7 sub-phases + 5 follow-up commits shipped in one session:
/dashboard trimmed from 6 cols to 4 (dropped Items + Status; Payment was never rendered). Pure UI cleanup.bankToday field added to MirrorPosConfigSummary + PosConfigSummary + LandingConfig + Starter landing. Cafe-master path computes via JOIN cafe.order_payments → cafe.orders filtered to state='paid' AND is_cash=false, summed per session. Odoo-master path stays at 0 (defer until a non-cafe-master Pro tenant exists).aggregateRevenueByPosConfig(tenantId, start, end, shopId?) in lib/db/orders.ts. GROUP BY pos_config_id, filters out NULL pos_config_id rows (legacy R5.2-pre orders). Card on /dashboard with rank number / register label / progress bar / revenue + order count. Labels resolve via cafe.pos_configs.name (cafe-master) → shop name + " Register" → "Register #N" fallback./dashboard right of Ranking (spec: "right under Ranking of POS" — read as either-direction adjacent). Reuses period.topProducts already returned by aggregateStarterPeriod — no new aggregator needed./reports gets a full-width Ranking of POS card above the existing 2-col Top Products + Tax row. /reports/daily gets a full-width Ranking card above the existing Tender + Top Products grid; the grid was reordered (Top Products on left, Tender on right) so Top Products sits "directly under" the ranking per spec.nix_cafe.dashboard.view. Click → slides a w-[440px] panel from the right with shop sections of register cards: state chip, Cash / Bank / Sold cells, hover-to-green border, click → /cafe/pos/register/<configId> in a new tab. fetch('/cafe/api/manager-live') on open + every 30s while open; ESC or backdrop closes; interval cleared on unmount. Shared loadManagerLiveData in lib/db/manager_live.ts mirrors pos/page.tsx's inline buildLanding but always scopes to the user's full accessible set (not ?shop=).R7-followup commits (same session):
490db70 — Refactor: pos/page.tsx inline buildLanding consolidated into lib/db/manager_live.ts's exported buildLandingFromConfigs. Net -133 LOC from pos/page.tsx; one canonical implementation drives both surfaces.fa3e88a — Odoo-master bankToday extension (two extra RPCs: pos.payment searchRead + pos.payment.method read; sums non-cash by session). Drawer auto-refresh tuning: Page Visibility API integrated (skip polling when document.hidden; refetch on visibility regain) + manual refresh button in drawer header (spinning while fetching) + footer shows "Updated Xs ago · auto-refresh 30s" once fetched. Bumps R7 prod test from 13 to 14 checks.NIX-OS-89 carried-forward follow-ups (closed in same session):
4ca801d backend + fa3fa44 cafe). cafe.product_categories gains odoo_pos_categ_id + source columns with same partial-unique discriminator pattern as products/customers/methods. New listCategoriesFromMirror DAO; Starter listCategories filtered with IS NULL. listProductsForAdminMirror + listProductsForPosFromMirror JOIN cafe.product_categories so the POS grid's category filter resolves names. Backfill script backfill-pro-tenant-categories.ts mirrors Odoo pos.category (6 rows on get-coffee) + UPDATEs cafe.products.category_id via odoo_pos_categ_ids[0] dereferencing (6 products linked).380f91a). MirrorProduct gains image_url?: string | null, surfaced from cafe.products.image_url. pro-handlers.ts prefers image_url over image_128 base64 fallback. Backfill script backfill-pro-tenant-product-images.ts pulls image_128 from Odoo + stores data:image/png;base64,... in the existing image_url text column. Skipped R2 — data URL works at current scale (~17 products × 4-8KB); R2 migration is a future cleanup. Get-coffee had 1 product with image_128 set in Odoo; rest have no images uploaded there.9f664b2 backend + 380f91a cafe). cafe.orders.cafe_customer_id UUID NULL REFERENCES cafe.customers(id) ON DELETE SET NULL + same-migration backfill from existing odoo_partner_id matches (linked 2 prod rows). Drizzle schema + CreateOrderInput + POST body all accept the new field. odoo_partner_id is kept (sync worker still uses it to push to Odoo). Picker UI not yet upgraded to send UUID — plumbed but unused on the client; future tweak.| Suite | Score | Status |
|---|---|---|
| test-r7-prod (this bundle, post-followups) | 14/14 | PASS |
| test-nix-os-87-prod (settings layout — touches modified layout.tsx) | 11/11 | PASS |
| test-receipt-fixes-prod | 6/6 | PASS |
| test-phase1-prod (smokes /pos route — touches refactored buildLanding) | 11/11 | PASS |
| test-phase4-prod (writes orders — exercises R6.3c cafeCustomerId path) | 10/10 | PASS |
| Total | 52/52 | PASS |
Phase4 ran solo per the documented "first-parallel-hit flake" memory; passed clean on first solo run. The other three regressions ran in parallel. Re-validation passed after each follow-up commit; final R7 14/14 + phase4 10/10 confirmed post-deploy of 380f91a.
R7 arc (commit f616460, +937/-76 across 14 files): app/(authed)/dashboard/page.tsx R7.2 + R7.3 + R7.4 app/(authed)/reports/page.tsx R7.5 (full-width Ranking above Top Products) app/(authed)/reports/daily/page.tsx R7.5 (server-side label resolution + ranking prop) app/(authed)/reports/daily/starter-daily-client.tsx R7.5 (Ranking row + Top Products / Tender reorder) app/(authed)/layout.tsx R7.6 (top strip always-on if dashboard.view) app/(authed)/pos/page.tsx R7.1 (bankToday plumbed into cardFor) app/(authed)/pos/pos-landing.tsx R7.1 (LandingConfig.bankToday) lib/db/orders.ts R7.3 (aggregateRevenueByPosConfig) lib/db/pos_configs.ts R7.1 (MirrorPosConfigSummary.bankToday + non-cash JOIN) lib/db/starter_landing.ts R7.1 (sessionBank lookup) lib/odoo/queries.ts R7.1 (PosConfigSummary.bankToday placeholder) lib/db/manager_live.ts NEW R7.6 shared loader (Starter + Pro paths) app/api/manager-live/route.ts NEW R7.6 GET endpoint, gated on dashboard.view components/manager-live-control.tsx NEW R7.6 trigger + drawer + 30s polling R7-followups (same session): 490db70 refactor: dedupe buildLanding into lib/db/manager_live (-133 LOC pos/page.tsx) fa3e88a feat: Odoo-master bankToday + drawer refresh button + visibility-aware polling NIX-OS-89 carried-forward follow-ups (same session): 4ca801d backend: cafe.product_categories Odoo mirror columns + partial unique fa3fa44 cafe: R6.7 mirror DAO + read flips + scripts/backfill-pro-tenant-categories.ts 9f664b2 backend: R6.3c cafe.orders.cafe_customer_id UUID FK + same-migration backfill 380f91a cafe: R6.8 image data URLs + R6.3c schema/route + scripts/backfill-pro-tenant-product-images.ts