Show a table of Task retries if there were some
This commit is contained in:
@@ -63,6 +63,18 @@ export class TaskDetailsPresenter {
|
||||
completedAt: true,
|
||||
style: true,
|
||||
parentId: true,
|
||||
attempts: {
|
||||
select: {
|
||||
number: true,
|
||||
status: true,
|
||||
error: true,
|
||||
runAt: true,
|
||||
updatedAt: true,
|
||||
},
|
||||
orderBy: {
|
||||
number: "asc",
|
||||
},
|
||||
},
|
||||
},
|
||||
where: {
|
||||
id,
|
||||
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
import {
|
||||
CheckCircleIcon,
|
||||
ClockIcon,
|
||||
XCircleIcon,
|
||||
} from "@heroicons/react/24/solid";
|
||||
import { TaskAttemptStatus } from "@trigger.dev/database";
|
||||
import { Paragraph } from "~/components/primitives/Paragraph";
|
||||
import { Spinner } from "~/components/primitives/Spinner";
|
||||
import { cn } from "~/utils/cn";
|
||||
|
||||
type TaskAttemptStatusProps = {
|
||||
status: TaskAttemptStatus;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export function TaskAttemptStatusLabel({
|
||||
status,
|
||||
}: {
|
||||
status: TaskAttemptStatus;
|
||||
}) {
|
||||
return (
|
||||
<span className="inline-flex items-center gap-1">
|
||||
<TaskAttemptStatusIcon status={status} className="h-4 w-4" />
|
||||
<Paragraph
|
||||
variant="small"
|
||||
className={taskAttemptStatusClassNameColor(status)}
|
||||
>
|
||||
{taskAttemptStatusTitle(status)}
|
||||
</Paragraph>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
export function TaskAttemptStatusIcon({
|
||||
status,
|
||||
className,
|
||||
}: TaskAttemptStatusProps) {
|
||||
switch (status) {
|
||||
case "COMPLETED":
|
||||
return (
|
||||
<CheckCircleIcon
|
||||
className={cn(taskAttemptStatusClassNameColor(status), className)}
|
||||
/>
|
||||
);
|
||||
case "PENDING":
|
||||
return (
|
||||
<ClockIcon
|
||||
className={cn(taskAttemptStatusClassNameColor(status), className)}
|
||||
/>
|
||||
);
|
||||
case "STARTED":
|
||||
return (
|
||||
<Spinner
|
||||
className={cn(taskAttemptStatusClassNameColor(status), className)}
|
||||
/>
|
||||
);
|
||||
case "ERRORED":
|
||||
return (
|
||||
<XCircleIcon
|
||||
className={cn(taskAttemptStatusClassNameColor(status), className)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function taskAttemptStatusClassNameColor(status: TaskAttemptStatus): string {
|
||||
switch (status) {
|
||||
case "COMPLETED":
|
||||
return "text-green-500";
|
||||
case "PENDING":
|
||||
return "text-slate-500";
|
||||
case "STARTED":
|
||||
return "text-blue-500";
|
||||
case "ERRORED":
|
||||
return "text-rose-500";
|
||||
}
|
||||
}
|
||||
|
||||
function taskAttemptStatusTitle(status: TaskAttemptStatus): string {
|
||||
switch (status) {
|
||||
case "COMPLETED":
|
||||
return "Complete";
|
||||
case "PENDING":
|
||||
return "Scheduled";
|
||||
case "STARTED":
|
||||
return "Running";
|
||||
case "ERRORED":
|
||||
return "Error";
|
||||
}
|
||||
}
|
||||
+41
@@ -23,6 +23,15 @@ import {
|
||||
} from "../_app.orgs.$organizationSlug.projects.$projectParam.jobs.$jobParam.runs.$runParam/RunCard";
|
||||
import { TaskStatusIcon } from "../_app.orgs.$organizationSlug.projects.$projectParam.jobs.$jobParam.runs.$runParam/TaskStatus";
|
||||
import { sensitiveDataReplacer } from "~/services/logger";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHeader,
|
||||
TableHeaderCell,
|
||||
TableRow,
|
||||
} from "~/components/primitives/Table";
|
||||
import { TaskAttemptStatusLabel } from "./TaskAttemptStatus";
|
||||
|
||||
export const loader = async ({ request, params }: LoaderArgs) => {
|
||||
const userId = await requireUserId(request);
|
||||
@@ -63,6 +72,7 @@ export default function Page() {
|
||||
properties,
|
||||
output,
|
||||
style,
|
||||
attempts,
|
||||
} = task;
|
||||
|
||||
return (
|
||||
@@ -132,6 +142,37 @@ export default function Page() {
|
||||
<RunPanelProperties properties={properties} layout="vertical" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{attempts.length > 0 && (
|
||||
<div className="mt-4 flex flex-col gap-2">
|
||||
<Header3>Retries</Header3>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableHeaderCell>Attempt</TableHeaderCell>
|
||||
<TableHeaderCell>Status</TableHeaderCell>
|
||||
<TableHeaderCell>Date</TableHeaderCell>
|
||||
<TableHeaderCell>Error</TableHeaderCell>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{attempts.map((attempt) => (
|
||||
<TableRow>
|
||||
<TableCell>{attempt.number}</TableCell>
|
||||
<TableCell>
|
||||
<TaskAttemptStatusLabel status={attempt.status} />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{attempt.status === "PENDING" && attempt.runAt
|
||||
? formatDateTime(attempt.runAt, "long")
|
||||
: formatDateTime(attempt.updatedAt, "long")}
|
||||
</TableCell>
|
||||
<TableCell>{attempt.error}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mt-4 flex flex-col gap-2">
|
||||
<Header3>Input</Header3>
|
||||
{params ? (
|
||||
|
||||
Reference in New Issue
Block a user