feat: refactor service API schema and update layout structure

This commit is contained in:
nguyenvanbao
2026-02-03 16:24:55 +07:00
parent a2a215dfd7
commit adef27b214
6 changed files with 92 additions and 84 deletions

View File

@@ -1,9 +1,8 @@
import type { Metadata } from "next";
import "./globals.css";
import Header from "./components/Header";
import Footer from "./components/Footer";
// import Header from "./components/Header";
// import Footer from "./components/Footer";
import Loader from "./components/Loader";
import BackToTop from "./components/BackToTop";
import MouseCursor from "./components/MouseCursor";
@@ -24,7 +23,7 @@ export default function RootLayout({
<head>
{/* Favicon */}
<link rel="shortcut icon" href="/assets/img/favicon.png" />
{/* Bootstrap min.css */}
<link rel="stylesheet" href="/assets/css/bootstrap.min.css" />
{/* All Min Css */}
@@ -35,7 +34,7 @@ export default function RootLayout({
<link rel="stylesheet" href="/assets/css/magnific-popup.css" />
{/* MeanMenu.css */}
<link rel="stylesheet" href="/assets/css/meanmenu.css" />
{/* Odometer.css */}
{/* Odometer.css */}
<link rel="stylesheet" href="/assets/css/odometer.css" />
{/* Swiper Bundle.css */}
<link rel="stylesheet" href="/assets/css/swiper-bundle.min.css" />
@@ -44,22 +43,37 @@ export default function RootLayout({
{/* Main.css */}
<link rel="stylesheet" href="/assets/css/main.css" />
</head>
<body className="smooth-scroll-yes">
<body className="smooth-scroll-yes">
<Loader />
<BackToTop />
<MouseCursor />
<Header />
{/* <Header /> */}
{children}
<Footer />
{/* <Footer /> */}
{/* Scripts */}
<Script src="/assets/js/jquery-3.7.1.min.js" strategy="beforeInteractive" />
<Script src="/assets/js/viewport.jquery.js" strategy="afterInteractive" />
<Script src="/assets/js/bootstrap.bundle.min.js" strategy="afterInteractive" />
<Script src="/assets/js/jquery.nice-select.min.js" strategy="afterInteractive" />
<Script src="/assets/js/jquery.waypoints.js" strategy="afterInteractive" />
<Script
src="/assets/js/jquery-3.7.1.min.js"
strategy="beforeInteractive"
/>
<Script
src="/assets/js/viewport.jquery.js"
strategy="afterInteractive"
/>
<Script
src="/assets/js/bootstrap.bundle.min.js"
strategy="afterInteractive"
/>
<Script
src="/assets/js/jquery.nice-select.min.js"
strategy="afterInteractive"
/>
<Script
src="/assets/js/jquery.waypoints.js"
strategy="afterInteractive"
/>
<Script src="/assets/js/odometer.min.js" strategy="afterInteractive" />
<Script
src="/assets/js/swiper-bundle.min.js"

View File

@@ -16,7 +16,6 @@ export async function generateMetadata({
try {
const { slug } = await params;
const data = await fetchServiceBySlug(slug);
if (!data || !data.serviceDetails) {
return {
title: "Service Not Found",
@@ -112,6 +111,10 @@ export default async function ServiceDetailsPage({
<div className="accordion" id="accordionExample">
{faq.items.map((faqItem: any, index: number) => {
const isExpanded = faqItem.isExpanded;
const questionNumber = String(index + 1).padStart(
2,
"0",
);
return (
<div
key={`faq-${index}`}
@@ -132,7 +135,7 @@ export default async function ServiceDetailsPage({
}
aria-controls={`collapse${index}`}
>
{faqItem.question}
{questionNumber}. {faqItem.question}
</button>
</h5>
<div

View File

@@ -11,6 +11,51 @@ export const metadata: Metadata = {
export default async function ServicesPage() {
const data = await fetchServicePageData();
const { services, destinations, visas, reviews } = data;
const country = [
{
id: "canada",
name: "Canada",
description:
"Canada provides quality education, rich culture and global opportunities",
image: "img/home-3/choose-us/01.jpg",
icon: "img/home-3/choose-us/icon-1.png",
link: "country-details.html",
},
{
id: "south-korea",
name: "South Korea",
description:
"South Korea offers advanced technology and cultural experiences",
image: "img/home-3/choose-us/02.jpg",
icon: "img/home-3/choose-us/icon-2.png",
link: "country-details.html",
},
{
id: "france",
name: "France",
description:
"France offers rich cultural heritage and educational excellence",
image: "img/home-3/choose-us/03.jpg",
icon: "img/home-3/choose-us/icon-3.png",
link: "country-details.html",
},
{
id: "uk",
name: "UK",
description: "UK provides world-class education and career opportunities",
image: "img/home-3/choose-us/04.jpg",
icon: "img/home-3/choose-us/icon-2.png",
link: "country-details.html",
},
{
id: "germany",
name: "Germany",
description: "Germany offers excellent education and strong economy",
image: "img/home-3/choose-us/05.jpg",
icon: "img/home-3/choose-us/icon-3.png",
link: "country-details.html",
},
];
return (
<>
@@ -99,7 +144,7 @@ export default async function ServicesPage() {
</h2>
</div>
<div className="destination-offer-wrapper-3 fade-up-anim row g-4 g-xl-4 row-cols-xl-5 row-cols-lg-4 row-cols-md-2 row-cols-1">
{destinations.items.map((country: any) => (
{country.map((country: any) => (
<div key={country.id} className="col destination-offer-item">
<div className="choose-us-image">
<img src={imageUrl(country.image)} alt="img" />
@@ -122,7 +167,7 @@ export default async function ServicesPage() {
</section>
{/* Service-Visa Section Start */}
<section className="service-visa-section fix">
{/* <section className="service-visa-section fix">
<div className="container">
<div className="service-visa-wrapper">
{visas.items.map((visa: any, index: number) => (
@@ -144,7 +189,7 @@ export default async function ServicesPage() {
))}
</div>
</div>
</section>
</section> */}
{/* Testimonial Section3 Start */}
<section className="testimonial-section section-padding fix">

View File

@@ -1,5 +1,13 @@
export const imageUrl = (path?: string) => {
if (!path) return "";
// Không có ảnh → ảnh mặc định
if (!path) return "/_images/default.jpg";
// Đã là full URL
if (path.startsWith("http")) return path;
return `${process.env.NEXT_PUBLIC_API_URL}/${path}`;
const base = (
process.env.NEXT_PUBLIC_API_URL || "http://localhost:3001"
).replace(/\/$/, "");
return `${base}/${path.replace(/^\//, "")}`;
};