# SPQ v2 — Key Patterns & Pitfalls Project at `/mnt/seagate8tb/Websites/ShopProQuote.backup-20260626-2058/spq-v2/`. Vite + React + TypeScript SPA, PocketBase backend. ## Dev Proxy — DeepSeek AI The app calls DeepSeek API at `/deepseek/v1/chat/completions` (OpenAI-compatible). The API key lives in **nginx** (`/etc/nginx/sites-enabled/shopproquote`), not in the Vite project. **Vite dev proxy MUST route through localhost nginx**, not direct to api.deepseek.com or Ollama: ```typescript '/deepseek': { target: 'https://localhost', changeOrigin: true, secure: false, // localhost self-signed cert }, ``` The old approach (pointing to Ollama :11434) fails because Ollama has no `deepseek-v4-flash`. ## Quote Service Defaults Services added to a quote default to **Pending** status (yellow), NOT Approved: ```typescript addService({ ...service, selected: true, approved: false, customerDecision: 'pending', priority: undefined, applyShopCharge: true }); ``` This applies to all three code paths in `QuoteGenerator.tsx`: - `handleAdd` (catalog search picker, ~line 147) - RO import → quote pre-fill (~lines 777-778) - `handleAddSuggestion` (AI suggestions, ~lines 959-960) The per-service "Shop charge" checkbox in the expanded row lets users override. The `QuoteSummary` only shows the shop charge line when `shopCharge > 0`. ## AI Write — Tech Notes `handleAiWrite` in `QuoteGenerator.tsx` passes `technicianNotes: svc.technicianNotes` to `aiWriteExplanation`. The AI prompt in `ai.ts` already reads `technicianNotes` and includes them as `Technician's internal notes: "..."` in the context. Always include this field when calling `aiWriteExplanation`. ## RO Number & Top-Level Fields `handleSave` writes customer info BOTH as nested `customerInfo` JSON AND as top-level fields (`customerName`, `vehicleInfo`, `roNumber`). The Dashboard queries top-level fields. The edit-load logic merges both sources: ```typescript const rawCustomerInfo = data.customerInfo ? { ...data, ...(typeof data.customerInfo === 'string' ? JSON.parse(data.customerInfo) : data.customerInfo) } : data; ``` ## PDF → PNG Export `downloadPdfAsImages(doc | Blob, customerName)` in `src/lib/pdf-to-images.ts` converts every PDF page to a PNG and triggers downloads. Accepts either a jsPDF instance or a Blob (from `generateQuotePDF()`). Uses pdfjs-dist with worker configured in `main.tsx` via Vite static-asset import. ## Repair Orders Dashboard See `references/v2-repair-orders-dashboard.md` for the complete time-management system: - 6-status model with live countdown timers - Multi-tier sorting (waiting parts → waiters → drop-off → due time) - Urgency styling (warning ≤30min, past due = red row) - Interactive Waiter/Drop-Off toggle with confirmation - "+ Add Time" feature via prompt - Hours + Minutes duration input in create/edit modal ## Dashboard Dropdown The Dashboard "Recent Quotes" section uses a custom dropdown with search instead of a native `