Files
sfera-new/src/components/fulfillment-warehouse/fulfillment-warehouse-dashboard.tsx

567 lines
28 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client"
import { useState, useEffect } from 'react'
import { Card } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
import { Sidebar } from '@/components/dashboard/sidebar'
import { useSidebar } from '@/hooks/useSidebar'
import { StatsCard } from '@/components/supplies/ui/stats-card'
import { StatsGrid } from '@/components/supplies/ui/stats-grid'
import {
Package,
TrendingUp,
AlertTriangle,
RotateCcw,
Wrench,
Users,
ShoppingBag,
ChevronDown,
ChevronUp,
Box,
Zap,
Target,
Activity,
BarChart3,
Eye,
EyeOff,
Warehouse
} from 'lucide-react'
export function FulfillmentWarehouseDashboard() {
const { getSidebarMargin } = useSidebar()
// Состояния для свёртывания блоков
const [expandedSections, setExpandedSections] = useState({
warehouse: true
})
// Состояние для живых изменений продуктов
const [liveChange, setLiveChange] = useState({
value: 12,
isPositive: true,
timestamp: Date.now()
})
// Состояние для модуля товары с дополнительными значениями
const [goodsData, setGoodsData] = useState({
processing: 245, // В обработке (положительное)
rejected: 18, // Отклонено (отрицательное)
efficiency: 87, // Эффективность обработки
isActive: true, // Активность процесса
pulse: 0 // Для анимации пульса
})
// Симуляция живых изменений для продуктов
useEffect(() => {
const interval = setInterval(() => {
const change = Math.floor(Math.random() * 20) - 10 // от -10 до +10
setLiveChange({
value: Math.abs(change),
isPositive: change >= 0,
timestamp: Date.now()
})
}, 3000) // каждые 3 секунды
return () => clearInterval(interval)
}, [])
// Симуляция изменений для товаров
useEffect(() => {
const interval = setInterval(() => {
setGoodsData(prev => ({
...prev,
processing: prev.processing + Math.floor(Math.random() * 10) - 5,
rejected: Math.max(0, prev.rejected + Math.floor(Math.random() * 6) - 3),
efficiency: Math.min(100, Math.max(70, prev.efficiency + Math.floor(Math.random() * 6) - 3)),
pulse: prev.pulse + 1
}))
}, 2500) // каждые 2.5 секунды
return () => clearInterval(interval)
}, [])
// Мок данные для статистики склада фулфилмента
const warehouseStats = {
// Текущие данные
currentProducts: 856, // Готовые продукты
currentGoods: 391, // Товары в процессе
currentDefects: 23,
currentReturns: 156,
currentFulfillmentSupplies: 89,
currentSellerSupplies: 234,
// Тренды (в процентах)
productsTrend: 12,
goodsTrend: 8,
defectsTrend: -5,
returnsTrend: 8,
suppliesTrend: 15,
// Дополнительная аналитика
efficiency: 94.5,
turnover: 2.3,
utilizationRate: 87
}
const formatNumber = (num: number) => {
return num.toLocaleString('ru-RU')
}
const toggleSection = (section: keyof typeof expandedSections) => {
setExpandedSections(prev => ({
...prev,
[section]: !prev[section]
}))
}
// Компонент заголовка секции с кнопкой свёртывания
const SectionHeader = ({ title, section, badge, color = "text-white" }: {
title: string
section: keyof typeof expandedSections
badge?: number
color?: string
}) => (
<div className="flex items-center justify-between mb-4">
<div className="flex items-center space-x-3">
<h2 className={`text-lg font-semibold ${color}`}>{title}</h2>
{badge && (
<span className="px-2 py-1 bg-blue-500/20 text-blue-300 text-xs rounded-full font-medium">
{badge}
</span>
)}
</div>
<Button
variant="ghost"
size="sm"
onClick={() => toggleSection(section)}
className="text-white/60 hover:text-white hover:bg-white/10 p-1 h-8 w-8"
>
{expandedSections[section] ? (
<ChevronUp className="h-4 w-4" />
) : (
<ChevronDown className="h-4 w-4" />
)}
</Button>
</div>
)
return (
<div className="h-screen flex overflow-hidden">
<Sidebar />
<main className={`flex-1 ${getSidebarMargin()} px-6 py-4 overflow-y-auto transition-all duration-300`}>
<div className="w-full">
{/* Объединенный блок склада - ПЕРВЫЙ СВЕРХУ */}
<div className="mb-6">
<SectionHeader
title="Состояние склада"
section="warehouse"
badge={warehouseStats.currentProducts + warehouseStats.currentGoods + warehouseStats.currentFulfillmentSupplies + warehouseStats.currentSellerSupplies}
color="text-blue-400"
/>
{expandedSections.warehouse && (
<div className="grid grid-cols-6 gap-4">
{/* Уникальный модуль "Продукты" */}
<div className="min-w-0 relative group">
<div className="bg-gradient-to-br from-blue-500/10 via-blue-400/5 to-cyan-500/10 backdrop-blur border border-blue-400/30 rounded-2xl p-4 hover:from-blue-500/20 hover:to-cyan-500/20 transition-all duration-500 hover:scale-[1.02] hover:shadow-2xl hover:shadow-blue-500/20">
{/* Живой индикатор изменений */}
<div className="absolute -top-1 -right-1 flex items-center space-x-1">
<div className="relative">
<div className={`w-3 h-3 rounded-full animate-pulse shadow-lg ${
liveChange.isPositive
? 'bg-green-500 shadow-green-500/50'
: 'bg-red-500 shadow-red-500/50'
}`}></div>
<div className={`absolute inset-0 w-3 h-3 rounded-full animate-ping opacity-75 ${
liveChange.isPositive ? 'bg-green-500' : 'bg-red-500'
}`}></div>
</div>
<div className={`backdrop-blur text-white text-[10px] font-bold px-2 py-0.5 rounded-full animate-bounce ${
liveChange.isPositive
? 'bg-green-500/90'
: 'bg-red-500/90'
}`}>
{liveChange.isPositive ? '+' : '-'}{liveChange.value}
</div>
</div>
{/* Заголовок с иконкой */}
<div className="flex items-center justify-between mb-3">
<div className="flex items-center space-x-2">
<div className="relative">
<div className="p-2 bg-blue-500/20 rounded-xl border border-blue-400/30">
<Box className="h-4 w-4 text-blue-400" />
</div>
<div className={`absolute -top-1 -right-1 w-2 h-2 rounded-full animate-pulse ${
liveChange.isPositive ? 'bg-green-400' : 'bg-red-400'
}`}></div>
</div>
<span className="text-blue-100 text-sm font-semibold tracking-wide">ПРОДУКТЫ</span>
</div>
{/* Мини-график тренда */}
<div className="flex items-center space-x-1">
<div className="flex items-end space-x-0.5 h-4">
<div className={`w-1 rounded-full ${liveChange.isPositive ? 'bg-green-400/60' : 'bg-red-400/60'}`} style={{height: '60%'}}></div>
<div className={`w-1 rounded-full ${liveChange.isPositive ? 'bg-green-400/70' : 'bg-red-400/70'}`} style={{height: '80%'}}></div>
<div className={`w-1 rounded-full ${liveChange.isPositive ? 'bg-green-400/80' : 'bg-red-400/80'}`} style={{height: '70%'}}></div>
<div className={`w-1 rounded-full animate-pulse ${liveChange.isPositive ? 'bg-green-400' : 'bg-red-400'}`} style={{height: '100%'}}></div>
</div>
</div>
</div>
{/* Основное значение */}
<div className="mb-2">
<div className="flex items-baseline space-x-2">
<span className="text-2xl font-black text-white tracking-tight">
{formatNumber(warehouseStats.currentProducts)}
</span>
<div className={`flex items-center space-x-1 px-2 py-1 rounded-full ${
liveChange.isPositive ? 'bg-green-500/20' : 'bg-red-500/20'
}`}>
<svg className={`w-3 h-3 ${liveChange.isPositive ? 'text-green-400' : 'text-red-400'}`} fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d={liveChange.isPositive
? "M5.293 7.707a1 1 0 010-1.414l4-4a1 1 0 011.414 0l4 4a1 1 0 01-1.414 1.414L11 5.414V17a1 1 0 11-2 0V5.414L6.707 7.707a1 1 0 01-1.414 0z"
: "M14.707 12.293a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 111.414-1.414L9 14.586V3a1 1 0 012 0v11.586l2.293-2.293a1 1 0 011.414 0z"
} clipRule="evenodd" />
</svg>
<span className={`text-xs font-bold ${liveChange.isPositive ? 'text-green-400' : 'text-red-400'}`}>
{liveChange.value}%
</span>
</div>
</div>
</div>
{/* Подпись */}
<div className="text-blue-200/70 text-xs">
Готовые к отправке
</div>
{/* Прогресс-бар */}
<div className="mt-3 relative">
<div className="h-1.5 bg-blue-900/30 rounded-full overflow-hidden">
<div
className="h-full bg-gradient-to-r from-blue-400 to-cyan-400 rounded-full transition-all duration-1000 ease-out relative"
style={{width: '78%'}}
>
<div className="absolute right-0 top-0 h-full w-4 bg-gradient-to-r from-transparent to-white/30 animate-pulse"></div>
</div>
</div>
<div className="flex justify-between text-[10px] text-blue-300/60 mt-1">
<span>0</span>
<span>1.2К</span>
</div>
</div>
{/* Живое изменение значения */}
<div className="absolute bottom-2 left-2 opacity-0 group-hover:opacity-100 transition-opacity duration-300">
<div className={`flex items-center space-x-1 backdrop-blur px-2 py-1 rounded-lg ${
liveChange.isPositive ? 'bg-green-500/90' : 'bg-red-500/90'
}`}>
<div className="w-2 h-2 bg-white rounded-full animate-pulse"></div>
<span className="text-white text-[10px] font-bold">
LIVE {liveChange.isPositive ? '+' : '-'}{liveChange.value}
</span>
</div>
</div>
{/* Декоративные элементы */}
<div className="absolute top-1 left-1 w-8 h-8 bg-gradient-to-br from-blue-400/10 to-transparent rounded-full"></div>
<div className="absolute bottom-1 right-1 w-6 h-6 bg-gradient-to-tl from-cyan-400/10 to-transparent rounded-full"></div>
</div>
</div>
{/* Уникальный модуль "Товары" */}
<div className="min-w-0 relative group">
<div className="bg-gradient-to-br from-cyan-500/10 via-teal-400/5 to-emerald-500/10 backdrop-blur border border-cyan-400/30 rounded-2xl p-4 hover:from-cyan-500/20 hover:to-emerald-500/20 transition-all duration-700 hover:scale-[1.03] hover:shadow-2xl hover:shadow-cyan-500/20 relative overflow-hidden">
{/* Анимированный фон */}
<div className="absolute inset-0 opacity-10">
<div className={`absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-transparent via-cyan-400 to-transparent transform transition-transform duration-2000 ${
goodsData.pulse % 2 === 0 ? 'translate-x-full' : '-translate-x-full'
}`}></div>
</div>
{/* Статус активности */}
<div className="absolute -top-1 -left-1 flex items-center space-x-1">
<div className="relative">
<div className="w-4 h-4 bg-cyan-500 rounded-full animate-spin shadow-lg shadow-cyan-500/50" style={{
animation: 'spin 2s linear infinite'
}}></div>
<div className="absolute inset-1 w-2 h-2 bg-white rounded-full"></div>
</div>
<div className="bg-cyan-500/90 backdrop-blur text-white text-[9px] font-bold px-2 py-0.5 rounded-full">
ACTIVE
</div>
</div>
{/* Заголовок с двойной иконкой */}
<div className="flex items-center justify-between mb-3">
<div className="flex items-center space-x-2">
<div className="relative">
<div className="p-2 bg-cyan-500/20 rounded-xl border border-cyan-400/30 relative">
<Package className="h-4 w-4 text-cyan-400" />
{/* Мини-индикатор обработки */}
<div className="absolute -bottom-1 -right-1 w-3 h-3 bg-gradient-to-br from-green-400 to-emerald-500 rounded-full flex items-center justify-center">
<div className="w-1 h-1 bg-white rounded-full animate-pulse"></div>
</div>
</div>
</div>
<span className="text-cyan-100 text-sm font-semibold tracking-wide">ТОВАРЫ</span>
</div>
{/* Круговой прогресс эффективности */}
<div className="relative w-8 h-8">
<svg className="w-8 h-8 transform -rotate-90" viewBox="0 0 32 32">
<circle cx="16" cy="16" r="14" stroke="currentColor" strokeWidth="2" fill="none" className="text-cyan-900/30" />
<circle
cx="16" cy="16" r="14"
stroke="currentColor"
strokeWidth="2"
fill="none"
strokeDasharray={`${goodsData.efficiency * 0.88} 88`}
className="text-cyan-400 transition-all duration-1000"
/>
</svg>
<div className="absolute inset-0 flex items-center justify-center">
<span className="text-[8px] font-bold text-cyan-300">{goodsData.efficiency}%</span>
</div>
</div>
</div>
{/* Основное значение */}
<div className="mb-3">
<div className="flex items-baseline space-x-2">
<span className="text-2xl font-black text-white tracking-tight">
{formatNumber(warehouseStats.currentGoods)}
</span>
<div className="flex items-center space-x-1 bg-cyan-500/20 px-2 py-1 rounded-full">
<div className="w-2 h-2 bg-cyan-400 rounded-full animate-bounce"></div>
<span className="text-cyan-400 text-xs font-bold">
LIVE
</span>
</div>
</div>
</div>
{/* Дополнительные значения */}
<div className="grid grid-cols-2 gap-2 mb-3">
{/* Положительное значение */}
<div className="bg-green-500/10 border border-green-400/30 rounded-lg p-2">
<div className="flex items-center space-x-1 mb-1">
<div className="w-2 h-2 bg-green-400 rounded-full animate-pulse"></div>
<span className="text-green-400 text-[10px] font-semibold">ОБРАБОТКА</span>
</div>
<div className="text-green-300 text-sm font-bold">
+{formatNumber(goodsData.processing)}
</div>
</div>
{/* Отрицательное значение */}
<div className="bg-red-500/10 border border-red-400/30 rounded-lg p-2">
<div className="flex items-center space-x-1 mb-1">
<div className="w-2 h-2 bg-red-400 rounded-full animate-pulse"></div>
<span className="text-red-400 text-[10px] font-semibold">ОТКЛОНЕНО</span>
</div>
<div className="text-red-300 text-sm font-bold">
-{formatNumber(goodsData.rejected)}
</div>
</div>
</div>
{/* Подпись */}
<div className="text-cyan-200/70 text-xs mb-2">
В обработке
</div>
{/* Волновой прогресс */}
<div className="relative h-2 bg-cyan-900/30 rounded-full overflow-hidden">
<div className="absolute inset-0 bg-gradient-to-r from-cyan-400 to-emerald-400 rounded-full" style={{
width: `${(goodsData.processing / (goodsData.processing + goodsData.rejected)) * 100}%`
}}></div>
{/* Волновая анимация */}
<div className="absolute inset-0 opacity-50">
<div className={`h-full w-full bg-gradient-to-r from-transparent via-white/30 to-transparent transform transition-transform duration-2000 ${
goodsData.pulse % 3 === 0 ? 'translate-x-full' : goodsData.pulse % 3 === 1 ? 'translate-x-0' : '-translate-x-full'
}`}></div>
</div>
</div>
{/* Hover эффект с детальной информацией */}
<div className="absolute inset-0 bg-gradient-to-br from-cyan-500/20 to-emerald-500/20 rounded-2xl opacity-0 group-hover:opacity-100 transition-opacity duration-300 flex items-center justify-center">
<div className="text-center">
<div className="text-white text-xs font-bold mb-1">ДЕТАЛИ</div>
<div className="flex space-x-4 text-[10px]">
<div className="text-green-300">
<div className="font-bold">+{goodsData.processing}</div>
<div className="opacity-70">Активных</div>
</div>
<div className="text-red-300">
<div className="font-bold">-{goodsData.rejected}</div>
<div className="opacity-70">Проблем</div>
</div>
<div className="text-cyan-300">
<div className="font-bold">{goodsData.efficiency}%</div>
<div className="opacity-70">Успех</div>
</div>
</div>
</div>
</div>
{/* Декоративные частицы */}
<div className="absolute top-2 right-2 w-1 h-1 bg-cyan-400 rounded-full animate-ping"></div>
<div className="absolute bottom-3 left-3 w-1 h-1 bg-emerald-400 rounded-full animate-ping" style={{animationDelay: '1s'}}></div>
<div className="absolute top-1/2 left-1 w-1 h-1 bg-teal-400 rounded-full animate-ping" style={{animationDelay: '2s'}}></div>
</div>
</div>
<StatsCard
title="Брак"
value={formatNumber(warehouseStats.currentDefects)}
icon={AlertTriangle}
iconColor="text-red-400"
iconBg="bg-red-500/20"
trend={{ value: Math.abs(warehouseStats.defectsTrend), isPositive: warehouseStats.defectsTrend < 0 }}
subtitle="Требует утилизации"
className="min-w-0"
/>
<StatsCard
title="Возвраты с ПВЗ"
value={formatNumber(warehouseStats.currentReturns)}
icon={RotateCcw}
iconColor="text-yellow-400"
iconBg="bg-yellow-500/20"
trend={{ value: warehouseStats.returnsTrend, isPositive: false }}
subtitle="К обработке"
className="min-w-0"
/>
<StatsCard
title="Расходники ФФ"
value={formatNumber(warehouseStats.currentFulfillmentSupplies)}
icon={Wrench}
iconColor="text-purple-400"
iconBg="bg-purple-500/20"
subtitle="Упаковка, этикетки, пленка"
className="min-w-0"
/>
<StatsCard
title="Расходники селлеров"
value={formatNumber(warehouseStats.currentSellerSupplies)}
icon={Users}
iconColor="text-green-400"
iconBg="bg-green-500/20"
subtitle="Материалы клиентов"
className="min-w-0"
/>
</div>
)}
</div>
{/* Компактный заголовок с ключевыми метриками */}
<div className="mb-6 flex-shrink-0">
<div className="flex items-center justify-between mb-4">
<div className="flex items-center space-x-4">
<div className="p-2 bg-gradient-to-r from-blue-500/20 to-purple-500/20 rounded-xl">
<Warehouse className="h-6 w-6 text-blue-400" />
</div>
<div>
<div className="flex items-center space-x-3">
<span className="text-sm text-white/60">Эффективность</span>
<span className="text-lg font-bold text-green-400">{warehouseStats.efficiency}%</span>
</div>
<div className="flex items-center space-x-3">
<span className="text-sm text-white/60">Оборачиваемость</span>
<span className="text-lg font-bold text-blue-400">{warehouseStats.turnover}x</span>
</div>
</div>
</div>
<div className="text-right">
<div className="text-sm text-white/60">Загрузка склада</div>
<div className="text-2xl font-bold text-white">{warehouseStats.utilizationRate}%</div>
</div>
</div>
</div>
{/* Нестандартные решения */}
<div className="mt-8 space-y-6">
{/* Интеллектуальные инсайты */}
<Card className="glass-card p-6">
<div className="flex items-center justify-between mb-4">
<div className="flex items-center space-x-3">
<div className="p-2 bg-gradient-to-r from-green-500/20 to-blue-500/20 rounded-xl">
<Zap className="h-5 w-5 text-green-400" />
</div>
<h3 className="text-lg font-semibold text-white">Умные рекомендации</h3>
</div>
<Badge variant="secondary" className="bg-green-500/20 text-green-300">
AI-анализ
</Badge>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div className="p-4 bg-white/5 rounded-xl border border-white/10">
<div className="flex items-center space-x-2 mb-2">
<Target className="h-4 w-4 text-yellow-400" />
<span className="text-sm font-medium text-yellow-400">Оптимизация</span>
</div>
<p className="text-xs text-white/70">
Рекомендуется увеличить запас расходников на 15% для покрытия пикового спроса
</p>
</div>
<div className="p-4 bg-white/5 rounded-xl border border-white/10">
<div className="flex items-center space-x-2 mb-2">
<Activity className="h-4 w-4 text-blue-400" />
<span className="text-sm font-medium text-blue-400">Прогноз</span>
</div>
<p className="text-xs text-white/70">
Ожидается рост возвратов на 12% в следующем месяце. Подготовьте дополнительные места
</p>
</div>
<div className="p-4 bg-white/5 rounded-xl border border-white/10">
<div className="flex items-center space-x-2 mb-2">
<BarChart3 className="h-4 w-4 text-purple-400" />
<span className="text-sm font-medium text-purple-400">Тренд</span>
</div>
<p className="text-xs text-white/70">
Эффективность обработки товаров выросла на 8% за последний месяц
</p>
</div>
</div>
</Card>
{/* Быстрые действия */}
<Card className="glass-card p-6">
<div className="flex items-center justify-between mb-4">
<h3 className="text-lg font-semibold text-white">Быстрые действия</h3>
<div className="flex space-x-2">
<Button size="sm" variant="outline" className="border-white/20 text-white/70 hover:bg-white/10">
<Eye className="h-4 w-4 mr-2" />
Обзор
</Button>
<Button size="sm" variant="outline" className="border-white/20 text-white/70 hover:bg-white/10">
<Activity className="h-4 w-4 mr-2" />
Отчеты
</Button>
</div>
</div>
<div className="text-center py-8">
<Package className="h-12 w-12 text-white/40 mx-auto mb-4" />
<p className="text-white/60 text-sm">
Основная функциональность склада будет добавлена на следующем этапе
</p>
</div>
</Card>
</div>
</div>
</main>
</div>
)
}