4.9 KiB
4.9 KiB
Customer DB Integration in SPQ-v2 (All Forms → Customers Collection)
Overview
When a shop creates a quote, appointment, or repair order, the customer info should be saved to the customers PocketBase collection and linked via customerId across all record types. This reference documents the full pattern as applied to Quotes, Appointments, and Repair Orders.
Architecture
User types first/middle/last name + phone in form
↓
debounced client-side customer matching (400ms)
↓
Match found? ─yes─→ Show "Use Existing Customer" banner
| ↓
no User clicks "Use"
| ↓
↓ Form populated, customerId set
Auto-create on save
(ensureCustomerRecord)
↓
customers collection (PB)
↓
customerId on primary record (appointment / RO / quote)
Shared Helper Library
src/lib/customerName.ts provides three reusable functions:
composeName(first, middle, last)— joins parts into a singlenamestringparseName(fullName)— splits into{ firstName, middleName, lastName }(first-word/last-word heuristic)findMatchingCustomer<T>(customers, query)— generic matcher: phone (exact 3+ digits) → exact full name → partial name. Generic so it preserves the input type.
Type Changes
CustomerFormData (src/components/customers/types.ts):
- Added
firstName,middleName,lastNamealongside existingnamefield - The
namefield is auto-composed from the three parts viacomposeName()
customerWriteSchema (src/schemas/customer.ts):
- Added optional
firstName,middleName,lastName(defaults to '')
Form-Specific Implementations
AppointmentModal (src/components/appointments/AppointmentModal.tsx)
- Inputs: 3-column grid for First / Middle / Last Name (middle optional)
- Customer matching: Debounced 400ms, searches loaded customer list client-side
- Suggestion banner: Shows "Existing customer found — Name [Use] [Continue as New]"
- Auto-create:
ensureCustomerRecord()on submit — creates customer in PB, setscustomerId - Linked indicator: Green badge "Linked to customer record: Name"
- Props:
customers: CustomerWithVehicles[](loaded by parent AppointmentsPage)
ROForm (src/components/ROForm.tsx)
- Same 3-input layout as AppointmentModal
- Same matching + suggestion banner + use/dismiss controls
- Same
ensureCustomerRecord()on submit - Props:
customers?: CustomerWithVehicles[](optional, defaults to []) - Threaded through
ROModal→RepairOrders.tsx(parent fetches customer list)
Customer Form (CustomerFormModal.tsx)
- Updated to use first/middle/last name inputs instead of single "Name" field
- Composes
namefield internally on save
Quote Form (CustomerInfoPanel in QuoteGenerator)
- Same 3-name-input layout + customer search at top
- Duplicate detection via debounce + PB filter
- Auto-create via
ensureCustomerRecord()on save/share
Customer Matching Logic
Implemented in findMatchingCustomer() (customerName.ts):
- Phone match (exact): If phone has 3+ digits, find customer whose phone (digits-only) equals query
- Exact name match: If first + last name entered, find customer whose
nameequals composed name - Partial name: Try
startsWithon customer name, thenincludes
Important: Matching is client-side on the loaded customers array — it does NOT use PB ~ filter which can 400 on some collections.
Auto-Create on Save
ensureCustomerRecord() is called before saving any record when no customerId exists:
- If customerId already set → return it
- Get userId from pb.authStore
- Compose name from first/middle/last
- Double-check for existing customer (in case one was added since page load)
- If found → auto-link (set customerId, return id)
- If not found →
pb.collection('customers').create({ name, phone, email, ... }) - Return new id
CustomerId Propagation
- Appointment → Check-In: The check-in flow reads
appointment.customerIdand passes to RO create - Quote → RO Conversion:
customerIdis passed through in QuoteGenerator's RO conversion - Customers page detail view: Shows linked ROs by querying
customerIdor (legacy)customerName
Key Files
| File | Role |
|---|---|
src/lib/customerName.ts |
Shared helpers (composeName, parseName, findMatchingCustomer) |
src/components/appointments/AppointmentModal.tsx |
First/middle/last inputs + matching + auto-save |
src/components/ROForm.tsx |
Same pattern on RO creation/edit |
src/components/customers/CustomerFormModal.tsx |
Customer CRUD modal (also first/middle/last) |
src/components/customers/types.ts |
CustomerRecord, CustomerWithVehicles, CustomerFormData |
src/schemas/customer.ts |
Zod schema (firstName/middleName/lastName fields) |