This commit is contained in:
2025-12-23 15:29:31 +07:00
parent 194de99b62
commit 423a68f410
51 changed files with 40790 additions and 9536 deletions

View File

@@ -1,3 +1,8 @@
{
"prettier.useTabs": false
"prettier.useTabs": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}

View File

@@ -1,12 +1,12 @@
import type { NextConfig } from "next";
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
images: {
remotePatterns: [
{
protocol: "https",
hostname: "nguyencongpc.vn",
pathname: "/**", // Cho phép tất cả đường dẫn từ domain này
protocol: 'https',
hostname: 'nguyencongpc.vn',
pathname: '/**',
},
],
},

View File

@@ -5,7 +5,8 @@ import 'swiper/css';
import 'swiper/css/navigation';
import 'swiper/css/pagination';
import '@styles/globals.css';
import Header from '@components/layout/Header';
import Header from '@/components/layout/other/Header';
import Footer from '@/components/layout/other/Footer';
import PreLoader from '@components/common/PreLoader';
@@ -29,6 +30,7 @@ export default function RootLayout({
<>
<Header />
{children}
<Footer />
</>
)}
</body>

View File

@@ -0,0 +1,53 @@
'use client';
import React from 'react';
import { useParams } from 'next/navigation';
import Link from 'next/link';
import { findCategoryBySlug } from '@/lib/category';
const CategoryPage: React.FC = () => {
const params = useParams();
const slugArray = params?.slug as string[];
// tìm danh mục hiện tại theo slug
const category: Category | null = findCategoryBySlug(slugArray, categories);
if (!category) {
return (
<div className="container mx-auto py-8">
<h1 className="text-2xl font-bold">Không tìm thấy danh mục</h1>
</div>
);
}
return (
<div className="container mx-auto py-8">
{/* Breadcrumb */}
<nav className="mb-4 text-sm text-gray-600">
{slugArray.map((slug, idx) => (
<span key={idx}>
<Link href={`/${slugArray.slice(0, idx + 1).join('/')}`}>{slug}</Link>
{idx < slugArray.length - 1 && ' / '}
</span>
))}
</nav>
{/* Tiêu đề danh mục */}
<h1 className="mb-6 text-2xl font-bold">{category.name}</h1>
{/* Nếu có danh mục con thì hiển thị */}
{category.children && category.children.length > 0 ? (
<div className="grid grid-cols-2 gap-6 md:grid-cols-4">
{category.children.map((child) => (
<div key={child.id} className="rounded-lg border p-4">
<Link href={`/${slugArray.join('/')}/${child.slug}`}>{child.name}</Link>
</div>
))}
</div>
) : (
<p>Danh mục này chưa danh mục con hoặc sản phẩm.</p>
)}
</div>
);
};
export default CategoryPage;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,56 @@
'use client';
import React from 'react';
import { Article } from '@/types';
import Link from 'next/link';
import Image from 'next/image';
type ItemArticleProps = {
item: Article;
};
const ItemArticle: React.FC<ItemArticleProps> = ({ item }) => {
// chọn link: nếu có external_url thì dùng, ngược lại dùng url
const linkHref = item.external_url && item.external_url !== '' ? item.external_url : item.url;
// chọn ảnh: nếu có original thì dùng, ngược lại ảnh mặc định
const imageSrc =
item.image?.original && item.image.original !== ''
? item.image.original
: '/static/assets/nguyencong_2023/images/not-image.png';
// chọn thời gian: ưu tiên article_time, fallback createDate
const timeDisplay =
item.article_time && item.article_time !== '' ? item.article_time : item.createDate;
return (
<div className="item-article flex gap-3">
<Link href={linkHref} className="img-article boder-radius-10 position-relative">
<Image
className="boder-radius-10"
src={imageSrc}
width={265}
height={180}
alt={item.title}
/>
{/* icon video nếu cần */}
<i className="sprite sprite-icon-play-video-detail icon-video-feature incon-play-youtube"></i>
<i className="sprite sprite-play-youtube incon-play-youtube"></i>
</Link>
<div className="content-article content-article-item flex flex-1 flex-col">
<Link href={linkHref} className="title-article">
<h3 className="line-clamp-2 font-[400]">{item.title}</h3>
</Link>
<p className="time-article flex items-center gap-4">
<i className="sprite sprite-clock-item-article"></i>
<span>{timeDisplay}</span>
</p>
<p className="descreption-article line-clamp-2">{item.summary}</p>
</div>
</div>
);
};
export default ItemArticle;

View File

@@ -0,0 +1,282 @@
import { ListArticle } from '@/types';
export const dataArticle: ListArticle = [
{
id: 4200,
title: 'Top PC 15 triệu tối ưu hiệu năng nhất trong mùa bão giá RAM',
extend: {
pixel_code: '',
},
summary:
'Chỉ với 15 triệu đồng, người dùng đã có thể sở hữu một bộ máy tính tối ưu hiệu năng cho nhu cầu học tập, làm việc và giải trí. Nguyễn Công PC mang đến nhiều cấu hình cân bằng giữa sức mạnh và giá trị, đảm bảo hoạt động mượt mà trong mọi tác vụ. Đây là lựa chọn lý tưởng cho những ai muốn đầu tư một hệ thống mạnh mẽ với chi phí hợp lý.',
createDate: '10-12-2025, 5:44 pm',
createBy: '75',
lastUpdate: '22-12-2025, 5:03 pm',
lastUpdateBy: '75',
visit: 157,
is_featured: 0,
article_time: '',
review_rate: 0,
review_count: 0,
video_code: '',
external_url: '',
author: 'Diệu Linh',
counter: 1,
url: '/top-pc-15-trieu-toi-uu-hieu-nang-cho-gaming-va-lam-viec',
image: {
thum: 'https://nguyencongpc.vn/media/news/120-4200-chi-voi-15-trieu-ban-da-co-ngay-mot-bo-pc-chat-luong-dam-bao-hieu-nang1.jpg',
original:
'https://nguyencongpc.vn/media/news/4200-chi-voi-15-trieu-ban-da-co-ngay-mot-bo-pc-chat-luong-dam-bao-hieu-nang1.jpg',
},
},
{
id: 4195,
title: 'Cách nhận chứng chỉ Google Gemini Educator làm đẹp CV của bạn ngay hôm nay!',
extend: {
pixel_code: '',
},
summary:
'Chứng chỉ Google Gemini Educator giúp bạn khẳng định kỹ năng sử dụng AI trong giáo dục và công nghệ. Việc sở hữu chứng chỉ này không chỉ tăng tính chuyên nghiệp cho CV mà còn mở ra nhiều cơ hội nghề nghiệp mới. Bài viết sẽ hướng dẫn bạn cách đăng ký, học và nhận chứng chỉ nhanh chóng nhất.',
createDate: '08-12-2025, 11:26 am',
createBy: '75',
lastUpdate: '08-12-2025, 12:07 pm',
lastUpdateBy: '75',
visit: 3067,
is_featured: 0,
article_time: '',
review_rate: 0,
review_count: 0,
video_code: '',
external_url: '',
author: 'Diệu Linh',
counter: 2,
url: '/cach-nhan-chung-chi-google-gemini-educator-mien-phi-nam-2025',
image: {
thum: 'https://nguyencongpc.vn/media/news/120-4195-cach-nhan-chung-chi-google-gemini-educator-mien-phi-nam-20251.jpg',
original:
'https://nguyencongpc.vn/media/news/4195-cach-nhan-chung-chi-google-gemini-educator-mien-phi-nam-20251.jpg',
},
},
{
id: 2722,
title: 'Top 100+ cấu hình PC Gaming giá tốt nhất năm 2025',
extend: {
pixel_code: '',
},
summary:
'Trong bài viết, Nguyễn Công PC đã tổng hợp hơn 100 cấu hình PC gaming tối ưu nhất năm 2025, phù hợp với nhiều mức ngân sách từ phổ thông đến cao cấp. Mỗi cấu hình cân bằng giữa hiệu năng và giá thành, đáp ứng nhu cầu chơi game mượt mà, đồ họa sắc nét và khả năng nâng cấp linh hoạt trong tương lai.\r\n\r\n\r\n',
createDate: '16-01-2024, 10:52 am',
createBy: '50',
lastUpdate: '06-12-2025, 4:30 pm',
lastUpdateBy: '53',
visit: 36705,
is_featured: 0,
article_time: '07-11-2025, 9:00 am',
review_rate: 0,
review_count: 0,
video_code: '',
external_url: '',
author: 'Trần Mạnh',
counter: 3,
url: '/top-100-cau-hinh-pc-gaming-gia-tot-nhat',
image: {
thum: 'https://nguyencongpc.vn/media/news/120-2722-pc-gaming.jpg',
original: 'https://nguyencongpc.vn/media/news/2722-pc-gaming.jpg',
},
},
{
id: 2718,
title: 'Top 50 cấu hình PC đồ họa giá tốt nhất hiện nay',
extend: {
pixel_code: '',
},
summary:
'Với đà phát triển của truyền thông, công nghệ số, kỹ thuật số,... Cần rất nhiều công cụ để hỗ trợ cho công việc, làm việc của bạn. Sức mạnh ngành chuyền thông nói riêng cũng như công nghệ nói chung càng ngày càng phát triển mạnh mẽ, vượt trội, chính vì để hỗ trợ cho việc xây dựng các bộ (PC Render) làm việc cũng như giải trí đang là nhu cầu lơn trên thị trường hiện nay.\r\n\r\n',
createDate: '15-01-2024, 1:39 pm',
createBy: '50',
lastUpdate: '24-11-2025, 10:23 am',
lastUpdateBy: '74',
visit: 24390,
is_featured: 0,
article_time: '05-11-2025, 10:00 am',
review_rate: 0,
review_count: 0,
video_code: '',
external_url: '',
author: 'Anh Tuấn',
counter: 4,
url: '/top-cau-hinh-do-hoa-gia-tot-nhat',
image: {
thum: 'https://nguyencongpc.vn/media/news/120-2718-pc-do-hoa.jpg',
original: 'https://nguyencongpc.vn/media/news/2718-pc-do-hoa.jpg',
},
},
{
id: 4203,
title:
'NGUYỄN CÔNG PC - NHÀ TÀI TRỢ KIM CƯƠNG CHÀO TÂN SINH VIÊN ĐẠI HỌC KIẾN TRÚC HÀ NỘI 2025',
extend: {
pixel_code: '',
},
summary:
'Máy tính Nguyễn Công tiếp tục khẳng định vị thế là đối tác tin cậy hàng đầu khi vinh dự trở thành Nhà Tài Trợ Kim Cương liên tục trong 7 năm (2019 2025) cho chương trình Chào Tân Sinh Viên của Đại học Kiến Trúc Hà Nội (HAU).',
createDate: '15-12-2025, 10:09 am',
createBy: '74',
lastUpdate: '15-12-2025, 4:00 pm',
lastUpdateBy: '53',
visit: 51,
is_featured: 0,
article_time: '',
review_rate: 0,
review_count: 0,
video_code: '',
external_url: '',
author: 'Trần Mạnh',
counter: 5,
url: '/nguyen-cong-pc-nha-tai-tro-kim-cuong-chao-tan-sinh-vien-dai-hoc-kien-truc-ha-noi-2025',
image: {
thum: 'https://nguyencongpc.vn/media/news/120-4203-nguyen-cong-pc-nha-tai-tro-kim-cuong-chao-tan-sinh-vien-dai-hoc-kien-truc-ha-noi-2025-06.jpg',
original:
'https://nguyencongpc.vn/media/news/4203-nguyen-cong-pc-nha-tai-tro-kim-cuong-chao-tan-sinh-vien-dai-hoc-kien-truc-ha-noi-2025-06.jpg',
},
},
{
id: 4199,
title: 'Đại chiến đồ họa: Canva và Photoshop: Ai là "Vua" thiết kế hiện nay',
extend: {
pixel_code: '',
},
summary:
'Canva và Photoshop đang là hai nền tảng thiết kế phổ biến nhất, mỗi công cụ sở hữu những ưu nhược điểm riêng. Trong khi Canva mang đến sự tiện lợi và tốc độ, Photoshop lại vượt trội về sức mạnh xử lý và khả năng sáng tạo chuyên sâu. Cuộc đối đầu này giúp người dùng lựa chọn đúng công cụ phù hợp với nhu cầu thiết kế của mình.',
createDate: '09-12-2025, 6:56 pm',
createBy: '75',
lastUpdate: '11-12-2025, 2:21 pm',
lastUpdateBy: '75',
visit: 72,
is_featured: 0,
article_time: '',
review_rate: 0,
review_count: 0,
video_code: '',
external_url: '',
author: 'Diệu Linh',
counter: 6,
url: '/dai-chien-do-hoa-canva-va-photoshop-ai-la-vua-thiet-ke-hien-nay',
image: {
thum: 'https://nguyencongpc.vn/media/news/120-4199-dai-chien-do-hoa-canva-va-photoshop-ai-la-vua-thiet-ke-hien-nay5.jpg',
original:
'https://nguyencongpc.vn/media/news/4199-dai-chien-do-hoa-canva-va-photoshop-ai-la-vua-thiet-ke-hien-nay5.jpg',
},
},
{
id: 4197,
title: 'Người dùng nên nâng cấp Windows 11 hiện đại hay tiếp tục sử dụng Windows 10 ổn định? ',
extend: {
pixel_code: '',
},
summary: '',
createDate: '09-12-2025, 11:03 am',
createBy: '75',
lastUpdate: '09-12-2025, 5:23 pm',
lastUpdateBy: '75',
visit: 92,
is_featured: 0,
article_time: '',
review_rate: 0,
review_count: 0,
video_code: '',
external_url: '',
author: 'Diệu Linh',
counter: 7,
url: '/nguoi-dung-nen-nang-cap-windows-11-hien-dai-hay-tiep-tuc-su-dung-windows-10-on-dinh',
image: {
thum: 'https://nguyencongpc.vn/media/news/120-4197-nguoi-dung-nen-nang-cap-windows-11-hien-dai-hay-tiep-tuc-su-dung-windows-10-on-dinh2.jpg',
original:
'https://nguyencongpc.vn/media/news/4197-nguoi-dung-nen-nang-cap-windows-11-hien-dai-hay-tiep-tuc-su-dung-windows-10-on-dinh2.jpg',
},
},
{
id: 3954,
title: 'Hướng Dẫn Các Bước Cài Đặt Plugin Sketch Up Nhanh Chóng, Đơn Giản Nhất',
extend: {
pixel_code: '',
},
summary:
'Theo dõi các hướng dẫn chi tiết cách cài đặt plugin cho phần mềm SketchUp cùng Nguyễn Công PC để giúp người dùng mở rộng chức năng, tiết kiệm thời gian thiết kế và nâng cao hiệu suất làm việc. ',
createDate: '21-07-2025, 10:39 am',
createBy: '75',
lastUpdate: '22-07-2025, 9:06 am',
lastUpdateBy: '75',
visit: 6532,
is_featured: 0,
article_time: '',
review_rate: 0,
review_count: 0,
video_code: '',
external_url: '',
author: 'Diệu Linh',
counter: 8,
url: '/huong-dan-cac-buoc-cai-dat-plugin-sketch-up-nhanh-chong-don-gian-nhat',
image: {
thum: 'https://nguyencongpc.vn/media/news/120-3954-huong-dan-cac-buoc-cai-dat-plugin-sketch-up-nhanh-chong-don-gian-nhat10.jpg',
original:
'https://nguyencongpc.vn/media/news/3954-huong-dan-cac-buoc-cai-dat-plugin-sketch-up-nhanh-chong-don-gian-nhat10.jpg',
},
},
{
id: 4198,
title: 'Bạn đã biết cách tạo ảnh AI cực hot với công cụ Nano Banana từ Gemini chưa?',
extend: {
pixel_code: '',
},
summary:
'Nano Banana là công cụ AI mới giúp người dùng tạo ảnh nhanh, đẹp và chuẩn ý tưởng chỉ từ vài dòng mô tả. Tại Nguyễn Công PC, bạn có thể dễ dàng trải nghiệm Nano Banana với giao diện đơn giản, tốc độ xử lý mạnh mẽ. Công cụ này phù hợp cho designer, marketer, người làm nội dung và bất kỳ ai muốn tạo hình ảnh chuyên nghiệp.',
createDate: '09-12-2025, 4:59 pm',
createBy: '75',
lastUpdate: '09-12-2025, 6:48 pm',
lastUpdateBy: '75',
visit: 137,
is_featured: 0,
article_time: '',
review_rate: 0,
review_count: 0,
video_code: '',
external_url: '',
author: 'Diệu Linh',
counter: 9,
url: '/ban-da-biet-cach-tao-anh-ai-cuc-hot-voi-cong-cu-nano-banana-tu-gemini-chua',
image: {
thum: 'https://nguyencongpc.vn/media/news/120-4198-ban-da-biet-cach-tao-anh-ai-cuc-hot-voi-cong-cu-nano-banana-tu-gemini-chua7.jpg',
original:
'https://nguyencongpc.vn/media/news/4198-ban-da-biet-cach-tao-anh-ai-cuc-hot-voi-cong-cu-nano-banana-tu-gemini-chua7.jpg',
},
},
{
id: 4118,
title: 'Windows 10 chính thức ngừng hoạt động, người dùng cần làm gì để giữ máy tính an toàn?',
extend: {
pixel_code: '',
},
summary:
'Microsoft đã chính thức ngừng hỗ trợ Windows 10, đồng nghĩa với việc hệ điều hành này không còn nhận được các bản cập nhật bảo mật. Người dùng tiếp tục sử dụng có nguy cơ cao bị tấn công mạng hoặc gặp lỗi nghiêm trọng. Vì vậy, việc nâng cấp hoặc áp dụng các biện pháp bảo vệ bổ sung là điều cấp thiết để giữ máy tính an toàn.',
createDate: '14-10-2025, 5:37 pm',
createBy: '75',
lastUpdate: '15-10-2025, 10:14 am',
lastUpdateBy: '75',
visit: 325,
is_featured: 0,
article_time: '',
review_rate: 0,
review_count: 0,
video_code: '',
external_url: '',
author: 'Diệu Linh',
counter: 10,
url: '/windows-10-chinh-thuc-ngung-hoat-dong-nguoi-dung-can-lam-gi-de-giu-may-tinh-an-toan',
image: {
thum: 'https://nguyencongpc.vn/media/news/120-4118-huong-dan-reset-windows-10-ve-trang-thai-moi-cai-dat14.jpg',
original:
'https://nguyencongpc.vn/media/news/4118-huong-dan-reset-windows-10-ve-trang-thai-moi-cai-dat14.jpg',
},
},
];

View File

@@ -0,0 +1,26 @@
import { FaCaretRight } from 'react-icons/fa';
import { dataArticle } from './dataArticle';
import ItemArticle from './ItemArticle';
const BoxArticle: React.FC = () => {
return (
<div className="box-article-group boder-radius-10">
<div className="flex items-center justify-between">
<div className="title-box">
<h2 className="title-box font-[600]">Tin tức công nghệ</h2>
</div>
<a href="/tin-cong-nghe" className="btn-article-group flex items-center gap-1">
<span>Xem tất cả</span>
<FaCaretRight size={16} />
</a>
</div>
<div className="list-article-group flex items-center gap-10">
{dataArticle.slice(0, 4).map((item, index) => (
<ItemArticle item={item} key={index} />
))}
</div>
</div>
);
};
export default BoxArticle;

View File

@@ -0,0 +1,123 @@
'use client';
import React, { useState } from 'react';
import { Article } from '@/types';
import Link from 'next/link';
import Image from 'next/image';
import { FaTimes } from 'react-icons/fa';
type ItemArticleProps = {
item: Article;
};
const ItemArticleVideo: React.FC<ItemArticleProps> = ({ item }) => {
const [open, setOpen] = useState(false);
// chọn link: nếu có external_url thì dùng, ngược lại dùng url
const linkHref = item.external_url && item.external_url !== '' ? item.external_url : item.url;
// chọn ảnh: nếu có original thì dùng, ngược lại ảnh mặc định
const imageSrc =
item.image?.original && item.image.original !== ''
? item.image.original
: '/static/assets/nguyencong_2023/images/not-image.png';
// chọn thời gian: ưu tiên article_time, fallback createDate
const timeDisplay =
item.article_time && item.article_time !== '' ? item.article_time : item.createDate;
const getYoutubeEmbedUrl = (url: string): string => {
try {
const urlObj = new URL(url);
// nếu là link youtube dạng watch?v=...
if (urlObj.hostname.includes('youtube.com')) {
const videoId = urlObj.searchParams.get('v');
if (videoId) {
return `https://www.youtube.com/embed/${videoId}?autoplay=1`;
}
}
// nếu là link youtu.be/xxxx
if (urlObj.hostname.includes('youtu.be')) {
const videoId = urlObj.pathname.replace('/', '');
if (videoId) {
return `https://www.youtube.com/embed/${videoId}?autoplay=1`;
}
}
// fallback: trả về chính url
return url;
} catch {
return url;
}
};
return (
<>
<div className="item-article flex gap-3">
<Link
onClick={(e) => {
e.preventDefault();
setOpen(true);
}}
href="javascript:void(0)"
className="img-article boder-radius-10 relative"
>
<Image
className="boder-radius-10"
src={imageSrc}
width={265}
height={180}
alt={item.title}
/>
{/* icon video nếu cần */}
<i className="sprite sprite-icon-play-video-detail icon-video-feature incon-play-youtube"></i>
<i className="sprite sprite-play-youtube incon-play-youtube"></i>
</Link>
<div className="content-article content-article-item flex flex-1 flex-col">
<Link href={linkHref} className="title-article">
<h3 className="line-clamp-2 font-[400]">{item.title}</h3>
</Link>
<p className="time-article flex items-center gap-4">
<i className="sprite sprite-clock-item-article"></i>
<span>{timeDisplay}</span>
</p>
<p className="descreption-article line-clamp-2">{item.summary}</p>
</div>
</div>
{open && (
<dialog id="video_modal" className="modal modal-open">
<div className="modal-box w-11/12 max-w-3xl">
<div className="flex items-center justify-between">
<h3 className="text-lg font-bold">{item.title}</h3>
<form method="dialog">
<button
className="btn btn-sm btn-circle btn-ghost absolute top-2 right-2"
onClick={() => setOpen(false)}
>
<FaTimes size={16} />
</button>
</form>
</div>
<div className="video-wrapper mt-4">
<iframe
width="100%"
height="400"
src={getYoutubeEmbedUrl(item.external_url)}
title={item.title}
frameBorder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
></iframe>
</div>
</div>
<form method="dialog" className="modal-backdrop">
<button onClick={() => setOpen(false)}>close</button>
</form>
</dialog>
)}
</>
);
};
export default ItemArticleVideo;

View File

@@ -0,0 +1,265 @@
import { ListArticle } from '@/types';
export const dataArticle: ListArticle = [
{
id: 4185,
title: 'Chuyện RAM ĐẮT - Góc nhìn mà anh em chưa thấy',
extend: {
pixel_code: '',
},
summary: '',
createDate: '28-11-2025, 11:51 am',
createBy: '53',
lastUpdate: '28-11-2025, 11:51 am',
lastUpdateBy: '53',
visit: 8,
is_featured: 0,
article_time: '',
review_rate: 0,
review_count: 0,
video_code: '',
external_url: 'https://www.youtube.com/watch?v=rH9Aq_2yZEc',
author: 'Trần Mạnh',
counter: 1,
url: '/chuyen-ram-dat-goc-nhin-ma-anh-em-chua-thay',
image: {
thum: 'https://nguyencongpc.vn/media/news/120-4185---efwegweg.jpg',
original: 'https://nguyencongpc.vn/media/news/4185---efwegweg.jpg',
},
},
{
id: 4184,
title: 'Build PC GAMING tầm giá 20 Triệu trong mùa BÃO RAM - Cũng KHOAI phết',
extend: {
pixel_code: '',
},
summary: '',
createDate: '28-11-2025, 11:49 am',
createBy: '53',
lastUpdate: '28-11-2025, 11:49 am',
lastUpdateBy: '53',
visit: 7,
is_featured: 0,
article_time: '',
review_rate: 0,
review_count: 0,
video_code: '',
external_url: 'https://www.youtube.com/watch?v=c-JQPclPXmg',
author: 'Trần Mạnh',
counter: 2,
url: '/build-pc-gaming-tam-gia-20-trieu-trong-mua-bao-ram-cung-khoai-phet',
image: {
thum: 'https://nguyencongpc.vn/media/news/120-4184-maxresdefault.jpg',
original: 'https://nguyencongpc.vn/media/news/4184-maxresdefault.jpg',
},
},
{
id: 4171,
title: 'Điểm dừng cho PC GAMING - Nhiều tiền thì cũng PHÍ',
extend: {
pixel_code: '',
},
summary: '',
createDate: '10-11-2025, 2:41 pm',
createBy: '53',
lastUpdate: '10-11-2025, 2:41 pm',
lastUpdateBy: '53',
visit: 8,
is_featured: 0,
article_time: '',
review_rate: 0,
review_count: 0,
video_code: '',
external_url: 'https://www.youtube.com/watch?v=xUpMSpaa_H0',
author: 'Trần Mạnh',
counter: 3,
url: '/diem-dung-cho-pc-gaming-nhieu-tien-thi-cung-phi',
image: {
thum: 'https://nguyencongpc.vn/media/news/120-4171-dvsdfgrsdf.jpg',
original: 'https://nguyencongpc.vn/media/news/4171-dvsdfgrsdf.jpg',
},
},
{
id: 3683,
title: 'Bộ PC KHỦNG BỐ tới đâu mà đích thân Chủ Tịch MaxHome phải tự đi build ???',
extend: {
pixel_code: '',
},
summary: '',
createDate: '12-03-2025, 9:59 am',
createBy: '53',
lastUpdate: '12-03-2025, 9:59 am',
lastUpdateBy: '53',
visit: 64,
is_featured: 0,
article_time: '',
review_rate: 0,
review_count: 0,
video_code: '',
external_url: 'https://www.youtube.com/watch?v=Ir9zlznA9ms',
author: 'Trần Mạnh',
counter: 4,
url: '/bo-pc-khung-bo-toi-dau-ma-dich-than-chu-tich-maxhome-phai-tu-di-build',
image: {
thum: 'https://nguyencongpc.vn/media/news/120-3683-tymyumyj.jpg',
original: 'https://nguyencongpc.vn/media/news/3683-tymyumyj.jpg',
},
},
{
id: 4107,
title: 'Intel ĐẮT quá nên BUILD PC với AMD chỉ 17 TRIỆU mà chiến ALL GAME',
extend: {
pixel_code: '',
},
summary: '',
createDate: '04-10-2025, 5:39 pm',
createBy: '53',
lastUpdate: '04-10-2025, 5:40 pm',
lastUpdateBy: '53',
visit: 7,
is_featured: 0,
article_time: '',
review_rate: 0,
review_count: 0,
video_code: '',
external_url: 'https://www.youtube.com/watch?v=DBuud_Lwt6w',
author: 'Trần Mạnh',
counter: 5,
url: '/intel-dat-qua-nen-build-pc-voi-amd-chi-17-trieu-ma-chien-all-game',
image: {
thum: 'https://nguyencongpc.vn/media/news/120-4107---gherthert.jpg',
original: 'https://nguyencongpc.vn/media/news/4107---gherthert.jpg',
},
},
{
id: 4079,
title: 'Tôi thấy chán PC HIỆU NĂNG/GIÁ THÀNH sau khi thấy bộ PC này',
extend: {
pixel_code: '',
},
summary: '',
createDate: '20-09-2025, 10:42 am',
createBy: '53',
lastUpdate: '20-09-2025, 10:42 am',
lastUpdateBy: '53',
visit: 28,
is_featured: 0,
article_time: '',
review_rate: 0,
review_count: 0,
video_code: '',
external_url: 'https://www.youtube.com/watch?v=ceT_nSB1JCA',
author: 'Trần Mạnh',
counter: 6,
url: '/toi-thay-chan-pc-hieu-nang-gia-thanh-sau-khi-thay-bo-pc-nay',
image: {
thum: 'https://nguyencongpc.vn/media/news/120-4079-ewgergherth.jpg',
original: 'https://nguyencongpc.vn/media/news/4079-ewgergherth.jpg',
},
},
{
id: 4004,
title: 'Sinh Viên ĐỒ HOẠ lên cấu hình PC nào dưới 20 TRIỆU trong 2025',
extend: {
pixel_code: '',
},
summary: '',
createDate: '15-08-2025, 2:04 pm',
createBy: '53',
lastUpdate: '15-08-2025, 2:04 pm',
lastUpdateBy: '53',
visit: 44,
is_featured: 0,
article_time: '',
review_rate: 0,
review_count: 0,
video_code: '',
external_url: 'https://www.youtube.com/watch?v=k6rIzVmU9bA',
author: 'Trần Mạnh',
counter: 7,
url: '/sinh-vien-do-hoa-len-cau-hinh-pc-nao-duoi-20-trieu-trong-2025',
image: {
thum: 'https://nguyencongpc.vn/media/news/120-4004-dhtrhj.jpg',
original: 'https://nguyencongpc.vn/media/news/4004-dhtrhj.jpg',
},
},
{
id: 3951,
title: 'Cấu hình PC 10 Triệu cả Màn hình - Test GAME AAA vẫn OK',
extend: {
pixel_code: '',
},
summary: '',
createDate: '19-07-2025, 4:57 pm',
createBy: '53',
lastUpdate: '19-07-2025, 4:57 pm',
lastUpdateBy: '53',
visit: 43,
is_featured: 0,
article_time: '',
review_rate: 0,
review_count: 0,
video_code: '',
external_url: 'https://www.youtube.com/watch?v=QCQwdLcosQc',
author: 'Trần Mạnh',
counter: 8,
url: '/cau-hinh-pc-10-trieu-ca-man-hinh-test-game-aaa-van-ok',
image: {
thum: 'https://nguyencongpc.vn/media/news/120-3951-dfbeadbeat.jpg',
original: 'https://nguyencongpc.vn/media/news/3951-dfbeadbeat.jpg',
},
},
{
id: 3950,
title:
'Tại sao mình ít làm video CORE ULTRA - Có đáng không 40 Triệu cho Ultra 7 265K + RTX 5070',
extend: {
pixel_code: '',
},
summary: '',
createDate: '19-07-2025, 4:56 pm',
createBy: '53',
lastUpdate: '19-07-2025, 5:00 pm',
lastUpdateBy: '53',
visit: 51,
is_featured: 0,
article_time: '',
review_rate: 0,
review_count: 0,
video_code: '',
external_url: 'https://www.youtube.com/watch?v=Y6PBwYe5My0',
author: 'Trần Mạnh',
counter: 9,
url: '/tai-sao-minh-it-lam-video-core-ultra-co-dang-khong-40-trieu-cho-ultra-7-265k-rtx-5070',
image: {
thum: 'https://nguyencongpc.vn/media/news/120-3950-maxresdefault.jpg',
original: 'https://nguyencongpc.vn/media/news/3950-maxresdefault.jpg',
},
},
{
id: 3949,
title: 'Cấu hình PC PHỔ BIẾN nhất THẾ GIỚI gaming - Cũng rẻ phết',
extend: {
pixel_code: '',
},
summary: '',
createDate: '19-07-2025, 4:54 pm',
createBy: '53',
lastUpdate: '19-07-2025, 4:54 pm',
lastUpdateBy: '53',
visit: 28,
is_featured: 0,
article_time: '',
review_rate: 0,
review_count: 0,
video_code: '',
external_url: 'https://www.youtube.com/watch?v=KXfA10koGDk',
author: 'Trần Mạnh',
counter: 10,
url: '/cau-hinh-pc-pho-bien-nhat-the-gioi-gaming-cung-re-phet',
image: {
thum: 'https://nguyencongpc.vn/media/news/120-3949----herthrtn.jpg',
original: 'https://nguyencongpc.vn/media/news/3949----herthrtn.jpg',
},
},
];

View File

@@ -0,0 +1,31 @@
import { FaCaretRight } from 'react-icons/fa';
import Link from 'next/link';
import { dataArticle } from './dataArticle';
import ItemArticleVideo from './ItemArticleVideo';
const BoxArticleVideo: React.FC = () => {
return (
<div className="box-videos-group box-article-group boder-radius-10 relative">
<div className="flex items-center justify-between">
<div className="title-box">
<h2 className="title-box font-[600]">Youtube Channel</h2>
</div>
<Link
href="https://www.youtube.com/NguyenCongPC"
target="_blank"
rel="nofollow"
className="btn-article-group flex items-center gap-2"
>
<span>Xem tất cả </span>
<FaCaretRight size={16} />
</Link>
</div>
<div className="list-videos-group list-article-group flex items-center gap-10">
{dataArticle.slice(0, 4).map((item, index) => (
<ItemArticleVideo item={item} key={index} />
))}
</div>
</div>
);
};
export default BoxArticleVideo;

View File

@@ -1,83 +0,0 @@
import React from 'react';
import Tippy from '@tippyjs/react';
import 'tippy.js/dist/tippy.css';
import { Product } from '@/types/TypeListProduct';
import Image from 'next/image';
import Link from 'next/link';
type ProductItemProps = {
item: Product;
};
const formatCurrency = (value: number | string) => {
const num = typeof value === 'string' ? parseInt(value) : value;
return num.toLocaleString('vi-VN');
};
const ItemProduct: React.FC<ProductItemProps> = ({ item }) => {
const offers = item.specialOffer?.all ?? [];
return (
<div className="product-item js-p-item">
<a href={item.productUrl} className="product-image relative">
{item.productImage.large ? (
<Image src={item.productImage.large} width="203" height="203" alt={item.productName} />
) : (
<Image src="https://nguyencongpc.vn/static/assets/nguyencong_2023/images/not-image.png" alt={item.productName} />
)}
<span className="p-type-holder">
{item.productType.isHot === 1 && (
<i className="p-icon-type p-icon-hot"></i>
)}
{item.productType.isNew === 1 && (
<i className="p-icon-type p-icon-new"></i>
)}
</span>
<span className="p-type-holder p-type-holder-2">
{item.productType.isBestSale === 1 && (
<i className="p-icon-type p-icon-best-sale"></i>
)}
</span>
</a>
<div className="product-info">
<Link href={item.productUrl}>
<h3 className="product-title line-clamp-3">{item.productName}</h3>
</Link>
{item.marketPrice > 0 ? (
<div className="product-martket-main flex items-center">
<p className="product-market-price">
{item.marketPrice.toLocaleString()}<u>đ</u>
</p>
<div className="product-percent-price">-{Math.round(item.price_off)} %</div>
</div>
) : (
<div className="product-martket-main flex items-center">
</div>
)}
<div className="product-price-main font-[600]">
{item.price > '0'
? `${formatCurrency(item.price)}đ`
: 'Liên hệ'}
</div>
{item.specialOffer?.all?.length ? (
<div className="product-offer line-clamp-2"
dangerouslySetInnerHTML={{
__html: item.specialOffer!.all![0].title,
}} />
) : (
<div className="product-offer line-clamp-2"></div>
)}
{item.extend?.buy_count ? (
<div style={{ height: 18 }}> <b>Đã bán: </b> <span>{item.extend.buy_count}</span> </div>
) : (
<div style={{ height: 18, display: 'block' }}> </div> )}
</div>
</div>
);
};
export default ItemProduct;

View File

@@ -0,0 +1,84 @@
import React from 'react';
import Tippy from '@tippyjs/react';
import 'tippy.js/dist/tippy.css';
import { Product } from '@/types';
import Image from 'next/image';
import Link from 'next/link';
type ProductItemProps = {
item: Product;
};
const formatCurrency = (value: number | string) => {
const num = typeof value === 'string' ? parseInt(value) : value;
return num.toLocaleString('vi-VN');
};
const ItemProduct: React.FC<ProductItemProps> = ({ item }) => {
const offers = item.specialOffer?.all ?? [];
return (
<div className="product-item js-p-item">
<a href={item.productUrl} className="product-image relative">
{item.productImage.large ? (
<Image src={item.productImage.large} width="203" height="203" alt={item.productName} />
) : (
<Image
src="https://nguyencongpc.vn/static/assets/nguyencong_2023/images/not-image.png"
alt={item.productName}
/>
)}
<span className="p-type-holder">
{item.productType.isHot === 1 && <i className="p-icon-type p-icon-hot"></i>}
{item.productType.isNew === 1 && <i className="p-icon-type p-icon-new"></i>}
</span>
<span className="p-type-holder p-type-holder-2">
{item.productType.isBestSale === 1 && <i className="p-icon-type p-icon-best-sale"></i>}
</span>
</a>
<div className="product-info">
<Link href={item.productUrl}>
<h3 className="product-title line-clamp-3">{item.productName}</h3>
</Link>
{item.marketPrice > 0 ? (
<div className="product-martket-main flex items-center">
<p className="product-market-price">
{item.marketPrice.toLocaleString()}
<u>đ</u>
</p>
<div className="product-percent-price">-{Math.round(item.price_off)} %</div>
</div>
) : (
<div className="product-martket-main flex items-center"></div>
)}
<div className="product-price-main font-[600]">
{item.price > '0' ? `${formatCurrency(item.price)}đ` : 'Liên hệ'}
</div>
{item.specialOffer?.all?.length ? (
<div
className="product-offer line-clamp-2"
dangerouslySetInnerHTML={{
__html: item.specialOffer!.all![0].title,
}}
/>
) : (
<div className="product-offer line-clamp-2"></div>
)}
{item.extend?.buy_count ? (
<div style={{ height: 18 }}>
{' '}
<b>Đã bán: </b> <span>{item.extend.buy_count}</span>{' '}
</div>
) : (
<div style={{ height: 18, display: 'block' }}> </div>
)}
</div>
</div>
);
};
export default ItemProduct;

View File

@@ -4,53 +4,54 @@ import Link from 'next/link';
import { FaCaretDown } from 'react-icons/fa';
import { Swiper, SwiperSlide } from 'swiper/react';
import { Autoplay, Navigation, Pagination } from 'swiper/modules';
import ItemProduct from './ItemProduct'
import ItemProduct from './ItemProduct';
import { Category } from '../../../../types/Menu';
import { InfoCategory } from '@/types';
import { menuData } from '../../Header/menuData';
import {productData} from './productData'
import { menuData } from '@/components/layout/other/Header/menuData';
import { productData } from './productData';
const BoxListCategory: React.FC = () => {
return (
<>
{menuData[0].product.all_category.map((item,index) => (
{menuData[0].product.all_category.map((item, index) => (
<div className="box-product-category boder-radius-10" key={index}>
<div className="flex items-center justify-between">
<div className="flex items-center justify-between">
<div className="title">
<h2 className="title-box font-[600]">{item.title}</h2>
<h2 className="title-box font-[600]">{item.title}</h2>
</div>
<div className="list-category-child flex items-center justify-end flex-1">
<div className="list-category-child flex flex-1 items-center justify-end">
{item.children.slice(0, 4).map((item2, index2) => (
<Link key={index2} href={item2.url} className="title-category">
{item2.title}
</Link>
))}
{item.children.slice(0, 4).map((item2,index2) => (
<Link key={index2} href={item2.url} className="title-category">{item2.title}</Link>
))}
<Link href={item.url} className="title-all-category flex items-center gap-2">
<Link href={item.url} className="title-all-category flex items-center gap-2">
<span>Xem tất cả </span>
<FaCaretDown size={16} /></Link>
</div>
</div>
<div className="box-list-item-category swiper-product-category">
<Swiper
modules={[Autoplay, Navigation, Pagination]}
spaceBetween={12}
slidesPerView={5}
loop={true}
navigation={true}
>
{productData.map((item,index) => (
<SwiperSlide key={index}>
<ItemProduct item={item} />
</SwiperSlide>
))}
</Swiper>
<FaCaretDown size={16} />
</Link>
</div>
</div>
<div className="box-list-item-category swiper-product-category">
<Swiper
modules={[Autoplay, Navigation, Pagination]}
spaceBetween={12}
slidesPerView={5}
loop={true}
navigation={true}
>
{productData.map((item, index) => (
<SwiperSlide key={index}>
<ItemProduct item={item} />
</SwiperSlide>
))}
</Swiper>
</div>
</div>
))}
))}
</>
)
}
);
};
export default BoxListCategory
export default BoxListCategory;

File diff suppressed because it is too large Load Diff

View File

@@ -1,101 +0,0 @@
import React from 'react';
import Tippy from '@tippyjs/react';
import 'tippy.js/dist/tippy.css';
import { DealType } from '@/types/TypeListProductDeal';
type ProductItemProps = {
item: DealType;
};
const formatCurrency = (value: number | string) => {
const num = typeof value === 'string' ? parseInt(value) : value;
return num.toLocaleString('vi-VN');
};
const ProductItem: React.FC<ProductItemProps> = ({ item }) => {
const { product_info } = item;
const offers = product_info.specialOffer?.all ?? [];
return (
<div
className="product-item"
>
<a
href={product_info.productUrl}
className="product-image relative"
>
{product_info.productImage.large ? (
<img
src={product_info.productImage.large}
width="164"
height="164"
alt={product_info.productName}
className="lazy"
/>
) : (
<img
src="/static/assets/nguyencong_2023/images/not-image.png"
width="164"
height="164"
alt={product_info.productName}
className="lazy"
/>
)}
<span className="p-type-holder">
{product_info.productType.isHot === 1 && (
<i className="p-icon-type p-icon-hot"></i>
)}
{product_info.productType.isNew === 1 && (
<i className="p-icon-type p-icon-new"></i>
)}
</span>
</a>
<div className="product-info">
<a href={product_info.productUrl}>
<h3 className="product-title line-clamp-3">
{product_info.productName}
</h3>
</a>
<div className="product-martket-main flex items-center">
{product_info.marketPrice > 0 ? (
<p className="product-market-price">
{product_info.marketPrice.toLocaleString()}
</p>
) : (
<p className="product-market-price">
{product_info.sale_rules.normal_price.toLocaleString()}
</p>
)}
<div className="product-percent-price">
-{product_info.price_off || 0}%
</div>
</div>
<div className="product-price-main font-bold">
{item.price > '0'
? `${formatCurrency(product_info.price)}đ`
: 'Liên hệ'}
</div>
<div
className="p-quantity-sale"
>
<i className="sprite sprite-fire-deal"></i>
<div className="bg-gradient"></div>
<p className="js-line-deal-left"></p>
<span>
Còn {Number(item.quantity) - Number(item.sale_quantity)}/{Number(item.quantity)} sản phẩm
</span>
</div>
{offers.length > 0 && (
<div
className="product-offer line-clamp-2"
dangerouslySetInnerHTML={{
__html: product_info.specialOffer!.all![0].title,
}}
/>
)}
</div>
</div>
);
};
export default ProductItem;

View File

@@ -0,0 +1,83 @@
import React from 'react';
import Tippy from '@tippyjs/react';
import 'tippy.js/dist/tippy.css';
import { DealType } from '@/types';
type ProductItemProps = {
item: DealType;
};
const formatCurrency = (value: number | string) => {
const num = typeof value === 'string' ? parseInt(value) : value;
return num.toLocaleString('vi-VN');
};
const ProductItem: React.FC<ProductItemProps> = ({ item }) => {
const { product_info } = item;
const offers = product_info.specialOffer?.all ?? [];
return (
<div className="product-item">
<a href={product_info.productUrl} className="product-image relative">
{product_info.productImage.large ? (
<img
src={product_info.productImage.large}
width="164"
height="164"
alt={product_info.productName}
className="lazy"
/>
) : (
<img
src="/static/assets/nguyencong_2023/images/not-image.png"
width="164"
height="164"
alt={product_info.productName}
className="lazy"
/>
)}
<span className="p-type-holder">
{product_info.productType.isHot === 1 && <i className="p-icon-type p-icon-hot"></i>}
{product_info.productType.isNew === 1 && <i className="p-icon-type p-icon-new"></i>}
</span>
</a>
<div className="product-info">
<a href={product_info.productUrl}>
<h3 className="product-title line-clamp-3">{product_info.productName}</h3>
</a>
<div className="product-martket-main flex items-center">
{product_info.marketPrice > 0 ? (
<p className="product-market-price">{product_info.marketPrice.toLocaleString()} </p>
) : (
<p className="product-market-price">
{product_info.sale_rules.normal_price.toLocaleString()}
</p>
)}
<div className="product-percent-price">-{product_info.price_off || 0}%</div>
</div>
<div className="product-price-main font-bold">
{item.price > '0' ? `${formatCurrency(product_info.price)}đ` : 'Liên hệ'}
</div>
<div className="p-quantity-sale">
<i className="sprite sprite-fire-deal"></i>
<div className="bg-gradient"></div>
<p className="js-line-deal-left"></p>
<span>
Còn {Number(item.quantity) - Number(item.sale_quantity)}/{Number(item.quantity)} sản
phẩm
</span>
</div>
{offers.length > 0 && (
<div
className="product-offer line-clamp-2"
dangerouslySetInnerHTML={{
__html: product_info.specialOffer!.all![0].title,
}}
/>
)}
</div>
</div>
);
};
export default ProductItem;

View File

@@ -4,7 +4,7 @@ import { Swiper, SwiperSlide } from 'swiper/react';
import { Autoplay, Navigation, Pagination } from 'swiper/modules';
import { FaCaretRight } from 'react-icons/fa';
import CounDown from './CounDown';
import ProductItem from './ProductItem'
import ProductItem from './ProductItem';
import { productDealData } from './productDealData';
const BoxProductDeal: React.FC = () => {
@@ -31,11 +31,11 @@ const BoxProductDeal: React.FC = () => {
loop={true}
navigation={true}
>
{productDealData.map((item,index) => (
<SwiperSlide key={index}>
<ProductItem item={item} />
</SwiperSlide>
))}
{productDealData.map((item, index) => (
<SwiperSlide key={index}>
<ProductItem item={item} />
</SwiperSlide>
))}
</Swiper>
</div>
</div>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,35 @@
'use client';
import { Swiper, SwiperSlide } from 'swiper/react';
import Image from 'next/image';
import Link from 'next/link';
interface TypeReview {
avatar: string;
text: string;
author: string;
}
type ItemReviewProps = {
item: TypeReview;
};
const ItemReview: React.FC<ItemReviewProps> = ({ item }) => {
return (
<div className="item-review-customer-hompage relative flex items-center justify-between">
<div className="left-review relative">
<Image src={item.avatar} width={88} height={88} className="lazy" alt="avatar" />
</div>
<div className="right-reivew">
<p className="text-reivew line-clamp-2">{item.text}</p>
<b className="author-review font-[500]">{item.author}</b>
<Image
src="https://nguyencongpc.vn/static/assets/nguyencong_2023/images/5star-customer.png"
width={80}
height={15}
alt="rating"
/>
</div>
</div>
);
};
export default ItemReview;

View File

@@ -0,0 +1,43 @@
interface Review {
avatar: string;
text: string;
author: string;
}
export const dataReview: Review[] = [
{
avatar: 'https://nguyencongpc.vn/static/assets/nguyencong_2023/images/avatar_nc.png',
text: 'Cảm thấy hài lòng khi sử dụng sản phẩm và dịch vụ của Nguyễn Công PC',
author: '- Cầu thủ ĐTQG Hoàng Đức -',
},
{
avatar: 'https://nguyencongpc.vn/media/news/0709_khoapub.jpg',
text: 'Đến Nguyễn Công PC build là yên tâm rồi, tiền bạc không thành vấn đề. Anh chốt bộ PC 300 củ.',
author: '- Youtuber Khoa Pug -',
},
{
avatar:
'https://nguyencongpc.vn/media/lib/02-06-2025/z6664231683539_3ee948af405f481eaf36691798831c43saochp.jpg',
text: 'Hệ thống đào tạo đồ họa và thiết kế - thi công của anh tất cả bộ máy đều của Nguyễn Công PC',
author: '- Mr. Tuấn VietCG -',
},
{
avatar: 'https://nguyencongpc.vn/media/news/0709_tranngoc.jpg',
text: 'Không cần lo về giá vì đã có Nguyễn Công PC.',
author: '- MC Trần Ngọc -',
},
{
avatar: 'https://nguyencongpc.vn/media/news/0709_tienlinh.jpg',
text: 'Thằng em Hoàng Đức của mình rất hài lòng vì bộ PC mình sắm cho nó tại đây',
author: '- Cầu thủ ĐTQG Tiến Linh -',
},
{
avatar: 'https://nguyencongpc.vn/media/lib/02-06-2025/chimse.jpg',
text: 'Từ lúc sắm PC tại Nguyễn Công PC vẩy E không trượt phát nào',
author: '- Chim sẻ đi nắng -',
},
{
avatar: 'https://nguyencongpc.vn/media/lib/02-06-2025/sep-tuan-maxhome.jpg',
text: '12 chi nhánh Maxhome trên toàn quốc đều sử dụng máy tính tại NC. Mình build PC ở đây hơn 10 năm rồi vẫn rất hài lòng.',
author: '- Mr. Tuấn CEO Maxhome -',
},
];

View File

@@ -0,0 +1,30 @@
'use client';
import { dataReview } from './dataReview';
import { Swiper, SwiperSlide } from 'swiper/react';
import { Autoplay, Navigation, Pagination } from 'swiper/modules';
import ItemReview from './ItemReview';
const BoxReviewCustomer: React.FC = () => {
return (
<div className="box-review-from-customer boder-radius-10">
<div className="title-box">
<h2 className="title-box font-[600]">Đánh giá từ khách hàng về Nguyễn Công PC</h2>
</div>
<div className="list-review-customer-homepage">
<Swiper
modules={[Autoplay, Navigation, Pagination]}
spaceBetween={15}
slidesPerView={3}
loop={true}
pagination={{ clickable: true }}
>
{dataReview.map((item, index) => (
<SwiperSlide key={index} className="item">
<ItemReview item={item} />
</SwiperSlide>
))}
</Swiper>
</div>
</div>
);
};
export default BoxReviewCustomer;

View File

@@ -2,7 +2,7 @@
import React from 'react';
import Image from 'next/image';
import Link from 'next/link';
import { Category } from '@types/Menu';
import { Category } from '@/types/global/Menu';
const ItemCategory: React.FC<{ item: Category }> = ({ item }) => {
return (

View File

@@ -1,10 +1,10 @@
'use client';
import React from 'react';
import { menuData } from '../../Header/menuData';
import ItemCategory from './itemCategory';
import { Category } from '../../../../types/Menu';
import { menuData } from '../../other/Header/menuData';
import ItemCategory from './ItemCategory';
import { InfoCategory } from '@/types';
const renderFeaturedCategories = (categories: Category[]) => {
const renderFeaturedCategories = (categories: InfoCategory[]) => {
return categories.map((cat, idx) => (
<React.Fragment key={idx}>
{cat.is_featured == '1' && <ItemCategory item={cat} />}

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,9 @@ import SliderHome from './SliderHome';
import BoxProductDeal from './BoxDeal';
import CategoryFeature from './CategoryFeature';
import BoxListCategory from './BoxCategory';
import BoxArticle from './BoxArticle';
import BoxArticleVideo from './BoxArticleVideo';
import BoxReviewCustomer from './BoxReviewCustomer';
const Home = () => {
return (
@@ -17,6 +20,15 @@ const Home = () => {
{/* DANH SÁCH DANH MỤC */}
<BoxListCategory />
{/* tin tức công nghệ */}
<BoxArticle />
{/* tin tức video */}
<BoxArticleVideo />
{/* đánh giá từa khách hàng */}
<BoxReviewCustomer />
</div>
</div>
);

View File

@@ -0,0 +1,86 @@
import Link from 'next/link';
import Image from 'next/image';
import { FaFacebookF, FaYoutube, FaAngleUp } from 'react-icons/fa';
import { FaFacebookMessenger } from 'react-icons/fa';
import { SiZalo } from 'react-icons/si';
const IconFixRight: React.FC = () => {
return (
<div className="global-fixed-right">
<Link
href="https://www.facebook.com/NGUYENCONGPC.VN"
aria-label="Face Book"
target="_blank"
className="fix-face flex items-center justify-center"
>
<FaFacebookF size={20} />
</Link>
<Link
href="https://www.youtube.com/c/NGUYENCONGPC"
aria-label="Youtube"
target="_blank"
className="fix-youtube flex items-center justify-center"
>
<FaYoutube size={22} />
</Link>
<Link
href="javascript:window.scrollTo({ top: 0, behavior: 'smooth' });"
className="scroll-top-btn items-center justify-center"
title="Di chuyển lên đầu trang!"
style={{ display: 'none' }}
>
<FaAngleUp size={20} />
</Link>
<Link
href="https://m.me/nguyencongpc.vn"
target="_blank"
className="messenger flex items-center"
>
<Image
src="https://nguyencongpc.vn/static/assets/nguyencong_2023/images/facebook_messenger.png"
width="40"
height="40"
alt="mes"
className="lazy"
/>
<div className="contact-info">
<b className="d-block">Chat Facebook</b>
<span>(8h-22h30)</span>
</div>
</Link>
<Link
href="https://zalo.me/nguyencongpc"
target="_blank"
className="flex items-center"
style={{
background: '#fff',
bottom: '135px',
width: '170px',
padding: '10px',
borderRadius: '15px',
boxShadow: '0 1px 2px 1px #01010133',
color: '#007bff',
fontSize: '14px',
height: '65px',
}}
>
<Image
src="https://nguyencongpc.vn/media/lib/24-01-2024/zalo.png"
width="40"
height="40"
alt="mes"
className="lazy"
style={{ marginRight: '10px' }}
/>
<div className="contact-info">
<b className="d-block">Chat Zalo</b>
<span>(8h-22h30)</span>
</div>
</Link>
</div>
);
};
export default IconFixRight;

View File

@@ -0,0 +1,218 @@
import Link from 'next/link';
import Image from 'next/image';
import IconFixRight from './IconFixRight';
const Footer: React.FC = () => {
return (
<>
<footer className="footer-main">
{/* Chính sách */}
<div className="footer-policy">
<div className="container flex items-center justify-between gap-12">
<div className="item flex items-center justify-center">
<i className="sprite sprite-giaohang-footer"></i>
<p className="text box-title-policy m-0">
<b className="block">Chính sách giao hàng</b>
<span className="grey block">nhận hàng thanh toán tại nhà</span>
</p>
</div>
<div className="item flex items-center justify-center">
<i className="sprite sprite-doitra-footer"></i>
<p className="text box-title-policy m-0">
<b className="block">đi trả dễ dàng</b>
<span className="grey block">1 đi 1 trong 15 ngày</span>
</p>
</div>
<div className="item flex items-center justify-center">
<i className="sprite sprite-thanhtoan-footer"></i>
<p className="text box-title-policy m-0">
<b className="block">thanh toán tiện lợi</b>
<span className="grey block">tiền mặt, CK, trả góp 0%</span>
</p>
</div>
<div className="item flex items-center justify-center">
<i className="sprite sprite-hotro-footer"></i>
<p className="text box-title-policy m-0">
<b className="block">hỗ trợ nhiệt tình</b>
<span className="grey block"> vấn, giải đáp mọi thắc mắc</span>
</p>
</div>
</div>
</div>
{/* Box info */}
<div className="box-info-main">
<div className="justify-content-between footer-list-info-main container flex">
<div className="item-info-main">
<p className="title font-weight-700">Giới thiệu nguyễn công</p>
<Link href="https://nguyencongpc.vn/pages/profile.html" className="text">
Giới thiệu công ty
</Link>
<Link href="/lien-he" className="text">
Thông tin liên hệ
</Link>
<Link href="/tin-tuc" className="text">
Tin tức
</Link>
<Link href="/tuyen-dung" className="text">
Tuyển dụng
</Link>
<Link href="#fancybox-showroom" className="text">
Hệ thống cửa hàng
</Link>
<div className="list-social-footer flex items-center">
<Link
href="https://www.facebook.com/MAY.TINH.NGUYEN.CONG"
className="item-social"
aria-label="Facebook"
>
<i className="sprite sprite-fb-footer"></i>
</Link>
<Link
href="https://www.youtube.com/c/NGUYENCONGPC"
className="item-social"
aria-label="Youtube"
>
<i className="sprite sprite-youtube-fotoer"></i>
</Link>
<a href="javascript:;" className="item-social" aria-label="Instagram">
<i className="sprite sprite-instagram-footer"></i>
</a>
<a href="javascript:;" className="item-social" aria-label="Tiktok">
<i className="sprite sprite-tiktok-footer"></i>
</a>
</div>
<div className="bct-footer flex gap-3">
<Link
href="http://online.gov.vn/Home/WebDetails/47532"
target="_blank"
rel="nofollow"
>
<Image
src="https://nguyencongpc.vn/static/assets/nguyencong_2023/images/footer-bct.png"
alt="bộ công thương"
className="lazy"
width={132}
height="1"
/>
</Link>
<Link
href="https://www.dmca.com/compliance/nguyencongpc.vn"
title="DMCA Compliance information for nguyencongpc.vn"
target="_blank"
rel="nofollow"
>
<img
src="https://www.dmca.com/img/dmca-compliant-grayscale.png"
alt="DMCA compliant"
width={115}
height={40}
/>
</Link>
</div>
<div>
<i className="sprite sprite-dmca-footer"></i>
</div>
</div>
<div className="item-info-main">
<p className="title font-weight-700">Hỗ trợ khách hàng</p>
<Link href="/huong-dan-mua-hang-truc-tuyen" className="text">
Hướng dẫn mua hàng trực tuyến
</Link>
<Link href="/pages/chinh-sach-thanh-toan.html" className="text">
Hướng dẫn thanh toán
</Link>
<Link href="/lien-he" className="text">
Gửi yêu cầu bảo hành
</Link>
<Link href="/lien-he" className="text">
Góp ý, Khiếu Nại
</Link>
</div>
<div className="item-info-main">
<p className="title font-weight-700">Chính sách chung</p>
<Link href="/pages/chinh-sach-chung.html" className="text">
Chính sách, quy đnh chung
</Link>
<Link href="/pages/chinh-sach-van-chuyen.html" className="text">
Chính sách vận chuyển
</Link>
<Link href="/pages/chinh-sach-bao-hanh.html" className="text">
Chính sách bảo hành
</Link>
<Link href="/pages/dich-vu-tinh-phi.html" className="text">
Dịch vụ tính phí
</Link>
<Link href="/chinh-sach-nhap-lai" className="text">
Chính sách nhập lại tính phí
</Link>
<Link href="/pages/chinh-sach-doanh-nghiep.html" className="text">
Chính sách cho doanh nghiệp
</Link>
<Link href="/bao-mat-thong-tin-khach-hang" className="text">
Bảo mật thông tin khách hàng
</Link>
</div>
<div className="item-info-main">
<p className="title font-weight-700">Thông tin khuyến mại</p>
<Link href="https://khuyenmai.nguyencongpc.vn" className="text">
Thông tin khuyến mại
</Link>
<Link href="/san-pham-xa-hang" className="text">
Sản phẩm khuyến mại
</Link>
<Link href="/san-pham-ban-chay" className="text">
Sản phẩm bán chạy
</Link>
<Link href="/san-pham-moi" className="text">
Sản phẩm mới
</Link>
</div>
</div>
{/* Footer bottom */}
<div className="footer-bottom">
<div className="container">
<div className="copyright">
<p>WEBSITE ĐƯC SỞ HỮU QUẢN BỞI NGUYỄN VIẾT CÔNG</p>
<b>CÔNG TY TNHH MÁY TÍNH NGUYỄN CÔNG</b>
<p>Đa chỉ: 17 Kế Tấn - Phường Phương Liệt - Nội</p>
<p> số thuế: 0107568451 do Sở Kế Hoạch Đu TP. Nội (17/09/2016)</p>
<p>
Mua hàng: <Link href="tel:0866666166">089.9999.191</Link> -{' '}
<a href="tel:0812666665">0812.666.665</a>
</p>
<p className="list-contact-footer flex items-center">
<span>
GÓP Ý : <Link href="tel:0979999191">097.9999.191</Link> -{' '}
<a href="tel:0983333388">098.33333.88</a>.
</span>
<span>
Email: <Link href="mailto:info@nguyencongpc.vn">info@nguyencongpc.vn</Link>.
</span>
<span>
Website: <Link href="/">nguyencongpc.vn</Link>.
</span>
<span>
Fanpage:{' '}
<a href="https://www.facebook.com/MAY.TINH.NGUYEN.CONG">
facebook.com/MAY.TINH.NGUYEN.CONG
</a>
.
</span>
</p>
</div>
</div>
</div>
</div>
</footer>
<IconFixRight />
</>
);
};
export default Footer;

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

59
src/lib/category.ts Normal file
View File

@@ -0,0 +1,59 @@
import { CategoryDetail } from '@/types';
/**
* Tìm danh mục theo mảng slug (ví dụ: ["pc-gaming","cao-cap","rtx-4090"])
*/
export function findCategoryBySlug(
slugs: string[],
categories: CategoryDetail[],
): CategoryDetail | null {
let currentList: CategoryDetail[] = categories;
let currentCategory: CategoryDetail | null = null;
for (const slug of slugs) {
const found = currentList.find((cat) => cat.slug === slug);
if (!found) return null;
currentCategory = found;
currentList = found.children || [];
}
return currentCategory;
}
/**
* Lấy tất cả danh mục con của một danh mục
*/
export function getChildren(category: CategoryDetail): CategoryDetail[] {
return category.children || [];
}
/**
* Tìm danh mục theo id
*/
export function findCategoryById(id: number, categories: CategoryDetail[]): CategoryDetail | null {
for (const cat of categories) {
if (cat.id === id) return cat;
if (cat.children) {
const found = findCategoryById(id, cat.children);
if (found) return found;
}
}
return null;
}
/**
* Tìm đường dẫn breadcrumb từ root đến danh mục hiện tại
*/
export function getBreadcrumb(slugs: string[], categories: CategoryDetail[]): CategoryDetail[] {
const breadcrumb: CategoryDetail[] = [];
let currentList: CategoryDetail[] = categories;
for (const slug of slugs) {
const found = currentList.find((cat) => cat.slug === slug);
if (!found) break;
breadcrumb.push(found);
currentList = found.children || [];
}
return breadcrumb;
}

View File

@@ -1839,21 +1839,21 @@ textarea::placeholder {
line-height: 16px;
margin-left: 8px;
}
.footer .footer-policy {
.footer-main .footer-policy {
background: #e5e5e5;
padding: 27px 0;
}
.footer .footer-policy .item {
.footer-main .footer-policy .item {
width: 25%;
padding: 15px 12px;
border-radius: 10px;
height: 88px;
background: #fff;
}
.footer .footer-policy .box-title-policy {
.footer-main .footer-policy .box-title-policy {
margin-left: 10px;
}
.footer .footer-policy .box-title-policy b {
.footer-main .footer-policy .box-title-policy b {
text-transform: uppercase;
font-size: 16px;
line-height: 18px;
@@ -1861,54 +1861,54 @@ textarea::placeholder {
color: #000;
margin-bottom: 4px;
}
.footer .footer-policy .box-title-policy span {
.footer-main .footer-policy .box-title-policy span {
text-transform: capitalize;
}
.footer .box-info-main {
.footer-main .box-info-main {
background: #fff;
}
.footer .footer-list-info-main {
.footer-main .footer-list-info-main {
padding: 18px 10px 20px;
font-size: 13px;
line-height: 24px;
gap: 10px;
}
.footer .footer-list-info-main .item-info-main {
.footer-main .footer-list-info-main .item-info-main {
width: 25%;
}
.footer .footer-list-info-main .list-social-footer {
.footer-main .footer-list-info-main .list-social-footer {
margin: 10px 0;
gap: 8px;
}
.footer .footer-list-info-main .bct-footer {
.footer-main .footer-list-info-main .bct-footer {
margin-bottom: 10px;
}
.footer .footer-list-info-main .item-social {
.footer-main .footer-list-info-main .item-social {
border-radius: 5px;
}
.footer .footer-list-info-main p {
.footer-main .footer-list-info-main p {
font-size: 14px;
color: #000;
text-transform: uppercase;
margin-bottom: 10px;
}
.footer .footer-list-info-main a {
.footer-main .footer-list-info-main a {
display: block;
}
.footer .footer-list-info-main a:hover {
.footer-main .footer-list-info-main a:hover {
text-decoration: underline;
color: var(--color-primary);
}
.footer .footer-bottom {
.footer-main .footer-bottom {
background: #f4f4f4;
padding: 15px 0;
font-size: 13px;
line-height: 20px;
}
.footer .footer-bottom .list-contact-footer {
.footer-main .footer-bottom .list-contact-footer {
gap: 20px;
}
.footer .footer-bottom a:hover {
.footer-main .footer-bottom a:hover {
text-decoration: underline;
color: var(--color-primary);
}
@@ -2167,7 +2167,8 @@ textarea::placeholder {
.page-hompage .box-banner-under-slider {
margin-top: -50px;
}
.swiper .swiper-button-prev svg,.swiper .swiper-button-next svg {
.swiper .swiper-button-prev svg,
.swiper .swiper-button-next svg {
width: 8px !important;
color: #000;
}

View File

@@ -1,50 +0,0 @@
export interface BannerItem {
id: string,
display: string,
fileUrl: string,
desUrl: string,
title: string,
width: number,
height: number,
fileType: string,
summary: string
}
export interface BannerFooterData {
banner_feedback: BannerItem[];
banner_column_right: BannerItem[];
banner_column_left: BannerItem[];
}
export interface BannerHeaderData {
banner_buildpc: BannerItem[];
banner_header_top_mb_2023: BannerItem[];
banner_header_top: BannerItem[];
banner_page_deal_2023: BannerItem[];
banner_column_left: BannerItem[];
banner_column_right: BannerItem[];
}
export interface BannerHomePageData {
slider_home: BannerItem[];
banner_under_slider_trangchu: BannerItem[];
banner_slider_mobile_2023: BannerItem[];
banner_product_category: BannerItem[];
banner_slider_homepage_main: BannerItem[];
banner_underslider_trangchu_mobile: BannerItem[];
banner_bot_home: BannerItem[];
banner_mid_home: BannerItem[];
banner_right_home: BannerItem[];
banner_collection_pc: BannerItem[];
}
export interface TemplateBanner {
footer: BannerFooterData;
header: BannerHeaderData;
homepage: BannerHomePageData;
}
export type BannerType = TemplateBanner[];

View File

@@ -1,21 +0,0 @@
export interface Category {
id: string;
title: string;
parentId: string;
thumnail: string;
big_image: string;
isParent: string;
url: string;
is_featured: string;
summary: string;
children: Category[]; // Đệ quy: Quan trọng nhất để định nghĩa cấp con
}
export interface ProductCategory {
product: {
all_category: Category[];
};
}
export type MenuTypes = ProductCategory[];

20
src/types/global/Menu.ts Normal file
View File

@@ -0,0 +1,20 @@
export interface InfoCategory {
id: string;
title: string;
parentId: string;
thumnail: string;
big_image: string;
isParent: string;
url: string;
is_featured: string;
summary: string;
children: InfoCategory[]; // Đệ quy: Quan trọng nhất để định nghĩa cấp con
}
export interface all_category {
product: {
all_category: InfoCategory[];
};
}
export type MenuTypes = all_category[];

View File

@@ -0,0 +1 @@
export * from '@/types/global/Menu';

47
src/types/home/Banner.ts Normal file
View File

@@ -0,0 +1,47 @@
export interface BannerItem {
id: string;
display: string;
fileUrl: string;
desUrl: string;
title: string;
width: number;
height: number;
fileType: string;
summary: string;
}
export interface BannerFooterData {
banner_feedback: BannerItem[];
banner_column_right: BannerItem[];
banner_column_left: BannerItem[];
}
export interface BannerHeaderData {
banner_buildpc: BannerItem[];
banner_header_top_mb_2023: BannerItem[];
banner_header_top: BannerItem[];
banner_page_deal_2023: BannerItem[];
banner_column_left: BannerItem[];
banner_column_right: BannerItem[];
}
export interface BannerHomePageData {
slider_home: BannerItem[];
banner_under_slider_trangchu: BannerItem[];
banner_slider_mobile_2023: BannerItem[];
banner_product_category: BannerItem[];
banner_slider_homepage_main: BannerItem[];
banner_underslider_trangchu_mobile: BannerItem[];
banner_bot_home: BannerItem[];
banner_mid_home: BannerItem[];
banner_right_home: BannerItem[];
banner_collection_pc: BannerItem[];
}
export interface TemplateBanner {
footer: BannerFooterData;
header: BannerHeaderData;
homepage: BannerHomePageData;
}
export type BannerType = TemplateBanner[];

View File

@@ -0,0 +1,33 @@
// types/Article.ts
export interface ArticleImage {
thum: string;
original: string;
}
export interface ArticleExtend {
pixel_code: string;
}
export interface Article {
id: number;
title: string;
extend: ArticleExtend;
summary: string;
createDate: string;
createBy: string;
lastUpdate: string;
lastUpdateBy: string;
visit: number;
is_featured: number;
article_time: string;
review_rate: number;
review_count: number;
video_code: string;
external_url: string;
author: string;
counter: number;
url: string;
image: ArticleImage;
}
export type ListArticle = Article[];

4
src/types/home/index.ts Normal file
View File

@@ -0,0 +1,4 @@
export * from '@/types/home/Banner';
export * from '@/types/home/TypeArticle';
export * from '@/types/home/TypeListProduct';
export * from '@/types/home/TypeListProductDeal';

3
src/types/index.ts Normal file
View File

@@ -0,0 +1,3 @@
export * from '@/types/global';
export * from '@/types/home';
export * from '@/types/product';

View File

@@ -0,0 +1,24 @@
export interface CategoryDetail {
id: number;
name: string;
slug: string;
description?: string;
image?: string;
parentId?: number;
children?: CategoryDetail[];
}
export interface CategoryDetailNew {
title: string;
keywords: string;
description: string;
canonical: string;
image: string;
}
export interface current_category {
id: string;
catPath: string;
childListId: string;
display_option: string;
}

View File

@@ -0,0 +1 @@
export * from '@/types/product/category';