Enhance application structure: Add runtime configuration for environment variables, integrate new dependencies for Cloudinary and UI components, and refactor member management features including improved forms and member dashboard. Update styles and layout for better user experience.
This commit is contained in:
parent
6e7e27ac4e
commit
e4a0a9ab0f
61 changed files with 7902 additions and 950 deletions
65
server/api/events/[id].get.js
Normal file
65
server/api/events/[id].get.js
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
import Event from '../../models/event.js'
|
||||
import { connectDB } from '../../utils/mongoose.js'
|
||||
import mongoose from 'mongoose'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
// Ensure database connection
|
||||
await connectDB()
|
||||
const identifier = getRouterParam(event, 'id')
|
||||
|
||||
if (!identifier) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: 'Event identifier is required'
|
||||
})
|
||||
}
|
||||
|
||||
// Fetch event from database - try by slug first, then by ID
|
||||
let eventData
|
||||
|
||||
// Check if identifier is a valid MongoDB ObjectId
|
||||
if (mongoose.Types.ObjectId.isValid(identifier)) {
|
||||
eventData = await Event.findById(identifier)
|
||||
.select('-registrations.email') // Hide emails for privacy
|
||||
.lean()
|
||||
}
|
||||
|
||||
// If not found by ID or not a valid ObjectId, try by slug
|
||||
if (!eventData) {
|
||||
eventData = await Event.findOne({ slug: identifier })
|
||||
.select('-registrations.email') // Hide emails for privacy
|
||||
.lean()
|
||||
}
|
||||
|
||||
if (!eventData) {
|
||||
throw createError({
|
||||
statusCode: 404,
|
||||
statusMessage: 'Event not found'
|
||||
})
|
||||
}
|
||||
|
||||
// Add computed fields
|
||||
const eventWithMeta = {
|
||||
...eventData,
|
||||
id: eventData._id.toString(),
|
||||
registeredCount: eventData.registrations?.length || 0,
|
||||
isFull: eventData.maxAttendees ?
|
||||
(eventData.registrations?.length || 0) >= eventData.maxAttendees :
|
||||
false
|
||||
}
|
||||
|
||||
return eventWithMeta
|
||||
} catch (error) {
|
||||
console.error('Error fetching event:', error)
|
||||
|
||||
if (error.statusCode) {
|
||||
throw error
|
||||
}
|
||||
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
statusMessage: 'Failed to fetch event'
|
||||
})
|
||||
}
|
||||
})
|
||||
116
server/api/events/[id]/register.post.js
Normal file
116
server/api/events/[id]/register.post.js
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
import Event from '../../../models/event.js'
|
||||
import Member from '../../../models/member.js'
|
||||
import { connectDB } from '../../../utils/mongoose.js'
|
||||
import mongoose from 'mongoose'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
// Ensure database connection
|
||||
await connectDB()
|
||||
const identifier = getRouterParam(event, 'id')
|
||||
const body = await readBody(event)
|
||||
|
||||
if (!identifier) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: 'Event identifier is required'
|
||||
})
|
||||
}
|
||||
|
||||
// Validate required fields
|
||||
if (!body.name || !body.email) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: 'Name and email are required'
|
||||
})
|
||||
}
|
||||
|
||||
// Fetch the event - try by slug first, then by ID
|
||||
let eventData
|
||||
|
||||
// Check if identifier is a valid MongoDB ObjectId
|
||||
if (mongoose.Types.ObjectId.isValid(identifier)) {
|
||||
eventData = await Event.findById(identifier)
|
||||
}
|
||||
|
||||
// If not found by ID or not a valid ObjectId, try by slug
|
||||
if (!eventData) {
|
||||
eventData = await Event.findOne({ slug: identifier })
|
||||
}
|
||||
|
||||
if (!eventData) {
|
||||
throw createError({
|
||||
statusCode: 404,
|
||||
statusMessage: 'Event not found'
|
||||
})
|
||||
}
|
||||
|
||||
// Check if event is full
|
||||
if (eventData.maxAttendees && eventData.registrations.length >= eventData.maxAttendees) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: 'Event is full'
|
||||
})
|
||||
}
|
||||
|
||||
// Check if already registered
|
||||
const alreadyRegistered = eventData.registrations.some(
|
||||
reg => reg.email.toLowerCase() === body.email.toLowerCase()
|
||||
)
|
||||
|
||||
if (alreadyRegistered) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: 'You are already registered for this event'
|
||||
})
|
||||
}
|
||||
|
||||
// Check member status if event is members-only
|
||||
if (eventData.membersOnly && body.membershipLevel === 'non-member') {
|
||||
// Check if email belongs to a member
|
||||
const member = await Member.findOne({ email: body.email.toLowerCase() })
|
||||
|
||||
if (!member) {
|
||||
throw createError({
|
||||
statusCode: 403,
|
||||
statusMessage: 'This event is for members only. Please become a member to register.'
|
||||
})
|
||||
}
|
||||
|
||||
// Update membership level from database
|
||||
body.membershipLevel = `${member.circle}-${member.contributionTier}`
|
||||
}
|
||||
|
||||
// Add registration
|
||||
eventData.registrations.push({
|
||||
name: body.name,
|
||||
email: body.email.toLowerCase(),
|
||||
membershipLevel: body.membershipLevel || 'non-member',
|
||||
dietary: body.dietary || false,
|
||||
registeredAt: new Date()
|
||||
})
|
||||
|
||||
// Save the updated event
|
||||
await eventData.save()
|
||||
|
||||
// TODO: Send confirmation email using Resend
|
||||
// await sendEventRegistrationEmail(body.email, eventData)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Successfully registered for the event',
|
||||
registrationId: eventData.registrations[eventData.registrations.length - 1]._id
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error registering for event:', error)
|
||||
|
||||
if (error.statusCode) {
|
||||
throw error
|
||||
}
|
||||
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
statusMessage: 'Failed to register for event'
|
||||
})
|
||||
}
|
||||
})
|
||||
53
server/api/events/index.get.js
Normal file
53
server/api/events/index.get.js
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
import Event from '../../models/event.js'
|
||||
import { connectDB } from '../../utils/mongoose.js'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
// Ensure database connection
|
||||
await connectDB()
|
||||
// Get query parameters for filtering
|
||||
const query = getQuery(event)
|
||||
const filter = {}
|
||||
|
||||
// Only show visible events on public calendar (unless specifically requested)
|
||||
if (query.includeHidden !== 'true') {
|
||||
filter.isVisible = true
|
||||
}
|
||||
|
||||
// Filter for upcoming events only if requested
|
||||
if (query.upcoming === 'true') {
|
||||
filter.startDate = { $gte: new Date() }
|
||||
}
|
||||
|
||||
// Filter by event type if provided
|
||||
if (query.eventType) {
|
||||
filter.eventType = query.eventType
|
||||
}
|
||||
|
||||
// Filter for members-only events
|
||||
if (query.membersOnly !== undefined) {
|
||||
filter.membersOnly = query.membersOnly === 'true'
|
||||
}
|
||||
|
||||
// Fetch events from database
|
||||
const events = await Event.find(filter)
|
||||
.sort({ startDate: 1 })
|
||||
.select('-registrations') // Don't expose registration details in list view
|
||||
.lean()
|
||||
|
||||
// Add computed fields
|
||||
const eventsWithMeta = events.map(event => ({
|
||||
...event,
|
||||
id: event._id.toString(),
|
||||
registeredCount: event.registrations?.length || 0
|
||||
}))
|
||||
|
||||
return eventsWithMeta
|
||||
} catch (error) {
|
||||
console.error('Error fetching events:', error)
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
statusMessage: 'Failed to fetch events'
|
||||
})
|
||||
}
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue