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.

210 lines (189 loc) 6.37 kB
import mongoose from 'mongoose'; const wishlistItemSchema = new mongoose.Schema({ // Product reference (minimart only) productId: { type: mongoose.Schema.Types.ObjectId, required: true }, productType: { type: String, enum: ['minimart'], // Only minimart allowed required: true }, // Product details (matches cart structure) productDetails: { name: { type: String, required: true }, price: { type: Number, required: true }, description: String, images: [{ url: { type: String, required: true }, alt: String, isPrimary: { type: Boolean, default: false } }], category: String, stock: Number, sku: String, barcode: String, brand: String, specifications: mongoose.Schema.Types.Mixed, features: [String], pricing: { baseRate: { type: Number, required: true }, rateType: { type: String, enum: ['fixed'], default: 'fixed' }, currency: { type: String, default: 'NGN' } }, isActive: { type: Boolean, default: true } }, // User notes about this item notes: String, // Priority level (1-5, 5 being highest) priority: { type: Number, min: 1, max: 5, default: 3 }, // Date added to wishlist addedAt: { type: Date, default: Date.now } }, { _id: true }); const wishlistSchema = new mongoose.Schema({ // User who owns this wishlist user: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true, unique: true }, // Wishlist items items: [wishlistItemSchema], // Wishlist settings settings: { isPublic: { type: Boolean, default: false }, allowNotifications: { type: Boolean, default: true }, sortBy: { type: String, enum: ['addedAt', 'priority', 'price', 'name'], default: 'addedAt' }, sortOrder: { type: String, enum: ['asc', 'desc'], default: 'desc' } }, // Statistics stats: { totalItems: { type: Number, default: 0 }, totalValue: { type: Number, default: 0 }, lastUpdated: { type: Date, default: Date.now } } }, { timestamps: true, toJSON: { virtuals: true }, toObject: { virtuals: true } }); // Indexes for efficient queries // Note: user field already has unique index from schema definition wishlistSchema.index({ 'items.productId': 1, 'items.productType': 1 }); wishlistSchema.index({ 'items.addedAt': -1 }); // Virtual for item count wishlistSchema.virtual('itemCount').get(function () { return this.items.length; }); // Virtual for total value wishlistSchema.virtual('totalValue').get(function () { return this.items.reduce((total, item) => total + (item.productDetails.price || 0), 0); }); // Pre-save middleware to update stats wishlistSchema.pre('save', function (next) { this.stats.totalItems = this.items.length; this.stats.totalValue = this.items.reduce((total, item) => total + (item.productDetails.price || 0), 0); this.stats.lastUpdated = new Date(); next(); }); // Method to add item to wishlist wishlistSchema.methods.addItem = function (productId, productType, productDetails, notes = '', priority = 3) { // Check if item already exists const existingItem = this.items.find(item => item.productId.toString() === productId.toString() && item.productType === productType ); if (existingItem) { // Update existing item existingItem.notes = notes; existingItem.priority = priority; existingItem.addedAt = new Date(); } else { // Add new item this.items.push({ productId, productType, productDetails, notes, priority, addedAt: new Date() }); } return this.save(); }; // Method to remove item from wishlist wishlistSchema.methods.removeItem = function (productId, productType) { this.items = this.items.filter(item => !(item.productId.toString() === productId.toString() && item.productType === productType) ); return this.save(); }; // Method to clear wishlist wishlistSchema.methods.clear = function () { this.items = []; return this.save(); }; // Method to get sorted items wishlistSchema.methods.getSortedItems = function () { const { sortBy, sortOrder } = this.settings; const items = [...this.items]; items.sort((a, b) => { let aValue, bValue; switch (sortBy) { case 'addedAt': aValue = new Date(a.addedAt); bValue = new Date(b.addedAt); break; case 'priority': aValue = a.priority; bValue = b.priority; break; case 'price': aValue = a.productDetails.price || 0; bValue = b.productDetails.price || 0; break; case 'name': aValue = a.productDetails.name.toLowerCase(); bValue = b.productDetails.name.toLowerCase(); break; default: aValue = new Date(a.addedAt); bValue = new Date(b.addedAt); } if (sortOrder === 'asc') { return aValue > bValue ? 1 : -1; } else { return aValue < bValue ? 1 : -1; } }); return items; }; // Static method to get or create wishlist for user wishlistSchema.statics.getOrCreateWishlist = async function (userId) { let wishlist = await this.findOne({ user: userId }); if (!wishlist) { wishlist = new this({ user: userId, items: [], settings: { isPublic: false, allowNotifications: true, sortBy: 'addedAt', sortOrder: 'desc' } }); await wishlist.save(); } return wishlist; }; export default mongoose.model('Wishlist', wishlistSchema);