forked from UKSOURCE/cms.hailearning.edu.vn
feat: Implement core admin panel functionalities including appointment, contact, and pricing management with associated models, controllers, views, and routes.
This commit is contained in:
328
models/pricing.js
Normal file
328
models/pricing.js
Normal 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);
|
||||
Reference in New Issue
Block a user