diff --git a/src/components/supplies/create-suppliers/blocks/CartBlock.tsx b/src/components/supplies/create-suppliers/blocks/CartBlock.tsx new file mode 100644 index 0000000..acc0262 --- /dev/null +++ b/src/components/supplies/create-suppliers/blocks/CartBlock.tsx @@ -0,0 +1,158 @@ +/** + * БЛОК КОРЗИНЫ И НАСТРОЕК ПОСТАВКИ + * + * Выделен из create-suppliers-supply-page.tsx + * Отображение корзины, настроек доставки и создание поставки + */ + +'use client' + +import { ShoppingCart, X } from 'lucide-react' + +import { Button } from '@/components/ui/button' +import { DatePicker } from '@/components/ui/date-picker' + +import type { CartBlockProps } from '../types/supply-creation.types' + +export function CartBlock({ + selectedGoods, + selectedSupplier, + deliveryDate, + selectedFulfillment, + selectedLogistics, + allCounterparties, + totalAmount, + isFormValid, + isCreatingSupply, + onLogisticsChange, + onCreateSupply, + onItemRemove, +}: CartBlockProps) { + return ( +
+
+

+ + Корзина ({selectedGoods.length} шт) +

+ + {selectedGoods.length === 0 ? ( +
+
+ +
+

Корзина пуста

+

Добавьте товары из каталога для создания поставки

+
+ ) : ( + <> + {/* Список товаров в корзине */} +
+ {selectedGoods.map((item) => { + const priceWithRecipe = item.price // Здесь будет расчет с рецептурой + + return ( +
+
+

{item.name}

+

+ {priceWithRecipe.toLocaleString('ru-RU')} ₽ × {item.selectedQuantity} +

+
+ +
+ ) + })} +
+ + {/* Настройки поставки */} +
+
+

Дата поставки:

+ { + // Логика установки даты будет в родительском компоненте + }} + className="w-full" + /> +
+ + {selectedSupplier && ( +
+

Поставщик:

+

{selectedSupplier.name || selectedSupplier.fullName}

+
+ )} + + {deliveryDate && ( +
+

Дата поставки:

+

{new Date(deliveryDate).toLocaleDateString('ru-RU')}

+
+ )} + + {selectedFulfillment && ( +
+

Фулфилмент-центр:

+

+ {allCounterparties?.find((c) => c.id === selectedFulfillment)?.name || + allCounterparties?.find((c) => c.id === selectedFulfillment)?.fullName || + 'Выбранный центр'} +

+
+ )} + + {/* Выбор логистической компании */} +
+

Логистическая компания:

+ +
+
+ + {/* Итоговая сумма и кнопка создания */} +
+ Итого: + {totalAmount.toLocaleString('ru-RU')} ₽ +
+ + + + {/* Подсказка о валидации */} + {!isFormValid && selectedGoods.length > 0 && ( +
+ Заполните все поля и добавьте услуги к товарам +
+ )} + + )} +
+
+ ) +} diff --git a/src/components/supplies/create-suppliers/blocks/DetailedCatalogBlock.tsx b/src/components/supplies/create-suppliers/blocks/DetailedCatalogBlock.tsx new file mode 100644 index 0000000..08d6b9d --- /dev/null +++ b/src/components/supplies/create-suppliers/blocks/DetailedCatalogBlock.tsx @@ -0,0 +1,389 @@ +/** + * БЛОК ДЕТАЛЬНОГО КАТАЛОГА С РЕЦЕПТУРОЙ + * + * Выделен из create-suppliers-supply-page.tsx + * Детальный просмотр товаров с настройкой рецептуры и панелью управления + */ + +'use client' + +import { Package, Settings, Building2 } from 'lucide-react' +import Image from 'next/image' + +import { Badge } from '@/components/ui/badge' +import { DatePicker } from '@/components/ui/date-picker' +import { Input } from '@/components/ui/input' +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' + +import type { + DetailedCatalogBlockProps, + GoodsProduct, + ProductRecipe, + FulfillmentService, + FulfillmentConsumable, + SellerConsumable, +} from '../types/supply-creation.types' + +export function DetailedCatalogBlock({ + allSelectedProducts, + productRecipes, + fulfillmentServices, + fulfillmentConsumables, + sellerConsumables, + deliveryDate, + selectedFulfillment, + allCounterparties, + onQuantityChange, + onRecipeChange, + onDeliveryDateChange, + onFulfillmentChange, + onProductRemove, +}: DetailedCatalogBlockProps) { + const fulfillmentCenters = allCounterparties?.filter((partner) => partner.type === 'FULFILLMENT') || [] + + return ( +
+ {/* Панель управления */} +
+

+ + 3. Настройки поставки +

+ +
+ {/* Дата поставки */} +
+ + { + if (date) { + onDeliveryDateChange(date.toISOString().split('T')[0]) + } + }} + className="w-full" + /> +
+ + {/* Фулфилмент-центр */} +
+ + +
+
+
+ + {/* Каталог товаров с рецептурой */} +
+

Товары в поставке ({allSelectedProducts.length})

+ + {allSelectedProducts.length === 0 ? ( +
+
+ +
+

Товары не добавлены

+

Выберите товары из каталога выше для настройки рецептуры

+
+ ) : ( +
+ {allSelectedProducts.map((product) => { + const recipe = productRecipes[product.id] + const selectedServicesIds = recipe?.selectedServices || [] + const selectedFFConsumablesIds = recipe?.selectedFFConsumables || [] + const selectedSellerConsumablesIds = recipe?.selectedSellerConsumables || [] + + return ( + + ) + })} +
+ )} +
+
+ ) +} + +// Компонент детальной карточки товара с рецептурой +interface ProductDetailCardProps { + product: GoodsProduct & { selectedQuantity: number } + recipe?: ProductRecipe + fulfillmentServices: FulfillmentService[] + fulfillmentConsumables: FulfillmentConsumable[] + sellerConsumables: SellerConsumable[] + selectedServicesIds: string[] + selectedFFConsumablesIds: string[] + selectedSellerConsumablesIds: string[] + onQuantityChange: (productId: string, quantity: number) => void + onRecipeChange: (productId: string, recipe: ProductRecipe) => void + onRemove: (productId: string) => void +} + +function ProductDetailCard({ + product, + selectedServicesIds, + selectedFFConsumablesIds, + selectedSellerConsumablesIds, + fulfillmentServices, + fulfillmentConsumables, + sellerConsumables, + onQuantityChange, + onRemove, +}: ProductDetailCardProps) { + return ( +
+
+ {/* 1. ИЗОБРАЖЕНИЕ ТОВАРА (фиксированная ширина) */} +
+
+ {product.mainImage || (product.images && product.images[0]) ? ( + {product.name} + ) : ( +
+ +
+ )} +
+
+ + {/* 2. ОСНОВНАЯ ИНФОРМАЦИЯ (flex-1) */} +
+
+
+
{product.name}
+ {product.article &&

Арт: {product.article}

} +
+ +
+ + {product.category?.name && ( + + {product.category.name} + + )} + +
+ {product.price.toLocaleString('ru-RU')} ₽/{product.unit || 'шт'} +
+
+ + {/* 3. КОЛИЧЕСТВО/СУММА/ОСТАТОК (flex-1) */} +
+
+ {product.quantity !== undefined && ( +
+
0 ? 'bg-green-400' : 'bg-red-400'}`} /> + 0 ? 'text-green-400' : 'text-red-400'}`}> + {product.quantity > 0 ? `${product.quantity} шт` : 'Нет в наличии'} + +
+ )} + +
+ { + const inputValue = e.target.value + const newQuantity = inputValue === '' ? 0 : Math.max(0, parseInt(inputValue) || 0) + onQuantityChange(product.id, newQuantity) + }} + className="glass-input w-16 h-8 text-sm text-center text-white placeholder:text-white/50" + placeholder="0" + /> + шт +
+ +
+ {(product.price * product.selectedQuantity).toLocaleString('ru-RU')} ₽ +
+
+
+ + {/* 4-7. КОМПОНЕНТЫ РЕЦЕПТУРЫ */} + +
+
+ ) +} + +// Компонент компонентов рецептуры (услуги + расходники + WB карточка) +interface RecipeComponentsProps { + productId: string + selectedQuantity: number + selectedServicesIds: string[] + selectedFFConsumablesIds: string[] + selectedSellerConsumablesIds: string[] + fulfillmentServices: FulfillmentService[] + fulfillmentConsumables: FulfillmentConsumable[] + sellerConsumables: SellerConsumable[] +} + +function RecipeComponents({ + selectedServicesIds, + selectedFFConsumablesIds, + selectedSellerConsumablesIds, + fulfillmentServices, + fulfillmentConsumables, + sellerConsumables, +}: RecipeComponentsProps) { + return ( + <> + {/* 4. УСЛУГИ ФФ (flex-1) */} +
+
+
🛠️ Услуги ФФ
+
+
+ {fulfillmentServices.length > 0 ? ( + fulfillmentServices.map((service) => { + const isSelected = selectedServicesIds.includes(service.id) + return ( + + ) + }) + ) : ( +
Нет услуг
+ )} +
+
+ + {/* 5. РАСХОДНИКИ ФФ (flex-1) */} +
+
+
📦 Расходники ФФ
+
+
+ {fulfillmentConsumables.length > 0 ? ( + fulfillmentConsumables.map((consumable) => { + const isSelected = selectedFFConsumablesIds.includes(consumable.id) + return ( + + ) + }) + ) : ( +
Загрузка...
+ )} +
+
+ + {/* 6. РАСХОДНИКИ СЕЛЛЕРА (flex-1) */} +
+
+
🏪 Расходники сел.
+
+
+ {sellerConsumables.length > 0 ? ( + sellerConsumables.map((consumable) => { + const isSelected = selectedSellerConsumablesIds.includes(consumable.id) + return ( + + ) + }) + ) : ( +
Загрузка...
+ )} +
+
+ + {/* 7. МП + ИТОГО (flex-1) */} +
+
+
{/* Здесь будет общая стоимость с рецептурой */}
+ +
+
+ + ) +} diff --git a/src/components/supplies/create-suppliers/blocks/ProductCardsBlock.tsx b/src/components/supplies/create-suppliers/blocks/ProductCardsBlock.tsx new file mode 100644 index 0000000..35011b7 --- /dev/null +++ b/src/components/supplies/create-suppliers/blocks/ProductCardsBlock.tsx @@ -0,0 +1,144 @@ +/** + * БЛОК КАРТОЧЕК ТОВАРОВ (МИНИ-ПРЕВЬЮ) + * + * Выделен из create-suppliers-supply-page.tsx + * Горизонтальный скролл мини-карточек товаров поставщика + */ + +'use client' + +import { Package, Plus } from 'lucide-react' +import Image from 'next/image' + +import { Badge } from '@/components/ui/badge' + +import type { ProductCardsBlockProps } from '../types/supply-creation.types' + +export function ProductCardsBlock({ products, selectedSupplier, onProductAdd }: ProductCardsBlockProps) { + if (!selectedSupplier) { + return ( +
+
+
+ +
+

Выберите поставщика

+

Для просмотра каталога товаров сначала выберите поставщика

+
+
+ ) + } + + if (products.length === 0) { + return ( +
+

2. Товары поставщика (0)

+
+
+ +
+

Товары не найдены

+

У выбранного поставщика пока нет доступных товаров

+
+
+ ) + } + + return ( +
+

2. Товары поставщика ({products.length})

+ +
+
+ {products.slice(0, 10).map( + ( + product, // Показываем первые 10 товаров + ) => ( +
+ {/* Изображение товара */} +
+ {product.mainImage || (product.images && product.images[0]) ? ( + {product.name} + ) : ( +
+ +
+ )} + + {/* Статус наличия */} +
+ {product.quantity !== undefined && ( +
0 ? 'bg-green-400' : 'bg-red-400'}`} /> + )} +
+
+ + {/* Информация о товаре */} +
+
+

{product.name}

+ {product.article &&

Арт: {product.article}

} +
+ + {/* Категория */} + {product.category?.name && ( + + {product.category.name} + + )} + + {/* Цена и наличие */} +
+ {product.price.toLocaleString('ru-RU')} ₽ + {product.quantity !== undefined && ( + 0 ? 'text-green-400' : 'text-red-400'}`}> + {product.quantity > 0 ? `${product.quantity} шт` : 'Нет в наличии'} + + )} +
+ + {/* Кнопка добавления */} + +
+
+ ), + )} + + {/* Показать больше товаров */} + {products.length > 10 && ( +
+
+ +

Показать все

+

+{products.length - 10} товаров

+
+
+ )} +
+
+ + {/* Подсказка */} +
+

+ 💡 Подсказка: Нажмите на товар для быстрого добавления или перейдите к детальному каталогу + ниже для настройки рецептуры +

+
+
+ ) +} diff --git a/src/components/supplies/create-suppliers/blocks/SuppliersBlock.tsx b/src/components/supplies/create-suppliers/blocks/SuppliersBlock.tsx new file mode 100644 index 0000000..8169734 --- /dev/null +++ b/src/components/supplies/create-suppliers/blocks/SuppliersBlock.tsx @@ -0,0 +1,149 @@ +/** + * БЛОК ВЫБОРА ПОСТАВЩИКОВ + * + * Выделен из create-suppliers-supply-page.tsx + * Горизонтальный скролл поставщиков с поиском + */ + +'use client' + +import { Search } from 'lucide-react' + +import { OrganizationAvatar } from '@/components/market/organization-avatar' +import { Input } from '@/components/ui/input' + +import type { SuppliersBlockProps } from '../types/supply-creation.types' + +export function SuppliersBlock({ + suppliers, + selectedSupplier, + searchQuery, + loading, + onSupplierSelect, + onSearchChange, +}: SuppliersBlockProps) { + if (loading) { + return ( +
+
+
Загрузка поставщиков...
+
+
+ ) + } + + return ( +
+ {/* Заголовок и поиск */} +
+

1. Выберите поставщика ({suppliers.length})

+
+ + onSearchChange(e.target.value)} + className="glass-input pl-10 h-9 text-sm text-white placeholder:text-white/50" + /> +
+
+ + {suppliers.length === 0 ? ( +
+
+
+ {searchQuery ? 'Поставщики не найдены' : 'Нет доступных поставщиков'} +
+ {searchQuery && ( + + )} +
+
+ ) : ( +
+
+ {suppliers.map((supplier) => ( +
onSupplierSelect(supplier)} + className={`flex-shrink-0 p-3 rounded-lg cursor-pointer group transition-all duration-200 + w-[184px] md:w-[200px] lg:w-[216px] h-[92px] + ${ + selectedSupplier?.id === supplier.id + ? 'bg-green-500/20 border border-green-400/60 shadow-lg ring-1 ring-green-400/30' + : 'bg-white/5 border border-white/10 hover:border-white/20 hover:bg-white/10 hover:shadow-md' + }`} + > +
+
+ +
+
+

+ {supplier.name || supplier.fullName} +

+

ИНН: {supplier.inn}

+ + {/* Дополнительная информация */} +
+ {supplier.rating && ★ {supplier.rating}} + {supplier.market && ( + + {getMarketLabel(supplier.market)} + + )} +
+
+
+
+ ))} +
+
+ )} + + {/* Информация о выбранном поставщике */} + {selectedSupplier && ( +
+
+
+ + Выбран: {selectedSupplier.name || selectedSupplier.fullName} + + {selectedSupplier.address && ( + • {selectedSupplier.address} + )} +
+
+ )} +
+ ) +} + +// Утилитарная функция для меток рынков (временно, потом перенести в хук) +function getMarketLabel(market?: string) { + switch (market) { + case 'wildberries': + return 'WB' + case 'ozon': + return 'OZON' + case 'yandexmarket': + return 'YM' + default: + return 'Универсальный' + } +}