425 lines
14 KiB
TypeScript
425 lines
14 KiB
TypeScript
"use client"
|
||
|
||
import React, { useState } from 'react'
|
||
import { Card } from '@/components/ui/card'
|
||
import { Button } from '@/components/ui/button'
|
||
import { Badge } from '@/components/ui/badge'
|
||
import { Input } from '@/components/ui/input'
|
||
import {
|
||
ArrowLeft,
|
||
Package,
|
||
Plus,
|
||
Minus,
|
||
ShoppingCart,
|
||
Eye,
|
||
Info
|
||
} from 'lucide-react'
|
||
import Image from 'next/image'
|
||
|
||
interface Wholesaler {
|
||
id: string
|
||
inn: string
|
||
name: string
|
||
fullName: string
|
||
address: string
|
||
phone?: string
|
||
email?: string
|
||
rating: number
|
||
productCount: number
|
||
avatar?: string
|
||
specialization: string[]
|
||
}
|
||
|
||
interface Product {
|
||
id: string
|
||
name: string
|
||
article: string
|
||
description: string
|
||
price: number
|
||
quantity: number
|
||
category: string
|
||
brand?: string
|
||
color?: string
|
||
size?: string
|
||
weight?: number
|
||
dimensions?: string
|
||
material?: string
|
||
images: string[]
|
||
mainImage?: string
|
||
}
|
||
|
||
interface SelectedProduct extends Product {
|
||
selectedQuantity: number
|
||
}
|
||
|
||
interface WholesalerProductsProps {
|
||
wholesaler: Wholesaler
|
||
onBack: () => void
|
||
onClose: () => void
|
||
onSupplyCreated: () => void
|
||
}
|
||
|
||
// Моковые данные товаров
|
||
const mockProducts: Product[] = [
|
||
{
|
||
id: '1',
|
||
name: 'Смартфон Samsung Galaxy A54',
|
||
article: 'SGX-A54-128',
|
||
description: 'Смартфон с экраном 6.4", камерой 50 МП, 128 ГБ памяти',
|
||
price: 28900,
|
||
quantity: 150,
|
||
category: 'Смартфоны',
|
||
brand: 'Samsung',
|
||
color: 'Черный',
|
||
size: '6.4"',
|
||
weight: 202,
|
||
dimensions: '158.2 x 76.7 x 8.2 мм',
|
||
material: 'Алюминий, стекло',
|
||
images: ['/api/placeholder/300/300?text=Samsung+A54'],
|
||
mainImage: '/api/placeholder/300/300?text=Samsung+A54'
|
||
},
|
||
{
|
||
id: '2',
|
||
name: 'Наушники Sony WH-1000XM4',
|
||
article: 'SNY-WH1000XM4',
|
||
description: 'Беспроводные наушники с шумоподавлением',
|
||
price: 24900,
|
||
quantity: 85,
|
||
category: 'Наушники',
|
||
brand: 'Sony',
|
||
color: 'Черный',
|
||
weight: 254,
|
||
material: 'Пластик, кожа',
|
||
images: ['/api/placeholder/300/300?text=Sony+WH1000XM4'],
|
||
mainImage: '/api/placeholder/300/300?text=Sony+WH1000XM4'
|
||
},
|
||
{
|
||
id: '3',
|
||
name: 'Планшет iPad Air 10.9"',
|
||
article: 'APL-IPADAIR-64',
|
||
description: 'Планшет Apple iPad Air с чипом M1, 64 ГБ',
|
||
price: 54900,
|
||
quantity: 45,
|
||
category: 'Планшеты',
|
||
brand: 'Apple',
|
||
color: 'Серый космос',
|
||
size: '10.9"',
|
||
weight: 461,
|
||
dimensions: '247.6 x 178.5 x 6.1 мм',
|
||
material: 'Алюминий',
|
||
images: ['/api/placeholder/300/300?text=iPad+Air'],
|
||
mainImage: '/api/placeholder/300/300?text=iPad+Air'
|
||
},
|
||
{
|
||
id: '4',
|
||
name: 'Ноутбук Lenovo ThinkPad E15',
|
||
article: 'LNV-TE15-I5',
|
||
description: 'Ноутбук 15.6" Intel Core i5, 8 ГБ ОЗУ, 256 ГБ SSD',
|
||
price: 45900,
|
||
quantity: 25,
|
||
category: 'Ноутбуки',
|
||
brand: 'Lenovo',
|
||
color: 'Черный',
|
||
size: '15.6"',
|
||
weight: 1700,
|
||
dimensions: '365 x 240 x 19.9 мм',
|
||
material: 'Пластик',
|
||
images: ['/api/placeholder/300/300?text=ThinkPad+E15'],
|
||
mainImage: '/api/placeholder/300/300?text=ThinkPad+E15'
|
||
},
|
||
{
|
||
id: '5',
|
||
name: 'Умные часы Apple Watch SE',
|
||
article: 'APL-AWSE-40',
|
||
description: 'Умные часы Apple Watch SE 40 мм',
|
||
price: 21900,
|
||
quantity: 120,
|
||
category: 'Умные часы',
|
||
brand: 'Apple',
|
||
color: 'Белый',
|
||
size: '40 мм',
|
||
weight: 30,
|
||
dimensions: '40 x 34 x 10.7 мм',
|
||
material: 'Алюминий',
|
||
images: ['/api/placeholder/300/300?text=Apple+Watch+SE'],
|
||
mainImage: '/api/placeholder/300/300?text=Apple+Watch+SE'
|
||
},
|
||
{
|
||
id: '6',
|
||
name: 'Клавиатура Logitech MX Keys',
|
||
article: 'LGT-MXKEYS',
|
||
description: 'Беспроводная клавиатура для продуктивной работы',
|
||
price: 8900,
|
||
quantity: 75,
|
||
category: 'Клавиатуры',
|
||
brand: 'Logitech',
|
||
color: 'Графит',
|
||
weight: 810,
|
||
dimensions: '430.2 x 20.5 x 131.6 мм',
|
||
material: 'Пластик, металл',
|
||
images: ['/api/placeholder/300/300?text=MX+Keys'],
|
||
mainImage: '/api/placeholder/300/300?text=MX+Keys'
|
||
}
|
||
]
|
||
|
||
export function WholesalerProducts({ wholesaler, onBack, onClose, onSupplyCreated }: WholesalerProductsProps) {
|
||
const [selectedProducts, setSelectedProducts] = useState<SelectedProduct[]>([])
|
||
const [showSummary, setShowSummary] = useState(false)
|
||
|
||
const formatCurrency = (amount: number) => {
|
||
return new Intl.NumberFormat('ru-RU', {
|
||
style: 'currency',
|
||
currency: 'RUB',
|
||
minimumFractionDigits: 0
|
||
}).format(amount)
|
||
}
|
||
|
||
const updateProductQuantity = (productId: string, quantity: number) => {
|
||
const product = mockProducts.find(p => p.id === productId)
|
||
if (!product) return
|
||
|
||
setSelectedProducts(prev => {
|
||
const existing = prev.find(p => p.id === productId)
|
||
|
||
if (quantity === 0) {
|
||
// Удаляем продукт если количество 0
|
||
return prev.filter(p => p.id !== productId)
|
||
}
|
||
|
||
if (existing) {
|
||
// Обновляем количество существующего продукта
|
||
return prev.map(p =>
|
||
p.id === productId ? { ...p, selectedQuantity: quantity } : p
|
||
)
|
||
} else {
|
||
// Добавляем новый продукт
|
||
return [...prev, { ...product, selectedQuantity: quantity }]
|
||
}
|
||
})
|
||
}
|
||
|
||
const getSelectedQuantity = (productId: string): number => {
|
||
const selected = selectedProducts.find(p => p.id === productId)
|
||
return selected ? selected.selectedQuantity : 0
|
||
}
|
||
|
||
const getTotalAmount = () => {
|
||
return selectedProducts.reduce((sum, product) =>
|
||
sum + (product.price * product.selectedQuantity), 0
|
||
)
|
||
}
|
||
|
||
const getTotalItems = () => {
|
||
return selectedProducts.reduce((sum, product) => sum + product.selectedQuantity, 0)
|
||
}
|
||
|
||
const handleCreateSupply = () => {
|
||
console.log('Создание поставки с товарами:', selectedProducts)
|
||
// TODO: Здесь будет реальное создание поставки
|
||
onSupplyCreated()
|
||
}
|
||
|
||
return (
|
||
<div className="space-y-6">
|
||
<div className="flex items-center justify-between">
|
||
<div className="flex items-center space-x-3">
|
||
<Button
|
||
variant="ghost"
|
||
size="sm"
|
||
onClick={onBack}
|
||
className="text-white/60 hover:text-white hover:bg-white/10"
|
||
>
|
||
<ArrowLeft className="h-4 w-4 mr-2" />
|
||
Назад
|
||
</Button>
|
||
<div>
|
||
<h2 className="text-2xl font-bold text-white mb-1">Товары оптовика</h2>
|
||
<p className="text-white/60">{wholesaler.name} • {mockProducts.length} товаров</p>
|
||
</div>
|
||
</div>
|
||
<div className="flex items-center space-x-3">
|
||
<Button
|
||
variant="ghost"
|
||
size="sm"
|
||
onClick={() => setShowSummary(!showSummary)}
|
||
className="text-white/60 hover:text-white hover:bg-white/10"
|
||
>
|
||
<Info className="h-4 w-4 mr-2" />
|
||
Резюме ({selectedProducts.length})
|
||
</Button>
|
||
<Button
|
||
variant="ghost"
|
||
size="sm"
|
||
onClick={onClose}
|
||
className="text-white/60 hover:text-white hover:bg-white/10"
|
||
>
|
||
Отмена
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
|
||
{showSummary && selectedProducts.length > 0 && (
|
||
<Card className="bg-purple-500/10 backdrop-blur border-purple-500/30 p-6">
|
||
<h3 className="text-white font-semibold text-lg mb-4">Резюме заказа</h3>
|
||
<div className="space-y-3">
|
||
{selectedProducts.map((product) => (
|
||
<div key={product.id} className="flex justify-between items-center">
|
||
<div>
|
||
<span className="text-white">{product.name}</span>
|
||
<span className="text-white/60 text-sm ml-2">× {product.selectedQuantity}</span>
|
||
</div>
|
||
<span className="text-white font-medium">
|
||
{formatCurrency(product.price * product.selectedQuantity)}
|
||
</span>
|
||
</div>
|
||
))}
|
||
<div className="border-t border-white/20 pt-3 flex justify-between items-center">
|
||
<span className="text-white font-semibold">
|
||
Итого: {getTotalItems()} товаров
|
||
</span>
|
||
<span className="text-white font-bold text-xl">
|
||
{formatCurrency(getTotalAmount())}
|
||
</span>
|
||
</div>
|
||
<Button
|
||
className="w-full bg-gradient-to-r from-green-500 to-emerald-500 hover:from-green-600 hover:to-emerald-600 text-white"
|
||
onClick={handleCreateSupply}
|
||
disabled={selectedProducts.length === 0}
|
||
>
|
||
<ShoppingCart className="h-4 w-4 mr-2" />
|
||
Создать поставку
|
||
</Button>
|
||
</div>
|
||
</Card>
|
||
)}
|
||
|
||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||
{mockProducts.map((product) => {
|
||
const selectedQuantity = getSelectedQuantity(product.id)
|
||
|
||
return (
|
||
<Card key={product.id} className="bg-white/10 backdrop-blur border-white/20 overflow-hidden">
|
||
<div className="aspect-square relative bg-white/5">
|
||
<Image
|
||
src={product.mainImage || '/api/placeholder/300/300'}
|
||
alt={product.name}
|
||
fill
|
||
className="object-cover"
|
||
/>
|
||
<div className="absolute top-2 right-2">
|
||
<Badge className="bg-green-500/20 text-green-300 border-green-500/30">
|
||
В наличии: {product.quantity}
|
||
</Badge>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="p-4 space-y-3">
|
||
<div>
|
||
<h3 className="text-white font-semibold mb-1 line-clamp-2">
|
||
{product.name}
|
||
</h3>
|
||
<p className="text-white/60 text-xs mb-2">
|
||
Артикул: {product.article}
|
||
</p>
|
||
<div className="flex items-center space-x-2 mb-2">
|
||
<Badge className="bg-blue-500/20 text-blue-300 border-blue-500/30 text-xs">
|
||
{product.category}
|
||
</Badge>
|
||
{product.brand && (
|
||
<Badge className="bg-gray-500/20 text-gray-300 border-gray-500/30 text-xs">
|
||
{product.brand}
|
||
</Badge>
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
<p className="text-white/60 text-sm line-clamp-2">
|
||
{product.description}
|
||
</p>
|
||
|
||
<div className="space-y-2">
|
||
{product.color && (
|
||
<div className="text-white/60 text-xs">
|
||
Цвет: <span className="text-white">{product.color}</span>
|
||
</div>
|
||
)}
|
||
{product.size && (
|
||
<div className="text-white/60 text-xs">
|
||
Размер: <span className="text-white">{product.size}</span>
|
||
</div>
|
||
)}
|
||
{product.weight && (
|
||
<div className="text-white/60 text-xs">
|
||
Вес: <span className="text-white">{product.weight} г</span>
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
<div className="flex items-center justify-between pt-2 border-t border-white/10">
|
||
<div>
|
||
<div className="text-white font-bold text-lg">
|
||
{formatCurrency(product.price)}
|
||
</div>
|
||
<div className="text-white/60 text-xs">за штуку</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="flex items-center space-x-2">
|
||
<Button
|
||
variant="ghost"
|
||
size="sm"
|
||
onClick={() => updateProductQuantity(product.id, Math.max(0, selectedQuantity - 1))}
|
||
disabled={selectedQuantity === 0}
|
||
className="h-8 w-8 p-0 text-white/60 hover:text-white hover:bg-white/10"
|
||
>
|
||
<Minus className="h-4 w-4" />
|
||
</Button>
|
||
<Input
|
||
type="number"
|
||
value={selectedQuantity}
|
||
onChange={(e) => {
|
||
const value = Math.max(0, Math.min(product.quantity, parseInt(e.target.value) || 0))
|
||
updateProductQuantity(product.id, value)
|
||
}}
|
||
className="h-8 w-16 text-center bg-white/10 border-white/20 text-white"
|
||
min={0}
|
||
max={product.quantity}
|
||
/>
|
||
<Button
|
||
variant="ghost"
|
||
size="sm"
|
||
onClick={() => updateProductQuantity(product.id, Math.min(product.quantity, selectedQuantity + 1))}
|
||
disabled={selectedQuantity >= product.quantity}
|
||
className="h-8 w-8 p-0 text-white/60 hover:text-white hover:bg-white/10"
|
||
>
|
||
<Plus className="h-4 w-4" />
|
||
</Button>
|
||
</div>
|
||
|
||
{selectedQuantity > 0 && (
|
||
<div className="bg-green-500/20 border border-green-500/30 rounded-lg p-2">
|
||
<div className="text-green-300 text-sm font-medium">
|
||
Сумма: {formatCurrency(product.price * selectedQuantity)}
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</Card>
|
||
)
|
||
})}
|
||
</div>
|
||
|
||
{selectedProducts.length > 0 && (
|
||
<div className="fixed bottom-6 right-6">
|
||
<Button
|
||
className="bg-gradient-to-r from-purple-500 to-pink-500 hover:from-purple-600 hover:to-pink-600 text-white shadow-lg"
|
||
onClick={() => setShowSummary(!showSummary)}
|
||
>
|
||
<ShoppingCart className="h-4 w-4 mr-2" />
|
||
Корзина ({selectedProducts.length}) • {formatCurrency(getTotalAmount())}
|
||
</Button>
|
||
</div>
|
||
)}
|
||
</div>
|
||
)
|
||
}
|