# 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 single `name` string
- **`parseName(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`, `lastName` alongside existing `name` field
- The `name` field is auto-composed from the three parts via `composeName()`

**`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, sets `customerId`
- **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 `name` field 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):

1. **Phone match (exact):** If phone has 3+ digits, find customer whose phone (digits-only) equals query
2. **Exact name match:** If first + last name entered, find customer whose `name` equals composed name
3. **Partial name:** Try `startsWith` on customer name, then `includes`

**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:

1. If customerId already set → return it
2. Get userId from pb.authStore
3. Compose name from first/middle/last
4. Double-check for existing customer (in case one was added since page load)
5. If found → auto-link (set customerId, return id)
6. If not found → `pb.collection('customers').create({ name, phone, email, ... })`
7. Return new id

## CustomerId Propagation

- **Appointment → Check-In:** The check-in flow reads `appointment.customerId` and passes to RO create
- **Quote → RO Conversion:** `customerId` is passed through in QuoteGenerator's RO conversion
- **Customers page detail view:** Shows linked ROs by querying `customerId` or (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) |
