diff --git a/src/app/article/[slug]/page.tsx b/src/app/article/[slug]/page.tsx index 440bcf8..cb7b2a0 100644 --- a/src/app/article/[slug]/page.tsx +++ b/src/app/article/[slug]/page.tsx @@ -14,7 +14,9 @@ const ArticleDetail = () => { if (typeof slug === "string") { const getArticleDetail = async () => { const data = await fetchArticleDetail(slug); - setArticleDetails(data[0]); + if (Array.isArray(data) && data.length > 0) { + setArticleDetails(data[0]); + } }; getArticleDetail(); } diff --git a/src/app/article/page.tsx b/src/app/article/page.tsx index 6438fee..2cf2e0e 100644 --- a/src/app/article/page.tsx +++ b/src/app/article/page.tsx @@ -1,9 +1,8 @@ "use client"; import { Suspense, useEffect, useState } from "react"; -import { format } from "date-fns"; -import Link from "next/link"; -import { ArticleListDataType } from "@/types/article"; import { fetchListArticles } from "@/api/apiService"; +import ArticleItem from "@/components/articles/ArticleItem"; // Import ArticleItem component +import { ArticleListDataType } from "@/types/article"; const HomeArticle = () => { const [articleList, setArticleList] = useState( @@ -13,240 +12,149 @@ const HomeArticle = () => { useEffect(() => { const fetchArticleList = async () => { - const data = await fetchListArticles(); - setArticleList(data.list); - setLoadingUI(false); + try { + const data = await fetchListArticles(); + setArticleList(data.list); + } catch (error) { + console.error("Failed to fetch articles:", error); + } finally { + setLoadingUI(false); + } }; fetchArticleList(); }, []); return ( - - {loadingUi ? ( -
-
-
-

- Blog -

-
- Cập nhật từ Hurasoft -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Xem thêm bài khác -

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+

Blog

+
+ Cập nhật từ Hurasoft
- ) : ( -
-
-
-

- Blog -

-
- Cập nhật từ Hurasoft -
-
- {articleList && - Array.isArray(articleList) && - articleList.length > 0 ? ( -
-
-
- {articleList.slice(0, 1).map((item) => ( -
- - {item.title} - -
-
- {" "} - - {format( - new Date(item.last_update * 1000), - "dd/MM/yyyy HH:mm" - )} - -
- - {item.title} - -
-
- ))} -
-
- {articleList.slice(1, 3).map((item) => ( -
- - {item.title} - -
-
- {" "} - - {format( - new Date(item.last_update * 1000), - "dd/MM/yyyy HH:mm" - )} - -
- - {item.title} - -
-
- ))} + + {loadingUi ? ( + <> +
+
+
+
+
+
+
+
-
-

- Xem thêm bài khác -

-
- {articleList.map((item) => ( -
- - {item.title} - -
-
- {" "} - - {format( - new Date(item.last_update * 1000), - "dd/MM/yyyy HH:mm" - )} - -
- - {item.title} - -
-
- ))} +
+
+
+
+
+
+
+
+
+
+
+
+
+
- ) : ( - <> - )} -
-
- )} -
+
+

+ Xem thêm bài khác +

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + ) : ( + <> + {articleList && + Array.isArray(articleList) && + articleList.length > 0 ? ( +
+
+
+ {articleList.slice(0, 1).map((article) => ( + + ))} +
+
+ {articleList?.slice(1, 3).map((article) => ( + + ))} +
+
+ +
+

+ Xem thêm bài khác +

+
+ {articleList.map((article) => ( + + ))} +
+
+
+ ) : ( + <> + )} + + )} + +
+
); }; diff --git a/src/app/job/[slug]/page.tsx b/src/app/job/[slug]/page.tsx index bbc805f..3632345 100644 --- a/src/app/job/[slug]/page.tsx +++ b/src/app/job/[slug]/page.tsx @@ -30,35 +30,35 @@ const JobDetails = () => { {loadingUi ? (
-

+

-

- +

+
-

- +

+
-

- +

+
-
-
+
+
-
+
    -
  • -
  • -
  • -
  • -
  • +
  • +
  • +
  • +
  • +
diff --git a/src/app/job/page.tsx b/src/app/job/page.tsx index 0b10b33..0ecf0f2 100644 --- a/src/app/job/page.tsx +++ b/src/app/job/page.tsx @@ -1,104 +1,62 @@ "use client"; -import Link from "next/link"; -import { Suspense, useState, useEffect } from "react"; -import { ListJobDataType } from "@/types/job"; +import { useState, useEffect } from "react"; import { fetchListJobs } from "@/api/apiService"; +import JobItem from "@/components/jobs/JobItem"; +import { ListJobDataType } from "@/types/job"; const HomeJob = () => { - const [ListJob, setListJob] = useState(null); + const [jobList, setJobList] = useState(null); const [loadingUi, setLoadingUI] = useState(true); useEffect(() => { - const getListJob = async () => { - const data = await fetchListJobs(); - setListJob(data.list); - setLoadingUI(false); + const getJobList = async () => { + try { + const data = await fetchListJobs(); + setJobList(data.list); + } catch (error) { + console.error("Failed to fetch jobs:", error); + } finally { + setLoadingUI(false); + } }; - getListJob(); + getJobList(); }, []); return (

Tuyển dụng

- - {loadingUi ? ( -
-
+ {loadingUi ? ( +
+ {[...Array(4)].map((_, index) => ( +
-
-
+
+
-
-
+
+
-
-
-
-
-
-
-
-
-
+ ))} +
+ ) : ( +
+ {jobList && Array.isArray(jobList) && jobList.length > 0 ? ( +
+ {jobList.map((job) => ( + + ))}
-
-
-
-
-
-
-
-
-
+ ) : ( +
+ Không có công việc nào.
-
-
-
-
-
-
-
-
-
-
-
- ) : ( -
- {ListJob && Array.isArray(ListJob) && ListJob.length > 0 ? ( -
- {ListJob.map((item) => ( -
-
- - {item.title} - -
{item.end_date}
-
-
-
{item.location}
- - Ứng tuyển ngay{" "} - - -
-
- ))} -
- ) : ( -
- Không có công việc nào. -
- )} -
- )} - + )} +
+ )}
); diff --git a/src/app/page.tsx b/src/app/page.tsx index 6454264..3dd4c4b 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,668 +1,561 @@ "use client"; -import { Suspense, useState, useEffect } from "react"; -import Image from "next/image"; +import { Suspense, useEffect } from "react"; import Link from "next/link"; -import { format } from "date-fns"; +import Image from "next/image"; +import useArticles from "@/hooks/useArticles"; // Import custom hook +import ProductCard from "@/components/products/ProductCard"; +import ArticleCard from "@/components/articles/ArticleCard"; import { homePageEffect } from "@/effects/homeEffect"; -import { ArticleListDataType } from "@/types/article"; -import { fetchListArticles } from "@/api/apiService"; export default function Home() { - const [articlesList, setArticleList] = useState( - null - ); - - const [loadingUi, setLoadingUI] = useState(true); + const { articles, loading, error } = useArticles(); // Dùng hook để lấy dữ liệu useEffect(() => { - const typingNode = document.getElementById("typewriter") as HTMLElement; - if (typingNode) { - homePageEffect.showTypingEffect(typingNode); + const typingElement = document.getElementById("typewriter") as HTMLElement; + if (typingElement) { + homePageEffect.showTypingEffect(typingElement); } - - // slider đối tác homePageEffect.startCarousel("#navheight", true, 3000); - - const fetchArticleNews = async () => { - try { - const data = await fetchListArticles(); - setArticleList(data.list); - setLoadingUI(false); - } catch (error) { - console.log("Failed to fetch articles", error); - } - }; - - fetchArticleNews(); }, []); return ( - <> -
-
-
-
- Cung cấp{" "} - - các giải pháp tmđt toàn diện & chuyên nghiệp. - - -
-
- Tư vấn và phát triển website, marketing online, chăm sóc khách - hàng, tên miền, cloud hosting -
-
- - - Liên hệ - -
+
+ {/* Slogan */} +
+
+
+ Cung cấp{" "} + + các giải pháp tmđt toàn diện & chuyên nghiệp. + + +
+
+ Tư vấn và phát triển website, marketing online, chăm sóc khách hàng, + tên miền, cloud hosting +
+
+ + + Liên hệ +
- -
-
-

Khách hàng đồng hành

- -
-
- -
-
-

Sản phẩm

- - {loadingUi ? ( -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ) : ( -
-
-
-
- hura 8 -
-

- Phần mềm tạo website TMĐT chạy nên nền tảng Cloud dành - cho doanh nghiệp lớn -

-
- - Xem thêm - -
-
-
-
- xstore -
-

- Nền tảng quản lý cửa hàng nhỏ toàn diện, bao gồm: Quản - lý đơn hàng đa kênh, Kho hàng, Website và POS -

-
- - Xem thêm - -
-
-
-
- icon-adman -
-

- Nền tảng marketing toàn diện dành cho các chuyên gia. -

-
- - Xem thêm - -
-
-
-
- icon-chatngay -
-

- Phần mềm hỗ trợ, cskh tích hợp website giúp khách hàng - tương tác nhanh chóng và thuận tiện. -

-
- - Xem thêm - -
-
- )} -
-
-
- - -
-
-

Có gì mới?

- {loadingUi ? ( -
-
-
-
-
-
-
-
-
-
-
-
- ) : Array.isArray(articlesList) && articlesList.length > 0 ? ( -
- {articlesList.slice(0, 1).map((item) => ( -
-
-
- - Blog -
- - {item.title} - -
- {item.summary} -
- - Chi tiết - - -
-
- Đăng bởi - Admin -
-
- {" "} - - {format( - new Date(item.last_update * 1000), - "dd/MM/yyyy HH:mm" - )} - -
-
-
-
- {item.title} -
-
- ))} -
- ) : ( - <> // Nếu không có dữ liệu, không hiển thị gì - )} -
-
-
- + + {/* Khách hàng đồng hành */} +
+
+

Khách hàng đồng hành

+ +
+
+ + {/* Sản phẩm */} + +
+
+

Sản phẩm

+ {loading ? ( +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ) : ( +
+ + + + +
+ )} +
+
+
+ + {/* Bài viết mới */} + +
+
+

Có gì mới?

+ {loading || error ? ( +
+
+
+
+
+
+
+
+
+
+
+
+ ) : Array.isArray(articles) && articles.length > 0 ? ( +
+ {articles.slice(0, 1).map((article) => ( + + ))} +
+ ) : ( +
Không có bài viết nào.
+ )} +
+
+
+
); } diff --git a/src/components/LoadingUi.tsx b/src/components/LoadingUi.tsx deleted file mode 100644 index 198c69d..0000000 --- a/src/components/LoadingUi.tsx +++ /dev/null @@ -1,7 +0,0 @@ -export default function LoadingUI({ width = "100%", height = "150px" }) { - return ( -
-
Đang tải...
-
- ); -} diff --git a/src/components/articles/ArticleCard.tsx b/src/components/articles/ArticleCard.tsx new file mode 100644 index 0000000..328a8d2 --- /dev/null +++ b/src/components/articles/ArticleCard.tsx @@ -0,0 +1,44 @@ +// ArticleCard.tsx +import { format } from "date-fns"; +import { Article } from "@/types/article"; + +const ArticleCard = ({ article }: { article: Article }) => ( +
+
+
+ + Blog +
+ + {article.title} + +
{article.summary}
+ + Chi tiết + + +
+
+ Đăng bởi + Admin +
+
+ {" "} + + {format(new Date(article.last_update * 1000), "dd/MM/yyyy HH:mm")} + +
+
+
+
+ {article.title} +
+
+); + +export default ArticleCard; diff --git a/src/components/articles/ArticleItem.tsx b/src/components/articles/ArticleItem.tsx new file mode 100644 index 0000000..20b7b1b --- /dev/null +++ b/src/components/articles/ArticleItem.tsx @@ -0,0 +1,28 @@ +import { format } from "date-fns"; +import { Article } from "@/types/article"; + +const ArticleItem = ({ article }: { article: Article }) => ( +
+ + {article.title} + +
+
+ {" "} + + {format(new Date(article.last_update * 1000), "dd/MM/yyyy HH:mm")} + +
+ + {article.title} + +
+
+); + +export default ArticleItem; diff --git a/src/components/jobs/JobItem.tsx b/src/components/jobs/JobItem.tsx new file mode 100644 index 0000000..bbd89ef --- /dev/null +++ b/src/components/jobs/JobItem.tsx @@ -0,0 +1,21 @@ +import Link from "next/link"; +import { JobdataType } from "@/types/job"; + +const JobItem = ({ job }: { job: JobdataType }) => ( +
+
+ + {job.title} + +
{job.end_date}
+
+
+
{job.location}
+ + Ứng tuyển ngay + +
+
+); + +export default JobItem; diff --git a/src/components/products/ProductCard.tsx b/src/components/products/ProductCard.tsx new file mode 100644 index 0000000..278aa81 --- /dev/null +++ b/src/components/products/ProductCard.tsx @@ -0,0 +1,27 @@ +import Image from "next/image"; + +const ProductCard = ({ + title, + description, + imageSrc, + link, +}: { + title: string; + description: string; + imageSrc: string; + link: string; +}) => ( +
+
+
+ {title} +
+

{description}

+
+ + Xem thêm + +
+); + +export default ProductCard; diff --git a/src/hooks/useArticles.ts b/src/hooks/useArticles.ts new file mode 100644 index 0000000..e07fb37 --- /dev/null +++ b/src/hooks/useArticles.ts @@ -0,0 +1,29 @@ +import { useState, useEffect } from "react"; +import { ArticleListDataType } from "@/types/article"; +import { fetchListArticles } from "@/api/apiService"; + +// Custom Hook để lấy danh sách bài viết +const useArticles = () => { + const [articles, setArticles] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchArticles = async () => { + try { + const data = await fetchListArticles(); + setArticles(data.list); + } catch { + setError('Lỗi khi tải bài viết.'); + } finally { + setLoading(false); + } + }; + + fetchArticles(); + }, []); + + return { articles, loading, error }; +}; + +export default useArticles;