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 Cart = () => {
|
||||||
const [timeLeft, setTimeLeft] = useState(countdownTime());
|
const [timeLeft, setTimeLeft] = useState(countdownTime());
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const [cart, setCart] = useState([])
|
const { cartState, updateCart, removeFromCart } = useCart();
|
||||||
const [token, setToken] = useState<string | null>()
|
const [isLoggedIn, setIsLoggedIn] = useState(false)
|
||||||
|
|
||||||
|
// Check if user is logged in
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getCartData();
|
const token = localStorage.getItem('token')
|
||||||
}, []);
|
setIsLoggedIn(!!token)
|
||||||
|
|
||||||
useEffect(() => {
|
// If logged in, optionally sync with API (cart is already in cartState)
|
||||||
const Token = localStorage.getItem('token')
|
if (token) {
|
||||||
setToken(Token)
|
console.log("✅ Logged in - using CartContext (already synced)")
|
||||||
|
} else {
|
||||||
|
console.log("🛒 Guest mode - using CartContext from localStorage")
|
||||||
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const getCartData = async () => {
|
// ✅ Use cartState.cartArray as the source of truth for BOTH guest and logged-in users
|
||||||
try {
|
const cart = cartState.cartArray;
|
||||||
const token = localStorage.getItem("token"); // should be plain token string
|
console.log("📦 Cart from CartContext:", cart)
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -63,57 +45,40 @@ const Cart = () => {
|
|||||||
return () => clearInterval(timer);
|
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) => {
|
const handleQuantityChange = async (item: any, newQuantity: number) => {
|
||||||
if (newQuantity < 1) return;
|
if (newQuantity < 1) return;
|
||||||
|
|
||||||
// Optimistic Update: Update local state immediately
|
// Get the product ID (handle both formats)
|
||||||
const updatedCart: any = cart.map((cartItem: any) => {
|
const productId = item._id || item.id;
|
||||||
if (cartItem._id === item._id) {
|
const size = item.selectedSize || item.size;
|
||||||
return { ...cartItem, quantity: newQuantity };
|
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;
|
let moneyForFreeship = 150;
|
||||||
@ -157,30 +122,25 @@ const Cart = () => {
|
|||||||
|
|
||||||
|
|
||||||
const handleRemoveCart = async (item: any) => {
|
const handleRemoveCart = async (item: any) => {
|
||||||
// Optimistic update for local cart
|
const productId = item._id || item.id;
|
||||||
const updatedCart = cart.filter((cartItem: any) => cartItem._id !== item._id);
|
const size = item.selectedSize || item.size;
|
||||||
setCart(updatedCart);
|
const color = item.selectedColor || item.color;
|
||||||
const token = localStorage.getItem("token");
|
|
||||||
|
|
||||||
// ✅ Immediately remove from CartContext (localStorage + Redux)
|
// Remove from CartContext immediately (works for both)
|
||||||
const cartItem = cartState.cartArray.find(
|
removeFromCart(productId);
|
||||||
(cartItem) => (cartItem.id === item.product._id || cartItem.id === item.product.id)
|
|
||||||
);
|
|
||||||
if (cartItem) {
|
|
||||||
removeFromCart(cartItem.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ✅ Then sync with API
|
// If logged in, also sync with API
|
||||||
try {
|
if (isLoggedIn) {
|
||||||
const res = await axios.delete(`${BaseURL}/cart/${item.product._id}`, {
|
const token = localStorage.getItem("token");
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
try {
|
||||||
params: { color: item.color, size: item.size }, // ✅ query params
|
await axios.delete(`${BaseURL}/cart/${productId}`, {
|
||||||
});
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
|
params: { color, size },
|
||||||
getCartData();
|
});
|
||||||
console.log("Removed from cart:", res.data);
|
console.log("✅ Item removed from API cart");
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("Error removing item:", error.response?.data || error.message);
|
console.error("❌ Error removing from API cart:", error.response?.data || error.message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -238,58 +198,72 @@ const Cart = () => {
|
|||||||
{cart?.length < 1 ? (
|
{cart?.length < 1 ? (
|
||||||
<p className='text-button pt-3'>No product in cart</p>
|
<p className='text-button pt-3'>No product in cart</p>
|
||||||
) : (
|
) : (
|
||||||
cart?.map((cart: any) => (
|
cart?.map((item: any) => {
|
||||||
<div className="item flex md:mt-7 md:pb-7 mt-5 pb-5 border-b border-line w-full" key={cart._id}>
|
// Handle both API format (item.product.name) and CartContext format (item.name)
|
||||||
<div className="w-1/2">
|
const itemId = item._id || item.id;
|
||||||
<div className="flex items-center gap-6">
|
const itemName = item.product?.name || item.name;
|
||||||
<div className="bg-img md:w-[100px] w-20 aspect-[3/4]">
|
const itemImage = item.product?.thumbImage?.[0] || item.thumbImage?.[0] || item.images?.[0];
|
||||||
<Image
|
const itemPrice = item.price;
|
||||||
src={cart.product.thumbImage[0]}
|
const itemQuantity = item.quantity || item.quantityPurchase || 1;
|
||||||
width={1000}
|
const itemSize = item.selectedSize || item.size || item.sizes?.[0];
|
||||||
height={1000}
|
const itemColor = item.selectedColor || item.color || item.variation?.[0]?.color;
|
||||||
alt={cart.product.name}
|
|
||||||
className='w-full h-full object-cover rounded-lg'
|
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>
|
|
||||||
<div className="text-title">{cart.product.name}</div>
|
|
||||||
<div className="list-select mt-3"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="w-1/6 flex total-price items-center justify-center">
|
||||||
<div className="w-1/12 price flex items-center justify-center">
|
<div className="text-title text-center">${itemQuantity * itemPrice}.00</div>
|
||||||
<div className="text-title text-center">${cart.price}.00</div>
|
</div>
|
||||||
</div>
|
<div className="w-1/12 flex items-center justify-center">
|
||||||
<div className="w-1/6 flex items-center justify-center">
|
<Icon.XCircle
|
||||||
<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">
|
className='text-xl max-md:text-base text-red cursor-pointer hover:text-black duration-500'
|
||||||
<Icon.Minus
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (cart.quantity > 1) {
|
handleRemoveCart(item)
|
||||||
handleQuantityChange(cart, cart.quantity - 1)
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
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>
|
</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>
|
||||||
</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>
|
||||||
</div> */}
|
</div> */}
|
||||||
</li>
|
</li>
|
||||||
<li className='h-full'>
|
{/* <li className='h-full'>
|
||||||
<Link
|
<Link
|
||||||
href="#!"
|
href="#!"
|
||||||
className={`text-button-uppercase duration-300 h-full flex items-center justify-center ${pathname.includes('/product/') ? 'active' : ''}`}
|
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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li> */}
|
||||||
<li className='h-full relative'>
|
<li className='h-full relative'>
|
||||||
<Link href="#!" className={`text-button-uppercase duration-300 h-full flex items-center justify-center ${pathname.includes('/blog') ? 'active' : ''}`}>
|
<Link href="#!" className={`text-button-uppercase duration-300 h-full flex items-center justify-center ${pathname.includes('/blog') ? 'active' : ''}`}>
|
||||||
Blog
|
Blog
|
||||||
|
|||||||
@ -25,12 +25,13 @@ const ModalCart = ({ serverTimeLeft }: { serverTimeLeft: CountdownTimeType }) =>
|
|||||||
return () => clearInterval(timer);
|
return () => clearInterval(timer);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Refresh cart when modal opens
|
// Cart is already in cartState.cartArray - no need to refresh
|
||||||
useEffect(() => {
|
// Removing this prevents clearing the cart when modal opens
|
||||||
if (isModalOpen) {
|
// useEffect(() => {
|
||||||
refreshCart()
|
// if (isModalOpen) {
|
||||||
}
|
// refreshCart()
|
||||||
}, [isModalOpen, refreshCart])
|
// }
|
||||||
|
// }, [isModalOpen, refreshCart])
|
||||||
|
|
||||||
const handleRemoveFromCart = async (item: any) => {
|
const handleRemoveFromCart = async (item: any) => {
|
||||||
const token = localStorage.getItem("token");
|
const token = localStorage.getItem("token");
|
||||||
|
|||||||
@ -48,18 +48,42 @@ const Product: React.FC<ProductProps> = ({ data, type, style }) => {
|
|||||||
|
|
||||||
const handleAddToCart = async () => {
|
const handleAddToCart = async () => {
|
||||||
const token = localStorage.getItem("token"); // check login
|
const token = localStorage.getItem("token"); // check login
|
||||||
|
|
||||||
|
// Get the product ID (handle both _id and id)
|
||||||
|
const productId = data._id || data.id;
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
productId: data._id,
|
productId: productId,
|
||||||
quantity: data.quantityPurchase,
|
quantity: data.quantityPurchase || 1,
|
||||||
color: activeColor ? activeColor : data?.variation[0]?.color,
|
color: activeColor ? activeColor : data?.variation[0]?.color,
|
||||||
size: activeSize ? activeSize : data?.sizes[0],
|
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)
|
// ✅ Always update Redux (works for both guest + logged-in users)
|
||||||
if (!cartState.cartArray.find((item) => item.id === data.id)) {
|
const existingItem = cartState.cartArray.find((item) =>
|
||||||
addToCart({ ...data, ...payload });
|
(item.id === productId || item._id === productId)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!existingItem) {
|
||||||
|
console.log("➕ Adding new item to cart");
|
||||||
|
addToCart(cartItem);
|
||||||
} else {
|
} 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
|
// ✅ If logged in → also sync with API
|
||||||
@ -81,7 +105,7 @@ const Product: React.FC<ProductProps> = ({ data, type, style }) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log("🛒 Guest mode: cart stored only in Redux");
|
console.log("🛒 Guest mode: cart stored in Redux + localStorage (via CartContext)");
|
||||||
}
|
}
|
||||||
|
|
||||||
openModalCart();
|
openModalCart();
|
||||||
|
|||||||
@ -10,6 +10,7 @@ interface CartItem extends ProductType {
|
|||||||
quantity: number
|
quantity: number
|
||||||
selectedSize: string
|
selectedSize: string
|
||||||
selectedColor: string
|
selectedColor: string
|
||||||
|
_id?: string // Support MongoDB _id format
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CartState {
|
interface CartState {
|
||||||
@ -65,18 +66,32 @@ const CART_KEY = 'guestCart' // Using same key for all users now
|
|||||||
|
|
||||||
const saveCartToLocalStorage = (cart: CartItem[]) => {
|
const saveCartToLocalStorage = (cart: CartItem[]) => {
|
||||||
try {
|
try {
|
||||||
|
console.log("💾 saveCartToLocalStorage called with:", cart);
|
||||||
localStorage.setItem(CART_KEY, JSON.stringify(cart))
|
localStorage.setItem(CART_KEY, JSON.stringify(cart))
|
||||||
|
console.log("✅ Cart saved to localStorage successfully");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error saving cart to localStorage:', error)
|
console.error('❌ Error saving cart to localStorage:', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadCartFromLocalStorage = (): CartItem[] => {
|
const loadCartFromLocalStorage = (): CartItem[] => {
|
||||||
try {
|
try {
|
||||||
const cart = localStorage.getItem(CART_KEY)
|
const cartString = localStorage.getItem(CART_KEY)
|
||||||
return cart ? JSON.parse(cart) : []
|
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) {
|
} catch (error) {
|
||||||
console.error('Error loading cart from localStorage:', error)
|
console.error('❌ Error loading cart from localStorage:', error)
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -93,12 +108,16 @@ const clearCartFromLocalStorage = () => {
|
|||||||
export const CartProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
export const CartProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||||
const [cartState, dispatch] = useReducer(cartReducer, { cartArray: [] })
|
const [cartState, dispatch] = useReducer(cartReducer, { cartArray: [] })
|
||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
|
const [isInitialLoad, setIsInitialLoad] = useState(true)
|
||||||
|
|
||||||
const refreshCart = useCallback(async () => {
|
const refreshCart = useCallback(async () => {
|
||||||
// Load from localStorage for ALL users (both logged-in and guest)
|
// Load from localStorage for ALL users (both logged-in and guest)
|
||||||
|
console.log("🔄 refreshCart called");
|
||||||
const cart = loadCartFromLocalStorage()
|
const cart = loadCartFromLocalStorage()
|
||||||
|
console.log("🔄 refreshCart - Dispatching LOAD_CART with:", cart);
|
||||||
dispatch({ type: 'LOAD_CART', payload: cart })
|
dispatch({ type: 'LOAD_CART', payload: cart })
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
|
setIsInitialLoad(false) // Mark that initial load is complete
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
// Load cart initially
|
// Load cart initially
|
||||||
@ -107,17 +126,41 @@ export const CartProvider: React.FC<{ children: React.ReactNode }> = ({ children
|
|||||||
}, [refreshCart])
|
}, [refreshCart])
|
||||||
|
|
||||||
// Sync cart to localStorage for ALL users whenever cartState changes
|
// Sync cart to localStorage for ALL users whenever cartState changes
|
||||||
|
// BUT skip on initial load to prevent overwriting with empty cart
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loading) {
|
console.log("🔔 useEffect triggered - loading:", loading, "isInitialLoad:", isInitialLoad, "cartArray:", cartState.cartArray);
|
||||||
|
if (!loading && !isInitialLoad) {
|
||||||
|
console.log("💾 Calling saveCartToLocalStorage...");
|
||||||
saveCartToLocalStorage(cartState.cartArray)
|
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 addToCart = (item: CartItem) => {
|
||||||
const removeFromCart = (itemId: string) => dispatch({ type: 'REMOVE_FROM_CART', payload: itemId })
|
console.log("➕ addToCart:", item);
|
||||||
const updateCart = (itemId: string, quantity: number, selectedSize: string, selectedColor: string) =>
|
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 } })
|
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 = () => {
|
const clearCart = () => {
|
||||||
console.log("🗑️ Clearing cart - Redux store and localStorage");
|
console.log("🗑️ Clearing cart - Redux store and localStorage");
|
||||||
dispatch({ type: 'CLEAR_CART' })
|
dispatch({ type: 'CLEAR_CART' })
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user