import { ArrowPathIcon, BoltSlashIcon, BugAntIcon, CheckCircleIcon, ClockIcon, FireIcon, NoSymbolIcon, PauseCircleIcon, RectangleStackIcon, TrashIcon, XCircleIcon, } from "@heroicons/react/20/solid"; import type { TaskRunStatus } from "@trigger.dev/database"; import { runFriendlyStatus, type RunFriendlyStatus } from "@trigger.dev/core/v3"; import assertNever from "assert-never"; import { HourglassIcon } from "lucide-react"; import { TimedOutIcon } from "~/assets/icons/TimedOutIcon"; import { Callout } from "~/components/primitives/Callout"; import { Spinner } from "~/components/primitives/Spinner"; import { cn } from "~/utils/cn"; export const allTaskRunStatuses = [ "DELAYED", "WAITING_FOR_DEPLOY", "PENDING_VERSION", "PENDING", "DEQUEUED", "EXECUTING", "RETRYING_AFTER_FAILURE", "WAITING_TO_RESUME", "COMPLETED_SUCCESSFULLY", "COMPLETED_WITH_ERRORS", "CANCELED", "TIMED_OUT", "CRASHED", "PAUSED", "INTERRUPTED", "SYSTEM_FAILURE", "EXPIRED", ] as const satisfies Readonly>; export const filterableTaskRunStatuses = [ "PENDING_VERSION", "DELAYED", "PENDING", "DEQUEUED", "EXECUTING", "WAITING_TO_RESUME", "COMPLETED_SUCCESSFULLY", "COMPLETED_WITH_ERRORS", "TIMED_OUT", "CRASHED", "SYSTEM_FAILURE", "CANCELED", "EXPIRED", ] as const satisfies Readonly>; const taskRunStatusDescriptions: Record = { DELAYED: "Task has been delayed and is waiting to be executed.", PENDING: "Task is waiting to be executed.", PENDING_VERSION: "Run cannot execute until a version includes the task and queue.", WAITING_FOR_DEPLOY: "Run cannot execute until a version includes the task and queue.", DEQUEUED: "Task has been dequeued from the queue but is not yet executing.", EXECUTING: "Task is currently being executed.", RETRYING_AFTER_FAILURE: "Task is being reattempted after a failure.", WAITING_TO_RESUME: `You have used a "wait" function. When the wait is complete, the task will resume execution.`, COMPLETED_SUCCESSFULLY: "Task has been successfully completed.", CANCELED: "Task has been canceled.", COMPLETED_WITH_ERRORS: "Task has failed with errors.", INTERRUPTED: "Task has failed because it was interrupted.", SYSTEM_FAILURE: "Task has failed due to a system failure.", PAUSED: "Task has been paused by the user.", CRASHED: "Task has crashed and won't be retried.", EXPIRED: "Task has surpassed its ttl and won't be executed.", TIMED_OUT: "Task has failed because it exceeded its maxDuration.", }; export const QUEUED_STATUSES = [ "PENDING", "PENDING_VERSION", "WAITING_FOR_DEPLOY", "DELAYED", ] satisfies TaskRunStatus[]; export const RUNNING_STATUSES = [ "DEQUEUED", "EXECUTING", "RETRYING_AFTER_FAILURE", "WAITING_TO_RESUME", ] satisfies TaskRunStatus[]; export function descriptionForTaskRunStatus(status: TaskRunStatus): string { return taskRunStatusDescriptions[status]; } export function TaskRunStatusCombo({ status, className, iconClassName, }: { status: TaskRunStatus; className?: string; iconClassName?: string; }) { return ( ); } const statusReasonsToDescription: Record = { NO_DEPLOYMENT: "No deployment or deployment image reference found for deployed run", NO_WORKER: "No worker found for run", TASK_NEVER_REGISTERED: "Task never registered", QUEUE_NOT_FOUND: "Queue not found", TASK_NOT_IN_LATEST: "Task not in latest version", BACKGROUND_WORKER_MISMATCH: "Background worker mismatch", }; export function TaskRunStatusReason({ status, statusReason, }: { status: TaskRunStatus; statusReason?: string; }) { if (status !== "PENDING_VERSION") { return null; } if (!statusReason) { return null; } const description = statusReasonsToDescription[statusReason]; if (!description) { return null; } return ( {description} ); } export function TaskRunStatusLabel({ status }: { status: TaskRunStatus }) { return {runStatusTitle(status)}; } export function TaskRunStatusIcon({ status, className, }: { status: TaskRunStatus; className: string; }) { switch (status) { case "DELAYED": return ; case "PENDING": return ; case "PENDING_VERSION": case "WAITING_FOR_DEPLOY": return ; case "DEQUEUED": return ; case "EXECUTING": return ; case "WAITING_TO_RESUME": return ; case "RETRYING_AFTER_FAILURE": return ; case "PAUSED": return ; case "CANCELED": return ; case "INTERRUPTED": return ; case "COMPLETED_SUCCESSFULLY": return ; case "COMPLETED_WITH_ERRORS": return ; case "SYSTEM_FAILURE": return ; case "CRASHED": return ; case "EXPIRED": return ; case "TIMED_OUT": return ; default: { assertNever(status); } } } export function runStatusClassNameColor(status: TaskRunStatus): string { switch (status) { case "PENDING": case "DELAYED": return "text-text-faint"; case "PENDING_VERSION": case "WAITING_FOR_DEPLOY": return "text-amber-500"; case "EXECUTING": case "RETRYING_AFTER_FAILURE": case "DEQUEUED": return "text-pending"; case "WAITING_TO_RESUME": return "text-text-faint"; case "PAUSED": return "text-amber-300"; case "CANCELED": case "EXPIRED": return "text-text-faint"; case "INTERRUPTED": return "text-error"; case "COMPLETED_SUCCESSFULLY": return "text-success"; case "COMPLETED_WITH_ERRORS": return "text-error"; case "SYSTEM_FAILURE": return "text-error"; case "CRASHED": return "text-error"; case "TIMED_OUT": return "text-error"; default: { assertNever(status); } } } export function runStatusTitle(status: TaskRunStatus): RunFriendlyStatus { return runStatusTitleFromStatus[status]; } export function runStatusFromFriendlyTitle(friendly: RunFriendlyStatus): TaskRunStatus { const result = titlesStatusesArray.find(([_, f]) => f === friendly); if (!result) { throw new Error(`Unknown friendly status: ${friendly}`); } return result[0] as TaskRunStatus; } // runFriendlyStatus and RunFriendlyStatus are imported from @trigger.dev/core/v3 // and re-exported here for backward compatibility. export { runFriendlyStatus, type RunFriendlyStatus } from "@trigger.dev/core/v3"; /** * Check if a value is a valid TaskRunStatus */ export function isTaskRunStatus(value: unknown): value is TaskRunStatus { return typeof value === "string" && allTaskRunStatuses.includes(value as TaskRunStatus); } /** * Check if a value is a valid RunFriendlyStatus */ export function isRunFriendlyStatus(value: unknown): value is RunFriendlyStatus { return typeof value === "string" && runFriendlyStatus.includes(value as RunFriendlyStatus); } export const runStatusTitleFromStatus: Record = { DELAYED: "Delayed", PENDING: "Queued", PENDING_VERSION: "Pending version", WAITING_FOR_DEPLOY: "Pending version", DEQUEUED: "Dequeued", EXECUTING: "Executing", WAITING_TO_RESUME: "Waiting", RETRYING_AFTER_FAILURE: "Reattempting", PAUSED: "Paused", CANCELED: "Canceled", INTERRUPTED: "Interrupted", COMPLETED_SUCCESSFULLY: "Completed", COMPLETED_WITH_ERRORS: "Failed", SYSTEM_FAILURE: "System failure", CRASHED: "Crashed", EXPIRED: "Expired", TIMED_OUT: "Timed out", }; const titlesStatusesArray = Object.entries(runStatusTitleFromStatus); /** * Chart series color for each TaskRunStatus, mirroring `runStatusClassNameColor`. * Values are CSS variables (defined in tailwind.css) so they follow the theme; * only usable in CSS contexts (SVG attributes, chart config styles). */ const RUN_STATUS_CHART_COLORS: Record = { PENDING: "var(--color-run-pending)", DELAYED: "var(--color-run-delayed)", PENDING_VERSION: "var(--color-run-pending-version)", WAITING_FOR_DEPLOY: "var(--color-run-waiting-for-deploy)", EXECUTING: "var(--color-run-executing)", RETRYING_AFTER_FAILURE: "var(--color-run-retrying-after-failure)", DEQUEUED: "var(--color-run-dequeued)", WAITING_TO_RESUME: "var(--color-run-waiting-to-resume)", PAUSED: "var(--color-run-paused)", CANCELED: "var(--color-run-canceled)", EXPIRED: "var(--color-run-expired)", INTERRUPTED: "var(--color-run-interrupted)", COMPLETED_SUCCESSFULLY: "var(--color-run-completed-successfully)", COMPLETED_WITH_ERRORS: "var(--color-run-completed-with-errors)", SYSTEM_FAILURE: "var(--color-run-system-failure)", CRASHED: "var(--color-run-crashed)", TIMED_OUT: "var(--color-run-timed-out)", }; /** * Get the chart color for a run status value. Accepts either a raw TaskRunStatus * (e.g. "COMPLETED_SUCCESSFULLY") or a friendly name (e.g. "Completed"). * Returns `undefined` when the value is not a recognised status. */ export function getRunStatusChartColor(value: string): string | undefined { if (isTaskRunStatus(value)) { return RUN_STATUS_CHART_COLORS[value]; } if (isRunFriendlyStatus(value)) { return RUN_STATUS_CHART_COLORS[runStatusFromFriendlyTitle(value)]; } return undefined; }