feat: standardize admin form limits and guidance

This commit is contained in:
Tống Thành Đạt
2026-04-10 15:55:15 +07:00
parent 7ce5921fe0
commit 51c6303437
34 changed files with 1692 additions and 361 deletions

View File

@@ -3,87 +3,87 @@ const mongoose = require("mongoose");
const aboutUsSchema = new mongoose.Schema(
{
hero: {
title: String,
breadcrumb: [String],
backgroundImage: String,
title: { type: String, trim: true, maxlength: 80 },
breadcrumb: [{ type: String, trim: true, maxlength: 80 }],
backgroundImage: { type: String, trim: true, maxlength: 255 },
},
intro: {
subheading: String,
heading: String,
description: String,
image: String,
subheading: { type: String, trim: true, maxlength: 80 },
heading: { type: String, trim: true, maxlength: 120 },
description: { type: String, trim: true, maxlength: 1000 },
image: { type: String, trim: true, maxlength: 255 },
},
mission: {
subheading: String,
heading: String,
description: String,
subheading: { type: String, trim: true, maxlength: 80 },
heading: { type: String, trim: true, maxlength: 120 },
description: { type: String, trim: true, maxlength: 1000 },
images: {
main: String,
secondary: String,
bgShape: String,
planeShape: String,
topShape: String,
globeShape: String,
main: { type: String, trim: true, maxlength: 255 },
secondary: { type: String, trim: true, maxlength: 255 },
bgShape: { type: String, trim: true, maxlength: 255 },
planeShape: { type: String, trim: true, maxlength: 255 },
topShape: { type: String, trim: true, maxlength: 255 },
globeShape: { type: String, trim: true, maxlength: 255 },
},
items: [
new mongoose.Schema(
{
icon: String,
label: String,
description: String,
icon: { type: String, trim: true, maxlength: 255 },
label: { type: String, trim: true, maxlength: 80 },
description: { type: String, trim: true, maxlength: 240 },
},
{ _id: false },
),
],
features: [String],
features: [{ type: String, trim: true, maxlength: 80 }],
ctaButton: {
label: String,
href: String,
label: { type: String, trim: true, maxlength: 64 },
href: { type: String, trim: true, maxlength: 255 },
},
},
features: {
backgroundImage: String,
subheading: String,
heading: String,
description: String,
image: String,
backgroundImage: { type: String, trim: true, maxlength: 255 },
subheading: { type: String, trim: true, maxlength: 80 },
heading: { type: String, trim: true, maxlength: 120 },
description: { type: String, trim: true, maxlength: 1000 },
image: { type: String, trim: true, maxlength: 255 },
items: [
new mongoose.Schema(
{
icon: String,
title: String,
description: String,
icon: { type: String, trim: true, maxlength: 255 },
title: { type: String, trim: true, maxlength: 80 },
description: { type: String, trim: true, maxlength: 240 },
},
{ _id: false },
),
],
ctaButton: {
label: String,
href: String,
label: { type: String, trim: true, maxlength: 64 },
href: { type: String, trim: true, maxlength: 255 },
},
},
news: {
subheading: String,
heading: String,
subheading: { type: String, trim: true, maxlength: 80 },
heading: { type: String, trim: true, maxlength: 120 },
ctaButton: {
label: String,
href: String,
label: { type: String, trim: true, maxlength: 64 },
href: { type: String, trim: true, maxlength: 255 },
},
selectedBlogIds: [{ type: mongoose.Schema.Types.ObjectId, ref: "Blog" }],
// Deprecated: items field kept for backward compatibility during migration
items: [
new mongoose.Schema(
{
title: String,
category: String,
date: String,
title: { type: String, trim: true, maxlength: 120 },
category: { type: String, trim: true, maxlength: 48 },
date: { type: String, trim: true, maxlength: 32 },
comments: Number,
author: {
name: String,
avatar: String,
name: { type: String, trim: true, maxlength: 48 },
avatar: { type: String, trim: true, maxlength: 255 },
},
link: String,
thumbnail: String,
link: { type: String, trim: true, maxlength: 255 },
thumbnail: { type: String, trim: true, maxlength: 255 },
},
{ _id: false },
),

View File

@@ -7,28 +7,33 @@ const activitySchema = new mongoose.Schema(
titleActivities: {
type: String,
trim: true,
default: ''
default: "",
maxlength: 80,
},
titleBooking: {
type: String,
trim: true,
default: ''
default: "",
maxlength: 80,
},
bannerImageActivities: {
type: String,
trim: true,
default: ''
default: "",
maxlength: 255,
},
bannerImageBooking: {
type: String,
trim: true,
default: ''
default: "",
maxlength: 255,
},
},
name: {
type: String,
required: true,
trim: true,
maxlength: 120,
},
price: {
type: Number,
@@ -38,6 +43,7 @@ const activitySchema = new mongoose.Schema(
priceText: {
type: String,
trim: true,
maxlength: 32,
},
season: [
{
@@ -58,25 +64,28 @@ const activitySchema = new mongoose.Schema(
{
type: String,
trim: true,
maxlength: 80,
},
],
image: {
type: String,
trim: true,
maxlength: 255,
},
link: {
type: String,
trim: true,
maxlength: 255,
},
// Global filters document (single document in Activity collection)
filters: [
{
label: { type: String, required: true, trim: true },
value: { type: String, required: true, trim: true },
label: { type: String, required: true, trim: true, maxlength: 64 },
value: { type: String, required: true, trim: true, maxlength: 64 },
items: [
{
value: { type: String, required: true },
label: { type: String, required: true },
value: { type: String, required: true, maxlength: 64 },
label: { type: String, required: true, maxlength: 64 },
},
],
order: { type: Number, default: 0 },
@@ -85,6 +94,7 @@ const activitySchema = new mongoose.Schema(
program: {
type: String,
trim: true,
maxlength: 80,
},
rating: {
type: Number,
@@ -113,7 +123,7 @@ const activitySchema = new mongoose.Schema(
// Booking sessions - các đợt booking với thông số riêng
bookingSessions: [
{
sessionId: { type: String, required: true },
sessionId: { type: String, required: true, maxlength: 80 },
startDate: { type: Date, required: true },
endDate: { type: Date, required: true },
overnightStays: { type: Number, required: true, default: 14 },
@@ -127,11 +137,11 @@ const activitySchema = new mongoose.Schema(
// Danh sách booking cho session này
bookingList: [
{
address: { type: String, required: true },
address: { type: String, required: true, maxlength: 255 },
agreeNewsletter: { type: Boolean, default: false },
agreeTerms: { type: Boolean, required: true },
city: { type: String, required: true },
country: { type: String, required: true },
city: { type: String, required: true, maxlength: 80 },
country: { type: String, required: true, maxlength: 80 },
dietaryRestrictions: {
type: String,
enum: ['none', 'vegetarian', 'vegan', 'halal', 'kosher', 'gluten-free', 'other'],
@@ -141,26 +151,27 @@ const activitySchema = new mongoose.Schema(
type: String,
required: true,
lowercase: true,
trim: true
trim: true,
maxlength: 120
},
emergencyContact: { type: String, required: true },
emergencyPhone: { type: String, required: true },
medicalConditions: { type: String, default: '' },
emergencyContact: { type: String, required: true, maxlength: 80 },
emergencyPhone: { type: String, required: true, maxlength: 40 },
medicalConditions: { type: String, default: '', maxlength: 500 },
numberOfParticipants: { type: Number, required: true, min: 1 },
parentFirstName: { type: String, required: true, trim: true },
parentLastName: { type: String, required: true, trim: true },
parentFirstName: { type: String, required: true, trim: true, maxlength: 80 },
parentLastName: { type: String, required: true, trim: true, maxlength: 80 },
participantBirthDate: { type: Date, required: true },
participantFirstName: { type: String, required: true, trim: true },
participantFirstName: { type: String, required: true, trim: true, maxlength: 80 },
participantGender: {
type: String,
enum: ['male', 'female', 'other'],
required: true
},
participantLastName: { type: String, required: true, trim: true },
phone: { type: String, required: true },
postalCode: { type: String, required: true },
sessionDate: { type: String, required: true }, // sessionId reference
specialRequests: { type: String, default: '' },
participantLastName: { type: String, required: true, trim: true, maxlength: 80 },
phone: { type: String, required: true, maxlength: 40 },
postalCode: { type: String, required: true, maxlength: 20 },
sessionDate: { type: String, required: true, maxlength: 80 }, // sessionId reference
specialRequests: { type: String, default: '', maxlength: 500 },
// Thêm các trường quản lý
bookingStatus: {
type: String,
@@ -175,8 +186,8 @@ const activitySchema = new mongoose.Schema(
totalAmount: { type: Number, default: 0 },
paidAmount: { type: Number, default: 0 },
bookingDate: { type: Date, default: Date.now },
confirmationCode: { type: String, unique: true },
adminNotes: { type: String, default: '' }
confirmationCode: { type: String, unique: true, maxlength: 32 },
adminNotes: { type: String, default: '', maxlength: 1000 }
}
]
}

View File

@@ -11,70 +11,70 @@ if (mongoose.connection.models.Booking) {
const bookingSchema = new mongoose.Schema(
{
hero: {
title: String,
backgroundImage: String,
title: { type: String, trim: true, maxlength: 80 },
backgroundImage: { type: String, trim: true, maxlength: 255 },
},
searchBar: {
locationLabel: String,
holidaySeasonLabel: String,
searchButtonText: String,
locationLabel: { type: String, trim: true, maxlength: 64 },
holidaySeasonLabel: { type: String, trim: true, maxlength: 64 },
searchButtonText: { type: String, trim: true, maxlength: 64 },
},
filterPanel: {
title: String,
priceTitle: String,
priceLabel: String,
pricePlaceholder: String,
title: { type: String, trim: true, maxlength: 80 },
priceTitle: { type: String, trim: true, maxlength: 64 },
priceLabel: { type: String, trim: true, maxlength: 64 },
pricePlaceholder: { type: String, trim: true, maxlength: 64 },
priceMin: Number,
priceMax: Number,
activitiesTitle: String,
ageTitle: String,
ageSelectPlaceholder: String,
activitiesTitle: { type: String, trim: true, maxlength: 64 },
ageTitle: { type: String, trim: true, maxlength: 64 },
ageSelectPlaceholder: { type: String, trim: true, maxlength: 64 },
ageMin: Number,
ageMax: Number,
ratingTitle: String,
ratingTitle: { type: String, trim: true, maxlength: 64 },
ratingOptions: [
{
value: String,
label: String,
value: { type: String, trim: true, maxlength: 48 },
label: { type: String, trim: true, maxlength: 64 },
},
],
resetButtonText: String,
resetButtonText: { type: String, trim: true, maxlength: 64 },
},
programs: [
{
value: String,
label: String,
value: { type: String, trim: true, maxlength: 64 },
label: { type: String, trim: true, maxlength: 64 },
},
],
holidays: [
{
value: String,
label: String,
value: { type: String, trim: true, maxlength: 64 },
label: { type: String, trim: true, maxlength: 64 },
},
],
locations: [
{
value: String,
label: String,
value: { type: String, trim: true, maxlength: 64 },
label: { type: String, trim: true, maxlength: 64 },
},
],
camps: [
{
name: String,
name: { type: String, trim: true, maxlength: 120 },
price: Number,
priceText: String,
priceText: { type: String, trim: true, maxlength: 32 },
season: [String],
age: [Number],
locations: [String],
image: String,
link: String,
program: String,
image: { type: String, trim: true, maxlength: 255 },
link: { type: String, trim: true, maxlength: 255 },
program: { type: String, trim: true, maxlength: 80 },
rating: Number,
},
],
@@ -103,4 +103,4 @@ const bookingSchema = new mongoose.Schema(
}
);
module.exports = mongoose.model("Booking", bookingSchema);
module.exports = mongoose.model("Booking", bookingSchema);

View File

@@ -15,11 +15,13 @@ const breadcrumbItemSchema = new mongoose.Schema(
type: String,
trim: true,
default: "",
maxlength: 40,
},
link: {
type: String,
trim: true,
default: "",
maxlength: 255,
},
},
{ _id: false }
@@ -32,16 +34,19 @@ const heroSchema = new mongoose.Schema(
type: String,
trim: true,
default: "Pricing Plan",
maxlength: 60,
},
backgroundImage: {
type: String,
trim: true,
default: "/assets/img/inner-page/breadcrumb.jpg",
maxlength: 255,
},
shapeImage: {
type: String,
trim: true,
default: "/assets/img/inner-page/shape.png",
maxlength: 255,
},
breadcrumb: {
type: [breadcrumbItemSchema],
@@ -58,16 +63,19 @@ const pricingSectionSchema = new mongoose.Schema(
type: String,
trim: true,
default: "pricing plan",
maxlength: 64,
},
heading: {
type: String,
trim: true,
default: "Flexible Plans to Suit Every Traveler",
maxlength: 120,
},
description: {
type: String,
trim: true,
default: "",
maxlength: 500,
},
},
{ _id: false }
@@ -80,36 +88,43 @@ const planSchema = new mongoose.Schema(
type: String,
trim: true,
required: true,
maxlength: 64,
},
price: {
type: String,
trim: true,
default: "0",
maxlength: 32,
},
period: {
type: String,
trim: true,
default: "mo",
maxlength: 8,
},
currency: {
type: String,
trim: true,
default: "$",
maxlength: 8,
},
buttonText: {
type: String,
trim: true,
default: "Get Started Today",
maxlength: 64,
},
buttonLink: {
type: String,
trim: true,
default: "/pricing",
maxlength: 255,
},
buttonIcon: {
type: String,
trim: true,
default: "fa-solid fa-arrow-right",
maxlength: 64,
},
style: {
type: String,
@@ -118,7 +133,7 @@ const planSchema = new mongoose.Schema(
default: "default",
},
features: {
type: [String],
type: [{ type: String, maxlength: 96 }],
default: [],
},
},
@@ -147,11 +162,13 @@ const testimonialItemSchema = new mongoose.Schema(
type: String,
trim: true,
default: "",
maxlength: 64,
},
role: {
type: String,
trim: true,
default: "",
maxlength: 64,
},
rating: {
type: Number,
@@ -163,6 +180,7 @@ const testimonialItemSchema = new mongoose.Schema(
type: String,
trim: true,
default: "",
maxlength: 400,
},
},
{ _id: false }
@@ -175,31 +193,37 @@ const testimonialsSchema = new mongoose.Schema(
type: String,
trim: true,
default: "What Our Clients Say",
maxlength: 64,
},
heading: {
type: String,
trim: true,
default: "Immigration Success Stories",
maxlength: 120,
},
buttonText: {
type: String,
trim: true,
default: "View All Review",
maxlength: 64,
},
buttonLink: {
type: String,
trim: true,
default: "/contact",
maxlength: 255,
},
buttonIcon: {
type: String,
trim: true,
default: "fa-solid fa-arrow-right",
maxlength: 64,
},
image: {
type: String,
trim: true,
default: "",
maxlength: 255,
},
items: {
type: [testimonialItemSchema],