diff --git a/brandassets/images/heroes/hero-3.png b/brandassets/images/heroes/hero-3.png new file mode 100644 index 0000000..fa86ac6 Binary files /dev/null and b/brandassets/images/heroes/hero-3.png differ diff --git a/docs/memory/component-registry.md b/docs/memory/component-registry.md index aff570b..1eee4ad 100644 --- a/docs/memory/component-registry.md +++ b/docs/memory/component-registry.md @@ -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 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 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. | | 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 @@ -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. | | 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. | -| 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 diff --git a/docs/memory/session-log.md b/docs/memory/session-log.md index 43cbc24..d39127e 100644 --- a/docs/memory/session-log.md +++ b/docs/memory/session-log.md @@ -26,6 +26,65 @@ Each entry follows this structure: ## 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 **Agent(s):** Claude Opus 4.6 (1M context) diff --git a/src/components/organisms/Footer/Footer.tsx b/src/components/organisms/Footer/Footer.tsx index 8bfca11..09b14e5 100644 --- a/src/components/organisms/Footer/Footer.tsx +++ b/src/components/organisms/Footer/Footer.tsx @@ -69,7 +69,7 @@ export const Footer = React.forwardRef( const copyrightText = copyright || `\u00A9 ${year} Funeral Arranger. All rights reserved.`; const overlineSx = { - color: 'var(--fa-color-brand-400)', + color: 'text.secondary', textTransform: 'uppercase' as const, letterSpacing: '0.08em', display: 'block', @@ -77,8 +77,8 @@ export const Footer = React.forwardRef( }; const contactLinkSx = { - color: 'var(--fa-color-white)', - '&:hover': { color: 'var(--fa-color-brand-300)' }, + color: 'text.primary', + '&:hover': { color: 'var(--fa-color-brand-600)' }, }; return ( @@ -87,8 +87,8 @@ export const Footer = React.forwardRef( component="footer" sx={[ { - bgcolor: 'var(--fa-color-brand-950)', - color: 'var(--fa-color-white)', + bgcolor: 'var(--fa-color-surface-subtle)', + color: 'text.primary', pt: { xs: 5, md: 8 }, pb: 0, }, @@ -104,7 +104,7 @@ export const Footer = React.forwardRef( {tagline && ( {tagline} @@ -168,7 +168,7 @@ export const Footer = React.forwardRef( ( href={link.href} onClick={link.onClick} sx={{ - color: 'var(--fa-color-brand-200)', + color: 'text.primary', fontSize: '0.875rem', fontWeight: 500, display: 'inline-flex', alignItems: 'center', minHeight: 44, - '&:hover': { color: 'var(--fa-color-white)' }, + '&:hover': { color: 'var(--fa-color-brand-600)' }, }} > {link.label} @@ -211,7 +211,7 @@ export const Footer = React.forwardRef( {/* Bottom bar */} - + ( py: 3, }} > - + {copyrightText} @@ -242,13 +242,13 @@ export const Footer = React.forwardRef( href={link.href} onClick={link.onClick} sx={{ - color: 'var(--fa-color-brand-400)', + color: 'text.secondary', fontSize: '0.75rem', fontWeight: 500, display: 'inline-flex', alignItems: 'center', minHeight: 44, - '&:hover': { color: 'var(--fa-color-white)' }, + '&:hover': { color: 'var(--fa-color-brand-600)' }, }} > {link.label} diff --git a/src/components/organisms/FuneralFinder/FuneralFinderV3.tsx b/src/components/organisms/FuneralFinder/FuneralFinderV3.tsx index c7dde09..c42ea8a 100644 --- a/src/components/organisms/FuneralFinder/FuneralFinderV3.tsx +++ b/src/components/organisms/FuneralFinder/FuneralFinderV3.tsx @@ -244,7 +244,7 @@ export const FuneralFinderV3 = React.forwardRef {heading} - - {subheading} - + {subheading && ( + + {subheading} + + )} @@ -527,7 +529,7 @@ export const FuneralFinderV3 = React.forwardRef - Find Funeral Directors + Search Local Providers = { + title: 'Archive/FuneralFinder V4', + component: FuneralFinderV4, + parameters: { + layout: 'padded', + }, + args: { + onSearch: (params) => { + console.log('Search params:', params); + }, + }, +}; + +export default meta; +type Story = StoryObj; + +/** 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) => ( + + + Funeral Arranger + Find trusted funeral directors near you + + + + + + ), + ], +}; + +/** Constrained width — typical sidebar or narrow column */ +export const Narrow: Story = { + decorators: [ + (Story) => ( + + + + ), + ], +}; diff --git a/src/components/organisms/FuneralFinder/FuneralFinderV4.tsx b/src/components/organisms/FuneralFinder/FuneralFinderV4.tsx new file mode 100644 index 0000000..2770cda --- /dev/null +++ b/src/components/organisms/FuneralFinder/FuneralFinderV4.tsx @@ -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; +} + +// ─── 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; diff --git a/src/components/pages/HomePage/HomePage.stories.tsx b/src/components/pages/HomePage/HomePage.stories.tsx index fec511f..53a1d2a 100644 --- a/src/components/pages/HomePage/HomePage.stories.tsx +++ b/src/components/pages/HomePage/HomePage.stories.tsx @@ -5,7 +5,6 @@ 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'; @@ -210,9 +209,8 @@ const faqItems = [ // ─── Meta ──────────────────────────────────────────────────────────────────── const meta: Meta = { - title: 'Pages/HomePage', + title: 'Archive/HomePage V1', component: HomePage, - tags: ['autodocs'], parameters: { layout: 'fullscreen', }, @@ -265,68 +263,3 @@ export const Mobile: Story = { 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'), - }, -}; diff --git a/src/components/pages/HomePage/HomePage.tsx b/src/components/pages/HomePage/HomePage.tsx index 4446463..8195f15 100644 --- a/src/components/pages/HomePage/HomePage.tsx +++ b/src/components/pages/HomePage/HomePage.tsx @@ -80,9 +80,11 @@ export interface HomePageProps { /** Hero background image URL (full-bleed layout — V2). Takes priority over heroImage. */ 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; - /** FuneralFinder loading state */ + /** FuneralFinder loading state (used when finderSlot is not provided) */ searchLoading?: boolean; /** Trust stats bar (e.g. "1,500+ families helped") */ @@ -174,6 +176,7 @@ export const HomePage = React.forwardRef( 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, heroImageUrl, + finderSlot, onSearch, searchLoading, partnerLogos = [], @@ -242,7 +245,7 @@ export const HomePage = React.forwardRef( position: 'relative', zIndex: 1, textAlign: 'center', - pt: { xs: 6, md: 8 }, + pt: { xs: 8, md: 11 }, pb: 4, }} > @@ -251,7 +254,7 @@ export const HomePage = React.forwardRef( component="h1" id="hero-heading" tabIndex={-1} - sx={{ mb: 2, color: 'var(--fa-color-white)' }} + sx={{ mb: 3, color: 'var(--fa-color-white)' }} > {heroHeading} @@ -269,17 +272,19 @@ export const HomePage = React.forwardRef( position: 'relative', zIndex: 2, px: 2, + pt: 2, pb: 0, mb: { xs: -14, md: -18 }, }} > - - + + {finderSlot || ( + + )} @@ -362,12 +367,13 @@ export const HomePage = React.forwardRef( }} > - + {finderSlot || ( + + )} )} @@ -381,17 +387,17 @@ export const HomePage = React.forwardRef( aria-labelledby="discover-heading" sx={{ bgcolor: 'var(--fa-color-surface-subtle)', - pt: { xs: 20, md: 24 }, - pb: { xs: 6, md: 10 }, + pt: { xs: 22, md: 28 }, + pb: { xs: 8, md: 12 }, }} > - + See what you'll discover @@ -413,24 +419,38 @@ export const HomePage = React.forwardRef( alignItems: 'stretch', }} > - {/* Map placeholder — stretches to match card stack */} + {/* Map — fills the grid cell to match card stack height */} img, & > div': { + position: { md: 'absolute' }, + inset: { md: 0 }, + width: '100%', + height: '100%', + objectFit: 'cover', + }, }} > {discoverMapSlot || ( - - Map coming soon - + + + Map coming soon + + )} @@ -473,15 +493,15 @@ export const HomePage = React.forwardRef( aria-label="Trusted partners" sx={{ bgcolor: 'var(--fa-color-surface-cool)', - pt: { xs: 8, md: 10 }, - pb: { xs: 6, md: 8 }, + pt: { xs: 10, md: 13 }, + pb: { xs: 8, md: 10 }, }} > {partnerTrustLine} @@ -518,7 +538,7 @@ export const HomePage = React.forwardRef( aria-label="Partner funeral directors" sx={{ display: 'flex', - gap: { xs: 6, md: 8 }, + gap: { xs: 8, md: 12 }, alignItems: 'center', width: 'max-content', animation: 'logoScroll 35s linear infinite', @@ -538,13 +558,12 @@ export const HomePage = React.forwardRef( alt={i < partnerLogos.length ? logo.alt : ''} aria-hidden={i >= partnerLogos.length ? true : undefined} sx={{ - height: { xs: 48, md: 64 }, + height: { xs: 46, md: 55 }, + maxWidth: { xs: 140, md: 184 }, width: 'auto', objectFit: 'contain', - filter: 'grayscale(100%)', - opacity: 0.5, - transition: 'opacity 0.2s, filter 0.2s', - '&:hover': { filter: 'grayscale(0%)', opacity: 1 }, + filter: 'grayscale(100%) brightness(1.2)', + opacity: 0.4, flexShrink: 0, }} /> @@ -563,16 +582,16 @@ export const HomePage = React.forwardRef( aria-labelledby="features-heading" sx={{ bgcolor: 'var(--fa-color-surface-default)', - py: { xs: 6, md: 10 }, + py: { xs: 8, md: 12 }, }} > - + {featuresHeading} @@ -628,7 +647,7 @@ export const HomePage = React.forwardRef( component="section" aria-labelledby="reviews-heading" sx={{ - py: { xs: 6, md: 10 }, + py: { xs: 8, md: 12 }, bgcolor: 'var(--fa-color-surface-subtle)', }} > @@ -728,20 +747,21 @@ export const HomePage = React.forwardRef( component="section" aria-labelledby="cta-heading" 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 }, }} > - - + {ctaHeading} - @@ -756,7 +776,7 @@ export const HomePage = React.forwardRef( aria-labelledby="faq-heading" sx={{ bgcolor: 'var(--fa-color-surface-default)', - py: { xs: 6, md: 10 }, + py: { xs: 8, md: 12 }, }} > @@ -764,7 +784,7 @@ export const HomePage = React.forwardRef( variant="h2" component="h2" 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 diff --git a/src/components/pages/HomePage/HomePageV2.stories.tsx b/src/components/pages/HomePage/HomePageV2.stories.tsx new file mode 100644 index 0000000..cef686c --- /dev/null +++ b/src/components/pages/HomePage/HomePageV2.stories.tsx @@ -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 = () => ( + + + + +); + +const FALogoInverse = () => ( + +); + +const nav = ( + } + items={[ + { label: 'FAQ', href: '/faq' }, + { label: 'Contact Us', href: '/contact' }, + { label: 'Log in', href: '/login' }, + ]} + /> +); + +const footer = ( +