'use client' import { Package, Building2, Calendar, DollarSign, Search, Filter, ChevronDown, ChevronRight, Smartphone, Eye, MoreHorizontal, MapPin, TrendingUp, AlertTriangle, Warehouse, } from 'lucide-react' import React, { useState } from 'react' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { Card } from '@/components/ui/card' import { Input } from '@/components/ui/input' import { formatCurrency } from '@/lib/utils' // Простые компоненты таблицы const Table = ({ children, ...props }: any) => (
{children}
) const TableHeader = ({ children, ...props }: any) => {children} const TableBody = ({ children, ...props }: any) => {children} const TableRow = ({ children, className, ...props }: any) => ( {children} ) const TableHead = ({ children, className, ...props }: any) => ( {children} ) const TableCell = ({ children, className, ...props }: any) => ( {children} ) // Расширенные типы данных для детальной структуры поставок interface ProductParameter { id: string name: string value: string unit?: string } interface GoodsSupplyProduct { id: string name: string sku: string category: string plannedQty: number actualQty: number defectQty: number productPrice: number parameters: ProductParameter[] } interface GoodsSupplyWholesaler { id: string name: string inn: string contact: string address: string products: GoodsSupplyProduct[] totalAmount: number } interface GoodsSupplyRoute { id: string from: string fromAddress: string to: string toAddress: string wholesalers: GoodsSupplyWholesaler[] totalProductPrice: number fulfillmentServicePrice: number logisticsPrice: number totalAmount: number } // Основной интерфейс поставки товаров согласно rules2.md 9.5.4 interface GoodsSupply { id: string number: string creationMethod: 'cards' | 'suppliers' // 📱 карточки / 🏢 поставщик deliveryDate: string createdAt: string status: string // Агрегированные данные plannedTotal: number actualTotal: number defectTotal: number totalProductPrice: number totalFulfillmentPrice: number totalLogisticsPrice: number grandTotal: number // Детальная структура routes: GoodsSupplyRoute[] // Для обратной совместимости goodsCount?: number totalAmount?: number supplier?: string items?: GoodsSupplyItem[] } // Простой интерфейс товара для базовой детализации interface GoodsSupplyItem { id: string name: string quantity: number price: number category?: string } interface GoodsSuppliesTableProps { supplies?: GoodsSupply[] loading?: boolean } // Компонент для иконки способа создания function CreationMethodIcon({ method }: { method: 'cards' | 'suppliers' }) { if (method === 'cards') { return (
Карточки
) } return (
Поставщик
) } // Компонент для статуса поставки function StatusBadge({ status }: { status: string }) { const getStatusColor = (status: string) => { switch (status.toLowerCase()) { case 'pending': return 'bg-yellow-500/20 text-yellow-300 border-yellow-500/30' case 'supplier_approved': return 'bg-blue-500/20 text-blue-300 border-blue-500/30' case 'confirmed': return 'bg-purple-500/20 text-purple-300 border-purple-500/30' case 'shipped': return 'bg-orange-500/20 text-orange-300 border-orange-500/30' case 'in_transit': return 'bg-indigo-500/20 text-indigo-300 border-indigo-500/30' case 'delivered': return 'bg-green-500/20 text-green-300 border-green-500/30' default: return 'bg-gray-500/20 text-gray-300 border-gray-500/30' } } const getStatusText = (status: string) => { switch (status.toLowerCase()) { case 'pending': return 'Ожидает' case 'supplier_approved': return 'Одобрена' case 'confirmed': return 'Подтверждена' case 'shipped': return 'Отгружена' case 'in_transit': return 'В пути' case 'delivered': return 'Доставлена' default: return status } } return {getStatusText(status)} } export function GoodsSuppliesTable({ supplies = [], loading = false }: GoodsSuppliesTableProps) { const [searchQuery, setSearchQuery] = useState('') const [selectedMethod, setSelectedMethod] = useState('all') const [selectedStatus, setSelectedStatus] = useState('all') const [expandedSupplies, setExpandedSupplies] = useState>(new Set()) const [expandedRoutes, setExpandedRoutes] = useState>(new Set()) const [expandedWholesalers, setExpandedWholesalers] = useState>(new Set()) const [expandedProducts, setExpandedProducts] = useState>(new Set()) // Фильтрация согласно rules2.md 9.5.4 с поддержкой расширенной структуры const filteredSupplies = supplies.filter((supply) => { const matchesSearch = supply.number.toLowerCase().includes(searchQuery.toLowerCase()) || (supply.supplier && supply.supplier.toLowerCase().includes(searchQuery.toLowerCase())) || (supply.routes && supply.routes.some((route) => route.wholesalers.some((wholesaler) => wholesaler.name.toLowerCase().includes(searchQuery.toLowerCase())), )) const matchesMethod = selectedMethod === 'all' || supply.creationMethod === selectedMethod const matchesStatus = selectedStatus === 'all' || supply.status === selectedStatus return matchesSearch && matchesMethod && matchesStatus }) const toggleSupplyExpansion = (supplyId: string) => { const newExpanded = new Set(expandedSupplies) if (newExpanded.has(supplyId)) { newExpanded.delete(supplyId) } else { newExpanded.add(supplyId) } setExpandedSupplies(newExpanded) } const toggleRouteExpansion = (routeId: string) => { const newExpanded = new Set(expandedRoutes) if (newExpanded.has(routeId)) { newExpanded.delete(routeId) } else { newExpanded.add(routeId) } setExpandedRoutes(newExpanded) } const toggleWholesalerExpansion = (wholesalerId: string) => { const newExpanded = new Set(expandedWholesalers) if (newExpanded.has(wholesalerId)) { newExpanded.delete(wholesalerId) } else { newExpanded.add(wholesalerId) } setExpandedWholesalers(newExpanded) } const toggleProductExpansion = (productId: string) => { const newExpanded = new Set(expandedProducts) if (newExpanded.has(productId)) { newExpanded.delete(productId) } else { newExpanded.add(productId) } setExpandedProducts(newExpanded) } // Вспомогательные функции const getStatusBadge = (status: string) => { const statusMap = { pending: { label: 'Ожидает', color: 'bg-yellow-500/20 text-yellow-300 border-yellow-500/30' }, supplier_approved: { label: 'Одобрена', color: 'bg-blue-500/20 text-blue-300 border-blue-500/30' }, confirmed: { label: 'Подтверждена', color: 'bg-purple-500/20 text-purple-300 border-purple-500/30' }, shipped: { label: 'Отгружена', color: 'bg-orange-500/20 text-orange-300 border-orange-500/30' }, in_transit: { label: 'В пути', color: 'bg-indigo-500/20 text-indigo-300 border-indigo-500/30' }, delivered: { label: 'Доставлена', color: 'bg-green-500/20 text-green-300 border-green-500/30' }, planned: { label: 'Запланирована', color: 'bg-blue-500/20 text-blue-300 border-blue-500/30' }, completed: { label: 'Завершена', color: 'bg-purple-500/20 text-purple-300 border-purple-500/30' }, } const statusInfo = statusMap[status as keyof typeof statusMap] || { label: status, color: 'bg-gray-500/20 text-gray-300 border-gray-500/30', } return {statusInfo.label} } const getEfficiencyBadge = (planned: number, actual: number, defect: number) => { const efficiency = ((actual - defect) / planned) * 100 if (efficiency >= 95) { return Отлично } else if (efficiency >= 90) { return Хорошо } else { return Проблемы } } const calculateProductTotal = (product: GoodsSupplyProduct) => { return product.actualQty * product.productPrice } const formatDate = (dateString: string) => { return new Date(dateString).toLocaleDateString('ru-RU', { day: '2-digit', month: '2-digit', year: 'numeric', }) } if (loading) { return (
{[...Array(5)].map((_, i) => (
))}
) } return (
{/* Фильтры */}
{/* Поиск */}
setSearchQuery(e.target.value)} className="bg-white/10 border-white/20 text-white placeholder-white/50 pl-10" />
{/* Фильтр по способу создания */} {/* Фильтр по статусу */}
{/* Таблица поставок согласно rules2.md 9.5.4 */} Дата поставки Поставка Создана План Факт Брак Цена товаров Цена ФФ Логистика Итого Статус Способ {filteredSupplies.length === 0 ? ( {searchQuery || selectedMethod !== 'all' || selectedStatus !== 'all' ? 'Поставки не найдены по заданным фильтрам' : 'Поставки товаров отсутствуют'} ) : ( filteredSupplies.map((supply) => { const isSupplyExpanded = expandedSupplies.has(supply.id) return ( {/* Основная строка поставки */} toggleSupplyExpansion(supply.id)} >
{isSupplyExpanded ? ( ) : ( )} {supply.number}
{formatDate(supply.deliveryDate)}
{formatDate(supply.createdAt)} {supply.plannedTotal || supply.goodsCount || 0} {supply.actualTotal || supply.goodsCount || 0} 0 ? 'text-red-400' : 'text-white' }`} > {supply.defectTotal || 0} {formatCurrency(supply.totalProductPrice || supply.totalAmount || 0)} {formatCurrency(supply.totalFulfillmentPrice || 0)} {formatCurrency(supply.totalLogisticsPrice || 0)}
{formatCurrency(supply.grandTotal || supply.totalAmount || 0)}
{getStatusBadge(supply.status)}
{/* Развернутые уровни - маршруты, поставщики, товары */} {isSupplyExpanded && supply.routes && supply.routes.map((route) => { const isRouteExpanded = expandedRoutes.has(route.id) return ( toggleRouteExpansion(route.id)} >
Маршрут
{route.from} {route.to}
{route.fromAddress} → {route.toAddress}
{route.wholesalers.reduce( (sum, w) => sum + w.products.reduce((pSum, p) => pSum + p.plannedQty, 0), 0, )} {route.wholesalers.reduce( (sum, w) => sum + w.products.reduce((pSum, p) => pSum + p.actualQty, 0), 0, )} {route.wholesalers.reduce( (sum, w) => sum + w.products.reduce((pSum, p) => pSum + p.defectQty, 0), 0, )} {formatCurrency(route.totalProductPrice)} {formatCurrency(route.fulfillmentServicePrice)} {formatCurrency(route.logisticsPrice)} {formatCurrency(route.totalAmount)}
{/* Поставщики в маршруте */} {isRouteExpanded && route.wholesalers.map((wholesaler) => { const isWholesalerExpanded = expandedWholesalers.has(wholesaler.id) return ( toggleWholesalerExpansion(wholesaler.id)} >
Поставщик
{wholesaler.name}
ИНН: {wholesaler.inn}
{wholesaler.address}
{wholesaler.contact}
{wholesaler.products.reduce((sum, p) => sum + p.plannedQty, 0)} {wholesaler.products.reduce((sum, p) => sum + p.actualQty, 0)} {wholesaler.products.reduce((sum, p) => sum + p.defectQty, 0)} {formatCurrency( wholesaler.products.reduce((sum, p) => sum + calculateProductTotal(p), 0), )} {formatCurrency(wholesaler.totalAmount)}
{/* Товары поставщика */} {isWholesalerExpanded && wholesaler.products.map((product) => { const isProductExpanded = expandedProducts.has(product.id) return ( toggleProductExpansion(product.id)} >
Товар
{product.name}
Артикул: {product.sku}
{product.category}
{product.plannedQty} {product.actualQty} 0 ? 'text-red-400' : 'text-white' }`} > {product.defectQty}
{formatCurrency(calculateProductTotal(product))}
{formatCurrency(product.productPrice)} за шт.
{getEfficiencyBadge( product.plannedQty, product.actualQty, product.defectQty, )} {formatCurrency(calculateProductTotal(product))}
{/* Параметры товара */} {isProductExpanded && (

📋 Параметры товара:

{product.parameters.map((param) => (
{param.name}
{param.value} {param.unit || ''}
))}
)}
) })}
) })}
) })} {/* Базовая детализация для поставок без маршрутов */} {isSupplyExpanded && supply.items && !supply.routes && (

Детализация товаров:

{supply.items.map((item) => (
{item.name} {item.category && ( ({item.category}) )}
{item.quantity} шт {formatCurrency(item.price)} {formatCurrency(item.price * item.quantity)}
))}
)}
) }) )}
) }