Files
imprink/ui/src/app/components/gallery/ProductCard.tsx
2025-06-29 23:54:15 +03:00

191 lines
6.9 KiB
TypeScript

import {
Card,
CardContent,
CardMedia,
Typography,
Button,
Chip,
Box,
useMediaQuery,
useTheme
} from '@mui/material';
import { useUser } from '@auth0/nextjs-auth0';
import { useRouter } from 'next/navigation';
import { GalleryProduct } from '@/types';
interface ProductCardProps {
product: GalleryProduct;
}
export default function ProductCard({ product }: ProductCardProps) {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
const { user, isLoading } = useUser();
const router = useRouter();
const handleViewProduct = () => {
router.push(`/products/${product.id}`);
};
const handleBuild = () => {
router.push(`/builder/${product.id}`);
};
return (
<Card
sx={{
minWidth: {
xs: 'calc(50% - 6px)',
sm: 'calc(50% - 8px)',
md: '280px',
lg: '320px'
},
flex: {
xs: '0 0 calc(50% - 6px)',
sm: '0 0 calc(50% - 8px)',
md: '1 1 280px',
lg: '1 1 320px'
},
maxWidth: {
xs: 'calc(50% - 6px)',
sm: 'calc(50% - 8px)',
md: 'none',
lg: 'none'
},
height: { xs: 240, sm: 300, lg: 340 },
display: 'flex',
flexDirection: 'column',
transition: 'transform 0.3s ease-in-out, box-shadow 0.3s ease-in-out',
'&:hover': {
transform: { xs: 'none', sm: 'translateY(-2px)', md: 'translateY(-4px)' },
boxShadow: { xs: 1, sm: 3, md: 4 }
}
}}
>
<CardMedia
component="img"
image={product.imageUrl || '/placeholder-product.jpg'}
alt={product.name}
sx={{
objectFit: 'cover',
height: { xs: 80, sm: 120, lg: 140 }
}}
/>
<CardContent sx={{
flexGrow: 1,
display: 'flex',
flexDirection: 'column',
p: { xs: 0.75, sm: 1, lg: 1.25 },
'&:last-child': { pb: { xs: 0.75, sm: 1, lg: 1.25 } }
}}>
<Typography variant="h6" gutterBottom sx={{
fontWeight: 600,
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
fontSize: { xs: '0.75rem', sm: '0.85rem', md: '0.9rem' },
mb: { xs: 0.25, sm: 0.5 },
lineHeight: 1.2
}}>
{product.name}
</Typography>
{product.category && (
<Chip
label={product.category.name}
size="small"
color="secondary"
sx={{
alignSelf: 'flex-start',
mb: { xs: 0.25, sm: 0.5 },
fontSize: { xs: '0.55rem', sm: '0.65rem' },
height: { xs: 16, sm: 20 },
'& .MuiChip-label': {
px: { xs: 0.5, sm: 0.75 }
}
}}
/>
)}
<Typography variant="body2" color="text.secondary" sx={{
flexGrow: 1,
mb: { xs: 0.5, sm: 0.75 },
overflow: 'hidden',
display: '-webkit-box',
WebkitLineClamp: { xs: 1, sm: 2, md: 2 },
WebkitBoxOrient: 'vertical',
minHeight: { xs: 16, sm: 24, md: 32 },
fontSize: { xs: '0.65rem', sm: '0.7rem', md: '0.75rem' },
lineHeight: 1.3
}}>
{product.description}
</Typography>
<Box sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
mb: { xs: 0.5, sm: 0.75 }
}}>
<Typography variant="subtitle1" color="primary" sx={{
fontWeight: 600,
fontSize: { xs: '0.7rem', sm: '0.8rem', md: '0.85rem' }
}}>
From ${product.basePrice?.toFixed(2)}
</Typography>
{product.isCustomizable && (
<Chip
label="Custom"
color="primary"
size="small"
sx={{
fontSize: { xs: '0.55rem', sm: '0.65rem' },
height: { xs: 16, sm: 20 },
'& .MuiChip-label': {
px: { xs: 0.5, sm: 0.75 }
}
}}
/>
)}
</Box>
{!isLoading && (
<Box sx={{
display: 'flex',
gap: { xs: 0.5, sm: 0.75 },
flexDirection: { xs: 'column', sm: user ? 'row' : 'column' }
}}>
<Button
variant={user ? "outlined" : "contained"}
size="small"
onClick={handleViewProduct}
sx={{
fontSize: { xs: '0.65rem', sm: '0.7rem' },
py: { xs: 0.25, sm: 0.5 },
flex: 1,
minHeight: { xs: 24, sm: 32 }
}}
>
{user ? "View" : "View Product"}
</Button>
{user && (
<Button
variant="contained"
size="small"
onClick={handleBuild}
sx={{
fontSize: { xs: '0.65rem', sm: '0.7rem' },
py: { xs: 0.25, sm: 0.5 },
flex: 1,
minHeight: { xs: 24, sm: 32 }
}}
>
Build
</Button>
)}
</Box>
)}
</CardContent>
</Card>
);
}