
КРИТИЧНЫЕ КОМПОНЕНТЫ ОПТИМИЗИРОВАНЫ: • AdminDashboard (346 kB) - добавлены React.memo, useCallback, useMemo • SellerStatisticsDashboard (329 kB) - мемоизация кэша и callback функций • CreateSupplyPage (276 kB) - оптимизированы вычисления и обработчики • EmployeesDashboard (268 kB) - мемоизация списков и функций • SalesTab + AdvertisingTab - React.memo обертка ТЕХНИЧЕСКИЕ УЛУЧШЕНИЯ: ✅ React.memo() для предотвращения лишних рендеров ✅ useMemo() для тяжелых вычислений ✅ useCallback() для стабильных ссылок на функции ✅ Мемоизация фильтрации и сортировки списков ✅ Оптимизация пропсов в компонентах-контейнерах РЕЗУЛЬТАТЫ: • Все компоненты успешно компилируются • Линтер проходит без критических ошибок • Сохранена вся функциональность • Улучшена производительность рендеринга • Снижена нагрузка на React дерево 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
226 lines
8.9 KiB
TypeScript
226 lines
8.9 KiB
TypeScript
'use client'
|
||
|
||
import { ArrowLeft, Search, Filter, BarChart3, Grid3X3, List, Download, RotateCcw, Layers } from 'lucide-react'
|
||
import { useRouter } from 'next/navigation'
|
||
import React from 'react'
|
||
|
||
import { Badge } from '@/components/ui/badge'
|
||
import { Button } from '@/components/ui/button'
|
||
import { Input } from '@/components/ui/input'
|
||
|
||
import { SuppliesHeaderProps } from './types'
|
||
|
||
export function SuppliesHeader({
|
||
viewMode,
|
||
onViewModeChange,
|
||
groupBy,
|
||
onGroupByChange,
|
||
filters,
|
||
onFiltersChange,
|
||
showFilters,
|
||
onToggleFilters,
|
||
onExport,
|
||
onRefresh,
|
||
}: SuppliesHeaderProps) {
|
||
const router = useRouter()
|
||
|
||
const handleFilterChange = (key: keyof typeof filters, value: any) => {
|
||
onFiltersChange({ ...filters, [key]: value })
|
||
}
|
||
|
||
return (
|
||
<div className="space-y-6">
|
||
{/* Заголовок страницы */}
|
||
<div className="flex items-center justify-between">
|
||
<div className="flex items-center space-x-4">
|
||
<Button
|
||
variant="ghost"
|
||
size="sm"
|
||
onClick={() => router.back()}
|
||
className="text-white/70 hover:text-white hover:bg-white/10"
|
||
>
|
||
<ArrowLeft className="h-4 w-4 mr-2" />
|
||
Назад
|
||
</Button>
|
||
|
||
<div>
|
||
<h1 className="text-2xl font-bold text-white mb-1">Расходники фулфилмента</h1>
|
||
<p className="text-white/60 text-sm">Управление расходными материалами фулфилмент-центра</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="flex items-center space-x-3">
|
||
{/* Экспорт данных */}
|
||
<Button
|
||
variant="outline"
|
||
size="sm"
|
||
onClick={onExport}
|
||
className="border-white/20 text-white/70 hover:text-white hover:bg-white/10"
|
||
>
|
||
<Download className="h-4 w-4 mr-2" />
|
||
Экспорт
|
||
</Button>
|
||
|
||
{/* Обновить */}
|
||
<Button
|
||
variant="outline"
|
||
size="sm"
|
||
onClick={onRefresh}
|
||
className="border-white/20 text-white/70 hover:text-white hover:bg-white/10"
|
||
>
|
||
<RotateCcw className="h-4 w-4" />
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Панель управления */}
|
||
<div className="flex items-center justify-between">
|
||
<div className="flex items-center space-x-4">
|
||
{/* Поиск */}
|
||
<div className="relative">
|
||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-white/40" />
|
||
<Input
|
||
placeholder="Поиск расходников..."
|
||
value={filters.search}
|
||
onChange={(e) => handleFilterChange('search', e.target.value)}
|
||
className="pl-10 w-64 bg-white/5 border-white/20 text-white placeholder:text-white/40 focus:border-blue-400"
|
||
/>
|
||
</div>
|
||
|
||
{/* Фильтры */}
|
||
<Button
|
||
variant="outline"
|
||
size="sm"
|
||
onClick={onToggleFilters}
|
||
className={`border-white/20 ${
|
||
showFilters ? 'bg-white/10 text-white' : 'text-white/70 hover:text-white hover:bg-white/10'
|
||
}`}
|
||
>
|
||
<Filter className="h-4 w-4 mr-2" />
|
||
Фильтры
|
||
{(filters.category || filters.status || filters.supplier || filters.lowStock) && (
|
||
<Badge className="ml-2 bg-blue-500/20 text-blue-300 text-xs">
|
||
{[filters.category, filters.status, filters.supplier, filters.lowStock].filter(Boolean).length}
|
||
</Badge>
|
||
)}
|
||
</Button>
|
||
</div>
|
||
|
||
<div className="flex items-center space-x-3">
|
||
{/* Переключатель режимов просмотра */}
|
||
<div className="flex items-center bg-white/5 rounded-lg p-1">
|
||
<Button
|
||
variant={viewMode === 'grid' ? 'default' : 'ghost'}
|
||
size="sm"
|
||
onClick={() => onViewModeChange('grid')}
|
||
className={`h-8 px-3 ${
|
||
viewMode === 'grid' ? 'bg-blue-500 text-white' : 'text-white/70 hover:text-white hover:bg-white/10'
|
||
}`}
|
||
>
|
||
<Grid3X3 className="h-4 w-4" />
|
||
</Button>
|
||
<Button
|
||
variant={viewMode === 'list' ? 'default' : 'ghost'}
|
||
size="sm"
|
||
onClick={() => onViewModeChange('list')}
|
||
className={`h-8 px-3 ${
|
||
viewMode === 'list' ? 'bg-blue-500 text-white' : 'text-white/70 hover:text-white hover:bg-white/10'
|
||
}`}
|
||
>
|
||
<List className="h-4 w-4" />
|
||
</Button>
|
||
<Button
|
||
variant={viewMode === 'analytics' ? 'default' : 'ghost'}
|
||
size="sm"
|
||
onClick={() => onViewModeChange('analytics')}
|
||
className={`h-8 px-3 ${
|
||
viewMode === 'analytics' ? 'bg-blue-500 text-white' : 'text-white/70 hover:text-white hover:bg-white/10'
|
||
}`}
|
||
>
|
||
<BarChart3 className="h-4 w-4" />
|
||
</Button>
|
||
</div>
|
||
|
||
{/* Группировка */}
|
||
{viewMode !== 'analytics' && (
|
||
<div className="flex items-center space-x-2">
|
||
<Layers className="h-4 w-4 text-white/60" />
|
||
<select
|
||
value={groupBy}
|
||
onChange={(e) => onGroupByChange(e.target.value as any)}
|
||
className="bg-white/5 border border-white/20 rounded-md px-3 py-1 text-sm text-white focus:border-blue-400 focus:outline-none"
|
||
>
|
||
<option value="none">Без группировки</option>
|
||
<option value="category">По категориям</option>
|
||
<option value="status">По статусу</option>
|
||
<option value="supplier">По поставщикам</option>
|
||
</select>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Развернутые фильтры */}
|
||
{showFilters && (
|
||
<div className="bg-white/5 rounded-lg p-4 space-y-4">
|
||
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||
<div>
|
||
<label className="block text-sm font-medium text-white/70 mb-2">Категория</label>
|
||
<select
|
||
value={filters.category}
|
||
onChange={(e) => handleFilterChange('category', e.target.value)}
|
||
className="w-full bg-white/5 border border-white/20 rounded-md px-3 py-2 text-sm text-white focus:border-blue-400 focus:outline-none"
|
||
>
|
||
<option value="">Все категории</option>
|
||
<option value="packaging">Упаковка</option>
|
||
<option value="tools">Инструменты</option>
|
||
<option value="maintenance">Обслуживание</option>
|
||
<option value="office">Офисные принадлежности</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-white/70 mb-2">Статус</label>
|
||
<select
|
||
value={filters.status}
|
||
onChange={(e) => handleFilterChange('status', e.target.value)}
|
||
className="w-full bg-white/5 border border-white/20 rounded-md px-3 py-2 text-sm text-white focus:border-blue-400 focus:outline-none"
|
||
>
|
||
<option value="">Все статусы</option>
|
||
<option value="available">Доступен</option>
|
||
<option value="low-stock">Мало на складе</option>
|
||
<option value="out-of-stock">Нет в наличии</option>
|
||
<option value="in-transit">В пути</option>
|
||
<option value="reserved">Зарезервирован</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-white/70 mb-2">Поставщик</label>
|
||
<Input
|
||
placeholder="Поставщик..."
|
||
value={filters.supplier}
|
||
onChange={(e) => handleFilterChange('supplier', e.target.value)}
|
||
className="bg-white/5 border-white/20 text-white placeholder:text-white/40 focus:border-blue-400"
|
||
/>
|
||
</div>
|
||
|
||
<div className="flex items-center space-x-2 pt-6">
|
||
<input
|
||
type="checkbox"
|
||
id="lowStock"
|
||
checked={filters.lowStock}
|
||
onChange={(e) => handleFilterChange('lowStock', e.target.checked)}
|
||
className="rounded border-white/20 bg-white/5 text-blue-500 focus:ring-blue-400"
|
||
/>
|
||
<label htmlFor="lowStock" className="text-sm text-white/70">
|
||
Только с низким остатком
|
||
</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
)
|
||
}
|