const mongoose = require('mongoose'); /** * نموذج معرض القوالب - يدعم القوالب الجاهزة للتحميل والتثبيت بنقرة واحدة * @version 2.0.0 - متوافق مع نظام التطبيق الكامل للقوالب */ const templateReviewSchema = new mongoose.Schema({ userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true }, rating: { type: Number, required: true, min: 1, max: 5 }, comment: { type: String, maxlength: 500 }, createdAt: { type: Date, default: Date.now } }); const templateVersionSchema = new mongoose.Schema({ version: { type: String, required: true, match: /^\d+\.\d+\.\d+$/ }, changelog: { type: String }, zipUrl: { type: String }, releasedAt: { type: Date, default: Date.now } }); const templateSchema = new mongoose.Schema({ // ============================================ // المعلومات الأساسية // ============================================ name: { type: String, required: true, trim: true, maxlength: 100, index: true }, slug: { type: String, required: true, unique: true, lowercase: true, trim: true, match: /^[a-z0-9]+(?:-[a-z0-9]+)*$/ }, description: { type: String, required: true, maxlength: 500 }, // ============================================ // التصنيفات // ============================================ category: { type: String, enum: ['fashion', 'electronics', 'food', 'art', 'beauty', 'sports', 'education', 'business', 'portfolio', 'blog', 'minimal', 'modern', 'dark', 'light'], default: 'general', index: true }, tags: [{ type: String, trim: true, lowercase: true, index: true }], // ============================================ // الصور والمعاينة // ============================================ previewImage: { type: String, required: true }, previewImages: [{ type: String, description: { type: String } }], previewVideo: { type: String }, demoUrl: { type: String }, thumbnail: { type: String }, // ============================================ // ملفات القالب // ============================================ themeZipUrl: { type: String, required: true }, themeZipSize: { type: Number }, themeZipHash: { type: String }, // ============================================ // 🎨 بيانات القالب الكاملة (محدثة) // ============================================ themeData: { // 🎨 الألوان colors: { primary: { type: String, default: '#3b82f6' }, secondary: { type: String, default: '#8b5cf6' }, accent: { type: String, default: '#f59e0b' }, background: { type: String, default: '#ffffff' }, text: { type: String, default: '#1f2937' }, textLight: { type: String, default: '#6b7280' }, border: { type: String, default: '#e5e7eb' }, success: { type: String, default: '#10b981' }, error: { type: String, default: '#ef4444' }, warning: { type: String, default: '#f59e0b' } }, // 🎨 الخطوط typography: { fontFamily: { type: String, default: 'Inter, sans-serif' }, headingFont: { type: String, default: 'Inter, sans-serif' }, baseFontSize: { type: String, default: '16px' }, enableGoogleFonts: { type: Boolean, default: true } }, // 🎨 التخطيط layout: { type: { type: String, enum: ['full-width', 'boxed', 'fluid'], default: 'full-width' }, headerLayout: { type: String, enum: ['default', 'centered', 'minimal', 'transparent'], default: 'default' }, footerLayout: { type: String, enum: ['default', 'minimal', 'columns'], default: 'default' }, sidebarPosition: { type: String, enum: ['left', 'right', 'none'], default: 'none' }, columns: { type: Number, default: 4, min: 2, max: 6 }, stickyHeader: { type: Boolean, default: false }, containerWidth: { type: String, default: '1280px' }, containerPadding: { type: String, default: '20px' }, showBreadcrumb: { type: Boolean, default: true }, backToTop: { type: Boolean, default: true }, showSearchBar: { type: Boolean, default: true }, showCategories: { type: Boolean, default: true }, productsPerRowMobile: { type: Number, default: 2 }, productsPerRowTablet: { type: Number, default: 3 }, enableStickyAddToCart: { type: Boolean, default: true }, enableCompareProducts: { type: Boolean, default: false }, enableWishlist: { type: Boolean, default: false }, enableRecentlyViewed: { type: Boolean, default: true } }, // 🎨 تأثيرات إضافية borderRadius: { type: Number, default: 16, min: 0, max: 48 }, shadowType: { type: String, enum: ['none', 'sm', 'md', 'lg', 'xl'], default: 'md' }, animationType: { type: String, enum: ['none', 'lift', 'scale', 'glow'], default: 'lift' }, glassEffect: { type: Boolean, default: false }, // 🎨 الخلفية backgroundType: { type: String, enum: ['default', 'dots', 'grid', 'cross', 'gradient', 'image'], default: 'default' }, backgroundValue: { type: String, default: '' }, // 🎨 CSS/JS مخصص customCss: { type: String, default: '' }, customJs: { type: String, default: '' }, // 📄 الأقسام sections: { type: mongoose.Schema.Types.Mixed, default: [] }, // 🏪 معلومات المتجر الأساسية storeInfo: { storeName: { type: String, default: '' }, storeLogo: { type: String, default: '' }, storeBanner: { type: String, default: '' }, storeDescription: { type: String, default: '' }, contactEmail: { type: String, default: '' } }, // 📄 الصفحات pages: [{ slug: { type: String, required: true }, title: { type: String, required: true }, content: { type: String, default: '' }, css: { type: String, default: '' }, js: { type: String, default: '' }, seo: { title: { type: String }, description: { type: String }, keywords: { type: String }, ogImage: { type: String } }, isEnabled: { type: Boolean, default: true }, order: { type: Number, default: 0 }, type: { type: String, default: 'custom' } }], // 📄 الصفحات الافتراضية defaultPages: { about: { title: { type: String, default: 'About Us' }, content: { type: String, default: '' }, seo: { type: mongoose.Schema.Types.Mixed, default: {} } }, contact: { title: { type: String, default: 'Contact Us' }, content: { type: String, default: '' }, seo: { type: mongoose.Schema.Types.Mixed, default: {} } } }, // 🔗 روابط التواصل الاجتماعي socialLinks: { instagram: { type: String, default: '' }, facebook: { type: String, default: '' }, twitter: { type: String, default: '' }, tiktok: { type: String, default: '' }, linkedin: { type: String, default: '' }, youtube: { type: String, default: '' }, whatsapp: { type: String, default: '' }, telegram: { type: String, default: '' } }, // 💰 إعدادات الدفع paymentSettings: { enableCOD: { type: Boolean, default: false }, enableInstallments: { type: Boolean, default: false }, minOrderAmount: { type: Number, default: 0 }, freeShippingThreshold: { type: Number, default: 0 }, taxRate: { type: Number, default: 0, min: 0, max: 100 }, shippingCost: { type: Number, default: 0 } }, // 🎯 إعدادات المنتجات productsSettings: { productViewType: { type: String, enum: ['modal', 'page'], default: 'modal' }, productsPerRow: { type: Number, default: 4, min: 2, max: 6 }, enableQuickView: { type: Boolean, default: true }, enableProductReviews: { type: Boolean, default: true }, autoApproveReviews: { type: Boolean, default: false } }, // 🎨 الهيدر والفوتر المخصص customHeaderFooter: { header: { type: String, default: '' }, footer: { type: String, default: '' }, sidebar: { type: String, default: '' }, productCard: { type: String, default: '' }, categoryCard: { type: String, default: '' }, css: { type: String, default: '' }, js: { type: String, default: '' } }, templateUrl: { type: String, default: null }, uploadedFiles: { type: mongoose.Schema.Types.Mixed, default: null }, hasIndexHtml: { type: Boolean, default: false }, // 💱 العملة currency: { type: String, default: 'USD', uppercase: true }, // 📑 ترتيب الصفحات pagesOrder: { type: mongoose.Schema.Types.Mixed, default: { products: 1, about: 2, contact: 3 } }, // 🏷️ أكواد الخصم storeCoupons: [{ code: { type: String, uppercase: true }, discount: { type: Number, min: 0, max: 100 }, discountType: { type: String, enum: ['percentage', 'fixed'], default: 'percentage' }, minPurchase: { type: Number, default: 0 }, maxDiscount: { type: Number }, validUntil: { type: Date }, isActive: { type: Boolean, default: true } }], // 📦 مناطق الشحن shippingZones: [{ name: { type: String }, countries: [{ type: String }], cost: { type: Number, default: 0 }, freeShippingAbove: { type: Number, default: 0 }, estimatedDays: { type: String, default: '3-7 business days' } }], // 🔍 إعدادات SEO seoSettings: { metaTitleTemplate: { type: String, default: '{{title}} | {{storeName}}' }, metaDescriptionTemplate: { type: String, default: '' }, enableBreadcrumbs: { type: Boolean, default: true }, sitemapEnabled: { type: Boolean, default: true } }, // ⚡ إعدادات الأداء performanceSettings: { enableLazyLoad: { type: Boolean, default: true }, enableImageOptimization: { type: Boolean, default: true }, enablePreload: { type: Boolean, default: true }, cacheTTL: { type: Number, default: 3600 } }, // 🎨 معلومات إضافية عن القالب templateMeta: { minVersion: { type: String, default: '1.0.0' }, maxVersion: { type: String, default: '3.0.0' }, requires: [{ type: String }], screenshots: [{ type: String }], demoUrl: { type: String }, documentationUrl: { type: String }, supportUrl: { type: String } } }, // ============================================ // متطلبات التثبيت // ============================================ requiredSubscription: { type: String, enum: ['free', 'basic', 'pro', 'business', 'enterprise'], default: 'free', index: true }, requiredPermissions: [{ type: String, enum: ['store_enabled', 'custom_domain', 'analytics'] }], // ============================================ // إحصائيات // ============================================ downloadsCount: { type: Number, default: 0, index: true }, viewsCount: { type: Number, default: 0 }, rating: { type: Number, default: 0, min: 0, max: 5 }, ratingCount: { type: Number, default: 0 }, reviews: [templateReviewSchema], // ============================================ // الإصدارات // ============================================ version: { type: String, default: '1.0.0', match: /^\d+\.\d+\.\d+$/ }, versions: [templateVersionSchema], // ============================================ // حالة القالب // ============================================ isActive: { type: Boolean, default: true, index: true }, isFeatured: { type: Boolean, default: false, index: true }, isVerified: { type: Boolean, default: false, index: true }, // ============================================ // معلومات الناشر // ============================================ authorId: { type: mongoose.Schema.Types.ObjectId, ref: 'User', index: true }, authorName: { type: String }, authorEmail: { type: String }, authorWebsite: { type: String }, // ============================================ // إعدادات إضافية // ============================================ settings: { requiresSetup: { type: Boolean, default: false }, setupInstructions: { type: String }, compatibleWith: [{ platformVersion: { type: String }, minVersion: { type: String } }], dependencies: [{ type: String }] }, // ============================================ // سجل التثبيت // ============================================ installedBy: [{ userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }, installedAt: { type: Date, default: Date.now }, storeId: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }, appliedSettings: { type: mongoose.Schema.Types.Mixed } // تخزين الإعدادات التي تم تطبيقها }], createdAt: { type: Date, default: Date.now, immutable: true }, updatedAt: { type: Date, default: Date.now } }, { timestamps: true, toJSON: { virtuals: true }, toObject: { virtuals: true } }); // ============================================ // ✅ Indexes محسنة للبحث // ============================================ templateSchema.index({ category: 1, isActive: 1, isFeatured: -1 }); templateSchema.index({ downloadsCount: -1, rating: -1 }); templateSchema.index({ tags: 1 }); templateSchema.index({ name: 'text', description: 'text', tags: 'text' }); templateSchema.index({ requiredSubscription: 1 }); templateSchema.index({ 'installedBy.userId': 1 }); templateSchema.index({ 'themeData.currency': 1 }); templateSchema.index({ 'themeData.layout.type': 1 }); templateSchema.index({ version: -1 }); // ============================================ // ✅ Virtuals // ============================================ templateSchema.virtual('downloadUrl').get(function() { return `/api/templates/${this.slug}/download`; }); templateSchema.virtual('previewUrl').get(function() { return `/api/templates/${this.slug}/preview`; }); templateSchema.virtual('isFree').get(function() { return this.requiredSubscription === 'free'; }); templateSchema.virtual('totalInstalls').get(function() { return this.installedBy?.length || 0; }); templateSchema.virtual('lastInstalledAt').get(function() { if (!this.installedBy || this.installedBy.length === 0) return null; return this.installedBy[this.installedBy.length - 1]?.installedAt; }); // ============================================ // ✅ Pre-save Middleware // ============================================ templateSchema.pre('save', function(next) { this.updatedAt = Date.now(); // إنشاء slug من الاسم إذا لم يكن موجوداً if (!this.slug && this.name) { this.slug = this.name .toLowerCase() .replace(/[^a-z0-9]+/g, '-') .replace(/^-|-$/g, ''); } // تنظيف روابط الصور if (this.previewImages && this.previewImages.length > 0) { this.previewImages = this.previewImages.filter(img => img && img.trim() !== ''); } // تحديث متوسط التقييم عند إضافة مراجعة جديدة if (this.reviews && this.reviews.length > 0) { const totalRating = this.reviews.reduce((sum, r) => sum + r.rating, 0); this.rating = totalRating / this.reviews.length; this.ratingCount = this.reviews.length; } next(); }); // ============================================ // ✅ Static Methods // ============================================ /** * جلب القوالب المميزة */ templateSchema.statics.getFeatured = async function(limit = 6) { return this.find({ isActive: true, isFeatured: true }) .sort({ downloadsCount: -1, rating: -1 }) .limit(limit) .select('name slug category previewImage downloadsCount rating requiredSubscription themeData.storeInfo'); }; /** * جلب القوالب الأكثر تحميلاً */ templateSchema.statics.getMostDownloaded = async function(limit = 12) { return this.find({ isActive: true }) .sort({ downloadsCount: -1 }) .limit(limit) .select('name slug category previewImage downloadsCount rating requiredSubscription'); }; /** * جلب قوالب حسب التصنيف */ templateSchema.statics.getByCategory = async function(category, limit = 20) { return this.find({ isActive: true, category }) .sort({ downloadsCount: -1 }) .limit(limit); }; /** * البحث في القوالب */ templateSchema.statics.searchTemplates = async function(query, filters = {}) { const searchQuery = { isActive: true }; if (query) { searchQuery.$text = { $search: query }; } if (filters.category && filters.category !== 'all') { searchQuery.category = filters.category; } if (filters.requiredSubscription && filters.requiredSubscription !== 'all') { searchQuery.requiredSubscription = filters.requiredSubscription; } if (filters.minRating) { searchQuery.rating = { $gte: parseFloat(filters.minRating) }; } let sort = {}; switch (filters.sort) { case 'popular': sort = { downloadsCount: -1 }; break; case 'rating': sort = { rating: -1 }; break; case 'newest': sort = { createdAt: -1 }; break; default: sort = { downloadsCount: -1 }; } return this.find(searchQuery) .sort(sort) .limit(filters.limit || 20) .skip(filters.offset || 0); }; /** * زيادة عدد المشاهدات */ templateSchema.statics.incrementViews = async function(templateId) { return this.findByIdAndUpdate(templateId, { $inc: { viewsCount: 1 } }); }; /** * زيادة عدد التحميلات وتسجيل التثبيت */ templateSchema.statics.recordDownload = async function(templateId, userId, storeId, appliedSettings = {}) { const template = await this.findById(templateId); if (!template) throw new Error('Template not found'); template.downloadsCount += 1; // تسجيل التثبيت مع الإعدادات المطبقة template.installedBy.push({ userId, storeId, installedAt: new Date(), appliedSettings }); // الاحتفاظ بآخر 100 تثبيت فقط if (template.installedBy.length > 100) { template.installedBy = template.installedBy.slice(-100); } await template.save(); return template; }; /** * إضافة تقييم للقالب */ templateSchema.statics.addReview = async function(templateId, userId, rating, comment) { const template = await this.findById(templateId); if (!template) throw new Error('Template not found'); const existingReview = template.reviews.find(r => r.userId.toString() === userId); if (existingReview) { existingReview.rating = rating; existingReview.comment = comment; existingReview.createdAt = new Date(); } else { template.reviews.push({ userId, rating, comment }); } await template.save(); return template; }; /** * جلب قوالب مشابهة حسب التصنيف والعلامات */ templateSchema.statics.getSimilarTemplates = async function(templateId, limit = 4) { const template = await this.findById(templateId); if (!template) return []; return this.find({ isActive: true, _id: { $ne: templateId }, $or: [ { category: template.category }, { tags: { $in: template.tags } } ] }) .sort({ downloadsCount: -1, rating: -1 }) .limit(limit); }; /** * تحديث إحصائيات القالب بعد التطبيق */ templateSchema.statics.updateAfterApply = async function(templateId, userId, success = true) { if (!success) return; const template = await this.findById(templateId); if (template) { template.downloadsCount += 1; // تحديث آخر تثبيت ناجح const existingInstall = template.installedBy.find(i => i.userId.toString() === userId); if (existingInstall) { existingInstall.installedAt = new Date(); } else { template.installedBy.push({ userId, storeId: userId, installedAt: new Date() }); } await template.save(); } }; const TemplateStore = mongoose.model('TemplateStore', templateSchema); module.exports = TemplateStore;