Files
sfera/src/components/warehouse/product-card.tsx

236 lines
8.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client"
import Image from 'next/image'
import { useMutation } from '@apollo/client'
import { Card } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from '@/components/ui/alert-dialog'
import { DELETE_PRODUCT } from '@/graphql/mutations'
import { Edit3, Trash2, Package, Eye, EyeOff } from 'lucide-react'
import { toast } from 'sonner'
interface Product {
id: string
name: string
article: string
description: string
price: number
quantity: number
type: 'PRODUCT' | 'CONSUMABLE'
category: { id: string; name: string } | null
brand: string
color: string
size: string
weight: number
dimensions: string
material: string
images: string[]
mainImage: string
isActive: boolean
createdAt: string
updatedAt: string
}
interface ProductCardProps {
product: Product
onEdit: (product: Product) => void
onDeleted: () => void
}
export function ProductCard({ product, onEdit, onDeleted }: ProductCardProps) {
const [deleteProduct, { loading: deleting }] = useMutation(DELETE_PRODUCT)
const handleDelete = async () => {
try {
await deleteProduct({
variables: { id: product.id }
})
toast.success('Товар успешно удален')
onDeleted()
} catch (error) {
console.error('Error deleting product:', error)
toast.error('Ошибка при удалении товара')
}
}
const formatPrice = (price: number) => {
return new Intl.NumberFormat('ru-RU', {
style: 'currency',
currency: 'RUB',
minimumFractionDigits: 0
}).format(price)
}
const getStatusColor = () => {
if (!product.isActive) return 'bg-gray-500/20 text-gray-300 border-gray-400/30'
if (product.quantity === 0) return 'bg-red-500/20 text-red-300 border-red-400/30'
if (product.quantity < 10) return 'bg-yellow-500/20 text-yellow-300 border-yellow-400/30'
return 'bg-green-500/20 text-green-300 border-green-400/30'
}
const getStatusText = () => {
if (!product.isActive) return 'Неактивен'
if (product.quantity === 0) return 'Нет в наличии'
if (product.quantity < 10) return 'Мало на складе'
return 'В наличии'
}
return (
<Card className="glass-card group relative overflow-hidden transition-all duration-300 hover:scale-[1.02] hover:shadow-xl hover:shadow-purple-500/20">
{/* Изображение товара */}
<div className="relative h-48 bg-white/5 overflow-hidden">
{product.mainImage || product.images[0] ? (
<Image
src={product.mainImage || product.images[0]}
alt={product.name}
width={300}
height={200}
className="w-full h-full object-cover transition-transform duration-300 group-hover:scale-110"
/>
) : (
<div className="w-full h-full flex items-center justify-center">
<Package className="h-16 w-16 text-white/30" />
</div>
)}
{/* Статус товара */}
<div className="absolute top-2 left-2">
<Badge className={`text-xs px-2 py-1 ${getStatusColor()}`}>
{getStatusText()}
</Badge>
</div>
{/* Индикатор активности */}
<div className="absolute top-2 right-2">
{product.isActive ? (
<Eye className="h-4 w-4 text-green-300" />
) : (
<EyeOff className="h-4 w-4 text-gray-400" />
)}
</div>
{/* Кнопки управления */}
<div className="absolute bottom-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity flex gap-1">
<Button
size="sm"
variant="outline"
onClick={() => onEdit(product)}
className="p-1 h-7 w-7 bg-white/20 border-white/30 hover:bg-white/30 backdrop-blur"
>
<Edit3 className="h-3 w-3 text-white" />
</Button>
<AlertDialog>
<AlertDialogTrigger asChild>
<Button
size="sm"
variant="outline"
className="p-1 h-7 w-7 bg-red-500/20 border-red-400/30 hover:bg-red-500/30 backdrop-blur"
>
<Trash2 className="h-3 w-3 text-white" />
</Button>
</AlertDialogTrigger>
<AlertDialogContent className="glass-card border-white/10">
<AlertDialogHeader>
<AlertDialogTitle className="text-white">Удалить товар?</AlertDialogTitle>
<AlertDialogDescription className="text-white/70">
Вы уверены, что хотите удалить товар &quot;{product.name}&quot;?
Это действие нельзя отменить.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel className="glass-secondary text-white hover:text-white">
Отмена
</AlertDialogCancel>
<AlertDialogAction
onClick={handleDelete}
disabled={deleting}
className="bg-red-600 hover:bg-red-700 text-white"
>
{deleting ? 'Удаление...' : 'Удалить'}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
</div>
{/* Информация о товаре */}
<div className="p-4 space-y-3">
{/* Название и артикул */}
<div>
<h3 className="text-white font-medium text-sm line-clamp-2 leading-tight">
{product.name}
</h3>
<p className="text-white/60 text-xs mt-1">
Арт. {product.article}
</p>
</div>
{/* Цена и количество */}
<div className="flex items-center justify-between">
<div className="text-white font-semibold">
{formatPrice(product.price)}
</div>
<div className="text-white/70 text-sm">
{product.quantity} шт.
</div>
</div>
{/* Дополнительная информация */}
<div className="space-y-1">
<div className="flex items-center gap-2 flex-wrap">
{/* Тип товара */}
<Badge
variant="outline"
className={`text-xs ${
product.type === 'PRODUCT'
? 'bg-blue-500/20 text-blue-300 border-blue-400/30'
: 'bg-orange-500/20 text-orange-300 border-orange-400/30'
}`}
>
{product.type === 'PRODUCT' ? 'Товар' : 'Расходник'}
</Badge>
{/* Категория */}
{product.category && (
<Badge variant="outline" className="glass-secondary text-white/60 border-white/20 text-xs">
{product.category.name}
</Badge>
)}
</div>
<div className="flex flex-wrap gap-1">
{product.brand && (
<span className="text-white/50 text-xs bg-white/10 px-2 py-1 rounded">
{product.brand}
</span>
)}
{product.color && (
<span className="text-white/50 text-xs bg-white/10 px-2 py-1 rounded">
{product.color}
</span>
)}
{product.size && (
<span className="text-white/50 text-xs bg-white/10 px-2 py-1 rounded">
{product.size}
</span>
)}
</div>
</div>
{/* Описание (если есть) */}
{product.description && (
<p className="text-white/60 text-xs line-clamp-2 leading-relaxed">
{product.description}
</p>
)}
</div>
{/* Эффект градиента при наведении */}
<div className="absolute inset-0 bg-gradient-to-t from-purple-600/0 via-transparent to-transparent opacity-0 group-hover:opacity-20 transition-opacity duration-300 pointer-events-none" />
</Card>
)
}