ConfirmationStep redesign, page tweaks across wizard

- ConfirmationStep: animated SVG tick, "What happens next" warm card,
  bullet-point layout, contactPhone prop, link-based secondary actions
- VenueStep + ProvidersStep: sticky search bar padding fix, off-white
  bg behind card lists
- IntroStep, CemeteryStep, CrematoriumStep, DateTimeStep: add divider
  under subheading for visual separation
- CoffinsStep: h4 heading (matches VenueStep/ProvidersStep list layout),
  sidebar headings h5 → h6

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-31 15:09:12 +11:00
parent 9b75aa7ef3
commit 7a06f89e84
9 changed files with 217 additions and 71 deletions

View File

@@ -134,12 +134,14 @@ export const CemeteryStep: React.FC<CemeteryStepProps> = ({
Cemetery Cemetery
</Typography> </Typography>
<Typography variant="body1" color="text.secondary" sx={{ mb: 5 }}> <Typography variant="body1" color="text.secondary" sx={{ mb: 2 }}>
{isPrePlanning {isPrePlanning
? 'If you haven\u2019t decided on a cemetery yet, the funeral provider can help with this later.' ? 'If you haven\u2019t decided on a cemetery yet, the funeral provider can help with this later.'
: 'Choose where the burial will take place.'} : 'Choose where the burial will take place.'}
</Typography> </Typography>
<Divider sx={{ mb: 4 }} />
<Box <Box
component="form" component="form"
noValidate noValidate

View File

@@ -418,12 +418,11 @@ export const CoffinsStep: React.FC<CoffinsStepProps> = ({
> >
{/* ─── Sidebar (left panel) ─── */} {/* ─── Sidebar (left panel) ─── */}
{/* Heading */} {/* Heading — matches VenueStep / ProvidersStep list layout */}
<Typography variant="display3" component="h1" sx={{ mb: 2 }} tabIndex={-1}> <Typography variant="h4" component="h1" sx={{ mb: 0.5, pt: 2 }} tabIndex={-1}>
Choose a coffin Choose a coffin
</Typography> </Typography>
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
<Typography variant="body1" color="text.secondary" sx={{ mb: 3 }}>
{isPrePlanning {isPrePlanning
? 'Browse the range to get an idea of styles and pricing.' ? 'Browse the range to get an idea of styles and pricing.'
: 'Browse our selection of bespoke designer coffins.'} : 'Browse our selection of bespoke designer coffins.'}
@@ -457,7 +456,7 @@ export const CoffinsStep: React.FC<CoffinsStepProps> = ({
<Divider sx={{ mb: 3 }} /> <Divider sx={{ mb: 3 }} />
{/* ─── Categories menu — single-select with expandable subcategories ─── */} {/* ─── Categories menu — single-select with expandable subcategories ─── */}
<Typography variant="h5" sx={{ mb: 1.5 }}> <Typography variant="h6" sx={{ mb: 1.5 }}>
Categories Categories
</Typography> </Typography>
<Box component="nav" aria-label="Filter by category" sx={{ mb: 3 }}> <Box component="nav" aria-label="Filter by category" sx={{ mb: 3 }}>
@@ -518,7 +517,7 @@ export const CoffinsStep: React.FC<CoffinsStepProps> = ({
<Divider sx={{ mb: 3 }} /> <Divider sx={{ mb: 3 }} />
{/* ─── Price range slider with editable inputs ─── */} {/* ─── Price range slider with editable inputs ─── */}
<Typography variant="h5" sx={{ mb: 1.5 }}> <Typography variant="h6" sx={{ mb: 1.5 }}>
Price Price
</Typography> </Typography>
<Box sx={{ px: 1, mb: 1.5 }}> <Box sx={{ px: 1, mb: 1.5 }}>
@@ -568,7 +567,7 @@ export const CoffinsStep: React.FC<CoffinsStepProps> = ({
<Divider sx={{ mb: 3 }} /> <Divider sx={{ mb: 3 }} />
{/* ─── Sort by ─── */} {/* ─── Sort by ─── */}
<Typography variant="h5" sx={{ mb: 2 }}> <Typography variant="h6" sx={{ mb: 2 }}>
Sort by Sort by
</Typography> </Typography>
<TextField <TextField

View File

@@ -55,9 +55,10 @@ export const Default: Story = {
email="jane.smith@example.com" email="jane.smith@example.com"
phone="0412 345 678" phone="0412 345 678"
callbackTimeframe="within 2 hours" callbackTimeframe="within 2 hours"
contactPhone="1800 987 888"
onViewPlan={() => alert('View plan')} onViewPlan={() => alert('View plan')}
nextSteps={[ nextSteps={[
{ label: 'Start detailed arrangement', onClick: () => alert('Arrangement') }, { label: 'Start another arrangement', onClick: () => alert('Arrangement') },
{ label: 'Go to dashboard', onClick: () => alert('Dashboard') }, { label: 'Go to dashboard', onClick: () => alert('Dashboard') },
]} ]}
navigation={nav} navigation={nav}
@@ -86,3 +87,12 @@ export const PrePlanning: Story = {
export const Minimal: Story = { export const Minimal: Story = {
render: () => <ConfirmationStep email="jane.smith@example.com" navigation={nav} />, render: () => <ConfirmationStep email="jane.smith@example.com" navigation={nav} />,
}; };
// ─── No email ───────────────────────────────────────────────────────────────
/** At-need without email — phone callback only */
export const NoEmail: Story = {
render: () => (
<ConfirmationStep phone="0412 345 678" onViewPlan={() => alert('View plan')} navigation={nav} />
),
};

View File

@@ -1,12 +1,69 @@
import React from 'react'; import React from 'react';
import Box from '@mui/material/Box'; import Box from '@mui/material/Box';
import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline'; import Card from '@mui/material/Card';
import type { SxProps, Theme } from '@mui/material/styles'; import type { SxProps, Theme } from '@mui/material/styles';
import { keyframes } from '@mui/material/styles';
import { WizardLayout } from '../../templates/WizardLayout'; import { WizardLayout } from '../../templates/WizardLayout';
import { Typography } from '../../atoms/Typography'; import { Typography } from '../../atoms/Typography';
import { Button } from '../../atoms/Button'; import { Button } from '../../atoms/Button';
import { Divider } from '../../atoms/Divider';
import { Link } from '../../atoms/Link'; import { Link } from '../../atoms/Link';
// ─── Animated tick ──────────────────────────────────────────────────────────
const scaleIn = keyframes`
0% { transform: scale(0.85); opacity: 0; }
60% { transform: scale(1.04); opacity: 1; }
100% { transform: scale(1); opacity: 1; }
`;
/** Animated success tick — circle draws, then checkmark appears */
const AnimatedTick: React.FC<{ size?: number }> = ({ size = 80 }) => (
<Box
sx={{
width: size,
height: size,
animation: `${scaleIn} 0.6s ease-out both`,
'@keyframes drawCircle': {
from: { strokeDashoffset: 251 },
to: { strokeDashoffset: 0 },
},
'@keyframes drawCheck': {
from: { strokeDashoffset: 60 },
to: { strokeDashoffset: 0 },
},
'& circle': {
strokeDasharray: 251,
strokeDashoffset: 251,
animation: 'drawCircle 0.7s ease-out 0.2s forwards',
},
'& polyline': {
strokeDasharray: 60,
strokeDashoffset: 60,
animation: 'drawCheck 0.4s ease-out 0.8s forwards',
},
}}
>
<svg viewBox="0 0 100 100" width={size} height={size} fill="none">
<circle
cx="50"
cy="50"
r="40"
stroke="var(--fa-color-brand-400)"
strokeWidth="4"
strokeLinecap="round"
/>
<polyline
points="32,52 45,65 68,38"
stroke="var(--fa-color-brand-500)"
strokeWidth="5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</Box>
);
// ─── Types ─────────────────────────────────────────────────────────────────── // ─── Types ───────────────────────────────────────────────────────────────────
/** Next step link shown on the confirmation page */ /** Next step link shown on the confirmation page */
@@ -26,6 +83,8 @@ export interface ConfirmationStepProps {
phone?: string; phone?: string;
/** Expected callback timeframe (at-need) */ /** Expected callback timeframe (at-need) */
callbackTimeframe?: string; callbackTimeframe?: string;
/** Business contact phone number for making changes */
contactPhone?: string;
/** Navigation links to next steps */ /** Navigation links to next steps */
nextSteps?: ConfirmationLink[]; nextSteps?: ConfirmationLink[];
/** Callback for "View your plan" */ /** Callback for "View your plan" */
@@ -60,6 +119,7 @@ export const ConfirmationStep: React.FC<ConfirmationStepProps> = ({
email, email,
phone, phone,
callbackTimeframe = 'within 2 hours', callbackTimeframe = 'within 2 hours',
contactPhone = '1800 987 888',
nextSteps = [], nextSteps = [],
onViewPlan, onViewPlan,
navigation, navigation,
@@ -68,73 +128,124 @@ export const ConfirmationStep: React.FC<ConfirmationStepProps> = ({
}) => { }) => {
return ( return (
<WizardLayout variant="centered-form" navigation={navigation} hideHelpBar={hideHelpBar} sx={sx}> <WizardLayout variant="centered-form" navigation={navigation} hideHelpBar={hideHelpBar} sx={sx}>
<Box sx={{ textAlign: 'center', py: 4 }}> <Box sx={{ textAlign: 'center', py: { xs: 4, md: 6 } }}>
{/* Success icon — muted, not celebratory */} {/* Animated success tick */}
<CheckCircleOutlineIcon <Box sx={{ display: 'flex', justifyContent: 'center', mb: 3 }}>
sx={{ <AnimatedTick size={80} />
fontSize: 64, </Box>
color: 'var(--fa-color-brand-500)',
mb: 3,
opacity: 0.8,
}}
aria-hidden
/>
{/* Heading */} {/* Heading */}
<Typography variant="display3" component="h1" sx={{ mb: 2 }} tabIndex={-1}> <Typography variant="display3" component="h1" sx={{ mb: 2 }} tabIndex={-1}>
{isPrePlanning ? 'Your plan has been saved' : 'Your arrangement has been submitted'} {isPrePlanning ? 'Your plan has been saved' : 'Your arrangement has been submitted'}
</Typography> </Typography>
{/* Body text */} {/* Brief reassurance line */}
{isPrePlanning ? ( <Typography
<Box sx={{ maxWidth: 480, mx: 'auto', mb: 4 }}> variant="body1"
<Typography variant="body1" color="text.secondary"> color="text.secondary"
You can return and update it anytime. sx={{ mb: 4, maxWidth: 480, mx: 'auto' }}
{email && ` We've sent a copy to ${email}.`} >
{isPrePlanning
? 'Everything is safely stored. You can come back and update it anytime.'
: `We've received your arrangement and someone will be in touch shortly.`}
</Typography> </Typography>
<Typography variant="body1" color="text.secondary" sx={{ mt: 2 }}>
When the time comes, your family can contact us and we&apos;ll have everything ready. {/* What happens next — warm card */}
<Card
variant="outlined"
sx={{
textAlign: 'left',
p: { xs: 3, md: 4 },
mb: 4,
maxWidth: 560,
mx: 'auto',
bgcolor: 'var(--fa-color-surface-warm)',
borderColor: 'var(--fa-color-brand-200)',
}}
>
<Typography variant="h5" sx={{ mb: 2 }}>
What happens next
</Typography>
{isPrePlanning ? (
<Box
component="ul"
sx={{ m: 0, pl: 2.5, display: 'flex', flexDirection: 'column', gap: 1.5 }}
>
{email && (
<Typography component="li" variant="body2" color="text.secondary">
We&apos;ve sent a copy of your plan to <strong>{email}</strong>
</Typography>
)}
<Typography component="li" variant="body2" color="text.secondary">
You can return and make changes at any time your plan will be here
</Typography>
<Typography component="li" variant="body2" color="text.secondary">
When the time comes, your family can contact us and we&apos;ll have everything ready
</Typography> </Typography>
</Box> </Box>
) : ( ) : (
<Box sx={{ maxWidth: 480, mx: 'auto', mb: 4 }}> <Box
<Typography variant="body1" color="text.secondary"> component="ul"
sx={{ m: 0, pl: 2.5, display: 'flex', flexDirection: 'column', gap: 1.5 }}
>
<Typography component="li" variant="body2" color="text.secondary">
A funeral arranger will call you A funeral arranger will call you
{phone && ` on ${phone}`} {callbackTimeframe} to confirm the details. {phone && (
{email && ` A confirmation has been sent to ${email}.`} <>
{' '}
on <strong>{phone}</strong>
</>
)}{' '}
{callbackTimeframe} to confirm the details
</Typography> </Typography>
<Typography variant="body1" color="text.secondary" sx={{ mt: 2 }}> {email && (
If you need to make changes before then, call us on{' '} <Typography component="li" variant="body2" color="text.secondary">
<Link href="tel:1300000000" underline="always"> A confirmation has been sent to <strong>{email}</strong>
1300 000 000 </Typography>
)}
<Typography component="li" variant="body2" color="text.secondary">
Need to make changes before then? Call us on{' '}
<Link href={`tel:${contactPhone.replace(/\s/g, '')}`} underline="always">
{contactPhone}
</Link> </Link>
.
</Typography> </Typography>
</Box> </Box>
)} )}
</Card>
{/* View plan CTA */} {/* Primary CTA */}
{onViewPlan && ( {onViewPlan && (
<Button variant="contained" size="large" onClick={onViewPlan} sx={{ mb: 4 }}> <Button variant="contained" size="large" onClick={onViewPlan} sx={{ mb: 2 }}>
View your plan View your plan
</Button> </Button>
)} )}
{/* Next steps */} {/* Secondary links */}
{nextSteps.length > 0 && ( {nextSteps.length > 0 && (
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1.5, alignItems: 'center' }}> <>
<Divider sx={{ my: 3, maxWidth: 480, mx: 'auto' }} />
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1, alignItems: 'center' }}>
{nextSteps.map((link) => ( {nextSteps.map((link) => (
<Button <Link
key={link.label} key={link.label}
variant="text" href={link.href || '#'}
color="secondary" onClick={
onClick={link.onClick} link.onClick
href={link.href} ? (e: React.MouseEvent) => {
e.preventDefault();
link.onClick?.();
}
: undefined
}
underline="hover"
sx={{ fontWeight: 500 }}
> >
{link.label} {link.label}
</Button> </Link>
))} ))}
</Box> </Box>
</>
)} )}
</Box> </Box>
</WizardLayout> </WizardLayout>

View File

@@ -128,7 +128,7 @@ export const CrematoriumStep: React.FC<CrematoriumStepProps> = ({
Crematorium Crematorium
</Typography> </Typography>
<Typography variant="body1" color="text.secondary" sx={{ mb: 5 }}> <Typography variant="body1" color="text.secondary" sx={{ mb: 2 }}>
{isPrePlanning {isPrePlanning
? 'Review the crematorium details. You can update this later.' ? 'Review the crematorium details. You can update this later.'
: isCremationOnly : isCremationOnly
@@ -136,6 +136,8 @@ export const CrematoriumStep: React.FC<CrematoriumStepProps> = ({
: 'Confirm the crematorium and let us know about any preferences.'} : 'Confirm the crematorium and let us know about any preferences.'}
</Typography> </Typography>
<Divider sx={{ mb: 4 }} />
<Box <Box
component="form" component="form"
noValidate noValidate

View File

@@ -165,12 +165,14 @@ export const DateTimeStep: React.FC<DateTimeStepProps> = ({
A few important details A few important details
</Typography> </Typography>
<Typography variant="body1" color="text.secondary" sx={{ mb: 5 }}> <Typography variant="body1" color="text.secondary" sx={{ mb: 2 }}>
{isAtNeed {isAtNeed
? 'We just need a few details to help arrange the service.' ? 'We just need a few details to help arrange the service.'
: "If you're not sure about dates yet, that's fine. You can update this later."} : "If you're not sure about dates yet, that's fine. You can update this later."}
</Typography> </Typography>
<Divider sx={{ mb: 4 }} />
<Box <Box
component="form" component="form"
noValidate noValidate

View File

@@ -103,10 +103,12 @@ export const IntroStep: React.FC<IntroStepProps> = ({
Let&apos;s get started Let&apos;s get started
</Typography> </Typography>
<Typography variant="body1" color="text.secondary" sx={{ mb: 5 }}> <Typography variant="body1" color="text.secondary" sx={{ mb: 2 }}>
{SUBHEADING} {SUBHEADING}
</Typography> </Typography>
<Divider sx={{ mb: 4 }} />
<Box <Box
component="form" component="form"
noValidate noValidate

View File

@@ -154,7 +154,7 @@ export const ProvidersStep: React.FC<ProvidersStepProps> = ({
top: 0, top: 0,
zIndex: 1, zIndex: 1,
bgcolor: 'background.default', bgcolor: 'background.default',
pt: 1.5, pt: 3,
pb: 1.5, pb: 1.5,
mx: { xs: -2, md: -3 }, mx: { xs: -2, md: -3 },
px: { xs: 2, md: 3 }, px: { xs: 2, md: 3 },
@@ -224,7 +224,16 @@ export const ProvidersStep: React.FC<ProvidersStepProps> = ({
<Box <Box
role="list" role="list"
aria-label="Funeral providers" aria-label="Funeral providers"
sx={{ display: 'flex', flexDirection: 'column', gap: 2, pb: 3 }} sx={{
display: 'flex',
flexDirection: 'column',
gap: 2,
pb: 3,
pt: 2,
px: { xs: 2, md: 3 },
mx: { xs: -2, md: -3 },
bgcolor: 'var(--fa-color-surface-subtle)',
}}
> >
{providers.map((provider) => ( {providers.map((provider) => (
<ProviderCard <ProviderCard

View File

@@ -139,7 +139,7 @@ export const VenueStep: React.FC<VenueStepProps> = ({
top: 0, top: 0,
zIndex: 1, zIndex: 1,
bgcolor: 'background.default', bgcolor: 'background.default',
pt: 1.5, pt: 3,
pb: 1.5, pb: 1.5,
mx: { xs: -2, md: -3 }, mx: { xs: -2, md: -3 },
px: { xs: 2, md: 3 }, px: { xs: 2, md: 3 },
@@ -205,7 +205,16 @@ export const VenueStep: React.FC<VenueStepProps> = ({
<Box <Box
role="list" role="list"
aria-label="Available venues" aria-label="Available venues"
sx={{ display: 'flex', flexDirection: 'column', gap: 2, pb: 3 }} sx={{
display: 'flex',
flexDirection: 'column',
gap: 2,
pb: 3,
pt: 2,
px: { xs: 2, md: 3 },
mx: { xs: -2, md: -3 },
bgcolor: 'var(--fa-color-surface-subtle)',
}}
> >
{venues.map((venue) => ( {venues.map((venue) => (
<VenueCard <VenueCard