'use client'; import { useState } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { Trash2, RotateCcw } from 'lucide-react'; import { apiFetch } from '@/lib/api/client'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table'; import type { QueueJobSummary, PaginatedQueueJobs } from '@/lib/services/system-monitoring.service'; type JobStatus = 'waiting' | 'active' | 'completed' | 'failed' | 'delayed'; interface QueueDetailTableProps { queueName: string; } const statusVariant: Record = { waiting: 'outline', active: 'default', completed: 'secondary', failed: 'destructive', delayed: 'outline', }; function formatDate(ts: number | undefined): string { if (!ts) return '-'; return new Date(ts).toLocaleString(); } function truncateId(id: string): string { return id.length > 12 ? `${id.slice(0, 8)}...` : id; } function truncateReason(reason: string | undefined): string { if (!reason) return '-'; return reason.length > 80 ? `${reason.slice(0, 80)}…` : reason; } export function QueueDetailTable({ queueName }: QueueDetailTableProps) { const queryClient = useQueryClient(); const [status, setStatus] = useState('failed'); const [page, setPage] = useState(1); const limit = 20; const { data, isLoading } = useQuery({ queryKey: ['queue', 'jobs', queueName, status, page], queryFn: () => apiFetch<{ data: PaginatedQueueJobs }>( `/api/v1/admin/queues/${queueName}?status=${status}&page=${page}&limit=${limit}`, ).then((r) => r.data), staleTime: 10_000, }); const retryMutation = useMutation({ mutationFn: (jobId: string) => apiFetch(`/api/v1/admin/queues/${queueName}/${jobId}/retry`, { method: 'POST' }), onSuccess: () => { void queryClient.invalidateQueries({ queryKey: ['queue', 'jobs', queueName] }); void queryClient.invalidateQueries({ queryKey: ['system', 'queues'] }); }, }); const deleteMutation = useMutation({ mutationFn: (jobId: string) => apiFetch(`/api/v1/admin/queues/${queueName}/${jobId}`, { method: 'DELETE' }), onSuccess: () => { void queryClient.invalidateQueries({ queryKey: ['queue', 'jobs', queueName] }); void queryClient.invalidateQueries({ queryKey: ['system', 'queues'] }); }, }); const jobs: QueueJobSummary[] = data?.jobs ?? []; const total = data?.total ?? 0; const totalPages = Math.ceil(total / limit); function handleStatusChange(value: string) { setStatus(value as JobStatus); setPage(1); } return (
{(['waiting', 'active', 'completed', 'failed', 'delayed'] as JobStatus[]).map((s) => ( {s} ))}
ID Name Status Created Processed Failed Reason Actions {isLoading ? ( Loading jobs... ) : jobs.length === 0 ? ( No {status} jobs ) : ( jobs.map((job) => ( {truncateId(job.id)} {job.name} {status} {formatDate(job.timestamp)} {formatDate(job.processedOn)} {truncateReason(job.failedReason)}
{status === 'failed' && ( )}
)) )}
{totalPages > 1 && (
{total} total jobs - page {page} of {totalPages}
)}
); }