forked from UKSOURCE/cms.hailearning.edu.vn
first commit
This commit is contained in:
194
models/activity.js
Normal file
194
models/activity.js
Normal file
@@ -0,0 +1,194 @@
|
||||
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: ''
|
||||
},
|
||||
titleBooking: {
|
||||
type: String,
|
||||
trim: true,
|
||||
default: ''
|
||||
},
|
||||
bannerImageActivities: {
|
||||
type: String,
|
||||
trim: true,
|
||||
default: ''
|
||||
},
|
||||
bannerImageBooking: {
|
||||
type: String,
|
||||
trim: true,
|
||||
default: ''
|
||||
},
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
trim: true,
|
||||
},
|
||||
price: {
|
||||
type: Number,
|
||||
required: true,
|
||||
min: 0,
|
||||
},
|
||||
priceText: {
|
||||
type: String,
|
||||
trim: true,
|
||||
},
|
||||
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,
|
||||
},
|
||||
],
|
||||
image: {
|
||||
type: String,
|
||||
trim: true,
|
||||
},
|
||||
link: {
|
||||
type: String,
|
||||
trim: true,
|
||||
},
|
||||
// Global filters document (single document in Activity collection)
|
||||
filters: [
|
||||
{
|
||||
label: { type: String, required: true, trim: true },
|
||||
value: { type: String, required: true, trim: true },
|
||||
items: [
|
||||
{
|
||||
value: { type: String, required: true },
|
||||
label: { type: String, required: true },
|
||||
},
|
||||
],
|
||||
order: { type: Number, default: 0 },
|
||||
},
|
||||
],
|
||||
program: {
|
||||
type: String,
|
||||
trim: true,
|
||||
},
|
||||
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 },
|
||||
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 },
|
||||
agreeNewsletter: { type: Boolean, default: false },
|
||||
agreeTerms: { type: Boolean, required: true },
|
||||
city: { type: String, required: true },
|
||||
country: { type: String, required: true },
|
||||
dietaryRestrictions: {
|
||||
type: String,
|
||||
enum: ['none', 'vegetarian', 'vegan', 'halal', 'kosher', 'gluten-free', 'other'],
|
||||
default: 'none'
|
||||
},
|
||||
email: {
|
||||
type: String,
|
||||
required: true,
|
||||
lowercase: true,
|
||||
trim: true
|
||||
},
|
||||
emergencyContact: { type: String, required: true },
|
||||
emergencyPhone: { type: String, required: true },
|
||||
medicalConditions: { type: String, default: '' },
|
||||
numberOfParticipants: { type: Number, required: true, min: 1 },
|
||||
parentFirstName: { type: String, required: true, trim: true },
|
||||
parentLastName: { type: String, required: true, trim: true },
|
||||
participantBirthDate: { type: Date, required: true },
|
||||
participantFirstName: { type: String, required: true, trim: true },
|
||||
participantGender: {
|
||||
type: String,
|
||||
enum: ['male', 'female', 'other'],
|
||||
required: true
|
||||
},
|
||||
participantLastName: { type: String, required: true, trim: true },
|
||||
phone: { type: String, required: true },
|
||||
postalCode: { type: String, required: true },
|
||||
sessionDate: { type: String, required: true }, // sessionId reference
|
||||
specialRequests: { type: String, default: '' },
|
||||
// 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 },
|
||||
adminNotes: { type: String, default: '' }
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
{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);
|
||||
Reference in New Issue
Block a user