DateTimeStep: rework date preferences, remove service tradition, fix inputs

- Heading: display3 for centered-form consistency with IntroStep
- Name fields: swap MUI TextField for Input atom (external label, no clipping)
- Date preferences: single date → up to 3 preferred dates with progressive
  disclosure ("+ Add another date" link, × remove on 2nd/3rd)
- Remove service style/religion field — tradition flows from provider/package
  selection and is confirmed on summary step
- Add dividers between question sections for visual separation
- Fix spacing between sections (mb: 5, mb: 3 on headings)
- FormLabels styled with fontWeight 600 for readability
- Updated types: preferredDates: string[] replaces funeralDateSpecific,
  removed religion field

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-30 12:34:55 +11:00
parent 1faa320f4b
commit 6aca83dc90
2 changed files with 162 additions and 103 deletions

View File

@@ -1,17 +1,20 @@
import React from 'react';
import Box from '@mui/material/Box';
import TextField from '@mui/material/TextField';
import Autocomplete from '@mui/material/Autocomplete';
import FormControl from '@mui/material/FormControl';
import FormLabel from '@mui/material/FormLabel';
import FormControlLabel from '@mui/material/FormControlLabel';
import RadioGroup from '@mui/material/RadioGroup';
import Radio from '@mui/material/Radio';
import IconButton from '@mui/material/IconButton';
import CloseIcon from '@mui/icons-material/Close';
import type { SxProps, Theme } from '@mui/material/styles';
import { WizardLayout } from '../../templates/WizardLayout';
import { Input } from '../../atoms/Input';
import { Collapse } from '../../atoms/Collapse';
import { Typography } from '../../atoms/Typography';
import { Button } from '../../atoms/Button';
import { Link } from '../../atoms/Link';
import { Divider } from '../../atoms/Divider';
// ─── Types ───────────────────────────────────────────────────────────────────
@@ -30,12 +33,10 @@ export interface DateTimeStepValues {
surname: string;
/** Date preference (ASAP or specific) */
funeralDate: FuneralDatePref;
/** Specific date string (ISO format) when funeralDate is "specific" */
funeralDateSpecific: string;
/** Preferred dates (up to 3) when funeralDate is "specific" */
preferredDates: string[];
/** Time-of-day preference */
funeralTime: FuneralTimePref;
/** Service style / religion preference */
religion: string | null;
}
/** Field-level error messages */
@@ -43,8 +44,7 @@ export interface DateTimeStepErrors {
firstName?: string;
surname?: string;
funeralDate?: string;
funeralDateSpecific?: string;
funeralTime?: string;
preferredDates?: string;
}
/** Props for the DateTimeStep page component */
@@ -69,8 +69,6 @@ export interface DateTimeStepProps {
showNameFields?: boolean;
/** Whether scheduling fields should be shown */
showScheduling?: boolean;
/** Available religion/service style options */
religionOptions?: string[];
/** Navigation bar — passed through to WizardLayout */
navigation?: React.ReactNode;
/** Hide the help bar */
@@ -81,30 +79,9 @@ export interface DateTimeStepProps {
// ─── Constants ───────────────────────────────────────────────────────────────
const DEFAULT_RELIGIONS = [
'No Religion',
'Civil Celebrant',
'Aboriginal',
'Anglican',
'Baptist',
'Buddhism',
'Catholic',
'Christian',
'Hinduism',
'Indigenous',
'Islam',
'Judaism',
'Lutheran',
'Oriental Orthodox',
'Eastern Orthodox',
'Greek Orthodox',
'Russian Orthodox',
'Serbian Orthodox',
'Pentecostal',
'Presbyterian',
'Sikhism',
'Uniting Church',
];
const MAX_PREFERRED_DATES = 3;
const DATE_LABELS = ['First preference', 'Second preference', 'Third preference'];
// ─── Component ───────────────────────────────────────────────────────────────
@@ -115,11 +92,13 @@ const DEFAULT_RELIGIONS = [
* Two logical sections: "About the person" and "Service timing".
* Each wrapped in fieldset/legend for screen reader structure.
*
* Field visibility is controlled by props (showNameFields, showScheduling)
* since it depends on funeral type and pre-planning status.
* Date preferences support up to 3 preferred dates via progressive
* disclosure ("+ Add another date" link).
*
* Service tradition (religion) is NOT captured here — it flows through
* from provider/package selection and is confirmed on the summary step.
*
* Grief-sensitive labels: "Their first name" not "Deceased First Name".
* Pre-planning copy: "About the person" (no "who died").
*
* Pure presentation component — props in, callbacks out.
*
@@ -136,7 +115,6 @@ export const DateTimeStep: React.FC<DateTimeStepProps> = ({
isAtNeed = true,
showNameFields = true,
showScheduling = true,
religionOptions = DEFAULT_RELIGIONS,
navigation,
hideHelpBar,
sx,
@@ -148,12 +126,31 @@ export const DateTimeStep: React.FC<DateTimeStepProps> = ({
onChange({ ...values, [field]: value });
};
const handleDateChange = (index: number, value: string) => {
const next = [...values.preferredDates];
next[index] = value;
onChange({ ...values, preferredDates: next });
};
const handleAddDate = () => {
if (values.preferredDates.length < MAX_PREFERRED_DATES) {
onChange({ ...values, preferredDates: [...values.preferredDates, ''] });
}
};
const handleRemoveDate = (index: number) => {
const next = values.preferredDates.filter((_, i) => i !== index);
onChange({ ...values, preferredDates: next.length === 0 ? [''] : next });
};
const personSectionHeading = isAtNeed ? 'About the person who has passed' : 'About the person';
const schedulingHeading = isAtNeed
? 'When are you hoping to have the service?'
: 'When would you like the service?';
const minDate = new Date().toISOString().split('T')[0];
return (
<WizardLayout
variant="centered-form"
@@ -169,11 +166,11 @@ export const DateTimeStep: React.FC<DateTimeStepProps> = ({
A few important details
</Typography>
{!isAtNeed && (
<Typography variant="body1" color="text.secondary" sx={{ mb: 4 }}>
If you&apos;re not sure about dates yet, that&apos;s fine. You can update this later.
</Typography>
)}
<Typography variant="body1" color="text.secondary" sx={{ mb: 5 }}>
{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>
<Box
component="form"
@@ -186,8 +183,8 @@ export const DateTimeStep: React.FC<DateTimeStepProps> = ({
>
{/* ─── Section 1: About the person ─── */}
{showNameFields && (
<Box component="fieldset" sx={{ border: 0, m: 0, p: 0, mb: 4 }}>
<Typography component="legend" variant="h5" sx={{ mb: 2.5 }}>
<Box component="fieldset" sx={{ border: 0, m: 0, p: 0, mb: 2 }}>
<Typography component="legend" variant="h5" sx={{ mb: 3 }}>
{personSectionHeading}
</Typography>
@@ -195,12 +192,10 @@ export const DateTimeStep: React.FC<DateTimeStepProps> = ({
sx={{
display: 'flex',
flexDirection: { xs: 'column', sm: 'row' },
gap: 2,
mb: 2,
pt: 0.5,
gap: 2.5,
}}
>
<TextField
<Input
label="Their first name"
value={values.firstName}
onChange={(e) => handleFieldChange('firstName', e.target.value)}
@@ -210,7 +205,7 @@ export const DateTimeStep: React.FC<DateTimeStepProps> = ({
fullWidth
required
/>
<TextField
<Input
label="Their surname"
value={values.surname}
onChange={(e) => handleFieldChange('surname', e.target.value)}
@@ -224,53 +219,113 @@ export const DateTimeStep: React.FC<DateTimeStepProps> = ({
</Box>
)}
{showNameFields && showScheduling && <Divider sx={{ my: 4 }} />}
{/* ─── Section 2: Scheduling ─── */}
{showScheduling && (
<Box component="fieldset" sx={{ border: 0, m: 0, p: 0, mb: 4 }}>
<Typography component="legend" variant="h5" sx={{ mb: 2.5 }}>
<Box component="fieldset" sx={{ border: 0, m: 0, p: 0, mb: 5 }}>
<Typography component="legend" variant="h5" sx={{ mb: 3 }}>
{schedulingHeading}
</Typography>
{/* Date preference */}
<FormControl component="fieldset" sx={{ mb: 3, display: 'block' }}>
<FormLabel component="legend" sx={{ mb: 1 }}>
<FormLabel
component="legend"
sx={{
mb: 1.5,
color: 'text.primary',
fontWeight: 600,
'&.Mui-focused': { color: 'text.primary' },
}}
>
Preferred timing
</FormLabel>
<RadioGroup
value={values.funeralDate ?? ''}
onChange={(e) =>
handleFieldChange('funeralDate', e.target.value as FuneralDatePref)
}
onChange={(e) => {
const pref = e.target.value as FuneralDatePref;
onChange({
...values,
funeralDate: pref,
// Initialise with one empty date slot when switching to specific
preferredDates:
pref === 'specific' && values.preferredDates.length === 0
? ['']
: values.preferredDates,
});
}}
>
<FormControlLabel value="asap" control={<Radio />} label="As soon as possible" />
<FormControlLabel
value="asap"
control={<Radio />}
label="As soon as possible"
sx={{ mb: 0.5 }}
/>
<FormControlLabel
value="specific"
control={<Radio />}
label="I have a specific date in mind"
label="I have a preferred date"
/>
</RadioGroup>
</FormControl>
{/* Specific date — progressive disclosure */}
{/* Preferred dates — progressive disclosure, up to 3 */}
<Collapse in={values.funeralDate === 'specific'}>
<TextField
label="Preferred date"
type="date"
value={values.funeralDateSpecific}
onChange={(e) => handleFieldChange('funeralDateSpecific', e.target.value)}
error={!!errors?.funeralDateSpecific}
helperText={errors?.funeralDateSpecific}
InputLabelProps={{ shrink: true }}
inputProps={{ min: new Date().toISOString().split('T')[0] }}
fullWidth
required
sx={{ mb: 3 }}
/>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2, mb: 3, pl: 4 }}>
{values.preferredDates.map((date, index) => (
<Box key={index} sx={{ display: 'flex', alignItems: 'flex-start', gap: 1 }}>
<TextField
label={DATE_LABELS[index] ?? `Preference ${index + 1}`}
type="date"
value={date}
onChange={(e) => handleDateChange(index, e.target.value)}
error={index === 0 && !!errors?.preferredDates}
helperText={index === 0 ? errors?.preferredDates : undefined}
InputLabelProps={{ shrink: true }}
inputProps={{ min: minDate }}
fullWidth
required={index === 0}
/>
{index > 0 && (
<IconButton
onClick={() => handleRemoveDate(index)}
aria-label={`Remove ${DATE_LABELS[index]?.toLowerCase() ?? 'date'}`}
sx={{ mt: 1, color: 'text.secondary' }}
>
<CloseIcon fontSize="small" />
</IconButton>
)}
</Box>
))}
{values.preferredDates.length < MAX_PREFERRED_DATES && (
<Link
component="button"
type="button"
onClick={handleAddDate}
underline="hover"
sx={{ alignSelf: 'flex-start', fontSize: '0.875rem' }}
>
+ Add another date
</Link>
)}
</Box>
</Collapse>
<Divider sx={{ my: 4 }} />
{/* Time-of-day preference */}
<FormControl component="fieldset" sx={{ mb: 3, display: 'block' }}>
<FormLabel component="legend" sx={{ mb: 1 }}>
<FormControl component="fieldset" sx={{ display: 'block' }}>
<FormLabel
component="legend"
sx={{
mb: 1.5,
color: 'text.primary',
fontWeight: 600,
'&.Mui-focused': { color: 'text.primary' },
}}
>
Do you have a preferred time of day?
</FormLabel>
<RadioGroup
@@ -279,32 +334,37 @@ export const DateTimeStep: React.FC<DateTimeStepProps> = ({
handleFieldChange('funeralTime', e.target.value as FuneralTimePref)
}
>
<FormControlLabel value="no_preference" control={<Radio />} label="No preference" />
<FormControlLabel value="morning" control={<Radio />} label="Morning" />
<FormControlLabel value="midday" control={<Radio />} label="Midday" />
<FormControlLabel value="afternoon" control={<Radio />} label="Afternoon" />
<FormControlLabel
value="no_preference"
control={<Radio />}
label="No preference"
sx={{ mb: 0.5 }}
/>
<FormControlLabel
value="morning"
control={<Radio />}
label="Morning"
sx={{ mb: 0.5 }}
/>
<FormControlLabel
value="midday"
control={<Radio />}
label="Midday"
sx={{ mb: 0.5 }}
/>
<FormControlLabel
value="afternoon"
control={<Radio />}
label="Afternoon"
sx={{ mb: 0.5 }}
/>
<FormControlLabel value="evening" control={<Radio />} label="Evening" />
</RadioGroup>
</FormControl>
</Box>
)}
{/* ─── Service style (religion) ─── */}
<Autocomplete
options={religionOptions}
value={values.religion}
onChange={(_, newValue) => handleFieldChange('religion', newValue)}
renderInput={(params) => (
<TextField
{...params}
label="Service style preference"
placeholder="Select a service style (optional)"
/>
)}
sx={{ mb: 4 }}
/>
<Divider sx={{ my: 3 }} />
<Divider sx={{ my: 4 }} />
{/* CTAs */}
<Box