import React from 'react'; import Box from '@mui/material/Box'; import TextField from '@mui/material/TextField'; import MenuItem from '@mui/material/MenuItem'; import type { SxProps, Theme } from '@mui/material/styles'; import { WizardLayout } from '../../templates/WizardLayout'; import { ProviderCardCompact } from '../../molecules/ProviderCardCompact'; import { ServiceOption } from '../../molecules/ServiceOption'; import { PackageDetail } from '../../organisms/PackageDetail'; import type { PackageSection } from '../../organisms/PackageDetail'; import { Typography } from '../../atoms/Typography'; import { Badge } from '../../atoms/Badge'; import { Button } from '../../atoms/Button'; // ─── Types ─────────────────────────────────────────────────────────────────── /** Provider summary for the compact card */ export interface PackagesStepProvider { /** Provider name */ name: string; /** Location */ location: string; /** Image URL */ imageUrl?: string; /** Rating */ rating?: number; /** Review count */ reviewCount?: number; } /** Package data for the selection list */ export interface PackageData { /** Unique package ID */ id: string; /** Package display name */ name: string; /** Package price in dollars */ price: number; /** Short description */ description?: string; /** Whether this is a "Most Popular" package */ popular?: boolean; /** Line item sections for the detail panel */ sections: PackageSection[]; /** Total price (may differ from base price with extras) */ total?: number; /** Extra items section (after total) */ extras?: PackageSection; /** Terms and conditions */ terms?: string; } /** Budget filter option */ export interface BudgetOption { /** Option value */ value: string; /** Display label */ label: string; } /** Props for the PackagesStep page component */ export interface PackagesStepProps { /** Provider summary shown at top of the list panel */ provider: PackagesStepProvider; /** Available packages */ packages: PackageData[]; /** Currently selected package ID */ selectedPackageId: string | null; /** Callback when a package is selected */ onSelectPackage: (id: string) => void; /** Current budget filter value */ budgetFilter: string; /** Callback when budget filter changes */ onBudgetFilterChange: (value: string) => void; /** Budget filter options */ budgetOptions?: BudgetOption[]; /** Callback for the Continue button */ onContinue: () => void; /** Callback for the Back button */ onBack: () => void; /** Validation error */ error?: string; /** Whether Continue is loading */ loading?: boolean; /** Navigation bar */ navigation?: React.ReactNode; /** Whether this is a pre-planning flow */ isPrePlanning?: boolean; /** MUI sx prop */ sx?: SxProps; } // ─── Constants ─────────────────────────────────────────────────────────────── const DEFAULT_BUDGET_OPTIONS: BudgetOption[] = [ { value: 'all', label: 'All packages' }, { value: '2000-4000', label: '$2,000 \u2013 $4,000' }, { value: '4000-7000', label: '$4,000 \u2013 $7,000' }, { value: '7000-10000', label: '$7,000 \u2013 $10,000+' }, ]; // ─── Component ─────────────────────────────────────────────────────────────── /** * Step 3 — Package selection page for the FA arrangement wizard. * * List + Detail split layout. Left panel shows the selected provider * (compact), a budget filter, and selectable package cards. Right panel * shows the full detail breakdown of the selected package. * * Packages are displayed as ServiceOption cards in a radiogroup pattern. * "Most Popular" badge on qualifying packages reduces decision paralysis. * * Pure presentation component — props in, callbacks out. * * Spec: documentation/steps/steps/03_packages.yaml */ export const PackagesStep: React.FC = ({ provider, packages, selectedPackageId, onSelectPackage, budgetFilter, onBudgetFilterChange, budgetOptions = DEFAULT_BUDGET_OPTIONS, onContinue, onBack, error, loading = false, navigation, isPrePlanning = false, sx, }) => { const selectedPackage = packages.find((p) => p.id === selectedPackageId); const subheading = 'Each package includes a set of services. You can customise your selections in the next steps.'; const helperText = isPrePlanning ? 'Compare packages to find what suits your wishes. Nothing is committed until you confirm.' : 'Prices shown include the base services listed. Additional options may change the total.'; return ( ) : ( Select a package to see what's included. ) } > {/* Provider compact card */} {/* Heading */} Choose a funeral package {subheading} {helperText} {/* Budget filter */} onBudgetFilterChange(e.target.value)} label="Budget range" sx={{ width: { xs: '100%', sm: 240 } }} > {budgetOptions.map((opt) => ( {opt.label} ))} {/* Error message */} {error && ( {error} )} {/* Package list — radiogroup pattern */} {packages.map((pkg) => ( {pkg.popular && ( Most Popular )} onSelectPackage(pkg.id)} /> ))} {packages.length === 0 && ( No packages match the selected budget range. Try selecting "All packages" to see the full range. )} {/* Mobile: Continue button (desktop uses PackageDetail's CTA) */} ); }; PackagesStep.displayName = 'PackagesStep'; export default PackagesStep;