4.4 KiB
4.4 KiB
SPQ PocketBase Field Mapping — Frontend ↔ DB
Critical: Frontend Field Names ≠ PB Column Names
The repairOrders collection schema has different field names than what the TypeScript types use. Saving to the wrong name causes silent data loss — PB accepts unknown fields through the API but does NOT persist them.
Frontend → PB Translation Table
| TypeScript (types.ts) | PB Column | PB Type | Translation |
|---|---|---|---|
estimatedDuration: number (minutes) |
estimatedTime |
TEXT | (estimatedDuration / 60).toFixed(1) → stores hours string like "1.5" |
customerType: 'waiter' | 'drop-off' |
N/A — no column | — | Store in financial JSON blob: JSON.stringify({ ...existingFinancial, customerType }) |
writeupTime: string (ISO) |
writeupTime |
TEXT | Direct match ✓ |
promisedTime: string (ISO) |
promisedTime |
TEXT | Direct match ✓ |
Normalize on Load
In fetchOrders normalization, convert PB fields back to frontend types:
// PB returns estimatedTime as hours string → convert to minutes number
let estimatedDuration: number | undefined;
if (item.estimatedTime && item.estimatedTime !== '') {
const hours = parseFloat(item.estimatedTime);
if (!isNaN(hours)) estimatedDuration = Math.round(hours * 60);
}
// PB returns financial JSON blob → extract customerType
let customerType: CustomerType | undefined;
if (item.financial) {
try {
const fin = typeof item.financial === 'string' ? JSON.parse(item.financial) : item.financial;
if (fin && (fin.customerType === 'waiter' || fin.customerType === 'drop-off')) {
customerType = fin.customerType;
}
} catch {}
}
return { ...item, services, estimatedDuration, customerType };
Translate on Save
In handleSave, transform before sending to PB:
const { estimatedDuration, customerType, ...rest } = data;
const payload = {
...rest,
userId,
services: JSON.parse(JSON.stringify(data.services || [])),
estimatedTime: estimatedDuration != null ? (estimatedDuration / 60).toFixed(1) : '',
};
// Store customerType in financial JSON
if (customerType) {
try {
const existing = typeof rest.financial === 'string'
? JSON.parse(rest.financial) : (rest.financial || {});
payload.financial = JSON.stringify({ ...existing, customerType });
} catch {
payload.financial = JSON.stringify({ customerType });
}
}
For Add-Time / Inline Saves
When updating just the estimated duration inline (like the "+ Add Time" feature), save directly to estimatedTime:
await pb.collection('repairOrders').update(id, {
estimatedTime: (newDuration / 60).toFixed(1),
});
For Customer-Type Toggle Inline
When toggling waiter/drop-off inline, update the financial JSON blob:
const ro = orders.find((o) => o.id === id);
let financial: Record<string, any> = {};
if (ro?.financial) {
try { financial = typeof ro.financial === 'string' ? JSON.parse(ro.financial) : ro.financial; } catch {}
}
financial.customerType = newType;
await pb.collection('repairOrders').update(id, {
financial: JSON.stringify(financial),
});
RO Status Values (React App)
| TS Value | PB Value | Display | Badge |
|---|---|---|---|
active |
active |
Active | Blue |
in_progress |
in_progress |
In Progress | Yellow |
waiting_parts |
waiting_parts |
Waiting Parts | Orange |
waiting_pickup |
waiting_pickup |
Waiting Pick-up | Teal |
completed |
completed |
Completed | Green |
delivered |
delivered |
Delivered | Gray |
Appointment to Repair Order (Check-In)
When converting an appointment into an RO, the handleCheckInSubmit maps fields like this:
| Appointment field | RO field |
|---|---|
customerName |
customerName |
customerPhone |
customerPhone |
customerEmail |
customerEmail |
vehicleInfo |
vehicleInfo |
advisorName |
advisorName |
notes |
notes (mapped from "customer concerns" input) |
| — | vin (prompted in check-in form) |
| — | mileage (prompted in check-in form) |
| — | writeupTime = new Date().toISOString() (auto-stamped) |
| — | estimatedTime = (estimatedDuration / 60).toFixed(1) (from hours+minutes input) |
| — | financial = { customerType } (from radio toggle) |
| — | status = 'active' (initial) |
| — | roNumber = auto-generated (e.g., RO-20260629-0842) |
After RO creation, appointment status changes to checked_in.