Исправлено отображение данных WB складов и структура интерфейса

- Исправлен парсинг данных WB API: данные теперь извлекаются из analytics.data.regions вместо analytics.regions
- Обновлена логика сохранения аналитических данных в кеш (analyticsResults вместо пустого analyticsData)
- Улучшена обработка кеша: добавлены детальные логи и проверки валидности данных
- Переработана структура интерфейса WildberriesWarehouseTab: убран дебаг блок с JSON, исправлены проблемы с высотой и скроллингом
- Установлена максимальная высота таблицы (60vh) с вертикальным скроллом
- Упрощена flex-структура компонентов для корректного отображения

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Bivekich
2025-08-07 00:00:55 +03:00
parent c2b342a527
commit d2b4164d7d
2 changed files with 82 additions and 65 deletions

View File

@ -78,7 +78,8 @@ export function WBWarehouseDashboard() {
refetch: refetchCache, refetch: refetchCache,
} = useQuery(GET_WB_WAREHOUSE_DATA, { } = useQuery(GET_WB_WAREHOUSE_DATA, {
skip: !hasWBApiKey, skip: !hasWBApiKey,
fetchPolicy: 'cache-and-network', fetchPolicy: 'no-cache', // Всегда получаем свежие данные с сервера
errorPolicy: 'all',
}) })
const [saveCache] = useMutation(SAVE_WB_WAREHOUSE_CACHE) const [saveCache] = useMutation(SAVE_WB_WAREHOUSE_CACHE)
@ -112,18 +113,23 @@ export function WBWarehouseDashboard() {
// Получаем аналитические данные для данного nmId // Получаем аналитические данные для данного nmId
const analytics = analyticsMap.get(card.nmID) const analytics = analyticsMap.get(card.nmID)
if (analytics && Array.isArray(analytics)) { const regionsData = analytics?.data?.regions || analytics?.regions
analytics.forEach((item: any) => {
if (item.stocks && Array.isArray(item.stocks)) { if (analytics && regionsData && Array.isArray(regionsData)) {
item.stocks.forEach((stockItem: any) => { // Парсим новый формат данных: regions -> offices -> metrics
stock.stocks.push({ regionsData.forEach((region: any) => {
warehouseId: stockItem.warehouseId || 0, if (region.offices && Array.isArray(region.offices)) {
warehouseName: String(stockItem.warehouseName || 'Неизвестный склад'), region.offices.forEach((office: any) => {
quantity: Number(stockItem.quantity) || 0, if (office.metrics) {
quantityFull: Number(stockItem.quantityFull) || 0, stock.stocks.push({
inWayToClient: Number(stockItem.inWayToClient) || 0, warehouseId: office.officeID || 0,
inWayFromClient: Number(stockItem.inWayFromClient) || 0, warehouseName: String(office.officeName || 'Неизвестный склад'),
}) quantity: Number(office.metrics.stockCount) || 0,
quantityFull: Number(office.metrics.stockCount) || 0,
inWayToClient: Number(office.metrics.toClientCount) || 0,
inWayFromClient: Number(office.metrics.fromClientCount) || 0,
})
}
}) })
} }
}) })
@ -182,20 +188,34 @@ export function WBWarehouseDashboard() {
// Загрузка данных из кеша // Загрузка данных из кеша
const loadWarehouseDataFromCache = (cacheData: any) => { const loadWarehouseDataFromCache = (cacheData: any) => {
try { try {
const parsedData = typeof cacheData.data === 'string' ? JSON.parse(cacheData.data) : cacheData.data console.warn('WB Warehouse: Loading from cache, cacheData:', {
hasData: !!cacheData?.data,
totalProducts: cacheData?.totalProducts,
totalStocks: cacheData?.totalStocks,
dataType: typeof cacheData?.data,
})
const cachedStocks = parsedData.stocks || [] const parsedData = typeof cacheData.data === 'string' ? JSON.parse(cacheData.data) : cacheData.data
const cachedWarehouses = parsedData.warehouses || []
const cachedAnalytics = parsedData.analyticsData || [] console.warn('WB Warehouse: Parsed cache data:', {
hasStocks: !!parsedData?.stocks,
stocksLength: parsedData?.stocks?.length || 0,
hasWarehouses: !!parsedData?.warehouses,
warehousesLength: parsedData?.warehouses?.length || 0,
})
const cachedStocks = parsedData?.stocks || []
const cachedWarehouses = parsedData?.warehouses || []
const cachedAnalytics = parsedData?.analyticsData || []
setStocks(cachedStocks) setStocks(cachedStocks)
setWarehouses(cachedWarehouses) setWarehouses(cachedWarehouses)
setAnalyticsData(cachedAnalytics) setAnalyticsData(cachedAnalytics)
// Обновляем статистику из кеша // Обновляем статистику из кеша
setTotalProducts(cacheData.totalProducts) setTotalProducts(cacheData.totalProducts || 0)
setTotalStocks(cacheData.totalStocks) setTotalStocks(cacheData.totalStocks || 0)
setTotalReserved(cacheData.totalReserved) setTotalReserved(cacheData.totalReserved || 0)
const totalFromClientCount = (cachedStocks || []).reduce( const totalFromClientCount = (cachedStocks || []).reduce(
(sum: number, item: WBStock) => (sum: number, item: WBStock) =>
@ -210,9 +230,17 @@ export function WBWarehouseDashboard() {
setActiveWarehouses(warehousesWithStock.size) setActiveWarehouses(warehousesWithStock.size)
console.warn('WB Warehouse: Data loaded from cache:', cachedStocks?.length || 0, 'items') console.warn('WB Warehouse: Data loaded from cache:', cachedStocks?.length || 0, 'items')
toast.success(`Загружено из кеша: ${cachedStocks?.length || 0} товаров`)
if (cachedStocks?.length > 0) {
toast.success(`Загружено из кеша: ${cachedStocks.length} товаров`)
} else {
console.warn('WB Warehouse: Cache is empty, will try to load from API')
toast.info('Кеш пуст, загружаем данные из API...')
loadWarehouseDataFromAPI()
return
}
} catch (error) { } catch (error) {
console.error('WB Warehouse: Error parsing cache data:', error) console.error('WB Warehouse: Error parsing cache data:', error, 'cacheData:', cacheData)
toast.error('Ошибка загрузки данных из кеша') toast.error('Ошибка загрузки данных из кеша')
// Если кеш поврежден, загружаем из API // Если кеш поврежден, загружаем из API
loadWarehouseDataFromAPI() loadWarehouseDataFromAPI()
@ -300,7 +328,7 @@ export function WBWarehouseDashboard() {
data: JSON.stringify({ data: JSON.stringify({
stocks: combinedStocks, stocks: combinedStocks,
warehouses: extractedWarehouses, warehouses: extractedWarehouses,
analyticsData: analyticsData, analyticsData: analyticsResults,
}), }),
totalProducts: stats.totalProducts, totalProducts: stats.totalProducts,
totalStocks: stats.totalStocks, totalStocks: stats.totalStocks,
@ -335,17 +363,30 @@ export function WBWarehouseDashboard() {
return return
} }
// Сначала пытаемся получить данные из кеша // Получаем данные (либо из кеша, либо загружаем из API если кеш устарел)
try { try {
console.warn('WB Warehouse: Starting data load, hasWBApiKey:', hasWBApiKey)
const result = await refetchCache() const result = await refetchCache()
const cacheResponse = result.data?.getWBWarehouseData const cacheResponse = result.data?.getWBWarehouseData
console.warn('WB Warehouse: Cache response:', {
success: cacheResponse?.success,
fromCache: cacheResponse?.fromCache,
hasCache: !!cacheResponse?.cache,
message: cacheResponse?.message,
})
if (cacheResponse?.success && cacheResponse?.fromCache && cacheResponse?.cache) { if (cacheResponse?.success && cacheResponse?.fromCache === true && cacheResponse?.cache) {
// Данные найдены в кеше // Данные найдены в актуальном кеше
console.warn('WB Warehouse: Using fresh cache data')
loadWarehouseDataFromCache(cacheResponse.cache) loadWarehouseDataFromCache(cacheResponse.cache)
} else if (cacheResponse?.success && cacheResponse?.fromCache === false) {
// Кеш устарел или отсутствует, загружаем из API
console.warn('WB Warehouse: Cache expired/missing, loading from API:', cacheResponse.message)
await loadWarehouseDataFromAPI()
} else { } else {
// Кеша нет или он устарел, загружаем из API // Ошибка при получении кеша
console.warn('WB Warehouse: No cache found, loading from API') console.warn('WB Warehouse: Cache check failed, loading from API. Response:', cacheResponse)
await loadWarehouseDataFromAPI() await loadWarehouseDataFromAPI()
} }
} catch (error) { } catch (error) {
@ -390,7 +431,7 @@ export function WBWarehouseDashboard() {
</TabsTrigger> </TabsTrigger>
</TabsList> </TabsList>
<div className="flex-1 overflow-hidden"> <div className="flex-1 min-h-0">
<TabsContent value="fulfillment" className="h-full mt-0"> <TabsContent value="fulfillment" className="h-full mt-0">
<FulfillmentWarehouseTab /> <FulfillmentWarehouseTab />
</TabsContent> </TabsContent>

View File

@ -97,7 +97,7 @@ export function WildberriesWarehouseTab({
} }
return ( return (
<div className="h-full flex flex-col"> <div className="flex flex-col space-y-6">
{/* Статистика */} {/* Статистика */}
<StatsCards <StatsCards
totalProducts={totalProducts} totalProducts={totalProducts}
@ -108,31 +108,9 @@ export function WildberriesWarehouseTab({
loading={!initialized || loading || cacheLoading} loading={!initialized || loading || cacheLoading}
/> />
{/* Аналитика по складам WB */}
{initialized && analyticsData.length > 0 && (
<Card className="glass-card border-white/10 p-4 mb-6">
<h3 className="text-lg font-semibold text-white mb-4 flex items-center">
<TrendingUp className="h-5 w-5 mr-2 text-blue-400" />
Аналитика по складам WB
</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{analyticsData.slice(0, 6).map((item, index) => (
<div key={index} className="bg-white/5 rounded-lg p-3">
<div className="text-sm text-white/60">Склад {index + 1}</div>
<div className="text-lg font-medium text-white">
{JSON.stringify(item).length > 50
? `${JSON.stringify(item).substring(0, 50)}...`
: JSON.stringify(item)}
</div>
</div>
))}
</div>
</Card>
)}
{/* Основной контент */} {/* Основной контент */}
<Card className="glass-card border-white/10 flex-1 flex flex-col overflow-hidden"> <Card className="glass-card border-white/10">
<div className="p-6 border-b border-white/10 flex-shrink-0"> <div className="p-6 border-b border-white/10">
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4"> <div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
<div> <div>
<h2 className="text-xl font-semibold text-white flex items-center"> <h2 className="text-xl font-semibold text-white flex items-center">
@ -155,13 +133,13 @@ export function WildberriesWarehouseTab({
</div> </div>
{/* Контент с таблицей */} {/* Контент с таблицей */}
<div className="flex-1 overflow-hidden"> <div className="max-h-[60vh] overflow-y-auto">
{!initialized || loading || cacheLoading ? ( {!initialized || loading || cacheLoading ? (
<div className="p-6"> <div className="p-6">
<LoadingSkeleton /> <LoadingSkeleton />
</div> </div>
) : filteredStocks.length === 0 ? ( ) : filteredStocks.length === 0 ? (
<div className="flex items-center justify-center h-full"> <div className="flex items-center justify-center h-64">
<div className="text-center"> <div className="text-center">
<Package className="h-12 w-12 text-white/20 mx-auto mb-4" /> <Package className="h-12 w-12 text-white/20 mx-auto mb-4" />
<p className="text-white/60">{searchTerm ? 'Товары не найдены' : 'Нет данных о товарах'}</p> <p className="text-white/60">{searchTerm ? 'Товары не найдены' : 'Нет данных о товарах'}</p>
@ -173,16 +151,14 @@ export function WildberriesWarehouseTab({
</div> </div>
</div> </div>
) : ( ) : (
<div className="h-full overflow-auto"> <div className="p-6 space-y-3">
<div className="p-6 space-y-3"> {/* Заголовок таблицы */}
{/* Заголовок таблицы */} <TableHeader />
<TableHeader />
{/* Строки товаров */} {/* Строки товаров */}
{filteredStocks.map((item) => ( {filteredStocks.map((item) => (
<StockTableRow key={item.nmId} item={item} /> <StockTableRow key={item.nmId} item={item} />
))} ))}
</div>
</div> </div>
)} )}
</div> </div>