🍗 WINGHOUSE POS — System Architecture

Every component, data flow, and technology in one place

https://wh-pos.pages.dev/?ws={workspaceId}#{route}
👥 User Devices — Any browser, any device
🖥️
Staff — Counter / Manager Tablet
Full POS interface. Places orders, manages live dashboard, accesses all admin sections. Desktop sidebar nav.
Desktop Full sidebar nav Admin PIN access
📺
Kitchen Display Screen
Opens Dashboard on a wall-mounted screen. Live order board auto-updates via Firestore onSnapshot() — no refresh ever needed.
Read-only Real-time push Same workspace URL
📱
Customer Phone (QR Ordering)
Scans table QR code → opens CustomerOrderView directly. No app install. "Order More" appends items to the same live Firestore order.
QR scan Mobile bottom sheet No staff chrome
HTTPS · Any modern browser
☁️ Hosting — Cloudflare Pages (Free plan)
wh-pos.pages.dev
wh-pos.pages.dev/?ws={UUID}#{route}
Source: private GitHub org repo · winghouse-pos/wh-pos
Auto Deploy
push to main
→ webhook fires
→ builds in ~15s
Env Secrets
VITE_FIREBASE_*
set in Cloudflare
dashboard → baked
into JS at build
Serves
Static JS/CSS/HTML
Global CDN edge
No backend server
Why Cloudflare?
Private repo support
No username in URL
GitHub Pages = public only
Delivers JS bundles · App boots in browser · No server-side rendering
⚡ Vue 3 App — Runs entirely in the browser (SPA)
🔑 Workspace ID Mechanism
wh-pos.pages.dev/?ws=abe64f8c-9bf8-456e-b93a-67ff7a28f7e8#/dashboard
workspace.js
reads ?ws=
UUID saved to
localStorage
ALL Firestore ops
scoped to UUID
Share URL =
share workspace
🗺 Vue Router 4 — Client Routes
  • / → DashboardView
  • /new-order → NewOrderView
  • /past-orders → OrdersView
  • /item-sales → ItemSalesView
  • /analytics → AnalyticsView
  • /admin/menu-mgmt → MenuMgmtView
  • /admin/finance → FinanceView (Sales)
  • /admin/expenses → ExpensesView
  • /admin/employees → EmployeesView
  • /admin/settings → SettingsView
  • /customer/:tableId → CustomerOrderView
🍍 Pinia Stores — Global State
workspace.js
Reads ?ws= URL param or generates new UUID. Persists to localStorage. Single source of truth for workspace scoping.
app.js
Loads settings doc from Firestore (settings/config). Restaurant name, tax rate, payment methods, admin PIN, table count, theme. Also manages toast notifications.
cart.js
Active order state — items, order type, table, customer, discount, payment method. placeOrder() writes the completed order to Firestore.
🧩 Layout Components
AppSidebar.vue
Desktop left nav. Staff section always visible. Admin nav hidden until PIN entered.
AppHeader.vue
Top bar — restaurant name (from Firestore settings), current time in IST, dark/light toggle.
BottomNav.vue
Mobile-only bottom navigation. Mirrors sidebar items.
CartContent.vue
Shared component used in two places: desktop horizontal cart band (full-width, above menu) and mobile bottom sheet (triggered by floating gold cart button).
📋 Staff Views
DashboardView
Stat cards: Orders Today, Avg Order Value, Avg Completion
Live Orders board via onSnapshot() — updates instantly
Elapsed time: white <30m · orange 30-60m · red blink >1hr
Inline edit (items, type, table) · Mark complete/cancel/in-progress
🔔 New Items badge when customer adds via QR
NewOrderView
Step 1: Order type popup (Dine In / Parcel / Call Order)
Step 2: Table picker (configurable count + "Other" text)
Step 3: Menu grid with diet filter tabs, super-category chips, live search
Size picker modal · Add-on upsell popup · Veg/non-veg dots
Desktop: horizontal cart band · Mobile: floating button + bottom sheet
OrdersView (Past Orders)
Date filters: Today/Yesterday/This Week/This Month/Custom
Status filter: All/Pending/In-Progress/Completed/Cancelled
Re-print KOT or Bill from any historical order
ItemSalesView
Per-item revenue breakdown pulled from Firestore orders
Sortable by quantity sold or total revenue
🔐 Admin Views — PIN Protected
MenuManagementView
Add / edit / disable menu items · Set prices per size · Upload photos (stored in IndexedDB cache) · Configure size variants
FinanceView (Sales)
Date presets: Today / Yesterday / This Week / This Month / Choose Month / Custom · Revenue summary cards · Bar/area charts via ApexCharts · CSV export
ExpensesView
Log expenses: date, description, category, vendor, amount · Same date presets as Sales · Category filter · CSV export · Stored in IndexedDB (Dexie)
AnalyticsView
Monthly Revenue vs Expenses (12-month grouped bar) · Revenue by Day of Week (Mon–Sun) · Payment Methods donut · Order Types donut · Busiest Hours bar · All via ApexCharts (lazy loaded)
EmployeesView
Staff register: name, role, salary, join date · Stored locally in Dexie IndexedDB (device-local)
SettingsView
General: restaurant name, address, phone, GST · Tax rate toggle · Payment methods list · Table count · Dark/Light theme · Admin PIN · QR Codes tab (per-table, printable A4) · Data tab: backup to JSON / restore / clear orders
📱 Customer View
CustomerOrderView
No staff nav, no header chrome. Clean menu-only UI for customer.
🔗 QR Codes
Generated per-table in
Settings → QR Codes tab
Printable 3-col A4 layout
URL: /customer/{tableId}?ws=...
🔄 Order Flow
Session-persistent order ID
"Order More" appends items
to same Firestore order doc
(doesn't create a new order)
🔔 Staff Notification
New items trigger
newItemsBadge flag
on Firestore order doc
→ Badge blinks gold on
Dashboard for kitchen staff
→ Print New KOT clears it
🖨️
printReceipt.js
Generates KOT (kitchen, no prices) and Customer Bill HTML. Opens native browser print dialog. Works with PDF, USB, Bluetooth printers. Addendum print for updated orders.
📊
ApexCharts
Area, Bar, Donut chart types. Used in FinanceView (Sales) and AnalyticsView. Lazy-loaded (~1MB) — only downloads when those views are opened.
🖼️
useItemImages
Composable for IndexedDB image caching. Stores menu item photos as blobs locally after first load. Avoids re-fetching on every page visit.
Lucide Vue Next
Icon library. Tree-shaken at build time — only imported icons are bundled. Result: ~7KB in vendor-icons chunk.
🎨
Custom CSS + Themes
CSS custom properties throughout. Dark mode default (tuned for low-light kitchen). Light mode warm wheat palette for front-of-house. Toggle in header.
Firestore SDK · Real-time
Dexie.js · Device-local
🔥
Firebase Firestore
Google Cloud · Real-time · Multi-device sync via shared URL
workspaces/ (collection)
{workspaceId}/ (UUID from ?ws= param or localStorage)
orders/ (collection)
{orderId} (e.g. ORD-20260309-KQWB0)
items[] name, size, price, qty, notes
orderType "dine-in" | "parcel" | "call"
table, customerName, phone
subtotal, taxAmount, discount, total
paymentMethod Cash | PhonePe | Card | UPI | Other
status "pending" | "in-progress" | "completed" | "cancelled"
createdAt, updatedAt (IST — Asia/Kolkata)
newItemsBadge boolean — QR customer added items
settings/ (collection)
config (single document)
restaurantName, address, phone, gstNumber
taxEnabled, taxRate
paymentMethods[]
tableCount, adminPin
theme "dark" | "light"
onSnapshot() real-time push No polling needed Share ?ws= URL = instant sync All timestamps IST
💾
IndexedDB via Dexie.js 4
Device-local · Fast reads · No cloud cost · Offline capable
imageCache
itemId · imageBlob · timestamp
Menu item photos. Stored as blobs after first upload. useItemImages composable reads from here before fetching remotely.
expenses
id · date · description · category · vendor · amount
Filtered by date presets in ExpensesView. CSV export. Device-local only — not synced across devices.
employees
id · name · role · salary · joinDate · phone
Staff register in EmployeesView. Local to the device it was entered on.
💡 Why local?
Expenses and employee records don't need cross-device sync. Local IndexedDB = instant reads, works offline, zero Firestore reads/billing for these features.
Offline capable Zero cloud cost Instant reads ~95KB bundle
⚙️ Build & Deploy Pipeline
📝
Local Dev
npm run dev
localhost:5173
.env.local secrets
📁
GitHub Org Repo
winghouse-pos/wh-pos
Private · push to main
🔗
Cloudflare Webhook
Detects push
Triggers build job
📦
Vite 7 Build
npm ci
VITE_FIREBASE_* secrets
injected from Cloudflare
Settings → Secrets
📤
dist/ Deployed
Uploaded to
Cloudflare global CDN
~15s total
🌐
wh-pos.pages.dev
Live · Edge-cached
Instant loads
Vite 7 Bundle Chunks — Manual splitting for optimal parallel loading & long-term caching
index.js
~23 KB
App entry, routes, views
vendor-firebase
~267 KB
Firebase SDK · Firestore
vendor-vue
~106 KB
Vue 3 + Pinia + Vue Router
vendor-dexie
~95 KB
Dexie.js / IndexedDB layer
vendor-icons
~7 KB
Lucide (tree-shaken)
vendor-charts ⚡ lazy
~1 MB
ApexCharts · only loads when
Sales or Analytics is opened
⚡ vendor-charts is lazy-loaded — the 1MB ApexCharts bundle only downloads the first time a user navigates to Sales or Analytics. Initial page load stays fast.
User / Devices
Cloudflare / Firebase (Google)
Vue 3 App (Frontend SPA)
IndexedDB / Dexie (Local Storage)
Build & Deploy Pipeline