12/02/2026
This commit is contained in:
12
src/app/(main)/buildpc/page.tsx
Normal file
12
src/app/(main)/buildpc/page.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import BuildPc from "@/components/buildpc"
|
||||
import type { Metadata } from "next";
|
||||
export const metadata: Metadata = {
|
||||
title: "Xây Dựng Cấu Hình PC, Build PC Chuẩn Nhất✔️Giá Rẻ",
|
||||
description: "Xây dựng cấu hình máy tính PC chuyên nghiệp ✳️ máy tính đồ họa, máy tính làm việc giá rẻ ✳️ Build PC.",
|
||||
};
|
||||
|
||||
export default function Home() {
|
||||
return(
|
||||
<BuildPc />
|
||||
)
|
||||
}
|
||||
13
src/app/(main)/deal/page.tsx
Normal file
13
src/app/(main)/deal/page.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import DealPage from "@/components/deal"
|
||||
|
||||
import type { Metadata } from "next";
|
||||
export const metadata: Metadata = {
|
||||
title: "Flash Sale Mỗi Ngày Cực Sốc - Hoàng Hà PC ",
|
||||
description: "Flash Sale Mỗi Ngày Cực Sốc - Hoàng Hà PC",
|
||||
};
|
||||
|
||||
export default function Home() {
|
||||
return(
|
||||
<DealPage />
|
||||
)
|
||||
}
|
||||
@@ -1,12 +1,26 @@
|
||||
import { findBySlug } from "@/lib/slug/slugMap";
|
||||
import type { Metadata } from "next";
|
||||
import { notFound } from "next/navigation";
|
||||
import LayoutTypeSetter from "@/components/layout/LayoutTypeSetter"
|
||||
import { findBySlug } from "@/lib/slug/slugMap";
|
||||
import { metadataBySlug } from "@/app/[slug]/metadataBySlug";
|
||||
import { SLUG_CONFIG } from "@/app/[slug]/slugConfig";
|
||||
import { renderBySlug } from "@/app/[slug]/renderBySlug";
|
||||
import LayoutTypeSetter from "@/components/layout/LayoutTypeSetter"
|
||||
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ slug: string }>;
|
||||
}): Promise<Metadata> {
|
||||
const { slug } = await params;
|
||||
const result = await findBySlug(slug);
|
||||
|
||||
return metadataBySlug(result);
|
||||
}
|
||||
|
||||
|
||||
export default async function SlugPage({ params }: { params: { slug: string } }) {
|
||||
console.log("designer-tool layout check");
|
||||
|
||||
const { slug } = await params;
|
||||
const result = await findBySlug(slug);
|
||||
if (!result) notFound();
|
||||
|
||||
37
src/app/(main)/tim/[slug]/page.tsx
Normal file
37
src/app/(main)/tim/[slug]/page.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import type { Metadata } from "next";
|
||||
import { notFound } from "next/navigation";
|
||||
import { findBySlug } from "@/lib/slug/slugMap";
|
||||
import { metadataBySlug } from "@/app/[slug]/metadataBySlug";
|
||||
import { SLUG_CONFIG } from "@/app/[slug]/slugConfig";
|
||||
import { renderBySlug } from "@/app/[slug]/renderBySlug";
|
||||
import LayoutTypeSetter from "@/components/layout/LayoutTypeSetter"
|
||||
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ slug: string }>;
|
||||
}): Promise<Metadata> {
|
||||
const { slug } = await params;
|
||||
const result = await findBySlug(slug);
|
||||
|
||||
return metadataBySlug(result);
|
||||
}
|
||||
|
||||
|
||||
export default async function SlugPage({ params }: { params: { slug: string } }) {
|
||||
|
||||
const { slug } = await params;
|
||||
const result = await findBySlug(slug);
|
||||
if (!result) notFound();
|
||||
|
||||
const config = SLUG_CONFIG[result.type];
|
||||
if (!config) notFound();
|
||||
|
||||
return (
|
||||
<LayoutTypeSetter layout="main">
|
||||
{renderBySlug(result)}
|
||||
</LayoutTypeSetter>
|
||||
);
|
||||
|
||||
}
|
||||
13
src/app/(main)/tim/page.tsx
Normal file
13
src/app/(main)/tim/page.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import ProductSearch from "@/components/search"
|
||||
|
||||
import type { Metadata } from "next";
|
||||
export const metadata: Metadata = {
|
||||
title: "Danh sách tìm kiếm ",
|
||||
description: "Danh sách kết quả thỏa mãn",
|
||||
};
|
||||
|
||||
export default function Search() {
|
||||
return(
|
||||
<ProductSearch />
|
||||
)
|
||||
}
|
||||
@@ -1,16 +1,13 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import LayoutTypeSetter from "@/components/layout/LayoutTypeSetter";
|
||||
import StaticStyleLoader from "@/components/layout/StaticStyleLoader";
|
||||
|
||||
// import '@/styles/static_page.css';
|
||||
// import '@/styles/tuyen_dung.css';
|
||||
import '@/styles/static_page.css';
|
||||
import '@/styles/tuyen_dung.css';
|
||||
|
||||
export default function StaticLayout({ children }: { children: ReactNode }) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<StaticStyleLoader />
|
||||
|
||||
<LayoutTypeSetter layout="static">
|
||||
{children}
|
||||
</LayoutTypeSetter>
|
||||
|
||||
@@ -69,6 +69,15 @@ export function metadataBySlug(result: any): Metadata {
|
||||
}
|
||||
};
|
||||
|
||||
case "designer_detail":
|
||||
return {
|
||||
title: result.data.title,
|
||||
description: result.data.meta_description,
|
||||
openGraph: {
|
||||
type: 'website',
|
||||
}
|
||||
};
|
||||
|
||||
default:
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { cache } from "react";
|
||||
import { renderBySlug } from "./renderBySlug";
|
||||
import { metadataBySlug } from "./metadataBySlug";
|
||||
import { findBySlug } from "@/lib/slug/slugMap";
|
||||
@@ -15,7 +14,7 @@ export async function generateMetadata({
|
||||
}): Promise<Metadata> {
|
||||
const { slug } = await params;
|
||||
const result = await findBySlug(slug);
|
||||
if (!result) return { title: "Local PC" };
|
||||
|
||||
return metadataBySlug(result);
|
||||
}
|
||||
|
||||
|
||||
@@ -266,13 +266,13 @@ export default function Info() {
|
||||
<img src="/images/static-about-us-pic-4.png" alt="customer-image" />
|
||||
</div>
|
||||
<ul className="ul customer-list text-20">
|
||||
<li><span> Doanh nghiệp nhà nước </span></li>
|
||||
<li><span> Tập đoàn lớn </span></li>
|
||||
<li><span> Doanh nghiệp tư nhân vừa và nhỏ </span></li>
|
||||
<li><span> Tổ chức phi chính phủ </span></li>
|
||||
<li><span> Doanh nghiệp vốn đầu tư nước ngoài </span></li>
|
||||
<li><span> Trường học, bệnh viện </span></li>
|
||||
<li><span> Team Youtube, MMO </span></li>
|
||||
<li><span/> Doanh nghiệp nhà nước </li>
|
||||
<li><span/> Tập đoàn lớn </li>
|
||||
<li><span/> Doanh nghiệp tư nhân vừa và nhỏ </li>
|
||||
<li><span/> Tổ chức phi chính phủ </li>
|
||||
<li><span/> Doanh nghiệp vốn đầu tư nước ngoài </li>
|
||||
<li><span/> Trường học, bệnh viện </li>
|
||||
<li><span/> Team Youtube, MMO </li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
3
src/components/buildpc/index.tsx
Normal file
3
src/components/buildpc/index.tsx
Normal file
@@ -0,0 +1,3 @@
|
||||
export default function BuildPc() {
|
||||
return(<> cpn buildpc </>)
|
||||
}
|
||||
93
src/components/deal/DealItemType2.tsx
Normal file
93
src/components/deal/DealItemType2.tsx
Normal file
@@ -0,0 +1,93 @@
|
||||
'use client';
|
||||
import Link from "next/link";
|
||||
import parse from "html-react-parser";
|
||||
import { formatPrice } from "@/lib/utils";
|
||||
import { useDealItem } from "@/hooks/useDealItem"
|
||||
import { useCart } from '@/hooks/useCart';
|
||||
import { DealCountdown } from "@/lib/times"
|
||||
|
||||
export default function DealItem( {item}: any ) {
|
||||
|
||||
const deal = useDealItem(item);
|
||||
if (!deal) return null;
|
||||
|
||||
const { addToCart, isInCart } = useCart();
|
||||
|
||||
const {
|
||||
productInfo,
|
||||
price,
|
||||
marketPrice,
|
||||
discount,
|
||||
remain,
|
||||
saleRemainPercent,
|
||||
specialOffer,
|
||||
} = deal;
|
||||
|
||||
const discountView = discount > 0 ? (<>
|
||||
<del>{formatPrice(marketPrice)} đ</del>
|
||||
<span className="deal-discount">-{discount}%</span>
|
||||
</>
|
||||
) : null;
|
||||
const checkIncart = isInCart(productInfo.id);
|
||||
|
||||
return(
|
||||
<div className="deal-item js-p-item js-deal-item p-[10px]">
|
||||
<Link href={ productInfo.productUrl } className="deal-img">
|
||||
<img
|
||||
src={ productInfo.productImage.large }
|
||||
alt={ productInfo.productName }
|
||||
width={200}
|
||||
height={200}
|
||||
className="fit-img"
|
||||
/>
|
||||
</Link>
|
||||
|
||||
<div className="deal-text">
|
||||
<Link href={ productInfo.productUrl } className="deal-name"> { item.title } </Link>
|
||||
|
||||
<div className="deal-price-holder">
|
||||
<div>
|
||||
<p className="deal-price"> {formatPrice(price)} đ </p>
|
||||
|
||||
{discountView}
|
||||
</div>
|
||||
|
||||
<button className={`deal-btn bx ${checkIncart ? 'bx-check' : 'bx-plus' }`}
|
||||
style={{ background: `${checkIncart ? '#ccc' : ''}` }}
|
||||
disabled={checkIncart}
|
||||
type="button" aria-label="Mua"
|
||||
onClick={() => addToCart(productInfo.id)}
|
||||
></button>
|
||||
</div>
|
||||
|
||||
<div className="deal-count">
|
||||
<i className="deal-line" style={{ width: saleRemainPercent + '%' }}></i>
|
||||
<span>
|
||||
{ Number(item.sale_quantity) < Number(item.quantity)
|
||||
? `Còn ${remain}/${item.quantity} sản phẩm`
|
||||
: 'Hết DEAL'
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="deal-time-container my-2 flex flex-wrap justify-content-between items-center text-13 gap-1">
|
||||
<p className="m-0"> Kết thúc sau: </p>
|
||||
|
||||
<div className="deal-time-holder js-deal-time" data-time-left={item.to_time}>
|
||||
<DealCountdown endTime={item.to_time} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{specialOffer && (
|
||||
<div className="deal-offer">
|
||||
<span className="text-[#BE1F2D]"> Quà tặng: </span>
|
||||
|
||||
{parse(specialOffer)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
46
src/components/deal/index.tsx
Normal file
46
src/components/deal/index.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
'use client';
|
||||
import { dealList } from "@/data/deals"
|
||||
|
||||
import { useMemo } from "react";
|
||||
import { usePagination } from "@/hooks/usePagination";
|
||||
import ButtonShowMore from "@/components/shared/ProductShowMore";
|
||||
import DealItemType2 from "@/components/deal/DealItemType2"
|
||||
|
||||
|
||||
export default function DealPage() {
|
||||
|
||||
const data = useMemo(() => {
|
||||
return dealList.flatMap((item: any) => item.list);
|
||||
}, []);
|
||||
|
||||
const {
|
||||
currentData,
|
||||
hasMore,
|
||||
loadMore,
|
||||
total
|
||||
} = usePagination(data);
|
||||
|
||||
return (
|
||||
<div className="deal-page container">
|
||||
<div className="text-center mb-4">
|
||||
<img src="https://hoanghapccdn.com/media/lib/09-07-2024/hoanghapc-deal-hot.jpg" alt="DEAL" width={1600} height={490} className="deal-featured-image" style={{ width: '100%', borderRadius: 12 }} />
|
||||
</div>
|
||||
|
||||
|
||||
<div className="product-holder grid grid-cols-2 gap-3 lg:grid-cols-5 lg:gap-x-5 lg:gap-y-8 mb-6 relative min-h-[300px]">
|
||||
{currentData.map((item: any) => (
|
||||
<DealItemType2 key={item.id} item={item} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
{hasMore &&
|
||||
<ButtonShowMore
|
||||
onClick={loadMore}
|
||||
displayCount={currentData.length}
|
||||
total={total}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
|
||||
)
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
export default function DesignerItem({ item }: any) {
|
||||
console.log('DesignerItem: ', item)
|
||||
return (
|
||||
<>
|
||||
<div className="p-item">
|
||||
<a href="{{ _item.productUrl }}" className="p-img">
|
||||
<img src="{{ _item.productImage.large }}" alt="{{ _item.productName }}" width={250} height={250} />
|
||||
</a>
|
||||
<div className="p-text">
|
||||
<a href="{{ _item.productUrl }}" className="p-name">
|
||||
<h3> {'{'}{'{'} _item.productName {'}'}{'}'} </h3>
|
||||
</a>
|
||||
<div className="p-price-group">
|
||||
<p className="p-price">
|
||||
{'{'}% if _item.price > 0 %{'}'} {'{'}{'{'} _item.price | format_price {'}'}{'}'} đ
|
||||
{'{'}% else %{'}'} Liên hệ
|
||||
{'{'}% endif %{'}'}
|
||||
</p>
|
||||
{'{'}% if _item.price_off > 0 %{'}'}
|
||||
<del>{'{'}{'{'} _item.marketPrice | format_price {'}'}{'}'} đ</del>
|
||||
<span className="p-discount">-{'{'}{'{'} _item.price_off {'}'}{'}'}%</span>
|
||||
{'{'}% endif %{'}'}
|
||||
</div>
|
||||
{'{'}% if _item.productSummary %{'}'}
|
||||
<div className="p-summary">
|
||||
{'{'}% assign _summary = _item.productSummary | get_line %{'}'}
|
||||
{'{'}% for _item in _summary | limit: 5 %{'}'}
|
||||
<div className="item-circle"> {'{'}{'{'} _item {'}'}{'}'} </div>
|
||||
{'{'}% endfor %{'}'}
|
||||
</div>
|
||||
{'{'}% endif %{'}'}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,18 +1,41 @@
|
||||
'use client';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import parse from "html-react-parser";
|
||||
import { useState } from 'react';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import Link from "next/link";
|
||||
import FAQ from "./Faq";
|
||||
import DesignerItem from "@/components/shared/DesignerItem";
|
||||
import { usePagination } from "@/hooks/usePagination";
|
||||
import ButtonShowMore from "@/components/shared/ProductShowMore";
|
||||
|
||||
export default function Detail({ slug }: any) {
|
||||
// console.log('DesignerDetail: ', slug)
|
||||
const title = usePathname().includes('device=laptop') ? 'Laptop' : 'PC';
|
||||
const deviceTitle = useSearchParams().get('device') == 'laptop' ? 'Laptop' : 'PC';
|
||||
|
||||
const ignoreKeys = ["Loại máy", "Loại máy tính"];
|
||||
const {
|
||||
attribute_list,
|
||||
product_list
|
||||
} = slug
|
||||
|
||||
const filteredAttribute = slug.attribute_list.filter(
|
||||
(item: any) => !ignoreKeys.includes(item.name)
|
||||
const ignoreKeys = ["loại máy", "loại máy tính"];
|
||||
|
||||
const filteredAttribute = attribute_list.filter(
|
||||
(item: any) =>
|
||||
!ignoreKeys.map((key: string) =>
|
||||
key.toLowerCase()).includes(item.name.toLowerCase())
|
||||
);
|
||||
|
||||
const [attribute, setAttribute] = useState(0)
|
||||
const result = product_list.filter((item: any) =>
|
||||
attribute === 0 || item.attribute?.includes(attribute)
|
||||
);
|
||||
|
||||
const {
|
||||
currentData,
|
||||
hasMore,
|
||||
loadMore,
|
||||
total
|
||||
} = usePagination(result, 24);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -45,38 +68,54 @@ export default function Detail({ slug }: any) {
|
||||
|
||||
<div className="container">
|
||||
<h1 className="text-center text-[#004BA4] font-600 text-24 leading-[30px] lg:text-[32px] lg:leading-10 mb-4">
|
||||
{title} dành cho {slug.item_info.name}
|
||||
{deviceTitle} dành cho {slug.item_info.name}
|
||||
</h1>
|
||||
|
||||
{filteredAttribute &&
|
||||
{filteredAttribute.length > 0 &&
|
||||
<div className="text-center tool-btn-list mb-4 lg:mb-6">
|
||||
<div className="inline-flex items-center justify-center p-1 border border-[#DFE4EC] bg-[#F5F8FF] overflow-auto whitespace-nowrap no-scroll gap-1 leading-9 font-500 text-[#5D6776] text-14 rounded-[8px]">
|
||||
{
|
||||
filteredAttribute.map((item: any) =>
|
||||
<button
|
||||
key={item.id}
|
||||
className="js-attribute-btn px-9"
|
||||
data-id="{{ _item.id }}"
|
||||
data-current="current"
|
||||
<div className="inline-flex items-center justify-center p-1 border border-[#DFE4EC] bg-[#F5F8FF] overflow-auto whitespace-nowrap no-scroll gap-1 leading-9 font-500 text-[#5D6776] rounded-[8px]">
|
||||
{filteredAttribute.map((item: any) =>
|
||||
item.list.map((list: any) =>
|
||||
<button className={`js-attribute-btn px-9
|
||||
${list.id === attribute ? 'current' : ""}
|
||||
`}
|
||||
key={list.id}
|
||||
onClick={() => setAttribute(list.id)}
|
||||
>
|
||||
{item.name}
|
||||
{list.name}
|
||||
</button>
|
||||
)}
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div className="mb-8 lg:mb-12">
|
||||
<div className="tool-product-holder grid grid-cols-2 lg:grid-cols-4 gap-x-3 gap-y-4 lg:gap-x-4 lg:gap-y-6" id="js-product-holder">
|
||||
{currentData.length == 0
|
||||
? parse(` <div class="text-center py-20">
|
||||
<p class="text-20 font-700">Sản phẩm đang được cập nhật ...!</p>
|
||||
<a href="/" class="color-main text-18"> Quay lại trang chủ </a>
|
||||
</div>
|
||||
`) : (
|
||||
<div className="tool-product-holder grid grid-cols-2 lg:grid-cols-4 gap-x-3 gap-y-4 lg:gap-x-4 lg:gap-y-6" id="js-product-holder">
|
||||
{currentData.map((item: any) =>
|
||||
<DesignerItem
|
||||
key={item.id}
|
||||
item={item}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
</div>
|
||||
|
||||
<div className="text-center mt-12" id="js-paging-holder">
|
||||
<button type="button" className="mb-3 bg-btn text-white rounded-[30px] h-10 font-500 text-16 table max-w-[240px] w-full m-auto mb-3" aria-label="Xem thêm"> TẢI THÊM </button>
|
||||
<p className="text-14 leading-[18px] m-0" id="js-paging-count"> </p>
|
||||
</div>
|
||||
{hasMore &&
|
||||
<ButtonShowMore
|
||||
onClick={loadMore}
|
||||
displayCount={currentData.length}
|
||||
total={total}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{slug.item_info.description &&
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
'use client';
|
||||
import Link from "next/link";
|
||||
import { useEffect, useState, useMemo } from "react";
|
||||
import { useState, useMemo } from "react";
|
||||
import { DesignerToolData } from "@/data/designer-tool"
|
||||
import { convertToSlug } from "@/lib/utils";
|
||||
import FAQ from "./Faq";
|
||||
import { convertToSlug } from "@/lib/utils"
|
||||
|
||||
export default function DesignerTool() {
|
||||
|
||||
@@ -21,13 +21,12 @@ export default function DesignerTool() {
|
||||
|
||||
const [ device, setDevice ] = useState("desktop");
|
||||
|
||||
useEffect(() => {
|
||||
document.body.style.background = '#FFFFFF';
|
||||
console.log('device: ', device)
|
||||
}, [device]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div style={{
|
||||
background : "#FFFFFF",
|
||||
marginTop : "-20px",
|
||||
paddingTop : "20px"
|
||||
}}>
|
||||
<div className="global-breadcrumb">
|
||||
<div className="container">
|
||||
<ol itemScope itemType="http://schema.org/BreadcrumbList" className="ul clearfix">
|
||||
@@ -58,10 +57,11 @@ export default function DesignerTool() {
|
||||
<div className="w-full lg:w-[520px] flex flex-wrap items-center justify-between p-[6px] pl-5 bg-white rounded-[30px]">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Nhập phần mềm cần tìm"
|
||||
className="w-[calc(100%_-_36px)] pr-3 placeholder:!text-[#5F5F5F] placeholder:!text-[14px] h-9"
|
||||
value={keyword}
|
||||
onChange={(e) => setKeyword(e.target.value)}
|
||||
placeholder="Nhập phần mềm cần tìm"
|
||||
className="w-[calc(100%_-_36px)] pr-3 placeholder:!text-[#5F5F5F] placeholder:!text-[14px] h-9" />
|
||||
/>
|
||||
|
||||
<button type="button" aria-label="button" className="bg-linear rounded-full w-9 h-9">
|
||||
<i className="block !w-full !h-full icons icon-search" />
|
||||
@@ -96,10 +96,10 @@ export default function DesignerTool() {
|
||||
|
||||
<div className="software-list grid grid-cols-4 lg:grid-cols-7 gap-x-[16px] gap-y-[24px] lg:gap-x-[32px] lg:gap-y-[48px] text-center mb-8">
|
||||
{filteredSoftware.map((item: any) =>
|
||||
<button
|
||||
<Link
|
||||
key={item.id}
|
||||
data-id={item.url_index}
|
||||
className="js-software-item item text-12 leading-4 lg:text-[14px] lg:leading-[18px] font-500 hover:text-[#0676DA] flex flex-col"
|
||||
href={`/designer-tool/${item.url_index}?device=${device}`}
|
||||
className="item text-12 leading-4 lg:text-[14px] lg:leading-[18px] font-500 hover:text-[#0676DA] flex flex-col"
|
||||
>
|
||||
<span className="img block mb-2 lg:mb-3 relative pb-[100%]">
|
||||
<img src={item.image}
|
||||
@@ -108,8 +108,9 @@ export default function DesignerTool() {
|
||||
height={1}
|
||||
className="block absolute w-full h-full object-cover rounded-[24px]" />
|
||||
</span>
|
||||
|
||||
<span className="text"> {item.name} </span>
|
||||
</button>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -128,6 +129,6 @@ export default function DesignerTool() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
// components/layout/StaticStyleLoader.tsx
|
||||
'use client';
|
||||
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export default function StaticStyleLoader() {
|
||||
useEffect(() => {
|
||||
// Import CSS chỉ khi component được mount
|
||||
import('@/styles/static_page.css');
|
||||
import('@/styles/tuyen_dung.css');
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -66,7 +66,7 @@ export default function Header() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white shadow-[0px_4px_20px_0px_#004AA11A] text-16 font-500 mb-5">
|
||||
<div className="bg-white shadow-[0px_4px_20px_0px_#004AA11A] text-16 font-500 mb-5 relative">
|
||||
<div className="container flex items-center justify-between leading-[20px] py-[17px]">
|
||||
<Link href="/buildpc" className="flex items-center gap-2 hover:text-[#0678DB]">
|
||||
<i className="icons icon-buildpc"></i>
|
||||
|
||||
@@ -6,13 +6,13 @@ export default function otherHeader() {
|
||||
<div>
|
||||
<div className="global-header-container global-another-header">
|
||||
<div className="container d-flex align-items-center justify-content-between position-relative">
|
||||
<Link href="/" className="logo-header">
|
||||
<a href="/" className="logo-header">
|
||||
<img src="/images/logo.png"
|
||||
alt="HoangHaPc"
|
||||
width={247}
|
||||
height={96}
|
||||
className="d-block h-auto w-auto m-auto" />
|
||||
</Link>
|
||||
</a>
|
||||
|
||||
<div className="another-header-right">
|
||||
<Link href="/he-thong-cua-hang" className="header-item font-700"> Hệ thống Showroom </Link>
|
||||
|
||||
@@ -1,41 +1,48 @@
|
||||
'use client';
|
||||
import { useState } from "react";
|
||||
import parse from "html-react-parser";
|
||||
import { productList } from "@/data/products/productList";
|
||||
import ProductItem from "@/components/shared/ProductItem";
|
||||
|
||||
const PRODUCT_PER_PAGE = 30;
|
||||
import { usePagination } from "@/hooks/usePagination";
|
||||
import ButtonShowMore from "@/components/shared/ProductShowMore";
|
||||
|
||||
export default function ProductList( {id} : any) {
|
||||
|
||||
const productsFilter = productList.find(item => item.id === id)?.list || [];
|
||||
const total = productsFilter?.length;
|
||||
export default function ProductList({ id }: any) {
|
||||
|
||||
const [page, setPage] = useState(1);
|
||||
const displayCount = page * PRODUCT_PER_PAGE;
|
||||
const hasMore = displayCount < total;
|
||||
const products = productsFilter.slice(0, displayCount);
|
||||
const data = productList.find(item => item.id === id)?.list || [];
|
||||
|
||||
const {
|
||||
currentData,
|
||||
hasMore,
|
||||
loadMore,
|
||||
total
|
||||
} = usePagination(data);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="product-holder grid grid-cols-4 gap-x-5 gap-y-8 mb-6">
|
||||
{products.map((item: any) => (
|
||||
<ProductItem key={item.id} item={item} />
|
||||
))}
|
||||
</div>
|
||||
{currentData.length == 0
|
||||
? parse(`
|
||||
<div class="text-center py-20">
|
||||
<p class="text-20 font-700">Sản phẩm đang được cập nhật ...!</p>
|
||||
<a href="/" class="color-main text-18"> Quay lại trang chủ </a>
|
||||
</div>
|
||||
`)
|
||||
: (
|
||||
<div className="product-holder grid grid-cols-4 gap-x-5 gap-y-8 mb-6">
|
||||
{currentData.map((item: any) => (
|
||||
<ProductItem key={item.id} item={item} />
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{ hasMore &&
|
||||
<div className="text-center mt-12">
|
||||
<button type="button" aria-label="Xem thêm"
|
||||
className="mb-3 bg-btn text-white rounded-[30px] h-10 font-500 text-16 table max-w-[240px] w-full m-auto mb-3"
|
||||
onClick={() => setPage(prev => prev + 1)}
|
||||
>
|
||||
TẢI THÊM
|
||||
</button>
|
||||
<p className="text-14 leading-[18px] m-0">
|
||||
Hiển thị {Math.min(displayCount, total)} trên tổng số {total} sản phẩm
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
{hasMore &&
|
||||
<ButtonShowMore
|
||||
onClick={loadMore}
|
||||
displayCount={currentData.length}
|
||||
total={total}
|
||||
/>
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -35,6 +35,8 @@ export default async function ProductDetail({ slug }: any) {
|
||||
quantity : slug.quantity
|
||||
}
|
||||
|
||||
console.log('aaaaaaaa: ', priceData)
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="product-detail-page container">
|
||||
|
||||
@@ -7,8 +7,8 @@ export default function DealPrice( {item} : any ) {
|
||||
const normal_price = item.sale_rules.normal_price;
|
||||
const discount = Math.ceil(((normal_price - price) / normal_price) * 100);
|
||||
|
||||
const sale_quantity = item.deal_list[0].sale_quantity;
|
||||
const quantity = item.deal_list[0].quantity;
|
||||
const sale_quantity = Number(item.deal_list[0].sale_quantity);
|
||||
const quantity = Number(item.deal_list[0].quantity);
|
||||
const saleRemainPercent = 100 - (sale_quantity / quantity) * 100;
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
|
||||
'use client';
|
||||
import { useEffect, useState } from 'react'
|
||||
import { renderSummary } from "@/lib/utils"
|
||||
|
||||
export default function ProductSummary({ item }: any) {
|
||||
const [mounted, setMounted] = useState(false)
|
||||
@@ -22,33 +23,4 @@ export default function ProductSummary({ item }: any) {
|
||||
}
|
||||
|
||||
|
||||
function renderSummary(data: any) {
|
||||
if (!data) return null;
|
||||
|
||||
if (typeof data === 'string' && data.includes('<')) {
|
||||
const parser = new DOMParser()
|
||||
const doc = parser.parseFromString(data, 'text/html')
|
||||
|
||||
return Array.from(doc.body.childNodes)
|
||||
.filter(
|
||||
node =>
|
||||
node.nodeType === 1 &&
|
||||
node.textContent &&
|
||||
node.textContent.trim() !== ''
|
||||
)
|
||||
.map((node, index) => (
|
||||
<div key={index} className="item-circle">
|
||||
{node.textContent!.trim()}
|
||||
</div>
|
||||
))
|
||||
}
|
||||
|
||||
return data
|
||||
.split(/\r?\n/)
|
||||
.filter((line: string) => line.trim() !== '')
|
||||
.map((line: string, index: number) => (
|
||||
<div key={index} className="item-circle">
|
||||
{line.trim()}
|
||||
</div>
|
||||
))
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@ import parse from "html-react-parser"
|
||||
import { JobData } from "@/data/articles/Job";
|
||||
import RecruitForm from "./Form";
|
||||
|
||||
import '@/styles/static_page.css';
|
||||
import '@/styles/tuyen_dung.css';
|
||||
|
||||
export default function JobDetail({ slug }: any) {
|
||||
|
||||
|
||||
96
src/components/search/index.tsx
Normal file
96
src/components/search/index.tsx
Normal file
@@ -0,0 +1,96 @@
|
||||
'use client';
|
||||
import parse from "html-react-parser";
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import { productCategory } from "@/data/products/productCategory";
|
||||
import { productList } from "@/data/products/productList";
|
||||
import { usePagination } from "@/hooks/usePagination";
|
||||
import ButtonShowMore from "@/components/shared/ProductShowMore";
|
||||
import ProductFilter from "@/components/product/category/filter";
|
||||
import SortByCollection from "@/components/product/category/sort";
|
||||
import ProductItem from "@/components/shared/ProductItem";
|
||||
import { useMemo } from "react";
|
||||
|
||||
|
||||
export default function ProductSearch() {
|
||||
const searchParams = useSearchParams();
|
||||
const search_query = searchParams.get('q') || "";
|
||||
|
||||
const {
|
||||
sort_by_collection,
|
||||
} = productCategory.current_category;
|
||||
|
||||
const totalProduct = useMemo(() => {
|
||||
return productList.flatMap((item: any) => item.list);
|
||||
}, []);
|
||||
|
||||
const filterData = useMemo(() => {
|
||||
return totalProduct.filter((item: any) =>
|
||||
item.productName?.trim()
|
||||
.toLowerCase()
|
||||
.includes(search_query.trim().toLowerCase())
|
||||
);
|
||||
}, [totalProduct, search_query]);
|
||||
|
||||
const {
|
||||
currentData,
|
||||
hasMore,
|
||||
loadMore,
|
||||
total
|
||||
} = usePagination(filterData);
|
||||
|
||||
return (
|
||||
<div className="product-page container">
|
||||
<h1 className="text-[#004BA4] text-[32px] leading-10 mb-4 font-600">
|
||||
Tìm kiếm sản phẩm: "{search_query}"
|
||||
</h1>
|
||||
|
||||
{currentData.length == 0
|
||||
? parse(`
|
||||
<div class="text-center py-20">
|
||||
<p class="text-22 font-500">
|
||||
Rất tiếc, chúng tôi không tìm thấy kết quả của "${search_query}"
|
||||
</p>
|
||||
|
||||
<div class="text-left" style="border:solid 1px #ccc; max-width:500px; margin:auto; padding:20px;">
|
||||
<p class="text-center"><b>Để tìm được kết quả chính xác hơn, xin vui lòng</b></p>
|
||||
<ul>
|
||||
<li>Kiểm tra lại chính tả của từ khóa đã nhập</li>
|
||||
<li>Thử lại bằng từ khóa khác</li>
|
||||
<li>Thử lại bằng các từ khóa tổng quát hơn</li>
|
||||
<li>Thử lại bằng các từ khóa ngắn gọn hơn</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
`) : (
|
||||
<div className="product-page-content flex flex-wrap items-start gap-4 mb-5">
|
||||
<div className="col-left-group w-[264px] rounded-[16px] bg-white p-4 pb-6">
|
||||
<ProductFilter data={productCategory} />
|
||||
</div>
|
||||
|
||||
<div className="col-right-group w-[968px]">
|
||||
<div className="box-item rounded-[24px] bg-white px-6 pt-4 pb-8 mb-4">
|
||||
|
||||
<SortByCollection sort={sort_by_collection} total={total} />
|
||||
|
||||
<div className="product-holder grid grid-cols-4 gap-x-5 gap-y-8 mb-6">
|
||||
{currentData.map((item: any) => (
|
||||
<ProductItem key={item.id} item={item} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
{hasMore &&
|
||||
<ButtonShowMore
|
||||
onClick={loadMore}
|
||||
displayCount={currentData.length}
|
||||
total={total}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -63,9 +63,11 @@ export default function DealItem( {item} : any) {
|
||||
|
||||
<div className="deal-count">
|
||||
<i className="deal-line" style={{ width: saleRemainPercent + '%' }}></i>
|
||||
<span> Còn:
|
||||
{remain}/{item.quantity}
|
||||
sản phẩm
|
||||
<span>
|
||||
{ Number(item.sale_quantity) < Number(item.quantity)
|
||||
? `Còn ${remain}/${item.quantity} sản phẩm`
|
||||
: 'Hết DEAL'
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
43
src/components/shared/DesignerItem.tsx
Normal file
43
src/components/shared/DesignerItem.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import { formatPrice, renderSummary } from "@/lib/utils";
|
||||
|
||||
export default function DesignerItem({ item }: any) {
|
||||
return (
|
||||
<div className="p-item">
|
||||
<a href={item.productUrl} className="p-img">
|
||||
<img src={item.productImage.large}
|
||||
alt={item.productName}
|
||||
width={250}
|
||||
height={250}
|
||||
/>
|
||||
</a>
|
||||
|
||||
<div className="p-text">
|
||||
<a href={item.productUrl} className="p-name">
|
||||
<h3> {item.productName} </h3>
|
||||
</a>
|
||||
|
||||
<div className="p-price-group">
|
||||
<p className="p-price">
|
||||
{item.price > 0
|
||||
? formatPrice(item.price) + 'đ'
|
||||
: 'Liên hệ'
|
||||
}
|
||||
</p>
|
||||
|
||||
{Number(item.price_off) > 0 &&
|
||||
<>
|
||||
<del> {formatPrice(item.marketPrice)} đ </del>
|
||||
<span className="p-discount"> -{item.price_off}% </span>
|
||||
</>
|
||||
}
|
||||
</div>
|
||||
|
||||
{item.productSummary &&
|
||||
<div className="p-summary">
|
||||
{renderSummary(item.productSummary)}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
35
src/components/shared/ProductShowMore.tsx
Normal file
35
src/components/shared/ProductShowMore.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
interface ButtonShowMoreProps {
|
||||
onClick: () => void;
|
||||
label?: string;
|
||||
loading?: boolean;
|
||||
displayCount: number;
|
||||
total: number;
|
||||
}
|
||||
|
||||
export default function ButtonShowMore({
|
||||
onClick,
|
||||
label = "TẢI THÊM",
|
||||
loading = false,
|
||||
displayCount,
|
||||
total
|
||||
}: ButtonShowMoreProps) {
|
||||
|
||||
return (
|
||||
<div className="text-center mt-12">
|
||||
<button
|
||||
type="button"
|
||||
disabled={loading}
|
||||
className="mb-3 bg-btn text-white rounded-[30px] h-10 font-500 text-16 table max-w-[240px] w-full m-auto"
|
||||
aria-label="Xem thêm"
|
||||
onClick={onClick}
|
||||
>
|
||||
{loading ? "ĐANG TẢI..." : label}
|
||||
<i className="bx bx-chevron-down text-20 align-middle mt-[-3px]" />
|
||||
</button>
|
||||
|
||||
<p className="text-14 leading-[18px] m-0">
|
||||
Hiển thị {displayCount} trên tổng số {total} sản phẩm
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -14,13 +14,13 @@ export const dealList = [
|
||||
"quantity": "10",
|
||||
"min_purchase": "1",
|
||||
"max_purchase": "10",
|
||||
"to_time": "02-02-2026, 8:30 am",
|
||||
"to_time": "22-02-2026, 8:30 am",
|
||||
"is_featured": "0",
|
||||
"last_update": "1767576809",
|
||||
"sale_order": "0",
|
||||
"deal_time_happen": 796220,
|
||||
"deal_time_left": 1624780,
|
||||
"sale_quantity": "0",
|
||||
"sale_quantity": "10",
|
||||
"request_path": "\/deal\/342",
|
||||
"is_start": 1,
|
||||
"is_end": 0,
|
||||
@@ -79,7 +79,7 @@ export const dealList = [
|
||||
"quantity": "10",
|
||||
"min_purchase": "1",
|
||||
"max_purchase": "10",
|
||||
"to_time": "02-02-2026, 8:30 am",
|
||||
"to_time": "02-02-2027, 8:30 am",
|
||||
"is_featured": "0",
|
||||
"last_update": "1767576805",
|
||||
"sale_order": "0",
|
||||
@@ -144,7 +144,7 @@ export const dealList = [
|
||||
"quantity": "10",
|
||||
"min_purchase": "1",
|
||||
"max_purchase": "10",
|
||||
"to_time": "02-02-2026, 8:30 am",
|
||||
"to_time": "02-02-2027, 8:30 am",
|
||||
"is_featured": "0",
|
||||
"last_update": "1767576799",
|
||||
"sale_order": "0",
|
||||
@@ -180,7 +180,7 @@ export const dealList = [
|
||||
"quantity": "10",
|
||||
"min_purchase": "1",
|
||||
"max_purchase": "10",
|
||||
"to_time": "02-02-2026, 8:30 am",
|
||||
"to_time": "02-02-2027, 8:30 am",
|
||||
"is_featured": "0",
|
||||
"last_update": "1767576954",
|
||||
"sale_order": "0",
|
||||
@@ -216,7 +216,7 @@ export const dealList = [
|
||||
"quantity": "10",
|
||||
"min_purchase": "1",
|
||||
"max_purchase": "10",
|
||||
"to_time": "02-02-2026, 8:30 am",
|
||||
"to_time": "02-02-2027, 8:30 am",
|
||||
"is_featured": "0",
|
||||
"last_update": "1767576950",
|
||||
"sale_order": "0",
|
||||
@@ -252,7 +252,7 @@ export const dealList = [
|
||||
"quantity": "10",
|
||||
"min_purchase": "1",
|
||||
"max_purchase": "10",
|
||||
"to_time": "02-02-2026, 8:30 am",
|
||||
"to_time": "02-02-2027, 8:30 am",
|
||||
"is_featured": "0",
|
||||
"last_update": "1767576946",
|
||||
"sale_order": "0",
|
||||
@@ -288,7 +288,7 @@ export const dealList = [
|
||||
"quantity": "10",
|
||||
"min_purchase": "1",
|
||||
"max_purchase": "10",
|
||||
"to_time": "02-02-2026, 8:30 am",
|
||||
"to_time": "02-02-2027, 8:30 am",
|
||||
"is_featured": "0",
|
||||
"last_update": "1767577069",
|
||||
"sale_order": "0",
|
||||
@@ -324,7 +324,7 @@ export const dealList = [
|
||||
"quantity": "10",
|
||||
"min_purchase": "1",
|
||||
"max_purchase": "10",
|
||||
"to_time": "02-02-2026, 8:30 am",
|
||||
"to_time": "02-02-2027, 8:30 am",
|
||||
"is_featured": "0",
|
||||
"last_update": "1767576941",
|
||||
"sale_order": "0",
|
||||
@@ -360,7 +360,7 @@ export const dealList = [
|
||||
"quantity": "10",
|
||||
"min_purchase": "1",
|
||||
"max_purchase": "10",
|
||||
"to_time": "02-02-2026, 8:30 am",
|
||||
"to_time": "02-02-2027, 8:30 am",
|
||||
"is_featured": "0",
|
||||
"last_update": "1767576936",
|
||||
"sale_order": "0",
|
||||
@@ -396,7 +396,7 @@ export const dealList = [
|
||||
"quantity": "10",
|
||||
"min_purchase": "1",
|
||||
"max_purchase": "10",
|
||||
"to_time": "02-02-2026, 8:30 am",
|
||||
"to_time": "02-02-2027, 8:30 am",
|
||||
"is_featured": "0",
|
||||
"last_update": "1767576932",
|
||||
"sale_order": "0",
|
||||
@@ -442,7 +442,7 @@ export const dealList = [
|
||||
"quantity": "10",
|
||||
"min_purchase": "1",
|
||||
"max_purchase": "10",
|
||||
"to_time": "02-02-2026, 8:30 am",
|
||||
"to_time": "02-02-2027, 8:30 am",
|
||||
"is_featured": "0",
|
||||
"last_update": "1768017144",
|
||||
"sale_order": "0",
|
||||
@@ -507,7 +507,7 @@ export const dealList = [
|
||||
"quantity": "10",
|
||||
"min_purchase": "1",
|
||||
"max_purchase": "10",
|
||||
"to_time": "02-02-2026, 8:30 am",
|
||||
"to_time": "02-02-2027, 8:30 am",
|
||||
"is_featured": "0",
|
||||
"last_update": "1768017107",
|
||||
"sale_order": "0",
|
||||
@@ -543,7 +543,7 @@ export const dealList = [
|
||||
"quantity": "10",
|
||||
"min_purchase": "1",
|
||||
"max_purchase": "10",
|
||||
"to_time": "02-02-2026, 8:30 am",
|
||||
"to_time": "02-02-2027, 8:30 am",
|
||||
"is_featured": "0",
|
||||
"last_update": "1767853933",
|
||||
"sale_order": "1",
|
||||
@@ -579,7 +579,7 @@ export const dealList = [
|
||||
"quantity": "10",
|
||||
"min_purchase": "1",
|
||||
"max_purchase": "10",
|
||||
"to_time": "02-02-2026, 8:30 am",
|
||||
"to_time": "02-02-2027, 8:30 am",
|
||||
"is_featured": "0",
|
||||
"last_update": "1767585250",
|
||||
"sale_order": "2",
|
||||
@@ -615,7 +615,7 @@ export const dealList = [
|
||||
"quantity": "10",
|
||||
"min_purchase": "1",
|
||||
"max_purchase": "10",
|
||||
"to_time": "02-02-2026, 8:30 am",
|
||||
"to_time": "02-02-2027, 8:30 am",
|
||||
"is_featured": "0",
|
||||
"last_update": "1767576873",
|
||||
"sale_order": "0",
|
||||
@@ -651,7 +651,7 @@ export const dealList = [
|
||||
"quantity": "10",
|
||||
"min_purchase": "1",
|
||||
"max_purchase": "10",
|
||||
"to_time": "02-02-2026, 8:30 am",
|
||||
"to_time": "02-02-2027, 8:30 am",
|
||||
"is_featured": "0",
|
||||
"last_update": "1767576869",
|
||||
"sale_order": "0",
|
||||
@@ -687,7 +687,7 @@ export const dealList = [
|
||||
"quantity": "10",
|
||||
"min_purchase": "1",
|
||||
"max_purchase": "10",
|
||||
"to_time": "02-02-2026, 8:30 am",
|
||||
"to_time": "02-02-2027, 8:30 am",
|
||||
"is_featured": "0",
|
||||
"last_update": "1767576982",
|
||||
"sale_order": "0",
|
||||
@@ -723,7 +723,7 @@ export const dealList = [
|
||||
"quantity": "10",
|
||||
"min_purchase": "1",
|
||||
"max_purchase": "10",
|
||||
"to_time": "02-02-2026, 8:30 am",
|
||||
"to_time": "02-02-2027, 8:30 am",
|
||||
"is_featured": "0",
|
||||
"last_update": "1767576860",
|
||||
"sale_order": "0",
|
||||
@@ -759,7 +759,7 @@ export const dealList = [
|
||||
"quantity": "10",
|
||||
"min_purchase": "1",
|
||||
"max_purchase": "10",
|
||||
"to_time": "02-02-2026, 8:30 am",
|
||||
"to_time": "02-02-2027, 8:30 am",
|
||||
"is_featured": "0",
|
||||
"last_update": "1767576852",
|
||||
"sale_order": "0",
|
||||
@@ -795,7 +795,7 @@ export const dealList = [
|
||||
"quantity": "10",
|
||||
"min_purchase": "1",
|
||||
"max_purchase": "10",
|
||||
"to_time": "02-02-2026, 8:30 am",
|
||||
"to_time": "02-02-2027, 8:30 am",
|
||||
"is_featured": "0",
|
||||
"last_update": "1767576848",
|
||||
"sale_order": "0",
|
||||
|
||||
@@ -25,13 +25,13 @@ export const DesignerToolDetail = [
|
||||
"id": 5975,
|
||||
"productId": 5975,
|
||||
"marketPrice": 50000000,
|
||||
"price": 47740000,
|
||||
"price_off": "5",
|
||||
"price": 48230000,
|
||||
"price_off": "4",
|
||||
"warranty": `theo từng linh kiện`,
|
||||
"productName": `HHPC CORE i7 14700KF | 32GB DDR5 | NVIDIA RTX 5060 Ti 16G`,
|
||||
"productSummary": `<p>CPU : INTEL CORE i7 14700KF UP 5.6GHz | 20 CORES | 28 THREADS</p>
|
||||
"productSummary": `<p>CPU : INTEL CORE i7 14700KF UP 5.6GHz | 20 CORES | 28 THREADS</p>
|
||||
<p>RAM : DDR5 32GB 6000 MHz (2x16G)</p>
|
||||
<p>VGA: NVIDIA RTX 5060 Ti 16G GDDR7 - 2 FAN</p>`,
|
||||
<p>VGA: NVIDIA RTX 5060 Ti 16G GDDR7 - 2 FAN</p>`,
|
||||
"productUrl": `/pc-i7-14700kf-rtx-5060-ti-16g`,
|
||||
"productImage": {
|
||||
"small": "https://hoanghapccdn.com/media/product/75_6518_pc_anubis_astrobeat_kh__ng_logo_h2.jpg",
|
||||
@@ -39,15 +39,16 @@ export const DesignerToolDetail = [
|
||||
"original": "",
|
||||
},
|
||||
"sale_rules": {
|
||||
"price": 47740000,
|
||||
"normal_price": 47740000,
|
||||
"price": 48230000,
|
||||
"normal_price": 48230000,
|
||||
"min_purchase": 1,
|
||||
"max_purchase": 1,
|
||||
"remain_quantity": 1,
|
||||
"from_time": 0,
|
||||
"to_time": 0,
|
||||
"type": "component"
|
||||
}
|
||||
},
|
||||
"attribute": [25, 26]
|
||||
},
|
||||
|
||||
{
|
||||
@@ -58,9 +59,9 @@ export const DesignerToolDetail = [
|
||||
"price_off": "6",
|
||||
"warranty": `theo từng linh kiện`,
|
||||
"productName": `HHPC CORE i5 14600K | 32G | NVIDIA RTX 5060 Ti 16G`,
|
||||
"productSummary": `<p>CPU : INTEL CORE i5 14600K UP 5.3GHz | 14 CORES | 20 THREADS</p>
|
||||
<p>RAM : DDR4 32GB (2x16G) 3200 MHz</p>
|
||||
<p>VGA: NVIDIA RTX 5060 Ti 16G GDDR7 - 3 FAN</p>`,
|
||||
"productSummary": `<p>CPU : INTEL CORE i5 14600K UP 5.3GHz | 14 CORES | 20 THREADS</p>
|
||||
<p>RAM : DDR4 32GB (2x16G) 3200 MHz</p>
|
||||
<p>VGA: NVIDIA RTX 5060 Ti 16G GDDR7 - 3 FAN</p>`,
|
||||
"productUrl": `/hhpc-core-i5-14600k-32g-rtx-5060-ti-16g`,
|
||||
"productImage": {
|
||||
"small": "https://hoanghapccdn.com/media/product/75_5989_pc_gaming_x_ii_a620.jpg",
|
||||
@@ -76,7 +77,8 @@ export const DesignerToolDetail = [
|
||||
"from_time": 0,
|
||||
"to_time": 0,
|
||||
"type": "component"
|
||||
}
|
||||
},
|
||||
"attribute": [24, 26]
|
||||
},
|
||||
|
||||
{
|
||||
@@ -89,7 +91,7 @@ export const DesignerToolDetail = [
|
||||
"productName": `HHPC CORE i9 14900KF | 32G | RX 7800 XT 16G`,
|
||||
"productSummary": `<p>CPU : INTEL CORE i9 14900KF UP 6.0GHz | 24 CORES | 32 THREADS</p>
|
||||
<p>RAM : DDR4 32GB 3200 MHz (2x16G)</p>
|
||||
<p>VGA: RADEON RX 7800 XT 16G GDDR6</p>`,
|
||||
<p>VGA: RADEON RX 7800 XT 16G GDDR6</p>`,
|
||||
"productUrl": `/hhpc-core-i9-14900kf-32g-rx-7800-xt-16g`,
|
||||
"productImage": {
|
||||
"small": "https://hoanghapccdn.com/media/product/75_6011_pc_anubis_le360_k_logo_ha3.jpg",
|
||||
@@ -105,7 +107,8 @@ export const DesignerToolDetail = [
|
||||
"from_time": 0,
|
||||
"to_time": 0,
|
||||
"type": "component"
|
||||
}
|
||||
},
|
||||
"attribute": [24, 26]
|
||||
},
|
||||
|
||||
{
|
||||
@@ -116,9 +119,9 @@ export const DesignerToolDetail = [
|
||||
"price_off": "6",
|
||||
"warranty": `theo từng linh kiện`,
|
||||
"productName": `HHPC CORE i7 14700KF | 32GB | RX 7800 XT 16G`,
|
||||
"productSummary": `<p>CPU : INTEL CORE i7 14700KF up 5.6GHz | 20 CORE | 28 THREAD</p>
|
||||
<p>RAM : DDR4 32GB (2x16G) 3200 MHz</p>
|
||||
<p>VGA: RADEON RX 7800 XT 16G GDDR6</p>`,
|
||||
"productSummary": `<p>CPU : INTEL CORE i7 14700KF up 5.6GHz | 20 CORE | 28 THREAD</p>
|
||||
<p>RAM : DDR4 32GB (2x16G) 3200 MHz</p>
|
||||
<p>VGA: RADEON RX 7800 XT 16G GDDR6</p>`,
|
||||
"productUrl": `/pc-core-i7-14700kf-32gb-rx-7800-xt-16g`,
|
||||
"productImage": {
|
||||
"small": "https://hoanghapccdn.com/media/product/75_6012_pc_anubis_le360_k_logo_ha1.jpg",
|
||||
@@ -134,7 +137,8 @@ export const DesignerToolDetail = [
|
||||
"from_time": 0,
|
||||
"to_time": 0,
|
||||
"type": "component"
|
||||
}
|
||||
},
|
||||
"attribute": [24, 26]
|
||||
},
|
||||
|
||||
{
|
||||
@@ -145,9 +149,9 @@ export const DesignerToolDetail = [
|
||||
"price_off": "5",
|
||||
"warranty": `theo từng linh kiện`,
|
||||
"productName": `HHPC GAMING CORE i5 14600K | 16GB | NVIDIA RTX 5070 Ti 16G`,
|
||||
"productSummary": `<p>CPU : INTEL CORE i5 14600K UP 5.3GHz | 14 CORES | 20 THREADS</p>
|
||||
<p>RAM : DDR4 16GB (1x16G) 3200 MHz</p>
|
||||
<p>VGA : NVIDIA RTX 5070 Ti 16G GDDR7</p>`,
|
||||
"productSummary": `<p>CPU : INTEL CORE i5 14600K UP 5.3GHz | 14 CORES | 20 THREADS</p>
|
||||
<p>RAM : DDR4 16GB (1x16G) 3200 MHz</p>
|
||||
<p>VGA : NVIDIA RTX 5070 Ti 16G GDDR7</p>`,
|
||||
"productUrl": `/pc-gaming-core-i5-14600kf-16gb-nvidia-rtx-5070-ti`,
|
||||
"productImage": {
|
||||
"small": "https://hoanghapccdn.com/media/product/75_6045_pc_blast_m_ha1.jpg",
|
||||
@@ -163,7 +167,8 @@ export const DesignerToolDetail = [
|
||||
"from_time": 0,
|
||||
"to_time": 0,
|
||||
"type": "component"
|
||||
}
|
||||
},
|
||||
"attribute": [24, 26]
|
||||
},
|
||||
|
||||
{
|
||||
@@ -174,9 +179,9 @@ export const DesignerToolDetail = [
|
||||
"price_off": "6",
|
||||
"warranty": `theo từng linh kiện`,
|
||||
"productName": `HHPC GAMING CORE i5 14600K | 16GB | RX 7800 XT 16G`,
|
||||
"productSummary": `<p>CPU : INTEL CORE i5 14600K UP 5.3GHz | 14 CORES | 20 THREADS</p>
|
||||
<p>RAM : DDR4 16GB (1x16G) 3200 MHz</p>
|
||||
<p>VGA: RADEON RX 7800 XT 16G GDDR6</p>`,
|
||||
"productSummary": `<p>CPU : INTEL CORE i5 14600K UP 5.3GHz | 14 CORES | 20 THREADS</p>
|
||||
<p>RAM : DDR4 16GB (1x16G) 3200 MHz</p>
|
||||
<p>VGA: RADEON RX 7800 XT 16G GDDR6</p>`,
|
||||
"productUrl": `/hhpc-gaming-core-i5-14600k-16gb-rx-7800-xt`,
|
||||
"productImage": {
|
||||
"small": "https://hoanghapccdn.com/media/product/75_6046_pc_blast_m_ha1.jpg",
|
||||
@@ -192,7 +197,8 @@ export const DesignerToolDetail = [
|
||||
"from_time": 0,
|
||||
"to_time": 0,
|
||||
"type": "component"
|
||||
}
|
||||
},
|
||||
"attribute": [24, 27]
|
||||
},
|
||||
|
||||
{
|
||||
@@ -203,9 +209,9 @@ export const DesignerToolDetail = [
|
||||
"price_off": "3",
|
||||
"warranty": `theo từng linh kiện`,
|
||||
"productName": `HHPC CORE i5 14600K | 32G | NVIDIA RTX 5060 8G`,
|
||||
"productSummary": `<p>CPU : INTEL CORE i5 14600K UP 5.3GHz | 14 CORES | 20 THREADS</p>
|
||||
<p>RAM : DDR4 32GB (2x16G) 3200 MHz</p>
|
||||
<p>VGA: NVIDIA RTX 5060 8G GDDR7</p>`,
|
||||
"productSummary": `<p>CPU : INTEL CORE i5 14600K UP 5.3GHz | 14 CORES | 20 THREADS</p>
|
||||
<p>RAM : DDR4 32GB (2x16G) 3200 MHz</p>
|
||||
<p>VGA: NVIDIA RTX 5060 8G GDDR7</p>`,
|
||||
"productUrl": `/pc-core-i5-14600k-32g-rtx-5060-8g`,
|
||||
"productImage": {
|
||||
"small": "https://hoanghapccdn.com/media/product/75_6139_pc_gaming_x_ii_a620.jpg",
|
||||
@@ -221,7 +227,8 @@ export const DesignerToolDetail = [
|
||||
"from_time": 0,
|
||||
"to_time": 0,
|
||||
"type": "component"
|
||||
}
|
||||
},
|
||||
"attribute": [24, 27]
|
||||
},
|
||||
|
||||
{
|
||||
@@ -232,9 +239,9 @@ export const DesignerToolDetail = [
|
||||
"price_off": "",
|
||||
"warranty": `theo từng linh kiện`,
|
||||
"productName": `HHPC CORE i7 14700KF | 32GB | NVIDIA RTX 5060 8G`,
|
||||
"productSummary": `<p>CPU : INTEL CORE i7 14700KF up 5.6GHz | 20 CORE | 28 THREAD</p>
|
||||
<p>RAM : DDR4 32GB (2x16G) 3200 MHz</p>
|
||||
<p>VGA: NVIDIA RTX 5060 8G GDDR7</p>`,
|
||||
"productSummary": `<p>CPU : INTEL CORE i7 14700KF up 5.6GHz | 20 CORE | 28 THREAD</p>
|
||||
<p>RAM : DDR4 32GB (2x16G) 3200 MHz</p>
|
||||
<p>VGA: NVIDIA RTX 5060 8G GDDR7</p>`,
|
||||
"productUrl": `/pc-i7-14700-32g-rtx-5060-8g`,
|
||||
"productImage": {
|
||||
"small": "https://hoanghapccdn.com/media/product/75_6140_pc_anubis_lt720_ram_d4_14900k_ha6ss.jpg",
|
||||
@@ -250,7 +257,8 @@ export const DesignerToolDetail = [
|
||||
"from_time": 1767574800,
|
||||
"to_time": 1772415000,
|
||||
"type": "deal"
|
||||
}
|
||||
},
|
||||
"attribute": [24, 27]
|
||||
},
|
||||
|
||||
{
|
||||
@@ -261,9 +269,9 @@ export const DesignerToolDetail = [
|
||||
"price_off": "0",
|
||||
"warranty": `theo từng linh kiện`,
|
||||
"productName": `HHPC CORE i5 14600K | 16G | NVIDIA QUADRO P400 2GB`,
|
||||
"productSummary": `<p>CPU : INTEL CORE i5 14600K UP 5.3GHz | 14 CORES | 20 THREADS</p>
|
||||
<p>RAM : DDR4 16GB (1x16G) 3200 MHz</p>
|
||||
<p>VGA : NVIDIA QUADRO P400 2GB GDDR5</p>`,
|
||||
"productSummary": `<p>CPU : INTEL CORE i5 14600K UP 5.3GHz | 14 CORES | 20 THREADS</p>
|
||||
<p>RAM : DDR4 16GB (1x16G) 3200 MHz</p>
|
||||
<p>VGA : NVIDIA QUADRO P400 2GB GDDR5</p>`,
|
||||
"productUrl": `/pc-core-i5-14600k-16g-quadro-p400-2gb`,
|
||||
"productImage": {
|
||||
"small": "https://hoanghapccdn.com/media/product/75_6240_pc_gaming_x_ii_a620_ha1.jpg",
|
||||
@@ -279,7 +287,8 @@ export const DesignerToolDetail = [
|
||||
"from_time": 0,
|
||||
"to_time": 0,
|
||||
"type": "component"
|
||||
}
|
||||
},
|
||||
"attribute": [24, 27]
|
||||
},
|
||||
|
||||
{
|
||||
@@ -292,8 +301,8 @@ export const DesignerToolDetail = [
|
||||
"productName": `HHPC CORE i9 14900KF | 64G DDR5 | NVIDIA RTX 5090 32G`,
|
||||
"productSummary": `<p>CPU : INTEL CORE i9 14900KF UP 6.0GHz | 24 CORES | 32 THREADS</p>
|
||||
<p>RAM : DDR5 64GB 6000 MHz (2x32G)</p>
|
||||
<p>SSD: 1TB M.2 2280 PCIe 4.0 NVMe</p>
|
||||
<p>VGA: NVIDIA RTX 5090 32G GDDR7</p>`,
|
||||
<p>SSD: 1TB M.2 2280 PCIe 4.0 NVMe</p>
|
||||
<p>VGA: NVIDIA RTX 5090 32G GDDR7</p>`,
|
||||
"productUrl": `/pc-core-i9-14900kf-rtx-5090-32g`,
|
||||
"productImage": {
|
||||
"small": "https://hoanghapccdn.com/media/product/75_6095_pc_anubis_le360_k_logo_ha2.jpg",
|
||||
@@ -309,7 +318,8 @@ export const DesignerToolDetail = [
|
||||
"from_time": 0,
|
||||
"to_time": 0,
|
||||
"type": "component"
|
||||
}
|
||||
},
|
||||
"attribute": [24, 26]
|
||||
},
|
||||
|
||||
{
|
||||
@@ -320,9 +330,9 @@ export const DesignerToolDetail = [
|
||||
"price_off": "6",
|
||||
"warranty": `theo từng linh kiện`,
|
||||
"productName": `HHPC ULTRA 9 285 | 32GB DDR5 | NVIDIA RTX 5070 Ti 16G`,
|
||||
"productSummary": `<p>CPU : INTEL CORE ULTRA 9 285 UP 5.6GHz | 24 CORE | 24 THREAD</p>
|
||||
"productSummary": `<p>CPU : INTEL CORE ULTRA 9 285 UP 5.6GHz | 24 CORE | 24 THREAD</p>
|
||||
<p>RAM : DDR5 32GB 6000 MHz (2x16G)</p>
|
||||
<p>VGA: NVIDIA RTX 5070 Ti 16G GDDR7</p>`,
|
||||
<p>VGA: NVIDIA RTX 5070 Ti 16G GDDR7</p>`,
|
||||
"productUrl": `/pc-ultra-9-285-32gb-d5-rtx-5070-ti-16g`,
|
||||
"productImage": {
|
||||
"small": "https://hoanghapccdn.com/media/product/75_6127_pc_anubis_le360_k_logo_ha3.jpg",
|
||||
@@ -338,7 +348,8 @@ export const DesignerToolDetail = [
|
||||
"from_time": 0,
|
||||
"to_time": 0,
|
||||
"type": "component"
|
||||
}
|
||||
},
|
||||
"attribute": [24, 26]
|
||||
},
|
||||
|
||||
{
|
||||
@@ -351,7 +362,7 @@ export const DesignerToolDetail = [
|
||||
"productName": `HHPC CORE i9 14900KF | 64G DDR5 | NVIDIA RTX 5070 Ti 16G`,
|
||||
"productSummary": `<p>CPU : INTEL CORE i9 14900KF UP 6.0GHz | 24 CORES | 32 THREADS</p>
|
||||
<p>RAM : DDR5 64GB 6000 MHz (2x32G)</p>
|
||||
<p>VGA : NVIDIA RTX 5070 Ti 16G GDDR7</p>`,
|
||||
<p>VGA : NVIDIA RTX 5070 Ti 16G GDDR7</p>`,
|
||||
"productUrl": `/pc-core-i9-14900kf-rtx-5070-ti-16g`,
|
||||
"productImage": {
|
||||
"small": "https://hoanghapccdn.com/media/product/75_5997_pc_anubis_le360_k_logo_ha3.jpg",
|
||||
@@ -367,7 +378,8 @@ export const DesignerToolDetail = [
|
||||
"from_time": 0,
|
||||
"to_time": 0,
|
||||
"type": "component"
|
||||
}
|
||||
},
|
||||
"attribute": [24, 26]
|
||||
},
|
||||
|
||||
{
|
||||
@@ -378,10 +390,10 @@ export const DesignerToolDetail = [
|
||||
"price_off": "",
|
||||
"warranty": `theo từng linh kiện`,
|
||||
"productName": `HHPC ULTRA 9 285K | 32GB DDR5 | NVIDIA RTX 5070 Ti 16G`,
|
||||
"productSummary": `<p>CPU : INTEL CORE ULTRA 9 285K UP 5.7GHz | 24 CORES | 24 THREADS</p>
|
||||
"productSummary": `<p>CPU : INTEL CORE ULTRA 9 285K UP 5.7GHz | 24 CORES | 24 THREADS</p>
|
||||
<p>RAM : DDR5 32GB 6000 MHz (2x16G)</p>
|
||||
<p>SSD: 1TB M.2 2280 PCIe 4.0 NVMe</p>
|
||||
<p>VGA : NVIDIA RTX 5070 Ti 16G GDDR7</p>`,
|
||||
<p>SSD: 1TB M.2 2280 PCIe 4.0 NVMe</p>
|
||||
<p>VGA : NVIDIA RTX 5070 Ti 16G GDDR7</p>`,
|
||||
"productUrl": `/hhpc-ultra-9-285k-32gb-ddr5-nvidia-rtx-5070-ti-16g`,
|
||||
"productImage": {
|
||||
"small": "https://hoanghapccdn.com/media/product/75_5877_pc_view_290_tg_lc360_sale.jpg",
|
||||
@@ -397,7 +409,8 @@ export const DesignerToolDetail = [
|
||||
"from_time": 1767574800,
|
||||
"to_time": 1772415000,
|
||||
"type": "deal"
|
||||
}
|
||||
},
|
||||
"attribute": [24, 26]
|
||||
},
|
||||
|
||||
{
|
||||
@@ -408,9 +421,9 @@ export const DesignerToolDetail = [
|
||||
"price_off": "5",
|
||||
"warranty": `theo từng linh kiện`,
|
||||
"productName": `HHPC RYZEN 9 9950X | 32G DDR5 | NVIDIA RTX 5070 Ti 16G`,
|
||||
"productSummary": `<p>CPU : AMD RYZEN 9 9950X up to 5.7 GHz 16 CORE | 32 THREAD</p>
|
||||
"productSummary": `<p>CPU : AMD RYZEN 9 9950X up to 5.7 GHz 16 CORE | 32 THREAD</p>
|
||||
<p>RAM : DDR5 32GB 6000Mhz (2x16GB)</p>
|
||||
<p>VGA : NVIDIA RTX 5070 Ti 16G GDDR7</p>`,
|
||||
<p>VGA : NVIDIA RTX 5070 Ti 16G GDDR7</p>`,
|
||||
"productUrl": `/pc-ryzen-9-9950x-32g-d5-rtx-5070-ti-16g`,
|
||||
"productImage": {
|
||||
"small": "https://hoanghapccdn.com/media/product/75_6349_pc_view_290_tg_argb_ha3.jpg",
|
||||
@@ -426,7 +439,8 @@ export const DesignerToolDetail = [
|
||||
"from_time": 0,
|
||||
"to_time": 0,
|
||||
"type": "component"
|
||||
}
|
||||
},
|
||||
"attribute": [24, 26]
|
||||
},
|
||||
|
||||
{
|
||||
@@ -437,9 +451,9 @@ export const DesignerToolDetail = [
|
||||
"price_off": "3",
|
||||
"warranty": `theo từng linh kiện`,
|
||||
"productName": `HHPC ULTRA 7 265KF | 32GB DDR5 | NVIDIA RTX 5070 12G`,
|
||||
"productSummary": `<p>CPU : INTEL CORE ULTRA 7 265KF UP 5.5GHz | 20 CORES | 20 THREADS</p>
|
||||
"productSummary": `<p>CPU : INTEL CORE ULTRA 7 265KF UP 5.5GHz | 20 CORES | 20 THREADS</p>
|
||||
<p>RAM : DDR5 32GB 6000 MHz (2x16G)</p>
|
||||
<p>VGA : NVIDIA RTX 5070 12G GDDR7</p>`,
|
||||
<p>VGA : NVIDIA RTX 5070 12G GDDR7</p>`,
|
||||
"productUrl": `/hhpc-ultra-7-265kf-5070-12g`,
|
||||
"productImage": {
|
||||
"small": "https://hoanghapccdn.com/media/product/75_5866_pc_gaming_x_ii_a620_do_hoa.jpg",
|
||||
@@ -455,7 +469,8 @@ export const DesignerToolDetail = [
|
||||
"from_time": 0,
|
||||
"to_time": 0,
|
||||
"type": "component"
|
||||
}
|
||||
},
|
||||
"attribute": [24, 27]
|
||||
},
|
||||
|
||||
{
|
||||
@@ -466,10 +481,10 @@ export const DesignerToolDetail = [
|
||||
"price_off": "",
|
||||
"warranty": `theo từng linh kiện`,
|
||||
"productName": `HHPC ULTRA 9 285K | 32GB DDR5 | NVIDIA RTX 5070 12G`,
|
||||
"productSummary": `<p>CPU : INTEL CORE ULTRA 9 285K UP 5.7GHz | 24 CORE | 24 THREAD</p>
|
||||
"productSummary": `<p>CPU : INTEL CORE ULTRA 9 285K UP 5.7GHz | 24 CORE | 24 THREAD</p>
|
||||
<p>RAM : DDR5 32GB 6000 MHz (2x16G)</p>
|
||||
<p>SSD: 1TB M.2 2280 PCIe 4.0 NVMe</p>
|
||||
<p>VGA : NVIDIA RTX 5070 12G GDDR7</p>`,
|
||||
<p>SSD: 1TB M.2 2280 PCIe 4.0 NVMe</p>
|
||||
<p>VGA : NVIDIA RTX 5070 12G GDDR7</p>`,
|
||||
"productUrl": `/hhpc-ultra-9-285k-32gb-ddr5-nvidia-rtx-5070-12g`,
|
||||
"productImage": {
|
||||
"small": "https://hoanghapccdn.com/media/product/75_5867_pc_anubis_le360_d5_sale_2.jpg",
|
||||
@@ -485,7 +500,8 @@ export const DesignerToolDetail = [
|
||||
"from_time": 1767574800,
|
||||
"to_time": 1772415000,
|
||||
"type": "deal"
|
||||
}
|
||||
},
|
||||
"attribute": [24, 27]
|
||||
},
|
||||
|
||||
{
|
||||
@@ -496,9 +512,9 @@ export const DesignerToolDetail = [
|
||||
"price_off": "6",
|
||||
"warranty": `theo từng linh kiện`,
|
||||
"productName": `HHPC RYZEN 9 9950X | 32G DDR5 | RX 7800 XT 16G`,
|
||||
"productSummary": `<p>CPU : AMD RYZEN 9 9950X up to 5.7 GHz 16 CORE | 32 THREAD</p>
|
||||
"productSummary": `<p>CPU : AMD RYZEN 9 9950X up to 5.7 GHz 16 CORE | 32 THREAD</p>
|
||||
<p>RAM : DDR5 32GB 6000Mhz (2x16GB)</p>
|
||||
<p>VGA: RADEON RX 7800 XT 16G GDDR6</p>`,
|
||||
<p>VGA: RADEON RX 7800 XT 16G GDDR6</p>`,
|
||||
"productUrl": `/hhpc-ryzen-9-9950x-32g-ddr5-rx-7800-xt-16g`,
|
||||
"productImage": {
|
||||
"small": "https://hoanghapccdn.com/media/product/75_6007_pc_anubis_le360_k_logo_ha3.jpg",
|
||||
@@ -514,7 +530,8 @@ export const DesignerToolDetail = [
|
||||
"from_time": 0,
|
||||
"to_time": 0,
|
||||
"type": "component"
|
||||
}
|
||||
},
|
||||
"attribute": [24, 27]
|
||||
},
|
||||
|
||||
{
|
||||
@@ -525,10 +542,10 @@ export const DesignerToolDetail = [
|
||||
"price_off": "7",
|
||||
"warranty": `theo từng linh kiện`,
|
||||
"productName": `HHPC ULTRA 9 285K | 32GB DDR5 | RX 7800 XT 16G`,
|
||||
"productSummary": `<p>CPU : INTEL CORE ULTRA 9 285K UP 5.7GHz | 24 CORE | 24 THREAD</p>
|
||||
"productSummary": `<p>CPU : INTEL CORE ULTRA 9 285K UP 5.7GHz | 24 CORE | 24 THREAD</p>
|
||||
<p>RAM : DDR5 32GB 6000 MHz (2x16G)</p>
|
||||
<p>SSD: 1TB M.2 2280 PCIe 4.0 NVMe</p>
|
||||
<p>VGA: RADEON RX 7800 XT 16G GDDR6</p>`,
|
||||
<p>SSD: 1TB M.2 2280 PCIe 4.0 NVMe</p>
|
||||
<p>VGA: RADEON RX 7800 XT 16G GDDR6</p>`,
|
||||
"productUrl": `/hhpc-ultra-9-285k-32gb-ddr5-rx-7800-xt-16g`,
|
||||
"productImage": {
|
||||
"small": "https://hoanghapccdn.com/media/product/75_6009_pc_anubis_le360_k_logo_ha2.jpg",
|
||||
@@ -544,7 +561,8 @@ export const DesignerToolDetail = [
|
||||
"from_time": 0,
|
||||
"to_time": 0,
|
||||
"type": "component"
|
||||
}
|
||||
},
|
||||
"attribute": [24, 27]
|
||||
},
|
||||
|
||||
{
|
||||
@@ -555,9 +573,9 @@ export const DesignerToolDetail = [
|
||||
"price_off": "5",
|
||||
"warranty": `theo từng linh kiện`,
|
||||
"productName": `HHPC ULTRA 7 265KF | 32GB DDR5 | RX 7800 XT 16G`,
|
||||
"productSummary": `<p>CPU : INTEL CORE ULTRA 7 265KF UP 5.5GHz | 20 CORES | 20 THREADS</p>
|
||||
"productSummary": `<p>CPU : INTEL CORE ULTRA 7 265KF UP 5.5GHz | 20 CORES | 20 THREADS</p>
|
||||
<p>RAM : DDR5 32GB 6000 MHz (2x16G)</p>
|
||||
<p>VGA: RADEON RX 7800 XT 16G GDDR6</p>`,
|
||||
<p>VGA: RADEON RX 7800 XT 16G GDDR6</p>`,
|
||||
"productUrl": `/hhpc-ultra-7-265kf-32gb-d5-rx-7800-xt-16g`,
|
||||
"productImage": {
|
||||
"small": "https://hoanghapccdn.com/media/product/75_6010_pc_gaming_x_ii_a620.jpg",
|
||||
@@ -573,7 +591,8 @@ export const DesignerToolDetail = [
|
||||
"from_time": 0,
|
||||
"to_time": 0,
|
||||
"type": "component"
|
||||
}
|
||||
},
|
||||
"attribute": [24, 27]
|
||||
},
|
||||
|
||||
{
|
||||
@@ -584,9 +603,9 @@ export const DesignerToolDetail = [
|
||||
"price_off": "7",
|
||||
"warranty": `theo từng linh kiện`,
|
||||
"productName": `HHPC ULTRA 9 285 | 32GB DDR5 | NVIDIA RTX 5070 12G`,
|
||||
"productSummary": `<p>CPU : INTEL CORE ULTRA 9 285 UP 5.6GHz | 24 CORE | 24 THREAD</p>
|
||||
"productSummary": `<p>CPU : INTEL CORE ULTRA 9 285 UP 5.6GHz | 24 CORE | 24 THREAD</p>
|
||||
<p>RAM : DDR5 32GB 6000 MHz (2x16G)</p>
|
||||
<p>VGA: NVIDIA RTX 5070 12G GDDR7</p>`,
|
||||
<p>VGA: NVIDIA RTX 5070 12G GDDR7</p>`,
|
||||
"productUrl": `/hhpc-ultra-9-285-32gb-ddr5-nvidia-rtx-5070-12g`,
|
||||
"productImage": {
|
||||
"small": "https://hoanghapccdn.com/media/product/75_6126_pc_anubis_le360_k_logo_ha2.jpg",
|
||||
@@ -602,7 +621,8 @@ export const DesignerToolDetail = [
|
||||
"from_time": 0,
|
||||
"to_time": 0,
|
||||
"type": "component"
|
||||
}
|
||||
},
|
||||
"attribute": [24, 27]
|
||||
},
|
||||
|
||||
{
|
||||
@@ -613,9 +633,9 @@ export const DesignerToolDetail = [
|
||||
"price_off": "4",
|
||||
"warranty": `theo từng linh kiện`,
|
||||
"productName": `HHPC CORE i7 14700KF | 32GB DDR5 | NVIDIA RTX 5080 16G`,
|
||||
"productSummary": `<p>CPU : INTEL CORE i7 14700KF up 5.6GHz | 20 CORE | 28 THREAD</p>
|
||||
"productSummary": `<p>CPU : INTEL CORE i7 14700KF up 5.6GHz | 20 CORE | 28 THREAD</p>
|
||||
<p>RAM : DDR5 32GB 6000 MHz (2x16G)</p>
|
||||
<p>VGA: NVIDIA RTX 5080 16G GDDR7</p>`,
|
||||
<p>VGA: NVIDIA RTX 5080 16G GDDR7</p>`,
|
||||
"productUrl": `/pc-core-i7-14700kf-32gb-d5-rtx-5080-16g`,
|
||||
"productImage": {
|
||||
"small": "https://hoanghapccdn.com/media/product/75_6189_pc_anubis_le360_k_logo_ha3.jpg",
|
||||
@@ -631,7 +651,8 @@ export const DesignerToolDetail = [
|
||||
"from_time": 0,
|
||||
"to_time": 0,
|
||||
"type": "component"
|
||||
}
|
||||
},
|
||||
"attribute": [24, 27]
|
||||
},
|
||||
|
||||
],
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -9,8 +9,8 @@ export function useDealItem(item: DealItemProps) {
|
||||
const marketPrice = Number(productInfo.price);
|
||||
const discount = calculateDiscount(price, marketPrice);
|
||||
|
||||
const remain = item.quantity - item.sale_quantity;
|
||||
const saleRemainPercent = 100 - (item.sale_quantity / item.quantity) * 100;
|
||||
const remain = Number(item.quantity) - Number(item.sale_quantity);
|
||||
const saleRemainPercent = 100 - (Number(item.sale_quantity) / Number(item.quantity)) * 100;
|
||||
|
||||
const specialOffer = productInfo?.specialOffer?.all?.[0]?.title ?? '';
|
||||
|
||||
|
||||
35
src/hooks/usePagination.ts
Normal file
35
src/hooks/usePagination.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
'use client'
|
||||
|
||||
import { useState, useMemo, useEffect } from "react";
|
||||
|
||||
export function usePagination<T>(data: any, perPage: number = 30) {
|
||||
|
||||
const [page, setPage] = useState(1);
|
||||
|
||||
const total = data.length;
|
||||
const totalPage = Math.ceil(total / perPage);
|
||||
|
||||
useEffect(() => {
|
||||
setPage(1); // reset khi data đổi
|
||||
}, [data]);
|
||||
|
||||
const currentData = useMemo(() => {
|
||||
return data.slice(0, page * perPage);
|
||||
// nếu muốn paging thật thì đổi thành:
|
||||
// const start = (page - 1) * perPage;
|
||||
// return data.slice(start, start + perPage);
|
||||
}, [data, page, perPage]);
|
||||
|
||||
const hasMore = page < totalPage;
|
||||
|
||||
const loadMore = () => setPage(prev => prev + 1);
|
||||
|
||||
return {
|
||||
currentData,
|
||||
hasMore,
|
||||
loadMore,
|
||||
page,
|
||||
total,
|
||||
totalPage
|
||||
};
|
||||
}
|
||||
@@ -1,19 +1,36 @@
|
||||
'use client';
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useState, useMemo } from "react";
|
||||
|
||||
export function DealCountdown({ endTime }: { endTime: any }) {
|
||||
|
||||
const formatTime = useMemo(() => {
|
||||
if (!endTime) return 0;
|
||||
|
||||
if (typeof endTime === "string") {
|
||||
const parsed = new Date(endTime).getTime();
|
||||
return isNaN(parsed) ? 0 : Math.floor(parsed / 1000);
|
||||
}
|
||||
|
||||
// Nếu là milliseconds (13 số)
|
||||
if (endTime > 9999999999) {
|
||||
return Math.floor(endTime / 1000);
|
||||
}
|
||||
|
||||
return endTime;
|
||||
}, [endTime]);
|
||||
|
||||
export function DealCountdown({ endTime }: { endTime: number }) {
|
||||
const [mounted, setMounted] = useState(false);
|
||||
const [timeLeft, setTimeLeft] = useState(getTimeLeft(endTime));
|
||||
const [timeLeft, setTimeLeft] = useState(getTimeLeft(formatTime));
|
||||
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
|
||||
const timer = setInterval(() => {
|
||||
setTimeLeft(getTimeLeft(endTime));
|
||||
setTimeLeft(getTimeLeft(formatTime));
|
||||
}, 1000);
|
||||
|
||||
return () => clearInterval(timer);
|
||||
}, [endTime]);
|
||||
}, [formatTime]);
|
||||
|
||||
if (!mounted) return null;
|
||||
|
||||
@@ -37,20 +54,20 @@ export function getTimeLeft(endTime: number) {
|
||||
|
||||
if (distance <= 0) {
|
||||
return {
|
||||
total : 0,
|
||||
days : '00',
|
||||
hours : '00',
|
||||
minutes : '00',
|
||||
seconds : '00'
|
||||
total: 0,
|
||||
days: '00',
|
||||
hours: '00',
|
||||
minutes: '00',
|
||||
seconds: '00'
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
total : distance,
|
||||
days : String(Math.floor(distance / 86400)).padStart(2, '0'),
|
||||
hours : String(Math.floor((distance % 86400) / 3600)).padStart(2, '0'),
|
||||
minutes : String(Math.floor((distance % 3600) / 60)).padStart(2, '0'),
|
||||
seconds : String(distance % 60).padStart(2, '0'),
|
||||
total: distance,
|
||||
days: String(Math.floor(distance / 86400)).padStart(2, '0'),
|
||||
hours: String(Math.floor((distance % 86400) / 3600)).padStart(2, '0'),
|
||||
minutes: String(Math.floor((distance % 3600) / 60)).padStart(2, '0'),
|
||||
seconds: String(distance % 60).padStart(2, '0'),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,37 @@ export function formatTextList(
|
||||
.join('');
|
||||
}
|
||||
|
||||
export function renderSummary(data: any) {
|
||||
if (!data) return null;
|
||||
|
||||
// Nếu là HTML string
|
||||
if (typeof data === 'string' && data.includes('<')) {
|
||||
|
||||
// Bỏ toàn bộ tag HTML
|
||||
const textOnly = data.replace(/<[^>]*>/g, '\n');
|
||||
|
||||
return textOnly
|
||||
.split(/\r?\n/)
|
||||
.filter((line: string) => line.trim() !== '')
|
||||
.map((line: string, index: number) => (
|
||||
<div key={index} className="item-circle">
|
||||
{line.trim()}
|
||||
</div>
|
||||
));
|
||||
}
|
||||
|
||||
// Nếu là text thường
|
||||
return data
|
||||
.split(/\r?\n/)
|
||||
.filter((line: string) => line.trim() !== '')
|
||||
.map((line: string, index: number) => (
|
||||
<div key={index} className="item-circle">
|
||||
{line.trim()}
|
||||
</div>
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
// Format giá
|
||||
export function formatPrice(amount: number) {
|
||||
return amount.toLocaleString('vi-VN');
|
||||
@@ -114,3 +145,4 @@ export function convertToSlug(text: string) {
|
||||
.trim()
|
||||
.replace(/ +/g, "-");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user