feat: Implement core admin panel functionalities including appointment, contact, and pricing management with associated models, controllers, views, and routes.

This commit is contained in:
LNHA
2026-02-03 14:58:00 +07:00
parent d1b931d547
commit df8e1f9665
25 changed files with 4574 additions and 659 deletions

328
models/pricing.js Normal file
View File

@@ -0,0 +1,328 @@
const mongoose = require("mongoose");
// Clear cache
if (mongoose.models.Pricing) {
delete mongoose.models.Pricing;
}
if (mongoose.connection.models.Pricing) {
delete mongoose.connection.models.Pricing;
}
// Schema for breadcrumb item
const breadcrumbItemSchema = new mongoose.Schema(
{
text: {
type: String,
trim: true,
default: "",
},
link: {
type: String,
trim: true,
default: "",
},
},
{ _id: false }
);
// Schema for hero section
const heroSchema = new mongoose.Schema(
{
title: {
type: String,
trim: true,
default: "Pricing Plan",
},
backgroundImage: {
type: String,
trim: true,
default: "/assets/img/inner-page/breadcrumb.jpg",
},
shapeImage: {
type: String,
trim: true,
default: "/assets/img/inner-page/shape.png",
},
breadcrumb: {
type: [breadcrumbItemSchema],
default: [],
},
},
{ _id: false }
);
// Schema for pricing section header
const pricingSectionSchema = new mongoose.Schema(
{
subtitle: {
type: String,
trim: true,
default: "pricing plan",
},
heading: {
type: String,
trim: true,
default: "Flexible Plans to Suit Every Traveler",
},
description: {
type: String,
trim: true,
default: "",
},
},
{ _id: false }
);
// Schema for individual plan
const planSchema = new mongoose.Schema(
{
name: {
type: String,
trim: true,
required: true,
},
price: {
type: String,
trim: true,
default: "0",
},
period: {
type: String,
trim: true,
default: "mo",
},
currency: {
type: String,
trim: true,
default: "$",
},
buttonText: {
type: String,
trim: true,
default: "Get Started Today",
},
buttonLink: {
type: String,
trim: true,
default: "/pricing",
},
buttonIcon: {
type: String,
trim: true,
default: "fa-solid fa-arrow-right",
},
style: {
type: String,
trim: true,
enum: ["default", "style-2"],
default: "default",
},
features: {
type: [String],
default: [],
},
},
{ _id: false }
);
// Schema for plans container
const plansSchema = new mongoose.Schema(
{
monthly: {
type: [planSchema],
default: [],
},
yearly: {
type: [planSchema],
default: [],
},
},
{ _id: false }
);
// Schema for testimonial item
const testimonialItemSchema = new mongoose.Schema(
{
name: {
type: String,
trim: true,
default: "",
},
role: {
type: String,
trim: true,
default: "",
},
rating: {
type: Number,
min: 1,
max: 5,
default: 5,
},
content: {
type: String,
trim: true,
default: "",
},
},
{ _id: false }
);
// Schema for testimonials section
const testimonialsSchema = new mongoose.Schema(
{
subtitle: {
type: String,
trim: true,
default: "What Our Clients Say",
},
heading: {
type: String,
trim: true,
default: "Immigration Success Stories",
},
buttonText: {
type: String,
trim: true,
default: "View All Review",
},
buttonLink: {
type: String,
trim: true,
default: "/contact",
},
buttonIcon: {
type: String,
trim: true,
default: "fa-solid fa-arrow-right",
},
image: {
type: String,
trim: true,
default: "",
},
items: {
type: [testimonialItemSchema],
default: [],
},
},
{ _id: false }
);
// Main Pricing Schema
const pricingSchema = new mongoose.Schema(
{
name: {
type: String,
default: "default",
unique: true,
},
hero: {
type: heroSchema,
default: () => ({}),
},
pricingSection: {
type: pricingSectionSchema,
default: () => ({}),
},
plans: {
type: plansSchema,
default: () => ({}),
},
testimonials: {
type: testimonialsSchema,
default: () => ({}),
},
},
{
timestamps: true,
}
);
// Migration method to import data from JSON
pricingSchema.statics.migrateFromJson = async function (jsonData) {
try {
// Check if default pricing exists
const existingPricing = await this.findOne({ name: "default" });
// Process data from JSON
const processedData = {
hero: {
title: jsonData.hero?.title || "Pricing Plan",
backgroundImage: jsonData.hero?.backgroundImage || "/assets/img/inner-page/breadcrumb.jpg",
shapeImage: jsonData.hero?.shapeImage || "/assets/img/inner-page/shape.png",
breadcrumb: (jsonData.hero?.breadcrumb || []).map((item) => ({
text: item.text || "",
link: item.link || "",
})),
},
pricingSection: {
subtitle: jsonData.pricingSection?.subtitle || "pricing plan",
heading: jsonData.pricingSection?.heading || "Flexible Plans to Suit Every Traveler",
description: jsonData.pricingSection?.description || "",
},
plans: {
monthly: (jsonData.plans?.monthly || []).map((plan) => ({
name: plan.name || "",
price: plan.price || "0",
period: plan.period || "mo",
currency: plan.currency || "$",
buttonText: plan.buttonText || "Get Started Today",
buttonLink: plan.buttonLink || "/pricing",
buttonIcon: plan.buttonIcon || "fa-solid fa-arrow-right",
style: plan.style || "default",
features: plan.features || [],
})),
yearly: (jsonData.plans?.yearly || []).map((plan) => ({
name: plan.name || "",
price: plan.price || "0",
period: plan.period || "mo",
currency: plan.currency || "$",
buttonText: plan.buttonText || "Get Started Today",
buttonLink: plan.buttonLink || "/pricing",
buttonIcon: plan.buttonIcon || "fa-solid fa-arrow-right",
style: plan.style || "default",
features: plan.features || [],
})),
},
testimonials: {
subtitle: jsonData.testimonials?.subtitle || "What Our Clients Say",
heading: jsonData.testimonials?.heading || "Immigration Success Stories",
buttonText: jsonData.testimonials?.buttonText || "View All Review",
buttonLink: jsonData.testimonials?.buttonLink || "/contact",
buttonIcon: jsonData.testimonials?.buttonIcon || "fa-solid fa-arrow-right",
image: jsonData.testimonials?.image || "",
items: (jsonData.testimonials?.items || []).map((item) => ({
name: item.name || "",
role: item.role || "",
rating: item.rating || 5,
content: item.content || "",
})),
},
};
if (existingPricing) {
// Update existing pricing
existingPricing.hero = processedData.hero;
existingPricing.pricingSection = processedData.pricingSection;
existingPricing.plans = processedData.plans;
existingPricing.testimonials = processedData.testimonials;
await existingPricing.save();
console.log("Pricing data updated successfully");
return existingPricing;
} else {
// Create new pricing
const newPricing = await this.create({
name: "default",
...processedData,
});
console.log("Pricing data imported successfully");
return newPricing;
}
} catch (error) {
console.error("Error migrating pricing data:", error);
throw error;
}
};
module.exports = mongoose.model("Pricing", pricingSchema);