import React from 'react'; import Box from '@mui/material/Box'; import TextField from '@mui/material/TextField'; import MenuItem from '@mui/material/MenuItem'; import Pagination from '@mui/material/Pagination'; import type { SxProps, Theme } from '@mui/material/styles'; import { WizardLayout } from '../../templates/WizardLayout'; import { Card } from '../../atoms/Card'; import { Badge } from '../../atoms/Badge'; import { Typography } from '../../atoms/Typography'; import { Button } from '../../atoms/Button'; import { Divider } from '../../atoms/Divider'; // ─── Types ─────────────────────────────────────────────────────────────────── /** A coffin available for selection */ export interface Coffin { id: string; name: string; imageUrl: string; price: number; category: string; isPopular?: boolean; } /** Filter category option */ export interface CoffinCategory { value: string; label: string; } /** Price range filter option */ export interface CoffinPriceRange { value: string; label: string; } /** Form values for the coffins step */ export interface CoffinsStepValues { /** Selected coffin ID */ selectedCoffinId: string | null; /** Active category filter */ categoryFilter: string; /** Active price filter */ priceFilter: string; /** Current page (1-indexed) */ page: number; } /** Field-level error messages */ export interface CoffinsStepErrors { selectedCoffinId?: string; } /** Props for the CoffinsStep page component */ export interface CoffinsStepProps { /** Current form values */ values: CoffinsStepValues; /** Callback when any field value changes */ onChange: (values: CoffinsStepValues) => void; /** Callback when the Continue button is clicked */ onContinue: () => void; /** Callback for back navigation */ onBack?: () => void; /** Callback for save-and-exit */ onSaveAndExit?: () => void; /** Field-level validation errors */ errors?: CoffinsStepErrors; /** Whether the Continue button is in a loading state */ loading?: boolean; /** Available coffins (already filtered by parent) */ coffins: Coffin[]; /** Total count before pagination (for display) */ totalCount?: number; /** Total pages */ totalPages?: number; /** Category filter options */ categories?: CoffinCategory[]; /** Price range filter options */ priceRanges?: CoffinPriceRange[]; /** Whether this is a pre-planning flow */ isPrePlanning?: boolean; /** Navigation bar */ navigation?: React.ReactNode; /** Progress stepper */ progressStepper?: React.ReactNode; /** Running total */ runningTotal?: React.ReactNode; /** Hide the help bar */ hideHelpBar?: boolean; /** MUI sx prop */ sx?: SxProps; } // ─── Component ─────────────────────────────────────────────────────────────── /** * Step 10 — Coffin selection for the FA arrangement wizard. * * Grid-sidebar layout: filter sidebar (left/top on mobile) + coffin * card grid (main area). Cards show image, name, price, and optional * "Most Popular" badge (Rec #10). Pagination at the bottom. * * Uses Australian terminology: "coffin" not "casket" (both types may * appear in the catalogue — the product type is shown per card). * * Pure presentation component — props in, callbacks out. * Filtering and pagination logic handled by parent. * * Spec: documentation/steps/steps/10_coffins.yaml */ export const CoffinsStep: React.FC = ({ values, onChange, onContinue, onBack, onSaveAndExit, errors, loading = false, coffins, totalCount, totalPages = 1, categories = [ { value: 'all', label: 'All categories' }, { value: 'solid_timber', label: 'Solid Timber' }, { value: 'custom_board', label: 'Custom Board' }, { value: 'environmental', label: 'Environmental' }, { value: 'designer', label: 'Designer' }, { value: 'protective_metal', label: 'Protective Metal' }, ], priceRanges = [ { value: 'all', label: 'All prices' }, { value: 'under_2000', label: 'Under $2,000' }, { value: '2000_4000', label: '$2,000 – $4,000' }, { value: 'over_4000', label: 'Over $4,000' }, ], isPrePlanning = false, navigation, progressStepper, runningTotal, hideHelpBar, sx, }) => { const displayCount = totalCount ?? coffins.length; const handleFilterChange = (field: 'categoryFilter' | 'priceFilter', value: string) => { onChange({ ...values, [field]: value, page: 1 }); }; // ─── Sidebar content (filters) ─── const sidebar = ( Filters handleFilterChange('categoryFilter', e.target.value)} fullWidth > {categories.map((cat) => ( {cat.label} ))} handleFilterChange('priceFilter', e.target.value)} fullWidth > {priceRanges.map((range) => ( {range.label} ))} ); // ─── Main content (card grid) ─── const mainContent = ( { e.preventDefault(); onContinue(); }} > {/* Page heading */} Choose a coffin {isPrePlanning ? 'Browse the range to get an idea of styles and pricing. You can change your selection later.' : 'Browse the range available with your selected provider. Use the filters to narrow your options.'} Selecting a coffin within your package allowance won't change your total. Coffins outside the allowance will adjust the price. {/* Results count */} Showing {displayCount} coffin{displayCount !== 1 ? 's' : ''} {/* Coffin card grid */} {coffins.map((coffin, index) => ( onChange({ ...values, selectedCoffinId: coffin.id })} role="radio" aria-checked={coffin.id === values.selectedCoffinId} tabIndex={ values.selectedCoffinId === null ? index === 0 ? 0 : -1 : coffin.id === values.selectedCoffinId ? 0 : -1 } sx={{ overflow: 'hidden' }} > {/* Image */} {coffin.isPopular && ( Most Popular )} {/* Content */} {coffin.name} {coffin.category} ${coffin.price.toLocaleString('en-AU')} ))} {/* Validation error */} {errors?.selectedCoffinId && ( {errors.selectedCoffinId} )} {/* Pagination */} {totalPages > 1 && ( onChange({ ...values, page })} color="primary" /> )} {/* CTAs */} {onSaveAndExit ? ( ) : ( )} ); return ( {sidebar} ); }; CoffinsStep.displayName = 'CoffinsStep'; export default CoffinsStep;