Implement shopping cart functionality with dedicated context, product integration, and UI components. and removed unwanted components
This commit is contained in:
parent
a7e48ddb6c
commit
3d8be45a96
@ -16,43 +16,25 @@ import { BaseURL } from '../../../utils/BaseUrl'
|
||||
const Cart = () => {
|
||||
const [timeLeft, setTimeLeft] = useState(countdownTime());
|
||||
const router = useRouter()
|
||||
const [cart, setCart] = useState([])
|
||||
const [token, setToken] = useState<string | null>()
|
||||
const { cartState, updateCart, removeFromCart } = useCart();
|
||||
const [isLoggedIn, setIsLoggedIn] = useState(false)
|
||||
|
||||
// Check if user is logged in
|
||||
useEffect(() => {
|
||||
getCartData();
|
||||
}, []);
|
||||
const token = localStorage.getItem('token')
|
||||
setIsLoggedIn(!!token)
|
||||
|
||||
useEffect(() => {
|
||||
const Token = localStorage.getItem('token')
|
||||
setToken(Token)
|
||||
// If logged in, optionally sync with API (cart is already in cartState)
|
||||
if (token) {
|
||||
console.log("✅ Logged in - using CartContext (already synced)")
|
||||
} else {
|
||||
console.log("🛒 Guest mode - using CartContext from localStorage")
|
||||
}
|
||||
}, [])
|
||||
|
||||
const getCartData = async () => {
|
||||
try {
|
||||
const token = localStorage.getItem("token"); // should be plain token string
|
||||
console.log("Stored token:", token);
|
||||
|
||||
if (!token) {
|
||||
console.error("❌ No token found in localStorage");
|
||||
return;
|
||||
}
|
||||
|
||||
const res: any = await axios.get(`${BaseURL}/cart`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`, // must include "Bearer "
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
withCredentials: true, // if backend uses cookies + JWT
|
||||
});
|
||||
|
||||
setCart(res?.data?.cart?.items)
|
||||
console.log("Cart response ✅:", res.data);
|
||||
} catch (error: any) {
|
||||
console.error("❌ Error fetching cart:", error.response?.data || error.message);
|
||||
}
|
||||
};
|
||||
console.log("cart", cart)
|
||||
// ✅ Use cartState.cartArray as the source of truth for BOTH guest and logged-in users
|
||||
const cart = cartState.cartArray;
|
||||
console.log("📦 Cart from CartContext:", cart)
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
@ -63,57 +45,40 @@ const Cart = () => {
|
||||
return () => clearInterval(timer);
|
||||
}, []);
|
||||
|
||||
const { cartState, updateCart, removeFromCart } = useCart();
|
||||
|
||||
|
||||
|
||||
// Handle quantity change for both guest and logged-in users
|
||||
const handleQuantityChange = async (item: any, newQuantity: number) => {
|
||||
if (newQuantity < 1) return;
|
||||
|
||||
// Optimistic Update: Update local state immediately
|
||||
const updatedCart: any = cart.map((cartItem: any) => {
|
||||
if (cartItem._id === item._id) {
|
||||
return { ...cartItem, quantity: newQuantity };
|
||||
// Get the product ID (handle both formats)
|
||||
const productId = item._id || item.id;
|
||||
const size = item.selectedSize || item.size;
|
||||
const color = item.selectedColor || item.color;
|
||||
|
||||
// Update CartContext immediately (works for both guest and logged-in)
|
||||
updateCart(productId, newQuantity, size, color);
|
||||
|
||||
// If logged in, also sync with API
|
||||
if (isLoggedIn) {
|
||||
const token = localStorage.getItem("token");
|
||||
const payload = {
|
||||
quantity: newQuantity,
|
||||
color: color,
|
||||
size: size
|
||||
};
|
||||
|
||||
try {
|
||||
await axios.put(
|
||||
`${BaseURL}/cart/${productId}`,
|
||||
payload,
|
||||
{
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
}
|
||||
);
|
||||
console.log("✅ Cart quantity updated on API");
|
||||
} catch (error) {
|
||||
console.error("❌ Error updating cart on API:", error);
|
||||
}
|
||||
return cartItem;
|
||||
});
|
||||
setCart(updatedCart);
|
||||
|
||||
// Optimistic Update: Update Context (for header, etc.)
|
||||
if (item.product && item.product._id) {
|
||||
updateCart(item.product._id, newQuantity, item.size, item.color);
|
||||
}
|
||||
|
||||
const payload = {
|
||||
quantity: newQuantity,
|
||||
color: item.color,
|
||||
size: item.size
|
||||
}
|
||||
|
||||
const token = localStorage.getItem("token")
|
||||
|
||||
try {
|
||||
const res = await axios.put(
|
||||
`${BaseURL}/cart/${item.product._id}`, // ✅ use product._id, not item._id
|
||||
payload,
|
||||
{
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
}
|
||||
)
|
||||
console.log("Updated Cart:", res.data)
|
||||
getCartData()
|
||||
} catch (error) {
|
||||
console.log("error", error)
|
||||
}
|
||||
|
||||
// const itemToUpdate = cart.find((item) => item._id === cart._id);
|
||||
|
||||
|
||||
// // Kiểm tra xem sản phẩm có tồn tại không
|
||||
// if (itemToUpdate) {
|
||||
// // Truyền giá trị hiện tại của selectedSize và selectedColor
|
||||
// updateCart(cart._id, newQuantity, itemToUpdate.selectedSize, itemToUpdate.selectedColor);
|
||||
// }
|
||||
};
|
||||
|
||||
let moneyForFreeship = 150;
|
||||
@ -157,30 +122,25 @@ const Cart = () => {
|
||||
|
||||
|
||||
const handleRemoveCart = async (item: any) => {
|
||||
// Optimistic update for local cart
|
||||
const updatedCart = cart.filter((cartItem: any) => cartItem._id !== item._id);
|
||||
setCart(updatedCart);
|
||||
const token = localStorage.getItem("token");
|
||||
const productId = item._id || item.id;
|
||||
const size = item.selectedSize || item.size;
|
||||
const color = item.selectedColor || item.color;
|
||||
|
||||
// ✅ Immediately remove from CartContext (localStorage + Redux)
|
||||
const cartItem = cartState.cartArray.find(
|
||||
(cartItem) => (cartItem.id === item.product._id || cartItem.id === item.product.id)
|
||||
);
|
||||
if (cartItem) {
|
||||
removeFromCart(cartItem.id);
|
||||
}
|
||||
// Remove from CartContext immediately (works for both)
|
||||
removeFromCart(productId);
|
||||
|
||||
// ✅ Then sync with API
|
||||
try {
|
||||
const res = await axios.delete(`${BaseURL}/cart/${item.product._id}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
params: { color: item.color, size: item.size }, // ✅ query params
|
||||
});
|
||||
|
||||
getCartData();
|
||||
console.log("Removed from cart:", res.data);
|
||||
} catch (error: any) {
|
||||
console.error("Error removing item:", error.response?.data || error.message);
|
||||
// If logged in, also sync with API
|
||||
if (isLoggedIn) {
|
||||
const token = localStorage.getItem("token");
|
||||
try {
|
||||
await axios.delete(`${BaseURL}/cart/${productId}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
params: { color, size },
|
||||
});
|
||||
console.log("✅ Item removed from API cart");
|
||||
} catch (error: any) {
|
||||
console.error("❌ Error removing from API cart:", error.response?.data || error.message);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -238,58 +198,72 @@ const Cart = () => {
|
||||
{cart?.length < 1 ? (
|
||||
<p className='text-button pt-3'>No product in cart</p>
|
||||
) : (
|
||||
cart?.map((cart: any) => (
|
||||
<div className="item flex md:mt-7 md:pb-7 mt-5 pb-5 border-b border-line w-full" key={cart._id}>
|
||||
<div className="w-1/2">
|
||||
<div className="flex items-center gap-6">
|
||||
<div className="bg-img md:w-[100px] w-20 aspect-[3/4]">
|
||||
<Image
|
||||
src={cart.product.thumbImage[0]}
|
||||
width={1000}
|
||||
height={1000}
|
||||
alt={cart.product.name}
|
||||
className='w-full h-full object-cover rounded-lg'
|
||||
cart?.map((item: any) => {
|
||||
// Handle both API format (item.product.name) and CartContext format (item.name)
|
||||
const itemId = item._id || item.id;
|
||||
const itemName = item.product?.name || item.name;
|
||||
const itemImage = item.product?.thumbImage?.[0] || item.thumbImage?.[0] || item.images?.[0];
|
||||
const itemPrice = item.price;
|
||||
const itemQuantity = item.quantity || item.quantityPurchase || 1;
|
||||
const itemSize = item.selectedSize || item.size || item.sizes?.[0];
|
||||
const itemColor = item.selectedColor || item.color || item.variation?.[0]?.color;
|
||||
|
||||
return (
|
||||
<div className="item flex md:mt-7 md:pb-7 mt-5 pb-5 border-b border-line w-full" key={itemId}>
|
||||
<div className="w-1/2">
|
||||
<div className="flex items-center gap-6">
|
||||
<div className="bg-img md:w-[100px] w-20 aspect-[3/4]">
|
||||
<Image
|
||||
src={itemImage}
|
||||
width={1000}
|
||||
height={1000}
|
||||
alt={itemName}
|
||||
className='w-full h-full object-cover rounded-lg'
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-title">{itemName}</div>
|
||||
<div className="list-select mt-3">
|
||||
<span className='text-secondary2'>Size: {itemSize}</span>
|
||||
{itemColor && <span className='text-secondary2 ml-2'>Color: {itemColor}</span>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-1/12 price flex items-center justify-center">
|
||||
<div className="text-title text-center">${itemPrice}.00</div>
|
||||
</div>
|
||||
<div className="w-1/6 flex items-center justify-center">
|
||||
<div className="quantity-block bg-surface md:p-3 p-2 flex items-center justify-between rounded-lg border border-line md:w-[100px] flex-shrink-0 w-20">
|
||||
<Icon.Minus
|
||||
onClick={() => {
|
||||
if (itemQuantity > 1) {
|
||||
handleQuantityChange(item, itemQuantity - 1)
|
||||
}
|
||||
}}
|
||||
className={`text-base max-md:text-sm cursor-pointer ${itemQuantity === 1 ? 'opacity-50' : ''}`}
|
||||
/>
|
||||
<div className="text-button quantity">{itemQuantity}</div>
|
||||
<Icon.Plus
|
||||
onClick={() => handleQuantityChange(item, itemQuantity + 1)}
|
||||
className='text-base max-md:text-sm cursor-pointer'
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-title">{cart.product.name}</div>
|
||||
<div className="list-select mt-3"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-1/12 price flex items-center justify-center">
|
||||
<div className="text-title text-center">${cart.price}.00</div>
|
||||
</div>
|
||||
<div className="w-1/6 flex items-center justify-center">
|
||||
<div className="quantity-block bg-surface md:p-3 p-2 flex items-center justify-between rounded-lg border border-line md:w-[100px] flex-shrink-0 w-20">
|
||||
<Icon.Minus
|
||||
<div className="w-1/6 flex total-price items-center justify-center">
|
||||
<div className="text-title text-center">${itemQuantity * itemPrice}.00</div>
|
||||
</div>
|
||||
<div className="w-1/12 flex items-center justify-center">
|
||||
<Icon.XCircle
|
||||
className='text-xl max-md:text-base text-red cursor-pointer hover:text-black duration-500'
|
||||
onClick={() => {
|
||||
if (cart.quantity > 1) {
|
||||
handleQuantityChange(cart, cart.quantity - 1)
|
||||
}
|
||||
handleRemoveCart(item)
|
||||
}}
|
||||
className={`text-base max-md:text-sm ${cart.quantity === 1 ? 'disabled' : ''}`}
|
||||
/>
|
||||
<div className="text-button quantity">{cart.quantity}</div>
|
||||
<Icon.Plus
|
||||
onClick={() => handleQuantityChange(cart, cart.quantity + 1)}
|
||||
className='text-base max-md:text-sm'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-1/6 flex total-price items-center justify-center">
|
||||
<div className="text-title text-center">${cart.quantity * cart.price}.00</div>
|
||||
</div>
|
||||
<div className="w-1/12 flex items-center justify-center">
|
||||
<Icon.XCircle
|
||||
className='text-xl max-md:text-base text-red cursor-pointer hover:text-black duration-500'
|
||||
onClick={() => {
|
||||
handleRemoveCart(cart)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
);
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,32 +0,0 @@
|
||||
'use client'
|
||||
import React from 'react'
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import TopNavOne from '@/components/Header/TopNav/TopNavOne'
|
||||
import MenuOne from '@/components/Header/Menu/MenuOne'
|
||||
import BreadcrumbProduct from '@/components/Breadcrumb/BreadcrumbProduct'
|
||||
import BoughtTogether from '@/components/Product/Detail/BoughtTogether';
|
||||
import Footer from '@/components/Footer/Footer'
|
||||
import productData from '@/data/Product.json'
|
||||
|
||||
const ProductBoughtTogether = () => {
|
||||
const searchParams = useSearchParams()
|
||||
let productId = searchParams.get('id')
|
||||
|
||||
if (productId === null) {
|
||||
productId = '1'
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopNavOne props="style-one bg-black" slogan="New customers save 10% with the code GET10" />
|
||||
<div id="header" className='relative w-full'>
|
||||
<MenuOne props="bg-white" />
|
||||
<BreadcrumbProduct data={productData} productPage='bought-together' productId={productId} />
|
||||
</div>
|
||||
<BoughtTogether data={productData} productId={productId} />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProductBoughtTogether
|
||||
@ -1,32 +0,0 @@
|
||||
'use client'
|
||||
import React from 'react'
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import TopNavOne from '@/components/Header/TopNav/TopNavOne'
|
||||
import MenuOne from '@/components/Header/Menu/MenuOne'
|
||||
import BreadcrumbProduct from '@/components/Breadcrumb/BreadcrumbProduct'
|
||||
import VariableProduct from '@/components/Product/Detail/VariableProduct';
|
||||
import Footer from '@/components/Footer/Footer'
|
||||
import productData from '@/data/Product.json'
|
||||
|
||||
const ProductCombinedOne = () => {
|
||||
const searchParams = useSearchParams()
|
||||
let productId = searchParams.get('id')
|
||||
|
||||
if (productId === null) {
|
||||
productId = '1'
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopNavOne props="style-one bg-black" slogan="New customers save 10% with the code GET10" />
|
||||
<div id="header" className='relative w-full'>
|
||||
<MenuOne props="bg-white" />
|
||||
<BreadcrumbProduct data={productData} productPage='variable' productId={productId} />
|
||||
</div>
|
||||
<VariableProduct data={productData} productId={productId} />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProductCombinedOne
|
||||
@ -1,32 +0,0 @@
|
||||
'use client'
|
||||
import React from 'react'
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import TopNavOne from '@/components/Header/TopNav/TopNavOne'
|
||||
import MenuOne from '@/components/Header/Menu/MenuOne'
|
||||
import BreadcrumbProduct from '@/components/Breadcrumb/BreadcrumbProduct'
|
||||
import External from '@/components/Product/Detail/External';
|
||||
import Footer from '@/components/Footer/Footer'
|
||||
import productData from '@/data/Product.json'
|
||||
|
||||
const ProductCombinedTwo = () => {
|
||||
const searchParams = useSearchParams()
|
||||
let productId = searchParams.get('id')
|
||||
|
||||
if (productId === null) {
|
||||
productId = '1'
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopNavOne props="style-one bg-black" slogan="New customers save 10% with the code GET10" />
|
||||
<div id="header" className='relative w-full'>
|
||||
<MenuOne props="bg-white" />
|
||||
<BreadcrumbProduct data={productData} productPage='external' productId={productId} />
|
||||
</div>
|
||||
<External data={productData} productId={productId} />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProductCombinedTwo
|
||||
@ -1,32 +0,0 @@
|
||||
'use client'
|
||||
import React from 'react'
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import TopNavOne from '@/components/Header/TopNav/TopNavOne'
|
||||
import MenuOne from '@/components/Header/Menu/MenuOne'
|
||||
import BreadcrumbProduct from '@/components/Breadcrumb/BreadcrumbProduct'
|
||||
import CountdownTimer from '@/components/Product/Detail/CountdownTimer';
|
||||
import Footer from '@/components/Footer/Footer'
|
||||
import productData from '@/data/Product.json'
|
||||
|
||||
const ProductCountdownTimer = () => {
|
||||
const searchParams = useSearchParams()
|
||||
let productId = searchParams.get('id')
|
||||
|
||||
if (productId === null) {
|
||||
productId = '1'
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopNavOne props="style-one bg-black" slogan="New customers save 10% with the code GET10" />
|
||||
<div id="header" className='relative w-full'>
|
||||
<MenuOne props="bg-white" />
|
||||
<BreadcrumbProduct data={productData} productPage='countdown-timer' productId={productId} />
|
||||
</div>
|
||||
<CountdownTimer data={productData} productId={productId} />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProductCountdownTimer
|
||||
@ -1,34 +0,0 @@
|
||||
'use client'
|
||||
import React, { useState } from 'react'
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import Link from 'next/link'
|
||||
import TopNavOne from '@/components/Header/TopNav/TopNavOne'
|
||||
import MenuOne from '@/components/Header/Menu/MenuOne'
|
||||
import BreadcrumbProduct from '@/components/Breadcrumb/BreadcrumbProduct'
|
||||
import Default from '@/components/Product/Detail/Default';
|
||||
import Footer from '@/components/Footer/Footer'
|
||||
import { ProductType } from '@/type/ProductType'
|
||||
import productData from '@/data/Product.json'
|
||||
|
||||
const ProductDefault = () => {
|
||||
const searchParams = useSearchParams()
|
||||
let productId = searchParams.get('id')
|
||||
|
||||
if (productId === null) {
|
||||
productId = '1'
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopNavOne props="style-one bg-black" slogan="New customers save 10% with the code GET10" />
|
||||
<div id="header" className='relative w-full'>
|
||||
<MenuOne props="bg-white" />
|
||||
<BreadcrumbProduct data={productData} productPage='default' productId={productId} />
|
||||
</div>
|
||||
<Default data={productData} productId={productId} />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProductDefault
|
||||
@ -1,32 +0,0 @@
|
||||
'use client'
|
||||
import React from 'react'
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import TopNavOne from '@/components/Header/TopNav/TopNavOne'
|
||||
import MenuOne from '@/components/Header/Menu/MenuOne'
|
||||
import BreadcrumbProduct from '@/components/Breadcrumb/BreadcrumbProduct'
|
||||
import Discount from '@/components/Product/Detail/Discount';
|
||||
import Footer from '@/components/Footer/Footer'
|
||||
import productData from '@/data/Product.json'
|
||||
|
||||
const ProductDiscount = () => {
|
||||
const searchParams = useSearchParams()
|
||||
let productId = searchParams.get('id')
|
||||
|
||||
if (productId === null) {
|
||||
productId = '1'
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopNavOne props="style-one bg-black" slogan="New customers save 10% with the code GET10" />
|
||||
<div id="header" className='relative w-full style-discount'>
|
||||
<MenuOne props="bg-white" />
|
||||
<BreadcrumbProduct data={productData} productPage='discount' productId={productId} />
|
||||
</div>
|
||||
<Discount data={productData} productId={productId} />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProductDiscount
|
||||
32
src/app/product/external/page.tsx
vendored
32
src/app/product/external/page.tsx
vendored
@ -1,32 +0,0 @@
|
||||
'use client'
|
||||
import React from 'react'
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import TopNavOne from '@/components/Header/TopNav/TopNavOne'
|
||||
import MenuOne from '@/components/Header/Menu/MenuOne'
|
||||
import BreadcrumbProduct from '@/components/Breadcrumb/BreadcrumbProduct'
|
||||
import External from '@/components/Product/Detail/External';
|
||||
import Footer from '@/components/Footer/Footer'
|
||||
import productData from '@/data/Product.json'
|
||||
|
||||
const ProductExternal = () => {
|
||||
const searchParams = useSearchParams()
|
||||
let productId = searchParams.get('id')
|
||||
|
||||
if (productId === null) {
|
||||
productId = '1'
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopNavOne props="style-one bg-black" slogan="New customers save 10% with the code GET10" />
|
||||
<div id="header" className='relative w-full'>
|
||||
<MenuOne props="bg-white" />
|
||||
<BreadcrumbProduct data={productData} productPage='external' productId={productId} />
|
||||
</div>
|
||||
<External data={productData} productId={productId} />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProductExternal
|
||||
@ -1,32 +0,0 @@
|
||||
'use client'
|
||||
import React from 'react'
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import TopNavOne from '@/components/Header/TopNav/TopNavOne'
|
||||
import MenuOne from '@/components/Header/Menu/MenuOne'
|
||||
import BreadcrumbProduct from '@/components/Breadcrumb/BreadcrumbProduct'
|
||||
import FixedPrice from '@/components/Product/Detail/FixedPrice';
|
||||
import Footer from '@/components/Footer/Footer'
|
||||
import productData from '@/data/Product.json'
|
||||
|
||||
const ProductFixedPrice = () => {
|
||||
const searchParams = useSearchParams()
|
||||
let productId = searchParams.get('id')
|
||||
|
||||
if (productId === null) {
|
||||
productId = '1'
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopNavOne props="style-one bg-black" slogan="New customers save 10% with the code GET10" />
|
||||
<div id="header" className='relative w-full'>
|
||||
<MenuOne props="bg-white" />
|
||||
<BreadcrumbProduct data={productData} productPage='fixed-price' productId={productId} />
|
||||
</div>
|
||||
<FixedPrice data={productData} productId={productId} />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProductFixedPrice
|
||||
@ -1,32 +0,0 @@
|
||||
'use client'
|
||||
import React from 'react'
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import TopNavOne from '@/components/Header/TopNav/TopNavOne'
|
||||
import MenuOne from '@/components/Header/Menu/MenuOne'
|
||||
import BreadcrumbProduct from '@/components/Breadcrumb/BreadcrumbProduct'
|
||||
import Grouped from '@/components/Product/Detail/Grouped';
|
||||
import Footer from '@/components/Footer/Footer'
|
||||
import productData from '@/data/Product.json'
|
||||
|
||||
const ProductGrouped = () => {
|
||||
const searchParams = useSearchParams()
|
||||
let productId = searchParams.get('id')
|
||||
|
||||
if (productId === null) {
|
||||
productId = '1'
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopNavOne props="style-one bg-black" slogan="New customers save 10% with the code GET10" />
|
||||
<div id="header" className='relative w-full'>
|
||||
<MenuOne props="bg-white" />
|
||||
<BreadcrumbProduct data={productData} productPage='grouped' productId={productId} />
|
||||
</div>
|
||||
<Grouped data={productData} productId={productId} />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProductGrouped
|
||||
@ -1,32 +0,0 @@
|
||||
'use client'
|
||||
import React from 'react'
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import TopNavOne from '@/components/Header/TopNav/TopNavOne'
|
||||
import MenuOne from '@/components/Header/Menu/MenuOne'
|
||||
import BreadcrumbProduct from '@/components/Breadcrumb/BreadcrumbProduct'
|
||||
import OnSale from '@/components/Product/Detail/OnSale';
|
||||
import Footer from '@/components/Footer/Footer'
|
||||
import productData from '@/data/Product.json'
|
||||
|
||||
const ProductOnSale = () => {
|
||||
const searchParams = useSearchParams()
|
||||
let productId = searchParams.get('id')
|
||||
|
||||
if (productId === null) {
|
||||
productId = '1'
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopNavOne props="style-one bg-black" slogan="New customers save 10% with the code GET10" />
|
||||
<div id="header" className='relative w-full'>
|
||||
<MenuOne props="bg-white" />
|
||||
<BreadcrumbProduct data={productData} productPage='on-sale' productId={productId} />
|
||||
</div>
|
||||
<OnSale data={productData} productId={productId} />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProductOnSale
|
||||
@ -1,32 +0,0 @@
|
||||
'use client'
|
||||
import React from 'react'
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import TopNavOne from '@/components/Header/TopNav/TopNavOne'
|
||||
import MenuOne from '@/components/Header/Menu/MenuOne'
|
||||
import BreadcrumbProduct from '@/components/Breadcrumb/BreadcrumbProduct'
|
||||
import Grouped from '@/components/Product/Detail/Grouped';
|
||||
import Footer from '@/components/Footer/Footer'
|
||||
import productData from '@/data/Product.json'
|
||||
|
||||
const ProductOneScrolling = () => {
|
||||
const searchParams = useSearchParams()
|
||||
let productId = searchParams.get('id')
|
||||
|
||||
if (productId === null) {
|
||||
productId = '1'
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopNavOne props="style-one bg-black" slogan="New customers save 10% with the code GET10" />
|
||||
<div id="header" className='relative w-full'>
|
||||
<MenuOne props="bg-white" />
|
||||
<BreadcrumbProduct data={productData} productPage='grouped' productId={productId} />
|
||||
</div>
|
||||
<Grouped data={productData} productId={productId} />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProductOneScrolling
|
||||
@ -1,32 +0,0 @@
|
||||
'use client'
|
||||
import React from 'react'
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import TopNavOne from '@/components/Header/TopNav/TopNavOne'
|
||||
import MenuOne from '@/components/Header/Menu/MenuOne'
|
||||
import BreadcrumbProduct from '@/components/Breadcrumb/BreadcrumbProduct'
|
||||
import OutOfStock from '@/components/Product/Detail/OutOfStock';
|
||||
import Footer from '@/components/Footer/Footer'
|
||||
import productData from '@/data/Product.json'
|
||||
|
||||
const ProductOutOfStock = () => {
|
||||
const searchParams = useSearchParams()
|
||||
let productId = searchParams.get('id')
|
||||
|
||||
if (productId === null) {
|
||||
productId = '1'
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopNavOne props="style-one bg-black" slogan="New customers save 10% with the code GET10" />
|
||||
<div id="header" className='relative w-full'>
|
||||
<MenuOne props="bg-white" />
|
||||
<BreadcrumbProduct data={productData} productPage='out-of-stock' productId={productId} />
|
||||
</div>
|
||||
<OutOfStock data={productData} productId={productId} />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProductOutOfStock
|
||||
@ -1,34 +0,0 @@
|
||||
'use client'
|
||||
import React, { useState } from 'react'
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import Link from 'next/link'
|
||||
import TopNavOne from '@/components/Header/TopNav/TopNavOne'
|
||||
import MenuOne from '@/components/Header/Menu/MenuOne'
|
||||
import BreadcrumbProduct from '@/components/Breadcrumb/BreadcrumbProduct'
|
||||
import Sale from '@/components/Product/Detail/Sale';
|
||||
import Footer from '@/components/Footer/Footer'
|
||||
import { ProductType } from '@/type/ProductType'
|
||||
import productData from '@/data/Product.json'
|
||||
|
||||
const ProductSale = () => {
|
||||
const searchParams = useSearchParams()
|
||||
let productId = searchParams.get('id')
|
||||
|
||||
if (productId === null) {
|
||||
productId = '1'
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopNavOne props="style-one bg-black" slogan="New customers save 10% with the code GET10" />
|
||||
<div id="header" className='relative w-full'>
|
||||
<MenuOne props="bg-white" />
|
||||
<BreadcrumbProduct data={productData} productPage='sale' productId={productId} />
|
||||
</div>
|
||||
<Sale data={productData} productId={productId} />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProductSale
|
||||
@ -1,34 +0,0 @@
|
||||
'use client'
|
||||
import React, { useState } from 'react'
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import Link from 'next/link'
|
||||
import TopNavOne from '@/components/Header/TopNav/TopNavOne'
|
||||
import MenuOne from '@/components/Header/Menu/MenuOne'
|
||||
import BreadcrumbProduct from '@/components/Breadcrumb/BreadcrumbProduct'
|
||||
import Sidebar from '@/components/Product/Detail/Sidebar';
|
||||
import Footer from '@/components/Footer/Footer'
|
||||
import { ProductType } from '@/type/ProductType'
|
||||
import productData from '@/data/Product.json'
|
||||
|
||||
const ProductSidebar = () => {
|
||||
const searchParams = useSearchParams()
|
||||
let productId = searchParams.get('id')
|
||||
|
||||
if (productId === null) {
|
||||
productId = '1'
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopNavOne props="style-one bg-black" slogan="New customers save 10% with the code GET10" />
|
||||
<div id="header" className='relative w-full'>
|
||||
<MenuOne props="bg-white" />
|
||||
<BreadcrumbProduct data={productData} productPage='sidebar' productId={productId} />
|
||||
</div>
|
||||
<Sidebar data={productData} productId={productId} />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProductSidebar
|
||||
@ -1,25 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import TopNavOne from '@/components/Header/TopNav/TopNavOne'
|
||||
import MenuOne from '@/components/Header/Menu/MenuOne'
|
||||
import ShopFilterCanvas from '@/components/Shop/ShopFilterCanvas'
|
||||
import productData from '@/data/Product.json'
|
||||
import Footer from '@/components/Footer/Footer'
|
||||
|
||||
export default function FilterCanvasProductOne() {
|
||||
const searchParams = useSearchParams()
|
||||
const type = searchParams.get('type')
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopNavOne props="style-one bg-black" slogan="New customers save 10% with the code GET10" />
|
||||
<div id="header" className='relative w-full'>
|
||||
<MenuOne props="bg-transparent" />
|
||||
</div>
|
||||
<ShopFilterCanvas data={productData} productPerPage={12} dataType={type} productStyle='style-1' />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import TopNavOne from '@/components/Header/TopNav/TopNavOne'
|
||||
import MenuOne from '@/components/Header/Menu/MenuOne'
|
||||
import ShopFilterCanvas from '@/components/Shop/ShopFilterCanvas'
|
||||
import productData from '@/data/Product.json'
|
||||
import Footer from '@/components/Footer/Footer'
|
||||
|
||||
export default function FilterCanvasProductTwo() {
|
||||
const searchParams = useSearchParams()
|
||||
const type = searchParams.get('type')
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopNavOne props="style-one bg-black" slogan="New customers save 10% with the code GET10" />
|
||||
<div id="header" className='relative w-full'>
|
||||
<MenuOne props="bg-transparent" />
|
||||
</div>
|
||||
<ShopFilterCanvas data={productData} productPerPage={12} dataType={type} productStyle='style-2' />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import TopNavOne from '@/components/Header/TopNav/TopNavOne'
|
||||
import MenuOne from '@/components/Header/Menu/MenuOne'
|
||||
import ShopFilterCanvas from '@/components/Shop/ShopFilterCanvas'
|
||||
import productData from '@/data/Product.json'
|
||||
import Footer from '@/components/Footer/Footer'
|
||||
|
||||
export default function FilterCanvasProductThree() {
|
||||
const searchParams = useSearchParams()
|
||||
const type = searchParams.get('type')
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopNavOne props="style-one bg-black" slogan="New customers save 10% with the code GET10" />
|
||||
<div id="header" className='relative w-full'>
|
||||
<MenuOne props="bg-transparent" />
|
||||
</div>
|
||||
<ShopFilterCanvas data={productData} productPerPage={12} dataType={type} productStyle='style-3' />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import TopNavOne from '@/components/Header/TopNav/TopNavOne'
|
||||
import MenuOne from '@/components/Header/Menu/MenuOne'
|
||||
import ShopFilterCanvas from '@/components/Shop/ShopFilterCanvas'
|
||||
import productData from '@/data/Product.json'
|
||||
import Footer from '@/components/Footer/Footer'
|
||||
|
||||
export default function FilterCanvasProductFour() {
|
||||
const searchParams = useSearchParams()
|
||||
const type = searchParams.get('type')
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopNavOne props="style-one bg-black" slogan="New customers save 10% with the code GET10" />
|
||||
<div id="header" className='relative w-full'>
|
||||
<MenuOne props="bg-transparent" />
|
||||
</div>
|
||||
<ShopFilterCanvas data={productData} productPerPage={12} dataType={type} productStyle='style-4' />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import TopNavOne from '@/components/Header/TopNav/TopNavOne'
|
||||
import MenuOne from '@/components/Header/Menu/MenuOne'
|
||||
import ShopFilterCanvas from '@/components/Shop/ShopFilterCanvas'
|
||||
import productData from '@/data/Product.json'
|
||||
import Footer from '@/components/Footer/Footer'
|
||||
|
||||
export default function FilterCanvasProductFive() {
|
||||
const searchParams = useSearchParams()
|
||||
const type = searchParams.get('type')
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopNavOne props="style-one bg-black" slogan="New customers save 10% with the code GET10" />
|
||||
<div id="header" className='relative w-full'>
|
||||
<MenuOne props="bg-transparent" />
|
||||
</div>
|
||||
<ShopFilterCanvas data={productData} productPerPage={12} dataType={type} productStyle='style-5' />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -1,33 +0,0 @@
|
||||
'use client'
|
||||
import React, { useState } from 'react'
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import Link from 'next/link'
|
||||
import TopNavOne from '@/components/Header/TopNav/TopNavOne'
|
||||
import MenuOne from '@/components/Header/Menu/MenuOne'
|
||||
import BreadcrumbProduct from '@/components/Breadcrumb/BreadcrumbProduct'
|
||||
import Sale from '@/components/Product/Detail/Sale';
|
||||
import Footer from '@/components/Footer/Footer'
|
||||
import productData from '@/data/Product.json'
|
||||
|
||||
const ProductThumbnailBottom = () => {
|
||||
const searchParams = useSearchParams()
|
||||
let productId = searchParams.get('id')
|
||||
|
||||
if (productId === null) {
|
||||
productId = '1'
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopNavOne props="style-one bg-black" slogan="New customers save 10% with the code GET10" />
|
||||
<div id="header" className='relative w-full'>
|
||||
<MenuOne props="bg-white" />
|
||||
<BreadcrumbProduct data={productData} productPage='sale' productId={productId} />
|
||||
</div>
|
||||
<Sale data={productData} productId={productId} />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProductThumbnailBottom
|
||||
@ -1,34 +0,0 @@
|
||||
'use client'
|
||||
import React, { useState } from 'react'
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import Link from 'next/link'
|
||||
import TopNavOne from '@/components/Header/TopNav/TopNavOne'
|
||||
import MenuOne from '@/components/Header/Menu/MenuOne'
|
||||
import BreadcrumbProduct from '@/components/Breadcrumb/BreadcrumbProduct'
|
||||
import Default from '@/components/Product/Detail/Default';
|
||||
import Footer from '@/components/Footer/Footer'
|
||||
import { ProductType } from '@/type/ProductType'
|
||||
import productData from '@/data/Product.json'
|
||||
|
||||
const ProductThumbnailLeft = () => {
|
||||
const searchParams = useSearchParams()
|
||||
let productId = searchParams.get('id')
|
||||
|
||||
if (productId === null) {
|
||||
productId = '1'
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopNavOne props="style-one bg-black" slogan="New customers save 10% with the code GET10" />
|
||||
<div id="header" className='relative w-full'>
|
||||
<MenuOne props="bg-white" />
|
||||
<BreadcrumbProduct data={productData} productPage='default' productId={productId} />
|
||||
</div>
|
||||
<Default data={productData} productId={productId} />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProductThumbnailLeft
|
||||
@ -1,32 +0,0 @@
|
||||
'use client'
|
||||
import React from 'react'
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import TopNavOne from '@/components/Header/TopNav/TopNavOne'
|
||||
import MenuOne from '@/components/Header/Menu/MenuOne'
|
||||
import BreadcrumbProduct from '@/components/Breadcrumb/BreadcrumbProduct'
|
||||
import CountdownTimer from '@/components/Product/Detail/CountdownTimer';
|
||||
import Footer from '@/components/Footer/Footer'
|
||||
import productData from '@/data/Product.json'
|
||||
|
||||
const ProductTwoScrolling = () => {
|
||||
const searchParams = useSearchParams()
|
||||
let productId = searchParams.get('id')
|
||||
|
||||
if (productId === null) {
|
||||
productId = '1'
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopNavOne props="style-one bg-black" slogan="New customers save 10% with the code GET10" />
|
||||
<div id="header" className='relative w-full'>
|
||||
<MenuOne props="bg-white" />
|
||||
<BreadcrumbProduct data={productData} productPage='countdown-timer' productId={productId} />
|
||||
</div>
|
||||
<CountdownTimer data={productData} productId={productId} />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProductTwoScrolling
|
||||
@ -1,32 +0,0 @@
|
||||
'use client'
|
||||
import React from 'react'
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import TopNavOne from '@/components/Header/TopNav/TopNavOne'
|
||||
import MenuOne from '@/components/Header/Menu/MenuOne'
|
||||
import BreadcrumbProduct from '@/components/Breadcrumb/BreadcrumbProduct'
|
||||
import VariableProduct from '@/components/Product/Detail/VariableProduct';
|
||||
import Footer from '@/components/Footer/Footer'
|
||||
import productData from '@/data/Product.json'
|
||||
|
||||
const ProductVariableProduct = () => {
|
||||
const searchParams = useSearchParams()
|
||||
let productId = searchParams.get('id')
|
||||
|
||||
if (productId === null) {
|
||||
productId = '1'
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopNavOne props="style-one bg-black" slogan="New customers save 10% with the code GET10" />
|
||||
<div id="header" className='relative w-full'>
|
||||
<MenuOne props="bg-white" />
|
||||
<BreadcrumbProduct data={productData} productPage='variable' productId={productId} />
|
||||
</div>
|
||||
<VariableProduct data={productData} productId={productId} />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProductVariableProduct
|
||||
@ -1,61 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import TopNavOne from '@/components/Header/TopNav/TopNavOne'
|
||||
import MenuOne from '@/components/Header/Menu/MenuOne'
|
||||
import ShopBreadCrumbImg from '@/components/Shop/ShopBreadCrumbImg';
|
||||
import Footer from '@/components/Footer/Footer'
|
||||
import { BaseURL } from '../../../../utils/BaseUrl'
|
||||
import axios from 'axios';
|
||||
|
||||
export default function BreadcrumbImg() {
|
||||
const searchParams = useSearchParams()
|
||||
const type = searchParams.get('type')
|
||||
const categoryName = searchParams.get('category')
|
||||
|
||||
const [products, setProducts] = useState([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [categories, setCategories] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
fetchProducts()
|
||||
getCategory()
|
||||
}, [])
|
||||
|
||||
const fetchProducts = async () => {
|
||||
try {
|
||||
const response = await axios.get(`${BaseURL}/products`)
|
||||
setProducts(response?.data?.data)
|
||||
setLoading(false)
|
||||
} catch (error) {
|
||||
console.error('Error fetching products:', error)
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const getCategory = async () => {
|
||||
try {
|
||||
const res: any = await axios?.get(`${BaseURL}/categories`)
|
||||
setCategories(res?.data?.data)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return <div>Loading...</div>
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopNavOne props="style-one bg-black" slogan="New customers save 10% with the code GET10" />
|
||||
<div id="header" className='relative w-full'>
|
||||
<MenuOne props="bg-transparent" />
|
||||
</div>
|
||||
<ShopBreadCrumbImg data={products} productPerPage={12} dataType={categoryName} category={categories} />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -1,33 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import TopNavOne from '@/components/Header/TopNav/TopNavOne'
|
||||
import MenuOne from '@/components/Header/Menu/MenuOne'
|
||||
import ShopBreadCrumb1 from '@/components/Shop/ShopBreadCrumb1'
|
||||
import productData from '@/data/Product.json'
|
||||
import Footer from '@/components/Footer/Footer'
|
||||
|
||||
export default function BreadCrumb1() {
|
||||
const searchParams = useSearchParams()
|
||||
let [type,setType] = useState<string | null | undefined>()
|
||||
let datatype = searchParams.get('type')
|
||||
let gender = searchParams.get('gender')
|
||||
let category = searchParams.get('category')
|
||||
|
||||
useEffect(() => {
|
||||
setType(datatype);
|
||||
}, [datatype]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopNavOne props="style-one bg-black" slogan="New customers save 10% with the code GET10" />
|
||||
<div id="header" className='relative w-full'>
|
||||
<MenuOne props="bg-transparent" />
|
||||
</div>
|
||||
<ShopBreadCrumb1 data={productData} productPerPage={9} dataType={type} gender={gender} category={category} />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import TopNavOne from '@/components/Header/TopNav/TopNavOne'
|
||||
import MenuOne from '@/components/Header/Menu/MenuOne'
|
||||
import ShopBreadCrumb2 from '@/components/Shop/ShopBreadCrumb2'
|
||||
import productData from '@/data/Product.json'
|
||||
import Footer from '@/components/Footer/Footer'
|
||||
|
||||
export default function BreadCrumb2() {
|
||||
const searchParams = useSearchParams()
|
||||
const type = searchParams.get('type')
|
||||
const category = searchParams.get('category')
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopNavOne props="style-one bg-black" slogan="New customers save 10% with the code GET10" />
|
||||
<div id="header" className='relative w-full'>
|
||||
<MenuOne props="bg-transparent" />
|
||||
</div>
|
||||
<ShopBreadCrumb2 data={productData} productPerPage={9} dataType={type} />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import TopNavOne from '@/components/Header/TopNav/TopNavOne'
|
||||
import MenuOne from '@/components/Header/Menu/MenuOne'
|
||||
import Breadcrumb from '@/components/Breadcrumb/Breadcrumb'
|
||||
import productData from '@/data/Product.json'
|
||||
import ShopCollection from '@/components/Shop/ShopCollection'
|
||||
import Footer from '@/components/Footer/Footer'
|
||||
|
||||
export default function Collection() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopNavOne props="style-one bg-black" slogan="New customers save 10% with the code GET10" />
|
||||
<div id="header" className='relative w-full'>
|
||||
<MenuOne props="bg-transparent" />
|
||||
<Breadcrumb heading='Shop Collection' subHeading='Collection' />
|
||||
</div>
|
||||
<ShopCollection data={productData} />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -1,27 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import TopNavOne from '@/components/Header/TopNav/TopNavOne'
|
||||
import MenuOne from '@/components/Header/Menu/MenuOne'
|
||||
import ShopBreadCrumb1 from '@/components/Shop/ShopBreadCrumb1'
|
||||
import productData from '@/data/Product.json'
|
||||
import Footer from '@/components/Footer/Footer'
|
||||
|
||||
export default function DefaultGrid() {
|
||||
const searchParams = useSearchParams()
|
||||
let type = searchParams.get('type')
|
||||
let gender = searchParams.get('gender')
|
||||
let category = searchParams.get('category')
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopNavOne props="style-one bg-black" slogan="New customers save 10% with the code GET10" />
|
||||
<div id="header" className='relative w-full'>
|
||||
<MenuOne props="bg-transparent" />
|
||||
</div>
|
||||
<ShopBreadCrumb1 data={productData} productPerPage={9} dataType={type} gender={gender} category={category} />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import TopNavOne from '@/components/Header/TopNav/TopNavOne'
|
||||
import MenuOne from '@/components/Header/Menu/MenuOne'
|
||||
import ShopSidebarList from '@/components/Shop/ShopSidebarList'
|
||||
import productData from '@/data/Product.json'
|
||||
import Footer from '@/components/Footer/Footer'
|
||||
|
||||
export default function DefaultList() {
|
||||
const searchParams = useSearchParams()
|
||||
const type = searchParams.get('type')
|
||||
const category = searchParams.get('category')
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopNavOne props="style-one bg-black" slogan="New customers save 10% with the code GET10" />
|
||||
<div id="header" className='relative w-full'>
|
||||
<MenuOne props="bg-transparent" />
|
||||
</div>
|
||||
<ShopSidebarList data={productData} productPerPage={4} dataType={type} />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import TopNavOne from '@/components/Header/TopNav/TopNavOne'
|
||||
import MenuOne from '@/components/Header/Menu/MenuOne'
|
||||
import ShopBreadCrumbImg from '@/components/Shop/ShopBreadCrumbImg';
|
||||
import productData from '@/data/Product.json'
|
||||
import Footer from '@/components/Footer/Footer'
|
||||
|
||||
export default function Default() {
|
||||
const searchParams = useSearchParams()
|
||||
const type = searchParams.get('type')
|
||||
const category = searchParams.get('category')
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopNavOne props="style-one bg-black" slogan="New customers save 10% with the code GET10" />
|
||||
<div id="header" className='relative w-full'>
|
||||
<MenuOne props="bg-transparent" />
|
||||
</div>
|
||||
<ShopBreadCrumbImg data={productData} productPerPage={12} dataType={type} />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -1,362 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { fabric } from 'fabric';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import { BaseURL } from '../../../../utils/BaseUrl';
|
||||
|
||||
export default function TshirtCustomizer() {
|
||||
const searchParams = useSearchParams();
|
||||
const productSlug = searchParams.get('slug');
|
||||
|
||||
const canvasRef = useRef<fabric.Canvas | null>(null);
|
||||
const [shirtColor, setShirtColor] = useState('#ffffff');
|
||||
const [textInput, setTextInput] = useState('');
|
||||
const [fontFamily, setFontFamily] = useState('Arial');
|
||||
const [layers, setLayers] = useState<{ name: string; id: number }[]>([]);
|
||||
const [history, setHistory] = useState<any[]>([]);
|
||||
const [redoStack, setRedoStack] = useState<any[]>([]);
|
||||
const [products, setProducts] = useState<any>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [shirtImageObject, setShirtImageObject] = useState<fabric.Image | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (productSlug) fetchProducts();
|
||||
}, [productSlug]);
|
||||
|
||||
const fetchProducts = async () => {
|
||||
try {
|
||||
const response = await fetch(`${BaseURL}/products/slug/${productSlug}`);
|
||||
const data = await response.json();
|
||||
setProducts(data?.data);
|
||||
setLoading(false);
|
||||
} catch (error) {
|
||||
console.error('Error fetching product:', error);
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const canvas = new fabric.Canvas('tshirt-canvas', {
|
||||
width: window.innerWidth < 500 ? 300 : 500,
|
||||
height: 600,
|
||||
});
|
||||
|
||||
canvasRef.current = canvas;
|
||||
saveHistory();
|
||||
updateLayers();
|
||||
|
||||
canvas.on('mouse:wheel', (opt) => {
|
||||
const delta = opt.e.deltaY;
|
||||
let zoom = canvas.getZoom();
|
||||
zoom *= 0.999 ** delta;
|
||||
zoom = Math.min(Math.max(zoom, 0.5), 2);
|
||||
canvas.zoomToPoint({ x: opt.e.offsetX, y: opt.e.offsetY }, zoom);
|
||||
opt.e.preventDefault();
|
||||
opt.e.stopPropagation();
|
||||
});
|
||||
|
||||
canvas.on('object:added', updateLayers);
|
||||
canvas.on('object:removed', updateLayers);
|
||||
canvas.on('object:modified', saveHistory);
|
||||
canvas.on('selection:created', updateLayers);
|
||||
canvas.on('selection:updated', updateLayers);
|
||||
|
||||
return () => canvas.dispose();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (products && canvasRef.current) {
|
||||
const imageUrl = products?.thumbImage?.[0];
|
||||
|
||||
if (imageUrl) {
|
||||
const existing = canvasRef.current
|
||||
.getObjects('image')
|
||||
.find((img) => (img as fabric.Image).getSrc?.() === imageUrl);
|
||||
|
||||
if (!existing) {
|
||||
fabric.Image.fromURL(
|
||||
imageUrl,
|
||||
(img) => {
|
||||
img.set({
|
||||
left: 50,
|
||||
top: 50,
|
||||
scaleX: 0.5,
|
||||
scaleY: 0.5,
|
||||
selectable: false,
|
||||
});
|
||||
setShirtImageObject(img);
|
||||
canvasRef.current?.add(img);
|
||||
canvasRef.current?.sendToBack(img);
|
||||
canvasRef.current?.renderAll();
|
||||
saveHistory();
|
||||
},
|
||||
{ crossOrigin: 'anonymous' } // ✅ FIXED HERE
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [products]);
|
||||
|
||||
const applyColorFilter = (color: string) => {
|
||||
const canvas = canvasRef.current;
|
||||
if (!canvas || !shirtImageObject) return;
|
||||
|
||||
try {
|
||||
const blendColorFilter = new (fabric as any).Image.filters.BlendColor({
|
||||
color,
|
||||
mode: 'tint',
|
||||
alpha: 0.5,
|
||||
});
|
||||
|
||||
shirtImageObject.filters = [blendColorFilter];
|
||||
shirtImageObject.applyFilters();
|
||||
canvas.renderAll();
|
||||
saveHistory();
|
||||
} catch (err) {
|
||||
console.error('Color filter failed:', err);
|
||||
alert('Failed to apply color filter. Make sure image is CORS-enabled.');
|
||||
}
|
||||
};
|
||||
|
||||
const updateLayers = () => {
|
||||
const objs = canvasRef.current?.getObjects() || [];
|
||||
setLayers(objs.map((obj, i) => ({ name: obj.type + ' ' + (i + 1), id: i })));
|
||||
};
|
||||
|
||||
const saveHistory = () => {
|
||||
const json = canvasRef.current?.toJSON();
|
||||
if (json) setHistory((prev) => [...prev, json.objects]);
|
||||
setRedoStack([]);
|
||||
};
|
||||
|
||||
const handleUndo = () => {
|
||||
if (!canvasRef.current || history.length <= 1) return;
|
||||
const last = history[history.length - 2];
|
||||
setRedoStack((r) => [canvasRef.current?.toJSON().objects, ...r]);
|
||||
canvasRef.current.clear();
|
||||
canvasRef.current.loadFromJSON(
|
||||
{ objects: last },
|
||||
canvasRef.current.renderAll.bind(canvasRef.current)
|
||||
);
|
||||
setHistory((h) => h.slice(0, -1));
|
||||
};
|
||||
|
||||
const handleRedo = () => {
|
||||
if (!canvasRef.current || redoStack.length === 0) return;
|
||||
const next = redoStack[0];
|
||||
setRedoStack((r) => r.slice(1));
|
||||
setHistory((h) => [...h, next]);
|
||||
canvasRef.current.clear();
|
||||
canvasRef.current.loadFromJSON(
|
||||
{ objects: next },
|
||||
canvasRef.current.renderAll.bind(canvasRef.current)
|
||||
);
|
||||
};
|
||||
|
||||
const handleUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const files = e.target.files;
|
||||
if (!files || !canvasRef.current) return;
|
||||
|
||||
Array.from(files).forEach((file) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
if (typeof reader.result === 'string') {
|
||||
fabric.Image.fromURL(reader.result, (img) => {
|
||||
img.scale(0.4).set({ left: 50, top: 50 });
|
||||
canvasRef.current?.add(img);
|
||||
saveHistory();
|
||||
});
|
||||
}
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
};
|
||||
|
||||
const addText = () => {
|
||||
if (!textInput.trim() || !canvasRef.current) return;
|
||||
const text = new fabric.Textbox(textInput, {
|
||||
left: 100,
|
||||
top: 100,
|
||||
fontFamily,
|
||||
fill: '#000',
|
||||
fontSize: 30,
|
||||
});
|
||||
canvasRef.current.add(text);
|
||||
saveHistory();
|
||||
};
|
||||
|
||||
const loadTemplate = async () => {
|
||||
const res = await fetch('/api/designs/my-template');
|
||||
const data = await res.json();
|
||||
if (canvasRef.current && data?.json) {
|
||||
canvasRef.current.loadFromJSON(
|
||||
data.json,
|
||||
canvasRef.current.renderAll.bind(canvasRef.current)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const downloadImage = () => {
|
||||
const dataURL = canvasRef.current?.toDataURL({ format: 'png', quality: 1 });
|
||||
const link = document.createElement('a');
|
||||
link.href = dataURL!;
|
||||
link.download = 'tshirt-design.png';
|
||||
link.click();
|
||||
};
|
||||
|
||||
const deleteObject = (index: number) => {
|
||||
const obj = canvasRef.current?.getObjects()[index];
|
||||
if (obj) {
|
||||
canvasRef.current?.remove(obj);
|
||||
updateLayers();
|
||||
saveHistory();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="product-detail sale sidebar">
|
||||
<div className="featured-product underwear md:py-20 py-10">
|
||||
<div className="container flex justify-between gap-y-6 flex-wrap max-sm:flex-col-reverse">
|
||||
<div className="sidebar lg:w-1/4 sm:w-[40%] lg:pr-12 sm:pr-10 w-full">
|
||||
<div className="filter-type pb-8 border-b border-line">
|
||||
<div className="heading6">Products Type</div>
|
||||
</div>
|
||||
|
||||
<div className="benefit pb-3 border-b border-line mt-3">
|
||||
<div className="heading6">T-shirt Color</div>
|
||||
<input type="color" value={shirtColor} onChange={(e) => { setShirtColor(e.target.value); applyColorFilter(e.target.value); }} className="w-full h-10 border mb-4" />
|
||||
</div>
|
||||
|
||||
<div className="benefit pb-3 border-b border-line mt-3">
|
||||
<div className="heading6">Add Text</div>
|
||||
<input type="text" value={textInput} onChange={(e) => setTextInput(e.target.value)} className="w-full border mb-2" placeholder="Type text" />
|
||||
<select value={fontFamily} onChange={(e) => setFontFamily(e.target.value)} className="w-full p-2 border">
|
||||
<option value="Arial">Arial</option>
|
||||
<option value="Courier New">Courier New</option>
|
||||
<option value="Georgia">Georgia</option>
|
||||
<option value="Comic Sans MS">Comic Sans</option>
|
||||
<option value="Impact">Impact</option>
|
||||
</select>
|
||||
<button onClick={addText} className="w-full p-2 bg-black text-white">Add Text</button>
|
||||
</div>
|
||||
|
||||
<div className="benefit pb-3 border-b border-line mt-3">
|
||||
<div className="heading6">Upload Images</div>
|
||||
<input type="file" accept="image/*" onChange={handleUpload} className="mb-2" />
|
||||
</div>
|
||||
|
||||
<div className="benefit pb-3 border-b border-line mt-3">
|
||||
<div className="heading6">History</div>
|
||||
<div className="flex justify-center mt-2 gap-2">
|
||||
<button onClick={handleUndo} className="w-full bg-black text-white p-2 mb-2 rounded">Undo</button>
|
||||
<button onClick={handleRedo} className="w-full bg-black text-white p-2 mb-2 rounded">Redo</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="benefit pb-3 border-b border-line mt-3">
|
||||
<div className="heading6">Sample</div>
|
||||
<div className="flex justify-center mt-2 gap-2">
|
||||
<button onClick={loadTemplate} className="w-full p-2 bg-black text-white">Load Sample</button>
|
||||
<button onClick={downloadImage} className="w-full p-2 bg-black text-white">Download</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="benefit pb-3 border-b border-line mt-3 ">
|
||||
<div className="heading6">Canvas Controls</div>
|
||||
<div className="flex gap-2 mt-2">
|
||||
<button onClick={() => canvasRef.current?.setZoom(canvasRef.current.getZoom() + 0.1)} className="w-full p-2 bg-black text-white">Zoom In</button>
|
||||
<button onClick={() => canvasRef.current?.setZoom(canvasRef.current.getZoom() - 0.1)} className="w-full p-2 bg-black text-white">Zoom Out</button>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => {
|
||||
if (!canvasRef.current) return;
|
||||
const canvas = canvasRef.current;
|
||||
let isDragging = false;
|
||||
let lastPosX = 0;
|
||||
let lastPosY = 0;
|
||||
|
||||
canvas.on('mouse:down', function (opt) {
|
||||
const evt = opt.e;
|
||||
isDragging = true;
|
||||
lastPosX = evt.clientX;
|
||||
lastPosY = evt.clientY;
|
||||
canvas.setCursor('grab');
|
||||
});
|
||||
|
||||
canvas.on('mouse:move', function (opt) {
|
||||
if (isDragging) {
|
||||
const e = opt.e;
|
||||
const vpt = canvas.viewportTransform!;
|
||||
vpt[4] += e.clientX - lastPosX;
|
||||
vpt[5] += e.clientY - lastPosY;
|
||||
canvas.requestRenderAll();
|
||||
lastPosX = e.clientX;
|
||||
lastPosY = e.clientY;
|
||||
}
|
||||
});
|
||||
|
||||
canvas.on('mouse:up', function () {
|
||||
isDragging = false;
|
||||
canvas.setCursor('default');
|
||||
});
|
||||
}}
|
||||
className="w-full p-2 bg-yellow-500 text-black mt-2 bg-black text-white"
|
||||
>
|
||||
Enable Pan
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="benefit pb-3 border-b border-line mt-3">
|
||||
<div className="heading6">Object Tools</div>
|
||||
<button onClick={() => {
|
||||
const obj = canvasRef.current?.getActiveObject();
|
||||
if (obj) {
|
||||
canvasRef.current.remove(obj);
|
||||
updateLayers();
|
||||
saveHistory();
|
||||
}
|
||||
}} className="w-full p-2 mt-2 bg-black text-white">Delete Selected</button>
|
||||
<button onClick={() => canvasRef.current?.clear()} className="w-full p-2 mt-2 bg-black text-white">Clear Canvas</button>
|
||||
<button onClick={() => {
|
||||
const obj = canvasRef.current?.getActiveObject();
|
||||
if (obj) canvasRef.current.bringForward(obj);
|
||||
}} className="w-full p-2 mt-2 bg-black text-white">Bring Forward</button>
|
||||
<button onClick={() => {
|
||||
const obj = canvasRef.current?.getActiveObject();
|
||||
if (obj) canvasRef.current.sendBackwards(obj);
|
||||
}} className="w-full p-2 mt-2 bg-black text-white">Send Backward</button>
|
||||
</div>
|
||||
|
||||
<div className="heading6 mt-4">Layers</div>
|
||||
<ul className="list-disc list-inside text-sm mt-2">
|
||||
{layers.map((layer, index) => (
|
||||
<li key={layer.id} className="flex justify-between">
|
||||
<span>{layer.name}</span>
|
||||
<button
|
||||
onClick={() => {
|
||||
const obj = canvasRef.current?.getObjects()[layer.id];
|
||||
if (obj) {
|
||||
canvasRef.current?.remove(obj);
|
||||
updateLayers();
|
||||
saveHistory();
|
||||
}
|
||||
}}
|
||||
className="bg-black text-white"
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="right lg:w-3/4 sm:w-[60%] sm:pl-3 w-full">
|
||||
<canvas id="tshirt-canvas" className="border w-full w-100" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import TopNavOne from '@/components/Header/TopNav/TopNavOne'
|
||||
import MenuOne from '@/components/Header/Menu/MenuOne'
|
||||
import ShopFilterCanvas from '@/components/Shop/ShopFilterCanvas'
|
||||
import productData from '@/data/Product.json'
|
||||
import Footer from '@/components/Footer/Footer'
|
||||
|
||||
export default function FilterCanvas() {
|
||||
const searchParams = useSearchParams()
|
||||
const type = searchParams.get('type')
|
||||
const category = searchParams.get('category')
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopNavOne props="style-one bg-black" slogan="New customers save 10% with the code GET10" />
|
||||
<div id="header" className='relative w-full'>
|
||||
<MenuOne props="bg-transparent" />
|
||||
</div>
|
||||
<ShopFilterCanvas data={productData} productPerPage={12} dataType={type} />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import TopNavOne from '@/components/Header/TopNav/TopNavOne'
|
||||
import MenuOne from '@/components/Header/Menu/MenuOne'
|
||||
import ShopFilterDropdown from '@/components/Shop/ShopFilterDropdown'
|
||||
import productData from '@/data/Product.json'
|
||||
import Footer from '@/components/Footer/Footer'
|
||||
|
||||
export default function FilterDropdown() {
|
||||
const searchParams = useSearchParams()
|
||||
const type = searchParams.get('type')
|
||||
const category = searchParams.get('category')
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopNavOne props="style-one bg-black" slogan="New customers save 10% with the code GET10" />
|
||||
<div id="header" className='relative w-full'>
|
||||
<MenuOne props="bg-transparent" />
|
||||
</div>
|
||||
<ShopFilterDropdown data={productData} productPerPage={12} dataType={type} />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import TopNavOne from '@/components/Header/TopNav/TopNavOne'
|
||||
import MenuOne from '@/components/Header/Menu/MenuOne'
|
||||
import ShopFilterOptions from '@/components/Shop/ShopFilterOptions'
|
||||
import productData from '@/data/Product.json'
|
||||
import Footer from '@/components/Footer/Footer'
|
||||
|
||||
export default function FilterOptions() {
|
||||
const searchParams = useSearchParams()
|
||||
const type = searchParams.get('type')
|
||||
const category = searchParams.get('category')
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopNavOne props="style-one bg-black" slogan="New customers save 10% with the code GET10" />
|
||||
<div id="header" className='relative w-full'>
|
||||
<MenuOne props="bg-transparent" />
|
||||
</div>
|
||||
<ShopFilterOptions data={productData} productPerPage={12} />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import TopNavOne from '@/components/Header/TopNav/TopNavOne'
|
||||
import MenuOne from '@/components/Header/Menu/MenuOne'
|
||||
import ShopFilterCanvas from '@/components/Shop/ShopFilterCanvas'
|
||||
import productData from '@/data/Product.json'
|
||||
import Footer from '@/components/Footer/Footer'
|
||||
|
||||
export default function Fullwidth() {
|
||||
const searchParams = useSearchParams()
|
||||
const type = searchParams.get('type')
|
||||
const category = searchParams.get('category')
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopNavOne props="style-one bg-black" slogan="New customers save 10% with the code GET10" />
|
||||
<div id="header" className='relative w-full'>
|
||||
<MenuOne props="bg-transparent" />
|
||||
</div>
|
||||
<ShopFilterCanvas data={productData} productPerPage={12} dataType={type} />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import TopNavOne from '@/components/Header/TopNav/TopNavOne'
|
||||
import MenuOne from '@/components/Header/Menu/MenuOne'
|
||||
import ShopSidebarList from '@/components/Shop/ShopSidebarList'
|
||||
import productData from '@/data/Product.json'
|
||||
import Footer from '@/components/Footer/Footer'
|
||||
|
||||
export default function SidebarList() {
|
||||
const searchParams = useSearchParams()
|
||||
const type = searchParams.get('type')
|
||||
const category = searchParams.get('category')
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopNavOne props="style-one bg-black" slogan="New customers save 10% with the code GET10" />
|
||||
<div id="header" className='relative w-full'>
|
||||
<MenuOne props="bg-transparent" />
|
||||
</div>
|
||||
<ShopSidebarList data={productData} productPerPage={4} dataType={type} />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import TopNavOne from '@/components/Header/TopNav/TopNavOne'
|
||||
import MenuOne from '@/components/Header/Menu/MenuOne'
|
||||
import ShopFilterDropdown from '@/components/Shop/ShopFilterDropdown'
|
||||
import productData from '@/data/Product.json'
|
||||
import Footer from '@/components/Footer/Footer'
|
||||
|
||||
export default function FilterDropdown() {
|
||||
const searchParams = useSearchParams()
|
||||
const type = searchParams.get('type')
|
||||
const category = searchParams.get('category')
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopNavOne props="style-one bg-black" slogan="New customers save 10% with the code GET10" />
|
||||
<div id="header" className='relative w-full'>
|
||||
<MenuOne props="bg-transparent" />
|
||||
</div>
|
||||
<div className="shop-square">
|
||||
<ShopFilterDropdown data={productData} productPerPage={12} dataType={type} />
|
||||
</div>
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -842,7 +842,7 @@ const MenuOne: React.FC<Props> = ({ props }) => {
|
||||
</div>
|
||||
</div> */}
|
||||
</li>
|
||||
<li className='h-full'>
|
||||
{/* <li className='h-full'>
|
||||
<Link
|
||||
href="#!"
|
||||
className={`text-button-uppercase duration-300 h-full flex items-center justify-center ${pathname.includes('/product/') ? 'active' : ''}`}
|
||||
@ -1059,7 +1059,7 @@ const MenuOne: React.FC<Props> = ({ props }) => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</li> */}
|
||||
<li className='h-full relative'>
|
||||
<Link href="#!" className={`text-button-uppercase duration-300 h-full flex items-center justify-center ${pathname.includes('/blog') ? 'active' : ''}`}>
|
||||
Blog
|
||||
|
||||
@ -25,12 +25,13 @@ const ModalCart = ({ serverTimeLeft }: { serverTimeLeft: CountdownTimeType }) =>
|
||||
return () => clearInterval(timer);
|
||||
}, []);
|
||||
|
||||
// Refresh cart when modal opens
|
||||
useEffect(() => {
|
||||
if (isModalOpen) {
|
||||
refreshCart()
|
||||
}
|
||||
}, [isModalOpen, refreshCart])
|
||||
// Cart is already in cartState.cartArray - no need to refresh
|
||||
// Removing this prevents clearing the cart when modal opens
|
||||
// useEffect(() => {
|
||||
// if (isModalOpen) {
|
||||
// refreshCart()
|
||||
// }
|
||||
// }, [isModalOpen, refreshCart])
|
||||
|
||||
const handleRemoveFromCart = async (item: any) => {
|
||||
const token = localStorage.getItem("token");
|
||||
|
||||
@ -48,18 +48,42 @@ const Product: React.FC<ProductProps> = ({ data, type, style }) => {
|
||||
|
||||
const handleAddToCart = async () => {
|
||||
const token = localStorage.getItem("token"); // check login
|
||||
|
||||
// Get the product ID (handle both _id and id)
|
||||
const productId = data._id || data.id;
|
||||
|
||||
const payload = {
|
||||
productId: data._id,
|
||||
quantity: data.quantityPurchase,
|
||||
productId: productId,
|
||||
quantity: data.quantityPurchase || 1,
|
||||
color: activeColor ? activeColor : data?.variation[0]?.color,
|
||||
size: activeSize ? activeSize : data?.sizes[0],
|
||||
};
|
||||
|
||||
// Create cart item with proper structure for localStorage
|
||||
const cartItem = {
|
||||
...data,
|
||||
id: productId, // ✅ Ensure id is set for CartContext
|
||||
_id: productId, // ✅ Also set _id for consistency
|
||||
quantity: payload.quantity,
|
||||
selectedColor: payload.color,
|
||||
selectedSize: payload.size,
|
||||
quantityPurchase: payload.quantity
|
||||
};
|
||||
|
||||
console.log("🛒 Adding to cart:", cartItem);
|
||||
console.log("📦 Current cart:", cartState.cartArray);
|
||||
|
||||
// ✅ Always update Redux (works for both guest + logged-in users)
|
||||
if (!cartState.cartArray.find((item) => item.id === data.id)) {
|
||||
addToCart({ ...data, ...payload });
|
||||
const existingItem = cartState.cartArray.find((item) =>
|
||||
(item.id === productId || item._id === productId)
|
||||
);
|
||||
|
||||
if (!existingItem) {
|
||||
console.log("➕ Adding new item to cart");
|
||||
addToCart(cartItem);
|
||||
} else {
|
||||
updateCart(data.id, data.quantityPurchase, payload.size, payload.color);
|
||||
console.log("🔄 Updating existing item");
|
||||
updateCart(productId, payload.quantity, payload.size, payload.color);
|
||||
}
|
||||
|
||||
// ✅ If logged in → also sync with API
|
||||
@ -81,7 +105,7 @@ const Product: React.FC<ProductProps> = ({ data, type, style }) => {
|
||||
);
|
||||
}
|
||||
} else {
|
||||
console.log("🛒 Guest mode: cart stored only in Redux");
|
||||
console.log("🛒 Guest mode: cart stored in Redux + localStorage (via CartContext)");
|
||||
}
|
||||
|
||||
openModalCart();
|
||||
|
||||
@ -10,6 +10,7 @@ interface CartItem extends ProductType {
|
||||
quantity: number
|
||||
selectedSize: string
|
||||
selectedColor: string
|
||||
_id?: string // Support MongoDB _id format
|
||||
}
|
||||
|
||||
interface CartState {
|
||||
@ -65,18 +66,32 @@ const CART_KEY = 'guestCart' // Using same key for all users now
|
||||
|
||||
const saveCartToLocalStorage = (cart: CartItem[]) => {
|
||||
try {
|
||||
console.log("💾 saveCartToLocalStorage called with:", cart);
|
||||
localStorage.setItem(CART_KEY, JSON.stringify(cart))
|
||||
console.log("✅ Cart saved to localStorage successfully");
|
||||
} catch (error) {
|
||||
console.error('Error saving cart to localStorage:', error)
|
||||
console.error('❌ Error saving cart to localStorage:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const loadCartFromLocalStorage = (): CartItem[] => {
|
||||
try {
|
||||
const cart = localStorage.getItem(CART_KEY)
|
||||
return cart ? JSON.parse(cart) : []
|
||||
const cartString = localStorage.getItem(CART_KEY)
|
||||
console.log("📖 loadCartFromLocalStorage - Raw data:", cartString);
|
||||
console.log("📖 loadCartFromLocalStorage - Type:", typeof cartString);
|
||||
console.log("📖 loadCartFromLocalStorage - Length:", cartString?.length);
|
||||
|
||||
// Check if it's the string "[]" vs actual empty
|
||||
if (cartString === "[]") {
|
||||
console.warn("⚠️ localStorage contains empty array string '[]'");
|
||||
}
|
||||
|
||||
const cart = cartString ? JSON.parse(cartString) : []
|
||||
console.log("📖 loadCartFromLocalStorage - Parsed cart:", cart);
|
||||
console.log("📖 loadCartFromLocalStorage - Parsed cart length:", cart.length);
|
||||
return cart
|
||||
} catch (error) {
|
||||
console.error('Error loading cart from localStorage:', error)
|
||||
console.error('❌ Error loading cart from localStorage:', error)
|
||||
return []
|
||||
}
|
||||
}
|
||||
@ -93,12 +108,16 @@ const clearCartFromLocalStorage = () => {
|
||||
export const CartProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||
const [cartState, dispatch] = useReducer(cartReducer, { cartArray: [] })
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [isInitialLoad, setIsInitialLoad] = useState(true)
|
||||
|
||||
const refreshCart = useCallback(async () => {
|
||||
// Load from localStorage for ALL users (both logged-in and guest)
|
||||
console.log("🔄 refreshCart called");
|
||||
const cart = loadCartFromLocalStorage()
|
||||
console.log("🔄 refreshCart - Dispatching LOAD_CART with:", cart);
|
||||
dispatch({ type: 'LOAD_CART', payload: cart })
|
||||
setLoading(false)
|
||||
setIsInitialLoad(false) // Mark that initial load is complete
|
||||
}, [])
|
||||
|
||||
// Load cart initially
|
||||
@ -107,17 +126,41 @@ export const CartProvider: React.FC<{ children: React.ReactNode }> = ({ children
|
||||
}, [refreshCart])
|
||||
|
||||
// Sync cart to localStorage for ALL users whenever cartState changes
|
||||
// BUT skip on initial load to prevent overwriting with empty cart
|
||||
useEffect(() => {
|
||||
if (!loading) {
|
||||
console.log("🔔 useEffect triggered - loading:", loading, "isInitialLoad:", isInitialLoad, "cartArray:", cartState.cartArray);
|
||||
if (!loading && !isInitialLoad) {
|
||||
console.log("💾 Calling saveCartToLocalStorage...");
|
||||
saveCartToLocalStorage(cartState.cartArray)
|
||||
} else {
|
||||
if (loading) {
|
||||
console.log("⏳ Skipping save - still loading");
|
||||
}
|
||||
if (isInitialLoad) {
|
||||
console.log("🚀 Skipping save - initial load (preventing empty cart overwrite)");
|
||||
}
|
||||
}
|
||||
}, [cartState.cartArray, loading])
|
||||
}, [cartState.cartArray, loading, isInitialLoad])
|
||||
|
||||
const addToCart = (item: CartItem) => dispatch({ type: 'ADD_TO_CART', payload: item })
|
||||
const removeFromCart = (itemId: string) => dispatch({ type: 'REMOVE_FROM_CART', payload: itemId })
|
||||
const updateCart = (itemId: string, quantity: number, selectedSize: string, selectedColor: string) =>
|
||||
const addToCart = (item: CartItem) => {
|
||||
console.log("➕ addToCart:", item);
|
||||
dispatch({ type: 'ADD_TO_CART', payload: item })
|
||||
}
|
||||
|
||||
const removeFromCart = (itemId: string) => {
|
||||
console.log("➖ removeFromCart:", itemId);
|
||||
dispatch({ type: 'REMOVE_FROM_CART', payload: itemId })
|
||||
}
|
||||
|
||||
const updateCart = (itemId: string, quantity: number, selectedSize: string, selectedColor: string) => {
|
||||
console.log("🔄 updateCart:", { itemId, quantity, selectedSize, selectedColor });
|
||||
dispatch({ type: 'UPDATE_CART', payload: { itemId, quantity, selectedSize, selectedColor } })
|
||||
const loadCart = (items: CartItem[]) => dispatch({ type: 'LOAD_CART', payload: items })
|
||||
}
|
||||
|
||||
const loadCart = (items: CartItem[]) => {
|
||||
console.log("📥 loadCart:", items);
|
||||
dispatch({ type: 'LOAD_CART', payload: items })
|
||||
}
|
||||
const clearCart = () => {
|
||||
console.log("🗑️ Clearing cart - Redux store and localStorage");
|
||||
dispatch({ type: 'CLEAR_CART' })
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user