refactor: restructure home model and update homeController

This commit is contained in:
Wini_Fy
2026-02-05 13:24:07 +07:00
parent c3a55b13f8
commit aea70f89ac
5 changed files with 708 additions and 871 deletions

View File

@@ -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);