Skip to content

Commit a68d7cf

Browse files
committed
Add EditTripPage and itinerary components; implement trip editing and itinerary management features
1 parent cb3ecad commit a68d7cf

11 files changed

Lines changed: 606 additions & 3 deletions

File tree

12 KB
Binary file not shown.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import {
2+
Box,
3+
Typography,
4+
Button,
5+
Paper,
6+
Stack
7+
} from '@mui/material';
8+
import { PostAdd as PostAddIcon, AutoAwesome as AutoAwesomeIcon } from '@mui/icons-material';
9+
10+
const EmptyItinerary = ({ onCreateEmpty, onUseTemplate }) => {
11+
return (
12+
<Paper
13+
sx={{
14+
p: 4,
15+
textAlign: 'center',
16+
bgcolor: 'background.default'
17+
}}
18+
>
19+
<Typography variant="h6" gutterBottom>
20+
Start Planning Your Trip
21+
</Typography>
22+
<Typography variant="body1" color="text.secondary" sx={{ mb: 4 }}>
23+
Create an itinerary to organize your daily activities, meals, and travel arrangements.
24+
</Typography>
25+
26+
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={2} justifyContent="center">
27+
<Button
28+
variant="outlined"
29+
startIcon={<PostAddIcon />}
30+
onClick={onCreateEmpty}
31+
>
32+
Start from Scratch
33+
</Button>
34+
<Button
35+
variant="contained"
36+
startIcon={<AutoAwesomeIcon />}
37+
onClick={onUseTemplate}
38+
>
39+
Use Template
40+
</Button>
41+
</Stack>
42+
</Paper>
43+
);
44+
};
45+
46+
export default EmptyItinerary;
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import { useState } from 'react';
2+
import {
3+
Box,
4+
Paper,
5+
Typography,
6+
IconButton,
7+
Button,
8+
Collapse,
9+
Divider
10+
} from '@mui/material';
11+
import {
12+
ExpandMore as ExpandMoreIcon,
13+
ExpandLess as ExpandLessIcon,
14+
Add as AddIcon
15+
} from '@mui/icons-material';
16+
import TimeSlot from './TimeSlot';
17+
import dayjs from 'dayjs';
18+
19+
const ItineraryDay = ({ date, slots = [], onAddSlot, onUpdateSlot, onDeleteSlot }) => {
20+
const [isExpanded, setIsExpanded] = useState(true);
21+
22+
const handleAddSlot = () => {
23+
const newSlot = {
24+
id: Date.now(),
25+
time: '12:00',
26+
activity: '',
27+
location: '',
28+
type: 'activity'
29+
};
30+
onAddSlot(date, newSlot);
31+
};
32+
33+
const sortedSlots = [...slots].sort((a, b) => a.time.localeCompare(b.time));
34+
35+
return (
36+
<Paper elevation={1} sx={{ mb: 2, overflow: 'hidden' }}>
37+
<Box
38+
sx={{
39+
p: 2,
40+
display: 'flex',
41+
alignItems: 'center',
42+
justifyContent: 'space-between',
43+
bgcolor: 'primary.light',
44+
cursor: 'pointer'
45+
}}
46+
onClick={() => setIsExpanded(!isExpanded)}
47+
>
48+
<Typography variant="h6">
49+
{dayjs(date).format('dddd, MMMM D')}
50+
</Typography>
51+
<IconButton size="small">
52+
{isExpanded ? <ExpandLessIcon /> : <ExpandMoreIcon />}
53+
</IconButton>
54+
</Box>
55+
56+
<Collapse in={isExpanded}>
57+
<Box sx={{ p: 2 }}>
58+
{sortedSlots.length === 0 ? (
59+
<Typography
60+
color="text.secondary"
61+
sx={{ textAlign: 'center', py: 2 }}
62+
>
63+
No activities planned for this day yet.
64+
</Typography>
65+
) : (
66+
sortedSlots.map((slot) => (
67+
<TimeSlot
68+
key={slot.id}
69+
slot={slot}
70+
onUpdate={(updatedSlot) => onUpdateSlot(date, updatedSlot)}
71+
onDelete={() => onDeleteSlot(date, slot.id)}
72+
/>
73+
))
74+
)}
75+
76+
<Button
77+
startIcon={<AddIcon />}
78+
onClick={handleAddSlot}
79+
sx={{ mt: 2 }}
80+
>
81+
Add Activity
82+
</Button>
83+
</Box>
84+
</Collapse>
85+
</Paper>
86+
);
87+
};
88+
89+
export default ItineraryDay;
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import { useState } from 'react';
2+
import {
3+
Box,
4+
TextField,
5+
IconButton,
6+
Card,
7+
CardContent,
8+
Typography,
9+
Stack,
10+
MenuItem
11+
} from '@mui/material';
12+
import {
13+
Edit as EditIcon,
14+
Delete as DeleteIcon,
15+
Save as SaveIcon,
16+
Cancel as CancelIcon,
17+
AccessTime as TimeIcon,
18+
Place as PlaceIcon
19+
} from '@mui/icons-material';
20+
21+
const TimeSlot = ({ slot, onUpdate, onDelete }) => {
22+
const [isEditing, setIsEditing] = useState(false);
23+
const [editedSlot, setEditedSlot] = useState(slot);
24+
25+
const handleSave = () => {
26+
onUpdate(editedSlot);
27+
setIsEditing(false);
28+
};
29+
30+
const handleCancel = () => {
31+
setEditedSlot(slot);
32+
setIsEditing(false);
33+
};
34+
35+
if (isEditing) {
36+
return (
37+
<Card variant="outlined" sx={{ mb: 1 }}>
38+
<CardContent>
39+
<Stack spacing={2}>
40+
<TextField
41+
fullWidth
42+
label="Time"
43+
type="time"
44+
value={editedSlot.time}
45+
onChange={(e) => setEditedSlot({ ...editedSlot, time: e.target.value })}
46+
InputLabelProps={{ shrink: true }}
47+
/>
48+
<TextField
49+
fullWidth
50+
label="Activity"
51+
value={editedSlot.activity}
52+
onChange={(e) => setEditedSlot({ ...editedSlot, activity: e.target.value })}
53+
/>
54+
<TextField
55+
fullWidth
56+
label="Location"
57+
value={editedSlot.location}
58+
onChange={(e) => setEditedSlot({ ...editedSlot, location: e.target.value })}
59+
/>
60+
<TextField
61+
select
62+
fullWidth
63+
label="Type"
64+
value={editedSlot.type}
65+
onChange={(e) => setEditedSlot({ ...editedSlot, type: e.target.value })}
66+
>
67+
<MenuItem value="activity">Activity</MenuItem>
68+
<MenuItem value="transportation">Transportation</MenuItem>
69+
<MenuItem value="accommodation">Accommodation</MenuItem>
70+
<MenuItem value="food">Food</MenuItem>
71+
</TextField>
72+
<Box sx={{ display: 'flex', justifyContent: 'flex-end', gap: 1 }}>
73+
<IconButton onClick={handleCancel} color="error" size="small">
74+
<CancelIcon />
75+
</IconButton>
76+
<IconButton onClick={handleSave} color="success" size="small">
77+
<SaveIcon />
78+
</IconButton>
79+
</Box>
80+
</Stack>
81+
</CardContent>
82+
</Card>
83+
);
84+
}
85+
86+
return (
87+
<Card variant="outlined" sx={{ mb: 1 }}>
88+
<CardContent>
89+
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
90+
<Stack spacing={1} sx={{ flex: 1 }}>
91+
<Typography variant="subtitle1" component="div">
92+
{slot.activity}
93+
</Typography>
94+
<Stack direction="row" spacing={2} color="text.secondary">
95+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
96+
<TimeIcon fontSize="small" />
97+
<Typography variant="body2">{slot.time}</Typography>
98+
</Box>
99+
{slot.location && (
100+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
101+
<PlaceIcon fontSize="small" />
102+
<Typography variant="body2">{slot.location}</Typography>
103+
</Box>
104+
)}
105+
</Stack>
106+
</Stack>
107+
<Box>
108+
<IconButton onClick={() => setIsEditing(true)} size="small">
109+
<EditIcon />
110+
</IconButton>
111+
<IconButton onClick={() => onDelete(slot.id)} color="error" size="small">
112+
<DeleteIcon />
113+
</IconButton>
114+
</Box>
115+
</Box>
116+
</CardContent>
117+
</Card>
118+
);
119+
};
120+
121+
export default TimeSlot;

0 commit comments

Comments
 (0)