
- Обновлена схема Prisma с новыми полями и связями - Актуализированы правила системы в rules-complete.md - Оптимизированы GraphQL типы, запросы и мутации - Улучшены компоненты интерфейса и валидация данных - Исправлены критические ESLint ошибки: удалены неиспользуемые импорты и переменные - Добавлены тестовые файлы для проверки функционала 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
164 lines
6.5 KiB
TypeScript
164 lines
6.5 KiB
TypeScript
'use client'
|
||
|
||
import { Plus, Minus, Eye, Heart, ShoppingCart } from 'lucide-react'
|
||
import React from 'react'
|
||
|
||
import { Badge } from '@/components/ui/badge'
|
||
import { Button } from '@/components/ui/button'
|
||
import { Card } from '@/components/ui/card'
|
||
|
||
import { WholesalerProduct } from './types'
|
||
|
||
interface ProductCardProps {
|
||
product: WholesalerProduct
|
||
selectedQuantity: number
|
||
onQuantityChange: (quantity: number) => void
|
||
formatCurrency: (amount: number) => string
|
||
}
|
||
|
||
export function ProductCard({ product, selectedQuantity, onQuantityChange, formatCurrency }: ProductCardProps) {
|
||
const discountedPrice = product.discount ? product.price * (1 - product.discount / 100) : product.price
|
||
|
||
const handleQuantityChange = (newQuantity: number) => {
|
||
const clampedQuantity = Math.max(0, Math.min(product.quantity, newQuantity))
|
||
onQuantityChange(clampedQuantity)
|
||
}
|
||
|
||
return (
|
||
<Card className="bg-white/10 backdrop-blur border-white/20 overflow-hidden group hover:bg-white/15 hover:border-white/30 transition-all duration-300">
|
||
<div className="aspect-square relative bg-white/5 overflow-hidden">
|
||
<img
|
||
src={product.mainImage || '/api/placeholder/400/400'}
|
||
alt={product.name}
|
||
className="w-full h-full object-cover"
|
||
/>
|
||
|
||
{/* Количество в наличии */}
|
||
<div className="absolute top-2 right-2">
|
||
<Badge
|
||
className={`${
|
||
product.quantity > 50 ? 'bg-green-500/80' : product.quantity > 10 ? 'bg-yellow-500/80' : 'bg-red-500/80'
|
||
} text-white border-0 backdrop-blur text-xs`}
|
||
>
|
||
{product.quantity}
|
||
</Badge>
|
||
</div>
|
||
|
||
{/* Скидка */}
|
||
{product.discount && (
|
||
<div className="absolute top-2 left-2">
|
||
<Badge className="bg-red-500/80 text-white border-0 backdrop-blur text-xs">-{product.discount}%</Badge>
|
||
</div>
|
||
)}
|
||
|
||
{/* Overlay с кнопками */}
|
||
<div className="absolute inset-0 bg-black/40 opacity-0 group-hover:opacity-100 transition-opacity duration-300 flex items-center justify-center">
|
||
<div className="flex space-x-2">
|
||
<Button
|
||
size="sm"
|
||
variant="secondary"
|
||
className="bg-white/20 backdrop-blur text-white border-white/30 hover:bg-white/30"
|
||
>
|
||
<Eye className="h-4 w-4" />
|
||
</Button>
|
||
<Button
|
||
size="sm"
|
||
variant="secondary"
|
||
className="bg-white/20 backdrop-blur text-white border-white/30 hover:bg-white/30"
|
||
>
|
||
<Heart className="h-4 w-4" />
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="p-3 space-y-3">
|
||
{/* Заголовок и бренд */}
|
||
<div>
|
||
<div className="flex items-center justify-between mb-1">
|
||
{product.brand && (
|
||
<Badge className="bg-gray-500/20 text-gray-300 border-gray-500/30 text-xs">{product.brand}</Badge>
|
||
)}
|
||
<div className="flex items-center space-x-1">
|
||
{product.isNew && (
|
||
<Badge className="bg-green-500/20 text-green-300 border-green-500/30 text-xs">NEW</Badge>
|
||
)}
|
||
{product.isBestseller && (
|
||
<Badge className="bg-orange-500/20 text-orange-300 border-orange-500/30 text-xs">HIT</Badge>
|
||
)}
|
||
</div>
|
||
</div>
|
||
<h3 className="text-white font-semibold text-sm mb-1 line-clamp-2 leading-tight">{product.name}</h3>
|
||
</div>
|
||
|
||
{/* Основная характеристика */}
|
||
<div className="text-white/60 text-xs">
|
||
{product.color && <span className="text-white">{product.color}</span>}
|
||
{product.size && <span className="text-white ml-2">{product.size}</span>}
|
||
</div>
|
||
|
||
{/* Цена */}
|
||
<div className="pt-2 border-t border-white/10">
|
||
<div className="flex items-center space-x-2">
|
||
<div className="text-white font-bold text-lg">{formatCurrency(discountedPrice)}</div>
|
||
{product.discount && (
|
||
<div className="text-white/40 text-sm line-through">{formatCurrency(product.price)}</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Управление количеством */}
|
||
<div className="flex items-center space-x-2">
|
||
<Button
|
||
variant="ghost"
|
||
size="sm"
|
||
onClick={() => handleQuantityChange(selectedQuantity - 1)}
|
||
disabled={selectedQuantity === 0}
|
||
className="h-8 w-8 p-0 text-white/60 hover:text-white hover:bg-white/10 border border-white/20"
|
||
>
|
||
<Minus className="h-3 w-3" />
|
||
</Button>
|
||
<input
|
||
type="text"
|
||
inputMode="numeric"
|
||
pattern="[0-9]*"
|
||
value={selectedQuantity}
|
||
onChange={(e) => {
|
||
const value = e.target.value.replace(/[^0-9]/g, '')
|
||
const numValue = parseInt(value) || 0
|
||
handleQuantityChange(numValue)
|
||
}}
|
||
onFocus={(e) => e.target.select()}
|
||
className="h-8 w-12 text-center bg-white/10 border border-white/20 text-white text-sm rounded focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
|
||
/>
|
||
<Button
|
||
variant="ghost"
|
||
size="sm"
|
||
onClick={() => handleQuantityChange(selectedQuantity + 1)}
|
||
disabled={selectedQuantity >= product.quantity}
|
||
className="h-8 w-8 p-0 text-white/60 hover:text-white hover:bg-white/10 border border-white/20"
|
||
>
|
||
<Plus className="h-3 w-3" />
|
||
</Button>
|
||
|
||
{selectedQuantity > 0 && (
|
||
<Badge className="bg-gradient-to-r from-purple-500 to-pink-500 text-white border-0 text-xs ml-auto">
|
||
<ShoppingCart className="h-3 w-3 mr-1" />
|
||
{selectedQuantity}
|
||
</Badge>
|
||
)}
|
||
</div>
|
||
|
||
{/* Сумма для выбранного товара */}
|
||
{selectedQuantity > 0 && (
|
||
<div className="bg-gradient-to-r from-green-500/20 to-emerald-500/20 border border-green-500/30 rounded p-2">
|
||
<div className="text-green-300 text-xs font-medium text-center">
|
||
{formatCurrency(discountedPrice * selectedQuantity)}
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</Card>
|
||
)
|
||
}
|