Progressive Home Config-Driven POC
This POC shows that Progressive Home can run as a config-driven flow inside Fastlane Portal. Instead of hardcoding step components and validation rules, the frontend asks the API gateway for live Progressive step metadata, builds the form from that metadata, validates it, and sends the results back through config-driven endpoints.
Use this page for the demo when you want to explain three things:
- where the config comes from
- how we process it into a rendered form
- how validation and submission work end to end
For debugging sequences and known issues, see Troubleshooting - Config-Driven.
What The POC Proves
- Progressive is already exposing the form definition we need through its HAL step responses.
- Fastlane can normalize that response into a stable
ProgressiveStepConfigcontract. - The frontend can render multiple Progressive Home steps from one generic renderer.
- Validation is split cleanly between client-side guardrails and Progressive's server-side business rules.
- Step fetch, refresh, submit, package updates, and navigation all work through explicit API gateway endpoints.
Demo Entry
The config-driven flow is normally reached via a DA/GAQ landing URL with a real Progressive Q-number — see entry-from-da.md. Fastlane duplicates that quote under our agent credentials and the config-driven shell renders against the duplicate.
For a quick dev test of the config-driven shell (no DA landing), you can pass a Q-number explicitly:
http://localhost:4202/?sessionId=<uuid>&carrierId=377&lob=Home&state=TX&q=<quoteNumber>&config-driven=true
Optional query params:
q=<quoteNumber>— a Progressive Q-number visible to our agent credentials (a DA-duplicated one, or one created manually in the FAO portal for iteration)config-driven=true— required; enables the config-driven shell
There is no default quote fallback in the gateway anymore. PROGRESSIVE_POC_QUOTE_NUMBER is deprecated and no longer consulted.
End-To-End Architecture
Where The Config Comes From
The source of truth is not CMS config and not hardcoded React props. The source of truth is Progressive's live workflow metadata returned from their step APIs.
The first frontend request is:
POST /api/v1/progressive/home/step-config
That request is handled by ProgressiveController.getStepConfig() in apps/apis/fastlane-api-gateway/src/app/controllers/progressive.controller.ts.
The request contract is GetStepConfigDto in libs/apis/carriers/progressive/src/lib/application/dtos/progressive-step-dtos.ts:
quoteNumber?state?productCode?workflowNode?
The frontend must pass a quote number. There is no environment-level fallback — the DA/GAQ landing flow (entry-from-da.md) is the normal source of that value, and it's threaded through the URL + carrier_quote_sessions → Progressive duplicate Q-number.
From there, ProgressiveStepService.getStepConfig() in libs/apis/carriers/progressive/src/lib/application/services/progressive-step.service.ts does the orchestration:
- Resolve or create a Progressive session for the quote.
- Call
getCurrentState()for the requested workflow node. - Detect step mismatches and use go-to navigation if the live quote is on a different step.
- Extract questions, progress-bar items, form values, and premium summary data.
- Return a normalized
ProgressiveStepConfigobject back to the portal.
The normalized frontend shape lives in apps/fastlane-portal/src/app/pages/carriers/progressive/home/services/progressive-config-api.ts:
ProgressiveStepConfigProgressiveQuestionConfigProgressiveStepConfigSection
At the question level, Progressive gives us metadata like:
| Field | Why It Matters |
|---|---|
Property | Stable field name for RHF and payload mapping |
FieldType | Drives control type and validation behavior |
ValidValues | Supplies select and yes-no options |
Required | Drives zod required checks |
ShouldDisplay | Lets Progressive control visibility |
ShouldDisplayControl | Lets Progressive show or hide the control itself |
ShouldDisableControl | Lets Progressive lock fields dynamically |
MaxLength | Drives input trimming and validation |
LastAnswer | Provides current or default values |
HasEdit and Edits | Carries server-side validation errors |
ForceRefreshRelevancy | Signals fields that should trigger refresh logic |
How We Process The Config
The frontend pipeline is:
ProgressiveHomeFlowswitches toConfigDrivenFlowShellwhenuseConfigDriven()is true.ConfigDrivenFlowProviderperforms the initialuseProgressiveStepConfig('TX', 'HO3', 'NamedInsured', true, quoteNumber)fetch and stores the returnedstepConfig.ConfigDrivenFlowShellbuilds the page shell, sidebar, and stepper from Progressive'sprogressBarmetadata.ConfigDrivenStepRenderercollects visible questions fromstepConfig.sections, orders them, and renders generic fields.buildDefaultValues()creates the starting form state fromformValues,LastAnswer, hidden defaults, andsyncId.field-renderer.tsxmaps eachProgressiveQuestionConfiginto the appropriate control.
This is the important architectural point for the demo: the renderer is generic. The same machinery can render Named Insured, Products, Coverages, Point of Sale, and Final Sale because the UI is driven by Progressive's metadata instead of per-step React code.
Form Validation
Validation happens in two layers.
Client-Side Validation
The client schema is built dynamically in apps/fastlane-portal/src/app/pages/carriers/progressive/home/config-driven/build-step-schema.ts.
buildStepSchema() converts Progressive question metadata into zod rules:
Requiredbecomes required field validationFieldType === 'MVP_INTEGER'becomes numeric-only validationFieldType === 'date'becomes date-format validationMaxLengthbecomes max length enforcement- hidden and disabled fields are excluded from the interactive schema
buildDefaultValues() prepares the initial RHF state, including:
- converting Progressive date values into ISO strings for browser date inputs
- applying forced defaults for hidden Progressive fields
- preserving existing
formValuesfrom the server when they exist
Before submission, buildSanitizedPayload() merges current form values with server formValues, then sanitizeFormPayload() normalizes the outgoing payload by:
- trimming values to Progressive's
MaxLength - converting ISO dates back into Progressive's expected format
- preserving hidden fields that must be sent even when they are not rendered
Server-Side Validation
Progressive remains the source of truth for business rules.
When the user submits:
- if Progressive advances the workflow, the response comes back with
advanced: trueand the next step config - if Progressive rejects the step, the response comes back with
advanced: falseand the current step config, including question-levelHasEditandEdits
extractServerErrors() in map-server-errors.ts maps those Progressive edits back into field-level errors for React Hook Form.
extractKnockouts() in the same file detects ineligible or declined responses and lets the UI present them as a knockout state instead of a generic form error.
How Submission Works
The submit path is:
- User clicks
Save & Continue. ConfigDrivenFlowShelltriggersform.requestSubmit().ConfigDrivenStepRenderervalidates through React Hook Form plus zod.ConfigDrivenFlowProvider.submitStepWithData()callsuseSubmitProgressiveStep().- The frontend sends
POST /api/v1/progressive/home/step-config/submit. ProgressiveController.submitStepConfig()delegates toProgressiveStepService.submitStepRaw().- The service fetches the current Progressive state, overlays the flat form data onto Progressive's nested view-model payload, performs any required step-specific calls, then advances the workflow.
- The frontend receives
{ advanced, stepConfig }and either renders the next step or paints server errors on the current one.
For Products, Coverages, and sold quote flows, the backend also exposes dedicated config-driven operations for eligibility, package refresh, and sold-state refresh.
Endpoints And Methods
Frontend To Gateway
All calls are POST requests from apps/fastlane-portal/src/app/pages/carriers/progressive/home/services/progressive-config-api.ts.
| Frontend Action | Method | Path |
|---|---|---|
| Fetch initial step config | POST | /api/v1/progressive/home/step-config |
| Submit current step | POST | /api/v1/progressive/home/step-config/submit |
| Refresh field relevancy | POST | /api/v1/progressive/home/step-config/refresh |
| Validate Products eligibility | POST | /api/v1/progressive/home/step-config/validate-eligibility |
| Update coverage package | POST | /api/v1/progressive/home/step-config/update-package |
| Go to a completed step | POST | /api/v1/progressive/home/step-config/go-to |
| Refresh sold quote data | POST | /api/v1/progressive/home/step-config/refresh-sold |
| Fetch print documents | POST | /api/v1/progressive/home/print-documents |
| Generate print preview | POST | /api/v1/progressive/home/print-preview |
Gateway Request DTOs
The main config-driven request types are in libs/apis/carriers/progressive/src/lib/application/dtos/progressive-step-dtos.ts:
| DTO | Used By |
|---|---|
GetStepConfigDto | initial config fetch |
SubmitStepConfigDto | step submission |
RefreshRelevancyDto | visibility refresh |
ValidateEligibilityDto | Products eligibility |
UpdateCoveragePackageDto | Coverages package change |
GoToStepConfigDto | step navigation |
Gateway To Progressive
Behind those gateway routes, the service talks to Progressive's workflow APIs, including:
GET CurrentWorkflowStatePUT CurrentWorkflowStatePOST NextWorkflowStatePOST GoToWorkflowState- eligibility and package-specific Progressive endpoints for Products and Coverages
That translation layer is what lets the frontend stay stable while Progressive keeps its nested HAL view-model format.
Suggested Demo Walkthrough
Use this order in the meeting:
- Start on the page and show that the shell is generic, not step-specific.
- Show the network request to
POST /api/v1/progressive/home/step-config. - Explain that Progressive returns
Questions,ProgressBar, and values, and that we normalize that intoProgressiveStepConfig. - Point to a field whose label, options, required state, or visibility is coming from Progressive metadata.
- Change a refresh-sensitive field and explain that we call
step-config/refreshto get new visibility rules. - Trigger a validation error and show the split between zod validation and Progressive
HasEditorEdits. - Submit a valid step and show the next step arriving through
step-config/submit. - Close by emphasizing that the renderer, stepper, and validation pipeline all run from the same config contract.
Constraints And Notes
- The flow is enabled by
config-driven=trueon the URL (the oldmock=truerequirement has been dropped). - The active quote always comes from the URL — either a DA/GAQ-minted Q-number (entry-from-da.md) or an explicit
q=for dev iteration. There is no environment fallback.PROGRESSIVE_POC_QUOTE_NUMBERis deprecated. - Parity checks against the FAO portal must run in the Playwright MCP against
PROGRESSIVE_POC_PLAYWRIGHT_QUOTE_NUMBER— never against the active DA-duplicated quote. See troubleshooting-config-driven.md "Quote Isolation" for the full rules. - Quote visibility is still constrained by the Progressive agent session that owns the quote.
- Progressive remains stateful, so quote reuse and concurrent access can corrupt the workflow state.
For lower-level request sequences, known edge cases, and debugging guidance, see Troubleshooting - Config-Driven.