update
This commit is contained in:
28
src/api/apiService.ts
Normal file
28
src/api/apiService.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { ArticleDetailDataType } from "@/types/article";
|
||||
import { JobDetailDataType } from "@/types/job";
|
||||
|
||||
|
||||
// Hàm chung để gọi API
|
||||
const apiRequest = async (endpoint: string, method: string = "GET", body?: object) => {
|
||||
const response = await fetch(`http://localhost:5000${endpoint}`, {
|
||||
method,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: body ? JSON.stringify(body) : null,
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error(`Error: ${response.statusText}`);
|
||||
return response.json();
|
||||
};
|
||||
// API cho bài viết
|
||||
export const fetchListArticles = () => apiRequest("/articles");
|
||||
export const fetchArticleDetail = async (slug: string): Promise<ArticleDetailDataType> => {
|
||||
return apiRequest(`/articleDetails?path=${slug}`);
|
||||
};
|
||||
|
||||
// API cho công việc
|
||||
export const fetchListJobs = () => apiRequest("/jobs");
|
||||
export const fetchJobDetail = async (slug: string): Promise<JobDetailDataType> => {
|
||||
return apiRequest(`/jobDetails?path=${slug}`);
|
||||
};
|
||||
@@ -2,58 +2,55 @@
|
||||
import { useParams } from "next/navigation";
|
||||
import { format } from "date-fns";
|
||||
import { useEffect, useState } from "react";
|
||||
import { ArticleDetails } from "@/types/article";
|
||||
import { ArticleDetailDataType } from "@/types/article";
|
||||
import { fetchArticleDetail } from "@/api/apiService";
|
||||
|
||||
const ArticleDetail = () => {
|
||||
const { slug } = useParams();
|
||||
const [article, setArticle] = useState<ArticleDetails | null>(null);
|
||||
const [ArticleDetail, setArticleDetails] =
|
||||
useState<ArticleDetailDataType | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (slug) {
|
||||
const fetchArticleDetail = async () => {
|
||||
const response = await fetch(
|
||||
`http://localhost:5000/articleDetails?path=${slug}`
|
||||
);
|
||||
const data = await response.json();
|
||||
setArticle(data[0]);
|
||||
if (typeof slug === "string") {
|
||||
const getArticleDetail = async () => {
|
||||
const data = await fetchArticleDetail(slug);
|
||||
setArticleDetails(data[0]);
|
||||
};
|
||||
fetchArticleDetail();
|
||||
getArticleDetail();
|
||||
}
|
||||
}, [slug]);
|
||||
|
||||
if (!article) {
|
||||
return (
|
||||
<div className="text-center text-2xl py-[50px] font-bold italic">
|
||||
Article not found.
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="page-article">
|
||||
<div className="container">
|
||||
<div className="content-article-detail">
|
||||
<div className="time">
|
||||
{format(new Date(article.last_update * 1000), "dd/MM/yyyy")}
|
||||
</div>
|
||||
<h1 className="title-article">{article.title}</h1>
|
||||
{ArticleDetail ? (
|
||||
<div className="content-article-detail">
|
||||
<div className="time">
|
||||
{format(new Date(ArticleDetail.last_update * 1000), "dd/MM/yyyy")}
|
||||
</div>
|
||||
<h1 className="title-article">{ArticleDetail.title}</h1>
|
||||
|
||||
<div className="summary">{article.summary}</div>
|
||||
<div className="summary">{ArticleDetail.summary}</div>
|
||||
|
||||
<div className="thumbnail">
|
||||
<img
|
||||
src={`https://hurasoft8.hurasoft.com/${article.image.large}`}
|
||||
width="100%"
|
||||
height="100%"
|
||||
alt=""
|
||||
<div className="thumbnail">
|
||||
<img
|
||||
src={`https://hurasoft8.hurasoft.com/${ArticleDetail.image.large}`}
|
||||
width="100%"
|
||||
height="100%"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="content nd"
|
||||
dangerouslySetInnerHTML={{ __html: ArticleDetail.content }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="content nd"
|
||||
dangerouslySetInnerHTML={{ __html: article.content }}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="h-screen flex justify-center items-center text-2xl font-bold">
|
||||
Bài viết đang cập nhật...!
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -2,29 +2,23 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { format } from "date-fns";
|
||||
import Link from "next/link";
|
||||
import { ArticlesType } from "@/types/article";
|
||||
import { ArticleListDataType } from "@/types/article";
|
||||
import { fetchListArticles } from "@/api/apiService";
|
||||
|
||||
const HomeArticle = () => {
|
||||
const [article, setArticle] = useState<ArticlesType | null>(null);
|
||||
const [articleList, setArticleList] = useState<ArticleListDataType | null>(
|
||||
null
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchArticles = async () => {
|
||||
const response = await fetch(`http://localhost:5000/articles`);
|
||||
const data = await response.json();
|
||||
setArticle(data);
|
||||
const fetchArticleList = async () => {
|
||||
const data = await fetchListArticles();
|
||||
setArticleList(data.list);
|
||||
};
|
||||
|
||||
fetchArticles();
|
||||
fetchArticleList();
|
||||
}, []);
|
||||
|
||||
if (!article) {
|
||||
return (
|
||||
<div className="text-center text-2xl py-[50px] font-bold italic">
|
||||
Article not found.
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="page-article">
|
||||
<div className="container">
|
||||
@@ -34,106 +28,115 @@ const HomeArticle = () => {
|
||||
Cập nhật từ Hurasoft
|
||||
</div>
|
||||
</div>
|
||||
<div className="box-big-article flex">
|
||||
<div className="box-big" id="js-holder-big">
|
||||
{article.list.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>
|
||||
{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>
|
||||
<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">
|
||||
{article.list.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 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>
|
||||
<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">
|
||||
{article.list.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>
|
||||
<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>
|
||||
<Link
|
||||
href={`/article/${item.url}`}
|
||||
className="name-article line-clamp-2"
|
||||
>
|
||||
{item.title}
|
||||
</Link>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,82 @@
|
||||
"use client";
|
||||
|
||||
import React, { useState } from "react";
|
||||
|
||||
interface FormData {
|
||||
name: string;
|
||||
email: string;
|
||||
phone: string;
|
||||
website: string;
|
||||
note: string;
|
||||
}
|
||||
|
||||
interface Errors {
|
||||
name?: string;
|
||||
email?: string;
|
||||
phone?: string;
|
||||
website?: string;
|
||||
note?: string;
|
||||
}
|
||||
|
||||
// Hàm kiểm tra định dạng email và số điện thoại
|
||||
const validateEmail = (email: string) =>
|
||||
/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(email);
|
||||
const validatePhone = (phone: string) => /^[0-9]{10,11}$/.test(phone);
|
||||
|
||||
const Contact = () => {
|
||||
const [formData, setFormData] = useState<FormData>({
|
||||
name: "",
|
||||
email: "",
|
||||
phone: "",
|
||||
website: "",
|
||||
note: "",
|
||||
});
|
||||
|
||||
const [errors, setErrors] = useState<Errors>({});
|
||||
|
||||
// Cập nhật dữ liệu form
|
||||
const handleChange = (e) =>
|
||||
setFormData({ ...formData, [e.target.name]: e.target.value });
|
||||
|
||||
// Kiểm tra và gửi form
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
// Kiểm tra form trước khi gửi
|
||||
const newErrors: Errors = {};
|
||||
if (!formData.name) newErrors.name = "Tên không được để trống!";
|
||||
if (!formData.email || !validateEmail(formData.email))
|
||||
newErrors.email = "Email không hợp lệ!";
|
||||
if (!formData.phone || !validatePhone(formData.phone))
|
||||
newErrors.phone = "Số điện thoại không hợp lệ!";
|
||||
if (!formData.note) newErrors.note = "Nội dung không được để trống!";
|
||||
setErrors(newErrors);
|
||||
|
||||
if (Object.keys(newErrors).length > 0) return;
|
||||
|
||||
// Gửi dữ liệu
|
||||
try {
|
||||
const response = await fetch("http://localhost:5000/contacts", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(formData),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
alert("Bạn đã gửi liên hệ thành công!");
|
||||
setFormData({ name: "", email: "", phone: "", website: "", note: "" });
|
||||
setErrors({});
|
||||
} else {
|
||||
alert(
|
||||
"Liên hệ không thành công. Vui lòng kiểm tra lại thông tin của bạn."
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error submitting form:", error);
|
||||
alert("An error occurred. Please try again.");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="page-contact">
|
||||
<div className="container">
|
||||
@@ -9,14 +86,13 @@ const Contact = () => {
|
||||
<div className="note">
|
||||
Đội ngũ hỗ trợ của Hurasoft luôn sẵn sàng hỗ trợ quý khách
|
||||
</div>
|
||||
|
||||
<div className="list-info">
|
||||
<div className="item">
|
||||
<div className="icon">
|
||||
<i className="icon_2024 map"></i>
|
||||
</div>
|
||||
<div className="txt">
|
||||
Tầng 5 - Số 3,ngõ 18 Yên Lãng,Đống Đa, Hà Nội
|
||||
Tầng 5 - Số 3, ngõ 18 Yên Lãng, Đống Đa, Hà Nội
|
||||
</div>
|
||||
</div>
|
||||
<div className="item">
|
||||
@@ -24,7 +100,7 @@ const Contact = () => {
|
||||
<i className="icon_2024 phone"></i>
|
||||
</div>
|
||||
<div className="txt d-flex align-items">
|
||||
<a href="02422138068">02422.138.068</a>
|
||||
<a href="tel:02422138068">02422.138.068</a>
|
||||
<span>-</span>
|
||||
<a href="tel:0904580181">0904.580.181</a>
|
||||
</div>
|
||||
@@ -42,72 +118,44 @@ const Contact = () => {
|
||||
<div className="contact-right">
|
||||
<div className="box-form">
|
||||
<b>Để lại lời nhắn</b>
|
||||
<div className="item-form">
|
||||
<label>
|
||||
Tên <span>*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="name"
|
||||
className="input-item"
|
||||
id="js-contact-name"
|
||||
placeholder="Nhập họ và tên"
|
||||
/>
|
||||
<div className="note-error"></div>
|
||||
</div>
|
||||
<div className="item-form">
|
||||
<label>
|
||||
Email <span>*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="email"
|
||||
className="input-item"
|
||||
id="js-contact-email"
|
||||
placeholder="hello@example.com..."
|
||||
/>
|
||||
<div className="note-error"></div>
|
||||
</div>
|
||||
|
||||
<div className="item-form">
|
||||
<label>
|
||||
Số điện thoại <span>*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="phone"
|
||||
className="input-item"
|
||||
id="js-contact-tel"
|
||||
placeholder="Nhập số điện thoại của bạn"
|
||||
/>
|
||||
<div className="note-error"></div>
|
||||
</div>
|
||||
|
||||
<div className="item-form">
|
||||
<label>website</label>
|
||||
<input
|
||||
type="text"
|
||||
name="web"
|
||||
className="input-item"
|
||||
id="js-contact-web"
|
||||
placeholder="Nhập website của bạn"
|
||||
/>
|
||||
<div className="note-error"></div>
|
||||
</div>
|
||||
|
||||
<div className="item-form">
|
||||
<label>Nội dung</label>
|
||||
<textarea
|
||||
name="note"
|
||||
id="js-contact-content"
|
||||
className="input-item"
|
||||
placeholder="Nhập nội dung"
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<a href="javascript:void(0)" className="btn btn-submit">
|
||||
Gửi thông tin <i className="fa-solid fa-arrow-right-long"></i>
|
||||
</a>
|
||||
<form onSubmit={handleSubmit}>
|
||||
{["name", "email", "phone", "website", "note"].map(
|
||||
(field, idx) => (
|
||||
<div className="item-form" key={idx}>
|
||||
<label>
|
||||
{field === "note"
|
||||
? "Nội dung"
|
||||
: `${
|
||||
field.charAt(0).toUpperCase() + field.slice(1)
|
||||
}`}{" "}
|
||||
<span>{field === "note" ? "" : "*"}</span>
|
||||
</label>
|
||||
<input
|
||||
type={
|
||||
field === "email"
|
||||
? "email"
|
||||
: field === "phone"
|
||||
? "tel"
|
||||
: "text"
|
||||
}
|
||||
name={field}
|
||||
className="input-item"
|
||||
value={formData[field]}
|
||||
onChange={handleChange}
|
||||
placeholder={`Nhập ${
|
||||
field === "note" ? "nội dung" : field
|
||||
}`}
|
||||
/>
|
||||
{errors[field] && (
|
||||
<div className="note-error">{errors[field]}</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
<button type="submit" className="btn btn-submit">
|
||||
Gửi thông tin <i className="fa-solid fa-arrow-right-long"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,41 +1,38 @@
|
||||
"use client";
|
||||
import { useState, useEffect } from "react";
|
||||
import { useParams } from "next/navigation";
|
||||
import { JobDetailType } from "@/types/job";
|
||||
import { JobDetailDataType } from "@/types/job";
|
||||
import { fetchJobDetail } from "@/api/apiService";
|
||||
|
||||
const JobDetails = () => {
|
||||
const [job, setJob] = useState<JobDetailType | null>(null);
|
||||
const [JobDetail, setJobDetail] = useState<JobDetailDataType | null>(null);
|
||||
const [activeTab, setActiveTab] = useState("#info");
|
||||
const { slug } = useParams();
|
||||
|
||||
const showTab = (tab: string) => {
|
||||
console.log(`Tab clicked: ${tab}`);
|
||||
setActiveTab(tab);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (slug) {
|
||||
const fetchJobDetail = async () => {
|
||||
const response = await fetch(
|
||||
`http://localhost:5000/jobDetails?path=${slug}`
|
||||
);
|
||||
const data = await response.json();
|
||||
setJob(data[0]);
|
||||
if (typeof slug === "string") {
|
||||
const getJobDetail = async () => {
|
||||
const data = await fetchJobDetail(slug);
|
||||
setJobDetail(data[0]);
|
||||
};
|
||||
fetchJobDetail();
|
||||
getJobDetail();
|
||||
}
|
||||
}, [slug]);
|
||||
|
||||
return (
|
||||
<div className="page-job detail">
|
||||
{job ? (
|
||||
{JobDetail ? (
|
||||
<div className="container-job">
|
||||
<h2 className="title">{job.title}</h2>
|
||||
<h2 className="title">{JobDetail.title}</h2>
|
||||
<div className="content-job flex">
|
||||
<div className="left-job">
|
||||
<div className="item">
|
||||
<p>Địa điểm</p>
|
||||
<b>{job.location}</b>
|
||||
<b>{JobDetail.location}</b>
|
||||
</div>
|
||||
<div className="item">
|
||||
<p>Hình thức làm việc</p>
|
||||
@@ -43,7 +40,7 @@ const JobDetails = () => {
|
||||
</div>
|
||||
<div className="item">
|
||||
<p>Số lượng tuyển</p>
|
||||
<b>{job.applicant_count}</b>
|
||||
<b>{JobDetail.applicant_count}</b>
|
||||
</div>
|
||||
</div>
|
||||
<div className="right-job">
|
||||
@@ -73,7 +70,9 @@ const JobDetails = () => {
|
||||
}`}
|
||||
id="info"
|
||||
>
|
||||
<div dangerouslySetInnerHTML={{ __html: job.description }} />
|
||||
<div
|
||||
dangerouslySetInnerHTML={{ __html: JobDetail.description }}
|
||||
/>
|
||||
<a
|
||||
href="javascript:void(0)"
|
||||
onClick={() => showTab("#formjob")}
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
"use client";
|
||||
import Link from "next/link";
|
||||
import { useState, useEffect } from "react";
|
||||
import { JobType } from "@/types/job";
|
||||
import { ListJobDataType } from "@/types/job";
|
||||
import { fetchListJobs } from "@/api/apiService";
|
||||
|
||||
const HomeJob = () => {
|
||||
const [job, setJob] = useState<JobType | null>(null);
|
||||
const [ListJob, setListJob] = useState<ListJobDataType | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchJob = async () => {
|
||||
const response = await fetch(`http://localhost:5000/jobs`);
|
||||
const data = await response.json();
|
||||
setJob(data);
|
||||
const getListJob = async () => {
|
||||
const data = await fetchListJobs();
|
||||
setListJob(data.list);
|
||||
};
|
||||
|
||||
fetchJob();
|
||||
getListJob();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="page-job list">
|
||||
<div className="container-job">
|
||||
<h2 className="title">Tuyển dụng</h2>
|
||||
{job && job.list && job.list.length > 0 ? (
|
||||
{ListJob && Array.isArray(ListJob) && ListJob.length > 0 ? (
|
||||
<div className="list-job">
|
||||
{job.list.map((item) => (
|
||||
{ListJob.map((item) => (
|
||||
<div className="item-job" key={item.id}>
|
||||
<div className="job-left">
|
||||
<Link href={`${item.url}`} className="name line-clamp-1">
|
||||
|
||||
@@ -4,10 +4,13 @@ import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { format } from "date-fns";
|
||||
import { homePageEffect } from "@/effects/homeEffect";
|
||||
import { ArticlesType } from "@/types/article";
|
||||
import { ArticleListDataType } from "@/types/article";
|
||||
import { fetchListArticles } from "@/api/apiService";
|
||||
|
||||
export default function Home() {
|
||||
const [article, setArticle] = useState<ArticlesType | null>(null);
|
||||
const [articleList, setArticleList] = useState<ArticleListDataType | null>(
|
||||
null
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const typingNode = document.getElementById("typewriter") as HTMLElement;
|
||||
@@ -19,9 +22,8 @@ export default function Home() {
|
||||
homePageEffect.startCarousel("#navheight", true, 3000);
|
||||
|
||||
const fetchArticleNews = async () => {
|
||||
const response = await fetch(`http://localhost:5000/articles`);
|
||||
const data = await response.json();
|
||||
setArticle(data);
|
||||
const data = await fetchListArticles();
|
||||
setArticleList(data.list);
|
||||
};
|
||||
|
||||
fetchArticleNews();
|
||||
@@ -548,13 +550,13 @@ export default function Home() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{article && article.list && article.list.length > 0 ? (
|
||||
{articleList && Array.isArray(articleList) && articleList.length > 0 ? (
|
||||
<div className="box-article">
|
||||
<div className="container">
|
||||
<h2 className="title">Có gì mới?</h2>
|
||||
|
||||
<div className="content-item-article" id="js-article-new">
|
||||
{article.list.slice(0, 1).map((item) => (
|
||||
{articleList.slice(0, 1).map((item) => (
|
||||
<div className="flex" key={item.id}>
|
||||
<div className="info">
|
||||
<div className="tag-blog flex items-center">
|
||||
|
||||
@@ -1110,6 +1110,12 @@ main {
|
||||
.page-contact .item-form {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.page-contact .item-form .note-error {
|
||||
color: red;
|
||||
margin-top: 5px;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.page-contact .input-item {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ export interface Article {
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface ArticleDetails {
|
||||
export interface ArticleDetailDataType {
|
||||
id: number;
|
||||
title: string;
|
||||
summary: string;
|
||||
@@ -35,7 +35,7 @@ export interface ArticleDetails {
|
||||
last_update: number;
|
||||
}
|
||||
|
||||
export interface ArticlesType {
|
||||
export interface ArticleListDataType {
|
||||
total: number;
|
||||
list: Article[];
|
||||
}
|
||||
@@ -3,8 +3,7 @@ export interface JobImage {
|
||||
large: string; // Đường dẫn ảnh lớn
|
||||
}
|
||||
|
||||
|
||||
export interface Job {
|
||||
export interface JobdataType {
|
||||
id: number;
|
||||
title: string;
|
||||
summary: string;
|
||||
@@ -21,12 +20,12 @@ export interface Job {
|
||||
}
|
||||
|
||||
|
||||
export interface JobType {
|
||||
export interface ListJobDataType {
|
||||
total: number;
|
||||
list: Job[];
|
||||
list: JobdataType[];
|
||||
}
|
||||
|
||||
export interface JobDetailType {
|
||||
export interface JobDetailDataType {
|
||||
title: string;
|
||||
path: string;
|
||||
salary: string;
|
||||
|
||||
Reference in New Issue
Block a user