import mongoose from "mongoose"; const seriesSchema = new mongoose.Schema({ id: { type: String, required: true, unique: true }, // Simple string ID (e.g., "coop-game-dev-2025") title: { type: String, required: true }, slug: { type: String, unique: true }, // Auto-generated in pre-save hook description: String, type: { type: String, enum: [ "workshop_series", "recurring_meetup", "multi_day", "course", "tournament", ], default: "workshop_series", }, // Visibility and status isVisible: { type: Boolean, default: true }, isActive: { type: Boolean, default: true }, // Date range (calculated from events or set manually) startDate: Date, endDate: Date, // Series ticketing configuration tickets: { enabled: { type: Boolean, default: false }, requiresSeriesTicket: { type: Boolean, default: false }, // If true, must buy series pass allowIndividualEventTickets: { type: Boolean, default: true }, // Allow drop-in for individual events currency: { type: String, default: "CAD" }, // Member series pass configuration member: { available: { type: Boolean, default: true }, isFree: { type: Boolean, default: true }, price: { type: Number, default: 0 }, name: { type: String, default: "Member Series Pass" }, description: String, // Circle-specific overrides circleOverrides: { community: { isFree: { type: Boolean }, price: { type: Number }, }, founder: { isFree: { type: Boolean }, price: { type: Number }, }, practitioner: { isFree: { type: Boolean }, price: { type: Number }, }, }, }, // Public (non-member) series pass configuration public: { available: { type: Boolean, default: false }, name: { type: String, default: "Series Pass" }, description: String, price: { type: Number, default: 0 }, quantity: Number, // null/undefined = unlimited sold: { type: Number, default: 0 }, reserved: { type: Number, default: 0 }, earlyBirdPrice: Number, earlyBirdDeadline: Date, }, // Series-wide capacity capacity: { total: Number, // null/undefined = unlimited reserved: { type: Number, default: 0 }, }, // Waitlist configuration waitlist: { enabled: { type: Boolean, default: false }, maxSize: Number, entries: [ { name: String, email: String, membershipLevel: String, addedAt: { type: Date, default: Date.now }, notified: { type: Boolean, default: false }, }, ], }, }, // Series pass purchases (registrations) registrations: [ { memberId: { type: mongoose.Schema.Types.ObjectId, ref: "Member" }, name: String, email: String, membershipLevel: String, isMember: { type: Boolean, default: false }, // Ticket information ticketType: { type: String, enum: ["member", "public", "guest"], default: "guest", }, ticketPrice: { type: Number, default: 0 }, // Payment information paymentStatus: { type: String, enum: ["pending", "completed", "failed", "refunded", "not_required"], default: "not_required", }, paymentId: String, // Helcim transaction ID amountPaid: { type: Number, default: 0 }, // Metadata registeredAt: { type: Date, default: Date.now }, cancelledAt: Date, refundedAt: Date, refundAmount: Number, // Events they've been registered for (references) eventRegistrations: [ { eventId: { type: mongoose.Schema.Types.ObjectId, ref: "Event" }, registrationId: mongoose.Schema.Types.ObjectId, // ID of registration in event.registrations }, ], }, ], // Target circles targetCircles: [ { type: String, enum: ["community", "founder", "practitioner"], }, ], // Metadata totalEvents: { type: Number, default: 0 }, // Number of events in this series createdBy: { type: String, required: true }, createdAt: { type: Date, default: Date.now }, updatedAt: { type: Date, default: Date.now }, }); // Generate slug from title function generateSlug(title) { return title .toLowerCase() .replace(/[^a-z0-9]+/g, "-") .replace(/^-+|-+$/g, ""); } // Pre-save hook to generate slug seriesSchema.pre("save", async function (next) { try { // Always generate slug if it doesn't exist or if title has changed if (!this.slug || this.isNew || this.isModified("title")) { let baseSlug = generateSlug(this.title); let slug = baseSlug; let counter = 1; // Ensure slug is unique while (await this.constructor.findOne({ slug, _id: { $ne: this._id } })) { slug = `${baseSlug}-${counter}`; counter++; } this.slug = slug; } // Update timestamps this.updatedAt = new Date(); next(); } catch (error) { console.error("Error in pre-save hook:", error); next(error); } }); export default mongoose.models.Series || mongoose.model("Series", seriesSchema);