From 36757bcdb03511bad60b4665f09f6b7b838c724e Mon Sep 17 00:00:00 2001 From: Richie Date: Sun, 29 Mar 2026 15:08:41 +1100 Subject: [PATCH] Add SummaryStep, PaymentStep, ConfirmationStep (wizard steps 13-15) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SummaryStep (step 13): - Accordion sections with edit IconButtons linking back to each step - dl/dt/dd definition list for label-value pairs - Total bar with prominent price display (aria-live) - Share plan icon button, deposit display - Pre-planning: "Save your plan" CTA; at-need: "Confirm" CTA PaymentStep (step 14): - Payment plan (full/deposit) shown before method (amount before how) - ToggleButtonGroup for plan + method selection - Card: PayWay iframe slot with placeholder; Bank: account details display - Terms checkbox with service agreement + privacy links - Security reassurance (lock icon, no-surprise copy) ConfirmationStep (step 15): - Terminal page — no back button, no progress indicator - At-need: "submitted" + callback timeframe + arranger contact - Pre-planning: "saved" + return-anytime + family-ready copy - Muted success icon (not celebratory), next-steps link buttons - VenueCard selected prop also staged (from step 7 work) Co-Authored-By: Claude Opus 4.6 (1M context) --- .../ConfirmationStep.stories.tsx | 88 +++++ .../ConfirmationStep/ConfirmationStep.tsx | 152 ++++++++ .../pages/ConfirmationStep/index.ts | 2 + .../pages/PaymentStep/PaymentStep.stories.tsx | 184 ++++++++++ .../pages/PaymentStep/PaymentStep.tsx | 332 ++++++++++++++++++ src/components/pages/PaymentStep/index.ts | 8 + .../pages/SummaryStep/SummaryStep.stories.tsx | 150 ++++++++ .../pages/SummaryStep/SummaryStep.tsx | 264 ++++++++++++++ src/components/pages/SummaryStep/index.ts | 2 + 9 files changed, 1182 insertions(+) create mode 100644 src/components/pages/ConfirmationStep/ConfirmationStep.stories.tsx create mode 100644 src/components/pages/ConfirmationStep/ConfirmationStep.tsx create mode 100644 src/components/pages/ConfirmationStep/index.ts create mode 100644 src/components/pages/PaymentStep/PaymentStep.stories.tsx create mode 100644 src/components/pages/PaymentStep/PaymentStep.tsx create mode 100644 src/components/pages/PaymentStep/index.ts create mode 100644 src/components/pages/SummaryStep/SummaryStep.stories.tsx create mode 100644 src/components/pages/SummaryStep/SummaryStep.tsx create mode 100644 src/components/pages/SummaryStep/index.ts diff --git a/src/components/pages/ConfirmationStep/ConfirmationStep.stories.tsx b/src/components/pages/ConfirmationStep/ConfirmationStep.stories.tsx new file mode 100644 index 0000000..cbe4834 --- /dev/null +++ b/src/components/pages/ConfirmationStep/ConfirmationStep.stories.tsx @@ -0,0 +1,88 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { ConfirmationStep } from './ConfirmationStep'; +import { Navigation } from '../../organisms/Navigation'; +import Box from '@mui/material/Box'; + +// ─── Helpers ───────────────────────────────────────────────────────────────── + +const FALogo = () => ( + + + + +); + +const nav = ( + } + items={[ + { label: 'FAQ', href: '/faq' }, + { label: 'Contact Us', href: '/contact' }, + ]} + /> +); + +// ─── Meta ──────────────────────────────────────────────────────────────────── + +const meta: Meta = { + title: 'Pages/ConfirmationStep', + component: ConfirmationStep, + tags: ['autodocs'], + parameters: { + layout: 'fullscreen', + }, +}; + +export default meta; +type Story = StoryObj; + +// ─── At-need (default) ────────────────────────────────────────────────────── + +/** At-need confirmation — arranger will call */ +export const Default: Story = { + render: () => ( + alert('View plan')} + nextSteps={[ + { label: 'Start detailed arrangement', onClick: () => alert('Arrangement') }, + { label: 'Go to dashboard', onClick: () => alert('Dashboard') }, + ]} + navigation={nav} + /> + ), +}; + +// ─── Pre-planning ─────────────────────────────────────────────────────────── + +/** Pre-planning confirmation — plan saved, return anytime */ +export const PrePlanning: Story = { + render: () => ( + alert('View plan')} + nextSteps={[{ label: 'Go to dashboard', onClick: () => alert('Dashboard') }]} + navigation={nav} + /> + ), +}; + +// ─── Minimal ──────────────────────────────────────────────────────────────── + +/** Minimal — no next steps, no view plan */ +export const Minimal: Story = { + render: () => , +}; diff --git a/src/components/pages/ConfirmationStep/ConfirmationStep.tsx b/src/components/pages/ConfirmationStep/ConfirmationStep.tsx new file mode 100644 index 0000000..58f218e --- /dev/null +++ b/src/components/pages/ConfirmationStep/ConfirmationStep.tsx @@ -0,0 +1,152 @@ +import React from 'react'; +import Box from '@mui/material/Box'; +import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline'; +import type { SxProps, Theme } from '@mui/material/styles'; +import { WizardLayout } from '../../templates/WizardLayout'; +import { Typography } from '../../atoms/Typography'; +import { Button } from '../../atoms/Button'; + +// ─── Types ─────────────────────────────────────────────────────────────────── + +/** Next step link shown on the confirmation page */ +export interface ConfirmationLink { + label: string; + href?: string; + onClick?: () => void; +} + +/** Props for the ConfirmationStep page component */ +export interface ConfirmationStepProps { + /** Whether this is a pre-planning flow (different copy) */ + isPrePlanning?: boolean; + /** User's email address (shown in confirmation text) */ + email?: string; + /** User's phone number (shown in at-need confirmation) */ + phone?: string; + /** Expected callback timeframe (at-need) */ + callbackTimeframe?: string; + /** Navigation links to next steps */ + nextSteps?: ConfirmationLink[]; + /** Callback for "View your plan" */ + onViewPlan?: () => void; + /** Navigation bar */ + navigation?: React.ReactNode; + /** Hide the help bar */ + hideHelpBar?: boolean; + /** MUI sx prop */ + sx?: SxProps; +} + +// ─── Component ─────────────────────────────────────────────────────────────── + +/** + * Step 15 — Confirmation / Plan View for the FA arrangement wizard. + * + * Terminal confirmation page. Different copy for at-need vs pre-planning. + * Confirming and orienting — not celebratory. + * + * At-need: "Your arrangement has been submitted" + callback info + * Pre-planning: "Your plan has been saved" + return-anytime info + * + * No progress indicator. No back button. Links to post-plan flows. + * + * Pure presentation component — props in, callbacks out. + * + * Spec: documentation/steps/steps/15_view.yaml + */ +export const ConfirmationStep: React.FC = ({ + isPrePlanning = false, + email, + phone, + callbackTimeframe = 'within 2 hours', + nextSteps = [], + onViewPlan, + navigation, + hideHelpBar, + sx, +}) => { + return ( + + + {/* Success icon — muted, not celebratory */} + + + {/* Heading */} + + {isPrePlanning ? 'Your plan has been saved' : 'Your arrangement has been submitted'} + + + {/* Body text */} + {isPrePlanning ? ( + + + You can return and update it anytime. + {email && ` We've sent a copy to ${email}.`} + + + When the time comes, your family can contact us and we'll have everything ready. + + + ) : ( + + + A funeral arranger will call you + {phone && ` on ${phone}`} {callbackTimeframe} to confirm the details. + {email && ` A confirmation has been sent to ${email}.`} + + + If you need to make changes before then, call us on{' '} + + 1300 000 000 + + . + + + )} + + {/* View plan CTA */} + {onViewPlan && ( + + )} + + {/* Next steps */} + {nextSteps.length > 0 && ( + + {nextSteps.map((link) => ( + + ))} + + )} + + + ); +}; + +ConfirmationStep.displayName = 'ConfirmationStep'; +export default ConfirmationStep; diff --git a/src/components/pages/ConfirmationStep/index.ts b/src/components/pages/ConfirmationStep/index.ts new file mode 100644 index 0000000..9214a89 --- /dev/null +++ b/src/components/pages/ConfirmationStep/index.ts @@ -0,0 +1,2 @@ +export { ConfirmationStep, default } from './ConfirmationStep'; +export type { ConfirmationStepProps, ConfirmationLink } from './ConfirmationStep'; diff --git a/src/components/pages/PaymentStep/PaymentStep.stories.tsx b/src/components/pages/PaymentStep/PaymentStep.stories.tsx new file mode 100644 index 0000000..7cafe73 --- /dev/null +++ b/src/components/pages/PaymentStep/PaymentStep.stories.tsx @@ -0,0 +1,184 @@ +import { useState } from 'react'; +import type { Meta, StoryObj } from '@storybook/react'; +import { PaymentStep } from './PaymentStep'; +import type { PaymentStepValues, PaymentStepErrors } from './PaymentStep'; +import { Navigation } from '../../organisms/Navigation'; +import Box from '@mui/material/Box'; + +// ─── Helpers ───────────────────────────────────────────────────────────────── + +const FALogo = () => ( + + + + +); + +const nav = ( + } + items={[ + { label: 'FAQ', href: '/faq' }, + { label: 'Contact Us', href: '/contact' }, + ]} + /> +); + +const defaultValues: PaymentStepValues = { + paymentPlan: null, + paymentMethod: null, + termsAccepted: false, +}; + +// ─── Meta ──────────────────────────────────────────────────────────────────── + +const meta: Meta = { + title: 'Pages/PaymentStep', + component: PaymentStep, + tags: ['autodocs'], + parameters: { + layout: 'fullscreen', + }, +}; + +export default meta; +type Story = StoryObj; + +// ─── Interactive (default) ────────────────────────────────────────────────── + +/** Full interactive payment flow */ +export const Default: Story = { + render: () => { + const [values, setValues] = useState({ ...defaultValues }); + const [errors, setErrors] = useState({}); + + const handleConfirm = () => { + const newErrors: PaymentStepErrors = {}; + if (!values.paymentPlan) newErrors.paymentPlan = 'Please choose a payment option.'; + if (!values.paymentMethod) newErrors.paymentMethod = 'Please choose a payment method.'; + if (!values.termsAccepted) + newErrors.termsAccepted = 'Please review and accept the service agreement to continue.'; + setErrors(newErrors); + if (Object.keys(newErrors).length === 0) alert('Payment confirmed!'); + }; + + return ( + { + setValues(v); + setErrors({}); + }} + onConfirmPayment={handleConfirm} + onBack={() => alert('Back to summary')} + errors={errors} + totalPrice={9850} + depositAmount={2000} + navigation={nav} + /> + ); + }, +}; + +// ─── Card selected ────────────────────────────────────────────────────────── + +/** Card payment selected — shows payment form placeholder */ +export const CardPayment: Story = { + render: () => { + const [values, setValues] = useState({ + paymentPlan: 'full', + paymentMethod: 'Card', + termsAccepted: false, + }); + return ( + alert('Confirm')} + onBack={() => alert('Back')} + totalPrice={9850} + navigation={nav} + /> + ); + }, +}; + +// ─── Bank transfer selected ───────────────────────────────────────────────── + +/** Bank transfer selected — shows account details */ +export const BankTransfer: Story = { + render: () => { + const [values, setValues] = useState({ + paymentPlan: 'deposit', + paymentMethod: 'Bank', + termsAccepted: true, + }); + return ( + alert('Confirm')} + onBack={() => alert('Back')} + totalPrice={9850} + depositAmount={2000} + navigation={nav} + /> + ); + }, +}; + +// ─── Validation errors ────────────────────────────────────────────────────── + +/** All errors showing */ +export const WithErrors: Story = { + render: () => { + const [values, setValues] = useState({ ...defaultValues }); + return ( + {}} + errors={{ + paymentPlan: 'Please choose a payment option.', + paymentMethod: 'Please choose a payment method.', + termsAccepted: 'Please review and accept the service agreement to continue.', + }} + totalPrice={9850} + navigation={nav} + /> + ); + }, +}; + +// ─── Processing ───────────────────────────────────────────────────────────── + +/** Payment processing */ +export const Processing: Story = { + render: () => { + const [values, setValues] = useState({ + paymentPlan: 'full', + paymentMethod: 'Card', + termsAccepted: true, + }); + return ( + {}} + loading + totalPrice={9850} + navigation={nav} + /> + ); + }, +}; diff --git a/src/components/pages/PaymentStep/PaymentStep.tsx b/src/components/pages/PaymentStep/PaymentStep.tsx new file mode 100644 index 0000000..95ad5c6 --- /dev/null +++ b/src/components/pages/PaymentStep/PaymentStep.tsx @@ -0,0 +1,332 @@ +import React from 'react'; +import Box from '@mui/material/Box'; +import Paper from '@mui/material/Paper'; +import Checkbox from '@mui/material/Checkbox'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import type { SxProps, Theme } from '@mui/material/styles'; +import LockOutlinedIcon from '@mui/icons-material/LockOutlined'; +import { WizardLayout } from '../../templates/WizardLayout'; +import { ToggleButtonGroup } from '../../atoms/ToggleButtonGroup'; +import { Collapse } from '../../atoms/Collapse'; +import { Typography } from '../../atoms/Typography'; +import { Button } from '../../atoms/Button'; +import { Divider } from '../../atoms/Divider'; + +// ─── Types ─────────────────────────────────────────────────────────────────── + +/** Payment plan choice */ +export type PaymentPlan = 'full' | 'deposit' | null; + +/** Payment method choice */ +export type PaymentMethod = 'Card' | 'Bank' | null; + +/** Form values for the payment step */ +export interface PaymentStepValues { + /** Full payment or deposit */ + paymentPlan: PaymentPlan; + /** Card or bank transfer */ + paymentMethod: PaymentMethod; + /** Terms accepted */ + termsAccepted: boolean; +} + +/** Field-level error messages */ +export interface PaymentStepErrors { + paymentPlan?: string; + paymentMethod?: string; + termsAccepted?: string; + card?: string; +} + +/** Props for the PaymentStep page component */ +export interface PaymentStepProps { + /** Current form values */ + values: PaymentStepValues; + /** Callback when any field value changes */ + onChange: (values: PaymentStepValues) => void; + /** Callback when Confirm Payment is clicked */ + onConfirmPayment: () => void; + /** Callback for back navigation */ + onBack?: () => void; + /** Field-level validation errors */ + errors?: PaymentStepErrors; + /** Whether the button is in a loading/processing state */ + loading?: boolean; + /** Total amount */ + totalPrice: number; + /** Deposit amount */ + depositAmount?: number; + /** Bank account details for transfer */ + bankDetails?: { + accountName: string; + bsb: string; + accountNumber: string; + reference?: string; + }; + /** Card payment iframe slot (PayWay integration) */ + cardFormSlot?: React.ReactNode; + /** Navigation bar */ + navigation?: React.ReactNode; + /** Progress stepper */ + progressStepper?: React.ReactNode; + /** Hide the help bar */ + hideHelpBar?: boolean; + /** MUI sx prop */ + sx?: SxProps; +} + +// ─── Component ─────────────────────────────────────────────────────────────── + +/** + * Step 14 — Payment for the FA arrangement wizard. + * + * Collect payment via credit card or bank transfer. Supports full + * payment or deposit. Hidden for pre-planning users. + * + * Payment plan shown before method so users know the amount first. + * Card details via PayWay iframe (slot). Bank transfer shows account info. + * + * Pure presentation component — props in, callbacks out. + * + * Spec: documentation/steps/steps/14_payment.yaml + */ +export const PaymentStep: React.FC = ({ + values, + onChange, + onConfirmPayment, + onBack, + errors, + loading = false, + totalPrice, + depositAmount = 2000, + bankDetails = { + accountName: 'Funeral Arranger Services Pty Ltd', + bsb: '112-879', + accountNumber: '481 449 385', + }, + cardFormSlot, + navigation, + progressStepper, + hideHelpBar, + sx, +}) => { + const payingAmount = values.paymentPlan === 'deposit' ? depositAmount : totalPrice; + + return ( + + {/* Page heading */} + + Payment + + + + + + Your payment is processed securely. You won't be charged more than the total shown. + + + + {/* ─── Amount display ─── */} + + + {values.paymentPlan === 'deposit' ? 'Deposit amount' : 'Total due'} + + + ${payingAmount.toLocaleString('en-AU')} + + {values.paymentPlan === 'deposit' && ( + + Remaining balance: ${(totalPrice - depositAmount).toLocaleString('en-AU')} + + )} + + + { + e.preventDefault(); + onConfirmPayment(); + }} + > + {/* ─── Payment plan ─── */} + + onChange({ ...values, paymentPlan: v as PaymentPlan })} + error={!!errors?.paymentPlan} + helperText={errors?.paymentPlan} + required + fullWidth + /> + + + {/* ─── Payment method ─── */} + + onChange({ ...values, paymentMethod: v as PaymentMethod })} + error={!!errors?.paymentMethod} + helperText={errors?.paymentMethod} + required + fullWidth + /> + + + {/* ─── Card form (PayWay iframe slot) ─── */} + + + {cardFormSlot || ( + + + Secure card payment form will appear here + + + )} + {errors?.card && ( + + {errors.card} + + )} + + + + {/* ─── Bank transfer details ─── */} + + + + Bank transfer details + + + {[ + { label: 'Account name', value: bankDetails.accountName }, + { label: 'BSB', value: bankDetails.bsb }, + { label: 'Account number', value: bankDetails.accountNumber }, + ].map(({ label, value }) => ( + + + {label} + + + {value} + + + ))} + + {bankDetails.reference && ( + + Reference: {bankDetails.reference} + + )} + + + + {/* ─── Terms checkbox ─── */} + onChange({ ...values, termsAccepted: e.target.checked })} + /> + } + label={ + + I agree to the{' '} + + service agreement + {' '} + and{' '} + + privacy policy + + + } + sx={{ mb: 1, alignItems: 'flex-start', '& .MuiCheckbox-root': { pt: 0.5 } }} + /> + {errors?.termsAccepted && ( + + {errors.termsAccepted} + + )} + + + + {/* CTA */} + + + + + + ); +}; + +PaymentStep.displayName = 'PaymentStep'; +export default PaymentStep; diff --git a/src/components/pages/PaymentStep/index.ts b/src/components/pages/PaymentStep/index.ts new file mode 100644 index 0000000..0709757 --- /dev/null +++ b/src/components/pages/PaymentStep/index.ts @@ -0,0 +1,8 @@ +export { PaymentStep, default } from './PaymentStep'; +export type { + PaymentStepProps, + PaymentStepValues, + PaymentStepErrors, + PaymentPlan, + PaymentMethod, +} from './PaymentStep'; diff --git a/src/components/pages/SummaryStep/SummaryStep.stories.tsx b/src/components/pages/SummaryStep/SummaryStep.stories.tsx new file mode 100644 index 0000000..c4b2fb1 --- /dev/null +++ b/src/components/pages/SummaryStep/SummaryStep.stories.tsx @@ -0,0 +1,150 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { SummaryStep } from './SummaryStep'; +import type { SummarySection } from './SummaryStep'; +import { Navigation } from '../../organisms/Navigation'; +import Box from '@mui/material/Box'; + +// ─── Helpers ───────────────────────────────────────────────────────────────── + +const FALogo = () => ( + + + + +); + +const nav = ( + } + items={[ + { label: 'FAQ', href: '/faq' }, + { label: 'Contact Us', href: '/contact' }, + ]} + /> +); + +const sampleSections: SummarySection[] = [ + { + id: 'provider', + title: 'Funeral Provider', + editStepId: 'providers', + items: [ + { label: 'Provider', value: 'H. Parsons Funeral Directors' }, + { label: 'Package', value: 'Essential Service Package', price: 4950 }, + ], + }, + { + id: 'venue', + title: 'Service Venue', + editStepId: 'venue', + items: [ + { label: 'Venue', value: 'West Chapel, Strathfield', price: 900 }, + { label: 'Photo presentation', value: 'Included', price: 150 }, + { label: 'Livestream', value: 'Included', price: 200 }, + ], + }, + { + id: 'crematorium', + title: 'Crematorium', + editStepId: 'crematorium', + items: [ + { label: 'Crematorium', value: 'Warrill Park Crematorium', price: 850 }, + { label: 'Following hearse', value: 'Yes' }, + ], + }, + { + id: 'coffin', + title: 'Coffin', + editStepId: 'coffins', + items: [ + { label: 'Coffin', value: 'Cedar Classic', price: 2800 }, + { label: 'Handles', value: 'Brass Bar Handle', price: 0 }, + { label: 'Lining', value: 'White Satin', price: 0 }, + ], + }, + { + id: 'services', + title: 'Additional Services', + editStepId: 'additional_services', + items: [ + { label: 'Funeral announcement', value: 'Included' }, + { label: 'Bearing', value: 'Family and friends' }, + ], + }, +]; + +// ─── Meta ──────────────────────────────────────────────────────────────────── + +const meta: Meta = { + title: 'Pages/SummaryStep', + component: SummaryStep, + tags: ['autodocs'], + parameters: { + layout: 'fullscreen', + }, +}; + +export default meta; +type Story = StoryObj; + +// ─── At-need (default) ────────────────────────────────────────────────────── + +/** Full summary for at-need flow */ +export const Default: Story = { + render: () => ( + alert('Confirmed — proceed to payment')} + onBack={() => alert('Back')} + onSaveAndExit={() => alert('Save')} + onEdit={(stepId) => alert(`Edit: ${stepId}`)} + onShare={() => alert('Share plan')} + navigation={nav} + /> + ), +}; + +// ─── Pre-planning ─────────────────────────────────────────────────────────── + +/** Pre-planning variant — "Save your plan" CTA, no payment */ +export const PrePlanning: Story = { + render: () => ( + alert('Plan saved')} + onBack={() => alert('Back')} + onEdit={(stepId) => alert(`Edit: ${stepId}`)} + onShare={() => alert('Share plan')} + isPrePlanning + navigation={nav} + /> + ), +}; + +// ─── Loading ──────────────────────────────────────────────────────────────── + +/** Confirm button loading */ +export const Loading: Story = { + render: () => ( + {}} + loading + navigation={nav} + /> + ), +}; diff --git a/src/components/pages/SummaryStep/SummaryStep.tsx b/src/components/pages/SummaryStep/SummaryStep.tsx new file mode 100644 index 0000000..088e4df --- /dev/null +++ b/src/components/pages/SummaryStep/SummaryStep.tsx @@ -0,0 +1,264 @@ +import React from 'react'; +import Box from '@mui/material/Box'; +import Paper from '@mui/material/Paper'; +import Accordion from '@mui/material/Accordion'; +import AccordionSummary from '@mui/material/AccordionSummary'; +import AccordionDetails from '@mui/material/AccordionDetails'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import EditOutlinedIcon from '@mui/icons-material/EditOutlined'; +import ShareOutlinedIcon from '@mui/icons-material/ShareOutlined'; +import type { SxProps, Theme } from '@mui/material/styles'; +import { WizardLayout } from '../../templates/WizardLayout'; +import { Typography } from '../../atoms/Typography'; +import { Button } from '../../atoms/Button'; +import { IconButton } from '../../atoms/IconButton'; +import { Divider } from '../../atoms/Divider'; + +// ─── Types ─────────────────────────────────────────────────────────────────── + +/** A single line item in the summary */ +export interface SummaryLineItem { + label: string; + value: string; + price?: number; +} + +/** A section in the summary (e.g. Provider, Venue, Coffin) */ +export interface SummarySection { + id: string; + title: string; + items: SummaryLineItem[]; + /** Step ID to navigate back to for editing */ + editStepId?: string; +} + +/** Props for the SummaryStep page component */ +export interface SummaryStepProps { + /** Summary sections */ + sections: SummarySection[]; + /** Total cost */ + totalPrice: number; + /** Deposit amount (if applicable) */ + depositAmount?: number; + /** Callback when Confirm is clicked */ + onConfirm: () => void; + /** Callback for back navigation */ + onBack?: () => void; + /** Callback for save-and-exit */ + onSaveAndExit?: () => void; + /** Callback when edit is clicked on a section */ + onEdit?: (stepId: string) => void; + /** Callback for sharing the plan */ + onShare?: () => void; + /** Whether the Confirm button is in a loading state */ + loading?: boolean; + /** Whether this is a pre-planning flow */ + isPrePlanning?: boolean; + /** Navigation bar */ + navigation?: React.ReactNode; + /** Progress stepper */ + progressStepper?: React.ReactNode; + /** Hide the help bar */ + hideHelpBar?: boolean; + /** MUI sx prop */ + sx?: SxProps; +} + +// ─── Component ─────────────────────────────────────────────────────────────── + +/** + * Step 13 — Summary / Review for the FA arrangement wizard. + * + * Complete summary of the funeral plan with all selections and pricing. + * Accordion sections with edit links back to each step. Total bar at bottom. + * + * For pre-planning: CTA is "Save your plan" instead of "Confirm". + * For at-need: CTA is "Confirm and continue to payment". + * + * Pure presentation component — props in, callbacks out. + * + * Spec: documentation/steps/steps/13_summary.yaml + */ +export const SummaryStep: React.FC = ({ + sections, + totalPrice, + depositAmount, + onConfirm, + onBack, + onSaveAndExit, + onEdit, + onShare, + loading = false, + isPrePlanning = false, + navigation, + progressStepper, + hideHelpBar, + sx, +}) => { + return ( + + {/* Header with share */} + + + Review your plan + + {onShare && ( + + + + )} + + + + Check everything looks right before confirming. + + + + You can edit any section by tapping the edit icon. + + + {/* ─── Summary sections ─── */} + {sections.map((section) => ( + + } sx={{ px: 3, py: 1 }}> + + + {section.title} + + {section.editStepId && onEdit && ( + { + e.stopPropagation(); + onEdit(section.editStepId!); + }} + > + + + )} + + + + + {section.items.map((item, i) => ( + + + + {item.label} + + + {item.value} + + + {item.price != null && ( + + ${item.price.toLocaleString('en-AU')} + + )} + + ))} + + + + ))} + + {/* ─── Total bar ─── */} + + + Total cost + {depositAmount != null && !isPrePlanning && ( + + Deposit: ${depositAmount.toLocaleString('en-AU')} + + )} + + + ${totalPrice.toLocaleString('en-AU')} + + + + {/* Payment reassurance */} + {!isPrePlanning && ( + + You won't be charged until you complete the next step. + + )} + + + + {/* CTAs */} + + {onSaveAndExit ? ( + + ) : ( + + )} + + + + ); +}; + +SummaryStep.displayName = 'SummaryStep'; +export default SummaryStep; diff --git a/src/components/pages/SummaryStep/index.ts b/src/components/pages/SummaryStep/index.ts new file mode 100644 index 0000000..020b817 --- /dev/null +++ b/src/components/pages/SummaryStep/index.ts @@ -0,0 +1,2 @@ +export { SummaryStep, default } from './SummaryStep'; +export type { SummaryStepProps, SummarySection, SummaryLineItem } from './SummaryStep';