UNPKG

@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
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);