DN Ops Dashboard β Live API Architecture
Migrate from nightly snapshot.json to a live FastAPI backend. Same visuals, live data, date-range filtering. Architecture migrates ops.delinerds.com to match the BEO app pattern.
Context & Goal
The DN Ops Dashboard at ops.delinerds.com is currently a static ~2,000-line HTML file served via CF Pages. Data comes from a nightly-generated snapshot.json baked at build time. This makes the dashboard stale, unfilterable, and unable to support any write-back actions.
Architecture After This Build
Port 5050 and the LaunchAgent plist (com.openclaw.deli-ops-dashboard) are pre-configured with WAREHOUSE_BACKEND=postgres and correct DSN. The backend code directory is empty β this build populates it.
Build Steps
FastAPI Backend β Port 5050
Create workspace/projects/deli-ops-dashboard/backend/ with the following structure. Use beo-api as the template for auth, CORS, and DB pool patterns.
βββ main.py
βββ db.py β import get_connection from warehouse_db.py
βββ config.py
βββ routers/
β βββ overview.py
β βββ sales.py
β βββ inventory.py
β βββ labor.py
β βββ catering.py
β βββ finance.py
βββ requirements.txt
Cf-Access-Authenticated-User-Email header. No app-level login β CF Access handles it.API Endpoints
All endpoints accept optional from_date and to_date query params (ISO date, default last 30 days). Auth required on all.
| Endpoint | Source Table(s) | Returns |
|---|---|---|
GET /health | β | DB status + warehouse last sync timestamp |
GET /api/overview | square_orders, sync_log | Revenue 30d, orders 30d, AOV, top category, warehouse freshness |
GET /api/sales/daily | square_orders, square_order_items | Daily revenue + order buckets |
GET /api/sales/by-channel | square_orders | In-Person / Online / Catering / Delivery split |
GET /api/sales/by-category | square_order_items, square_catalog | Revenue by menu category |
GET /api/items/top | square_order_items, square_catalog | Top items by revenue + units |
GET /api/labor/summary | square_labor | Total hours, OT, labor cost, labor % revenue |
GET /api/labor/daily | square_labor | Daily hours + cost buckets |
GET /api/food-cost/summary | marketman_invoices | Total spend, food cost %, top vendor |
GET /api/food-cost/by-vendor | marketman_invoices, marketman_invoice_items | Vendor spend breakdown |
GET /api/prime-cost | square_orders + square_labor + marketman_invoices | Revenue, COGS, labor, prime cost % |
GET /api/inventory/eighty-six | marketman_inventory_counts, marketman_items | Items at qty β€ 0 with days_at_zero |
GET /api/inventory/at-risk | marketman_inventory_counts, marketman_items | Items below par level |
GET /api/inventory/count-discipline | marketman_inventory_counts | Count completeness stats |
GET /api/catering/summary | public.airtable_beos | Event count, confirmed, revenue, avg event value |
public.airtable_beos (historical) until dn_catering β dn_warehouse migration completes. Mark with # TODO: switch to catering schema post-migration.Frontend Rewrite
Replace frontend/dist/index.html. Same visual design (dark theme, Inter, existing tab structure). Key changes:
- Remove
snapshot.jsonβ delete all references - Remove password gate (
Delinerd$1) β CF Access handles auth entirely - Add global date range picker in top nav (default: last 30 days) β all API calls use selected range
- Add loading skeletons per section while fetch resolves
- Add "Data as of [timestamp]" freshness indicator in top nav
- Add inline error states β never full-page error
- Implement URL hash routing per approved 2026-05-05 spec:
#exec,#daily,#inventory/eighty6,#inventory/oos,#inventory/discipline,#finance,#catering - Add NAV RULE comment block in JS as specified
Infrastructure
Three infrastructure changes required:
- Add to
~/.cloudflared/config.ymlingress:hostname: ops-api.delinerds.com β http://localhost:5050. Restart cloudflared. - Add DNS CNAME in
delinerds.comzone (id:dffc74fe06aedf828b67012bcc616f87):ops-api β <tunnel-id>.cfargotunnel.com, proxied. - Create CF Access app on account
bd7970f7f6f702c95697ea234e39d3b2: domainops-api.delinerds.com, allow@delinerds.comemail domain.
After backend code is written: launchctl unload then launchctl load the plist. Deploy frontend: npx wrangler pages deploy dist --project-name deli-nerds-ops-dashboard.
ARCHITECTURE.md
Create workspace/projects/deli-ops-dashboard/ARCHITECTURE.md documenting: port 5050, DB (dn_warehouse public schema), auth pattern, tunnel hostname, CF Pages deploy command, LaunchAgent label.