feat: Add service details page with API integration

This commit is contained in:
nguyenvanbao
2026-02-03 10:18:53 +07:00
parent 8d105dda9c
commit 9f67fd44ef
8 changed files with 1136 additions and 71 deletions

View File

@@ -0,0 +1,170 @@
import { Metadata } from "next";
import { fetchServiceBySlug } from "../../../../api/servicesApi";
import { notFound } from "next/navigation";
import { imageUrl } from "../../../utils/image";
import Breadcrumb from "../../../components/Breadcrumb";
interface ServiceDetailsPageProps {
params: Promise<{
slug: string;
}>;
}
export async function generateMetadata({
params,
}: ServiceDetailsPageProps): Promise<Metadata> {
try {
const { slug } = await params;
const data = await fetchServiceBySlug(slug);
if (!data || !data.serviceDetails) {
return {
title: "Service Not Found",
description: "The requested service could not be found.",
};
}
return {
title: `${data.serviceDetails.content.title} - Visaway Immigration Services`,
description: data.serviceDetails.content.description,
};
} catch (error) {
return {
title: "Service Not Found",
description: "The requested service could not be found.",
};
}
}
export default async function ServiceDetailsPage({
params,
}: ServiceDetailsPageProps) {
try {
const { slug } = await params;
console.log("Service details page - slug:", slug);
const data = await fetchServiceBySlug(slug);
if (!data || !data.serviceDetails) {
notFound();
}
const { serviceDetails } = data;
const { content, keyFeatures, faq } = serviceDetails;
return (
<>
{/* Breadcrumb Section */}
<Breadcrumb title="Services Details" current="Service Details" />
{/* Service-details Section Start */}
<section className="service-details-section section-padding fix">
<div className="container">
<div className="service-details-wrapper">
<div className="row">
<div className="col-xl-12">
<div className="service-details-post">
<h2>{content.title}</h2>
<p className="mt-2">{content.description}</p>
<div className="details-image">
<img src={imageUrl(content.mainImage)} alt="img" />
</div>
<h3 className="text">{content.overviewTitle}</h3>
<p className="mt-3 mb-3">{content.overviewDescription}</p>
<p className="mb-4">{content.additionalDescription}</p>
<div className="row g-4">
<div className="col-lg-6">
<div className="service-left-content">
<h3>{keyFeatures.title}</h3>
<ul className="list-item">
{keyFeatures.items.map(
(feature: any, index: number) => (
<li key={index}>
<i className="fa-solid fa-chevrons-right"></i>
<span>{feature.title} -</span>
{feature.description}
</li>
),
)}
</ul>
</div>
</div>
<div className="col-lg-6">
<div className="thumb">
<img
src={imageUrl(keyFeatures.sideImage)}
alt="img"
/>
</div>
</div>
</div>
<div className="row mt-4 mt-xl-0 g-4">
<div className="col-lg-6">
<div className="thumb">
<img src={imageUrl(faq.sideImage)} alt="img" />
</div>
</div>
<div className="col-lg-6">
<div className="faq-items">
<h3 className="mb-3">{faq.title}</h3>
<div className="accordion" id="accordionExample">
{faq.items.map((faqItem: any, index: number) => {
const isExpanded = faqItem.isExpanded;
return (
<div
key={`faq-${index}`}
className="accordion-item wow fadeInUp"
data-wow-delay={`.${(index + 1) * 2}s`}
>
<h5
className="accordion-header"
id={`heading${index}`}
>
<button
className={`accordion-button ${!isExpanded ? "collapsed" : ""}`}
type="button"
data-bs-toggle="collapse"
data-bs-target={`#collapse${index}`}
aria-expanded={
isExpanded ? "true" : "false"
}
aria-controls={`collapse${index}`}
>
{faqItem.question}
</button>
</h5>
<div
id={`collapse${index}`}
className={`accordion-collapse collapse ${isExpanded ? "show" : ""}`}
aria-labelledby={`heading${index}`}
data-bs-parent="#accordionExample"
>
<div className="accordion-body">
<p>{faqItem.answer}</p>
</div>
</div>
</div>
);
})}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</>
);
} catch (error) {
console.error("Error loading service details:", error);
const { slug } = await params;
console.log("Slug that failed:", slug);
console.log("Falling back to not found page");
notFound();
}
}