Clean up codebase and reorganize plugin architecture
Build And Push Image / docker (push) Successful in 1m30s
Details
Build And Push Image / docker (push) Successful in 1m30s
Details
- Archive documentation files to docs-archive/ - Remove numbered prefixes from plugin files for cleaner organization - Remove unused dependencies (@nuxt/ui, @vuepic/vue-datepicker) - Update event components and API endpoints - Simplify plugin structure with descriptive names
This commit is contained in:
parent
b4e72ddf9a
commit
9ee0b2f14e
|
|
@ -80,50 +80,26 @@
|
||||||
|
|
||||||
<!-- Date and Time -->
|
<!-- Date and Time -->
|
||||||
<v-col cols="12" md="6">
|
<v-col cols="12" md="6">
|
||||||
<div class="date-picker-wrapper">
|
<VDateInput
|
||||||
<label class="date-picker-label">Start Date & Time*</label>
|
v-model="startDateModel"
|
||||||
<VueDatePicker
|
label="Start Date & Time*"
|
||||||
v-model="startDateModel"
|
:rules="[v => !!v || 'Start date is required']"
|
||||||
:timezone="{
|
variant="outlined"
|
||||||
timezone: 'Europe/Monaco',
|
prepend-inner-icon="mdi-calendar"
|
||||||
emitTimezone: 'UTC'
|
required
|
||||||
}"
|
/>
|
||||||
:format="dateTimeFormat"
|
|
||||||
placeholder="Select start date and time"
|
|
||||||
:enable-time-picker="true"
|
|
||||||
:is-24="true"
|
|
||||||
:auto-apply="false"
|
|
||||||
:action-row="{ showSelect: true, showCancel: true, showNow: false, showPreview: true }"
|
|
||||||
:clearable="false"
|
|
||||||
:required="true"
|
|
||||||
@update:model-value="handleStartDateUpdate"
|
|
||||||
@closed="onDatePickerClosed"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|
||||||
<v-col cols="12" md="6">
|
<v-col cols="12" md="6">
|
||||||
<div class="date-picker-wrapper">
|
<VDateInput
|
||||||
<label class="date-picker-label">End Date & Time*</label>
|
v-model="endDateModel"
|
||||||
<VueDatePicker
|
label="End Date & Time*"
|
||||||
v-model="endDateModel"
|
:rules="[v => !!v || 'End date is required']"
|
||||||
:timezone="{
|
variant="outlined"
|
||||||
timezone: 'Europe/Monaco',
|
prepend-inner-icon="mdi-calendar"
|
||||||
emitTimezone: 'UTC'
|
:min="startDateModel"
|
||||||
}"
|
required
|
||||||
:format="dateTimeFormat"
|
/>
|
||||||
placeholder="Select end date and time"
|
|
||||||
:enable-time-picker="true"
|
|
||||||
:is-24="true"
|
|
||||||
:auto-apply="false"
|
|
||||||
:action-row="{ showSelect: true, showCancel: true, showNow: false, showPreview: true }"
|
|
||||||
:clearable="false"
|
|
||||||
:required="true"
|
|
||||||
:min-date="startDateModel"
|
|
||||||
@update:model-value="handleEndDateUpdate"
|
|
||||||
@closed="onDatePickerClosed"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|
||||||
<!-- Location -->
|
<!-- Location -->
|
||||||
|
|
|
||||||
|
|
@ -479,39 +479,73 @@ const close = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const submitRSVP = async (status: 'confirmed' | 'declined') => {
|
const submitRSVP = async (status: 'confirmed' | 'declined') => {
|
||||||
if (!props.event) return;
|
console.log('[EventDetailsDialog] submitRSVP called with status:', status);
|
||||||
|
|
||||||
|
if (!props.event) {
|
||||||
|
console.error('[EventDetailsDialog] No event provided');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
rsvpLoading.value = true;
|
rsvpLoading.value = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Use event_id field for consistent RSVP relationships
|
// Use event_id field for consistent RSVP relationships
|
||||||
// This ensures RSVPs are linked properly to events using the business identifier
|
// This ensures RSVPs are linked properly to events using the business identifier
|
||||||
const eventId = props.event.event_id ||
|
let eventId = props.event.event_id ||
|
||||||
(props.event as any).extendedProps?.event_id ||
|
(props.event as any).extendedProps?.event_id ||
|
||||||
(props.event as any).Id || // Fallback to database ID if event_id not available
|
(props.event as any).Id || // Fallback to database ID if event_id not available
|
||||||
props.event.id;
|
props.event.id ||
|
||||||
|
(props.event as any).id; // Additional fallback
|
||||||
|
|
||||||
|
// Direct access to Id field as backup
|
||||||
|
if (!eventId && 'Id' in props.event) {
|
||||||
|
eventId = (props.event as any)['Id'];
|
||||||
|
console.log('[EventDetailsDialog] Found Id via direct property access:', eventId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to access the Id property using Object.keys approach
|
||||||
|
if (!eventId) {
|
||||||
|
const keys = Object.keys(props.event);
|
||||||
|
console.log('[EventDetailsDialog] Available keys:', keys);
|
||||||
|
if (keys.includes('Id')) {
|
||||||
|
eventId = props.event['Id' as keyof Event];
|
||||||
|
console.log('[EventDetailsDialog] Found Id via keys lookup:', eventId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
console.log('[EventDetailsDialog] Using event identifier for RSVP:', eventId);
|
console.log('[EventDetailsDialog] Using event identifier for RSVP:', eventId);
|
||||||
console.log('[EventDetailsDialog] Event object keys:', Object.keys(props.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 event_id field:', props.event.event_id);
|
||||||
console.log('[EventDetailsDialog] Event database Id field:', (props.event as any).Id);
|
console.log('[EventDetailsDialog] Event database Id field:', (props.event as any).Id);
|
||||||
|
console.log('[EventDetailsDialog] Event id field:', props.event.id);
|
||||||
|
console.log('[EventDetailsDialog] Full event object:', JSON.stringify(props.event, null, 2));
|
||||||
|
|
||||||
if (!eventId) {
|
if (!eventId) {
|
||||||
|
console.error('[EventDetailsDialog] Unable to determine event identifier');
|
||||||
throw new Error('Unable to determine event identifier');
|
throw new Error('Unable to determine event identifier');
|
||||||
}
|
}
|
||||||
|
|
||||||
await rsvpToEvent(eventId, {
|
console.log('[EventDetailsDialog] Calling rsvpToEvent with:', {
|
||||||
|
eventId,
|
||||||
|
status,
|
||||||
|
notes: rsvpNotes.value,
|
||||||
|
guests: selectedGuests.value
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = 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()
|
extra_guests: selectedGuests.value.toString()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log('[EventDetailsDialog] RSVP submitted successfully:', result);
|
||||||
|
|
||||||
emit('rsvp-updated', props.event);
|
emit('rsvp-updated', props.event);
|
||||||
// TODO: Show success message
|
// TODO: Show success message
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error submitting RSVP:', error);
|
console.error('[EventDetailsDialog] Error submitting RSVP:', error);
|
||||||
// TODO: Show error message
|
// TODO: Show error message
|
||||||
} finally {
|
} finally {
|
||||||
rsvpLoading.value = false;
|
rsvpLoading.value = false;
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -15,13 +15,11 @@
|
||||||
"@fullcalendar/interaction": "^6.1.19",
|
"@fullcalendar/interaction": "^6.1.19",
|
||||||
"@fullcalendar/list": "^6.1.19",
|
"@fullcalendar/list": "^6.1.19",
|
||||||
"@fullcalendar/vue3": "^6.1.19",
|
"@fullcalendar/vue3": "^6.1.19",
|
||||||
"@nuxt/ui": "^3.2.0",
|
|
||||||
"@nuxtjs/device": "^3.2.4",
|
"@nuxtjs/device": "^3.2.4",
|
||||||
"@types/handlebars": "^4.0.40",
|
"@types/handlebars": "^4.0.40",
|
||||||
"@types/jsonwebtoken": "^9.0.10",
|
"@types/jsonwebtoken": "^9.0.10",
|
||||||
"@types/nodemailer": "^6.4.17",
|
"@types/nodemailer": "^6.4.17",
|
||||||
"@vite-pwa/nuxt": "^0.10.8",
|
"@vite-pwa/nuxt": "^0.10.8",
|
||||||
"@vuepic/vue-datepicker": "^11.0.2",
|
|
||||||
"cookie": "^0.6.0",
|
"cookie": "^0.6.0",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"flag-icons": "^7.5.0",
|
"flag-icons": "^7.5.0",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
import VueDatePicker from '@vuepic/vue-datepicker'
|
|
||||||
import '@vuepic/vue-datepicker/dist/main.css'
|
|
||||||
|
|
||||||
export default defineNuxtPlugin((nuxtApp) => {
|
|
||||||
nuxtApp.vueApp.component('VueDatePicker', VueDatePicker)
|
|
||||||
})
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
// plugins/vuetify-date-input.client.ts
|
||||||
|
import { VDateInput } from 'vuetify/labs/VDateInput'
|
||||||
|
|
||||||
|
export default defineNuxtPlugin((nuxtApp) => {
|
||||||
|
// Register VDateInput from Vuetify Labs
|
||||||
|
nuxtApp.vueApp.component('VDateInput', VDateInput)
|
||||||
|
})
|
||||||
|
|
@ -117,10 +117,24 @@ export default defineEventHandler(async (event) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the event to find the proper event_id for RSVP tracking
|
||||||
|
let rsvpEventId = eventId; // Default to the provided eventId
|
||||||
|
try {
|
||||||
|
// Try to get the event_id field if it exists in the event
|
||||||
|
if (eventDetails.event_id) {
|
||||||
|
rsvpEventId = eventDetails.event_id;
|
||||||
|
console.log('[RSVP] Using event.event_id for RSVP:', rsvpEventId);
|
||||||
|
} else {
|
||||||
|
console.log('[RSVP] Using provided eventId for RSVP:', rsvpEventId);
|
||||||
|
}
|
||||||
|
} catch (eventIdError) {
|
||||||
|
console.log('[RSVP] Could not get event_id, using provided eventId:', rsvpEventId);
|
||||||
|
}
|
||||||
|
|
||||||
// Create RSVP record
|
// Create RSVP record
|
||||||
const rsvpData = {
|
const rsvpData = {
|
||||||
record_type: 'rsvp',
|
record_type: 'rsvp',
|
||||||
event_id: eventId,
|
event_id: rsvpEventId, // Use the business event_id field
|
||||||
member_id: member.member_id || member.Id,
|
member_id: member.member_id || member.Id,
|
||||||
rsvp_status: body.rsvp_status,
|
rsvp_status: body.rsvp_status,
|
||||||
payment_status: paymentStatus,
|
payment_status: paymentStatus,
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,15 @@ import { createNocoDBEventsClient } from '~/server/utils/nocodb-events';
|
||||||
import { createSessionManager } from '~/server/utils/session';
|
import { createSessionManager } from '~/server/utils/session';
|
||||||
import type { EventCreateRequest } from '~/utils/types';
|
import type { EventCreateRequest } from '~/utils/types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a unique event ID
|
||||||
|
*/
|
||||||
|
function generateEventId(): string {
|
||||||
|
const timestamp = Date.now().toString(36);
|
||||||
|
const randomStr = Math.random().toString(36).substring(2, 8);
|
||||||
|
return `EVT-${timestamp}-${randomStr}`.toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
console.log('[api/events.post] =========================');
|
console.log('[api/events.post] =========================');
|
||||||
console.log('[api/events.post] POST /api/events - Create event');
|
console.log('[api/events.post] POST /api/events - Create event');
|
||||||
|
|
@ -98,8 +107,13 @@ export default defineEventHandler(async (event) => {
|
||||||
|
|
||||||
const eventsClient = createNocoDBEventsClient();
|
const eventsClient = createNocoDBEventsClient();
|
||||||
|
|
||||||
|
// Generate unique event_id
|
||||||
|
const eventId = generateEventId();
|
||||||
|
console.log('[api/events.post] Generated event_id:', eventId);
|
||||||
|
|
||||||
// Prepare event data
|
// Prepare event data
|
||||||
const eventData = {
|
const eventData = {
|
||||||
|
event_id: eventId, // Add the business identifier
|
||||||
title: body.title.trim(),
|
title: body.title.trim(),
|
||||||
description: body.description?.trim() || '',
|
description: body.description?.trim() || '',
|
||||||
event_type: body.event_type as 'meeting' | 'social' | 'fundraiser' | 'workshop' | 'board-only',
|
event_type: body.event_type as 'meeting' | 'social' | 'fundraiser' | 'workshop' | 'board-only',
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue