'use client' import { useMutation } from '@apollo/client' import { Calendar, Package, Truck, User, CheckCircle, Clock, XCircle, MapPin, Phone, Mail, Building, Hash, ChevronDown, ChevronUp, MessageCircle, Loader2, } from 'lucide-react' import { useState } from 'react' import { toast } from 'sonner' import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { Card } from '@/components/ui/card' import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog' import { Textarea } from '@/components/ui/textarea' import { SUPPLIER_APPROVE_ORDER, SUPPLIER_REJECT_ORDER, SUPPLIER_SHIP_ORDER } from '@/graphql/mutations' import { GET_SUPPLY_ORDERS } from '@/graphql/queries' interface SupplierOrderCardProps { order: { id: string organizationId: string partnerId: string deliveryDate: string status: | 'PENDING' | 'SUPPLIER_APPROVED' | 'CONFIRMED' | 'LOGISTICS_CONFIRMED' | 'SHIPPED' | 'IN_TRANSIT' | 'DELIVERED' | 'CANCELLED' totalAmount: number totalItems: number createdAt: string organization: { id: string name?: string fullName?: string type: string inn?: string } fulfillmentCenter?: { id: string name?: string fullName?: string type: string } logisticsPartner?: { id: string name?: string fullName?: string type: string } items: Array<{ id: string quantity: number price: number totalPrice: number product: { id: string name: string article: string description?: string category?: { id: string name: string } } }> } } export function SupplierOrderCard({ order }: SupplierOrderCardProps) { const [isExpanded, setIsExpanded] = useState(false) const [showRejectModal, setShowRejectModal] = useState(false) const [rejectReason, setRejectReason] = useState('') // Мутации для действий поставщика const [supplierApproveOrder, { loading: approving }] = useMutation(SUPPLIER_APPROVE_ORDER, { refetchQueries: [{ query: GET_SUPPLY_ORDERS }], onCompleted: (data) => { if (data.supplierApproveOrder.success) { toast.success(data.supplierApproveOrder.message) } else { toast.error(data.supplierApproveOrder.message) } }, onError: (error) => { console.error('Error approving order:', error) toast.error('Ошибка при одобрении заказа') }, }) const [supplierRejectOrder, { loading: rejecting }] = useMutation(SUPPLIER_REJECT_ORDER, { refetchQueries: [{ query: GET_SUPPLY_ORDERS }], onCompleted: (data) => { if (data.supplierRejectOrder.success) { toast.success(data.supplierRejectOrder.message) } else { toast.error(data.supplierRejectOrder.message) } setShowRejectModal(false) setRejectReason('') }, onError: (error) => { console.error('Error rejecting order:', error) toast.error('Ошибка при отклонении заказа') }, }) const [supplierShipOrder, { loading: shipping }] = useMutation(SUPPLIER_SHIP_ORDER, { refetchQueries: [{ query: GET_SUPPLY_ORDERS }], onCompleted: (data) => { if (data.supplierShipOrder.success) { toast.success(data.supplierShipOrder.message) } else { toast.error(data.supplierShipOrder.message) } }, onError: (error) => { console.error('Error shipping order:', error) toast.error('Ошибка при отправке заказа') }, }) const handleApproveOrder = async () => { try { await supplierApproveOrder({ variables: { id: order.id }, }) } catch (error) { console.error('Error in handleApproveOrder:', error) } } const handleRejectOrder = async () => { if (!rejectReason.trim()) { toast.error('Укажите причину отклонения заявки') return } try { await supplierRejectOrder({ variables: { id: order.id, reason: rejectReason, }, }) } catch (error) { console.error('Error in handleRejectOrder:', error) } } const handleShipOrder = async () => { try { await supplierShipOrder({ variables: { id: order.id }, }) } catch (error) { console.error('Error in handleShipOrder:', error) } } const getStatusBadge = (status: string) => { switch (status) { case 'PENDING': return 🟡 ОЖИДАЕТ case 'SUPPLIER_APPROVED': return 🟢 ОДОБРЕНО case 'CONFIRMED': case 'LOGISTICS_CONFIRMED': return 🔵 В РАБОТЕ case 'SHIPPED': case 'IN_TRANSIT': return 🟠 В ПУТИ case 'DELIVERED': return ✅ ДОСТАВЛЕНО default: return {status} } } const formatDate = (dateString: string) => { return new Date(dateString).toLocaleDateString('ru-RU', { day: '2-digit', month: '2-digit', year: 'numeric', }) } const getInitials = (name: string) => { return name .split(' ') .map((word) => word[0]) .join('') .toUpperCase() .slice(0, 2) } const calculateVolume = () => { // Примерный расчет объема - можно улучшить на основе реальных данных о товарах return (order.totalItems * 0.02).toFixed(1) // 0.02 м³ на единицу товара } return ( <> {/* Основная информация - структура согласно правилам */} {/* Шапка заявки */} СФ-{order.id.slice(-8)} {formatDate(order.createdAt)} {getStatusBadge(order.status)} {/* Информация об участниках */} {/* Заказчик */} Заказчик: {getInitials(order.organization.name || order.organization.fullName || 'ОРГ')} {order.organization.name || order.organization.fullName} {order.organization.inn && ИНН: {order.organization.inn}} {/* Фулфилмент */} {order.fulfillmentCenter && ( Фулфилмент: {order.fulfillmentCenter.name || order.fulfillmentCenter.fullName} )} {/* Логистика */} {order.logisticsPartner && ( Логистика: {order.logisticsPartner.name || order.logisticsPartner.fullName} )} {/* Краткая информация о заказе */} {order.items.length} вид {order.items.length === 1 ? '' : order.items.length < 5 ? 'а' : 'ов'} товаров {order.totalItems} единиц 📏 {calculateVolume()} м³ 💰 {order.totalAmount.toLocaleString()}₽ {/* Кнопки действий */} setIsExpanded(!isExpanded)} className="text-white/70 hover:text-white" > Подробности {isExpanded ? : } {/* Действия для PENDING */} {order.status === 'PENDING' && ( <> {approving ? ( ) : ( )} Одобрить setShowRejectModal(true)} disabled={rejecting} className="glass-secondary bg-red-500/20 hover:bg-red-500/30 text-red-300 border border-red-500/30" > Отклонить > )} {/* Действие для LOGISTICS_CONFIRMED */} {order.status === 'LOGISTICS_CONFIRMED' && ( {shipping ? : } Отгрузить )} {/* Кнопка связаться всегда доступна */} Связаться {/* Срок доставки */} Доставка: Склад фулфилмента Срок: {formatDate(order.deliveryDate)} {/* Расширенная детализация */} {isExpanded && ( 📋 ДЕТАЛИ ЗАЯВКИ #{order.id.slice(-8)} {/* Товары в заявке */} 📦 ТОВАРЫ В ЗАЯВКЕ: {order.items.map((item) => ( {item.product.name} • {item.quantity} шт • {item.price} ₽/шт = {item.totalPrice.toLocaleString()}₽ Артикул: {item.product.article} {item.product.category && ` • ${item.product.category.name}`} ))} Общая стоимость: {order.totalAmount.toLocaleString()}₽ {/* Логистическая информация */} 📍 ЛОГИСТИЧЕСКАЯ ИНФОРМАЦИЯ: • Объем груза: {calculateVolume()} м³ • Предварительная стоимость доставки: ~ {Math.round(parseFloat(calculateVolume()) * 3500).toLocaleString()}₽ • Маршрут: Склад поставщика → {order.fulfillmentCenter?.name || 'Фулфилмент-центр'} {/* Контактная информация */} 📞 КОНТАКТЫ: • Заказчик: {order.organization.name || order.organization.fullName} {order.organization.inn && ` (ИНН: ${order.organization.inn})`} {order.fulfillmentCenter && ( • Фулфилмент: {order.fulfillmentCenter.name || order.fulfillmentCenter.fullName} )} )} {/* Модал отклонения заявки */} Отклонить заявку Причина отклонения заявки: setRejectReason(e.target.value)} placeholder="Укажите причину отклонения..." className="glass-input text-white placeholder:text-white/50" rows={3} /> setShowRejectModal(false)} className="text-white/70 hover:text-white" > Отмена {rejecting ? : } Отклонить заявку > ) }
{order.organization.name || order.organization.fullName}
ИНН: {order.organization.inn}
{order.fulfillmentCenter.name || order.fulfillmentCenter.fullName}
{order.logisticsPartner.name || order.logisticsPartner.fullName}