forked from UKSOURCE/cms.hailearning.edu.vn
refactor: restructure home model and update homeController
This commit is contained in:
400
models/home.js
400
models/home.js
@@ -1,177 +1,231 @@
|
||||
const mongoose = require('mongoose');
|
||||
const mongoose = require("mongoose");
|
||||
|
||||
const homeSchema = new mongoose.Schema({
|
||||
// New structure - Camp data
|
||||
hero: {
|
||||
title: { type: String, default: '' },
|
||||
description: { type: String, default: '' },
|
||||
backgroundImage: { type: String, default: '' },
|
||||
button: {
|
||||
label: { type: String, default: 'Book Your Adventure' },
|
||||
href: { type: String, default: '/booking' }
|
||||
},
|
||||
contactBox: {
|
||||
welcomeText: { type: String, default: '' },
|
||||
phone: {
|
||||
label: { type: String, default: 'Call us' },
|
||||
number: { type: String, default: '' },
|
||||
href: { type: String, default: '' }
|
||||
},
|
||||
email: {
|
||||
label: { type: String, default: 'Email' },
|
||||
address: { type: String, default: '' },
|
||||
href: { type: String, default: '' }
|
||||
},
|
||||
workingHours: {
|
||||
label: { type: String, default: 'Working Hours' },
|
||||
hours: { type: String, default: '' }
|
||||
}
|
||||
}
|
||||
},
|
||||
about: {
|
||||
title: { type: String, default: '' },
|
||||
subtitle: { type: String, default: '' },
|
||||
description: { type: String, default: '' },
|
||||
images: {
|
||||
mainImage1: { type: String, default: '' },
|
||||
mainImage2: { type: String, default: '' },
|
||||
avatars: [{ type: String }]
|
||||
},
|
||||
features: [{ type: String }],
|
||||
quote: { type: String, default: '' },
|
||||
button: {
|
||||
label: { type: String, default: '' },
|
||||
href: { type: String, default: '' }
|
||||
},
|
||||
stats: {
|
||||
customerCount: { type: Number, default: 0 },
|
||||
customerLabel: { type: String, default: '' }
|
||||
}
|
||||
},
|
||||
missionVision: {
|
||||
title: { type: String, default: '' },
|
||||
subtitle: { type: String, default: '' },
|
||||
backgroundImage: { type: String, default: '' },
|
||||
cards: [{
|
||||
title: { type: String, default: '' },
|
||||
description: { type: String, default: '' }
|
||||
}]
|
||||
},
|
||||
whyChooseUs: {
|
||||
title: { type: String, default: '' },
|
||||
subtitle: { type: String, default: '' },
|
||||
description: { type: String, default: '' },
|
||||
button: {
|
||||
label: { type: String, default: '' },
|
||||
href: { type: String, default: '' }
|
||||
},
|
||||
features: [{
|
||||
title: { type: String, default: '' },
|
||||
description: { type: String, default: '' }
|
||||
}],
|
||||
tags: [{ type: String }],
|
||||
cta: {
|
||||
text: { type: String, default: '' },
|
||||
linkText: { type: String, default: '' },
|
||||
linkHref: { type: String, default: '' }
|
||||
}
|
||||
},
|
||||
activities: {
|
||||
cards: [{
|
||||
title: { type: String, default: '' },
|
||||
description: { type: String, default: '' },
|
||||
image: { type: String, default: '' }
|
||||
}]
|
||||
},
|
||||
faq: {
|
||||
title: { type: String, default: '' },
|
||||
subtitle: { type: String, default: '' },
|
||||
description: { type: String, default: '' },
|
||||
image: { type: String, default: '' },
|
||||
contact: {
|
||||
title: { type: String, default: '' },
|
||||
info: { type: String, default: '' }
|
||||
},
|
||||
questions: [{
|
||||
question: { type: String, default: '' },
|
||||
answer: { type: String, default: '' }
|
||||
}]
|
||||
},
|
||||
partners: {
|
||||
title: { type: String, default: '' },
|
||||
subtitle: { type: String, default: '' },
|
||||
backgroundImage: { type: String, default: '' },
|
||||
logos: [{ type: String }],
|
||||
cta: {
|
||||
badge: { type: String, default: '' },
|
||||
text: { type: String, default: '' },
|
||||
linkText: { type: String, default: '' },
|
||||
linkHref: { type: String, default: '' }
|
||||
}
|
||||
},
|
||||
programs: {
|
||||
title: { type: String, default: '' },
|
||||
subtitle: { type: String, default: '' },
|
||||
button: {
|
||||
label: { type: String, default: '' },
|
||||
href: { type: String, default: '' }
|
||||
},
|
||||
card: {
|
||||
pricePrefix: { type: String, default: 'from' },
|
||||
priceSuffix: { type: String, default: 'USD' },
|
||||
buttonLabel: { type: String, default: 'Camp Detail' },
|
||||
buttonHref: { type: String, default: '/camp-profiles' }
|
||||
},
|
||||
items: [{
|
||||
id: { type: String, default: '' },
|
||||
title: { type: String, default: '' },
|
||||
price: { type: String, default: '' },
|
||||
seasons: [{ type: String }],
|
||||
age: { type: String, default: '' },
|
||||
location: { type: String, default: '' },
|
||||
image: { type: String, default: '' },
|
||||
slug: { type: String, default: '' }
|
||||
}]
|
||||
},
|
||||
const { Schema } = mongoose;
|
||||
|
||||
newsletter: {
|
||||
title: { type: String, default: '' },
|
||||
subtitle: { type: String, default: '' },
|
||||
description: { type: String, default: '' },
|
||||
image: { type: String, default: '' },
|
||||
decorativeImage: { type: String, default: '' },
|
||||
button: {
|
||||
label: { type: String, default: '' },
|
||||
placeholder: { type: String, default: '' },
|
||||
href: { type: String, default: '' }
|
||||
}
|
||||
// Reusable small schemas
|
||||
const LinkSchema = new Schema(
|
||||
{
|
||||
label: { type: String, default: "" },
|
||||
href: { type: String, default: "" },
|
||||
},
|
||||
latestPosts: {
|
||||
title: { type: String, default: '' },
|
||||
subtitle: { type: String, default: '' },
|
||||
searchPlaceholder: { type: String, default: '' },
|
||||
sidebarTitle: { type: String, default: '' },
|
||||
blogPosts: [{
|
||||
id: { type: Number },
|
||||
image: { type: String, default: '' },
|
||||
title: { type: String, default: '' },
|
||||
description: { type: String, default: '' },
|
||||
date: { type: String, default: '' }
|
||||
}],
|
||||
sidebarPosts: [{
|
||||
id: { type: Number },
|
||||
image: { type: String, default: '' },
|
||||
title: { type: String, default: '' },
|
||||
description: { type: String, default: '' }
|
||||
}],
|
||||
featuredCard: {
|
||||
image: { type: String, default: '' },
|
||||
title: { type: String, default: '' },
|
||||
description: { type: String, default: '' }
|
||||
}
|
||||
}
|
||||
}, {
|
||||
timestamps: true
|
||||
});
|
||||
{ _id: false },
|
||||
);
|
||||
|
||||
const HeroSchema = new Schema(
|
||||
{
|
||||
title: { type: String, default: "" },
|
||||
subtitle: { type: String, default: "" },
|
||||
description: { type: String, default: "" },
|
||||
primaryButton: { type: LinkSchema, default: () => ({}) },
|
||||
secondaryButton: { type: LinkSchema, default: () => ({}) },
|
||||
backgroundImage: { type: String, default: "" },
|
||||
videoUrl: { type: String, default: "" },
|
||||
},
|
||||
{ _id: false },
|
||||
);
|
||||
|
||||
const WhyChooseUsItemSchema = new Schema(
|
||||
{
|
||||
icon: { type: String, default: "" },
|
||||
title: { type: String, default: "" },
|
||||
description: { type: String, default: "" },
|
||||
},
|
||||
{ _id: false },
|
||||
);
|
||||
|
||||
const WhyChooseUsSchema = new Schema(
|
||||
{
|
||||
heading: { type: String, default: "" },
|
||||
subheading: { type: String, default: "" },
|
||||
description: { type: String, default: "" },
|
||||
items: { type: [WhyChooseUsItemSchema], default: [] },
|
||||
features: { type: [String], default: [] },
|
||||
ctaButton: { type: LinkSchema, default: () => ({}) },
|
||||
},
|
||||
{ _id: false },
|
||||
);
|
||||
|
||||
const VisaSolutionItemSchema = new Schema(
|
||||
{
|
||||
number: { type: String, default: "" },
|
||||
title: { type: String, default: "" },
|
||||
description: { type: String, default: "" },
|
||||
link: { type: String, default: "" },
|
||||
},
|
||||
{ _id: false },
|
||||
);
|
||||
|
||||
const VisaSolutionsSchema = new Schema(
|
||||
{
|
||||
heading: { type: String, default: "" },
|
||||
subheading: { type: String, default: "" },
|
||||
items: { type: [VisaSolutionItemSchema], default: [] },
|
||||
},
|
||||
{ _id: false },
|
||||
);
|
||||
|
||||
const VisaCountrySchema = new Schema(
|
||||
{
|
||||
name: { type: String, default: "" },
|
||||
code: { type: String, default: "" },
|
||||
flag: { type: String, default: "" },
|
||||
link: { type: String, default: "" },
|
||||
visaTypes: { type: [String], default: [] },
|
||||
},
|
||||
{ _id: false },
|
||||
);
|
||||
|
||||
const VisaCountriesSchema = new Schema(
|
||||
{
|
||||
heading: { type: String, default: "" },
|
||||
subheading: { type: String, default: "" },
|
||||
description: { type: String, default: "" },
|
||||
countries: { type: [VisaCountrySchema], default: [] },
|
||||
ctaButton: { type: LinkSchema, default: () => ({}) },
|
||||
},
|
||||
{ _id: false },
|
||||
);
|
||||
|
||||
const TestimonialSchema = new Schema(
|
||||
{
|
||||
name: { type: String, default: "" },
|
||||
role: { type: String, default: "" },
|
||||
country: { type: String, default: "" },
|
||||
rating: { type: Number, default: 5 },
|
||||
comment: { type: String, default: "" },
|
||||
avatar: { type: String, default: "" },
|
||||
},
|
||||
{ _id: false },
|
||||
);
|
||||
|
||||
const TestimonialsSchema = new Schema(
|
||||
{
|
||||
heading: { type: String, default: "" },
|
||||
subheading: { type: String, default: "" },
|
||||
videoUrl: { type: String, default: "" },
|
||||
videoThumbnail: { type: String, default: "" },
|
||||
items: { type: [TestimonialSchema], default: [] },
|
||||
},
|
||||
{ _id: false },
|
||||
);
|
||||
|
||||
const VideoGallerySchema = new Schema(
|
||||
{
|
||||
heading: { type: String, default: "" },
|
||||
videoUrl: { type: String, default: "" },
|
||||
thumbnail: { type: String, default: "" },
|
||||
},
|
||||
{ _id: false },
|
||||
);
|
||||
|
||||
const FaqItemSchema = new Schema(
|
||||
{
|
||||
question: { type: String, default: "" },
|
||||
answer: { type: String, default: "" },
|
||||
},
|
||||
{ _id: false },
|
||||
);
|
||||
|
||||
const FaqSchema = new Schema(
|
||||
{
|
||||
heading: { type: String, default: "" },
|
||||
subheading: { type: String, default: "" },
|
||||
description: { type: String, default: "" },
|
||||
ctaButton: { type: LinkSchema, default: () => ({}) },
|
||||
items: { type: [FaqItemSchema], default: [] },
|
||||
},
|
||||
{ _id: false },
|
||||
);
|
||||
|
||||
const AchievementItemSchema = new Schema(
|
||||
{
|
||||
value: { type: String, default: "" },
|
||||
suffix: { type: String, default: "" },
|
||||
label: { type: String, default: "" },
|
||||
description: { type: String, default: "" },
|
||||
},
|
||||
{ _id: false },
|
||||
);
|
||||
|
||||
const AchievementsSchema = new Schema(
|
||||
{
|
||||
heading: { type: String, default: "" },
|
||||
subheading: { type: String, default: "" },
|
||||
items: { type: [AchievementItemSchema], default: [] },
|
||||
},
|
||||
{ _id: false },
|
||||
);
|
||||
|
||||
const PartnerItemSchema = new Schema(
|
||||
{
|
||||
name: { type: String, default: "" },
|
||||
logo: { type: String, default: "" },
|
||||
year: { type: String, default: "" },
|
||||
},
|
||||
{ _id: false },
|
||||
);
|
||||
|
||||
const PartnersSchema = new Schema(
|
||||
{
|
||||
heading: { type: String, default: "" },
|
||||
items: { type: [PartnerItemSchema], default: [] },
|
||||
},
|
||||
{ _id: false },
|
||||
);
|
||||
|
||||
const BlogPreviewItemSchema = new Schema(
|
||||
{
|
||||
title: { type: String, default: "" },
|
||||
excerpt: { type: String, default: "" },
|
||||
category: { type: String, default: "" },
|
||||
date: { type: String, default: "" }, // keep as string for easy JSON compatibility (e.g. "2025-08-20")
|
||||
author: {
|
||||
name: { type: String, default: "" },
|
||||
avatar: { type: String, default: "" },
|
||||
},
|
||||
comments: { type: Number, default: 0 },
|
||||
link: { type: String, default: "" },
|
||||
thumbnail: { type: String, default: "" },
|
||||
},
|
||||
{ _id: false },
|
||||
);
|
||||
|
||||
const BlogPreviewSchema = new Schema(
|
||||
{
|
||||
heading: { type: String, default: "" },
|
||||
subheading: { type: String, default: "" },
|
||||
ctaButton: { type: LinkSchema, default: () => ({}) },
|
||||
items: { type: [BlogPreviewItemSchema], default: [] },
|
||||
},
|
||||
{ _id: false },
|
||||
);
|
||||
|
||||
/**
|
||||
* Home page content model
|
||||
*
|
||||
* NOTE:
|
||||
* - This schema is based on `hailearning.edu.vn/app/home.json`.
|
||||
* - `strict: false` keeps backward compatibility with any existing CMS-only sections
|
||||
* (e.g. about/missionVision/programs/newsletter/latestPosts...) that the admin UI might still send.
|
||||
*/
|
||||
const HomeSchema = new Schema(
|
||||
{
|
||||
hero: { type: HeroSchema, default: () => ({}) },
|
||||
whyChooseUs: { type: WhyChooseUsSchema, default: () => ({}) },
|
||||
visaSolutions: { type: VisaSolutionsSchema, default: () => ({}) },
|
||||
visaCountries: { type: VisaCountriesSchema, default: () => ({}) },
|
||||
testimonials: { type: TestimonialsSchema, default: () => ({}) },
|
||||
videoGallery: { type: VideoGallerySchema, default: () => ({}) },
|
||||
faq: { type: FaqSchema, default: () => ({}) },
|
||||
achievements: { type: AchievementsSchema, default: () => ({}) },
|
||||
partners: { type: PartnersSchema, default: () => ({}) },
|
||||
blogPreview: { type: BlogPreviewSchema, default: () => ({}) },
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
strict: false,
|
||||
},
|
||||
);
|
||||
|
||||
module.exports = mongoose.model("Home", HomeSchema);
|
||||
|
||||
module.exports = mongoose.model('Home', homeSchema);
|
||||
Reference in New Issue
Block a user