Добавлен новый компонент FulfillmentWarehouseDemo и соответствующая вкладка в UIKitSection. Обновлены импорты и форматирование кода для улучшения читаемости и структуры. Исправлены стили вкладок для более удобного взаимодействия с пользователем.
This commit is contained in:
@ -1,78 +1,131 @@
|
||||
"use client"
|
||||
"use client";
|
||||
|
||||
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
import { ButtonsDemo } from './ui-kit/buttons-demo'
|
||||
import { FormsDemo } from './ui-kit/forms-demo'
|
||||
import { CardsDemo } from './ui-kit/cards-demo'
|
||||
import { TypographyDemo } from './ui-kit/typography-demo'
|
||||
import { ColorsDemo } from './ui-kit/colors-demo'
|
||||
import { IconsDemo } from './ui-kit/icons-demo'
|
||||
import { LayoutsDemo } from './ui-kit/layouts-demo'
|
||||
import { NavigationDemo } from './ui-kit/navigation-demo'
|
||||
import { SpecializedDemo } from './ui-kit/specialized-demo'
|
||||
import { AnimationsDemo } from './ui-kit/animations-demo'
|
||||
import { StatesDemo } from './ui-kit/states-demo'
|
||||
import { MediaDemo } from './ui-kit/media-demo'
|
||||
import { InteractiveDemo } from './ui-kit/interactive-demo'
|
||||
import { BusinessDemo } from './ui-kit/business-demo'
|
||||
import { TimesheetDemo } from './ui-kit/timesheet-demo'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { ButtonsDemo } from "./ui-kit/buttons-demo";
|
||||
import { FormsDemo } from "./ui-kit/forms-demo";
|
||||
import { CardsDemo } from "./ui-kit/cards-demo";
|
||||
import { TypographyDemo } from "./ui-kit/typography-demo";
|
||||
import { ColorsDemo } from "./ui-kit/colors-demo";
|
||||
import { IconsDemo } from "./ui-kit/icons-demo";
|
||||
import { LayoutsDemo } from "./ui-kit/layouts-demo";
|
||||
import { NavigationDemo } from "./ui-kit/navigation-demo";
|
||||
import { SpecializedDemo } from "./ui-kit/specialized-demo";
|
||||
import { AnimationsDemo } from "./ui-kit/animations-demo";
|
||||
import { StatesDemo } from "./ui-kit/states-demo";
|
||||
import { MediaDemo } from "./ui-kit/media-demo";
|
||||
import { InteractiveDemo } from "./ui-kit/interactive-demo";
|
||||
import { BusinessDemo } from "./ui-kit/business-demo";
|
||||
import { TimesheetDemo } from "./ui-kit/timesheet-demo";
|
||||
import { FulfillmentWarehouseDemo } from "./ui-kit/fulfillment-warehouse-demo";
|
||||
|
||||
export function UIKitSection() {
|
||||
return (
|
||||
<div className="p-8">
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold text-white mb-2">UI Kit</h1>
|
||||
<p className="text-white/70">Полная коллекция компонентов дизайн-системы SferaV</p>
|
||||
<p className="text-white/70">
|
||||
Полная коллекция компонентов дизайн-системы SferaV
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Tabs defaultValue="buttons" className="w-full">
|
||||
<TabsList className="flex flex-wrap gap-1 bg-white/5 backdrop-blur border-white/10 p-2 rounded-lg mb-8 h-auto">
|
||||
<TabsTrigger value="buttons" className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 text-xs px-3 py-2">
|
||||
<TabsTrigger
|
||||
value="buttons"
|
||||
className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 text-xs px-3 py-2"
|
||||
>
|
||||
Кнопки
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="forms" className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 text-xs px-3 py-2">
|
||||
<TabsTrigger
|
||||
value="forms"
|
||||
className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 text-xs px-3 py-2"
|
||||
>
|
||||
Формы
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="cards" className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 text-xs px-3 py-2">
|
||||
<TabsTrigger
|
||||
value="cards"
|
||||
className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 text-xs px-3 py-2"
|
||||
>
|
||||
Карточки
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="typography" className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 text-xs px-3 py-2">
|
||||
<TabsTrigger
|
||||
value="typography"
|
||||
className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 text-xs px-3 py-2"
|
||||
>
|
||||
Типографика
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="colors" className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 text-xs px-3 py-2">
|
||||
<TabsTrigger
|
||||
value="colors"
|
||||
className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 text-xs px-3 py-2"
|
||||
>
|
||||
Цвета
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="icons" className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 text-xs px-3 py-2">
|
||||
<TabsTrigger
|
||||
value="icons"
|
||||
className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 text-xs px-3 py-2"
|
||||
>
|
||||
Иконки
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="layouts" className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 text-xs px-3 py-2">
|
||||
<TabsTrigger
|
||||
value="layouts"
|
||||
className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 text-xs px-3 py-2"
|
||||
>
|
||||
Макеты
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="navigation" className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 text-xs px-3 py-2">
|
||||
<TabsTrigger
|
||||
value="navigation"
|
||||
className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 text-xs px-3 py-2"
|
||||
>
|
||||
Навигация
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="specialized" className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 text-xs px-3 py-2">
|
||||
<TabsTrigger
|
||||
value="specialized"
|
||||
className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 text-xs px-3 py-2"
|
||||
>
|
||||
Специальные
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="animations" className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 text-xs px-3 py-2">
|
||||
<TabsTrigger
|
||||
value="animations"
|
||||
className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 text-xs px-3 py-2"
|
||||
>
|
||||
Анимации
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="states" className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 text-xs px-3 py-2">
|
||||
<TabsTrigger
|
||||
value="states"
|
||||
className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 text-xs px-3 py-2"
|
||||
>
|
||||
Состояния
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="media" className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 text-xs px-3 py-2">
|
||||
<TabsTrigger
|
||||
value="media"
|
||||
className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 text-xs px-3 py-2"
|
||||
>
|
||||
Медиа
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="interactive" className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 text-xs px-3 py-2">
|
||||
<TabsTrigger
|
||||
value="interactive"
|
||||
className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 text-xs px-3 py-2"
|
||||
>
|
||||
Интерактив
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="business" className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 text-xs px-3 py-2">
|
||||
<TabsTrigger
|
||||
value="business"
|
||||
className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 text-xs px-3 py-2"
|
||||
>
|
||||
Бизнес
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="timesheet" className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 text-xs px-3 py-2">
|
||||
<TabsTrigger
|
||||
value="timesheet"
|
||||
className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 text-xs px-3 py-2"
|
||||
>
|
||||
Табель
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="fulfillment-warehouse"
|
||||
className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 text-xs px-3 py-2"
|
||||
>
|
||||
Склад фулфилмент
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="buttons" className="space-y-6">
|
||||
@ -134,7 +187,11 @@ export function UIKitSection() {
|
||||
<TabsContent value="timesheet" className="space-y-6">
|
||||
<TimesheetDemo />
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="fulfillment-warehouse" className="space-y-6">
|
||||
<FulfillmentWarehouseDemo />
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
}
|
||||
|
332
src/components/admin/ui-kit/fulfillment-warehouse-demo.tsx
Normal file
332
src/components/admin/ui-kit/fulfillment-warehouse-demo.tsx
Normal 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>
|
||||
);
|
||||
}
|
Reference in New Issue
Block a user