4.0 KiB
4.0 KiB
SPQ-v2 Phase 4 Implementation Reference (2026-07-07)
Completed Features
Six features implemented in a single session using batched parallel delegation.
Batch A — Pure frontend (no PB migrations)
4.5 Technician time-card CSV export
src/pages/advisor/TimeCards.tsx— date-range picker, queriestechnicianAssignmentsby shop, aggregates clock entries per-tech per-day, CSV download via Blob + URL.createObjectURL- Route
/time-cardsin App.tsx, nav in both Layout.tsx and MobileLayout.tsx (Clock icon) - Preload helper in
src/lib/routePreload.ts
4.9 Data export / GDPR
src/pages/Privacy.tsx— export all data as JSON download (repairOrders, customers, appointments, invoices, settings), delete account with two-step confirmation- Route
/privacyin App.tsx under<AdvisorOnly>, link in Settings Account tab
Batch B — PB hooks (no new collections)
4.1 Quote approval notifications
- PB M23:
app.onRecordUpdateRequestonquotes— when status changessent → approved/declined, emails shop owner via PB mailer
4.2 Quote expiry enforcement
- PB M24:
app.onRecordUpdateRequestonquotes— rejects share-token updates pastcreated + quoteExpiryDayswith 410 src/pages/QuoteApprove.tsx— distinct "Quote Has Expired" UI (Clock icon, red styling), catches 410 on load and submit
Batch C — New PB collections + frontend
4.3 Financial audit log
- PB M25:
roFinancialAuditcollection (roId, field, from, to, by, at, prevHash, hash) with hash-chained hook onrepairOrdersfinancial blob diffs - Viewer in RODetailModal Timeline tab — queries by roId, shows field labels, from→to JSON, hash chain
4.6 Recurring maintenance reminders
- PB M26:
reminderscollection (customerId, customerName, vehicleInfo, shopUserId, mileageAtService, intervalMileage, dueMileage, notes, active) - "Set Reminder" button in RODetailModal header, inline form with computed due mileage
- "Maintenance Reminders" card in Dashboard with count badge
Skipped / Deferred
- 4.4 Multi-vehicle history — skipped per user
- 4.7 Offline-first for technicians — sub-project scope, skipped
- 4.8 Two-factor auth — skipped per user
- 4.10 Multi-shop readiness — decision deferred (no code change needed)
PB Migrations Applied
- M23
1740000000023_quote_approval_notify.js - M24
1740000000024_quote_expiry_enforce.js - M25
1740000000025_ro_financial_audit.js - M26
1740000000026_reminders.js - M27
1740000000027_set_audit_and_reminder_rules.js(no-op placeholder due to PB 0.39 rule validation quirk)
Verification
npm run build ✓, npm run lint ✓ (0 errors, 24 pre-existing warnings), npm run test ✓ (86/86 tests)
PB 0.39 Quirks Discovered
See pb-0.39-migration-quirks.md for full details. Key ones:
- Field-based API rules can't be set via JS migrations (even in separate files) — use empty rules + frontend filtering
DateTimeFielddoesn't exist — useTextFieldwith ISO stringsonRecordBeforeUpdateRequestandonRecordAfterUpdateRequestdon't exist — useonRecordUpdateRequest- Collection constructor and
new Record()both work in JS migrations
GlitchTip DSN Format (FIXED 2026-07-07)
The Sentry client-side SDK v10 rejects DSNs with multi-segment paths like /glitchtip/1. Fix applied:
.env.production: DSN path changed from/glitchtip/1to/1- nginx: Added regex location
^/([0-9]+)/(api|store)/(.*)$that proxies bare project-ID paths to GlitchTip at127.0.0.1:8001 - After hard refresh, Sentry initializes with zero console errors
The nginx snippet:
location ~ ^/([0-9]+)/(api|store)/(.*)$ {
rewrite ^/([0-9]+)/api/(.*)$ /api/$1/$2 break;
rewrite ^/([0-9]+)/store/(.*)$ /api/$1/$2 break;
proxy_pass http://127.0.0.1:8001;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
}