@ideal-photography/shared
Version:
Shared MongoDB and utility logic for Ideal Photography PWAs: users, products, services, bookings, orders/cart, galleries, reviews, notifications, campaigns, settings, audit logs, minimart items/orders, and push notification subscriptions.
120 lines (109 loc) • 3.65 kB
JavaScript
import mongoose from 'mongoose';
const studioSessionSchema = new mongoose.Schema({
name: {
type: String,
required: [true, 'Session name is required'],
trim: true,
maxlength: [100, 'Session name must be less than 100 characters']
},
description: {
type: String,
required: [true, 'Description is required'],
trim: true,
maxlength: [1000, 'Description must be less than 1000 characters']
},
price: {
type: Number,
required: [true, 'Price is required'],
min: [0, 'Price must be a positive number']
},
duration: {
type: Number,
required: [true, 'Duration is required'],
min: [0.5, 'Duration must be at least 30 minutes (0.5 hours)'],
max: [12, 'Duration cannot exceed 12 hours']
},
tags: {
type: [String],
default: [],
validate: {
validator: function (tags) {
return tags.length <= 10;
},
message: 'Cannot have more than 10 tags'
}
},
// Media - Array of image URLs (max 2 for UI cards)
images: {
type: [String],
default: [],
validate: {
validator: function (images) {
return images.length <= 2;
},
message: 'Maximum 2 images allowed for studio sessions'
}
},
rating: {
type: Number,
default: 0,
min: [0, 'Rating must be between 0 and 5'],
max: [5, 'Rating must be between 0 and 5']
},
reviewCount: {
type: Number,
default: 0,
min: [0, 'Review count cannot be negative']
},
isActive: {
type: Boolean,
default: true
},
createdBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Admin',
required: [true, 'Creator is required']
},
lastModifiedBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Admin'
}
}, {
timestamps: true,
toJSON: { virtuals: true },
toObject: { virtuals: true }
});
// Virtual for final price (same as price since we removed originalPrice complexity)
studioSessionSchema.virtual('finalPrice').get(function () {
return this.price;
});
// Virtual for primary image
studioSessionSchema.virtual('primaryImage').get(function () {
return this.images && this.images.length > 0 ? this.images[0] : null;
});
// Indexes for performance
studioSessionSchema.index({ isActive: 1, createdAt: -1 });
studioSessionSchema.index({ price: 1 });
studioSessionSchema.index({ rating: -1 });
studioSessionSchema.index({ name: 'text', description: 'text', tags: 'text' });
// Static method to get active sessions
studioSessionSchema.statics.getActiveSessions = function () {
return this.find({ isActive: true }).sort({ createdAt: -1 });
};
// Instance method to toggle active status
studioSessionSchema.methods.toggleActive = function () {
this.isActive = !this.isActive;
return this.save();
};
// Instance method to update rating
studioSessionSchema.methods.updateRating = function (newRating, incrementReviewCount = true) {
if (incrementReviewCount) {
const totalRating = (this.rating * this.reviewCount) + newRating;
this.reviewCount += 1;
this.rating = Math.round((totalRating / this.reviewCount) * 10) / 10; // Round to 1 decimal
} else {
this.rating = newRating;
}
return this.save();
};
export default mongoose.model('StudioSession', studioSessionSchema);