forked from UKSOURCE/cms.hailearning.edu.vn
feat: Improve home and contact CMS field guidance
This commit is contained in:
168
models/home.js
168
models/home.js
@@ -5,15 +5,15 @@ const { Schema } = mongoose;
|
||||
// Reusable small schemas
|
||||
const LinkSchema = new Schema(
|
||||
{
|
||||
label: { type: String, default: "" },
|
||||
href: { type: String, default: "" },
|
||||
label: { type: String, default: "", maxlength: 32 },
|
||||
href: { type: String, default: "", maxlength: 255 },
|
||||
},
|
||||
{ _id: false },
|
||||
);
|
||||
|
||||
const FloatingContactBrandSchema = new Schema(
|
||||
{
|
||||
imageSrc: { type: String, default: "" },
|
||||
imageSrc: { type: String, default: "", maxlength: 255 },
|
||||
imageAlt: { type: String, default: "", maxlength: 60 },
|
||||
},
|
||||
{ _id: false },
|
||||
@@ -21,8 +21,8 @@ const FloatingContactBrandSchema = new Schema(
|
||||
|
||||
const FloatingContactTriggerSchema = new Schema(
|
||||
{
|
||||
imageSrc: { type: String, default: "" },
|
||||
icon: { type: String, default: "fa-comments" },
|
||||
imageSrc: { type: String, default: "", maxlength: 255 },
|
||||
icon: { type: String, default: "fa-comments", maxlength: 64 },
|
||||
},
|
||||
{ _id: false },
|
||||
);
|
||||
@@ -34,10 +34,10 @@ const FloatingContactActionSchema = new Schema(
|
||||
enabled: { type: Boolean, default: true },
|
||||
label: { type: String, default: "", maxlength: 48 },
|
||||
subtitle: { type: String, default: "", maxlength: 48 },
|
||||
href: { type: String, default: "" },
|
||||
iconImage: { type: String, default: "" },
|
||||
href: { type: String, default: "", maxlength: 255 },
|
||||
iconImage: { type: String, default: "", maxlength: 255 },
|
||||
iconType: { type: String, default: "iconClass" },
|
||||
iconClass: { type: String, default: "" },
|
||||
iconClass: { type: String, default: "", maxlength: 120 },
|
||||
iconText: { type: String, default: "", maxlength: 12 },
|
||||
order: { type: Number, default: 0 },
|
||||
},
|
||||
@@ -59,13 +59,13 @@ const FloatingContactSchema = new Schema(
|
||||
// Hero slide (for multiple hero items in slider)
|
||||
const HeroSlideSchema = new Schema(
|
||||
{
|
||||
title: { type: String, default: "" },
|
||||
subtitle: { type: String, default: "" },
|
||||
description: { type: String, default: "" },
|
||||
title: { type: String, default: "", maxlength: 72 },
|
||||
subtitle: { type: String, default: "", maxlength: 48 },
|
||||
description: { type: String, default: "", maxlength: 220 },
|
||||
primaryButton: { type: LinkSchema, default: () => ({}) },
|
||||
secondaryButton: { type: LinkSchema, default: () => ({}) },
|
||||
heroImage: { type: String, default: "" },
|
||||
videoUrl: { type: String, default: "" },
|
||||
heroImage: { type: String, default: "", maxlength: 255 },
|
||||
videoUrl: { type: String, default: "", maxlength: 255 },
|
||||
},
|
||||
{ _id: false },
|
||||
);
|
||||
@@ -73,42 +73,42 @@ const HeroSlideSchema = new Schema(
|
||||
const HeroSchema = new Schema(
|
||||
{
|
||||
// Background for whole hero section
|
||||
backgroundImage: { type: String, default: "" },
|
||||
backgroundImage: { type: String, default: "", maxlength: 255 },
|
||||
|
||||
// Multiple slides
|
||||
slides: { type: [HeroSlideSchema], default: [] },
|
||||
|
||||
// Legacy single-slide fields (backward compatible)
|
||||
title: { type: String, default: "" },
|
||||
subtitle: { type: String, default: "" },
|
||||
description: { type: String, default: "" },
|
||||
title: { type: String, default: "", maxlength: 72 },
|
||||
subtitle: { type: String, default: "", maxlength: 48 },
|
||||
description: { type: String, default: "", maxlength: 220 },
|
||||
primaryButton: { type: LinkSchema, default: () => ({}) },
|
||||
secondaryButton: { type: LinkSchema, default: () => ({}) },
|
||||
heroImage: { type: String, default: "" },
|
||||
videoUrl: { type: String, default: "" },
|
||||
heroImage: { type: String, default: "", maxlength: 255 },
|
||||
videoUrl: { type: String, default: "", maxlength: 255 },
|
||||
},
|
||||
{ _id: false },
|
||||
);
|
||||
|
||||
const WhyChooseUsItemSchema = new Schema(
|
||||
{
|
||||
icon: { type: String, default: "" },
|
||||
title: { type: String, default: "" },
|
||||
description: { type: String, default: "" },
|
||||
icon: { type: String, default: "", maxlength: 255 },
|
||||
title: { type: String, default: "", maxlength: 40 },
|
||||
description: { type: String, default: "", maxlength: 72 },
|
||||
},
|
||||
{ _id: false },
|
||||
);
|
||||
|
||||
const WhyChooseUsSchema = new Schema(
|
||||
{
|
||||
heading: { type: String, default: "" },
|
||||
subheading: { type: String, default: "" },
|
||||
description: { type: String, default: "" },
|
||||
highlightWord: { type: String, default: "" },
|
||||
mainImage: { type: String, default: "" },
|
||||
secondaryImage: { type: String, default: "" },
|
||||
heading: { type: String, default: "", maxlength: 72 },
|
||||
subheading: { type: String, default: "", maxlength: 48 },
|
||||
description: { type: String, default: "", maxlength: 260 },
|
||||
highlightWord: { type: String, default: "", maxlength: 24 },
|
||||
mainImage: { type: String, default: "", maxlength: 255 },
|
||||
secondaryImage: { type: String, default: "", maxlength: 255 },
|
||||
items: { type: [WhyChooseUsItemSchema], default: [] },
|
||||
features: { type: [String], default: [] },
|
||||
features: { type: [{ type: String, maxlength: 96 }], default: [] },
|
||||
ctaButton: { type: LinkSchema, default: () => ({}) },
|
||||
},
|
||||
{ _id: false },
|
||||
@@ -116,18 +116,18 @@ const WhyChooseUsSchema = new Schema(
|
||||
|
||||
const VisaSolutionItemSchema = new Schema(
|
||||
{
|
||||
number: { type: String, default: "" },
|
||||
title: { type: String, default: "" },
|
||||
description: { type: String, default: "" },
|
||||
link: { type: String, default: "" },
|
||||
number: { type: String, default: "", maxlength: 4 },
|
||||
title: { type: String, default: "", maxlength: 56 },
|
||||
description: { type: String, default: "", maxlength: 180 },
|
||||
link: { type: String, default: "", maxlength: 255 },
|
||||
},
|
||||
{ _id: false },
|
||||
);
|
||||
|
||||
const VisaSolutionsSchema = new Schema(
|
||||
{
|
||||
heading: { type: String, default: "" },
|
||||
subheading: { type: String, default: "" },
|
||||
heading: { type: String, default: "", maxlength: 64 },
|
||||
subheading: { type: String, default: "", maxlength: 40 },
|
||||
items: { type: [VisaSolutionItemSchema], default: [] },
|
||||
},
|
||||
{ _id: false },
|
||||
@@ -135,20 +135,20 @@ const VisaSolutionsSchema = new Schema(
|
||||
|
||||
const VisaCountrySchema = new Schema(
|
||||
{
|
||||
name: { type: String, default: "" },
|
||||
code: { type: String, default: "" },
|
||||
flag: { type: String, default: "" },
|
||||
link: { type: String, default: "" },
|
||||
visaTypes: { type: [String], default: [] },
|
||||
name: { type: String, default: "", maxlength: 40 },
|
||||
code: { type: String, default: "", maxlength: 12 },
|
||||
flag: { type: String, default: "", maxlength: 255 },
|
||||
link: { type: String, default: "", maxlength: 255 },
|
||||
visaTypes: { type: [{ type: String, maxlength: 48 }], default: [] },
|
||||
},
|
||||
{ _id: false },
|
||||
);
|
||||
|
||||
const VisaCountriesSchema = new Schema(
|
||||
{
|
||||
heading: { type: String, default: "" },
|
||||
subheading: { type: String, default: "" },
|
||||
description: { type: String, default: "" },
|
||||
heading: { type: String, default: "", maxlength: 88 },
|
||||
subheading: { type: String, default: "", maxlength: 56 },
|
||||
description: { type: String, default: "", maxlength: 240 },
|
||||
countries: { type: [VisaCountrySchema], default: [] },
|
||||
ctaButton: { type: LinkSchema, default: () => ({}) },
|
||||
},
|
||||
@@ -157,22 +157,22 @@ const VisaCountriesSchema = new Schema(
|
||||
|
||||
const TestimonialSchema = new Schema(
|
||||
{
|
||||
name: { type: String, default: "" },
|
||||
role: { type: String, default: "" },
|
||||
country: { type: String, default: "" },
|
||||
name: { type: String, default: "", maxlength: 48 },
|
||||
role: { type: String, default: "", maxlength: 48 },
|
||||
country: { type: String, default: "", maxlength: 48 },
|
||||
rating: { type: Number, default: 5 },
|
||||
comment: { type: String, default: "" },
|
||||
avatar: { type: String, default: "" },
|
||||
comment: { type: String, default: "", maxlength: 280 },
|
||||
avatar: { type: String, default: "", maxlength: 255 },
|
||||
},
|
||||
{ _id: false },
|
||||
);
|
||||
|
||||
const TestimonialsSchema = new Schema(
|
||||
{
|
||||
heading: { type: String, default: "" },
|
||||
subheading: { type: String, default: "" },
|
||||
videoUrl: { type: String, default: "" },
|
||||
videoThumbnail: { type: String, default: "" },
|
||||
heading: { type: String, default: "", maxlength: 64 },
|
||||
subheading: { type: String, default: "", maxlength: 40 },
|
||||
videoUrl: { type: String, default: "", maxlength: 255 },
|
||||
videoThumbnail: { type: String, default: "", maxlength: 255 },
|
||||
items: { type: [TestimonialSchema], default: [] },
|
||||
},
|
||||
{ _id: false },
|
||||
@@ -180,26 +180,40 @@ const TestimonialsSchema = new Schema(
|
||||
|
||||
const VideoGallerySchema = new Schema(
|
||||
{
|
||||
heading: { type: String, default: "" },
|
||||
videoUrl: { type: String, default: "" },
|
||||
thumbnail: { type: String, default: "" },
|
||||
heading: {
|
||||
type: String,
|
||||
default: "",
|
||||
maxlength: 32,
|
||||
validate: {
|
||||
validator(value) {
|
||||
const words = String(value || "")
|
||||
.trim()
|
||||
.split(/\s+/)
|
||||
.filter(Boolean);
|
||||
return words.length <= 4;
|
||||
},
|
||||
message: "Video gallery heading must be 4 words or fewer.",
|
||||
},
|
||||
},
|
||||
videoUrl: { type: String, default: "", maxlength: 255 },
|
||||
thumbnail: { type: String, default: "", maxlength: 255 },
|
||||
},
|
||||
{ _id: false },
|
||||
);
|
||||
|
||||
const FaqItemSchema = new Schema(
|
||||
{
|
||||
question: { type: String, default: "" },
|
||||
answer: { type: String, default: "" },
|
||||
question: { type: String, default: "", maxlength: 120 },
|
||||
answer: { type: String, default: "", maxlength: 320 },
|
||||
},
|
||||
{ _id: false },
|
||||
);
|
||||
|
||||
const FaqSchema = new Schema(
|
||||
{
|
||||
heading: { type: String, default: "" },
|
||||
subheading: { type: String, default: "" },
|
||||
description: { type: String, default: "" },
|
||||
heading: { type: String, default: "", maxlength: 64 },
|
||||
subheading: { type: String, default: "", maxlength: 40 },
|
||||
description: { type: String, default: "", maxlength: 220 },
|
||||
ctaButton: { type: LinkSchema, default: () => ({}) },
|
||||
items: { type: [FaqItemSchema], default: [] },
|
||||
},
|
||||
@@ -208,18 +222,18 @@ const FaqSchema = new Schema(
|
||||
|
||||
const AchievementItemSchema = new Schema(
|
||||
{
|
||||
value: { type: String, default: "" },
|
||||
suffix: { type: String, default: "" },
|
||||
label: { type: String, default: "" },
|
||||
description: { type: String, default: "" },
|
||||
value: { type: String, default: "", maxlength: 6 },
|
||||
suffix: { type: String, default: "", maxlength: 4 },
|
||||
label: { type: String, default: "", maxlength: 40 },
|
||||
description: { type: String, default: "", maxlength: 120 },
|
||||
},
|
||||
{ _id: false },
|
||||
);
|
||||
|
||||
const AchievementsSchema = new Schema(
|
||||
{
|
||||
heading: { type: String, default: "" },
|
||||
subheading: { type: String, default: "" },
|
||||
heading: { type: String, default: "", maxlength: 56 },
|
||||
subheading: { type: String, default: "", maxlength: 32 },
|
||||
items: { type: [AchievementItemSchema], default: [] },
|
||||
},
|
||||
{ _id: false },
|
||||
@@ -227,9 +241,9 @@ const AchievementsSchema = new Schema(
|
||||
|
||||
const VisaConsultancyItemSchema = new Schema(
|
||||
{
|
||||
name: { type: String, default: "" },
|
||||
icon: { type: String, default: "" },
|
||||
year: { type: String, default: "" },
|
||||
name: { type: String, default: "", maxlength: 48 },
|
||||
icon: { type: String, default: "", maxlength: 255 },
|
||||
year: { type: String, default: "", maxlength: 8 },
|
||||
},
|
||||
{ _id: false },
|
||||
);
|
||||
@@ -243,7 +257,7 @@ const VisaConsultancySchema = new Schema(
|
||||
|
||||
const BrandItemSchema = new Schema(
|
||||
{
|
||||
logo: { type: String, default: "" },
|
||||
logo: { type: String, default: "", maxlength: 255 },
|
||||
},
|
||||
{ _id: false },
|
||||
);
|
||||
@@ -265,16 +279,16 @@ const PartnersSchema = new Schema(
|
||||
|
||||
const BlogPreviewItemSchema = new Schema(
|
||||
{
|
||||
title: { type: String, default: "" },
|
||||
excerpt: { type: String, default: "" },
|
||||
category: { type: String, default: "" },
|
||||
title: { type: String, default: "", maxlength: 120 },
|
||||
excerpt: { type: String, default: "", maxlength: 280 },
|
||||
category: { type: String, default: "", maxlength: 48 },
|
||||
date: { type: String, default: "" }, // keep as string for easy JSON compatibility (e.g. "2025-08-20")
|
||||
author: {
|
||||
name: { type: String, default: "" },
|
||||
name: { type: String, default: "", maxlength: 48 },
|
||||
avatar: { type: String, default: "" },
|
||||
},
|
||||
comments: { type: Number, default: 0 },
|
||||
link: { type: String, default: "" },
|
||||
link: { type: String, default: "", maxlength: 255 },
|
||||
thumbnail: { type: String, default: "" },
|
||||
},
|
||||
{ _id: false },
|
||||
@@ -282,8 +296,8 @@ const BlogPreviewItemSchema = new Schema(
|
||||
|
||||
const BlogPreviewSchema = new Schema(
|
||||
{
|
||||
heading: { type: String, default: "" },
|
||||
subheading: { type: String, default: "" },
|
||||
heading: { type: String, default: "", maxlength: 64 },
|
||||
subheading: { type: String, default: "", maxlength: 40 },
|
||||
ctaButton: { type: LinkSchema, default: () => ({}) },
|
||||
items: { type: [BlogPreviewItemSchema], default: [] },
|
||||
selectedBlogIds: [{ type: Schema.Types.ObjectId, ref: 'Blog' }],
|
||||
|
||||
Reference in New Issue
Block a user