This commit is contained in:
@kirill.komarov
2026-05-13 12:17:08 +05:00
parent 83bbf9d263
commit c6228dfaab
+81 -29
View File
@@ -40,21 +40,30 @@ export function ProductCard({ product, mediaHeight = 200, actions }: Props) {
swiperRef.current.slideTo(idx, 0) swiperRef.current.slideTo(idx, 0)
} }
const stockLabel =
product.inStock && product.quantity === 0
? { label: 'Нет в наличии', color: 'default' as const }
: !product.inStock
? { label: `Под заказ · ${product.leadTimeDays ?? '—'} дн.`, color: 'warning' as const }
: null
return ( return (
<Card <Card
variant="outlined"
sx={{ sx={{
height: '100%', height: '100%',
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
overflow: 'hidden', overflow: 'hidden',
transition: 'transform 160ms ease, box-shadow 160ms ease, border-color 160ms ease', borderRadius: 3,
border: '1px solid',
borderColor: 'divider',
bgcolor: 'background.paper',
transition: 'transform 200ms ease, box-shadow 250ms ease',
'&:hover': { '&:hover': {
transform: 'translateY(-3px)', transform: 'translateY(-4px)',
boxShadow: 6, boxShadow: '0 8px 30px rgba(0,0,0,0.10)',
borderColor: 'divider',
}, },
'&:hover .product-card__media': { transform: 'scale(1.03)' }, '&:hover .product-card__media': { transform: 'scale(1.06)' },
'@media (prefers-reduced-motion: reduce)': { '@media (prefers-reduced-motion: reduce)': {
transition: 'none', transition: 'none',
'&:hover': { transform: 'none' }, '&:hover': { transform: 'none' },
@@ -67,10 +76,10 @@ export function ProductCard({ product, mediaHeight = 200, actions }: Props) {
to={`/products/${product.id}`} to={`/products/${product.id}`}
underline="none" underline="none"
color="inherit" color="inherit"
sx={{ display: 'block' }} sx={{ display: 'block', position: 'relative' }}
> >
{imageUrls.length ? ( {imageUrls.length ? (
<Box onMouseMove={onMouseMove} sx={{ height: mediaHeight }}> <Box onMouseMove={onMouseMove} sx={{ height: mediaHeight, overflow: 'hidden' }}>
<Swiper <Swiper
onSwiper={(s) => { onSwiper={(s) => {
swiperRef.current = s swiperRef.current = s
@@ -90,9 +99,10 @@ export function ProductCard({ product, mediaHeight = 200, actions }: Props) {
height: mediaHeight, height: mediaHeight,
objectFit: 'cover', objectFit: 'cover',
display: 'block', display: 'block',
transition: 'transform 240ms ease', transition: 'transform 320ms ease',
'@media (prefers-reduced-motion: reduce)': { transition: 'none' }, '@media (prefers-reduced-motion: reduce)': { transition: 'none' },
userSelect: 'none', userSelect: 'none',
bgcolor: 'grey.50',
}} }}
/> />
</SwiperSlide> </SwiperSlide>
@@ -103,62 +113,104 @@ export function ProductCard({ product, mediaHeight = 200, actions }: Props) {
<CardMedia <CardMedia
component="div" component="div"
sx={{ sx={{
height: 200, height: mediaHeight,
bgcolor: 'grey.100', bgcolor: 'grey.50',
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
}} }}
> >
<Typography color="text.secondary">Нет фото</Typography> <Typography color="text.disabled" variant="body2">
Нет фото
</Typography>
</CardMedia> </CardMedia>
)} )}
{stockLabel && (
<Chip
label={stockLabel.label}
size="small"
color={stockLabel.color}
variant={stockLabel.color === 'warning' ? 'outlined' : 'filled'}
sx={{
position: 'absolute',
top: 8,
left: 8,
fontWeight: 600,
fontSize: '0.7rem',
backdropFilter: 'blur(4px)',
bgcolor: stockLabel.color === 'default' ? 'rgba(0,0,0,0.55)' : undefined,
color: stockLabel.color === 'default' ? 'common.white' : undefined,
}}
/>
)}
</Link> </Link>
<CardContent sx={{ flexGrow: 1 }}>
<Stack spacing={1}> <CardContent sx={{ flexGrow: 1, p: 2, '&:last-child': { pb: 2 } }}>
{product.category && <Chip label={product.category.name} size="small" />} <Stack spacing={1.25}>
{product.inStock && product.quantity === 0 && <Chip label="Нет в наличии" size="small" color="default" />} {product.category && (
{!product.inStock && (
<Chip <Chip
label={`Под заказ · ${product.leadTimeDays ?? '—'} дн.`} label={product.category.name}
size="small" size="small"
variant="outlined" variant="outlined"
color="warning" sx={{ alignSelf: 'flex-start', fontWeight: 500, fontSize: '0.7rem' }}
/> />
)} )}
<Typography <Typography
variant="h6" variant="subtitle1"
component={RouterLink} component={RouterLink}
to={`/products/${product.id}`} to={`/products/${product.id}`}
style={{ textDecoration: 'none', color: 'inherit' }} sx={{
textDecoration: 'none',
color: 'text.primary',
fontWeight: 600,
lineHeight: 1.3,
transition: 'color 150ms ease',
'&:hover': { color: 'primary.main' },
}}
> >
{product.title} {product.title}
</Typography> </Typography>
{(product.materials?.length ?? 0) > 0 && ( {(product.materials?.length ?? 0) > 0 && (
<Stack direction="row" spacing={1} useFlexGap sx={{ flexWrap: 'wrap' }}> <Stack direction="row" spacing={0.5} useFlexGap sx={{ flexWrap: 'wrap' }}>
{materials.map((m) => ( {materials.map((m) => (
<Chip key={m} label={m} size="small" variant="outlined" /> <Chip key={m} label={m} size="small" variant="outlined" sx={{ fontSize: '0.7rem', height: 22 }} />
))} ))}
{moreMaterials > 0 && <Chip label={`+${moreMaterials}`} size="small" variant="outlined" />} {moreMaterials > 0 && (
<Chip
label={`+${moreMaterials}`}
size="small"
variant="outlined"
sx={{ fontSize: '0.7rem', height: 22 }}
/>
)}
</Stack> </Stack>
)} )}
<Typography <Typography
variant="body2" variant="body2"
color="text.secondary" color="text.secondary"
sx={{ sx={{
overflow: 'hidden', overflow: 'hidden',
textOverflow: 'ellipsis', textOverflow: 'ellipsis',
WebkitLineClamp: 3, WebkitLineClamp: 2,
display: '-webkit-box', display: '-webkit-box',
WebkitBoxOrient: 'vertical', WebkitBoxOrient: 'vertical',
fontSize: '0.8125rem',
lineHeight: 1.45,
}} }}
> >
{product.shortDescription ?? 'Описание появится позже.'} {product.shortDescription ?? 'Описание появится позже.'}
</Typography> </Typography>
<Typography variant="h6" color="primary">
{formatPriceRub(product.priceCents)} <Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mt: 'auto', pt: 0.5 }}>
</Typography> <Typography variant="h6" color="primary" sx={{ fontWeight: 700, fontSize: '1.1rem' }}>
{actions} {formatPriceRub(product.priceCents)}
</Typography>
{actions}
</Box>
</Stack> </Stack>
</CardContent> </CardContent>
</Card> </Card>