From 6f5946805760cbe7d60fbaf0abcec67e51155f42 Mon Sep 17 00:00:00 2001 From: Richie Date: Wed, 25 Mar 2026 22:15:38 +1100 Subject: [PATCH] Remove ArrangementForm (premature) + fix Footer P2s from audit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ArrangementForm reverted to planned — need more building blocks first - Footer: extract shared overline + contact link sx, use overlineSm variant - Footer: remove hardcoded fontSize/fontWeight on contact links Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/memory/component-registry.md | 2 +- .../ArrangementForm.stories.tsx | 380 ------------------ .../ArrangementForm/ArrangementForm.tsx | 174 -------- .../organisms/ArrangementForm/index.ts | 1 - src/components/organisms/Footer/Footer.tsx | 48 +-- 5 files changed, 18 insertions(+), 587 deletions(-) delete mode 100644 src/components/organisms/ArrangementForm/ArrangementForm.stories.tsx delete mode 100644 src/components/organisms/ArrangementForm/ArrangementForm.tsx delete mode 100644 src/components/organisms/ArrangementForm/index.ts diff --git a/docs/memory/component-registry.md b/docs/memory/component-registry.md index ff8bfec..17f4247 100644 --- a/docs/memory/component-registry.md +++ b/docs/memory/component-registry.md @@ -51,7 +51,7 @@ duplicates) and MUST update it after completing one. |-----------|--------|-------------|-------| | ServiceSelector | done | ServiceOption × n + Typography + Button | Single-select service panel for arrangement flow. Heading + subheading + ServiceOption list (radiogroup) + optional continue Button. Manages selection state via selectedId/onSelect. maxDescriptionLines pass-through. | | PricingTable | planned | PriceCard × n + Typography | Comparative pricing display | -| ArrangementForm | review | StepIndicator + ServiceSelector + AddOnOption + Button + Typography | Multi-step arrangement wizard. Controlled by parent (currentStep/onNext/onBack). Each step renders arbitrary content with consistent nav buttons. canContinue per step. Back/Continue/Complete labels configurable. | +| ArrangementForm | planned | StepIndicator + ServiceSelector + AddOnOption + Button + Typography | Multi-step arrangement wizard. Deferred — build remaining atoms/molecules first. | | Navigation | done | AppBar + Link + IconButton + Button + Divider + Drawer | Responsive site header. Desktop: logo left, links right, optional CTA. Mobile: hamburger + drawer with nav items, CTA, help footer. Sticky, grey surface bg (surface.subtle). Real FA logo from brandassets/. Maps to Figma Main Nav (14:108) + Mobile Header (2391:41508). | | Footer | done | Link × n + Typography + Divider + Container + Grid | Dark espresso (brand.950) site footer. Logo + tagline + contact (phone/email) + link group columns + legal bar. Semantic HTML (footer, nav, ul). Critique: 38/40 (Excellent). | diff --git a/src/components/organisms/ArrangementForm/ArrangementForm.stories.tsx b/src/components/organisms/ArrangementForm/ArrangementForm.stories.tsx deleted file mode 100644 index 62719a9..0000000 --- a/src/components/organisms/ArrangementForm/ArrangementForm.stories.tsx +++ /dev/null @@ -1,380 +0,0 @@ -import { useState } from 'react'; -import type { Meta, StoryObj } from '@storybook/react'; -import Box from '@mui/material/Box'; -import { ArrangementForm } from './ArrangementForm'; -import { ServiceSelector } from '../ServiceSelector'; -import { AddOnOption } from '../../molecules/AddOnOption'; -import { Typography } from '../../atoms/Typography'; -import { Navigation } from '../Navigation'; - -// ─── Shared data ───────────────────────────────────────────────────────────── - -const serviceTypes = [ - { id: 'burial', name: 'Traditional Burial', price: 4200, description: 'Full service with chapel ceremony, viewing, hearse, and graveside committal.' }, - { id: 'cremation', name: 'Cremation with Service', price: 2800, description: 'Chapel ceremony followed by cremation. Ashes returned in a standard urn.' }, - { id: 'direct-cremation', name: 'Direct Cremation', price: 1600, description: 'Simple cremation without a formal service. Ashes returned within 5 business days.' }, -]; - -const coffinOptions = [ - { id: 'eco', name: 'Eco Willow', price: 850, description: 'Handwoven natural willow. Biodegradable and sustainable.' }, - { id: 'classic', name: 'Classic Maple', price: 1400, description: 'Solid maple with satin finish and brass handles.' }, - { id: 'premium', name: 'Premium Oak', price: 2200, description: 'Quarter-sawn oak with high-gloss lacquer and gold-plated handles.' }, -]; - -const addOns = [ - { id: 'flowers', name: 'Floral Arrangements', price: 450, description: 'Seasonal flowers for the chapel and casket spray.' }, - { id: 'video', name: 'Memorial Video', price: 350, description: 'Professional video tribute with photos and music, played during the service.' }, - { id: 'catering', name: 'Wake Catering', price: 800, description: 'Light refreshments for up to 50 guests after the service.' }, - { id: 'transport', name: 'Family Limousine', price: 300, description: 'Luxury vehicle for immediate family to and from the service.' }, - { id: 'death-notice', name: 'Newspaper Death Notice', price: 180, description: 'Published in one major metropolitan newspaper of your choice.' }, -]; - -const FALogoNav = () => ( - -); - -// ─── Meta ──────────────────────────────────────────────────────────────────── - -const meta: Meta = { - title: 'Organisms/ArrangementForm', - component: ArrangementForm, - tags: ['autodocs'], - parameters: { - layout: 'centered', - }, - decorators: [ - (Story) => ( - - - - ), - ], -}; - -export default meta; -type Story = StoryObj; - -// --- Default (Static) -------------------------------------------------------- - -/** Static view at step 1 — shows structure without interactivity */ -export const Default: Story = { - args: { - heading: 'Plan your arrangement', - subheading: 'We\'ll guide you through each step. You can go back and change your selections at any time.', - steps: [ - { label: 'Service', content: Service selection content }, - { label: 'Coffin', content: Coffin selection content }, - { label: 'Extras', content: Optional extras content }, - { label: 'Review', content: Review summary content }, - ], - currentStep: 0, - }, -}; - -// --- Interactive Full Flow --------------------------------------------------- - -/** Complete 4-step arrangement flow with real components */ -export const InteractiveFlow: Story = { - render: () => { - const [step, setStep] = useState(0); - const [serviceId, setServiceId] = useState(); - const [coffinId, setCoffinId] = useState(); - const [selectedAddOns, setSelectedAddOns] = useState>({}); - - const toggleAddOn = (id: string, checked: boolean) => { - setSelectedAddOns((prev) => ({ ...prev, [id]: checked })); - }; - - const getServiceName = () => serviceTypes.find((s) => s.id === serviceId)?.name ?? '—'; - const getServicePrice = () => serviceTypes.find((s) => s.id === serviceId)?.price ?? 0; - const getCoffinName = () => coffinOptions.find((c) => c.id === coffinId)?.name ?? '—'; - const getCoffinPrice = () => coffinOptions.find((c) => c.id === coffinId)?.price ?? 0; - const getAddOnTotal = () => - addOns.filter((a) => selectedAddOns[a.id]).reduce((sum, a) => sum + a.price, 0); - const getTotal = () => getServicePrice() + getCoffinPrice() + getAddOnTotal(); - - const steps = [ - { - label: 'Service', - canContinue: !!serviceId, - content: ( - - ), - }, - { - label: 'Coffin', - canContinue: !!coffinId, - content: ( - - ), - }, - { - label: 'Extras', - canContinue: true, - content: ( - - - Optional extras - - - Add any optional services. You can skip this step if none are needed. - - - {addOns.map((addOn) => ( - toggleAddOn(addOn.id, checked)} - maxDescriptionLines={1} - /> - ))} - - - ), - }, - { - label: 'Review', - canContinue: true, - content: ( - - - Review your arrangement - - - {/* Service */} - - - Service - - - {getServiceName()} - - ${getServicePrice().toLocaleString('en-AU')} - - - - - {/* Coffin */} - - - Coffin - - - {getCoffinName()} - - ${getCoffinPrice().toLocaleString('en-AU')} - - - - - {/* Extras */} - {addOns.filter((a) => selectedAddOns[a.id]).length > 0 && ( - - - Extras - - {addOns - .filter((a) => selectedAddOns[a.id]) - .map((addOn) => ( - - {addOn.name} - - ${addOn.price.toLocaleString('en-AU')} - - - ))} - - )} - - {/* Total */} - - Estimated total - - ${getTotal().toLocaleString('en-AU')} - - - - - This is an estimate only. Final pricing will be confirmed by your chosen funeral director. - - - ), - }, - ]; - - return ( - setStep((s) => Math.min(s + 1, steps.length - 1))} - onBack={() => setStep((s) => Math.max(s - 1, 0))} - onComplete={() => alert(`Arrangement complete! Total: $${getTotal().toLocaleString('en-AU')}`)} - /> - ); - }, -}; - -// --- Mid-Flow ---------------------------------------------------------------- - -/** Starts at step 2 with prior selections visible */ -export const MidFlow: Story = { - args: { - steps: [ - { label: 'Service', content: Completed }, - { label: 'Coffin', content: Completed }, - { - label: 'Extras', - content: ( - - - Optional extras - - - Add any optional services you'd like. - - - {addOns.slice(0, 3).map((addOn) => ( - - ))} - - - ), - }, - { label: 'Review', content: Review step }, - ], - currentStep: 2, - }, -}; - -// --- Two Steps --------------------------------------------------------------- - -/** Simplified 2-step flow for simpler arrangements */ -export const TwoSteps: Story = { - render: () => { - const [step, setStep] = useState(0); - const [serviceId, setServiceId] = useState(); - - return ( - - ), - }, - { - label: 'Review', - content: ( - - - Confirm your selection - - - {serviceTypes.find((s) => s.id === serviceId)?.name ?? 'Nothing selected'} - - - ), - }, - ]} - currentStep={step} - onNext={() => setStep(1)} - onBack={() => setStep(0)} - onComplete={() => alert('Complete!')} - completeLabel="Confirm arrangement" - /> - ); - }, -}; - -// --- In Page Context --------------------------------------------------------- - -/** Full page with Navigation wrapping the arrangement flow */ -export const InPageContext: Story = { - decorators: [ - (Story) => ( - - - - ), - ], - render: () => { - const [step, setStep] = useState(0); - const [serviceId, setServiceId] = useState(); - - return ( - - } - items={[ - { label: 'FAQ', href: '/faq' }, - { label: 'Contact Us', href: '/contact' }, - ]} - /> - - - ), - }, - { label: 'Coffin', content: Coffin step... }, - { label: 'Extras', content: Extras step... }, - { label: 'Review', content: Review step... }, - ]} - currentStep={step} - onNext={() => setStep((s) => s + 1)} - onBack={() => setStep((s) => s - 1)} - onComplete={() => alert('Done!')} - /> - - - ); - }, -}; diff --git a/src/components/organisms/ArrangementForm/ArrangementForm.tsx b/src/components/organisms/ArrangementForm/ArrangementForm.tsx deleted file mode 100644 index 7969ef8..0000000 --- a/src/components/organisms/ArrangementForm/ArrangementForm.tsx +++ /dev/null @@ -1,174 +0,0 @@ -import React from 'react'; -import Box from '@mui/material/Box'; -import type { SxProps, Theme } from '@mui/material/styles'; -import { Typography } from '../../atoms/Typography'; -import { Button } from '../../atoms/Button'; -import { StepIndicator } from '../../molecules/StepIndicator'; - -// ─── Types ─────────────────────────────────────────────────────────────────── - -/** A single step in the arrangement flow */ -export interface ArrangementStep { - /** Step label shown in the StepIndicator */ - label: string; - /** Step content — rendered when this step is active */ - content: React.ReactNode; - /** Whether the user can proceed past this step. Defaults to true. */ - canContinue?: boolean; -} - -/** Props for the FA ArrangementForm organism */ -export interface ArrangementFormProps { - /** The steps in the arrangement flow */ - steps: ArrangementStep[]; - /** Current step index (0-based). Controlled by parent. */ - currentStep: number; - /** Called when the user advances to the next step */ - onNext?: () => void; - /** Called when the user goes back to the previous step */ - onBack?: () => void; - /** Called when the user completes the final step */ - onComplete?: () => void; - /** Label for the next button — defaults to "Continue" */ - nextLabel?: string; - /** Label for the back button — defaults to "Back" */ - backLabel?: string; - /** Label for the final step's button — defaults to "Review arrangement" */ - completeLabel?: string; - /** Optional heading above the step content */ - heading?: string; - /** Optional subheading below the heading */ - subheading?: string; - /** MUI sx prop for the root element */ - sx?: SxProps; -} - -// ─── Component ─────────────────────────────────────────────────────────────── - -/** - * Multi-step arrangement form for the FA design system. - * - * The core planning flow — guides users through service selection, - * coffin choice, venue, extras, and review. Each step renders - * arbitrary content (ServiceSelector, AddOnOption lists, forms, etc.) - * with consistent navigation and progress indication. - * - * Composes StepIndicator + Typography + Button + step content. - * - * State is controlled by the parent — the form doesn't own step - * state or selection state. This keeps it composable and testable. - * - * Usage: - * ```tsx - * , canContinue: !!selected }, - * { label: 'Coffin', content: }, - * { label: 'Extras', content: }, - * { label: 'Review', content: }, - * ]} - * currentStep={step} - * onNext={() => setStep(s => s + 1)} - * onBack={() => setStep(s => s - 1)} - * onComplete={() => submit()} - * /> - * ``` - */ -export const ArrangementForm = React.forwardRef( - ( - { - steps, - currentStep, - onNext, - onBack, - onComplete, - nextLabel = 'Continue', - backLabel = 'Back', - completeLabel = 'Review arrangement', - heading, - subheading, - sx, - }, - ref, - ) => { - const isFirstStep = currentStep === 0; - const isLastStep = currentStep === steps.length - 1; - const activeStep = steps[currentStep]; - const canContinue = activeStep?.canContinue ?? true; - - const stepLabels = steps.map((s) => ({ label: s.label })); - - return ( - - {/* Progress indicator */} - - - {/* Optional heading */} - {heading && ( - - - {heading} - - {subheading && ( - - {subheading} - - )} - - )} - - {/* Step content */} - - {activeStep?.content} - - - {/* Navigation buttons */} - - {!isFirstStep ? ( - - ) : ( - - )} - - - - - ); - }, -); - -ArrangementForm.displayName = 'ArrangementForm'; -export default ArrangementForm; diff --git a/src/components/organisms/ArrangementForm/index.ts b/src/components/organisms/ArrangementForm/index.ts deleted file mode 100644 index 2390bfe..0000000 --- a/src/components/organisms/ArrangementForm/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { ArrangementForm, type ArrangementFormProps, type ArrangementStep } from './ArrangementForm'; diff --git a/src/components/organisms/Footer/Footer.tsx b/src/components/organisms/Footer/Footer.tsx index 7d1f397..dc171cd 100644 --- a/src/components/organisms/Footer/Footer.tsx +++ b/src/components/organisms/Footer/Footer.tsx @@ -80,6 +80,19 @@ export const Footer = React.forwardRef( const year = new Date().getFullYear(); const copyrightText = copyright || `\u00A9 ${year} Funeral Arranger. All rights reserved.`; + const overlineSx = { + color: 'var(--fa-color-brand-400)', + textTransform: 'uppercase' as const, + letterSpacing: '0.08em', + display: 'block', + mb: 0.5, + }; + + const contactLinkSx = { + color: 'var(--fa-color-white)', + '&:hover': { color: 'var(--fa-color-brand-300)' }, + }; + return ( ( {phone && ( - + Call us {phone} @@ -141,25 +140,12 @@ export const Footer = React.forwardRef( )} {email && ( - + Email {email}