Commit Graph

9 Commits

Author SHA1 Message Date
12bd589bb7 feat(connect): Phase 6 — menu-app and manager-app frontend SPAs
Two Vite + React 18 + Tailwind apps under connect_frontend/.
Both use React Router v6, Axios, react-hot-toast, and Lucide icons.
Neither uses localStorage — tokens live in sessionStorage (manager-app)
or component state (menu-app cart).

── menu-app (public menu + ordering) ──────────────────────────────

Routing (basename /menu):
  /:siteSlug              → MenuPage
  /:siteSlug/order        → CartPage
  /:siteSlug/confirm/:ref → OrderConfirm

Pages:
  MenuPage      — fetches menu snapshot, category nav tabs, product
                  cards; floating CartButton; opens ProductModal
  ProductModal  — quantity picker, quick-option toggles, price/discount
                  display, "Add to order" with line total
  CartPage      — order type selector (dine_in/delivery), customer
                  details form, cart summary, submits to cloud API
  OrderConfirm  — polls GET /api/orders/status/:ref every 10s;
                  renders status icon + label; stops polling on
                  terminal states (delivered/rejected)

Components:
  CategoryNav   — horizontal scrollable pill tabs
  ProductCard   — image, name, description, price with discount badge;
                  greyed + "Out of stock" badge when unavailable
  CartButton    — fixed bottom bar with item count + total

── manager-app (remote dashboard) ─────────────────────────────────

Routing (basename /manage):
  /login              → LoginPage
  /                   → SiteSelectorPage (auto-navigates if one site)
  /:siteId            → DashboardPage
  /:siteId/orders     → IncomingOrdersPage
  /:siteId/orders/history → OrderHistoryPage
  /:siteId/orders/:id → OrderDetailPage
  RequireAuth wrapper redirects to /login if no sessionStorage token

Pages:
  LoginPage          — email/password → POST /api/manager/login;
                       stores JWT in sessionStorage
  SiteSelectorPage   — lists accessible venues; auto-selects if one
  DashboardPage      — polls snapshot + pending orders every 15s;
                       2×2 stat grid + pending order list
  IncomingOrdersPage — polls pending orders every 15s; order cards
  OrderDetailPage    — full order detail; Accept/Reject with optional
                       rejection reason; lifecycle progression buttons
                       (Preparing → Ready → Delivered etc.)
  OrderHistoryPage   — all orders with status filter tabs

Components:
  RequireAuth   — route guard using sessionStorage token
  StatCard      — labelled metric tile
  StatusBadge   — colour-coded status pill
  OrderCard     — summary card linking to OrderDetailPage
  (no OrderCard uses useParams to get siteId for nav — wired correctly)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 00:25:28 +03:00
abe7f4ff0d feat(connect): return site_numeric_id in heartbeat response
Local backend needs the cloud DB integer PK to call Connect API
endpoints (menu sync, order pending poll). Heartbeat is the only
authenticated channel available at startup, so we piggyback the id
there rather than adding a new endpoint.

Changes:
  - schemas/site.py: site_numeric_id: int | None added to HeartbeatResponse
  - routers/heartbeat.py: site_numeric_id=site.id included in response

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 00:11:48 +03:00
cd8a6c53cf feat(connect): Phase 3 — cloud API routes for Xenia Connect
Adds four new routers, three schema files, and one new model.
All new endpoints use new URL prefixes — no existing routes touched.

routers/menu.py
  GET  /api/menu/{site_slug}     — public menu snapshot (no auth)
  POST /api/menu/sync            — upsert snapshot (site API key)

routers/orders.py
  POST /api/orders/{site_slug}         — customer places order (public)
  GET  /api/orders/status/{public_ref} — customer tracks order (public)
  GET  /api/orders/pending/{site_id}   — local backend polls (site key)
  POST /api/orders/{id}/synced         — mark synced (site key)
  PATCH /api/orders/{id}/status        — update status with transition
                                         validation (site key)

routers/manager_auth.py
  POST /api/manager/register  — create manager account (admin JWT)
  POST /api/manager/login     — returns manager JWT
  POST /api/manager/refresh   — refreshes manager JWT
  Shared _get_current_manager dependency used by remote_dashboard

routers/remote_dashboard.py
  GET  /api/remote/sites                           — manager JWT
  GET  /api/remote/sites/{id}/snapshot             — manager JWT
  GET  /api/remote/sites/{id}/orders               — manager JWT
  GET  /api/remote/sites/{id}/orders/pending       — manager JWT
  PATCH /api/remote/sites/{id}/orders/{oid}/status — manager JWT
  POST /api/remote/snapshot                        — site API key
                                                     (stats push)

models/stats_snapshot.py (NEW)
  stats_snapshots table — one row per site, holds serialized
  operational stats; created by create_all on startup

config.py / .env.example
  MANAGER_JWT_SECRET and MANAGER_JWT_EXPIRE_HOURS (default 72h)
  added as separate settings from the admin SECRET_KEY

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 00:05:48 +03:00
835806f92d feat(connect): Phase 2 — cloud DB models for Xenia Connect
Adds three new tables created automatically by create_all on next
startup, plus an additive migration for an existing table:

models/menu_snapshot.py (NEW)
  - menu_snapshots: one row per site, holds the full serialized
    category+product JSON; upserted on each sync from local_backend

models/online_order.py (NEW)
  - online_orders: full order lifecycle from customer submission
    through delivery; tracks sync state so local_backend can poll
    for unprocessed orders (synced_to_local=0)

models/manager_account.py (NEW)
  - manager_accounts + manager_site_access (M2M join table): remote
    manager login accounts with per-site access control

models/site.py (MODIFIED)
  - order_counter column added; incremented atomically when each
    online order is created to generate "ORD-XXXX" public_ref values

main.py (MODIFIED)
  - imports all three new models so create_all registers their tables
  - _run_migrations() added (same pattern as local_backend) for the
    additive order_counter column on the existing sites table

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 00:01:02 +03:00
2d1b927670 feat: add LATEST_VERSION config for client update detection
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 15:24:43 +03:00
06b01533d4 feat: waiter domain + local IP tracking
- Site model: add waiter_domain and last_seen_local_ip columns
- HeartbeatRequest: accept optional local_ip field from local backend
- HeartbeatResponse: return waiter_domain to local backend
- heartbeat router: persist local_ip on each check-in
- SiteDetailPage: show Public IP / Local IP separately, add Waiter Domain
  card with inline edit modal

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 14:06:36 +03:00
bbbd421aec Update: Production ready with proper mapping and variables 2026-05-08 15:39:34 +03:00
dcf43d382c fix: changed to production with nginx serving the files 2026-05-08 14:21:17 +03:00
4cbf8986df Initial Commit. Split cloud service from the combined project 2026-05-08 13:20:23 +03:00