const mongoose = require("mongoose"); // Schema cho hero section const heroSchema = new mongoose.Schema( { title: { type: String, required: true, trim: true, maxlength: 40, }, backgroundImage: { type: String, trim: true, default: "", maxlength: 255, }, overlayColor: { type: String, trim: true, default: "rgba(0, 0, 0, 0)", }, sectionClass: { type: String, trim: true, default: "", }, titleClass: { type: String, trim: true, default: "", }, enableScrollspy: { type: Boolean, default: false, }, backgroundPosition: { type: String, trim: true, default: "center", }, }, { _id: false } ); // Schema cho contact card const contactCardSchema = new mongoose.Schema( { type: { type: String, required: true, trim: true, enum: [ "phone", "email", "location", "hours", "website", "social", "custom", ], }, title: { type: String, required: true, trim: true, maxlength: 40, }, content: { type: [{ type: String, maxlength: 96 }], default: [], }, iconType: { type: String, required: false, trim: true, default: "", maxlength: 255, }, iconSource: { type: String, required: false, trim: true, enum: ["fontawesome", "image"], default: "fontawesome", }, }, { _id: false } ); // Schema cho map coordinates const coordinatesSchema = new mongoose.Schema( { lat: { type: Number, required: true, }, lng: { type: Number, required: true, }, }, { _id: false } ); // Schema cho tile layer const tileLayerSchema = new mongoose.Schema( { url: { type: String, required: true, trim: true, }, attribution: { type: String, trim: true, default: "", }, maxZoom: { type: Number, default: 18, }, minZoom: { type: Number, default: 0, }, }, { _id: false } ); // Schema cho map const mapSchema = new mongoose.Schema( { coordinates: { type: coordinatesSchema, required: true, }, zoom: { type: Number, default: 15, }, location: { type: String, required: true, trim: true, maxlength: 120, }, markerTitle: { type: String, trim: true, default: "", maxlength: 48, }, embedUrl: { type: String, trim: true, default: "", maxlength: 1000, }, tileLayer: { type: tileLayerSchema, required: true, }, }, { _id: false } ); // Schema cho form field const formFieldSchema = new mongoose.Schema( { name: { type: String, required: true, trim: true, maxlength: 32, }, label: { type: String, trim: true, default: "", maxlength: 32, }, type: { type: String, required: true, trim: true, enum: ["text", "email", "tel", "textarea", "programme", "date"], }, placeholder: { type: String, trim: true, default: "", maxlength: 72, }, required: { type: Boolean, default: false, }, colClass: { type: String, trim: true, default: "col-lg-12", }, programmeName: { type: String, trim: true, default: "", maxlength: 48, }, }, { _id: false } ); // Schema cho submit button const submitButtonSchema = new mongoose.Schema( { text: { type: String, required: true, trim: true, maxlength: 24, }, icon: { type: String, trim: true, default: "fa-solid fa-arrow-right", }, buttonClass: { type: String, trim: true, default: "theme-btn style-2", }, }, { _id: false } ); // Schema cho form const formSchema = new mongoose.Schema( { sectionLabel: { type: String, trim: true, default: "", maxlength: 32, }, heading: { type: String, trim: true, default: "", maxlength: 48, }, description: { type: String, trim: true, default: "", maxlength: 160, }, fields: { type: [formFieldSchema], default: [], }, submitButton: { type: submitButtonSchema, required: true, }, }, { _id: false } ); // Main Contact Schema const contactSchema = new mongoose.Schema( { name: { type: String, default: "default", unique: true, }, hero: { type: heroSchema, required: true, }, contactCards: { type: [contactCardSchema], default: [], }, map: { type: mapSchema, required: true, }, form: { type: formSchema, required: true, }, }, { timestamps: true, } ); // Mapping iconType cũ sang Font Awesome icon mới const iconTypeMapping = { phone: "fas fa-phone", email: "fas fa-envelope", location: "fas fa-map-marker-alt", clock: "fas fa-clock", hours: "fas fa-clock", }; // Tạo migration script để import dữ liệu từ contact-data.json contactSchema.statics.migrateFromJson = async function (jsonData) { try { // Kiểm tra xem đã có contact mặc định chưa const existingContact = await this.findOne({ name: "default" }); // Xử lý và chuẩn hóa dữ liệu từ JSON const processedData = { hero: { title: jsonData.hero?.title || "Contact Us", backgroundImage: jsonData.hero?.backgroundImage || "", overlayColor: jsonData.hero?.overlayColor || "rgba(0, 0, 0, 0)", sectionClass: jsonData.hero?.sectionClass || "", titleClass: jsonData.hero?.titleClass || "", enableScrollspy: jsonData.hero?.enableScrollspy || false, backgroundPosition: jsonData.hero?.backgroundPosition || "center", }, contactCards: (jsonData.contactCards || []).map((card) => { let iconType = card.iconType || ""; let iconSource = card.iconSource; // Nếu không có iconSource, tự động detect từ iconType if (!iconSource) { // Nếu iconType là image path (bắt đầu bằng /uploads/ hoặc http) if ( iconType.startsWith("/uploads/") || iconType.startsWith("http://") || iconType.startsWith("https://") ) { iconSource = "image"; } else { // Nếu iconType là string cũ (phone, email, location, clock) iconSource = "fontawesome"; // Map iconType cũ sang Font Awesome icon mới if (iconTypeMapping[iconType]) { iconType = iconTypeMapping[iconType]; } else if ( iconType && !iconType.startsWith("fas ") && !iconType.startsWith("fab ") ) { // Nếu iconType không phải là Font Awesome class hợp lệ, thử map iconType = iconTypeMapping[iconType] || iconType; } } } else { // Nếu đã có iconSource nhưng iconType là string cũ, map sang Font Awesome if ( iconSource === "fontawesome" && iconType && !iconType.startsWith("fas ") && !iconType.startsWith("fab ") && iconTypeMapping[iconType] ) { iconType = iconTypeMapping[iconType]; } } return { type: card.type || "custom", title: card.title || "", content: Array.isArray(card.content) ? card.content : [], iconType: iconType, iconSource: iconSource || "fontawesome", }; }), map: { coordinates: { lat: jsonData.map?.coordinates?.lat || 0, lng: jsonData.map?.coordinates?.lng || 0, }, zoom: jsonData.map?.zoom || 15, location: jsonData.map?.location || "", markerTitle: jsonData.map?.markerTitle || "", embedUrl: jsonData.map?.embedUrl || "", tileLayer: { url: jsonData.map?.tileLayer?.url || "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", attribution: jsonData.map?.tileLayer?.attribution || "", maxZoom: jsonData.map?.tileLayer?.maxZoom || 18, minZoom: jsonData.map?.tileLayer?.minZoom || 0, }, }, form: { sectionLabel: jsonData.form?.sectionLabel || "", heading: jsonData.form?.heading || "", description: jsonData.form?.description || "", fields: (jsonData.form?.fields || []).map((field) => ({ name: field.name || "", label: field.label || "", type: field.type || "text", placeholder: field.placeholder || "", required: field.required || false, colClass: field.colClass || "col-lg-12", programmeName: field.programmeName || "", })), submitButton: { text: jsonData.form?.submitButton?.text || "Send Message", icon: jsonData.form?.submitButton?.icon || "fa-solid fa-arrow-right", buttonClass: jsonData.form?.submitButton?.buttonClass || "theme-btn style-2", }, }, }; if (existingContact) { // Cập nhật contact hiện có với dữ liệu đã xử lý existingContact.hero = processedData.hero; existingContact.contactCards = processedData.contactCards; existingContact.map = processedData.map; existingContact.form = processedData.form; await existingContact.save(); console.log("Contact data updated successfully"); return existingContact; } else { // Tạo contact mới với dữ liệu đã xử lý const newContact = await this.create({ name: "default", ...processedData, }); console.log("Contact data imported successfully"); return newContact; } } catch (error) { console.error("Error migrating contact data:", error); throw error; } }; module.exports = mongoose.model("Contact", contactSchema);