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 (
);
}
return (
{/* Header */}
Tablero de Ordenes
{activeOrders.length} ordenes activas
{/* 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) => (
))}
);
}