VenueDetailStep: move services to informational cards on left panel

- Removed service toggles from right panel (selection moves to next step)
- Added informational service cards on left panel with name, description, price
- Intro text: "You can choose which ones to include in the next step"
- Removed VenueDetailStepValues type, values/onChange props, handleToggle
- Simplified stories (no more useState for service state)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-30 20:53:02 +11:00
parent 289dc18025
commit 5ccb7ccb8e
3 changed files with 88 additions and 151 deletions

View File

@@ -1,7 +1,6 @@
import { useState } from 'react';
import type { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import { VenueDetailStep } from './VenueDetailStep'; import { VenueDetailStep } from './VenueDetailStep';
import type { VenueDetailStepValues, VenueDetail, VenueService } from './VenueDetailStep'; import type { VenueDetail, VenueService } from './VenueDetailStep';
import { Navigation } from '../../organisms/Navigation'; import { Navigation } from '../../organisms/Navigation';
import Box from '@mui/material/Box'; import Box from '@mui/material/Box';
@@ -57,17 +56,26 @@ const sampleVenue: VenueDetail = {
}; };
const sampleServices: VenueService[] = [ const sampleServices: VenueService[] = [
{ id: 'photo', name: 'Photo presentation', price: 150 }, {
{ id: 'streaming', name: 'Livestream', price: 200 }, id: 'photo',
{ id: 'recording', name: 'Recording', price: 100 }, name: 'Photo presentation',
description: 'Display a photo slideshow during the service',
price: 150,
},
{
id: 'streaming',
name: 'Livestream',
description: 'Allow family and friends to watch the service remotely',
price: 200,
},
{
id: 'recording',
name: 'Recording',
description: 'Receive a recording of the service to keep',
price: 100,
},
]; ];
const defaultValues: VenueDetailStepValues = {
photoDisplay: false,
streaming: false,
recording: false,
};
// ─── Meta ──────────────────────────────────────────────────────────────────── // ─── Meta ────────────────────────────────────────────────────────────────────
const meta: Meta<typeof VenueDetailStep> = { const meta: Meta<typeof VenueDetailStep> = {
@@ -84,48 +92,15 @@ type Story = StoryObj<typeof VenueDetailStep>;
// ─── Default ─────────────────────────────────────────────────────────────── // ─── Default ───────────────────────────────────────────────────────────────
/** Full venue detail with service toggles */ /** Full venue detail with available services */
export const Default: Story = { export const Default: Story = {
render: () => { args: {
const [values, setValues] = useState<VenueDetailStepValues>({ ...defaultValues }); venue: sampleVenue,
onAddVenue: () => alert('Venue added!'),
return ( onBack: () => alert('Back'),
<VenueDetailStep onSaveAndExit: () => alert('Save and exit'),
venue={sampleVenue} services: sampleServices,
values={values} navigation: nav,
onChange={setValues}
onAddVenue={() => alert('Venue added!')}
onBack={() => alert('Back')}
onSaveAndExit={() => alert('Save and exit')}
services={sampleServices}
navigation={nav}
/>
);
},
};
// ─── With services selected ────────────────────────────────────────────────
/** Services already toggled on */
export const WithServices: Story = {
render: () => {
const [values, setValues] = useState<VenueDetailStepValues>({
photoDisplay: true,
streaming: true,
recording: false,
});
return (
<VenueDetailStep
venue={sampleVenue}
values={values}
onChange={setValues}
onAddVenue={() => alert('Venue added!')}
onBack={() => alert('Back')}
services={sampleServices}
navigation={nav}
/>
);
}, },
}; };
@@ -133,21 +108,13 @@ export const WithServices: Story = {
/** Pre-planning variant */ /** Pre-planning variant */
export const PrePlanning: Story = { export const PrePlanning: Story = {
render: () => { args: {
const [values, setValues] = useState<VenueDetailStepValues>({ ...defaultValues }); venue: sampleVenue,
onAddVenue: () => alert('Venue selected!'),
return ( onBack: () => alert('Back'),
<VenueDetailStep services: sampleServices,
venue={sampleVenue} isPrePlanning: true,
values={values} navigation: nav,
onChange={setValues}
onAddVenue={() => alert('Venue selected!')}
onBack={() => alert('Back')}
services={sampleServices}
isPrePlanning
navigation={nav}
/>
);
}, },
}; };
@@ -155,26 +122,17 @@ export const PrePlanning: Story = {
/** Venue with minimal data (no features, religions, services) */ /** Venue with minimal data (no features, religions, services) */
export const Minimal: Story = { export const Minimal: Story = {
render: () => { args: {
const [values, setValues] = useState<VenueDetailStepValues>({ ...defaultValues }); venue: {
return (
<VenueDetailStep
venue={{
id: 'basic', id: 'basic',
name: 'Community Hall', name: 'Community Hall',
imageUrl: imageUrl: 'https://images.unsplash.com/photo-1519167758481-83f550bb49b3?w=800&h=600&fit=crop',
'https://images.unsplash.com/photo-1519167758481-83f550bb49b3?w=800&h=600&fit=crop',
location: 'Parramatta', location: 'Parramatta',
price: 500, price: 500,
address: '10 Church Street, Parramatta NSW 2150', address: '10 Church Street, Parramatta NSW 2150',
}} },
values={values} onAddVenue: () => alert('Venue added!'),
onChange={setValues} onBack: () => alert('Back'),
onAddVenue={() => alert('Venue added!')} navigation: nav,
onBack={() => alert('Back')}
navigation={nav}
/>
);
}, },
}; };

View File

@@ -6,7 +6,6 @@ import PeopleOutlinedIcon from '@mui/icons-material/PeopleOutlined';
import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline'; import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline';
import type { SxProps, Theme } from '@mui/material/styles'; import type { SxProps, Theme } from '@mui/material/styles';
import { WizardLayout } from '../../templates/WizardLayout'; import { WizardLayout } from '../../templates/WizardLayout';
import { AddOnOption } from '../../molecules/AddOnOption';
import { Card } from '../../atoms/Card'; import { Card } from '../../atoms/Card';
import { Chip } from '../../atoms/Chip'; import { Chip } from '../../atoms/Chip';
import { Typography } from '../../atoms/Typography'; import { Typography } from '../../atoms/Typography';
@@ -40,21 +39,10 @@ export interface VenueService {
price?: number; price?: number;
} }
/** Form values for service toggles */
export interface VenueDetailStepValues {
photoDisplay: boolean;
streaming: boolean;
recording: boolean;
}
/** Props for the VenueDetailStep page component */ /** Props for the VenueDetailStep page component */
export interface VenueDetailStepProps { export interface VenueDetailStepProps {
/** The venue to display */ /** The venue to display */
venue: VenueDetail; venue: VenueDetail;
/** Current service toggle values */
values: VenueDetailStepValues;
/** Callback when service toggles change */
onChange: (values: VenueDetailStepValues) => void;
/** Callback when "Add Venue" is clicked */ /** Callback when "Add Venue" is clicked */
onAddVenue: () => void; onAddVenue: () => void;
/** Callback for back navigation */ /** Callback for back navigation */
@@ -111,8 +99,6 @@ const MetaRow: React.FC<{ icon: React.ReactNode; children: React.ReactNode }> =
*/ */
export const VenueDetailStep: React.FC<VenueDetailStepProps> = ({ export const VenueDetailStep: React.FC<VenueDetailStepProps> = ({
venue, venue,
values,
onChange,
onAddVenue, onAddVenue,
onBack, onBack,
onSaveAndExit, onSaveAndExit,
@@ -125,14 +111,6 @@ export const VenueDetailStep: React.FC<VenueDetailStepProps> = ({
hideHelpBar, hideHelpBar,
sx, sx,
}) => { }) => {
const handleToggle = (field: keyof VenueDetailStepValues, checked: boolean) => {
const next = { ...values, [field]: checked };
if (field === 'streaming' && !checked) {
next.recording = false;
}
onChange(next);
};
return ( return (
<WizardLayout <WizardLayout
variant="detail-toggles" variant="detail-toggles"
@@ -238,41 +216,6 @@ export const VenueDetailStep: React.FC<VenueDetailStepProps> = ({
</Box> </Box>
</Box> </Box>
)} )}
{/* ─── Service toggles ─── */}
{services.length > 0 && (
<>
<Divider sx={{ my: 3 }} />
<Typography variant="h5" sx={{ mb: 2 }}>
Venue services
</Typography>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
<AddOnOption
name="Photo presentation"
description="Display a photo slideshow during the service"
price={services.find((s) => s.id === 'photo')?.price}
checked={values.photoDisplay}
onChange={(c) => handleToggle('photoDisplay', c)}
/>
<AddOnOption
name="Livestream"
description="Allow family and friends to watch the service remotely"
price={services.find((s) => s.id === 'streaming')?.price}
checked={values.streaming}
onChange={(c) => handleToggle('streaming', c)}
/>
{values.streaming && (
<AddOnOption
name="Recording"
description="Receive a recording of the service to keep"
price={services.find((s) => s.id === 'recording')?.price}
checked={values.recording}
onChange={(c) => handleToggle('recording', c)}
/>
)}
</Box>
</>
)}
</Box> </Box>
} }
> >
@@ -359,6 +302,47 @@ export const VenueDetailStep: React.FC<VenueDetailStepProps> = ({
</Typography> </Typography>
</Box> </Box>
)} )}
{/* ─── Available services (informational) ─── */}
{services.length > 0 && (
<>
<Divider sx={{ my: 3 }} />
<Typography variant="h5" sx={{ mb: 1 }}>
Available venue services
</Typography>
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
This venue offers the following optional services. You can choose which ones to include
in the next step.
</Typography>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1.5 }}>
{services.map((service) => (
<Card key={service.id} variant="outlined" padding="compact">
<Box
sx={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'flex-start',
}}
>
<Box>
<Typography variant="label">{service.name}</Typography>
{service.description && (
<Typography variant="body2" color="text.secondary">
{service.description}
</Typography>
)}
</Box>
{service.price != null && (
<Typography variant="labelSm" color="primary" sx={{ whiteSpace: 'nowrap' }}>
${service.price.toLocaleString('en-AU')}
</Typography>
)}
</Box>
</Card>
))}
</Box>
</>
)}
</WizardLayout> </WizardLayout>
); );
}; };

View File

@@ -1,7 +1,2 @@
export { VenueDetailStep, default } from './VenueDetailStep'; export { VenueDetailStep, default } from './VenueDetailStep';
export type { export type { VenueDetailStepProps, VenueDetail, VenueService } from './VenueDetailStep';
VenueDetailStepProps,
VenueDetailStepValues,
VenueDetail,
VenueService,
} from './VenueDetailStep';