forked from UKSOURCE/cms.hailearning.edu.vn
206 lines
6.2 KiB
JavaScript
206 lines
6.2 KiB
JavaScript
const mongoose = require("mongoose");
|
|
|
|
const activitySchema = new mongoose.Schema(
|
|
{
|
|
// Hero section for activity page header (supports Activities and Booking variants)
|
|
hero: {
|
|
titleActivities: {
|
|
type: String,
|
|
trim: true,
|
|
default: "",
|
|
maxlength: 80,
|
|
},
|
|
titleBooking: {
|
|
type: String,
|
|
trim: true,
|
|
default: "",
|
|
maxlength: 80,
|
|
},
|
|
bannerImageActivities: {
|
|
type: String,
|
|
trim: true,
|
|
default: "",
|
|
maxlength: 255,
|
|
},
|
|
bannerImageBooking: {
|
|
type: String,
|
|
trim: true,
|
|
default: "",
|
|
maxlength: 255,
|
|
},
|
|
},
|
|
name: {
|
|
type: String,
|
|
required: true,
|
|
trim: true,
|
|
maxlength: 120,
|
|
},
|
|
price: {
|
|
type: Number,
|
|
required: true,
|
|
min: 0,
|
|
},
|
|
priceText: {
|
|
type: String,
|
|
trim: true,
|
|
maxlength: 32,
|
|
},
|
|
season: [
|
|
{
|
|
type: String,
|
|
enum: ["spring", "summer", "autumn", "winter"],
|
|
},
|
|
],
|
|
age: {
|
|
type: [Number],
|
|
validate: {
|
|
validator: function (v) {
|
|
return v.length === 2 && v[0] <= v[1];
|
|
},
|
|
message: "Age must be an array of [minAge, maxAge]",
|
|
},
|
|
},
|
|
locations: [
|
|
{
|
|
type: String,
|
|
trim: true,
|
|
maxlength: 80,
|
|
},
|
|
],
|
|
image: {
|
|
type: String,
|
|
trim: true,
|
|
maxlength: 255,
|
|
},
|
|
link: {
|
|
type: String,
|
|
trim: true,
|
|
maxlength: 255,
|
|
},
|
|
// Global filters document (single document in Activity collection)
|
|
filters: [
|
|
{
|
|
label: { type: String, required: true, trim: true, maxlength: 64 },
|
|
value: { type: String, required: true, trim: true, maxlength: 64 },
|
|
items: [
|
|
{
|
|
value: { type: String, required: true, maxlength: 64 },
|
|
label: { type: String, required: true, maxlength: 64 },
|
|
},
|
|
],
|
|
order: { type: Number, default: 0 },
|
|
},
|
|
],
|
|
program: {
|
|
type: String,
|
|
trim: true,
|
|
maxlength: 80,
|
|
},
|
|
rating: {
|
|
type: Number,
|
|
min: 1,
|
|
max: 5,
|
|
default: 4,
|
|
},
|
|
isActive: {
|
|
type: Boolean,
|
|
default: true,
|
|
},
|
|
order: {
|
|
type: Number,
|
|
default: 0,
|
|
},
|
|
// marker for the single document that stores global filters
|
|
isFiltersDoc: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
// Rich camp details from camp-detail field in activities.json
|
|
campDetail: {
|
|
type: mongoose.Schema.Types.Mixed,
|
|
default: {},
|
|
},
|
|
// Booking sessions - các đợt booking với thông số riêng
|
|
bookingSessions: [
|
|
{
|
|
sessionId: { type: String, required: true, maxlength: 80 },
|
|
startDate: { type: Date, required: true },
|
|
endDate: { type: Date, required: true },
|
|
overnightStays: { type: Number, required: true, default: 14 },
|
|
// Spots theo giới tính
|
|
totalMaleSpots: { type: Number, default: 25 },
|
|
totalFemaleSpots: { type: Number, default: 25 },
|
|
bookedMaleSpots: { type: Number, default: 0 },
|
|
bookedFemaleSpots: { type: Number, default: 0 },
|
|
price: { type: Number },
|
|
isActive: { type: Boolean, default: true },
|
|
// Danh sách booking cho session này
|
|
bookingList: [
|
|
{
|
|
address: { type: String, required: true, maxlength: 255 },
|
|
agreeNewsletter: { type: Boolean, default: false },
|
|
agreeTerms: { type: Boolean, required: true },
|
|
city: { type: String, required: true, maxlength: 80 },
|
|
country: { type: String, required: true, maxlength: 80 },
|
|
dietaryRestrictions: {
|
|
type: String,
|
|
enum: ['none', 'vegetarian', 'vegan', 'halal', 'kosher', 'gluten-free', 'other'],
|
|
default: 'none'
|
|
},
|
|
email: {
|
|
type: String,
|
|
required: true,
|
|
lowercase: true,
|
|
trim: true,
|
|
maxlength: 120
|
|
},
|
|
emergencyContact: { type: String, required: true, maxlength: 80 },
|
|
emergencyPhone: { type: String, required: true, maxlength: 40 },
|
|
medicalConditions: { type: String, default: '', maxlength: 500 },
|
|
numberOfParticipants: { type: Number, required: true, min: 1 },
|
|
parentFirstName: { type: String, required: true, trim: true, maxlength: 80 },
|
|
parentLastName: { type: String, required: true, trim: true, maxlength: 80 },
|
|
participantBirthDate: { type: Date, required: true },
|
|
participantFirstName: { type: String, required: true, trim: true, maxlength: 80 },
|
|
participantGender: {
|
|
type: String,
|
|
enum: ['male', 'female', 'other'],
|
|
required: true
|
|
},
|
|
participantLastName: { type: String, required: true, trim: true, maxlength: 80 },
|
|
phone: { type: String, required: true, maxlength: 40 },
|
|
postalCode: { type: String, required: true, maxlength: 20 },
|
|
sessionDate: { type: String, required: true, maxlength: 80 }, // sessionId reference
|
|
specialRequests: { type: String, default: '', maxlength: 500 },
|
|
// Thêm các trường quản lý
|
|
bookingStatus: {
|
|
type: String,
|
|
enum: ['pending', 'confirmed', 'cancelled', 'completed'],
|
|
default: 'pending'
|
|
},
|
|
paymentStatus: {
|
|
type: String,
|
|
enum: ['pending', 'partial', 'paid', 'refunded'],
|
|
default: 'pending'
|
|
},
|
|
totalAmount: { type: Number, default: 0 },
|
|
paidAmount: { type: Number, default: 0 },
|
|
bookingDate: { type: Date, default: Date.now },
|
|
confirmationCode: { type: String, unique: true, maxlength: 32 },
|
|
adminNotes: { type: String, default: '', maxlength: 1000 }
|
|
}
|
|
]
|
|
}
|
|
],
|
|
},
|
|
{timestamps: true}
|
|
);
|
|
|
|
// Add index for better query performance
|
|
activitySchema.index({name: 1});
|
|
activitySchema.index({isActive: 1, order: 1});
|
|
activitySchema.index({season: 1});
|
|
activitySchema.index({locations: 1});
|
|
|
|
module.exports = mongoose.model("Activity", activitySchema);
|