Update UI, Call API

This commit is contained in:
r2xrzh9q2z-lab
2026-02-05 05:55:00 +07:00
parent 0bad73da4c
commit 0b2e3d2123
10 changed files with 1489 additions and 1386 deletions

View File

@@ -2,17 +2,8 @@
import { useState, useEffect } from "react";
import React from "react";
import visaData from "../visa.json";
import Breadcrumb from "../components/Breadcrumb";
const ASSET_URL = process.env.NEXT_PUBLIC_API_URL || "";
interface VisaCountryData {
id: number;
name: string;
icon: string;
services: string[];
}
import VisaDetail from "./VisaDetail";
import { fetchVisaData, type VisaCountry } from "@/api/visa";
interface CountryDetailsProps {
params: Promise<{
@@ -20,48 +11,39 @@ interface CountryDetailsProps {
}>;
}
// Helper function to map slugs to country names
const getCountryFromSlug = (slug: string): string => {
const slugToCountry: { [key: string]: string } = {
france: "France",
uk: "UK",
canada: "Canada",
germany: "Germany",
spain: "Spain",
"south-korea": "South Korea",
japan: "Japan",
croatia: "Croatia",
england: "England",
indonesia: "Indonesia",
"united-states-of-america": "United States of America",
};
return slugToCountry[slug] || slug;
};
export default function CountryDetailsPage({ params }: CountryDetailsProps) {
const [country, setCountry] = useState<VisaCountryData | null>(null);
// 1. Quản lý trạng thái dữ liệu
const [country, setCountry] = useState<VisaCountry | null>(null);
const [loading, setLoading] = useState(true);
const [slug, setSlug] = useState<string>("");
useEffect(() => {
// Unwrap the params Promise
Promise.resolve(params).then((resolvedParams) => {
const currentSlug = resolvedParams.slug;
setSlug(currentSlug);
const initPage = async () => {
try {
setLoading(true);
const countryName = getCountryFromSlug(currentSlug);
const foundCountry = visaData.visaSystem.summaryList.find(
(c) => c.name === countryName,
);
// 2. Giải nén params (Vì params là Promise trong Next.js mới)
const resolvedParams = await params;
const currentSlug = resolvedParams.slug;
if (foundCountry) {
setCountry(foundCountry);
// 3. Lấy dữ liệu từ API bên trong useEffect
const visaData = await fetchVisaData();
// 4. Tìm nước trong danh sách summaryList dựa trên slug từ URL
const found = visaData.hero.summaryList.find(
(item: VisaCountry) => item.slug === currentSlug,
);
if (found) {
setCountry(found);
}
} catch (error) {
console.error("Lỗi khi tải dữ liệu:", error);
} finally {
setLoading(false);
}
setLoading(false);
});
}, [params]);
};
initPage();
}, [params]);
if (loading) {
return (
<div className="flex items-center justify-center min-h-screen">
@@ -70,215 +52,14 @@ export default function CountryDetailsPage({ params }: CountryDetailsProps) {
);
}
if (!country) {
// Xử lý khi không tìm thấy dữ liệu
if (!country || !country.detailedView?.activeCountry) {
return (
<div className="flex items-center justify-center min-h-screen">
<h1>Country not found</h1>
<h1>Country details not found</h1>
</div>
);
}
const breadcrumbData = visaData.visaSystem.breadcrumb;
const countryData = visaData.visaSystem.detailedView.activeCountry;
const relatedCountries = visaData.visaSystem.detailedView.relatedCountries;
const contactInfo = visaData.visaSystem.contactInfo;
return (
<>
{/* Breadcrumb-Wrapper Section Start */}
<Breadcrumb
title={breadcrumbData.list.title}
breadcrumbItems={[
{ label: "Home", href: "/" },
{ label: countryData.name },
]}
backgroundImage={`${ASSET_URL}/${breadcrumbData.list.image}`}
/>
{/* Country-details Section Start */}
<section className="country-details-section section-padding fix">
<div className="container">
<div className="country-details-wrapper">
<div className="row g-4">
{/* Main Content */}
<div className="col-lg-8">
<div className="country-details-post">
<div className="details-image">
<img
src={`${ASSET_URL}/${countryData.mainImage}`}
alt="img"
/>
</div>
<div className="country-details-content">
<h2>{countryData.name}</h2>
<p>{countryData.description}</p>
<p className="mt-3">{countryData.additionalInfo}</p>
<h5>{countryData.tagline}</h5>
{/* Visa Types */}
<div className="tourist-visa-box">
{/* Render mảng đầu tiên (index 0) */}
{countryData.visaTypes[0] && (
<div className="tourist-box style-2">
{countryData.visaTypes[0].items.map(
(item: any, itemIdx: number) => (
<div key={itemIdx} className="tourist-content">
<h5>{item.title}</h5>
<p>{item.description}</p>
</div>
),
)}
</div>
)}
{/* Render mảng thứ hai (index 1) */}
{countryData.visaTypes[1] && (
<div className="tourist-box">
{countryData.visaTypes[1].items.map(
(item: any, itemIdx: number) => (
<div key={itemIdx} className="tourist-content">
<h5>{item.title}</h5>
<p>{item.description}</p>
</div>
),
)}
</div>
)}
</div>
{/* Visa Process */}
<h3 className="text">{countryData.visaProcess.title}</h3>
<ul className="list-item">
{countryData.visaProcess.steps.map(
(process: any, idx: number) => (
<li key={idx}>
{process.number}. {process.title}
<span>{process.description}</span>
</li>
),
)}
</ul>
{/* Gallery */}
<div className="row g-4 mb-4">
{countryData.gallery.map((image: string, idx: number) => (
<div key={idx} className="col-lg-6">
<div className="thumb">
<img src={`${ASSET_URL}/${image}`} alt="img" />
</div>
</div>
))}
</div>
{/* Visa Categories */}
<h3 className="text mb-3">
{countryData.visaCategories.title}
</h3>
{countryData.visaCategories.steps.map(
(subGroup: string[], groupIdx: number) => (
<ul className="visa-list-2" key={groupIdx}>
{/* Map lần 2 để render từng chuỗi trong mảng con */}
{subGroup.map((category: string, idx: number) => (
<li key={idx}>
<i className="fa-solid fa-chevrons-right"></i>
{category}
</li>
))}
</ul>
),
)}
{/* Service Options */}
<h3 className="text">{countryData.visaService.title}</h3>
<ul className="list-item">
{countryData.visaService.steps.map(
(process: any, idx: number) => (
<li key={idx}>
{process.number}. {process.title}
<span>{process.description}</span>
</li>
),
)}
</ul>
</div>
</div>
</div>
{/* Sidebar */}
<div className="col-lg-4">
<div className="country-details-sideber">
{relatedCountries.map((relCountry: any, idx: number) => (
<div key={idx} className="icon-box-item">
<div className="left-item">
<div className="icon">
<img
src={`${ASSET_URL}/${relCountry.icon}`}
alt="img"
/>
</div>
<h5>{relCountry.name}</h5>
</div>
<i className="fa-solid fa-chevrons-right"></i>
</div>
))}
{/* Contact Box */}
<div
className="visa-contact-box bg-cover"
style={{
backgroundImage: `url(${ASSET_URL}/assets/img/inner-page/country-details/bg.jpg)`,
padding: "30px",
borderRadius: "8px",
color: "white",
}}
>
<div className="content">
<h3>{contactInfo.sectionTitle}</h3>
<p>{contactInfo.helpText}</p>
<div className="icon-item">
<div className="icon">
<i className="fa-solid fa-phone"></i>
</div>
<div className="cont">
<span>{contactInfo.phone.label}: </span>
<h6>
<a href={`tel:${contactInfo.phone.link}`}>
{contactInfo.phone.value}
</a>
</h6>
</div>
</div>
<div className="icon-item">
<div className="icon">
<i className="fa-regular fa-envelope"></i>
</div>
<div className="cont">
<span>{contactInfo.email.label}: </span>
<h6>
<a href={`mailto:${contactInfo.email.link}`}>
{contactInfo.email.value}
</a>
</h6>
</div>
</div>
<div className="icon-item">
<div className="icon">
<i className="fa-regular fa-location-dot"></i>
</div>
<div className="cont">
<span>{contactInfo.location.label}: </span>
<h6>{contactInfo.location.address}</h6>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</>
);
return <VisaDetail country={country} />;
}