SummaryStep: visual cart layout with arrangement details and share dialog

- Visual cart-style cards with image thumbnails for provider, venue,
  crematorium, coffin (replaces accordion text lists)
- Arrangement details section at top: arranger name, deceased name,
  service tradition, preferred dates/times
- Location pin icons on all location-based cards
- Allowance display: fully covered shows "Included in your package",
  partially covered shows price/allowance/remaining breakdown
- Share dialog: "Share this plan" button opens DialogShell with multi-email
  input, add/remove recipients, send confirmation state
- Included services as checkmark list, extras as priced list (consistent
  tick logic — only in sections with no priced items)
- Full-width CTA, deposit deferred to payment step
- Edit buttons with pencil icon in secondary colour

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-31 12:58:25 +11:00
parent 6cb3184130
commit 59f4eb46db
3 changed files with 711 additions and 179 deletions

View File

@@ -1,6 +1,6 @@
import type { Meta, StoryObj } from '@storybook/react';
import { SummaryStep } from './SummaryStep';
import type { SummarySection } from './SummaryStep';
import type { SummarySection, ArrangementDetails } from './SummaryStep';
import { Navigation } from '../../organisms/Navigation';
import Box from '@mui/material/Box';
@@ -33,52 +33,78 @@ const nav = (
/>
);
const sampleDetails: ArrangementDetails = {
arrangerName: 'Sarah Mitchell',
deceasedName: 'Robert Mitchell',
serviceTradition: 'Non-denominational',
preferredDates: ['Tuesday 15 April', 'Wednesday 16 April'],
preferredTime: 'Morning',
editStepId: 'date_time',
};
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 },
],
imageUrl: 'https://placehold.co/320x200/F5F5F0/8B8B7E?text=H.+Parsons',
name: 'H. Parsons Funeral Directors',
subtitle: 'Essential Service Package',
location: 'Mackay, QLD',
price: 4950,
},
{
id: 'venue',
title: 'Service Venue',
editStepId: 'venue',
imageUrl: 'https://placehold.co/320x200/F5F5F0/8B8B7E?text=West+Chapel',
name: 'West Chapel',
location: 'Strathfield, NSW',
price: 900,
items: [
{ label: 'Venue', value: 'West Chapel, Strathfield', price: 900 },
{ label: 'Photo presentation', value: 'Included', price: 150 },
{ label: 'Livestream', value: 'Included', price: 200 },
{ label: 'Photo presentation', price: 150 },
{ label: 'Livestream', price: 200 },
],
},
{
id: 'crematorium',
title: 'Crematorium',
editStepId: 'crematorium',
items: [
{ label: 'Crematorium', value: 'Warrill Park Crematorium', price: 850 },
{ label: 'Following hearse', value: 'Yes' },
],
imageUrl: 'https://placehold.co/320x200/F5F5F0/8B8B7E?text=Warrill+Park',
name: 'Warrill Park Crematorium',
location: 'Ipswich, QLD',
price: 850,
},
{
id: 'coffin',
title: 'Coffin',
editStepId: 'coffins',
imageUrl: 'https://images.unsplash.com/photo-1618220179428-22790b461013?w=320&h=200&fit=crop',
name: 'Richmond Rosewood Coffin',
colourName: 'Natural Oak',
colourHex: '#D4A76A',
price: 1750,
allowanceAmount: 500,
},
{
id: 'included',
title: 'Included Services',
editStepId: 'included_services',
items: [
{ label: 'Coffin', value: 'Cedar Classic', price: 2800 },
{ label: 'Handles', value: 'Brass Bar Handle', price: 0 },
{ label: 'Lining', value: 'White Satin', price: 0 },
{ label: 'Dressing and preparation' },
{ label: 'Viewing', value: 'Same venue' },
{ label: 'Funeral announcement' },
],
},
{
id: 'services',
title: 'Additional Services',
editStepId: 'additional_services',
id: 'extras',
title: 'Optional Extras',
editStepId: 'extras',
items: [
{ label: 'Funeral announcement', value: 'Included' },
{ label: 'Bearing', value: 'Family and friends' },
{ label: 'Catering', priceLabel: 'Price on application' },
{ label: 'Live musician', value: 'Vocalist', price: 450 },
{ label: 'Coffin bearing', value: 'Family and friends' },
{ label: 'Newspaper notice', price: 250 },
],
},
];
@@ -99,35 +125,61 @@ type Story = StoryObj<typeof SummaryStep>;
// ─── At-need (default) ──────────────────────────────────────────────────────
/** Full summary for at-need flow */
/** Full summary for at-need flow with allowances */
export const Default: Story = {
render: () => (
<SummaryStep
arrangementDetails={sampleDetails}
sections={sampleSections}
totalPrice={9850}
depositAmount={2000}
totalPrice={9050}
totalAllowances={500}
onConfirm={() => alert('Confirmed — proceed to payment')}
onBack={() => alert('Back')}
onSaveAndExit={() => alert('Save')}
onEdit={(stepId) => alert(`Edit: ${stepId}`)}
onShare={() => alert('Share plan')}
onShareByEmail={(emails) => alert(`Share to: ${emails.join(', ')}`)}
navigation={nav}
/>
),
};
// ─── Coffin fully covered ───────────────────────────────────────────────────
/** Coffin fully covered by allowance */
export const FullyCovered: Story = {
render: () => {
const sections = sampleSections.map((s) =>
s.id === 'coffin' ? { ...s, price: 900, allowanceAmount: 1000 } : s,
);
return (
<SummaryStep
arrangementDetails={sampleDetails}
sections={sections}
totalPrice={7500}
onConfirm={() => alert('Confirmed')}
onBack={() => alert('Back')}
onEdit={(stepId) => alert(`Edit: ${stepId}`)}
onShareByEmail={(emails) => alert(`Share to: ${emails.join(', ')}`)}
navigation={nav}
/>
);
},
};
// ─── Pre-planning ───────────────────────────────────────────────────────────
/** Pre-planning variant — "Save your plan" CTA, no payment */
/** Pre-planning variant — "Save your plan" CTA */
export const PrePlanning: Story = {
render: () => (
<SummaryStep
arrangementDetails={sampleDetails}
sections={sampleSections}
totalPrice={9850}
totalPrice={9050}
totalAllowances={500}
onConfirm={() => alert('Plan saved')}
onBack={() => alert('Back')}
onEdit={(stepId) => alert(`Edit: ${stepId}`)}
onShare={() => alert('Share plan')}
onShareByEmail={(emails) => alert(`Share to: ${emails.join(', ')}`)}
isPrePlanning
navigation={nav}
/>
@@ -140,8 +192,9 @@ export const PrePlanning: Story = {
export const Loading: Story = {
render: () => (
<SummaryStep
arrangementDetails={sampleDetails}
sections={sampleSections}
totalPrice={9850}
totalPrice={9050}
onConfirm={() => {}}
loading
navigation={nav}