'use client' import { useQuery, useMutation } from '@apollo/client' import { Users, ArrowUpCircle, ArrowDownCircle, Search, Filter, SortAsc, SortDesc, Calendar, Building, Phone, Mail, MapPin, X, Copy, Gift, TrendingUp, } from 'lucide-react' import React, { useState, useMemo } from 'react' import { toast } from 'sonner' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { Card } from '@/components/ui/card' import { GlassInput } from '@/components/ui/input' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { RESPOND_TO_COUNTERPARTY_REQUEST, CANCEL_COUNTERPARTY_REQUEST, REMOVE_COUNTERPARTY } from '@/graphql/mutations' import { GET_MY_COUNTERPARTIES, GET_INCOMING_REQUESTS, GET_OUTGOING_REQUESTS, SEARCH_ORGANIZATIONS, GET_MY_PARTNER_LINK, } from '@/graphql/queries' import { OrganizationAvatar } from './organization-avatar' interface Organization { id: string inn: string name?: string fullName?: string type: 'FULFILLMENT' | 'SELLER' | 'LOGIST' | 'WHOLESALE' address?: string phones?: Array<{ value: string }> emails?: Array<{ value: string }> createdAt: string users?: Array<{ id: string; avatar?: string }> } interface CounterpartyRequest { id: string message?: string status: 'PENDING' | 'ACCEPTED' | 'REJECTED' | 'CANCELLED' createdAt: string sender: Organization receiver: Organization } type SortField = 'name' | 'date' | 'inn' | 'type' type SortOrder = 'asc' | 'desc' export function MarketCounterparties() { const [searchQuery, setSearchQuery] = useState('') const [typeFilter, setTypeFilter] = useState('all') const [sortField, setSortField] = useState('name') const [sortOrder, setSortOrder] = useState('asc') const { data: counterpartiesData, loading: counterpartiesLoading } = useQuery(GET_MY_COUNTERPARTIES) const { data: incomingData, loading: incomingLoading } = useQuery(GET_INCOMING_REQUESTS) const { data: outgoingData, loading: outgoingLoading } = useQuery(GET_OUTGOING_REQUESTS) const { data: partnerLinkData } = useQuery(GET_MY_PARTNER_LINK) const [respondToRequest] = useMutation(RESPOND_TO_COUNTERPARTY_REQUEST, { refetchQueries: [ { query: GET_INCOMING_REQUESTS }, { query: GET_MY_COUNTERPARTIES }, { query: SEARCH_ORGANIZATIONS, variables: { type: 'SELLER' } }, { query: SEARCH_ORGANIZATIONS, variables: { type: 'FULFILLMENT' } }, { query: SEARCH_ORGANIZATIONS, variables: { type: 'LOGIST' } }, { query: SEARCH_ORGANIZATIONS, variables: { type: 'WHOLESALE' } }, ], awaitRefetchQueries: true, }) const [cancelRequest] = useMutation(CANCEL_COUNTERPARTY_REQUEST, { refetchQueries: [ { query: GET_OUTGOING_REQUESTS }, { query: SEARCH_ORGANIZATIONS, variables: { type: 'SELLER' } }, { query: SEARCH_ORGANIZATIONS, variables: { type: 'FULFILLMENT' } }, { query: SEARCH_ORGANIZATIONS, variables: { type: 'LOGIST' } }, { query: SEARCH_ORGANIZATIONS, variables: { type: 'WHOLESALE' } }, ], awaitRefetchQueries: true, }) const [removeCounterparty] = useMutation(REMOVE_COUNTERPARTY, { refetchQueries: [ { query: GET_MY_COUNTERPARTIES }, { query: SEARCH_ORGANIZATIONS, variables: { type: 'SELLER' } }, { query: SEARCH_ORGANIZATIONS, variables: { type: 'FULFILLMENT' } }, { query: SEARCH_ORGANIZATIONS, variables: { type: 'LOGIST' } }, { query: SEARCH_ORGANIZATIONS, variables: { type: 'WHOLESALE' } }, ], awaitRefetchQueries: true, }) // Функция копирования партнерской ссылки const copyPartnerLink = async () => { try { const partnerLink = partnerLinkData?.myPartnerLink if (!partnerLink) { toast.error('Партнерская ссылка недоступна') return } await navigator.clipboard.writeText(partnerLink) toast.success('Партнерская ссылка скопирована!', { description: 'Поделитесь ей для прямого делового сотрудничества', }) } catch { toast.error('Не удалось скопировать ссылку') } } // Фильтрация и сортировка контрагентов const filteredAndSortedCounterparties = useMemo(() => { const filtered = (counterpartiesData?.myCounterparties || []).filter((org: Organization) => { const matchesSearch = !searchQuery || org.name?.toLowerCase().includes(searchQuery.toLowerCase()) || org.fullName?.toLowerCase().includes(searchQuery.toLowerCase()) || org.inn.includes(searchQuery) || org.address?.toLowerCase().includes(searchQuery.toLowerCase()) const matchesType = typeFilter === 'all' || org.type === typeFilter return matchesSearch && matchesType }) // Сортировка filtered.sort((a: Organization, b: Organization) => { let aValue: string | number let bValue: string | number switch (sortField) { case 'name': aValue = (a.name || a.fullName || '').toLowerCase() bValue = (b.name || b.fullName || '').toLowerCase() break case 'date': aValue = new Date(a.createdAt).getTime() bValue = new Date(b.createdAt).getTime() break case 'inn': aValue = a.inn bValue = b.inn break case 'type': aValue = a.type bValue = b.type break default: return 0 } if (sortOrder === 'asc') { return aValue < bValue ? -1 : aValue > bValue ? 1 : 0 } else { return aValue > bValue ? -1 : aValue < bValue ? 1 : 0 } }) return filtered }, [counterpartiesData?.myCounterparties, searchQuery, typeFilter, sortField, sortOrder]) const handleAcceptRequest = async (requestId: string) => { try { await respondToRequest({ variables: { requestId, accept: true }, }) } catch (error) { console.error('Ошибка при принятии заявки:', error) } } const handleRejectRequest = async (requestId: string) => { try { await respondToRequest({ variables: { requestId, accept: false }, }) } catch (error) { console.error('Ошибка при отклонении заявки:', error) } } const handleCancelRequest = async (requestId: string) => { try { await cancelRequest({ variables: { requestId }, }) } catch (error) { console.error('Ошибка при отмене заявки:', error) } } const handleRemoveCounterparty = async (organizationId: string) => { try { await removeCounterparty({ variables: { organizationId }, }) } catch (error) { console.error('Ошибка при удалении контрагента:', error) } } const formatDate = (dateString: string) => { if (!dateString) return '' try { let date: Date // Проверяем, является ли строка числом (Unix timestamp) if (/^\d+$/.test(dateString)) { // Если это Unix timestamp в миллисекундах const timestamp = parseInt(dateString, 10) date = new Date(timestamp) } else { // Обычная строка даты date = new Date(dateString) } if (isNaN(date.getTime())) { return 'Неверная дата' } return date.toLocaleDateString('ru-RU', { year: 'numeric', month: 'long', day: 'numeric', }) } catch { return 'Ошибка даты' } } const clearFilters = () => { setSearchQuery('') setTypeFilter('all') setSortField('name') setSortOrder('asc') } const hasActiveFilters = searchQuery || typeFilter !== 'all' || sortField !== 'name' || sortOrder !== 'asc' const counterparties = counterpartiesData?.myCounterparties || [] const incomingRequests = incomingData?.incomingRequests || [] const outgoingRequests = outgoingData?.outgoingRequests || [] const getTypeLabel = (type: string) => { switch (type) { case 'FULFILLMENT': return 'Фулфилмент' case 'SELLER': return 'Селлер' case 'LOGIST': return 'Логистика' case 'WHOLESALE': return 'Поставщик' default: return type } } const getTypeBadgeStyles = (type: string) => { switch (type) { case 'FULFILLMENT': return 'bg-blue-500/20 text-blue-300 border-blue-500/30' case 'SELLER': return 'bg-green-500/20 text-green-300 border-green-500/30' case 'LOGIST': return 'bg-orange-500/20 text-orange-300 border-orange-500/30' case 'WHOLESALE': return 'bg-purple-500/20 text-purple-300 border-purple-500/30' default: return 'bg-gray-500/20 text-gray-300 border-gray-500/30' } } return (
Контрагенты ({counterparties.length}) 0 ? 'ring-2 ring-green-400/50 animate-pulse' : ''}`} > Входящие ({incomingRequests.length}) {incomingRequests.length > 0 && (
)}
Исходящие ({outgoingRequests.length})
{/* Компактный блок с партнерской ссылкой */}

Партнерская ссылка

Прямое деловое сотрудничество с автоматическим добавлением
{partnerLinkData?.myPartnerLink || 'http://localhost:3000/register?partner=LOADING'}
{/* Компактная статистика */}

Партнеров

{counterpartiesLoading ? ( ) : ( counterparties.length )}

Заявок

{incomingLoading ? ( ) : ( incomingRequests.length )}

За месяц

{counterpartiesLoading ? ( ) : ( counterparties.filter(org => { const monthAgo = new Date(); monthAgo.setMonth(monthAgo.getMonth() - 1); return new Date(org.createdAt) > monthAgo; }).length )}

Исходящих

{outgoingLoading ? ( ) : ( outgoingRequests.length )}

{/* Компактные фильтры */}
{/* Поиск */}
setSearchQuery(e.target.value)} className="pl-10 h-9" />
{/* Фильтры и сортировка */}
{hasActiveFilters && ( )}
{/* Статистика и быстрые фильтры */}
{filteredAndSortedCounterparties.length} из {counterparties.length}
{['FULFILLMENT', 'SELLER', 'LOGIST', 'WHOLESALE'].map((type) => { const count = counterparties.filter((org: Organization) => org.type === type).length if (count === 0) return null return ( ) })}
{/* Таблица контрагентов */}
{/* Заголовок таблицы */}
Дата добавления
Организация
Тип
Контакты
Адрес
Действия
{/* Строки таблицы */} {counterpartiesLoading ? (
Загрузка...
) : filteredAndSortedCounterparties.length === 0 ? (
{counterparties.length === 0 ? ( <>

У вас пока нет контрагентов

Перейдите на другие вкладки, чтобы найти партнеров

) : ( <>

Ничего не найдено

Попробуйте изменить параметры поиска или фильтрации

)}
) : ( filteredAndSortedCounterparties.map((organization: Organization) => (
{formatDate(organization.createdAt)}

{organization.name || organization.fullName}

{organization.inn}

{getTypeLabel(organization.type)}
{organization.phones && organization.phones.length > 0 && (
{organization.phones[0].value}
)} {organization.emails && organization.emails.length > 0 && (
{organization.emails[0].value}
)} {!organization.phones?.length && !organization.emails?.length && ( Нет контактов )}
{organization.address ? (

{organization.address}

) : ( Не указан )}
)) )}
{incomingLoading ? (
Загрузка...
) : incomingRequests.length === 0 ? (

Нет входящих заявок

) : (
{incomingRequests.map((request: CounterpartyRequest) => (

{request.sender.name || request.sender.fullName}

{getTypeLabel(request.sender.type)}

ИНН: {request.sender.inn}

{request.sender.address && (
{request.sender.address}
)} {request.message && (

"{request.message}"

)}
Заявка от {formatDate(request.createdAt)}
))}
)}
{outgoingLoading ? (
Загрузка...
) : outgoingRequests.length === 0 ? (

Нет исходящих заявок

) : (
{outgoingRequests.map((request: CounterpartyRequest) => (

{request.receiver.name || request.receiver.fullName}

{getTypeLabel(request.receiver.type)} {request.status === 'PENDING' ? 'Ожидает ответа' : request.status === 'REJECTED' ? 'Отклонено' : request.status}

ИНН: {request.receiver.inn}

{request.receiver.address && (
{request.receiver.address}
)} {request.message && (

"{request.message}"

)}
Отправлено {formatDate(request.createdAt)}
{request.status === 'PENDING' && ( )}
))}
)}
) }