import { getUser } from '@/auth/authService'; import { POLLING_INTERVALS } from '@/constants'; import { fetchTaskStatus, cancelTask } from '@/services'; import { TaskStatus, type TaskResult } from '@/types'; import type { User } from 'oidc-client-ts'; import { useEffect, useState } from 'react'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from './ui/tooltip'; import { Button } from './ui/button'; import { Loader2, CheckCircle2, XCircle, X } from 'lucide-react'; interface TaskIndicatorProps { taskID: string | null; onTaskCancelled?: () => void; } export function TaskIndicator({ taskID, onTaskCancelled }: TaskIndicatorProps) { const [user, setUser] = useState(null); const [progressPercentage, setProgressPercentage] = useState(0); const [taskStatus, setTaskStatus] = useState(null); const [isCancelling, setIsCancelling] = useState(false); useEffect(() => { getUser().then(setUser); }, []); useEffect(() => { if (!user || !taskID) { setTaskStatus(null); return; } // Reset state for new task setTaskStatus(TaskStatus.PENDING); setProgressPercentage(0); const pollTaskStatus = async () => { try { const data = await fetchTaskStatus(user, taskID); const status = data.status as TaskStatus; setTaskStatus(status); if (status === TaskStatus.SUCCESS) { setProgressPercentage(100); return true; // Stop polling } if (status === TaskStatus.FAILURE || status === TaskStatus.REVOKED) { return true; // Stop polling } // Parse progress for in-progress tasks if (data.result) { try { const parsedResult: TaskResult = JSON.parse(data.result); setProgressPercentage(parsedResult.progress * 100); } catch { // Ignore parsing errors } } return false; // Continue polling } catch { setTaskStatus(TaskStatus.FAILURE); return true; // Stop polling on error } }; // Initial poll pollTaskStatus(); const interval = setInterval(async () => { const shouldStop = await pollTaskStatus(); if (shouldStop) { clearInterval(interval); } }, POLLING_INTERVALS.TASK_STATUS_MS); return () => clearInterval(interval); }, [taskID, user]); const handleCancel = async () => { if (!user || !taskID || isCancelling) return; setIsCancelling(true); try { const result = await cancelTask(user, taskID); if (result.success) { setTaskStatus(TaskStatus.REVOKED); onTaskCancelled?.(); } } catch { // Ignore cancel errors } finally { setIsCancelling(false); } }; if (!taskID || !taskStatus) { return null; } const isInProgress = taskStatus !== TaskStatus.SUCCESS && taskStatus !== TaskStatus.FAILURE && taskStatus !== TaskStatus.REVOKED; const getStatusIcon = () => { if (isInProgress) { return ; } if (taskStatus === TaskStatus.SUCCESS) { return ; } return ; }; const getTooltipContent = () => { if (isInProgress) { return `Task running: ${Math.round(progressPercentage)}%`; } if (taskStatus === TaskStatus.SUCCESS) { return 'Task completed successfully'; } if (taskStatus === TaskStatus.REVOKED) { return 'Task was cancelled'; } return 'Task failed'; }; return (
{getStatusIcon()} {isInProgress ? `${Math.round(progressPercentage)}%` : taskStatus}

{getTooltipContent()}

ID: {taskID.slice(0, 8)}...

{isInProgress && (

Cancel task

)}
); }