Add series management and ticketing features: Introduce series event functionality in event creation, enhance event display with series information, and implement ticketing options for public events. Update layouts and improve form handling for better user experience.

This commit is contained in:
Jennie Robinson Faber 2025-08-27 20:40:54 +01:00
parent c3a29fa47c
commit a88aa62198
24 changed files with 2897 additions and 44 deletions

View file

@ -0,0 +1,78 @@
import Event from '../../models/event.js'
import { connectDB } from '../../utils/mongoose.js'
export default defineEventHandler(async (event) => {
try {
await connectDB()
const id = getRouterParam(event, 'id')
if (!id) {
throw createError({
statusCode: 400,
statusMessage: 'Series ID is required'
})
}
// Fetch all events in this series
const events = await Event.find({
'series.id': id,
'series.isSeriesEvent': true
})
.sort({ 'series.position': 1, startDate: 1 })
.select('-registrations')
.lean()
if (events.length === 0) {
throw createError({
statusCode: 404,
statusMessage: 'Event series not found'
})
}
// Get series metadata from the first event
const seriesInfo = events[0].series
// Calculate series statistics
const now = new Date()
const completedEvents = events.filter(e => e.endDate < now).length
const upcomingEvents = events.filter(e => e.startDate > now).length
const ongoingEvents = events.filter(e => e.startDate <= now && e.endDate >= now).length
const firstEventDate = events[0].startDate
const lastEventDate = events[events.length - 1].endDate
// Return series with additional metadata
return {
id: id,
title: seriesInfo.title,
description: seriesInfo.description,
type: seriesInfo.type,
totalEvents: seriesInfo.totalEvents,
startDate: firstEventDate,
endDate: lastEventDate,
events: events.map(e => ({
...e,
id: e._id.toString()
})),
statistics: {
totalEvents: events.length,
completedEvents,
upcomingEvents,
ongoingEvents,
isOngoing: firstEventDate <= now && lastEventDate >= now,
isUpcoming: firstEventDate > now,
isCompleted: lastEventDate < now,
totalRegistrations: events.reduce((sum, e) => sum + (e.registrations?.length || 0), 0)
}
}
} catch (error) {
console.error('Error fetching event series:', error)
if (error.statusCode) throw error
throw createError({
statusCode: 500,
statusMessage: 'Failed to fetch event series'
})
}
})

View file

@ -0,0 +1,91 @@
import Event from '../../models/event.js'
import { connectDB } from '../../utils/mongoose.js'
export default defineEventHandler(async (event) => {
try {
await connectDB()
const query = getQuery(event)
// Build filter for series events only
const filter = {
'series.isSeriesEvent': true,
isVisible: query.includeHidden === 'true' ? { $exists: true } : true
}
// Filter by series type
if (query.seriesType) {
filter['series.type'] = query.seriesType
}
// Filter for upcoming series
if (query.upcoming === 'true') {
filter.startDate = { $gte: new Date() }
}
// Fetch all series events and group them by series.id
const events = await Event.find(filter)
.sort({ 'series.id': 1, 'series.position': 1, startDate: 1 })
.select('-registrations')
.lean()
// Group events by series ID
const seriesMap = new Map()
events.forEach(event => {
const seriesId = event.series?.id
if (!seriesId) return
if (!seriesMap.has(seriesId)) {
seriesMap.set(seriesId, {
id: seriesId,
title: event.series.title,
description: event.series.description,
type: event.series.type,
totalEvents: event.series.totalEvents,
events: [],
firstEventDate: event.startDate,
lastEventDate: event.endDate
})
}
const series = seriesMap.get(seriesId)
series.events.push({
...event,
id: event._id.toString()
})
// Update date range
if (event.startDate < series.firstEventDate) {
series.firstEventDate = event.startDate
}
if (event.endDate > series.lastEventDate) {
series.lastEventDate = event.endDate
}
})
// Convert to array and add computed fields
const seriesArray = Array.from(seriesMap.values()).map(series => {
const now = new Date()
return {
...series,
eventCount: series.events.length,
startDate: series.firstEventDate,
endDate: series.lastEventDate,
isOngoing: series.firstEventDate <= now && series.lastEventDate >= now,
isUpcoming: series.firstEventDate > now,
isCompleted: series.lastEventDate < now,
status: series.lastEventDate < now ? 'completed' :
series.firstEventDate <= now && series.lastEventDate >= now ? 'active' : 'upcoming'
}
})
return seriesArray
} catch (error) {
console.error('Error fetching event series:', error)
throw createError({
statusCode: 500,
statusMessage: 'Failed to fetch event series'
})
}
})