diff --git a/public/images/404-page-1.png b/public/images/404-page-1.png new file mode 100644 index 0000000..76b62b8 Binary files /dev/null and b/public/images/404-page-1.png differ diff --git a/src/app/[slug]/page.tsx b/src/app/[slug]/page.tsx index 351c6c8..86158bb 100644 --- a/src/app/[slug]/page.tsx +++ b/src/app/[slug]/page.tsx @@ -11,13 +11,18 @@ import ArticleHome from "@/components/article/home"; export default async function SlugPage({ params, }: { - params: { slug: string }; + params: Promise<{ slug: string }>; }) { - const { slug } = await params; - if (!slug) return notFound(); - + const { slug } = await params; + if (!slug) { + notFound(); + } + const result = findBySlug(slug); - if (!result) return notFound(); + + if (!result) { + notFound(); + } switch (result.type) { case "product_category": @@ -36,6 +41,7 @@ export default async function SlugPage({ return ; default: - return notFound(); + const _exhaustive: never = result; + notFound(); } } diff --git a/src/app/not-found.tsx b/src/app/not-found.tsx index 107ffd4..509490e 100644 --- a/src/app/not-found.tsx +++ b/src/app/not-found.tsx @@ -1,8 +1,21 @@ +import Link from "next/link" + export default function NotFound() { return ( -
-

404

-

Trang không tồn tại

+
+
+
+ Không tìm thấy +

Không Tìm Thấy

+

+ Xin lỗi, nhưng trang bạn yêu cầu không tìm thấy hoặc đã bị xóa bỏ. Vui lòng thử lại. +

+ + ← Quay lại trang chủ + +
+
+ ); } diff --git a/src/components/home/Product/CategoryIcon.tsx b/src/components/home/Product/CategoryIcon.tsx new file mode 100644 index 0000000..5590b60 --- /dev/null +++ b/src/components/home/Product/CategoryIcon.tsx @@ -0,0 +1,34 @@ +export default function CategoryIcon({item}:any) { + return( + <> + {item == 1 && +

+ + Trả góp 0% +

+ } + + + {item == 91 || item == 27 && +

+ + Miễn phí giao hàng +

+ } + + {item == 103 && +

+ + Giao hàng toàn quốc +

+ } + + {item == 92 && +

+ + Bảo hành tận nơi +

+ } + + ) +} \ No newline at end of file diff --git a/src/components/home/Product/index.tsx b/src/components/home/Product/index.tsx index 8c13d40..886233f 100644 --- a/src/components/home/Product/index.tsx +++ b/src/components/home/Product/index.tsx @@ -1,169 +1,86 @@ 'use client'; import { Swiper, SwiperSlide } from 'swiper/react'; import { Navigation, Pagination, Autoplay } from 'swiper/modules'; +import { categories } from "@/data/categories" +import { productList } from '@/data/products'; +import CategoryIcon from "./CategoryIcon" +import ProductItem from "@/components/shared/ProductItem" export default function ProductCategories() { + + const { all_category } = categories.product; + const featuredCategories = all_category.filter((item: any) => item.is_featured === 1); + return ( -
-
- - - -
+ featuredCategories.map((item: any) => { -
-
-
-

PC Render, Edit Video

+ const categoryProducts = productList.filter( + (product: any) => product.id === item.id + ); + + return( +
+ +
+
+
+

{item.title}

-

- - Trả góp 0% -

- -

- - Miễn phí giao hàng -

- -

- - Giao hàng toàn quốc -

- -

- - Bảo hành tận nơi -

-
- - -
- -
-
-
-
-
- - - -
- - - - -
-
- 52.000.000 đ - -10% -

22.000.000 đ

-
- - -

Lorem ipsum dolor sit, amet consectetur adipisicing elit. Suscipit quos obcaecati totam, atque vel

-
- -
-
-

- - Sẵn hàng -

- - {/*

- - Liên hệ -

*/} - -

- - Quà tặng -

-
- - -
-
- -
-
-

- [Tặng bàn phím] HHPC ULTRA 7 265K | 32GB DDR5 | NVIDIA RTX 3060 12GB -

- -
-
-

- Giá bán: - 48.990.000 đ - 52.000.000 đ - -6% -

- -

- Bảo hành: - Theo từng linh kiện -

-
- -
-

- - Thông số sản phẩm -

- -
-
CPU: INTEL CORE i5 13400F up 4.6GHz | 10 CORE | 16 THREAD
-
RAM: DDR4 16GB (1x16G) 3200 MHz
-
VGA: NVIDIA RTX 3060 12GB GDDR6
-
-
- -
-

- - Khuyến mại hấp dẫn -

- -
-

⭐ Bảo Hành Tại Nơi Sử Dụng (Áp Dụng Nội Thành Hà Nội và Hồ Chí Minh)

-

⭐ Bảo Hành Siêu Tốc 1 Đổi 1 Trong 24h

⭐ Miễn Phí 100% Vận Chuyển Toàn Quốc

-
-
- -
-
-
+
-
-

[Tặng bàn phím] HHPC ULTRA 7 265K | 32GB DDR5 | NVIDIA RTX 3060 12GB

Giá bán:48.990.000 đ52.000.000 đ-6%

Bảo hành:Theo từng linh kiện

Thông số sản phẩm

CPU: INTEL CORE i5 13400F up 4.6GHz | 10 CORE | 16 THREAD
RAM: DDR4 16GB (1x16G) 3200 MHz
VGA: NVIDIA RTX 3060 12GB GDDR6

[Tặng bàn phím] HHPC ULTRA 7 265K | 32GB DDR5 | NVIDIA RTX 3060 12GB

Giá bán:48.990.000 đ52.000.000 đ-6%

Bảo hành:Theo từng linh kiện

Thông số sản phẩm

CPU: INTEL CORE i5 13400F up 4.6GHz | 10 CORE | 16 THREAD
RAM: DDR4 16GB (1x16G) 3200 MHz
VGA: NVIDIA RTX 3060 12GB GDDR6

Khuyến mại hấp dẫn

⭐ Bảo Hành Tại Nơi Sử Dụng (Áp Dụng Nội Thành Hà Nội và Hồ Chí Minh)

⭐ Bảo Hành Siêu Tốc 1 Đổi 1 Trong 24h

⭐ Miễn Phí 100% Vận Chuyển Toàn Quốc

[Tặng bàn phím] HHPC ULTRA 7 265K | 32GB DDR5 | NVIDIA RTX 3060 12GB

Giá bán:48.990.000 đ52.000.000 đ-6%

Bảo hành:Theo từng linh kiện

[Tặng bàn phím] HHPC ULTRA 7 265K | 32GB DDR5 | NVIDIA RTX 3060 12GB

Giá bán:48.990.000 đ

Bảo hành:Theo từng linh kiện

[Tặng bàn phím] HHPC ULTRA 7 265K | 32GB DDR5 | NVIDIA RTX 3060 12GB

Giá bán:48.990.000 đ52.000.000 đ-6%

Bảo hành:Theo từng linh kiện

Thông số sản phẩm

CPU: INTEL CORE i5 13400F up 4.6GHz | 10 CORE | 16 THREAD
RAM: DDR4 16GB (1x16G) 3200 MHz
VGA: NVIDIA RTX 3060 12GB GDDR6

Khuyến mại hấp dẫn

⭐ Bảo Hành Tại Nơi Sử Dụng (Áp Dụng Nội Thành Hà Nội và Hồ Chí Minh)

⭐ Bảo Hành Siêu Tốc 1 Đổi 1 Trong 24h

⭐ Miễn Phí 100% Vận Chuyển Toàn Quốc

[Tặng bàn phím] HHPC ULTRA 7 265K | 32GB DDR5 | NVIDIA RTX 3060 12GB

Giá bán:48.990.000 đ52.000.000 đ-6%

Bảo hành:Theo từng linh kiện

Thông số sản phẩm

CPU: INTEL CORE i5 13400F up 4.6GHz | 10 CORE | 16 THREAD
RAM: DDR4 16GB (1x16G) 3200 MHz
VGA: NVIDIA RTX 3060 12GB GDDR6

Khuyến mại hấp dẫn

⭐ Bảo Hành Tại Nơi Sử Dụng (Áp Dụng Nội Thành Hà Nội và Hồ Chí Minh)

⭐ Bảo Hành Siêu Tốc 1 Đổi 1 Trong 24h

⭐ Miễn Phí 100% Vận Chuyển Toàn Quốc

-
+
+ {item.children.length > 0 && + item.children + .slice(0, 4) + .map((child: any) => + {child.title} + ) + } + Xem tất cả +
+
+ +
+
+
+
+
+ + 5} + autoplay={{ + delay: 3000, + disableOnInteraction: false, + }} + navigation={{ + prevEl: '.custom-nav .swiper-button-prev', + nextEl: '.custom-nav .swiper-button-next', + }} + breakpoints={{ + 1600: { + slidesPerView: 6, + }, + }} + > + {categoryProducts[0].list.map( (item:any) => + + + + )} + +
+
-
-
+ ) + + }) ) -} \ No newline at end of file +} + + diff --git a/src/components/home/Category/index.tsx b/src/components/home/featured-category/index.tsx similarity index 59% rename from src/components/home/Category/index.tsx rename to src/components/home/featured-category/index.tsx index 33b9e63..698b06d 100644 --- a/src/components/home/Category/index.tsx +++ b/src/components/home/featured-category/index.tsx @@ -2,20 +2,39 @@ import { categories } from "@/data/categories"; export default function FeaturedProductCategories() { const {all_category} = categories.product; + + const getAllCategoriesFlat = (cats: any[]): any[] => { + const result: any[] = []; + + cats.forEach((cat) => { + result.push(cat); + + if (cat.children && cat.children.length > 0) { + result.push(...getAllCategoriesFlat(cat.children)); + } + }); + + return result; + }; + + const allCategoriesFlat = getAllCategoriesFlat(all_category); + // Lọc các danh mục nổi bật + const featuredCategories = allCategoriesFlat.filter( + (item: any) => item.is_featured === 1 + ); return ( <> - {all_category && + {featuredCategories.length &&

Danh mục nổi bật

- {all_category - .filter( (item:any) => item.is_featured === 1 ) + {featuredCategories .map( (item:any) => diff --git a/src/components/home/index.tsx b/src/components/home/index.tsx index e13bc53..4520860 100644 --- a/src/components/home/index.tsx +++ b/src/components/home/index.tsx @@ -1,6 +1,6 @@ import Slider from "./slider"; import Deal from "./deal"; -import FeaturedProductCategories from "./category"; +import FeaturedProductCategories from "./featured-category"; import ProductCategories from "./product"; import Article from "./article"; diff --git a/src/components/other/Header/Cart.tsx b/src/components/other/Header/Cart.tsx index 4e2d2c4..430c8bd 100644 --- a/src/components/other/Header/Cart.tsx +++ b/src/components/other/Header/Cart.tsx @@ -1,18 +1,47 @@ import Link from "next/link"; import { useCart } from "@/hooks/useCart"; -import { getAllProducts, formatPrice } from "@/lib/utils" +import { getAllProducts } from "@/lib/utils" +import CartItem from "@/components/other/header/cart/index" export default function Cart() { + const { + cartItems, + cartCount, + totalItems, + loading, + } = useCart(); - const { cartCount, cartIds, loading } = useCart(); - const allProducts = getAllProducts(); + const allProducts = getAllProducts(); + + // Lọc sản phẩm có trong giỏ hàng với quantity + const productsInCart = cartItems + .map(cartItem => { + const product = allProducts.find(p => p.id === cartItem.id); + if (product) { + return { + ...product, + cartQuantity: cartItem.cartQuantity, + }; + } + return null; + }) + .filter(item => item !== null); + + + // Tính tổng tiền + const totalPrice = productsInCart.reduce((sum, item) => { + if (item && item.price) { + return sum + (item.price * item.quantity); + } + return sum; + }, 0); - const productsInCart = allProducts.filter(p => cartIds.includes(p.id) ); - console.log(productsInCart); + const hasProducts = cartCount > 0; + return ( <> -
+
{cartCount} @@ -20,39 +49,24 @@ export default function Cart() { Giỏ
Hàng
- {productsInCart.length > 0 && + {!loading && hasProducts &&
+

+ 1 số sp thêm từ DEAL sẽ không có trong DB tĩnh nên không hiển thị +

{productsInCart.map((item:any) => -
- - image - - -
- - {item.productName} - - -

- x1 - - {item.price > 0 ? formatPrice(item.price) +'đ' : 'Liên hệ'} - -

-
-
+ )} -

1 số sp không có trong DB tĩnh

Tổng tiền hàng - ({cartCount} sản phẩm): - 10.770.000đ + ({totalItems} sản phẩm): + {totalPrice}đ

THANH TOÁN NGAY
diff --git a/src/components/other/Header/cart/index.tsx b/src/components/other/Header/cart/index.tsx new file mode 100644 index 0000000..83470e3 --- /dev/null +++ b/src/components/other/Header/cart/index.tsx @@ -0,0 +1,30 @@ +import Link from "next/link"; +import { formatPrice } from "@/lib/utils"; + +export default function CartItem({item}:any) { + return( +
+ + {item.productName} + + +
+ + {item.productName} + + +

+ x{item.cartQuantity} + + {item.price > 0 ? formatPrice(item.price) +'đ' : 'Liên hệ'} + +

+
+
+ ) +} \ No newline at end of file diff --git a/src/components/shared/DealItem.tsx b/src/components/shared/DealItem.tsx index 28fac7f..1cba834 100644 --- a/src/components/shared/DealItem.tsx +++ b/src/components/shared/DealItem.tsx @@ -26,6 +26,7 @@ export default function DealItem( {item} : any) { -{discount}% ) : null; + const checkIncart = isInCart(productInfo.id); return (
@@ -51,9 +52,12 @@ export default function DealItem( {item} : any) { {discountView}
-
diff --git a/src/components/shared/ProductItem.tsx b/src/components/shared/ProductItem.tsx new file mode 100644 index 0000000..54ebc95 --- /dev/null +++ b/src/components/shared/ProductItem.tsx @@ -0,0 +1,109 @@ +'use client'; +import Link from "next/link"; +import { formatPrice } from "@/lib/utils"; + +export default function ProductItem({item}:any){ + console.log('ProductItem: ', item) + return ( +
+ + {item.productName} + + +
+
+ 52.000.000 đ + -10% +

22.000.000 đ

+
+ + +

{item.productName}

+ + +
+
+

+ + Sẵn hàng +

+ + {/*

+ + Liên hệ +

*/} + +

+ + Quà tặng +

+
+ + + +
+
+ +
+
+

+ [Tặng bàn phím] HHPC ULTRA 7 265K | 32GB DDR5 | NVIDIA RTX 3060 12GB +

+ +
+
+

+ Giá bán: + 48.990.000 đ + 52.000.000 đ + -6% +

+ +

+ Bảo hành: + Theo từng linh kiện +

+
+ +
+

+ + Thông số sản phẩm +

+ +
+
CPU: INTEL CORE i5 13400F up 4.6GHz | 10 CORE | 16 THREAD
+
RAM: DDR4 16GB (1x16G) 3200 MHz
+
VGA: NVIDIA RTX 3060 12GB GDDR6
+
+
+ +
+

+ + Khuyến mại hấp dẫn +

+ +
+

⭐ Bảo Hành Tại Nơi Sử Dụng (Áp Dụng Nội Thành Hà Nội và Hồ Chí Minh)

+

⭐ Bảo Hành Siêu Tốc 1 Đổi 1 Trong 24h

⭐ Miễn Phí 100% Vận Chuyển Toàn Quốc

+
+
+ +
+
+
+
+ ) +} \ No newline at end of file diff --git a/src/data/categories/index.tsx b/src/data/categories/index.tsx index 4a33f9d..4e66b66 100644 --- a/src/data/categories/index.tsx +++ b/src/data/categories/index.tsx @@ -478,7 +478,7 @@ export const categories = { id : 27, parentId : 0, isParent : 1, - is_featured : 1, + is_featured : 0, type: "product", title : 'PC Gaming, Học Tập', url : '/pc-gaming', @@ -518,7 +518,7 @@ export const categories = { { id : 93, parentId : 0, - isParent : 1, + isParent : 1, is_featured : 0, type: "product", title : 'PC Văn Phòng', diff --git a/src/data/products/index.tsx b/src/data/products/index.tsx index 5af121f..5684093 100644 --- a/src/data/products/index.tsx +++ b/src/data/products/index.tsx @@ -1288,8 +1288,8 @@ export const productList = [ "total": 7, "list": [ { - "id": 5965, - "productId": 5965, + "id": 6667, + "productId": 6667, "priceUnit": "chi\u1ebfc", "marketPrice": 11000000, "price": 10600000, @@ -1407,8 +1407,8 @@ export const productList = [ ] }, { - "id": 5210, - "productId": 5210, + "id": 6111, + "productId": 6111, "priceUnit": "chi\u1ebfc", "marketPrice": 23000000, "price": 21810000, @@ -2095,8 +2095,8 @@ export const productList = [ "total": 10, "list": [ { - "id": 5401, - "productId": 5401, + "id": 6110, + "productId": 6110, "priceUnit": "chi\u1ebfc", "marketPrice": 0, "price": 12990000, diff --git a/src/hooks/useCart.ts b/src/hooks/useCart.ts index fc8a288..43b73fe 100644 --- a/src/hooks/useCart.ts +++ b/src/hooks/useCart.ts @@ -2,52 +2,120 @@ import { useEffect, useState, useCallback } from 'react'; import { - getCartProductIds, + getCartItems, addProductToCart, removeProductFromCart, clearCart, + increaseQuantity, + decreaseQuantity, + updateQuantity, + getProductQuantity, + isProductInCart, + CART_CHANGE_EVENT, + type CartItem, } from '../services/cart'; export function useCart() { - const [cartIds, setCartIds] = useState(null); + const [cartItems, setCartItems] = useState(null); // Load cart lần đầu useEffect(() => { - setCartIds(getCartProductIds()); + setCartItems(getCartItems()); + }, []); + + // Listen to cart changes from ANY source + useEffect(() => { + const handleCartChange = () => { + setCartItems(getCartItems()); + }; + + // Listen to custom event + window.addEventListener(CART_CHANGE_EVENT, handleCartChange); + + // Also listen to storage event (for changes from other tabs) + window.addEventListener('storage', (e) => { + if (e.key === 'cart_products') { + handleCartChange(); + } + }); + + return () => { + window.removeEventListener(CART_CHANGE_EVENT, handleCartChange); + window.removeEventListener('storage', handleCartChange); + }; }, []); const refresh = useCallback(() => { - setCartIds(getCartProductIds()); + setCartItems(getCartItems()); }, []); - const addToCart = useCallback((productId: number) => { - addProductToCart(productId); - refresh(); - }, [refresh]); + const addToCart = useCallback((productId: number, quantity: number = 1) => { + const result = addProductToCart(productId, quantity); + // Không cần refresh() nữa vì event listener sẽ tự động update + return result; + }, []); const removeFromCart = useCallback((productId: number) => { removeProductFromCart(productId); - refresh(); - }, [refresh]); + // Không cần refresh() + }, []); + + const increase = useCallback((productId: number, amount: number = 1) => { + const success = increaseQuantity(productId, amount); + // Không cần refresh() + return success; + }, []); + + const decrease = useCallback((productId: number, amount: number = 1) => { + const success = decreaseQuantity(productId, amount); + if (!success) { + console.log('Số lượng tối thiểu: 1'); + } + // Không cần refresh() + return success; + }, []); + + const updateQty = useCallback((productId: number, cartQuantity: number) => { + const success = updateQuantity(productId, cartQuantity); + if (!success) { + console.log('Số lượng phải lớn hơn hoặc bằng 1'); + } + // Không cần refresh() + return success; + }, []); const clear = useCallback(() => { clearCart(); - setCartIds([]); + // Không cần setCartItems([]) nữa, event sẽ tự động update }, []); const isInCart = useCallback( - (productId: number) => cartIds?.includes(productId) ?? false, - [cartIds] + (productId: number) => cartItems?.some(item => item.id === productId) ?? false, + [cartItems] ); - return { - cartIds: cartIds ?? [], - cartCount: cartIds?.length ?? 0, - loading: cartIds === null, + const getQuantity = useCallback( + (productId: number) => { + return cartItems?.find(item => item.id === productId)?.cartQuantity ?? 0; + }, + [cartItems] + ); + const totalItems = cartItems?.reduce((sum, item) => sum + item.cartQuantity, 0) ?? 0; + + return { + cartItems: cartItems ?? [], + cartCount: cartItems?.length ?? 0, + totalItems, + loading: cartItems === null, addToCart, removeFromCart, + increaseQuantity: increase, + decreaseQuantity: decrease, + updateQuantity: updateQty, clear, isInCart, + getQuantity, + refresh, }; -} +} \ No newline at end of file diff --git a/src/hooks/useProductItem.ts b/src/hooks/useProductItem.ts new file mode 100644 index 0000000..8089316 --- /dev/null +++ b/src/hooks/useProductItem.ts @@ -0,0 +1,28 @@ +import { calculateDiscount } from "../lib/utils"; +import type { ProductItemProps } from "../types/product" + +export function useDealItem(item: ProductItemProps) { + const productUrl = item.productUrl; + const productImage = item.productImage.large; + const productName = item.productName; + const price = Number(item.price); + const marketPrice = Number(item.marketPrice); + const discount = calculateDiscount(price, marketPrice); + const quantity = item.quantity; + const specialOffer = item.specialOffer.all?.[0]?.title ?? ''; + const warranty = item.warranty; + const productSummary= item.productSummary; + + return { + productUrl, + productImage, + productName, + quantity, + price, + marketPrice, + discount, + specialOffer, + warranty, + productSummary + }; +} diff --git a/src/lib/slug/resolveArticlePage.ts b/src/lib/slug/resolveArticlePage.ts index 723fac8..4da064e 100644 --- a/src/lib/slug/resolveArticlePage.ts +++ b/src/lib/slug/resolveArticlePage.ts @@ -1,5 +1,5 @@ // src/lib/articlePage.ts -import { categories } from "../../data/categories"; +import { categories } from "@/data/categories"; export type ArticleResult = | { type: "article_home"; data: any } @@ -29,5 +29,8 @@ export function resolveArticlePage(slug: string): ArticleResult | null { } // DETAIL - return { type: "article_detail", data: { slug } }; + const isValidSlugFormat = slug.includes('-') && slug.length > 5; + if (!isValidSlugFormat) { + return null; + } } diff --git a/src/lib/slug/resolveProductPage.ts b/src/lib/slug/resolveProductPage.ts index cbde8b5..19c8675 100644 --- a/src/lib/slug/resolveProductPage.ts +++ b/src/lib/slug/resolveProductPage.ts @@ -1,6 +1,6 @@ // hoanghapc/src/lib/productPage.ts -import { categories } from "../../data/categories"; -import { productList } from "../../data/product-list"; +import { categories } from "@/data/categories"; +import { productList } from "@/data/products"; export type ProductResult = | { type: "product_category"; data: any } @@ -23,7 +23,7 @@ export function resolveProductPage(slug: string): ProductResult | null { } // DETAIL - const product = productList.find(p => p.productUrl === url); + const product = productList.list?.find(p => p.productUrl === url); if (product) { return { type: "product_detail", data: product }; } diff --git a/src/lib/slug/slugMap.ts b/src/lib/slug/slugMap.ts index a3fd9c3..7e930c1 100644 --- a/src/lib/slug/slugMap.ts +++ b/src/lib/slug/slugMap.ts @@ -1,3 +1,4 @@ +import { notFound } from "next/navigation"; import { resolveArticlePage } from "./resolveArticlePage"; import { resolveProductPage } from "./resolveProductPage"; @@ -6,12 +7,18 @@ export type SlugResult = | ReturnType; export function findBySlug(slug?: string): SlugResult | null { - if (!slug) return null; + if (!slug || slug.trim() === '') { + return null; + } // PRODUCT const product = resolveProductPage(slug); if (product) return product; // ARTICLE - return resolveArticlePage(slug); + const articler = resolveArticlePage(slug); + if (articler) return articler; + + // 404 + return null; } \ No newline at end of file diff --git a/src/services/cart.ts b/src/services/cart.ts index 38c6ec0..9b2c1c4 100644 --- a/src/services/cart.ts +++ b/src/services/cart.ts @@ -1,48 +1,143 @@ 'use client'; -const CART_KEY = 'cart_product_ids'; +export interface CartItem { + id: number; + cartQuantity: number; +} -// 1. Lấy danh sách ID -export function getCartProductIds(): number[] { +const CART_KEY = 'cart_products'; +const CART_CHANGE_EVENT = 'cart-changed'; + +// Helper để dispatch event khi cart thay đổi +function notifyCartChange() { + if (typeof window !== 'undefined') { + window.dispatchEvent(new Event(CART_CHANGE_EVENT)); + } +} + +// 1. Lấy danh sách sản phẩm trong giỏ +export function getCartItems(): CartItem[] { if (typeof window === 'undefined') return []; - try { const raw = localStorage.getItem(CART_KEY); - return raw ? (JSON.parse(raw) as number[]) : []; + return raw ? (JSON.parse(raw) as CartItem[]) : []; } catch (error) { console.error('Invalid cart data', error); return []; } } -// 2. Thêm sản phẩm -export function addProductToCart(productId: number) { - if (typeof window === 'undefined') return; - - const ids = getCartProductIds(); - - if (ids.includes(productId)) { - console.log('Product đã tồn tại:', productId); - return; +// 2. Thêm sản phẩm vào giỏ +export function addProductToCart(productId: number, quantity: number = 1): { success: boolean; message: string; isNew: boolean } { + if (typeof window === 'undefined') { + return { success: false, message: 'Window is undefined', isNew: false }; } - - ids.push(productId); - localStorage.setItem(CART_KEY, JSON.stringify(ids)); + + const items = getCartItems(); + const existingItem = items.find(item => item.id === productId); + + if (existingItem) { + existingItem.cartQuantity += quantity; + localStorage.setItem(CART_KEY, JSON.stringify(items)); + notifyCartChange(); // Notify change + return { + success: true, + message: `Đã cập nhật số lượng thành ${existingItem.cartQuantity}`, + isNew: false + }; + } + + items.push({ id: productId, cartQuantity: quantity }); + localStorage.setItem(CART_KEY, JSON.stringify(items)); + notifyCartChange(); // Notify change + return { success: true, message: 'Đã thêm sản phẩm vào giỏ hàng', isNew: true }; } -// 3. Xóa 1 sản phẩm +// 3. Tăng số lượng sản phẩm +export function increaseQuantity(productId: number, amount: number = 1): boolean { + if (typeof window === 'undefined') return false; + + const items = getCartItems(); + const item = items.find(item => item.id === productId); + + if (item) { + item.cartQuantity += amount; + localStorage.setItem(CART_KEY, JSON.stringify(items)); + notifyCartChange(); // Notify change + return true; + } + return false; +} + +// 4. Giảm số lượng sản phẩm +export function decreaseQuantity(productId: number, amount: number = 1): boolean { + if (typeof window === 'undefined') return false; + + const items = getCartItems(); + const item = items.find(item => item.id === productId); + + if (item) { + const newQuantity = item.cartQuantity - amount; + if (newQuantity < 1) { + return false; + } + item.cartQuantity = newQuantity; + localStorage.setItem(CART_KEY, JSON.stringify(items)); + notifyCartChange(); // Notify change + return true; + } + return false; +} + +// 5. Update số lượng trực tiếp +export function updateQuantity(productId: number, cartQuantity: number): boolean { + if (typeof window === 'undefined') return false; + + if (cartQuantity < 1) { + return false; + } + + const items = getCartItems(); + const item = items.find(item => item.id === productId); + + if (item) { + item.cartQuantity = cartQuantity; + localStorage.setItem(CART_KEY, JSON.stringify(items)); + notifyCartChange(); // Notify change + return true; + } + return false; +} + +// 6. Xóa 1 sản phẩm export function removeProductFromCart(productId: number) { if (typeof window === 'undefined') return; - - const ids = getCartProductIds(); - const newIds = ids.filter(id => id !== productId); - - localStorage.setItem(CART_KEY, JSON.stringify(newIds)); + + const items = getCartItems(); + const newItems = items.filter(item => item.id !== productId); + localStorage.setItem(CART_KEY, JSON.stringify(newItems)); + notifyCartChange(); // Notify change } -// 4. Xóa giỏ hàng +// 7. Xóa toàn bộ giỏ hàng export function clearCart() { if (typeof window === 'undefined') return; - localStorage.removeItem(CART_KEY); + notifyCartChange(); // Notify change } + +// 8. Lấy cartQuantity của 1 sản phẩm +export function getProductQuantity(productId: number): number { + const items = getCartItems(); + const item = items.find(item => item.id === productId); + return item?.cartQuantity ?? 0; +} + +// 9. Kiểm tra sản phẩm có trong giỏ hàng không +export function isProductInCart(productId: number): boolean { + const items = getCartItems(); + return items.some(item => item.id === productId); +} + +// 10. Export event name để hooks sử dụng +export { CART_CHANGE_EVENT }; \ No newline at end of file diff --git a/src/types/product.ts b/src/types/product.ts new file mode 100644 index 0000000..4e42d34 --- /dev/null +++ b/src/types/product.ts @@ -0,0 +1,12 @@ +// src/types/deal.ts +export type ProductItemProps = { + price: number; + quantity: number; + productUrl: string; + productImage: string; + productName: string; + marketPrice : number; + specialOffer : string; + warranty: string; + productSummary: string; +};