Compare commits

..

6 Commits

Author SHA1 Message Date
f121ac7168 Strip AI tooling and working docs for dev push 2026-04-16 16:02:06 +10:00
9ac8e31516 Refine HomePage layout and add Locations dropdown to Navigation
- Navigation: add NavItem.children support for desktop dropdown + mobile collapsible sections. Wire Locations (Melbourne, Brisbane, Sydney, South Coast NSW, Central Coast NSW) before FAQ in all HomePage stories.
- HomePage hero: add italic "Trusted by thousands of families across Australia" tagline above h1, widen text container to 990px, use hero-couple.jpg background.
- HomePage features: rename "How it works" to "4 Reasons to use Funeral Arranger", add "Why Use Funeral Arranger" overline, remove placeholder body copy.
- HomePage testimonials: add "Funeral Arranger Reviews" overline above "What families are saying".
- HomePage CTA: promote "Start planning" to primary contained button (medium).
- FuneralFinderV3: remove header + divider so the form starts directly at "How can we help".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 16:00:28 +10:00
348f3912fd Promote hero h1 to display2 for visual hierarchy over section h2s
Hero heading was display3 (same as every section h2), giving no visual
distinction. Now display2 (52px/24px) vs display3 (40px/22px). Still
renders as semantic h1 for SEO.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:03:32 +10:00
9e627d88a6 Refine HomePage mobile spacing and typography scaling
Increase hero top padding, heading-to-subheading gap, and finder card
side padding on mobile. Scale body1 text to 14px on mobile under display3
headings for better hierarchy contrast. Centre-align "Why Use FA" section
on mobile. Remove arrow from "Start exploring" button.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:49:33 +10:00
ab25af2e67 Add external asset hosting for Chromatic image rendering
Migrate Gitea remotes to git.tensordesign.com.au. Add assetUrl() utility
that resolves image paths from Gitea ParsonsAssets repo when
STORYBOOK_ASSET_BASE is set, enabling images on Chromatic-published
Storybook while keeping local dev unchanged via staticDirs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:28:14 +10:00
5b2a41f4e4 Add /publish skill for consistent multi-target deployment
Pushes to backup (full), fa-dev + sheffield (stripped via worktree),
and Chromatic in one command. Replaces ad-hoc push workflow.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 23:50:41 +10:00
10 changed files with 413 additions and 175 deletions

3
.gitignore vendored
View File

@@ -28,6 +28,9 @@ docs/reference/how-to-work-with-both-tools.md
docs/reference/mcp-setup.md
docs/reference/retroactive-review-plan.md
# Deploy scripts (contain credentials)
scripts/
# Build logs
build-storybook.log

View File

@@ -41,10 +41,6 @@ export interface FuneralFinderV3Props {
onSearch?: (params: FuneralFinderV3SearchParams) => void;
/** Shows loading state on the CTA */
loading?: boolean;
/** Optional heading override */
heading?: string;
/** Optional subheading override */
subheading?: string;
/** MUI sx override for the root container */
sx?: SxProps<Theme>;
}
@@ -251,13 +247,7 @@ const selectMenuProps = {
*/
export const FuneralFinderV3 = React.forwardRef<HTMLDivElement, FuneralFinderV3Props>(
(props, ref) => {
const {
onSearch,
loading = false,
heading = 'Find funeral directors near you',
subheading,
sx,
} = props;
const { onSearch, loading = false, sx } = props;
// ─── IDs for aria-labelledby ──────────────────────────────
const id = React.useId();
@@ -392,29 +382,6 @@ export const FuneralFinderV3 = React.forwardRef<HTMLDivElement, FuneralFinderV3P
...(Array.isArray(sx) ? sx : [sx]),
]}
>
{/* ── Header ──────────────────────────────────────────── */}
<Box sx={{ textAlign: 'center' }}>
<Typography
variant="h3"
component="h2"
sx={{
fontFamily: 'var(--fa-font-family-display)',
fontWeight: 600,
fontSize: { xs: '1.25rem', sm: '1.5rem' },
mb: subheading ? 1 : 0,
}}
>
{heading}
</Typography>
{subheading && (
<Typography variant="body2" color="text.secondary">
{subheading}
</Typography>
)}
</Box>
<Divider />
{/* ── How can we help ─────────────────────────────────── */}
<Box ref={statusSectionRef}>
<SectionLabel id={statusLabelId}>How Can We Help</SectionLabel>

View File

@@ -143,3 +143,28 @@ export const ExtendedNavigation: Story = {
ctaLabel: 'Start planning',
},
};
// --- With Dropdown -----------------------------------------------------------
/** Items with `children` render as a dropdown on desktop and a collapsible
* section in the mobile drawer */
export const WithDropdown: Story = {
args: {
logo: <FALogo />,
items: [
{
label: 'Locations',
children: [
{ label: 'Melbourne', href: '/locations/melbourne' },
{ label: 'Brisbane', href: '/locations/brisbane' },
{ label: 'Sydney', href: '/locations/sydney' },
{ label: 'South Coast NSW', href: '/locations/south-coast-nsw' },
{ label: 'Central Coast NSW', href: '/locations/central-coast-nsw' },
],
},
{ label: 'FAQ', href: '/faq' },
{ label: 'Contact Us', href: '/contact' },
{ label: 'Log in', href: '/login' },
],
},
};

View File

@@ -6,9 +6,14 @@ import Drawer from '@mui/material/Drawer';
import List from '@mui/material/List';
import ListItemButton from '@mui/material/ListItemButton';
import ListItemText from '@mui/material/ListItemText';
import Menu from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';
import Collapse from '@mui/material/Collapse';
import useMediaQuery from '@mui/material/useMediaQuery';
import MenuIcon from '@mui/icons-material/Menu';
import CloseIcon from '@mui/icons-material/Close';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
import type { SxProps, Theme } from '@mui/material/styles';
import { IconButton } from '../../atoms/IconButton';
import { Link } from '../../atoms/Link';
@@ -18,14 +23,16 @@ import { Divider } from '../../atoms/Divider';
// ─── Types ───────────────────────────────────────────────────────────────────
/** A navigation link item */
/** A navigation link item. May have children to render as a dropdown. */
export interface NavItem {
/** Display label */
label: string;
/** URL to navigate to */
href: string;
/** URL to navigate to (ignored when `children` is provided) */
href?: string;
/** Click handler (alternative to href for SPA navigation) */
onClick?: () => void;
/** Sub-items rendered as a dropdown (desktop) or collapsible (mobile) */
children?: NavItem[];
}
/** Props for the FA Navigation organism */
@@ -44,6 +51,163 @@ export interface NavigationProps {
sx?: SxProps<Theme>;
}
// ─── Desktop dropdown link ───────────────────────────────────────────────────
interface DesktopDropdownProps {
item: NavItem;
}
const DesktopDropdown: React.FC<DesktopDropdownProps> = ({ item }) => {
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const open = Boolean(anchorEl);
const handleOpen = (event: React.MouseEvent<HTMLElement>) => setAnchorEl(event.currentTarget);
const handleClose = () => setAnchorEl(null);
return (
<>
<Box
component="button"
type="button"
aria-haspopup="menu"
aria-expanded={open}
onClick={handleOpen}
sx={{
background: 'none',
border: 'none',
padding: 0,
cursor: 'pointer',
color: 'var(--fa-color-brand-900)',
fontFamily: 'inherit',
fontWeight: 600,
fontSize: '1rem',
'&:hover': {
color: 'primary.main',
},
}}
>
{item.label}
</Box>
<Menu
anchorEl={anchorEl}
open={open}
onClose={handleClose}
anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
transformOrigin={{ vertical: 'top', horizontal: 'left' }}
slotProps={{
paper: {
sx: {
mt: 1,
minWidth: 200,
borderRadius: 'var(--fa-border-radius-md, 8px)',
boxShadow: '0 8px 24px rgba(0,0,0,0.08)',
},
},
}}
>
{item.children?.map((child) => (
<MenuItem
key={child.label}
component="a"
href={child.href}
onClick={(e: React.MouseEvent) => {
if (child.onClick) {
e.preventDefault();
child.onClick();
}
handleClose();
}}
sx={{
color: 'var(--fa-color-brand-900)',
fontWeight: 500,
py: 1.25,
'&:hover': {
bgcolor: 'var(--fa-color-brand-100)',
color: 'primary.main',
},
}}
>
{child.label}
</MenuItem>
))}
</Menu>
</>
);
};
// ─── Mobile collapsible item ─────────────────────────────────────────────────
interface MobileCollapsibleProps {
item: NavItem;
onItemClick: () => void;
}
const MobileCollapsible: React.FC<MobileCollapsibleProps> = ({ item, onItemClick }) => {
const [open, setOpen] = React.useState(false);
return (
<>
<ListItemButton
onClick={() => setOpen((prev) => !prev)}
aria-expanded={open}
sx={{
py: 1.5,
px: 3,
minHeight: 44,
'&:hover': {
bgcolor: 'var(--fa-color-brand-100)',
},
}}
>
<ListItemText
primary={item.label}
primaryTypographyProps={{
fontWeight: 500,
fontSize: '1rem',
}}
/>
{open ? <ExpandLessIcon /> : <ExpandMoreIcon />}
</ListItemButton>
<Collapse in={open} timeout="auto" unmountOnExit>
<List component="div" disablePadding>
{item.children?.map((child) => (
<ListItemButton
key={child.label}
component="a"
href={child.href}
onClick={(e: React.MouseEvent) => {
if (child.onClick) {
e.preventDefault();
child.onClick();
}
onItemClick();
}}
sx={{
py: 1.25,
pl: 5,
pr: 3,
minHeight: 44,
'&:hover': {
bgcolor: 'var(--fa-color-brand-100)',
},
}}
>
<ListItemText
primary={child.label}
primaryTypographyProps={{
fontWeight: 400,
fontSize: '0.9375rem',
color: 'text.secondary',
}}
/>
</ListItemButton>
))}
</List>
</Collapse>
</>
);
};
// ─── Component ───────────────────────────────────────────────────────────────
/**
@@ -51,26 +215,13 @@ export interface NavigationProps {
*
* Responsive header with logo, navigation links, and optional CTA.
* Desktop shows links inline; mobile collapses to hamburger + drawer.
* Items with `children` render as a dropdown (desktop) or collapsible
* section (mobile).
*
* Maps to Figma "Main Nav" (14:108) desktop and "Mobile Header"
* (2391:41508) mobile patterns.
*
* Composes AppBar + Link + IconButton + Button + Divider + Drawer.
*
* Usage:
* ```tsx
* <Navigation
* logo={<img src="/logo.svg" alt="Funeral Arranger" height={40} />}
* onLogoClick={() => navigate('/')}
* items={[
* { label: 'FAQ', href: '/faq' },
* { label: 'Contact Us', href: '/contact' },
* { label: 'Log in', href: '/login' },
* ]}
* ctaLabel="Start planning"
* onCtaClick={() => navigate('/arrange')}
* />
* ```
* Composes AppBar + Link + IconButton + Button + Divider + Drawer + Menu.
*/
export const Navigation = React.forwardRef<HTMLDivElement, NavigationProps>(
({ logo, onLogoClick, items = [], ctaLabel, onCtaClick, sx }, ref) => {
@@ -78,6 +229,7 @@ export const Navigation = React.forwardRef<HTMLDivElement, NavigationProps>(
const isMobile = useMediaQuery((theme: Theme) => theme.breakpoints.down('md'));
const handleDrawerToggle = () => setDrawerOpen((prev) => !prev);
const closeDrawer = () => setDrawerOpen(false);
return (
<>
@@ -147,7 +299,10 @@ export const Navigation = React.forwardRef<HTMLDivElement, NavigationProps>(
aria-label="Main navigation"
sx={{ display: 'flex', alignItems: 'center', gap: 3.5 }}
>
{items.map((item) => (
{items.map((item) =>
item.children && item.children.length > 0 ? (
<DesktopDropdown key={item.label} item={item} />
) : (
<Link
key={item.label}
href={item.href}
@@ -164,7 +319,8 @@ export const Navigation = React.forwardRef<HTMLDivElement, NavigationProps>(
>
{item.label}
</Link>
))}
),
)}
{ctaLabel && (
<Button variant="contained" size="medium" onClick={onCtaClick}>
@@ -210,7 +366,10 @@ export const Navigation = React.forwardRef<HTMLDivElement, NavigationProps>(
{/* Nav items */}
<List component="nav" aria-label="Main navigation">
{items.map((item) => (
{items.map((item) =>
item.children && item.children.length > 0 ? (
<MobileCollapsible key={item.label} item={item} onItemClick={closeDrawer} />
) : (
<ListItemButton
key={item.label}
component="a"
@@ -220,7 +379,7 @@ export const Navigation = React.forwardRef<HTMLDivElement, NavigationProps>(
e.preventDefault();
item.onClick();
}
setDrawerOpen(false);
closeDrawer();
}}
sx={{
py: 1.5,
@@ -239,7 +398,8 @@ export const Navigation = React.forwardRef<HTMLDivElement, NavigationProps>(
}}
/>
</ListItemButton>
))}
),
)}
</List>
{ctaLabel && (
@@ -250,7 +410,7 @@ export const Navigation = React.forwardRef<HTMLDivElement, NavigationProps>(
fullWidth
onClick={() => {
if (onCtaClick) onCtaClick();
setDrawerOpen(false);
closeDrawer();
}}
>
{ctaLabel}

View File

@@ -40,6 +40,16 @@ const nav = (
<Navigation
logo={<FALogo />}
items={[
{
label: 'Locations',
children: [
{ label: 'Melbourne', href: '/locations/melbourne' },
{ label: 'Brisbane', href: '/locations/brisbane' },
{ label: 'Sydney', href: '/locations/sydney' },
{ label: 'South Coast NSW', href: '/locations/south-coast-nsw' },
{ label: 'Central Coast NSW', href: '/locations/central-coast-nsw' },
],
},
{ label: 'FAQ', href: '/faq' },
{ label: 'Contact Us', href: '/contact' },
{ label: 'Log in', href: '/login' },

View File

@@ -13,6 +13,7 @@ import type { SxProps, Theme } from '@mui/material/styles';
import { Typography } from '../../atoms/Typography';
import { Button } from '../../atoms/Button';
import { ProviderCardCompact } from '../../molecules/ProviderCardCompact';
import { assetUrl } from '../../../utils/assetUrl';
import { Divider } from '../../atoms/Divider';
import { FuneralFinderV3, type FuneralFinderV3SearchParams } from '../../organisms/FuneralFinder';
@@ -185,8 +186,8 @@ export const HomePage = React.forwardRef<HTMLDivElement, HomePageProps>(
discoverMapSlot,
onSelectFeaturedProvider,
features = [],
featuresHeading = 'How it works',
featuresBody = 'Search local funeral directors, compare transparent pricing, and personalise a plan — all in your own time. No pressure, no hidden costs.',
featuresHeading = '4 Reasons to use Funeral Arranger',
featuresBody,
googleRating,
googleReviewCount,
testimonials = [],
@@ -240,21 +241,32 @@ export const HomePage = React.forwardRef<HTMLDivElement, HomePageProps>(
}}
>
<Container
maxWidth="md"
maxWidth={false}
sx={{
position: 'relative',
zIndex: 1,
textAlign: 'center',
pt: { xs: 8, md: 11 },
pb: 4,
maxWidth: 990,
pt: { xs: 10, md: 14 },
pb: { xs: 3, md: 4 },
}}
>
<Typography
variant="display3"
variant="body1"
sx={{
color: 'rgba(255,255,255,0.85)',
fontStyle: 'italic',
mb: 2,
}}
>
Trusted by thousands of families across Australia
</Typography>
<Typography
variant="display2"
component="h1"
id="hero-heading"
tabIndex={-1}
sx={{ mb: 3, color: 'var(--fa-color-white)' }}
sx={{ mb: 5, color: 'var(--fa-color-white)' }}
>
{heroHeading}
</Typography>
@@ -272,20 +284,14 @@ export const HomePage = React.forwardRef<HTMLDivElement, HomePageProps>(
position: 'relative',
zIndex: 2,
width: '100%',
px: 2,
pt: 2,
px: { xs: 3, md: 2 },
pt: 6,
pb: 0,
mb: { xs: -14, md: -18 },
}}
>
<Box sx={{ width: '100%', maxWidth: finderSlot ? 500 : 520, mx: 'auto' }}>
{finderSlot || (
<FuneralFinderV3
heading="Find your local providers"
onSearch={onSearch}
loading={searchLoading}
/>
)}
{finderSlot || <FuneralFinderV3 onSearch={onSearch} loading={searchLoading} />}
</Box>
</Box>
</Box>
@@ -315,7 +321,7 @@ export const HomePage = React.forwardRef<HTMLDivElement, HomePageProps>(
}}
>
<Typography
variant="display3"
variant="display2"
component="h1"
id="hero-heading"
tabIndex={-1}
@@ -368,13 +374,7 @@ export const HomePage = React.forwardRef<HTMLDivElement, HomePageProps>(
}}
>
<Box sx={{ maxWidth: 620, mx: 'auto' }}>
{finderSlot || (
<FuneralFinderV3
heading="Find your local providers"
onSearch={onSearch}
loading={searchLoading}
/>
)}
{finderSlot || <FuneralFinderV3 onSearch={onSearch} loading={searchLoading} />}
</Box>
</Box>
)}
@@ -498,7 +498,7 @@ export const HomePage = React.forwardRef<HTMLDivElement, HomePageProps>(
<Typography
variant="body1"
color="text.secondary"
sx={{ maxWidth: 520, mx: 'auto' }}
sx={{ maxWidth: 520, mx: 'auto', fontSize: { xs: '0.875rem', md: '1rem' } }}
>
From trusted local providers to personalised options, find the right care near
you.
@@ -571,7 +571,7 @@ export const HomePage = React.forwardRef<HTMLDivElement, HomePageProps>(
{/* CTA */}
<Box sx={{ textAlign: 'center', mt: 4 }}>
<Button variant="text" size="medium" onClick={onCtaClick}>
Start exploring &rarr;
Start exploring
</Button>
</Box>
</Container>
@@ -601,7 +601,7 @@ export const HomePage = React.forwardRef<HTMLDivElement, HomePageProps>(
}}
>
{/* Text */}
<Box>
<Box sx={{ textAlign: { xs: 'center', md: 'left' } }}>
<Typography
variant="overline"
component="div"
@@ -617,7 +617,11 @@ export const HomePage = React.forwardRef<HTMLDivElement, HomePageProps>(
>
Making an impossible time a little easier
</Typography>
<Typography variant="body1" color="text.secondary">
<Typography
variant="body1"
color="text.secondary"
sx={{ fontSize: { xs: '0.875rem', md: '1rem' } }}
>
Funeral planning doesn&rsquo;t have to be overwhelming. Whether a loved one has
just passed, is imminent, or you&rsquo;re pre-planning the future for yourself.
Compare transparent pricing from local funeral directors. Explore the service
@@ -638,7 +642,7 @@ export const HomePage = React.forwardRef<HTMLDivElement, HomePageProps>(
}}
>
<img
src="/brandassets/images/Homepage/people.png"
src={assetUrl('/images/Homepage/people.png')}
alt="Family planning together with care and confidence"
/>
</Box>
@@ -796,21 +800,30 @@ export const HomePage = React.forwardRef<HTMLDivElement, HomePageProps>(
>
<Container maxWidth="lg">
<Box sx={{ textAlign: 'center', mb: { xs: 5, md: 8 } }}>
<Typography
variant="overline"
component="div"
sx={{ color: 'var(--fa-color-brand-600)', mb: 1.5 }}
>
Why Use Funeral Arranger
</Typography>
<Typography
variant="display3"
component="h2"
id="features-heading"
sx={{ mb: 2.5, color: 'text.primary' }}
sx={{ mb: featuresBody ? 2.5 : 0, color: 'text.primary' }}
>
{featuresHeading}
</Typography>
{featuresBody && (
<Typography
variant="body1"
color="text.secondary"
sx={{ maxWidth: 560, mx: 'auto' }}
sx={{ maxWidth: 560, mx: 'auto', fontSize: { xs: '0.875rem', md: '1rem' } }}
>
{featuresBody}
</Typography>
)}
</Box>
<Box
@@ -861,6 +874,17 @@ export const HomePage = React.forwardRef<HTMLDivElement, HomePageProps>(
}}
>
<Container maxWidth="md">
<Typography
variant="overline"
component="div"
sx={{
textAlign: 'center',
color: 'var(--fa-color-brand-600)',
mb: 1.5,
}}
>
Funeral Arranger Reviews
</Typography>
<Typography
variant="display3"
component="h2"
@@ -973,7 +997,7 @@ export const HomePage = React.forwardRef<HTMLDivElement, HomePageProps>(
>
{ctaHeading}
</Typography>
<Button variant="text" size="large" onClick={onCtaClick}>
<Button variant="contained" size="medium" onClick={onCtaClick}>
{ctaButtonLabel}
</Button>
</Container>
@@ -1019,7 +1043,13 @@ export const HomePage = React.forwardRef<HTMLDivElement, HomePageProps>(
}}
>
<AccordionSummary expandIcon={<ExpandMoreIcon />} sx={{ px: 0, py: 1.5 }}>
<Typography variant="body1" sx={{ fontWeight: 500 }}>
<Typography
variant="body1"
sx={{
fontWeight: 500,
fontSize: { xs: '0.875rem', md: '1rem' },
}}
>
{item.question}
</Typography>
</AccordionSummary>

View File

@@ -8,6 +8,7 @@ import { HomePage } from './HomePage';
import type { FeaturedProvider, TrustStat } from './HomePage';
import { Navigation } from '../../organisms/Navigation';
import { Footer } from '../../organisms/Footer';
import { assetUrl } from '../../../utils/assetUrl';
// ─── Shared helpers ──────────────────────────────────────────────────────────
@@ -41,6 +42,16 @@ const nav = (
<Navigation
logo={<FALogo />}
items={[
{
label: 'Locations',
children: [
{ label: 'Melbourne', href: '/locations/melbourne' },
{ label: 'Brisbane', href: '/locations/brisbane' },
{ label: 'Sydney', href: '/locations/sydney' },
{ label: 'South Coast NSW', href: '/locations/south-coast-nsw' },
{ label: 'Central Coast NSW', href: '/locations/central-coast-nsw' },
],
},
{ label: 'FAQ', href: '/faq' },
{ label: 'Contact Us', href: '/contact' },
{ label: 'Log in', href: '/login' },
@@ -231,7 +242,7 @@ export const Default: Story = {
args: {
navigation: nav,
footer,
heroImageUrl: '/brandassets/images/heroes/parsonshero.png',
heroImageUrl: assetUrl('/images/heroes/parsonshero.png'),
stats: trustStats,
featuredProviders,
onSelectFeaturedProvider: (id) => console.log('Featured provider:', id),

View File

@@ -9,6 +9,7 @@ import type { FeaturedProvider, TrustStat, PartnerLogo } from './HomePage';
import React from 'react';
import { Navigation } from '../../organisms/Navigation';
import { Footer } from '../../organisms/Footer';
import { assetUrl } from '../../../utils/assetUrl';
// ─── Shared helpers ──────────────────────────────────────────────────────────
@@ -37,6 +38,16 @@ const nav = (
<Navigation
logo={<FALogo />}
items={[
{
label: 'Locations',
children: [
{ label: 'Melbourne', href: '/locations/melbourne' },
{ label: 'Brisbane', href: '/locations/brisbane' },
{ label: 'Sydney', href: '/locations/sydney' },
{ label: 'South Coast NSW', href: '/locations/south-coast-nsw' },
{ label: 'Central Coast NSW', href: '/locations/central-coast-nsw' },
],
},
{ label: 'FAQ', href: '/faq' },
{ label: 'Contact Us', href: '/contact' },
{ label: 'Log in', href: '/login' },
@@ -177,8 +188,8 @@ const featuredProviders: FeaturedProvider[] = [
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',
imageUrl: assetUrl('/images/venues/hparsons-funeral-home-kiama/01.jpg'),
logoUrl: assetUrl('/images/providers/hparsons-funeral-directors/logo.png'),
rating: 4.6,
reviewCount: 7,
startingPrice: 900,
@@ -188,8 +199,8 @@ const featuredProviders: FeaturedProvider[] = [
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',
imageUrl: assetUrl('/images/venues/rankins-funeral-home-warrawong/01.jpg'),
logoUrl: assetUrl('/images/providers/rankins-funerals/logo.png'),
rating: 4.8,
reviewCount: 23,
startingPrice: 1200,
@@ -199,8 +210,8 @@ const featuredProviders: FeaturedProvider[] = [
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',
imageUrl: assetUrl('/images/venues/lakeside-memorial-park-chapel/01.jpg'),
logoUrl: assetUrl('/images/providers/easy-funerals/logo.png'),
rating: 4.5,
reviewCount: 42,
startingPrice: 850,
@@ -209,30 +220,30 @@ const featuredProviders: FeaturedProvider[] = [
const partnerLogos: PartnerLogo[] = [
{
src: '/brandassets/images/providers/hparsons-funeral-directors/logo.png',
src: assetUrl('/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: assetUrl('/images/providers/rankins-funerals/logo.png'), alt: 'Rankins Funerals' },
{ src: assetUrl('/images/providers/easy-funerals/logo.png'), alt: 'Easy Funerals' },
{ src: assetUrl('/images/providers/lady-anne-funerals/logo.png'), alt: 'Lady Anne Funerals' },
{
src: '/brandassets/images/providers/killick-family-funerals/logo.png',
src: assetUrl('/images/providers/killick-family-funerals/logo.png'),
alt: 'Killick Family Funerals',
},
{
src: '/brandassets/images/providers/kenneallys-funerals/logo.png',
src: assetUrl('/images/providers/kenneallys-funerals/logo.png'),
alt: "Kenneally's Funerals",
},
{
src: '/brandassets/images/providers/wollongong-city-funerals/logo.png',
src: assetUrl('/images/providers/wollongong-city-funerals/logo.png'),
alt: 'Wollongong City Funerals',
},
{
src: '/brandassets/images/providers/hparsons-funeral-directors-shoalhaven/logo.png',
src: assetUrl('/images/providers/hparsons-funeral-directors-shoalhaven/logo.png'),
alt: 'H.Parsons Shoalhaven',
},
{
src: '/brandassets/images/providers/mackay-family-funerals/logo.webp',
src: assetUrl('/images/providers/mackay-family-funerals/logo.webp'),
alt: 'Mackay Family Funerals',
},
];
@@ -257,13 +268,13 @@ export const Default: Story = {
args: {
navigation: nav,
footer,
heroImageUrl: '/brandassets/images/heroes/hero-3.png',
heroImageUrl: assetUrl('/images/heroes/hero-couple.jpg'),
heroHeading: 'Compare funeral director 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',
src: assetUrl('/images/placeholder/map.png'),
alt: 'Map showing provider locations',
style: { width: '100%', height: '100%', objectFit: 'cover' },
}),

View File

@@ -10,6 +10,7 @@ import { FuneralFinderV4 } from '../../organisms/FuneralFinder/FuneralFinderV4';
import React from 'react';
import { Navigation } from '../../organisms/Navigation';
import { Footer } from '../../organisms/Footer';
import { assetUrl } from '../../../utils/assetUrl';
// ─── Shared helpers ──────────────────────────────────────────────────────────
@@ -38,6 +39,16 @@ const nav = (
<Navigation
logo={<FALogo />}
items={[
{
label: 'Locations',
children: [
{ label: 'Melbourne', href: '/locations/melbourne' },
{ label: 'Brisbane', href: '/locations/brisbane' },
{ label: 'Sydney', href: '/locations/sydney' },
{ label: 'South Coast NSW', href: '/locations/south-coast-nsw' },
{ label: 'Central Coast NSW', href: '/locations/central-coast-nsw' },
],
},
{ label: 'FAQ', href: '/faq' },
{ label: 'Contact Us', href: '/contact' },
{ label: 'Log in', href: '/login' },
@@ -178,8 +189,8 @@ const featuredProviders: FeaturedProvider[] = [
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',
imageUrl: assetUrl('/images/venues/hparsons-funeral-home-kiama/01.jpg'),
logoUrl: assetUrl('/images/providers/hparsons-funeral-directors/logo.png'),
rating: 4.6,
reviewCount: 7,
startingPrice: 900,
@@ -189,8 +200,8 @@ const featuredProviders: FeaturedProvider[] = [
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',
imageUrl: assetUrl('/images/venues/rankins-funeral-home-warrawong/01.jpg'),
logoUrl: assetUrl('/images/providers/rankins-funerals/logo.png'),
rating: 4.8,
reviewCount: 23,
startingPrice: 1200,
@@ -200,8 +211,8 @@ const featuredProviders: FeaturedProvider[] = [
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',
imageUrl: assetUrl('/images/venues/lakeside-memorial-park-chapel/01.jpg'),
logoUrl: assetUrl('/images/providers/easy-funerals/logo.png'),
rating: 4.5,
reviewCount: 42,
startingPrice: 850,
@@ -210,30 +221,30 @@ const featuredProviders: FeaturedProvider[] = [
const partnerLogos: PartnerLogo[] = [
{
src: '/brandassets/images/providers/hparsons-funeral-directors/logo.png',
src: assetUrl('/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: assetUrl('/images/providers/rankins-funerals/logo.png'), alt: 'Rankins Funerals' },
{ src: assetUrl('/images/providers/easy-funerals/logo.png'), alt: 'Easy Funerals' },
{ src: assetUrl('/images/providers/lady-anne-funerals/logo.png'), alt: 'Lady Anne Funerals' },
{
src: '/brandassets/images/providers/killick-family-funerals/logo.png',
src: assetUrl('/images/providers/killick-family-funerals/logo.png'),
alt: 'Killick Family Funerals',
},
{
src: '/brandassets/images/providers/kenneallys-funerals/logo.png',
src: assetUrl('/images/providers/kenneallys-funerals/logo.png'),
alt: "Kenneally's Funerals",
},
{
src: '/brandassets/images/providers/wollongong-city-funerals/logo.png',
src: assetUrl('/images/providers/wollongong-city-funerals/logo.png'),
alt: 'Wollongong City Funerals',
},
{
src: '/brandassets/images/providers/hparsons-funeral-directors-shoalhaven/logo.png',
src: assetUrl('/images/providers/hparsons-funeral-directors-shoalhaven/logo.png'),
alt: 'H.Parsons Shoalhaven',
},
{
src: '/brandassets/images/providers/mackay-family-funerals/logo.webp',
src: assetUrl('/images/providers/mackay-family-funerals/logo.webp'),
alt: 'Mackay Family Funerals',
},
];
@@ -258,7 +269,7 @@ export const Default: Story = {
args: {
navigation: nav,
footer,
heroImageUrl: '/brandassets/images/heroes/hero-3.png',
heroImageUrl: assetUrl('/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, {
@@ -267,7 +278,7 @@ export const Default: Story = {
stats: trustStats,
featuredProviders,
discoverMapSlot: React.createElement('img', {
src: '/brandassets/images/placeholder/map.png',
src: assetUrl('/images/placeholder/map.png'),
alt: 'Map showing provider locations',
style: { width: '100%', height: '100%', objectFit: 'cover' },
}),

10
src/utils/assetUrl.ts Normal file
View File

@@ -0,0 +1,10 @@
/**
* Resolves a static asset path. In local dev the path is served by Storybook's
* staticDirs; when STORYBOOK_ASSET_BASE is set (e.g. Chromatic builds) it
* prepends the external host URL so images load from Gitea.
*/
export const assetUrl = (path: string): string => {
const base =
typeof import.meta !== 'undefined' ? (import.meta.env?.STORYBOOK_ASSET_BASE ?? '') : '';
return `${base}${path}`;
};