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 FuneralFinderV2SearchParams { /** 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 v2 quick-form organism */ export interface FuneralFinderV2Props { /** Called when the user submits with valid data */ onSearch?: (params: FuneralFinderV2SearchParams) => void; /** Shows loading state on the CTA */ loading?: boolean; /** Optional heading override */ heading?: string; /** Optional subheading override */ subheading?: string; /** 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)' }, ]; // ─── Layout constants ──────────────────────────────────────────────────────── const STEP_CIRCLE_SIZE = 48; // ─── Sub-components ────────────────────────────────────────────────────────── /** Step number circle — transitions to a check icon when the step is completed */ function StepCircle({ step, completed, active = false, showConnector = false, }: { step: number; completed: boolean; /** Use primary brand fill even when not completed (for the first/always-active step) */ active?: boolean; /** Show a vertical connector line below this circle to the next step */ showConnector?: boolean; }) { const usePrimary = completed || active; return ( {completed ? ( ) : ( {step} )} ); } // ─── Shared 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-feedback-error-500, #d32f2f)', }, '.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 ─────────────────────────────────────────────────────────────── /** * Quick-form funeral search widget for the homepage hero. * * Three dropdown selects (intent, planning-for, type), a location input, * and a CTA — all always visible at a fixed height. Step numbers transition * to check icons as the user completes each field. * * 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). */ export const FuneralFinderV2 = React.forwardRef( ( { onSearch, loading = false, heading = 'Find funeral directors near you', subheading = "Tell us what you need and we'll show options in your area.", 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; const step4Disabled = !funeralType; // Errors only show after first submit attempt, then clear as fields fill const errs = submitted ? { lookingTo: !lookingTo, planningFor: !planningFor, funeralType: !funeralType, location: location.trim().length < 3, } : { lookingTo: false, planningFor: false, funeralType: false, location: false }; const hasErrors = submitted && (errs.lookingTo || errs.planningFor || errs.funeralType || errs.location); // ─── 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 ( {/* ── Header ──────────────────────────────────────────── */} {heading} {subheading} {/* ── Steps ───────────────────────────────────────────── */} {/* Step 1: I'm looking to */} I’m looking to… {/* Step 2: I'm planning for */} I’m planning for {/* Step 3: Type of funeral */} Type of funeral {/* Step 4: Location */} = 3} /> Looking for providers in setLocation(e.target.value)} fullWidth disabled={step4Disabled} 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-feedback-error-500, #d32f2f)', }, '& .MuiOutlinedInput-input': { py: '14px', px: 2, fontSize: '0.875rem', }, }} /> {/* ── CTA ─────────────────────────────────────────────── */} Free to use · No obligation {/* Error hint — always rendered to maintain fixed height */} {errs.location ? 'Please enter a suburb or postcode' : errs.lookingTo ? 'Please tell us what you need help with' : errs.funeralType ? 'Please select a funeral type' : errs.planningFor ? 'Please select who you are planning for' : '\u00A0'} ); }, ); FuneralFinderV2.displayName = 'FuneralFinderV2'; export default FuneralFinderV2;