This commit is contained in:
2025-03-22 09:52:08 +07:00
parent c0518b4866
commit ef212024fd
11 changed files with 385 additions and 273 deletions

View File

@@ -4,12 +4,12 @@
"list": [ "list": [
{ {
"id": 13, "id": 13,
"title": "Nh\u00e2n vi\u00ean ph\u00e1t tri\u1ec3n th\u1ecb tr\u01b0\u1eddng", "title": "Nhân viên phát triển thị trường",
"summary": "", "summary": "",
"salary": "Th\u1ecfa thu\u1eadn", "salary": "Thỏa thuận",
"vacancy_num": 2, "vacancy_num": 2,
"end_date": "31-12-2024", "end_date": "31-12-2024",
"location": "H\u00e0 N\u1ed9i", "location": "Hà Nội",
"visit": 0, "visit": 0,
"create_time": 1731653302, "create_time": 1731653302,
"last_update": 1733899503, "last_update": 1733899503,
@@ -22,12 +22,12 @@
}, },
{ {
"id": 12, "id": 12,
"title": "Nh\u00e2n vi\u00ean ch\u0103m s\u00f3c kh\u00e1ch h\u00e0ng", "title": "Nhân viên chăm sóc khách hàng",
"summary": "", "summary": "",
"salary": "7-12 tri\u1ec7u", "salary": "7-12 triu",
"vacancy_num": 2, "vacancy_num": 2,
"end_date": "31-12-2024", "end_date": "31-12-2024",
"location": "H\u00e0 N\u1ed9i", "location": "Hà Nội",
"visit": 0, "visit": 0,
"create_time": 1731653230, "create_time": 1731653230,
"last_update": 1733899517, "last_update": 1733899517,
@@ -40,12 +40,12 @@
}, },
{ {
"id": 11, "id": 11,
"title": "L\u1eadp tr\u00ecnh vi\u00ean front-end", "title": "Lập trình viên front-end",
"summary": "", "summary": "",
"salary": "Th\u1ecfa thu\u1eadn", "salary": "Thỏa thuận",
"vacancy_num": 2, "vacancy_num": 2,
"end_date": "31-12-2024", "end_date": "31-12-2024",
"location": "H\u00e0 N\u1ed9i", "location": "Hà Nội",
"visit": 0, "visit": 0,
"create_time": 1731652851, "create_time": 1731652851,
"last_update": 1733899525, "last_update": 1733899525,
@@ -58,12 +58,12 @@
}, },
{ {
"id": 10, "id": 10,
"title": "Thi\u1ebft k\u1ebf \u0111\u1ed3 h\u1ecda Website, Mobile (UX Designer)", "title": "Thiết kế đồ họa Website, Mobile (UX Designer)",
"summary": "", "summary": "",
"salary": "$500-1,000/th\u00e1ng", "salary": "$500-1,000/tháng",
"vacancy_num": 1, "vacancy_num": 1,
"end_date": "31-12-2024", "end_date": "31-12-2024",
"location": "H\u00e0 N\u1ed9i", "location": "Hà Nội",
"visit": 0, "visit": 0,
"create_time": 1731652564, "create_time": 1731652564,
"last_update": 1733899533, "last_update": 1733899533,
@@ -283,7 +283,7 @@
}, },
"articleDetails": [ "articleDetails": [
{ {
"id": 726, "id": "726",
"category": 39, "category": 39,
"title": "An toàn thông tin website", "title": "An toàn thông tin website",
"summary": "An toàn thông tin website đề cập đến các biện pháp và quy trình nhằm bảo vệ dữ liệu và thông tin trên website khỏi các mối đe dọa như hacker, phần mềm độc hại, và các cuộc tấn công mạng khác", "summary": "An toàn thông tin website đề cập đến các biện pháp và quy trình nhằm bảo vệ dữ liệu và thông tin trên website khỏi các mối đe dọa như hacker, phần mềm độc hại, và các cuộc tấn công mạng khác",
@@ -301,7 +301,7 @@
"path": "an-toan-thong-tin-website" "path": "an-toan-thong-tin-website"
}, },
{ {
"id": 724, "id": "724",
"category": 39, "category": 39,
"title": "Xu hướng cá nhân hóa trải nghiệm người dùng trong thương mại điện tử", "title": "Xu hướng cá nhân hóa trải nghiệm người dùng trong thương mại điện tử",
"summary": "", "summary": "",
@@ -319,7 +319,7 @@
"path": "xu-huong-ca-nhan-hoa-trai-nghiem-nguoi-dung-trong-thuong-mai-dien-tue" "path": "xu-huong-ca-nhan-hoa-trai-nghiem-nguoi-dung-trong-thuong-mai-dien-tue"
}, },
{ {
"id": 723, "id": "723",
"category": 39, "category": 39,
"title": "Thu hút người dùng bằng minigame trên website", "title": "Thu hút người dùng bằng minigame trên website",
"summary": "", "summary": "",
@@ -337,7 +337,7 @@
"path": "marketing-bang-minigame-tren-website" "path": "marketing-bang-minigame-tren-website"
}, },
{ {
"id": 723, "id": "723",
"category": 39, "category": 39,
"title": "Test bài viết tin tức", "title": "Test bài viết tin tức",
"summary": "", "summary": "",
@@ -363,7 +363,8 @@
"end_date": "31-12-2024", "end_date": "31-12-2024",
"location": "Hà Nội", "location": "Hà Nội",
"applicant_count": 7, "applicant_count": 7,
"description": "<h2 class='title'>Mô tả công việc</h2><ul><li>Tìm kiếm khách hàng mới, tiếp cận khai thác Khách hàng tiềm năng.</li><li>Tư vấn khách hàng về sản phẩm, dịch vụ của công ty.</li><li>Chốt hợp đồng khách hàng, triển khai quy trình làm việc với khách hàng.</li><li>Hỗ trợ và chăm sóc khách hàng (tiềm năng, hiện tại, cũ) tạo mối quan hệ với khách hàng.</li><li>Tiếp thu ý kiến khách hàng, phối hợp với các bộ phận khác để khai thác tối đa lợi ích của dịch vụ, khách hàng.</li><li>Hoàn thành chỉ tiêu kinh doanh nhóm và chỉ tiêu cá nhân.</li></ul><p> </p><h2 class='title'>Yêu cầu ứng viên:</h2><ul><li>Tốt nghiệp Cao Đẳng trở lên các trường đào tạo chuyên ngành quản trị kinh doanh, CNTT hoặc các ngành liên quan.</li><li>Đặc biệt ưu tiên có kinh nghiệm làm việc ở các lĩnh vực kinh doanh phần mềm, bán hàng, tư vấn, tiếp thị các sản phẩm về CNTT là 1 lợi thế.</li><li>Có kỹ năng giao tiếp thuyết phục đàm phán tốt , năng động, cải tiến, sáng tạo, chủ động trong công việc.</li><li>Tinh thần trách nhiệm trong công việc. Yêu thích công việc kinh doanh và không ngừng học hỏi, sẳn sàng tiếp thu các kiến thức mới.</li><li>Có laptop, có phương tiện đi lại.</li></ul><p> </p><h2 class='title'>Quyền lợi:</h2><ul><li>Môi trường làm việc chuyên nghiệp, năng động, thân thiện</li><li>Luôn được tiếp xúc với công nghệ và thử thách mới.</li><li>Lương: Lương cứng + hoa hồng + phụ cấp xăng xe, điện thoại</li><li>Thưởng theo ngày lễ, tết</li><li>Được đào tạo kĩ năng mềm ( kỹ năng giao tiếp, xử lý tình huống…..)</li><li>Làm việc từ T2 sáng T7</li><li>Đóng bảo hiểm theo quy định của Nhà nước</li><li>Được hưởng đầy đủ các chế độ theo quy định của luật lao động</li></ul><p>Ứng viên vui lòng gửi hồ sơ cho chúng tôi qua email <a href='mailto:info@hurasoft.com'>info@hurasoft.com</a></p>" "description": "<h2 class='title'>Mô tả công việc</h2><ul><li>Tìm kiếm khách hàng mới, tiếp cận khai thác Khách hàng tiềm năng.</li><li>Tư vấn khách hàng về sản phẩm, dịch vụ của công ty.</li><li>Chốt hợp đồng khách hàng, triển khai quy trình làm việc với khách hàng.</li><li>Hỗ trợ và chăm sóc khách hàng (tiềm năng, hiện tại, cũ) tạo mối quan hệ với khách hàng.</li><li>Tiếp thu ý kiến khách hàng, phối hợp với các bộ phận khác để khai thác tối đa lợi ích của dịch vụ, khách hàng.</li><li>Hoàn thành chỉ tiêu kinh doanh nhóm và chỉ tiêu cá nhân.</li></ul><p> </p><h2 class='title'>Yêu cầu ứng viên:</h2><ul><li>Tốt nghiệp Cao Đẳng trở lên các trường đào tạo chuyên ngành quản trị kinh doanh, CNTT hoặc các ngành liên quan.</li><li>Đặc biệt ưu tiên có kinh nghiệm làm việc ở các lĩnh vực kinh doanh phần mềm, bán hàng, tư vấn, tiếp thị các sản phẩm về CNTT là 1 lợi thế.</li><li>Có kỹ năng giao tiếp thuyết phục đàm phán tốt , năng động, cải tiến, sáng tạo, chủ động trong công việc.</li><li>Tinh thần trách nhiệm trong công việc. Yêu thích công việc kinh doanh và không ngừng học hỏi, sẳn sàng tiếp thu các kiến thức mới.</li><li>Có laptop, có phương tiện đi lại.</li></ul><p> </p><h2 class='title'>Quyền lợi:</h2><ul><li>Môi trường làm việc chuyên nghiệp, năng động, thân thiện</li><li>Luôn được tiếp xúc với công nghệ và thử thách mới.</li><li>Lương: Lương cứng + hoa hồng + phụ cấp xăng xe, điện thoại</li><li>Thưởng theo ngày lễ, tết</li><li>Được đào tạo kĩ năng mềm ( kỹ năng giao tiếp, xử lý tình huống…..)</li><li>Làm việc từ T2 sáng T7</li><li>Đóng bảo hiểm theo quy định của Nhà nước</li><li>Được hưởng đầy đủ các chế độ theo quy định của luật lao động</li></ul><p>Ứng viên vui lòng gửi hồ sơ cho chúng tôi qua email <a href='mailto:info@hurasoft.com'>info@hurasoft.com</a></p>",
"id": "c5c1"
}, },
{ {
"title": "Nhân viên chăm sóc khách hàng", "title": "Nhân viên chăm sóc khách hàng",
@@ -372,7 +373,8 @@
"end_date": "31-12-2024", "end_date": "31-12-2024",
"location": "Hà Nội", "location": "Hà Nội",
"applicant_count": 1, "applicant_count": 1,
"description": "<h2><strong>MÔ TẢ CÔNG VIỆC</strong></h2><p>- Tư vấn các khách hàng tiềm năng sản phẩm, dịch vụ của công ty ( sản phẩm phần mềm)</p><p>- Chốt hợp đồng khách hàng, triển khai quy trình làm việc với khách hàng.</p><p>- Hỗ trợ và chăm sóc khách hàng để đảm bảo khách hàng khai thác tối đa lợi ích của sản phẩm.</p><p>- Làm việc với các phòng ban liên quan để liên tục cải thiện, nâng cao chất lượng sản phẩm</p><p> </p><h2><strong>YÊU CẦU CÔNG VIỆC</strong></h2><p>- Nữ từ 1992 - 2002</p><p>- Ưu tiên ứng viên được đào tạo chuyên ngành quản trị kinh doanh, CNTT, Thương mại điện tử, marketing hoặc các ngành liên quan.</p><p>- Không có kinh nghiệm sẽ được đào tạo, nhưng đã có kinh nghiệm làm việc ở môi trường doanh nghiệp là một lợi thế</p><p>- Ứng viên muốn tìm một nơi gắn bó lâu dài, được đào tạo để phát triển sự nghiệp</p><p>- Tinh thần trách nhiệm trong công việc. Yêu thích công việc làm việc với khách hàng, sẳn sàng tiếp thu các kiến thức mới</p><p> </p><h2><strong>QUYỀN LỢI ĐƯỢC HƯỞNG</strong></h2><p>- Môi trường trẻ, năng động, được đào tạo kiến thức về sản phẩm TMĐT, công nghệ</p><p>- Thưởng theo ngày lễ, tết</p><p>- Được đào tạo kĩ năng mềm ( kỹ năng giao tiếp, xử lý tình huống…..)</p><p>- Thời gian làm việc: 8h30 - 17h30 (từ T2 sáng T7 )</p><p>- Đóng bảo hiểm theo quy định của Nhà nước</p><p>- Được hưởng đầy đủ các chế độ theo quy định của luật lao động</p><p> </p><h2 class='jd-row-heading flex-center mb-2'><strong><span class='text-uppercase bold'>LIÊN HỆ</span></strong></h2><p>Ứng viên vui lòng gửi hồ sơ đến email <a href='mailto:info@hurasoft.com'info@hurasoft.com</a>.</p>" "description": "<h2><strong>MÔ TẢ CÔNG VIỆC</strong></h2><p>- Tư vấn các khách hàng tiềm năng sản phẩm, dịch vụ của công ty ( sản phẩm phần mềm)</p><p>- Chốt hợp đồng khách hàng, triển khai quy trình làm việc với khách hàng.</p><p>- Hỗ trợ và chăm sóc khách hàng để đảm bảo khách hàng khai thác tối đa lợi ích của sản phẩm.</p><p>- Làm việc với các phòng ban liên quan để liên tục cải thiện, nâng cao chất lượng sản phẩm</p><p> </p><h2><strong>YÊU CẦU CÔNG VIỆC</strong></h2><p>- Nữ từ 1992 - 2002</p><p>- Ưu tiên ứng viên được đào tạo chuyên ngành quản trị kinh doanh, CNTT, Thương mại điện tử, marketing hoặc các ngành liên quan.</p><p>- Không có kinh nghiệm sẽ được đào tạo, nhưng đã có kinh nghiệm làm việc ở môi trường doanh nghiệp là một lợi thế</p><p>- Ứng viên muốn tìm một nơi gắn bó lâu dài, được đào tạo để phát triển sự nghiệp</p><p>- Tinh thần trách nhiệm trong công việc. Yêu thích công việc làm việc với khách hàng, sẳn sàng tiếp thu các kiến thức mới</p><p> </p><h2><strong>QUYỀN LỢI ĐƯỢC HƯỞNG</strong></h2><p>- Môi trường trẻ, năng động, được đào tạo kiến thức về sản phẩm TMĐT, công nghệ</p><p>- Thưởng theo ngày lễ, tết</p><p>- Được đào tạo kĩ năng mềm ( kỹ năng giao tiếp, xử lý tình huống…..)</p><p>- Thời gian làm việc: 8h30 - 17h30 (từ T2 sáng T7 )</p><p>- Đóng bảo hiểm theo quy định của Nhà nước</p><p>- Được hưởng đầy đủ các chế độ theo quy định của luật lao động</p><p> </p><h2 class='jd-row-heading flex-center mb-2'><strong><span class='text-uppercase bold'>LIÊN HỆ</span></strong></h2><p>Ứng viên vui lòng gửi hồ sơ đến email <a href='mailto:info@hurasoft.com'info@hurasoft.com</a>.</p>",
"id": "7faa"
}, },
{ {
"title": "Lập trình viên front-end", "title": "Lập trình viên front-end",
@@ -381,7 +383,8 @@
"end_date": "31-12-2024", "end_date": "31-12-2024",
"location": "Hà Nội", "location": "Hà Nội",
"applicant_count": 2, "applicant_count": 2,
"description": "<h2><strong>Mô tả công việc:</strong></h2><ul><li>Tham gia phát triển các sản phẩm web/app của công ty.</li><li>Phối hợp với các bộ phận liên quan để đảm bảo tiến độ bàn giao website được triển khai theo đúng cam kết và chất lượng website bàn giao.</li><li>Công việc sẽ được trao đổi cụ thể hơn trong quá trình phỏng vấn.</li></ul><p> </p><h2><strong>Yêu cầu ứng viên:</strong></h2><ul><li>Có kinh nghiệm từ 1 năm trở lên phát triển website.</li><li>Thành thạo HTML, CSS, Bootstrap.</li><li>Biết cơ bản Javascript trở lên (sẽ được đào tạo thêm)</li><li>Có kiến thức về chuẩn SEO onpage.</li><li>Có khả năng làm việc độc lập.</li><li>Có tinh thần trách nhiệm trong công việc.</li></ul><p> </p><h2><strong>Quyền lợi:</strong></h2><ul><li>Lương + hoa hồng dự án theo năng lực.</li><li>Review tăng lương 3-6 tháng/ lần.</li><li>BHXH BHYT theo quy định.</li><li>Được làm việc trong môi trường năng động, có cơ hội học hỏi, phát triển nhanh.</li></ul><p> </p><h2><strong>Liên hệ:</strong></h2><p>Ứng viên vui lòng gửi hồ sơ cho chúng tôi qua email <a href='mailto:info@hurasoft.com'>info@hurasoft.com</a></p>" "description": "<h2><strong>Mô tả công việc:</strong></h2><ul><li>Tham gia phát triển các sản phẩm web/app của công ty.</li><li>Phối hợp với các bộ phận liên quan để đảm bảo tiến độ bàn giao website được triển khai theo đúng cam kết và chất lượng website bàn giao.</li><li>Công việc sẽ được trao đổi cụ thể hơn trong quá trình phỏng vấn.</li></ul><p> </p><h2><strong>Yêu cầu ứng viên:</strong></h2><ul><li>Có kinh nghiệm từ 1 năm trở lên phát triển website.</li><li>Thành thạo HTML, CSS, Bootstrap.</li><li>Biết cơ bản Javascript trở lên (sẽ được đào tạo thêm)</li><li>Có kiến thức về chuẩn SEO onpage.</li><li>Có khả năng làm việc độc lập.</li><li>Có tinh thần trách nhiệm trong công việc.</li></ul><p> </p><h2><strong>Quyền lợi:</strong></h2><ul><li>Lương + hoa hồng dự án theo năng lực.</li><li>Review tăng lương 3-6 tháng/ lần.</li><li>BHXH BHYT theo quy định.</li><li>Được làm việc trong môi trường năng động, có cơ hội học hỏi, phát triển nhanh.</li></ul><p> </p><h2><strong>Liên hệ:</strong></h2><p>Ứng viên vui lòng gửi hồ sơ cho chúng tôi qua email <a href='mailto:info@hurasoft.com'>info@hurasoft.com</a></p>",
"id": "2faf"
}, },
{ {
"title": "Thiết kế đồ họa Website, Mobile (UX Designer)", "title": "Thiết kế đồ họa Website, Mobile (UX Designer)",
@@ -390,7 +393,34 @@
"end_date": "31-12-2024", "end_date": "31-12-2024",
"location": "Hà Nội", "location": "Hà Nội",
"applicant_count": 2, "applicant_count": 2,
"description": "<h2><strong>Mô tả công việc:</strong></h2><p>- Tham gia thiết kế các sản phẩm của công ty (bao gồm website, mobile web, mobile app)</p><p>- Nghiên cứu các xu hướng thiết kế mới để liên tục cải thiện, nâng cao chất lượng sản phẩm</p><p>- Phối hợp với các phòng ban liên quan để hoàn thành nhiệm vụ được giao</p><p> </p><h2><strong>Yêu cầu công việc:</strong></h2><p>- Thành thạo Figma, Photoshop</p><p>- Tiếng Anh đọc hiểu tài liệu</p><p>- Ưu tiên ứng viên có hiểu biết về HTML/CSS và các thư viện Ant Design, Material Design, Tailwind CSS ..</p><p>- Năng động, tinh thần làm việc tốt.</p><p> </p><h2><strong>Liên hệ:</strong></h2><p>Ứng viên vui lòng gửi hồ sơ cho chúng tôi qua email <a href='mailto:info@hurasoft.com'>info@hurasoft.com</a></p>" "description": "<h2><strong>Mô tả công việc:</strong></h2><p>- Tham gia thiết kế các sản phẩm của công ty (bao gồm website, mobile web, mobile app)</p><p>- Nghiên cứu các xu hướng thiết kế mới để liên tục cải thiện, nâng cao chất lượng sản phẩm</p><p>- Phối hợp với các phòng ban liên quan để hoàn thành nhiệm vụ được giao</p><p> </p><h2><strong>Yêu cầu công việc:</strong></h2><p>- Thành thạo Figma, Photoshop</p><p>- Tiếng Anh đọc hiểu tài liệu</p><p>- Ưu tiên ứng viên có hiểu biết về HTML/CSS và các thư viện Ant Design, Material Design, Tailwind CSS ..</p><p>- Năng động, tinh thần làm việc tốt.</p><p> </p><h2><strong>Liên hệ:</strong></h2><p>Ứng viên vui lòng gửi hồ sơ cho chúng tôi qua email <a href='mailto:info@hurasoft.com'>info@hurasoft.com</a></p>",
"id": "5632"
}
],
"contacts": [
{
"id": "e59b",
"name": "",
"email": "",
"phone": "",
"website": "",
"note": ""
},
{
"id": "54e2",
"name": "",
"email": "",
"phone": "",
"website": "",
"note": ""
},
{
"id": "3be7",
"name": "hura",
"email": "hura@gmail.com",
"phone": "0987654321",
"website": "mega@gmail.com",
"note": "liên hệ tôi"
} }
] ]
} }

28
src/api/apiService.ts Normal file
View 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}`);
};

View File

@@ -2,47 +2,39 @@
import { useParams } from "next/navigation"; import { useParams } from "next/navigation";
import { format } from "date-fns"; import { format } from "date-fns";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { ArticleDetails } from "@/types/article"; import { ArticleDetailDataType } from "@/types/article";
import { fetchArticleDetail } from "@/api/apiService";
const ArticleDetail = () => { const ArticleDetail = () => {
const { slug } = useParams(); const { slug } = useParams();
const [article, setArticle] = useState<ArticleDetails | null>(null); const [ArticleDetail, setArticleDetails] =
useState<ArticleDetailDataType | null>(null);
useEffect(() => { useEffect(() => {
if (slug) { if (typeof slug === "string") {
const fetchArticleDetail = async () => { const getArticleDetail = async () => {
const response = await fetch( const data = await fetchArticleDetail(slug);
`http://localhost:5000/articleDetails?path=${slug}` setArticleDetails(data[0]);
);
const data = await response.json();
setArticle(data[0]);
}; };
fetchArticleDetail(); getArticleDetail();
} }
}, [slug]); }, [slug]);
if (!article) {
return (
<div className="text-center text-2xl py-[50px] font-bold italic">
Article not found.
</div>
);
}
return ( return (
<div className="page-article"> <div className="page-article">
<div className="container"> <div className="container">
{ArticleDetail ? (
<div className="content-article-detail"> <div className="content-article-detail">
<div className="time"> <div className="time">
{format(new Date(article.last_update * 1000), "dd/MM/yyyy")} {format(new Date(ArticleDetail.last_update * 1000), "dd/MM/yyyy")}
</div> </div>
<h1 className="title-article">{article.title}</h1> <h1 className="title-article">{ArticleDetail.title}</h1>
<div className="summary">{article.summary}</div> <div className="summary">{ArticleDetail.summary}</div>
<div className="thumbnail"> <div className="thumbnail">
<img <img
src={`https://hurasoft8.hurasoft.com/${article.image.large}`} src={`https://hurasoft8.hurasoft.com/${ArticleDetail.image.large}`}
width="100%" width="100%"
height="100%" height="100%"
alt="" alt=""
@@ -51,9 +43,14 @@ const ArticleDetail = () => {
<div <div
className="content nd" className="content nd"
dangerouslySetInnerHTML={{ __html: article.content }} dangerouslySetInnerHTML={{ __html: ArticleDetail.content }}
/> />
</div> </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>
</div> </div>
); );

View File

@@ -2,29 +2,23 @@
import { useEffect, useState } from "react"; import { 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 { ArticlesType } from "@/types/article"; import { ArticleListDataType } from "@/types/article";
import { fetchListArticles } from "@/api/apiService";
const HomeArticle = () => { const HomeArticle = () => {
const [article, setArticle] = useState<ArticlesType | null>(null); const [articleList, setArticleList] = useState<ArticleListDataType | null>(
null
);
useEffect(() => { useEffect(() => {
const fetchArticles = async () => { const fetchArticleList = async () => {
const response = await fetch(`http://localhost:5000/articles`); const data = await fetchListArticles();
const data = await response.json(); setArticleList(data.list);
setArticle(data);
}; };
fetchArticles(); fetchArticleList();
}, []); }, []);
if (!article) {
return (
<div className="text-center text-2xl py-[50px] font-bold italic">
Article not found.
</div>
);
}
return ( return (
<div className="page-article"> <div className="page-article">
<div className="container"> <div className="container">
@@ -34,9 +28,11 @@ const HomeArticle = () => {
Cập nhật từ Hurasoft Cập nhật từ Hurasoft
</div> </div>
</div> </div>
{articleList && Array.isArray(articleList) && articleList.length > 0 ? (
<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">
{article.list.slice(0, 1).map((item) => ( {articleList.slice(0, 1).map((item) => (
<div className="item-article" key={item.id}> <div className="item-article" key={item.id}>
<a href={`/article${item.url}`} className="image-article"> <a href={`/article${item.url}`} className="image-article">
<img <img
@@ -67,7 +63,7 @@ const HomeArticle = () => {
))} ))}
</div> </div>
<div className="box-small" id="js-holder-small"> <div className="box-small" id="js-holder-small">
{article.list.slice(1, 3).map((item) => ( {articleList.slice(1, 3).map((item) => (
<div className="item-article" key={item.id}> <div className="item-article" key={item.id}>
<a href={`/article/${item.url}`} className="image-article"> <a href={`/article/${item.url}`} className="image-article">
<img <img
@@ -103,9 +99,12 @@ 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">
{article.list.map((item) => ( {articleList.map((item) => (
<div className="item-article" key={item.id}> <div className="item-article" key={item.id}>
<Link href={`/article/${item.url}`} className="image-article"> <Link
href={`/article/${item.url}`}
className="image-article"
>
<img <img
src={`https://hurasoft8.hurasoft.com/${item.image.large}`} src={`https://hurasoft8.hurasoft.com/${item.image.large}`}
width={100} width={100}
@@ -135,6 +134,10 @@ const HomeArticle = () => {
</div> </div>
</div> </div>
</div> </div>
) : (
<></>
)}
</div>
</div> </div>
); );
}; };

View File

@@ -1,5 +1,82 @@
"use client"; "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 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 ( return (
<div className="page-contact"> <div className="page-contact">
<div className="container"> <div className="container">
@@ -9,7 +86,6 @@ const Contact = () => {
<div className="note"> <div className="note">
Đi ngũ hỗ trợ của Hurasoft luôn sẵn sàng hỗ trợ quý khách Đi ngũ hỗ trợ của Hurasoft luôn sẵn sàng hỗ trợ quý khách
</div> </div>
<div className="list-info"> <div className="list-info">
<div className="item"> <div className="item">
<div className="icon"> <div className="icon">
@@ -24,7 +100,7 @@ const Contact = () => {
<i className="icon_2024 phone"></i> <i className="icon_2024 phone"></i>
</div> </div>
<div className="txt d-flex align-items"> <div className="txt d-flex align-items">
<a href="02422138068">02422.138.068</a> <a href="tel:02422138068">02422.138.068</a>
<span>-</span> <span>-</span>
<a href="tel:0904580181">0904.580.181</a> <a href="tel:0904580181">0904.580.181</a>
</div> </div>
@@ -42,72 +118,44 @@ const Contact = () => {
<div className="contact-right"> <div className="contact-right">
<div className="box-form"> <div className="box-form">
<b>Đ lại lời nhắn</b> <b>Đ lại lời nhắn</b>
<div className="item-form"> <form onSubmit={handleSubmit}>
{["name", "email", "phone", "website", "note"].map(
(field, idx) => (
<div className="item-form" key={idx}>
<label> <label>
Tên <span>*</span> {field === "note"
? "Nội dung"
: `${
field.charAt(0).toUpperCase() + field.slice(1)
}`}{" "}
<span>{field === "note" ? "" : "*"}</span>
</label> </label>
<input <input
type="text" type={
name="name" field === "email"
? "email"
: field === "phone"
? "tel"
: "text"
}
name={field}
className="input-item" className="input-item"
id="js-contact-name" value={formData[field]}
placeholder="Nhập họ và tên" onChange={handleChange}
placeholder={`Nhập ${
field === "note" ? "nội dung" : field
}`}
/> />
<div className="note-error"></div> {errors[field] && (
<div className="note-error">{errors[field]}</div>
)}
</div> </div>
<div className="item-form"> )
<label> )}
Email <span>*</span> <button type="submit" className="btn btn-submit">
</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> Gửi thông tin <i className="fa-solid fa-arrow-right-long"></i>
</a> </button>
</form>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -1,41 +1,38 @@
"use client"; "use client";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { useParams } from "next/navigation"; import { useParams } from "next/navigation";
import { JobDetailType } from "@/types/job"; import { JobDetailDataType } from "@/types/job";
import { fetchJobDetail } from "@/api/apiService";
const JobDetails = () => { const JobDetails = () => {
const [job, setJob] = useState<JobDetailType | null>(null); const [JobDetail, setJobDetail] = useState<JobDetailDataType | null>(null);
const [activeTab, setActiveTab] = useState("#info"); const [activeTab, setActiveTab] = useState("#info");
const { slug } = useParams(); const { slug } = useParams();
const showTab = (tab: string) => { const showTab = (tab: string) => {
console.log(`Tab clicked: ${tab}`);
setActiveTab(tab); setActiveTab(tab);
}; };
useEffect(() => { useEffect(() => {
if (slug) { if (typeof slug === "string") {
const fetchJobDetail = async () => { const getJobDetail = async () => {
const response = await fetch( const data = await fetchJobDetail(slug);
`http://localhost:5000/jobDetails?path=${slug}` setJobDetail(data[0]);
);
const data = await response.json();
setJob(data[0]);
}; };
fetchJobDetail(); getJobDetail();
} }
}, [slug]); }, [slug]);
return ( return (
<div className="page-job detail"> <div className="page-job detail">
{job ? ( {JobDetail ? (
<div className="container-job"> <div className="container-job">
<h2 className="title">{job.title}</h2> <h2 className="title">{JobDetail.title}</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>Đa điểm</p> <p>Đa điểm</p>
<b>{job.location}</b> <b>{JobDetail.location}</b>
</div> </div>
<div className="item"> <div className="item">
<p>Hình thức làm việc</p> <p>Hình thức làm việc</p>
@@ -43,7 +40,7 @@ const JobDetails = () => {
</div> </div>
<div className="item"> <div className="item">
<p>Số lượng tuyển</p> <p>Số lượng tuyển</p>
<b>{job.applicant_count}</b> <b>{JobDetail.applicant_count}</b>
</div> </div>
</div> </div>
<div className="right-job"> <div className="right-job">
@@ -73,7 +70,9 @@ const JobDetails = () => {
}`} }`}
id="info" id="info"
> >
<div dangerouslySetInnerHTML={{ __html: job.description }} /> <div
dangerouslySetInnerHTML={{ __html: JobDetail.description }}
/>
<a <a
href="javascript:void(0)" href="javascript:void(0)"
onClick={() => showTab("#formjob")} onClick={() => showTab("#formjob")}

View File

@@ -1,28 +1,28 @@
"use client"; "use client";
import Link from "next/link"; import Link from "next/link";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { JobType } from "@/types/job"; import { ListJobDataType } from "@/types/job";
import { fetchListJobs } from "@/api/apiService";
const HomeJob = () => { const HomeJob = () => {
const [job, setJob] = useState<JobType | null>(null); const [ListJob, setListJob] = useState<ListJobDataType | null>(null);
useEffect(() => { useEffect(() => {
const fetchJob = async () => { const getListJob = async () => {
const response = await fetch(`http://localhost:5000/jobs`); const data = await fetchListJobs();
const data = await response.json(); setListJob(data.list);
setJob(data);
}; };
fetchJob(); getListJob();
}, []); }, []);
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>
{job && job.list && job.list.length > 0 ? ( {ListJob && Array.isArray(ListJob) && ListJob.length > 0 ? (
<div className="list-job"> <div className="list-job">
{job.list.map((item) => ( {ListJob.map((item) => (
<div className="item-job" key={item.id}> <div className="item-job" key={item.id}>
<div className="job-left"> <div className="job-left">
<Link href={`${item.url}`} className="name line-clamp-1"> <Link href={`${item.url}`} className="name line-clamp-1">

View File

@@ -4,10 +4,13 @@ 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";
import { homePageEffect } from "@/effects/homeEffect"; import { homePageEffect } from "@/effects/homeEffect";
import { ArticlesType } from "@/types/article"; import { ArticleListDataType } from "@/types/article";
import { fetchListArticles } from "@/api/apiService";
export default function Home() { export default function Home() {
const [article, setArticle] = useState<ArticlesType | null>(null); const [articleList, setArticleList] = useState<ArticleListDataType | null>(
null
);
useEffect(() => { useEffect(() => {
const typingNode = document.getElementById("typewriter") as HTMLElement; const typingNode = document.getElementById("typewriter") as HTMLElement;
@@ -19,9 +22,8 @@ export default function Home() {
homePageEffect.startCarousel("#navheight", true, 3000); homePageEffect.startCarousel("#navheight", true, 3000);
const fetchArticleNews = async () => { const fetchArticleNews = async () => {
const response = await fetch(`http://localhost:5000/articles`); const data = await fetchListArticles();
const data = await response.json(); setArticleList(data.list);
setArticle(data);
}; };
fetchArticleNews(); fetchArticleNews();
@@ -548,13 +550,13 @@ export default function Home() {
</div> </div>
</div> </div>
</div> </div>
{article && article.list && article.list.length > 0 ? ( {articleList && Array.isArray(articleList) && articleList.length > 0 ? (
<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>
<div className="content-item-article" id="js-article-new"> <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="flex" key={item.id}>
<div className="info"> <div className="info">
<div className="tag-blog flex items-center"> <div className="tag-blog flex items-center">

View File

@@ -1110,6 +1110,12 @@ main {
.page-contact .item-form { .page-contact .item-form {
margin-bottom: 10px; margin-bottom: 10px;
} }
.page-contact .item-form .note-error {
color: red;
margin-top: 5px;
font-style: italic;
}
.page-contact .input-item { .page-contact .input-item {
background: #fff; background: #fff;
} }

View File

@@ -21,7 +21,7 @@ export interface Article {
url: string; url: string;
} }
export interface ArticleDetails { export interface ArticleDetailDataType {
id: number; id: number;
title: string; title: string;
summary: string; summary: string;
@@ -35,7 +35,7 @@ export interface ArticleDetails {
last_update: number; last_update: number;
} }
export interface ArticlesType { export interface ArticleListDataType {
total: number; total: number;
list: Article[]; list: Article[];
} }

View File

@@ -3,8 +3,7 @@ export interface JobImage {
large: string; // Đường dẫn ảnh lớn large: string; // Đường dẫn ảnh lớn
} }
export interface JobdataType {
export interface Job {
id: number; id: number;
title: string; title: string;
summary: string; summary: string;
@@ -21,12 +20,12 @@ export interface Job {
} }
export interface JobType { export interface ListJobDataType {
total: number; total: number;
list: Job[]; list: JobdataType[];
} }
export interface JobDetailType { export interface JobDetailDataType {
title: string; title: string;
path: string; path: string;
salary: string; salary: string;