Files
hermes-config/skills/self-hosting/shop-pro-quote/references/recent-quotes-status-derivation.md
T
2026-07-12 10:17:17 -04:00

2.3 KiB

Recent Quotes Status Derivation (v2)

The Bug

In the Recent Quotes panel (src/pages/QuoteGenerator.tsx), the status badge was showing "Draft" even when ALL services had been decided (approved or declined) by shop staff in the QuoteGenerator.

Root Cause

The quote's status field in PocketBase is only a lifecycle field:

  • 'draft' — set on Save (QuoteSummary.tsx:123)
  • 'sent' — set on Share (QuoteSummary.tsx:246)
  • 'approved' / 'declined' — set by the customer via QuoteApprove.tsx:221-225

When shop staff approve/decline individual services in the QuoteGenerator, the customerDecision values are saved inside the services JSON blob, but the quote-level status field is NOT updated. It stays 'draft'.

The Fix

In loadRecentQuotes() (QuoteGenerator.tsx:196), derive the display status from individual service decisions when ALL services have been decided:

const hasPending = servicesList.some((s: any) => !s.customerDecision || s.customerDecision === 'pending');
let derivedStatus: QuoteRecord['status'] = record.status;
if (!hasPending && servicesList.length > 0) {
  const allApproved = servicesList.every((s: any) => s.customerDecision === 'approved');
  const allDeclined = servicesList.every((s: any) => s.customerDecision === 'declined');
  if (allApproved) derivedStatus = 'approved';
  else if (allDeclined) derivedStatus = 'declined';
}
// Use derivedStatus in the returned object's `status` field

This overrides status to:

  • 'approved' — when ALL services have customerDecision === 'approved'
  • 'declined' — when ALL services have customerDecision === 'declined'
  • Keeps original PocketBase status — when some services are still pending, or when decisions are mixed (some approved, some declined)

Because the mapped object's status field feeds both the RecentStatusBadge component and the filter/count logic (recentCounts, matchesRecentStatusFilter), this single change fixes the badge, the filter tabs, and the filter counts.

Also Fixed

The hasPending check was previously servicesList.some(s => s.customerDecision === 'pending'). A service with no customerDecision field at all would NOT be counted as pending. Changed to servicesList.some(s => !s.customerDecision || s.customerDecision === 'pending') to handle services that don't have the field yet (edge case with legacy data).