import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from '@/components/ui/card'; import { ContainerHeader } from '@/components/ui/container-header'; import { DetailCardSkeleton } from '@/components/ui/detail-card-skeleton'; import { DetailEmptyView } from '@/components/ui/detail-empty-view'; import { Label } from '@/components/ui/label'; import { QueryErrorView } from '@/components/ui/query-error-view'; import { Separator } from '@/components/ui/separator'; import { GET_TASKS, RETRY_TASKS } from '@/domains/recorder/schema/tasks'; import { getApolloQueryError } from '@/infra/errors/apollo'; import { apolloErrorToMessage } from '@/infra/errors/apollo'; import { type GetTasksQuery, type GetTasksQueryVariables, type RetryTasksMutation, type RetryTasksMutationVariables, SubscriberTaskStatusEnum, } from '@/infra/graphql/gql/graphql'; import type { RouteStateDataOption } from '@/infra/routes/traits'; import { useMutation, useQuery } from '@apollo/client'; import { createFileRoute } from '@tanstack/react-router'; import { format } from 'date-fns'; import { RefreshCw } from 'lucide-react'; import { useMemo } from 'react'; import { toast } from 'sonner'; import { prettyTaskType } from './-pretty-task-type'; import { getStatusBadge } from './-status-badge'; export const Route = createFileRoute('/_app/tasks/detail/$id')({ component: TaskDetailRouteComponent, staticData: { breadcrumb: { label: 'Detail' }, } satisfies RouteStateDataOption, }); function TaskDetailRouteComponent() { const { id } = Route.useParams(); const { data, loading, error, refetch } = useQuery< GetTasksQuery, GetTasksQueryVariables >(GET_TASKS, { variables: { filter: { id: { eq: id, }, }, pagination: { page: { page: 0, limit: 1, }, }, orderBy: {}, }, pollInterval: 5000, // Auto-refresh every 5 seconds for running tasks }); const task = data?.subscriberTasks?.nodes?.[0]; const [retryTasks] = useMutation< RetryTasksMutation, RetryTasksMutationVariables >(RETRY_TASKS, { onCompleted: async () => { const refetchResult = await refetch(); const error = getApolloQueryError(refetchResult); if (error) { toast.error('Failed to retry task', { description: apolloErrorToMessage(error), }); return; } toast.success('Task retried successfully'); }, onError: (error) => { toast.error('Failed to retry task', { description: apolloErrorToMessage(error), }); }, }); const job = useMemo(() => { if (!task) { return null; } return { ...task.job, subscription: task.subscription, }; }, [task]); if (loading) { return ; } if (error) { return ; } if (!task) { return ; } return (
refetch()}> Refresh } />
Task Information View task execution details
{getStatusBadge(task.status)} {task.status === (SubscriberTaskStatusEnum.Killed || SubscriberTaskStatusEnum.Failed) && ( )}
{/* Basic Information */}
{task.id}
{prettyTaskType(task.taskType)}
{task.priority}
{task.attempts} / {task.maxAttempts}
{format(new Date(task.runAt), 'yyyy-MM-dd HH:mm:ss')}
{task.doneAt ? format(new Date(task.doneAt), 'yyyy-MM-dd HH:mm:ss') : '-'}
{task.lockAt ? format(new Date(task.lockAt), 'yyyy-MM-dd HH:mm:ss') : '-'}
{task.lockBy || '-'}
{/* Job Details */} {job && ( <>
                      {JSON.stringify(job, null, 2)}
                    
)} {/* Error Information */} {(task.status === SubscriberTaskStatusEnum.Failed || task.status === SubscriberTaskStatusEnum.Killed) && task.lastError && ( <>

{task.lastError}

)}
); }