update
This commit is contained in:
@@ -10,7 +10,6 @@ export const BoxInfoRight = (item: ProductDetailData) => {
|
||||
<h1 className="product-name color-black line-clamp-3 font-bold">
|
||||
{item.product_info.productName}
|
||||
</h1>
|
||||
|
||||
<div className="list-basic-product-info flex flex-wrap items-center">
|
||||
<div className="item-basic">
|
||||
Mã SP: <span className="color-primary">{item.product_info.productSKU}</span>
|
||||
@@ -32,7 +31,6 @@ export const BoxInfoRight = (item: ProductDetailData) => {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* tình trạng */}
|
||||
<div className="list-basic-product-info flex flex-wrap items-center gap-6">
|
||||
<div className="item-basic">
|
||||
@@ -45,11 +43,8 @@ export const BoxInfoRight = (item: ProductDetailData) => {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* giá */}
|
||||
|
||||
<BoxPrice {...item} />
|
||||
|
||||
{item.product_info.specialOffer.all.length > 0 && (
|
||||
<div className="box-offer-detail border-radius-10">
|
||||
<div className="title-offer-detail flex items-center">
|
||||
@@ -66,7 +61,6 @@ export const BoxInfoRight = (item: ProductDetailData) => {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* mua hàng */}
|
||||
{(item.product_info.quantity > '0' || item.product_info.price > '0') && (
|
||||
<>
|
||||
@@ -87,11 +81,7 @@ export const BoxInfoRight = (item: ProductDetailData) => {
|
||||
+{' '}
|
||||
</p>
|
||||
</div>
|
||||
<Link
|
||||
href="#"
|
||||
onClick={() => addProductToCart(item.product_info.id, 0, '')}
|
||||
className="addCart flex flex-wrap items-center justify-center gap-3"
|
||||
>
|
||||
<Link href="#" className="addCart flex flex-wrap items-center justify-center gap-3">
|
||||
<i className="sprite sprite-cart-detail"></i>
|
||||
<p className="title-cart">Thêm vào giỏ hàng</p>
|
||||
</Link>
|
||||
@@ -99,37 +89,24 @@ export const BoxInfoRight = (item: ProductDetailData) => {
|
||||
</div>
|
||||
|
||||
<div id="detail-buy-ads" className="detail-buy grid grid-cols-2 gap-2">
|
||||
<Link
|
||||
href="#"
|
||||
className="detail-buy-now col-span-2"
|
||||
onClick={() => buyNow(item.product_info.id, 0, '')}
|
||||
>
|
||||
<Link href="#" className="detail-buy-now col-span-2">
|
||||
<span>ĐẶT MUA NGAY</span>
|
||||
Giao hàng tận nơi nhanh chóng
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
href="#"
|
||||
className="detail-add-cart"
|
||||
onClick={() => buyPayInstall(item.product_info.id, 0, '')}
|
||||
>
|
||||
<Link href="#" className="detail-add-cart">
|
||||
<span>TRẢ GÓP QUA HỒ SƠ</span>
|
||||
Chỉ từ 2.665.000₫/ tháng
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
href="#"
|
||||
className="detail-add-cart"
|
||||
onClick={() => buyAlepay(item.product_info.id, 0, '')}
|
||||
>
|
||||
<Link href="#" className="detail-add-cart">
|
||||
<span>TRẢ GÓP QUA THẺ</span>
|
||||
Chỉ từ 1.332.500₫/ tháng
|
||||
</Link>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* yên tâm mua hàng */}
|
||||
n{/* yên tâm mua hàng */}
|
||||
<div className="box-product-policy-detal boder-radius-10" style={{ marginTop: '24px' }}>
|
||||
<h2 className="title font-[600]">Yên tâm mua hàng</h2>
|
||||
<div className="list-showroom-detail flex flex-wrap justify-between">
|
||||
@@ -151,7 +128,6 @@ export const BoxInfoRight = (item: ProductDetailData) => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<BoxBought />
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import React from 'react';
|
||||
interface FormCommentProps {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
export const FormComment: React.FC<FormCommentProps> = ({ open, onClose }) => {
|
||||
return (
|
||||
<dialog id="commentDialog" className={`modal ${open ? 'modal-open' : ''}`}>
|
||||
{' '}
|
||||
<div className="modal-box">
|
||||
<h3 className="mb-4 font-semibold">Nhập thông tin</h3>
|
||||
<div className="space-y-4">
|
||||
<div className="flex gap-4">
|
||||
<label className="label cursor-pointer">
|
||||
<input type="radio" name="sex" value="Anh" className="radio radio-xs" />
|
||||
<span className="ml-2">Anh</span>
|
||||
</label>
|
||||
<label className="label cursor-pointer">
|
||||
<input type="radio" name="sex" value="Chị" className="radio radio-xs" />
|
||||
<span className="ml-2">Chị</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<input type="text" className="input input-md w-[95%]" placeholder="Họ tên (bắt buộc)" />
|
||||
|
||||
<input
|
||||
type="email"
|
||||
className="input input-md w-[95%]"
|
||||
placeholder="Email (để nhận phản hồi qua mail)"
|
||||
/>
|
||||
|
||||
<button className="btn btn-active w-[93.5%] bg-red-500 text-white">Cập nhật</button>
|
||||
</div>
|
||||
</div>
|
||||
<form method="dialog" className="modal-backdrop">
|
||||
<button onClick={onClose}>Đóng</button>
|
||||
</form>
|
||||
</dialog>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,72 @@
|
||||
import React, { useState } from 'react';
|
||||
import { ListCommentData } from '@/data/ListComment';
|
||||
import Image from 'next/image';
|
||||
|
||||
export const ListComment = () => {
|
||||
return (
|
||||
<div className="comment-list">
|
||||
{ListCommentData.slice(0.3).map((item, index) => (
|
||||
<div className="item-comment" id={`comment_${item.id}`} key={index}>
|
||||
<div className="form-reply-comment">
|
||||
{/* header */}
|
||||
<div className="comment-name flex justify-between">
|
||||
<div className="comment-form-left flex items-center gap-2">
|
||||
{item.user_avatar ? (
|
||||
<b className="avatar-user">
|
||||
<img src={item.user_avatar} alt={item.user_name} />
|
||||
</b>
|
||||
) : (
|
||||
<b className="avatar-user flex items-center justify-center">
|
||||
{' '}
|
||||
{item.user_name.charAt(0)}{' '}
|
||||
</b>
|
||||
)}
|
||||
<b className="user-name">{item.user_name}</b>
|
||||
</div>
|
||||
<div className="comment-form-right flex items-center gap-2 text-sm text-gray-500">
|
||||
<i className="fa-regular fa-clock"></i> <span>{item.post_time}</span>
|
||||
</div>
|
||||
</div>{' '}
|
||||
{/* content */}
|
||||
<div className="comment-content mt-3 rounded p-2">
|
||||
<p>{item.content}</p>
|
||||
<div className="info_feeback mt-2 flex items-center gap-2">
|
||||
<i className="sprite sprite-icon-reply-detail"></i>
|
||||
<button className="btn-reply font-medium"> Trả lời </button>{' '}
|
||||
</div>{' '}
|
||||
</div>{' '}
|
||||
{/* reply list */}
|
||||
<div className="reply-list-container mt-4">
|
||||
{item.new_replies.map((reply) => (
|
||||
<div key={reply.id} className="item_reply mt-3">
|
||||
<div className="flex justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
{reply.user_avatar !== '0' ? (
|
||||
<b className="avatar-user flex items-center justify-center">
|
||||
{' '}
|
||||
<img src={reply.user_avatar} alt={reply.user_name} />{' '}
|
||||
</b>
|
||||
) : (
|
||||
<b className="avatar-user flex items-center justify-center">
|
||||
{reply.user_name.charAt(0)}
|
||||
</b>
|
||||
)}
|
||||
<div className="comment-name">
|
||||
<b className="user-name">{reply.user_name}</b>
|
||||
{reply.is_user_admin === '1' && <i className="note font-medium">QTV</i>}
|
||||
</div>{' '}
|
||||
</div>
|
||||
<div className="text-sm text-gray-500">
|
||||
{new Date(Number(reply.post_time) * 1000).toLocaleDateString('vi-VN')}
|
||||
</div>{' '}
|
||||
</div>
|
||||
<div className="comment-content mt-2 rounded p-2">{reply.content} </div>{' '}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
import React, { useState } from 'react';
|
||||
import { FormComment } from './FormComment';
|
||||
import { ListComment } from './ListComment';
|
||||
|
||||
export const ProductComment: React.FC = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
return (
|
||||
<div className="box-comment">
|
||||
<p className="title-comment font-[600]">Hỏi và đáp</p>
|
||||
<div className="comment-detail">
|
||||
<div className="form-comment flex justify-between gap-2">
|
||||
<textarea
|
||||
className="comment_reply_content boder-radius-10"
|
||||
id="content0"
|
||||
placeholder="Xin mời để lại câu hỏi, Nguyencong sẽ trả lời ngay trong 1h, các câu hỏi sau 22h - 8h sẽ được trả lời vào sáng hôm sau."
|
||||
name="user_post[content]"
|
||||
></textarea>
|
||||
<button
|
||||
className="btn-send-form-comment send-comment-pc flex items-center justify-center gap-2"
|
||||
onClick={() => setOpen(true)}
|
||||
>
|
||||
<i className="sprite sprite-icon-send-detail"></i>Gửi
|
||||
</button>
|
||||
</div>
|
||||
<FormComment open={open} onClose={() => setOpen(false)} />
|
||||
</div>
|
||||
|
||||
{/* list comment */}
|
||||
<ListComment />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
import { useState } from 'react';
|
||||
import { FaAngleDown, FaAngleUp } from 'react-icons/fa6';
|
||||
import type { ProductDetailData } from '@/types';
|
||||
import Link from 'next/link';
|
||||
|
||||
export const ProductDescription = (item: ProductDetailData) => {
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
|
||||
if (!item.product_info.productDescription) return null;
|
||||
|
||||
return (
|
||||
<div className="box-descreption-detail">
|
||||
<h2 className="titlle-descreption font-[500]">Giới thiệu {item.product_info.productName}</h2>
|
||||
<div
|
||||
className={`content-descreption-detail static-html relative ${
|
||||
expanded ? 'max-h-none' : 'max-h-[467px] overflow-hidden'
|
||||
}`}
|
||||
dangerouslySetInnerHTML={{ __html: item.product_info.productDescription }}
|
||||
/>
|
||||
<div
|
||||
onClick={() => setExpanded(!expanded)}
|
||||
className="btn-article-col js-viewmore-content flex items-center justify-center gap-2 font-[500]"
|
||||
>
|
||||
<span>{expanded ? 'Thu gọn' : 'Xem tất cả'}</span>
|
||||
{expanded ? <FaAngleUp /> : <FaAngleDown />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,137 @@
|
||||
import React from 'react';
|
||||
|
||||
export const FormReview: React.FC = () => {
|
||||
return (
|
||||
<div className="box-form-review" id="js-box-review">
|
||||
<textarea
|
||||
className="review_reply_content"
|
||||
id="rating-content"
|
||||
placeholder="Mời bạn để lại đánh giá..."
|
||||
name="user_post[content]"
|
||||
></textarea>
|
||||
|
||||
<div className="actions-comment">
|
||||
<div className="infomation-customer">
|
||||
<table>
|
||||
<tbody>
|
||||
<tr className="flex items-center">
|
||||
<td>
|
||||
<label>Đánh giá:</label>
|
||||
</td>
|
||||
<td>
|
||||
<div className="rating" id="select-rate-pro">
|
||||
<div className="rating-selection" id="rating-review0">
|
||||
<input
|
||||
type="radio"
|
||||
className="rating-input"
|
||||
id="rating-input-review-0-5"
|
||||
value="5"
|
||||
name="user_post[rate]"
|
||||
defaultChecked
|
||||
/>
|
||||
<label
|
||||
htmlFor="rating-input-review-0-5"
|
||||
className="sprite-1star rating-star"
|
||||
></label>
|
||||
|
||||
<input
|
||||
type="radio"
|
||||
className="rating-input"
|
||||
id="rating-input-review-0-4"
|
||||
value="4"
|
||||
name="user_post[rate]"
|
||||
/>
|
||||
<label
|
||||
htmlFor="rating-input-review-0-4"
|
||||
className="sprite-1star rating-star"
|
||||
></label>
|
||||
|
||||
<input
|
||||
type="radio"
|
||||
className="rating-input"
|
||||
id="rating-input-review-0-3"
|
||||
value="3"
|
||||
name="user_post[rate]"
|
||||
/>
|
||||
<label
|
||||
htmlFor="rating-input-review-0-3"
|
||||
className="sprite-1star rating-star"
|
||||
></label>
|
||||
|
||||
<input
|
||||
type="radio"
|
||||
className="rating-input"
|
||||
id="rating-input-review-0-2"
|
||||
value="2"
|
||||
name="user_post[rate]"
|
||||
/>
|
||||
<label
|
||||
htmlFor="rating-input-review-0-2"
|
||||
className="sprite-1star rating-star"
|
||||
></label>
|
||||
|
||||
<input
|
||||
type="radio"
|
||||
className="rating-input"
|
||||
id="rating-input-review-0-1"
|
||||
value="1"
|
||||
name="user_post[rate]"
|
||||
/>
|
||||
<label
|
||||
htmlFor="rating-input-review-0-1"
|
||||
className="sprite-1star rating-star"
|
||||
></label>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr className="flex items-center">
|
||||
<td>Tên bạn</td>
|
||||
<td>
|
||||
<input
|
||||
type="text"
|
||||
id="rating-name"
|
||||
name="user_post[user_name]"
|
||||
className="form-control"
|
||||
defaultValue=""
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr className="flex items-center">
|
||||
<td>Email</td>
|
||||
<td>
|
||||
<input
|
||||
type="text"
|
||||
id="rating-email"
|
||||
name="user_post[user_email]"
|
||||
className="form-control"
|
||||
defaultValue=""
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<p
|
||||
id="js-review-note"
|
||||
className="font-weight-700 flex"
|
||||
style={{ color: 'red', maxWidth: '100%' }}
|
||||
></p>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className="btn-review send_form mt-12 mb-10"
|
||||
onClick={() => {
|
||||
// TODO: viết hàm send_vote() trong React
|
||||
console.log('Send vote clicked');
|
||||
}}
|
||||
>
|
||||
Gửi đánh giá
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,122 @@
|
||||
import React, { useState } from 'react';
|
||||
import { ListReviewData } from '@/data/ListReview';
|
||||
import Image from 'next/image';
|
||||
|
||||
export const ListReview = () => {
|
||||
const [showAll, setShowAll] = useState(false);
|
||||
const visibleReviews = showAll ? ListReviewData : ListReviewData.slice(0, 3);
|
||||
|
||||
return (
|
||||
<div className="list-review">
|
||||
{visibleReviews.map((review) => {
|
||||
const avatarLetter = review.user_name.charAt(0).toUpperCase();
|
||||
const date = new Date(Number(review.post_time) * 1000).toLocaleDateString('vi-VN');
|
||||
|
||||
return (
|
||||
<div key={review.id} className="item-comment">
|
||||
<div className="form-reply-comment">
|
||||
{/* header */}
|
||||
<div className="comment-name flex items-center justify-between">
|
||||
<div className="comment-form-left flex items-center gap-2">
|
||||
{review.user_avatar ? (
|
||||
<b className="avatar-user js-avatar-user flex items-center justify-center">
|
||||
<Image src={review.user_avatar} alt={review.user_name} />
|
||||
</b>
|
||||
) : (
|
||||
<b className="avatar-user js-avatar-user flex items-center justify-center">
|
||||
{avatarLetter}
|
||||
</b>
|
||||
)}
|
||||
<b className="user-name flex items-center gap-2">{review.user_name}</b>
|
||||
</div>
|
||||
<div className="comment-form-right flex items-center gap-2">
|
||||
<i className="fa-regular fa-clock"></i>
|
||||
<span style={{ color: '#787878', fontSize: 12, marginRight: 4 }}>{date}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* content */}
|
||||
<div className="comment-content boder-radius-10 relative mt-3">
|
||||
<div className="text-review flex flex-col gap-2">
|
||||
<p className="flex items-center">
|
||||
<b>Đánh giá:</b> <i className={`sprite-star-5 star${review.rate}`}></i>
|
||||
</p>
|
||||
<p className="flex items-center">
|
||||
<b>Nhận xét:</b>
|
||||
<span style={{ width: '80%' }}>{review.content}</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* feedback actions */}
|
||||
<div className="info_feeback flex items-center gap-2">
|
||||
<i className="sprite sprite-icon-reply-detail"></i>
|
||||
<button className="write_reply btn-reply font-weight-500">Trả lời</button>
|
||||
</div>
|
||||
|
||||
{/* images nếu có */}
|
||||
<div className="jd-img-review flex flex-col gap-2">
|
||||
{review.files.map((file) => (
|
||||
<Image
|
||||
key={file.id}
|
||||
src={file.file_path}
|
||||
alt={file.title}
|
||||
width={100}
|
||||
height={60}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* reply list */}
|
||||
<div className="reply-holder reply-list-container">
|
||||
{review.new_replies.map((reply) => (
|
||||
<div key={reply.id} className="item_reply mt-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="comment-left-form item-center flex gap-2">
|
||||
<b className="avatar-user avatar-admin">
|
||||
{reply.user_avatar !== '0' ? (
|
||||
<img src={reply.user_avatar} alt={reply.user_name} />
|
||||
) : (
|
||||
reply.user_name.charAt(0)
|
||||
)}
|
||||
</b>
|
||||
<div className="comment-name mb-10">
|
||||
<b className="user-name">{reply.user_name}</b>
|
||||
{reply.is_user_admin === '1' && <i className="note font-[500]">QTV</i>}
|
||||
</div>
|
||||
</div>
|
||||
<div className="info_feeback comment-right-form">
|
||||
<span style={{ color: '#787878', fontSize: 12 }}>({reply.post_time})</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="comment-content boder-radius-10">{reply.content}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
{!showAll && ListReviewData.length > 3 && (
|
||||
<button
|
||||
id="first-review"
|
||||
className="btn-more cursor-pointer"
|
||||
onClick={() => setShowAll(true)}
|
||||
>
|
||||
Xem thêm đánh giá
|
||||
</button>
|
||||
)}
|
||||
|
||||
{showAll && (
|
||||
<button
|
||||
id="hide-review"
|
||||
className="btn-more cursor-pointer"
|
||||
onClick={() => setShowAll(false)}
|
||||
>
|
||||
Thu gọn
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
100
src/components/product/ProductDetail/ProductReview/index.tsx
Normal file
100
src/components/product/ProductDetail/ProductReview/index.tsx
Normal file
@@ -0,0 +1,100 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Review } from '@/types';
|
||||
import { FaStar } from 'react-icons/fa6';
|
||||
import { FormReview } from './FormReview';
|
||||
import { ListReview } from './ListReview';
|
||||
|
||||
interface Props {
|
||||
ItemReview: Review;
|
||||
}
|
||||
|
||||
export const ProductReview: React.FC<Props> = ({ ItemReview }) => {
|
||||
const [showForm, setShowForm] = useState(false);
|
||||
|
||||
const { summary } = ItemReview;
|
||||
const totalRate = summary.list_rate.reduce((acc, item) => acc + Number(item.total), 0);
|
||||
|
||||
// Tạo object chứa số lượng và phần trăm cho từng sao const
|
||||
const rates = {
|
||||
rate1: Number(summary.list_rate.find((r) => r.rate === '1')?.total || 0),
|
||||
rate2: Number(summary.list_rate.find((r) => r.rate === '2')?.total || 0),
|
||||
rate3: Number(summary.list_rate.find((r) => r.rate === '3')?.total || 0),
|
||||
rate4: Number(summary.list_rate.find((r) => r.rate === '4')?.total || 0),
|
||||
rate5: Number(summary.list_rate.find((r) => r.rate === '5')?.total || 0),
|
||||
};
|
||||
const percents = {
|
||||
percent1: totalRate > 0 ? (rates.rate1 / totalRate) * 100 : 0,
|
||||
percent2: totalRate > 0 ? (rates.rate2 / totalRate) * 100 : 0,
|
||||
percent3: totalRate > 0 ? (rates.rate3 / totalRate) * 100 : 0,
|
||||
percent4: totalRate > 0 ? (rates.rate4 / totalRate) * 100 : 0,
|
||||
percent5: totalRate > 0 ? (rates.rate5 / totalRate) * 100 : 0,
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="box-review">
|
||||
<p className="title-review font-[600]">Bình luận và đánh giá</p>
|
||||
<div className="review-customer-detail">
|
||||
<form
|
||||
action="/ajax/post_comment.php"
|
||||
method="post"
|
||||
encType="multipart/form-data"
|
||||
className="form-post"
|
||||
>
|
||||
<div className="review-info boder-radius-10 flex">
|
||||
<div className="avgRate flex flex-col items-center justify-center">
|
||||
<span className="font-bold">{summary.avgRate}/5</span>
|
||||
<i className={`sprite-star-5 star${summary.avgRate} icon-star-detail`}></i>
|
||||
<p className="mt-3">{summary.total} đánh giá và nhận xét</p>
|
||||
</div>
|
||||
<div className="box-avg-rate-count">
|
||||
<div className="avg-rate-count">
|
||||
{[5, 4, 3, 2, 1].map((rate) => {
|
||||
const percent = percents[`percent${rate}` as keyof typeof percents];
|
||||
const total = rates[`rate${rate}` as keyof typeof rates];
|
||||
return (
|
||||
<div key={rate} className="avg-rate-item mt-2 flex items-center justify-center">
|
||||
<span className="rate-number flex items-center gap-1">
|
||||
{rate} <FaStar className="text-yellow-500" />
|
||||
</span>
|
||||
<div className="nhan-xet-bar">
|
||||
<div
|
||||
className={`percent percent${rate}`}
|
||||
style={{ width: `${percent}%` }}
|
||||
></div>
|
||||
</div>
|
||||
<span className="total-avg-rate">
|
||||
<strong>{total}</strong> đánh giá
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-danh-gia mb-5">Bạn đánh giá sao sản phẩm này</p>
|
||||
{!showForm ? (
|
||||
<div
|
||||
className="button-review mx-auto flex cursor-pointer items-center justify-center"
|
||||
onClick={() => setShowForm(true)}
|
||||
>
|
||||
{' '}
|
||||
Đánh giá ngay{' '}
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
className="button-review mx-auto flex cursor-pointer items-center justify-center"
|
||||
onClick={() => setShowForm(false)}
|
||||
>
|
||||
{' '}
|
||||
Đóng lại{' '}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* form */}
|
||||
{showForm && <FormReview />}
|
||||
</form>
|
||||
</div>
|
||||
<ListReview />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
45
src/components/product/ProductDetail/ProductSpec/index.tsx
Normal file
45
src/components/product/ProductDetail/ProductSpec/index.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import { useState } from 'react';
|
||||
import { FaAngleDown, FaAngleUp } from 'react-icons/fa6';
|
||||
import useFancybox from '@/hooks/useFancybox';
|
||||
|
||||
interface Props {
|
||||
ItemSpec: string;
|
||||
}
|
||||
|
||||
export const ProductSpec: React.FC<Props> = ({ ItemSpec }) => {
|
||||
const [fancyboxRef] = useFancybox({
|
||||
closeButton: 'auto',
|
||||
dragToClose: true,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="box-spec">
|
||||
<h2 className="title font-[600]">Thông số kỹ thuật</h2>
|
||||
<div className="content-spec relative" dangerouslySetInnerHTML={{ __html: ItemSpec }} />
|
||||
<div id="product-spec" style={{ display: 'none' }} ref={fancyboxRef}>
|
||||
<div className="box-top-centent-spec d-flex justify-content-between hide">
|
||||
<h2 className="font-weight-600">Thông số kỹ thuật</h2>
|
||||
<p
|
||||
className="delelte-content-spec d-flex justify-content-center align-items-center"
|
||||
data-fancybox-close
|
||||
>
|
||||
<i className="fa-solid fa-xmark"></i>
|
||||
</p>
|
||||
</div>
|
||||
<div className="content-spec">
|
||||
{/* thay vì {{ page.product_info.productSpec }} bạn truyền từ props */}
|
||||
{ItemSpec}
|
||||
</div>
|
||||
</div>
|
||||
<a
|
||||
data-fancybox
|
||||
data-options='{"src": "#product-spec", "touch": false, "smallBtn": false}'
|
||||
href="javascript:;"
|
||||
className="btn-article-col font-weight-500 flex items-center justify-center gap-2"
|
||||
>
|
||||
Xem đầy đủ thông số kỹ thuật
|
||||
<FaAngleDown />
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,16 +1,28 @@
|
||||
'use client';
|
||||
import React from 'react';
|
||||
import Link from 'next/link';
|
||||
import { Swiper, SwiperSlide } from 'swiper/react';
|
||||
import { Autoplay, Navigation, Pagination } from 'swiper/modules';
|
||||
|
||||
// type
|
||||
import type { ProductDetailData } from '@/types';
|
||||
|
||||
// data
|
||||
import { productDetailData } from '@/data/product/detail';
|
||||
import { productData } from '@/data/ListProduct';
|
||||
|
||||
import { findProductDetailBySlug } from '@/lib/product/productdetail';
|
||||
import { ErrorLink } from '@/components/Common/error';
|
||||
|
||||
import { Breadcrumb } from '@/components/Common/Breadcrumb';
|
||||
import { ImageProduct } from './ImageProduct';
|
||||
import { ProductSummary } from './ProductSummary';
|
||||
import { ComboSetBox } from './ComboSet';
|
||||
import { BoxInfoRight } from './BoxInfoRight';
|
||||
import ItemProduct from '@/components/Common/ItemProduct';
|
||||
import { ProductDescription } from './ProductDescription';
|
||||
import { ProductSpec } from './ProductSpec';
|
||||
import { ProductReview } from './ProductReview';
|
||||
import { ProductComment } from './ProductComment';
|
||||
|
||||
interface ProductDetailPageProps {
|
||||
slug: string;
|
||||
@@ -51,6 +63,65 @@ const ProductDetailPage: React.FC<ProductDetailPageProps> = ({ slug }) => {
|
||||
<BoxInfoRight {...Products} />
|
||||
</div>
|
||||
</div>
|
||||
{/* sản phẩm tương tự */}
|
||||
<div className="box-relative-product box-history-product page-hompage">
|
||||
<div className="box-product-category">
|
||||
<div className="title-box">
|
||||
<h2 className="title title-box font-[600]">Sản phẩm tương tự</h2>
|
||||
</div>
|
||||
<div className="box-list-history-product">
|
||||
<Swiper
|
||||
modules={[Autoplay, Navigation, Pagination]}
|
||||
spaceBetween={12}
|
||||
slidesPerView={5}
|
||||
loop={true}
|
||||
>
|
||||
{productData.map((item, index) => (
|
||||
<SwiperSlide key={index}>
|
||||
<ItemProduct item={item} />
|
||||
</SwiperSlide>
|
||||
))}
|
||||
</Swiper>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* nội dung chi tiết sản phẩm */}
|
||||
<div className="box-read-product-detail flex justify-between gap-3">
|
||||
<div className="box-left">
|
||||
{/* mô tả chi tiết sản phẩm */}
|
||||
<ProductDescription {...Products} />
|
||||
{/* đánh giá sản phẩm */}
|
||||
<ProductReview ItemReview={Products.product_info.review} />
|
||||
{/* bình luận sản phẩm */}
|
||||
<ProductComment />
|
||||
</div>
|
||||
<div className="box-right">
|
||||
<ProductSpec ItemSpec={Products.product_info.productSpec} />
|
||||
</div>
|
||||
</div>
|
||||
{/* sản phẩm đã xem */}
|
||||
<div className="box-history-product page-hompage mt-5">
|
||||
<div className="box-product-category">
|
||||
<div className="title-box">
|
||||
<h2 className="title title-box font-[600]">Sản phẩm đã xem</h2>
|
||||
</div>
|
||||
<div className="box-list-history-product">
|
||||
<Swiper
|
||||
modules={[Autoplay, Navigation, Pagination]}
|
||||
spaceBetween={12}
|
||||
slidesPerView={5}
|
||||
loop={true}
|
||||
>
|
||||
{productData.map((item, index) => (
|
||||
<SwiperSlide key={index}>
|
||||
<ItemProduct item={item} />
|
||||
</SwiperSlide>
|
||||
))}
|
||||
</Swiper>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user