feat: add pricing page with API integration and dynamic rendering

This commit is contained in:
LNHA
2026-02-03 14:53:10 +07:00
parent 0398135018
commit 6c5c92c9d9
7 changed files with 282 additions and 345 deletions

View File

@@ -1,29 +1,119 @@
export default function PricingPage() {
import Breadcrumb from "../components/Breadcrumb";
import { PricingData, PricingAPIResponse, Plan, TestimonialItem } from "./types";
const API_URL = process.env.NEXT_PUBLIC_API_URL || "http://localhost:3001";
async function getPricingData(): Promise<PricingData | null> {
try {
const res = await fetch(`${API_URL}/api/pricing`, {
next: { revalidate: 60 }, // Revalidate every 60 seconds
});
if (!res.ok) {
throw new Error("Failed to fetch pricing data");
}
const response: PricingAPIResponse = await res.json();
return response.data;
} catch (error) {
console.error("Error fetching pricing data:", error);
return null;
}
}
// Component to render a single plan
function PlanCard({ plan, index }: { plan: Plan; index: number }) {
const styleClass = plan.style === "style-2" ? " style-2" : "";
const buttonStyleClass = plan.style === "style-2" ? " style-2" : "";
return (
<div className={`pricing-box-items${styleClass}`}>
<div className="pricing-header">
<h2>
<sup>{plan.currency}</sup>
{plan.price}
<sub>/{plan.period}</sub>
</h2>
<span className="sub-texts">{plan.name}</span>
</div>
<a href={plan.buttonLink} className={`theme-btn${buttonStyleClass}`}>
{plan.buttonText}
<i className={plan.buttonIcon}></i>
</a>
<ul className="pricing-list">
{plan.features.map((feature, idx) => (
<li key={idx}>
<i className="fa-solid fa-chevrons-right"></i> {feature}
</li>
))}
</ul>
</div>
);
}
// Component to render star rating
function StarRating({ rating }: { rating: number }) {
return (
<div className="star">
{[...Array(rating)].map((_, i) => (
<i key={i} className="fa-solid fa-star"></i>
))}
</div>
);
}
// Component to render testimonial slide
function TestimonialSlide({ item }: { item: TestimonialItem }) {
return (
<div className="swiper-slide">
<div className="content">
<StarRating rating={item.rating} />
<h3>&ldquo;{item.content}&rdquo;</h3>
<div className="info-item">
<div className="icon">
<i className="fa-solid fa-quote-right"></i>
</div>
<div className="content">
<h5>{item.name},</h5>
<span>{item.role}</span>
</div>
</div>
</div>
</div>
);
}
export default async function PricingPage() {
const data = await getPricingData();
// Fallback values if data is not available
const pricingSection = data?.pricingSection || {
subtitle: "pricing plan",
heading: "Flexible Plans to Suit Every Traveler",
description: "Choose the plan that fits your visa needs and enjoy expert guidance every step of the way.",
};
const plans = data?.plans || {
monthly: [],
yearly: [],
};
const testimonials = data?.testimonials || {
subtitle: "What Our Clients Say",
heading: "Immigration Success Stories",
buttonText: "View All Review",
buttonLink: "/contact",
buttonIcon: "fa-solid fa-arrow-right",
image: "/assets/img/home-3/test-thumb.jpg",
items: [],
};
const hero = data?.hero || {
title: "pricing plan",
};
return (
<>
{/* Breadcrumb-Wrapper Section Start */}
<section
className="breadcrumb-wrapper fix bg-cover"
style={{ backgroundImage: "url(/assets/img/inner-page/breadcrumb.jpg)" }}
>
<div className="shape">
<img src="/assets/img/inner-page/shape.png" alt="img" />
</div>
<div className="container">
<div className="page-heading">
<h1 className="breadcrumb-title">pricing plan</h1>
<ul className="breadcrumb-list">
<li>
<a href="/">Home</a>
</li>
<li>
<i className="fa-solid fa-chevron-right"></i>
</li>
<li>Pricing Plan</li>
</ul>
</div>
</div>
</section>
<Breadcrumb title={hero.title} current={hero.title} />
{/* Pricing Section Start */}
<section className="pricing-section-2 section-padding fix section-bg-1">
@@ -34,15 +124,14 @@ export default function PricingPage() {
<div className="pricing-content">
<div className="section-title mb-0">
<span className="sub-title-2 wow fadeInUp">
pricing plan
{pricingSection.subtitle}
</span>
<h2 className="split-text-right split-text-in-right">
Flexible Plans to Suit Every Traveler
{pricingSection.heading}
</h2>
</div>
<p className="pricing-text wow fadeInUp" data-wow-delay=".5s">
Choose the plan that fits your visa needs and enjoy expert
guidance every step of the way.
{pricingSection.description}
</p>
<div
className="d-flex mt-3 mt-md-0 wow fadeInUp"
@@ -85,6 +174,7 @@ export default function PricingPage() {
<div className="col-xl-6 col-lg-7">
<div className="pricing__tab-content">
<div className="tab-content" id="nav-tabContent">
{/* Monthly Plans Tab */}
<div
className="tab-pane fade active show"
id="pt-1"
@@ -92,80 +182,12 @@ export default function PricingPage() {
aria-labelledby="pt-1-tab"
>
<div className="pricing-right-items">
<div className="pricing-box-items">
<div className="pricing-header">
<h2>
<sup>$</sup>
32
<sub>/mo</sub>
</h2>
<span className="sub-texts">Basic Plan</span>
</div>
<a href="/pricing" className="theme-btn">
Get Started Today
<i className="fa-solid fa-arrow-right"></i>
</a>
<ul className="pricing-list">
<li>
<i className="fa-solid fa-chevrons-right"></i>{" "}
Everything in Basic Plan
</li>
<li>
<i className="fa-solid fa-chevrons-right"></i>{" "}
Visa Interview Preparation
</li>
<li>
<i className="fa-solid fa-chevrons-right"></i>{" "}
Priority Processing Support
</li>
<li>
<i className="fa-solid fa-chevrons-right"></i>{" "}
Phone &amp; Email Assistance
</li>
<li>
<i className="fa-solid fa-chevrons-right"></i>{" "}
Step-by-Step Application Support
</li>
</ul>
</div>
<div className="pricing-box-items style-2">
<div className="pricing-header">
<h2>
<sup>$</sup>
32
<sub>/mo</sub>
</h2>
<span className="sub-texts">Premium Plan</span>
</div>
<a href="/pricing" className="theme-btn style-2">
Get Started Today
<i className="fa-solid fa-arrow-right"></i>
</a>
<ul className="pricing-list">
<li>
<i className="fa-solid fa-chevrons-right"></i>{" "}
Everything in Basic Plan
</li>
<li>
<i className="fa-solid fa-chevrons-right"></i>{" "}
Visa Interview Preparation
</li>
<li>
<i className="fa-solid fa-chevrons-right"></i>{" "}
Priority Processing Support
</li>
<li>
<i className="fa-solid fa-chevrons-right"></i>{" "}
Phone &amp; Email Assistance
</li>
<li>
<i className="fa-solid fa-chevrons-right"></i>{" "}
Step-by-Step Application Support
</li>
</ul>
</div>
{plans.monthly.map((plan, index) => (
<PlanCard key={index} plan={plan} index={index} />
))}
</div>
</div>
{/* Yearly Plans Tab */}
<div
className="tab-pane fade"
id="pt-2"
@@ -173,78 +195,9 @@ export default function PricingPage() {
aria-labelledby="pt-2-tab"
>
<div className="pricing-right-items">
<div className="pricing-box-items">
<div className="pricing-header">
<h2>
<sup>$</sup>
32
<sub>/mo</sub>
</h2>
<span className="sub-texts">Basic Plan</span>
</div>
<a href="/pricing" className="theme-btn">
Get Started Today
<i className="fa-solid fa-arrow-right"></i>
</a>
<ul className="pricing-list">
<li>
<i className="fa-solid fa-chevrons-right"></i>{" "}
Everything in Basic Plan
</li>
<li>
<i className="fa-solid fa-chevrons-right"></i>{" "}
Visa Interview Preparation
</li>
<li>
<i className="fa-solid fa-chevrons-right"></i>{" "}
Priority Processing Support
</li>
<li>
<i className="fa-solid fa-chevrons-right"></i>{" "}
Phone &amp; Email Assistance
</li>
<li>
<i className="fa-solid fa-chevrons-right"></i>{" "}
Step-by-Step Application Support
</li>
</ul>
</div>
<div className="pricing-box-items style-2">
<div className="pricing-header">
<h2>
<sup>$</sup>
32
<sub>/mo</sub>
</h2>
<span className="sub-texts">Premium Plan</span>
</div>
<a href="/pricing" className="theme-btn style-2">
Get Started Today
<i className="fa-solid fa-arrow-right"></i>
</a>
<ul className="pricing-list">
<li>
<i className="fa-solid fa-chevrons-right"></i>{" "}
Everything in Basic Plan
</li>
<li>
<i className="fa-solid fa-chevrons-right"></i>{" "}
Visa Interview Preparation
</li>
<li>
<i className="fa-solid fa-chevrons-right"></i>{" "}
Priority Processing Support
</li>
<li>
<i className="fa-solid fa-chevrons-right"></i>{" "}
Phone &amp; Email Assistance
</li>
<li>
<i className="fa-solid fa-chevrons-right"></i>{" "}
Step-by-Step Application Support
</li>
</ul>
</div>
{plans.yearly.map((plan, index) => (
<PlanCard key={index} plan={plan} index={index} />
))}
</div>
</div>
</div>
@@ -261,82 +214,31 @@ export default function PricingPage() {
<div className="section-title-area">
<div className="section-title mb-0">
<span className="sub-title-2 wow fadeInUp">
What Our Clients Say
{testimonials.subtitle}
</span>
<h2 className="split-text-right split-text-in-right">
Immigration Success Stories
{testimonials.heading}
</h2>
</div>
<a href="/contact" className="theme-btn">
View All Review
<i className="fa-solid fa-arrow-right"></i>
<a href={testimonials.buttonLink} className="theme-btn">
{testimonials.buttonText}
<i className={testimonials.buttonIcon}></i>
</a>
</div>
<div className="testimonial-wrapper-3">
<div className="row g-4 align-items-center">
<div className="col-lg-4">
<div className="testimonial-thumb">
<img src="/assets/img/home-3/test-thumb.jpg" alt="img" />
<img src={testimonials.image} alt="testimonial" />
</div>
</div>
<div className="col-lg-8">
<div className="testimonial-content">
<div className="swiper testimonial-slider-3">
<div className="swiper-wrapper">
<div className="swiper-slide">
<div className="content">
<div className="star">
<i className="fa-solid fa-star"></i>
<i className="fa-solid fa-star"></i>
<i className="fa-solid fa-star"></i>
<i className="fa-solid fa-star"></i>
<i className="fa-solid fa-star"></i>
</div>
<h3>
&ldquo;The team provided exceptional guidance
throughout my immigration process. Their expertise,
personalized support, and attention to detail
ensured a smooth, stress-free experience and
successful visa approval.&rdquo;
</h3>
<div className="info-item">
<div className="icon">
<i className="fa-solid fa-quote-right"></i>
</div>
<div className="content">
<h5>Mohammed Ali,</h5>
<span>Family Visa</span>
</div>
</div>
</div>
</div>
<div className="swiper-slide">
<div className="content">
<div className="star">
<i className="fa-solid fa-star"></i>
<i className="fa-solid fa-star"></i>
<i className="fa-solid fa-star"></i>
<i className="fa-solid fa-star"></i>
<i className="fa-solid fa-star"></i>
</div>
<h3>
&ldquo;The team provided exceptional guidance
throughout my immigration process. Their expertise,
personalized support, and attention to detail
ensured a smooth, stress-free experience and
successful visa approval.&rdquo;
</h3>
<div className="info-item">
<div className="icon">
<i className="fa-solid fa-quote-right"></i>
</div>
<div className="content">
<h5>Mohammed Ali,</h5>
<span>Family Visa</span>
</div>
</div>
</div>
</div>
{testimonials.items.map((item, index) => (
<TestimonialSlide key={index} item={item} />
))}
</div>
</div>
<div className="array-buttons-3">

65
app/pricing/types.ts Normal file
View File

@@ -0,0 +1,65 @@
// Type definitions for Pricing page
export interface BreadcrumbItem {
text: string;
link: string;
}
export interface Hero {
title: string;
backgroundImage: string;
shapeImage: string;
breadcrumb: BreadcrumbItem[];
}
export interface PricingSection {
subtitle: string;
heading: string;
description: string;
}
export interface Plan {
name: string;
price: string;
period: string;
currency: string;
buttonText: string;
buttonLink: string;
buttonIcon: string;
style: "default" | "style-2";
features: string[];
}
export interface Plans {
monthly: Plan[];
yearly: Plan[];
}
export interface TestimonialItem {
name: string;
role: string;
rating: number;
content: string;
}
export interface Testimonials {
subtitle: string;
heading: string;
buttonText: string;
buttonLink: string;
buttonIcon: string;
image: string;
items: TestimonialItem[];
}
export interface PricingData {
hero: Hero;
pricingSection: PricingSection;
plans: Plans;
testimonials: Testimonials;
}
export interface PricingAPIResponse {
success: boolean;
data: PricingData;
}