Files
Parsons/docs/reference/funeral-finder-logic.md
Richie fef27a2701 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) <noreply@anthropic.com>
2026-03-26 13:51:38 +11:00

8.5 KiB

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

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

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 handleSubmitonSearch() 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