Исправлено отображение данных 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:
@ -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
|
||||||
|
regionsData.forEach((region: any) => {
|
||||||
|
if (region.offices && Array.isArray(region.offices)) {
|
||||||
|
region.offices.forEach((office: any) => {
|
||||||
|
if (office.metrics) {
|
||||||
stock.stocks.push({
|
stock.stocks.push({
|
||||||
warehouseId: stockItem.warehouseId || 0,
|
warehouseId: office.officeID || 0,
|
||||||
warehouseName: String(stockItem.warehouseName || 'Неизвестный склад'),
|
warehouseName: String(office.officeName || 'Неизвестный склад'),
|
||||||
quantity: Number(stockItem.quantity) || 0,
|
quantity: Number(office.metrics.stockCount) || 0,
|
||||||
quantityFull: Number(stockItem.quantityFull) || 0,
|
quantityFull: Number(office.metrics.stockCount) || 0,
|
||||||
inWayToClient: Number(stockItem.inWayToClient) || 0,
|
inWayToClient: Number(office.metrics.toClientCount) || 0,
|
||||||
inWayFromClient: Number(stockItem.inWayFromClient) || 0,
|
inWayFromClient: Number(office.metrics.fromClientCount) || 0,
|
||||||
})
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -182,20 +188,34 @@ export function WBWarehouseDashboard() {
|
|||||||
// Загрузка данных из кеша
|
// Загрузка данных из кеша
|
||||||
const loadWarehouseDataFromCache = (cacheData: any) => {
|
const loadWarehouseDataFromCache = (cacheData: any) => {
|
||||||
try {
|
try {
|
||||||
|
console.warn('WB Warehouse: Loading from cache, cacheData:', {
|
||||||
|
hasData: !!cacheData?.data,
|
||||||
|
totalProducts: cacheData?.totalProducts,
|
||||||
|
totalStocks: cacheData?.totalStocks,
|
||||||
|
dataType: typeof cacheData?.data,
|
||||||
|
})
|
||||||
|
|
||||||
const parsedData = typeof cacheData.data === 'string' ? JSON.parse(cacheData.data) : cacheData.data
|
const parsedData = typeof cacheData.data === 'string' ? JSON.parse(cacheData.data) : cacheData.data
|
||||||
|
|
||||||
const cachedStocks = parsedData.stocks || []
|
console.warn('WB Warehouse: Parsed cache data:', {
|
||||||
const cachedWarehouses = parsedData.warehouses || []
|
hasStocks: !!parsedData?.stocks,
|
||||||
const cachedAnalytics = parsedData.analyticsData || []
|
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
|
||||||
|
|
||||||
if (cacheResponse?.success && cacheResponse?.fromCache && cacheResponse?.cache) {
|
console.warn('WB Warehouse: Cache response:', {
|
||||||
// Данные найдены в кеше
|
success: cacheResponse?.success,
|
||||||
|
fromCache: cacheResponse?.fromCache,
|
||||||
|
hasCache: !!cacheResponse?.cache,
|
||||||
|
message: cacheResponse?.message,
|
||||||
|
})
|
||||||
|
|
||||||
|
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>
|
||||||
|
@ -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,7 +151,6 @@ 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 />
|
||||||
@ -183,7 +160,6 @@ export function WildberriesWarehouseTab({
|
|||||||
<StockTableRow key={item.nmId} item={item} />
|
<StockTableRow key={item.nmId} item={item} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
Reference in New Issue
Block a user