26/02
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
export default function Popups() {
|
||||
export default function Popups({ onRebuild }: any) {
|
||||
|
||||
return (<>
|
||||
{/* Rebuild */}
|
||||
<div className="buildpc-popup-container buildpc-popup-rebuild text-black" id="popup-rebuild_config">
|
||||
@@ -13,7 +14,9 @@ export default function Popups() {
|
||||
<button
|
||||
className="btn-red"
|
||||
style={{ background: '#FA354A', color: '#fff' }}
|
||||
onClick={() => { /* reBuild(); */ }}>
|
||||
onClick={() => {
|
||||
onRebuild();
|
||||
}}>
|
||||
Xác nhận
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
export default function Promotion() {
|
||||
export default function Promotion({ total }: any) {
|
||||
return (
|
||||
<div className="buildpc-info-group">
|
||||
<p>
|
||||
Chi phí dự tính:
|
||||
<span className="js-config-summary font-600" style={{ color: '#FF4E2A' }}> 0đ </span>
|
||||
<span
|
||||
className="font-600"
|
||||
style={{ color: '#FF4E2A' }}
|
||||
>
|
||||
{total.toLocaleString()} đ
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<div className="buildpc-promotion-content js-buildpc-promotion-content"> {/* // Khuyến mại buildpc */} </div>
|
||||
<div className="buildpc-promotion-content" />
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -1,35 +1,22 @@
|
||||
'use client';
|
||||
import { useEffect, useState } from "react";
|
||||
import { Fancybox } from "@fancyapps/ui/dist/fancybox/";
|
||||
import { categoryDetail } from "@/data/buildpc/categoryDetail";
|
||||
import ModalContent from "../modal";
|
||||
import SelectedItemRow from "../modal/SelectedItemRow";
|
||||
|
||||
export default function BuildPCCategories({ categories }: any) {
|
||||
export default function BuildPCCategories({
|
||||
categories,
|
||||
activeTab,
|
||||
buildData,
|
||||
setBuildData
|
||||
}: any) {
|
||||
|
||||
const [selectedCategory, setSelectedCategory] = useState<any>(null);
|
||||
const [categoryInfo, setCategoryInfo] = useState<any>(null);
|
||||
const [buildData, setBuildData] = useState<any[]>([]);
|
||||
|
||||
const storageKey = "buildpc";
|
||||
const getStorageKey = () => `buildpc_tab_${activeTab}`;
|
||||
|
||||
// Load khi mount
|
||||
useEffect(() => {
|
||||
const oldData = localStorage.getItem(storageKey);
|
||||
setBuildData(oldData ? JSON.parse(oldData) : []);
|
||||
}, []);
|
||||
|
||||
// Nghe event update
|
||||
useEffect(() => {
|
||||
const handleUpdate = () => {
|
||||
const oldData = localStorage.getItem(storageKey);
|
||||
setBuildData(oldData ? JSON.parse(oldData) : []);
|
||||
};
|
||||
|
||||
window.addEventListener("buildpcUpdated", handleUpdate);
|
||||
return () => window.removeEventListener("buildpcUpdated", handleUpdate);
|
||||
}, []);
|
||||
|
||||
// Set category info khi mở modal
|
||||
useEffect(() => {
|
||||
if (selectedCategory) {
|
||||
const filterCategory = categoryDetail.find(
|
||||
@@ -39,15 +26,36 @@ export default function BuildPCCategories({ categories }: any) {
|
||||
}
|
||||
}, [selectedCategory]);
|
||||
|
||||
// Xoá sản phẩm
|
||||
const handleSaveProduct = (rowId: number, product: any) => {
|
||||
|
||||
const newData = [
|
||||
...buildData.filter((b: any) => b.rowId !== rowId), // loại bỏ row cũ
|
||||
{
|
||||
rowId,
|
||||
info: [product]
|
||||
}
|
||||
];
|
||||
|
||||
localStorage.setItem(getStorageKey(), JSON.stringify(newData));
|
||||
setBuildData(newData);
|
||||
|
||||
window.dispatchEvent(new Event("buildpcUpdated"));
|
||||
};
|
||||
|
||||
// ==============================
|
||||
// REMOVE
|
||||
// ==============================
|
||||
const handleRemove = (rowId: number) => {
|
||||
const newData = buildData.filter((b: any) => b.rowId !== rowId);
|
||||
localStorage.setItem(storageKey, JSON.stringify(newData));
|
||||
localStorage.setItem(getStorageKey(), JSON.stringify(newData));
|
||||
setBuildData(newData);
|
||||
};
|
||||
|
||||
// Đổi số lượng
|
||||
// ==============================
|
||||
// QUANTITY
|
||||
// ==============================
|
||||
const handleQuantityChange = (rowId: number, quantity: number) => {
|
||||
|
||||
const newData = buildData.map((b: any) => {
|
||||
if (b.rowId === rowId) {
|
||||
return {
|
||||
@@ -63,7 +71,7 @@ export default function BuildPCCategories({ categories }: any) {
|
||||
return b;
|
||||
});
|
||||
|
||||
localStorage.setItem(storageKey, JSON.stringify(newData));
|
||||
localStorage.setItem(getStorageKey(), JSON.stringify(newData));
|
||||
setBuildData(newData);
|
||||
};
|
||||
|
||||
@@ -83,24 +91,34 @@ export default function BuildPCCategories({ categories }: any) {
|
||||
</p>
|
||||
|
||||
<div className="item-drive-info">
|
||||
|
||||
{product ? (
|
||||
{ product
|
||||
? (
|
||||
<SelectedItemRow
|
||||
product={product}
|
||||
rowId={item.id}
|
||||
onRemove={handleRemove}
|
||||
onQuantityChange={handleQuantityChange}
|
||||
onEdit={(rowId) => setSelectedCategory(rowId)}
|
||||
onEdit={(rowId: number) => setSelectedCategory(rowId)}
|
||||
/>
|
||||
) : (
|
||||
<a
|
||||
href="#js-modal-popup"
|
||||
data-fancybox=""
|
||||
<button
|
||||
type="button"
|
||||
className="open-selection"
|
||||
onClick={() => setSelectedCategory(item.id)}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
|
||||
setSelectedCategory(item.id);
|
||||
|
||||
Fancybox.show([
|
||||
{
|
||||
src: "#js-modal-popup",
|
||||
type: "inline",
|
||||
},
|
||||
]);
|
||||
}}
|
||||
>
|
||||
<i className="bx bx-plus" /> Chọn {item.name}
|
||||
</a>
|
||||
</button>
|
||||
)}
|
||||
|
||||
</div>
|
||||
@@ -114,7 +132,10 @@ export default function BuildPCCategories({ categories }: any) {
|
||||
id="js-modal-popup"
|
||||
style={{ display: 'none', padding: 0 }}
|
||||
>
|
||||
<ModalContent item={categoryInfo} />
|
||||
<ModalContent
|
||||
item={categoryInfo}
|
||||
onSave={handleSaveProduct}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
'use client';
|
||||
import Link from "next/link";
|
||||
import useFancybox from '@/hooks/useFancyBox';
|
||||
import { useState } from "react";
|
||||
import { Fancybox } from "@fancyapps/ui/dist/fancybox/";
|
||||
import { useState, useEffect, useMemo } from "react";
|
||||
import { buildPcData } from "@/data/buildpc";
|
||||
|
||||
import Content from "./Content";
|
||||
@@ -10,16 +11,48 @@ import BuildPcPopups from "./Popups";
|
||||
import Promotion from "./Promotion";
|
||||
import Buttons from "./Buttons";
|
||||
|
||||
|
||||
export default function BuildPc() {
|
||||
|
||||
const [fancyboxRef] = useFancybox({});
|
||||
|
||||
const [activeTab, setActiveTab] = useState(1);
|
||||
const [buildData, setBuildData] = useState<any[]>([]);
|
||||
|
||||
const getStorageKey = (tab: number) => `buildpc_tab_${tab}`;
|
||||
|
||||
// Load data khi đổi tab
|
||||
useEffect(() => {
|
||||
const oldData = localStorage.getItem(getStorageKey(activeTab));
|
||||
setBuildData(oldData ? JSON.parse(oldData) : []);
|
||||
}, [activeTab]);
|
||||
|
||||
|
||||
const handleTabChange = (tabIndex: number) => {
|
||||
|
||||
setActiveTab(tabIndex);
|
||||
};
|
||||
|
||||
}
|
||||
const handleRebuild = () => {
|
||||
const storageKey = getStorageKey(activeTab);
|
||||
|
||||
localStorage.removeItem(storageKey);
|
||||
|
||||
setBuildData([]);
|
||||
|
||||
Fancybox.close();
|
||||
};
|
||||
|
||||
const totalPrice = useMemo(() => {
|
||||
return buildData.reduce((total, item) => {
|
||||
const product = item?.info?.[0];
|
||||
if (!product) return total;
|
||||
|
||||
const price = Number(product.price) || 0;
|
||||
const quantity = Number(product.quantity) || 1;
|
||||
|
||||
return total + (price * quantity);
|
||||
}, 0);
|
||||
}, [buildData]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -60,7 +93,7 @@ export default function BuildPc() {
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="btn-buildpc-group mb-6" id="js-buildpc-tab">
|
||||
<div className="btn-buildpc-group mb-6">
|
||||
{
|
||||
[1, 2, 3, 4, 5].map((item) => (
|
||||
<button
|
||||
@@ -76,21 +109,26 @@ export default function BuildPc() {
|
||||
</div>
|
||||
|
||||
<div className="buildpc-detail-group gap-4">
|
||||
<Promotion />
|
||||
<Promotion total={totalPrice} />
|
||||
|
||||
<Link
|
||||
href="#popup-rebuild_config"
|
||||
data-fancybox
|
||||
data-fancybox=""
|
||||
className="flex items-center gap-3 bg-btn text-white rounded-[30px] leading-10 text-16 font-500 px-6"
|
||||
>
|
||||
LÀM MỚI <i className="bx bx-rotate-ccw" />
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<BuildPCCategories categories={buildPcData.category_config} />
|
||||
<BuildPCCategories
|
||||
categories={ buildPcData.category_config }
|
||||
activeTab={ activeTab }
|
||||
buildData={ buildData }
|
||||
setBuildData={ setBuildData }
|
||||
/>
|
||||
|
||||
<div className="flex flex-wrap items-center justify-between text-18 font-500 leading-6">
|
||||
<Promotion />
|
||||
<Promotion total={totalPrice} />
|
||||
|
||||
<Buttons />
|
||||
</div>
|
||||
@@ -100,7 +138,9 @@ export default function BuildPc() {
|
||||
|
||||
<Content />
|
||||
|
||||
<BuildPcPopups />
|
||||
<BuildPcPopups
|
||||
onRebuild={handleRebuild}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
'use client';
|
||||
import Link from "next/link";
|
||||
import { Fancybox } from "@fancyapps/ui/dist/fancybox/";
|
||||
|
||||
export default function ProductItem({ item, rowId }: any) {
|
||||
export default function ProductItem({ item, rowId, onSelect }: any) {
|
||||
|
||||
const handleBuy = () => {
|
||||
if (typeof window === "undefined") return;
|
||||
|
||||
const storageKey = "buildpc";
|
||||
const oldData = localStorage.getItem(storageKey);
|
||||
const parsed = oldData ? JSON.parse(oldData) : [];
|
||||
const handleSelect = () => {
|
||||
|
||||
const productData = {
|
||||
id : item.productId,
|
||||
@@ -22,24 +16,7 @@ export default function ProductItem({ item, rowId }: any) {
|
||||
warranty : item.warranty || ''
|
||||
};
|
||||
|
||||
const buildIndex = parsed.findIndex((b: any) => b.rowId === rowId);
|
||||
|
||||
if (buildIndex !== -1) {
|
||||
parsed[buildIndex].info = [productData];
|
||||
} else {
|
||||
parsed.push({
|
||||
rowId: rowId,
|
||||
info: [productData]
|
||||
});
|
||||
}
|
||||
|
||||
localStorage.setItem(storageKey, JSON.stringify(parsed));
|
||||
|
||||
// báo cho component cha
|
||||
window.dispatchEvent(new Event("buildpcUpdated"));
|
||||
|
||||
// đóng popup
|
||||
Fancybox.close();
|
||||
onSelect(productData);
|
||||
};
|
||||
|
||||
|
||||
@@ -89,7 +66,7 @@ export default function ProductItem({ item, rowId }: any) {
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={handleBuy}
|
||||
onClick={handleSelect}
|
||||
className="btn-buy p-btn bx bx-plus bg-btn text-white rounded-full w-9 h-9 text-20"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
'use client';
|
||||
|
||||
import { Fancybox } from "@fancyapps/ui/dist/fancybox/";
|
||||
interface Props {
|
||||
product: any;
|
||||
rowId: number;
|
||||
@@ -17,75 +17,83 @@ export default function SelectedItemRow({
|
||||
}: Props) {
|
||||
|
||||
return (
|
||||
<div className="js-item-row">
|
||||
<div className="contain-item-drive">
|
||||
<div className="contain-item-drive">
|
||||
|
||||
<a href={product.url} target="_blank" className="item-img">
|
||||
<img src={product.image} alt={product.name} />
|
||||
</a>
|
||||
<a href={product.url} target="_blank" className="item-img">
|
||||
<img src={product.image} alt={product.name} />
|
||||
</a>
|
||||
|
||||
<div className="item-text">
|
||||
<div className="item-text">
|
||||
|
||||
<div className="item-left">
|
||||
<a href={product.url} target="_blank" className="item-name">
|
||||
{product.name}
|
||||
</a>
|
||||
<div className="item-left">
|
||||
<a href={product.url} target="_blank" className="item-name">
|
||||
{product.name}
|
||||
</a>
|
||||
|
||||
<p>
|
||||
<span className="font-500">- Kho hàng:</span>
|
||||
{product.quantity > 0 ? "Còn hàng" : "Hết hàng"}
|
||||
</p>
|
||||
<p>
|
||||
<span className="font-500">- Kho hàng:</span>
|
||||
{product.quantity > 0 ? "Còn hàng" : "Hết hàng"}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<span className="font-500">- Bảo hành:</span>
|
||||
{product.warranty || "—"}
|
||||
</p>
|
||||
</div>
|
||||
<p>
|
||||
<span className="font-500">- Bảo hành:</span>
|
||||
{product.warranty || "—"}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="item-right">
|
||||
<div className="item-right">
|
||||
|
||||
<div className="item-quantity-group">
|
||||
<b>{product.price.toLocaleString()}</b>
|
||||
<div className="item-quantity-group">
|
||||
<b>{product.price.toLocaleString()}</b>
|
||||
|
||||
<div className="flex items-center" style={{ display: "flex", gap: "10px" }}>
|
||||
<span>x</span>
|
||||
<div className="flex items-center" style={{ display: "flex", gap: "10px" }}>
|
||||
<span>x</span>
|
||||
|
||||
<input
|
||||
type="number"
|
||||
value={product.quantity}
|
||||
className="item-quantity"
|
||||
min={1}
|
||||
max={50}
|
||||
onChange={(e) =>
|
||||
onQuantityChange(rowId, Number(e.target.value))
|
||||
}
|
||||
/>
|
||||
|
||||
<span>=</span>
|
||||
</div>
|
||||
|
||||
<b className="item-price">
|
||||
{(product.price * product.quantity).toLocaleString()}
|
||||
</b>
|
||||
</div>
|
||||
|
||||
<div className="item-button-group">
|
||||
<a href="#js-modal-popup" data-fancybox
|
||||
type="button"
|
||||
title="Thay đổi"
|
||||
className="btn-action_seclect show-popup_select bx bx-edit"
|
||||
onClick={() => onEdit(rowId)}
|
||||
<input
|
||||
type="number"
|
||||
className="item-quantity"
|
||||
value={product.quantity}
|
||||
min={1}
|
||||
max={50}
|
||||
onChange={(e) =>
|
||||
onQuantityChange(rowId, Number(e.target.value))
|
||||
}
|
||||
/>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
title="Xóa"
|
||||
className="btn-action_seclect delete_select bx bx-trash remove-item"
|
||||
onClick={() => onRemove(rowId)}
|
||||
/>
|
||||
<span>=</span>
|
||||
</div>
|
||||
|
||||
<b className="item-price">
|
||||
{(product.price * product.quantity).toLocaleString()}
|
||||
</b>
|
||||
</div>
|
||||
|
||||
<div className="item-button-group">
|
||||
<button
|
||||
type="button"
|
||||
className="btn-action_seclect show-popup_select bx bx-edit"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
|
||||
onEdit(rowId);
|
||||
|
||||
Fancybox.show([
|
||||
{
|
||||
src: "#js-modal-popup",
|
||||
type: "inline",
|
||||
},
|
||||
]);
|
||||
}}
|
||||
/>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
title="Xóa"
|
||||
className="btn-action_seclect delete_select bx bx-trash remove-item"
|
||||
onClick={() => onRemove(rowId)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import { Fancybox } from "@fancyapps/ui/dist/fancybox/";
|
||||
import Filter from "./Filter";
|
||||
import ProductItem from "./Products"
|
||||
import Sort from "./Sort";
|
||||
import Paing from "./Paging";
|
||||
|
||||
export default function ModalContent({ item }: any) {
|
||||
export default function ModalContent({
|
||||
item,
|
||||
onSave
|
||||
}: any) {
|
||||
if (!item) return null;
|
||||
|
||||
const {
|
||||
@@ -69,15 +73,18 @@ export default function ModalContent({ item }: any) {
|
||||
</div>
|
||||
|
||||
<div className="popup-product-list">
|
||||
{
|
||||
product_list.map((item: any) => (
|
||||
<ProductItem
|
||||
rowId={id}
|
||||
key={item.id}
|
||||
item={item}
|
||||
/>
|
||||
))
|
||||
}
|
||||
{product_list.map((product: any) => (
|
||||
<ProductItem
|
||||
key={product.id}
|
||||
rowId={id}
|
||||
item={product}
|
||||
onSelect={(selectedProduct: any) => {
|
||||
onSave(id, selectedProduct);
|
||||
|
||||
Fancybox.close()
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -15,10 +15,6 @@ export default function ProductSearch() {
|
||||
const searchParams = useSearchParams();
|
||||
const search_query = searchParams.get('q') || "";
|
||||
|
||||
const {
|
||||
sort_by_collection,
|
||||
} = productCategory.current_category;
|
||||
|
||||
const totalProduct = useMemo(() => {
|
||||
return productList.flatMap((item: any) => item.list);
|
||||
}, []);
|
||||
@@ -70,7 +66,10 @@ export default function ProductSearch() {
|
||||
<div className="col-right-group w-[968px]">
|
||||
<div className="box-item rounded-[24px] bg-white px-6 pt-4 pb-8 mb-4">
|
||||
|
||||
<SortByCollection sort={sort_by_collection} total={total} />
|
||||
<SortByCollection
|
||||
sort={productCategory.current_category.sort_by_collection}
|
||||
total={total}
|
||||
/>
|
||||
|
||||
<div className="product-holder grid grid-cols-4 gap-x-5 gap-y-8 mb-6">
|
||||
{currentData.map((item: any) => (
|
||||
@@ -89,8 +88,8 @@ export default function ProductSearch() {
|
||||
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -38,7 +38,7 @@
|
||||
.buildpc-page .contain-item-drive .item-quantity-group > span{text-align:center;padding:0 5px}
|
||||
.buildpc-page .contain-item-drive .item-quantity-group > b {width: calc(50% - 65px)}
|
||||
.buildpc-page .contain-item-drive .item-quantity-group .item-price{color:#f71400}
|
||||
.buildpc-page .contain-item-drive .btn-action_seclect{cursor: pointer;border: 1px solid #DFE4EC;border-radius: 50%;width: 40px;height: 40px;line-height: 39px;font-size: 20px;color: #0678DB;background: #EAF1FF;}
|
||||
.buildpc-page .contain-item-drive .btn-action_seclect{cursor: pointer;border: 1px solid #DFE4EC;border-radius: 50%;width: 40px;height: 40px;line-height: 39px;font-size: 20px;color: #0678DB;background: #EAF1FF;text-align: center;}
|
||||
.buildpc-page .contain-item-drive .delete_select{border-color:#E7D9D9;background: #F8F3F3;color: #BE1F2D}
|
||||
.buildpc-page .contain-item-drive input::-webkit-outer-spin-button,.buildpc-page .contain-item-drive input::-webkit-inner-spin-button{-webkit-appearance:none}
|
||||
.buildpc-page .contain-item-drive input[type=number]{-moz-appearance:textfield}
|
||||
|
||||
Reference in New Issue
Block a user