From d7dddb077373f934ab3b3cc0ffc399e5cc46de23 Mon Sep 17 00:00:00 2001 From: Richie Date: Thu, 26 Mar 2026 09:15:07 +1100 Subject: [PATCH] =?UTF-8?q?Add=20FuneralFinder=20organism=20=E2=80=94=20he?= =?UTF-8?q?ro=20search=20with=20stepped=20flow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Procedural stepped search widget for the homepage: 1. "I'm here to" — Arrange now / Pre-plan (radiogroup) 2. "I'm planning for" — Myself / Someone else (conditional, pre-plan only) 3. "Type of funeral" — dynamic Chip list from prop 4. Location — suburb or postcode input 5. CTA: "Find funeral directors" (disabled until complete) Features: - Progressive disclosure: steps unlock sequentially - Brand checkmarks + edit icon on completed steps - Click completed value to revert (only resets dependents) - Step numbering adjusts when conditional step hidden - Trust signal: "Free to use · No obligation" Audit: 17/20 (Good). P1/P2 fixes applied: - Location input aria-label for screen readers - Option groups wrapped in role="radiogroup" - Completed values have edit icon affordance - Heading uses display font CSS variable - CheckCircleIcon aria-hidden 5 stories: Default, FewerTypes, CustomHeading, InHeroDesktop, InHeroMobile Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/memory/component-registry.md | 1 + .../FuneralFinder/FuneralFinder.stories.tsx | 220 ++++++++ .../organisms/FuneralFinder/FuneralFinder.tsx | 478 ++++++++++++++++++ .../organisms/FuneralFinder/index.ts | 6 + 4 files changed, 705 insertions(+) create mode 100644 src/components/organisms/FuneralFinder/FuneralFinder.stories.tsx create mode 100644 src/components/organisms/FuneralFinder/FuneralFinder.tsx create mode 100644 src/components/organisms/FuneralFinder/index.ts diff --git a/docs/memory/component-registry.md b/docs/memory/component-registry.md index 9c1a561..b8679b5 100644 --- a/docs/memory/component-registry.md +++ b/docs/memory/component-registry.md @@ -54,6 +54,7 @@ duplicates) and MUST update it after completing one. | ServiceSelector | done | ServiceOption × n + Typography + Button | Single-select service panel for arrangement flow. Heading + subheading + ServiceOption list (radiogroup) + optional continue Button. Manages selection state via selectedId/onSelect. maxDescriptionLines pass-through. | | PricingTable | planned | PriceCard × n + Typography | Comparative pricing display | | PackageDetail | done | LineItem × n + Typography + Button + Divider | Right-side package detail panel. Warm header band (surface.warm) with "Package" overline, name, price (brand colour), Make Arrangement + Compare (with loading) buttons. Sections (before total) + total + extras (after total, with subtext). T&C grey footer. Audit: 19/20. Maps to Figma Package Select (5405:181955). | +| FuneralFinder | review | Typography + Button + Chip + Input + custom OptionCard | Hero search widget. Procedural stepped flow: Intent → Planning For (conditional) → Funeral Type (chips, dynamic) → Location → CTA. Brand checkmarks on completion, edit icon to revert, radiogroup semantics. Audit: 17/20 → fixes applied. | | ArrangementForm | planned | StepIndicator + ServiceSelector + AddOnOption + Button + Typography | Multi-step arrangement wizard. Deferred — build remaining atoms/molecules first. | | Navigation | done | AppBar + Link + IconButton + Button + Divider + Drawer | Responsive site header. Desktop: logo left, links right, optional CTA. Mobile: hamburger + drawer with nav items, CTA, help footer. Sticky, grey surface bg (surface.subtle). Real FA logo from brandassets/. Maps to Figma Main Nav (14:108) + Mobile Header (2391:41508). | | Footer | done | Link × n + Typography + Divider + Container + Grid | Dark espresso (brand.950) site footer. Logo + tagline + contact (phone/email) + link group columns + legal bar. Semantic HTML (footer, nav, ul). Critique: 38/40 (Excellent). | diff --git a/src/components/organisms/FuneralFinder/FuneralFinder.stories.tsx b/src/components/organisms/FuneralFinder/FuneralFinder.stories.tsx new file mode 100644 index 0000000..bb40a39 --- /dev/null +++ b/src/components/organisms/FuneralFinder/FuneralFinder.stories.tsx @@ -0,0 +1,220 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import Box from '@mui/material/Box'; +import { FuneralFinder } from './FuneralFinder'; +import { Navigation } from '../Navigation'; +import { Typography } from '../../atoms/Typography'; + +const funeralTypes = [ + { id: 'cremation', label: 'Cremation' }, + { id: 'burial', label: 'Burial' }, + { id: 'memorial', label: 'Memorial' }, + { id: 'catholic', label: 'Catholic' }, + { id: 'direct-cremation', label: 'Direct Cremation' }, + { id: 'natural-burial', label: 'Natural Burial' }, +]; + +const FALogoNav = () => ( + +); + +const meta: Meta = { + title: 'Organisms/FuneralFinder', + component: FuneralFinder, + tags: ['autodocs'], + parameters: { + layout: 'centered', + }, + decorators: [ + (Story) => ( + + + + ), + ], +}; + +export default meta; +type Story = StoryObj; + +// --- Default ----------------------------------------------------------------- + +/** Initial state — step 1 active, all others locked */ +export const Default: Story = { + args: { + funeralTypes, + onSearch: (params) => alert(JSON.stringify(params, null, 2)), + }, +}; + +// --- Fewer Funeral Types ----------------------------------------------------- + +/** With only 3 funeral types — shows compact chip row */ +export const FewerTypes: Story = { + args: { + funeralTypes: funeralTypes.slice(0, 3), + onSearch: (params) => alert(JSON.stringify(params, null, 2)), + }, +}; + +// --- Custom Heading ---------------------------------------------------------- + +/** With custom heading and subheading */ +export const CustomHeading: Story = { + args: { + funeralTypes, + heading: 'Compare funeral directors in your area', + subheading: 'Transparent pricing · No hidden fees · 24/7', + onSearch: (params) => alert(JSON.stringify(params, null, 2)), + }, +}; + +// --- In Hero Context (Desktop) ----------------------------------------------- + +/** As it appears in the homepage hero — desktop layout */ +export const InHeroDesktop: Story = { + decorators: [ + (Story) => ( + + + + ), + ], + render: () => ( + + } + items={[ + { label: 'Provider Portal', href: '/provider-portal' }, + { label: 'FAQ', href: '/faq' }, + { label: 'Contact Us', href: '/contact' }, + { label: 'Log in', href: '/login' }, + ]} + /> + + {/* Hero section */} + + {/* Left: heading + search widget */} + + + + Discover, Explore, and Plan Funerals in Minutes + + + Whether you're thinking ahead or arranging for a loved one, find + trusted local providers with transparent pricing. + + + alert(JSON.stringify(params, null, 2))} + /> + + + + {/* Right: hero image placeholder */} + + + + ), +}; + +// --- In Hero Context (Mobile) ------------------------------------------------ + +/** Mobile viewport — stacked layout with image above search */ +export const InHeroMobile: Story = { + decorators: [ + (Story) => ( + + + + ), + ], + render: () => ( + + } + items={[ + { label: 'FAQ', href: '/faq' }, + { label: 'Contact Us', href: '/contact' }, + { label: 'Log in', href: '/login' }, + ]} + /> + + {/* Hero heading */} + + + Discover, Explore, and Plan Funerals in Minutes + + + Find trusted local providers with transparent pricing, at your own pace. + + + + {/* Hero image */} + + + {/* Search widget — overlaps image slightly */} + + alert(JSON.stringify(params, null, 2))} + /> + + + ), +}; diff --git a/src/components/organisms/FuneralFinder/FuneralFinder.tsx b/src/components/organisms/FuneralFinder/FuneralFinder.tsx new file mode 100644 index 0000000..a55c263 --- /dev/null +++ b/src/components/organisms/FuneralFinder/FuneralFinder.tsx @@ -0,0 +1,478 @@ +import React from 'react'; +import Box from '@mui/material/Box'; +import CheckCircleIcon from '@mui/icons-material/CheckCircle'; +import EditOutlinedIcon from '@mui/icons-material/EditOutlined'; +import type { SxProps, Theme } from '@mui/material/styles'; +import { Typography } from '../../atoms/Typography'; +import { Button } from '../../atoms/Button'; +import { Chip } from '../../atoms/Chip'; +import { Input } from '../../atoms/Input'; + +// ─── Types ─────────────────────────────────────────────────────────────────── + +/** A funeral type option (dynamic, from API) */ +export interface FuneralTypeOption { + /** Unique identifier */ + id: string; + /** Display label */ + label: string; +} + +/** Search parameters returned when the user submits */ +export interface FuneralSearchParams { + /** "arrange" (immediate need) or "preplan" */ + intent: 'arrange' | 'preplan'; + /** Only present when intent is "preplan" */ + planningFor?: 'myself' | 'someone-else'; + /** Selected funeral type ID */ + funeralTypeId: string; + /** Suburb or postcode entered */ + location: string; +} + +/** Props for the FA FuneralFinder organism */ +export interface FuneralFinderProps { + /** Available funeral types — dynamic list from API */ + funeralTypes: FuneralTypeOption[]; + /** Called when the user clicks "Find funeral directors" */ + onSearch?: (params: FuneralSearchParams) => void; + /** Optional heading override */ + heading?: string; + /** Optional subheading override */ + subheading?: string; + /** MUI sx prop for the root card */ + sx?: SxProps; +} + +// ─── Step state types ──────────────────────────────────────────────────────── + +type Intent = 'arrange' | 'preplan' | null; +type PlanningFor = 'myself' | 'someone-else' | null; + +type StepStatus = 'active' | 'completed' | 'locked'; + +// ─── Sub-components ────────────────────────────────────────────────────────── + +/** Step number badge or completed checkmark */ +function StepBadge({ step, status }: { step: number; status: StepStatus }) { + if (status === 'completed') { + return ( +