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:
@@ -1,7 +1,6 @@
|
||||
import { useState } from 'react';
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { VenueDetailStep } from './VenueDetailStep';
|
||||
import type { VenueDetailStepValues, VenueDetail, VenueService } from './VenueDetailStep';
|
||||
import type { VenueDetail, VenueService } from './VenueDetailStep';
|
||||
import { Navigation } from '../../organisms/Navigation';
|
||||
import Box from '@mui/material/Box';
|
||||
|
||||
@@ -57,17 +56,26 @@ const sampleVenue: VenueDetail = {
|
||||
};
|
||||
|
||||
const sampleServices: VenueService[] = [
|
||||
{ id: 'photo', name: 'Photo presentation', price: 150 },
|
||||
{ id: 'streaming', name: 'Livestream', price: 200 },
|
||||
{ id: 'recording', name: 'Recording', price: 100 },
|
||||
{
|
||||
id: 'photo',
|
||||
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 ────────────────────────────────────────────────────────────────────
|
||||
|
||||
const meta: Meta<typeof VenueDetailStep> = {
|
||||
@@ -84,48 +92,15 @@ type Story = StoryObj<typeof VenueDetailStep>;
|
||||
|
||||
// ─── Default ───────────────────────────────────────────────────────────────
|
||||
|
||||
/** Full venue detail with service toggles */
|
||||
/** Full venue detail with available services */
|
||||
export const Default: Story = {
|
||||
render: () => {
|
||||
const [values, setValues] = useState<VenueDetailStepValues>({ ...defaultValues });
|
||||
|
||||
return (
|
||||
<VenueDetailStep
|
||||
venue={sampleVenue}
|
||||
values={values}
|
||||
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}
|
||||
/>
|
||||
);
|
||||
args: {
|
||||
venue: sampleVenue,
|
||||
onAddVenue: () => alert('Venue added!'),
|
||||
onBack: () => alert('Back'),
|
||||
onSaveAndExit: () => alert('Save and exit'),
|
||||
services: sampleServices,
|
||||
navigation: nav,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -133,21 +108,13 @@ export const WithServices: Story = {
|
||||
|
||||
/** Pre-planning variant */
|
||||
export const PrePlanning: Story = {
|
||||
render: () => {
|
||||
const [values, setValues] = useState<VenueDetailStepValues>({ ...defaultValues });
|
||||
|
||||
return (
|
||||
<VenueDetailStep
|
||||
venue={sampleVenue}
|
||||
values={values}
|
||||
onChange={setValues}
|
||||
onAddVenue={() => alert('Venue selected!')}
|
||||
onBack={() => alert('Back')}
|
||||
services={sampleServices}
|
||||
isPrePlanning
|
||||
navigation={nav}
|
||||
/>
|
||||
);
|
||||
args: {
|
||||
venue: sampleVenue,
|
||||
onAddVenue: () => alert('Venue selected!'),
|
||||
onBack: () => alert('Back'),
|
||||
services: sampleServices,
|
||||
isPrePlanning: true,
|
||||
navigation: nav,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -155,26 +122,17 @@ export const PrePlanning: Story = {
|
||||
|
||||
/** Venue with minimal data (no features, religions, services) */
|
||||
export const Minimal: Story = {
|
||||
render: () => {
|
||||
const [values, setValues] = useState<VenueDetailStepValues>({ ...defaultValues });
|
||||
|
||||
return (
|
||||
<VenueDetailStep
|
||||
venue={{
|
||||
id: 'basic',
|
||||
name: 'Community Hall',
|
||||
imageUrl:
|
||||
'https://images.unsplash.com/photo-1519167758481-83f550bb49b3?w=800&h=600&fit=crop',
|
||||
location: 'Parramatta',
|
||||
price: 500,
|
||||
address: '10 Church Street, Parramatta NSW 2150',
|
||||
}}
|
||||
values={values}
|
||||
onChange={setValues}
|
||||
onAddVenue={() => alert('Venue added!')}
|
||||
onBack={() => alert('Back')}
|
||||
navigation={nav}
|
||||
/>
|
||||
);
|
||||
args: {
|
||||
venue: {
|
||||
id: 'basic',
|
||||
name: 'Community Hall',
|
||||
imageUrl: 'https://images.unsplash.com/photo-1519167758481-83f550bb49b3?w=800&h=600&fit=crop',
|
||||
location: 'Parramatta',
|
||||
price: 500,
|
||||
address: '10 Church Street, Parramatta NSW 2150',
|
||||
},
|
||||
onAddVenue: () => alert('Venue added!'),
|
||||
onBack: () => alert('Back'),
|
||||
navigation: nav,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -6,7 +6,6 @@ import PeopleOutlinedIcon from '@mui/icons-material/PeopleOutlined';
|
||||
import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline';
|
||||
import type { SxProps, Theme } from '@mui/material/styles';
|
||||
import { WizardLayout } from '../../templates/WizardLayout';
|
||||
import { AddOnOption } from '../../molecules/AddOnOption';
|
||||
import { Card } from '../../atoms/Card';
|
||||
import { Chip } from '../../atoms/Chip';
|
||||
import { Typography } from '../../atoms/Typography';
|
||||
@@ -40,21 +39,10 @@ export interface VenueService {
|
||||
price?: number;
|
||||
}
|
||||
|
||||
/** Form values for service toggles */
|
||||
export interface VenueDetailStepValues {
|
||||
photoDisplay: boolean;
|
||||
streaming: boolean;
|
||||
recording: boolean;
|
||||
}
|
||||
|
||||
/** Props for the VenueDetailStep page component */
|
||||
export interface VenueDetailStepProps {
|
||||
/** The venue to display */
|
||||
venue: VenueDetail;
|
||||
/** Current service toggle values */
|
||||
values: VenueDetailStepValues;
|
||||
/** Callback when service toggles change */
|
||||
onChange: (values: VenueDetailStepValues) => void;
|
||||
/** Callback when "Add Venue" is clicked */
|
||||
onAddVenue: () => void;
|
||||
/** Callback for back navigation */
|
||||
@@ -111,8 +99,6 @@ const MetaRow: React.FC<{ icon: React.ReactNode; children: React.ReactNode }> =
|
||||
*/
|
||||
export const VenueDetailStep: React.FC<VenueDetailStepProps> = ({
|
||||
venue,
|
||||
values,
|
||||
onChange,
|
||||
onAddVenue,
|
||||
onBack,
|
||||
onSaveAndExit,
|
||||
@@ -125,14 +111,6 @@ export const VenueDetailStep: React.FC<VenueDetailStepProps> = ({
|
||||
hideHelpBar,
|
||||
sx,
|
||||
}) => {
|
||||
const handleToggle = (field: keyof VenueDetailStepValues, checked: boolean) => {
|
||||
const next = { ...values, [field]: checked };
|
||||
if (field === 'streaming' && !checked) {
|
||||
next.recording = false;
|
||||
}
|
||||
onChange(next);
|
||||
};
|
||||
|
||||
return (
|
||||
<WizardLayout
|
||||
variant="detail-toggles"
|
||||
@@ -238,41 +216,6 @@ export const VenueDetailStep: React.FC<VenueDetailStepProps> = ({
|
||||
</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>
|
||||
}
|
||||
>
|
||||
@@ -359,6 +302,47 @@ export const VenueDetailStep: React.FC<VenueDetailStepProps> = ({
|
||||
</Typography>
|
||||
</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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,7 +1,2 @@
|
||||
export { VenueDetailStep, default } from './VenueDetailStep';
|
||||
export type {
|
||||
VenueDetailStepProps,
|
||||
VenueDetailStepValues,
|
||||
VenueDetail,
|
||||
VenueService,
|
||||
} from './VenueDetailStep';
|
||||
export type { VenueDetailStepProps, VenueDetail, VenueService } from './VenueDetailStep';
|
||||
|
||||
Reference in New Issue
Block a user