Compare commits

...

27 Commits

Author SHA1 Message Date
Ryderjj89
53340fc210 selected items fix 2025-10-24 18:41:48 -04:00
Ryderjj89
bd8f0fa2cd added selected items to rsvp form 2025-10-24 18:27:23 -04:00
Ryderjj89
ecd53fbadb feat: improve guest selection in rsvp form 2025-06-24 17:17:39 -04:00
Joshua Ryder
630cf7be41 Merge pull request #21 from Ryderjj89/dev
Fixing back to events button on thank you page
2025-06-04 19:15:11 -04:00
Ryderjj89
9760eccf4c Fixing back to events button on thank you page 2025-06-04 19:14:02 -04:00
Ryderjj89
54d0e8aab4 Fixing buttons on the Thank you page 2025-06-04 19:07:52 -04:00
Joshua Ryder
5d67d42205 Merge pull request #20 from Ryderjj89/dev
Fixing buttons on the Thank you page
2025-06-04 19:07:34 -04:00
Ryderjj89
becc530c18 Spacing issue with guest names 2025-06-04 18:53:27 -04:00
Joshua Ryder
2587a5663b Merge pull request #19 from Ryderjj89/dev
Spacing issue with guest names
2025-06-04 18:53:10 -04:00
Ryderjj89
3af12b431b Rearranging event details & more width for view/manage pages 2025-06-04 18:45:50 -04:00
Joshua Ryder
f366a9e4f3 Merge pull request #18 from Ryderjj89/dev
Rearranging event details & more width for view/manage pages
2025-06-04 18:45:28 -04:00
Joshua Ryder
044b7f2cde Merge pull request #17 from Ryderjj89/dev
Button spacing and formatting for rsvp form
2025-06-04 18:35:40 -04:00
Joshua Ryder
88c06e06f6 Merge pull request #16 from Ryderjj89/dev
Button spacing and formatting for rsvp form
2025-06-04 18:29:45 -04:00
Joshua Ryder
e0a3f3f889 Merge pull request #15 from Ryderjj89/dev
Back to Events button and RSVP form details
2025-06-04 18:19:25 -04:00
Joshua Ryder
aa69252657 Merge pull request #14 from Ryderjj89/dev
Changing card actions to grid
2025-06-04 18:06:55 -04:00
Joshua Ryder
01939f5881 Merge pull request #13 from Ryderjj89/dev
Flex spacing changes on the event cards for smaller screens
2025-06-04 18:02:07 -04:00
Joshua Ryder
2b6f4cfd2f Merge pull request #12 from Ryderjj89/dev
Padding and spacing fixes
2025-06-04 17:54:30 -04:00
Joshua Ryder
16db01292c Merge pull request #11 from Ryderjj89/dev
Updated docker compose for new volume mounting for the database
2025-06-04 17:41:50 -04:00
Joshua Ryder
52cacc8646 Merge pull request #10 from Ryderjj89/dev
Rearranged how the sqlite database is stored for better volume mounting
2025-06-04 17:39:04 -04:00
Joshua Ryder
4546185a89 Merge pull request #9 from Ryderjj89/dev
Fixing snackbar issue
2025-06-04 17:26:19 -04:00
Joshua Ryder
4722aeeb22 Merge pull request #8 from Ryderjj89/dev
Fixing snackbar issue
2025-06-04 17:21:20 -04:00
Joshua Ryder
4e66ce876d Merge pull request #7 from Ryderjj89/dev
Updating frontend to have a copy link to RSVP and improvements to the…
2025-06-04 17:16:47 -04:00
Joshua Ryder
482718050d Merge pull request #6 from Ryderjj89/dev
Event conclusion message, calendar invites, & more attendee management
2025-06-03 17:22:01 -04:00
Joshua Ryder
b5ecb32893 Merge pull request #5 from Ryderjj89/dev
Update README.md
2025-05-25 19:51:58 -04:00
Joshua Ryder
4d4920a751 Update README.md
More info about email notifications
2025-05-25 19:49:03 -04:00
Joshua Ryder
3521643196 Update README.md 2025-05-24 17:32:04 -04:00
Joshua Ryder
64db6c4c08 Merge pull request #4 from Ryderjj89/dev
Update README.md
2025-05-16 21:59:22 -04:00
3 changed files with 164 additions and 128 deletions

View File

@@ -729,7 +729,7 @@ const EventAdmin: React.FC = () => {
if (loading) {
return (
<Container maxWidth="lg">
<Container maxWidth="xl">
<Typography>Loading...</Typography>
</Container>
);
@@ -737,7 +737,7 @@ const EventAdmin: React.FC = () => {
if (error || !event) {
return (
<Container maxWidth="lg">
<Container maxWidth="xl">
<Typography color="error">{error || 'Event not found'}</Typography>
</Container>
);
@@ -763,7 +763,7 @@ const EventAdmin: React.FC = () => {
}}
>
<Box sx={{ py: 4 }}>
<Container maxWidth="lg">
<Container maxWidth="xl">
<Paper elevation={3} sx={{ p: { xs: 2, sm: 4 }, mt: 4 }}>
<Box sx={{ mb: 4 }}>
<Typography variant="h4" component="h2" color="primary" gutterBottom>
@@ -826,16 +826,16 @@ const EventAdmin: React.FC = () => {
<Typography variant="subtitle1" gutterBottom>
<strong>Info:</strong> {event.description || 'None'}
</Typography>
<Typography variant="subtitle1" gutterBottom>
<strong>Location:</strong> {event.location}
</Typography>
<Typography variant="subtitle1" gutterBottom>
<strong>Date:</strong> {new Date(event.date).toLocaleString()}
</Typography>
<Typography variant="subtitle1" gutterBottom>
<strong>Location:</strong> {event.location}
</Typography>
{event.rsvp_cutoff_date && (
<Typography variant="subtitle1" gutterBottom>
<strong>RSVP cut-off date:</strong> {new Date(event.rsvp_cutoff_date).toLocaleString()}
</Typography>
<Typography variant="subtitle1" gutterBottom>
<strong>RSVP cut-off date:</strong> {new Date(event.rsvp_cutoff_date).toLocaleString()}
</Typography>
)}
</Box>
@@ -945,9 +945,9 @@ const EventAdmin: React.FC = () => {
<TableCell>
{rsvp.bringing_guests === 'yes' ?
`${rsvp.guest_count || 0} (${Array.isArray(rsvp.guest_names) ?
rsvp.guest_names.join(', ') :
rsvp.guest_names.map(name => name.trim()).join(', ') :
typeof rsvp.guest_names === 'string' ?
rsvp.guest_names.replace(/\s+/g, ', ') :
rsvp.guest_names.replace(/\s+/g, ', ').trim() :
'No names provided'})` :
'No'
}

View File

@@ -126,7 +126,7 @@ const EventView: React.FC = () => {
if (loading) {
return (
<Container maxWidth="lg">
<Container maxWidth="xl">
<Typography>Loading...</Typography>
</Container>
);
@@ -134,7 +134,7 @@ const EventView: React.FC = () => {
if (error || !event) {
return (
<Container maxWidth="lg">
<Container maxWidth="xl">
<Typography color="error">{error || 'Event not found'}</Typography>
</Container>
);
@@ -160,7 +160,7 @@ const EventView: React.FC = () => {
}}
>
<Box sx={{ py: 4 }}>
<Container maxWidth="lg">
<Container maxWidth="xl">
<Paper elevation={3} sx={{ p: { xs: 2, sm: 4 }, mt: 4 }}>
<Box sx={{ mb: 4 }}>
<Typography variant="h4" component="h2" color="primary" gutterBottom>
@@ -189,16 +189,16 @@ const EventView: React.FC = () => {
<Typography variant="subtitle1" gutterBottom>
<strong>Info:</strong> {event.description || 'None'}
</Typography>
<Typography variant="subtitle1" gutterBottom>
<strong>Location:</strong> {event.location}
</Typography>
<Typography variant="subtitle1" gutterBottom>
<strong>Date:</strong> {new Date(event.date).toLocaleString()}
</Typography>
<Typography variant="subtitle1" gutterBottom>
<strong>Location:</strong> {event.location}
</Typography>
{event.rsvp_cutoff_date && (
<Typography variant="subtitle1" gutterBottom>
<strong>RSVP cut-off date:</strong> {new Date(event.rsvp_cutoff_date).toLocaleString()}
</Typography>
<Typography variant="subtitle1" gutterBottom>
<strong>RSVP cut-off date:</strong> {new Date(event.rsvp_cutoff_date).toLocaleString()}
</Typography>
)}
</Box>
@@ -293,9 +293,9 @@ const EventView: React.FC = () => {
<TableCell>
{rsvp.bringing_guests === 'yes' ?
`${rsvp.guest_count} (${Array.isArray(rsvp.guest_names) ?
rsvp.guest_names.join(', ') :
rsvp.guest_names.map(name => name.trim()).join(', ') :
typeof rsvp.guest_names === 'string' ?
rsvp.guest_names.replace(/\s+/g, ', ') :
rsvp.guest_names.replace(/\s+/g, ', ').trim() :
'No names provided'})` :
'No'
}
@@ -350,4 +350,4 @@ const EventView: React.FC = () => {
);
};
export default EventView;
export default EventView;

View File

@@ -399,19 +399,19 @@ const RSVPForm: React.FC = () => {
</Typography>
<Box sx={{ display: 'flex', gap: 2, justifyContent: 'center' }}>
<Button
variant="contained"
variant="outlined"
color="primary"
onClick={() => navigate('/')}
>
Back to Events
</Button>
<Button
variant="outlined"
variant="contained"
color="primary"
onClick={() => navigate('/')}
onClick={() => navigate(`/view/events/${slug}`)}
sx={{ flexGrow: 1 }}
>
Back to Events
View RSVPs
</Button>
</Box>
</Paper>
@@ -538,51 +538,69 @@ const RSVPForm: React.FC = () => {
{formData.bringing_guests === 'yes' && (
<>
<TextField
label="Number of Guests"
name="guest_count"
type="number"
value={formData.guest_count}
onChange={(e) => {
const value = parseInt(e.target.value);
if (isNaN(value)) return;
// Check if there's a maximum guest limit
const maxGuests = event?.max_guests_per_rsvp;
let newCount = value;
// If max_guests_per_rsvp is set and not -1 (unlimited), enforce the limit
if (maxGuests !== undefined && maxGuests !== -1 && value > maxGuests) {
newCount = maxGuests;
}
// Ensure count is at least 1
if (newCount < 1) newCount = 1;
setFormData(prev => ({
...prev,
guest_count: newCount,
guest_names: Array(newCount).fill('').map((_, i) => prev.guest_names[i] || '')
}));
}}
fullWidth
variant="outlined"
required
inputProps={{
min: 1,
max: event?.max_guests_per_rsvp === -1 ? undefined : event?.max_guests_per_rsvp
}}
error={formData.guest_count < 1}
helperText={
formData.guest_count < 1
? "Number of guests must be at least 1"
: event?.max_guests_per_rsvp === 0
? "No additional guests allowed for this event"
: event?.max_guests_per_rsvp === -1
? "No limit on number of guests"
: `Maximum ${event?.max_guests_per_rsvp} additional guests allowed`
}
/>
{/* Render dropdown if there's a max limit, otherwise render number input */}
{event?.max_guests_per_rsvp !== undefined && event?.max_guests_per_rsvp !== -1 ? (
<FormControl fullWidth required>
<InputLabel>Number of Guests</InputLabel>
<Select
name="guest_count"
value={formData.guest_count.toString()}
onChange={(e) => {
const value = parseInt(e.target.value);
setFormData(prev => ({
...prev,
guest_count: value,
guest_names: Array(value).fill('').map((_, i) => prev.guest_names[i] || '')
}));
}}
label="Number of Guests"
required
>
{Array.from({ length: event.max_guests_per_rsvp }, (_, i) => i + 1).map((num) => (
<MenuItem key={num} value={num.toString()}>
{num}
</MenuItem>
))}
</Select>
<Typography variant="caption" color="text.secondary" sx={{ mt: 0.5, ml: 1.75 }}>
Maximum {event.max_guests_per_rsvp} additional guests allowed
</Typography>
</FormControl>
) : (
<TextField
label="Number of Guests"
name="guest_count"
value={formData.guest_count}
onChange={(e) => {
const value = e.target.value;
// Only allow numbers
if (!/^\d*$/.test(value)) return;
const numValue = parseInt(value) || 0;
// Ensure count is at least 1 when not empty
const newCount = value === '' ? 0 : Math.max(1, numValue);
setFormData(prev => ({
...prev,
guest_count: newCount,
guest_names: Array(newCount).fill('').map((_, i) => prev.guest_names[i] || '')
}));
}}
fullWidth
variant="outlined"
required
inputProps={{
inputMode: 'numeric',
pattern: '[0-9]*'
}}
error={formData.guest_count < 1}
helperText={
formData.guest_count < 1
? "Number of guests must be at least 1"
: "No limit on number of guests"
}
/>
)}
{Array.from({ length: formData.guest_count }).map((_, index) => (
<TextField
@@ -599,65 +617,83 @@ const RSVPForm: React.FC = () => {
)}
{neededItems.length > 0 && (
<FormControl fullWidth>
<InputLabel>What items are you bringing?</InputLabel>
<Select
multiple
name="items_bringing"
value={formData.items_bringing}
onChange={handleItemsChange}
input={<OutlinedInput label="What items are you bringing?" />}
renderValue={(selected) => (
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5 }}>
{selected.map((value) => (
<Chip key={value} label={value} />
<>
{claimedItems.length > 0 && (
<Box sx={{ mb: 2 }}>
<Typography variant="subtitle2" gutterBottom sx={{ fontWeight: 500 }}>
Items already being brought:
</Typography>
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1 }}>
{claimedItems.map((item) => (
<Chip
key={item}
label={item}
color="success"
/>
))}
</Box>
)}
open={isItemsSelectOpen} // Control open state
onOpen={() => setIsItemsSelectOpen(true)} // Set open when opened
onClose={() => setIsItemsSelectOpen(false)} // Set closed when closed
MenuProps={{
PaperProps: {
sx: {
maxHeight: 300, // Limit height of the dropdown
overflowY: 'auto',
},
},
MenuListProps: {
sx: {
},
},
}}
>
{neededItems.map((item) => (
<MenuItem key={item} value={item}>
<Checkbox checked={formData.items_bringing.includes(item)} />
<ListItemText primary={item} />
</MenuItem>
))}
<Box sx={{
position: 'sticky',
bottom: 0,
left: 0,
right: 0,
backgroundColor: 'background.paper',
padding: 1,
zIndex: 1,
borderTop: '1px solid',
borderColor: 'divider',
textAlign: 'center',
}}>
<Button
variant="contained"
onClick={() => setIsItemsSelectOpen(false)}
fullWidth
>
Done
</Button>
</Box>
</Select>
</FormControl>
)}
<FormControl fullWidth>
<InputLabel>What items are you bringing?</InputLabel>
<Select
multiple
name="items_bringing"
value={formData.items_bringing}
onChange={handleItemsChange}
input={<OutlinedInput label="What items are you bringing?" />}
renderValue={(selected) => (
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5 }}>
{selected.map((value) => (
<Chip key={value} label={value} />
))}
</Box>
)}
open={isItemsSelectOpen} // Control open state
onOpen={() => setIsItemsSelectOpen(true)} // Set open when opened
onClose={() => setIsItemsSelectOpen(false)} // Set closed when closed
MenuProps={{
PaperProps: {
sx: {
maxHeight: 300, // Limit height of the dropdown
overflowY: 'auto',
},
},
MenuListProps: {
sx: {
},
},
}}
>
{neededItems.map((item) => (
<MenuItem key={item} value={item}>
<Checkbox checked={formData.items_bringing.includes(item)} />
<ListItemText primary={item} />
</MenuItem>
))}
<Box sx={{
position: 'sticky',
bottom: 0,
left: 0,
right: 0,
backgroundColor: 'background.paper',
padding: 1,
zIndex: 1,
borderTop: '1px solid',
borderColor: 'divider',
textAlign: 'center',
}}>
<Button
variant="contained"
onClick={() => setIsItemsSelectOpen(false)}
fullWidth
>
Done
</Button>
</Box>
</Select>
</FormControl>
</>
)}
<TextField