469 lines
15 KiB
TypeScript
469 lines
15 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 Supplier {
|
||
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 SupplierProductsProps {
|
||
supplier: Supplier;
|
||
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 SupplierProducts({
|
||
supplier,
|
||
onBack,
|
||
onClose,
|
||
onSupplyCreated,
|
||
}: SupplierProductsProps) {
|
||
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">
|
||
{supplier.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>
|
||
);
|
||
}
|