Merge pull request #1 from Ryderjj89/dev
Added other items to display on the view rsvps & manage rsvps pages
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
container_name: rsvp_manager
|
container_name: rsvp_manager-dev
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ const EventAdmin: React.FC = () => {
|
|||||||
const [rsvps, setRsvps] = useState<RSVP[]>([]);
|
const [rsvps, setRsvps] = useState<RSVP[]>([]);
|
||||||
const [neededItems, setNeededItems] = useState<string[]>([]);
|
const [neededItems, setNeededItems] = useState<string[]>([]);
|
||||||
const [claimedItems, setClaimedItems] = useState<string[]>([]);
|
const [claimedItems, setClaimedItems] = useState<string[]>([]);
|
||||||
|
const [otherItems, setOtherItems] = useState<string[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
||||||
@@ -149,6 +150,7 @@ const EventAdmin: React.FC = () => {
|
|||||||
|
|
||||||
// Get all claimed items from existing RSVPs
|
// Get all claimed items from existing RSVPs
|
||||||
const claimed = new Set<string>();
|
const claimed = new Set<string>();
|
||||||
|
const otherItemsSet = new Set<string>();
|
||||||
const processedRsvps = rsvpsResponse.data.map((rsvp: RSVP) => {
|
const processedRsvps = rsvpsResponse.data.map((rsvp: RSVP) => {
|
||||||
let itemsBringing: string[] = [];
|
let itemsBringing: string[] = [];
|
||||||
try {
|
try {
|
||||||
@@ -169,6 +171,11 @@ const EventAdmin: React.FC = () => {
|
|||||||
console.error('Error processing items for RSVP:', e);
|
console.error('Error processing items for RSVP:', e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add non-empty other_items to set
|
||||||
|
if (typeof rsvp.other_items === 'string' && rsvp.other_items.trim() !== '') {
|
||||||
|
otherItemsSet.add(rsvp.other_items.trim());
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...rsvp,
|
...rsvp,
|
||||||
items_bringing: itemsBringing
|
items_bringing: itemsBringing
|
||||||
@@ -178,8 +185,8 @@ const EventAdmin: React.FC = () => {
|
|||||||
// Update state with processed data
|
// Update state with processed data
|
||||||
setRsvps(processedRsvps);
|
setRsvps(processedRsvps);
|
||||||
setClaimedItems(Array.from(claimed));
|
setClaimedItems(Array.from(claimed));
|
||||||
// Filter needed items to only show unclaimed ones
|
|
||||||
setNeededItems(items.filter(item => !claimed.has(item)));
|
setNeededItems(items.filter(item => !claimed.has(item)));
|
||||||
|
setOtherItems(Array.from(otherItemsSet));
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load event data:', error);
|
console.error('Failed to load event data:', error);
|
||||||
@@ -208,6 +215,7 @@ const EventAdmin: React.FC = () => {
|
|||||||
setRsvps(rsvps.filter((r: RSVP) => r.id !== rsvpToDelete.id));
|
setRsvps(rsvps.filter((r: RSVP) => r.id !== rsvpToDelete.id));
|
||||||
setDeleteDialogOpen(false);
|
setDeleteDialogOpen(false);
|
||||||
setRsvpToDelete(null);
|
setRsvpToDelete(null);
|
||||||
|
fetchEventAndRsvps();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setError('Failed to delete RSVP');
|
setError('Failed to delete RSVP');
|
||||||
}
|
}
|
||||||
@@ -221,6 +229,12 @@ const EventAdmin: React.FC = () => {
|
|||||||
processedGuestNames = rsvp.guest_names.split(',').map(name => name.trim());
|
processedGuestNames = rsvp.guest_names.split(',').map(name => name.trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert stored comma-separated other_items to multiline for textarea
|
||||||
|
let otherItemsMultiline = rsvp.other_items || '';
|
||||||
|
if (otherItemsMultiline.includes(',')) {
|
||||||
|
otherItemsMultiline = otherItemsMultiline.split(',').map(s => s.trim()).join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
setRsvpToEdit(rsvp);
|
setRsvpToEdit(rsvp);
|
||||||
setEditForm({
|
setEditForm({
|
||||||
name: rsvp.name,
|
name: rsvp.name,
|
||||||
@@ -231,7 +245,7 @@ const EventAdmin: React.FC = () => {
|
|||||||
items_bringing: Array.isArray(rsvp.items_bringing) ? rsvp.items_bringing :
|
items_bringing: Array.isArray(rsvp.items_bringing) ? rsvp.items_bringing :
|
||||||
typeof rsvp.items_bringing === 'string' ?
|
typeof rsvp.items_bringing === 'string' ?
|
||||||
(rsvp.items_bringing ? JSON.parse(rsvp.items_bringing) : []) : [],
|
(rsvp.items_bringing ? JSON.parse(rsvp.items_bringing) : []) : [],
|
||||||
other_items: rsvp.other_items || ''
|
other_items: otherItemsMultiline
|
||||||
});
|
});
|
||||||
setEditDialogOpen(true);
|
setEditDialogOpen(true);
|
||||||
};
|
};
|
||||||
@@ -327,6 +341,13 @@ const EventAdmin: React.FC = () => {
|
|||||||
.map(name => name.trim())
|
.map(name => name.trim())
|
||||||
.filter(name => name.length > 0);
|
.filter(name => name.length > 0);
|
||||||
|
|
||||||
|
// Split other_items on newlines and commas, trim, filter, join with commas
|
||||||
|
const splitOtherItems = editForm.other_items
|
||||||
|
.split(/\r?\n|,/)
|
||||||
|
.map(s => s.trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(', ');
|
||||||
|
|
||||||
// Prepare submission data in the exact format the backend expects
|
// Prepare submission data in the exact format the backend expects
|
||||||
const submissionData = {
|
const submissionData = {
|
||||||
name: editForm.name,
|
name: editForm.name,
|
||||||
@@ -335,7 +356,7 @@ const EventAdmin: React.FC = () => {
|
|||||||
guest_count: editForm.bringing_guests === 'yes' ? Math.max(1, parseInt(editForm.guest_count.toString(), 10)) : 0,
|
guest_count: editForm.bringing_guests === 'yes' ? Math.max(1, parseInt(editForm.guest_count.toString(), 10)) : 0,
|
||||||
guest_names: filteredGuestNames,
|
guest_names: filteredGuestNames,
|
||||||
items_bringing: JSON.stringify(editForm.items_bringing),
|
items_bringing: JSON.stringify(editForm.items_bringing),
|
||||||
other_items: editForm.other_items,
|
other_items: splitOtherItems,
|
||||||
event_id: event.id
|
event_id: event.id
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -370,7 +391,7 @@ const EventAdmin: React.FC = () => {
|
|||||||
...submissionData,
|
...submissionData,
|
||||||
guest_names: filteredGuestNames,
|
guest_names: filteredGuestNames,
|
||||||
items_bringing: editForm.items_bringing,
|
items_bringing: editForm.items_bringing,
|
||||||
other_items: editForm.other_items
|
other_items: splitOtherItems
|
||||||
};
|
};
|
||||||
|
|
||||||
// Update the local state
|
// Update the local state
|
||||||
@@ -423,6 +444,7 @@ const EventAdmin: React.FC = () => {
|
|||||||
setClaimedItems(claimedArray);
|
setClaimedItems(claimedArray);
|
||||||
setEditDialogOpen(false);
|
setEditDialogOpen(false);
|
||||||
setRsvpToEdit(null);
|
setRsvpToEdit(null);
|
||||||
|
fetchEventAndRsvps();
|
||||||
|
|
||||||
// Verify the update was successful but don't throw error if verification response is empty
|
// Verify the update was successful but don't throw error if verification response is empty
|
||||||
try {
|
try {
|
||||||
@@ -761,6 +783,17 @@ const EventAdmin: React.FC = () => {
|
|||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
{/* Other Items Section */}
|
||||||
|
<Box>
|
||||||
|
<Typography variant="subtitle1" gutterBottom>
|
||||||
|
Other Items:
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" color="text.secondary">
|
||||||
|
{otherItems.length > 0
|
||||||
|
? otherItems.join(', ')
|
||||||
|
: 'No other items have been brought'}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ interface RSVP {
|
|||||||
guest_count: number;
|
guest_count: number;
|
||||||
guest_names: string[] | string;
|
guest_names: string[] | string;
|
||||||
items_bringing: string[] | string;
|
items_bringing: string[] | string;
|
||||||
other_items?: string[];
|
other_items?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Event {
|
interface Event {
|
||||||
@@ -46,6 +46,7 @@ const EventView: React.FC = () => {
|
|||||||
const [rsvps, setRsvps] = useState<RSVP[]>([]);
|
const [rsvps, setRsvps] = useState<RSVP[]>([]);
|
||||||
const [neededItems, setNeededItems] = useState<string[]>([]);
|
const [neededItems, setNeededItems] = useState<string[]>([]);
|
||||||
const [claimedItems, setClaimedItems] = useState<string[]>([]);
|
const [claimedItems, setClaimedItems] = useState<string[]>([]);
|
||||||
|
const [otherItems, setOtherItems] = useState<string[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
@@ -79,6 +80,7 @@ const EventView: React.FC = () => {
|
|||||||
|
|
||||||
// Get all claimed items from existing RSVPs
|
// Get all claimed items from existing RSVPs
|
||||||
const claimed = new Set<string>();
|
const claimed = new Set<string>();
|
||||||
|
const otherItemsSet = new Set<string>();
|
||||||
const processedRsvps = rsvpsResponse.data.map((rsvp: RSVP) => {
|
const processedRsvps = rsvpsResponse.data.map((rsvp: RSVP) => {
|
||||||
let itemsBringing: string[] = [];
|
let itemsBringing: string[] = [];
|
||||||
try {
|
try {
|
||||||
@@ -98,6 +100,10 @@ const EventView: React.FC = () => {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Error processing items for RSVP:', e);
|
console.error('Error processing items for RSVP:', e);
|
||||||
}
|
}
|
||||||
|
// Add non-empty other_items to set
|
||||||
|
if (typeof rsvp.other_items === 'string' && rsvp.other_items.trim() !== '') {
|
||||||
|
otherItemsSet.add(rsvp.other_items.trim());
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...rsvp,
|
...rsvp,
|
||||||
@@ -110,6 +116,7 @@ const EventView: React.FC = () => {
|
|||||||
setClaimedItems(Array.from(claimed));
|
setClaimedItems(Array.from(claimed));
|
||||||
// Filter needed items to only show unclaimed ones
|
// Filter needed items to only show unclaimed ones
|
||||||
setNeededItems(items.filter(item => !claimed.has(item)));
|
setNeededItems(items.filter(item => !claimed.has(item)));
|
||||||
|
setOtherItems(Array.from(otherItemsSet));
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setError('Failed to load event data');
|
setError('Failed to load event data');
|
||||||
@@ -239,6 +246,16 @@ const EventView: React.FC = () => {
|
|||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Typography variant="subtitle1" gutterBottom>
|
||||||
|
Other Items:
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" color="text.secondary">
|
||||||
|
{otherItems.length > 0
|
||||||
|
? otherItems.join(', ')
|
||||||
|
: 'No other items have been brought'}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
|||||||
@@ -252,9 +252,15 @@ const RSVPForm: React.FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const splitOtherItems = formData.other_items
|
||||||
|
.split(/\r?\n|,/)
|
||||||
|
.map(s => s.trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(', ');
|
||||||
const submissionData = {
|
const submissionData = {
|
||||||
...formData,
|
...formData,
|
||||||
items_bringing: formData.items_bringing
|
items_bringing: formData.items_bringing,
|
||||||
|
other_items: splitOtherItems
|
||||||
};
|
};
|
||||||
const response = await axios.post(`/api/events/${slug}/rsvp`, submissionData);
|
const response = await axios.post(`/api/events/${slug}/rsvp`, submissionData);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user