- 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) <noreply@anthropic.com>
204 lines
8.5 KiB
Markdown
204 lines
8.5 KiB
Markdown
# 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<Theme>` | — | 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 `<Collapse in={activeStep === N}>` block in the render
|
|
4. Add a `<Collapse>` 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
|