Добавлен новый демо-компонент FulfillmentWarehouse2Demo в интерфейс управления складами. Обновлен компонент UIKitSection для интеграции нового демо и изменения текстовых меток. Оптимизирован интерфейс с использованием новых компонентов и улучшена логика отображения данных о складах.
This commit is contained in:
699
src/components/admin/ui-kit/fulfillment-warehouse-2-demo.tsx
Normal file
699
src/components/admin/ui-kit/fulfillment-warehouse-2-demo.tsx
Normal file
@ -0,0 +1,699 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useMemo } from "react";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import {
|
||||
Package,
|
||||
TrendingUp,
|
||||
TrendingDown,
|
||||
AlertTriangle,
|
||||
RotateCcw,
|
||||
Wrench,
|
||||
Users,
|
||||
ChevronDown,
|
||||
ChevronUp,
|
||||
Box,
|
||||
Search,
|
||||
ArrowUpDown,
|
||||
Store,
|
||||
Package2,
|
||||
} from "lucide-react";
|
||||
|
||||
// Типы данных
|
||||
interface StoreData {
|
||||
id: string;
|
||||
name: string;
|
||||
logo?: string;
|
||||
products: number;
|
||||
goods: number;
|
||||
defects: number;
|
||||
sellerSupplies: number;
|
||||
pvzReturns: number;
|
||||
// Изменения за сутки
|
||||
productsChange: number;
|
||||
goodsChange: number;
|
||||
defectsChange: number;
|
||||
sellerSuppliesChange: number;
|
||||
pvzReturnsChange: number;
|
||||
}
|
||||
|
||||
interface WarehouseStats {
|
||||
products: { current: number; change: number };
|
||||
goods: { current: number; change: number };
|
||||
defects: { current: number; change: number };
|
||||
pvzReturns: { current: number; change: number };
|
||||
fulfillmentSupplies: { current: number; change: number };
|
||||
sellerSupplies: { current: number; change: number };
|
||||
}
|
||||
|
||||
export function FulfillmentWarehouse2Demo() {
|
||||
// Состояния для поиска и фильтрации
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [sortField, setSortField] = useState<keyof StoreData>("name");
|
||||
const [sortOrder, setSortOrder] = useState<"asc" | "desc">("asc");
|
||||
const [expandedStores, setExpandedStores] = useState<Set<string>>(new Set());
|
||||
|
||||
// Мок данные для статистики
|
||||
const warehouseStats: WarehouseStats = {
|
||||
products: { current: 2856, change: 124 },
|
||||
goods: { current: 1391, change: 87 },
|
||||
defects: { current: 43, change: -12 },
|
||||
pvzReturns: { current: 256, change: 34 },
|
||||
fulfillmentSupplies: { current: 189, change: 23 },
|
||||
sellerSupplies: { current: 534, change: 67 },
|
||||
};
|
||||
|
||||
// Мок данные для магазинов
|
||||
const mockStoreData: StoreData[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
id: "1",
|
||||
name: "Электроника Плюс",
|
||||
products: 456,
|
||||
goods: 234,
|
||||
defects: 12,
|
||||
sellerSupplies: 89,
|
||||
pvzReturns: 45,
|
||||
productsChange: 23,
|
||||
goodsChange: 15,
|
||||
defectsChange: -3,
|
||||
sellerSuppliesChange: 12,
|
||||
pvzReturnsChange: 8,
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
name: "Мода и Стиль",
|
||||
products: 678,
|
||||
goods: 345,
|
||||
defects: 8,
|
||||
sellerSupplies: 123,
|
||||
pvzReturns: 67,
|
||||
productsChange: 34,
|
||||
goodsChange: 22,
|
||||
defectsChange: -2,
|
||||
sellerSuppliesChange: 18,
|
||||
pvzReturnsChange: 12,
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
name: "Дом и Сад",
|
||||
products: 289,
|
||||
goods: 156,
|
||||
defects: 5,
|
||||
sellerSupplies: 67,
|
||||
pvzReturns: 23,
|
||||
productsChange: 12,
|
||||
goodsChange: 8,
|
||||
defectsChange: -1,
|
||||
sellerSuppliesChange: 9,
|
||||
pvzReturnsChange: 4,
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
name: "Спорт и Отдых",
|
||||
products: 567,
|
||||
goods: 289,
|
||||
defects: 15,
|
||||
sellerSupplies: 134,
|
||||
pvzReturns: 78,
|
||||
productsChange: 28,
|
||||
goodsChange: 19,
|
||||
defectsChange: -4,
|
||||
sellerSuppliesChange: 21,
|
||||
pvzReturnsChange: 15,
|
||||
},
|
||||
{
|
||||
id: "5",
|
||||
name: "Красота и Здоровье",
|
||||
products: 234,
|
||||
goods: 123,
|
||||
defects: 3,
|
||||
sellerSupplies: 45,
|
||||
pvzReturns: 19,
|
||||
productsChange: 8,
|
||||
goodsChange: 5,
|
||||
defectsChange: 0,
|
||||
sellerSuppliesChange: 6,
|
||||
pvzReturnsChange: 3,
|
||||
},
|
||||
],
|
||||
[]
|
||||
);
|
||||
|
||||
// Фильтрация и сортировка данных
|
||||
const filteredAndSortedStores = useMemo(() => {
|
||||
const filtered = mockStoreData.filter((store) =>
|
||||
store.name.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
);
|
||||
|
||||
filtered.sort((a, b) => {
|
||||
const aValue = a[sortField];
|
||||
const bValue = b[sortField];
|
||||
|
||||
if (typeof aValue === "string" && typeof bValue === "string") {
|
||||
return sortOrder === "asc"
|
||||
? aValue.localeCompare(bValue)
|
||||
: bValue.localeCompare(aValue);
|
||||
}
|
||||
|
||||
if (typeof aValue === "number" && typeof bValue === "number") {
|
||||
return sortOrder === "asc" ? aValue - bValue : bValue - aValue;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
return filtered;
|
||||
}, [searchTerm, sortField, sortOrder, mockStoreData]);
|
||||
|
||||
// Подсчет общих сумм
|
||||
const totals = useMemo(() => {
|
||||
return filteredAndSortedStores.reduce(
|
||||
(acc, store) => ({
|
||||
products: acc.products + store.products,
|
||||
goods: acc.goods + store.goods,
|
||||
defects: acc.defects + store.defects,
|
||||
sellerSupplies: acc.sellerSupplies + store.sellerSupplies,
|
||||
pvzReturns: acc.pvzReturns + store.pvzReturns,
|
||||
productsChange: acc.productsChange + store.productsChange,
|
||||
goodsChange: acc.goodsChange + store.goodsChange,
|
||||
defectsChange: acc.defectsChange + store.defectsChange,
|
||||
sellerSuppliesChange:
|
||||
acc.sellerSuppliesChange + store.sellerSuppliesChange,
|
||||
pvzReturnsChange: acc.pvzReturnsChange + store.pvzReturnsChange,
|
||||
}),
|
||||
{
|
||||
products: 0,
|
||||
goods: 0,
|
||||
defects: 0,
|
||||
sellerSupplies: 0,
|
||||
pvzReturns: 0,
|
||||
productsChange: 0,
|
||||
goodsChange: 0,
|
||||
defectsChange: 0,
|
||||
sellerSuppliesChange: 0,
|
||||
pvzReturnsChange: 0,
|
||||
}
|
||||
);
|
||||
}, [filteredAndSortedStores]);
|
||||
|
||||
const formatNumber = (num: number) => {
|
||||
return num.toLocaleString("ru-RU");
|
||||
};
|
||||
|
||||
const formatChange = (change: number) => {
|
||||
const sign = change > 0 ? "+" : "";
|
||||
return `${sign}${change}`;
|
||||
};
|
||||
|
||||
const toggleStoreExpansion = (storeId: string) => {
|
||||
const newExpanded = new Set(expandedStores);
|
||||
if (newExpanded.has(storeId)) {
|
||||
newExpanded.delete(storeId);
|
||||
} else {
|
||||
newExpanded.add(storeId);
|
||||
}
|
||||
setExpandedStores(newExpanded);
|
||||
};
|
||||
|
||||
const handleSort = (field: keyof StoreData) => {
|
||||
if (sortField === field) {
|
||||
setSortOrder(sortOrder === "asc" ? "desc" : "asc");
|
||||
} else {
|
||||
setSortField(field);
|
||||
setSortOrder("asc");
|
||||
}
|
||||
};
|
||||
|
||||
// Компонент компактной статистической карточки
|
||||
const StatCard = ({
|
||||
title,
|
||||
icon: Icon,
|
||||
current,
|
||||
change,
|
||||
description,
|
||||
}: {
|
||||
title: string;
|
||||
icon: React.ComponentType<{ className?: string }>;
|
||||
current: number;
|
||||
change: number;
|
||||
description: string;
|
||||
}) => (
|
||||
<div
|
||||
className={`glass-card p-3 hover:bg-white/15 transition-all duration-300 relative overflow-hidden`}
|
||||
>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="p-1.5 bg-white/10 rounded-lg">
|
||||
<Icon className="h-3 w-3 text-white" />
|
||||
</div>
|
||||
<span className="text-white text-xs font-semibold">{title}</span>
|
||||
</div>
|
||||
<div
|
||||
className={`flex items-center space-x-1 px-1.5 py-0.5 rounded-full ${
|
||||
change >= 0 ? "bg-green-500/20" : "bg-red-500/20"
|
||||
}`}
|
||||
>
|
||||
{change >= 0 ? (
|
||||
<TrendingUp className="h-2.5 w-2.5 text-green-400" />
|
||||
) : (
|
||||
<TrendingDown className="h-2.5 w-2.5 text-red-400" />
|
||||
)}
|
||||
<span
|
||||
className={`text-[10px] font-bold ${
|
||||
change >= 0 ? "text-green-400" : "text-red-400"
|
||||
}`}
|
||||
>
|
||||
{formatChange(change)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-lg font-bold text-white mb-1">
|
||||
{formatNumber(current)}
|
||||
</div>
|
||||
<div className="text-white/60 text-[10px]">{description}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
// Компонент заголовка таблицы
|
||||
const TableHeader = ({
|
||||
field,
|
||||
children,
|
||||
sortable = false,
|
||||
}: {
|
||||
field?: keyof StoreData;
|
||||
children: React.ReactNode;
|
||||
sortable?: boolean;
|
||||
}) => (
|
||||
<div
|
||||
className={`px-3 py-2 text-left text-xs font-medium text-white/80 uppercase tracking-wider ${
|
||||
sortable ? "cursor-pointer hover:text-white hover:bg-white/5" : ""
|
||||
} flex items-center space-x-1`}
|
||||
onClick={sortable && field ? () => handleSort(field) : undefined}
|
||||
>
|
||||
<span>{children}</span>
|
||||
{sortable && field && (
|
||||
<ArrowUpDown
|
||||
className={`h-3 w-3 ${
|
||||
sortField === field ? "text-blue-400" : "text-white/40"
|
||||
}`}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-white mb-4">
|
||||
Склад фулфилмент - 2
|
||||
</h2>
|
||||
<p className="text-white/70 mb-6">
|
||||
Обновленная версия компонента склада фулфилмента с оптимизацией для
|
||||
компактных экранов
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Card
|
||||
className="glass-card p-6 overflow-hidden"
|
||||
style={{ height: "700px" }}
|
||||
>
|
||||
<div className="h-full flex flex-col">
|
||||
{/* Компактная статичная верхняя секция со статистикой - максимум 30% экрана */}
|
||||
<div className="flex-shrink-0 mb-4" style={{ maxHeight: "30%" }}>
|
||||
<div className="glass-card p-4">
|
||||
<h3 className="text-base font-semibold text-blue-400 mb-3">
|
||||
Статистика склада
|
||||
</h3>
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 xl:grid-cols-6 gap-3">
|
||||
<StatCard
|
||||
title="Продукты"
|
||||
icon={Box}
|
||||
current={warehouseStats.products.current}
|
||||
change={warehouseStats.products.change}
|
||||
description="Готовые к отправке"
|
||||
/>
|
||||
<StatCard
|
||||
title="Товары"
|
||||
icon={Package}
|
||||
current={warehouseStats.goods.current}
|
||||
change={warehouseStats.goods.change}
|
||||
description="В обработке"
|
||||
/>
|
||||
<StatCard
|
||||
title="Брак"
|
||||
icon={AlertTriangle}
|
||||
current={warehouseStats.defects.current}
|
||||
change={warehouseStats.defects.change}
|
||||
description="Требует утилизации"
|
||||
/>
|
||||
<StatCard
|
||||
title="Возвраты с ПВЗ"
|
||||
icon={RotateCcw}
|
||||
current={warehouseStats.pvzReturns.current}
|
||||
change={warehouseStats.pvzReturns.change}
|
||||
description="К обработке"
|
||||
/>
|
||||
<StatCard
|
||||
title="Расходники ФФ"
|
||||
icon={Wrench}
|
||||
current={warehouseStats.fulfillmentSupplies.current}
|
||||
change={warehouseStats.fulfillmentSupplies.change}
|
||||
description="Упаковка, этикетки"
|
||||
/>
|
||||
<StatCard
|
||||
title="Расходники селлеров"
|
||||
icon={Users}
|
||||
current={warehouseStats.sellerSupplies.current}
|
||||
change={warehouseStats.sellerSupplies.change}
|
||||
description="Материалы клиентов"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Основная скроллируемая часть - оставшиеся 70% экрана */}
|
||||
<div
|
||||
className="flex-1 flex flex-col overflow-hidden"
|
||||
style={{ minHeight: "60%" }}
|
||||
>
|
||||
<div className="glass-card flex-1 flex flex-col overflow-hidden">
|
||||
{/* Компактная шапка таблицы - максимум 10% экрана */}
|
||||
<div
|
||||
className="p-4 border-b border-white/10 flex-shrink-0"
|
||||
style={{ maxHeight: "10%" }}
|
||||
>
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h3 className="text-base font-semibold text-white">
|
||||
Детализация по магазинам
|
||||
</h3>
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="bg-blue-500/20 text-blue-300 text-xs"
|
||||
>
|
||||
{filteredAndSortedStores.length} магазинов
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
{/* Компактный поиск */}
|
||||
<div className="relative">
|
||||
<Search className="absolute left-2.5 top-1/2 transform -translate-y-1/2 h-3.5 w-3.5 text-white/40" />
|
||||
<Input
|
||||
placeholder="Поиск по магазинам..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="pl-8 h-8 text-sm glass-input text-white placeholder:text-white/40"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Фиксированные заголовки таблицы */}
|
||||
<div className="flex-shrink-0 bg-white/5 border-b border-white/10">
|
||||
<div className="grid grid-cols-7 gap-0">
|
||||
<TableHeader field="name" sortable>
|
||||
№ / Магазин
|
||||
</TableHeader>
|
||||
<TableHeader field="products" sortable>
|
||||
Продукты
|
||||
</TableHeader>
|
||||
<TableHeader field="goods" sortable>
|
||||
Товары
|
||||
</TableHeader>
|
||||
<TableHeader field="defects" sortable>
|
||||
Брак
|
||||
</TableHeader>
|
||||
<TableHeader field="sellerSupplies" sortable>
|
||||
Расходники селлера
|
||||
</TableHeader>
|
||||
<TableHeader field="pvzReturns" sortable>
|
||||
Возвраты с ПВЗ
|
||||
</TableHeader>
|
||||
<TableHeader>Действия</TableHeader>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Строка с суммами */}
|
||||
<div className="flex-shrink-0 bg-blue-500/10 border-b border-blue-500/20">
|
||||
<div className="grid grid-cols-7 gap-0">
|
||||
<div className="px-3 py-2 text-xs font-bold text-blue-300">
|
||||
ИТОГО ({filteredAndSortedStores.length})
|
||||
</div>
|
||||
<div className="px-3 py-2 text-xs font-bold text-white">
|
||||
{formatNumber(totals.products)}
|
||||
<div
|
||||
className={`text-[10px] ${
|
||||
totals.productsChange >= 0
|
||||
? "text-green-400"
|
||||
: "text-red-400"
|
||||
}`}
|
||||
>
|
||||
{formatChange(totals.productsChange)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-3 py-2 text-xs font-bold text-white">
|
||||
{formatNumber(totals.goods)}
|
||||
<div
|
||||
className={`text-[10px] ${
|
||||
totals.goodsChange >= 0
|
||||
? "text-green-400"
|
||||
: "text-red-400"
|
||||
}`}
|
||||
>
|
||||
{formatChange(totals.goodsChange)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-3 py-2 text-xs font-bold text-white">
|
||||
{formatNumber(totals.defects)}
|
||||
<div
|
||||
className={`text-[10px] ${
|
||||
totals.defectsChange >= 0
|
||||
? "text-green-400"
|
||||
: "text-red-400"
|
||||
}`}
|
||||
>
|
||||
{formatChange(totals.defectsChange)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-3 py-2 text-xs font-bold text-white">
|
||||
{formatNumber(totals.sellerSupplies)}
|
||||
<div
|
||||
className={`text-[10px] ${
|
||||
totals.sellerSuppliesChange >= 0
|
||||
? "text-green-400"
|
||||
: "text-red-400"
|
||||
}`}
|
||||
>
|
||||
{formatChange(totals.sellerSuppliesChange)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-3 py-2 text-xs font-bold text-white">
|
||||
{formatNumber(totals.pvzReturns)}
|
||||
<div
|
||||
className={`text-[10px] ${
|
||||
totals.pvzReturnsChange >= 0
|
||||
? "text-green-400"
|
||||
: "text-red-400"
|
||||
}`}
|
||||
>
|
||||
{formatChange(totals.pvzReturnsChange)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-3 py-2"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Скроллируемый контент таблицы - оставшееся пространство */}
|
||||
<div className="flex-1 overflow-y-auto scrollbar-thin scrollbar-thumb-white/20 scrollbar-track-transparent">
|
||||
{filteredAndSortedStores.map((store, index) => (
|
||||
<div
|
||||
key={store.id}
|
||||
className="border-b border-white/10 hover:bg-white/5 transition-colors"
|
||||
>
|
||||
{/* Основная строка магазина */}
|
||||
<div className="grid grid-cols-7 gap-0">
|
||||
<div className="px-3 py-2.5 flex items-center space-x-2">
|
||||
<span className="text-white/60 text-xs">
|
||||
{index + 1}
|
||||
</span>
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="w-6 h-6 bg-gradient-to-br from-blue-500 to-purple-500 rounded-md flex items-center justify-center">
|
||||
<Store className="h-3 w-3 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-white font-medium text-xs">
|
||||
{store.name}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="px-3 py-2.5">
|
||||
<div className="text-white font-semibold text-sm">
|
||||
{formatNumber(store.products)}
|
||||
</div>
|
||||
<div
|
||||
className={`text-[10px] ${
|
||||
store.productsChange >= 0
|
||||
? "text-green-400"
|
||||
: "text-red-400"
|
||||
}`}
|
||||
>
|
||||
{formatChange(store.productsChange)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="px-3 py-2.5">
|
||||
<div className="text-white font-semibold text-sm">
|
||||
{formatNumber(store.goods)}
|
||||
</div>
|
||||
<div
|
||||
className={`text-[10px] ${
|
||||
store.goodsChange >= 0
|
||||
? "text-green-400"
|
||||
: "text-red-400"
|
||||
}`}
|
||||
>
|
||||
{formatChange(store.goodsChange)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="px-3 py-2.5">
|
||||
<div className="text-white font-semibold text-sm">
|
||||
{formatNumber(store.defects)}
|
||||
</div>
|
||||
<div
|
||||
className={`text-[10px] ${
|
||||
store.defectsChange >= 0
|
||||
? "text-green-400"
|
||||
: "text-red-400"
|
||||
}`}
|
||||
>
|
||||
{formatChange(store.defectsChange)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="px-3 py-2.5">
|
||||
<div className="text-white font-semibold text-sm">
|
||||
{formatNumber(store.sellerSupplies)}
|
||||
</div>
|
||||
<div
|
||||
className={`text-[10px] ${
|
||||
store.sellerSuppliesChange >= 0
|
||||
? "text-green-400"
|
||||
: "text-red-400"
|
||||
}`}
|
||||
>
|
||||
{formatChange(store.sellerSuppliesChange)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="px-3 py-2.5">
|
||||
<div className="text-white font-semibold text-sm">
|
||||
{formatNumber(store.pvzReturns)}
|
||||
</div>
|
||||
<div
|
||||
className={`text-[10px] ${
|
||||
store.pvzReturnsChange >= 0
|
||||
? "text-green-400"
|
||||
: "text-red-400"
|
||||
}`}
|
||||
>
|
||||
{formatChange(store.pvzReturnsChange)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="px-3 py-2.5">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => toggleStoreExpansion(store.id)}
|
||||
className="text-white/60 hover:text-white hover:bg-white/10 h-6 w-6 p-0"
|
||||
>
|
||||
{expandedStores.has(store.id) ? (
|
||||
<ChevronUp className="h-3 w-3" />
|
||||
) : (
|
||||
<ChevronDown className="h-3 w-3" />
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Расширенная информация */}
|
||||
{expandedStores.has(store.id) && (
|
||||
<div className="bg-white/5 px-3 py-3 border-t border-white/10">
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
|
||||
<div className="glass-secondary rounded-lg p-2">
|
||||
<div className="flex items-center space-x-1.5 mb-1">
|
||||
<Package2 className="h-3 w-3 text-blue-400" />
|
||||
<span className="text-blue-300 text-xs font-medium">
|
||||
Продукты
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-white text-sm font-bold">
|
||||
{formatNumber(store.products)}
|
||||
</div>
|
||||
<div className="text-blue-200/60 text-[10px]">
|
||||
Готовые к отправке
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="glass-secondary rounded-lg p-2">
|
||||
<div className="flex items-center space-x-1.5 mb-1">
|
||||
<Package className="h-3 w-3 text-cyan-400" />
|
||||
<span className="text-cyan-300 text-xs font-medium">
|
||||
Товары
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-white text-sm font-bold">
|
||||
{formatNumber(store.goods)}
|
||||
</div>
|
||||
<div className="text-cyan-200/60 text-[10px]">
|
||||
В обработке
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="glass-secondary rounded-lg p-2">
|
||||
<div className="flex items-center space-x-1.5 mb-1">
|
||||
<AlertTriangle className="h-3 w-3 text-red-400" />
|
||||
<span className="text-red-300 text-xs font-medium">
|
||||
Брак
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-white text-sm font-bold">
|
||||
{formatNumber(store.defects)}
|
||||
</div>
|
||||
<div className="text-red-200/60 text-[10px]">
|
||||
К утилизации
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="glass-secondary rounded-lg p-2">
|
||||
<div className="flex items-center space-x-1.5 mb-1">
|
||||
<RotateCcw className="h-3 w-3 text-yellow-400" />
|
||||
<span className="text-yellow-300 text-xs font-medium">
|
||||
Возвраты
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-white text-sm font-bold">
|
||||
{formatNumber(store.pvzReturns)}
|
||||
</div>
|
||||
<div className="text-yellow-200/60 text-[10px]">
|
||||
С ПВЗ
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
Reference in New Issue
Block a user