update 2/2/2026

This commit is contained in:
2026-02-02 16:42:40 +07:00
parent 12509e3a7b
commit 6318621cc0
25 changed files with 1568 additions and 570 deletions

View File

@@ -41,7 +41,7 @@ export default function Banner( {id}:any ) {
alt={item.name}
width={100}
height={100}
className="block w-full lazy rounded-[24px]"
className="block w-full rounded-[24px]"
unoptimized
/>
</Link>

View File

@@ -1,138 +0,0 @@
export default function Article() {
return (
<div className="pd-box-group bg-white mb-6 px-4 py-6 rounded-[24px]">
<p className="text-20 font-600 mb-4"> Tin tức mới nhất </p>
{/* limit: 5 */}
<div className="pd-article-holder flex flex-col gap-4">
<div className="art-item">
<a href="" className="art-img">
<img
src="https://hoanghapccdn.com/media/news/14_100__c___u_h__nh_m__y_t__nh_______h___a_theo_ng__n_s__ch.jpg"
alt=""
width={1}
height={1}
/>
</a>
<div className="art-text">
<a href="" className="art-title">
<h3>
Lorem, ipsum dolor sit amet consectetur adipisicing elit. Eum
quidem asperiores provident dicta veniam deleniti eaque
repudiandae cum esse, ducimus officiis quibusdam pariatur
neque voluptates voluptas. Quisquam qui minus dolorum?
</h3>
</a>
<div className="art-summary">
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Velit,
obcaecati ducimus veritatis aliquid sunt accusamus unde nisi
nostrum fugit facere illo quos. Ad error suscipit, quidem optio
aut laudantium at!
</div>
<div className="art-time">
<i className="bx bx-calendar-alt" />
<time>23/4/2024</time>
<i className="w-[1.5px] h-[12px] bg-[#A0A5AC] mx-1" />
<span>Mai Văn Học</span>
</div>
</div>
</div>
<div className="art-item">
<a href="" className="art-img">
<img
src="https://hoanghapccdn.com/media/news/14_100__c___u_h__nh_m__y_t__nh_______h___a_theo_ng__n_s__ch.jpg"
alt=""
width={1}
height={1}
/>
</a>
<div className="art-text">
<a href="" className="art-title">
<h3>
Lorem, ipsum dolor sit amet consectetur adipisicing elit. Eum
quidem asperiores provident dicta veniam deleniti eaque
repudiandae cum esse, ducimus officiis quibusdam pariatur
neque voluptates voluptas. Quisquam qui minus dolorum?
</h3>
</a>
<div className="art-summary">
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Velit,
obcaecati ducimus veritatis aliquid sunt accusamus unde nisi
nostrum fugit facere illo quos. Ad error suscipit, quidem optio
aut laudantium at!
</div>
<div className="art-time">
<i className="bx bx-calendar-alt text-16 text-[#A0A5AC]" />
<time>23/4/2024</time>
<i className="w-[1.5px] h-[12px] bg-[#A0A5AC]" />
<span>Mai Văn Học</span>
</div>
</div>
</div>
<div className="art-item">
<a href="" className="art-img">
<img
src="https://hoanghapccdn.com/media/news/14_100__c___u_h__nh_m__y_t__nh_______h___a_theo_ng__n_s__ch.jpg"
alt=""
width={1}
height={1}
/>
</a>
<div className="art-text">
<a href="" className="art-title">
<h3>
Lorem, ipsum dolor sit amet consectetur adipisicing elit. Eum
quidem asperiores provident dicta veniam deleniti eaque
repudiandae cum esse, ducimus officiis quibusdam pariatur
neque voluptates voluptas. Quisquam qui minus dolorum?
</h3>
</a>
<div className="art-summary">
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Velit,
obcaecati ducimus veritatis aliquid sunt accusamus unde nisi
nostrum fugit facere illo quos. Ad error suscipit, quidem optio
aut laudantium at!
</div>
<div className="art-time">
<i className="bx bx-calendar-alt text-16 text-[#A0A5AC]" />
<time>23/4/2024</time>
<i className="w-[1.5px] h-[12px] bg-[#A0A5AC]" />
<span>Mai Văn Học</span>
</div>
</div>
</div>
<div className="art-item">
<a href="" className="art-img">
<img
src="https://hoanghapccdn.com/media/news/14_100__c___u_h__nh_m__y_t__nh_______h___a_theo_ng__n_s__ch.jpg"
alt=""
width={1}
height={1}
/>
</a>
<div className="art-text">
<a href="" className="art-title">
<h3>
Lorem, ipsum dolor sit amet consectetur adipisicing elit. Eum
quidem asperiores provident dicta veniam deleniti eaque
repudiandae cum esse, ducimus officiis quibusdam pariatur
neque voluptates voluptas. Quisquam qui minus dolorum?
</h3>
</a>
<div className="art-summary">
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Velit,
obcaecati ducimus veritatis aliquid sunt accusamus unde nisi
nostrum fugit facere illo quos. Ad error suscipit, quidem optio
aut laudantium at!
</div>
<div className="art-time">
<i className="bx bx-calendar-alt text-16 text-[#A0A5AC]" />
<time>23/4/2024</time>
<i className="w-[1.5px] h-[12px] bg-[#A0A5AC]" />
<span>Mai Văn Học</span>
</div>
</div>
</div>
</div>
</div>
)
}

View File

@@ -0,0 +1,17 @@
import ArticleItem from "@/components/shared/ArticleItem"
export default function Article( {item} : any ) {
return (
<div className="pd-box-group bg-white mb-6 px-4 py-6 rounded-[24px]">
<p className="text-20 font-600 mb-4"> Tin tức mới nhất </p>
<div className="pd-article-holder flex flex-col gap-4">
{
item.slice(0,5).map((item:any) =>
<ArticleItem key={item.id} item={item} />
)
}
</div>
</div>
)
}

View File

@@ -1,161 +0,0 @@
export default function Comment() {
return (
<div>
<div className="flex items-center justify-between leading-8 gap-2 mb-4">
<p className="m-0 text-18 font-500"> 0 Bình luận </p>
<div className="flex flex-wrap gap-2 text-14 font-500 pd-comment-btn">
<button
className="h-8 border border-[#D1D5DB] rounded-[40px] flex items-center gap-[3px] px-8 hover:border-[#0678DB] hover:text-[#0678DB] current"
type="button"
aria-label="Đánh giá"
>
{" "}
Tất cả{" "}
</button>
<button
className="h-8 border border-[#D1D5DB] rounded-[40px] flex items-center gap-[3px] px-4 hover:border-[#0678DB] hover:text-[#0678DB]"
type="button"
aria-label="Đánh giá"
>
{" "}
5 <i className="bxr bx-star" />{" "}
</button>
<button
className="h-8 border border-[#D1D5DB] rounded-[40px] flex items-center gap-[3px] px-4 hover:border-[#0678DB] hover:text-[#0678DB]"
type="button"
aria-label="Đánh giá"
>
{" "}
4 <i className="bxr bx-star" />{" "}
</button>
<button
className="h-8 border border-[#D1D5DB] rounded-[40px] flex items-center gap-[3px] px-4 hover:border-[#0678DB] hover:text-[#0678DB]"
type="button"
aria-label="Đánh giá"
>
{" "}
3 <i className="bxr bx-star" />{" "}
</button>
<button
className="h-8 border border-[#D1D5DB] rounded-[40px] flex items-center gap-[3px] px-4 hover:border-[#0678DB] hover:text-[#0678DB]"
type="button"
aria-label="Đánh giá"
>
{" "}
2 <i className="bxr bx-star" />{" "}
</button>
<button
className="h-8 border border-[#D1D5DB] rounded-[40px] flex items-center gap-[3px] px-4 hover:border-[#0678DB] hover:text-[#0678DB]"
type="button"
aria-label="Đánh giá"
>
{" "}
1 <i className="bxr bx-star" />{" "}
</button>
</div>
</div>
<div className="border border-[#DDDDDD] rounded-[12px] overflow-hidden ">
<textarea
className="p-3 w-full resize-none h-[96px] outline-none border-none"
defaultValue={""}
/>
<div className="border-t border-[#DDDDDD] bg-[#F5F6F7] p-[10px_12px] text-right">
<button
className="bg-btn text-white h-10 px-9 text-18 font-500 rounded-[30px]"
type="button"
aria-label="submit"
>
{" "}
GỬI{" "}
</button>
</div>
</div>
<div id="">
<div className="first:border-t first:mt-4 first:pt-4 border-[#D1D5DB] mb-5 flex gap-3 text-14 leading-[18px]">
<div className="w-10 h-10 rounded-full bg-[#9CA3AF] leading-10 text-center uppercase text-white font-600 overflow-hidden">
<span>p</span>
{/* <img src="images/avatar-admin.png" class="block w-full h-full"/> */}
</div>
<div className="w-[calc(100%_-_52px)]">
<div className="flex items-center gap-2 mb-1">
<b className="capitalize"> tên khách hàng </b>
<i className="bxr bxs-radio-circle text-[7px] text-[#6B7280]" />
<span className="text-[#6B7280]"> 11-11-2025, 11:11 </span>
</div>
<i className="star star-2" />
<div className="my-2">
Lorem ipsum dolor, sit amet consectetur adipisicing elit.
Fugiat magnam ipsam pariatur mollitia ratione distinctio magni
corrupti ad expedita. Natus, ullam inventore. Amet
consequuntur aspernatur deserunt accusantium, tempore
blanditiis magni!
</div>
<div className="flex gap-2 leading-[30px]">
<button
className="group flex items-center gap-[6px] border border-[#D1D5DB] px-3 rounded-[20px] hover:border-[#0678DB] hover:text-[#0678DB]"
type="button"
aria-label="actions"
>
{" "}
<i className="group-hover:text-[#0678DB] text-[#928FA8] bxr bx-heart" />{" "}
0{" "}
</button>
<button
className="group flex items-center gap-[6px] border border-[#D1D5DB] px-3 rounded-[20px] hover:border-[#0678DB] hover:text-[#0678DB]"
type="button"
aria-label="actions"
>
{" "}
<i className="group-hover:text-[#0678DB] text-[#928FA8] bxr bx-reply-stroke" />{" "}
Trả lời{" "}
</button>
</div>
<div className="bg-[#F3F4F6] rounded-[12px] overflow-hidden mt-3">
<div className="first:border-0 flex items-start gap-3 p-3 border-t border-[#D1D5DB]">
<div className="w-10 h-10 rounded-full bg-[#9CA3AF] leading-10 text-center uppercase text-white font-600 overflow-hidden">
{/* <span>p</span> */}
<img
src="images/avatar-admin.png"
className="block w-full h-full"
/>
</div>
<div className="w-[calc(100%_-_52px)]">
<div className="flex items-center gap-2 mb-1">
<b className="capitalize"> tên khách hàng </b>
<span className="bg-[linear-gradient(70.1deg,#75798B_62.94%,#ADB5CD_100%)] text-white px-[6px] leading-[18px] rounded-[20px] font-500 text-10">
{" "}
Quản trị viên{" "}
</span>
<i className="bxr bxs-radio-circle text-[7px] text-[#6B7280]" />
<span className="text-[#6B7280]">
{" "}
11-11-2025, 11:11{" "}
</span>
</div>
<div className="my-2" style={{ whiteSpace: "pre-line" }}>
Lorem ipsum dolor, sit amet consectetur adipisicing
elit. Fugiat magnam ipsam pariatur mollitia ratione
distinctio magni corrupti ad expedita. Natus, ullam
inventore. Amet consequuntur aspernatur deserunt
accusantium, tempore blanditiis magni!{" "}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{/* xem thêm */}
<div className="text-center mt-4">
<button
type="button"
className="border border-[#0678DB] text-[#0678DB] rounded-[30px] h-10 px-6 hover:bg-[#0678DB] hover:text-white"
aria-label="Xem thêm"
>
XEM THÊM{" "}
<i className="bx bx-chevron-down text-20 align-middle mt-[-3px]" />
</button>
</div>
</div>
)
}

View File

@@ -0,0 +1,36 @@
'use client';
import { useState } from "react";
import CommentItem from "@/components/shared/CommentItem"
const COMMENT_PER_PAGE = 5;
export default function CommentList( {item}:any ) {
const total = item?.length;
const [page, setPage] = useState(1);
const displayCount = page * COMMENT_PER_PAGE;
const hasMore = displayCount < total;
const commentData = item.slice(0, displayCount);
return (
<>
<div>
{
commentData.map( (comment:any) => <CommentItem item={comment} key={comment.id} />)
}
</div>
{hasMore &&
<div className="text-center mt-4">
<button type="button" aria-label="Xem thêm"
className="border border-[#0678DB] text-[#0678DB] rounded-[30px] h-10 px-6 hover:bg-[#0678DB] hover:text-white"
onClick={()=> setPage(prev => prev+1) }
>
XEM THÊM
<i className="bx bx-chevron-down text-20 align-middle mt-[-3px]" />
</button>
</div>
}
</>
)
}

View File

@@ -0,0 +1,16 @@
export default function Form() {
return (
<>
<textarea
className="p-3 w-full resize-none h-[96px] outline-none border-none"
defaultValue={""}
/>
<div className="border-t border-[#DDDDDD] bg-[#F5F6F7] p-[10px_12px] text-right">
<button type="button" aria-label="submit"
className="bg-btn text-white h-10 px-9 text-18 font-500 rounded-[30px]"
> GỬI </button>
</div>
</>
)
}

View File

@@ -0,0 +1,40 @@
import { CommentData } from "@/data/comments";
import Form from "./Form";
import CommentList from "./CommentList";
export default function Comment() {
return (
<div>
<div className="flex items-center justify-between leading-8 gap-2 mb-4">
<p className="m-0 text-18 font-500"> {CommentData.list.length} Bình luận </p>
<div className="flex flex-wrap gap-2 text-14 font-500 pd-comment-btn">
<button type="button" aria-label="Đánh giá"
className="h-8 border border-[#D1D5DB] rounded-[40px] flex items-center gap-[3px] px-8 hover:border-[#0678DB] hover:text-[#0678DB] current"
> Tất cả </button>
{buildButtonFilter()}
</div>
</div>
<div className="border border-[#DDDDDD] rounded-[12px] overflow-hidden js-comment-form">
<Form />
</div>
{CommentData.list.length > 0 &&
<CommentList item={CommentData.list}/>
}
</div>
)
}
function buildButtonFilter(){
const star = [5,4,3,2,1]
return star.map(item => (
<button type="button" aria-label="Đánh giá" key={item}
className="h-8 border border-[#D1D5DB] rounded-[40px] flex items-center gap-[3px] px-4 hover:border-[#0678DB] hover:text-[#0678DB]"
> {item} <i className="bxr bx-star" /> </button>
))
}

View File

@@ -1,38 +1,34 @@
import { formatPrice } from "@/lib/utils";
import ProductImage from "./image";
import ProductImage from "./images";
import Static from "./static";
import ProductDescription from "./description"
import Comment from "./comment";
import Review from "./review";
import Comment from "./comments";
import Review from "./reviews";
import ProductSpec from "./specifications";
import Article from "./article";
import Article from "./articles";
import ProductPrice from "./price";
import ProductOffer from "./offer";
import Buttons from "./button";
import Buttons from "./buttons";
import ProductSummary from "./summary";
export default async function ProductDetail({ slug }: any) {
const {
productName,
productId,
review,
visit,
quantity,
productSummary,
productImage, imageCollection,
price, marketPrice, deal_list, price_off, sale_rules,
hasVAT, warranty,
specialOffer,
productDescription,
productSpec
} = slug
import { ReviewData } from "@/data/reviews";
const image = {
productImage, imageCollection
export default async function ProductDetail({ slug }: any) {
const imageList = {
productImage : slug.productImage,
imageCollection : slug.imageCollection
}
const priceData = {
price, marketPrice, deal_list, price_off, sale_rules, hasVAT, warranty, quantity
price : slug.price,
marketPrice : slug.marketPrice,
deal_list : slug.deal_list,
price_off : slug.price_off,
sale_rules : slug.sale_rules,
hasVAT : slug.hasVAT,
warranty : slug.warranty,
quantity : slug.quantity
}
console.log(slug)
@@ -42,27 +38,27 @@ export default async function ProductDetail({ slug }: any) {
<div className="product-detail-page container">
<div className="pd-info-container static bg-white rounded-[24px] p-6 mb-6">
<h1 className="leading-8 text-[#004BA4] text-24 mb-6 font-600">
{productName}
{slug.productName}
</h1>
<div className="gap-6 flex flex-wrap items-start leading-[18px]">
<div className="col-left-group w-[424px] sticky top-[90px]">
<ProductImage data={image} />
<ProductImage data={imageList} />
</div>
<div className="col-middle-group w-[464px]">
<div className="pb-3 mb-3 border-b border-[#DEE4EC] flex flex-wrap items-center gap-2">
<button type="button" className="m-0 flex items-center gap-1">
<i className={`star star-${review.rate}`} />
<span className="font-500"> ({review.total}) </span>
<i className={`star star-${ReviewData.summary.avgRate}`} />
<span className="font-500"> ({ReviewData.summary.total}) </span>
</button>
<i className="w-[1px] h-4 bg-[#DEE4EC]" />
<p className="m-0">
Lượt xem:
<span className="text-[#004BA4] font-500">{formatPrice(visit)}</span>
<span className="text-[#004BA4] font-500">{formatPrice(slug.visit)}</span>
</p>
<i className="w-[1px] h-4 bg-[#DEE4EC]" />
@@ -71,7 +67,7 @@ export default async function ProductDetail({ slug }: any) {
Tình trạng:
<span
dangerouslySetInnerHTML={{
__html: quantity > 0
__html: slug.quantity > 0
? '<span class="font-500 text-[#00AD4F]">Còn hàng</span>'
: '<span class="font-500 red">Liên hệ</span>'
}}
@@ -79,15 +75,15 @@ export default async function ProductDetail({ slug }: any) {
</p>
</div>
{ productSummary &&
<ProductSummary item={productSummary} />
{ slug.productSummary &&
<ProductSummary item={slug.productSummary} />
}
<ProductPrice item={priceData} />
<ProductOffer item={specialOffer}/>
<ProductOffer item={slug.specialOffer}/>
<Buttons item={productId} />
<Buttons item={slug.productId} />
<p className="m-0 flex items-center gap-3 text-16 leading-[22px]">
<i className="icons icon-truck-2 !w-6" />
@@ -103,23 +99,32 @@ export default async function ProductDetail({ slug }: any) {
<div className="pd-content-container flex flex-wrap items-baseline gap-6">
<div className="col-left w-[784px]">
{ productDescription &&
<ProductDescription name={productName} description={productDescription} />
}
{ slug.productDescription &&
<ProductDescription
name={slug.productName}
description={slug.productDescription}
/>
}
<div className="pd-comment-container bg-white mb-6 p-8 pt-6 rounded-[24px] text-16 leading-[22px]">
<Review />
<p className="leading-[31px] font-600 text-24 mb-4 pb-4">
Đánh giá bình luận
</p>
<Review item={ReviewData}/>
<Comment />
</div>
</div>
<div className="col-right w-[440px]">
{productSpec &&
<ProductSpec item={productSpec} />
{slug.productSpec &&
<ProductSpec item={slug.productSpec} />
}
<Article />
{ slug.related['article-article'].length > 0 &&
<Article item={ slug.related['article-article'] } />
}
</div>
</div>

View File

@@ -1,227 +0,0 @@
export default function Review() {
return (
<>
<p className="leading-[31px] font-600 text-24 mb-4 pb-4">
{" "}
Đánh giá bình luận{" "}
</p>
{/* Rating */}
<div className="pd-rating-conatiner mb-9" id="js-pd-rating">
<div className="flex flex-wrap justify-between gap-6">
<div className="w-[200px] text-center">
<p className="font-600 text-[40px] leading-[48px] mb-2"> 0 </p>
<p className="my-2 text-[#6B7280]"> 0 lượt đánh giá </p>
<i className="star star-3" />
<button
className="rating-btn block h-10 w-full text-white text-14 font-500 rounded-[30px] bg-btn uppercase mt-3"
type="button"
aria-label="đánh giá"
/>
</div>
<div className="w-[calc(100%_-_224px)] text-14 font-500 leading-[18px] flex flex-col gap-4">
<div className="flex items-center justify-between gap-2 flex-wrap">
<p className="m-0 flex gap-[3px] w-[30px]">
{" "}
<span>5</span>{" "}
<i className="bx bxs-star text-[#FBBF24] text-16" />{" "}
</p>
<div className="relative bg-[#E8ECF6] overflow-hidden rounded-[30px] h-3 w-[calc(100%_-_71px)]">
<i
className="max-w-[100%] bg-[#0678DB] absolute inset-0"
style={{ width: "0%" }}
/>
</div>
<p className="m-0 text-[#6B7280] w-[25px] text-right"> 0 </p>
</div>
<div className="flex items-center justify-between gap-2 flex-wrap">
<p className="m-0 flex gap-[3px] w-[30px]">
<span>4</span>{" "}
<i className="bx bxs-star text-[#FBBF24] text-16" />
</p>
<div className="relative bg-[#E8ECF6] overflow-hidden rounded-[30px] h-3 w-[calc(100%_-_71px)]">
<i
className="max-w-[100%] bg-[#0678DB] absolute inset-0"
style={{ width: "0%" }}
/>
</div>
<p className="m-0 text-[#6B7280] w-[25px] text-right"> 0 </p>
</div>
<div className="flex items-center justify-between gap-2 flex-wrap">
<p className="m-0 flex gap-[3px] w-[30px]">
<span>3</span>{" "}
<i className="bx bxs-star text-[#FBBF24] text-16" />
</p>
<div className="relative bg-[#E8ECF6] overflow-hidden rounded-[30px] h-3 w-[calc(100%_-_71px)]">
<i
className="max-w-[100%] bg-[#0678DB] absolute inset-0"
style={{ width: "0%" }}
/>
</div>
<p className="m-0 text-[#6B7280] w-[25px] text-right"> 0 </p>
</div>
<div className="flex items-center justify-between gap-2 flex-wrap">
<p className="m-0 flex gap-[3px] w-[30px]">
<span>2</span>{" "}
<i className="bx bxs-star text-[#FBBF24] text-16" />
</p>
<div className="relative bg-[#E8ECF6] overflow-hidden rounded-[30px] h-3 w-[calc(100%_-_71px)]">
<i
className="max-w-[100%] bg-[#0678DB] absolute inset-0"
style={{ width: "0%" }}
/>
</div>
<p className="m-0 text-[#6B7280] w-[25px] text-right"> 0 </p>
</div>
<div className="flex items-center justify-between gap-2 flex-wrap">
<p className="m-0 flex gap-[3px] w-[30px]">
<span>1</span>{" "}
<i className="bx bxs-star text-[#FBBF24] text-16" />
</p>
<div className="relative bg-[#E8ECF6] overflow-hidden rounded-[30px] h-3 w-[calc(100%_-_71px)]">
<i
className="max-w-[100%] bg-[#0678DB] absolute inset-0"
style={{ width: "0%" }}
/>
</div>
<p className="m-0 text-[#6B7280] w-[25px] text-right"> 0 </p>
</div>
</div>
</div>
<div className="pd-rating-form mt-8 hidden">
<div className="flex items-center mb-4 gap-4">
<p className="m-0"> Chọn đánh giá của bạn </p>
<div className="rating-comment clearfix">
<input
type="radio"
className="rating-input"
id="rating-input-review-0-5"
defaultValue={5}
data-title="Quá tuyệt vời"
name="user_post[rate]"
defaultChecked={true}
/>
<label
htmlFor="rating-input-review-0-5"
className="rating-star js-rating-star"
data-title="Quá tuyệt vời"
/>
<input
type="radio"
className="rating-input"
id="rating-input-review-0-4"
defaultValue={4}
data-title="Rất tốt"
name="user_post[rate]"
/>
<label
htmlFor="rating-input-review-0-4"
className="rating-star js-rating-star"
data-title="Rất tốt"
/>
<input
type="radio"
className="rating-input"
id="rating-input-review-0-3"
defaultValue={3}
data-title="Bình thường"
name="user_post[rate]"
/>
<label
htmlFor="rating-input-review-0-3"
className="rating-star js-rating-star"
data-title="Bình thường"
/>
<input
type="radio"
className="rating-input"
id="rating-input-review-0-2"
defaultValue={2}
data-title="Tạm được"
name="user_post[rate]"
/>
<label
htmlFor="rating-input-review-0-2"
className="rating-star js-rating-star"
data-title="Tạm được"
/>
<input
type="radio"
className="rating-input"
id="rating-input-review-0-1"
defaultValue={1}
data-title="Không thích"
name="user_post[rate]"
/>
<label
htmlFor="rating-input-review-0-1"
className="rating-star js-rating-star"
data-title="Không thích"
/>
</div>
<span
id="js-star-tip"
className="star-tip bg-[#2b8ae0] text-white rounded-[3px] relative px-2 leading-[26px]"
>
{" "}
Quá tuyệt vời{" "}
</span>
</div>
<div className="lg:grid grid-cols-2 gap-3">
<textarea
className="w-full block p-3 resize-none h-[100px] outline-none border border-[#DDDDDD] rounded-[12px]"
placeholder="Nhập đánh giá của bạn"
defaultValue={""}
/>
<div className="grid lg:grid-cols-2 gap-2">
<input
type="text"
className="border border-[#DDDDDD] rounded-[8px] px-3"
placeholder="Họ tên*"
/>
<input
type="tel"
className="border border-[#DDDDDD] rounded-[8px] px-3"
inputMode="numeric"
pattern="[0-9]{10,11}"
maxLength={11}
placeholder="Số điện thoại*"
/>
<input
type="text"
className="border border-[#DDDDDD] rounded-[8px] px-3"
placeholder="Email*"
/>
<button
type="button"
className="bg-btn text-white rounded-[8px]"
aria-label="Đánh giá"
>
{" "}
Gửi đánh giá{" "}
</button>
</div>
<p className="red font-600"> </p>
</div>
</div>
<div className="text-14 leading-[18px] mt-4" id="">
<div className="last:border-0 border-b border-[#DDDDDD] py-5">
<div className="flex items-center gap-2 mb-2">
<b className="font-600 capitalize"> tên khách hàng </b>
<i className="bxr bxs-radio-circle text-[7px] text-[#6B7280]" />
<span className="text-[#6B7280]"> 11-11-2025, 11:11 </span>
</div>
<div className="flex flex-wrap gap-3">
<i className="star star-3 scale-[0.8] ml-[-7px]" />
<div className="w-[calc(100%-98px)]">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Vel
illum deserunt similique cumque accusantium qui assumenda
quod. Saepe illum beatae aspernatur odit, voluptatum voluptate
maiores dolore expedita similique officia consequuntur?
</div>
</div>
</div>
</div>
</div>
</>
)
}

View File

@@ -0,0 +1,101 @@
'use client';
import { useState, Fragment } from 'react';
export default function ReviewForm() {
const [rate, setRate] = useState(5);
const [rateTitle, setRateTitle] = useState('Quá tuyệt vời');
return (
<>
<div className="flex items-center mb-4 gap-4">
<p className="m-0"> Chọn đánh giá của bạn </p>
<div className="rating-comment clearfix">
<CreateStar
rate={rate}
setRate={setRate}
setRateTitle={setRateTitle}
/>
</div>
<span className="star-tip bg-[#2b8ae0] text-white rounded-[3px] px-2 leading-[26px]">
{rateTitle}
</span>
</div>
<div className="lg:grid grid-cols-2 gap-3">
<textarea
className="w-full block p-3 resize-none h-[100px] outline-none border border-[#DDDDDD] rounded-[12px]"
placeholder="Nhập đánh giá của bạn"
defaultValue={""}
/>
<div className="grid lg:grid-cols-2 gap-2">
<input
type="text"
className="border border-[#DDDDDD] rounded-[8px] px-3"
placeholder="Họ tên*"
/>
<input
type="tel"
className="border border-[#DDDDDD] rounded-[8px] px-3"
inputMode="numeric"
pattern="[0-9]{10,11}"
maxLength={11}
placeholder="Số điện thoại*"
/>
<input
type="text"
className="border border-[#DDDDDD] rounded-[8px] px-3"
placeholder="Email*"
/>
<button
type="button"
className="bg-btn text-white rounded-[8px]"
aria-label="Đánh giá"
>
Gửi đánh giá
</button>
</div>
<p className="red font-600"> </p>
</div>
</>
);
}
function CreateStar({ rate, setRate, setRateTitle }) {
const stars = [
{ rate: 5, title: 'Quá tuyệt vời' },
{ rate: 4, title: 'Rất tốt' },
{ rate: 3, title: 'Bình thường' },
{ rate: 2, title: 'Tạm được' },
{ rate: 1, title: 'Không thích' },
];
return (
<>
{stars.map(({ rate: star, title }) => (
<Fragment key={star}>
<input
type="radio"
className="rating-input"
name="user_post[rate]"
id={`rating-${star}`}
value={star}
checked={rate === star}
onChange={() => setRate(star)}
/>
<label
htmlFor={`rating-${star}`}
title={title}
className="rating-star js-rating-star"
onMouseEnter={() => setRateTitle(title)}
onClick={() => setRateTitle(title)}
/>
</Fragment>
))}
</>
);
}

View File

@@ -0,0 +1,38 @@
'use client';
import { useState } from "react";
import ReviewItem from "@/components/shared/ReviewItem";
const REVIEW_PER_PAGE = 5;
export default function ReviewList({ item }: any) {
const total = item?.length;
const [page, setPage] = useState(1);
const displayCount = page * REVIEW_PER_PAGE;
const hasMore = displayCount < total;
const reviewData = item.slice(0, displayCount);
return (
<>
{reviewData &&
reviewData.map((item:any) =>
<ReviewItem key={item.id} item={item} />
)
}
{ hasMore &&
<div className="text-center mt-4">
<button
type="button"
className="border border-[#0678DB] text-[#0678DB] rounded-[30px] h-10 px-6 hover:bg-[#0678DB] hover:text-white"
aria-label="Xem thêm"
onClick={() => setPage(prev => prev + 1)}
>
XEM THÊM
<i className="bx bx-chevron-down text-20 align-middle mt-[-3px]" />
</button>
</div>
}
</>
)
}

View File

@@ -0,0 +1,71 @@
export default function StarPercent({ item }: any) {
const listRate = item?.list_rate || [];
const { count, percent } = getStarPercent(listRate);
return (
<>
{[5, 4, 3, 2, 1].map((star) => {
const starRating = star as StarRating;
return (
<div key={star}
className="flex items-center justify-between gap-2 flex-wrap">
<p className="m-0 flex gap-[3px] w-[30px]">
<span>{star}</span>
<i className="bx bxs-star text-[#FBBF24] text-16" />
</p>
<div className="relative bg-[#E8ECF6] overflow-hidden rounded-[30px] h-3 w-[calc(100%_-_71px)]">
<i
className="max-w-[100%] bg-[#0678DB] absolute inset-0"
style={{ width: `${percent[starRating]}%` }}
/>
</div>
<p className="m-0 text-[#6B7280] w-[25px] text-right"> {count[starRating]} </p>
</div>
);
})}
</>
)
}
type StarRating = 1 | 2 | 3 | 4 | 5;
function getStarPercent(list_rate: any[] = []) {
const result: Record<StarRating, number> = {
1: 0,
2: 0,
3: 0,
4: 0,
5: 0,
};
let totalCount = 0;
list_rate.forEach(item => {
totalCount += item.total;
result[item.rate as StarRating] = item.total;
});
const percent: Record<StarRating, number> = {
1: 0,
2: 0,
3: 0,
4: 0,
5: 0,
};
if (totalCount > 0) {
Object.keys(result).forEach(key => {
percent[key as unknown as StarRating] = Math.round(
(result[key as unknown as StarRating] / totalCount) * 100
);
});
}
return {
count: result,
percent,
totalCount
};
}

View File

@@ -0,0 +1,45 @@
'use client';
import { useState } from "react";
import StarPercent from "./StarPercent";
import ReviewForm from "./Form"
import ReviewList from "./ReviewList";
export default function Review( {item} : any ) {
const [ show, setShow ] = useState(false);
return (
<>
<div className={`pd-rating-conatiner mb-9 ${show ? 'active' : ''}`} id="js-pd-rating">
<div className="flex flex-wrap justify-between gap-6">
<div className="w-[200px] text-center">
<p className="font-600 text-[40px] leading-[48px] mb-2"> {item.summary.avgRate}.0 </p>
<p className="my-2 text-[#6B7280]"> {item.summary.total} lượt đánh giá </p>
<i className={`star star-${item.summary.avgRate}`} />
<button
className="rating-btn block h-10 w-full text-white text-14 font-500 rounded-[30px] bg-btn uppercase mt-3"
type="button"
aria-label="đánh giá"
onClick={ () => setShow(show => !show) }
/>
</div>
<div className="w-[calc(100%_-_224px)] text-14 font-500 leading-[18px] flex flex-col gap-4">
<StarPercent item={item.summary} />
</div>
</div>
<div className="pd-rating-form mt-8 hidden">
<ReviewForm />
</div>
{item.list.length > 0 &&
<div className="text-14 leading-[18px] mt-4">
<ReviewList item={item.list}/>
</div>
}
</div>
</>
)
}

View File

@@ -1,7 +1,7 @@
import parse from 'html-react-parser';
export default function ProductSpec( {item} : any ) {
console.log(item)
return (
<div className="pd-box-group bg-white mb-6 px-4 py-6 rounded-[24px]">
<p className="group-title border-b border-[#D0D8E3] leading-[31px] font-600 text-24 mb-4 pb-4">
@@ -11,14 +11,15 @@ export default function ProductSpec( {item} : any ) {
<div className="pd-spec-group">
{parse(item)}
</div>
<a
href="#fancybox-spec"
data-fancybox=""
className="table m-auto mt-4 text-white leading-10 uppercase rounded-[40px] bg-btn font-500 text-16 px-6"
>
Xem tất cả thông số
</a>
<div
className="pd-spec-group p-3"
id="fancybox-spec"

View File

@@ -0,0 +1,78 @@
'use client';
import { useState } from 'react';
import { formatDate } from "@/lib/utils";
import parse from 'html-react-parser';
import CommentReply from "@/components/shared/CommentReply";
import Form from "../product/detail/comments/Form";
export default function CommentItem({ item }: any) {
const [activeFormId, setActiveFormId] = useState<number | null>(null);
const handleReply = () => {
setActiveFormId(prev =>
prev === item.id ? null : item.id
);
};
console.log('aaaaaaaaaa', item)
return (
<>
{ item.is_user_admin == 1 || item.approved == 1 &&
<div className="first:border-t first:mt-4 first:pt-4 border-[#D1D5DB] mb-5 flex gap-3 text-14 leading-[18px]">
<div className="w-10 h-10 rounded-full bg-[#9CA3AF] leading-10 text-center uppercase text-white font-600 overflow-hidden">
{ item.is_user_admin == 1
? parse(`<img src="images/avatar-admin.png" className="block w-full h-full"/>`)
: parse(`<span> ${item.user_name.substring(0,1)} </span>`)
}
</div>
<div className="w-[calc(100%_-_52px)]">
<div className="flex items-center gap-2 mb-1">
<b className="capitalize"> {item.user_name.split(' -')[0]} </b>
<i className="bxr bxs-radio-circle text-[7px] text-[#6B7280]" />
<span className="text-[#6B7280]"> {formatDate(item.post_time)} </span>
</div>
<i className={`star star-${item.rate}`} />
<div className="my-2"> {item.content} </div>
<div className="flex gap-2 leading-[30px]">
<button type="button" aria-label="actions"
className="group flex items-center gap-[6px] border border-[#D1D5DB] px-3 rounded-[20px] hover:border-[#0678DB] hover:text-[#0678DB]"
>
<i className="group-hover:text-[#0678DB] text-[#928FA8] bxr bx-heart" />
{item.people_like_count}
</button>
<button type="button" aria-label="actions"
className="group flex items-center gap-[6px] border border-[#D1D5DB] px-3 rounded-[20px] hover:border-[#0678DB] hover:text-[#0678DB]"
onClick={handleReply}
>
<i className="group-hover:text-[#0678DB] text-[#928FA8] bxr bx-reply-stroke" />
Trả lời
</button>
</div>
{activeFormId === item.id && (
<div className="my-2 border border-[#DDDDDD] rounded-[12px] overflow-hidden js-comment-form" id={`js-comment-form-${item.id}`}>
<Form />
</div>
)}
{ item.new_replies.length > 0 &&
<div className="bg-[#F3F4F6] rounded-[12px] overflow-hidden mt-3">
{
item.new_replies.map( (reply:any) =>
<CommentReply item={reply} key={reply.id} />)
}
</div>
}
</div>
</div>
}
</>
)
}

View File

@@ -0,0 +1,28 @@
import { formatDate } from "@/lib/utils";
import parse from 'html-react-parser';
export default function CommentReply({ item }: any) {
return (
<div className="first:border-0 flex items-start gap-3 p-3 border-t border-[#D1D5DB]">
<div className="w-10 h-10 rounded-full bg-[#9CA3AF] leading-10 text-center uppercase text-white font-600 overflow-hidden">
{ item.is_user_admin == 1
? parse(`<img src="images/avatar-admin.png" className="block w-full h-full"/>`)
: parse(`<span> ${item.user_name.substring(0,1)} </span>`)
}
</div>
<div className="w-[calc(100%_-_52px)]">
<div className="flex items-center gap-2 mb-1">
{ item.is_user_admin == 1
? parse(`<span className="bg-[linear-gradient(70.1deg,#75798B_62.94%,#ADB5CD_100%)] text-white px-[6px] leading-[18px] rounded-[20px] font-500 text-10">Quản trị viên</span>`)
: parse(`<b className="capitalize"> ${item.user_name.split(' -')[0]} </b>`)
}
<i className="bxr bxs-radio-circle text-[7px] text-[#6B7280]" />
<span className="text-[#6B7280]"> {formatDate(item.post_time)} </span>
</div>
<div className="my-2" style={{ whiteSpace: "pre-line" }}> {item.content} </div>
</div>
</div>
)
}

View File

@@ -0,0 +1,21 @@
import { formatDate } from "@/lib/utils";
export default function ReviewItem({ item }: any) {
return (
<div className="last:border-0 border-b border-[#DDDDDD] py-5">
<div className="flex items-center gap-2 mb-2">
<b className="font-600 capitalize">
{item.user_name.split(' -')[0]}
</b>
<i className="bxr bxs-radio-circle text-[7px] text-[#6B7280]" />
<span className="text-[#6B7280]"> {formatDate(item.post_time)} </span>
</div>
<div className="flex flex-wrap gap-3">
<i className={`star star-${item.rate} scale-[0.8] ml-[-7px]`} />
<div className="w-[calc(100%-98px)]"> {item.content} </div>
</div>
</div>
)
}

219
src/data/comments/index.tsx Normal file
View File

@@ -0,0 +1,219 @@
export const CommentData = {
"summary": {
"list_rate": [],
"avgRate": 0,
"total": 0,
},
"list": [
{
"id": "10187",
"item_type": "product",
"item_id": "5631",
"people_like_count": "2",
"people_dislike_count": "0",
"reply_count": "0",
"is_user_admin": "0",
"user_avatar": "",
"user_name": "Test - S\u0110T: 0988776521",
"rate": "5",
"title": "VGA ASUS ROG ASTRAL LC GEFORCE RTX 5090 32GB GDDR7 OC EDITION (512-bit, HDMI+DP, 1x16-pin)",
"content": "sp t\u1ed1t ....................................",
"files": [],
"approved": "1",
"post_time": "1767776160",
"counter": 1,
"new_replies": [
{
"id": "10186",
"item_type": "product",
"item_id": "2106",
"people_like_count": "0",
"people_dislike_count": "0",
"reply_count": "0",
"is_user_admin": "1",
"user_avatar": "",
"user_name": "Admin",
"rate": "5",
"title": "\u1ed4 c\u1ee9ng SSD Samsung PM9A1 512GB M.2 NVMe PCIe Gen 4 x4 (\u0110\u1ecdc 6900MB\/s - Ghi 5000MB\/s)",
"content": "SSD Samsung PM9A1 512GB c\u00f2n kh\u00f4ng \u1ea1, cho em xin gi\u00e1 v\u1edbi \u1ea1.",
"files": [],
"approved": "0",
"post_time": "1767666181",
"counter": 2,
"new_replies": []
},
{
"id": "10185",
"item_type": "product",
"item_id": "6669",
"people_like_count": "0",
"people_dislike_count": "0",
"reply_count": "0",
"is_user_admin": "0",
"user_avatar": "",
"user_name": "VU THANH CHUNG - S\u0110T: 0975064835",
"rate": "5",
"title": "HHPC CORE i7 14700KF | 64GB | NVIDIA RTX 5070 Ti 16G",
"content": "Qu\u00e1 l\u00e0 r\u1ebb",
"files": [],
"approved": "0",
"post_time": "1766984906",
"counter": 3,
"new_replies": []
}
]
},
{
"id": "10186",
"item_type": "product",
"item_id": "2106",
"people_like_count": "0",
"people_dislike_count": "0",
"reply_count": "0",
"is_user_admin": "1",
"user_avatar": "",
"user_name": "Quang Phuc",
"rate": "5",
"title": "\u1ed4 c\u1ee9ng SSD Samsung PM9A1 512GB M.2 NVMe PCIe Gen 4 x4 (\u0110\u1ecdc 6900MB\/s - Ghi 5000MB\/s)",
"content": "SSD Samsung PM9A1 512GB c\u00f2n kh\u00f4ng \u1ea1, cho em xin gi\u00e1 v\u1edbi \u1ea1.",
"files": [],
"approved": "1",
"post_time": "1767666181",
"counter": 2,
"new_replies": []
},
{
"id": "10185",
"item_type": "product",
"item_id": "6669",
"people_like_count": "0",
"people_dislike_count": "0",
"reply_count": "0",
"is_user_admin": "0",
"user_avatar": "",
"user_name": "VU THANH CHUNG - S\u0110T: 0975064835",
"rate": "5",
"title": "HHPC CORE i7 14700KF | 64GB | NVIDIA RTX 5070 Ti 16G",
"content": "Qu\u00e1 l\u00e0 r\u1ebb",
"files": [],
"approved": "1",
"post_time": "1766984906",
"counter": 3,
"new_replies": []
},
{
"id": "10184",
"item_type": "product",
"item_id": "5053",
"people_like_count": "0",
"people_dislike_count": "0",
"reply_count": "0",
"is_user_admin": "0",
"user_avatar": "",
"user_name": "L\u00ea Tr\u01b0\u01a1ng - S\u0110T: 0865660958",
"rate": "5",
"title": "HH SERVER - DUAL XEON 2696 V4 | 128GB | NVIDIA RTX 3060 12G",
"content": "Video To Future mong mu\u1ed1n h\u1ee3p t\u00e1c v\u1edbi qu\u00fd doanhh nghi\u1ec7p \u0111\u1ec3 ph\u00e2n ph\u1ed1i ph\u1ea7n m\u1ec1m g\u1eedi nh\u1eefng kho\u1ea3nh kh\u1eafc \u0111\u1eb9p v\u00e0 y\u00eau th\u01b0\u01a1ng \u0111\u1ebfn t\u01b0\u01a1ng lai. M\u00ecnh mu\u1ed1n xin s\u1ed1 fone c\u1ee7a ph\u00f2ng kinh doanh \u0111\u1ec3 g\u1eb7p tr\u1ef1c ti\u1ebfp trao \u0111\u1ed5i. Tr\u00e2n tr\u1ecdng c\u1ea3m \u01a1n!\n",
"files": [],
"approved": "1",
"post_time": "1765177283",
"counter": 4,
"new_replies": []
},
{
"id": "10183",
"item_type": "product",
"item_id": "6500",
"people_like_count": "0",
"people_dislike_count": "0",
"reply_count": "0",
"is_user_admin": "0",
"user_avatar": "",
"user_name": "Phan Phu - S\u0110T: 0816550160",
"rate": "5",
"title": "HH SERVER - DUAL XEON E5 2676 V3 | 64G | NVIDIA GT 730 2GB - H\u00e0ng l\u01b0\u1edbt",
"content": "B\u1ed9 n\u00e0y b\u00e1n nguy\u00ean v\u1eady hay c\u00f3 \u0111\u1ed5i case kh\u00e1c \u0111\u01b0\u1ee3c kh\u00f4ng \u1ea1. \u1ede c\u1ea7n th\u01a1 giao h\u00e0ng m\u1ea5y ng\u00e0y v\u1eady b\u1ea1n",
"files": [],
"approved": "1",
"post_time": "1764308881",
"counter": 5,
"new_replies": []
},
{
"id": "10182",
"item_type": "product",
"item_id": "917",
"people_like_count": "0",
"people_dislike_count": "0",
"reply_count": "0",
"is_user_admin": "0",
"user_avatar": "",
"user_name": "L\u00ea Th\u01b0\u1edbng - S\u0110T: 0349711051",
"rate": "5",
"title": "B\u1ed9 Ph\u00e1t Wifi ASUS RT-AC68U (Chu\u1ea9n Doanh Nghi\u1ec7p)",
"content": "Wifi r\u1ea5t kh\u1ecfe, ph\u00e1t xa, 40 ng\u01b0\u1eddi d\u00f9ng c\u00f9ng l\u00fac v\u1eabn ok.",
"files": [],
"approved": "0",
"post_time": "1763457018",
"counter": 6,
"new_replies": []
},
{
"id": "8768",
"item_type": "product",
"item_id": "6669",
"people_like_count": "0",
"people_dislike_count": "0",
"reply_count": "0",
"is_user_admin": "0",
"user_avatar": "",
"user_name": "VU THANH CHUNG - S\u0110T: 0988358888",
"rate": "5",
"title": "HHPC CORE i7 14700KF | 64GB | NVIDIA RTX 5070 Ti 16G",
"content": "Qu\u00e1 l\u00e0 r\u1ebb lu\u00f4n ",
"files": [],
"approved": "0",
"post_time": "1766984975",
"counter": 1,
"new_replies": []
},
{
"id": "8767",
"item_type": "product",
"item_id": "5981",
"people_like_count": "0",
"people_dislike_count": "0",
"reply_count": "0",
"is_user_admin": "0",
"user_avatar": "",
"user_name": "NMinh - S\u1ed1 \u0111t : 0367357631",
"rate": "5",
"title": "\u0110\u00e1nh gi\u00e1 s\u1ea3n ph\u1ea9m VGA SAPPHIRE PULSE AMD RADEON RX 7800 XT 16GB GDDR6 (256-bit, HDMI+DP, 2x 8-pin)",
"content": "Ch\u1ea1y \u00eam",
"files": [],
"approved": "1",
"post_time": "1761374084",
"counter": 2,
"new_replies": []
},
{
"id": "8762",
"item_type": "product",
"item_id": "6434",
"people_like_count": "0",
"people_dislike_count": "0",
"reply_count": "0",
"is_user_admin": "0",
"user_avatar": "",
"user_name": "Hurasoft \u0110\u1ee9c - S\u0110T: 0987876213",
"rate": "4",
"title": "HuraSoft - S\u1ea3n ph\u1ea9m test (Kh\u00f4ng x\u00f3a)",
"content": "account test rating",
"files": [],
"approved": "1",
"post_time": "1761027763",
"counter": 3,
"new_replies": []
}
]
}

File diff suppressed because one or more lines are too long

416
src/data/reviews/index.tsx Normal file
View File

@@ -0,0 +1,416 @@
export const ReviewData = {
"summary": {
"list_rate": [
{
"rate": 4,
"total": 1
},
{
"rate": 2,
"total": 1
},
{
"rate": 3,
"total": 1
}
],
"avgRate": 3,
"total": 20,
},
"list": [
{
"id": "8768",
"item_type": "product",
"item_id": "6669",
"people_like_count": "0",
"people_dislike_count": "0",
"reply_count": "0",
"is_user_admin": "0",
"user_avatar": "",
"user_name": "VU THANH CHUNG - S\u0110T: 0988358888",
"rate": "5",
"title": "HHPC CORE i7 14700KF | 64GB | NVIDIA RTX 5070 Ti 16G",
"content": "Qu\u00e1 l\u00e0 r\u1ebb lu\u00f4n ",
"files": [],
"approved": "0",
"post_time": "1766984975",
"counter": 1,
"new_replies": []
},
{
"id": "8767",
"item_type": "product",
"item_id": "5981",
"people_like_count": "0",
"people_dislike_count": "0",
"reply_count": "0",
"is_user_admin": "0",
"user_avatar": "",
"user_name": "NMinh - S\u1ed1 \u0111t : 0367357631",
"rate": "5",
"title": "\u0110\u00e1nh gi\u00e1 s\u1ea3n ph\u1ea9m VGA SAPPHIRE PULSE AMD RADEON RX 7800 XT 16GB GDDR6 (256-bit, HDMI+DP, 2x 8-pin)",
"content": "Ch\u1ea1y \u00eam",
"files": [],
"approved": "0",
"post_time": "1761374084",
"counter": 2,
"new_replies": []
},
{
"id": "8762",
"item_type": "product",
"item_id": "6434",
"people_like_count": "0",
"people_dislike_count": "0",
"reply_count": "0",
"is_user_admin": "0",
"user_avatar": "",
"user_name": "Hurasoft \u0110\u1ee9c - S\u0110T: 0987876213",
"rate": "4",
"title": "HuraSoft - S\u1ea3n ph\u1ea9m test (Kh\u00f4ng x\u00f3a)",
"content": "account test rating",
"files": [],
"approved": "1",
"post_time": "1761027763",
"counter": 3,
"new_replies": []
},
{
"id": "8761",
"item_type": "product",
"item_id": "6434",
"people_like_count": "0",
"people_dislike_count": "0",
"reply_count": "0",
"is_user_admin": "1",
"user_avatar": "",
"user_name": "Hurasoft \u0110\u1ee9c",
"rate": "3",
"title": "HuraSoft - S\u1ea3n ph\u1ea9m test (Kh\u00f4ng x\u00f3a)",
"content": "test form",
"files": [],
"approved": "1",
"post_time": "1760337751",
"counter": 4,
"new_replies": []
},
{
"id": "8760",
"item_type": "product",
"item_id": "6434",
"people_like_count": "0",
"people_dislike_count": "0",
"reply_count": "1",
"is_user_admin": "0",
"user_avatar": "",
"user_name": "hura test - S\u0110T: 0987654321",
"rate": "2",
"title": "HuraSoft - S\u1ea3n ph\u1ea9m test (Kh\u00f4ng x\u00f3a)",
"content": "test review 2025 ",
"files": [],
"approved": "1",
"post_time": "1760330892",
"counter": 5,
"new_replies": [
{
"id": 742,
"comment_id": 8760,
"user_avatar": "0",
"user_name": "Hurasoft \u0110\u1ee9c",
"is_user_admin": 1,
"people_like_count": 0,
"approved": 1,
"people_dislike_count": 0,
"content": "admin test",
"post_time": 1760331038
}
]
},
{
"id": "8757",
"item_type": "product",
"item_id": "6270",
"people_like_count": "0",
"people_dislike_count": "0",
"reply_count": "0",
"is_user_admin": "0",
"user_avatar": "",
"user_name": "Ch\u1ecbu - S\u1ed1 \u0111t : 0938553437",
"rate": "5",
"title": "\u0110\u00e1nh gi\u00e1 s\u1ea3n ph\u1ea9m Fan Xigmatek Infinity Pro 2 RGB Reverse",
"content": "5 sao",
"files": [],
"approved": "0",
"post_time": "1759639059",
"counter": 6,
"new_replies": []
},
{
"id": "8756",
"item_type": "product",
"item_id": "4751",
"people_like_count": "0",
"people_dislike_count": "0",
"reply_count": "0",
"is_user_admin": "0",
"user_avatar": "",
"user_name": "\u0110\u1ea1i Nguy\u1ec5n - S\u1ed1 \u0111t : 0866657510",
"rate": "1",
"title": "\u0110\u00e1nh gi\u00e1 s\u1ea3n ph\u1ea9m \u1ed4 c\u1ee9ng SSD SK Hynix PC801 1TB NVMe PCIe Gen4.0 - RW 7000MB\/s",
"content": "con n\u00e0y c\u00e0i win ch\u1ea1y 6 th\u00e1ng th\u00ec t\u1ed1c v\u1ec1 2000",
"files": [],
"approved": "0",
"post_time": "1757729354",
"counter": 7,
"new_replies": []
},
{
"id": "8755",
"item_type": "product",
"item_id": "4620",
"people_like_count": "0",
"people_dislike_count": "0",
"reply_count": "0",
"is_user_admin": "0",
"user_avatar": "",
"user_name": "Thien Than - S\u1ed1 \u0111t : 0355001407",
"rate": "5",
"title": "\u0110\u00e1nh gi\u00e1 s\u1ea3n ph\u1ea9m Ngu\u1ed3n MSI MAG A850GL PCIE5 ATX 3.0 850W (80 Plus Gold\/ Full Modular)",
"content": "Ngu\u1ed3n qu\u00e1 \u0111\u00e1ng ti\u1ec1n trong ph\u00e2n kh\u00fac \u0111\u1ea7y \u0111\u1ee7 c\u1ed5ng k\u1ebft n\u1ed1i c\u1ea7n cho linh ki\u1ec7n hi\u1ec7n nay",
"files": [],
"approved": "0",
"post_time": "1755744191",
"counter": 8,
"new_replies": []
},
{
"id": "8754",
"item_type": "product",
"item_id": "5356",
"people_like_count": "0",
"people_dislike_count": "0",
"reply_count": "0",
"is_user_admin": "0",
"user_avatar": "",
"user_name": "Huy \u0110inh - S\u1ed1 \u0111t : 0966930673",
"rate": "5",
"title": "\u0110\u00e1nh gi\u00e1 s\u1ea3n ph\u1ea9m Mainboard MSI MAG X870 TOMAHAWK WIFI (AMD X870, Socket AM5, ATX, 4 khe RAM DDR5)",
"content": "Msi si\u00eau \u0111\u1ec9nh",
"files": [],
"approved": "0",
"post_time": "1754999244",
"counter": 9,
"new_replies": []
},
{
"id": "8753",
"item_type": "product",
"item_id": "5475",
"people_like_count": "0",
"people_dislike_count": "0",
"reply_count": "0",
"is_user_admin": "0",
"user_avatar": "",
"user_name": "Xxxxx - S\u1ed1 \u0111t : 0987767896",
"rate": "5",
"title": "\u0110\u00e1nh gi\u00e1 s\u1ea3n ph\u1ea9m HHPC ULTRA 7 265K | 32GB DDR5 | NVIDIA RTX 3060 12GB",
"content": "Xxxxxxx",
"files": [],
"approved": "0",
"post_time": "1753883033",
"counter": 10,
"new_replies": []
},
{
"id": "8752",
"item_type": "product",
"item_id": "5562",
"people_like_count": "0",
"people_dislike_count": "0",
"reply_count": "0",
"is_user_admin": "0",
"user_avatar": "",
"user_name": "kghasnh \u00e1d - S\u1ed1 \u0111t : 0936542873",
"rate": "1",
"title": "\u0110\u00e1nh gi\u00e1 s\u1ea3n ph\u1ea9m HHPC ALPHARD M CORE i5 14600KF | 32G | NVIDIA RTX 4070 12G",
"content": "K\u00c9M! qu\u00e1 k\u00e9mmmmmmmmmmmmmmmmmmmmmmmm",
"files": [],
"approved": "0",
"post_time": "1752472074",
"counter": 11,
"new_replies": []
},
{
"id": "8751",
"item_type": "product",
"item_id": "1356",
"people_like_count": "0",
"people_dislike_count": "0",
"reply_count": "0",
"is_user_admin": "0",
"user_avatar": "",
"user_name": "Tr\u1ecdng Ngh\u0129a - S\u1ed1 \u0111t : 0963544681",
"rate": "1",
"title": "\u0110\u00e1nh gi\u00e1 s\u1ea3n ph\u1ea9m Ngu\u1ed3n Super Flower Leadex Platinum 2000W (80 Plus Platinum\/Full Modular)",
"content": "https:\/\/hoanghapc.vn\/nguon-may-tinh-super-flower-leadex-platinum-2000w-sale\n\n10.990.000 \u0111 } 5.990.000 \u0111",
"files": [],
"approved": "0",
"post_time": "1749750914",
"counter": 12,
"new_replies": []
},
{
"id": "8750",
"item_type": "product",
"item_id": "4670",
"people_like_count": "0",
"people_dislike_count": "0",
"reply_count": "0",
"is_user_admin": "0",
"user_avatar": "",
"user_name": "\u0110inh L\u1ed9c - S\u1ed1 \u0111t : 0835110115",
"rate": "5",
"title": "\u0110\u00e1nh gi\u00e1 s\u1ea3n ph\u1ea9m RAM DDR5 CORSAIR VENGEANCE RGB 64GB 6400MHz (2x32GB) C32 BLACK",
"content": "Xem l\u1ea1i c\u00e1ch b\u00e1n h\u00e0ng",
"files": [],
"approved": "0",
"post_time": "1747476672",
"counter": 13,
"new_replies": []
},
{
"id": "8749",
"item_type": "product",
"item_id": "4670",
"people_like_count": "0",
"people_dislike_count": "0",
"reply_count": "0",
"is_user_admin": "0",
"user_avatar": "",
"user_name": "\u0110inh L\u1ed9c - S\u1ed1 \u0111t : 0835110115",
"rate": "1",
"title": "\u0110\u00e1nh gi\u00e1 s\u1ea3n ph\u1ea9m RAM DDR5 CORSAIR VENGEANCE RGB 64GB 6400MHz (2x32GB) C32 BLACK",
"content": "B\u00e1n h\u00e0ng th\u00ec treo \u0111\u1ea7u d\u00ea b\u00e1n th\u1ecbt ch\u00f3, b\u00e1n h\u00e0ng kh\u00f4ng \u0111\u00fang h\u00e0ng, mua c\u00e1i n\u00e0y giao c\u00e1i kia, qu\u1ea3n l\u00fd b\u00e1n h\u00e0ng b\u00e0n thua nh\u00e2n vi\u00ean, xem l\u1ea1i c\u00e1i qu\u1ea3n l\u00fd c\u1ee7a m\u00ecnh \\. B\u00ecnh",
"files": [],
"approved": "0",
"post_time": "1747476567",
"counter": 14,
"new_replies": []
},
{
"id": "8748",
"item_type": "product",
"item_id": "5355",
"people_like_count": "0",
"people_dislike_count": "0",
"reply_count": "0",
"is_user_admin": "0",
"user_avatar": "",
"user_name": "Thinh - S\u1ed1 \u0111t : 0964373778",
"rate": "5",
"title": "\u0110\u00e1nh gi\u00e1 s\u1ea3n ph\u1ea9m Mainboard MSI MPG X870E CARBON WIFI (AMD X870E, Socket AM5, ATX, 4 khe RAM DDR5)",
"content": "Thi\u1ebft k\u1ebf \u0111\u1eb9p, RGB tr\u00ean VRM v\u00e0 SSD slot b\u1eaft m\u1eaft. VRM cung c\u1ea5p \u0111i\u1ec7n kh\u1ee7ng. T\u00ednh n\u0103ng tooless khi c\u00e0i \u0111\u1eb7t SSD v\u00e0 card \u0111\u1ed3 h\u1ecda r\u1ea5t ti\u1ec7n l\u1ee3i. Back IO c\u00f3 r\u1ea5t nhi\u1ec1u c\u1ed5ng k\u1ebft n\u1ed1i. Bios \u0111\u01b0\u1ee3c thi\u1ebft k\u1ebf l\u1ea1i r\u1ea5t \u0111\u1eb9p v\u00e0 thu\u1eadn l\u1ee3i cho ng\u01b0\u1eddi d\u00f9ng. T\u00f3m l\u1ea1i l\u00e0 1 chi\u1ebfc x870E r\u1ea5t \u0111\u00e1ng \u0111\u1ec3 mua trong t\u1ea7m gi\u00e1.",
"files": [],
"approved": "0",
"post_time": "1747296869",
"counter": 15,
"new_replies": []
},
{
"id": "8747",
"item_type": "product",
"item_id": "4514",
"people_like_count": "0",
"people_dislike_count": "0",
"reply_count": "0",
"is_user_admin": "0",
"user_avatar": "",
"user_name": "H\u00e0ng \u0110\u1ec3u - S\u1ed1 \u0111t : 0987654321",
"rate": "1",
"title": "\u0110\u00e1nh gi\u00e1 s\u1ea3n ph\u1ea9m AI-Deep Learning THREADRIPPER PRO 5975WX | 256GB | Dual RTX 4090 24G",
"content": "Pc \u0110\u1ec3u ",
"files": [],
"approved": "0",
"post_time": "1746253902",
"counter": 16,
"new_replies": []
},
{
"id": "8746",
"item_type": "product",
"item_id": "4620",
"people_like_count": "0",
"people_dislike_count": "0",
"reply_count": "0",
"is_user_admin": "0",
"user_avatar": "",
"user_name": "Danh Th\u00e0nh - S\u1ed1 \u0111t : 0933444745",
"rate": "5",
"title": "\u0110\u00e1nh gi\u00e1 s\u1ea3n ph\u1ea9m Ngu\u1ed3n MSI MAG A850GL PCIE5 ATX 3.0 850W (80 Plus Gold\/ Full Modular)",
"content": "Ngu\u1ed3n t\u1ed1t nh\u1ea5t trong t\u1ea7m gi\u00e1 3tr",
"files": [],
"approved": "0",
"post_time": "1743072855",
"counter": 17,
"new_replies": []
},
{
"id": "8745",
"item_type": "product",
"item_id": "4771",
"people_like_count": "0",
"people_dislike_count": "0",
"reply_count": "0",
"is_user_admin": "0",
"user_avatar": "",
"user_name": "Nguy\u1ec5n V\u0103n Th\u1ecbnh - S\u1ed1 \u0111t : 0862130910",
"rate": "1",
"title": "\u0110\u00e1nh gi\u00e1 s\u1ea3n ph\u1ea9m HHPC CORE i9 14900K | 64G DDR5 | NVIDIA RTX 4090 24G",
"content": "Kh\u00f4ng c\u1ea7n",
"files": [],
"approved": "0",
"post_time": "1740978395",
"counter": 18,
"new_replies": []
},
{
"id": "8744",
"item_type": "product",
"item_id": "5377",
"people_like_count": "0",
"people_dislike_count": "0",
"reply_count": "0",
"is_user_admin": "0",
"user_avatar": "",
"user_name": "b\u1ea3o trung - S\u1ed1 \u0111t : 0326059410",
"rate": "5",
"title": "\u0110\u00e1nh gi\u00e1 s\u1ea3n ph\u1ea9m Mainboard MSI MAG Z890 TOMAHAWK WIFI (Intel Z890, Socket 1851, ATX, 4 khe RAM DDR5)",
"content": "S\u1ea3n ph\u1ea9m tuy\u1ec7t v\u1eddi c\u00e2n t\u1ed1t core ultra 9",
"files": [],
"approved": "0",
"post_time": "1740898627",
"counter": 19,
"new_replies": []
},
{
"id": "8743",
"item_type": "product",
"item_id": "5421",
"people_like_count": "0",
"people_dislike_count": "0",
"reply_count": "0",
"is_user_admin": "0",
"user_avatar": "",
"user_name": "Thi\u00ean Long - S\u1ed1 \u0111t : 0931287086",
"rate": "5",
"title": "\u0110\u00e1nh gi\u00e1 s\u1ea3n ph\u1ea9m HHPC RYZEN 9 9950X | 64G DDR5 | NVIDIA RTX 4070 Ti SUPER 16G",
"content": "C\u1ea5u h\u00ecnh \u0111\u00e1ng \u0111\u1ea7u t\u01b0 cho nh\u1eefng ai c\u1ea7n m\u1ed9t c\u1ed7 m\u00e1y m\u1ea1nh m\u1ebd \u0111\u1ec3 l\u00e0m vi\u1ec7c.",
"files": [],
"approved": "0",
"post_time": "1738828475",
"counter": 20,
"new_replies": []
}
]
}

View File

@@ -32,7 +32,8 @@ export function resolveProductPage(slug: string): ProductResult | null {
const data = {
...product,
productDescription : productDetail.productDescription,
productSpec : productDetail.productSpec
productSpec : productDetail.productSpec,
related : productDetail.related
}
return {

View File

@@ -71,6 +71,18 @@ export function formatArticleTime(article_time: string) {
return `${day}/${month}/${year}`;
}
export function formatDate(a:any){
let dateObj = new Date(parseInt(a)*1000);
let year = dateObj.getFullYear();
let month = ((dateObj.getMonth()+1) <= 9) ? '0' + (dateObj.getMonth()+1) : dateObj.getMonth()+1;
let date = (dateObj.getDate() < 10) ? '0' + dateObj.getDate() : dateObj.getDate();
let hour = (dateObj.getHours() < 10) ? '0' + dateObj.getHours() : dateObj.getHours();
let min = (dateObj.getMinutes() < 10) ? '0' + dateObj.getMinutes() : dateObj.getMinutes();
let time = `${date}/${month}/${year}, ${hour}:${min}`;
return time;
}
//
export function normalizeKey(str: string) {
return str