From 44f159a45324b02f14702d13854c4b13efc96c26 Mon Sep 17 00:00:00 2001 From: Richie Date: Tue, 31 Mar 2026 20:08:53 +1100 Subject: [PATCH] HomePage V2: full-bleed hero, stats bar, discover section, editorial testimonials New layout inspired by reference designs: - Full-bleed hero with parsonshero.png + gradient overlay (heroImageUrl prop) - Trust stats bar: 1,500+ families, 4.9 rating, 300+ directors - "See what you'll discover" section: map placeholder + featured ProviderCards - Editorial testimonials: alternating left/right with quote marks (no cards) - Minimal FAQ: borderless accordion with divider lines - V1 split hero preserved when heroImage prop used instead New types: TrustStat, FeaturedProvider. New props: heroImageUrl, stats, featuredProviders, discoverMapSlot, onSelectFeaturedProvider. V1 stories unchanged, new V2 story added. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../pages/HomePage/HomePage.stories.tsx | 66 +++ src/components/pages/HomePage/HomePage.tsx | 510 ++++++++++++------ 2 files changed, 416 insertions(+), 160 deletions(-) diff --git a/src/components/pages/HomePage/HomePage.stories.tsx b/src/components/pages/HomePage/HomePage.stories.tsx index 54947ab..fec511f 100644 --- a/src/components/pages/HomePage/HomePage.stories.tsx +++ b/src/components/pages/HomePage/HomePage.stories.tsx @@ -5,6 +5,7 @@ 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'; @@ -264,3 +265,68 @@ 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 fd004fe..c122956 100644 --- a/src/components/pages/HomePage/HomePage.tsx +++ b/src/components/pages/HomePage/HomePage.tsx @@ -5,6 +5,7 @@ import Accordion from '@mui/material/Accordion'; import AccordionSummary from '@mui/material/AccordionSummary'; import AccordionDetails from '@mui/material/AccordionDetails'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import FormatQuoteIcon from '@mui/icons-material/FormatQuote'; import StarIcon from '@mui/icons-material/Star'; import StarHalfIcon from '@mui/icons-material/StarHalf'; import StarBorderIcon from '@mui/icons-material/StarBorder'; @@ -12,6 +13,7 @@ import type { SxProps, Theme } from '@mui/material/styles'; import { Typography } from '../../atoms/Typography'; import { Button } from '../../atoms/Button'; import { Card } from '../../atoms/Card'; +import { ProviderCard } from '../../molecules/ProviderCard'; import { FuneralFinderV3, type FuneralFinderV3SearchParams } from '../../organisms/FuneralFinder'; // ─── Types ─────────────────────────────────────────────────────────────────── @@ -43,6 +45,25 @@ export interface FAQItem { answer: React.ReactNode; } +/** A trust stat for the stats bar */ +export interface TrustStat { + value: string; + label: string; +} + +/** A featured provider for the discover section */ +export interface FeaturedProvider { + id: string; + name: string; + location: string; + verified?: boolean; + imageUrl?: string; + logoUrl?: string; + rating?: number; + reviewCount?: number; + startingPrice?: number; +} + /** Props for the HomePage component */ export interface HomePageProps { /** Navigation bar */ @@ -54,19 +75,31 @@ export interface HomePageProps { heroHeading?: string; /** Hero subheading paragraph */ heroSubheading?: string; - /** Hero image slot */ + /** Hero image slot (split layout — V1) */ heroImage?: React.ReactNode; + /** Hero background image URL (full-bleed layout — V2). Takes priority over heroImage. */ + heroImageUrl?: string; /** FuneralFinder search callback */ onSearch?: (params: FuneralFinderV3SearchParams) => void; /** FuneralFinder loading state */ searchLoading?: boolean; + /** Trust stats bar (e.g. "1,500+ families helped") */ + stats?: TrustStat[]; + /** Partner logos for the trust carousel */ partnerLogos?: PartnerLogo[]; /** Trust line text above the carousel */ partnerTrustLine?: string; + /** Featured providers for the "Discover" section */ + featuredProviders?: FeaturedProvider[]; + /** Map slot for the discover section */ + discoverMapSlot?: React.ReactNode; + /** Callback when a featured provider card is clicked */ + onSelectFeaturedProvider?: (id: string) => void; + /** Feature cards for "Why Use FA" */ features?: FeatureCard[]; @@ -136,10 +169,15 @@ export const HomePage = React.forwardRef( heroHeading = 'Compare Funeral Directors Near You and Arrange with Confidence', 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, onSearch, searchLoading, + stats = [], partnerLogos = [], partnerTrustLine = 'Providing services from hundreds of trusted funeral homes across Australia', + featuredProviders = [], + discoverMapSlot, + onSelectFeaturedProvider, features = [], googleRating, googleReviewCount, @@ -152,6 +190,8 @@ export const HomePage = React.forwardRef( }, ref, ) => { + const isFullBleedHero = !!heroImageUrl; + return ( ( {/* ═══════════════════════════════════════════════════════════════════ Section 1: Hero ═══════════════════════════════════════════════════════════════════ */} - - - {/* Hero text */} - {heroHeading} - + {heroSubheading} - - - {/* Hero image */} - + + ) : ( + /* ── V1: Split hero ── */ + + - {heroImage || ( - - )} - - - + + + {heroHeading} + + + {heroSubheading} + + + + {heroImage || ( + + )} + + + + )} {/* ═══════════════════════════════════════════════════════════════════ Section 2: FuneralFinder Widget (overlapping card) @@ -256,6 +337,131 @@ export const HomePage = React.forwardRef( + {/* ═══════════════════════════════════════════════════════════════════ + Section 2b: Stats Bar (V2 only) + ═══════════════════════════════════════════════════════════════════ */} + {stats.length > 0 && ( + + + + {stats.map((stat) => ( + + + {stat.value} + + + {stat.label} + + + ))} + + + + )} + + {/* ═══════════════════════════════════════════════════════════════════ + Section 2c: Discover — Map + Featured Providers (V2) + ═══════════════════════════════════════════════════════════════════ */} + {featuredProviders.length > 0 && ( + + + + + See what you’ll discover + + + From trusted local providers to personalised options, find the right care near + you. + + + + + {/* Map placeholder */} + + {discoverMapSlot || ( + + Map coming soon + + )} + + + {/* Featured provider cards */} + + {featuredProviders.map((provider) => ( + onSelectFeaturedProvider(provider.id) + : undefined + } + /> + ))} + + + + + )} + {/* ═══════════════════════════════════════════════════════════════════ Section 3: Partner Logos Carousel ═══════════════════════════════════════════════════════════════════ */} @@ -319,12 +525,9 @@ export const HomePage = React.forwardRef( '100%': { transform: 'translateX(-50%)' }, }, '&:hover': { animationPlayState: 'paused' }, - '@media (prefers-reduced-motion: reduce)': { - animation: 'none', - }, + '@media (prefers-reduced-motion: reduce)': { animation: 'none' }, }} > - {/* Duplicate logos for seamless infinite scroll */} {[...partnerLogos, ...partnerLogos].map((logo, i) => ( ( filter: 'grayscale(100%)', opacity: 0.5, transition: 'opacity 0.2s, filter 0.2s', - '&:hover': { - filter: 'grayscale(0%)', - opacity: 1, - }, + '&:hover': { filter: 'grayscale(0%)', opacity: 1 }, flexShrink: 0, }} /> @@ -442,112 +642,100 @@ export const HomePage = React.forwardRef( )} {/* ═══════════════════════════════════════════════════════════════════ - Section 5: Reviews / Testimonials + Section 5: Testimonials ═══════════════════════════════════════════════════════════════════ */} - {(testimonials.length > 0 || googleRating != null) && ( + {testimonials.length > 0 && ( - - - - What our customers say - - - How we have helped families like yours - + + + Testimonials + - {/* Google aggregate */} - {googleRating != null && ( - - - Google Reviews + {googleRating != null && ( + + + + + {googleRating} Google Rating + {googleReviewCount + ? ` · ${googleReviewCount.toLocaleString('en-AU')} reviews` + : ''} - - {googleRating} - - - {googleReviewCount != null && ( - - {googleReviewCount.toLocaleString('en-AU')} reviews - - )} - )} - - - {/* Testimonial cards */} - {testimonials.length > 0 && ( - - {testimonials.map((t, i) => ( - - - - {t.name} - - - - - “{t.quote}” - - - {t.timeAgo} - - - ))} )} + + {/* Editorial testimonials — alternating alignment */} + + {testimonials.map((t, i) => { + const isRight = i % 2 === 1; + return ( + + + + {t.quote} + + + {t.name} + + + + + · {t.timeAgo} + + + + ); + })} + )} @@ -597,32 +785,34 @@ export const HomePage = React.forwardRef( id="faq-heading" sx={{ textAlign: 'center', mb: { xs: 4, md: 6 }, color: 'text.primary' }} > - Frequently Asked Questions + FAQ - + {faqItems.map((item, i) => ( - } sx={{ px: 3, py: 1 }}> - + } sx={{ px: 0, py: 1.5 }}> + {item.question} - + {typeof item.answer === 'string' ? ( - + {item.answer} ) : (