update 2/2/2026
This commit is contained in:
@@ -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>
|
||||
)
|
||||
}
|
||||
17
src/components/product/detail/articles/index.tsx
Normal file
17
src/components/product/detail/articles/index.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
36
src/components/product/detail/comments/CommentList.tsx
Normal file
36
src/components/product/detail/comments/CommentList.tsx
Normal 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>
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
||||
16
src/components/product/detail/comments/Form.tsx
Normal file
16
src/components/product/detail/comments/Form.tsx
Normal 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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
40
src/components/product/detail/comments/index.tsx
Normal file
40
src/components/product/detail/comments/index.tsx
Normal 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>
|
||||
))
|
||||
}
|
||||
@@ -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á và 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>
|
||||
|
||||
|
||||
@@ -1,227 +0,0 @@
|
||||
export default function Review() {
|
||||
return (
|
||||
<>
|
||||
<p className="leading-[31px] font-600 text-24 mb-4 pb-4">
|
||||
{" "}
|
||||
Đánh giá và 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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
101
src/components/product/detail/reviews/Form.jsx
Normal file
101
src/components/product/detail/reviews/Form.jsx
Normal 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>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
38
src/components/product/detail/reviews/ReviewList.tsx
Normal file
38
src/components/product/detail/reviews/ReviewList.tsx
Normal 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>
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
||||
71
src/components/product/detail/reviews/StarPercent.tsx
Normal file
71
src/components/product/detail/reviews/StarPercent.tsx
Normal 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
|
||||
};
|
||||
}
|
||||
45
src/components/product/detail/reviews/index.tsx
Normal file
45
src/components/product/detail/reviews/index.tsx
Normal 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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user