Add FuneralFinder V4, HomePage V3/V4, restyle Footer to light grey

- FuneralFinder V4: 3 numbered steps (48px circles), ungated location,
  no heading, inline copper errors, "Search" CTA. Archived.
- FuneralFinderV3: heading weight 600, "Find your local providers",
  "Search Local Providers" CTA, optional subheading
- HomePage V1/V2: split into separate archived stories
- HomePage V3: hero-3.png, updated copy, venue photos, map placeholder,
  scrolling partner logo bar, warm gradient CTA, increased spacing
- HomePage V4: same as V3 with FuneralFinderV4 via new finderSlot prop
- Footer: surface.subtle bg (matches header), dark-on-light text
- All versions archived in Storybook

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-01 14:02:52 +11:00
parent aaa4f80a05
commit eb6cf6a185
12 changed files with 1521 additions and 138 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

View File

@@ -61,9 +61,10 @@ duplicates) and MUST update it after completing one.
| FuneralFinder (V3) | done | Typography + Button + Divider + Select + MenuItem + OutlinedInput + custom StatusCard/SectionLabel | **Production version.** Hero search widget — clean form with status cards. Standard card container (surface.raised, card shadow). "How Can We Help" section: two side-by-side StatusCards (Immediate Need default-selected / Pre-planning) — white bg, neutral border, brand border + warm bg when selected, stack on mobile. "Funeral Type" Select + "Location" OutlinedInput with pin icon — standard outlined fields, no focus ring (per design). Overline section labels (text.secondary). CTA "Find Funeral Directors →" always active — validates on click, scrolls to first missing field. Required: status + location. Funeral type defaults to "show all". Dividers after header and before CTA. WAI-ARIA roving tabindex on radiogroup. aria-labelledby via useId(). Critique: 33/40 (Good). Audit: 18/20 (Excellent). | | FuneralFinder (V3) | done | Typography + Button + Divider + Select + MenuItem + OutlinedInput + custom StatusCard/SectionLabel | **Production version.** Hero search widget — clean form with status cards. Standard card container (surface.raised, card shadow). "How Can We Help" section: two side-by-side StatusCards (Immediate Need default-selected / Pre-planning) — white bg, neutral border, brand border + warm bg when selected, stack on mobile. "Funeral Type" Select + "Location" OutlinedInput with pin icon — standard outlined fields, no focus ring (per design). Overline section labels (text.secondary). CTA "Find Funeral Directors →" always active — validates on click, scrolls to first missing field. Required: status + location. Funeral type defaults to "show all". Dividers after header and before CTA. WAI-ARIA roving tabindex on radiogroup. aria-labelledby via useId(). Critique: 33/40 (Good). Audit: 18/20 (Excellent). |
| FuneralFinder V1 | archived | Typography + Button + Chip + Input + Divider + Link + custom ChoiceCard/TypeCard/CompletedRow/StepHeading | Archived — viewable in Storybook under Archive/. Stepped conversational flow. Audit: 14/20. Critique: 29/40. | | FuneralFinder V1 | archived | Typography + Button + Chip + Input + Divider + Link + custom ChoiceCard/TypeCard/CompletedRow/StepHeading | Archived — viewable in Storybook under Archive/. Stepped conversational flow. Audit: 14/20. Critique: 29/40. |
| FuneralFinder V2 | archived | Typography + Button + Input + Divider + Select + MenuItem + custom StepCircle | Archived — viewable in Storybook under Archive/. Quick-form with step circles. Audit: 18/20. Critique: 33/40. | | FuneralFinder V2 | archived | Typography + Button + Input + Divider + Select + MenuItem + custom StepCircle | Archived — viewable in Storybook under Archive/. Quick-form with step circles. Audit: 18/20. Critique: 33/40. |
| FuneralFinder V4 | archived | Typography + Button + Input + Divider + Select + MenuItem + custom StepIndicator/FieldError | Archived. Based on V2 with: 3 numbered steps (48px circles, outline-to-fill + tick), ungated location field, no heading/subheading, "Search" CTA, inline copper error messages. |
| ArrangementForm | planned | StepIndicator + ServiceSelector + AddOnOption + Button + Typography | Multi-step arrangement wizard. Deferred — build remaining atoms/molecules first. | | 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). | | 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). | | Footer | done | Link × n + Typography + Divider + Container + Grid | Light grey (surface.subtle) site footer — matches header. Logo + tagline + contact (phone/email) + link group columns + legal bar. Semantic HTML (footer, nav, ul). Critique: 38/40 (Excellent). |
## Templates ## Templates
@@ -95,7 +96,7 @@ duplicates) and MUST update it after completing one.
| PaymentStep | done | WizardLayout (centered-form) + ToggleButtonGroup + Paper + Collapse + Checkbox + Divider + Button | Wizard step 14 — payment. Plan (full/deposit) + method (card/bank). PayWay iframe slot. Bank transfer details. Terms checkbox. | | PaymentStep | done | WizardLayout (centered-form) + ToggleButtonGroup + Paper + Collapse + Checkbox + Divider + Button | Wizard step 14 — payment. Plan (full/deposit) + method (card/bank). PayWay iframe slot. Bank transfer details. Terms checkbox. |
| ConfirmationStep | done | WizardLayout (centered-form) + Button | Wizard step 15 — confirmation. Terminal page. At-need: "submitted" + callback. Pre-planning: "saved" + return-anytime. Muted success icon. | | ConfirmationStep | done | WizardLayout (centered-form) + Button | Wizard step 15 — confirmation. Terminal page. At-need: "submitted" + callback. Pre-planning: "saved" + return-anytime. Muted success icon. |
| UnverifiedProviderStep | done | WizardLayout (list-detail) + ProviderCardCompact + ProviderCard + Badge + Button + Divider + Typography | Unverified provider detail. Left: compact card + "Listing" badge + available info (conditional dl) + verified recommendations. Right: warm header band + detail rows + "Make an Enquiry" CTA. Graceful degradation (no data → straight to enquiry). 4 story variants. | | UnverifiedProviderStep | done | WizardLayout (list-detail) + ProviderCardCompact + ProviderCard + Badge + Button + Divider + Typography | Unverified provider detail. Left: compact card + "Listing" badge + available info (conditional dl) + verified recommendations. Right: warm header band + detail rows + "Make an Enquiry" CTA. Graceful degradation (no data → straight to enquiry). 4 story variants. |
| HomePage | done | FuneralFinderV3 + ProviderCardCompact + Button + Typography + Accordion + Divider + Navigation (prop) + Footer (prop) | Marketing landing page. V1: split hero + card features. V2: full-bleed hero (parsonshero.png) + FuneralFinder inside hero + "See what you'll discover" map+compact cards + clean icon features (no cards/circles) + editorial alternating testimonials + minimal FAQ accordion. V2 uses `heroImageUrl` prop. | | HomePage | done | FuneralFinderV3/V4 (via finderSlot) + ProviderCardCompact + Button + Typography + Accordion + Divider + Navigation (prop) + Footer (prop) | Marketing landing page. 4 archived versions: V1 (split hero), V2 (full-bleed parsonshero.png), V3 (hero-3.png + updated copy + logo bar + venue photos + warm CTA gradient), V4 (same as V3 but with FuneralFinderV4 stepped form via finderSlot). `finderSlot` prop allows swapping finder widget. Light grey footer (surface.subtle). |
## Future enhancements ## Future enhancements

View File

@@ -26,6 +26,65 @@ Each entry follows this structure:
## Sessions ## Sessions
### Session 2026-04-01 — FuneralFinder V4, HomePage V3/V4, Footer restyle
**Agent(s):** Claude Opus 4.6 (1M context)
**Work completed:**
- **FuneralFinder V4 (new organism):** Based on V2 with major changes:
- 3 numbered steps only (lookingTo, planningFor, funeralType) — location is ungated, no step number
- No heading/subheading — compact widget for embedding in heroes
- Refined step indicators: 48px circles matching input height, outline-to-fill + tick animation, no connector lines
- Labels above, circle + input on same row centred
- Location field full-width, disabled until step 3 complete
- "Search" CTA, inline copper error messages per field (D034)
- Archived in Storybook under Archive/FuneralFinder V4
- **FuneralFinder V3 updates:**
- Removed default subheading (now optional)
- Heading font weight bumped from 400 → 600 (semibold) for more presence
- Heading copy: "Find your local providers"
- CTA: "Search Local Providers"
- **HomePage V1 archived:** Original split-hero layout moved to Archive/HomePage V1
- **HomePage V2 archived:** Full-bleed hero version broken out as Archive/HomePage V2
- **HomePage V3 (new, archived):** Based on V2 with:
- hero-3.png background image
- Updated hero copy + bullet-point subheading ("Transparent pricing · No hidden fees · Arrange 24/7")
- Real venue photos in discover section provider cards
- Map placeholder image from brandassets
- Scrolling partner logo bar (9 transparent logos, greyscale, edge-to-edge, continuous loop)
- CTA section: warm gradient bg, text button, no dividers, wider container
- Increased section spacing throughout (~25% more breathing room)
- Hero top padding increased 40%
- **HomePage V4 (new, archived):** Same as V3 but uses FuneralFinder V4 via `finderSlot` prop. Wider container (620px) for stepped form.
- **HomePage component updates:**
- Added `finderSlot` prop — allows swapping the finder widget without changing the component
- Increased spacing: hero heading/subtext gap, finder-to-discover gap, all section padding
- Map container: absolute positioning for dynamic height matching card stack
- CTA section: gradient bg (brand.100 → surface.warm), text button, no dividers
- Partner logos: lighter treatment (40% opacity, brightness lift), 48→55px height, 96px gap, no hover interaction
- **Footer restyle:** Changed from dark espresso (brand.950) to light grey (surface.subtle) matching the header. All text colours flipped to dark-on-light. Logo no longer needs inverse filter.
**Decisions made:**
- FuneralFinder V4 step indicators use 48px circles (same height as inputs) for visual alignment
- Location field in V4 is gated behind step 3 completion but not numbered
- Inline error messages replace single bottom error — each field shows its own copper-coloured message
- Footer matches header bg colour (surface.subtle) for visual consistency
- Partner logos: only transparent PNGs/WebPs used, opaque logos excluded
- `finderSlot` prop added to HomePage for widget flexibility without component duplication
**Open questions:**
- HomePage V4 finder width may need further adjustment — looked narrow in Storybook preview panel (620px container, may need 680-720px)
- Which HomePage version (V3 with FuneralFinderV3 or V4 with FuneralFinderV4) will be the production version?
- FuneralFinder V4 step indicator design — user liked the direction but may want further iteration
**Next steps:**
- Resolve HomePage V4 finder width — test at wider viewport or increase container
- Decide on production HomePage + FuneralFinder version
- Build next components/pages as needed
- Phase 4 remaining: /typeset sample, /preflight full codebase
---
### Session 2026-03-31d — Filters, control bars, UnverifiedProviderStep, HomePage V2 ### Session 2026-03-31d — Filters, control bars, UnverifiedProviderStep, HomePage V2
**Agent(s):** Claude Opus 4.6 (1M context) **Agent(s):** Claude Opus 4.6 (1M context)

View File

@@ -69,7 +69,7 @@ export const Footer = React.forwardRef<HTMLDivElement, FooterProps>(
const copyrightText = copyright || `\u00A9 ${year} Funeral Arranger. All rights reserved.`; const copyrightText = copyright || `\u00A9 ${year} Funeral Arranger. All rights reserved.`;
const overlineSx = { const overlineSx = {
color: 'var(--fa-color-brand-400)', color: 'text.secondary',
textTransform: 'uppercase' as const, textTransform: 'uppercase' as const,
letterSpacing: '0.08em', letterSpacing: '0.08em',
display: 'block', display: 'block',
@@ -77,8 +77,8 @@ export const Footer = React.forwardRef<HTMLDivElement, FooterProps>(
}; };
const contactLinkSx = { const contactLinkSx = {
color: 'var(--fa-color-white)', color: 'text.primary',
'&:hover': { color: 'var(--fa-color-brand-300)' }, '&:hover': { color: 'var(--fa-color-brand-600)' },
}; };
return ( return (
@@ -87,8 +87,8 @@ export const Footer = React.forwardRef<HTMLDivElement, FooterProps>(
component="footer" component="footer"
sx={[ sx={[
{ {
bgcolor: 'var(--fa-color-brand-950)', bgcolor: 'var(--fa-color-surface-subtle)',
color: 'var(--fa-color-white)', color: 'text.primary',
pt: { xs: 5, md: 8 }, pt: { xs: 5, md: 8 },
pb: 0, pb: 0,
}, },
@@ -104,7 +104,7 @@ export const Footer = React.forwardRef<HTMLDivElement, FooterProps>(
{tagline && ( {tagline && (
<Typography <Typography
variant="body2" variant="body2"
sx={{ color: 'var(--fa-color-brand-300)', maxWidth: { xs: '100%', md: 280 } }} sx={{ color: 'text.secondary', maxWidth: { xs: '100%', md: 280 } }}
> >
{tagline} {tagline}
</Typography> </Typography>
@@ -168,7 +168,7 @@ export const Footer = React.forwardRef<HTMLDivElement, FooterProps>(
<Typography <Typography
variant="label" variant="label"
sx={{ sx={{
color: 'var(--fa-color-brand-300)', color: 'text.secondary',
mb: 2, mb: 2,
display: 'block', display: 'block',
}} }}
@@ -192,13 +192,13 @@ export const Footer = React.forwardRef<HTMLDivElement, FooterProps>(
href={link.href} href={link.href}
onClick={link.onClick} onClick={link.onClick}
sx={{ sx={{
color: 'var(--fa-color-brand-200)', color: 'text.primary',
fontSize: '0.875rem', fontSize: '0.875rem',
fontWeight: 500, fontWeight: 500,
display: 'inline-flex', display: 'inline-flex',
alignItems: 'center', alignItems: 'center',
minHeight: 44, minHeight: 44,
'&:hover': { color: 'var(--fa-color-white)' }, '&:hover': { color: 'var(--fa-color-brand-600)' },
}} }}
> >
{link.label} {link.label}
@@ -211,7 +211,7 @@ export const Footer = React.forwardRef<HTMLDivElement, FooterProps>(
</Grid> </Grid>
{/* Bottom bar */} {/* Bottom bar */}
<Divider sx={{ borderColor: 'var(--fa-color-brand-900)', mt: { xs: 5, md: 8 } }} /> <Divider sx={{ mt: { xs: 5, md: 8 } }} />
<Box <Box
sx={{ sx={{
@@ -223,7 +223,7 @@ export const Footer = React.forwardRef<HTMLDivElement, FooterProps>(
py: 3, py: 3,
}} }}
> >
<Typography variant="captionSm" sx={{ color: 'var(--fa-color-brand-400)' }}> <Typography variant="captionSm" sx={{ color: 'text.secondary' }}>
{copyrightText} {copyrightText}
</Typography> </Typography>
@@ -242,13 +242,13 @@ export const Footer = React.forwardRef<HTMLDivElement, FooterProps>(
href={link.href} href={link.href}
onClick={link.onClick} onClick={link.onClick}
sx={{ sx={{
color: 'var(--fa-color-brand-400)', color: 'text.secondary',
fontSize: '0.75rem', fontSize: '0.75rem',
fontWeight: 500, fontWeight: 500,
display: 'inline-flex', display: 'inline-flex',
alignItems: 'center', alignItems: 'center',
minHeight: 44, minHeight: 44,
'&:hover': { color: 'var(--fa-color-white)' }, '&:hover': { color: 'var(--fa-color-brand-600)' },
}} }}
> >
{link.label} {link.label}

View File

@@ -244,7 +244,7 @@ export const FuneralFinderV3 = React.forwardRef<HTMLDivElement, FuneralFinderV3P
onSearch, onSearch,
loading = false, loading = false,
heading = 'Find funeral directors near you', heading = 'Find funeral directors near you',
subheading = 'Tell us what you need and we\u2019ll show options in your area.', subheading,
sx, sx,
} = props; } = props;
@@ -367,15 +367,17 @@ export const FuneralFinderV3 = React.forwardRef<HTMLDivElement, FuneralFinderV3P
component="h2" component="h2"
sx={{ sx={{
fontFamily: 'var(--fa-font-family-display)', fontFamily: 'var(--fa-font-family-display)',
fontWeight: 400, fontWeight: 600,
mb: 1, mb: subheading ? 1 : 0,
}} }}
> >
{heading} {heading}
</Typography> </Typography>
<Typography variant="body2" color="text.secondary"> {subheading && (
{subheading} <Typography variant="body2" color="text.secondary">
</Typography> {subheading}
</Typography>
)}
</Box> </Box>
<Divider /> <Divider />
@@ -527,7 +529,7 @@ export const FuneralFinderV3 = React.forwardRef<HTMLDivElement, FuneralFinderV3P
onClick={handleSubmit} onClick={handleSubmit}
sx={{ minHeight: 52 }} sx={{ minHeight: 52 }}
> >
Find Funeral Directors Search Local Providers
</Button> </Button>
<Typography <Typography
variant="captionSm" variant="captionSm"

View File

@@ -0,0 +1,63 @@
import type { Meta, StoryObj } from '@storybook/react';
import Box from '@mui/material/Box';
import { FuneralFinderV4 } from './FuneralFinderV4';
const meta: Meta<typeof FuneralFinderV4> = {
title: 'Archive/FuneralFinder V4',
component: FuneralFinderV4,
parameters: {
layout: 'padded',
},
args: {
onSearch: (params) => {
console.log('Search params:', params);
},
},
};
export default meta;
type Story = StoryObj<typeof FuneralFinderV4>;
/** Default empty state — 3 steps + location ready for input */
export const Default: Story = {};
/** Loading state — CTA shows spinner */
export const Loading: Story = {
args: { loading: true },
};
/** Placed inside a dark hero section to preview in context */
export const InsideHero: Story = {
decorators: [
(Story) => (
<Box>
<Box
sx={{
bgcolor: 'var(--fa-color-sage-800, #4c5459)',
color: '#fff',
py: 8,
px: 4,
textAlign: 'center',
}}
>
<Box sx={{ fontSize: '2.5rem', fontWeight: 700, mb: 1 }}>Funeral Arranger</Box>
<Box sx={{ opacity: 0.8, mb: 4 }}>Find trusted funeral directors near you</Box>
<Box sx={{ maxWidth: 480, mx: 'auto' }}>
<Story />
</Box>
</Box>
</Box>
),
],
};
/** Constrained width — typical sidebar or narrow column */
export const Narrow: Story = {
decorators: [
(Story) => (
<Box sx={{ maxWidth: 380, mx: 'auto' }}>
<Story />
</Box>
),
],
};

View File

@@ -0,0 +1,493 @@
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<Theme>;
}
// ─── 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 (
<Box
sx={{
width: INDICATOR_SIZE,
height: INDICATOR_SIZE,
borderRadius: '50%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
flexShrink: 0,
transition: 'all 250ms cubic-bezier(0.4, 0, 0.2, 1)',
...(state === 'completed' && {
bgcolor: 'var(--fa-color-brand-500)',
color: 'common.white',
boxShadow: '0 0 0 3px var(--fa-color-brand-100, #F5EDE4)',
}),
...(state === 'active' && {
bgcolor: 'var(--fa-color-brand-500)',
color: 'common.white',
boxShadow: '0 0 0 3px var(--fa-color-brand-100, #F5EDE4)',
}),
...(state === 'inactive' && {
bgcolor: 'transparent',
border: '2px solid var(--fa-color-neutral-300, #C4C4C4)',
color: 'var(--fa-color-neutral-400, #9E9E9E)',
}),
}}
>
{state === 'completed' ? (
<CheckIcon
sx={{
fontSize: ICON_SIZE,
animation: 'fadeScaleIn 250ms cubic-bezier(0.34, 1.56, 0.64, 1)',
'@keyframes fadeScaleIn': {
'0%': { opacity: 0, transform: 'scale(0.5)' },
'100%': { opacity: 1, transform: 'scale(1)' },
},
}}
/>
) : (
<Typography
variant="captionSm"
component="span"
sx={{
fontWeight: 700,
fontSize: '1.125rem',
lineHeight: 1,
color: 'inherit',
}}
>
{step}
</Typography>
)}
</Box>
);
}
/** Inline error message shown below a field */
function FieldError({ children }: { children: React.ReactNode }) {
return (
<Typography
variant="captionSm"
role="alert"
sx={{
mt: 0.5,
color: 'var(--fa-color-brand-600, #B0610F)',
}}
>
{children}
</Typography>
);
}
// ─── Shared select styles ───────────────────────────────────────────────────
const selectSx: SxProps<Theme> = {
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<HTMLDivElement, FuneralFinderV4Props>(
({ onSearch, loading = false, sx }, ref) => {
// ─── State ───────────────────────────────────────────────────
const [lookingTo, setLookingTo] = React.useState<LookingTo | ''>('');
const [planningFor, setPlanningFor] = React.useState<PlanningFor | ''>('');
const [funeralType, setFuneralType] = React.useState<FuneralType | ''>('');
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<string>) => {
const val = e.target.value as LookingTo;
setLookingTo(val);
if (val === 'arrange-now') {
setPlanningFor('someone-else');
} else {
setPlanningFor('');
}
};
const handlePlanningFor = (e: SelectChangeEvent<string>) => {
setPlanningFor(e.target.value as PlanningFor);
};
const handleFuneralType = (e: SelectChangeEvent<string>) => {
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 = (
<span style={{ color: 'var(--fa-color-neutral-400)' }}>Select&hellip;</span>
);
const findLabel = (opts: { value: string; label: string }[], val: string) =>
opts.find((o) => o.value === val)?.label ?? '';
// ─── Render ──────────────────────────────────────────────────
return (
<Box
ref={ref}
role="search"
aria-label="Find funeral providers"
sx={[
{
bgcolor: 'background.paper',
borderRadius: 'var(--fa-card-border-radius-default, 12px)',
boxShadow: 'var(--fa-shadow-md)',
textAlign: 'left',
px: { xs: 3, sm: 5 },
py: { xs: 3, sm: 4 },
},
...(Array.isArray(sx) ? sx : [sx]),
]}
>
{/* ── Steps ───────────────────────────────────────────── */}
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3.5 }}>
{/* Step 1: I'm looking to */}
<Box>
<Typography
variant="body1"
sx={{ fontWeight: 600, mb: 0.75, color: 'var(--fa-color-brand-700)' }}
>
I&rsquo;m looking to&hellip;
</Typography>
<Box sx={{ display: 'flex', gap: 2, alignItems: 'center' }}>
<StepIndicator step={1} state={step1State} />
<Select
value={lookingTo}
onChange={handleLookingTo}
displayEmpty
error={errs.lookingTo}
renderValue={(v) => (v ? findLabel(LOOKING_TO_OPTIONS, v) : placeholder)}
MenuProps={selectMenuProps}
sx={{ ...selectSx, flex: 1 } as SxProps<Theme>}
inputProps={{ 'aria-label': "I'm looking to", 'aria-required': true }}
>
{LOOKING_TO_OPTIONS.map((o) => (
<MenuItem key={o.value} value={o.value}>
{o.label}
</MenuItem>
))}
</Select>
</Box>
{errs.lookingTo && <FieldError>Please tell us what you need help with</FieldError>}
</Box>
{/* Step 2: I'm planning for */}
<Box>
<Typography
variant="body1"
sx={{
fontWeight: 600,
mb: 0.75,
color: lookingTo ? 'var(--fa-color-brand-700)' : 'text.disabled',
}}
>
I&rsquo;m planning for&hellip;
</Typography>
<Box sx={{ display: 'flex', gap: 2, alignItems: 'center' }}>
<StepIndicator step={2} state={step2State} />
<Select
value={planningFor}
onChange={handlePlanningFor}
displayEmpty
disabled={step2Disabled}
error={errs.planningFor}
renderValue={(v) => (v ? findLabel(PLANNING_FOR_OPTIONS, v) : placeholder)}
MenuProps={selectMenuProps}
sx={{ ...selectSx, flex: 1 } as SxProps<Theme>}
inputProps={{ 'aria-label': "I'm planning for", 'aria-required': true }}
>
{PLANNING_FOR_OPTIONS.map((o) => (
<MenuItem
key={o.value}
value={o.value}
disabled={isArrangeNow && o.value === 'myself'}
>
{o.label}
</MenuItem>
))}
</Select>
</Box>
{errs.planningFor && <FieldError>Please select who you are planning for</FieldError>}
</Box>
{/* Step 3: Type of funeral */}
<Box>
<Typography
variant="body1"
sx={{
fontWeight: 600,
mb: 0.75,
color: step3Disabled ? 'text.disabled' : 'var(--fa-color-brand-700)',
}}
>
Type of funeral
</Typography>
<Box sx={{ display: 'flex', gap: 2, alignItems: 'center' }}>
<StepIndicator step={3} state={step3State} />
<Select
value={funeralType}
onChange={handleFuneralType}
displayEmpty
disabled={step3Disabled}
error={errs.funeralType}
renderValue={(v) => (v ? findLabel(FUNERAL_TYPE_OPTIONS, v) : placeholder)}
MenuProps={selectMenuProps}
sx={{ ...selectSx, flex: 1 } as SxProps<Theme>}
inputProps={{ 'aria-label': 'Type of funeral', 'aria-required': true }}
>
{FUNERAL_TYPE_OPTIONS.map((o) => (
<MenuItem key={o.value} value={o.value}>
{o.label}
</MenuItem>
))}
</Select>
</Box>
{errs.funeralType && <FieldError>Please select a funeral type</FieldError>}
</Box>
</Box>
{/* ── Location (not a numbered step) ─────────────────── */}
<Box sx={{ mt: 3.5 }}>
<Typography
variant="body1"
sx={{
fontWeight: 600,
mb: 0.75,
color: funeralType ? 'var(--fa-color-brand-700)' : 'text.disabled',
}}
>
Looking for providers in
</Typography>
<Input
placeholder="Suburb or postcode"
value={location}
onChange={(e) => 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 && <FieldError>Please enter a suburb or postcode</FieldError>}
</Box>
{/* ── CTA ─────────────────────────────────────────────── */}
<Divider sx={{ my: 3 }} />
<Box>
<Button
variant="contained"
size="large"
fullWidth
loading={loading}
disabled={loading}
onClick={handleSubmit}
>
Search
</Button>
<Typography
variant="captionSm"
color="text.secondary"
sx={{ textAlign: 'center', display: 'block', mt: 1.5 }}
>
Free to use &middot; No obligation
</Typography>
</Box>
</Box>
);
},
);
FuneralFinderV4.displayName = 'FuneralFinderV4';
export default FuneralFinderV4;

View File

@@ -5,7 +5,6 @@ import AccessTimeIcon from '@mui/icons-material/AccessTime';
import SearchIcon from '@mui/icons-material/Search'; import SearchIcon from '@mui/icons-material/Search';
import SupportAgentOutlinedIcon from '@mui/icons-material/SupportAgentOutlined'; import SupportAgentOutlinedIcon from '@mui/icons-material/SupportAgentOutlined';
import { HomePage } from './HomePage'; import { HomePage } from './HomePage';
import type { FeaturedProvider, TrustStat } from './HomePage';
import { Navigation } from '../../organisms/Navigation'; import { Navigation } from '../../organisms/Navigation';
import { Footer } from '../../organisms/Footer'; import { Footer } from '../../organisms/Footer';
@@ -210,9 +209,8 @@ const faqItems = [
// ─── Meta ──────────────────────────────────────────────────────────────────── // ─── Meta ────────────────────────────────────────────────────────────────────
const meta: Meta<typeof HomePage> = { const meta: Meta<typeof HomePage> = {
title: 'Pages/HomePage', title: 'Archive/HomePage V1',
component: HomePage, component: HomePage,
tags: ['autodocs'],
parameters: { parameters: {
layout: 'fullscreen', layout: 'fullscreen',
}, },
@@ -265,68 +263,3 @@ export const Mobile: Story = {
viewport: { defaultViewport: 'mobile1' }, viewport: { defaultViewport: 'mobile1' },
}, },
}; };
// ─── V2 data ────────────────────────────────────────────────────────────────
const trustStats: TrustStat[] = [
{ value: '1,500+', label: 'Families helped' },
{ value: '4.9', label: 'Google Rating' },
{ value: '300+', label: 'Funeral directors' },
];
const featuredProviders: FeaturedProvider[] = [
{
id: 'parsons',
name: 'H.Parsons Funeral Directors',
location: 'Wentworth, NSW',
verified: true,
imageUrl: 'https://placehold.co/600x200/E8E0D6/8B6F47?text=H.Parsons',
logoUrl: 'https://placehold.co/64x64/FEF9F5/BA834E?text=HP',
rating: 4.6,
reviewCount: 7,
startingPrice: 900,
},
{
id: 'rankins',
name: 'Rankins Funeral Services',
location: 'Wollongong, NSW',
verified: true,
imageUrl: 'https://placehold.co/600x200/D7E1E2/4C5B6B?text=Rankins',
logoUrl: 'https://placehold.co/64x64/F2F5F6/4C5B6B?text=R',
rating: 4.8,
reviewCount: 23,
startingPrice: 1200,
},
{
id: 'easy-funerals',
name: 'Easy Funerals',
location: 'Sydney, NSW',
verified: true,
imageUrl: 'https://placehold.co/600x200/F0F7F0/3B7A3B?text=Easy+Funerals',
logoUrl: 'https://placehold.co/64x64/F0F7F0/3B7A3B?text=EF',
rating: 4.5,
reviewCount: 42,
startingPrice: 850,
},
];
// ─── V2 Story ───────────────────────────────────────────────────────────────
/** V2 layout — full-bleed hero, stats bar, map + provider cards, editorial testimonials */
export const V2: Story = {
args: {
navigation: nav,
footer,
heroImageUrl: '/brandassets/images/heroes/parsonshero.png',
stats: trustStats,
featuredProviders,
onSelectFeaturedProvider: (id) => console.log('Featured provider:', id),
features,
googleRating: 4.9,
googleReviewCount: 2340,
testimonials,
faqItems,
onSearch: (params) => console.log('Search:', params),
onCtaClick: () => console.log('CTA clicked'),
},
};

View File

@@ -80,9 +80,11 @@ export interface HomePageProps {
/** Hero background image URL (full-bleed layout — V2). Takes priority over heroImage. */ /** Hero background image URL (full-bleed layout — V2). Takes priority over heroImage. */
heroImageUrl?: string; heroImageUrl?: string;
/** FuneralFinder search callback */ /** Override the default FuneralFinder widget with a custom element */
finderSlot?: React.ReactNode;
/** FuneralFinder search callback (used when finderSlot is not provided) */
onSearch?: (params: FuneralFinderV3SearchParams) => void; onSearch?: (params: FuneralFinderV3SearchParams) => void;
/** FuneralFinder loading state */ /** FuneralFinder loading state (used when finderSlot is not provided) */
searchLoading?: boolean; searchLoading?: boolean;
/** Trust stats bar (e.g. "1,500+ families helped") */ /** Trust stats bar (e.g. "1,500+ families helped") */
@@ -174,6 +176,7 @@ export const HomePage = React.forwardRef<HTMLDivElement, HomePageProps>(
heroSubheading = "Funeral planning doesn't have to be overwhelming. Whether you're thinking ahead or arranging for a loved one, find trusted local providers with transparent pricing, all at your own pace.", heroSubheading = "Funeral planning doesn't have to be overwhelming. Whether you're thinking ahead or arranging for a loved one, find trusted local providers with transparent pricing, all at your own pace.",
heroImage, heroImage,
heroImageUrl, heroImageUrl,
finderSlot,
onSearch, onSearch,
searchLoading, searchLoading,
partnerLogos = [], partnerLogos = [],
@@ -242,7 +245,7 @@ export const HomePage = React.forwardRef<HTMLDivElement, HomePageProps>(
position: 'relative', position: 'relative',
zIndex: 1, zIndex: 1,
textAlign: 'center', textAlign: 'center',
pt: { xs: 6, md: 8 }, pt: { xs: 8, md: 11 },
pb: 4, pb: 4,
}} }}
> >
@@ -251,7 +254,7 @@ export const HomePage = React.forwardRef<HTMLDivElement, HomePageProps>(
component="h1" component="h1"
id="hero-heading" id="hero-heading"
tabIndex={-1} tabIndex={-1}
sx={{ mb: 2, color: 'var(--fa-color-white)' }} sx={{ mb: 3, color: 'var(--fa-color-white)' }}
> >
{heroHeading} {heroHeading}
</Typography> </Typography>
@@ -269,17 +272,19 @@ export const HomePage = React.forwardRef<HTMLDivElement, HomePageProps>(
position: 'relative', position: 'relative',
zIndex: 2, zIndex: 2,
px: 2, px: 2,
pt: 2,
pb: 0, pb: 0,
mb: { xs: -14, md: -18 }, mb: { xs: -14, md: -18 },
}} }}
> >
<Box sx={{ maxWidth: 520, mx: 'auto' }}> <Box sx={{ maxWidth: finderSlot ? 800 : 520, mx: 'auto' }}>
<FuneralFinderV3 {finderSlot || (
heading="Find funeral providers near you" <FuneralFinderV3
subheading="Tell us a little about what you're looking for and we'll show you options in your area." heading="Find your local providers"
onSearch={onSearch} onSearch={onSearch}
loading={searchLoading} loading={searchLoading}
/> />
)}
</Box> </Box>
</Box> </Box>
</Box> </Box>
@@ -362,12 +367,13 @@ export const HomePage = React.forwardRef<HTMLDivElement, HomePageProps>(
}} }}
> >
<Box sx={{ maxWidth: 620, mx: 'auto' }}> <Box sx={{ maxWidth: 620, mx: 'auto' }}>
<FuneralFinderV3 {finderSlot || (
heading="Find funeral providers near you" <FuneralFinderV3
subheading="Tell us a little about what you're looking for and we'll show you options in your area." heading="Find your local providers"
onSearch={onSearch} onSearch={onSearch}
loading={searchLoading} loading={searchLoading}
/> />
)}
</Box> </Box>
</Box> </Box>
)} )}
@@ -381,17 +387,17 @@ export const HomePage = React.forwardRef<HTMLDivElement, HomePageProps>(
aria-labelledby="discover-heading" aria-labelledby="discover-heading"
sx={{ sx={{
bgcolor: 'var(--fa-color-surface-subtle)', bgcolor: 'var(--fa-color-surface-subtle)',
pt: { xs: 20, md: 24 }, pt: { xs: 22, md: 28 },
pb: { xs: 6, md: 10 }, pb: { xs: 8, md: 12 },
}} }}
> >
<Container maxWidth="lg"> <Container maxWidth="lg">
<Box sx={{ textAlign: 'center', mb: { xs: 4, md: 6 } }}> <Box sx={{ textAlign: 'center', mb: { xs: 5, md: 8 } }}>
<Typography <Typography
variant="display3" variant="display3"
component="h2" component="h2"
id="discover-heading" id="discover-heading"
sx={{ mb: 1.5, color: 'text.primary' }} sx={{ mb: 2, color: 'text.primary' }}
> >
See what you'll discover See what you'll discover
</Typography> </Typography>
@@ -413,24 +419,38 @@ export const HomePage = React.forwardRef<HTMLDivElement, HomePageProps>(
alignItems: 'stretch', alignItems: 'stretch',
}} }}
> >
{/* Map placeholder — stretches to match card stack */} {/* Map — fills the grid cell to match card stack height */}
<Box <Box
sx={{ sx={{
position: 'relative',
borderRadius: 2, borderRadius: 2,
overflow: 'hidden', overflow: 'hidden',
minHeight: { xs: 240, md: 0 }, minHeight: { xs: 240, md: 0 },
bgcolor: 'var(--fa-color-surface-cool)', bgcolor: 'var(--fa-color-surface-cool)',
border: '1px solid', border: '1px solid',
borderColor: 'divider', borderColor: 'divider',
display: 'flex', '& > img, & > div': {
alignItems: 'center', position: { md: 'absolute' },
justifyContent: 'center', inset: { md: 0 },
width: '100%',
height: '100%',
objectFit: 'cover',
},
}} }}
> >
{discoverMapSlot || ( {discoverMapSlot || (
<Typography variant="body2" color="text.secondary"> <Box
Map coming soon sx={{
</Typography> display: 'flex',
alignItems: 'center',
justifyContent: 'center',
height: '100%',
}}
>
<Typography variant="body2" color="text.secondary">
Map coming soon
</Typography>
</Box>
)} )}
</Box> </Box>
@@ -473,15 +493,15 @@ export const HomePage = React.forwardRef<HTMLDivElement, HomePageProps>(
aria-label="Trusted partners" aria-label="Trusted partners"
sx={{ sx={{
bgcolor: 'var(--fa-color-surface-cool)', bgcolor: 'var(--fa-color-surface-cool)',
pt: { xs: 8, md: 10 }, pt: { xs: 10, md: 13 },
pb: { xs: 6, md: 8 }, pb: { xs: 8, md: 10 },
}} }}
> >
<Container maxWidth="lg"> <Container maxWidth="lg">
<Typography <Typography
variant="body1" variant="body1"
color="text.secondary" color="text.secondary"
sx={{ textAlign: 'center', mb: { xs: 3, md: 4 } }} sx={{ textAlign: 'center', mb: { xs: 4, md: 6 } }}
> >
{partnerTrustLine} {partnerTrustLine}
</Typography> </Typography>
@@ -518,7 +538,7 @@ export const HomePage = React.forwardRef<HTMLDivElement, HomePageProps>(
aria-label="Partner funeral directors" aria-label="Partner funeral directors"
sx={{ sx={{
display: 'flex', display: 'flex',
gap: { xs: 6, md: 8 }, gap: { xs: 8, md: 12 },
alignItems: 'center', alignItems: 'center',
width: 'max-content', width: 'max-content',
animation: 'logoScroll 35s linear infinite', animation: 'logoScroll 35s linear infinite',
@@ -538,13 +558,12 @@ export const HomePage = React.forwardRef<HTMLDivElement, HomePageProps>(
alt={i < partnerLogos.length ? logo.alt : ''} alt={i < partnerLogos.length ? logo.alt : ''}
aria-hidden={i >= partnerLogos.length ? true : undefined} aria-hidden={i >= partnerLogos.length ? true : undefined}
sx={{ sx={{
height: { xs: 48, md: 64 }, height: { xs: 46, md: 55 },
maxWidth: { xs: 140, md: 184 },
width: 'auto', width: 'auto',
objectFit: 'contain', objectFit: 'contain',
filter: 'grayscale(100%)', filter: 'grayscale(100%) brightness(1.2)',
opacity: 0.5, opacity: 0.4,
transition: 'opacity 0.2s, filter 0.2s',
'&:hover': { filter: 'grayscale(0%)', opacity: 1 },
flexShrink: 0, flexShrink: 0,
}} }}
/> />
@@ -563,16 +582,16 @@ export const HomePage = React.forwardRef<HTMLDivElement, HomePageProps>(
aria-labelledby="features-heading" aria-labelledby="features-heading"
sx={{ sx={{
bgcolor: 'var(--fa-color-surface-default)', bgcolor: 'var(--fa-color-surface-default)',
py: { xs: 6, md: 10 }, py: { xs: 8, md: 12 },
}} }}
> >
<Container maxWidth="lg"> <Container maxWidth="lg">
<Box sx={{ textAlign: 'center', mb: { xs: 4, md: 6 } }}> <Box sx={{ textAlign: 'center', mb: { xs: 5, md: 8 } }}>
<Typography <Typography
variant="display3" variant="display3"
component="h2" component="h2"
id="features-heading" id="features-heading"
sx={{ mb: 2, color: 'text.primary' }} sx={{ mb: 2.5, color: 'text.primary' }}
> >
{featuresHeading} {featuresHeading}
</Typography> </Typography>
@@ -628,7 +647,7 @@ export const HomePage = React.forwardRef<HTMLDivElement, HomePageProps>(
component="section" component="section"
aria-labelledby="reviews-heading" aria-labelledby="reviews-heading"
sx={{ sx={{
py: { xs: 6, md: 10 }, py: { xs: 8, md: 12 },
bgcolor: 'var(--fa-color-surface-subtle)', bgcolor: 'var(--fa-color-surface-subtle)',
}} }}
> >
@@ -728,20 +747,21 @@ export const HomePage = React.forwardRef<HTMLDivElement, HomePageProps>(
component="section" component="section"
aria-labelledby="cta-heading" aria-labelledby="cta-heading"
sx={{ sx={{
py: { xs: 6, md: 8 }, background:
'linear-gradient(180deg, var(--fa-color-brand-100, #F5EDE4) 0%, var(--fa-color-surface-warm, #FEF9F5) 100%)',
py: { xs: 8, md: 10 },
}} }}
> >
<Container maxWidth="sm" sx={{ textAlign: 'center' }}> <Container maxWidth="md" sx={{ textAlign: 'center' }}>
<Divider sx={{ mb: { xs: 5, md: 6 } }} />
<Typography <Typography
variant="display3" variant="display3"
component="h2" component="h2"
id="cta-heading" id="cta-heading"
sx={{ mb: 3, color: 'text.primary' }} sx={{ mb: 3, color: 'text.primary', whiteSpace: { md: 'nowrap' } }}
> >
{ctaHeading} {ctaHeading}
</Typography> </Typography>
<Button variant="contained" size="large" onClick={onCtaClick}> <Button variant="text" size="large" onClick={onCtaClick}>
{ctaButtonLabel} {ctaButtonLabel}
</Button> </Button>
</Container> </Container>
@@ -756,7 +776,7 @@ export const HomePage = React.forwardRef<HTMLDivElement, HomePageProps>(
aria-labelledby="faq-heading" aria-labelledby="faq-heading"
sx={{ sx={{
bgcolor: 'var(--fa-color-surface-default)', bgcolor: 'var(--fa-color-surface-default)',
py: { xs: 6, md: 10 }, py: { xs: 8, md: 12 },
}} }}
> >
<Container maxWidth="lg"> <Container maxWidth="lg">
@@ -764,7 +784,7 @@ export const HomePage = React.forwardRef<HTMLDivElement, HomePageProps>(
variant="h2" variant="h2"
component="h2" component="h2"
id="faq-heading" id="faq-heading"
sx={{ textAlign: 'center', mb: { xs: 4, md: 6 }, color: 'text.primary' }} sx={{ textAlign: 'center', mb: { xs: 5, md: 8 }, color: 'text.primary' }}
> >
FAQ FAQ
</Typography> </Typography>

View File

@@ -0,0 +1,246 @@
import type { Meta, StoryObj } from '@storybook/react';
import Box from '@mui/material/Box';
import VerifiedOutlinedIcon from '@mui/icons-material/VerifiedOutlined';
import AccessTimeIcon from '@mui/icons-material/AccessTime';
import SearchIcon from '@mui/icons-material/Search';
import SupportAgentOutlinedIcon from '@mui/icons-material/SupportAgentOutlined';
import { HomePage } from './HomePage';
import type { FeaturedProvider, TrustStat } from './HomePage';
import { Navigation } from '../../organisms/Navigation';
import { Footer } from '../../organisms/Footer';
// ─── Shared helpers ──────────────────────────────────────────────────────────
const FALogo = () => (
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Box
component="img"
src="/brandlogo/logo-full.svg"
alt="Funeral Arranger"
sx={{ height: 28, display: { xs: 'none', md: 'block' } }}
/>
<Box
component="img"
src="/brandlogo/logo-short.svg"
alt="Funeral Arranger"
sx={{ height: 28, display: { xs: 'block', md: 'none' } }}
/>
</Box>
);
const FALogoInverse = () => (
<Box
component="img"
src="/brandlogo/logo-full.svg"
alt="Funeral Arranger"
sx={{ height: 24, filter: 'brightness(0) invert(1)', opacity: 0.9 }}
/>
);
const nav = (
<Navigation
logo={<FALogo />}
items={[
{ label: 'FAQ', href: '/faq' },
{ label: 'Contact Us', href: '/contact' },
{ label: 'Log in', href: '/login' },
]}
/>
);
const footer = (
<Footer
logo={<FALogoInverse />}
tagline="Helping Australian families plan with confidence"
linkGroups={[
{
heading: 'Services',
links: [
{ label: 'Find a Director', href: '/directors' },
{ label: 'Compare Venues', href: '/venues' },
{ label: 'Pricing Guide', href: '/pricing' },
{ label: 'Start Planning', href: '/arrange' },
],
},
{
heading: 'Support',
links: [
{ label: 'FAQ', href: '/faq' },
{ label: 'Contact Us', href: '/contact' },
{ label: 'Grief Resources', href: '/resources' },
],
},
{
heading: 'Company',
links: [
{ label: 'About Us', href: '/about' },
{ label: 'Provider Portal', href: '/provider-portal' },
{ label: 'Partner With Us', href: '/partners' },
],
},
]}
phone="1800 987 888"
email="support@funeralarranger.com.au"
legalLinks={[
{ label: 'Privacy Policy', href: '/privacy' },
{ label: 'Terms of Service', href: '/terms' },
{ label: 'Accessibility', href: '/accessibility' },
]}
/>
);
// ─── Data ───────────────────────────────────────────────────────────────────
const features = [
{
icon: <VerifiedOutlinedIcon />,
heading: 'Transparent, verified pricing',
description:
'All costs are itemised for verified partners. No surprise fees. See pricing and options before you commit.',
},
{
icon: <AccessTimeIcon />,
heading: 'Available 24 hours a day',
description:
'Compare, plan and arrange at your own pace, day or night. No pressure to commit online.',
},
{
icon: <SearchIcon />,
heading: 'Find local & compare',
description: 'Search and compare local funeral directors to find the right choice for you.',
},
{
icon: <SupportAgentOutlinedIcon />,
heading: 'Support when you need it',
description:
'Arrange everything online or be guided through the steps by your preferred funeral director.',
},
];
const testimonials = [
{
name: 'Sarah H.',
rating: 5,
quote:
'At the most difficult time in our lives, this site made comparing costs so straightforward. We saved over $800.',
timeAgo: '3 weeks ago',
},
{
name: 'James M.',
rating: 5,
quote:
'The itemised quote builder meant we could personalise the service within our budget. Highly recommended.',
timeAgo: '1 month ago',
},
{
name: 'Tracy W.',
rating: 5,
quote:
'I had no idea there was such a price difference between local directors. This saved us from overpaying.',
timeAgo: '2 months ago',
},
];
const faqItems = [
{
question: 'What is Funeral Arranger?',
answer:
'Funeral Arranger is an online platform that helps Australian families find, compare and arrange funeral services. We connect you with trusted local funeral directors and provide transparent pricing so you can make informed decisions during a difficult time.',
},
{
question: 'What makes Funeral Arranger different from other funeral service providers?',
answer:
'Unlike traditional funeral homes, we are an independent comparison platform. We show you transparent, itemised pricing from multiple verified providers in your area so you can compare options and choose what is right for your family and budget.',
},
{
question: 'Do I need to complete all steps at once?',
answer:
'No. You can save your progress at any time and return when you are ready. Whether you are pre-planning or arranging at short notice, the process works at your pace with no time pressure.',
},
{
question: 'How much does a funeral cost in Australia?',
answer:
'Funeral costs in Australia typically range from $4,000 for a simple cremation to $15,000 or more for a full traditional service. Costs vary by location, provider, and the options you choose. Our platform helps you compare real prices from local providers.',
},
{
question: 'What is the cheapest funeral option?',
answer:
'A direct cremation (no service) is generally the most affordable option, starting from around $2,000\u2013$4,000 depending on your location. Our platform shows you all available options so you can find the right balance of service and cost.',
},
];
const trustStats: TrustStat[] = [
{ value: '1,500+', label: 'Families helped' },
{ value: '4.9', label: 'Google Rating' },
{ value: '300+', label: 'Funeral directors' },
];
const featuredProviders: FeaturedProvider[] = [
{
id: 'parsons',
name: 'H.Parsons Funeral Directors',
location: 'Wentworth, NSW',
verified: true,
imageUrl: 'https://placehold.co/600x200/E8E0D6/8B6F47?text=H.Parsons',
logoUrl: 'https://placehold.co/64x64/FEF9F5/BA834E?text=HP',
rating: 4.6,
reviewCount: 7,
startingPrice: 900,
},
{
id: 'rankins',
name: 'Rankins Funeral Services',
location: 'Wollongong, NSW',
verified: true,
imageUrl: 'https://placehold.co/600x200/D7E1E2/4C5B6B?text=Rankins',
logoUrl: 'https://placehold.co/64x64/F2F5F6/4C5B6B?text=R',
rating: 4.8,
reviewCount: 23,
startingPrice: 1200,
},
{
id: 'easy-funerals',
name: 'Easy Funerals',
location: 'Sydney, NSW',
verified: true,
imageUrl: 'https://placehold.co/600x200/F0F7F0/3B7A3B?text=Easy+Funerals',
logoUrl: 'https://placehold.co/64x64/F0F7F0/3B7A3B?text=EF',
rating: 4.5,
reviewCount: 42,
startingPrice: 850,
},
];
// ─── Meta ────────────────────────────────────────────────────────────────────
const meta: Meta<typeof HomePage> = {
title: 'Archive/HomePage V2',
component: HomePage,
parameters: {
layout: 'fullscreen',
},
};
export default meta;
type Story = StoryObj<typeof HomePage>;
// ─── Stories ────────────────────────────────────────────────────────────────
/** V2 layout — full-bleed hero, stats bar, map + provider cards, editorial testimonials */
export const Default: Story = {
args: {
navigation: nav,
footer,
heroImageUrl: '/brandassets/images/heroes/parsonshero.png',
stats: trustStats,
featuredProviders,
onSelectFeaturedProvider: (id) => console.log('Featured provider:', id),
features,
googleRating: 4.9,
googleReviewCount: 2340,
testimonials,
faqItems,
onSearch: (params) => console.log('Search:', params),
onCtaClick: () => console.log('CTA clicked'),
},
};

View File

@@ -0,0 +1,281 @@
import type { Meta, StoryObj } from '@storybook/react';
import Box from '@mui/material/Box';
import VerifiedOutlinedIcon from '@mui/icons-material/VerifiedOutlined';
import AccessTimeIcon from '@mui/icons-material/AccessTime';
import SearchIcon from '@mui/icons-material/Search';
import SupportAgentOutlinedIcon from '@mui/icons-material/SupportAgentOutlined';
import { HomePage } from './HomePage';
import type { FeaturedProvider, TrustStat, PartnerLogo } from './HomePage';
import React from 'react';
import { Navigation } from '../../organisms/Navigation';
import { Footer } from '../../organisms/Footer';
// ─── Shared helpers ──────────────────────────────────────────────────────────
const FALogo = () => (
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Box
component="img"
src="/brandlogo/logo-full.svg"
alt="Funeral Arranger"
sx={{ height: 28, display: { xs: 'none', md: 'block' } }}
/>
<Box
component="img"
src="/brandlogo/logo-short.svg"
alt="Funeral Arranger"
sx={{ height: 28, display: { xs: 'block', md: 'none' } }}
/>
</Box>
);
const FALogoFooter = () => (
<Box component="img" src="/brandlogo/logo-full.svg" alt="Funeral Arranger" sx={{ height: 24 }} />
);
const nav = (
<Navigation
logo={<FALogo />}
items={[
{ label: 'FAQ', href: '/faq' },
{ label: 'Contact Us', href: '/contact' },
{ label: 'Log in', href: '/login' },
]}
/>
);
const footer = (
<Footer
logo={<FALogoFooter />}
tagline="Helping Australian families plan with confidence"
linkGroups={[
{
heading: 'Services',
links: [
{ label: 'Find a Director', href: '/directors' },
{ label: 'Compare Venues', href: '/venues' },
{ label: 'Pricing Guide', href: '/pricing' },
{ label: 'Start Planning', href: '/arrange' },
],
},
{
heading: 'Support',
links: [
{ label: 'FAQ', href: '/faq' },
{ label: 'Contact Us', href: '/contact' },
{ label: 'Grief Resources', href: '/resources' },
],
},
{
heading: 'Company',
links: [
{ label: 'About Us', href: '/about' },
{ label: 'Provider Portal', href: '/provider-portal' },
{ label: 'Partner With Us', href: '/partners' },
],
},
]}
phone="1800 987 888"
email="support@funeralarranger.com.au"
legalLinks={[
{ label: 'Privacy Policy', href: '/privacy' },
{ label: 'Terms of Service', href: '/terms' },
{ label: 'Accessibility', href: '/accessibility' },
]}
/>
);
// ─── Data ───────────────────────────────────────────────────────────────────
const features = [
{
icon: <VerifiedOutlinedIcon />,
heading: 'Transparent, verified pricing',
description:
'All costs are itemised for verified partners. No surprise fees. See pricing and options before you commit.',
},
{
icon: <AccessTimeIcon />,
heading: 'Available 24 hours a day',
description:
'Compare, plan and arrange at your own pace, day or night. No pressure to commit online.',
},
{
icon: <SearchIcon />,
heading: 'Find local & compare',
description: 'Search and compare local funeral directors to find the right choice for you.',
},
{
icon: <SupportAgentOutlinedIcon />,
heading: 'Support when you need it',
description:
'Arrange everything online or be guided through the steps by your preferred funeral director.',
},
];
const testimonials = [
{
name: 'Sarah H.',
rating: 5,
quote:
'At the most difficult time in our lives, this site made comparing costs so straightforward. We saved over $800.',
timeAgo: '3 weeks ago',
},
{
name: 'James M.',
rating: 5,
quote:
'The itemised quote builder meant we could personalise the service within our budget. Highly recommended.',
timeAgo: '1 month ago',
},
{
name: 'Tracy W.',
rating: 5,
quote:
'I had no idea there was such a price difference between local directors. This saved us from overpaying.',
timeAgo: '2 months ago',
},
];
const faqItems = [
{
question: 'What is Funeral Arranger?',
answer:
'Funeral Arranger is an online platform that helps Australian families find, compare and arrange funeral services. We connect you with trusted local funeral directors and provide transparent pricing so you can make informed decisions during a difficult time.',
},
{
question: 'What makes Funeral Arranger different from other funeral service providers?',
answer:
'Unlike traditional funeral homes, we are an independent comparison platform. We show you transparent, itemised pricing from multiple verified providers in your area so you can compare options and choose what is right for your family and budget.',
},
{
question: 'Do I need to complete all steps at once?',
answer:
'No. You can save your progress at any time and return when you are ready. Whether you are pre-planning or arranging at short notice, the process works at your pace with no time pressure.',
},
{
question: 'How much does a funeral cost in Australia?',
answer:
'Funeral costs in Australia typically range from $4,000 for a simple cremation to $15,000 or more for a full traditional service. Costs vary by location, provider, and the options you choose. Our platform helps you compare real prices from local providers.',
},
{
question: 'What is the cheapest funeral option?',
answer:
'A direct cremation (no service) is generally the most affordable option, starting from around $2,000\u2013$4,000 depending on your location. Our platform shows you all available options so you can find the right balance of service and cost.',
},
];
const trustStats: TrustStat[] = [
{ value: '1,500+', label: 'Families helped' },
{ value: '4.9', label: 'Google Rating' },
{ value: '300+', label: 'Funeral directors' },
];
const featuredProviders: FeaturedProvider[] = [
{
id: 'parsons',
name: 'H.Parsons Funeral Directors',
location: 'Wollongong, NSW',
verified: true,
imageUrl: '/brandassets/images/venues/hparsons-funeral-home-kiama/01.jpg',
logoUrl: '/brandassets/images/providers/hparsons-funeral-directors/logo.png',
rating: 4.6,
reviewCount: 7,
startingPrice: 900,
},
{
id: 'rankins',
name: 'Rankins Funerals',
location: 'Wollongong, NSW',
verified: true,
imageUrl: '/brandassets/images/venues/rankins-funeral-home-warrawong/01.jpg',
logoUrl: '/brandassets/images/providers/rankins-funerals/logo.png',
rating: 4.8,
reviewCount: 23,
startingPrice: 1200,
},
{
id: 'easy-funerals',
name: 'Easy Funerals',
location: 'Sydney, NSW',
verified: true,
imageUrl: '/brandassets/images/venues/lakeside-memorial-park-chapel/01.jpg',
logoUrl: '/brandassets/images/providers/easy-funerals/logo.png',
rating: 4.5,
reviewCount: 42,
startingPrice: 850,
},
];
const partnerLogos: PartnerLogo[] = [
{
src: '/brandassets/images/providers/hparsons-funeral-directors/logo.png',
alt: 'H.Parsons Funeral Directors',
},
{ src: '/brandassets/images/providers/rankins-funerals/logo.png', alt: 'Rankins Funerals' },
{ src: '/brandassets/images/providers/easy-funerals/logo.png', alt: 'Easy Funerals' },
{ src: '/brandassets/images/providers/lady-anne-funerals/logo.png', alt: 'Lady Anne Funerals' },
{
src: '/brandassets/images/providers/killick-family-funerals/logo.png',
alt: 'Killick Family Funerals',
},
{
src: '/brandassets/images/providers/kenneallys-funerals/logo.png',
alt: "Kenneally's Funerals",
},
{
src: '/brandassets/images/providers/wollongong-city-funerals/logo.png',
alt: 'Wollongong City Funerals',
},
{
src: '/brandassets/images/providers/hparsons-funeral-directors-shoalhaven/logo.png',
alt: 'H.Parsons Shoalhaven',
},
{
src: '/brandassets/images/providers/mackay-family-funerals/logo.webp',
alt: 'Mackay Family Funerals',
},
];
// ─── Meta ────────────────────────────────────────────────────────────────────
const meta: Meta<typeof HomePage> = {
title: 'Archive/HomePage V3',
component: HomePage,
parameters: {
layout: 'fullscreen',
},
};
export default meta;
type Story = StoryObj<typeof HomePage>;
// ─── Stories ────────────────────────────────────────────────────────────────
/** V3 layout — hero-3 background, updated copy, bullet-point subheading */
export const Default: Story = {
args: {
navigation: nav,
footer,
heroImageUrl: '/brandassets/images/heroes/hero-3.png',
heroHeading: 'Compare funeral directors pricing near you and arrange with confidence',
heroSubheading: 'Transparent pricing \u00B7 No hidden fees \u00B7 Arrange 24/7',
stats: trustStats,
featuredProviders,
discoverMapSlot: React.createElement('img', {
src: '/brandassets/images/placeholder/map.png',
alt: 'Map showing provider locations',
style: { width: '100%', height: '100%', objectFit: 'cover' },
}),
onSelectFeaturedProvider: (id) => console.log('Featured provider:', id),
partnerLogos,
partnerTrustLine: 'Trusted by hundreds of verified funeral directors across Australia',
features,
googleRating: 4.9,
googleReviewCount: 2340,
testimonials,
faqItems,
onSearch: (params) => console.log('Search:', params),
onCtaClick: () => console.log('CTA clicked'),
},
};

View File

@@ -0,0 +1,285 @@
import type { Meta, StoryObj } from '@storybook/react';
import Box from '@mui/material/Box';
import VerifiedOutlinedIcon from '@mui/icons-material/VerifiedOutlined';
import AccessTimeIcon from '@mui/icons-material/AccessTime';
import SearchIcon from '@mui/icons-material/Search';
import SupportAgentOutlinedIcon from '@mui/icons-material/SupportAgentOutlined';
import { HomePage } from './HomePage';
import type { FeaturedProvider, TrustStat, PartnerLogo } from './HomePage';
import { FuneralFinderV4 } from '../../organisms/FuneralFinder/FuneralFinderV4';
import React from 'react';
import { Navigation } from '../../organisms/Navigation';
import { Footer } from '../../organisms/Footer';
// ─── Shared helpers ──────────────────────────────────────────────────────────
const FALogo = () => (
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Box
component="img"
src="/brandlogo/logo-full.svg"
alt="Funeral Arranger"
sx={{ height: 28, display: { xs: 'none', md: 'block' } }}
/>
<Box
component="img"
src="/brandlogo/logo-short.svg"
alt="Funeral Arranger"
sx={{ height: 28, display: { xs: 'block', md: 'none' } }}
/>
</Box>
);
const FALogoFooter = () => (
<Box component="img" src="/brandlogo/logo-full.svg" alt="Funeral Arranger" sx={{ height: 24 }} />
);
const nav = (
<Navigation
logo={<FALogo />}
items={[
{ label: 'FAQ', href: '/faq' },
{ label: 'Contact Us', href: '/contact' },
{ label: 'Log in', href: '/login' },
]}
/>
);
const footer = (
<Footer
logo={<FALogoFooter />}
tagline="Helping Australian families plan with confidence"
linkGroups={[
{
heading: 'Services',
links: [
{ label: 'Find a Director', href: '/directors' },
{ label: 'Compare Venues', href: '/venues' },
{ label: 'Pricing Guide', href: '/pricing' },
{ label: 'Start Planning', href: '/arrange' },
],
},
{
heading: 'Support',
links: [
{ label: 'FAQ', href: '/faq' },
{ label: 'Contact Us', href: '/contact' },
{ label: 'Grief Resources', href: '/resources' },
],
},
{
heading: 'Company',
links: [
{ label: 'About Us', href: '/about' },
{ label: 'Provider Portal', href: '/provider-portal' },
{ label: 'Partner With Us', href: '/partners' },
],
},
]}
phone="1800 987 888"
email="support@funeralarranger.com.au"
legalLinks={[
{ label: 'Privacy Policy', href: '/privacy' },
{ label: 'Terms of Service', href: '/terms' },
{ label: 'Accessibility', href: '/accessibility' },
]}
/>
);
// ─── Data ───────────────────────────────────────────────────────────────────
const features = [
{
icon: <VerifiedOutlinedIcon />,
heading: 'Transparent, verified pricing',
description:
'All costs are itemised for verified partners. No surprise fees. See pricing and options before you commit.',
},
{
icon: <AccessTimeIcon />,
heading: 'Available 24 hours a day',
description:
'Compare, plan and arrange at your own pace, day or night. No pressure to commit online.',
},
{
icon: <SearchIcon />,
heading: 'Find local & compare',
description: 'Search and compare local funeral directors to find the right choice for you.',
},
{
icon: <SupportAgentOutlinedIcon />,
heading: 'Support when you need it',
description:
'Arrange everything online or be guided through the steps by your preferred funeral director.',
},
];
const testimonials = [
{
name: 'Sarah H.',
rating: 5,
quote:
'At the most difficult time in our lives, this site made comparing costs so straightforward. We saved over $800.',
timeAgo: '3 weeks ago',
},
{
name: 'James M.',
rating: 5,
quote:
'The itemised quote builder meant we could personalise the service within our budget. Highly recommended.',
timeAgo: '1 month ago',
},
{
name: 'Tracy W.',
rating: 5,
quote:
'I had no idea there was such a price difference between local directors. This saved us from overpaying.',
timeAgo: '2 months ago',
},
];
const faqItems = [
{
question: 'What is Funeral Arranger?',
answer:
'Funeral Arranger is an online platform that helps Australian families find, compare and arrange funeral services. We connect you with trusted local funeral directors and provide transparent pricing so you can make informed decisions during a difficult time.',
},
{
question: 'What makes Funeral Arranger different from other funeral service providers?',
answer:
'Unlike traditional funeral homes, we are an independent comparison platform. We show you transparent, itemised pricing from multiple verified providers in your area so you can compare options and choose what is right for your family and budget.',
},
{
question: 'Do I need to complete all steps at once?',
answer:
'No. You can save your progress at any time and return when you are ready. Whether you are pre-planning or arranging at short notice, the process works at your pace with no time pressure.',
},
{
question: 'How much does a funeral cost in Australia?',
answer:
'Funeral costs in Australia typically range from $4,000 for a simple cremation to $15,000 or more for a full traditional service. Costs vary by location, provider, and the options you choose. Our platform helps you compare real prices from local providers.',
},
{
question: 'What is the cheapest funeral option?',
answer:
'A direct cremation (no service) is generally the most affordable option, starting from around $2,000\u2013$4,000 depending on your location. Our platform shows you all available options so you can find the right balance of service and cost.',
},
];
const trustStats: TrustStat[] = [
{ value: '1,500+', label: 'Families helped' },
{ value: '4.9', label: 'Google Rating' },
{ value: '300+', label: 'Funeral directors' },
];
const featuredProviders: FeaturedProvider[] = [
{
id: 'parsons',
name: 'H.Parsons Funeral Directors',
location: 'Wollongong, NSW',
verified: true,
imageUrl: '/brandassets/images/venues/hparsons-funeral-home-kiama/01.jpg',
logoUrl: '/brandassets/images/providers/hparsons-funeral-directors/logo.png',
rating: 4.6,
reviewCount: 7,
startingPrice: 900,
},
{
id: 'rankins',
name: 'Rankins Funerals',
location: 'Wollongong, NSW',
verified: true,
imageUrl: '/brandassets/images/venues/rankins-funeral-home-warrawong/01.jpg',
logoUrl: '/brandassets/images/providers/rankins-funerals/logo.png',
rating: 4.8,
reviewCount: 23,
startingPrice: 1200,
},
{
id: 'easy-funerals',
name: 'Easy Funerals',
location: 'Sydney, NSW',
verified: true,
imageUrl: '/brandassets/images/venues/lakeside-memorial-park-chapel/01.jpg',
logoUrl: '/brandassets/images/providers/easy-funerals/logo.png',
rating: 4.5,
reviewCount: 42,
startingPrice: 850,
},
];
const partnerLogos: PartnerLogo[] = [
{
src: '/brandassets/images/providers/hparsons-funeral-directors/logo.png',
alt: 'H.Parsons Funeral Directors',
},
{ src: '/brandassets/images/providers/rankins-funerals/logo.png', alt: 'Rankins Funerals' },
{ src: '/brandassets/images/providers/easy-funerals/logo.png', alt: 'Easy Funerals' },
{ src: '/brandassets/images/providers/lady-anne-funerals/logo.png', alt: 'Lady Anne Funerals' },
{
src: '/brandassets/images/providers/killick-family-funerals/logo.png',
alt: 'Killick Family Funerals',
},
{
src: '/brandassets/images/providers/kenneallys-funerals/logo.png',
alt: "Kenneally's Funerals",
},
{
src: '/brandassets/images/providers/wollongong-city-funerals/logo.png',
alt: 'Wollongong City Funerals',
},
{
src: '/brandassets/images/providers/hparsons-funeral-directors-shoalhaven/logo.png',
alt: 'H.Parsons Shoalhaven',
},
{
src: '/brandassets/images/providers/mackay-family-funerals/logo.webp',
alt: 'Mackay Family Funerals',
},
];
// ─── Meta ────────────────────────────────────────────────────────────────────
const meta: Meta<typeof HomePage> = {
title: 'Archive/HomePage V4',
component: HomePage,
parameters: {
layout: 'fullscreen',
},
};
export default meta;
type Story = StoryObj<typeof HomePage>;
// ─── Stories ────────────────────────────────────────────────────────────────
/** V4 layout — uses FuneralFinder V4 (stepped form with numbered indicators) */
export const Default: Story = {
args: {
navigation: nav,
footer,
heroImageUrl: '/brandassets/images/heroes/hero-3.png',
heroHeading: 'Compare funeral directors pricing near you and arrange with confidence',
heroSubheading: 'Transparent pricing \u00B7 No hidden fees \u00B7 Arrange 24/7',
finderSlot: React.createElement(FuneralFinderV4, {
onSearch: (params) => console.log('Search:', params),
}),
stats: trustStats,
featuredProviders,
discoverMapSlot: React.createElement('img', {
src: '/brandassets/images/placeholder/map.png',
alt: 'Map showing provider locations',
style: { width: '100%', height: '100%', objectFit: 'cover' },
}),
onSelectFeaturedProvider: (id) => console.log('Featured provider:', id),
partnerLogos,
partnerTrustLine: 'Trusted by hundreds of verified funeral directors across Australia',
features,
googleRating: 4.9,
googleReviewCount: 2340,
testimonials,
faqItems,
onSearch: (params) => console.log('Search:', params),
onCtaClick: () => console.log('CTA clicked'),
},
};