'use client'; import { Box, Container, Typography, Button, Card, CardContent, CardMedia, Grid, Chip, CircularProgress, Alert, TextField, InputAdornment, Pagination, FormControl, Select, MenuItem, InputLabel, Drawer, List, ListItem, ListItemButton, ListItemText, Collapse, IconButton, useMediaQuery, useTheme, Paper, Slider, Switch, FormControlLabel } from '@mui/material'; import { useState, useEffect, useCallback } from 'react'; import { Search, ExpandLess, ExpandMore, Close as CloseIcon, Tune as TuneIcon } from '@mui/icons-material'; import clientApi from "@/lib/clientApi"; import useRoles from "@/app/components/hooks/useRoles"; const SORT_OPTIONS = [ { value: 'Name-ASC', label: 'Name (A-Z)' }, { value: 'Name-DESC', label: 'Name (Z-A)' }, { value: 'Price-ASC', label: 'Price (Low to High)' }, { value: 'Price-DESC', label: 'Price (High to Low)' }, { value: 'CreatedDate-DESC', label: 'Newest First' }, { value: 'CreatedDate-ASC', label: 'Oldest First' } ]; const PAGE_SIZE_OPTIONS = [12, 24, 48, 96]; export default function GalleryPage() { const theme = useTheme(); const isMobile = useMediaQuery(theme.breakpoints.down('md')); const { isAdmin } = useRoles(); const heightOffset = isAdmin ? 192 : 128; const [products, setProducts] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [totalPages, setTotalPages] = useState(0); const [totalCount, setTotalCount] = useState(0); const [categories, setCategories] = useState([]); const [categoriesLoading, setCategoriesLoading] = useState(true); const [expandedCategories, setExpandedCategories] = useState(new Set()); const [filters, setFilters] = useState({ pageNumber: 1, pageSize: 24, searchTerm: '', categoryId: '', minPrice: 0, maxPrice: 1000, isActive: true, isCustomizable: null, sortBy: 'Name', sortDirection: 'ASC' }); const [mobileDrawerOpen, setMobileDrawerOpen] = useState(false); const [priceRange, setPriceRange] = useState([0, 1000]); const [searchInput, setSearchInput] = useState(''); useEffect(() => { const fetchCategories = async () => { try { const response = await clientApi.get('/products/categories'); setCategories(response.data); } catch (err) { console.error('Error fetching categories:', err); } finally { setCategoriesLoading(false); } }; fetchCategories(); }, []); const fetchProducts = useCallback(async () => { setLoading(true); try { const params = { PageNumber: filters.pageNumber, PageSize: filters.pageSize, IsActive: filters.isActive, SortBy: filters.sortBy, SortDirection: filters.sortDirection }; if (filters.searchTerm) params.SearchTerm = filters.searchTerm; if (filters.categoryId) params.CategoryId = filters.categoryId; if (filters.minPrice > 0) params.MinPrice = filters.minPrice; if (filters.maxPrice < 1000) params.MaxPrice = filters.maxPrice; if (filters.isCustomizable !== null) params.IsCustomizable = filters.isCustomizable; const response = await clientApi.get('/products/', { params }); setProducts(response.data.items); setTotalPages(response.data.totalPages); setTotalCount(response.data.totalCount); } catch (err) { setError('Failed to load products'); console.error('Error fetching products:', err); } finally { setLoading(false); } }, [filters]); useEffect(() => { fetchProducts(); }, [fetchProducts]); const handleFilterChange = (key, value) => { setFilters(prev => ({ ...prev, [key]: value, pageNumber: key !== 'pageNumber' ? 1 : value })); }; const handleSearch = () => { handleFilterChange('searchTerm', searchInput); }; const handlePriceRangeChange = (event, newValue) => { setPriceRange(newValue); }; const handlePriceRangeCommitted = (event, newValue) => { handleFilterChange('minPrice', newValue[0]); handleFilterChange('maxPrice', newValue[1]); }; const handleSortChange = (value) => { const [sortBy, sortDirection] = value.split('-'); handleFilterChange('sortBy', sortBy); handleFilterChange('sortDirection', sortDirection); }; const toggleCategoryExpansion = (categoryId) => { const newExpanded = new Set(expandedCategories); if (newExpanded.has(categoryId)) { newExpanded.delete(categoryId); } else { newExpanded.add(categoryId); } setExpandedCategories(newExpanded); }; const getChildCategories = (parentId) => { return categories.filter(cat => cat.parentCategoryId === parentId); }; const getParentCategories = () => { return categories.filter(cat => !cat.parentCategoryId); }; const CategorySidebar = () => ( Categories handleFilterChange('categoryId', '')} sx={{ borderRadius: 1, mb: 0.5 }} > {getParentCategories().map((category) => { const childCategories = getChildCategories(category.id); const hasChildren = childCategories.length > 0; const isExpanded = expandedCategories.has(category.id); return ( handleFilterChange('categoryId', category.id)} sx={{ borderRadius: 1, mb: 0.5 }} > {hasChildren && ( { e.stopPropagation(); toggleCategoryExpansion(category.id); }} > {isExpanded ? : } )} {hasChildren && ( {childCategories.map((childCategory) => ( handleFilterChange('categoryId', childCategory.id)} sx={{ borderRadius: 1, mb: 0.5 }} > ))} )} ); })} Price Range `$${value}`} /> ${priceRange[0]} ${priceRange[1]} handleFilterChange('isCustomizable', e.target.checked ? true : null)} /> } label="Customizable Only" sx={{ mb: 0 }} /> ); return ( {!isMobile && ( {categoriesLoading ? ( ) : ( )} )} Product Gallery Explore our complete collection of customizable products {isMobile && ( setMobileDrawerOpen(true)} sx={{ mr: 1 }} size="small" > )} setSearchInput(e.target.value)} onKeyPress={(e) => e.key === 'Enter' && handleSearch()} size={isMobile ? "small" : "medium"} InputProps={{ startAdornment: ( ), endAdornment: searchInput && ( ) }} /> Sort By Per Page Showing {products.length} of {totalCount} products {loading && ( )} {error && ( {error} )} {!loading && !error && products.length === 0 && ( No products found Try adjusting your search criteria or filters )} {!loading && !error && products.length > 0 && ( {products.map((product) => ( {product.name} {product.category && ( )} {product.description} From ${product.basePrice?.toFixed(2)} {product.isCustomizable && ( )} ))} )} {!loading && !error && totalPages > 1 && ( handleFilterChange('pageNumber', page)} color="primary" size={isMobile ? 'small' : 'large'} showFirstButton={!isMobile} showLastButton={!isMobile} /> )} setMobileDrawerOpen(false)} ModalProps={{ keepMounted: true }} PaperProps={{ sx: { width: 280, display: 'flex', flexDirection: 'column' } }} > Filters setMobileDrawerOpen(false)}> {categoriesLoading ? ( ) : ( )} ); }