Skip to main content

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 ProgressiveStepConfig contract.
  • 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:

  1. Resolve or create a Progressive session for the quote.
  2. Call getCurrentState() for the requested workflow node.
  3. Detect step mismatches and use go-to navigation if the live quote is on a different step.
  4. Extract questions, progress-bar items, form values, and premium summary data.
  5. Return a normalized ProgressiveStepConfig object back to the portal.

The normalized frontend shape lives in apps/fastlane-portal/src/app/pages/carriers/progressive/home/services/progressive-config-api.ts:

  • ProgressiveStepConfig
  • ProgressiveQuestionConfig
  • ProgressiveStepConfigSection

At the question level, Progressive gives us metadata like:

FieldWhy It Matters
PropertyStable field name for RHF and payload mapping
FieldTypeDrives control type and validation behavior
ValidValuesSupplies select and yes-no options
RequiredDrives zod required checks
ShouldDisplayLets Progressive control visibility
ShouldDisplayControlLets Progressive show or hide the control itself
ShouldDisableControlLets Progressive lock fields dynamically
MaxLengthDrives input trimming and validation
LastAnswerProvides current or default values
HasEdit and EditsCarries server-side validation errors
ForceRefreshRelevancySignals fields that should trigger refresh logic

How We Process The Config

The frontend pipeline is:

  1. ProgressiveHomeFlow switches to ConfigDrivenFlowShell when useConfigDriven() is true.
  2. ConfigDrivenFlowProvider performs the initial useProgressiveStepConfig('TX', 'HO3', 'NamedInsured', true, quoteNumber) fetch and stores the returned stepConfig.
  3. ConfigDrivenFlowShell builds the page shell, sidebar, and stepper from Progressive's progressBar metadata.
  4. ConfigDrivenStepRenderer collects visible questions from stepConfig.sections, orders them, and renders generic fields.
  5. buildDefaultValues() creates the starting form state from formValues, LastAnswer, hidden defaults, and syncId.
  6. field-renderer.tsx maps each ProgressiveQuestionConfig into 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:

  • Required becomes required field validation
  • FieldType === 'MVP_INTEGER' becomes numeric-only validation
  • FieldType === 'date' becomes date-format validation
  • MaxLength becomes 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 formValues from 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: true and the next step config
  • if Progressive rejects the step, the response comes back with advanced: false and the current step config, including question-level HasEdit and Edits

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:

  1. User clicks Save & Continue.
  2. ConfigDrivenFlowShell triggers form.requestSubmit().
  3. ConfigDrivenStepRenderer validates through React Hook Form plus zod.
  4. ConfigDrivenFlowProvider.submitStepWithData() calls useSubmitProgressiveStep().
  5. The frontend sends POST /api/v1/progressive/home/step-config/submit.
  6. ProgressiveController.submitStepConfig() delegates to ProgressiveStepService.submitStepRaw().
  7. 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.
  8. 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 ActionMethodPath
Fetch initial step configPOST/api/v1/progressive/home/step-config
Submit current stepPOST/api/v1/progressive/home/step-config/submit
Refresh field relevancyPOST/api/v1/progressive/home/step-config/refresh
Validate Products eligibilityPOST/api/v1/progressive/home/step-config/validate-eligibility
Update coverage packagePOST/api/v1/progressive/home/step-config/update-package
Go to a completed stepPOST/api/v1/progressive/home/step-config/go-to
Refresh sold quote dataPOST/api/v1/progressive/home/step-config/refresh-sold
Fetch print documentsPOST/api/v1/progressive/home/print-documents
Generate print previewPOST/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:

DTOUsed By
GetStepConfigDtoinitial config fetch
SubmitStepConfigDtostep submission
RefreshRelevancyDtovisibility refresh
ValidateEligibilityDtoProducts eligibility
UpdateCoveragePackageDtoCoverages package change
GoToStepConfigDtostep navigation

Gateway To Progressive

Behind those gateway routes, the service talks to Progressive's workflow APIs, including:

  • GET CurrentWorkflowState
  • PUT CurrentWorkflowState
  • POST NextWorkflowState
  • POST 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:

  1. Start on the page and show that the shell is generic, not step-specific.
  2. Show the network request to POST /api/v1/progressive/home/step-config.
  3. Explain that Progressive returns Questions, ProgressBar, and values, and that we normalize that into ProgressiveStepConfig.
  4. Point to a field whose label, options, required state, or visibility is coming from Progressive metadata.
  5. Change a refresh-sensitive field and explain that we call step-config/refresh to get new visibility rules.
  6. Trigger a validation error and show the split between zod validation and Progressive HasEdit or Edits.
  7. Submit a valid step and show the next step arriving through step-config/submit.
  8. 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=true on the URL (the old mock=true requirement 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_NUMBER is 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.