Добавлен новый компонент 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

@ -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>
)
}
);
}

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>
);
}