forked from UKSOURCE/cms.hailearning.edu.vn
519 lines
16 KiB
JavaScript
519 lines
16 KiB
JavaScript
// models/terms.js
|
||
const mongoose = require("mongoose");
|
||
|
||
// Schema cho content items
|
||
const contentItemSchema = new mongoose.Schema(
|
||
{
|
||
type: {
|
||
type: String,
|
||
enum: ["paragraph", "section", "header", "list", "cancellation_table", "cancellation_section", "note", "embed", "image"],
|
||
required: true,
|
||
},
|
||
text: {
|
||
type: String,
|
||
trim: true,
|
||
default: "",
|
||
},
|
||
// Header level (h2, h3, h4, h5, h6)
|
||
level: {
|
||
type: Number,
|
||
min: 1,
|
||
max: 6,
|
||
default: 2,
|
||
},
|
||
title: {
|
||
type: String,
|
||
trim: true,
|
||
default: "",
|
||
},
|
||
content: {
|
||
type: String,
|
||
trim: true,
|
||
default: "",
|
||
},
|
||
subsections: {
|
||
type: [mongoose.Schema.Types.Mixed], // Recursive reference
|
||
default: [],
|
||
},
|
||
items: {
|
||
type: [String],
|
||
default: [],
|
||
},
|
||
// List style (for list type)
|
||
style: {
|
||
type: String,
|
||
enum: ["ordered", "unordered"],
|
||
default: "unordered",
|
||
},
|
||
// Embed/video fields (optional)
|
||
embed: {
|
||
type: String,
|
||
trim: true,
|
||
default: ''
|
||
},
|
||
url: {
|
||
type: String,
|
||
trim: true,
|
||
default: ''
|
||
},
|
||
source: {
|
||
type: String,
|
||
trim: true,
|
||
default: ''
|
||
},
|
||
videoId: {
|
||
type: String,
|
||
trim: true,
|
||
default: ''
|
||
},
|
||
caption: {
|
||
type: String,
|
||
trim: true,
|
||
default: ''
|
||
},
|
||
width: {
|
||
type: Number,
|
||
default: 0
|
||
},
|
||
height: {
|
||
type: Number,
|
||
default: 0
|
||
},
|
||
},
|
||
{ _id: false }
|
||
);
|
||
|
||
// Schema cho overlay style
|
||
const overlayStyleSchema = new mongoose.Schema(
|
||
{
|
||
backgroundColor: {
|
||
type: String,
|
||
trim: true,
|
||
default: "rgba(0, 0, 0, 0)",
|
||
},
|
||
},
|
||
{ _id: false }
|
||
);
|
||
|
||
// Schema cho hero section - CẤU TRÚC MỚI
|
||
const heroSchema = new mongoose.Schema(
|
||
{
|
||
title: {
|
||
type: String,
|
||
required: true,
|
||
trim: true,
|
||
default: "Frequently Asked Questions",
|
||
},
|
||
backgroundImage: {
|
||
type: String,
|
||
trim: true,
|
||
default: "/uploads/terms/faqimage.jpg",
|
||
},
|
||
sectionClass: {
|
||
type: String,
|
||
trim: true,
|
||
default: "uk-section-default uk-section-overlap uk-preserve-color uk-light uk-position-relative",
|
||
},
|
||
backgroundClasses: {
|
||
type: String,
|
||
trim: true,
|
||
default: "uk-background-norepeat uk-background-cover uk-background-top-center uk-section uk-section-xlarge",
|
||
},
|
||
overlayStyle: {
|
||
type: overlayStyleSchema,
|
||
default: () => ({ backgroundColor: "rgba(0, 0, 0, 0)" }),
|
||
},
|
||
titleClass: {
|
||
type: String,
|
||
trim: true,
|
||
default: "text-white text-[5vw] uk-text-center",
|
||
},
|
||
enableScrollspy: {
|
||
type: Boolean,
|
||
default: true,
|
||
},
|
||
},
|
||
{ _id: false }
|
||
);
|
||
|
||
// Schema cho page section - CẤU TRÚC MỚI
|
||
const pageSchema = new mongoose.Schema(
|
||
{
|
||
title: {
|
||
type: String,
|
||
required: true,
|
||
trim: true,
|
||
default: "Terms & Conditions Go and Grow Camp e.K.",
|
||
},
|
||
divider: {
|
||
type: Boolean,
|
||
default: true,
|
||
},
|
||
sectionClass: {
|
||
type: String,
|
||
trim: true,
|
||
default: "uk-section-default uk-section-overlap uk-section",
|
||
},
|
||
titleClass: {
|
||
type: String,
|
||
trim: true,
|
||
default: "text-[2.5vw] text-[#292c3d] uk-text-left@m uk-text-center",
|
||
},
|
||
dividerClass: {
|
||
type: String,
|
||
trim: true,
|
||
default: "uk-divider-small uk-text-left@m uk-text-center",
|
||
},
|
||
},
|
||
{ _id: false }
|
||
);
|
||
|
||
// Schema cho content section - CẤU TRÚC MỚI
|
||
const contentSchema = new mongoose.Schema(
|
||
{
|
||
sectionClass: {
|
||
type: String,
|
||
trim: true,
|
||
default: "uk-section-muted uk-section-overlap uk-section",
|
||
},
|
||
textClass: {
|
||
type: String,
|
||
trim: true,
|
||
default: "uk-panel uk-margin text-[1vw]",
|
||
},
|
||
content: {
|
||
type: [contentItemSchema],
|
||
default: [],
|
||
},
|
||
},
|
||
{ _id: false }
|
||
);
|
||
|
||
// Main Terms Schema - CẤU TRÚC MỚI
|
||
const termsSchema = new mongoose.Schema(
|
||
{
|
||
name: {
|
||
type: String,
|
||
default: "default",
|
||
unique: true,
|
||
},
|
||
// CHỈ CÒN 3 PHẦN CHÍNH
|
||
hero: {
|
||
type: heroSchema,
|
||
required: true,
|
||
},
|
||
page: {
|
||
type: pageSchema,
|
||
required: true,
|
||
},
|
||
content: {
|
||
type: contentSchema,
|
||
required: true,
|
||
},
|
||
language: {
|
||
type: String,
|
||
default: "en",
|
||
},
|
||
version: {
|
||
type: String,
|
||
default: "2.0.0", // Tăng version vì cấu trúc thay đổi
|
||
},
|
||
isActive: {
|
||
type: Boolean,
|
||
default: true,
|
||
},
|
||
migratedFromOldStructure: {
|
||
type: Boolean,
|
||
default: false,
|
||
},
|
||
},
|
||
{
|
||
timestamps: true,
|
||
}
|
||
);
|
||
|
||
// Static method: Lấy terms default - CẬP NHẬT THEO CẤU TRÚC MỚI
|
||
termsSchema.statics.getDefault = async function(language = "en") {
|
||
try {
|
||
let terms = await this.findOne({ name: "default", language: language });
|
||
|
||
if (!terms) {
|
||
// Tạo terms mặc định theo cấu trúc mới
|
||
terms = new this({
|
||
name: "default",
|
||
language: language,
|
||
hero: {
|
||
title: "Frequently Asked Questions",
|
||
subtitle: "Our Terms & Conditions",
|
||
backgroundImage: "/uploads/terms/faqimage.jpg",
|
||
sectionClass: "uk-section-default uk-section-overlap uk-preserve-color uk-light uk-position-relative",
|
||
backgroundClasses: "uk-background-norepeat uk-background-cover uk-background-top-center uk-section uk-section-xlarge",
|
||
overlayStyle: {
|
||
backgroundColor: "rgba(0, 0, 0, 0)"
|
||
},
|
||
titleClass: "text-white text-[5vw] uk-text-center",
|
||
subtitleClass: "uk-panel font-[Raleway] italic text-[1.5vw] uk-margin uk-text-center",
|
||
enableScrollspy: true
|
||
},
|
||
page: {
|
||
title: "Terms & Conditions Go and Grow Camp e.K.",
|
||
divider: true,
|
||
sectionClass: "uk-section-default uk-section-overlap uk-section",
|
||
titleClass: "text-[2.5vw] text-[#292c3d] uk-text-left@m uk-text-center",
|
||
dividerClass: "uk-divider-small uk-text-left@m uk-text-center"
|
||
},
|
||
content: {
|
||
sectionClass: "uk-section-muted uk-section-overlap uk-section",
|
||
textClass: "uk-panel uk-margin text-[1vw]",
|
||
content: [
|
||
{
|
||
type: "paragraph",
|
||
text: "This is an English translation of the original and legally binding German document \"Allgemeine Geschäftsbedingungen Go and Grow Camp e.K.\", which can be viewed at <a href=\"https://www.campadventure.de/de/infos/agb\" target=\"_self\">https://www.campadventure.de/de/infos/agb</a>. This translation is for your information only and is not legally binding."
|
||
},
|
||
{
|
||
type: "paragraph",
|
||
text: "<strong>Go and Grow Camp e.K. is the tour operator for individuals, for camps in Germany, England and Northern Ireland.</strong>"
|
||
},
|
||
{
|
||
type: "paragraph",
|
||
text: "GUARANTEE: All participants are protected in accordance with the legal regulations governing tour operators in Germany. As per §651, any payments made towards the travel price are insured against insolvency by tourVers."
|
||
}
|
||
]
|
||
},
|
||
version: "2.0.0",
|
||
isActive: true,
|
||
migratedFromOldStructure: false
|
||
});
|
||
|
||
await terms.save();
|
||
console.log(`Created default terms for language: ${language} (new structure)`);
|
||
}
|
||
|
||
return terms;
|
||
} catch (error) {
|
||
console.error("Error in getDefault:", error);
|
||
throw error;
|
||
}
|
||
};
|
||
|
||
// Method để get terms data
|
||
termsSchema.methods.getTermsData = function() {
|
||
return this.toObject();
|
||
};
|
||
|
||
// Migration method từ JSON CŨ sang cấu trúc MỚI
|
||
termsSchema.statics.migrateFromJson = async function(jsonData, language = "en") {
|
||
try {
|
||
console.log('Migrating from JSON to new structure...');
|
||
|
||
// Xóa document cũ nếu có
|
||
await this.deleteOne({ name: "default", language: language });
|
||
|
||
// Chuyển đổi từ cấu trúc cũ sang mới
|
||
const processedData = {
|
||
name: "default",
|
||
language: language,
|
||
version: "2.0.0",
|
||
isActive: true,
|
||
migratedFromOldStructure: true,
|
||
|
||
hero: {
|
||
title: jsonData.hero?.title || "Go and Grow Camp",
|
||
subtitle: jsonData.hero?.subtitle || "Our Terms & Conditions",
|
||
backgroundImage: jsonData.hero?.backgroundImage || "/uploads/terms/faqimage.jpg",
|
||
sectionClass: "uk-section-default uk-section-overlap uk-preserve-color uk-light uk-position-relative",
|
||
backgroundClasses: "uk-background-norepeat uk-background-cover uk-background-top-center uk-section uk-section-xlarge",
|
||
overlayStyle: {
|
||
backgroundColor: jsonData.hero?.overlayColor || "rgba(0, 0, 0, 0)"
|
||
},
|
||
titleClass: "text-white text-[5vw] uk-text-center",
|
||
subtitleClass: "uk-panel font-[Raleway] italic text-[1.5vw] uk-margin uk-text-center",
|
||
enableScrollspy: jsonData.hero?.enableScrollspy || true
|
||
},
|
||
|
||
page: {
|
||
title: jsonData.termsHeader?.title || "Terms & Conditions Go and Grow Camp e.K.",
|
||
divider: jsonData.termsHeader?.divider !== false,
|
||
sectionClass: jsonData.termsHeader?.sectionClass || "uk-section-default uk-section-overlap uk-section",
|
||
titleClass: jsonData.termsHeader?.titleClass || "text-[2.5vw] text-[#292c3d] uk-text-left@m uk-text-center",
|
||
dividerClass: jsonData.termsHeader?.dividerClass || "uk-divider-small uk-text-left@m uk-text-center"
|
||
},
|
||
|
||
content: {
|
||
sectionClass: jsonData.layout?.termsSectionClass || "uk-section-muted uk-section-overlap uk-section",
|
||
textClass: jsonData.layout?.textContentClass || "uk-panel uk-margin text-[1vw]",
|
||
content: []
|
||
}
|
||
};
|
||
|
||
// Chuyển đổi sections cũ sang content mới
|
||
const contentItems = [];
|
||
|
||
// Thêm disclaimer đầu tiên nếu có
|
||
if (jsonData.disclaimer?.text) {
|
||
contentItems.push({
|
||
type: "paragraph",
|
||
text: jsonData.disclaimer.text
|
||
});
|
||
}
|
||
|
||
if (jsonData.disclaimer?.importantNote) {
|
||
contentItems.push({
|
||
type: "paragraph",
|
||
text: `<strong>${jsonData.disclaimer.importantNote}</strong>`
|
||
});
|
||
}
|
||
|
||
if (jsonData.disclaimer?.legalNote) {
|
||
contentItems.push({
|
||
type: "paragraph",
|
||
text: jsonData.disclaimer.legalNote
|
||
});
|
||
}
|
||
|
||
// Thêm disclaimer note
|
||
if (jsonData.disclaimer?.note) {
|
||
contentItems.push({
|
||
type: "paragraph",
|
||
text: jsonData.disclaimer.note
|
||
});
|
||
}
|
||
|
||
// Thêm các sections
|
||
if (jsonData.sections && Array.isArray(jsonData.sections)) {
|
||
jsonData.sections.forEach(section => {
|
||
if (section.title && section.content) {
|
||
const contentItem = {
|
||
type: "section",
|
||
title: section.title,
|
||
content: section.content
|
||
};
|
||
|
||
// Thêm subsections nếu có
|
||
if (section.subsections && section.subsections.length > 0) {
|
||
contentItem.subsections = section.subsections.map(sub => ({
|
||
type: "note",
|
||
text: sub.content || sub
|
||
}));
|
||
}
|
||
|
||
// Thêm cancellation fees nếu có
|
||
if (section.fees) {
|
||
contentItem.subsections = contentItem.subsections || [];
|
||
|
||
// Individual fees
|
||
if (section.fees.individual && section.fees.individual.length > 0) {
|
||
contentItem.subsections.push({
|
||
type: "cancellation_table",
|
||
title: "Standard Cancellation Fees",
|
||
items: section.fees.individual.map(fee => `${fee.period} – ${fee.fee}`)
|
||
});
|
||
}
|
||
|
||
// School group fees
|
||
if (section.fees.schoolGroups && section.fees.schoolGroups.fees) {
|
||
contentItem.subsections.push({
|
||
type: "cancellation_section",
|
||
title: "Cancellation policy for school groups:",
|
||
items: [
|
||
section.fees.schoolGroups.freeCorrection,
|
||
...section.fees.schoolGroups.fees.map(fee => `${fee.period}: ${fee.fee}`)
|
||
]
|
||
});
|
||
}
|
||
|
||
// Fee note
|
||
if (section.fees.note) {
|
||
contentItem.subsections.push({
|
||
type: "note",
|
||
text: section.fees.note
|
||
});
|
||
}
|
||
}
|
||
|
||
contentItems.push(contentItem);
|
||
}
|
||
});
|
||
}
|
||
|
||
// Thêm footer note nếu có
|
||
if (jsonData.footerNote?.text) {
|
||
contentItems.push({
|
||
type: "paragraph",
|
||
text: jsonData.footerNote.text
|
||
});
|
||
}
|
||
|
||
// Gán content items đã chuyển đổi
|
||
processedData.content.content = contentItems;
|
||
|
||
// Tạo document mới
|
||
const newTerms = await this.create(processedData);
|
||
console.log(`Terms data migrated to new structure for language: ${language}`);
|
||
console.log(`Total content items: ${contentItems.length}`);
|
||
|
||
return newTerms;
|
||
} catch (error) {
|
||
console.error("Error migrating terms data to new structure:", error);
|
||
throw error;
|
||
}
|
||
};
|
||
|
||
// Migration method từ cấu trúc MỚI sang cấu trúc MỚI (dành cho JSON mới)
|
||
termsSchema.statics.migrateFromNewJson = async function(jsonData, language = "en") {
|
||
try {
|
||
console.log('Migrating from new JSON structure...');
|
||
|
||
// Xóa document cũ nếu có
|
||
await this.deleteOne({ name: "default", language: language });
|
||
|
||
// Tạo document mới với cấu trúc mới
|
||
const newTerms = await this.create({
|
||
name: "default",
|
||
language: language,
|
||
version: "2.0.0",
|
||
isActive: true,
|
||
migratedFromOldStructure: false,
|
||
|
||
hero: {
|
||
title: jsonData.hero?.title || "Go and Grow Camp",
|
||
subtitle: jsonData.hero?.subtitle || "Our Terms & Conditions",
|
||
backgroundImage: jsonData.hero?.backgroundImage || "/uploads/terms/faqimage.jpg",
|
||
sectionClass: jsonData.hero?.sectionClass || "uk-section-default uk-section-overlap uk-preserve-color uk-light uk-position-relative",
|
||
backgroundClasses: jsonData.hero?.backgroundClasses || "uk-background-norepeat uk-background-cover uk-background-top-center uk-section uk-section-xlarge",
|
||
overlayStyle: jsonData.hero?.overlayStyle || { backgroundColor: "rgba(0, 0, 0, 0)" },
|
||
titleClass: jsonData.hero?.titleClass || "text-white text-[5vw] uk-text-center",
|
||
subtitleClass: jsonData.hero?.subtitleClass || "uk-panel font-[Raleway] italic text-[1.5vw] uk-margin uk-text-center",
|
||
enableScrollspy: jsonData.hero?.enableScrollspy !== undefined ? jsonData.hero.enableScrollspy : true
|
||
},
|
||
|
||
page: {
|
||
title: jsonData.page?.title || "Terms & Conditions Go and Grow Camp e.K.",
|
||
divider: jsonData.page?.divider !== undefined ? jsonData.page.divider : true,
|
||
sectionClass: jsonData.page?.sectionClass || "uk-section-default uk-section-overlap uk-section",
|
||
titleClass: jsonData.page?.titleClass || "text-[2.5vw] text-[#292c3d] uk-text-left@m uk-text-center",
|
||
dividerClass: jsonData.page?.dividerClass || "uk-divider-small uk-text-left@m uk-text-center"
|
||
},
|
||
|
||
content: {
|
||
sectionClass: jsonData.content?.sectionClass || "uk-section-muted uk-section-overlap uk-section",
|
||
textClass: jsonData.content?.textClass || "uk-panel uk-margin text-[1vw]",
|
||
content: jsonData.content?.content || []
|
||
}
|
||
});
|
||
|
||
console.log(`Terms data created with new structure for language: ${language}`);
|
||
console.log(`Hero title: ${newTerms.hero.title}`);
|
||
console.log(`Page title: ${newTerms.page.title}`);
|
||
console.log(`Content items: ${newTerms.content.content.length}`);
|
||
|
||
return newTerms;
|
||
} catch (error) {
|
||
console.error("Error creating terms data from new structure:", error);
|
||
throw error;
|
||
}
|
||
};
|
||
|
||
const Terms = mongoose.model("Terms", termsSchema);
|
||
|
||
module.exports = Terms; |