Add guest support for events and RSVP system
Build And Push Image / docker (push) Successful in 3m52s
Details
Build And Push Image / docker (push) Successful in 3m52s
Details
- Add guest settings to event creation with configurable max guests per person - Implement guest selection in RSVP form when guests are permitted - Update API endpoints to handle guest count in RSVP requests - Extend event and RSVP types to support guest-related fields
This commit is contained in:
parent
234c939dcd
commit
62fb84d25e
|
|
@ -143,8 +143,33 @@
|
||||||
/>
|
/>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|
||||||
<!-- Payment Settings -->
|
<!-- Guest Settings -->
|
||||||
<v-col cols="12" md="6">
|
<v-col cols="12" md="6">
|
||||||
|
<v-switch
|
||||||
|
v-model="allowGuests"
|
||||||
|
label="Allow Guests"
|
||||||
|
color="primary"
|
||||||
|
inset
|
||||||
|
hint="Members can bring additional guests"
|
||||||
|
persistent-hint
|
||||||
|
/>
|
||||||
|
</v-col>
|
||||||
|
|
||||||
|
<!-- Max Guests Per Person (shown when guests allowed) -->
|
||||||
|
<v-col v-if="allowGuests" cols="12" md="6">
|
||||||
|
<v-text-field
|
||||||
|
v-model="maxGuestsPerPerson"
|
||||||
|
label="Max Guests Per Person"
|
||||||
|
type="number"
|
||||||
|
variant="outlined"
|
||||||
|
:rules="allowGuests ? [v => v && parseInt(v) > 0 || 'Must allow at least 1 guest'] : []"
|
||||||
|
hint="Maximum additional guests each member can bring"
|
||||||
|
persistent-hint
|
||||||
|
/>
|
||||||
|
</v-col>
|
||||||
|
|
||||||
|
<!-- Payment Settings -->
|
||||||
|
<v-col cols="12" :md="allowGuests ? 6 : 6">
|
||||||
<v-switch
|
<v-switch
|
||||||
v-model="isPaidEvent"
|
v-model="isPaidEvent"
|
||||||
label="Paid Event"
|
label="Paid Event"
|
||||||
|
|
@ -313,9 +338,15 @@ const eventData = reactive<EventCreateRequest>({
|
||||||
cost_non_members: '',
|
cost_non_members: '',
|
||||||
member_pricing_enabled: 'true',
|
member_pricing_enabled: 'true',
|
||||||
visibility: 'public',
|
visibility: 'public',
|
||||||
status: 'active'
|
status: 'active',
|
||||||
|
guests_permitted: 'false',
|
||||||
|
max_guests_permitted: '0'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Guest settings
|
||||||
|
const allowGuests = ref(false);
|
||||||
|
const maxGuestsPerPerson = ref(1);
|
||||||
|
|
||||||
// Computed
|
// Computed
|
||||||
const show = computed({
|
const show = computed({
|
||||||
get: () => props.modelValue,
|
get: () => props.modelValue,
|
||||||
|
|
@ -364,6 +395,35 @@ watch(memberPricingEnabled, (newValue) => {
|
||||||
eventData.member_pricing_enabled = newValue ? 'true' : 'false';
|
eventData.member_pricing_enabled = newValue ? 'true' : 'false';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
watch(allowGuests, (newValue) => {
|
||||||
|
eventData.guests_permitted = newValue ? 'true' : 'false';
|
||||||
|
if (!newValue) {
|
||||||
|
eventData.max_guests_permitted = '0';
|
||||||
|
maxGuestsPerPerson.value = 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(maxGuestsPerPerson, (newValue) => {
|
||||||
|
if (allowGuests.value) {
|
||||||
|
eventData.max_guests_permitted = newValue.toString();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fix date picker binding - ensure proper syncing
|
||||||
|
watch(startDateModel, (newDate) => {
|
||||||
|
if (newDate instanceof Date) {
|
||||||
|
eventData.start_datetime = newDate.toISOString();
|
||||||
|
console.log('[CreateEventDialog] Start date updated:', eventData.start_datetime);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(endDateModel, (newDate) => {
|
||||||
|
if (newDate instanceof Date) {
|
||||||
|
eventData.end_datetime = newDate.toISOString();
|
||||||
|
console.log('[CreateEventDialog] End date updated:', eventData.end_datetime);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
watch(isRecurring, (newValue) => {
|
watch(isRecurring, (newValue) => {
|
||||||
eventData.is_recurring = newValue ? 'true' : 'false';
|
eventData.is_recurring = newValue ? 'true' : 'false';
|
||||||
if (newValue) {
|
if (newValue) {
|
||||||
|
|
@ -440,6 +500,8 @@ const resetForm = () => {
|
||||||
eventData.cost_members = '';
|
eventData.cost_members = '';
|
||||||
eventData.cost_non_members = '';
|
eventData.cost_non_members = '';
|
||||||
eventData.member_pricing_enabled = 'true';
|
eventData.member_pricing_enabled = 'true';
|
||||||
|
eventData.guests_permitted = 'false';
|
||||||
|
eventData.max_guests_permitted = '0';
|
||||||
eventData.visibility = 'public';
|
eventData.visibility = 'public';
|
||||||
eventData.status = 'active';
|
eventData.status = 'active';
|
||||||
eventData.is_recurring = 'false';
|
eventData.is_recurring = 'false';
|
||||||
|
|
@ -449,10 +511,13 @@ const resetForm = () => {
|
||||||
startDateModel.value = null;
|
startDateModel.value = null;
|
||||||
endDateModel.value = null;
|
endDateModel.value = null;
|
||||||
|
|
||||||
|
// Reset UI state
|
||||||
isPaidEvent.value = false;
|
isPaidEvent.value = false;
|
||||||
memberPricingEnabled.value = true;
|
memberPricingEnabled.value = true;
|
||||||
isRecurring.value = false;
|
isRecurring.value = false;
|
||||||
recurrenceFrequency.value = 'weekly';
|
recurrenceFrequency.value = 'weekly';
|
||||||
|
allowGuests.value = false;
|
||||||
|
maxGuestsPerPerson.value = 1;
|
||||||
|
|
||||||
form.value?.resetValidation();
|
form.value?.resetValidation();
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -192,6 +192,28 @@
|
||||||
</v-card-title>
|
</v-card-title>
|
||||||
<v-card-text class="pt-0">
|
<v-card-text class="pt-0">
|
||||||
<v-form v-model="rsvpValid">
|
<v-form v-model="rsvpValid">
|
||||||
|
<!-- Guest Selection (if event allows guests) -->
|
||||||
|
<div v-if="allowsGuests" class="mb-4">
|
||||||
|
<v-card variant="tonal" class="mb-3">
|
||||||
|
<v-card-text class="py-3">
|
||||||
|
<div class="d-flex align-center mb-2">
|
||||||
|
<v-icon class="me-2">mdi-account-group</v-icon>
|
||||||
|
<span class="font-weight-medium">Bring Guests</span>
|
||||||
|
</div>
|
||||||
|
<p class="text-body-2 text-medium-emphasis mb-3">
|
||||||
|
This event allows up to {{ maxGuestsAllowed }} additional guests per person.
|
||||||
|
</p>
|
||||||
|
<v-select
|
||||||
|
v-model="selectedGuests"
|
||||||
|
:items="guestOptions"
|
||||||
|
label="Number of Additional Guests"
|
||||||
|
variant="outlined"
|
||||||
|
density="compact"
|
||||||
|
/>
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
<v-textarea
|
<v-textarea
|
||||||
v-model="rsvpNotes"
|
v-model="rsvpNotes"
|
||||||
label="Notes (optional)"
|
label="Notes (optional)"
|
||||||
|
|
@ -200,31 +222,18 @@
|
||||||
class="mb-3"
|
class="mb-3"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="d-flex justify-space-between gap-4">
|
|
||||||
<v-btn
|
<v-btn
|
||||||
@click="submitRSVP('confirmed')"
|
@click="submitRSVP('confirmed')"
|
||||||
color="success"
|
color="primary"
|
||||||
:loading="rsvpLoading"
|
:loading="rsvpLoading"
|
||||||
:disabled="isEventFull && !isWaitlistAvailable"
|
:disabled="isEventFull && !isWaitlistAvailable"
|
||||||
size="large"
|
size="large"
|
||||||
class="flex-grow-1"
|
block
|
||||||
|
class="mb-2"
|
||||||
>
|
>
|
||||||
<v-icon start>mdi-check</v-icon>
|
<v-icon start>mdi-check</v-icon>
|
||||||
{{ isEventFull ? 'Join Waitlist' : 'Confirm Attendance' }}
|
{{ isEventFull ? 'Join Waitlist' : 'RSVP' }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
|
|
||||||
<v-btn
|
|
||||||
@click="submitRSVP('declined')"
|
|
||||||
color="error"
|
|
||||||
variant="outlined"
|
|
||||||
:loading="rsvpLoading"
|
|
||||||
size="large"
|
|
||||||
class="flex-grow-1"
|
|
||||||
>
|
|
||||||
<v-icon start>mdi-close</v-icon>
|
|
||||||
Decline
|
|
||||||
</v-btn>
|
|
||||||
</div>
|
|
||||||
</v-form>
|
</v-form>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
</v-card>
|
</v-card>
|
||||||
|
|
@ -286,6 +295,7 @@ const { rsvpToEvent } = useEvents();
|
||||||
const rsvpValid = ref(false);
|
const rsvpValid = ref(false);
|
||||||
const rsvpLoading = ref(false);
|
const rsvpLoading = ref(false);
|
||||||
const rsvpNotes = ref('');
|
const rsvpNotes = ref('');
|
||||||
|
const selectedGuests = ref(0);
|
||||||
|
|
||||||
// Computed properties
|
// Computed properties
|
||||||
const show = computed({
|
const show = computed({
|
||||||
|
|
@ -440,6 +450,28 @@ const paymentInfo = computed(() => ({
|
||||||
recipient: 'MonacoUSA Association' // This should come from config
|
recipient: 'MonacoUSA Association' // This should come from config
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// Guest functionality
|
||||||
|
const allowsGuests = computed(() => {
|
||||||
|
return props.event?.guests_permitted === 'true';
|
||||||
|
});
|
||||||
|
|
||||||
|
const maxGuestsAllowed = computed(() => {
|
||||||
|
if (!allowsGuests.value) return 0;
|
||||||
|
return parseInt(props.event?.max_guests_permitted || '0');
|
||||||
|
});
|
||||||
|
|
||||||
|
const guestOptions = computed(() => {
|
||||||
|
const max = maxGuestsAllowed.value;
|
||||||
|
const options = [];
|
||||||
|
for (let i = 0; i <= max; i++) {
|
||||||
|
options.push({
|
||||||
|
title: i === 0 ? 'No additional guests' : `${i} guest${i > 1 ? 's' : ''}`,
|
||||||
|
value: i
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return options;
|
||||||
|
});
|
||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
const close = () => {
|
const close = () => {
|
||||||
show.value = false;
|
show.value = false;
|
||||||
|
|
@ -452,21 +484,27 @@ const submitRSVP = async (status: 'confirmed' | 'declined') => {
|
||||||
rsvpLoading.value = true;
|
rsvpLoading.value = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Extract database ID - props.event is a raw Event object, not FullCalendar object
|
// Use event_id field for consistent RSVP relationships
|
||||||
// Database ID is stored in 'Id' field (capital I) from NocoDB
|
// This ensures RSVPs are linked properly to events using the business identifier
|
||||||
const databaseId = (props.event as any).Id || (props.event as any).extendedProps?.database_id || props.event.id;
|
const eventId = props.event.event_id ||
|
||||||
console.log('[EventDetailsDialog] Using database ID for RSVP:', databaseId);
|
(props.event as any).extendedProps?.event_id ||
|
||||||
console.log('[EventDetailsDialog] Event object keys:', Object.keys(props.event));
|
(props.event as any).Id || // Fallback to database ID if event_id not available
|
||||||
console.log('[EventDetailsDialog] Event Id field:', (props.event as any).Id);
|
props.event.id;
|
||||||
|
|
||||||
if (!databaseId) {
|
console.log('[EventDetailsDialog] Using event identifier for RSVP:', eventId);
|
||||||
throw new Error('Unable to determine database ID for event');
|
console.log('[EventDetailsDialog] Event object keys:', Object.keys(props.event));
|
||||||
|
console.log('[EventDetailsDialog] Event event_id field:', props.event.event_id);
|
||||||
|
console.log('[EventDetailsDialog] Event database Id field:', (props.event as any).Id);
|
||||||
|
|
||||||
|
if (!eventId) {
|
||||||
|
throw new Error('Unable to determine event identifier');
|
||||||
}
|
}
|
||||||
|
|
||||||
await rsvpToEvent(databaseId, {
|
await rsvpToEvent(eventId, {
|
||||||
member_id: '', // This will be filled by the composable
|
member_id: '', // This will be filled by the composable
|
||||||
rsvp_status: status,
|
rsvp_status: status,
|
||||||
rsvp_notes: rsvpNotes.value
|
rsvp_notes: rsvpNotes.value,
|
||||||
|
extra_guests: selectedGuests.value.toString()
|
||||||
});
|
});
|
||||||
|
|
||||||
emit('rsvp-updated', props.event);
|
emit('rsvp-updated', props.event);
|
||||||
|
|
|
||||||
|
|
@ -106,13 +106,15 @@ export const useEvents = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RSVP to an event
|
* RSVP to an event with support for guests and real-time updates
|
||||||
*/
|
*/
|
||||||
const rsvpToEvent = async (eventId: string, rsvpData: Omit<EventRSVPRequest, 'event_id'>) => {
|
const rsvpToEvent = async (eventId: string, rsvpData: Omit<EventRSVPRequest, 'event_id'> & { extra_guests?: string }) => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
error.value = null;
|
error.value = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
console.log('[useEvents] RSVP to event:', eventId, 'with data:', rsvpData);
|
||||||
|
|
||||||
const response = await $fetch<{ success: boolean; data: any; message: string }>(`/api/events/${eventId}/rsvp`, {
|
const response = await $fetch<{ success: boolean; data: any; message: string }>(`/api/events/${eventId}/rsvp`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: {
|
body: {
|
||||||
|
|
@ -123,28 +125,46 @@ export const useEvents = () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
// Update local event data - match by database ID (stored in Id field or as fallback)
|
// Find event by event_id first, then fallback to database ID
|
||||||
const eventIndex = events.value.findIndex(e =>
|
let eventIndex = events.value.findIndex(e => e.event_id === eventId);
|
||||||
(e as any).Id === eventId || e.id === eventId
|
if (eventIndex === -1) {
|
||||||
);
|
eventIndex = events.value.findIndex(e => (e as any).Id === eventId || e.id === eventId);
|
||||||
|
}
|
||||||
|
|
||||||
console.log('[useEvents] Looking for event with database ID:', eventId, 'found at index:', eventIndex);
|
console.log('[useEvents] Event found at index:', eventIndex, 'using event_id:', eventId);
|
||||||
|
|
||||||
if (eventIndex !== -1) {
|
if (eventIndex !== -1) {
|
||||||
events.value[eventIndex].user_rsvp = response.data;
|
const event = events.value[eventIndex];
|
||||||
|
|
||||||
// Update attendee count if confirmed
|
// Update RSVP status
|
||||||
|
event.user_rsvp = response.data;
|
||||||
|
|
||||||
|
// Calculate attendee count including guests
|
||||||
if (rsvpData.rsvp_status === 'confirmed') {
|
if (rsvpData.rsvp_status === 'confirmed') {
|
||||||
const currentCount = typeof events.value[eventIndex].current_attendees === 'string'
|
const currentCount = parseInt(event.current_attendees || '0');
|
||||||
? parseInt(events.value[eventIndex].current_attendees) || 0
|
const guestCount = parseInt(rsvpData.extra_guests || '0');
|
||||||
: events.value[eventIndex].current_attendees || 0;
|
const totalAdded = 1 + guestCount; // Member + guests
|
||||||
events.value[eventIndex].current_attendees = (currentCount + 1).toString();
|
|
||||||
}
|
event.current_attendees = (currentCount + totalAdded).toString();
|
||||||
|
|
||||||
|
console.log('[useEvents] Updated attendee count:', {
|
||||||
|
previous: currentCount,
|
||||||
|
added: totalAdded,
|
||||||
|
new: event.current_attendees,
|
||||||
|
guests: guestCount
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear cache
|
// Trigger reactivity
|
||||||
|
events.value[eventIndex] = { ...event };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear cache for fresh data on next load
|
||||||
cache.clear();
|
cache.clear();
|
||||||
|
|
||||||
|
// Force refresh events data to ensure accuracy
|
||||||
|
await fetchEvents({ force: true });
|
||||||
|
|
||||||
return response.data;
|
return response.data;
|
||||||
} else {
|
} else {
|
||||||
throw new Error(response.message || 'Failed to RSVP');
|
throw new Error(response.message || 'Failed to RSVP');
|
||||||
|
|
@ -158,6 +178,61 @@ export const useEvents = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel RSVP to an event
|
||||||
|
*/
|
||||||
|
const cancelRSVP = async (eventId: string) => {
|
||||||
|
loading.value = true;
|
||||||
|
error.value = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Find the event to get current RSVP info
|
||||||
|
let event = events.value.find(e => e.event_id === eventId);
|
||||||
|
if (!event) {
|
||||||
|
event = events.value.find(e => (e as any).Id === eventId || e.id === eventId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!event?.user_rsvp) {
|
||||||
|
throw new Error('No RSVP found to cancel');
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await $fetch<{ success: boolean; data: any; message: string }>(`/api/events/${eventId}/rsvp`, {
|
||||||
|
method: 'DELETE'
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
const eventIndex = events.value.findIndex(e => e === event);
|
||||||
|
|
||||||
|
if (eventIndex !== -1) {
|
||||||
|
const currentCount = parseInt(events.value[eventIndex].current_attendees || '0');
|
||||||
|
const guestCount = parseInt(events.value[eventIndex].user_rsvp?.extra_guests || '0');
|
||||||
|
const totalRemoved = 1 + guestCount; // Member + guests
|
||||||
|
|
||||||
|
// Update attendee count and remove RSVP
|
||||||
|
events.value[eventIndex].current_attendees = Math.max(0, currentCount - totalRemoved).toString();
|
||||||
|
events.value[eventIndex].user_rsvp = undefined;
|
||||||
|
|
||||||
|
// Trigger reactivity
|
||||||
|
events.value[eventIndex] = { ...events.value[eventIndex] };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear cache and refresh
|
||||||
|
cache.clear();
|
||||||
|
await fetchEvents({ force: true });
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
} else {
|
||||||
|
throw new Error(response.message || 'Failed to cancel RSVP');
|
||||||
|
}
|
||||||
|
} catch (err: any) {
|
||||||
|
error.value = err.message || 'Failed to cancel RSVP';
|
||||||
|
console.error('Error canceling RSVP:', err);
|
||||||
|
throw err;
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update attendance for an event (board/admin only)
|
* Update attendance for an event (board/admin only)
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,38 @@ export default defineEventHandler(async (event) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate guest count if event allows guests
|
||||||
|
const extraGuests = parseInt((body as any).extra_guests || '0');
|
||||||
|
if (extraGuests > 0) {
|
||||||
|
if (eventDetails.guests_permitted !== 'true') {
|
||||||
|
throw createError({
|
||||||
|
statusCode: 400,
|
||||||
|
statusMessage: 'This event does not allow guests'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxGuestsAllowed = parseInt(eventDetails.max_guests_permitted || '0');
|
||||||
|
if (extraGuests > maxGuestsAllowed) {
|
||||||
|
throw createError({
|
||||||
|
statusCode: 400,
|
||||||
|
statusMessage: `Maximum ${maxGuestsAllowed} guests allowed per person`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check event capacity including guests
|
||||||
|
if (eventDetails.max_attendees && body.rsvp_status === 'confirmed') {
|
||||||
|
const maxCapacity = parseInt(eventDetails.max_attendees);
|
||||||
|
const currentAttendees = parseInt(eventDetails.current_attendees || '0');
|
||||||
|
const totalRequested = 1 + extraGuests; // Member + guests
|
||||||
|
|
||||||
|
if (currentAttendees + totalRequested > maxCapacity) {
|
||||||
|
// Auto-set to waitlist if over capacity
|
||||||
|
body.rsvp_status = 'waitlist';
|
||||||
|
console.log(`[RSVP] Event at capacity, placing on waitlist: ${currentAttendees} + ${totalRequested} > ${maxCapacity}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Create RSVP record
|
// Create RSVP record
|
||||||
const rsvpData = {
|
const rsvpData = {
|
||||||
record_type: 'rsvp',
|
record_type: 'rsvp',
|
||||||
|
|
@ -95,6 +127,7 @@ export default defineEventHandler(async (event) => {
|
||||||
payment_reference: paymentReference,
|
payment_reference: paymentReference,
|
||||||
attended: 'false',
|
attended: 'false',
|
||||||
rsvp_notes: body.rsvp_notes || '',
|
rsvp_notes: body.rsvp_notes || '',
|
||||||
|
extra_guests: extraGuests.toString(),
|
||||||
is_member_pricing: isMemberPricing,
|
is_member_pricing: isMemberPricing,
|
||||||
created_at: new Date().toISOString(),
|
created_at: new Date().toISOString(),
|
||||||
updated_at: new Date().toISOString()
|
updated_at: new Date().toISOString()
|
||||||
|
|
|
||||||
|
|
@ -30,25 +30,49 @@ export const getEventTableId = (tableName: 'Events' | 'EventRSVPs'): string => {
|
||||||
// Try to get effective configuration from admin config system first
|
// Try to get effective configuration from admin config system first
|
||||||
const effectiveConfig = getEffectiveNocoDBConfig();
|
const effectiveConfig = getEffectiveNocoDBConfig();
|
||||||
if (effectiveConfig?.tables) {
|
if (effectiveConfig?.tables) {
|
||||||
const tableKey = tableName === 'Events' ? 'events' : 'event_rsvps';
|
if (tableName === 'Events') {
|
||||||
const tableId = effectiveConfig.tables[tableKey] || effectiveConfig.tables[tableName];
|
// Check multiple possible keys for Events table
|
||||||
if (tableId) {
|
const eventsTableId = effectiveConfig.tables['events'] ||
|
||||||
console.log(`[nocodb-events] Using admin config table ID for ${tableName}:`, tableId);
|
effectiveConfig.tables['Events'] ||
|
||||||
return tableId;
|
effectiveConfig.tables['events_table'];
|
||||||
|
if (eventsTableId) {
|
||||||
|
console.log(`[nocodb-events] Using admin config table ID for ${tableName}:`, eventsTableId);
|
||||||
|
return eventsTableId;
|
||||||
|
}
|
||||||
|
} else if (tableName === 'EventRSVPs') {
|
||||||
|
// Check multiple possible keys for RSVP table
|
||||||
|
const rsvpTableId = effectiveConfig.tables['rsvps'] ||
|
||||||
|
effectiveConfig.tables['event_rsvps'] ||
|
||||||
|
effectiveConfig.tables['EventRSVPs'] ||
|
||||||
|
effectiveConfig.tables['rsvp_table'] ||
|
||||||
|
effectiveConfig.tables['RSVPs'];
|
||||||
|
if (rsvpTableId) {
|
||||||
|
console.log(`[nocodb-events] Using admin config table ID for ${tableName}:`, rsvpTableId);
|
||||||
|
return rsvpTableId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(`[nocodb-events] Admin config not available, trying fallback for ${tableName}`);
|
console.log(`[nocodb-events] Admin config not available, trying fallback for ${tableName}:`, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to get table ID from global configuration
|
// Try to get table ID from global configuration
|
||||||
const globalConfig = (global as any).globalNocoDBConfig;
|
const globalConfig = (global as any).globalNocoDBConfig;
|
||||||
if (globalConfig?.tables) {
|
if (globalConfig?.tables) {
|
||||||
const tableKey = tableName === 'Events' ? 'events' : 'event_rsvps';
|
if (tableName === 'Events') {
|
||||||
const tableId = globalConfig.tables[tableKey] || globalConfig.tables[tableName];
|
const eventsTableId = globalConfig.tables['events'] || globalConfig.tables['Events'];
|
||||||
if (tableId) {
|
if (eventsTableId) {
|
||||||
console.log(`[nocodb-events] Using global table ID for ${tableName}:`, tableId);
|
console.log(`[nocodb-events] Using global table ID for ${tableName}:`, eventsTableId);
|
||||||
return tableId;
|
return eventsTableId;
|
||||||
|
}
|
||||||
|
} else if (tableName === 'EventRSVPs') {
|
||||||
|
const rsvpTableId = globalConfig.tables['rsvps'] ||
|
||||||
|
globalConfig.tables['event_rsvps'] ||
|
||||||
|
globalConfig.tables['EventRSVPs'];
|
||||||
|
if (rsvpTableId) {
|
||||||
|
console.log(`[nocodb-events] Using global table ID for ${tableName}:`, rsvpTableId);
|
||||||
|
return rsvpTableId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -60,7 +84,7 @@ export const getEventTableId = (tableName: 'Events' | 'EventRSVPs'): string => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Final fallback to default
|
// Final fallback to default
|
||||||
const defaultTableId = tableName === 'Events' ? 'mt1mx3vkcw0vbmh' : 'rsvps-table-id';
|
const defaultTableId = tableName === 'Events' ? 'mp3wigub1fzdo1b' : 'mt1mx3vkcw0vbmh';
|
||||||
console.log(`[nocodb-events] Using fallback table ID for ${tableName}:`, defaultTableId);
|
console.log(`[nocodb-events] Using fallback table ID for ${tableName}:`, defaultTableId);
|
||||||
return defaultTableId;
|
return defaultTableId;
|
||||||
};
|
};
|
||||||
|
|
@ -276,6 +300,7 @@ export function createNocoDBEventsClient() {
|
||||||
"title", "description", "event_type", "event_id", "start_datetime", "end_datetime",
|
"title", "description", "event_type", "event_id", "start_datetime", "end_datetime",
|
||||||
"location", "is_recurring", "recurrence_pattern", "max_attendees",
|
"location", "is_recurring", "recurrence_pattern", "max_attendees",
|
||||||
"is_paid", "cost_members", "cost_non_members", "member_pricing_enabled",
|
"is_paid", "cost_members", "cost_non_members", "member_pricing_enabled",
|
||||||
|
"guests_permitted", "max_guests_permitted",
|
||||||
"visibility", "status", "creator", "current_attendees"
|
"visibility", "status", "creator", "current_attendees"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -455,6 +455,8 @@ export interface Event {
|
||||||
cost_members?: string;
|
cost_members?: string;
|
||||||
cost_non_members?: string;
|
cost_non_members?: string;
|
||||||
member_pricing_enabled: string; // 'true' or 'false' as string
|
member_pricing_enabled: string; // 'true' or 'false' as string
|
||||||
|
guests_permitted?: string; // 'true' or 'false' as string
|
||||||
|
max_guests_permitted?: string; // Maximum guests per person
|
||||||
visibility: 'public' | 'board-only' | 'admin-only';
|
visibility: 'public' | 'board-only' | 'admin-only';
|
||||||
status: 'active' | 'cancelled' | 'completed' | 'draft';
|
status: 'active' | 'cancelled' | 'completed' | 'draft';
|
||||||
creator: string; // member_id who created event
|
creator: string; // member_id who created event
|
||||||
|
|
@ -476,6 +478,7 @@ export interface EventRSVP {
|
||||||
payment_reference: string; // EVT-{member_id}-{date}
|
payment_reference: string; // EVT-{member_id}-{date}
|
||||||
attended: string; // 'true' or 'false' as string
|
attended: string; // 'true' or 'false' as string
|
||||||
rsvp_notes?: string;
|
rsvp_notes?: string;
|
||||||
|
extra_guests?: string; // Number of additional guests as string
|
||||||
created_time: string; // Updated to match database schema
|
created_time: string; // Updated to match database schema
|
||||||
updated_time: string; // Updated to match database schema
|
updated_time: string; // Updated to match database schema
|
||||||
|
|
||||||
|
|
@ -505,6 +508,8 @@ export interface EventCreateRequest {
|
||||||
cost_members?: string;
|
cost_members?: string;
|
||||||
cost_non_members?: string;
|
cost_non_members?: string;
|
||||||
member_pricing_enabled: string;
|
member_pricing_enabled: string;
|
||||||
|
guests_permitted?: string;
|
||||||
|
max_guests_permitted?: string;
|
||||||
visibility: string;
|
visibility: string;
|
||||||
status?: string;
|
status?: string;
|
||||||
}
|
}
|
||||||
|
|
@ -512,8 +517,9 @@ export interface EventCreateRequest {
|
||||||
export interface EventRSVPRequest {
|
export interface EventRSVPRequest {
|
||||||
event_id: string;
|
event_id: string;
|
||||||
member_id: string;
|
member_id: string;
|
||||||
rsvp_status: 'confirmed' | 'declined' | 'pending';
|
rsvp_status: 'confirmed' | 'declined' | 'pending' | 'waitlist';
|
||||||
rsvp_notes?: string;
|
rsvp_notes?: string;
|
||||||
|
extra_guests?: string; // Number of additional guests as string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EventAttendanceRequest {
|
export interface EventAttendanceRequest {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue