Skip to main content

QA Hardening Patch Summary — Save Persistence Fix

Commit SHA: bc2058f6eac79abd4792b0b4a31dbd640a671adb
Date: 2026-02-06
Branch: main

✅ Changes Implemented

1. Org Header Propagation (QA Requirement #1)

File: dashboard/src/lib/apiClient.ts
  • Added orgId option parameter to getBusinessOnboardingList() method
  • Ensures x-org-id header is explicitly passed to backend
  • Matches pattern used by other API methods (e.g., setBusinessPolicy)
Files Updated:
  • dashboard/src/components/intake/PreflightStep.tsx — Both getBusinessOnboardingList calls now pass orgId: orgScope.orgId
  • dashboard/src/pages/admin/onboarding/businesses.tsxloadBusinesses now passes orgId: orgScope.orgId
Verification:
  • Network requests will include x-org-id header when orgScope.orgId is set
  • Platform admins will have org context properly scoped
  • Org-scoped users will have header set correctly (or omitted per backend policy)

2. Shared Period Label Helper (QA Requirement #2)

New File: dashboard/src/lib/periodUtils.ts
  • Created getCurrentMonthStart() utility function
  • Returns current month start in YYYY-MM-01 format (deterministic, local time)
  • Single source of truth to avoid timezone drift from inline date calculations
Files Updated:
  • dashboard/src/pages/admin/onboarding/businesses.tsx — Replaced inline new Date().toISOString().slice(0, 7) + '-01' with getCurrentMonthStart()
  • dashboard/src/pages/admin/onboarding/businesses.tsx — Replaced inline fallback in loadBusinesses with shared helper
Tests:
  • dashboard/src/lib/__tests__/periodUtils.test.ts — 4 passing unit tests covering:
    • Current month format (YYYY-MM-01)
    • Single-digit month padding
    • Year boundary handling
    • Local time usage (not UTC)

3. Fallback Fetch Logic Verification (QA Requirement #3)

File: dashboard/src/components/intake/PreflightStep.tsx Verified:
  • Fallback fetch only runs when updatedBusiness is not found after initial search
  • Limit is bounded to 250 (not unbounded)
  • Both initial and fallback calls include orgId header
  • Both calls include period_label parameter
Code Location:
// Lines 489-503 in PreflightStep.tsx
// Fallback if search doesn't match business_id:
// QA: Only run if business not found, limit bounded to 250
if (!updatedBusiness) {
  const fallbackResp = await apiClient.getBusinessOnboardingList(
    {
      period_label: periodLabel,
      search: undefined,
      limit: 250, // QA: Bounded limit
    },
    {
      orgId: orgScope.orgId, // QA: Explicitly pass org scope header
    }
  );
  updatedBusiness = fallbackResp.businesses.find(b => b.business_id === savedBusinessId);
}

4. Production Logs Verification (QA Requirement #4)

Verified:
  • All console.log statements are guarded with process.env.NODE_ENV !== 'production' checks
  • console.error statements remain (appropriate for error logging)
  • No unguarded production logs introduced
Files Checked:
  • dashboard/src/components/intake/PreflightStep.tsx — All logs guarded
  • dashboard/src/components/admin/onboarding/BusinessDetailDrawer.tsx — All logs guarded
  • dashboard/src/pages/admin/onboarding/businesses.tsx — All logs guarded
Lint Status:
  • npm run lint passed (warnings only, no errors)
  • Warnings are pre-existing React Hook dependency warnings (not related to this patch)
Test Status:
  • periodUtils.test.ts — 4 tests passing
  • convertToBusinessListItem.test.ts — 3 tests passing

📋 Manual Verification Required (Post-Deploy)

Network Proof

After deploying to production, verify in browser DevTools Network tab:
  1. Open wizard preflight for a business
  2. Save AGENT_PEPM configuration (e.g., rate 12.00)
  3. Inspect Network tab for the GET /api/v1/admin/onboarding/businesses request that fires after save
Expected Headers:
GET /api/v1/admin/onboarding/businesses?period_label=YYYY-MM-01&search=<business_id>&limit=50
Headers:
  Authorization: Bearer <token>
  x-org-id: <org_id>  ← Must be present if orgScope.orgId is set
Screenshot Required:
  • Network request showing x-org-id header present
  • Query params showing period_label is included

UI Screenshot Proof

Before Save:
  1. Open drawer for a business (e.g., “AIC Inc”)
  2. Note PEPM source is “NONE” or empty
  3. Set PEPM rate to 12.00
  4. Click Save
After Save:
  1. Close drawer
  2. Reopen drawer for the same business
  3. Expected: PEPM source shows “OVERRIDE” (or “DEFAULT”) and rate shows 12.00
  4. Screenshot: Drawer showing persisted PEPM configuration
After Page Refresh:
  1. Hard refresh the page (Ctrl+F5 / Cmd+Shift+R)
  2. Reopen drawer for the same business
  3. Expected: PEPM configuration still persists
  4. Screenshot: Drawer showing persisted configuration after refresh

🚀 Deployment Status

✅ Deployment Completed

Deployment Date: 2026-02-06 14:48:54 CST
Commit SHA: bc2058f6eac79abd4792b0b4a31dbd640a671adb
Git Push: ✅ Completed (git push origin main)
Vercel Auto-Deploy: ⏳ In Progress (typically completes in 2-5 minutes)
Production URL: https://payroll-pipeline-cbs.vercel.app

📋 Verification Steps

Automated Verification (Run after deployment completes)

# Set environment variables
$env:TOKEN = "your-auth-token"
$env:ORG_ID = "your-org-id"  # Optional, omit for platform admin

# Run verification script
.\scripts\verify_qa_hardening.ps1
Expected Output:
  • ✅ Frontend accessible
  • ✅ Backend health check passed
  • ✅ Business list endpoint accepts period_label parameter
  • ⏳ Manual verification required (see below)

Manual Network Verification (Required)

  1. Open browser DevTools (F12) → Network tab
  2. Navigate to: https://payroll-pipeline-cbs.vercel.app
  3. Open wizard preflight and save a business configuration
  4. Inspect Network tab for GET /api/v1/admin/onboarding/businesses request
  5. Verify:
    • ✅ Query param: period_label=YYYY-MM-01 is present
    • ✅ Header: x-org-id is present (if org-scoped) or absent (if platform admin)
    • ✅ Status: 200 OK
Screenshot Required: Network request showing headers and query params

Manual UI Verification (Required)

Test Flow:
  1. Open business drawer in wizard (e.g., “AIC Inc”)
  2. Set PEPM rate to 12.00, click Save
  3. Close drawer, reopen same business
    • Expected: PEPM shows 12.00 (persisted)
  4. Hard refresh page (Ctrl+F5 / Cmd+Shift+R)
  5. Reopen drawer for same business
    • Expected: PEPM still shows 12.00 (persists after refresh)
Screenshots Required:
  • Before save (empty/NONE PEPM)
  • After save + reopen (shows 12.00)
  • After page refresh + reopen (still shows 12.00)

📝 Post-Verification Update

After completing manual verification, update this document with:
  • ✅ Vercel deployment timestamp (from Vercel dashboard)
  • ✅ Network proof screenshot/paste
  • ✅ UI screenshots (before/after save, after refresh)
  • ✅ Final status: VERIFIED — CLOSED

📝 Files Changed

dashboard/src/lib/apiClient.ts                                    |  28 +++---
dashboard/src/components/intake/PreflightStep.tsx                | 105 ++++++++++++++++-----
dashboard/src/components/intake/convertToBusinessListItem.ts     |  55 +++++++++++
dashboard/src/components/intake/__tests__/convertToBusinessListItem.test.ts |  81 ++++++++++++++++
dashboard/src/lib/periodUtils.ts                                 |  19 ++++
dashboard/src/lib/__tests__/periodUtils.test.ts                  |  63 +++++++++++++
dashboard/src/pages/admin/onboarding/businesses.tsx              |  35 ++++---
dashboard/src/components/admin/onboarding/BusinessDetailDrawer.tsx |  11 +++
Total: 8 files changed, 350 insertions(+), 47 deletions(-)

✅ Acceptance Criteria Status

Code-Level (Completed)

  • ✅ Org header (x-org-id) explicitly passed in all getBusinessOnboardingList calls
  • ✅ Shared period label helper created and used (no inline fallbacks)
  • ✅ Fallback fetch logic verified (bounded, conditional)
  • ✅ All tests passing (periodUtils.test.ts: 4 tests, convertToBusinessListItem.test.ts: 3 tests)
  • ✅ Lint passing (no errors, warnings are pre-existing)
  • ✅ No production logs introduced (all console.log guarded)

Deployment (In Progress)

  • ✅ Code committed and pushed to main
  • ⏳ Vercel deployment (auto-triggered, typically 2-5 minutes)
  • ⏳ Network proof (pending manual verification)
  • ⏳ UI screenshots (pending manual verification)

Final Status

  • Deployment: ✅ Pushed to main (bc2058f)
  • Verification: ⏳ Pending manual QA sign-off
  • Status: DEPLOYED — AWAITING QA VERIFICATION

  • Fixes: UI Save Succeeds (200) but State Does Not Persist
  • Related: Phase 8I — Execution Checklist (QA-Driven)
  • Related: Resolver 500 Error Fix (deployed SHA: bd958de)