From 7d9b76a7926d5a530290a87eaff3ab9315781546 Mon Sep 17 00:00:00 2001 From: Veronika Smirnova Date: Fri, 25 Jul 2025 15:23:11 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20=D0=BA=D0=BE=D0=BC=D0=BF=D0=BE=D0=BD=D0=B5=D0=BD=D1=82?= =?UTF-8?q?=20CreateSupplyPage:=20=D1=83=D0=BB=D1=83=D1=87=D1=88=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D1=81=D1=82=D1=80=D1=83=D0=BA=D1=82=D1=83=D1=80?= =?UTF-8?q?=D0=B0=20=D0=BA=D0=BE=D0=B4=D0=B0=20=D0=B8=20=D1=81=D1=82=D0=B8?= =?UTF-8?q?=D0=BB=D0=B8,=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D1=8B=20=D0=BD=D0=BE=D0=B2=D1=8B=D0=B5=20=D1=84=D1=83?= =?UTF-8?q?=D0=BD=D0=BA=D1=86=D0=B8=D0=B8=20=D0=B4=D0=BB=D1=8F=20=D0=BE?= =?UTF-8?q?=D0=B1=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=BA=D0=B8=20=D1=82=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D1=80=D0=BE=D0=B2=20=D0=B8=20=D0=BF=D0=BE=D1=81?= =?UTF-8?q?=D1=82=D0=B0=D0=B2=D0=BE=D0=BA.=20=D0=9E=D0=BF=D1=82=D0=B8?= =?UTF-8?q?=D0=BC=D0=B8=D0=B7=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=20=D0=B8?= =?UTF-8?q?=D0=BD=D1=82=D0=B5=D1=80=D1=84=D0=B5=D0=B9=D1=81=20=D1=81=20?= =?UTF-8?q?=D0=B8=D1=81=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=D0=BC=20=D0=BD=D0=BE=D0=B2=D1=8B=D1=85=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=BC=D0=BF=D0=BE=D0=BD=D0=B5=D0=BD=D1=82=D0=BE?= =?UTF-8?q?=D0=B2=20=D0=B8=20=D1=83=D0=BB=D1=83=D1=87=D1=88=D0=B5=D0=BD?= =?UTF-8?q?=D0=B0=20=D0=BB=D0=BE=D0=B3=D0=B8=D0=BA=D0=B0=20=D0=BE=D1=82?= =?UTF-8?q?=D0=BE=D0=B1=D1=80=D0=B0=D0=B6=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=B4?= =?UTF-8?q?=D0=B0=D0=BD=D0=BD=D1=8B=D1=85.=20=D0=98=D1=81=D0=BF=D1=80?= =?UTF-8?q?=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=BE=D1=88=D0=B8=D0=B1?= =?UTF-8?q?=D0=BA=D0=B8=20=D0=B8=20=D1=83=D0=BB=D1=83=D1=87=D1=88=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D1=87=D0=B8=D1=82=D0=B0=D0=B5=D0=BC=D0=BE=D1=81?= =?UTF-8?q?=D1=82=D1=8C=20=D0=BA=D0=BE=D0=B4=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../supplies/create-supply-page.tsx | 266 +++++---- .../supplies/direct-supply-creation.tsx | 516 ++++++++++-------- 2 files changed, 438 insertions(+), 344 deletions(-) diff --git a/src/components/supplies/create-supply-page.tsx b/src/components/supplies/create-supply-page.tsx index d5a450e..d8d4153 100644 --- a/src/components/supplies/create-supply-page.tsx +++ b/src/components/supplies/create-supply-page.tsx @@ -1,157 +1,184 @@ -"use client" +"use client"; -import React, { useState } from 'react' -import { Sidebar } from '@/components/dashboard/sidebar' -import { useSidebar } from '@/hooks/useSidebar' -import { useRouter } from 'next/navigation' -import { DirectSupplyCreation } from './direct-supply-creation' -import { WholesalerProductsPage } from './wholesaler-products-page' -import { TabsHeader } from './tabs-header' -import { WholesalerGrid } from './wholesaler-grid' -import { CartSummary } from './cart-summary' -import { FloatingCart } from './floating-cart' -import { - WholesalerForCreation, - WholesalerProduct, +import React, { useState } from "react"; +import { Sidebar } from "@/components/dashboard/sidebar"; +import { useSidebar } from "@/hooks/useSidebar"; +import { useRouter } from "next/navigation"; +import { DirectSupplyCreation } from "./direct-supply-creation"; +import { WholesalerProductsPage } from "./wholesaler-products-page"; +import { TabsHeader } from "./tabs-header"; +import { WholesalerGrid } from "./wholesaler-grid"; +import { CartSummary } from "./cart-summary"; +import { FloatingCart } from "./floating-cart"; +import { + WholesalerForCreation, + WholesalerProduct, SelectedProduct, - CounterpartyWholesaler -} from './types' -import { useQuery } from '@apollo/client' -import { GET_MY_COUNTERPARTIES, GET_ALL_PRODUCTS } from '@/graphql/queries' + CounterpartyWholesaler, +} from "./types"; +import { useQuery } from "@apollo/client"; +import { GET_MY_COUNTERPARTIES, GET_ALL_PRODUCTS } from "@/graphql/queries"; export function CreateSupplyPage() { - const router = useRouter() - const { getSidebarMargin } = useSidebar() - const [activeTab, setActiveTab] = useState<'cards' | 'wholesaler'>('cards') - const [selectedWholesaler, setSelectedWholesaler] = useState(null) - const [selectedProducts, setSelectedProducts] = useState([]) - const [showSummary, setShowSummary] = useState(false) - const [searchQuery, setSearchQuery] = useState('') - const [canCreateSupply, setCanCreateSupply] = useState(false) - const [isCreatingSupply, setIsCreatingSupply] = useState(false) + const router = useRouter(); + const { getSidebarMargin } = useSidebar(); + const [activeTab, setActiveTab] = useState<"cards" | "wholesaler">("cards"); + const [selectedWholesaler, setSelectedWholesaler] = + useState(null); + const [selectedProducts, setSelectedProducts] = useState( + [] + ); + const [showSummary, setShowSummary] = useState(false); + const [searchQuery, setSearchQuery] = useState(""); + const [canCreateSupply, setCanCreateSupply] = useState(false); + const [isCreatingSupply, setIsCreatingSupply] = useState(false); // Загружаем контрагентов-оптовиков - const { data: counterpartiesData, loading: counterpartiesLoading } = useQuery(GET_MY_COUNTERPARTIES) - + const { data: counterpartiesData, loading: counterpartiesLoading } = useQuery( + GET_MY_COUNTERPARTIES + ); + // Загружаем товары для выбранного оптовика - const { data: productsData, loading: productsLoading } = useQuery(GET_ALL_PRODUCTS, { - skip: !selectedWholesaler, - variables: { search: null, category: null } - }) - + const { data: productsData, loading: productsLoading } = useQuery( + GET_ALL_PRODUCTS, + { + skip: !selectedWholesaler, + variables: { search: null, category: null }, + } + ); + // Фильтруем только оптовиков - const wholesalers: CounterpartyWholesaler[] = (counterpartiesData?.myCounterparties || []) - .filter((org: { type: string }) => org.type === 'WHOLESALE') + const wholesalers: CounterpartyWholesaler[] = ( + counterpartiesData?.myCounterparties || [] + ).filter((org: { type: string }) => org.type === "WHOLESALE"); // Фильтруем товары по выбранному оптовику - const wholesalerProducts: WholesalerProduct[] = selectedWholesaler - ? (productsData?.allProducts || []).filter((product: { organization: { id: string } }) => - product.organization.id === selectedWholesaler.id + const wholesalerProducts: WholesalerProduct[] = selectedWholesaler + ? (productsData?.allProducts || []).filter( + (product: { organization: { id: string } }) => + product.organization.id === selectedWholesaler.id ) - : [] + : []; const formatCurrency = (amount: number) => { - return new Intl.NumberFormat('ru-RU', { - style: 'currency', - currency: 'RUB', - minimumFractionDigits: 0 - }).format(amount) - } + return new Intl.NumberFormat("ru-RU", { + style: "currency", + currency: "RUB", + minimumFractionDigits: 0, + }).format(amount); + }; const updateProductQuantity = (productId: string, quantity: number) => { - const product = wholesalerProducts.find((p) => p.id === productId) - if (!product || !selectedWholesaler) return + const product = wholesalerProducts.find((p) => p.id === productId); + if (!product || !selectedWholesaler) return; + + setSelectedProducts((prev) => { + const existing = prev.find( + (p) => p.id === productId && p.wholesalerId === selectedWholesaler.id + ); - setSelectedProducts(prev => { - const existing = prev.find(p => p.id === productId && p.wholesalerId === selectedWholesaler.id) - if (quantity === 0) { - return prev.filter(p => !(p.id === productId && p.wholesalerId === selectedWholesaler.id)) + return prev.filter( + (p) => + !(p.id === productId && p.wholesalerId === selectedWholesaler.id) + ); } - + if (existing) { - return prev.map(p => - p.id === productId && p.wholesalerId === selectedWholesaler.id - ? { ...p, selectedQuantity: quantity } + return prev.map((p) => + p.id === productId && p.wholesalerId === selectedWholesaler.id + ? { ...p, selectedQuantity: quantity } : p - ) + ); } else { - return [...prev, { - ...product, - selectedQuantity: quantity, - wholesalerId: selectedWholesaler.id, - wholesalerName: selectedWholesaler.name - }] + return [ + ...prev, + { + ...product, + selectedQuantity: quantity, + wholesalerId: selectedWholesaler.id, + wholesalerName: selectedWholesaler.name, + }, + ]; } - }) - } + }); + }; const getTotalAmount = () => { return selectedProducts.reduce((sum, product) => { - const discountedPrice = product.discount + const discountedPrice = product.discount ? product.price * (1 - product.discount / 100) - : product.price - return sum + (discountedPrice * product.selectedQuantity) - }, 0) - } + : product.price; + return sum + discountedPrice * product.selectedQuantity; + }, 0); + }; const getTotalItems = () => { - return selectedProducts.reduce((sum, product) => sum + product.selectedQuantity, 0) - } + return selectedProducts.reduce( + (sum, product) => sum + product.selectedQuantity, + 0 + ); + }; const handleCreateSupply = () => { - if (activeTab === 'cards') { - console.log('Создание поставки с карточками Wildberries') + if (activeTab === "cards") { + console.log("Создание поставки с карточками Wildberries"); } else { - console.log('Создание поставки с товарами:', selectedProducts) + console.log("Создание поставки с товарами:", selectedProducts); } - router.push('/supplies') - } + router.push("/supplies"); + }; const handleGoBack = () => { if (selectedWholesaler) { - setSelectedWholesaler(null) - setShowSummary(false) + setSelectedWholesaler(null); + setShowSummary(false); } else { - router.push('/supplies') + router.push("/supplies"); } - } + }; const handleRemoveProduct = (productId: string, wholesalerId: string) => { - setSelectedProducts(prev => - prev.filter(p => !(p.id === productId && p.wholesalerId === wholesalerId)) - ) - } + setSelectedProducts((prev) => + prev.filter( + (p) => !(p.id === productId && p.wholesalerId === wholesalerId) + ) + ); + }; - const handleCartQuantityChange = (productId: string, wholesalerId: string, quantity: number) => { - setSelectedProducts(prev => - prev.map(p => + const handleCartQuantityChange = ( + productId: string, + wholesalerId: string, + quantity: number + ) => { + setSelectedProducts((prev) => + prev.map((p) => p.id === productId && p.wholesalerId === wholesalerId ? { ...p, selectedQuantity: quantity } : p ) - ) - } + ); + }; const handleSupplyComplete = () => { - router.push('/supplies') - } + router.push("/supplies"); + }; const handleCreateSupplyClick = () => { - setIsCreatingSupply(true) - } + setIsCreatingSupply(true); + }; const handleCanCreateSupplyChange = (canCreate: boolean) => { - setCanCreateSupply(canCreate) - } + setCanCreateSupply(canCreate); + }; const handleSupplyCompleted = () => { - setIsCreatingSupply(false) - handleSupplyComplete() - } + setIsCreatingSupply(false); + handleSupplyComplete(); + }; // Рендер страницы товаров оптовика - if (selectedWholesaler && activeTab === 'wholesaler') { + if (selectedWholesaler && activeTab === "wholesaler") { return ( - ) + ); } // Главная страница с табами return (
-
-
+
+
router.push('/supplies')} + onBack={() => router.push("/supplies")} cartInfo={ - activeTab === 'wholesaler' && selectedProducts.length > 0 + activeTab === "wholesaler" && selectedProducts.length > 0 ? { itemCount: selectedProducts.length, totalAmount: getTotalAmount(), - formatCurrency + formatCurrency, } : undefined } @@ -194,18 +224,20 @@ export function CreateSupplyPage() { /> {/* Контент карточек - новый компонент прямого создания поставки */} - {activeTab === 'cards' && ( - + {activeTab === "cards" && ( +
+ +
)} {/* Контент оптовиков */} - {activeTab === 'wholesaler' && ( + {activeTab === "wholesaler" && (
- ) -} \ No newline at end of file + ); +} diff --git a/src/components/supplies/direct-supply-creation.tsx b/src/components/supplies/direct-supply-creation.tsx index 93963b0..3f0208c 100644 --- a/src/components/supplies/direct-supply-creation.tsx +++ b/src/components/supplies/direct-supply-creation.tsx @@ -668,14 +668,14 @@ export function DirectSupplyCreation({ return ( <> -
+
{/* НОВЫЙ БЛОК СОЗДАНИЯ ПОСТАВКИ */} - + {/* Первая строка */} -
+
{/* 1. Модуль выбора даты */}
-
@@ -692,7 +692,7 @@ export function DirectSupplyCreation({ {/* 2. Модуль выбора фулфилмента */}
-
{/* Вторая строка */} -
+
{/* 5. Цена товаров */}
-
{/* 6. Цена услуг фулфилмента */}
-
{/* 7. Цена логистики */}
-
{/* 8. Итоговая сумма */}
- -
+ +
{formatCurrency(getTotalSum()).replace(" ₽", " ₽")} @@ -804,143 +806,187 @@ export function DirectSupplyCreation({
- {/* Блок поиска товаров - оптимизированное расположение */} - -
- -
- setSearchTerm(e.target.value)} - className="bg-white/20 border-0 text-white placeholder-white/60 h-8 text-xs flex-1 focus:bg-white/30 focus:ring-1 focus:ring-white/20" - onKeyPress={(e) => e.key === "Enter" && searchCards()} - /> - -
-
+ {/* Элегантный блок поиска и товаров */} +
+ {/* Главная карточка с градиентом */} +
+ {/* Компактный заголовок с поиском */} +
+
+
+ +
+
+

+ Каталог товаров +

+

+ Найдено: {wbCards.length} +

+
+
- {/* Карточки товаров - увеличенный размер и единообразность */} -
-
- - Найдено товаров: {wbCards.length} - + {/* Поиск в заголовке */} +
+
+ setSearchTerm(e.target.value)} + className="pl-3 pr-16 py-2 bg-white/10 border border-white/20 rounded-lg text-white placeholder-white/50 focus:bg-white/15 focus:border-white/40 text-sm h-8" + onKeyPress={(e) => e.key === "Enter" && searchCards()} + /> + +
+
+ + {/* Статистика в поставке */} {supplyItems.length > 0 && ( - - В поставке: {supplyItems.length} - +
+
+
+ + В поставке: {supplyItems.length} + +
+
)}
-
- {loading - ? [...Array(12)].map((_, i) => ( + {/* Сетка товаров */} +
+ {loading ? ( + // Красивые skeleton-карточки + [...Array(16)].map((_, i) => ( +
+
+
+
+
+
+
+
+ )) + ) : wbCards.length > 0 ? ( + // Красивые карточки товаров + wbCards.map((card) => { + const isInSupply = supplyItems.some( + (item) => item.card.nmID === card.nmID + ); + return (
- )) - : wbCards.map((card) => { - const isInSupply = supplyItems.some( - (item) => item.card.nmID === card.nmID - ); - return ( + key={card.nmID} + className={`group cursor-pointer transition-all duration-300 hover:scale-105 ${ + isInSupply ? "scale-105" : "" + }`} + onClick={() => addToSupply(card)} + > + {/* Карточка товара */}
addToSupply(card)} > -
- {card.title} + {card.title} - {/* Оверлей с информацией */} -
-
-
- {card.title} -
-
- Арт: {card.vendorCode} -
- {card.sizes && card.sizes[0] && ( -
- от{" "} - {card.sizes[0].discountedPrice || - card.sizes[0].price}{" "} - ₽ -
- )} -
-
+ {/* Градиентный оверлей */} +
- {/* Индикатор добавления в поставку */} - {isInSupply && ( -
- ✓ -
+ {/* Информация при наведении */} +
+

+ {card.title} +

+

+ Арт: {card.vendorCode} +

+ {card.sizes && card.sizes[0] && ( +

+ от{" "} + {card.sizes[0].discountedPrice || + card.sizes[0].price}{" "} + ₽ +

)} - - {/* Индикатор при наведении */} -
- -
-
- ); - })} -
- {!loading && wbCards.length === 0 && ( -
- -

- {searchTerm - ? "Товары не найдены" - : "Введите запрос для поиска товаров"} -

- {searchTerm && ( -

- Попробуйте изменить условия поиска + {/* Индикаторы */} + {isInSupply ? ( +

+ + + +
+ ) : ( +
+ +
+ )} + + {/* Эффект при клике */} +
+
+ + {/* Название под карточкой */} +
+

+ {card.title} +

+
+
+ ); + }) + ) : ( + // Пустое состояние +
+
+ +
+

+ {searchTerm ? "Товары не найдены" : "Начните поиск товаров"} +

+

+ {searchTerm + ? "Попробуйте изменить поисковый запрос" + : "Введите название товара в поле поиска"}

- )} -
- )} +
+ )} +
- + + {/* Декоративные элементы */} +
+
+
{/* Услуги и расходники в одной строке */} {selectedFulfillmentOrg && ( @@ -1027,44 +1073,37 @@ export function DirectSupplyCreation({ )} - {/* Модуль товаров в поставке - новый дизайн */} - -
+ {/* Модуль товаров в поставке - растягивается до низа */} + +
Товары в поставке -
{supplyItems.length === 0 ? ( -
- -

- Добавьте товары из карточек выше -

+
+
+ +

+ Добавьте товары из карточек выше +

+
) : ( -
+
{supplyItems.map((item) => ( - {/* Заголовок товара с кнопкой удаления */} -
-
-
+ {/* Компактный заголовок товара */} +
+
+
{item.card.title}
-
+
Арт: {item.card.vendorCode}
@@ -1072,47 +1111,44 @@ export function DirectSupplyCreation({ onClick={() => removeFromSupply(item.card.nmID)} size="sm" variant="ghost" - className="h-6 w-6 p-0 text-white/60 hover:text-red-400" + className="h-5 w-5 p-0 text-white/60 hover:text-red-400 flex-shrink-0" > - +
- {/* Названия блоков */} -
-
+ {/* Компактные названия блоков */} +
+
Товар
-
+
Параметры
-
+
Заказать
-
+
Цена
-
- Услуги фулфилмента +
+ Услуги фф
-
+
Поставщик
-
- Расходники фулфилмента +
+ Расходники фф
-
- Расходники селлера +
+ Расходники
- {/* Оптимизированная сетка для 13" - все блоки в одну строку */} -
+ {/* Компактная сетка блоков */} +
{/* Блок 1: Картинка товара */} -
+
{/* Блок 2: Параметры */} -
+
-
+
{item.card.object}
-
+
{item.card.countryProduction}
{item.card.sizes && item.card.sizes[0] && ( -
+
{item.card.sizes[0].techSize}
)} @@ -1143,8 +1179,8 @@ export function DirectSupplyCreation({
{/* Блок 3: Заказать */} -
-
+
+
Количество
{/* Блок 4: Цена */} -
-
+
+
За единицу
-
+
{formatCurrency(item.totalPrice).replace(" ₽", "₽")}
{/* Блок 5: Услуги фулфилмента */} -
-
+
+
{selectedFulfillmentOrg && organizationServices[selectedFulfillmentOrg] ? ( organizationServices[selectedFulfillmentOrg] - .slice(0, 2) + .slice(0, 4) .map((service) => ( )) ) : ( - + Выберите фулфилмент )} @@ -1230,14 +1266,14 @@ export function DirectSupplyCreation({
{/* Блок 6: Поставщик */} -
+
+ + {/* Информация о выбранном поставщике */} + {item.supplierId && + suppliers.find((s) => s.id === item.supplierId) && ( +
+
+ { + suppliers.find((s) => s.id === item.supplierId) + ?.contactName + } +
+
+ { + suppliers.find((s) => s.id === item.supplierId) + ?.phone + } +
+
+ )} + + {/* Кнопка добавления поставщика */} +
- {/* Блок 7: Расходники фулфилмента */} -
-
+ {/* Блок 7: Расходники фф */} +
+
{selectedFulfillmentOrg && organizationSupplies[selectedFulfillmentOrg] ? ( organizationSupplies[selectedFulfillmentOrg] - .slice(0, 2) + .slice(0, 4) .map((supply) => ( )) ) : ( - + Выберите фулфилмент )} @@ -1295,23 +1361,19 @@ export function DirectSupplyCreation({
{/* Блок 8: Расходники селлера */} -
-
-