Добавлен новый компонент FulfillmentWarehouseDemo и соответствующая вкладка в UIKitSection. Обновлены импорты и форматирование кода для улучшения читаемости и структуры. Исправлены стили вкладок для более удобного взаимодействия с пользователем.

This commit is contained in:
Veronika Smirnova
2025-07-23 14:07:40 +03:00
parent ac3578aa7f
commit 87b1e4724d
2 changed files with 425 additions and 36 deletions

View File

@ -0,0 +1,332 @@
"use client";
import { useState } from "react";
import { ChevronUp, ChevronDown, TrendingUp, TrendingDown } from "lucide-react";
interface TableColumn {
key: string;
title: string;
sortable: boolean;
}
interface TableData {
id: number;
number: string;
store: string;
product: string;
goods: number;
defects: number;
sellerSupplies: number;
pvzReturns: number;
}
const columns: TableColumn[] = [
{ key: "number", title: "№", sortable: true },
{ key: "store", title: "Магазин", sortable: true },
{ key: "product", title: "Продукт", sortable: true },
{ key: "goods", title: "Товар", sortable: true },
{ key: "defects", title: "Брак", sortable: true },
{ key: "sellerSupplies", title: "Расходники селлеров", sortable: true },
{ key: "pvzReturns", title: "Возвраты с ПВЗ", sortable: true },
];
const sampleData: TableData[] = [
{
id: 1,
number: "001",
store: "Wildberries",
product: "Смартфон iPhone 15",
goods: 150,
defects: 5,
sellerSupplies: 25,
pvzReturns: 12,
},
{
id: 2,
number: "002",
store: "Ozon",
product: "Наушники AirPods",
goods: 89,
defects: 3,
sellerSupplies: 18,
pvzReturns: 8,
},
{
id: 3,
number: "003",
store: "Яндекс Маркет",
product: "Планшет iPad",
goods: 67,
defects: 2,
sellerSupplies: 12,
pvzReturns: 5,
},
{
id: 4,
number: "004",
store: "Wildberries",
product: "Умные часы",
goods: 234,
defects: 8,
sellerSupplies: 45,
pvzReturns: 18,
},
{
id: 5,
number: "005",
store: "Ozon",
product: "Ноутбук MacBook",
goods: 45,
defects: 1,
sellerSupplies: 8,
pvzReturns: 3,
},
];
type SortField = keyof TableData | null;
type SortDirection = "asc" | "desc";
export function FulfillmentWarehouseDemo() {
const [sortField, setSortField] = useState<SortField>(null);
const [sortDirection, setSortDirection] = useState<SortDirection>("asc");
const handleSort = (field: keyof TableData) => {
if (sortField === field) {
setSortDirection(sortDirection === "asc" ? "desc" : "asc");
} else {
setSortField(field);
setSortDirection("asc");
}
};
const sortedData = [...sampleData].sort((a, b) => {
if (!sortField) return 0;
const aValue = a[sortField];
const bValue = b[sortField];
if (typeof aValue === "string" && typeof bValue === "string") {
return sortDirection === "asc"
? aValue.localeCompare(bValue)
: bValue.localeCompare(aValue);
}
if (typeof aValue === "number" && typeof bValue === "number") {
return sortDirection === "asc" ? aValue - bValue : bValue - aValue;
}
return 0;
});
// Вычисляем суммарные значения
const totals = {
goods: sampleData.reduce((sum, item) => sum + item.goods, 0),
defects: sampleData.reduce((sum, item) => sum + item.defects, 0),
sellerSupplies: sampleData.reduce(
(sum, item) => sum + item.sellerSupplies,
0
),
pvzReturns: sampleData.reduce((sum, item) => sum + item.pvzReturns, 0),
};
const SortIcon = ({ field }: { field: keyof TableData }) => {
if (sortField !== field) {
return (
<div className="flex flex-col ml-1 opacity-30">
<ChevronUp size={12} />
<ChevronDown size={12} className="-mt-1" />
</div>
);
}
return sortDirection === "asc" ? (
<ChevronUp size={14} className="ml-1 text-blue-400" />
) : (
<ChevronDown size={14} className="ml-1 text-blue-400" />
);
};
const StatCell = ({
value,
prevValue,
nextValue,
}: {
value: number;
prevValue: number;
nextValue: number;
}) => (
<div className="flex items-center justify-center gap-2">
<div className="flex items-center text-xs text-green-400">
<TrendingUp size={12} className="mr-1" />
{prevValue}
</div>
<div className="text-lg font-semibold text-white">
{value.toLocaleString()}
</div>
<div className="flex items-center text-xs text-red-400">
<TrendingDown size={12} className="mr-1" />
{nextValue}
</div>
</div>
);
return (
<div className="space-y-8">
<div>
<h2 className="text-2xl font-bold text-white mb-4">Склад фулфилмент</h2>
<p className="text-white/70 mb-6">
Многоуровневая таблица с сортировкой и агрегированными данными
</p>
</div>
<div className="bg-white/5 backdrop-blur border border-white/10 rounded-lg overflow-hidden">
{/* Заголовки таблицы */}
<div className="bg-white/10 border-b border-white/10">
<div className="grid grid-cols-7 gap-4 p-4">
{columns.map((column) => (
<div
key={column.key}
className={`flex items-center justify-center text-sm font-medium text-white/90 ${
column.sortable ? "cursor-pointer hover:text-white" : ""
}`}
onClick={() =>
column.sortable && handleSort(column.key as keyof TableData)
}
>
{column.title}
{column.sortable && (
<SortIcon field={column.key as keyof TableData} />
)}
</div>
))}
</div>
</div>
{/* Суммарные значения */}
<div className="bg-white/5 border-b border-white/10">
<div className="grid grid-cols-7 gap-4 p-6">
<div className="text-center">
<div className="text-sm text-white/60">Всего</div>
</div>
<div className="text-center">
<div className="text-sm text-white/60">Магазинов</div>
<div className="text-lg font-semibold text-white">3</div>
</div>
<div className="text-center">
<div className="text-sm text-white/60">Продуктов</div>
<div className="text-lg font-semibold text-white">5</div>
</div>
<div className="text-center">
<div className="text-sm text-white/60 mb-2">Товар</div>
<StatCell value={totals.goods} prevValue={520} nextValue={610} />
</div>
<div className="text-center">
<div className="text-sm text-white/60 mb-2">Брак</div>
<StatCell value={totals.defects} prevValue={15} nextValue={25} />
</div>
<div className="text-center">
<div className="text-sm text-white/60 mb-2">Расходники</div>
<StatCell
value={totals.sellerSupplies}
prevValue={95}
nextValue={125}
/>
</div>
<div className="text-center">
<div className="text-sm text-white/60 mb-2">Возвраты</div>
<StatCell
value={totals.pvzReturns}
prevValue={38}
nextValue={55}
/>
</div>
</div>
</div>
{/* Данные таблицы */}
<div className="divide-y divide-white/10">
{sortedData.map((row) => (
<div
key={row.id}
className="grid grid-cols-7 gap-4 p-4 hover:bg-white/5 transition-colors"
>
<div className="text-center text-white/80">{row.number}</div>
<div className="text-center text-white/80">{row.store}</div>
<div
className="text-center text-white/80 truncate"
title={row.product}
>
{row.product}
</div>
<div className="text-center text-white font-medium">
{row.goods.toLocaleString()}
</div>
<div className="text-center text-red-400 font-medium">
{row.defects}
</div>
<div className="text-center text-yellow-400 font-medium">
{row.sellerSupplies}
</div>
<div className="text-center text-orange-400 font-medium">
{row.pvzReturns}
</div>
</div>
))}
</div>
</div>
{/* Дополнительная информация */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div className="bg-white/5 backdrop-blur border border-white/10 rounded-lg p-4">
<h3 className="text-lg font-semibold text-white mb-2">
Особенности таблицы
</h3>
<ul className="text-sm text-white/70 space-y-1">
<li> Сортировка по всем столбцам</li>
<li> Агрегированные данные с индикаторами</li>
<li> Многоуровневая структура</li>
<li> Цветовое кодирование данных</li>
</ul>
</div>
<div className="bg-white/5 backdrop-blur border border-white/10 rounded-lg p-4">
<h3 className="text-lg font-semibold text-white mb-2">Индикаторы</h3>
<div className="space-y-2 text-sm">
<div className="flex items-center">
<TrendingUp size={16} className="text-green-400 mr-2" />
<span className="text-white/70">Рост (левое значение)</span>
</div>
<div className="flex items-center">
<TrendingDown size={16} className="text-red-400 mr-2" />
<span className="text-white/70">Падение (правое значение)</span>
</div>
</div>
</div>
<div className="bg-white/5 backdrop-blur border border-white/10 rounded-lg p-4">
<h3 className="text-lg font-semibold text-white mb-2">
Цветовая схема
</h3>
<div className="space-y-2 text-sm">
<div className="flex items-center">
<div className="w-3 h-3 bg-white rounded mr-2"></div>
<span className="text-white/70">Товары</span>
</div>
<div className="flex items-center">
<div className="w-3 h-3 bg-red-400 rounded mr-2"></div>
<span className="text-white/70">Брак</span>
</div>
<div className="flex items-center">
<div className="w-3 h-3 bg-yellow-400 rounded mr-2"></div>
<span className="text-white/70">Расходники</span>
</div>
<div className="flex items-center">
<div className="w-3 h-3 bg-orange-400 rounded mr-2"></div>
<span className="text-white/70">Возвраты</span>
</div>
</div>
</div>
</div>
</div>
);
}