"use client"; import React, { useState, useEffect } from "react"; import { useAuth } from "@/hooks/useAuth"; import { Sidebar } from "@/components/dashboard/sidebar"; import { useSidebar } from "@/hooks/useSidebar"; import { Card } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { WildberriesService } from "@/services/wildberries-service"; import { toast } from "sonner"; import { StatsCards } from "./stats-cards"; import { SearchBar } from "./search-bar"; import { TableHeader } from "./table-header"; import { LoadingSkeleton } from "./loading-skeleton"; import { StockTableRow } from "./stock-table-row"; import { TrendingUp, Package } from "lucide-react"; interface WBStock { nmId: number; vendorCode: string; title: string; brand: string; price: number; stocks: Array<{ warehouseId: number; warehouseName: string; quantity: number; quantityFull: number; inWayToClient: number; inWayFromClient: number; }>; totalQuantity: number; totalReserved: number; photos?: Array<{ big?: string; c246x328?: string; c516x688?: string; square?: string; tm?: string; }>; mediaFiles?: string[]; characteristics?: Array<{ name: string; value: string | string[]; }>; subjectName: string; description: string; } interface WBWarehouse { id: number; name: string; cargoType: number; deliveryType: number; } export function WBWarehouseDashboard() { const { user } = useAuth(); const { isCollapsed, getSidebarMargin } = useSidebar(); const [stocks, setStocks] = useState([]); const [warehouses, setWarehouses] = useState([]); const [loading, setLoading] = useState(false); const [searchTerm, setSearchTerm] = useState(""); // Статистика const [totalProducts, setTotalProducts] = useState(0); const [totalStocks, setTotalStocks] = useState(0); const [totalReserved, setTotalReserved] = useState(0); const [totalFromClient, setTotalFromClient] = useState(0); const [activeWarehouses, setActiveWarehouses] = useState(0); // Analytics data const [analyticsData, setAnalyticsData] = useState([]); const hasWBApiKey = user?.wildberriesApiKey; // Комбинирование карточек с индивидуальными данными аналитики const combineCardsWithAnalytics = ( cards: unknown[], analyticsResults: unknown[] ) => { const stocksMap = new Map(); // Создаем карту аналитических данных для быстрого поиска const analyticsMap = new Map(); // Map nmId to its analytics data analyticsResults.forEach((result) => { analyticsMap.set(result.nmId, result.data); }); cards.forEach((card) => { const stock: WBStock = { nmId: card.nmID, vendorCode: String(card.vendorCode || card.supplierVendorCode || ""), title: String(card.title || card.object || `Товар ${card.nmID}`), brand: String(card.brand || ""), price: 0, stocks: [], totalQuantity: 0, totalReserved: 0, photos: Array.isArray(card.photos) ? card.photos : [], mediaFiles: Array.isArray(card.mediaFiles) ? card.mediaFiles : [], characteristics: Array.isArray(card.characteristics) ? card.characteristics : [], subjectName: String(card.subjectName || card.object || ""), description: String(card.description || ""), }; if (card.sizes && card.sizes.length > 0) { stock.price = Number(card.sizes[0].price || card.sizes[0].discountedPrice) || 0; } const analyticsData = analyticsMap.get(card.nmID); if (analyticsData?.data?.regions) { ( analyticsData.data.regions as { offices: { officeID: number; officeName: string; metrics: { stockCount: number }; }[]; }[] ).forEach((region) => { if (region.offices && region.offices.length > 0) { region.offices.forEach((office) => { stock.stocks.push({ warehouseId: office.officeID, warehouseName: office.officeName, quantity: office.metrics?.stockCount || 0, quantityFull: office.metrics?.stockCount || 0, inWayToClient: office.metrics?.toClientCount || 0, inWayFromClient: office.metrics?.fromClientCount || 0, }); stock.totalQuantity += office.metrics?.stockCount || 0; stock.totalReserved += office.metrics?.toClientCount || 0; }); } }); } stocksMap.set(card.nmID, stock); }); return Array.from(stocksMap.values()).sort( (a, b) => b.totalQuantity - a.totalQuantity ); }; // Извлечение информации о складах из данных const extractWarehousesFromStocks = ( stocksData: WBStock[] ): WBWarehouse[] => { const warehousesMap = new Map(); stocksData.forEach((stock) => { stock.stocks.forEach((stockInfo) => { if (!warehousesMap.has(stockInfo.warehouseId)) { warehousesMap.set(stockInfo.warehouseId, { id: stockInfo.warehouseId, name: stockInfo.warehouseName, cargoType: 1, deliveryType: 1, }); } }); }); return Array.from(warehousesMap.values()); }; // Обновление статистики const updateStatistics = ( stocksData: WBStock[], warehousesData: WBWarehouse[] ) => { setTotalProducts(stocksData.length); setTotalStocks( stocksData.reduce((sum, item) => sum + item.totalQuantity, 0) ); setTotalReserved( stocksData.reduce((sum, item) => sum + item.totalReserved, 0) ); const totalFromClientCount = stocksData.reduce( (sum, item) => sum + item.stocks.reduce( (stockSum, stock) => stockSum + stock.inWayFromClient, 0 ), 0 ); setTotalFromClient(totalFromClientCount); const warehousesWithStock = new Set( stocksData.flatMap((item) => item.stocks.map((s) => s.warehouseId)) ); setActiveWarehouses(warehousesWithStock.size); }; // Загрузка данных склада const loadWarehouseData = async () => { if (!user?.wildberriesApiKey) return; setLoading(true); try { const apiToken = user.wildberriesApiKey; const wbService = new WildberriesService(apiToken); // 1. Получаем карточки товаров const cards = await WildberriesService.getAllCards(apiToken).catch( () => [] ); console.log("WB Warehouse: Loaded cards:", cards.length); if (cards.length === 0) { toast.error("Нет карточек товаров в WB"); return; } const nmIds = cards.map((card) => card.nmID).filter((id) => id > 0); console.log("WB Warehouse: NM IDs to process:", nmIds.length); // 2. Получаем аналитику для каждого товара индивидуально const analyticsResults = []; for (const nmId of nmIds) { try { console.log(`WB Warehouse: Fetching analytics for nmId ${nmId}`); const result = await wbService.getStocksReportByOffices({ nmIDs: [nmId], stockType: "", }); analyticsResults.push({ nmId, data: result }); await new Promise((resolve) => setTimeout(resolve, 1000)); } catch (error) { console.error( `WB Warehouse: Error fetching analytics for nmId ${nmId}:`, error ); } } console.log("WB Warehouse: Analytics results:", analyticsResults.length); // 3. Комбинируем данные const combinedStocks = combineCardsWithAnalytics(cards, analyticsResults); console.log("WB Warehouse: Combined stocks:", combinedStocks.length); // 4. Извлекаем склады и обновляем статистику const extractedWarehouses = extractWarehousesFromStocks(combinedStocks); setStocks(combinedStocks); setWarehouses(extractedWarehouses); updateStatistics(combinedStocks, extractedWarehouses); toast.success(`Загружено товаров: ${combinedStocks.length}`); } catch (error: unknown) { console.error("Error loading warehouse data:", error); toast.error("Ошибка при загрузке данных склада"); } finally { setLoading(false); } }; useEffect(() => { if (hasWBApiKey) { loadWarehouseData(); } }, [hasWBApiKey]); // Фильтрация товаров const filteredStocks = stocks.filter((item) => { if (!searchTerm) return true; const search = searchTerm.toLowerCase(); return ( item.title.toLowerCase().includes(search) || String(item.nmId).includes(search) || item.brand.toLowerCase().includes(search) || item.vendorCode.toLowerCase().includes(search) ); }); return (
{/* Результирующие вкладки */} {/* Аналитика по складам WB */} {analyticsData.length > 0 && (

Движение товаров по складам WB

{analyticsData.map((warehouse) => (
{warehouse.warehouseName}
К клиенту: {warehouse.toClient}
От клиента: {warehouse.fromClient}
))}
)} {/* Поиск */} {/* Список товаров */}
{loading ? (
) : !hasWBApiKey ? (

Настройте API Wildberries

Для просмотра остатков добавьте API ключ Wildberries в настройках

) : filteredStocks.length === 0 ? (

Товары не найдены

Попробуйте изменить параметры поиска

) : (
{/* Таблица товаров */}
{filteredStocks.map((item, index) => ( ))}
)}
); }