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:
@@ -134,12 +134,14 @@ export const CemeteryStep: React.FC<CemeteryStepProps> = ({
|
||||
Cemetery
|
||||
</Typography>
|
||||
|
||||
<Typography variant="body1" color="text.secondary" sx={{ mb: 5 }}>
|
||||
<Typography variant="body1" color="text.secondary" sx={{ mb: 2 }}>
|
||||
{isPrePlanning
|
||||
? 'If you haven\u2019t decided on a cemetery yet, the funeral provider can help with this later.'
|
||||
: 'Choose where the burial will take place.'}
|
||||
</Typography>
|
||||
|
||||
<Divider sx={{ mb: 4 }} />
|
||||
|
||||
<Box
|
||||
component="form"
|
||||
noValidate
|
||||
|
||||
@@ -418,12 +418,11 @@ export const CoffinsStep: React.FC<CoffinsStepProps> = ({
|
||||
>
|
||||
{/* ─── Sidebar (left panel) ─── */}
|
||||
|
||||
{/* Heading */}
|
||||
<Typography variant="display3" component="h1" sx={{ mb: 2 }} tabIndex={-1}>
|
||||
{/* Heading — matches VenueStep / ProvidersStep list layout */}
|
||||
<Typography variant="h4" component="h1" sx={{ mb: 0.5, pt: 2 }} tabIndex={-1}>
|
||||
Choose a coffin
|
||||
</Typography>
|
||||
|
||||
<Typography variant="body1" color="text.secondary" sx={{ mb: 3 }}>
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
|
||||
{isPrePlanning
|
||||
? 'Browse the range to get an idea of styles and pricing.'
|
||||
: 'Browse our selection of bespoke designer coffins.'}
|
||||
@@ -457,7 +456,7 @@ export const CoffinsStep: React.FC<CoffinsStepProps> = ({
|
||||
<Divider sx={{ mb: 3 }} />
|
||||
|
||||
{/* ─── Categories menu — single-select with expandable subcategories ─── */}
|
||||
<Typography variant="h5" sx={{ mb: 1.5 }}>
|
||||
<Typography variant="h6" sx={{ mb: 1.5 }}>
|
||||
Categories
|
||||
</Typography>
|
||||
<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 }} />
|
||||
|
||||
{/* ─── Price range slider with editable inputs ─── */}
|
||||
<Typography variant="h5" sx={{ mb: 1.5 }}>
|
||||
<Typography variant="h6" sx={{ mb: 1.5 }}>
|
||||
Price
|
||||
</Typography>
|
||||
<Box sx={{ px: 1, mb: 1.5 }}>
|
||||
@@ -568,7 +567,7 @@ export const CoffinsStep: React.FC<CoffinsStepProps> = ({
|
||||
<Divider sx={{ mb: 3 }} />
|
||||
|
||||
{/* ─── Sort by ─── */}
|
||||
<Typography variant="h5" sx={{ mb: 2 }}>
|
||||
<Typography variant="h6" sx={{ mb: 2 }}>
|
||||
Sort by
|
||||
</Typography>
|
||||
<TextField
|
||||
|
||||
@@ -55,9 +55,10 @@ export const Default: Story = {
|
||||
email="jane.smith@example.com"
|
||||
phone="0412 345 678"
|
||||
callbackTimeframe="within 2 hours"
|
||||
contactPhone="1800 987 888"
|
||||
onViewPlan={() => alert('View plan')}
|
||||
nextSteps={[
|
||||
{ label: 'Start detailed arrangement', onClick: () => alert('Arrangement') },
|
||||
{ label: 'Start another arrangement', onClick: () => alert('Arrangement') },
|
||||
{ label: 'Go to dashboard', onClick: () => alert('Dashboard') },
|
||||
]}
|
||||
navigation={nav}
|
||||
@@ -86,3 +87,12 @@ export const PrePlanning: Story = {
|
||||
export const Minimal: Story = {
|
||||
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} />
|
||||
),
|
||||
};
|
||||
|
||||
@@ -1,12 +1,69 @@
|
||||
import React from 'react';
|
||||
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 { keyframes } from '@mui/material/styles';
|
||||
import { WizardLayout } from '../../templates/WizardLayout';
|
||||
import { Typography } from '../../atoms/Typography';
|
||||
import { Button } from '../../atoms/Button';
|
||||
import { Divider } from '../../atoms/Divider';
|
||||
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 ───────────────────────────────────────────────────────────────────
|
||||
|
||||
/** Next step link shown on the confirmation page */
|
||||
@@ -26,6 +83,8 @@ export interface ConfirmationStepProps {
|
||||
phone?: string;
|
||||
/** Expected callback timeframe (at-need) */
|
||||
callbackTimeframe?: string;
|
||||
/** Business contact phone number for making changes */
|
||||
contactPhone?: string;
|
||||
/** Navigation links to next steps */
|
||||
nextSteps?: ConfirmationLink[];
|
||||
/** Callback for "View your plan" */
|
||||
@@ -60,6 +119,7 @@ export const ConfirmationStep: React.FC<ConfirmationStepProps> = ({
|
||||
email,
|
||||
phone,
|
||||
callbackTimeframe = 'within 2 hours',
|
||||
contactPhone = '1800 987 888',
|
||||
nextSteps = [],
|
||||
onViewPlan,
|
||||
navigation,
|
||||
@@ -68,73 +128,124 @@ export const ConfirmationStep: React.FC<ConfirmationStepProps> = ({
|
||||
}) => {
|
||||
return (
|
||||
<WizardLayout variant="centered-form" navigation={navigation} hideHelpBar={hideHelpBar} sx={sx}>
|
||||
<Box sx={{ textAlign: 'center', py: 4 }}>
|
||||
{/* Success icon — muted, not celebratory */}
|
||||
<CheckCircleOutlineIcon
|
||||
sx={{
|
||||
fontSize: 64,
|
||||
color: 'var(--fa-color-brand-500)',
|
||||
mb: 3,
|
||||
opacity: 0.8,
|
||||
}}
|
||||
aria-hidden
|
||||
/>
|
||||
<Box sx={{ textAlign: 'center', py: { xs: 4, md: 6 } }}>
|
||||
{/* Animated success tick */}
|
||||
<Box sx={{ display: 'flex', justifyContent: 'center', mb: 3 }}>
|
||||
<AnimatedTick size={80} />
|
||||
</Box>
|
||||
|
||||
{/* Heading */}
|
||||
<Typography variant="display3" component="h1" sx={{ mb: 2 }} tabIndex={-1}>
|
||||
{isPrePlanning ? 'Your plan has been saved' : 'Your arrangement has been submitted'}
|
||||
</Typography>
|
||||
|
||||
{/* Body text */}
|
||||
{isPrePlanning ? (
|
||||
<Box sx={{ maxWidth: 480, mx: 'auto', mb: 4 }}>
|
||||
<Typography variant="body1" color="text.secondary">
|
||||
You can return and update it anytime.
|
||||
{email && ` We've sent a copy to ${email}.`}
|
||||
{/* Brief reassurance line */}
|
||||
<Typography
|
||||
variant="body1"
|
||||
color="text.secondary"
|
||||
sx={{ mb: 4, maxWidth: 480, mx: 'auto' }}
|
||||
>
|
||||
{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 variant="body1" color="text.secondary" sx={{ mt: 2 }}>
|
||||
When the time comes, your family can contact us and we'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'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'll have everything ready
|
||||
</Typography>
|
||||
</Box>
|
||||
) : (
|
||||
<Box sx={{ maxWidth: 480, mx: 'auto', mb: 4 }}>
|
||||
<Typography variant="body1" color="text.secondary">
|
||||
<Box
|
||||
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
|
||||
{phone && ` on ${phone}`} {callbackTimeframe} to confirm the details.
|
||||
{email && ` A confirmation has been sent to ${email}.`}
|
||||
{phone && (
|
||||
<>
|
||||
{' '}
|
||||
on <strong>{phone}</strong>
|
||||
</>
|
||||
)}{' '}
|
||||
{callbackTimeframe} to confirm the details
|
||||
</Typography>
|
||||
<Typography variant="body1" color="text.secondary" sx={{ mt: 2 }}>
|
||||
If you need to make changes before then, call us on{' '}
|
||||
<Link href="tel:1300000000" underline="always">
|
||||
1300 000 000
|
||||
{email && (
|
||||
<Typography component="li" variant="body2" color="text.secondary">
|
||||
A confirmation has been sent to <strong>{email}</strong>
|
||||
</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>
|
||||
.
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
{/* View plan CTA */}
|
||||
{/* Primary CTA */}
|
||||
{onViewPlan && (
|
||||
<Button variant="contained" size="large" onClick={onViewPlan} sx={{ mb: 4 }}>
|
||||
<Button variant="contained" size="large" onClick={onViewPlan} sx={{ mb: 2 }}>
|
||||
View your plan
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{/* Next steps */}
|
||||
{/* Secondary links */}
|
||||
{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) => (
|
||||
<Button
|
||||
<Link
|
||||
key={link.label}
|
||||
variant="text"
|
||||
color="secondary"
|
||||
onClick={link.onClick}
|
||||
href={link.href}
|
||||
href={link.href || '#'}
|
||||
onClick={
|
||||
link.onClick
|
||||
? (e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
link.onClick?.();
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
underline="hover"
|
||||
sx={{ fontWeight: 500 }}
|
||||
>
|
||||
{link.label}
|
||||
</Button>
|
||||
</Link>
|
||||
))}
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
</WizardLayout>
|
||||
|
||||
@@ -128,7 +128,7 @@ export const CrematoriumStep: React.FC<CrematoriumStepProps> = ({
|
||||
Crematorium
|
||||
</Typography>
|
||||
|
||||
<Typography variant="body1" color="text.secondary" sx={{ mb: 5 }}>
|
||||
<Typography variant="body1" color="text.secondary" sx={{ mb: 2 }}>
|
||||
{isPrePlanning
|
||||
? 'Review the crematorium details. You can update this later.'
|
||||
: isCremationOnly
|
||||
@@ -136,6 +136,8 @@ export const CrematoriumStep: React.FC<CrematoriumStepProps> = ({
|
||||
: 'Confirm the crematorium and let us know about any preferences.'}
|
||||
</Typography>
|
||||
|
||||
<Divider sx={{ mb: 4 }} />
|
||||
|
||||
<Box
|
||||
component="form"
|
||||
noValidate
|
||||
|
||||
@@ -165,12 +165,14 @@ export const DateTimeStep: React.FC<DateTimeStepProps> = ({
|
||||
A few important details
|
||||
</Typography>
|
||||
|
||||
<Typography variant="body1" color="text.secondary" sx={{ mb: 5 }}>
|
||||
<Typography variant="body1" color="text.secondary" sx={{ mb: 2 }}>
|
||||
{isAtNeed
|
||||
? '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."}
|
||||
</Typography>
|
||||
|
||||
<Divider sx={{ mb: 4 }} />
|
||||
|
||||
<Box
|
||||
component="form"
|
||||
noValidate
|
||||
|
||||
@@ -103,10 +103,12 @@ export const IntroStep: React.FC<IntroStepProps> = ({
|
||||
Let's get started
|
||||
</Typography>
|
||||
|
||||
<Typography variant="body1" color="text.secondary" sx={{ mb: 5 }}>
|
||||
<Typography variant="body1" color="text.secondary" sx={{ mb: 2 }}>
|
||||
{SUBHEADING}
|
||||
</Typography>
|
||||
|
||||
<Divider sx={{ mb: 4 }} />
|
||||
|
||||
<Box
|
||||
component="form"
|
||||
noValidate
|
||||
|
||||
@@ -154,7 +154,7 @@ export const ProvidersStep: React.FC<ProvidersStepProps> = ({
|
||||
top: 0,
|
||||
zIndex: 1,
|
||||
bgcolor: 'background.default',
|
||||
pt: 1.5,
|
||||
pt: 3,
|
||||
pb: 1.5,
|
||||
mx: { xs: -2, md: -3 },
|
||||
px: { xs: 2, md: 3 },
|
||||
@@ -224,7 +224,16 @@ export const ProvidersStep: React.FC<ProvidersStepProps> = ({
|
||||
<Box
|
||||
role="list"
|
||||
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) => (
|
||||
<ProviderCard
|
||||
|
||||
@@ -139,7 +139,7 @@ export const VenueStep: React.FC<VenueStepProps> = ({
|
||||
top: 0,
|
||||
zIndex: 1,
|
||||
bgcolor: 'background.default',
|
||||
pt: 1.5,
|
||||
pt: 3,
|
||||
pb: 1.5,
|
||||
mx: { xs: -2, md: -3 },
|
||||
px: { xs: 2, md: 3 },
|
||||
@@ -205,7 +205,16 @@ export const VenueStep: React.FC<VenueStepProps> = ({
|
||||
<Box
|
||||
role="list"
|
||||
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) => (
|
||||
<VenueCard
|
||||
|
||||
Reference in New Issue
Block a user