LunarFront Platform Frontend Strategy — Multiple UI Surfaces Version 1.0 | Draft # 1. Overview LunarFront serves different users in different contexts — a store owner at their desk, a cashier at the counter, a staff member receiving stock in the back room, and a salesperson at a trade show. One UI cannot serve all of these well. The platform uses a single backend API with multiple purpose-built frontends. Each frontend is a separate package in the monorepo, shares the same `@lunarfront/shared` schemas and types, and calls the same REST API. The backend doesn't know or care which frontend is making the request. # 2. Frontend Packages ## 2.1 Admin UI — `packages/admin` **Status:** Active development (current) | Attribute | Detail | |-----------|--------| | Form factor | Desktop / laptop, large tablet | | Stack | React, Vite, TanStack Router, TanStack Query, shadcn/ui | | Users | Store owner, manager, back-office staff | | Access | Web browser | **Purpose:** Full management interface. Everything that isn't a real-time transaction happens here — accounts, members, inventory configuration, RBAC, roles, reports, settings, lesson scheduling, repair management, vault. **Design priorities:** - Dense information display (data tables, detail pages) - Keyboard-friendly (tab navigation, search) - Multi-column layouts, sidebars, dialogs - Not optimized for touch or small screens — that's intentional ## 2.2 POS UI — `packages/pos` **Status:** Planned | Attribute | Detail | |-----------|--------| | Form factor | Counter-mounted tablet (10"+) or touchscreen monitor | | Stack | React, Vite (same toolchain as admin) | | Users | Cashier, sales associate | | Access | Web browser (full-screen / kiosk mode) | **Purpose:** Point-of-sale for in-store transactions. Staff stares at this 8 hours a day. Every interaction must be fast and require minimal taps. **Design priorities:** - Always-on, single-screen (no page navigation) - Large touch targets (44px+ minimum) - Fast product search (keyboard wedge barcode scanner + text search) - Cart management (add, remove, adjust quantity, apply discount) - Payment flow (cash, card via terminal, split payment) - Cash drawer integration - Receipt printing (thermal printer via browser print or ESC/POS) - Session management (clock in/out, cash drawer open/close, X/Z reports) - Offline resilience — queue transactions if network drops, sync when back **PIN unlock:** The POS terminal stays authenticated but locks between use. Staff unlock with a 4-6 digit PIN instead of email/password. The PIN maps to their real user account — every action (sale, void, drawer open) is attributed to the person who unlocked. - Each user sets a numeric PIN from their profile or admin assigns one - PIN is stored as a bcrypt hash on the user record (separate from password) - POS lock screen shows a numeric keypad — tap PIN, unlock, start selling - Auto-locks after configurable idle timeout (default: 2 minutes) - Manual lock button always visible - Shift change: current user walks away, screen locks, next user enters their PIN - Wrong PIN: brief delay, max 5 attempts then require full login - PIN unlock issues a short-lived JWT (or extends the existing session) scoped to POS permissions - Backend: `POST /v1/auth/pin-login` — accepts `{ pin }`, returns token with same user identity and permissions - PIN is optional — users without a PIN set must use full email/password login on the POS This gives full audit trail (who did what) without the friction of typing credentials hundreds of times a day. **Key screens:** 1. **Lock screen** — numeric keypad, current time, store name 2. **Sale screen** — product search (left), cart (right), totals + pay button (bottom) 3. **Payment screen** — amount due, payment method selection, card terminal status, change calculation 4. **Return/exchange screen** — lookup original transaction, select items, process refund 5. **Drawer management** — open session, cash drops, close session with counts **Hardware integration:** - Barcode scanner: keyboard wedge (types into focused input, no special API) - Card reader: local network or USB terminal via Stripe Terminal SDK or Global Payments SDK - Cash drawer: triggered via receipt printer (kick pulse) or USB relay - Receipt printer: ESC/POS over USB or network, or browser `window.print()` with receipt-formatted CSS ## 2.3 Floor App — `packages/floor` **Status:** Planned | Attribute | Detail | |-----------|--------| | Form factor | Phone or small tablet | | Stack | React Native (Expo) or PWA | | Users | Sales staff on floor, inventory staff, trade show sales | | Access | Native app (iOS/Android) or PWA in Safari/Chrome | **Purpose:** Two modes in one mobile app — inventory management and mobile sales. Both are walk-around-the-store (or walk-around-a-trade-show) workflows with scanning, big inputs, and minimal typing. **Design priorities:** - Phone-first layout (single column, stacked) - Camera barcode scanning (no external scanner needed) - Large buttons, minimal text input - Works on cellular (trade shows) and store WiFi - Offline support for inventory counts (sync when connected) ### Inventory Mode **Purpose:** Receiving, cycle counts, transfers, shelf checks. **Key screens:** 1. **Receive stock** — scan items, enter quantities, confirm receipt against purchase order 2. **Cycle count** — scan location/shelf, scan items, enter counts, submit discrepancies 3. **Quick lookup** — scan barcode, see product details, stock levels across locations, price history 4. **Transfer** — scan items to move between locations 5. **Condition check** — scan item, update condition (new, used, damaged), add notes/photo ### Mobile POS Mode **Purpose:** Sales at trade shows, on the shop floor, off-site events. **Key screens:** 1. **Quick sale** — search/scan products, add to cart, take payment 2. **Payment** — Bluetooth card reader integration, or manual card entry 3. **Customer lookup** — search by name/phone/account number for account-linked sales 4. **Receipt** — email or text receipt (no printer at a trade show) ### Build Strategy **Phase 1 — PWA:** - Build as a web app with mobile-optimized UI - "Add to Home Screen" for app-like experience - Camera scanning via `navigator.mediaDevices` + barcode detection library - Local network card reader for payments - No App Store, no developer cert, works on any device **Phase 2 — Native (if needed):** - React Native with Expo - Reuses `@lunarfront/shared` schemas and business logic - Native Bluetooth for card reader pairing - Native camera for faster barcode scanning - Requires Apple Developer ($99/year) and Google Play ($25 one-time) - Only worth it if PWA limitations become blockers (Bluetooth, offline, performance) # 3. Shared Infrastructure All frontends share: | Layer | Package | What it provides | |-------|---------|------------------| | Validation | `@lunarfront/shared` | Zod schemas — same validation client and server | | Types | `@lunarfront/shared` | TypeScript interfaces for all domain entities | | Business logic | `@lunarfront/shared` | Formatters, calculators, state normalization | | API | Backend REST | Same endpoints, same auth, same response format | | Auth | JWT | Same token works across all frontends | | Permissions | RBAC | Same permission checks — POS user doesn't need admin access | This means: - A bug fix in a Zod schema fixes validation everywhere - A new API endpoint is immediately available to all frontends - No data format translation between frontends - One test suite covers the API regardless of which frontend calls it # 4. Permission Mapping Different frontends surface different permissions: | Permission | Admin | POS | Floor | |------------|-------|-----|-------| | `accounts.view/edit/admin` | Yes | View only (customer lookup) | View only | | `inventory.view/edit/admin` | Yes | View only (product search) | Full access | | `pos.view/edit/admin` | Reports only | Full access | Mobile POS mode | | `rentals.*` | Yes | Process returns | No | | `lessons.*` | Yes | No | No | | `repairs.*` | Yes | Intake only | No | | `vault.*` | Yes | No | No | | `users.*` | Yes | No | No | The backend enforces permissions regardless — the frontend just decides what to show. # 5. Offline Strategy Offline support is critical for POS (network drops) and Floor (dead zones in warehouse). **POS offline:** - Cache product catalog in IndexedDB (refresh periodically) - Queue transactions locally when offline - Process card payments when back online (or show "cash only" mode) - Sync queue when connection restores - Visual indicator: "Offline — transactions will sync when connected" **Floor offline (inventory):** - Cache current inventory counts - Store count entries locally - Sync on reconnect - Conflict resolution: last-write-wins with audit log of discrepancies **Admin:** - No offline support needed. Back-office work requires connectivity. # 6. Deployment | Frontend | Hosting | Notes | |----------|---------|-------| | Admin | Same server as backend (Caddy/nginx serves static files) | Single origin, no CORS | | POS | Same server or dedicated POS terminal | Kiosk mode browser, auto-launch on boot | | Floor | PWA from same server, or App Store | Phone/tablet accesses over store WiFi or cellular | For on-prem: all frontends are served from the same LunarFront server. One box, one URL, multiple apps at different paths (`/admin`, `/pos`, `/floor`). # 7. Implementation Order 1. **Admin UI** — current, continue until core management is complete 2. **POS UI** — next major effort after admin is stable. This is the revenue-critical surface. 3. **Floor App (Inventory mode)** — PWA first. Enables stock receiving and cycle counts. 4. **Floor App (Mobile POS mode)** — add-on to floor app. Trade show / floor sales. 5. **Floor App (Native)** — only if PWA hits limitations. Bluetooth reader, performance, offline. Each frontend is independently deployable. Stores can run just Admin + POS if they don't need mobile inventory. Modules control which features are available, not which frontends are installed.