import React from 'react'; import Box from '@mui/material/Box'; import Select, { type SelectChangeEvent } from '@mui/material/Select'; import MenuItem from '@mui/material/MenuItem'; import CheckIcon from '@mui/icons-material/Check'; import type { SxProps, Theme } from '@mui/material/styles'; import { Typography } from '../../atoms/Typography'; import { Button } from '../../atoms/Button'; import { Input } from '../../atoms/Input'; import { Divider } from '../../atoms/Divider'; // ─── Types ─────────────────────────────────────────────────────────────────── type LookingTo = 'arrange-now' | 'expected' | 'future'; type PlanningFor = 'myself' | 'someone-else'; type FuneralType = | 'cremation-funeral' | 'cremation-only' | 'burial-funeral' | 'graveside-only' | 'water-cremation'; /** Search parameters returned on form submission */ export interface FuneralFinderV4SearchParams { /** User's situation — immediate, expected, or future need */ lookingTo: LookingTo; /** Who the funeral is for */ planningFor: PlanningFor; /** Type of funeral selected */ funeralType: FuneralType; /** Suburb or postcode */ location: string; } /** Props for the FuneralFinder v4 organism */ export interface FuneralFinderV4Props { /** Called when the user submits with valid data */ onSearch?: (params: FuneralFinderV4SearchParams) => void; /** Shows loading state on the CTA */ loading?: boolean; /** MUI sx override for the root container */ sx?: SxProps; } // ─── Options ───────────────────────────────────────────────────────────────── const LOOKING_TO_OPTIONS: { value: LookingTo; label: string }[] = [ { value: 'arrange-now', label: 'Arrange a funeral for someone who has passed' }, { value: 'expected', label: 'Plan ahead for someone who is unwell' }, { value: 'future', label: "Plan for a future need that isn't expected soon" }, ]; const PLANNING_FOR_OPTIONS: { value: PlanningFor; label: string }[] = [ { value: 'someone-else', label: 'Someone else' }, { value: 'myself', label: 'Myself' }, ]; const FUNERAL_TYPE_OPTIONS: { value: FuneralType; label: string }[] = [ { value: 'cremation-funeral', label: 'Cremation with funeral' }, { value: 'cremation-only', label: 'Cremation only (no funeral, no attendance)' }, { value: 'burial-funeral', label: 'Burial with funeral' }, { value: 'graveside-only', label: 'Graveside burial only' }, { value: 'water-cremation', label: 'Water cremation (QLD only)' }, ]; // ─── Step indicator ───────────────────────────────────────────────────────── const INDICATOR_SIZE = 48; const ICON_SIZE = 20; type StepState = 'inactive' | 'active' | 'completed'; function StepIndicator({ step, state }: { step: number; state: StepState }) { return ( {state === 'completed' ? ( ) : ( {step} )} ); } /** Inline error message shown below a field */ function FieldError({ children }: { children: React.ReactNode }) { return ( {children} ); } // ─── Shared select styles ─────────────────────────────────────────────────── const selectSx: SxProps = { width: '100%', bgcolor: 'var(--fa-color-surface-default, #fff)', '.MuiOutlinedInput-notchedOutline': { borderColor: 'var(--fa-color-neutral-200)', borderRadius: 'var(--fa-border-radius-md, 8px)', }, '&:hover:not(.Mui-disabled) .MuiOutlinedInput-notchedOutline': { borderColor: 'var(--fa-color-brand-400)', }, '&.Mui-focused': { boxShadow: 'none' }, '&.Mui-focused .MuiOutlinedInput-notchedOutline': { borderColor: 'var(--fa-color-brand-400)', borderWidth: 1, }, '&.Mui-disabled': { opacity: 0.6, '.MuiOutlinedInput-notchedOutline': { borderStyle: 'dashed' }, }, '&.Mui-error .MuiOutlinedInput-notchedOutline': { borderColor: 'var(--fa-color-brand-600, #B0610F)', }, '.MuiSelect-select': { py: '14px', px: 2, fontSize: '0.875rem', minHeight: 'unset !important', }, '.MuiSelect-icon': { color: 'var(--fa-color-neutral-400)' }, }; const selectMenuProps = { PaperProps: { sx: { mt: 0.5, borderRadius: 'var(--fa-border-radius-md, 8px)', boxShadow: 'var(--fa-shadow-md)', minWidth: 280, '& .MuiMenuItem-root': { py: 1.5, px: 2, fontSize: '0.875rem', whiteSpace: 'normal', '&:hover': { bgcolor: 'var(--fa-color-brand-50)' }, '&.Mui-selected': { bgcolor: 'var(--fa-color-surface-warm)', fontWeight: 600, '&:hover': { bgcolor: 'var(--fa-color-surface-warm)' }, }, }, }, }, }; // ─── Component ────────────────────────────────────────────────────────────── /** * FuneralFinder V4 — compact search widget. * * Based on V2's field set with a streamlined layout: * - No heading/subheading — designed to sit inside a hero or card * - 3 numbered steps (intent, planning-for, funeral type) with refined indicators * - Location field is always enabled (not a numbered step) * - "Search" CTA * * Conditional logic: * - "Arrange a funeral for someone who has passed" auto-sets step 2 * to "Someone else" and disables it. * - "Myself" is only available for pre-planning paths (expected / future). * - Steps 2 and 3 unlock sequentially; location is always available. */ export const FuneralFinderV4 = React.forwardRef( ({ onSearch, loading = false, sx }, ref) => { // ─── State ─────────────────────────────────────────────────── const [lookingTo, setLookingTo] = React.useState(''); const [planningFor, setPlanningFor] = React.useState(''); const [funeralType, setFuneralType] = React.useState(''); const [location, setLocation] = React.useState(''); const [submitted, setSubmitted] = React.useState(false); // ─── Derived ───────────────────────────────────────────────── const isArrangeNow = lookingTo === 'arrange-now'; const step2Disabled = !lookingTo || isArrangeNow; const step3Disabled = !planningFor; // Step states for indicators const step1State: StepState = lookingTo ? 'completed' : 'active'; const step2State: StepState = planningFor ? 'completed' : lookingTo ? 'active' : 'inactive'; const step3State: StepState = funeralType ? 'completed' : planningFor ? 'active' : 'inactive'; // Errors only show after first submit attempt const errs = submitted ? { lookingTo: !lookingTo, planningFor: !planningFor, funeralType: !funeralType, location: location.trim().length < 3, } : { lookingTo: false, planningFor: false, funeralType: false, location: false }; // ─── Handlers ──────────────────────────────────────────────── const handleLookingTo = (e: SelectChangeEvent) => { const val = e.target.value as LookingTo; setLookingTo(val); if (val === 'arrange-now') { setPlanningFor('someone-else'); } else { setPlanningFor(''); } }; const handlePlanningFor = (e: SelectChangeEvent) => { setPlanningFor(e.target.value as PlanningFor); }; const handleFuneralType = (e: SelectChangeEvent) => { setFuneralType(e.target.value as FuneralType); }; const handleSubmit = () => { setSubmitted(true); if (!lookingTo || !planningFor || !funeralType || location.trim().length < 3) return; onSearch?.({ lookingTo, planningFor, funeralType, location: location.trim(), }); }; // ─── Helpers ───────────────────────────────────────────────── const placeholder = ( Select… ); const findLabel = (opts: { value: string; label: string }[], val: string) => opts.find((o) => o.value === val)?.label ?? ''; // ─── Render ────────────────────────────────────────────────── return ( {/* ── Steps ───────────────────────────────────────────── */} {/* Step 1: I'm looking to */} I’m looking to… {errs.lookingTo && Please tell us what you need help with} {/* Step 2: I'm planning for */} I’m planning for… {errs.planningFor && Please select who you are planning for} {/* Step 3: Type of funeral */} Type of funeral {errs.funeralType && Please select a funeral type} {/* ── Location (not a numbered step) ─────────────────── */} Looking for providers in setLocation(e.target.value)} fullWidth disabled={!funeralType} error={errs.location} inputProps={{ 'aria-label': 'Suburb or postcode', 'aria-required': true }} onKeyDown={(e) => { if (e.key === 'Enter') handleSubmit(); }} sx={{ bgcolor: 'var(--fa-color-surface-default, #fff)', '& .MuiOutlinedInput-notchedOutline': { borderColor: 'var(--fa-color-neutral-200)', borderRadius: 'var(--fa-border-radius-md, 8px)', }, '&:hover:not(.Mui-disabled) .MuiOutlinedInput-notchedOutline': { borderColor: 'var(--fa-color-brand-400)', }, '&.Mui-focused': { boxShadow: 'none' }, '&.Mui-focused .MuiOutlinedInput-notchedOutline': { borderColor: 'var(--fa-color-brand-400)', borderWidth: '1px', }, '&.Mui-disabled': { opacity: 0.6, '& .MuiOutlinedInput-notchedOutline': { borderStyle: 'dashed' }, }, '&.Mui-error .MuiOutlinedInput-notchedOutline': { borderColor: 'var(--fa-color-brand-600, #B0610F)', }, '& .MuiOutlinedInput-input': { py: '14px', px: 2, fontSize: '0.875rem', }, }} /> {errs.location && Please enter a suburb or postcode} {/* ── CTA ─────────────────────────────────────────────── */} Free to use · No obligation ); }, ); FuneralFinderV4.displayName = 'FuneralFinderV4'; export default FuneralFinderV4;