# FuneralFinder — Flow Logic Reference Technical reference for the FuneralFinder stepped search widget. Use this when modifying the flow, adding steps, or integrating with a backend. ## Architecture Overview The widget is a **single React component** with internal state. No external state management required. The parent only needs to provide `funeralTypes`, optional `themeOptions`, and an `onSearch` callback. ``` ┌─────────────────────────────────────────┐ │ Header (h2 display + subheading) │ │ ───────────────────────────────── │ │ │ │ CompletedRows (stack of answered steps)│ │ │ │ Active Step (one at a time, Collapse) │ │ Step 1 │ Step 2 │ Step 3 │ Step 4 │ │ │ │ ─── always visible ─────────────────── │ │ Location input │ │ [Find funeral providers] CTA │ │ Free to use · No obligation │ └─────────────────────────────────────────┘ ``` ## State | State variable | Type | Default | Purpose | |---|---|---|---| | `intent` | `'arrange' \| 'preplan' \| null` | `null` | Step 1 answer | | `planningFor` | `'myself' \| 'someone-else' \| null` | `null` | Step 2 answer (preplan only) | | `typeSelection` | `string \| null` | `null` | Step 3 answer — funeral type ID or `'all'` | | `servicePref` | `'with-service' \| 'without-service' \| 'either'` | `'either'` | Step 4 answer | | `serviceAnswered` | `boolean` | `false` | Whether step 4 was explicitly answered | | `selectedThemes` | `string[]` | `[]` | Optional theme filter IDs (multi-select) | | `location` | `string` | `''` | Location input value | | `locationError` | `string` | `''` | Validation error for location | | `showIntentPrompt` | `boolean` | `false` | Show nudge when CTA clicked without intent | | `editingStep` | `number \| null` | `null` | Which step is being re-edited (via "Change") | ## Step Flow ### Active Step Calculation ```typescript const activeStep = (() => { if (editingStep !== null) return editingStep; // User clicked "Change" if (!intent) return 1; // Need intent if (needsPlanningFor && !planningFor) return 2; // Need planning-for (preplan only) if (!typeSelection) return 3; // Need funeral type if (showServiceStep && !serviceAnswered) return 4; // Need service pref return 0; // All complete })(); ``` `activeStep === 0` means all optional steps are answered. Only CompletedRows + location + CTA are visible. ### Step Details | Step | Question | Options | Auto-advances? | Conditional? | |---|---|---|---|---| | 1 | How can we help you today? | Arrange now / Pre-plan | Yes, on click | Always shown | | 2 | Who are you planning for? | Myself / Someone else | Yes, on click | Only when `intent === 'preplan'` | | 3 | What type of funeral? | TypeCards + Explore All + theme chips | Yes, on type card click | Always shown | | 4 | Would you like a service? | With / No / Flexible (chips) | Yes, on chip click | Only when selected type has `hasServiceOption: true` | ### Auto-advance Mechanic Steps 1, 2, and 4 auto-advance because selecting an option sets the state and clears `editingStep`. The `activeStep` recalculation on the next render determines the new step. Step 3 also auto-advances when a type card is clicked. Theme preferences within step 3 are optional — they're captured at whatever state they're in when the type card click triggers collapse. ### Editing (reverting to a previous step) Clicking "Change" on a CompletedRow calls `revertTo(stepNumber)`, which sets `editingStep`. This overrides the `activeStep` calculation, reopening that step. When the user makes a new selection, the handler clears `editingStep` and the flow recalculates. **Key behaviour:** Editing a step does NOT reset downstream answers. If you change from Cremation to Burial (both have `hasServiceOption`), the service preference carries forward. If you change to a type without `hasServiceOption` (or to "Explore all"), `servicePref` resets to `'either'` and `serviceAnswered` resets to `false`. ## CTA and Search Logic ### Minimum Requirements The CTA button is **always visible and always enabled** (except during loading). Minimum search requirements: **intent + location (3+ chars)**. ### Submit Behaviour ``` User clicks "Find funeral providers" │ ├─ intent is null? │ → Show intent prompt (role="alert"), keep step 1 visible │ → Return (don't search) │ ├─ location < 3 chars? │ → Show error on location input │ → Return (don't search) │ └─ Both present? → Call onSearch() with smart defaults for missing optional fields ``` ### Smart Defaults | Field | If not explicitly answered | Default value | |---|---|---| | `funeralTypeId` | User didn't select a type | `null` (= show all types) | | `servicePreference` | User didn't answer service step | `'either'` (= show all) | | `themes` | User didn't select any themes | `[]` (= no filter) | | `planningFor` | User on preplan path but didn't answer step 2 | `undefined` | This means a user can: select intent → type location → click CTA. Everything else defaults to "show all." ### Search Params Shape ```typescript interface FuneralSearchParams { intent: 'arrange' | 'preplan'; planningFor?: 'myself' | 'someone-else'; // Only on preplan path funeralTypeId: string | null; // null = all types servicePreference: 'with-service' | 'without-service' | 'either'; themes: string[]; // May be empty location: string; // Trimmed, 3+ chars } ``` ## Conditional Logic Map ``` intent === 'preplan' └─ Shows step 2 (planning-for) typeSelection !== 'all' && selectedType.hasServiceOption === true └─ Shows step 4 (service preference) typeSelection !== null └─ CompletedRow for type shows (with theme summary if any selected) serviceAnswered && showServiceStep └─ CompletedRow for service shows themeOptions.length > 0 └─ Theme chips appear within step 3 (always, not gated by type selection) loading === true └─ CTA button shows spinner, button disabled ``` ## Props Reference | Prop | Type | Default | Notes | |---|---|---|---| | `funeralTypes` | `FuneralTypeOption[]` | required | Each has `id`, `label`, optional `description`, `note`, `hasServiceOption` | | `themeOptions` | `ThemeOption[]` | `[]` | Each has `id`, `label`. Shown as optional chips in step 3 | | `onSearch` | `(params: FuneralSearchParams) => void` | — | Called on valid submit | | `loading` | `boolean` | `false` | Shows spinner on CTA, disables button | | `heading` | `string` | `'Find funeral directors near you'` | Main h2 heading | | `subheading` | `string` | `'Tell us a little about...'` | Below heading | | `showExploreAll` | `boolean` | `true` | Show "Explore all options" TypeCard | | `sx` | `SxProps` | — | MUI sx override on root card | ## Sub-components (internal) | Component | Purpose | Used in | |---|---|---| | `StepHeading` | Centered bodyLg heading with bottom margin | Steps 1-4 | | `ChoiceCard` | Full-width radio card with label + description | Steps 1, 2 | | `TypeCard` | Compact radio card with label + optional description/note | Step 3 | | `CompletedRow` | Summary row: question + bold answer + "Change" link | All completed steps | ## Adding a New Step 1. Add state variable(s) for the new step's answer 2. Add a condition in `activeStep` calculation (between existing steps) 3. Add a `` block in the render 4. Add a `` for the CompletedRow (with appropriate visibility condition) 5. Include the new data in `handleSubmit` → `onSearch()` params 6. Update `FuneralSearchParams` type ## Known Limitations (deferred) - **No progress indicator** — users can't see how many steps remain - **No roving tabindex** — radiogroups use button elements with `role="radio"` but arrow-key navigation between options is not implemented - **No location autocomplete** — free text input only, validated on length - **CSS vars used directly** — some styling uses `var(--fa-*)` tokens instead of MUI theme paths; works but doesn't support dynamic theme switching