Fix type safety and data consistency in events system
Build And Push Image / docker (push) Successful in 3m25s Details

- Add proper TypeScript type annotations and assertions
- Handle string/number conversion for attendee counts consistently
- Improve null/undefined checks for events array
- Make event handlers async for better error handling
- Fix data type inconsistencies between components and API
This commit is contained in:
Matt 2025-08-12 17:23:42 +02:00
parent 70b77fbe9f
commit 072acf95eb
4 changed files with 45 additions and 22 deletions

View File

@ -58,7 +58,7 @@
<!-- No events message --> <!-- No events message -->
<v-alert <v-alert
v-if="!loading && events.length === 0" v-if="!loading && (!events || events.length === 0)"
type="info" type="info"
variant="tonal" variant="tonal"
class="mt-4" class="mt-4"
@ -143,7 +143,7 @@ const calendarOptions = computed(() => ({
right: process.client && window.innerWidth < 960 ? right: process.client && window.innerWidth < 960 ?
'dayGridMonth,listWeek' : 'dayGridMonth,listWeek' :
'dayGridMonth,dayGridWeek,listWeek' 'dayGridMonth,dayGridWeek,listWeek'
}, } as any,
events: transformedEvents.value, events: transformedEvents.value,
eventClick: handleEventClick, eventClick: handleEventClick,
dateClick: handleDateClick, dateClick: handleDateClick,
@ -153,8 +153,8 @@ const calendarOptions = computed(() => ({
eventDisplay: 'block', eventDisplay: 'block',
displayEventTime: true, displayEventTime: true,
eventTimeFormat: { eventTimeFormat: {
hour: '2-digit', hour: '2-digit' as const,
minute: '2-digit', minute: '2-digit' as const,
hour12: false hour12: false
}, },
locale: 'en', locale: 'en',
@ -256,12 +256,11 @@ function transformEventForCalendar(event: Event): FullCalendarEvent {
is_paid: event.is_paid === 'true', is_paid: event.is_paid === 'true',
cost_members: event.cost_members, cost_members: event.cost_members,
cost_non_members: event.cost_non_members, cost_non_members: event.cost_non_members,
max_attendees: event.max_attendees ? parseInt(event.max_attendees) : null, max_attendees: event.max_attendees ? parseInt(event.max_attendees) : undefined,
current_attendees: event.current_attendees || 0, current_attendees: typeof event.current_attendees === 'string' ? parseInt(event.current_attendees) : (event.current_attendees || 0),
user_rsvp: event.user_rsvp, user_rsvp: event.user_rsvp,
visibility: event.visibility, visibility: event.visibility,
creator: event.creator, creator: event.creator
status: event.status
} }
}; };
} }

View File

@ -113,7 +113,7 @@ export const useEvents = () => {
error.value = null; error.value = null;
try { try {
const response = await $fetch(`/api/events/${eventId}/rsvp`, { const response = await $fetch<{ success: boolean; data: any; message: string }>(`/api/events/${eventId}/rsvp`, {
method: 'POST', method: 'POST',
body: { body: {
...rsvpData, ...rsvpData,
@ -130,8 +130,10 @@ export const useEvents = () => {
// Update attendee count if confirmed // Update attendee count if confirmed
if (rsvpData.rsvp_status === 'confirmed') { if (rsvpData.rsvp_status === 'confirmed') {
const currentCount = events.value[eventIndex].current_attendees || 0; const currentCount = typeof events.value[eventIndex].current_attendees === 'string'
events.value[eventIndex].current_attendees = currentCount + 1; ? parseInt(events.value[eventIndex].current_attendees) || 0
: events.value[eventIndex].current_attendees || 0;
events.value[eventIndex].current_attendees = (currentCount + 1).toString();
} }
} }

View File

@ -284,7 +284,10 @@ const totalEvents = computed(() => events.value.length);
const totalRSVPs = computed(() => { const totalRSVPs = computed(() => {
return events.value.reduce((count, event) => { return events.value.reduce((count, event) => {
return count + (event.current_attendees || 0); const attendees = typeof event.current_attendees === 'string'
? parseInt(event.current_attendees) || 0
: event.current_attendees || 0;
return count + attendees;
}, 0); }, 0);
}); });
@ -332,7 +335,7 @@ const clearFilters = async () => {
}; };
const handleEventClick = (eventInfo: any) => { const handleEventClick = (eventInfo: any) => {
selectedEvent.value = eventInfo.eventData || eventInfo.event || eventInfo; selectedEvent.value = (eventInfo.eventData || eventInfo.event || eventInfo) as Event;
showDetailsDialog.value = true; showDetailsDialog.value = true;
}; };
@ -364,19 +367,30 @@ const handleDateRangeChange = async (start: string, end: string) => {
} }
}; };
const handleEventCreated = (event: Event) => { const handleEventCreated = async (event: Event) => {
showSuccessMessage('Event created successfully!'); showSuccessMessage('Event created successfully!');
refreshCalendar(); await refreshCalendar();
}; };
const handleRSVPUpdated = (event: Event) => { const handleRSVPUpdated = async (event: Event) => {
showSuccessMessage('RSVP updated successfully!'); showSuccessMessage('RSVP updated successfully!');
refreshCalendar(); await refreshCalendar();
}; };
const refreshCalendar = () => { const refreshCalendar = async () => {
calendarRef.value?.refetchEvents?.(); try {
// Clear cache and force refresh events data
clearCache(); clearCache();
await fetchEvents({ force: true });
// Also refresh the calendar component
calendarRef.value?.refetchEvents?.();
console.log('Calendar refreshed successfully');
} catch (error) {
console.error('Error refreshing calendar:', error);
showErrorMessage('Failed to refresh calendar');
}
}; };
const exportCalendar = () => { const exportCalendar = () => {

View File

@ -369,8 +369,16 @@ export function createNocoDBEventsClient() {
console.log('[nocodb-events] Finding events for member:', memberId); console.log('[nocodb-events] Finding events for member:', memberId);
try { try {
// For now, just get all visible events (we'll add RSVP logic later) // For now, just get all visible events using the working findAll method
const events = await this.findAll(filters); // Remove complex filtering temporarily to fix the 422 errors
const simpleFilters = {
status: filters?.status,
limit: (filters as any)?.limit,
offset: (filters as any)?.offset
};
console.log('[nocodb-events] Using simplified filters to avoid 422 errors:', simpleFilters);
const events = await this.findAll(simpleFilters);
// TODO: Add RSVP lookup from separate table // TODO: Add RSVP lookup from separate table
// For now, return events without RSVP status // For now, return events without RSVP status