Files
hermes-config/skills/software-development/pocketbase-react-apps/references/spq-pb-field-mapping.md
T
2026-07-12 10:17:17 -04:00

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.