# SPQ v2 — React/Vite SPA Build The v2 frontend is a React + TypeScript + Vite SPA at `spq-v2/` under the ShopProQuote directory. ## Location Primary: `/mnt/seagate8tb/Websites/ShopProQuote.backup-20260626-2058/spq-v2/` (This is the live deployed source. The backup path IS the primary for v2.) ## Stack - React 19 + TypeScript - Vite (build tool, NOT `tsc` — see build section below) - PocketBase JS SDK (`pocketbase` npm package) - jsPDF 4.2.1 for PDF generation - Tailwind CSS (PostCSS, compiled at build time — classes are NOT generated dynamically) - React Router v7 for client-side routing - Zustand for state management (quote form, persist middleware `spq-quote`) - Lucide React for icons ## Build ```bash cd /mnt/seagate8tb/Websites/ShopProQuote.backup-20260626-2058/spq-v2 npx vite build ``` **DO NOT use `npm run build`** — it runs `tsc -b && vite build`. The `tsc -b` step fails with 30+ pre-existing type errors across Appointments, Customers, FinancialDashboard, Invoices, RepairOrders, Settings (unused imports, type mismatches from rapid prototyping). These are NOT bugs introduced by new work — they've been present since initial scaffolding. `vite build` alone handles TypeScript via esbuild and produces valid JS. **Hermes terminal note:** `vite build` is detected as a server process by Hermes. Use `background=true` with `notify_on_complete=true`, then `process wait` to retrieve the result. Build outputs to `dist/`, served live by nginx. The QuoteGenerator page lazy-loads as an async chunk (`QuoteGenerator-.js`). jsPDF is bundled inline (~300KB). ## Key differences from v1 (vanilla JS) | | v1 | v2 | |---|---|---| | Framework | Vanilla JS (ES modules) | React + TypeScript | | Tailwind | CDN (dynamic classes) | PostCSS build (compiled) | | Routing | Multi-page HTML files | React Router SPA | | Auth | `pb.authStore.isValid` check | Same, via React context | | PB URL | `/pb` (relative, same as below) | `/pb` (`VITE_PB_URL` env, defaults to `/pb`) | ## PB URL Configuration The PocketBase URL is set at build time via the `VITE_PB_URL` environment variable: ```ts // src/lib/pocketbase.ts const PB_URL = import.meta.env.VITE_PB_URL || '/pb'; export const pb = new PocketBase(PB_URL); ``` When serving the built `dist/`, the frontend requests `/pb/*` from the same origin. The Python proxy server in `references/local-testing.md` handles forwarding these to PocketBase on 8091. ## Routes | Path | Component | Auth | |---|---|---| | `/login` | Login | Public | | `/` | Dashboard | Protected | | `/quote` | QuoteGenerator | Protected | | `/settings` | Settings | Protected | | `*` | Redirect → `/` | — | ## Serving locally See `references/local-testing.md` for the Python reverse-proxy script that: 1. Serves static files from `dist/` 2. Falls back to `index.html` for client-side routes 3. Proxies `/pb/*` → `http://127.0.0.1:8091/api/*` 4. Adds CORS headers Test login: create a user via PocketBase API at `127.0.0.1:8091` (see `references/pocketbase-test-users.md`).