From fef27a2701d5d3bb11a1fc0a8342a604260c5123 Mon Sep 17 00:00:00 2001 From: Richie Date: Thu, 26 Mar 2026 13:51:38 +1100 Subject: [PATCH] Add FuneralFinder flow logic reference doc - Step flow, state management, conditional logic map - Smart defaults and CTA submit behaviour - Props reference and sub-component index - Guide for adding new steps Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/reference/funeral-finder-logic.md | 203 +++++++++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100644 docs/reference/funeral-finder-logic.md diff --git a/docs/reference/funeral-finder-logic.md b/docs/reference/funeral-finder-logic.md new file mode 100644 index 0000000..5be5c36 --- /dev/null +++ b/docs/reference/funeral-finder-logic.md @@ -0,0 +1,203 @@ +# 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