import { useEffect, useMemo, useState } from 'react' import Alert from '@mui/material/Alert' import Box from '@mui/material/Box' import Button from '@mui/material/Button' import Collapse from '@mui/material/Collapse' import Divider from '@mui/material/Divider' import FormControl from '@mui/material/FormControl' import Grid from '@mui/material/Grid' import InputLabel from '@mui/material/InputLabel' import MenuItem from '@mui/material/MenuItem' import Pagination from '@mui/material/Pagination' import Paper from '@mui/material/Paper' import Select from '@mui/material/Select' import type { SelectChangeEvent } from '@mui/material/Select' import Skeleton from '@mui/material/Skeleton' import Stack from '@mui/material/Stack' import TextField from '@mui/material/TextField' import ToggleButton from '@mui/material/ToggleButton' import ToggleButtonGroup from '@mui/material/ToggleButtonGroup' import Typography from '@mui/material/Typography' import { useQuery } from '@tanstack/react-query' import { useUnit } from 'effector-react' import { fetchCategories, fetchPublicProducts } from '@/entities/product/api/product-api' import { ProductCard } from '@/entities/product/ui/ProductCard' import { ToggleCartIcon } from '@/features/cart/toggle-cart-icon' import { $user } from '@/shared/model/auth' import { ReviewsBlock } from '@/widgets/reviews-block' export function HomePage() { const user = useUnit($user) const isAdmin = Boolean(user?.isAdmin) const [categorySlug, setCategorySlug] = useState('') const [availability, setAvailability] = useState<'all' | 'in_stock' | 'made_to_order'>('all') const [qInput, setQInput] = useState('') const [q, setQ] = useState('') const [moreOpen, setMoreOpen] = useState(false) const [sort, setSort] = useState<'price_asc' | 'price_desc' | ''>('') const [page, setPage] = useState(1) const [pageSize, setPageSize] = useState(12) const [priceMinRub, setPriceMinRub] = useState('') const [priceMaxRub, setPriceMaxRub] = useState('') const [cardScale, setCardScale] = useState<70 | 90 | 110 | 130>(90) const categoriesQuery = useQuery({ queryKey: ['categories'], queryFn: () => fetchCategories(), }) useEffect(() => { const t = window.setTimeout(() => { setQ(qInput.trim()) setPage(1) }, 250) return () => window.clearTimeout(t) }, [qInput]) const productsQuery = useQuery({ queryKey: [ 'products', 'public', { categorySlug: categorySlug || 'all', availability, q, sort, page, pageSize, priceMinRub, priceMaxRub, }, ], queryFn: () => { const toCents = (v: string) => { const n = Number(String(v).trim().replace(',', '.')) return Number.isFinite(n) && n >= 0 ? Math.round(n * 100) : undefined } return fetchPublicProducts({ categorySlug: categorySlug || undefined, availability: availability === 'all' ? undefined : availability, q: q || undefined, sort: sort || '', page, pageSize, priceMinCents: toCents(priceMinRub), priceMaxCents: toCents(priceMaxRub), }) }, }) const handleCategoryChange = (e: SelectChangeEvent) => { setCategorySlug(e.target.value) setPage(1) } const handleSortChange = (e: SelectChangeEvent) => { const v = e.target.value if (v === '' || v === 'price_asc' || v === 'price_desc') { setSort(v) setPage(1) } } const handlePageSizeChange = (e: SelectChangeEvent) => { const n = Number(e.target.value) if (Number.isFinite(n) && n > 0) { setPageSize(n) setPage(1) } } const title = useMemo( () => categorySlug ? `Категория: ${categoriesQuery.data?.find((c) => c.slug === categorySlug)?.name ?? ''}` : 'Каталог', [categorySlug, categoriesQuery.data], ) const products = productsQuery.data?.items ?? [] const total = productsQuery.data?.total ?? 0 const totalPages = Math.max(1, Math.ceil(total / pageSize)) const mediaHeight = Math.round(200 * (cardScale / 100)) return ( {title} Игрушки, сувениры и другие изделия ручной работы. Категория labelId="category-filter-label" label="Категория" value={categorySlug} onChange={handleCategoryChange} disabled={categoriesQuery.isLoading} > Все {(categoriesQuery.data ?? []).map((c) => ( {c.name} ))} setQInput(e.target.value)} sx={{ flexGrow: 1, minWidth: { xs: '100%', md: 360 } }} /> Наличие Быстрый фильтр по наличию { if (v === 'all' || v === 'in_stock' || v === 'made_to_order') { setAvailability(v) setPage(1) } }} sx={{ alignSelf: { xs: 'flex-start', sm: 'auto' }, '& .MuiToggleButton-root': { px: 2, fontWeight: 700, letterSpacing: 0.2, textTransform: 'none' }, '& .MuiToggleButton-root.Mui-selected': { bgcolor: 'primary.main', color: 'primary.contrastText', '&:hover': { bgcolor: 'primary.dark' }, }, }} > Все В наличии Под заказ Сортировка labelId="sort-label" label="Сортировка" value={sort} onChange={handleSortChange}> Сначала новые Цена: по возрастанию Цена: по убыванию { setPriceMinRub(e.target.value) setPage(1) }} sx={{ width: { xs: '100%', md: 180 } }} /> { setPriceMaxRub(e.target.value) setPage(1) }} sx={{ width: { xs: '100%', md: 180 } }} /> На странице labelId="page-size-label" label="На странице" value={String(pageSize)} onChange={handlePageSizeChange} > {[6, 12, 18, 24].map((n) => ( {n} ))} Масштаб карточек Выберите размер карточек в каталоге { if (v === 70 || v === 90 || v === 110 || v === 130) setCardScale(v) }} sx={{ alignSelf: { xs: 'flex-start', sm: 'auto' }, '& .MuiToggleButton-root': { px: 2, fontWeight: 700, letterSpacing: 0.2, textTransform: 'none', }, '& .MuiToggleButton-root.Mui-selected': { bgcolor: 'primary.main', color: 'primary.contrastText', '&:hover': { bgcolor: 'primary.dark' }, }, }} > S M L XL {productsQuery.isLoading && ( {[1, 2, 3].map((i) => ( ))} )} {productsQuery.isError && ( Не удалось загрузить товары. Проверьте, что API запущен. )} {productsQuery.isSuccess && products.length === 0 && ( Пока нет опубликованных товаров. )} {productsQuery.isSuccess && products.length > 0 && ( <> {products.map((p) => ( ) : undefined } /> ))} {totalPages > 1 && ( setPage(v)} color="primary" shape="rounded" showFirstButton showLastButton /> )} )} ) }