diff --git a/api/homeApi.ts b/api/homeApi.ts new file mode 100644 index 0000000..857329d --- /dev/null +++ b/api/homeApi.ts @@ -0,0 +1,43 @@ +/** + * Lấy API URL từ environment variable + */ +const getApiUrl = (): string => { + const apiUrl = process.env.NEXT_PUBLIC_API_URL; + + if (!apiUrl) { + console.warn('NEXT_PUBLIC_API_URL is not set. Using default http://localhost:3001'); + return 'http://localhost:3001'; + } + + return apiUrl; +}; + +/** + * Fetch home page data từ API + * @returns Promise + * @throws Error nếu fetch thất bại + */ +export const fetchHomeData = async (): Promise => { + const apiUrl = getApiUrl(); + const url = `${apiUrl}/api/home`; + + try { + const response = await fetch(url, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + cache: 'no-store', + }); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data = await response.json(); + return data; + } catch (error) { + console.error('Error fetching home data:', error); + return null; // Trả về null để fallback sang dữ liệu local + } +}; diff --git a/api/index.ts b/api/index.ts index e576090..34cd0fc 100644 --- a/api/index.ts +++ b/api/index.ts @@ -2,3 +2,4 @@ * Export all API functions */ export * from './blogsApi'; +export * from './homeApi'; diff --git a/app/components/home/BlogPreview.tsx b/app/components/home/BlogPreview.tsx index 28ca33a..ab19413 100644 --- a/app/components/home/BlogPreview.tsx +++ b/app/components/home/BlogPreview.tsx @@ -1,3 +1,4 @@ +import { getCmsImageUrl } from '@/utils/image'; import Link from 'next/link'; interface BlogPreviewProps { @@ -46,40 +47,66 @@ const BlogPreview = ({ data }: BlogPreviewProps) => {
- {data.items.map((item, index) => ( -
-
-
- img - {item.category} -
-
-
-
-
-
-
-
-
- Comment ({item.comments.toString().padStart(2, '0')}) - _ {formatDate(item.date)} -
-

- - {item.title} - -

-
-
- img - By {item.author.name} + {data.items.map((item, index) => { + const thumbUrl = getCmsImageUrl(item.thumbnail); + return ( +
+
+
+ img + {item.category} +
+
+
+
+
+
+
+
+
+ Comment ({item.comments.toString().padStart(2, '0')}) + _ {formatDate(item.date)} +
+

+ + {item.title} + +

+
+
+
+ {item.author.name.charAt(0).toUpperCase()} +
+ By {item.author.name} +
+ View Articles
- View Articles
-
- ))} + ); + })}
diff --git a/app/components/home/HeroSection.tsx b/app/components/home/HeroSection.tsx index 07d93e1..013979f 100644 --- a/app/components/home/HeroSection.tsx +++ b/app/components/home/HeroSection.tsx @@ -1,3 +1,4 @@ +import { getCmsImageUrl } from '@/utils/image'; import Link from 'next/link'; interface HeroSectionProps { @@ -20,7 +21,7 @@ interface HeroSectionProps { const HeroSection = ({ data }: HeroSectionProps) => { return ( -
+
img
diff --git a/app/components/home/Partners.tsx b/app/components/home/Partners.tsx index 4135917..fc41f3b 100644 --- a/app/components/home/Partners.tsx +++ b/app/components/home/Partners.tsx @@ -1,11 +1,19 @@ +import { getCmsImageUrl } from '@/utils/image'; + interface PartnersProps { data: { - heading: string; - items: { - name: string; - logo: string; - year: string; - }[]; + visaConsultancy: { + items: { + name: string; + icon: string; + year: string; + }[]; + }; + brands: { + items: { + logo: string; + }[]; + }; }; } @@ -16,11 +24,11 @@ const Partners = ({ data }: PartnersProps) => {
- {data.items.slice(0, 4).map((partner, index) => ( + {(data.visaConsultancy?.items || []).map((partner, index) => (
- img + {partner.name}

{partner.name}

{partner.year}
@@ -38,10 +46,10 @@ const Partners = ({ data }: PartnersProps) => {
- {data.items.slice(4).map((partner, index) => ( + {(data.brands?.items || []).map((brand, index) => (
- {partner.name} + brand-logo
))} diff --git a/app/home.json b/app/home.json index 4ecd14f..13205e5 100644 --- a/app/home.json +++ b/app/home.json @@ -212,7 +212,7 @@ "subheading": "Did You Know", "items": [ { - "value": "1000", + "value": "1", "suffix": "k+", "label": "Students Guided", "description": "Successfully assisted over a thousand students worldwide." @@ -238,54 +238,39 @@ ] }, "partners": { - "heading": "Our Trusted Partners", - "items": [ - { - "name": "Best Visa Consultancy", - "logo": "/assets/img/home-1/feature/icon-1.png", - "year": "2025" - }, - { - "name": "Visa Success Award", - "logo": "/assets/img/home-1/feature/icon-2.png", - "year": "2025" - }, - { - "name": "Innovation Award", - "logo": "/assets/img/home-1/feature/icon-3.png", - "year": "2025" - }, - { - "name": "Global Education Partner", - "logo": "/assets/img/home-1/feature/icon-4.png", - "year": "2025" - }, - { - "name": "University Partner 1", - "logo": "/assets/img/home-1/brand/01.png", - "year": "2025" - }, - { - "name": "University Partner 2", - "logo": "/assets/img/home-1/brand/02.png", - "year": "2025" - }, - { - "name": "University Partner 3", - "logo": "/assets/img/home-1/brand/03.png", - "year": "2025" - }, - { - "name": "University Partner 4", - "logo": "/assets/img/home-1/brand/04.png", - "year": "2025" - }, - { - "name": "University Partner 5", - "logo": "/assets/img/home-1/brand/05.png", - "year": "2025" - } - ] + "visaConsultancy": { + "items": [ + { + "name": "Best Visa Consultancy", + "icon": "/assets/img/home-1/feature/icon-1.png", + "year": "2025" + }, + { + "name": "Visa Success Award", + "icon": "/assets/img/home-1/feature/icon-2.png", + "year": "2025" + }, + { + "name": "Innovation Award", + "icon": "/assets/img/home-1/feature/icon-3.png", + "year": "2025" + }, + { + "name": "Global Education Partner", + "icon": "/assets/img/home-1/feature/icon-4.png", + "year": "2025" + } + ] + }, + "brands": { + "items": [ + { "logo": "/assets/img/home-1/brand/01.png" }, + { "logo": "/assets/img/home-1/brand/02.png" }, + { "logo": "/assets/img/home-1/brand/03.png" }, + { "logo": "/assets/img/home-1/brand/04.png" }, + { "logo": "/assets/img/home-1/brand/05.png" } + ] + } }, "blogPreview": { "heading": "Latest Insights & Updates", diff --git a/app/page.tsx b/app/page.tsx index 6daa385..3112ec8 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -8,21 +8,42 @@ import FAQSection from './components/home/FAQSection'; import Achievements from './components/home/Achievements'; import Partners from './components/home/Partners'; import BlogPreview from './components/home/BlogPreview'; -import homeData from './home.json'; +import localHomeData from './home.json'; +import { fetchHomeData } from '@/api'; +import { getCmsImageUrl } from '@/utils/image'; + +export default async function Home() { + // Fetch home data (blog aggregation is now handled by the backend) + const apiHomeData = await fetchHomeData(); + + // Use API home data if available, otherwise fallback to local JSON + const data = JSON.parse(JSON.stringify(apiHomeData || localHomeData)); + + // Process Hero Image URL + if (data.hero?.backgroundImage) { + data.hero.backgroundImage = getCmsImageUrl(data.hero.backgroundImage); + } + + // Process blog images from CMS (thumbnail normalization) + if (data.blogPreview?.items) { + data.blogPreview.items = data.blogPreview.items.map((item: any) => ({ + ...item, + thumbnail: getCmsImageUrl(item.thumbnail) || '/assets/img/home-1/news/01.jpg' + })); + } -export default function Home() { return ( <> - - - - - - - - - - + + + + + + + + + + ); } diff --git a/package.json b/package.json index 1272bc7..0858510 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "lint": "eslint" }, "dependencies": { + "axios": "^1.13.4", "next": "16.1.6", "react": "19.2.3", "react-dom": "19.2.3"