
ЭТАП 1.2: Безопасное выделение бизнес-логики в хуки - Create useSupplierSelection.ts: управление выбором поставщиков и поиском - Create useProductCatalog.ts: загрузка и управление каталогом товаров - Create useSupplyCart.ts: логика корзины и создания поставки - Create useRecipeBuilder.ts: построение рецептур товаров (услуги + расходники) Каждый хук инкапсулирует отдельную область ответственности: - Состояние и действия изолированы - GraphQL запросы сгруппированы по функциональности - Бизнес-логика отделена от UI компонентов - Полная типизация с TypeScript No functional changes - pure logic extraction for better maintainability. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
126 lines
3.9 KiB
TypeScript
126 lines
3.9 KiB
TypeScript
/**
|
||
* ХУКА ДЛЯ ЛОГИКИ ВЫБОРА ПОСТАВЩИКОВ
|
||
*
|
||
* Выделена из create-suppliers-supply-page.tsx
|
||
* Управляет состоянием выбранного поставщика и поиском
|
||
*/
|
||
|
||
import { useQuery } from '@apollo/client'
|
||
import { useState, useMemo } from 'react'
|
||
|
||
import { GET_MY_COUNTERPARTIES } from '@/graphql/queries'
|
||
|
||
import type { GoodsSupplier } from '../types/supply-creation.types'
|
||
|
||
export function useSupplierSelection() {
|
||
// Состояния
|
||
const [selectedSupplier, setSelectedSupplier] = useState<GoodsSupplier | null>(null)
|
||
const [searchQuery, setSearchQuery] = useState('')
|
||
|
||
// Загружаем партнеров-поставщиков согласно rules2.md 13.3
|
||
const {
|
||
data: counterpartiesData,
|
||
loading: counterpartiesLoading,
|
||
error: counterpartiesError,
|
||
} = useQuery(GET_MY_COUNTERPARTIES, {
|
||
errorPolicy: 'all', // Показываем все ошибки, но не прерываем работу
|
||
onError: (error) => {
|
||
try {
|
||
console.warn('🚨 GET_MY_COUNTERPARTIES ERROR:', {
|
||
errorMessage: error?.message || 'Unknown error',
|
||
hasGraphQLErrors: !!error?.graphQLErrors?.length,
|
||
hasNetworkError: !!error?.networkError,
|
||
})
|
||
} catch (logError) {
|
||
console.warn('❌ Error in counterparties error handler:', logError)
|
||
}
|
||
},
|
||
})
|
||
|
||
// Обработка данных контрагентов
|
||
const allCounterparties: GoodsSupplier[] = useMemo(() => {
|
||
try {
|
||
return counterpartiesData?.myCounterparties || []
|
||
} catch (error) {
|
||
console.warn('❌ Error processing counterparties data:', error)
|
||
return []
|
||
}
|
||
}, [counterpartiesData])
|
||
|
||
// Показываем только партнеров с типом WHOLESALE согласно rules2.md 13.3
|
||
const wholesaleSuppliers = useMemo(() => {
|
||
return allCounterparties.filter((cp: GoodsSupplier) => {
|
||
try {
|
||
return cp && cp.type === 'WHOLESALE'
|
||
} catch (error) {
|
||
console.warn('❌ Error filtering wholesale supplier:', error)
|
||
return false
|
||
}
|
||
})
|
||
}, [allCounterparties])
|
||
|
||
// Фильтрация поставщиков по поисковому запросу
|
||
const suppliers = useMemo(() => {
|
||
return wholesaleSuppliers.filter((cp: GoodsSupplier) => {
|
||
try {
|
||
if (!searchQuery.trim()) return true
|
||
const searchLower = searchQuery.toLowerCase()
|
||
const name = (cp.name || cp.fullName || '').toLowerCase()
|
||
const inn = (cp.inn || '').toLowerCase()
|
||
return name.includes(searchLower) || inn.includes(searchLower)
|
||
} catch (error) {
|
||
console.warn('❌ Error filtering supplier by search:', error)
|
||
return false
|
||
}
|
||
})
|
||
}, [wholesaleSuppliers, searchQuery])
|
||
|
||
// Утилитарные функции для работы с рынками
|
||
const getMarketLabel = (market?: string) => {
|
||
switch (market) {
|
||
case 'wildberries':
|
||
return 'WB'
|
||
case 'ozon':
|
||
return 'OZON'
|
||
case 'yandexmarket':
|
||
return 'YM'
|
||
default:
|
||
return 'Универсальный'
|
||
}
|
||
}
|
||
|
||
const getMarketBadgeStyle = (market?: string) => {
|
||
switch (market) {
|
||
case 'wildberries':
|
||
return 'bg-purple-500/20 text-purple-300 border-purple-500/30'
|
||
case 'ozon':
|
||
return 'bg-blue-500/20 text-blue-300 border-blue-500/30'
|
||
case 'yandexmarket':
|
||
return 'bg-yellow-500/20 text-yellow-300 border-yellow-500/30'
|
||
default:
|
||
return 'bg-gray-500/20 text-gray-300 border-gray-500/30'
|
||
}
|
||
}
|
||
|
||
return {
|
||
// Состояние
|
||
selectedSupplier,
|
||
setSelectedSupplier,
|
||
searchQuery,
|
||
setSearchQuery,
|
||
|
||
// Данные
|
||
suppliers,
|
||
allCounterparties,
|
||
wholesaleSuppliers,
|
||
|
||
// Статусы загрузки
|
||
loading: counterpartiesLoading,
|
||
error: counterpartiesError,
|
||
|
||
// Утилиты
|
||
getMarketLabel,
|
||
getMarketBadgeStyle,
|
||
}
|
||
}
|