update 26/03
This commit is contained in:
@@ -14,7 +14,9 @@ const ArticleDetail = () => {
|
|||||||
if (typeof slug === "string") {
|
if (typeof slug === "string") {
|
||||||
const getArticleDetail = async () => {
|
const getArticleDetail = async () => {
|
||||||
const data = await fetchArticleDetail(slug);
|
const data = await fetchArticleDetail(slug);
|
||||||
setArticleDetails(data[0]);
|
if (Array.isArray(data) && data.length > 0) {
|
||||||
|
setArticleDetails(data[0]);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
getArticleDetail();
|
getArticleDetail();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import { Suspense, useEffect, useState } from "react";
|
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 { fetchListArticles } from "@/api/apiService";
|
||||||
|
import ArticleItem from "@/components/articles/ArticleItem"; // Import ArticleItem component
|
||||||
|
import { ArticleListDataType } from "@/types/article";
|
||||||
|
|
||||||
const HomeArticle = () => {
|
const HomeArticle = () => {
|
||||||
const [articleList, setArticleList] = useState<ArticleListDataType | null>(
|
const [articleList, setArticleList] = useState<ArticleListDataType | null>(
|
||||||
@@ -13,240 +12,149 @@ const HomeArticle = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchArticleList = async () => {
|
const fetchArticleList = async () => {
|
||||||
const data = await fetchListArticles();
|
try {
|
||||||
setArticleList(data.list);
|
const data = await fetchListArticles();
|
||||||
setLoadingUI(false);
|
setArticleList(data.list);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to fetch articles:", error);
|
||||||
|
} finally {
|
||||||
|
setLoadingUI(false);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fetchArticleList();
|
fetchArticleList();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Suspense>
|
<div className="page-article">
|
||||||
{loadingUi ? (
|
<div className="container">
|
||||||
<div className="page-article">
|
<div className="main-title">
|
||||||
<div className="container">
|
<h2 className="text-center text-4xl text-sky-800 mb-[15px]">Blog</h2>
|
||||||
<div className="main-title">
|
<div className="text-center text-base text-gray-400">
|
||||||
<h2 className="text-center text-4xl text-sky-800 mb-[15px]">
|
Cập nhật từ Hurasoft
|
||||||
Blog
|
|
||||||
</h2>
|
|
||||||
<div className="text-center text-base text-gray-400">
|
|
||||||
Cập nhật từ Hurasoft
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="box-big-article flex">
|
|
||||||
<div className="box-big" id="js-holder-big">
|
|
||||||
<div className="relative">
|
|
||||||
<div className="bg-gray-300 w-full h-[606px] block rounded-[12px]"></div>
|
|
||||||
<div className="absolute bottom-[20px] left-[20px] right-0 p-[20px] bg-gray-50 rounded-[12px] w-[90%]">
|
|
||||||
<div className="time block bg-gray-200 w-[150px] h-[30px] rounded-[6px]"></div>
|
|
||||||
<div className="block bg-gray-200 w-full h-[50px] rounded-[6px]"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="box-small" id="js-holder-small">
|
|
||||||
<div className="item]">
|
|
||||||
<div className="bg-gray-300 w-[full] h-[202px] block rounded-[12px]"></div>
|
|
||||||
<div className="info mt-[10px]">
|
|
||||||
<div className="bg-gray-300 block w-[150px] h-[20px] mb-[5px]"></div>
|
|
||||||
<div className="bg-gray-300 block w-[full] h-[48px]"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="item]">
|
|
||||||
<div className="bg-gray-300 w-[full] h-[202px] block rounded-[12px]"></div>
|
|
||||||
<div className="info mt-[10px]">
|
|
||||||
<div className="bg-gray-300 block w-[150px] h-[20px] mb-[5px]"></div>
|
|
||||||
<div className="bg-gray-300 block w-[full] h-[48px]"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="box-article-other mt-[50px]">
|
|
||||||
<h2 className="title text-xl font-bold mb-[25px]">
|
|
||||||
Xem thêm bài khác
|
|
||||||
</h2>
|
|
||||||
<div className="list-article flex flex-wrap" id="js-list-article">
|
|
||||||
<div className="item-article">
|
|
||||||
<div className="bg-gray-300 w-[full] h-[202px] block rounded-[12px]"></div>
|
|
||||||
<div className="info mt-[10px]">
|
|
||||||
<div className="bg-gray-300 block w-[150px] h-[20px] mb-[5px]"></div>
|
|
||||||
<div className="bg-gray-300 block w-[full] h-[48px]"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="item-article">
|
|
||||||
<div className="bg-gray-300 w-[full] h-[202px] block rounded-[12px]"></div>
|
|
||||||
<div className="info mt-[10px]">
|
|
||||||
<div className="bg-gray-300 block w-[150px] h-[20px] mb-[5px]"></div>
|
|
||||||
<div className="bg-gray-300 block w-[full] h-[48px]"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="item-article">
|
|
||||||
<div className="bg-gray-300 w-[full] h-[202px] block rounded-[12px]"></div>
|
|
||||||
<div className="info mt-[10px]">
|
|
||||||
<div className="bg-gray-300 block w-[150px] h-[20px] mb-[5px]"></div>
|
|
||||||
<div className="bg-gray-300 block w-[full] h-[48px]"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="item-article">
|
|
||||||
<div className="bg-gray-300 w-[full] h-[202px] block rounded-[12px]"></div>
|
|
||||||
<div className="info mt-[10px]">
|
|
||||||
<div className="bg-gray-300 block w-[150px] h-[20px] mb-[5px]"></div>
|
|
||||||
<div className="bg-gray-300 block w-[full] h-[48px]"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="item-article">
|
|
||||||
<div className="bg-gray-300 w-[full] h-[202px] block rounded-[12px]"></div>
|
|
||||||
<div className="info mt-[10px]">
|
|
||||||
<div className="bg-gray-300 block w-[150px] h-[20px] mb-[5px]"></div>
|
|
||||||
<div className="bg-gray-300 block w-[full] h-[48px]"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="item-article">
|
|
||||||
<div className="bg-gray-300 w-[full] h-[202px] block rounded-[12px]"></div>
|
|
||||||
<div className="info mt-[10px]">
|
|
||||||
<div className="bg-gray-300 block w-[150px] h-[20px] mb-[5px]"></div>
|
|
||||||
<div className="bg-gray-300 block w-[full] h-[48px]"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
<Suspense>
|
||||||
<div className="page-article">
|
{loadingUi ? (
|
||||||
<div className="container">
|
<>
|
||||||
<div className="main-title">
|
<div className="box-big-article flex">
|
||||||
<h2 className="text-center text-4xl text-sky-800 mb-[15px]">
|
<div className="box-big" id="js-holder-big">
|
||||||
Blog
|
<div className="relative">
|
||||||
</h2>
|
<div className="bg-gray-300 w-full h-[606px] block rounded-[12px]"></div>
|
||||||
<div className="text-center text-base text-gray-400">
|
<div className="absolute bottom-[20px] left-[20px] right-0 p-[20px] bg-gray-50 rounded-[12px] w-[90%]">
|
||||||
Cập nhật từ Hurasoft
|
<div className="time block bg-gray-200 w-[150px] h-[30px] rounded-[6px]"></div>
|
||||||
</div>
|
<div className="block bg-gray-200 w-full h-[50px] rounded-[6px]"></div>
|
||||||
</div>
|
</div>
|
||||||
{articleList &&
|
|
||||||
Array.isArray(articleList) &&
|
|
||||||
articleList.length > 0 ? (
|
|
||||||
<div>
|
|
||||||
<div className="box-big-article flex">
|
|
||||||
<div className="box-big" id="js-holder-big">
|
|
||||||
{articleList.slice(0, 1).map((item) => (
|
|
||||||
<div className="item-article" key={item.id}>
|
|
||||||
<a
|
|
||||||
href={`/article${item.url}`}
|
|
||||||
className="image-article"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src={`https://hurasoft8.hurasoft.com/${item.image.large}`}
|
|
||||||
width={100}
|
|
||||||
height={100}
|
|
||||||
alt={item.title}
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
<div className="info">
|
|
||||||
<div className="time">
|
|
||||||
<i className="fa-regular fa-clock"></i>{" "}
|
|
||||||
<span>
|
|
||||||
{format(
|
|
||||||
new Date(item.last_update * 1000),
|
|
||||||
"dd/MM/yyyy HH:mm"
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<a
|
|
||||||
href={`/article${item.url}`}
|
|
||||||
className="name-article line-clamp-2"
|
|
||||||
>
|
|
||||||
{item.title}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<div className="box-small" id="js-holder-small">
|
|
||||||
{articleList.slice(1, 3).map((item) => (
|
|
||||||
<div className="item-article" key={item.id}>
|
|
||||||
<a
|
|
||||||
href={`/article/${item.url}`}
|
|
||||||
className="image-article"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src={`https://hurasoft8.hurasoft.com/${item.image.large}`}
|
|
||||||
width={100}
|
|
||||||
height={100}
|
|
||||||
alt={item.title}
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
<div className="info">
|
|
||||||
<div className="time">
|
|
||||||
<i className="fa-regular fa-clock"></i>{" "}
|
|
||||||
<span>
|
|
||||||
{format(
|
|
||||||
new Date(item.last_update * 1000),
|
|
||||||
"dd/MM/yyyy HH:mm"
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<a
|
|
||||||
href={`/article/${item.url}`}
|
|
||||||
className="name-article line-clamp-2"
|
|
||||||
>
|
|
||||||
{item.title}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="box-article-other mt-[50px]">
|
<div className="box-small" id="js-holder-small">
|
||||||
<h2 className="title text-xl font-bold mb-[25px]">
|
<div className="item]">
|
||||||
Xem thêm bài khác
|
<div className="bg-gray-300 w-[full] h-[202px] block rounded-[12px]"></div>
|
||||||
</h2>
|
<div className="info mt-[10px]">
|
||||||
<div
|
<div className="bg-gray-300 block w-[150px] h-[20px] mb-[5px]"></div>
|
||||||
className="list-article flex flex-wrap"
|
<div className="bg-gray-300 block w-[full] h-[48px]"></div>
|
||||||
id="js-list-article"
|
</div>
|
||||||
>
|
</div>
|
||||||
{articleList.map((item) => (
|
<div className="item]">
|
||||||
<div className="item-article" key={item.id}>
|
<div className="bg-gray-300 w-[full] h-[202px] block rounded-[12px]"></div>
|
||||||
<Link
|
<div className="info mt-[10px]">
|
||||||
href={`/article/${item.url}`}
|
<div className="bg-gray-300 block w-[150px] h-[20px] mb-[5px]"></div>
|
||||||
className="image-article"
|
<div className="bg-gray-300 block w-[full] h-[48px]"></div>
|
||||||
>
|
</div>
|
||||||
<img
|
|
||||||
src={`https://hurasoft8.hurasoft.com/${item.image.large}`}
|
|
||||||
width={100}
|
|
||||||
height={100}
|
|
||||||
alt={item.title}
|
|
||||||
/>
|
|
||||||
</Link>
|
|
||||||
<div className="info">
|
|
||||||
<div className="time">
|
|
||||||
<i className="fa-regular fa-clock"></i>{" "}
|
|
||||||
<span>
|
|
||||||
{format(
|
|
||||||
new Date(item.last_update * 1000),
|
|
||||||
"dd/MM/yyyy HH:mm"
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<Link
|
|
||||||
href={`/article/${item.url}`}
|
|
||||||
className="name-article line-clamp-2"
|
|
||||||
>
|
|
||||||
{item.title}
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
<div className="box-article-other mt-[50px]">
|
||||||
<></>
|
<h2 className="title text-xl font-bold mb-[25px]">
|
||||||
)}
|
Xem thêm bài khác
|
||||||
</div>
|
</h2>
|
||||||
</div>
|
<div
|
||||||
)}
|
className="list-article flex flex-wrap"
|
||||||
</Suspense>
|
id="js-list-article"
|
||||||
|
>
|
||||||
|
<div className="item-article">
|
||||||
|
<div className="bg-gray-300 w-[full] h-[202px] block rounded-[12px]"></div>
|
||||||
|
<div className="info mt-[10px]">
|
||||||
|
<div className="bg-gray-300 block w-[150px] h-[20px] mb-[5px]"></div>
|
||||||
|
<div className="bg-gray-300 block w-[full] h-[48px]"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="item-article">
|
||||||
|
<div className="bg-gray-300 w-[full] h-[202px] block rounded-[12px]"></div>
|
||||||
|
<div className="info mt-[10px]">
|
||||||
|
<div className="bg-gray-300 block w-[150px] h-[20px] mb-[5px]"></div>
|
||||||
|
<div className="bg-gray-300 block w-[full] h-[48px]"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="item-article">
|
||||||
|
<div className="bg-gray-300 w-[full] h-[202px] block rounded-[12px]"></div>
|
||||||
|
<div className="info mt-[10px]">
|
||||||
|
<div className="bg-gray-300 block w-[150px] h-[20px] mb-[5px]"></div>
|
||||||
|
<div className="bg-gray-300 block w-[full] h-[48px]"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="item-article">
|
||||||
|
<div className="bg-gray-300 w-[full] h-[202px] block rounded-[12px]"></div>
|
||||||
|
<div className="info mt-[10px]">
|
||||||
|
<div className="bg-gray-300 block w-[150px] h-[20px] mb-[5px]"></div>
|
||||||
|
<div className="bg-gray-300 block w-[full] h-[48px]"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="item-article">
|
||||||
|
<div className="bg-gray-300 w-[full] h-[202px] block rounded-[12px]"></div>
|
||||||
|
<div className="info mt-[10px]">
|
||||||
|
<div className="bg-gray-300 block w-[150px] h-[20px] mb-[5px]"></div>
|
||||||
|
<div className="bg-gray-300 block w-[full] h-[48px]"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="item-article">
|
||||||
|
<div className="bg-gray-300 w-[full] h-[202px] block rounded-[12px]"></div>
|
||||||
|
<div className="info mt-[10px]">
|
||||||
|
<div className="bg-gray-300 block w-[150px] h-[20px] mb-[5px]"></div>
|
||||||
|
<div className="bg-gray-300 block w-[full] h-[48px]"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{articleList &&
|
||||||
|
Array.isArray(articleList) &&
|
||||||
|
articleList.length > 0 ? (
|
||||||
|
<div>
|
||||||
|
<div className="box-big-article flex">
|
||||||
|
<div className="box-big w-full">
|
||||||
|
{articleList.slice(0, 1).map((article) => (
|
||||||
|
<ArticleItem key={article.id} article={article} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="box-small">
|
||||||
|
{articleList?.slice(1, 3).map((article) => (
|
||||||
|
<ArticleItem key={article.id} article={article} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="box-article-other mt-[50px]">
|
||||||
|
<h2 className="title text-xl font-bold mb-[25px]">
|
||||||
|
Xem thêm bài khác
|
||||||
|
</h2>
|
||||||
|
<div className="list-article flex flex-wrap">
|
||||||
|
{articleList.map((article) => (
|
||||||
|
<ArticleItem key={article.id} article={article} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Suspense>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -30,35 +30,35 @@ const JobDetails = () => {
|
|||||||
<Suspense>
|
<Suspense>
|
||||||
{loadingUi ? (
|
{loadingUi ? (
|
||||||
<div className="container-job">
|
<div className="container-job">
|
||||||
<h2 className="title animate-fade-in text-xl font-medium bg-gray-200 rounded-[12px]"></h2>
|
<h2 className="w-full h-[100px] mb-[50px] text-xl font-medium bg-gray-300 rounded-[12px] animate-pulse"></h2>
|
||||||
<div className="content-job flex">
|
<div className="content-job flex">
|
||||||
<div className="left-job">
|
<div className="left-job">
|
||||||
<div className="item">
|
<div className="item">
|
||||||
<p className="bg-gray-200 h-[20px] block w-full"></p>
|
<p className="bg-gray-300 h-[20px] block w-full animate-pulse"></p>
|
||||||
<b className="bg-gray-200 h-[20px] block w-full"></b>
|
<b className="bg-gray-300 h-[20px] block w-full animate-pulse"></b>
|
||||||
</div>
|
</div>
|
||||||
<div className="item">
|
<div className="item">
|
||||||
<p className="bg-gray-200 h-[20px] block w-full"></p>
|
<p className="bg-gray-300 h-[20px] block w-full animate-pulse"></p>
|
||||||
<b className="bg-gray-200 h-[20px] block w-full"></b>
|
<b className="bg-gray-300 h-[20px] block w-full animate-pulse"></b>
|
||||||
</div>
|
</div>
|
||||||
<div className="item">
|
<div className="item">
|
||||||
<p className="bg-gray-200 h-[20px] block w-full"></p>
|
<p className="bg-gray-300 h-[20px] block w-full animate-pulse"></p>
|
||||||
<b className="bg-gray-200 h-[20px] block w-full"></b>
|
<b className="bg-gray-300 h-[20px] block w-full animate-pulse"></b>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="right-job">
|
<div className="right-job">
|
||||||
<div className="list-tab flex items-center">
|
<div className="list-tab flex items-center">
|
||||||
<div className="item-tab bg-gray-200 w-[170px] h-[45px] mr-[12px] rounded-[5px]"></div>
|
<div className="item-tab bg-gray-300 w-[170px] h-[45px] mr-[12px] rounded-[5px] animate-pulse"></div>
|
||||||
<div className="item-tab bg-gray-200 w-[170px] h-[45px] mr-[12px] rounded-[5px]"></div>
|
<div className="item-tab bg-gray-300 w-[170px] h-[45px] mr-[12px] rounded-[5px] animate-pulse"></div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="bg-gray-200 w-full h-[30px] rounded-[5px] ml-[20px] mb-[20px]"></div>
|
<div className="bg-gray-300 w-full h-[30px] rounded-[5px] ml-[20px] mb-[20px] animate-pulse"></div>
|
||||||
<ul>
|
<ul>
|
||||||
<li className="bg-gray-200 w-full h-[20px] rounded-[5px] mb-[10px] ml-[10px]"></li>
|
<li className="bg-gray-300 w-full h-[20px] rounded-[5px] mb-[10px] ml-[10px] animate-pulse"></li>
|
||||||
<li className="bg-gray-200 w-full h-[20px] rounded-[5px] mb-[10px] ml-[10px]"></li>
|
<li className="bg-gray-300 w-full h-[20px] rounded-[5px] mb-[10px] ml-[10px] animate-pulse"></li>
|
||||||
<li className="bg-gray-200 w-full h-[20px] rounded-[5px] mb-[10px] ml-[10px]"></li>
|
<li className="bg-gray-300 w-full h-[20px] rounded-[5px] mb-[10px] ml-[10px] animate-pulse"></li>
|
||||||
<li className="bg-gray-200 w-full h-[20px] rounded-[5px] mb-[10px] ml-[10px]"></li>
|
<li className="bg-gray-300 w-full h-[20px] rounded-[5px] mb-[10px] ml-[10px] animate-pulse"></li>
|
||||||
<li className="bg-gray-200 w-full h-[20px] rounded-[5px] mb-[10px] ml-[10px]"></li>
|
<li className="bg-gray-300 w-full h-[20px] rounded-[5px] mb-[10px] ml-[10px] animate-pulse"></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,104 +1,62 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import Link from "next/link";
|
import { useState, useEffect } from "react";
|
||||||
import { Suspense, useState, useEffect } from "react";
|
|
||||||
import { ListJobDataType } from "@/types/job";
|
|
||||||
import { fetchListJobs } from "@/api/apiService";
|
import { fetchListJobs } from "@/api/apiService";
|
||||||
|
import JobItem from "@/components/jobs/JobItem";
|
||||||
|
import { ListJobDataType } from "@/types/job";
|
||||||
|
|
||||||
const HomeJob = () => {
|
const HomeJob = () => {
|
||||||
const [ListJob, setListJob] = useState<ListJobDataType | null>(null);
|
const [jobList, setJobList] = useState<ListJobDataType | null>(null);
|
||||||
const [loadingUi, setLoadingUI] = useState(true);
|
const [loadingUi, setLoadingUI] = useState(true);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const getListJob = async () => {
|
const getJobList = async () => {
|
||||||
const data = await fetchListJobs();
|
try {
|
||||||
setListJob(data.list);
|
const data = await fetchListJobs();
|
||||||
setLoadingUI(false);
|
setJobList(data.list);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to fetch jobs:", error);
|
||||||
|
} finally {
|
||||||
|
setLoadingUI(false);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
getListJob();
|
getJobList();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="page-job list">
|
<div className="page-job list">
|
||||||
<div className="container-job">
|
<div className="container-job">
|
||||||
<h2 className="title">Tuyển dụng</h2>
|
<h2 className="title">Tuyển dụng</h2>
|
||||||
<Suspense>
|
{loadingUi ? (
|
||||||
{loadingUi ? (
|
<div className="list-job">
|
||||||
<div className="list-job">
|
{[...Array(4)].map((_, index) => (
|
||||||
<div className="item-job">
|
<div className="item-job" key={index}>
|
||||||
<div className="job-left flex items-center">
|
<div className="job-left flex items-center">
|
||||||
<div className="name line-clamp-1 bg-gray-200 h-[21px] w-[300px] mr-[15px]"></div>
|
<div className="name line-clamp-1 bg-gray-200 h-[21px] w-[300px] mr-[15px] animate-pulse"></div>
|
||||||
<div className="time bg-gray-200 h-[20px] w-[120px] block"></div>
|
<div className="time bg-gray-200 h-[20px] w-[120px] block animate-pulse"></div>
|
||||||
</div>
|
</div>
|
||||||
<div className="job-right flex items-center">
|
<div className="job-right flex items-center">
|
||||||
<div className="localhost bg-gray-200 h-[21px] w-[60px] mr-[20px] block"></div>
|
<div className="localhost bg-gray-200 h-[21px] w-[60px] mr-[20px] block animate-pulse"></div>
|
||||||
<div className="more bg-gray-200 h-[21px] block w-[120px]"></div>
|
<div className="more bg-gray-200 h-[21px] block w-[120px] animate-pulse"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="item-job">
|
))}
|
||||||
<div className="job-left flex items-center">
|
</div>
|
||||||
<div className="name line-clamp-1 bg-gray-200 h-[21px] w-[300px] mr-[15px]"></div>
|
) : (
|
||||||
<div className="time bg-gray-200 h-[20px] w-[120px] block"></div>
|
<div>
|
||||||
</div>
|
{jobList && Array.isArray(jobList) && jobList.length > 0 ? (
|
||||||
<div className="job-right flex items-center">
|
<div className="list-job">
|
||||||
<div className="localhost bg-gray-200 h-[21px] w-[60px] mr-[20px] block"></div>
|
{jobList.map((job) => (
|
||||||
<div className="more bg-gray-200 h-[21px] block w-[120px]"></div>
|
<JobItem key={job.id} job={job} />
|
||||||
</div>
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="item-job">
|
) : (
|
||||||
<div className="job-left flex items-center">
|
<div className="text-center text-2xl py-[50px] font-bold italic">
|
||||||
<div className="name line-clamp-1 bg-gray-200 h-[21px] w-[300px] mr-[15px]"></div>
|
Không có công việc nào.
|
||||||
<div className="time bg-gray-200 h-[20px] w-[120px] block"></div>
|
|
||||||
</div>
|
|
||||||
<div className="job-right flex items-center">
|
|
||||||
<div className="localhost bg-gray-200 h-[21px] w-[60px] mr-[20px] block"></div>
|
|
||||||
<div className="more bg-gray-200 h-[21px] block w-[120px]"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="item-job">
|
)}
|
||||||
<div className="job-left flex items-center">
|
</div>
|
||||||
<div className="name line-clamp-1 bg-gray-200 h-[21px] w-[300px] mr-[15px]"></div>
|
)}
|
||||||
<div className="time bg-gray-200 h-[20px] w-[120px] block"></div>
|
|
||||||
</div>
|
|
||||||
<div className="job-right flex items-center">
|
|
||||||
<div className="localhost bg-gray-200 h-[21px] w-[60px] mr-[20px] block"></div>
|
|
||||||
<div className="more bg-gray-200 h-[21px] block w-[120px]"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div>
|
|
||||||
{ListJob && Array.isArray(ListJob) && ListJob.length > 0 ? (
|
|
||||||
<div className="list-job">
|
|
||||||
{ListJob.map((item) => (
|
|
||||||
<div className="item-job" key={item.id}>
|
|
||||||
<div className="job-left">
|
|
||||||
<Link
|
|
||||||
href={`${item.url}`}
|
|
||||||
className="name line-clamp-1"
|
|
||||||
>
|
|
||||||
{item.title}
|
|
||||||
</Link>
|
|
||||||
<div className="time">{item.end_date}</div>
|
|
||||||
</div>
|
|
||||||
<div className="job-right flex items-center">
|
|
||||||
<div className="localhost">{item.location}</div>
|
|
||||||
<Link href={`${item.url}`} className="more">
|
|
||||||
Ứng tuyển ngay{" "}
|
|
||||||
<i className="fa-solid fa-angle-right"></i>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="text-center text-2xl py-[50px] font-bold italic">
|
|
||||||
Không có công việc nào.
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Suspense>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
1195
src/app/page.tsx
1195
src/app/page.tsx
File diff suppressed because it is too large
Load Diff
@@ -1,7 +0,0 @@
|
|||||||
export default function LoadingUI({ width = "100%", height = "150px" }) {
|
|
||||||
return (
|
|
||||||
<div style={{ width, height }} className="loading-box">
|
|
||||||
<div className="loading-content">Đang tải...</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
44
src/components/articles/ArticleCard.tsx
Normal file
44
src/components/articles/ArticleCard.tsx
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
// ArticleCard.tsx
|
||||||
|
import { format } from "date-fns";
|
||||||
|
import { Article } from "@/types/article";
|
||||||
|
|
||||||
|
const ArticleCard = ({ article }: { article: Article }) => (
|
||||||
|
<div className="flex" key={article.id}>
|
||||||
|
<div className="info">
|
||||||
|
<div className="tag-blog flex items-center">
|
||||||
|
<i className="icon_2024 blog"></i>
|
||||||
|
<span>Blog</span>
|
||||||
|
</div>
|
||||||
|
<a href={article.url} className="name line-clamp-4">
|
||||||
|
{article.title}
|
||||||
|
</a>
|
||||||
|
<div className="summary line-clamp-4">{article.summary}</div>
|
||||||
|
<a href={article.url} className="more">
|
||||||
|
Chi tiết <i className="fa-solid fa-arrow-right"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<div className="info-author">
|
||||||
|
<div className="author flex items-center">
|
||||||
|
<span>Đăng bởi</span>
|
||||||
|
<span className="name-author">Admin</span>
|
||||||
|
</div>
|
||||||
|
<div className="time">
|
||||||
|
<i className="far fa-clock"></i>{" "}
|
||||||
|
<span>
|
||||||
|
{format(new Date(article.last_update * 1000), "dd/MM/yyyy HH:mm")}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="image-right">
|
||||||
|
<img
|
||||||
|
src={`https://hurasoft8.hurasoft.com/${article.image.large}`}
|
||||||
|
width={100}
|
||||||
|
height={100}
|
||||||
|
alt={article.title}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default ArticleCard;
|
||||||
28
src/components/articles/ArticleItem.tsx
Normal file
28
src/components/articles/ArticleItem.tsx
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { format } from "date-fns";
|
||||||
|
import { Article } from "@/types/article";
|
||||||
|
|
||||||
|
const ArticleItem = ({ article }: { article: Article }) => (
|
||||||
|
<div className="item-article">
|
||||||
|
<a href={`/article/${article.url}`} className="image-article">
|
||||||
|
<img
|
||||||
|
src={`https://hurasoft8.hurasoft.com/${article.image.large}`}
|
||||||
|
width={100}
|
||||||
|
height={100}
|
||||||
|
alt={article.title}
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<div className="info">
|
||||||
|
<div className="time">
|
||||||
|
<i className="fa-regular fa-clock"></i>{" "}
|
||||||
|
<span>
|
||||||
|
{format(new Date(article.last_update * 1000), "dd/MM/yyyy HH:mm")}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<a href={`/article/${article.url}`} className="name-article line-clamp-2">
|
||||||
|
{article.title}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default ArticleItem;
|
||||||
21
src/components/jobs/JobItem.tsx
Normal file
21
src/components/jobs/JobItem.tsx
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
import { JobdataType } from "@/types/job";
|
||||||
|
|
||||||
|
const JobItem = ({ job }: { job: JobdataType }) => (
|
||||||
|
<div className="item-job" key={job.id}>
|
||||||
|
<div className="job-left">
|
||||||
|
<Link href={job.url} className="name line-clamp-1">
|
||||||
|
{job.title}
|
||||||
|
</Link>
|
||||||
|
<div className="time">{job.end_date}</div>
|
||||||
|
</div>
|
||||||
|
<div className="job-right flex items-center">
|
||||||
|
<div className="localhost">{job.location}</div>
|
||||||
|
<Link href={job.url} className="more">
|
||||||
|
Ứng tuyển ngay <i className="fa-solid fa-angle-right"></i>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default JobItem;
|
||||||
27
src/components/products/ProductCard.tsx
Normal file
27
src/components/products/ProductCard.tsx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import Image from "next/image";
|
||||||
|
|
||||||
|
const ProductCard = ({
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
imageSrc,
|
||||||
|
link,
|
||||||
|
}: {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
imageSrc: string;
|
||||||
|
link: string;
|
||||||
|
}) => (
|
||||||
|
<div className="item-product">
|
||||||
|
<div className="top">
|
||||||
|
<div className="icon">
|
||||||
|
<Image src={imageSrc} width={100} height={100} alt={title} />
|
||||||
|
</div>
|
||||||
|
<p className="txt line-clamp-5">{description}</p>
|
||||||
|
</div>
|
||||||
|
<a href={link} target="_blank" className="more">
|
||||||
|
Xem thêm <i className="fa-solid fa-arrow-right"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default ProductCard;
|
||||||
29
src/hooks/useArticles.ts
Normal file
29
src/hooks/useArticles.ts
Normal file
@@ -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<ArticleListDataType | null>(null);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [error, setError] = useState<string | null>(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;
|
||||||
Reference in New Issue
Block a user