MerchantSchema
import mongoose from "mongoose";
const merchantSchema = new mongoose.Schema(
{
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: "user",
required: true,
index: true
},
storeProfile: {
storeName: {
type: String,
required: true,
trim: true,
maxLength: 100
},
category: {
type: String,
required: true,
enum: [
"Groceries",
"African Store",
"Electronics",
"Fashion",
"Home & Garden",
"Pharmacy & Beauty",
"Sports",
"Automotive",
"Books",
"Other",
],
default: "Other",
index: true
},
},
physicalAddress: {
region: {
type: String,
required: true,
enum: [
"United Kingdom",
"USA",
"Canada",
"UK",
"Australia",
"Germany",
"France",
"India",
"China",
"Japan",
"Other",
],
default: "United Kingdom",
},
streetAddress: {
type: String,
required: true,
trim: true,
},
city: {
type: String,
required: true,
trim: true,
},
postcode: {
type: String,
required: true,
trim: true,
uppercase: true,
},
},
handler: {
firstName: {
type: String,
required: true,
trim: true,
},
lastName: {
type: String,
required: true,
trim: true,
},
gender: {
type: String,
required: true,
enum: ["Male", "Female"],
default: "Male",
},
postalCode: {
type: String,
required: true,
trim: true,
uppercase: true,
},
},
contactDetails: {
email: {
type: String,
required: true,
trim: true,
lowercase: true,
match: [
/^[^\s@]+@[^\s@]+\.[^\s@]+$/,
"Please fill a valid email address",
],
},
phone: {
countryCode: {
type: String,
required: true,
default: '+44'
},
number: {
type: String,
required: true,
trim: true
},
isPublic: {
type: Boolean,
default: false
},
isVerified: {
type: Boolean,
default: false
},
verifiedAt: Date
}
},
phoneVerification: {
code: {
type: String,
select: false
},
codeExpiresAt: {
type: Date,
select: false
},
attempts: {
type: Number,
default: 0,
select: false
}
},
operatingHours: [
{
day: {
type: String,
enum: [ "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" ],
required: true
},
isOpen: {
type: Boolean,
default: false
},
openTime: {
type: String,
match: /^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$/
},
closeTime: {
type: String,
match: /^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$/
},
}
],
media: {
shopPhotos: [{
url: {
type: String,
required: true
},
publicId: String, // For cloud storage reference (e.g., Cloudinary)
type: {
type: String,
enum: ['image', 'video'],
default: 'image'
},
isPrimary: {
type: Boolean,
default: false
},
uploadedAt: {
type: Date,
default: Date.now
}
}],
maxPhotos: {
type: Number,
default: 10
}
},
advertisingCredits: {
amount: {
type: Number,
default: 500, // £500
min: 0
},
currency: {
type: String,
default: 'GBP'
},
validUntil: Date,
isClaimed: {
type: Boolean,
default: false
},
claimedAt: Date,
usedAmount: {
type: Number,
default: 0,
min: 0
}
},
onboardingStatus: {
currentStep: {
type: Number,
default: 1,
min: 1,
max: 12
},
isComplete: {
type: Boolean,
default: false
},
completedAt: Date,
stepsCompleted: {
type: Map,
of: Boolean,
default: new Map()
}
},
verification: {
status: {
type: String,
enum: ['pending', 'verified', 'rejected'],
default: 'pending'
},
verifiedAt: Date,
rejectionReason: String
},
isActive: {
type: Boolean,
default: true,
index: true
},
defaultPassword: {
type: String,
select: false
}
},
{
timestamps: true,
}
);
//Indexes for efficient querying
merchantSchema.index({ 'storeProfile.category': 1, isActive: 1 });
merchantSchema.index({ 'physicalAddress.city': 1 });
merchantSchema.index({ 'onboardingStatus.isComplete': 1 });
merchantSchema.index({ 'contactDetails.phone.number': 1 });
// Virtual for full name
merchantSchema.virtual('handler.fullName').get(function() {
return `${this.handler.firstName} ${this.handler.lastName}`;
});
// Ensure virtuals are included in JSON
merchantSchema.set('toJSON', { virtuals: true });
merchantSchema.set('toObject', { virtuals: true });
export default mongoose.model("Merchant", merchantSchema);
ProductSchema
import mongoose from 'mongoose';
const productSchema = new mongoose.Schema(
{
merchantId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'merchant',
required: true,
index: true
},
name: {
type: String,
required: true,
trim: true,
maxlength: 200
},
category: {
type: String,
required: true,
trim: true
},
description: {
type: String,
trim: true,
maxlength: 2000
},
media: {
primary: {
url: String,
publicId: String,
type: {
type: String,
enum: ['image', 'video']
}
},
gallery: [{
url: String,
publicId: String,
type: {
type: String,
enum: ['image', 'video']
}
}]
},
pricing: {
price: {
type: Number,
min: 0
},
currency: {
type: String,
default: 'GBP'
},
},
inventory: {
quantity: {
type: Number,
default: 0,
min: 0
},
trackInventory: {
type: Boolean,
default: true
},
lowStockThreshold: {
type: Number,
default: 5
}
},
isActive: {
type: Boolean,
default: true
},
isFeatured: {
type: Boolean,
default: false
},
tags: [String],
views: {
type: Number,
default: 0
}
},
{
timestamps: true,
}
);
// Indexes
productSchema.index({ merchantId: 1, isActive: 1 });
productSchema.index({ category: 1, isActive: 1 });
productSchema.index({ name: 'text', description: 'text', tags: 'text' });
const Product = mongoose.model('Product', productSchema);
export default Product;
serviceSchema
import mongoose from 'mongoose';
const serviceSchema = new mongoose.Schema(
{
merchantId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'merchant',
required: true,
index: true
},
name: {
type: String,
required: true,
trim: true,
maxlength: 200
},
category: {
type: String,
required: true,
trim: true
},
description: {
type: String,
trim: true,
maxlength: 2000
},
media: {
primary: {
url: String,
publicId: String,
type: {
type: String,
enum: ['image', 'video']
}
},
gallery: [{
url: String,
publicId: String,
type: {
type: String,
enum: ['image', 'video']
}
}]
},
booking: {
isBookable: {
type: Boolean,
default: false
},
duration: Number, // in minutes
maxBookingsPerDay: Number
},
isActive: {
type: Boolean,
default: true
},
isFeatured: {
type: Boolean,
default: false
},
tags: [String],
views: {
type: Number,
default: 0
}
},
{
timestamps: true,
}
);
// Indexes
serviceSchema.index({ merchantId: 1, isActive: 1 });
serviceSchema.index({ category: 1, isActive: 1 });
serviceSchema.index({ name: 'text', description: 'text', tags: 'text' });
serviceSchema.set('toJSON', { virtuals: true });
serviceSchema.set('toObject', { virtuals: true });
const Service = mongoose.model('Service', serviceSchema);
export default Service;
Top comments (0)