'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 ? (
) : (
)}
);
}