feat(header): add admin UI and APIs for header management

This commit is contained in:
2026-02-04 10:06:22 +07:00
parent 00f93cefab
commit befe6b30aa
12 changed files with 2359 additions and 1622 deletions

View File

@@ -1,129 +1,115 @@
const mongoose = require('mongoose');
const mongoose = require("mongoose");
// Schema cho các link trong topbar
const topbarLinkSchema = new mongoose.Schema({
text: {
type: String,
required: true,
trim: true
},
url: {
type: String,
required: true,
trim: true
}
}, { _id: false });
const socialLinkSchema = new mongoose.Schema(
{
platform: {
type: String,
required: true,
enum: ["linkedin", "twitter", "instagram", "youtube", "facebook"],
},
url: {
type: String,
required: true,
},
icon: String,
order: {
type: Number,
default: 0,
},
},
{ _id: false },
);
// Schema cho contact info trong topbar
const contactInfoSchema = new mongoose.Schema({
phone: {
type: String,
required: true,
trim: true
},
email: {
type: String,
required: true,
trim: true
}
}, { _id: false });
const languageSchema = new mongoose.Schema(
{
name: {
type: String,
required: true,
},
value: {
type: String,
required: true,
},
},
{ _id: false },
);
// Schema cho topbar
const topbarSchema = new mongoose.Schema({
contactInfo: {
type: contactInfoSchema,
required: true
},
links: {
type: [topbarLinkSchema],
default: []
}
}, { _id: false });
const menuItemSchema = new mongoose.Schema(
{
label: {
type: String,
required: true,
},
href: {
type: String,
required: true,
},
icon: String,
order: {
type: Number,
default: 0,
},
children: [this],
},
{ _id: false },
);
// Main Header Schema
const headerSchema = new mongoose.Schema({
name: {
type: String,
default: 'default',
unique: true
},
topbar: {
type: topbarSchema,
required: true
},
logo: {
type: String,
required: true,
trim: true
}
}, {
timestamps: true
});
const headerSchema = new mongoose.Schema(
{
// Top bar
top: {
phone: String,
email: String,
location: String,
socialLinks: [socialLinkSchema],
languages: [languageSchema],
},
// Method để lấy menu tree từ collection menuHeader
headerSchema.statics.getMenuTree = async function() {
try {
const Menu = require('./menuHeader');
return await Menu.getMenuTree();
} catch (error) {
console.error('Error getting menu tree:', error);
throw error;
}
};
// Offcanvas
offcanvas: {
description: String,
contactInfo: {
address: String,
email: String,
workingHours: String,
phone: String,
},
},
// Method để lấy menu tree với programmes từ collection menuHeader
headerSchema.statics.getMenuTreeWithProgrammes = async function() {
try {
const Menu = require('./menuHeader');
return await Menu.getMenuTreeWithProgrammes();
} catch (error) {
console.error('Error getting menu tree with programmes:', error);
throw error;
}
};
// Menu
menu: [menuItemSchema],
// Method để lấy programmes theo menu ID
headerSchema.statics.getProgrammesByMenuId = async function(menuId) {
try {
const Menu = require('./menuHeader');
return await Menu.getProgrammesByMenuId(menuId);
} catch (error) {
console.error('Error getting programmes by menu ID:', error);
throw error;
}
};
// Logo
logo: {
light: String,
dark: String,
alt: String,
},
// Tạo migration script để import dữ liệu từ menu.json
headerSchema.statics.migrateFromJson = async function(jsonData) {
try {
// Kiểm tra xem đã có header mặc định chưa
const existingHeader = await this.findOne({ name: 'default' });
// Chỉ giữ lại topbar và logo, bỏ search và mainMenu
const headerData = {
topbar: jsonData.topbar,
logo: jsonData.logo
};
if (existingHeader) {
// Cập nhật header hiện có
Object.assign(existingHeader, headerData);
await existingHeader.save();
console.log('Header data updated successfully');
return existingHeader;
} else {
// Tạo header mới với dữ liệu từ JSON
const newHeader = await this.create({
name: 'default',
...headerData
});
console.log('Header data imported successfully');
return newHeader;
}
} catch (error) {
console.error('Error migrating header data:', error);
throw error;
}
};
// CTA Button
ctaButton: {
label: String,
href: String,
style: {
type: String,
enum: ["primary", "secondary", "outline"],
default: "primary",
},
},
module.exports = mongoose.model('Header', headerSchema);
// Status
status: {
type: String,
enum: ["active", "inactive"],
default: "active",
},
order: {
type: Number,
default: 1,
},
},
{ timestamps: true },
);
module.exports = mongoose.model("Header", headerSchema);