update 24/03

This commit is contained in:
2025-03-24 16:00:41 +07:00
parent ef212024fd
commit b5019b41b4
6 changed files with 422 additions and 229 deletions

View File

@@ -1,5 +1,5 @@
{ {
"jobs": { "list_job": {
"total": 4, "total": 4,
"list": [ "list": [
{ {
@@ -76,7 +76,7 @@
} }
] ]
}, },
"articles": { "list_article": {
"total": 11, "total": 11,
"list": [ "list": [
{ {
@@ -423,4 +423,4 @@
"note": "liên hệ tôi" "note": "liên hệ tôi"
} }
] ]
} }

View File

@@ -16,13 +16,13 @@ const apiRequest = async (endpoint: string, method: string = "GET", body?: objec
return response.json(); return response.json();
}; };
// API cho bài viết // API cho bài viết
export const fetchListArticles = () => apiRequest("/articles"); export const fetchListArticles = () => apiRequest("/list_article");
export const fetchArticleDetail = async (slug: string): Promise<ArticleDetailDataType> => { export const fetchArticleDetail = async (slug: string): Promise<ArticleDetailDataType> => {
return apiRequest(`/articleDetails?path=${slug}`); return apiRequest(`/articleDetails?path=${slug}`);
}; };
// API cho công việc // API cho công việc
export const fetchListJobs = () => apiRequest("/jobs"); export const fetchListJobs = () => apiRequest("/list_job");
export const fetchJobDetail = async (slug: string): Promise<JobDetailDataType> => { export const fetchJobDetail = async (slug: string): Promise<JobDetailDataType> => {
return apiRequest(`/jobDetails?path=${slug}`); return apiRequest(`/jobDetails?path=${slug}`);
}; };

View File

@@ -1,5 +1,5 @@
"use client"; "use client";
import { useEffect, useState } from "react"; import { Suspense, useEffect, useState } from "react";
import { format } from "date-fns"; import { format } from "date-fns";
import Link from "next/link"; import Link from "next/link";
import { ArticleListDataType } from "@/types/article"; import { ArticleListDataType } from "@/types/article";
@@ -9,89 +9,56 @@ const HomeArticle = () => {
const [articleList, setArticleList] = useState<ArticleListDataType | null>( const [articleList, setArticleList] = useState<ArticleListDataType | null>(
null null
); );
const [loadingUi, setLoadingUI] = useState(true);
useEffect(() => { useEffect(() => {
const fetchArticleList = async () => { const fetchArticleList = async () => {
const data = await fetchListArticles(); const data = await fetchListArticles();
setArticleList(data.list); setArticleList(data.list);
setLoadingUI(false);
}; };
fetchArticleList(); fetchArticleList();
}, []); }, []);
return ( return (
<div className="page-article"> <Suspense>
<div className="container"> {loadingUi ? (
<div className="main-title"> <div className="page-article">
<h2 className="text-center text-4xl text-sky-800 mb-[15px]">Blog</h2> <div className="container">
<div className="text-center text-base text-gray-400"> <div className="main-title">
Cập nhật từ Hurasoft <h2 className="text-center text-4xl text-sky-800 mb-[15px]">
</div> Blog
</div> </h2>
{articleList && Array.isArray(articleList) && articleList.length > 0 ? ( <div className="text-center text-base text-gray-400">
<div> Cập nhật từ Hurasoft
</div>
</div>
<div className="box-big-article flex"> <div className="box-big-article flex">
<div className="box-big" id="js-holder-big"> <div className="box-big" id="js-holder-big">
{articleList.slice(0, 1).map((item) => ( <div className="relative">
<div className="item-article" key={item.id}> <div className="bg-gray-300 w-full h-[606px] block rounded-[12px]"></div>
<a href={`/article${item.url}`} className="image-article"> <div className="absolute bottom-[20px] left-[20px] right-0 p-[20px] bg-gray-50 rounded-[12px] w-[90%]">
<img <div className="time block bg-gray-200 w-[150px] h-[30px] rounded-[6px]"></div>
src={`https://hurasoft8.hurasoft.com/${item.image.large}`} <div className="block bg-gray-200 w-full h-[50px] rounded-[6px]"></div>
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-small" id="js-holder-small"> <div className="box-small" id="js-holder-small">
{articleList.slice(1, 3).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>
<a href={`/article/${item.url}`} className="image-article"> <div className="info mt-[10px]">
<img <div className="bg-gray-300 block w-[150px] h-[20px] mb-[5px]"></div>
src={`https://hurasoft8.hurasoft.com/${item.image.large}`} <div className="bg-gray-300 block w-[full] h-[48px]"></div>
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 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> </div>
<div className="box-article-other mt-[50px]"> <div className="box-article-other mt-[50px]">
@@ -99,46 +66,187 @@ const HomeArticle = () => {
Xem thêm bài khác Xem thêm bài khác
</h2> </h2>
<div className="list-article flex flex-wrap" id="js-list-article"> <div className="list-article flex flex-wrap" id="js-list-article">
{articleList.map((item) => ( <div className="item-article">
<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>
>
<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 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>
) : ( </div>
<></> ) : (
)} <div className="page-article">
</div> <div className="container">
</div> <div className="main-title">
<h2 className="text-center text-4xl text-sky-800 mb-[15px]">
Blog
</h2>
<div className="text-center text-base text-gray-400">
Cập nhật từ Hurasoft
</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 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"
>
{articleList.map((item) => (
<div className="item-article" key={item.id}>
<Link
href={`/article/${item.url}`}
className="image-article"
>
<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>
)}
</Suspense>
); );
}; };

View File

@@ -1,5 +1,5 @@
"use client"; "use client";
import { useState, useEffect } from "react"; import { Suspense, useState, useEffect } from "react";
import Image from "next/image"; import Image from "next/image";
import Link from "next/link"; import Link from "next/link";
import { format } from "date-fns"; import { format } from "date-fns";
@@ -12,6 +12,8 @@ export default function Home() {
null null
); );
const [loadingUi, setLoadingUI] = useState(true);
useEffect(() => { useEffect(() => {
const typingNode = document.getElementById("typewriter") as HTMLElement; const typingNode = document.getElementById("typewriter") as HTMLElement;
if (typingNode) { if (typingNode) {
@@ -24,6 +26,7 @@ export default function Home() {
const fetchArticleNews = async () => { const fetchArticleNews = async () => {
const data = await fetchListArticles(); const data = await fetchListArticles();
setArticleList(data.list); setArticleList(data.list);
setLoadingUI(false);
}; };
fetchArticleNews(); fetchArticleNews();
@@ -467,142 +470,194 @@ export default function Home() {
</div> </div>
</div> </div>
</div> </div>
<Suspense>
<div className="box-product">
<div className="container">
<h2 className="title">Sản phẩm</h2>
<div className="box-product"> {loadingUi ? (
<div className="container"> <div className="list-product flex">
<h2 className="title">Sản phẩm</h2> <div className="item-product">
<div className="list-product flex"> <div className="top">
<div className="item-product"> <div className="icon w-full bg-gray-50 h-[35px] rounded-[12px]"></div>
<div className="top"> <div className="w-full h-[100px] mb-[28px] mt-[20px] bg-gray-50 rounded-[12px]"></div>
<div className="icon"> </div>
<Image <div className="w-full h-[16px] bg-gray-50 rounded-[12px]"></div>
src="/images/icon-hura8.png"
width={100}
height={100}
alt="hura 8"
/>
</div> </div>
<p className="txt line-clamp-5"> <div className="item-product">
Phần mềm tạo website TMĐT chạy nên nền tảng Cloud dành cho <div className="top">
doanh nghiệp lớn <div className="icon w-full bg-gray-50 h-[35px] rounded-[12px]"></div>
</p> <div className="w-full h-[100px] mb-[28px] mt-[20px] bg-gray-50 rounded-[12px]"></div>
</div> </div>
<a href="//hura8.com" target="_blank" className="more"> <div className="w-full h-[16px] bg-gray-50 rounded-[12px]"></div>
Xem thêm <i className="fa-solid fa-arrow-right"></i>
</a>
</div>
<div className="item-product">
<div className="top">
<div className="icon">
<Image
src="/images/logo-xstore.png"
width={93}
height={35}
alt="xstore"
/>
</div> </div>
<p className="txt line-clamp-5"> <div className="item-product">
Nền tảng quản cửa hàng nhỏ toàn diện, bao gồm: Quản <div className="top">
đơn hàng đa kênh, Kho hàng, Website POS <div className="icon w-full bg-gray-50 h-[35px] rounded-[12px]"></div>
</p> <div className="w-full h-[100px] mb-[28px] mt-[20px] bg-gray-50 rounded-[12px]"></div>
</div> </div>
<a href="//xstore.vn" target="_blank" className="more"> <div className="w-full h-[16px] bg-gray-50 rounded-[12px]"></div>
Xem thêm <i className="fa-solid fa-arrow-right"></i>
</a>
</div>
<div className="item-product">
<div className="top">
<div className="icon">
<Image
src="/images/icon-adman.png"
width={100}
height={100}
alt="icon-adman"
/>
</div> </div>
<p className="txt line-clamp-5"> <div className="item-product">
Nền tảng marketing toàn diện dành cho các chuyên gia. <div className="top">
</p> <div className="icon w-full bg-gray-50 h-[35px] rounded-[12px]"></div>
</div> <div className="w-full h-[100px] mb-[28px] mt-[20px] bg-gray-50 rounded-[12px]"></div>
<a href="//adman.vn" target="_blank" className="more"> </div>
Xem thêm <i className="fa-solid fa-arrow-right"></i> <div className="w-full h-[16px] bg-gray-50 rounded-[12px]"></div>
</a>
</div>
<div className="item-product chatngay">
<div className="top">
<div className="icon">
<Image
src="/images/icon-chatngay.png"
width={100}
height={100}
alt="icon-chatngay"
/>
</div> </div>
<p className="txt line-clamp-5">
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 thuận tiện.
</p>
</div> </div>
<a href="//chatngay.com" target="_blank" className="more"> ) : (
Xem thêm <i className="fa-solid fa-arrow-right"></i> <div className="list-product flex">
</a> <div className="item-product">
</div> <div className="top">
<div className="icon">
<Image
src="/images/icon-hura8.png"
width={100}
height={100}
alt="hura 8"
/>
</div>
<p className="txt line-clamp-5">
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
</p>
</div>
<a href="//hura8.com" target="_blank" className="more">
Xem thêm <i className="fa-solid fa-arrow-right"></i>
</a>
</div>
<div className="item-product">
<div className="top">
<div className="icon">
<Image
src="/images/logo-xstore.png"
width={93}
height={35}
alt="xstore"
/>
</div>
<p className="txt line-clamp-5">
Nền tảng quản cửa hàng nhỏ toàn diện, bao gồm: Quản
đơn hàng đa kênh, Kho hàng, Website POS
</p>
</div>
<a href="//xstore.vn" target="_blank" className="more">
Xem thêm <i className="fa-solid fa-arrow-right"></i>
</a>
</div>
<div className="item-product">
<div className="top">
<div className="icon">
<Image
src="/images/icon-adman.png"
width={100}
height={100}
alt="icon-adman"
/>
</div>
<p className="txt line-clamp-5">
Nền tảng marketing toàn diện dành cho các chuyên gia.
</p>
</div>
<a href="//adman.vn" target="_blank" className="more">
Xem thêm <i className="fa-solid fa-arrow-right"></i>
</a>
</div>
<div className="item-product chatngay">
<div className="top">
<div className="icon">
<Image
src="/images/icon-chatngay.png"
width={100}
height={100}
alt="icon-chatngay"
/>
</div>
<p className="txt line-clamp-5">
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 thuận tiện.
</p>
</div>
<a href="//chatngay.com" target="_blank" className="more">
Xem thêm <i className="fa-solid fa-arrow-right"></i>
</a>
</div>
</div>
)}
</div> </div>
</div> </div>
</div> </Suspense>
{articleList && Array.isArray(articleList) && articleList.length > 0 ? (
<Suspense>
<div className="box-article"> <div className="box-article">
<div className="container"> <div className="container">
<h2 className="title"> mới?</h2> <h2 className="title"> mới?</h2>
{loadingUi ? (
<div className="content-item-article" id="js-article-new"> <div className="content-item-article" id="js-article-new">
{articleList.slice(0, 1).map((item) => ( <div className="flex">
<div className="flex" key={item.id}>
<div className="info"> <div className="info">
<div className="tag-blog flex items-center"> <div className="tag-blog block bg-gray-100 rounded-[12px] w-[74px] h-[28px]"></div>
<i className="icon_2024 blog"></i> <div className="name w-full bg-gray-100 h-[36px] rounded-[12px] mb-[24px]"></div>
<span>Blog</span> <div className="summary bg-gray-100 block line-clamp-4 w-full h-[80px] rounded-[12px] mb-[20px]"></div>
</div> <div className="more w-full h-[24px] bg-gray-100 block"></div>
<a href={item.url} className="name line-clamp-4"> <div className="info-author w-[200px] h-[50px] block bg-gray-100"></div>
{item.title}
</a>
<div className="summary line-clamp-4">{item.summary}</div>
<a href={item.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(item.last_update * 1000),
"dd/MM/yyyy HH:mm"
)}
</span>
</div>
</div>
</div>
<div className="image-right">
<img
src={`https://hurasoft8.hurasoft.com/${item.image.large}`}
width={100}
height={100}
alt={item.title}
/>
</div> </div>
<div className="image-right bg-gray-100 h-[370px] rounded-[12px]"></div>
</div> </div>
))} </div>
</div> ) : Array.isArray(articleList) && articleList.length > 0 ? (
<div className="content-item-article" id="js-article-new">
{articleList.slice(0, 1).map((item) => (
<div className="flex" key={item.id}>
<div className="info">
<div className="tag-blog flex items-center">
<i className="icon_2024 blog"></i>
<span>Blog</span>
</div>
<a href={item.url} className="name line-clamp-4">
{item.title}
</a>
<div className="summary line-clamp-4">
{item.summary}
</div>
<a href={item.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(item.last_update * 1000),
"dd/MM/yyyy HH:mm"
)}
</span>
</div>
</div>
</div>
<div className="image-right">
<img
src={`https://hurasoft8.hurasoft.com/${item.image.large}`}
width={100}
height={100}
alt={item.title}
/>
</div>
</div>
))}
</div>
) : (
<></> // Nếu không có dữ liệu, không hiển thị gì
)}
</div> </div>
</div> </div>
) : ( </Suspense>
<></>
)}
</div> </div>
</> </>
); );

View File

@@ -0,0 +1,7 @@
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>
);
}

View File

@@ -1137,6 +1137,29 @@ main {
width: 50%; width: 50%;
} }
.loading-box {
background-color: #f0f0f0;
border-radius: 8px;
animation: shimmer 1.5s infinite linear;
display: flex;
justify-content: center;
align-items: center;
}
.loading-content {
font-size: 18px;
font-weight: bold;
}
@keyframes shimmer {
0% {
background-position: -1000px 0;
}
100% {
background-position: 1000px 0;
}
}
@media only screen and (max-width: 768px) { @media only screen and (max-width: 768px) {
.page-job .container-job { .page-job .container-job {
width: 100%; width: 100%;