This commit is contained in:
2025-12-29 17:29:51 +07:00
parent 1bb5ad52ed
commit bf063f244c
14 changed files with 2922 additions and 66 deletions

View File

@@ -0,0 +1,82 @@
import { useState } from 'react';
export const FormCart = () => {
const [showTax, setShowTax] = useState(false);
return (
<>
<div className="box-cart-info-customer">
<p className="title-section-cart font-[600]">Thông tin khách hàng</p>
<div className="list-info-customer">
<input type="text" placeholder="Họ tên*" name="user_info[name]" id="buyer_name" />
<div className="flex justify-between gap-2">
<input type="text" placeholder="Số điện thoại*" name="user_info[tel]" id="buyer_tel" />
<input type="text" name="user_info[email]" id="buyer_email" placeholder="Email" />
</div>
<input type="text" placeholder="Địa chỉ*" id="buyer_address" name="user_info[address]" />
<div className="flex justify-between gap-2">
<select name="user_info[province]" className="text-black" id="buyer_province">
<option value="0">Tỉnh/Thành phố</option>
<option value=""> Nội</option>
</select>
<select name="user_info[district]" id="js-district-holder">
<option value="0">Quận/Huyện</option>
</select>
</div>
<textarea placeholder="Ghi chú" name="user_info[note]" id="buyer_note"></textarea>
<div className="form-group-taxt">
<label className="tax-title label flex items-center gap-2">
<input
type="checkbox"
className="w-[20px]"
checked={showTax}
onChange={(e) => setShowTax(e.target.checked)}
/>
Yêu cầu xuất hóa đơn công ty
</label>
</div>
{showTax && (
<div className="js-tax-group">
<div className="form-group row">
<div className="input-taxt">
<input
type="text"
id="txtTaxName"
placeholder="Tên công ty"
className="form-control"
name="user_info[tax_company]"
/>
</div>
</div>
<div className="form-group row">
<div className="input-taxt">
<input
type="text"
id="txtTaxAddress"
placeholder="Địa chỉ công ty"
className="form-control"
name="user_info[tax_address]"
/>
</div>
</div>
<div className="form-group row">
<div className="input-taxt">
<input
type="text"
id="txtTaxCode"
placeholder="Mã số thuế"
className="form-control"
name="user_info[tax_code]"
/>
</div>
</div>
</div>
)}
</div>
</div>
</>
);
};

View File

@@ -0,0 +1,110 @@
import Image from 'next/image';
import Link from 'next/link';
import { TypeCartItem } from '@/types/cart';
import { FaSortDown, FaTrashCan } from 'react-icons/fa6';
import { formatCurrency } from '@/lib/formatPrice';
interface PropsCart {
item: TypeCartItem;
onUpdate: (id: string, quantity: number) => void;
onDelete: (id: string) => void;
}
export const ItemCart: React.FC<PropsCart> = ({ item, onUpdate, onDelete }) => {
const handleChangeQuantity = (delta: number) => {
const newQuantity = Math.max(1, parseInt(item.in_cart.quantity) + delta);
onUpdate(item._id, newQuantity);
};
return (
<div className="cart-item-info js-item-row flex justify-between">
<div className="cart-item-left flex">
<Link className="cart-item-img relative" href={item.item_info.productUrl}>
<Image
src={item.item_info.productImage.large}
alt="model"
className="bk-product-image lazy"
width={100}
height={100}
/>
{item.item_info.sale_rules?.type == 'deal' && (
<Image
className="icon-deal-cart lazy"
src="https://nguyencongpc.vn/static/assets/nguyencong_2023/images/static-icon-cart-deal.png"
width={100}
height={100}
alt="deal"
/>
)}
</Link>
<div className="cart-info-item flex-1">
<Link
href={item.item_info.productUrl}
className="cart-item-name bk-product-name line-clamp-2"
>
{item.item_info.productName}
</Link>
{item.item_info.specialOffer?.all && (
<div className="item-offer relative mt-3">
<p className="title flex items-center pl-0">
Khuyến mại{' '}
<span className="flex gap-2">
{' '}
(Chi tiết)
<FaSortDown />
</span>
</p>
<div className="item-offer-content">
{item.item_info.specialOffer.all.map((_item, idx) => (
<div key={idx} dangerouslySetInnerHTML={{ __html: _item.title }} />
))}
</div>
</div>
)}
</div>
<div className="box-change-quantity flex items-center">
<button
onClick={() => handleChangeQuantity(-1)}
className="js-quantity-change quantity-change flex items-center"
data-value="-1"
>
-
</button>
<input
type="text"
className="js-buy-quantity js-quantity-change bk-product-qty font-bold"
value={item.in_cart.quantity}
onChange={() => handleChangeQuantity(1)}
/>
<button
onClick={() => handleChangeQuantity(1)}
className="js-quantity-change quantity-change flex items-center"
data-value="1"
>
+
</button>
</div>
</div>
<div className="box-item-right flex flex-col items-end justify-between">
<div className="price-cart-item">
{item.in_cart.price == '0' ? (
<p className="price cart-item-price item-cart-price js-total-item-price font-bold">
0 đ
</p>
) : (
<p className="price cart-item-price item-cart-price js-total-item-price font-bold">
{formatCurrency(item.in_cart.total_price)} đ
</p>
)}
</div>
<button
onClick={() => onDelete(item._id)}
className="delete-item-cart item-cart-icon js-delete-item flex cursor-pointer items-center justify-center"
>
<FaTrashCan />
</button>
</div>
</div>
);
};

View File

@@ -0,0 +1,179 @@
'use client';
import { useState } from 'react';
import Image from 'next/image';
import Link from 'next/link';
import { FaChevronLeft } from 'react-icons/fa6';
import { Breadcrumb } from '@/components/Common/Breadcrumb';
import { TypeCartItem } from '@/types/cart';
import { ItemCart } from './ItemCart';
import { FormCart } from './FormCart';
import { formatCurrency } from '@/lib/formatPrice';
const HomeCart = () => {
const breadcrumbItems = [{ name: 'Giỏ hàng', url: '/cart' }];
const [cart, setCart] = useState<TypeCartItem[]>(() => {
const storedCart = localStorage.getItem('cart');
return storedCart ? JSON.parse(storedCart) : [];
});
const [payMethod, setPayMethod] = useState('2');
const updateCartItem = (id: string, quantity: number) => {
const newCart = cart.map((item) =>
item._id === id
? {
...item,
in_cart: {
...item.in_cart,
quantity: quantity.toString(),
total_price: quantity * Number(item.in_cart.price),
},
}
: item,
);
setCart(newCart);
localStorage.setItem('cart', JSON.stringify(newCart));
};
const deleteCartItem = (id: string) => {
const isConfirm = confirm('Bạn có chắc chắn xóa sản phẩm này không ?');
if (isConfirm) {
const newCart = cart.filter((item) => item._id !== id);
setCart(newCart);
localStorage.setItem('cart', JSON.stringify(newCart));
}
};
const deleteCart = () => {
const isConfirm = confirm('Bạn có chắc chắn xóa sản phẩm này không ?');
if (isConfirm) {
setCart([]);
localStorage.removeItem('cart');
}
};
// tính tổng tiền
const getTotalPrice = () => {
return formatCurrency(cart.reduce((sum, item) => sum + Number(item.in_cart.total_price), 0));
};
return (
<>
<div className="container">
<Breadcrumb items={breadcrumbItems} />
</div>
<section className="page-cart">
{cart.length === 0 ? (
<div className="not-cart pt-5">
<Image
src="https://nguyencongpc.vn/static/assets/nguyencong_2023/images/cart-home-min.png"
className="lazy"
width={130}
height={130}
alt="icon-cart"
/>
<p>Không sản phẩm nào trong giỏ hàng của bạn.</p>
<Link href="/" className="back-cart">
Tiết tục mua sắm
</Link>
</div>
) : (
<>
<div className="container-cart cart-header-title flex items-center justify-between">
<p>Giỏ hàng của bạn</p>
<Link className="back-homepage flex items-center gap-2" href="/">
<FaChevronLeft />
Mua thêm sản phẩm khác
</Link>
</div>
<div className="box-info-cart container-cart">
<div className="box-delete-all flex justify-end">
<button className="delete-cart-all" onClick={() => deleteCart()}>
{' '}
Xóa giỏ hàng{' '}
</button>
</div>
<div className="box-cart-item-list">
{cart.map((item, index) => (
<ItemCart
item={item}
key={index}
onUpdate={updateCartItem}
onDelete={deleteCartItem}
/>
))}
</div>
{/* form mua hàng */}
<FormCart />
<div className="box-payment">
<p className="title-section-cart font-bold">Phương thức thanh toán</p>
<div className="list-method-payment">
<label className="label">
<input
type="radio"
name="pay_method"
id="pay2"
value="2"
checked={payMethod === '2'}
onChange={(e) => setPayMethod(e.target.value)}
/>
Thanh toán khi nhận hàng
</label>
</div>
<p className="title-section-cart font-bold">Tổng tiền</p>
<div className="list-price">
<p className="price-total1 flex items-center justify-between">
<b className="txt">Tổng cộng</b>
<b className="price js-total-before-fee-cart-price" id="total-cart-price">
{getTotalPrice()}
</b>
</p>
<p className="price-total2 flex items-center justify-between">
<b className="txt">Thành tiền</b>
<b className="price color-red js-total-cart-price font-bold">
{getTotalPrice()} đ
</b>
</p>
<span className="has-vat">(Giá đã bao gồm VAT)</span>
</div>
</div>
<div className="list-btn-cart">
<button type="submit" className="js-send-cart font-bold">
Đt hàng
</button>
<div className="list-print-cart flex justify-between gap-2">
<Link
href="/export_file.php?file_type=xls&content_type=shopping-cart"
className="down-excel font-bold"
target="_blank"
>
Tải file excel
</Link>
<Link
href="javascript:void(0)"
rel="nofollow"
className="down-img-cart font-bold"
>
Tải nh báo giá
</Link>
<Link
href="/print/user.php?view=cart"
target="_blank"
className="print-cart font-bold"
>
in báo giá
</Link>
</div>
</div>
</div>
</>
)}
</section>
</>
);
};
export default HomeCart;

View File

@@ -44,7 +44,7 @@ const ItemProduct: React.FC<ProductItemProps> = ({ item }) => {
{item.marketPrice.toLocaleString()}
<u>đ</u>
</p>
<div className="product-percent-price">-{Math.round(item.price_off)} %</div>
<div className="product-percent-price">-{Math.round(Number(item.price_off))} %</div>
</div>
) : (
<div className="product-martket-main flex items-center"></div>

View File

@@ -1,11 +1,30 @@
'use client';
import React, { useState } from 'react';
import React, { useState, useEffect } from 'react';
import Image from 'next/image';
import Link from 'next/link';
import { FaMapMarkerAlt, FaBars } from 'react-icons/fa';
import BoxShowroom from '@/components/Common/BoxShowroom';
import { TypeCartItem } from '@/types/cart';
import { formatCurrency } from '@/lib/formatPrice';
const HeaderMid: React.FC = () => {
const [cartCount, setCartCount] = useState(() => {
const storedCart = localStorage.getItem('cart');
return storedCart ? JSON.parse(storedCart).length : 0;
});
const [cart, setCart] = useState<TypeCartItem[]>(() => {
const storedCart = localStorage.getItem('cart');
return storedCart ? JSON.parse(storedCart) : [];
});
const [cartQuantity, setCartQuantity] = useState(() => {
return cart.reduce((sum: number, item) => sum + Number(item.in_cart.quantity), 0);
});
const [cartTotal, setCartTotal] = useState(() => {
return cart.reduce((sum: number, item) => sum + Number(item.in_cart.total_price), 0);
});
const PopupAddress = () => {
const modal = document.getElementById('boxShowroom') as HTMLDialogElement;
modal?.showModal();
@@ -63,7 +82,7 @@ const HeaderMid: React.FC = () => {
</div>
<div className="box-tabs-header flex items-center">
<Link href="/buildpc" className="item-tab-header flex-column flex items-center gap-4">
<Link href="/buildpc" className="item-tab-header flex flex-col items-center gap-4">
<p className="icon-item-tab flex items-center justify-center">
<i className="sprite sprite-buildpc-header"></i>
</p>
@@ -72,7 +91,7 @@ const HeaderMid: React.FC = () => {
<Link
href="javascript:void(0)"
className="item-tab-header flex-column flex items-center gap-4"
className="item-tab-header flex flex-col items-center gap-4"
>
<p className="icon-item-tab flex items-center justify-center">
<i className="sprite sprite-lienhe-header"></i>
@@ -80,46 +99,83 @@ const HeaderMid: React.FC = () => {
<span className="font-500">Khách hàng liên hệ</span>
</Link>
<Link href="/tin-tuc" className="item-tab-header flex-column flex items-center gap-4">
<Link href="/tin-tuc" className="item-tab-header flex flex-col items-center gap-4">
<p className="icon-item-tab flex items-center justify-center">
<i className="sprite sprite-article-header"></i>
</p>
<span className="font-weight-500">Tin tức công nghệ</span>
</Link>
<div id="js-header-cart" className="position-relative">
<Link href="/cart" className="item-tab-header flex-column flex items-center gap-4">
<div id="js-header-cart" className="relative">
<Link href="/cart" className="item-tab-header flex flex-col items-center gap-4">
<p className="icon-item-tab icon-cart-header flex items-center justify-center">
<i className="sprite sprite-cart-header"></i>
<u className="cart-count header-features-cart-amount">1</u>
<u className="cart-count header-features-cart-amount">{cartCount}</u>
</p>
<span className="font-weight-500">Giỏ hàng</span>
</Link>
<div className="cau-noi"></div>
<div className="cart-ttip" id="js-cart-tooltip">
<div className="cart-ttip-item-container"></div>
<div className="cart-ttip-price justify-content-end flex items-center gap-6">
<div className="cart-ttip-item-container">
{cart.map((item, index) => (
<div
className="compare-item js-compare-item flex items-center gap-2"
key={index}
>
<Link className="img-compare" href={item.item_info.productUrl}>
<Image
src={item.item_info.productImage.large}
width={80}
height={80}
alt={item.item_info.productName}
/>
</Link>
<div className="compare-item-text flex-1">
<Link
href={item.item_info.productUrl}
className="name-compare-item mb-10 line-clamp-2"
>
{item.item_info.productName}
</Link>
<div className="header-cart-item-price flex justify-between">
<b>x {item.in_cart.quantity}</b>
<b className="price-compare">
{item.in_cart.price == '0'
? 'Liên Hệ'
: `${formatCurrency(item.in_cart.total_price)} đ`}
</b>
</div>
</div>
</div>
))}
{/* end item */}
</div>
<div className="cart-ttip-price flex items-center justify-end gap-2">
<p>Tổng tiền hàng</p>
<p id="js-header-cart-quantity" className="font-weight-500"></p>
<p id="js-header-cart-total-price" className="font-weight-700"></p>
<p id="js-header-cart-quantity" className="font-[500]">
({cartQuantity} sản phẩm)
</p>
<p id="js-header-cart-total-price" className="font-bold">
{formatCurrency(cartTotal)}đ
</p>
</div>
<Link
href="/cart"
className="cart-ttip-price-button flex items-center justify-center"
>
<p className="font-weight-700">THANH TOÁN NGAY </p>
<p className="font-bold">THANH TOÁN NGAY </p>
</Link>
</div>
</div>
<Link
href="/taikhoan"
className="user-header item-tab-header flex-column flex items-center gap-4"
className="user-header item-tab-header flex flex-col items-center gap-4"
>
<p className="icon-item-tab flex items-center justify-center">
<i className="sprite sprite-account-header"></i>
</p>
<span className="font-weight-500">Tài khoản</span>
<span className="font-[500]">Tài khoản</span>
</Link>
</div>
</div>

View File

@@ -1,10 +1,22 @@
'use client';
import { useRouter } from 'next/navigation';
import type { ProductDetailData } from '@/types';
import Link from 'next/link';
import { BoxPrice } from './BoxPrice';
import { BoxBought } from './BoxBought';
// thêm giỏ hàng
import { addToCart } from '@/lib/ButtonCart';
export const BoxInfoRight = (item: ProductDetailData) => {
const router = useRouter();
const handleBuyNow = () => {
router.push('/cart');
addToCart(item.product_info.productId);
};
return (
<>
<h1 className="product-name color-black line-clamp-3 font-bold">
@@ -81,32 +93,42 @@ export const BoxInfoRight = (item: ProductDetailData) => {
+{' '}
</p>
</div>
<Link href="#" className="addCart flex flex-wrap items-center justify-center gap-3">
<button
className="addCart flex cursor-pointer flex-wrap items-center justify-center gap-3"
onClick={() => {
addToCart(item.product_info.productId);
alert('Sản phẩm đã được thêm vào giỏ hàng!');
}}
>
<i className="sprite sprite-cart-detail"></i>
<p className="title-cart">Thêm vào giỏ hàng</p>
</Link>
</button>
<input type="hidden" className="js-buy-quantity-temp" value="1" />
</div>
<div id="detail-buy-ads" className="detail-buy grid grid-cols-2 gap-2">
<Link href="#" className="detail-buy-now col-span-2">
<div
id="detail-buy-ads"
className="detail-buy grid grid-cols-2 gap-2"
onClick={() => handleBuyNow()}
>
<button className="detail-buy-now col-span-2 cursor-pointer">
<span>ĐT MUA NGAY</span>
Giao hàng tận nơi nhanh chóng
</Link>
</button>
<Link href="#" className="detail-add-cart">
<button className="detail-add-cart">
<span>TRẢ GÓP QUA HỒ </span>
Chỉ từ 2.665.000/ tháng
</Link>
</button>
<Link href="#" className="detail-add-cart">
<button className="detail-add-cart">
<span>TRẢ GÓP QUA THẺ</span>
Chỉ từ 1.332.500/ tháng
</Link>
</button>
</div>
</>
)}
n{/* yên tâm mua hàng */}
{/* 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">

View File

@@ -28,7 +28,7 @@ export const ListComment = () => {
</div>
</div>{' '}
{/* content */}
<div className="comment-content mt-3 rounded p-2">
<div className="comment-content relative 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>