'use client' import { useQuery } from '@apollo/client' import { Package, TrendingUp, TrendingDown, AlertTriangle, RotateCcw, Wrench, Users, Box, Search, ArrowUpDown, Store, Package2, Eye, EyeOff, ChevronRight, ChevronDown, Layers, Truck, Clock, CheckCircle, Settings, } from 'lucide-react' import { useRouter } from 'next/navigation' import { useState, useMemo } from 'react' import { toast } from 'sonner' import { Sidebar } from '@/components/dashboard/sidebar' import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar' 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 { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover' import { GET_MY_COUNTERPARTIES, GET_SUPPLY_ORDERS, GET_WAREHOUSE_PRODUCTS, GET_WAREHOUSE_DATA, // Новый запрос данных склада с партнерами GET_MY_SUPPLIES, // Расходники селлеров (старые данные заказов) GET_SELLER_SUPPLIES_ON_WAREHOUSE, // Расходники селлеров на складе (новый API) GET_MY_FULFILLMENT_SUPPLIES, // Расходники фулфилмента GET_FULFILLMENT_WAREHOUSE_STATS, // Статистика склада с изменениями за сутки GET_SUPPLY_MOVEMENTS, // Движения товаров (прибыло/убыло) } from '@/graphql/queries' import { useAuth } from '@/hooks/useAuth' import { useRealtime } from '@/hooks/useRealtime' import { useSidebar } from '@/hooks/useSidebar' import { WbReturnClaims } from '../wb-return-claims' import { StatCard } from './blocks/StatCard' // Типы данных для 3-уровневой иерархии interface ProductVariant { // 🟠 УРОВЕНЬ 3: Варианты товаров id: string name: string // Размер, характеристика, вариант упаковки // Места и количества для каждого типа на уровне варианта productPlace?: string productQuantity: number goodsPlace?: string goodsQuantity: number defectsPlace?: string defectsQuantity: number sellerSuppliesPlace?: string sellerSuppliesQuantity: number sellerSuppliesOwners?: string[] // Владельцы расходников pvzReturnsPlace?: string pvzReturnsQuantity: number } interface ProductItem { // 🟢 УРОВЕНЬ 2: Товары id: string name: string article: string // Места и количества для каждого типа productPlace?: string productQuantity: number goodsPlace?: string goodsQuantity: number defectsPlace?: string defectsQuantity: number sellerSuppliesPlace?: string sellerSuppliesQuantity: number sellerSuppliesOwners?: string[] // Владельцы расходников pvzReturnsPlace?: string pvzReturnsQuantity: number // Третий уровень - варианты товара variants?: ProductVariant[] } interface StoreData { // 🔵 УРОВЕНЬ 1: Магазины id: string name: string logo?: string avatar?: string // Аватар пользователя организации products: number goods: number defects: number sellerSupplies: number pvzReturns: number // Изменения за сутки productsChange: number goodsChange: number defectsChange: number sellerSuppliesChange: number pvzReturnsChange: number // Детализация по товарам items: ProductItem[] } interface WarehouseStats { products: { current: number; change: number; arrived: number; departed: number } goods: { current: number; change: number; arrived: number; departed: number } defects: { current: number; change: number; arrived: number; departed: number } pvzReturns: { current: number; change: number; arrived: number; departed: number } fulfillmentSupplies: { current: number; change: number; arrived: number; departed: number } sellerSupplies: { current: number; change: number; arrived: number; departed: number } } export function FulfillmentWarehouseDashboard() { const router = useRouter() const { getSidebarMargin } = useSidebar() const { user } = useAuth() // Состояния для поиска и фильтрации const [searchTerm, setSearchTerm] = useState('') const [sortField, setSortField] = useState('name') const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('asc') // Состояния для 3-уровневой иерархии const [expandedStores, setExpandedStores] = useState>(new Set()) // 🔵 Раскрытые магазины const [expandedItems, setExpandedItems] = useState>(new Set()) // 🟢 Раскрытые товары const [showReturnClaims, setShowReturnClaims] = useState(false) const [showAdditionalValues, setShowAdditionalValues] = useState(true) // Загружаем данные из GraphQL const { data: counterpartiesData, loading: counterpartiesLoading, error: counterpartiesError, refetch: refetchCounterparties, } = useQuery(GET_MY_COUNTERPARTIES, { pollInterval: 30000, errorPolicy: 'all', onError: (error) => { console.warn('Ошибка загрузки контрагентов:', error) }, }) const { data: ordersData, loading: ordersLoading, error: ordersError, refetch: refetchOrders, } = useQuery(GET_SUPPLY_ORDERS, { pollInterval: 30000, errorPolicy: 'all', onError: (error) => { console.warn('Ошибка загрузки заказов:', error) }, }) const { data: warehouseData, loading: warehouseLoading, error: warehouseError, refetch: refetchWarehouse, } = useQuery(GET_WAREHOUSE_PRODUCTS, { pollInterval: 30000, errorPolicy: 'all', onError: (error) => { console.warn('Ошибка загрузки товаров склада:', error) }, }) const { data: sellerSuppliesData, loading: sellerSuppliesLoading, error: sellerSuppliesError, refetch: refetchSellerSupplies, } = useQuery(GET_SELLER_SUPPLIES_ON_WAREHOUSE, { pollInterval: 30000, errorPolicy: 'all', onError: (error) => { console.warn('Ошибка загрузки расходников селлеров:', error) }, }) const { data: fulfillmentSuppliesData, loading: fulfillmentSuppliesLoading, error: fulfillmentSuppliesError, refetch: refetchFulfillmentSupplies, } = useQuery(GET_MY_FULFILLMENT_SUPPLIES, { pollInterval: 30000, errorPolicy: 'all', onError: (error) => { console.warn('Ошибка загрузки расходников фулфилмента:', error) }, }) const { data: warehouseStatsData, loading: warehouseStatsLoading, error: warehouseStatsError, refetch: refetchWarehouseStats, } = useQuery(GET_FULFILLMENT_WAREHOUSE_STATS, { fetchPolicy: 'cache-and-network', // Синхронизация с карточкой "ОСТАТОК" pollInterval: 30000, errorPolicy: 'all', onError: (error) => { console.warn('Ошибка загрузки статистики склада:', error) }, }) // Новый запрос данных склада с партнерами const { data: partnerWarehouseData, loading: partnerWarehouseLoading, error: partnerWarehouseError, refetch: refetchPartnerWarehouse, } = useQuery(GET_WAREHOUSE_DATA, { pollInterval: 60000, // Реже обновляем данные партнеров errorPolicy: 'all', onError: (error) => { console.warn('Ошибка загрузки данных склада с партнерами:', error) }, }) // Запрос движений товаров (прибыло/убыло) const { data: supplyMovementsData, loading: supplyMovementsLoading, error: supplyMovementsError, refetch: refetchSupplyMovements, } = useQuery(GET_SUPPLY_MOVEMENTS, { variables: { period: '24h' }, pollInterval: 30000, // Обновляем каждые 30 секунд errorPolicy: 'all', onError: (error) => { console.warn('Ошибка загрузки движений товаров:', error) }, }) // Real-time обновления useRealtime(() => { refetchCounterparties() refetchOrders() refetchWarehouse() refetchSellerSupplies() refetchFulfillmentSupplies() refetchWarehouseStats() refetchSupplyMovements() }) // Общий статус загрузки const loading = counterpartiesLoading || ordersLoading || warehouseLoading || sellerSuppliesLoading || fulfillmentSuppliesLoading || warehouseStatsLoading || supplyMovementsLoading // === КРИТИЧЕСКАЯ БИЗНЕС-ЛОГИКА ОБРАБОТКИ ДАННЫХ === const formatNumber = (num: number): string => { if (num === 0) return '0' return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ' ') } // Функция для расчета статистики склада const warehouseStats: WarehouseStats = useMemo(() => { const stats = warehouseStatsData?.fulfillmentWarehouseStats const movements = supplyMovementsData?.supplyMovements if (stats) { return { products: { current: stats.products?.current || 0, change: stats.products?.change || 0, arrived: movements?.arrived?.products || 0, departed: movements?.departed?.products || 0, }, goods: { current: stats.goods?.current || 0, change: stats.goods?.change || 0, arrived: movements?.arrived?.goods || 0, departed: movements?.departed?.goods || 0, }, defects: { current: stats.defects?.current || 0, change: stats.defects?.change || 0, arrived: movements?.arrived?.defects || 0, departed: movements?.departed?.defects || 0, }, pvzReturns: { current: stats.pvzReturns?.current || 0, change: stats.pvzReturns?.change || 0, arrived: movements?.arrived?.pvzReturns || 0, departed: movements?.departed?.pvzReturns || 0, }, fulfillmentSupplies: { current: stats.fulfillmentSupplies?.current || 0, change: stats.fulfillmentSupplies?.change || 0, arrived: movements?.arrived?.fulfillmentSupplies || 0, departed: movements?.departed?.fulfillmentSupplies || 0, }, sellerSupplies: { current: stats.sellerSupplies?.current || 0, change: stats.sellerSupplies?.change || 0, arrived: movements?.arrived?.sellerSupplies || 0, departed: movements?.departed?.sellerSupplies || 0, }, } } // Fallback: считаем из загруженных данных const warehouseProducts = warehouseData?.warehouseProducts || [] const sellerSupplies = sellerSuppliesData?.sellerSuppliesOnWarehouse || [] const fulfillmentSupplies = fulfillmentSuppliesData?.myFulfillmentSupplies || [] return { products: { current: warehouseProducts.filter((p: any) => p.type === 'PRODUCT').length, change: 0, arrived: movements?.arrived?.products || 0, departed: movements?.departed?.products || 0, }, goods: { current: warehouseProducts.filter((p: any) => p.type === 'GOODS').length, change: 0, arrived: movements?.arrived?.goods || 0, departed: movements?.departed?.goods || 0, }, defects: { current: warehouseProducts.filter((p: any) => p.type === 'DEFECTS').length, change: 0, arrived: movements?.arrived?.defects || 0, departed: movements?.departed?.defects || 0, }, pvzReturns: { current: warehouseProducts.filter((p: any) => p.type === 'PVZ_RETURNS').length, change: 0, arrived: movements?.arrived?.pvzReturns || 0, departed: movements?.departed?.pvzReturns || 0, }, fulfillmentSupplies: { current: fulfillmentSupplies.length, change: 0, arrived: movements?.arrived?.fulfillmentSupplies || 0, departed: movements?.departed?.fulfillmentSupplies || 0, }, sellerSupplies: { current: sellerSupplies.length, change: 0, arrived: movements?.arrived?.sellerSupplies || 0, departed: movements?.departed?.sellerSupplies || 0, }, } }, [warehouseStatsData, warehouseData, sellerSuppliesData, fulfillmentSuppliesData, supplyMovementsData]) // === КРИТИЧЕСКАЯ ЛОГИКА ГРУППИРОВКИ ДАННЫХ ПО МАГАЗИНАМ === const storeData: StoreData[] = useMemo(() => { console.warn('🔄 Пересчитываем storeData...') // НОВАЯ ЛОГИКА: Используем данные из GET_WAREHOUSE_DATA если доступны const partnerStores = partnerWarehouseData?.warehouseData?.stores || [] const sellerCounterparties = counterpartiesData?.getMyCounterparties?.filter((c: any) => c.type === 'SELLER') || [] const warehouseProducts = warehouseData?.getWarehouseProducts || [] const sellerSupplies = sellerSuppliesData?.getSellerSuppliesOnWarehouse || [] console.warn('📊 Исходные данные для группировки:', { partnerStores: partnerStores.length, sellers: sellerCounterparties.length, warehouseProducts: warehouseProducts.length, sellerSupplies: sellerSupplies.length, }) // Если есть данные от нового API - используем их if (partnerStores.length > 0) { console.warn('✨ ИСПОЛЬЗУЕМ НОВУЮ ЛОГИКУ С ПАРТНЕРАМИ') return partnerStores.map((store: any) => ({ id: store.id, name: store.storeName, logo: store.storeImage, avatar: null, products: store.storeQuantity, goods: 0, defects: 0, sellerSupplies: 0, pvzReturns: 0, // Движения товаров (прибыло/убыло) - по умолчанию 0 productsArrived: 0, // TODO: считать из реальных поставок на фулфилмент productsDeparted: 0, // TODO: считать из реальных поставок на маркетплейсы goodsArrived: 0, goodsDeparted: 0, defectsArrived: 0, defectsDeparted: 0, sellerSuppliesArrived: 0, sellerSuppliesDeparted: 0, pvzReturnsArrived: 0, pvzReturnsDeparted: 0, items: store.products?.map((product: any) => ({ id: product.id, name: product.productName, article: '', productQuantity: product.productQuantity, productPlace: product.productPlace, goodsQuantity: 0, defectsQuantity: 0, sellerSuppliesQuantity: 0, pvzReturnsQuantity: 0, variants: product.variants?.map((variant: any) => ({ id: variant.id, name: variant.variantName, quantity: variant.variantQuantity, place: variant.variantPlace, })) || [], })) || [], })) } // Fallback: используем старую логику return sellerCounterparties.map((seller: any) => { const sellerId = seller.id const sellerName = seller.organization?.name || seller.name || 'Неизвестный селлер' // КРИТИЧНО: Группировка товаров/продуктов по названию с суммированием const sellerProducts = warehouseProducts.filter((p: any) => p.sellerId === sellerId) // Группируем по названию товара const productGroups = sellerProducts.reduce((acc: any, product: any) => { const key = product.name || 'Без названия' if (!acc[key]) { acc[key] = { id: `${sellerId}-${key}`, name: key, article: product.article || '', productQuantity: 0, goodsQuantity: 0, defectsQuantity: 0, sellerSuppliesQuantity: 0, pvzReturnsQuantity: 0, sellerSuppliesOwners: [], variants: [], } } // Суммируем количества acc[key].productQuantity += product.productQuantity || 0 acc[key].goodsQuantity += product.goodsQuantity || 0 acc[key].defectsQuantity += product.defectsQuantity || 0 acc[key].pvzReturnsQuantity += product.pvzReturnsQuantity || 0 return acc }, {}) // КРИТИЧНО: Группировка расходников селлера по ВЛАДЕЛЬЦУ (не по названию!) const sellerSuppliesForThisSeller = sellerSupplies.filter((supply: any) => supply.type === 'SELLER_CONSUMABLES' && supply.sellerId === sellerId, ) console.warn(`📦 Расходники для селлера ${sellerName}:`, sellerSuppliesForThisSeller.length) // Группируем расходники по владельцу const suppliesGroups = sellerSuppliesForThisSeller.reduce((acc: any, supply: any) => { const ownerKey = supply.ownerName || supply.sellerName || 'Неизвестный владелец' if (!acc[ownerKey]) { acc[ownerKey] = { id: `${sellerId}-supply-${ownerKey}`, name: `Расходники ${ownerKey}`, article: '', productQuantity: 0, goodsQuantity: 0, defectsQuantity: 0, sellerSuppliesQuantity: 0, pvzReturnsQuantity: 0, sellerSuppliesOwners: [ownerKey], variants: [], } } acc[ownerKey].sellerSuppliesQuantity += supply.quantity || 0 return acc }, {}) const allItems = [...Object.values(productGroups), ...Object.values(suppliesGroups)] as ProductItem[] // Подсчет итогов для магазина const totals = allItems.reduce( (acc, item) => ({ products: acc.products + (item.productQuantity || 0), goods: acc.goods + (item.goodsQuantity || 0), defects: acc.defects + (item.defectsQuantity || 0), sellerSupplies: acc.sellerSupplies + (item.sellerSuppliesQuantity || 0), pvzReturns: acc.pvzReturns + (item.pvzReturnsQuantity || 0), }), { products: 0, goods: 0, defects: 0, sellerSupplies: 0, pvzReturns: 0 }, ) console.warn(`📊 Итоги для ${sellerName}:`, totals) return { id: sellerId, name: sellerName, logo: seller.organization?.logo, avatar: seller.organization?.user?.avatar, products: totals.products, goods: totals.goods, defects: totals.defects, sellerSupplies: totals.sellerSupplies, pvzReturns: totals.pvzReturns, // Движения товаров (прибыло/убыло) - по умолчанию 0 productsArrived: 0, // TODO: считать из реальных поставок на фулфилмент productsDeparted: 0, // TODO: считать из реальных поставок на маркетплейсы goodsArrived: 0, goodsDeparted: 0, defectsArrived: 0, defectsDeparted: 0, sellerSuppliesArrived: 0, sellerSuppliesDeparted: 0, pvzReturnsArrived: 0, pvzReturnsDeparted: 0, items: allItems, } }) }, [partnerWarehouseData, counterpartiesData, warehouseData, sellerSuppliesData]) // Фильтрация и сортировка данных const filteredAndSortedStores = useMemo(() => { let filtered = storeData if (searchTerm) { filtered = filtered.filter((store) => store.name.toLowerCase().includes(searchTerm.toLowerCase()), ) } return filtered.sort((a, b) => { const aValue = a[sortField] const bValue = b[sortField] if (typeof aValue === 'string' && typeof bValue === 'string') { return sortOrder === 'asc' ? aValue.localeCompare(bValue) : bValue.localeCompare(aValue) } if (typeof aValue === 'number' && typeof bValue === 'number') { return sortOrder === 'asc' ? aValue - bValue : bValue - aValue } return 0 }) }, [storeData, searchTerm, sortField, sortOrder]) // Подсчет общих итогов const totals = useMemo(() => { return filteredAndSortedStores.reduce( (acc, store) => ({ products: acc.products + store.products, goods: acc.goods + store.goods, defects: acc.defects + store.defects, sellerSupplies: acc.sellerSupplies + store.sellerSupplies, pvzReturns: acc.pvzReturns + store.pvzReturns, }), { products: 0, goods: 0, defects: 0, sellerSupplies: 0, pvzReturns: 0 }, ) }, [filteredAndSortedStores]) // Вспомогательные функции для UI const handleSort = (field: keyof StoreData) => { if (sortField === field) { setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc') } else { setSortField(field) setSortOrder('asc') } } // Функции управления 3-уровневой иерархией const toggleStoreExpansion = (storeId: string) => { setExpandedStores(prev => { const newSet = new Set(prev) if (newSet.has(storeId)) { newSet.delete(storeId) } else { newSet.add(storeId) } return newSet }) } const toggleItemExpansion = (itemId: string) => { setExpandedItems(prev => { const newSet = new Set(prev) if (newSet.has(itemId)) { newSet.delete(itemId) } else { newSet.add(itemId) } return newSet }) } // Компонент заголовка таблицы с сортировкой const TableHeader = ({ field, children, sortable = false, }: { field?: keyof StoreData children: React.ReactNode sortable?: boolean }) => (
handleSort(field) : undefined} > {children} {sortable && field === sortField && ( )}
) // === ОБРАБОТКА СОСТОЯНИЙ === if (loading && storeData.length === 0) { return (
Загрузка данных склада...
) } // === РЕНДЕР ИНТЕРФЕЙСА === return (
{/* Статистические карты склада */}

Статистика склада

{/* Блок статистических карт */}
{/* ЭТАП 1: Добавлены прибыло/убыло в карточки */} {/* ЭТАП 3: Добавлен индикатор загрузки */} router.push('/fulfillment-warehouse/supplies')} showMovements={true} arrived={warehouseStats.fulfillmentSupplies.arrived} departed={warehouseStats.fulfillmentSupplies.departed} isLoading={loading} /> {/* ОТКАТ ВСЕХ ЭТАПОВ: Вернуться к исходным карточкам */} {/* router.push('/fulfillment-warehouse/supplies')} /> */}
{/* Таблица данных */}

Детализация по магазинам

setSearchTerm(e.target.value)} className="pl-8 h-8 text-xs bg-white/10 border-white/20 text-white placeholder-white/40" />
{/* УРОВЕНЬ 1: Заголовки таблицы магазинов */}
№ / Магазин Продукты Товары Брак Расходники селлеров Возвраты с ПВЗ
{/* Строка итогов */}
 
 
{/* Пустое место под стрелку */}
 
{/* Пустое место под аватар */} ИТОГО ({filteredAndSortedStores.length})
{formatNumber(totals.products)} +{warehouseStats.products.arrived} | -{warehouseStats.products.departed}
{formatNumber(totals.goods)} +{warehouseStats.goods.arrived} | -{warehouseStats.goods.departed}
{formatNumber(totals.defects)} +{warehouseStats.defects.arrived} | -{warehouseStats.defects.departed}
{formatNumber(totals.sellerSupplies)} +{warehouseStats.sellerSupplies.arrived} | -{warehouseStats.sellerSupplies.departed}
{formatNumber(totals.pvzReturns)} +{warehouseStats.pvzReturns.arrived} | -{warehouseStats.pvzReturns.departed}
{/* ОСНОВНЫЕ ДАННЫЕ: 3-уровневая иерархия */}
{filteredAndSortedStores.map((store, index) => (
{/* 🔵 УРОВЕНЬ 1: Основная строка магазина */}
toggleStoreExpansion(store.id)} >
{filteredAndSortedStores.length - index}
{expandedStores.has(store.id) ? ( ) : ( )} {store.avatar && } {store.name.slice(0, 2)} {store.name}
{formatNumber(store.products)} +{store.productsArrived || 0} | -{store.productsDeparted || 0}
{formatNumber(store.goods)} +{store.goodsArrived || 0} | -{store.goodsDeparted || 0}
{formatNumber(store.defects)} +{store.defectsArrived || 0} | -{store.defectsDeparted || 0}
{formatNumber(store.sellerSupplies)} +{store.sellerSuppliesArrived || 0} | -{store.sellerSuppliesDeparted || 0}
{formatNumber(store.pvzReturns)} +{store.pvzReturnsArrived || 0} | -{store.pvzReturnsDeparted || 0}
{/* 🟢 УРОВЕНЬ 2: Развернутые товары */} {expandedStores.has(store.id) && (
{/* Заголовки второго уровня */}
Наименование
Кол-во
Место
Кол-во
Место
Кол-во
Место
Кол-во
Место
Кол-во
Место
{/* Данные товаров */}
{store.items?.map((item) => (
{/* Основная строка товара */}
toggleItemExpansion(item.id)} >
{/* Наименование */}
{expandedItems.has(item.id) ? ( ) : ( )}
{item.name} {item.variants && item.variants.length > 0 && ( {item.variants.length} вар. )}
{item.article && (
Артикул: {item.article}
)}
{/* Продукты */}
{formatNumber(item.productQuantity)}
{item.productPlace || '-'}
{/* Товары */}
{formatNumber(item.goodsQuantity)}
{item.goodsPlace || '-'}
{/* Брак */}
{formatNumber(item.defectsQuantity)}
{item.defectsPlace || '-'}
{/* Расходники селлера */}
{formatNumber(item.sellerSuppliesQuantity)}
Расходники селлеров:
{item.sellerSuppliesOwners && item.sellerSuppliesOwners.length > 0 ? ( item.sellerSuppliesOwners.map((owner, index) => (
• {owner}
)) ) : (
Нет данных о владельцах
)}
{item.sellerSuppliesPlace || '-'}
{/* Возвраты с ПВЗ */}
{formatNumber(item.pvzReturnsQuantity)}
{item.pvzReturnsPlace || '-'}
{/* 🟠 УРОВЕНЬ 3: Варианты товара */} {expandedItems.has(item.id) && item.variants && item.variants.length > 0 && (
{/* Заголовки для вариантов */}
Вариант
Кол-во
Место
Кол-во
Место
Кол-во
Место
Кол-во
Место
Кол-во
Место
{/* Данные по вариантам */}
{item.variants.map((variant) => (
{/* Название варианта */}
{variant.name}
{/* Продукты */}
{formatNumber(variant.productQuantity)}
{variant.productPlace || '-'}
{/* Товары */}
{formatNumber(variant.goodsQuantity)}
{variant.goodsPlace || '-'}
{/* Брак */}
{formatNumber(variant.defectsQuantity)}
{variant.defectsPlace || '-'}
{/* Расходники селлера */}
{formatNumber(variant.sellerSuppliesQuantity)}
Расходники селлеров:
{variant.sellerSuppliesOwners && variant.sellerSuppliesOwners.length > 0 ? ( variant.sellerSuppliesOwners.map((owner, index) => (
• {owner}
)) ) : (
Нет данных о владельцах
)}
{variant.sellerSuppliesPlace || '-'}
{/* Возвраты с ПВЗ */}
{formatNumber(variant.pvzReturnsQuantity)}
{variant.pvzReturnsPlace || '-'}
))}
)}
))}
)}
))}
{/* Блок возвратов WB */} {showReturnClaims && (
setShowReturnClaims(false)} />
)} {/* Информация об отсутствии результатов */} {filteredAndSortedStores.length === 0 && searchTerm && (

По запросу "{searchTerm}" ничего не найдено.{' '}

)} {/* Отладочная информация */} {process.env.NODE_ENV === 'development' && (

🔧 Debug Info:

• Загрузка: {loading ? 'да' : 'нет'}

• Всего магазинов: {storeData.length}

• Отфильтровано: {filteredAndSortedStores.length}

• Поиск: {searchTerm || 'нет'}

• Сортировка: {sortField} ({sortOrder})

)}
) }