Files
sfera/src/components/fulfillment-statistics/fulfillment-statistics-dashboard.tsx

686 lines
29 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 } 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 {
BarChart3,
TrendingUp,
AlertTriangle,
Send,
Archive,
Clock,
ShoppingBag,
ChevronDown,
ChevronUp,
Target,
Activity,
Zap,
PieChart,
Calendar,
Package,
DollarSign,
Users,
Truck,
Warehouse,
Eye,
EyeOff,
} from "lucide-react";
export function FulfillmentStatisticsDashboard() {
const { getSidebarMargin } = useSidebar();
// Состояния для свёртывания блоков
const [expandedSections, setExpandedSections] = useState({
allTime: true,
marketplaces: true,
analytics: false,
performance: false,
warehouseMetrics: true,
smartRecommendations: true,
quickActions: true,
});
// Мок данные для статистики
const statisticsData = {
// Данные за все время
totalProducts: 15678,
totalDefects: 145,
totalSupplies: 2341,
totalRevenue: 45670000,
totalOrders: 8934,
// Отправка на маркетплейсы
sentToWildberries: 8934,
sentToOzon: 4523,
sentToOthers: 1876,
// Аналитика производительности
avgProcessingTime: 2.4,
defectRate: 0.92,
returnRate: 4.3,
customerSatisfaction: 4.8,
// Тренды
revenueTrend: 18,
ordersTrend: 12,
defectsTrend: -8,
satisfactionTrend: 5,
};
// Данные склада (перенесено из fulfillment-warehouse)
const warehouseStats = {
efficiency: 94.5,
turnover: 2.3,
utilizationRate: 87,
};
const formatNumber = (num: number) => {
return num.toLocaleString("ru-RU");
};
const formatCurrency = (num: number) => {
return new Intl.NumberFormat("ru-RU", {
style: "currency",
currency: "RUB",
minimumFractionDigits: 0,
maximumFractionDigits: 0,
}).format(num);
};
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 | string;
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 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">
<BarChart3 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">
{formatCurrency(statisticsData.totalRevenue)}
</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">
{statisticsData.customerSatisfaction}/5.0
</span>
</div>
</div>
</div>
<div className="text-right">
<div className="text-sm text-white/60">Уровень брака</div>
<div className="text-2xl font-bold text-white">
{statisticsData.defectRate}%
</div>
</div>
</div>
</div>
{/* Блоки статистики */}
<div className="space-y-6">
{/* Накопленная статистика */}
<div>
<SectionHeader
title="Накопленная статистика"
section="allTime"
badge={formatNumber(statisticsData.totalProducts)}
color="text-cyan-400"
/>
{expandedSections.allTime && (
<div className="space-y-4">
<StatsGrid>
<StatsCard
title="Обработано товаров"
value={formatNumber(statisticsData.totalProducts)}
icon={Archive}
iconColor="text-cyan-400"
iconBg="bg-cyan-500/20"
subtitle="Общий объем"
/>
<StatsCard
title="Выявлено брака"
value={formatNumber(statisticsData.totalDefects)}
icon={AlertTriangle}
iconColor="text-red-400"
iconBg="bg-red-500/20"
trend={{
value: Math.abs(statisticsData.defectsTrend),
isPositive: statisticsData.defectsTrend < 0,
}}
subtitle="Всего единиц"
/>
<StatsCard
title="Поставок получено"
value={formatNumber(statisticsData.totalSupplies)}
icon={Clock}
iconColor="text-orange-400"
iconBg="bg-orange-500/20"
subtitle="Входящих поставок"
/>
<StatsCard
title="Общий доход"
value={formatCurrency(statisticsData.totalRevenue)}
icon={DollarSign}
iconColor="text-green-400"
iconBg="bg-green-500/20"
trend={{
value: statisticsData.revenueTrend,
isPositive: true,
}}
subtitle="За все время"
/>
</StatsGrid>
{/* Дополнительные метрики */}
<StatsGrid columns={2}>
<StatsCard
title="Выполнено заказов"
value={formatNumber(statisticsData.totalOrders)}
icon={Package}
iconColor="text-purple-400"
iconBg="bg-purple-500/20"
trend={{
value: statisticsData.ordersTrend,
isPositive: true,
}}
subtitle="Успешных отгрузок"
/>
<StatsCard
title="Удовлетворенность клиентов"
value={`${statisticsData.customerSatisfaction}/5.0`}
icon={Users}
iconColor="text-blue-400"
iconBg="bg-blue-500/20"
trend={{
value: statisticsData.satisfactionTrend,
isPositive: true,
}}
subtitle="Средний рейтинг"
/>
</StatsGrid>
</div>
)}
</div>
{/* Отгрузка на площадки */}
<div>
<SectionHeader
title="Отгрузка на площадки"
section="marketplaces"
badge={formatNumber(
statisticsData.sentToWildberries +
statisticsData.sentToOzon +
statisticsData.sentToOthers
)}
color="text-orange-400"
/>
{expandedSections.marketplaces && (
<div className="space-y-4">
<StatsGrid columns={3}>
<StatsCard
title="Wildberries"
value={formatNumber(statisticsData.sentToWildberries)}
icon={Send}
iconColor="text-purple-400"
iconBg="bg-purple-500/20"
subtitle="Отправлено единиц"
/>
<StatsCard
title="Ozon"
value={formatNumber(statisticsData.sentToOzon)}
icon={Send}
iconColor="text-blue-400"
iconBg="bg-blue-500/20"
subtitle="Отправлено единиц"
/>
<StatsCard
title="Другие маркетплейсы"
value={formatNumber(statisticsData.sentToOthers)}
icon={ShoppingBag}
iconColor="text-green-400"
iconBg="bg-green-500/20"
subtitle="Яндекс.Маркет, Авито"
/>
</StatsGrid>
{/* Диаграмма распределения */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
<Card className="glass-card p-4">
<div className="flex items-center justify-between mb-3">
<h3 className="text-sm font-medium text-white/80">
Распределение отгрузок
</h3>
<PieChart className="h-4 w-4 text-white/60" />
</div>
<div className="space-y-2">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-2">
<div className="w-3 h-3 bg-purple-500 rounded-full"></div>
<span className="text-xs text-white/70">WB</span>
</div>
<span className="text-xs text-white font-medium">
{(
(statisticsData.sentToWildberries /
(statisticsData.sentToWildberries +
statisticsData.sentToOzon +
statisticsData.sentToOthers)) *
100
).toFixed(1)}
%
</span>
</div>
<div className="flex items-center justify-between">
<div className="flex items-center space-x-2">
<div className="w-3 h-3 bg-blue-500 rounded-full"></div>
<span className="text-xs text-white/70">Ozon</span>
</div>
<span className="text-xs text-white font-medium">
{(
(statisticsData.sentToOzon /
(statisticsData.sentToWildberries +
statisticsData.sentToOzon +
statisticsData.sentToOthers)) *
100
).toFixed(1)}
%
</span>
</div>
<div className="flex items-center justify-between">
<div className="flex items-center space-x-2">
<div className="w-3 h-3 bg-green-500 rounded-full"></div>
<span className="text-xs text-white/70">
Другие
</span>
</div>
<span className="text-xs text-white font-medium">
{(
(statisticsData.sentToOthers /
(statisticsData.sentToWildberries +
statisticsData.sentToOzon +
statisticsData.sentToOthers)) *
100
).toFixed(1)}
%
</span>
</div>
</div>
</Card>
{/* Тренды по площадкам */}
<Card className="glass-card p-4">
<div className="flex items-center justify-between mb-3">
<h3 className="text-sm font-medium text-white/80">
Тренды роста
</h3>
<TrendingUp className="h-4 w-4 text-green-400" />
</div>
<div className="space-y-3">
<div className="flex items-center justify-between">
<span className="text-xs text-white/70">
Wildberries
</span>
<div className="flex items-center space-x-2">
<div className="w-16 h-2 bg-white/10 rounded-full overflow-hidden">
<div
className="h-full bg-purple-500 rounded-full"
style={{ width: "68%" }}
></div>
</div>
<span className="text-xs text-green-400">+12%</span>
</div>
</div>
<div className="flex items-center justify-between">
<span className="text-xs text-white/70">Ozon</span>
<div className="flex items-center space-x-2">
<div className="w-16 h-2 bg-white/10 rounded-full overflow-hidden">
<div
className="h-full bg-blue-500 rounded-full"
style={{ width: "45%" }}
></div>
</div>
<span className="text-xs text-green-400">+8%</span>
</div>
</div>
<div className="flex items-center justify-between">
<span className="text-xs text-white/70">Другие</span>
<div className="flex items-center space-x-2">
<div className="w-16 h-2 bg-white/10 rounded-full overflow-hidden">
<div
className="h-full bg-green-500 rounded-full"
style={{ width: "32%" }}
></div>
</div>
<span className="text-xs text-green-400">+15%</span>
</div>
</div>
</div>
</Card>
</div>
</div>
)}
</div>
{/* Аналитика производительности */}
<div>
<SectionHeader
title="Аналитика производительности"
section="performance"
badge={`${statisticsData.avgProcessingTime}ч`}
color="text-green-400"
/>
{expandedSections.performance && (
<StatsGrid>
<StatsCard
title="Среднее время обработки"
value={`${statisticsData.avgProcessingTime} ч`}
icon={Clock}
iconColor="text-blue-400"
iconBg="bg-blue-500/20"
subtitle="На единицу товара"
/>
<StatsCard
title="Уровень брака"
value={`${statisticsData.defectRate}%`}
icon={AlertTriangle}
iconColor="text-red-400"
iconBg="bg-red-500/20"
trend={{
value: Math.abs(statisticsData.defectsTrend),
isPositive: statisticsData.defectsTrend < 0,
}}
subtitle="От общего объема"
/>
<StatsCard
title="Уровень возвратов"
value={`${statisticsData.returnRate}%`}
icon={Truck}
iconColor="text-yellow-400"
iconBg="bg-yellow-500/20"
subtitle="Возвраты с площадок"
/>
<StatsCard
title="Рейтинг качества"
value={`${statisticsData.customerSatisfaction}/5.0`}
icon={Users}
iconColor="text-green-400"
iconBg="bg-green-500/20"
trend={{
value: statisticsData.satisfactionTrend,
isPositive: true,
}}
subtitle="Отзывы клиентов"
/>
</StatsGrid>
)}
</div>
{/* AI-аналитика и прогнозы */}
<div>
<SectionHeader
title="Умная аналитика"
section="analytics"
color="text-purple-400"
/>
{expandedSections.analytics && (
<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-purple-500/20 to-blue-500/20 rounded-xl">
<Zap className="h-5 w-5 text-purple-400" />
</div>
<h3 className="text-lg font-semibold text-white">
Прогнозы и рекомендации
</h3>
</div>
<Badge
variant="secondary"
className="bg-purple-500/20 text-purple-300"
>
AI-анализ
</Badge>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg: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-green-400" />
<span className="text-sm font-medium text-green-400">
Прогноз роста
</span>
</div>
<p className="text-xs text-white/70">
Ожидается увеличение объемов на 23% в следующем квартале
</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">
Возможно снижение времени обработки на 18% при
автоматизации
</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">
<Calendar className="h-4 w-4 text-orange-400" />
<span className="text-sm font-medium text-orange-400">
Сезонность
</span>
</div>
<p className="text-xs text-white/70">
Пиковые нагрузки ожидаются в ноябре-декабре (+45%)
</p>
</div>
</div>
</Card>
)}
</div>
{/* Ключевые метрики склада (перенесено из fulfillment-warehouse) */}
<div>
<SectionHeader
title="Ключевые метрики склада"
section="warehouseMetrics"
color="text-blue-400"
/>
{expandedSections.warehouseMetrics && (
<Card className="bg-gradient-to-r from-slate-900/40 to-slate-800/40 border-slate-700/50">
<div className="p-6">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-4">
<div className="p-3 bg-gradient-to-r from-blue-500/20 to-purple-500/20 rounded-xl border border-blue-400/20">
<Warehouse className="h-6 w-6 text-blue-400" />
</div>
<div className="grid grid-cols-2 gap-6">
<div className="flex items-center space-x-3">
<span className="text-sm text-slate-400 font-medium">Эффективность</span>
<Badge variant="secondary" className="bg-green-500/20 text-green-300 font-bold">
{warehouseStats.efficiency}%
</Badge>
</div>
<div className="flex items-center space-x-3">
<span className="text-sm text-slate-400 font-medium">Оборачиваемость</span>
<Badge variant="secondary" className="bg-blue-500/20 text-blue-300 font-bold">
{warehouseStats.turnover}x
</Badge>
</div>
</div>
</div>
<div className="text-right">
<div className="text-sm text-slate-400 font-medium mb-1">Загрузка склада</div>
<div className="text-3xl font-bold text-white">{warehouseStats.utilizationRate}%</div>
</div>
</div>
</div>
</Card>
)}
</div>
{/* Умные рекомендации склада (перенесено из fulfillment-warehouse) */}
<div>
<SectionHeader
title="Умные рекомендации склада"
section="smartRecommendations"
color="text-green-400"
/>
{expandedSections.smartRecommendations && (
<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>
)}
</div>
{/* Быстрые действия (перенесено из fulfillment-warehouse) */}
<div>
<SectionHeader
title="Быстрые действия"
section="quickActions"
color="text-orange-400"
/>
{expandedSections.quickActions && (
<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>
</div>
</main>
</div>
);
}