diff --git a/src/components/supplies/create-supply-page.tsx b/src/components/supplies/create-supply-page.tsx index 50001ce..d5a450e 100644 --- a/src/components/supplies/create-supply-page.tsx +++ b/src/components/supplies/create-supply-page.tsx @@ -172,8 +172,8 @@ export function CreateSupplyPage() { return (
-
-
+
+
void - onCreateSupply: () => void - canCreateSupply: boolean - isCreatingSupply: boolean - onCanCreateSupplyChange?: (canCreate: boolean) => void + onComplete: () => void; + onCreateSupply: () => void; + canCreateSupply: boolean; + isCreatingSupply: boolean; + onCanCreateSupplyChange?: (canCreate: boolean) => void; } -export function DirectSupplyCreation({ onComplete, onCreateSupply, canCreateSupply, isCreatingSupply, onCanCreateSupplyChange }: DirectSupplyCreationProps) { - const { user } = useAuth() - - // Состояние для товаров - const [searchTerm, setSearchTerm] = useState('') - const [loading, setLoading] = useState(false) - const [wbCards, setWbCards] = useState([]) - const [supplyItems, setSupplyItems] = useState([]) - - // Общие настройки - const [deliveryDate, setDeliveryDate] = useState(undefined) - const [selectedFulfillmentOrg, setSelectedFulfillmentOrg] = useState('') - const [selectedServices, setSelectedServices] = useState([]) - const [selectedConsumables, setSelectedConsumables] = useState([]) - +export function DirectSupplyCreation({ + onComplete, + onCreateSupply, + canCreateSupply, + isCreatingSupply, + onCanCreateSupplyChange, +}: DirectSupplyCreationProps) { + const { user } = useAuth(); + + // Новые состояния для блока создания поставки + const [deliveryDate, setDeliveryDate] = useState(""); + const [selectedFulfillment, setSelectedFulfillment] = useState(""); + const [goodsQuantity, setGoodsQuantity] = useState(1200); + const [goodsVolume, setGoodsVolume] = useState(0); + const [cargoPlaces, setCargoPlaces] = useState(0); + const [goodsPrice, setGoodsPrice] = useState(0); + const [fulfillmentServicesPrice, setFulfillmentServicesPrice] = + useState(0); + const [logisticsPrice, setLogisticsPrice] = useState(0); + + // Оригинальные состояния для товаров + const [searchTerm, setSearchTerm] = useState(""); + const [loading, setLoading] = useState(false); + const [wbCards, setWbCards] = useState([]); + const [supplyItems, setSupplyItems] = useState([]); + + // Общие настройки (оригинальные) + const [deliveryDateOriginal, setDeliveryDateOriginal] = useState< + Date | undefined + >(undefined); + const [selectedFulfillmentOrg, setSelectedFulfillmentOrg] = + useState(""); + const [selectedServices, setSelectedServices] = useState([]); + const [selectedConsumables, setSelectedConsumables] = useState([]); + // Поставщики - const [suppliers, setSuppliers] = useState([]) - const [showSupplierModal, setShowSupplierModal] = useState(false) + const [suppliers, setSuppliers] = useState([]); + const [showSupplierModal, setShowSupplierModal] = useState(false); const [newSupplier, setNewSupplier] = useState({ - name: '', - contactName: '', - phone: '', - market: '', - address: '', - place: '', - telegram: '' - }) - + name: "", + contactName: "", + phone: "", + market: "", + address: "", + place: "", + telegram: "", + }); + // Данные для фулфилмента - const [organizationServices, setOrganizationServices] = useState<{[orgId: string]: FulfillmentService[]}>({}) - const [organizationSupplies, setOrganizationSupplies] = useState<{[orgId: string]: FulfillmentService[]}>({}) + const [organizationServices, setOrganizationServices] = useState<{ + [orgId: string]: FulfillmentService[]; + }>({}); + const [organizationSupplies, setOrganizationSupplies] = useState<{ + [orgId: string]: FulfillmentService[]; + }>({}); // Загружаем контрагентов-фулфилментов - const { data: counterpartiesData } = useQuery(GET_MY_COUNTERPARTIES) - + const { data: counterpartiesData } = useQuery(GET_MY_COUNTERPARTIES); + // Мутация для создания поставки - const [createSupply, { loading: creatingSupply }] = useMutation(CREATE_WILDBERRIES_SUPPLY, { - onCompleted: (data) => { - if (data.createWildberriesSupply.success) { - toast.success(data.createWildberriesSupply.message) - onComplete() - } else { - toast.error(data.createWildberriesSupply.message) - } - }, - onError: (error) => { - toast.error('Ошибка при создании поставки') - console.error('Error creating supply:', error) + const [createSupply, { loading: creatingSupply }] = useMutation( + CREATE_WILDBERRIES_SUPPLY, + { + onCompleted: (data) => { + if (data.createWildberriesSupply.success) { + toast.success(data.createWildberriesSupply.message); + onComplete(); + } else { + toast.error(data.createWildberriesSupply.message); + } + }, + onError: (error) => { + toast.error("Ошибка при создании поставки"); + console.error("Error creating supply:", error); + }, } - }) + ); // Моковые данные товаров для демонстрации const getMockCards = (): WildberriesCard[] => [ { nmID: 123456789, - vendorCode: 'SKU001', - title: 'Платье летнее розовое', - description: 'Легкое летнее платье из натурального хлопка', - brand: 'Fashion', - object: 'Платья', - parent: 'Одежда', - countryProduction: 'Россия', - supplierVendorCode: 'SUPPLIER-001', - mediaFiles: ['/api/placeholder/400/400'], - sizes: [{ chrtID: 123456, techSize: 'M', wbSize: 'M Розовый', price: 2500, discountedPrice: 2000, quantity: 50 }] + vendorCode: "SKU001", + title: "Платье летнее розовое", + description: "Легкое летнее платье из натурального хлопка", + brand: "Fashion", + object: "Платья", + parent: "Одежда", + countryProduction: "Россия", + supplierVendorCode: "SUPPLIER-001", + mediaFiles: ["/api/placeholder/400/400"], + sizes: [ + { + chrtID: 123456, + techSize: "M", + wbSize: "M Розовый", + price: 2500, + discountedPrice: 2000, + quantity: 50, + }, + ], }, { nmID: 987654321, - vendorCode: 'SKU002', - title: 'Платье черное вечернее', - description: 'Элегантное вечернее платье для особых случаев', - brand: 'Fashion', - object: 'Платья', - parent: 'Одежда', - countryProduction: 'Россия', - supplierVendorCode: 'SUPPLIER-002', - mediaFiles: ['/api/placeholder/400/403'], - sizes: [{ chrtID: 987654, techSize: 'M', wbSize: 'M Черный', price: 3500, discountedPrice: 3000, quantity: 30 }] + vendorCode: "SKU002", + title: "Платье черное вечернее", + description: "Элегантное вечернее платье для особых случаев", + brand: "Fashion", + object: "Платья", + parent: "Одежда", + countryProduction: "Россия", + supplierVendorCode: "SUPPLIER-002", + mediaFiles: ["/api/placeholder/400/403"], + sizes: [ + { + chrtID: 987654, + techSize: "M", + wbSize: "M Черный", + price: 3500, + discountedPrice: 3000, + quantity: 30, + }, + ], }, { nmID: 555666777, - vendorCode: 'SKU003', - title: 'Блузка белая офисная', - description: 'Классическая белая блузка для офиса', - brand: 'Office', - object: 'Блузки', - parent: 'Одежда', - countryProduction: 'Турция', - supplierVendorCode: 'SUPPLIER-003', - mediaFiles: ['/api/placeholder/400/405'], - sizes: [{ chrtID: 555666, techSize: 'L', wbSize: 'L Белый', price: 1800, discountedPrice: 1500, quantity: 40 }] + vendorCode: "SKU003", + title: "Блузка белая офисная", + description: "Классическая белая блузка для офиса", + brand: "Office", + object: "Блузки", + parent: "Одежда", + countryProduction: "Турция", + supplierVendorCode: "SUPPLIER-003", + mediaFiles: ["/api/placeholder/400/405"], + sizes: [ + { + chrtID: 555666, + techSize: "L", + wbSize: "L Белый", + price: 1800, + discountedPrice: 1500, + quantity: 40, + }, + ], }, { nmID: 444333222, - vendorCode: 'SKU004', - title: 'Джинсы женские синие', - description: 'Классические женские джинсы прямого кроя', - brand: 'Denim', - object: 'Джинсы', - parent: 'Одежда', - countryProduction: 'Бангладеш', - supplierVendorCode: 'SUPPLIER-004', - mediaFiles: ['/api/placeholder/400/408'], - sizes: [{ chrtID: 444333, techSize: '30', wbSize: '30 Синий', price: 2800, discountedPrice: 2300, quantity: 25 }] + vendorCode: "SKU004", + title: "Джинсы женские синие", + description: "Классические женские джинсы прямого кроя", + brand: "Denim", + object: "Джинсы", + parent: "Одежда", + countryProduction: "Бангладеш", + supplierVendorCode: "SUPPLIER-004", + mediaFiles: ["/api/placeholder/400/408"], + sizes: [ + { + chrtID: 444333, + techSize: "30", + wbSize: "30 Синий", + price: 2800, + discountedPrice: 2300, + quantity: 25, + }, + ], }, { nmID: 111222333, - vendorCode: 'SKU005', - title: 'Кроссовки женские белые', - description: 'Удобные женские кроссовки для повседневной носки', - brand: 'Sport', - object: 'Кроссовки', - parent: 'Обувь', - countryProduction: 'Вьетнам', - supplierVendorCode: 'SUPPLIER-005', - mediaFiles: ['/api/placeholder/400/410'], - sizes: [{ chrtID: 111222, techSize: '37', wbSize: '37 Белый', price: 3200, discountedPrice: 2800, quantity: 35 }] + vendorCode: "SKU005", + title: "Кроссовки женские белые", + description: "Удобные женские кроссовки для повседневной носки", + brand: "Sport", + object: "Кроссовки", + parent: "Обувь", + countryProduction: "Вьетнам", + supplierVendorCode: "SUPPLIER-005", + mediaFiles: ["/api/placeholder/400/410"], + sizes: [ + { + chrtID: 111222, + techSize: "37", + wbSize: "37 Белый", + price: 3200, + discountedPrice: 2800, + quantity: 35, + }, + ], }, { nmID: 777888999, - vendorCode: 'SKU006', - title: 'Сумка женская черная', - description: 'Стильная женская сумка из экокожи', - brand: 'Accessories', - object: 'Сумки', - parent: 'Аксессуары', - countryProduction: 'Китай', - supplierVendorCode: 'SUPPLIER-006', - mediaFiles: ['/api/placeholder/400/411'], - sizes: [{ chrtID: 777888, techSize: 'Универсальный', wbSize: 'Черный', price: 1500, discountedPrice: 1200, quantity: 60 }] - } - ] + vendorCode: "SKU006", + title: "Сумка женская черная", + description: "Стильная женская сумка из экокожи", + brand: "Accessories", + object: "Сумки", + parent: "Аксессуары", + countryProduction: "Китай", + supplierVendorCode: "SUPPLIER-006", + mediaFiles: ["/api/placeholder/400/411"], + sizes: [ + { + chrtID: 777888, + techSize: "Универсальный", + wbSize: "Черный", + price: 1500, + discountedPrice: 1200, + quantity: 60, + }, + ], + }, + ]; // Загружаем товары при инициализации useEffect(() => { - loadCards() - }, [user]) + loadCards(); + }, [user]); const loadCards = async () => { - setLoading(true) + setLoading(true); try { - const wbApiKey = user?.organization?.apiKeys?.find(key => key.marketplace === 'WILDBERRIES') - + const wbApiKey = user?.organization?.apiKeys?.find( + (key) => key.marketplace === "WILDBERRIES" + ); + if (wbApiKey?.isActive) { - const validationData = wbApiKey.validationData as Record - const apiToken = validationData?.token || - validationData?.apiKey || - validationData?.key || - (wbApiKey as { apiKey?: string }).apiKey - + const validationData = wbApiKey.validationData as Record< + string, + string + >; + const apiToken = + validationData?.token || + validationData?.apiKey || + validationData?.key || + (wbApiKey as { apiKey?: string }).apiKey; + if (apiToken) { - console.log('Загружаем карточки из WB API...') - const cards = await WildberriesService.getAllCards(apiToken, 20) - setWbCards(cards) - console.log('Загружено карточек из WB API:', cards.length) - return + console.log("Загружаем карточки из WB API..."); + const cards = await WildberriesService.getAllCards(apiToken, 20); + setWbCards(cards); + console.log("Загружено карточек из WB API:", cards.length); + return; } } - + // Если API ключ не настроен, показываем моковые данные - console.log('API ключ WB не настроен, показываем моковые данные') - setWbCards(getMockCards()) + console.log("API ключ WB не настроен, показываем моковые данные"); + setWbCards(getMockCards()); } catch (error) { - console.error('Ошибка загрузки карточек WB:', error) + console.error("Ошибка загрузки карточек WB:", error); // При ошибке API показываем моковые данные - setWbCards(getMockCards()) + setWbCards(getMockCards()); } finally { - setLoading(false) + setLoading(false); } - } + }; const searchCards = async () => { if (!searchTerm.trim()) { - loadCards() - return + loadCards(); + return; } - - setLoading(true) + + setLoading(true); try { - const wbApiKey = user?.organization?.apiKeys?.find(key => key.marketplace === 'WILDBERRIES') - + const wbApiKey = user?.organization?.apiKeys?.find( + (key) => key.marketplace === "WILDBERRIES" + ); + if (wbApiKey?.isActive) { - const validationData = wbApiKey.validationData as Record - const apiToken = validationData?.token || - validationData?.apiKey || - validationData?.key || - (wbApiKey as { apiKey?: string }).apiKey - + const validationData = wbApiKey.validationData as Record< + string, + string + >; + const apiToken = + validationData?.token || + validationData?.apiKey || + validationData?.key || + (wbApiKey as { apiKey?: string }).apiKey; + if (apiToken) { - console.log('Поиск в WB API:', searchTerm) - const cards = await WildberriesService.searchCards(apiToken, searchTerm, 20) - setWbCards(cards) - console.log('Найдено карточек в WB API:', cards.length) - return + console.log("Поиск в WB API:", searchTerm); + const cards = await WildberriesService.searchCards( + apiToken, + searchTerm, + 20 + ); + setWbCards(cards); + console.log("Найдено карточек в WB API:", cards.length); + return; } } - + // Если API ключ не настроен, ищем в моковых данных - console.log('API ключ WB не настроен, поиск в моковых данных:', searchTerm) - const mockCards = getMockCards() - const filteredCards = mockCards.filter(card => - card.title.toLowerCase().includes(searchTerm.toLowerCase()) || - card.brand.toLowerCase().includes(searchTerm.toLowerCase()) || - card.nmID.toString().includes(searchTerm.toLowerCase()) || - card.object?.toLowerCase().includes(searchTerm.toLowerCase()) - ) - setWbCards(filteredCards) - console.log('Найдено моковых товаров:', filteredCards.length) + console.log( + "API ключ WB не настроен, поиск в моковых данных:", + searchTerm + ); + const mockCards = getMockCards(); + const filteredCards = mockCards.filter( + (card) => + card.title.toLowerCase().includes(searchTerm.toLowerCase()) || + card.brand.toLowerCase().includes(searchTerm.toLowerCase()) || + card.nmID.toString().includes(searchTerm.toLowerCase()) || + card.object?.toLowerCase().includes(searchTerm.toLowerCase()) + ); + setWbCards(filteredCards); + console.log("Найдено моковых товаров:", filteredCards.length); } catch (error) { - console.error('Ошибка поиска карточек WB:', error) + console.error("Ошибка поиска карточек WB:", error); // При ошибке ищем в моковых данных - const mockCards = getMockCards() - const filteredCards = mockCards.filter(card => - card.title.toLowerCase().includes(searchTerm.toLowerCase()) || - card.brand.toLowerCase().includes(searchTerm.toLowerCase()) || - card.nmID.toString().includes(searchTerm.toLowerCase()) || - card.object?.toLowerCase().includes(searchTerm.toLowerCase()) - ) - setWbCards(filteredCards) - console.log('Найдено моковых товаров (fallback):', filteredCards.length) + const mockCards = getMockCards(); + const filteredCards = mockCards.filter( + (card) => + card.title.toLowerCase().includes(searchTerm.toLowerCase()) || + card.brand.toLowerCase().includes(searchTerm.toLowerCase()) || + card.nmID.toString().includes(searchTerm.toLowerCase()) || + card.object?.toLowerCase().includes(searchTerm.toLowerCase()) + ); + setWbCards(filteredCards); + console.log("Найдено моковых товаров (fallback):", filteredCards.length); } finally { - setLoading(false) + setLoading(false); } - } + }; // Функции для работы с услугами и расходниками const loadOrganizationServices = async (organizationId: string) => { - if (organizationServices[organizationId]) return - + if (organizationServices[organizationId]) return; + try { const response = await apolloClient.query({ query: GET_COUNTERPARTY_SERVICES, - variables: { organizationId } - }) - + variables: { organizationId }, + }); + if (response.data?.counterpartyServices) { - setOrganizationServices(prev => ({ + setOrganizationServices((prev) => ({ ...prev, - [organizationId]: response.data.counterpartyServices - })) + [organizationId]: response.data.counterpartyServices, + })); } } catch (error) { - console.error('Ошибка загрузки услуг организации:', error) + console.error("Ошибка загрузки услуг организации:", error); } - } + }; const loadOrganizationSupplies = async (organizationId: string) => { - if (organizationSupplies[organizationId]) return - + if (organizationSupplies[organizationId]) return; + try { const response = await apolloClient.query({ query: GET_COUNTERPARTY_SUPPLIES, - variables: { organizationId } - }) - + variables: { organizationId }, + }); + if (response.data?.counterpartySupplies) { - setOrganizationSupplies(prev => ({ + setOrganizationSupplies((prev) => ({ ...prev, - [organizationId]: response.data.counterpartySupplies - })) + [organizationId]: response.data.counterpartySupplies, + })); } } catch (error) { - console.error('Ошибка загрузки расходников организации:', error) + console.error("Ошибка загрузки расходников организации:", error); } - } + }; // Работа с товарами поставки const addToSupply = (card: WildberriesCard) => { - const existingItem = supplyItems.find(item => item.card.nmID === card.nmID) + const existingItem = supplyItems.find( + (item) => item.card.nmID === card.nmID + ); if (existingItem) { - toast.info('Товар уже добавлен в поставку') - return + toast.info("Товар уже добавлен в поставку"); + return; } const newItem: SupplyItem = { @@ -353,114 +488,132 @@ export function DirectSupplyCreation({ onComplete, onCreateSupply, canCreateSupp quantity: 1200, pricePerUnit: 0, totalPrice: 0, - supplierId: '' - } + supplierId: "", + }; - setSupplyItems(prev => [...prev, newItem]) - toast.success('Товар добавлен в поставку') - } + setSupplyItems((prev) => [...prev, newItem]); + toast.success("Товар добавлен в поставку"); + }; const removeFromSupply = (nmID: number) => { - setSupplyItems(prev => prev.filter(item => item.card.nmID !== nmID)) - } + setSupplyItems((prev) => prev.filter((item) => item.card.nmID !== nmID)); + }; - const updateSupplyItem = (nmID: number, field: keyof SupplyItem, value: string | number) => { - setSupplyItems(prev => prev.map(item => { - if (item.card.nmID === nmID) { - const updatedItem = { ...item, [field]: value } - if (field === 'quantity' || field === 'pricePerUnit') { - updatedItem.totalPrice = updatedItem.quantity * updatedItem.pricePerUnit + const updateSupplyItem = ( + nmID: number, + field: keyof SupplyItem, + value: string | number + ) => { + setSupplyItems((prev) => + prev.map((item) => { + if (item.card.nmID === nmID) { + const updatedItem = { ...item, [field]: value }; + if (field === "quantity" || field === "pricePerUnit") { + updatedItem.totalPrice = + updatedItem.quantity * updatedItem.pricePerUnit; + } + return updatedItem; } - return updatedItem - } - return item - })) - } + return item; + }) + ); + }; // Работа с поставщиками const handleCreateSupplier = () => { if (!newSupplier.name || !newSupplier.contactName || !newSupplier.phone) { - toast.error('Заполните обязательные поля') - return + toast.error("Заполните обязательные поля"); + return; } const supplier: Supplier = { id: Date.now().toString(), - ...newSupplier - } + ...newSupplier, + }; - setSuppliers(prev => [...prev, supplier]) + setSuppliers((prev) => [...prev, supplier]); setNewSupplier({ - name: '', - contactName: '', - phone: '', - market: '', - address: '', - place: '', - telegram: '' - }) - setShowSupplierModal(false) - toast.success('Поставщик создан') - } + name: "", + contactName: "", + phone: "", + market: "", + address: "", + place: "", + telegram: "", + }); + setShowSupplierModal(false); + toast.success("Поставщик создан"); + }; - // Расчеты + // Расчеты для нового блока + const getTotalSum = () => { + return goodsPrice + fulfillmentServicesPrice + logisticsPrice; + }; + + // Оригинальные расчеты const getTotalQuantity = () => { - return supplyItems.reduce((sum, item) => sum + item.quantity, 0) - } + return supplyItems.reduce((sum, item) => sum + item.quantity, 0); + }; const getTotalItemsCost = () => { - return supplyItems.reduce((sum, item) => sum + item.totalPrice, 0) - } + return supplyItems.reduce((sum, item) => sum + item.totalPrice, 0); + }; const getServicesCost = () => { - if (!selectedFulfillmentOrg || selectedServices.length === 0) return 0 - - const services = organizationServices[selectedFulfillmentOrg] || [] - return selectedServices.reduce((sum, serviceId) => { - const service = services.find(s => s.id === serviceId) - return sum + (service ? service.price : 0) - }, 0) * getTotalQuantity() - } + if (!selectedFulfillmentOrg || selectedServices.length === 0) return 0; + + const services = organizationServices[selectedFulfillmentOrg] || []; + return ( + selectedServices.reduce((sum, serviceId) => { + const service = services.find((s) => s.id === serviceId); + return sum + (service ? service.price : 0); + }, 0) * getTotalQuantity() + ); + }; const getConsumablesCost = () => { - if (!selectedFulfillmentOrg || selectedConsumables.length === 0) return 0 - - const supplies = organizationSupplies[selectedFulfillmentOrg] || [] - return selectedConsumables.reduce((sum, supplyId) => { - const supply = supplies.find(s => s.id === supplyId) - return sum + (supply ? supply.price : 0) - }, 0) * getTotalQuantity() - } + if (!selectedFulfillmentOrg || selectedConsumables.length === 0) return 0; + + const supplies = organizationSupplies[selectedFulfillmentOrg] || []; + return ( + selectedConsumables.reduce((sum, supplyId) => { + const supply = supplies.find((s) => s.id === supplyId); + return sum + (supply ? supply.price : 0); + }, 0) * getTotalQuantity() + ); + }; 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 handleCreateSupplyInternal = async () => { if (supplyItems.length === 0) { - toast.error('Добавьте товары в поставку') - return + toast.error("Добавьте товары в поставку"); + return; } - if (!deliveryDate) { - toast.error('Выберите дату поставки') - return + if (!deliveryDateOriginal) { + toast.error("Выберите дату поставки"); + return; } - if (supplyItems.some(item => item.quantity <= 0 || item.pricePerUnit <= 0)) { - toast.error('Укажите количество и цену для всех товаров') - return + if ( + supplyItems.some((item) => item.quantity <= 0 || item.pricePerUnit <= 0) + ) { + toast.error("Укажите количество и цену для всех товаров"); + return; } try { const supplyInput = { - deliveryDate: deliveryDate.toISOString().split('T')[0], - cards: supplyItems.map(item => ({ + deliveryDate: deliveryDateOriginal.toISOString().split("T")[0], + cards: supplyItems.map((item) => ({ nmId: item.card.nmID.toString(), vendorCode: item.card.vendorCode, title: item.card.title, @@ -471,426 +624,846 @@ export function DirectSupplyCreation({ onComplete, onCreateSupply, canCreateSupp selectedFulfillmentServices: selectedServices, selectedConsumableOrg: selectedFulfillmentOrg, selectedConsumableServices: selectedConsumables, - deliveryDate: deliveryDate.toISOString().split('T')[0], - mediaFiles: item.card.mediaFiles - })) - } + deliveryDate: deliveryDateOriginal.toISOString().split("T")[0], + mediaFiles: item.card.mediaFiles, + })), + }; - await createSupply({ variables: { input: supplyInput } }) - toast.success('Поставка успешно создана!') - onComplete() + await createSupply({ variables: { input: supplyInput } }); + toast.success("Поставка успешно создана!"); + onComplete(); } catch (error) { - console.error('Error creating supply:', error) - toast.error('Ошибка при создании поставки') + console.error("Error creating supply:", error); + toast.error("Ошибка при создании поставки"); } - } + }; // Обработка внешнего вызова создания поставки React.useEffect(() => { if (isCreatingSupply) { - handleCreateSupplyInternal() + handleCreateSupplyInternal(); } - }, [isCreatingSupply]) + }, [isCreatingSupply]); // Обновление статуса возможности создания поставки React.useEffect(() => { - const canCreate = supplyItems.length > 0 && - deliveryDate !== null && - supplyItems.every(item => item.quantity > 0 && item.pricePerUnit > 0) - - if (onCanCreateSupplyChange) { - onCanCreateSupplyChange(canCreate) - } - }, [supplyItems, deliveryDate, onCanCreateSupplyChange]) + const canCreate = + supplyItems.length > 0 && + deliveryDateOriginal !== null && + supplyItems.every((item) => item.quantity > 0 && item.pricePerUnit > 0); - const fulfillmentOrgs = (counterpartiesData?.myCounterparties || []).filter((org: Organization) => org.type === 'FULFILLMENT') + if (onCanCreateSupplyChange) { + onCanCreateSupplyChange(canCreate); + } + }, [supplyItems, deliveryDateOriginal, onCanCreateSupplyChange]); + + const fulfillmentOrgs = (counterpartiesData?.myCounterparties || []).filter( + (org: Organization) => org.type === "FULFILLMENT" + ); const markets = [ - { value: 'sadovod', label: 'Садовод' }, - { value: 'tyak-moscow', label: 'ТЯК Москва' } - ] + { value: "sadovod", label: "Садовод" }, + { value: "tyak-moscow", label: "ТЯК Москва" }, + ]; return ( -
- {/* Основные настройки */} - - -
- {/* Дата поставки */} -
- - - - - - setDeliveryDate(date || undefined)} - minDate={new Date()} - inline - locale="ru" - /> - - -
- - {/* Фулфилмент */} -
- -
- - {/* Показатели */} -
-
Товаров
-
{getTotalQuantity()}
-
-
-
Стоимость
-
{formatCurrency(getTotalItemsCost()).replace(' ₽', '₽')}
-
-
-
Услуги ФФ
-
{formatCurrency(getServicesCost() + getConsumablesCost()).replace(' ₽', '₽')}
-
-
-
- - {/* Поиск и карточки */} - -
- setSearchTerm(e.target.value)} - className="bg-white/5 border-white/20 text-white placeholder-white/50 h-7 text-xs flex-1" - onKeyPress={(e) => e.key === 'Enter' && searchCards()} - /> - -
- -
- {loading ? ( - [...Array(6)].map((_, i) => ( -
- )) - ) : ( - wbCards.map((card) => { - const isInSupply = supplyItems.some(item => item.card.nmID === card.nmID) - return ( -
addToSupply(card)} - > - {card.title} - {isInSupply && ( -
- ✓ -
- )} -
- ) - }) - )} -
-
- - {/* Услуги и расходники в одной строке */} - {selectedFulfillmentOrg && ( - -
+ <> + +
+ {/* НОВЫЙ БЛОК СОЗДАНИЯ ПОСТАВКИ */} + + {/* Первая строка */} +
+ {/* 1. Модуль выбора даты */}
-
Услуги фулфилмента:
-
- {organizationServices[selectedFulfillmentOrg] ? ( - organizationServices[selectedFulfillmentOrg].map((service) => ( - - )) - ) : ( - Загрузка... - )} + +
+ setDeliveryDate(e.target.value)} + className="w-full h-8 rounded-lg border-0 bg-white/20 backdrop-blur px-2 py-1 text-white placeholder:text-white/50 focus:bg-white/30 focus:outline-none focus:ring-1 focus:ring-white/20 text-xs font-medium" + min={new Date().toISOString().split("T")[0]} + />
+ {/* 2. Модуль выбора фулфилмента */}
-
Расходные материалы:
-
- {organizationSupplies[selectedFulfillmentOrg] ? ( - organizationSupplies[selectedFulfillmentOrg].map((supply) => ( - - )) - ) : ( - Загрузка... - )} + + +
+ + {/* 3. Объём товаров */} +
+ + + setGoodsVolume(parseFloat(e.target.value) || 0) + } + placeholder="м³" + className="h-8 bg-white/20 border-0 text-white placeholder:text-white/50 focus:bg-white/30 focus:ring-1 focus:ring-white/20 text-xs" + /> +
+ + {/* 4. Грузовые места */} +
+ + setCargoPlaces(parseInt(e.target.value) || 0)} + placeholder="шт" + className="h-8 bg-white/20 border-0 text-white placeholder:text-white/50 focus:bg-white/30 focus:ring-1 focus:ring-white/20 text-xs" + /> +
+
+ + {/* Вторая строка */} +
+ {/* 5. Цена товаров */} +
+ + setGoodsPrice(parseFloat(e.target.value) || 0)} + placeholder="₽" + className="h-8 bg-white/20 border-0 text-white placeholder:text-white/50 focus:bg-white/30 focus:ring-1 focus:ring-white/20 text-xs" + /> +
+ + {/* 6. Цена услуг фулфилмента */} +
+ + + setFulfillmentServicesPrice(parseFloat(e.target.value) || 0) + } + placeholder="₽" + className="h-8 bg-white/20 border-0 text-white placeholder:text-white/50 focus:bg-white/30 focus:ring-1 focus:ring-white/20 text-xs" + /> +
+ + {/* 7. Цена логистики */} +
+ + + setLogisticsPrice(parseFloat(e.target.value) || 0) + } + placeholder="₽" + className="h-8 bg-white/20 border-0 text-white placeholder:text-white/50 focus:bg-white/30 focus:ring-1 focus:ring-white/20 text-xs" + /> +
+ + {/* 8. Итоговая сумма */} +
+ +
+ + {formatCurrency(getTotalSum()).replace(" ₽", " ₽")} +
- )} - {/* Компактная таблица товаров */} - -
- Товары в поставке - -
- - {supplyItems.length === 0 ? ( -
- -

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

+ {/* Блок поиска товаров - оптимизированное расположение */} + +
+ +
+ 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()} + /> + +
- ) : ( + + {/* Карточки товаров - увеличенный размер и единообразность */}
- {supplyItems.map((item) => ( -
- {/* Товар */} -
- {item.card.title} -
-
{item.card.title}
-
Арт: {item.card.vendorCode}
+
+ + Найдено товаров: {wbCards.length} + + {supplyItems.length > 0 && ( + + В поставке: {supplyItems.length} + + )} +
+ +
+ {loading + ? [...Array(12)].map((_, i) => ( +
+ )) + : wbCards.map((card) => { + const isInSupply = supplyItems.some( + (item) => item.card.nmID === card.nmID + ); + return ( +
addToSupply(card)} + > +
+ {card.title} + + {/* Оверлей с информацией */} +
+
+
+ {card.title} +
+
+ Арт: {card.vendorCode} +
+ {card.sizes && card.sizes[0] && ( +
+ от{" "} + {card.sizes[0].discountedPrice || + card.sizes[0].price}{" "} + ₽ +
+ )} +
+
+ + {/* Индикатор добавления в поставку */} + {isInSupply && ( +
+ ✓ +
+ )} + + {/* Индикатор при наведении */} +
+ +
+
+
+ ); + })} +
+ + {!loading && wbCards.length === 0 && ( +
+ +

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

+ {searchTerm && ( +

+ Попробуйте изменить условия поиска +

+ )} +
+ )} +
+ + + {/* Услуги и расходники в одной строке */} + {selectedFulfillmentOrg && ( + +
+
+
Услуги фулфилмента:
+
+ {organizationServices[selectedFulfillmentOrg] ? ( + organizationServices[selectedFulfillmentOrg].map( + (service) => ( + + ) + ) + ) : ( + Загрузка... + )} +
+
+ +
+
Расходные материалы:
+
+ {organizationSupplies[selectedFulfillmentOrg] ? ( + organizationSupplies[selectedFulfillmentOrg].map( + (supply) => ( + + ) + ) + ) : ( + Загрузка... + )} +
+
+
+
+ )} + + {/* Модуль товаров в поставке - новый дизайн */} + +
+ + Товары в поставке + + +
+ + {supplyItems.length === 0 ? ( +
+ +

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

+
+ ) : ( +
+ {supplyItems.map((item) => ( + + {/* Заголовок товара с кнопкой удаления */} +
+
+
+ {item.card.title} +
+
+ Арт: {item.card.vendorCode} +
+
+
-
- {/* Количество */} -
- updateSupplyItem(item.card.nmID, 'quantity', parseInt(e.target.value) || 0)} - className="bg-purple-500 border-0 text-white text-center h-6 text-xs font-bold" - min="1" - /> -
- - {/* Цена */} -
- updateSupplyItem(item.card.nmID, 'pricePerUnit', parseFloat(e.target.value) || 0)} - className="bg-white/10 border-white/20 text-white text-center h-6 text-xs" - placeholder="Цена" - /> -
- - {/* Поставщик */} -
- + updateSupplyItem( + item.card.nmID, + "quantity", + parseInt(e.target.value) || 0 + ) + } + className="bg-purple-500/20 border-purple-400/30 text-white text-center h-7 text-xs font-bold" + min="1" + /> +
+ + {/* Блок 4: Цена */} +
+
+ За единицу +
+ + updateSupplyItem( + item.card.nmID, + "pricePerUnit", + parseFloat(e.target.value) || 0 + ) + } + className="bg-white/20 border-white/20 text-white text-center h-7 text-xs" + placeholder="₽" + /> +
+ {formatCurrency(item.totalPrice).replace(" ₽", "₽")} +
+
+ + {/* Блок 5: Услуги фулфилмента */} +
+
+ {selectedFulfillmentOrg && + organizationServices[selectedFulfillmentOrg] ? ( + organizationServices[selectedFulfillmentOrg] + .slice(0, 2) + .map((service) => ( + + )) + ) : ( + + Выберите фулфилмент + + )} +
+
+ + {/* Блок 6: Поставщик */} +
+ +
+ + {/* Блок 7: Расходники фулфилмента */} +
+
+ {selectedFulfillmentOrg && + organizationSupplies[selectedFulfillmentOrg] ? ( + organizationSupplies[selectedFulfillmentOrg] + .slice(0, 2) + .map((supply) => ( + + )) + ) : ( + + Выберите фулфилмент + + )} +
+
+ + {/* Блок 8: Расходники селлера */} +
+
+ + + +
+
+
+ + ))} +
+ )} + + + {/* Модальное окно создания поставщика */} + + + + + Создать поставщика + + +
+
+
+ + + setNewSupplier((prev) => ({ + ...prev, + name: e.target.value, + })) + } + className="bg-white/10 border-white/20 text-white h-8 text-xs" + placeholder="Название" + /> +
+
+ + + setNewSupplier((prev) => ({ + ...prev, + contactName: e.target.value, + })) + } + className="bg-white/10 border-white/20 text-white h-8 text-xs" + placeholder="Имя" + /> +
+
+ +
+
+ + + setNewSupplier((prev) => ({ + ...prev, + phone: e.target.value, + })) + } + className="bg-white/10 border-white/20 text-white h-8 text-xs" + placeholder="+7 999 123-45-67" + /> +
+
+ +
+
- {/* Сумма и удаление */} -
- - {formatCurrency(item.totalPrice).replace(' ₽', '₽')} - - +
+
+ + + setNewSupplier((prev) => ({ + ...prev, + address: e.target.value, + })) + } + className="bg-white/10 border-white/20 text-white h-8 text-xs" + placeholder="Адрес" + /> +
+
+ + + setNewSupplier((prev) => ({ + ...prev, + place: e.target.value, + })) + } + className="bg-white/10 border-white/20 text-white h-8 text-xs" + placeholder="Павильон/место" + />
- ))} -
- )} - - {/* Модальное окно создания поставщика */} - - - - Создать поставщика - -
-
- + setNewSupplier(prev => ({ ...prev, name: e.target.value }))} + value={newSupplier.telegram} + onChange={(e) => + setNewSupplier((prev) => ({ + ...prev, + telegram: e.target.value, + })) + } className="bg-white/10 border-white/20 text-white h-8 text-xs" - placeholder="Название" + placeholder="@username" />
-
- - setNewSupplier(prev => ({ ...prev, contactName: e.target.value }))} - className="bg-white/10 border-white/20 text-white h-8 text-xs" - placeholder="Имя" - /> -
-
-
-
- - setNewSupplier(prev => ({ ...prev, phone: e.target.value }))} - className="bg-white/10 border-white/20 text-white h-8 text-xs" - placeholder="+7 999 123-45-67" - /> -
-
- - + Отмена + +
- -
-
- - setNewSupplier(prev => ({ ...prev, address: e.target.value }))} - className="bg-white/10 border-white/20 text-white h-8 text-xs" - placeholder="Адрес" - /> -
-
- - setNewSupplier(prev => ({ ...prev, place: e.target.value }))} - className="bg-white/10 border-white/20 text-white h-8 text-xs" - placeholder="Павильон/место" - /> -
-
- -
- - setNewSupplier(prev => ({ ...prev, telegram: e.target.value }))} - className="bg-white/10 border-white/20 text-white h-8 text-xs" - placeholder="@username" - /> -
- -
- - -
-
-
-
-
- ) -} \ No newline at end of file +
+
+
+ + ); +}