ыввы
This commit is contained in:
@@ -18,7 +18,7 @@ import type { Swiper as SwiperType } from 'swiper/types'
|
|||||||
|
|
||||||
type Props = { product: Product; mediaHeight?: number; actions?: ReactNode }
|
type Props = { product: Product; mediaHeight?: number; actions?: ReactNode }
|
||||||
|
|
||||||
const ProductCardInner = ({ product, mediaHeight = 300, actions }: Props) => {
|
const ProductCardInner = ({ product, mediaHeight = 390, actions }: Props) => {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const isMobile = useMediaQuery('(max-width:600px)')
|
const isMobile = useMediaQuery('(max-width:600px)')
|
||||||
const swiperRef = useRef<SwiperType | null>(null)
|
const swiperRef = useRef<SwiperType | null>(null)
|
||||||
@@ -78,7 +78,7 @@ const ProductCardInner = ({ product, mediaHeight = 300, actions }: Props) => {
|
|||||||
>
|
>
|
||||||
<Box sx={{ position: 'relative' }}>
|
<Box sx={{ position: 'relative' }}>
|
||||||
{imageUrls.length ? (
|
{imageUrls.length ? (
|
||||||
<Box onMouseMove={!isMobile ? onMouseMove : undefined} sx={{ height: mediaHeight, overflow: 'hidden' }}>
|
<Box onMouseMove={!isMobile ? onMouseMove : undefined} sx={{ width: '100%', aspectRatio: '3/4', maxHeight: mediaHeight, overflow: 'hidden' }}>
|
||||||
<Swiper
|
<Swiper
|
||||||
slidesPerView={1}
|
slidesPerView={1}
|
||||||
spaceBetween={16}
|
spaceBetween={16}
|
||||||
@@ -86,7 +86,7 @@ const ProductCardInner = ({ product, mediaHeight = 300, actions }: Props) => {
|
|||||||
onSwiper={(s) => {
|
onSwiper={(s) => {
|
||||||
swiperRef.current = s
|
swiperRef.current = s
|
||||||
}}
|
}}
|
||||||
style={{ width: '100%', height: mediaHeight, overflow: 'hidden' }}
|
style={{ width: '100%', height: '100%', overflow: 'hidden' }}
|
||||||
>
|
>
|
||||||
{imageUrls.map((url) => (
|
{imageUrls.map((url) => (
|
||||||
<SwiperSlide key={url}>
|
<SwiperSlide key={url}>
|
||||||
@@ -94,7 +94,7 @@ const ProductCardInner = ({ product, mediaHeight = 300, actions }: Props) => {
|
|||||||
className="product-card__media"
|
className="product-card__media"
|
||||||
sx={{
|
sx={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: mediaHeight,
|
height: '100%',
|
||||||
transition: 'transform 320ms ease',
|
transition: 'transform 320ms ease',
|
||||||
'@media (prefers-reduced-motion: reduce)': { transition: 'none' },
|
'@media (prefers-reduced-motion: reduce)': { transition: 'none' },
|
||||||
userSelect: 'none',
|
userSelect: 'none',
|
||||||
@@ -104,7 +104,7 @@ const ProductCardInner = ({ product, mediaHeight = 300, actions }: Props) => {
|
|||||||
<OptimizedImage
|
<OptimizedImage
|
||||||
src={url}
|
src={url}
|
||||||
alt={product.title}
|
alt={product.title}
|
||||||
sizes={`(max-width: 600px) ${mediaHeight}px, (max-width: 1024px) ${Math.round(mediaHeight * 1.5)}px, ${mediaHeight}px`}
|
sizes="(max-width: 600px) 100vw, (max-width: 1024px) 50vw, 33vw"
|
||||||
sx={{
|
sx={{
|
||||||
width: '101%',
|
width: '101%',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
@@ -120,7 +120,9 @@ const ProductCardInner = ({ product, mediaHeight = 300, actions }: Props) => {
|
|||||||
<CardMedia
|
<CardMedia
|
||||||
component="div"
|
component="div"
|
||||||
sx={{
|
sx={{
|
||||||
height: mediaHeight,
|
width: '100%',
|
||||||
|
aspectRatio: '3/4',
|
||||||
|
maxHeight: mediaHeight,
|
||||||
bgcolor: 'grey.50',
|
bgcolor: 'grey.50',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ export function useProductFilters() {
|
|||||||
const [pageSize, setPageSize] = useState(12)
|
const [pageSize, setPageSize] = useState(12)
|
||||||
const [priceMinRub, setPriceMinRub] = useState('')
|
const [priceMinRub, setPriceMinRub] = useState('')
|
||||||
const [priceMaxRub, setPriceMaxRub] = useState('')
|
const [priceMaxRub, setPriceMaxRub] = useState('')
|
||||||
const [cardScale, setCardScale] = useState<70 | 90 | 110 | 130>(90)
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const t = window.setTimeout(() => {
|
const t = window.setTimeout(() => {
|
||||||
@@ -54,10 +53,6 @@ export function useProductFilters() {
|
|||||||
setPage(1)
|
setPage(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleCardScaleChange = (v: number) => {
|
|
||||||
if (v === 70 || v === 90 || v === 110 || v === 130) setCardScale(v as 70 | 90 | 110 | 130)
|
|
||||||
}
|
|
||||||
|
|
||||||
const resetFilters = () => {
|
const resetFilters = () => {
|
||||||
setCategorySlug('')
|
setCategorySlug('')
|
||||||
setQInput('')
|
setQInput('')
|
||||||
@@ -65,7 +60,6 @@ export function useProductFilters() {
|
|||||||
setPriceMinRub('')
|
setPriceMinRub('')
|
||||||
setPriceMaxRub('')
|
setPriceMaxRub('')
|
||||||
setPageSize(12)
|
setPageSize(12)
|
||||||
setCardScale(90)
|
|
||||||
setMoreOpen(false)
|
setMoreOpen(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +80,6 @@ export function useProductFilters() {
|
|||||||
pageSize,
|
pageSize,
|
||||||
priceMinRub,
|
priceMinRub,
|
||||||
priceMaxRub,
|
priceMaxRub,
|
||||||
cardScale,
|
|
||||||
setPage,
|
setPage,
|
||||||
setQInput,
|
setQInput,
|
||||||
setMoreOpen,
|
setMoreOpen,
|
||||||
@@ -95,7 +88,6 @@ export function useProductFilters() {
|
|||||||
handlePageSizeChange,
|
handlePageSizeChange,
|
||||||
handlePriceMinChange,
|
handlePriceMinChange,
|
||||||
handlePriceMaxChange,
|
handlePriceMaxChange,
|
||||||
handleCardScaleChange,
|
|
||||||
resetFilters,
|
resetFilters,
|
||||||
toCents,
|
toCents,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,7 +64,6 @@ export function HomePage() {
|
|||||||
const products = productsQuery.data?.items ?? []
|
const products = productsQuery.data?.items ?? []
|
||||||
const total = productsQuery.data?.total ?? 0
|
const total = productsQuery.data?.total ?? 0
|
||||||
const totalPages = Math.max(1, Math.ceil(total / filters.pageSize))
|
const totalPages = Math.max(1, Math.ceil(total / filters.pageSize))
|
||||||
const mediaHeight = Math.round(300 * (filters.cardScale / 100))
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
@@ -100,8 +99,8 @@ export function HomePage() {
|
|||||||
{productsQuery.isLoading && (
|
{productsQuery.isLoading && (
|
||||||
<Grid container spacing={2} sx={{ mt: 2 }}>
|
<Grid container spacing={2} sx={{ mt: 2 }}>
|
||||||
{[1, 2, 3].map((i) => (
|
{[1, 2, 3].map((i) => (
|
||||||
<Grid size={{ xs: 12, sm: 6, md: 4 }} key={i}>
|
<Grid size={{ xs: 12, sm: 6, md: 3 }} key={i}>
|
||||||
<Skeleton variant="rectangular" height={360} />
|
<Skeleton variant="rectangular" sx={{ width: '100%', aspectRatio: '3/4' }} />
|
||||||
</Grid>
|
</Grid>
|
||||||
))}
|
))}
|
||||||
</Grid>
|
</Grid>
|
||||||
@@ -128,10 +127,9 @@ export function HomePage() {
|
|||||||
<>
|
<>
|
||||||
<Grid container spacing={2} sx={{ mt: 1 }}>
|
<Grid container spacing={2} sx={{ mt: 1 }}>
|
||||||
{products.map((p) => (
|
{products.map((p) => (
|
||||||
<Grid size={{ xs: 12, sm: 6, md: 4 }} key={p.id}>
|
<Grid size={{ xs: 12, sm: 6, md: 4, lg: 3 }} key={p.id}>
|
||||||
<ProductCard
|
<ProductCard
|
||||||
product={p}
|
product={p}
|
||||||
mediaHeight={mediaHeight}
|
|
||||||
actions={!isAdmin && p.quantity > 0 ? <ToggleCartIcon productId={p.id} /> : undefined}
|
actions={!isAdmin && p.quantity > 0 ? <ToggleCartIcon productId={p.id} /> : undefined}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import Box from '@mui/material/Box'
|
|||||||
import Button from '@mui/material/Button'
|
import Button from '@mui/material/Button'
|
||||||
import Chip from '@mui/material/Chip'
|
import Chip from '@mui/material/Chip'
|
||||||
import Collapse from '@mui/material/Collapse'
|
import Collapse from '@mui/material/Collapse'
|
||||||
import Divider from '@mui/material/Divider'
|
|
||||||
import FormControl from '@mui/material/FormControl'
|
import FormControl from '@mui/material/FormControl'
|
||||||
import InputAdornment from '@mui/material/InputAdornment'
|
import InputAdornment from '@mui/material/InputAdornment'
|
||||||
import InputLabel from '@mui/material/InputLabel'
|
import InputLabel from '@mui/material/InputLabel'
|
||||||
@@ -12,9 +11,6 @@ import Paper from '@mui/material/Paper'
|
|||||||
import Select from '@mui/material/Select'
|
import Select from '@mui/material/Select'
|
||||||
import Stack from '@mui/material/Stack'
|
import Stack from '@mui/material/Stack'
|
||||||
import TextField from '@mui/material/TextField'
|
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 { Search, SlidersHorizontal } from 'lucide-react'
|
import { Search, SlidersHorizontal } from 'lucide-react'
|
||||||
import type { Category } from '@/entities/product/model/types'
|
import type { Category } from '@/entities/product/model/types'
|
||||||
import type { UseProductFiltersResult } from '../lib/use-product-filters'
|
import type { UseProductFiltersResult } from '../lib/use-product-filters'
|
||||||
@@ -32,7 +28,6 @@ export function ProductFilters({
|
|||||||
pageSize,
|
pageSize,
|
||||||
priceMinRub,
|
priceMinRub,
|
||||||
priceMaxRub,
|
priceMaxRub,
|
||||||
cardScale,
|
|
||||||
categories,
|
categories,
|
||||||
categoriesLoading,
|
categoriesLoading,
|
||||||
setQInput,
|
setQInput,
|
||||||
@@ -42,7 +37,6 @@ export function ProductFilters({
|
|||||||
handlePageSizeChange,
|
handlePageSizeChange,
|
||||||
handlePriceMinChange,
|
handlePriceMinChange,
|
||||||
handlePriceMaxChange,
|
handlePriceMaxChange,
|
||||||
handleCardScaleChange,
|
|
||||||
resetFilters,
|
resetFilters,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const categoriesForFilter = useMemo(() => {
|
const categoriesForFilter = useMemo(() => {
|
||||||
@@ -188,39 +182,6 @@ export function ProductFilters({
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
<Divider />
|
|
||||||
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: { xs: 'column', sm: 'row' },
|
|
||||||
gap: 1.5,
|
|
||||||
alignItems: { sm: 'center' },
|
|
||||||
justifyContent: 'space-between',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography variant="subtitle2">Масштаб карточек</Typography>
|
|
||||||
<ToggleButtonGroup
|
|
||||||
exclusive
|
|
||||||
size="small"
|
|
||||||
value={cardScale}
|
|
||||||
onChange={(_, v) => handleCardScaleChange(v)}
|
|
||||||
sx={{
|
|
||||||
alignSelf: { xs: 'flex-start', sm: 'auto' },
|
|
||||||
'& .MuiToggleButton-root': { px: 1.5, fontWeight: 600, textTransform: 'none' },
|
|
||||||
'& .MuiToggleButton-root.Mui-selected': {
|
|
||||||
bgcolor: 'primary.main',
|
|
||||||
color: 'primary.contrastText',
|
|
||||||
'&:hover': { bgcolor: 'primary.dark' },
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ToggleButton value={70}>S</ToggleButton>
|
|
||||||
<ToggleButton value={90}>M</ToggleButton>
|
|
||||||
<ToggleButton value={110}>L</ToggleButton>
|
|
||||||
<ToggleButton value={130}>XL</ToggleButton>
|
|
||||||
</ToggleButtonGroup>
|
|
||||||
</Box>
|
|
||||||
</Paper>
|
</Paper>
|
||||||
</Collapse>
|
</Collapse>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export function ProductPage() {
|
|||||||
if (productQuery.isLoading) {
|
if (productQuery.isLoading) {
|
||||||
return (
|
return (
|
||||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
||||||
<Skeleton variant="rectangular" height={525} />
|
<Skeleton variant="rectangular" sx={{ width: '100%', aspectRatio: '3/4' }} />
|
||||||
<Skeleton variant="text" width="60%" />
|
<Skeleton variant="text" width="60%" />
|
||||||
<Skeleton variant="text" width="40%" />
|
<Skeleton variant="text" width="40%" />
|
||||||
<Skeleton variant="text" />
|
<Skeleton variant="text" />
|
||||||
@@ -72,121 +72,128 @@ export function ProductPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
|
<Stack direction={{ xs: 'column', md: 'row' }} spacing={{ xs: 3, md: 4 }}>
|
||||||
{imageUrls.length > 0 ? (
|
<Box sx={{ flex: { md: '1 1 50%' }, minWidth: 0 }}>
|
||||||
<Box
|
{imageUrls.length > 0 ? (
|
||||||
sx={{
|
<Box
|
||||||
borderRadius: '20px 20px 12px 12px',
|
sx={{
|
||||||
overflow: 'hidden',
|
borderRadius: '20px 20px 12px 12px',
|
||||||
border: 'none',
|
overflow: 'hidden',
|
||||||
boxShadow: '0 4px 20px rgba(0,0,0,0.08)',
|
border: 'none',
|
||||||
bgcolor: 'background.paper',
|
boxShadow: '0 4px 20px rgba(0,0,0,0.08)',
|
||||||
}}
|
bgcolor: 'background.paper',
|
||||||
>
|
width: '100%',
|
||||||
<Swiper modules={[Navigation]} navigation style={{ width: '100%', height: 525 }}>
|
aspectRatio: '3/4',
|
||||||
{imageUrls.map((url, idx) => (
|
}}
|
||||||
<SwiperSlide key={url}>
|
>
|
||||||
<Box
|
<Swiper modules={[Navigation]} navigation style={{ width: '100%', height: '100%' }}>
|
||||||
onClick={() => {
|
{imageUrls.map((url, idx) => (
|
||||||
setViewerIndex(idx)
|
<SwiperSlide key={url}>
|
||||||
setViewerOpen(true)
|
<Box
|
||||||
}}
|
onClick={() => {
|
||||||
sx={{
|
setViewerIndex(idx)
|
||||||
width: '100%',
|
setViewerOpen(true)
|
||||||
height: 525,
|
}}
|
||||||
cursor: 'zoom-in',
|
|
||||||
userSelect: 'none',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<OptimizedImage
|
|
||||||
src={url}
|
|
||||||
alt={p.title}
|
|
||||||
sizes="(max-width: 600px) 320px, (max-width: 1024px) 640px, 1024px"
|
|
||||||
sx={{
|
sx={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
objectFit: 'cover',
|
cursor: 'zoom-in',
|
||||||
|
userSelect: 'none',
|
||||||
}}
|
}}
|
||||||
/>
|
>
|
||||||
</Box>
|
<OptimizedImage
|
||||||
</SwiperSlide>
|
src={url}
|
||||||
))}
|
alt={p.title}
|
||||||
</Swiper>
|
sizes="(max-width: 900px) 100vw, 50vw"
|
||||||
</Box>
|
sx={{
|
||||||
) : (
|
width: '100%',
|
||||||
<Box
|
height: '100%',
|
||||||
sx={{
|
objectFit: 'cover',
|
||||||
height: 525,
|
}}
|
||||||
borderRadius: 2,
|
/>
|
||||||
overflow: 'hidden',
|
</Box>
|
||||||
border: 1,
|
</SwiperSlide>
|
||||||
borderColor: 'divider',
|
))}
|
||||||
bgcolor: 'grey.100',
|
</Swiper>
|
||||||
display: 'flex',
|
</Box>
|
||||||
alignItems: 'center',
|
) : (
|
||||||
justifyContent: 'center',
|
<Box
|
||||||
}}
|
sx={{
|
||||||
>
|
width: '100%',
|
||||||
<Typography color="text.secondary">Нет фото</Typography>
|
aspectRatio: '3/4',
|
||||||
</Box>
|
borderRadius: 2,
|
||||||
)}
|
overflow: 'hidden',
|
||||||
|
border: 1,
|
||||||
<Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap' }}>
|
borderColor: 'divider',
|
||||||
{p.category?.name && <Chip label={p.category.name} />}
|
bgcolor: 'grey.100',
|
||||||
{p.quantity > 0 && <Chip label="В наличии" color="success" />}
|
display: 'flex',
|
||||||
{p.quantity === 0 && <Chip label="Нет в наличии" color="default" />}
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography color="text.secondary">Нет фото</Typography>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{(p.materials?.length ?? 0) > 0 && (
|
<Box sx={{ flex: { md: '1 1 50%' }, minWidth: 0, display: 'flex', flexDirection: 'column', gap: 3 }}>
|
||||||
<Box>
|
<Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap' }}>
|
||||||
<Typography variant="subtitle2" color="text.secondary" sx={{ mb: 1 }}>
|
{p.category?.name && <Chip label={p.category.name} />}
|
||||||
Материалы
|
{p.quantity > 0 && <Chip label="В наличии" color="success" />}
|
||||||
</Typography>
|
{p.quantity === 0 && <Chip label="Нет в наличии" color="default" />}
|
||||||
<Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap' }}>
|
|
||||||
{(p.materials ?? []).map((m) => (
|
|
||||||
<Chip key={m} label={m} variant="outlined" />
|
|
||||||
))}
|
|
||||||
</Box>
|
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
|
||||||
|
|
||||||
<Typography variant="h3" component="h1" sx={{ fontWeight: 700, letterSpacing: '-0.75px' }}>
|
{(p.materials?.length ?? 0) > 0 && (
|
||||||
{p.title}
|
<Box>
|
||||||
</Typography>
|
<Typography variant="subtitle2" color="text.secondary" sx={{ mb: 1 }}>
|
||||||
<Typography variant="h4" color="primary" sx={{ fontWeight: 700, fontVariantNumeric: 'tabular-nums' }}>
|
Материалы
|
||||||
{formatPriceRub(p.priceCents)}
|
</Typography>
|
||||||
</Typography>
|
<Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap' }}>
|
||||||
|
{(p.materials ?? []).map((m) => (
|
||||||
|
<Chip key={m} label={m} variant="outlined" />
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
|
||||||
{!isAdmin && p.quantity > 0 ? <ToggleCartIcon productId={p.id} size="medium" /> : null}
|
<Typography variant="h3" component="h1" sx={{ fontWeight: 700, letterSpacing: '-0.75px' }}>
|
||||||
|
{p.title}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="h4" color="primary" sx={{ fontWeight: 700, fontVariantNumeric: 'tabular-nums' }}>
|
||||||
|
{formatPriceRub(p.priceCents)}
|
||||||
|
</Typography>
|
||||||
|
|
||||||
{p.description || p.shortDescription ? (
|
{!isAdmin && p.quantity > 0 ? <ToggleCartIcon productId={p.id} size="medium" /> : null}
|
||||||
<Typography sx={{ whiteSpace: 'pre-wrap' }}>{p.description}</Typography>
|
|
||||||
) : (
|
|
||||||
<Typography color="text.secondary">Описание появится позже.</Typography>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Divider sx={{ my: 2 }} />
|
{p.description || p.shortDescription ? (
|
||||||
|
<Typography sx={{ whiteSpace: 'pre-wrap' }}>{p.description}</Typography>
|
||||||
|
) : (
|
||||||
|
<Typography color="text.secondary">Описание появится позже.</Typography>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
<Typography variant="h6" sx={{ mb: 1 }}>
|
<Divider sx={{ my: { xs: 3, md: 4 } }} />
|
||||||
Отзывы
|
|
||||||
</Typography>
|
|
||||||
{p.reviewsSummary && p.reviewsSummary.approvedReviewCount > 0 && (
|
|
||||||
<Stack direction="row" spacing={1} sx={{ alignItems: 'center', mb: 2 }}>
|
|
||||||
<Rating
|
|
||||||
value={p.reviewsSummary.avgRating ?? 0}
|
|
||||||
readOnly
|
|
||||||
precision={0.25}
|
|
||||||
icon={<Star fontSize="inherit" />}
|
|
||||||
emptyIcon={<Star fontSize="inherit" />}
|
|
||||||
/>
|
|
||||||
<Typography variant="body2" color="text.secondary">
|
|
||||||
{reviewsCountRu(p.reviewsSummary.approvedReviewCount)}
|
|
||||||
</Typography>
|
|
||||||
</Stack>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<ProductReviewsList productId={id} />
|
<Typography variant="h6" sx={{ mb: 1 }}>
|
||||||
</Box>
|
Отзывы
|
||||||
|
</Typography>
|
||||||
|
{p.reviewsSummary && p.reviewsSummary.approvedReviewCount > 0 && (
|
||||||
|
<Stack direction="row" spacing={1} sx={{ alignItems: 'center', mb: 2 }}>
|
||||||
|
<Rating
|
||||||
|
value={p.reviewsSummary.avgRating ?? 0}
|
||||||
|
readOnly
|
||||||
|
precision={0.25}
|
||||||
|
icon={<Star fontSize="inherit" />}
|
||||||
|
emptyIcon={<Star fontSize="inherit" />}
|
||||||
|
/>
|
||||||
|
<Typography variant="body2" color="text.secondary">
|
||||||
|
{reviewsCountRu(p.reviewsSummary.approvedReviewCount)}
|
||||||
|
</Typography>
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<ProductReviewsList productId={id} />
|
||||||
|
|
||||||
<Dialog fullScreen open={viewerOpen} onClose={() => setViewerOpen(false)}>
|
<Dialog fullScreen open={viewerOpen} onClose={() => setViewerOpen(false)}>
|
||||||
<Box sx={{ position: 'relative', height: '100%', bgcolor: 'black' }}>
|
<Box sx={{ position: 'relative', height: '100%', bgcolor: 'black' }}>
|
||||||
|
|||||||
Reference in New Issue
Block a user