Files
sfera-new/src/components/supplies/direct-supply-creation/blocks/ProductGridBlock.tsx
Veronika Smirnova 6647299a05 feat(refactor): complete modular architecture for direct-supply-creation
 ПЛАН ЭТАПЫ 2-5 ЗАВЕРШЕНЫ:

ПЛАН ЭТАП 2: Создание типов (direct-supply.types.ts)
- 🎯 206 строк типов и интерфейсов
- 📋 Все основные сущности: SupplyItem, Organization, FulfillmentService, etc
- 🔄 Пропсы для всех 5 блоков и 5 хуков
- 📊 Утилиты для расчетов и валидации

ПЛАН ЭТАП 3: Извлечение custom hooks (5 хуков)
- 🎯 useWildberriesProducts.ts (200 строк) - управление товарами WB
- 🔄 useSupplyManagement.ts (125 строк) - логика поставки и расчеты
- ⚙️ useFulfillmentServices.ts (130 строк) - услуги и расходники
- 👥 useSupplierForm.ts (145 строк) - форма поставщиков с валидацией
- 🚀 useSupplyCreation.ts (160 строк) - создание поставки и валидация

ПЛАН ЭТАП 4: Создание блок-компонентов (5 блоков)
- 🔍 ProductSearchBlock.tsx (65 строк) - поиск товаров
- 📦 ProductGridBlock.tsx (120 строк) - сетка товаров WB
- 📋 SupplyItemsBlock.tsx (165 строк) - управление товарами в поставке
- ⚙️ ServicesConfigBlock.tsx (145 строк) - настройка услуг и фулфилмента
- 👤 SupplierModalBlock.tsx (135 строк) - форма создания поставщика

ПЛАН ЭТАП 5: Интеграция в главном компоненте (245 строк)
- 🎯 Композиция всех 5 хуков и 5 блоков
- 🔄 Реактивные связи между модулями
- 📊 Передача callback'ов родительскому компоненту
-  Оптимизация через useCallback и React.memo

📊 АРХИТЕКТУРНЫЕ ДОСТИЖЕНИЯ:
- Модульность: 12 файлов vs 1 монолитный
- Переиспользуемость: блоки можно использовать отдельно
- Типизация: 100% TypeScript coverage
- Тестируемость: каждый хук и блок изолирован
- Производительность: React.memo + useCallback

🔧 ИСПРАВЛЕНЫ ОШИБКИ ESLint:
- Префиксы _ для неиспользуемых переменных
- useCallback для функций в dependencies
- Типизация unknown вместо any
- ESLint disable для img элемента

🎯 Готово к тестированию новой архитектуры\!

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-12 21:25:46 +03:00

124 lines
5.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* БЛОК СЕТКИ ТОВАРОВ
*
* Выделен из direct-supply-creation.tsx
* Отображает товары WB в виде красивой сетки с возможностью добавления
*/
'use client'
import { Plus, Package } from 'lucide-react'
import React from 'react'
import { WildberriesService } from '@/services/wildberries-service'
import type { ProductGridBlockProps } from '../types/direct-supply.types'
export const ProductGridBlock = React.memo(function ProductGridBlock({
wbCards,
loading,
onAddToSupply,
}: ProductGridBlockProps) {
if (loading) {
return (
<div className="bg-white/10 backdrop-blur-xl border border-white/20 rounded-2xl p-4">
<h3 className="text-white font-semibold text-lg mb-4">Товары (загрузка...)</h3>
{/* Skeleton сетка */}
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 xl:grid-cols-8 gap-3">
{[...Array(16)].map((_, i) => (
<div key={i} className="group">
<div className="aspect-[3/4] bg-gradient-to-br from-white/10 to-white/5 rounded-xl animate-pulse">
<div className="w-full h-full bg-white/5 rounded-xl"></div>
</div>
<div className="mt-1 px-1">
<div className="h-3 bg-white/10 rounded animate-pulse"></div>
</div>
</div>
))}
</div>
</div>
)
}
if (wbCards.length === 0) {
return (
<div className="bg-white/10 backdrop-blur-xl border border-white/20 rounded-2xl p-4">
<h3 className="text-white font-semibold text-lg mb-4">Товары (0)</h3>
{/* Пустое состояние */}
<div className="flex flex-col items-center justify-center py-12">
<div className="w-16 h-16 bg-gradient-to-r from-purple-500/20 to-blue-500/20 rounded-2xl flex items-center justify-center mb-4">
<Package className="w-8 h-8 text-white/40" />
</div>
<h3 className="text-white/80 font-medium text-base mb-2">Товары не найдены</h3>
<p className="text-white/50 text-sm text-center max-w-md">
Введите поисковый запрос или проверьте настройки API Wildberries
</p>
</div>
</div>
)
}
return (
<div className="relative">
<div className="bg-gradient-to-br from-white/15 via-white/10 to-white/5 backdrop-blur-xl border border-white/20 rounded-2xl p-4 shadow-2xl">
<div className="flex items-center justify-between mb-4">
<h3 className="text-white font-semibold text-lg">Товары ({wbCards.length})</h3>
<p className="text-white/60 text-sm">Нажмите на товар для добавления</p>
</div>
{/* Сетка товаров */}
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 xl:grid-cols-8 gap-3">
{wbCards.map((card) => (
<div
key={card.nmID}
className="group cursor-pointer transition-all duration-300 hover:scale-105"
onClick={() => onAddToSupply(card, 1, '')}
>
{/* Карточка товара */}
<div className="relative aspect-[3/4] rounded-xl overflow-hidden shadow-lg transition-all duration-300 bg-white/10 hover:bg-white/15 hover:shadow-xl">
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src={WildberriesService.getCardImage(card, 'c516x688') || '/api/placeholder/200/267'}
alt={card.title}
className="w-full h-full object-cover transition-transform duration-500 group-hover:scale-110"
loading="lazy"
/>
{/* Градиентный оверлей */}
<div className="absolute inset-0 bg-gradient-to-t from-black/60 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
{/* Информация при наведении */}
<div className="absolute bottom-0 left-0 right-0 p-3 transform translate-y-full group-hover:translate-y-0 transition-transform duration-300">
<h4 className="text-white font-medium text-sm line-clamp-2 mb-1">{card.title}</h4>
<p className="text-white/80 text-xs">WB: {card.nmID}</p>
{card.vendorCode && <p className="text-white/60 text-xs">Арт: {card.vendorCode}</p>}
</div>
{/* Кнопка добавления */}
<div className="absolute top-3 right-3 w-8 h-8 bg-white/20 backdrop-blur rounded-full flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-300">
<Plus className="w-4 h-4 text-white" />
</div>
{/* Эффект при клике */}
<div className="absolute inset-0 bg-white/20 opacity-0 group-active:opacity-100 transition-opacity duration-150" />
</div>
{/* Название под карточкой */}
<div className="mt-2 px-1">
<h4 className="text-white/90 font-medium text-xs line-clamp-2 leading-tight">{card.title}</h4>
{card.brand && <p className="text-white/50 text-[10px] mt-1">{card.brand}</p>}
</div>
</div>
))}
</div>
{/* Декоративные элементы */}
<div className="absolute -top-1 -left-1 w-4 h-4 bg-gradient-to-r from-purple-500 to-blue-500 rounded-full opacity-60 animate-pulse" />
<div className="absolute -bottom-1 -right-1 w-3 h-3 bg-gradient-to-r from-blue-500 to-purple-500 rounded-full opacity-40 animate-pulse delay-700" />
</div>
</div>
)
})