import { useState } from 'react'; import { Link } from 'react-router-dom'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { Plus, Loader2, AlertCircle, Truck, Clock, User, List, ChevronRight, AlertTriangle, } from 'lucide-react'; import { serviceOrdersApi } from '../services/api/serviceOrders'; import type { ServiceOrder } from '../services/api/serviceOrders'; import type { ServiceOrderStatus } from '../types'; // Kanban columns configuration const KANBAN_COLUMNS: { status: ServiceOrderStatus; label: string; color: string; bgColor: string }[] = [ { status: 'received', label: 'Recibidos', color: 'border-blue-500', bgColor: 'bg-blue-50' }, { status: 'diagnosing', label: 'Diagnosticando', color: 'border-purple-500', bgColor: 'bg-purple-50' }, { status: 'quoted', label: 'Cotizados', color: 'border-yellow-500', bgColor: 'bg-yellow-50' }, { status: 'approved', label: 'Aprobados', color: 'border-green-500', bgColor: 'bg-green-50' }, { status: 'in_repair', label: 'En Reparacion', color: 'border-orange-500', bgColor: 'bg-orange-50' }, { status: 'waiting_parts', label: 'Esperando Partes', color: 'border-red-500', bgColor: 'bg-red-50' }, { status: 'ready', label: 'Listos', color: 'border-emerald-500', bgColor: 'bg-emerald-50' }, ]; const PRIORITY_CONFIG = { low: { label: 'Baja', color: 'text-gray-500', dot: 'bg-gray-400' }, medium: { label: 'Media', color: 'text-blue-500', dot: 'bg-blue-400' }, high: { label: 'Alta', color: 'text-orange-500', dot: 'bg-orange-400' }, urgent: { label: 'Urgente', color: 'text-red-500', dot: 'bg-red-400' }, }; interface KanbanCardProps { order: ServiceOrder; onMoveToNext: (orderId: string, currentStatus: ServiceOrderStatus) => void; isMoving: boolean; } function KanbanCard({ order, onMoveToNext, isMoving }: KanbanCardProps) { const priorityConfig = PRIORITY_CONFIG[order.priority as keyof typeof PRIORITY_CONFIG] || PRIORITY_CONFIG.medium; const isUrgent = order.priority === 'urgent' || order.priority === 'high'; return (
{order.order_number} {priorityConfig.label}
{order.vehicle_info}
{order.customer_name}
{order.promised_at && (
Prometido: {new Date(order.promised_at).toLocaleDateString('es-MX', { day: '2-digit', month: 'short', })}
)} {order.symptoms && (

{order.symptoms}

)}
{new Date(order.received_at).toLocaleDateString('es-MX', { day: '2-digit', month: 'short', })}
); } interface KanbanColumnProps { label: string; color: string; bgColor: string; orders: ServiceOrder[]; onMoveToNext: (orderId: string, currentStatus: ServiceOrderStatus) => void; movingOrderId: string | null; } function KanbanColumn({ label, color, bgColor, orders, onMoveToNext, movingOrderId }: KanbanColumnProps) { const urgentCount = orders.filter(o => o.priority === 'urgent' || o.priority === 'high').length; return (

{label}

{orders.length}
{urgentCount > 0 && ( {urgentCount} )}
{orders.length === 0 ? (
Sin ordenes
) : ( orders.map((order) => ( )) )}
); } // Status transitions for quick move const STATUS_NEXT: Record = { received: 'diagnosing', diagnosing: 'quoted', quoted: 'approved', approved: 'in_repair', in_repair: 'ready', waiting_parts: 'in_repair', ready: 'delivered', delivered: null, cancelled: null, }; export function ServiceOrdersKanbanPage() { const queryClient = useQueryClient(); const [movingOrderId, setMovingOrderId] = useState(null); // Fetch all orders for kanban (exclude delivered and cancelled) const { data, isLoading, error } = useQuery({ queryKey: ['service-orders-kanban'], queryFn: () => serviceOrdersApi.list({ pageSize: 200 }), // Get all active orders }); const allOrders: ServiceOrder[] = data?.data?.data || []; // Filter out delivered and cancelled const activeOrders = allOrders.filter( o => o.status !== 'delivered' && o.status !== 'cancelled' ); // Group orders by status const ordersByStatus = KANBAN_COLUMNS.reduce((acc, col) => { acc[col.status] = activeOrders .filter(o => o.status === col.status) .sort((a, b) => { // Sort by priority (urgent first) then by date const priorityOrder = { urgent: 0, high: 1, medium: 2, low: 3 }; const aPriority = priorityOrder[a.priority as keyof typeof priorityOrder] ?? 2; const bPriority = priorityOrder[b.priority as keyof typeof priorityOrder] ?? 2; if (aPriority !== bPriority) return aPriority - bPriority; return new Date(a.received_at).getTime() - new Date(b.received_at).getTime(); }); return acc; }, {} as Record); // Status change mutation const statusMutation = useMutation({ mutationFn: ({ orderId, status }: { orderId: string; status: ServiceOrderStatus }) => serviceOrdersApi.changeStatus(orderId, status), onMutate: ({ orderId }) => { setMovingOrderId(orderId); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['service-orders-kanban'] }); queryClient.invalidateQueries({ queryKey: ['service-orders'] }); }, onSettled: () => { setMovingOrderId(null); }, }); const handleMoveToNext = (orderId: string, currentStatus: ServiceOrderStatus) => { const nextStatus = STATUS_NEXT[currentStatus]; if (nextStatus) { statusMutation.mutate({ orderId, status: nextStatus }); } }; if (isLoading) { return (
); } if (error) { return (

Error al cargar ordenes

); } return (
{/* Header */}

Tablero de Ordenes

{activeOrders.length} ordenes activas

Vista Lista Nueva Orden
{/* Stats Summary */}
{KANBAN_COLUMNS.map((col) => { const count = ordersByStatus[col.status]?.length || 0; const urgentCount = ordersByStatus[col.status]?.filter( o => o.priority === 'urgent' || o.priority === 'high' ).length || 0; return (

{col.label}

{count}

{urgentCount > 0 && ( ({urgentCount} urgentes) )}
); })}
{/* Kanban Board */}
{KANBAN_COLUMNS.map((col) => ( ))}
); }