import React, { useState } from 'react'; import { useQuery } from '@tanstack/react-query'; import { useNavigate } from 'react-router-dom'; import { Search, Filter, X } from 'lucide-react'; import { apiClient } from '@/lib/api/client'; import { useCartStore } from '@/lib/cart/store'; import { Button } from '@/components/ui/button'; import Container from '@/components/Layout/Container'; import { ProductCard } from '@/components/ProductCard'; import { toast } from 'sonner'; import { useTheme, useLayout } from '@/contexts/ThemeContext'; import { useShopSettings } from '@/hooks/useAppearanceSettings'; import SEOHead from '@/components/SEOHead'; import type { ProductsResponse, ProductCategory, Product } from '@/types/product'; export default function Shop() { const navigate = useNavigate(); const { config } = useTheme(); const { layout } = useLayout(); const { layout: shopLayout, elements } = useShopSettings(); const [page, setPage] = useState(1); const [search, setSearch] = useState(''); const [category, setCategory] = useState(''); const [sortBy, setSortBy] = useState(''); const { addItem } = useCartStore(); // Map grid columns setting to Tailwind classes (responsive) const gridCols = typeof shopLayout.grid_columns === 'object' ? shopLayout.grid_columns : { mobile: '2', tablet: '3', desktop: '4' }; // Map to actual Tailwind classes (can't use template literals due to purging) const mobileClass = { '1': 'grid-cols-1', '2': 'grid-cols-2', '3': 'grid-cols-3', }[gridCols.mobile] || 'grid-cols-2'; const tabletClass = { '2': 'md:grid-cols-2', '3': 'md:grid-cols-3', '4': 'md:grid-cols-4', }[gridCols.tablet] || 'md:grid-cols-3'; const desktopClass = { '2': 'lg:grid-cols-2', '3': 'lg:grid-cols-3', '4': 'lg:grid-cols-4', '5': 'lg:grid-cols-5', '6': 'lg:grid-cols-6', }[gridCols.desktop] || 'lg:grid-cols-4'; const gridColsClass = `${mobileClass} ${tabletClass} ${desktopClass}`; // Masonry column classes const masonryMobileClass = { '1': 'columns-1', '2': 'columns-2', '3': 'columns-3', }[gridCols.mobile] || 'columns-2'; const masonryTabletClass = { '2': 'md:columns-2', '3': 'md:columns-3', '4': 'md:columns-4', }[gridCols.tablet] || 'md:columns-3'; const masonryDesktopClass = { '2': 'lg:columns-2', '3': 'lg:columns-3', '4': 'lg:columns-4', '5': 'lg:columns-5', '6': 'lg:columns-6', }[gridCols.desktop] || 'lg:columns-4'; const masonryColsClass = `${masonryMobileClass} ${masonryTabletClass} ${masonryDesktopClass}`; const isMasonry = shopLayout.grid_style === 'masonry'; // Fetch products const { data: productsData, isLoading: productsLoading } = useQuery({ queryKey: ['products', page, search, category], queryFn: () => apiClient.get(apiClient.endpoints.shop.products, { page, per_page: 12, search, category, }), }); // Fetch categories const { data: categories } = useQuery({ queryKey: ['categories'], queryFn: () => apiClient.get(apiClient.endpoints.shop.categories), }); const handleAddToCart = async (product: any) => { try { const response = await apiClient.post(apiClient.endpoints.cart.add, { product_id: product.id, quantity: 1, }); // Add to local cart store addItem({ key: `${product.id}`, product_id: product.id, name: product.name, price: parseFloat(product.price), quantity: 1, image: product.image, virtual: product.virtual, downloadable: product.downloadable, }); toast.success(`${product.name} added to cart!`, { action: { label: 'View Cart', onClick: () => navigate('/cart'), }, }); } catch (error) { toast.error('Failed to add to cart'); console.error(error); } }; return ( {/* SEO Meta Tags for Social Sharing */} {/* Header */}

Shop

Browse our collection of products

{/* Filters */} {(elements.search_bar || elements.category_filter) && (
{/* Search */} {elements.search_bar && (
setSearch(e.target.value)} className="w-full !pl-10 pr-10 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-primary" /> {search && ( )}
)} {/* Category Filter */} {elements.category_filter && categories && categories.length > 0 && (
)} {/* Sort Dropdown */} {elements.sort_dropdown && (
)}
)} {/* Products Grid */} {productsLoading ? (
{[...Array(8)].map((_, i) => (
))}
) : productsData?.products && productsData.products.length > 0 ? ( <>
{productsData.products.map((product: any) => (
))}
{/* Pagination */} {productsData.total_pages > 1 && (
Page {page} of {productsData.total_pages}
)} ) : (

No products found

{(search || category) && ( )}
)} ); }