forked from UKSOURCE/cms.hailearning.edu.vn
388 lines
8.9 KiB
JavaScript
388 lines
8.9 KiB
JavaScript
const mongoose = require("mongoose");
|
|
|
|
// Schema cho hero section
|
|
const heroSchema = new mongoose.Schema(
|
|
{
|
|
title: {
|
|
type: String,
|
|
required: true,
|
|
trim: true,
|
|
},
|
|
backgroundImage: {
|
|
type: String,
|
|
trim: true,
|
|
default: "",
|
|
},
|
|
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,
|
|
},
|
|
content: {
|
|
type: [String],
|
|
default: [],
|
|
},
|
|
iconType: {
|
|
type: String,
|
|
required: false,
|
|
trim: true,
|
|
default: "",
|
|
},
|
|
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,
|
|
},
|
|
markerTitle: {
|
|
type: String,
|
|
trim: true,
|
|
default: "",
|
|
},
|
|
tileLayer: {
|
|
type: tileLayerSchema,
|
|
required: true,
|
|
},
|
|
},
|
|
{ _id: false }
|
|
);
|
|
|
|
// Schema cho form field
|
|
const formFieldSchema = new mongoose.Schema(
|
|
{
|
|
name: {
|
|
type: String,
|
|
required: true,
|
|
trim: true,
|
|
},
|
|
type: {
|
|
type: String,
|
|
required: true,
|
|
trim: true,
|
|
enum: ["text", "email", "tel", "textarea", "programme"],
|
|
},
|
|
placeholder: {
|
|
type: String,
|
|
trim: true,
|
|
default: "",
|
|
},
|
|
required: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
programmeName: {
|
|
type: String,
|
|
trim: true,
|
|
default: "",
|
|
},
|
|
},
|
|
{ _id: false }
|
|
);
|
|
|
|
// Schema cho submit button
|
|
const submitButtonSchema = new mongoose.Schema(
|
|
{
|
|
text: {
|
|
type: String,
|
|
required: true,
|
|
trim: true,
|
|
},
|
|
},
|
|
{ _id: false }
|
|
);
|
|
|
|
// Schema cho form
|
|
const formSchema = new mongoose.Schema(
|
|
{
|
|
sectionLabel: {
|
|
type: String,
|
|
trim: true,
|
|
default: "",
|
|
},
|
|
heading: {
|
|
type: String,
|
|
trim: true,
|
|
default: "",
|
|
},
|
|
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 || "",
|
|
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 || "",
|
|
fields: (jsonData.form?.fields || []).map((field) => ({
|
|
name: field.name || "",
|
|
type: field.type || "text",
|
|
placeholder: field.placeholder || "",
|
|
required: field.required || false,
|
|
programmeName: field.programmeName || "",
|
|
})),
|
|
submitButton: {
|
|
text: jsonData.form?.submitButton?.text || "Send Message",
|
|
},
|
|
},
|
|
};
|
|
|
|
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);
|