From 57f8f762c9b23794f547ee4d81afbb1348720b8c Mon Sep 17 00:00:00 2001 From: Veronika Smirnova Date: Mon, 25 Aug 2025 22:39:14 +0300 Subject: [PATCH] =?UTF-8?q?feat(components):=20=D0=B4=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=B8=D1=82=D1=8C=20=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D1=8C?= =?UTF-8?q?=D0=BD=D1=83=D1=8E=20V2=20=D1=81=D0=B8=D1=81=D1=82=D0=B5=D0=BC?= =?UTF-8?q?=D1=83=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD=D0=B8=D1=8F=20?= =?UTF-8?q?=D0=BF=D0=BE=D1=81=D1=82=D0=B0=D0=B2=D0=BE=D0=BA=20=D1=80=D0=B0?= =?UTF-8?q?=D1=81=D1=85=D0=BE=D0=B4=D0=BD=D0=B8=D0=BA=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Создана новая модульная архитектура компонентов для создания поставок расходников фулфилмента с улучшенной организацией кода и разделением ответственности. ESLint warnings исправим в отдельном коммите для cleaner history. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../blocks/ConsumablesBlock.tsx | 307 +++++++ .../blocks/PageHeader.tsx | 32 + .../blocks/ShoppingCartBlock.tsx | 143 +++ .../blocks/SuppliersBlock.tsx | 143 +++ .../blocks/index.ts | 18 + .../seller-blocks/SellerShoppingCartBlock.tsx | 168 ++++ .../seller-blocks/SellerSuppliersBlock.tsx | 181 ++++ .../blocks/seller-blocks/index.ts | 6 + .../blocks/types.ts | 55 ++ .../hooks/index.ts | 22 + .../hooks/seller-hooks/index.ts | 7 + .../seller-hooks/useSellerSupplierData.ts | 90 ++ .../seller-hooks/useSellerSupplyCreation.ts | 124 +++ .../hooks/seller-hooks/useSellerSupplyForm.ts | 93 ++ .../hooks/types.ts | 65 ++ .../hooks/useCurrencyFormatting.ts | 20 + .../hooks/useProductData.ts | 65 ++ .../hooks/useQuantityManagement.ts | 96 ++ .../hooks/useStockValidation.ts | 76 ++ .../hooks/useSupplierData.ts | 59 ++ .../hooks/useSupplyCreation.ts | 112 +++ .../hooks/useSupplyForm.ts | 56 ++ .../index.tsx | 59 ++ .../modular-version.tsx | 169 ++++ .../monolithic-version.tsx | 820 ++++++++++++++++++ .../seller-modular-version.tsx | 183 ++++ .../types/index.ts | 252 ++++++ .../types/seller-types.ts | 193 +++++ 28 files changed, 3614 insertions(+) create mode 100644 src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2-modular/blocks/ConsumablesBlock.tsx create mode 100644 src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2-modular/blocks/PageHeader.tsx create mode 100644 src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2-modular/blocks/ShoppingCartBlock.tsx create mode 100644 src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2-modular/blocks/SuppliersBlock.tsx create mode 100644 src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2-modular/blocks/index.ts create mode 100644 src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2-modular/blocks/seller-blocks/SellerShoppingCartBlock.tsx create mode 100644 src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2-modular/blocks/seller-blocks/SellerSuppliersBlock.tsx create mode 100644 src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2-modular/blocks/seller-blocks/index.ts create mode 100644 src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2-modular/blocks/types.ts create mode 100644 src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2-modular/hooks/index.ts create mode 100644 src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2-modular/hooks/seller-hooks/index.ts create mode 100644 src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2-modular/hooks/seller-hooks/useSellerSupplierData.ts create mode 100644 src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2-modular/hooks/seller-hooks/useSellerSupplyCreation.ts create mode 100644 src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2-modular/hooks/seller-hooks/useSellerSupplyForm.ts create mode 100644 src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2-modular/hooks/types.ts create mode 100644 src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2-modular/hooks/useCurrencyFormatting.ts create mode 100644 src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2-modular/hooks/useProductData.ts create mode 100644 src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2-modular/hooks/useQuantityManagement.ts create mode 100644 src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2-modular/hooks/useStockValidation.ts create mode 100644 src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2-modular/hooks/useSupplierData.ts create mode 100644 src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2-modular/hooks/useSupplyCreation.ts create mode 100644 src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2-modular/hooks/useSupplyForm.ts create mode 100644 src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2-modular/index.tsx create mode 100644 src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2-modular/modular-version.tsx create mode 100644 src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2-modular/monolithic-version.tsx create mode 100644 src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2-modular/seller-modular-version.tsx create mode 100644 src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2-modular/types/index.ts create mode 100644 src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2-modular/types/seller-types.ts diff --git a/src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2-modular/blocks/ConsumablesBlock.tsx b/src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2-modular/blocks/ConsumablesBlock.tsx new file mode 100644 index 0000000..a3c8e58 --- /dev/null +++ b/src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2-modular/blocks/ConsumablesBlock.tsx @@ -0,0 +1,307 @@ +// ============================================================================= +// 📦 БЛОК РАСХОДНИКОВ +// ============================================================================= +// ВНИМАНИЕ: Визуал остается ТОЧНО таким же как в монолитной версии! +// Все gradients, glassmorphism, анимации, индикаторы остатков сохранены + +import { Search, Wrench, Package, Plus, Minus } from 'lucide-react' +import Image from 'next/image' + +import { Badge } from '@/components/ui/badge' +import { Button } from '@/components/ui/button' +import { Card } from '@/components/ui/card' +import { Input } from '@/components/ui/input' + +import type { FulfillmentConsumableProduct } from '../types' + +import type { ConsumablesBlockProps } from './types' + +export function ConsumablesBlock({ + selectedSupplier, + products, + productsLoading, + productSearchQuery, + getSelectedQuantity, + onProductSearchChange, + onUpdateQuantity, + formatCurrency, +}: ConsumablesBlockProps) { + return ( + +
+
+

+ + Расходники для фулфилмента + {selectedSupplier && ( + + - {selectedSupplier.name || selectedSupplier.fullName} + + )} +

+
+ {selectedSupplier && ( +
+ + onProductSearchChange(e.target.value)} + className="bg-white/10 border-white/20 text-white placeholder-white/40 pl-7 h-8 text-sm" + /> +
+ )} +
+ +
+ {!selectedSupplier ? ( +
+ +

Выберите поставщика для просмотра расходников

+
+ ) : productsLoading ? ( +
+
+

Загрузка...

+
+ ) : products.length === 0 ? ( +
+ +

Нет доступных расходников

+
+ ) : ( +
+ {products.map((product: FulfillmentConsumableProduct, index: number) => { + const selectedQuantity = getSelectedQuantity(product.id) + return ( + 0 + ? 'ring-2 ring-green-400/50 bg-gradient-to-br from-green-500/20 via-green-400/10 to-green-500/20' + : 'hover:from-white/20 hover:via-white/10 hover:to-white/20 hover:border-white/40' + }`} + style={{ + animationDelay: `${index * 50}ms`, + minHeight: '200px', + width: '100%', + }} + > +
+ {/* Изображение товара */} +
+ {/* 🚫 ОВЕРЛЕЙ НЕДОСТУПНОСТИ */} + {(() => { + const totalStock = product.stock || (product as any).quantity || 0 + const orderedStock = (product as any).ordered || 0 + const availableStock = totalStock - orderedStock + + if (availableStock <= 0) { + return ( +
+
+
НЕТ В НАЛИЧИИ
+
+
+ ) + } + return null + })()} + {product.images && product.images.length > 0 && product.images[0] ? ( + {product.name} + ) : product.mainImage ? ( + {product.name} + ) : ( +
+ +
+ )} + {selectedQuantity > 0 && ( +
+ + {selectedQuantity > 999 ? '999+' : selectedQuantity} + +
+ )} +
+ + {/* Информация о товаре */} +
+

+ {product.name} +

+
+ {product.category && ( + + {product.category.name.slice(0, 10)} + + )} + {/* 🚨 ИНДИКАТОР НИЗКИХ ОСТАТКОВ согласно правилам (раздел 6.3) */} + {(() => { + const totalStock = product.stock || product.quantity || 0 + const orderedStock = product.ordered || 0 + const availableStock = totalStock - orderedStock + + if (availableStock <= 0) { + return ( + + Нет в наличии + + ) + } else if (availableStock <= 10) { + return ( + + Мало остатков + + ) + } + return null + })()} +
+
+ + {formatCurrency(product.price)} + + {/* 📊 АКТУАЛЬНЫЙ ОСТАТОК согласно правилам (раздел 6.4.2) */} +
+ {(() => { + const totalStock = product.stock || product.quantity || 0 + const orderedStock = product.ordered || 0 + const availableStock = totalStock - orderedStock + + return ( +
+ + Доступно: {availableStock} + + {orderedStock > 0 && ( + Заказано: {orderedStock} + )} +
+ ) + })()} +
+
+
+ + {/* Управление количеством */} +
+ {(() => { + const totalStock = product.stock || (product as any).quantity || 0 + const orderedStock = (product as any).ordered || 0 + const availableStock = totalStock - orderedStock + + return ( +
+ + { + let inputValue = e.target.value + + // Удаляем все нецифровые символы + inputValue = inputValue.replace(/[^0-9]/g, '') + + // Удаляем ведущие нули + inputValue = inputValue.replace(/^0+/, '') + + // Если строка пустая после удаления нулей, устанавливаем 0 + const numericValue = inputValue === '' ? 0 : parseInt(inputValue) + + // Ограничиваем значение максимумом доступного остатка + const clampedValue = Math.min(numericValue, availableStock, 99999) + + onUpdateQuantity(product.id, clampedValue) + }} + onBlur={(e) => { + // При потере фокуса, если поле пустое, устанавливаем 0 + if (e.target.value === '') { + onUpdateQuantity(product.id, 0) + } + }} + className="w-16 h-7 text-center text-sm bg-white/10 border-white/20 text-white rounded px-1 focus:ring-2 focus:ring-purple-400/50 focus:border-purple-400/50" + placeholder="0" + /> + +
+ ) + })()} + + {selectedQuantity > 0 && ( +
+ + {formatCurrency(product.price * selectedQuantity)} + +
+ )} +
+
+ + {/* Hover эффект */} +
+
+ ) + })} +
+ )} +
+
+ ) +} \ No newline at end of file diff --git a/src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2-modular/blocks/PageHeader.tsx b/src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2-modular/blocks/PageHeader.tsx new file mode 100644 index 0000000..dc3c502 --- /dev/null +++ b/src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2-modular/blocks/PageHeader.tsx @@ -0,0 +1,32 @@ +// ============================================================================= +// 📄 БЛОК ЗАГОЛОВКА СТРАНИЦЫ +// ============================================================================= +// ВНИМАНИЕ: Визуал остается ТОЧНО таким же как в монолитной версии! + +import { ArrowLeft } from 'lucide-react' + +import { Button } from '@/components/ui/button' + +import type { PageHeaderProps } from './types' + +export function PageHeader({ onBack }: PageHeaderProps) { + return ( +
+
+

Создание поставки расходников фулфилмента

+

+ Выберите поставщика и добавьте расходники в заказ для вашего фулфилмент-центра +

+
+ +
+ ) +} \ No newline at end of file diff --git a/src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2-modular/blocks/ShoppingCartBlock.tsx b/src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2-modular/blocks/ShoppingCartBlock.tsx new file mode 100644 index 0000000..863b359 --- /dev/null +++ b/src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2-modular/blocks/ShoppingCartBlock.tsx @@ -0,0 +1,143 @@ +// ============================================================================= +// 🛒 БЛОК КОРЗИНЫ +// ============================================================================= +// ВНИМАНИЕ: Визуал остается ТОЧНО таким же как в монолитной версии! +// Все gradients, glassmorphism, анимации сохранены + +import { ShoppingCart } from 'lucide-react' + +import { Button } from '@/components/ui/button' +import { Card } from '@/components/ui/card' +import { Input } from '@/components/ui/input' + +import type { ShoppingCartBlockProps } from './types' + +export function ShoppingCartBlock({ + selectedConsumables, + deliveryDate, + notes, + selectedLogistics, + logisticsPartners, + isCreatingSupply, + getTotalAmount, + getTotalItems, + formatCurrency, + onUpdateQuantity, + onSetDeliveryDate, + onSetNotes, + onSetLogistics, + onCreateSupply, +}: ShoppingCartBlockProps) { + return ( + +

+ + Корзина ({getTotalItems()} шт) +

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

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

+

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

+
+ ) : ( +
+ {selectedConsumables.map((consumable) => ( +
+
+

{consumable.name}

+

+ {formatCurrency(consumable.price)} × {consumable.selectedQuantity} +

+
+
+ + {formatCurrency(consumable.price * consumable.selectedQuantity)} + + +
+
+ ))} +
+ )} + +
+
+ + onSetDeliveryDate(e.target.value)} + className="bg-white/10 border-white/20 text-white h-8 text-sm" + min={new Date().toISOString().split('T')[0]} + required + /> +
+ + {/* Выбор логистики */} +
+ +
+ +
+ + + +
+
+
+ + {/* Заметки */} +
+ +