Use TaskRunStatus (instead of TaskRunAttemptStatus) (#944)
* Using the new run status instead of run attempt status * The test page now uses the TaskRunStatus as well * Organized imports * Use TaskRun icons instead of attempt ones * 10x zoom on the run timeline
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { TrashIcon } from "@heroicons/react/20/solid";
|
||||
import { useNavigate } from "@remix-run/react";
|
||||
import { RuntimeEnvironment, TaskRunAttemptStatus } from "@trigger.dev/database";
|
||||
import { RuntimeEnvironment, TaskRunAttemptStatus, TaskRunStatus } from "@trigger.dev/database";
|
||||
import type { TaskRunStatus as TaskRunStatusType } from "@trigger.dev/database";
|
||||
import { useCallback } from "react";
|
||||
import { z } from "zod";
|
||||
import { useOptimisticLocation } from "~/hooks/useOptimisticLocation";
|
||||
@@ -16,21 +17,10 @@ import {
|
||||
SelectValue,
|
||||
} from "../../primitives/Select";
|
||||
import { TimeFrameFilter } from "../TimeFrameFilter";
|
||||
import { TaskRunStatus } from "./TaskRunStatus";
|
||||
import { TaskRunStatusCombo } from "./TaskRunStatus";
|
||||
|
||||
export const allTaskRunStatuses = [
|
||||
"ENQUEUED",
|
||||
"PENDING",
|
||||
"EXECUTING",
|
||||
"PAUSED",
|
||||
"FAILED",
|
||||
"COMPLETED",
|
||||
"CANCELED",
|
||||
] as const;
|
||||
|
||||
export type ExtendedTaskAttemptStatus = (typeof allTaskRunStatuses)[number];
|
||||
|
||||
export const TaskAttemptStatus = z.enum(allTaskRunStatuses);
|
||||
export const allTaskRunStatuses = Object.values(TaskRunStatus) as TaskRunStatusType[];
|
||||
export const TaskAttemptStatus = z.nativeEnum(TaskRunStatus);
|
||||
|
||||
export const TaskRunListSearchFilters = z.object({
|
||||
cursor: z.string().optional(),
|
||||
@@ -174,7 +164,7 @@ export function RunsFilters({ possibleEnvironments, possibleTasks }: RunFiltersP
|
||||
</SelectItem>
|
||||
{allTaskRunStatuses.map((status) => (
|
||||
<SelectItem key={status} value={status}>
|
||||
<TaskRunStatus status={status} className="text-xs" />
|
||||
<TaskRunStatusCombo status={status} className="text-xs" />
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
import {
|
||||
CheckCircleIcon,
|
||||
ClockIcon,
|
||||
NoSymbolIcon,
|
||||
PauseCircleIcon,
|
||||
RectangleStackIcon,
|
||||
XCircleIcon,
|
||||
} from "@heroicons/react/20/solid";
|
||||
import type { TaskRunAttemptStatus as TaskRunAttemptStatusType } from "@trigger.dev/database";
|
||||
import { TaskRunAttemptStatus } from "@trigger.dev/database";
|
||||
import { Spinner } from "~/components/primitives/Spinner";
|
||||
import { cn } from "~/utils/cn";
|
||||
|
||||
export const allTaskRunAttemptStatuses = Object.values(
|
||||
TaskRunAttemptStatus
|
||||
) as TaskRunAttemptStatusType[];
|
||||
|
||||
export type ExtendedTaskAttemptStatus = (typeof allTaskRunAttemptStatuses)[number] | "ENQUEUED";
|
||||
|
||||
export function TaskRunAttemptStatusCombo({
|
||||
status,
|
||||
className,
|
||||
}: {
|
||||
status: ExtendedTaskAttemptStatus | null;
|
||||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<span className={cn("flex items-center gap-1", className)}>
|
||||
<TaskRunAttemptStatusIcon status={status} className="h-4 w-4" />
|
||||
<TaskRunAttemptStatusLabel status={status} />
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
export function TaskRunAttemptStatusLabel({
|
||||
status,
|
||||
}: {
|
||||
status: ExtendedTaskAttemptStatus | null;
|
||||
}) {
|
||||
return (
|
||||
<span className={runAttemptStatusClassNameColor(status)}>{runAttemptStatusTitle(status)}</span>
|
||||
);
|
||||
}
|
||||
|
||||
export function TaskRunAttemptStatusIcon({
|
||||
status,
|
||||
className,
|
||||
}: {
|
||||
status: ExtendedTaskAttemptStatus | null;
|
||||
className: string;
|
||||
}) {
|
||||
if (status === null) {
|
||||
return <RectangleStackIcon className={cn(runAttemptStatusClassNameColor(status), className)} />;
|
||||
}
|
||||
|
||||
switch (status) {
|
||||
case "ENQUEUED":
|
||||
return (
|
||||
<RectangleStackIcon className={cn(runAttemptStatusClassNameColor(status), className)} />
|
||||
);
|
||||
case "PENDING":
|
||||
return <ClockIcon className={cn(runAttemptStatusClassNameColor(status), className)} />;
|
||||
case "EXECUTING":
|
||||
return <Spinner className={cn(runAttemptStatusClassNameColor(status), className)} />;
|
||||
case "PAUSED":
|
||||
return <PauseCircleIcon className={cn(runAttemptStatusClassNameColor(status), className)} />;
|
||||
case "FAILED":
|
||||
return <XCircleIcon className={cn(runAttemptStatusClassNameColor(status), className)} />;
|
||||
case "CANCELED":
|
||||
return <NoSymbolIcon className={cn(runAttemptStatusClassNameColor(status), className)} />;
|
||||
case "COMPLETED":
|
||||
return <CheckCircleIcon className={cn(runAttemptStatusClassNameColor(status), className)} />;
|
||||
default: {
|
||||
const _exhaustiveCheck: never = status;
|
||||
throw new Error(`Non-exhaustive match for value: ${status}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function runAttemptStatusClassNameColor(status: ExtendedTaskAttemptStatus | null): string {
|
||||
if (status === null) {
|
||||
return "text-charcoal-500";
|
||||
}
|
||||
|
||||
switch (status) {
|
||||
case "ENQUEUED":
|
||||
return "text-charcoal-500";
|
||||
case "PENDING":
|
||||
return "text-charcoal-500";
|
||||
case "EXECUTING":
|
||||
return "text-pending";
|
||||
case "PAUSED":
|
||||
return "text-amber-300";
|
||||
case "FAILED":
|
||||
return "text-error";
|
||||
case "CANCELED":
|
||||
return "text-charcoal-500";
|
||||
case "COMPLETED":
|
||||
return "text-success";
|
||||
default: {
|
||||
const _exhaustiveCheck: never = status;
|
||||
throw new Error(`Non-exhaustive match for value: ${status}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function runAttemptStatusTitle(status: ExtendedTaskAttemptStatus | null): string {
|
||||
if (status === null) {
|
||||
return "Enqueued";
|
||||
}
|
||||
|
||||
switch (status) {
|
||||
case "ENQUEUED":
|
||||
return "Enqueued";
|
||||
case "PENDING":
|
||||
return "Pending";
|
||||
case "EXECUTING":
|
||||
return "Executing";
|
||||
case "PAUSED":
|
||||
return "Paused";
|
||||
case "FAILED":
|
||||
return "Failed";
|
||||
case "CANCELED":
|
||||
return "Canceled";
|
||||
case "COMPLETED":
|
||||
return "Completed";
|
||||
default: {
|
||||
const _exhaustiveCheck: never = status;
|
||||
throw new Error(`Non-exhaustive match for value: ${status}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
import {
|
||||
ArrowPathIcon,
|
||||
BoltSlashIcon,
|
||||
BugAntIcon,
|
||||
CheckCircleIcon,
|
||||
ClockIcon,
|
||||
NoSymbolIcon,
|
||||
@@ -6,15 +9,15 @@ import {
|
||||
RectangleStackIcon,
|
||||
XCircleIcon,
|
||||
} from "@heroicons/react/20/solid";
|
||||
import { TaskRunStatus } from "@trigger.dev/database";
|
||||
import { Spinner } from "~/components/primitives/Spinner";
|
||||
import { cn } from "~/utils/cn";
|
||||
import { ExtendedTaskAttemptStatus } from "./RunFilters";
|
||||
|
||||
export function TaskRunStatus({
|
||||
export function TaskRunStatusCombo({
|
||||
status,
|
||||
className,
|
||||
}: {
|
||||
status: ExtendedTaskAttemptStatus | null;
|
||||
status: TaskRunStatus;
|
||||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
@@ -25,7 +28,7 @@ export function TaskRunStatus({
|
||||
);
|
||||
}
|
||||
|
||||
export function TaskRunStatusLabel({ status }: { status: ExtendedTaskAttemptStatus | null }) {
|
||||
export function TaskRunStatusLabel({ status }: { status: TaskRunStatus }) {
|
||||
return <span className={runStatusClassNameColor(status)}>{runStatusTitle(status)}</span>;
|
||||
}
|
||||
|
||||
@@ -33,28 +36,30 @@ export function TaskRunStatusIcon({
|
||||
status,
|
||||
className,
|
||||
}: {
|
||||
status: ExtendedTaskAttemptStatus | null;
|
||||
status: TaskRunStatus;
|
||||
className: string;
|
||||
}) {
|
||||
if (status === null) {
|
||||
return <RectangleStackIcon className={cn(runStatusClassNameColor(status), className)} />;
|
||||
}
|
||||
|
||||
switch (status) {
|
||||
case "ENQUEUED":
|
||||
return <RectangleStackIcon className={cn(runStatusClassNameColor(status), className)} />;
|
||||
case "PENDING":
|
||||
return <ClockIcon className={cn(runStatusClassNameColor(status), className)} />;
|
||||
return <RectangleStackIcon className={cn(runStatusClassNameColor(status), className)} />;
|
||||
case "EXECUTING":
|
||||
return <Spinner className={cn(runStatusClassNameColor(status), className)} />;
|
||||
case "WAITING_TO_RESUME":
|
||||
return <ClockIcon className={cn(runStatusClassNameColor(status), className)} />;
|
||||
case "RETRYING_AFTER_FAILURE":
|
||||
return <ArrowPathIcon className={cn(runStatusClassNameColor(status), className)} />;
|
||||
case "PAUSED":
|
||||
return <PauseCircleIcon className={cn(runStatusClassNameColor(status), className)} />;
|
||||
case "FAILED":
|
||||
return <XCircleIcon className={cn(runStatusClassNameColor(status), className)} />;
|
||||
case "CANCELED":
|
||||
return <NoSymbolIcon className={cn(runStatusClassNameColor(status), className)} />;
|
||||
case "COMPLETED":
|
||||
case "INTERRUPTED":
|
||||
return <BoltSlashIcon className={cn(runStatusClassNameColor(status), className)} />;
|
||||
case "COMPLETED_SUCCESSFULLY":
|
||||
return <CheckCircleIcon className={cn(runStatusClassNameColor(status), className)} />;
|
||||
case "COMPLETED_WITH_ERRORS":
|
||||
return <XCircleIcon className={cn(runStatusClassNameColor(status), className)} />;
|
||||
case "SYSTEM_FAILURE":
|
||||
return <BugAntIcon className={cn(runStatusClassNameColor(status), className)} />;
|
||||
|
||||
default: {
|
||||
const _exhaustiveCheck: never = status;
|
||||
@@ -63,26 +68,28 @@ export function TaskRunStatusIcon({
|
||||
}
|
||||
}
|
||||
|
||||
export function runStatusClassNameColor(status: ExtendedTaskAttemptStatus | null): string {
|
||||
if (status === null) {
|
||||
return "text-charcoal-500";
|
||||
}
|
||||
|
||||
export function runStatusClassNameColor(status: TaskRunStatus): string {
|
||||
switch (status) {
|
||||
case "ENQUEUED":
|
||||
return "text-charcoal-500";
|
||||
case "PENDING":
|
||||
return "text-charcoal-500";
|
||||
case "EXECUTING":
|
||||
return "text-pending";
|
||||
case "WAITING_TO_RESUME":
|
||||
return "text-charcoal-500";
|
||||
case "RETRYING_AFTER_FAILURE":
|
||||
return "text-charcoal-500";
|
||||
case "PAUSED":
|
||||
return "text-amber-300";
|
||||
case "FAILED":
|
||||
return "text-error";
|
||||
case "CANCELED":
|
||||
return "text-charcoal-500";
|
||||
case "COMPLETED":
|
||||
case "INTERRUPTED":
|
||||
return "text-error";
|
||||
case "COMPLETED_SUCCESSFULLY":
|
||||
return "text-success";
|
||||
case "COMPLETED_WITH_ERRORS":
|
||||
return "text-error";
|
||||
case "SYSTEM_FAILURE":
|
||||
return "text-error";
|
||||
default: {
|
||||
const _exhaustiveCheck: never = status;
|
||||
throw new Error(`Non-exhaustive match for value: ${status}`);
|
||||
@@ -90,26 +97,28 @@ export function runStatusClassNameColor(status: ExtendedTaskAttemptStatus | null
|
||||
}
|
||||
}
|
||||
|
||||
export function runStatusTitle(status: ExtendedTaskAttemptStatus | null): string {
|
||||
if (status === null) {
|
||||
return "Enqueued";
|
||||
}
|
||||
|
||||
export function runStatusTitle(status: TaskRunStatus): string {
|
||||
switch (status) {
|
||||
case "ENQUEUED":
|
||||
return "Enqueued";
|
||||
case "PENDING":
|
||||
return "Pending";
|
||||
return "Enqueued";
|
||||
case "EXECUTING":
|
||||
return "Executing";
|
||||
case "WAITING_TO_RESUME":
|
||||
return "Waiting";
|
||||
case "RETRYING_AFTER_FAILURE":
|
||||
return "Retrying";
|
||||
case "PAUSED":
|
||||
return "Paused";
|
||||
case "FAILED":
|
||||
return "Failed";
|
||||
case "CANCELED":
|
||||
return "Canceled";
|
||||
case "COMPLETED":
|
||||
case "INTERRUPTED":
|
||||
return "Interrupted";
|
||||
case "COMPLETED_SUCCESSFULLY":
|
||||
return "Completed";
|
||||
case "COMPLETED_WITH_ERRORS":
|
||||
return "Failed";
|
||||
case "SYSTEM_FAILURE":
|
||||
return "System failure";
|
||||
default: {
|
||||
const _exhaustiveCheck: never = status;
|
||||
throw new Error(`Non-exhaustive match for value: ${status}`);
|
||||
|
||||
@@ -19,8 +19,8 @@ import {
|
||||
TableHeaderCell,
|
||||
TableRow,
|
||||
} from "../../primitives/Table";
|
||||
import { TaskRunStatus } from "./TaskRunStatus";
|
||||
import { formatDuration } from "@trigger.dev/core/v3";
|
||||
import { TaskRunStatusCombo } from "./TaskRunStatus";
|
||||
|
||||
type RunsTableProps = {
|
||||
total: number;
|
||||
@@ -82,7 +82,7 @@ export function TaskRunsTable({
|
||||
<EnvironmentLabel environment={run.environment} userName={usernameForEnv} />
|
||||
</TableCell>
|
||||
<TableCell to={path}>
|
||||
<TaskRunStatus status={run.status} />
|
||||
<TaskRunStatusCombo status={run.status} />
|
||||
</TableCell>
|
||||
<TableCell to={path}>
|
||||
{run.startedAt ? <DateTime date={run.startedAt} /> : "–"}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Prisma, TaskRunAttemptStatus } from "@trigger.dev/database";
|
||||
import { Prisma, TaskRunAttemptStatus, TaskRunStatus } from "@trigger.dev/database";
|
||||
import { Direction } from "~/components/runs/RunStatuses";
|
||||
import { ExtendedTaskAttemptStatus } from "~/components/runs/v3/RunFilters";
|
||||
import { PrismaClient, prisma } from "~/db.server";
|
||||
import { getUsername } from "~/utils/username";
|
||||
|
||||
@@ -10,7 +9,7 @@ type RunListOptions = {
|
||||
//filters
|
||||
tasks: string[] | undefined;
|
||||
versions: string[] | undefined;
|
||||
statuses: ExtendedTaskAttemptStatus[] | undefined;
|
||||
statuses: TaskRunStatus[] | undefined;
|
||||
environments: string[] | undefined;
|
||||
from: number | undefined;
|
||||
to: number | undefined;
|
||||
@@ -45,10 +44,7 @@ export class RunListPresenter {
|
||||
cursor,
|
||||
pageSize = DEFAULT_PAGE_SIZE,
|
||||
}: RunListOptions) {
|
||||
const filterByEnqueuedStatus = statuses ? statuses.includes("ENQUEUED") : false;
|
||||
statuses = statuses ? statuses.filter((s) => s !== "ENQUEUED") : undefined;
|
||||
const hasStatusFilters =
|
||||
filterByEnqueuedStatus || (statuses !== undefined && statuses.length > 0);
|
||||
const hasStatusFilters = statuses && statuses.length > 0;
|
||||
|
||||
const hasFilters =
|
||||
tasks !== undefined ||
|
||||
@@ -100,9 +96,9 @@ export class RunListPresenter {
|
||||
taskIdentifier: string;
|
||||
version: string | null;
|
||||
runtimeEnvironmentId: string;
|
||||
status: TaskRunAttemptStatus | null;
|
||||
status: TaskRunStatus;
|
||||
createdAt: Date;
|
||||
startedAt: Date | null;
|
||||
lockedAt: Date | null;
|
||||
completedAt: Date | null;
|
||||
isTest: boolean;
|
||||
attempts: BigInt;
|
||||
@@ -115,9 +111,9 @@ export class RunListPresenter {
|
||||
tr."taskIdentifier" AS "taskIdentifier",
|
||||
bw.version AS version,
|
||||
tr."runtimeEnvironmentId" AS "runtimeEnvironmentId",
|
||||
tra.status AS status,
|
||||
tr.status AS status,
|
||||
tr."createdAt" AS "createdAt",
|
||||
tra."startedAt" AS "startedAt",
|
||||
tr."lockedAt" AS "lockedAt",
|
||||
tra."completedAt" AS "completedAt",
|
||||
tr."isTest" AS "isTest",
|
||||
COUNT(tra.id) AS attempts
|
||||
@@ -151,11 +147,11 @@ export class RunListPresenter {
|
||||
${hasStatusFilters ? Prisma.sql`AND (` : Prisma.empty}
|
||||
${
|
||||
statuses && statuses.length > 0
|
||||
? Prisma.sql`tra.status = ANY(ARRAY[${Prisma.join(statuses)}]::"TaskRunAttemptStatus"[])`
|
||||
? Prisma.sql`tr.status = ANY(ARRAY[${Prisma.join(statuses)}]::"TaskRunStatus"[])`
|
||||
: Prisma.empty
|
||||
}
|
||||
${statuses && statuses.length > 0 && filterByEnqueuedStatus ? Prisma.sql` OR ` : Prisma.empty}
|
||||
${filterByEnqueuedStatus ? Prisma.sql`tra.status IS NULL` : Prisma.empty}
|
||||
${statuses && statuses.length > 0 && hasStatusFilters ? Prisma.sql` OR ` : Prisma.empty}
|
||||
${hasStatusFilters ? Prisma.sql`tr.status IS NULL` : Prisma.empty}
|
||||
${hasStatusFilters ? Prisma.sql`) ` : Prisma.empty}
|
||||
${
|
||||
environments && environments.length > 0
|
||||
@@ -217,7 +213,7 @@ export class RunListPresenter {
|
||||
friendlyId: run.runFriendlyId,
|
||||
number: Number(run.number),
|
||||
createdAt: run.createdAt,
|
||||
startedAt: run.startedAt,
|
||||
startedAt: run.lockedAt,
|
||||
completedAt: run.completedAt,
|
||||
isTest: run.isTest,
|
||||
status: run.status,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Prisma, TaskRunAttemptStatus } from "@trigger.dev/database";
|
||||
import { Prisma, TaskRunAttemptStatus, TaskRunStatus } from "@trigger.dev/database";
|
||||
import { PrismaClient, prisma } from "~/db.server";
|
||||
import { Organization } from "~/models/organization.server";
|
||||
import { Project } from "~/models/project.server";
|
||||
@@ -79,35 +79,37 @@ export class TaskListPresenter {
|
||||
bwt."createdAt" DESC;`;
|
||||
|
||||
let latestRuns = [] as {
|
||||
updatedAt: Date;
|
||||
status: TaskRunAttemptStatus;
|
||||
backgroundWorkerTaskId: string;
|
||||
createdAt: Date;
|
||||
status: TaskRunStatus;
|
||||
lockedById: string;
|
||||
}[];
|
||||
|
||||
if (tasks.length > 0) {
|
||||
latestRuns = await this.#prismaClient.$queryRaw<
|
||||
{
|
||||
updatedAt: Date;
|
||||
status: TaskRunAttemptStatus;
|
||||
backgroundWorkerTaskId: string;
|
||||
createdAt: Date;
|
||||
status: TaskRunStatus;
|
||||
lockedById: string;
|
||||
}[]
|
||||
>`
|
||||
SELECT * FROM (
|
||||
SELECT
|
||||
"updatedAt",
|
||||
"createdAt",
|
||||
"status",
|
||||
"backgroundWorkerTaskId",
|
||||
ROW_NUMBER() OVER (PARTITION BY "backgroundWorkerTaskId" ORDER BY "updatedAt" DESC) AS rn
|
||||
"lockedById",
|
||||
ROW_NUMBER() OVER (PARTITION BY "lockedById" ORDER BY "updatedAt" DESC) AS rn
|
||||
FROM
|
||||
"TaskRunAttempt"
|
||||
"TaskRun"
|
||||
WHERE
|
||||
"backgroundWorkerTaskId" IN(${Prisma.join(tasks.map((t) => t.id))})
|
||||
"lockedById" IN(${Prisma.join(tasks.map((t) => t.id))})
|
||||
) t
|
||||
WHERE rn = 1;`;
|
||||
console.log(tasks.map((t) => t.id));
|
||||
console.log(latestRuns);
|
||||
}
|
||||
|
||||
return tasks.map((task) => {
|
||||
const latestRun = latestRuns.find((r) => r.backgroundWorkerTaskId === task.id);
|
||||
const latestRun = latestRuns.find((r) => r.lockedById === task.id);
|
||||
const environment = project.environments.find((env) => env.id === task.runtimeEnvironmentId);
|
||||
if (!environment) {
|
||||
throw new Error(`Environment not found for TaskRun ${task.id}`);
|
||||
@@ -122,7 +124,12 @@ export class TaskListPresenter {
|
||||
userId: environment.orgMember?.user.id,
|
||||
userName: getUsername(environment.orgMember?.user),
|
||||
},
|
||||
latestRun,
|
||||
latestRun: latestRun
|
||||
? {
|
||||
createdAt: latestRun.createdAt,
|
||||
status: latestRun.status,
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { TaskRunAttemptStatus } from "@trigger.dev/database";
|
||||
import { TaskRunAttemptStatus, TaskRunStatus } from "@trigger.dev/database";
|
||||
import { PrismaClient, prisma } from "~/db.server";
|
||||
import { getUsername } from "~/utils/username";
|
||||
|
||||
@@ -53,7 +53,7 @@ export class TestTaskPresenter {
|
||||
number: BigInt;
|
||||
friendlyId: string;
|
||||
createdAt: Date;
|
||||
status: TaskRunAttemptStatus;
|
||||
status: TaskRunStatus;
|
||||
payload: string;
|
||||
payloadType: string;
|
||||
runtimeEnvironmentId: string;
|
||||
@@ -73,13 +73,6 @@ export class TestTaskPresenter {
|
||||
ORDER BY
|
||||
tr."createdAt" DESC
|
||||
LIMIT 5
|
||||
), latestattempts AS (
|
||||
SELECT
|
||||
"taskRunId",
|
||||
status,
|
||||
ROW_NUMBER() OVER(PARTITION BY "taskRunId" ORDER BY "createdAt" DESC) AS rank
|
||||
FROM
|
||||
"TaskRunAttempt"
|
||||
)
|
||||
SELECT
|
||||
taskr.id,
|
||||
@@ -87,17 +80,12 @@ export class TestTaskPresenter {
|
||||
taskr."friendlyId",
|
||||
taskr."taskIdentifier",
|
||||
taskr."createdAt",
|
||||
tra.status,
|
||||
taskr.status,
|
||||
taskr.payload,
|
||||
taskr."payloadType",
|
||||
taskr."runtimeEnvironmentId"
|
||||
FROM
|
||||
taskruns AS taskr
|
||||
LEFT JOIN
|
||||
latestattempts AS tra
|
||||
ON taskr.id = tra."taskRunId"
|
||||
WHERE
|
||||
tra.rank = 1
|
||||
ORDER BY
|
||||
taskr."createdAt" DESC;`;
|
||||
|
||||
|
||||
+24
-12
@@ -1,6 +1,6 @@
|
||||
import { ChatBubbleLeftRightIcon } from "@heroicons/react/20/solid";
|
||||
import { LoaderFunctionArgs } from "@remix-run/server-runtime";
|
||||
import { TaskRunAttemptStatus } from "@trigger.dev/database";
|
||||
import { TaskRunAttemptStatus, TaskRunStatus } from "@trigger.dev/database";
|
||||
import { typedjson, useTypedLoaderData } from "remix-typedjson";
|
||||
import invariant from "tiny-invariant";
|
||||
import { Feedback } from "~/components/Feedback";
|
||||
@@ -25,9 +25,8 @@ import {
|
||||
TableHeaderCell,
|
||||
TableRow,
|
||||
} from "~/components/primitives/Table";
|
||||
import { TaskPath } from "~/components/runs/v3/TaskPath";
|
||||
import { TaskRunStatus } from "~/components/runs/v3/TaskRunStatus";
|
||||
import { useAppOrigin } from "~/hooks/useAppOrigin";
|
||||
import { TaskFunctionName, TaskPath } from "~/components/runs/v3/TaskPath";
|
||||
import { TaskRunStatusCombo } from "~/components/runs/v3/TaskRunStatus";
|
||||
import { useDevEnvironment } from "~/hooks/useEnvironments";
|
||||
import { useOrganization } from "~/hooks/useOrganizations";
|
||||
import { useProject } from "~/hooks/useProject";
|
||||
@@ -83,8 +82,12 @@ export default function Page() {
|
||||
<TableRow>
|
||||
<TableHeaderCell>Task ID</TableHeaderCell>
|
||||
<TableHeaderCell>Task</TableHeaderCell>
|
||||
<TableHeaderCell>Path</TableHeaderCell>
|
||||
<TableHeaderCell>Environment</TableHeaderCell>
|
||||
<TableHeaderCell>Last run</TableHeaderCell>
|
||||
<TableHeaderCell>
|
||||
<div className="sr-only">Last run status</div>
|
||||
</TableHeaderCell>
|
||||
<TableHeaderCell>Created at</TableHeaderCell>
|
||||
<TableHeaderCell hiddenLabel>Go to page</TableHeaderCell>
|
||||
</TableRow>
|
||||
@@ -104,32 +107,40 @@ export default function Page() {
|
||||
<TableRow key={task.id} className="group">
|
||||
<TableCell to={path}>{task.slug}</TableCell>
|
||||
<TableCell to={path}>
|
||||
<TaskPath
|
||||
filePath={task.filePath}
|
||||
functionName={`${task.exportName}()`}
|
||||
<TaskFunctionName
|
||||
functionName={task.exportName}
|
||||
variant="extra-small"
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell to={path}>{task.filePath}</TableCell>
|
||||
<TableCell to={path}>
|
||||
<EnvironmentLabel
|
||||
environment={task.environment}
|
||||
userName={usernameForEnv}
|
||||
/>
|
||||
</TableCell>
|
||||
|
||||
<TableCell to={path}>
|
||||
{task.latestRun ? (
|
||||
<div
|
||||
className={cn(
|
||||
"flex items-center gap-2",
|
||||
classForTaskAttemptStatus(task.latestRun.status)
|
||||
classForTaskRunStatus(task.latestRun.status)
|
||||
)}
|
||||
>
|
||||
<DateTime date={task.latestRun.updatedAt} />
|
||||
<TaskRunStatus status={task.latestRun.status} />
|
||||
<DateTime date={task.latestRun.createdAt} />
|
||||
</div>
|
||||
) : (
|
||||
"Never run"
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell to={path}>
|
||||
{task.latestRun ? (
|
||||
<TaskRunStatusCombo status={task.latestRun.status} />
|
||||
) : (
|
||||
"–"
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell to={path}>
|
||||
<DateTime date={task.createdAt} />
|
||||
</TableCell>
|
||||
@@ -157,9 +168,10 @@ export default function Page() {
|
||||
);
|
||||
}
|
||||
|
||||
function classForTaskAttemptStatus(status: TaskRunAttemptStatus) {
|
||||
function classForTaskRunStatus(status: TaskRunStatus) {
|
||||
switch (status) {
|
||||
case "FAILED":
|
||||
case "SYSTEM_FAILURE":
|
||||
case "COMPLETED_WITH_ERRORS":
|
||||
return "text-error";
|
||||
default:
|
||||
return "";
|
||||
|
||||
+2
-2
@@ -15,7 +15,7 @@ import { RunIcon } from "~/components/runs/v3/RunIcon";
|
||||
import { SpanEvents } from "~/components/runs/v3/SpanEvents";
|
||||
import { SpanTitle } from "~/components/runs/v3/SpanTitle";
|
||||
import { TaskPath } from "~/components/runs/v3/TaskPath";
|
||||
import { TaskRunStatus } from "~/components/runs/v3/TaskRunStatus";
|
||||
import { TaskRunAttemptStatusCombo } from "~/components/runs/v3/TaskRunAttemptStatus";
|
||||
import { useOrganization } from "~/hooks/useOrganizations";
|
||||
import { useProject } from "~/hooks/useProject";
|
||||
import { SpanPresenter } from "~/presenters/v3/SpanPresenter.server";
|
||||
@@ -85,7 +85,7 @@ export default function Page() {
|
||||
)}
|
||||
{event.style.variant === "primary" && (
|
||||
<Property label="Status">
|
||||
<TaskRunStatus
|
||||
<TaskRunAttemptStatusCombo
|
||||
status={
|
||||
event.isCancelled
|
||||
? "CANCELED"
|
||||
|
||||
+7
-6
@@ -3,7 +3,6 @@ import {
|
||||
ChevronRightIcon,
|
||||
MagnifyingGlassMinusIcon,
|
||||
MagnifyingGlassPlusIcon,
|
||||
NoSymbolIcon,
|
||||
} from "@heroicons/react/20/solid";
|
||||
import { Link, Outlet, useNavigate, useRevalidator } from "@remix-run/react";
|
||||
import { LoaderFunctionArgs } from "@remix-run/server-runtime";
|
||||
@@ -27,7 +26,6 @@ import { Slider } from "~/components/primitives/Slider";
|
||||
import { Switch } from "~/components/primitives/Switch";
|
||||
import * as Timeline from "~/components/primitives/Timeline";
|
||||
import { TreeView, useTree } from "~/components/primitives/TreeView/TreeView";
|
||||
import { LiveCountUp, LiveTimer } from "~/components/runs/v3/LiveTimer";
|
||||
import { RunIcon } from "~/components/runs/v3/RunIcon";
|
||||
import { SpanTitle, eventBackgroundClassName } from "~/components/runs/v3/SpanTitle";
|
||||
import { TaskRunStatusIcon, runStatusClassNameColor } from "~/components/runs/v3/TaskRunStatus";
|
||||
@@ -239,6 +237,9 @@ function TasksTreeView({
|
||||
},
|
||||
});
|
||||
|
||||
const minTimelineWidth = initialTimelineDimensions?.width ?? 300;
|
||||
const maxTimelineWidth = minTimelineWidth * 10;
|
||||
|
||||
return (
|
||||
<div className="grid h-full grid-rows-[2.5rem_1fr] overflow-hidden">
|
||||
<div className="mx-3 flex items-center justify-between gap-2 border-b border-grid-dimmed">
|
||||
@@ -384,8 +385,8 @@ function TasksTreeView({
|
||||
durationMs={nanosecondsToMilliseconds(totalDuration * 1.05)}
|
||||
scale={scale}
|
||||
className="h-full overflow-hidden"
|
||||
minWidth={initialTimelineDimensions?.width ?? 300}
|
||||
maxWidth={2000}
|
||||
minWidth={minTimelineWidth}
|
||||
maxWidth={maxTimelineWidth}
|
||||
>
|
||||
{/* Follows the cursor */}
|
||||
<CurrentTimeIndicator totalDuration={totalDuration} />
|
||||
@@ -568,14 +569,14 @@ function NodeStatusIcon({ node }: { node: RunEvent }) {
|
||||
}
|
||||
|
||||
if (node.data.isError) {
|
||||
return <TaskRunStatusIcon status="FAILED" className={cn("size-4")} />;
|
||||
return <TaskRunStatusIcon status="COMPLETED_WITH_ERRORS" className={cn("size-4")} />;
|
||||
}
|
||||
|
||||
if (node.data.isPartial) {
|
||||
return <TaskRunStatusIcon status={"EXECUTING"} className={cn("size-4")} />;
|
||||
}
|
||||
|
||||
return <TaskRunStatusIcon status="COMPLETED" className={cn("size-4")} />;
|
||||
return <TaskRunStatusIcon status="COMPLETED_SUCCESSFULLY" className={cn("size-4")} />;
|
||||
}
|
||||
|
||||
function TaskLine({ isError, isSelected }: { isError: boolean; isSelected: boolean }) {
|
||||
|
||||
+2
-2
@@ -20,7 +20,7 @@ import {
|
||||
ResizablePanelGroup,
|
||||
} from "~/components/primitives/Resizable";
|
||||
import { TaskPath } from "~/components/runs/v3/TaskPath";
|
||||
import { TaskRunStatus } from "~/components/runs/v3/TaskRunStatus";
|
||||
import { TaskRunStatusCombo } from "~/components/runs/v3/TaskRunStatus";
|
||||
import { redirectBackWithErrorMessage, redirectWithSuccessMessage } from "~/models/message.server";
|
||||
import { TestTaskPresenter } from "~/presenters/v3/TestTaskPresenter.server";
|
||||
import { requireUserId } from "~/services/session.server";
|
||||
@@ -209,7 +209,7 @@ export default function Page() {
|
||||
</Paragraph>
|
||||
<div className="flex items-center gap-1 text-xs text-text-dimmed">
|
||||
<div>Run #{run.number}</div>
|
||||
<TaskRunStatus status={run.status} />
|
||||
<TaskRunStatusCombo status={run.status} />
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user