Skip to content

Commit e9d215a

Browse files
feat: display schedules (#879)
1 parent 3e4e2c8 commit e9d215a

File tree

6 files changed

+155
-4
lines changed

6 files changed

+155
-4
lines changed

src/app/pipelines/[pipelineId]/runs/columns.tsx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { DisplayDate } from "@/components/DisplayDate";
44
import { ExecutionStatusIcon, getExecutionStatusColor } from "@/components/ExecutionStatus";
55
import { InlineAvatar } from "@/components/InlineAvatar";
66
import { SnapshotLink } from "@/components/pipeline-snapshots/snapshot-link";
7+
import { ScheduleTag } from "@/components/triggers/schedule-tag";
78
import { routes } from "@/router/routes";
89
import { PipelineRun, PipelineRunBody } from "@/types/pipeline-runs";
910
import { Stack } from "@/types/stack";
@@ -120,13 +121,28 @@ export function getPipelineDetailColumns(): ColumnDef<PipelineRun>[] {
120121

121122
return (
122123
<Link to={routes.stacks.overview}>
123-
<Tag rounded={false} className="inline-block" color="turquoise" emphasis="subtle">
124+
<Tag
125+
rounded={false}
126+
className="inline-block whitespace-nowrap"
127+
color="turquoise"
128+
emphasis="subtle"
129+
>
124130
{name}
125131
</Tag>
126132
</Link>
127133
);
128134
}
129135
},
136+
{
137+
id: "trigger",
138+
header: "Trigger",
139+
accessorFn: (row) => row.resources?.schedule?.name,
140+
cell: ({ row }) => {
141+
const schedule = row.original.resources?.schedule;
142+
if (!schedule) return null;
143+
return <ScheduleTag />;
144+
}
145+
},
130146
{
131147
id: "created",
132148
header: "Created at",

src/app/runs/columns.tsx

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { DisplayDate } from "@/components/DisplayDate";
44
import { ExecutionStatusIcon, getExecutionStatusColor } from "@/components/ExecutionStatus";
55
import { InlineAvatar } from "@/components/InlineAvatar";
66
import { PipelineLink } from "@/components/pipelines/pipeline-link";
7+
import { ScheduleTag } from "@/components/triggers/schedule-tag";
78
import { routes } from "@/router/routes";
89
import { ExecutionStatus, PipelineRun } from "@/types/pipeline-runs";
910
import { Stack } from "@/types/stack";
@@ -118,12 +119,27 @@ export const runsColumns: ColumnDef<PipelineRun>[] = [
118119
if (!name || !id) return null;
119120

120121
return (
121-
<Tag rounded={false} className="inline-block" color="turquoise" emphasis="subtle">
122+
<Tag
123+
rounded={false}
124+
className="inline-block whitespace-nowrap"
125+
color="turquoise"
126+
emphasis="subtle"
127+
>
122128
{name}
123129
</Tag>
124130
);
125131
}
126132
},
133+
{
134+
id: "trigger",
135+
header: "Trigger",
136+
accessorFn: (row) => row.resources?.schedule?.name,
137+
cell: ({ row }) => {
138+
const schedule = row.original.resources?.schedule;
139+
if (!schedule) return null;
140+
return <ScheduleTag />;
141+
}
142+
},
127143
{
128144
id: "created-at",
129145
header: "Created at",
@@ -135,7 +151,11 @@ export const runsColumns: ColumnDef<PipelineRun>[] = [
135151
date: string;
136152
}>();
137153

138-
return <DisplayDate dateString={date} />;
154+
return (
155+
<span className="whitespace-nowrap">
156+
<DisplayDate dateString={date} />
157+
</span>
158+
);
139159
}
140160
},
141161
{

src/components/runs/detail-tabs/Overview/Details.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ import { CopyButton } from "@/components/CopyButton";
55
import { DisplayDate } from "@/components/DisplayDate";
66
import { ExecutionStatusIcon, getExecutionStatusTagColor } from "@/components/ExecutionStatus";
77
import { InlineAvatar } from "@/components/InlineAvatar";
8-
import { Key, Value } from "@/components/KeyValue";
8+
import { Key, KeyValue, Value } from "@/components/KeyValue";
99
import { PipelineLink } from "@/components/pipelines/pipeline-link";
1010
import { RepoBadge } from "@/components/repositories/RepoBadge";
11+
import { ScheduleTag } from "@/components/triggers/schedule-tag";
1112
import { usePipelineRun } from "@/data/pipeline-runs/pipeline-run-detail-query";
1213
import { calculateTimeDifference } from "@/lib/dates";
1314
import { snakeCaseToTitleCase } from "@/lib/strings";
@@ -54,6 +55,7 @@ export function Details({ runId }: Props) {
5455
const statusReason = data.body?.status_reason;
5556
const executionMode = data.metadata?.config.execution_mode;
5657

58+
const schedule = data.resources?.schedule;
5759
return (
5860
<CollapsiblePanel open={open} onOpenChange={setOpen}>
5961
<CollapsibleHeader className="flex items-center gap-[10px]">
@@ -142,6 +144,7 @@ export function Details({ runId }: Props) {
142144
"Not available"
143145
)}
144146
</Value>
147+
{schedule && <KeyValue label="Triggered by" value={<ScheduleTag />} />}
145148
<Key className={data.metadata?.code_path ? "col-span-3" : ""}>
146149
<div className="flex items-center space-x-0.5 truncate">
147150
<span>Code Path</span>

src/components/runs/detail-tabs/Overview/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { AlertPanels } from "./AlertPanels";
22
import { Details } from "./Details";
33
import { OrchestratorCollapsible } from "./Orchestrator";
4+
import { ScheduleCollapsible } from "./schedule";
45
type Props = {
56
runId: string;
67
};
@@ -11,6 +12,7 @@ export function OverviewTab({ runId }: Props) {
1112
<AlertPanels runId={runId} />
1213
<Details runId={runId} />
1314
<OrchestratorCollapsible runId={runId} />
15+
<ScheduleCollapsible runId={runId} />
1416
</div>
1517
);
1618
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import { CollapsibleChevron } from "@/components/collapsible-chevron";
2+
import { DisplayDate } from "@/components/DisplayDate";
3+
import { KeyValue } from "@/components/KeyValue";
4+
import { usePipelineRun } from "@/data/pipeline-runs/pipeline-run-detail-query";
5+
import { isBoolean } from "@/lib/type-guards";
6+
import {
7+
CollapsibleContent,
8+
CollapsibleHeader,
9+
CollapsiblePanel,
10+
CollapsibleTrigger,
11+
Skeleton
12+
} from "@zenml-io/react-component-library";
13+
import { useState } from "react";
14+
15+
type Props = {
16+
runId: string;
17+
};
18+
19+
export function ScheduleCollapsible({ runId }: Props) {
20+
const [open, setOpen] = useState(true);
21+
const { data, isError, isPending } = usePipelineRun({ runId });
22+
23+
if (isError) return null;
24+
if (isPending) return <Skeleton className="h-[200px]" />;
25+
26+
const schedule = data.resources?.schedule;
27+
28+
if (!schedule) return null;
29+
30+
const cron = schedule.body?.cron_expression;
31+
const startTime = schedule.body?.start_time;
32+
const endTime = schedule.body?.end_time;
33+
const intervalSecond = schedule.body?.interval_second;
34+
const catchup = schedule.body?.catchup;
35+
const runOnceStartTime = schedule.body?.run_once_start_time;
36+
37+
return (
38+
<CollapsiblePanel open={open} onOpenChange={setOpen}>
39+
<CollapsibleHeader className="flex items-center gap-[10px]">
40+
<CollapsibleTrigger className="flex w-full items-center gap-2">
41+
<CollapsibleChevron open={open} />
42+
<p id="schedule-collapsible">Schedule</p>
43+
</CollapsibleTrigger>
44+
</CollapsibleHeader>
45+
<CollapsibleContent className="space-y-3 border-t border-theme-border-moderate bg-theme-surface-primary px-5 py-3">
46+
<dl className="grid grid-cols-1 gap-x-[10px] gap-y-2 md:grid-cols-3 md:gap-y-4">
47+
<KeyValue label="Cron" value={cron ? <p>{cron}</p> : "Not available"} />
48+
<KeyValue
49+
label="Start Time"
50+
value={
51+
startTime ? (
52+
<p>
53+
<DisplayDate dateString={startTime} />
54+
</p>
55+
) : (
56+
"Not available"
57+
)
58+
}
59+
/>
60+
<KeyValue
61+
label="End Time"
62+
value={
63+
endTime ? (
64+
<p>
65+
<DisplayDate dateString={endTime} />
66+
</p>
67+
) : (
68+
"Not available"
69+
)
70+
}
71+
/>
72+
<KeyValue
73+
label="Interval"
74+
value={intervalSecond ? <p>{intervalSecond} seconds</p> : "Not available"}
75+
/>
76+
<KeyValue
77+
label="Catchup"
78+
value={isBoolean(catchup) ? <p>{catchup ? "Yes" : "No"}</p> : "Not available"}
79+
/>
80+
<KeyValue
81+
label="Run Once Start Time"
82+
value={
83+
runOnceStartTime ? (
84+
<p>
85+
<DisplayDate dateString={runOnceStartTime} />
86+
</p>
87+
) : (
88+
"Not available"
89+
)
90+
}
91+
/>
92+
</dl>
93+
</CollapsibleContent>
94+
</CollapsiblePanel>
95+
);
96+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import Clock from "@/assets/icons/clock.svg?react";
2+
import { Tag } from "@zenml-io/react-component-library";
3+
import { ComponentProps } from "react";
4+
5+
type Props = ComponentProps<typeof Tag>;
6+
7+
export function ScheduleTag({ ...props }: Props) {
8+
return (
9+
<Tag rounded={false} className="inline-flex" emphasis="subtle" color="blue" {...props}>
10+
<Clock className="h-4 w-4 shrink-0 fill-current" />
11+
<span className="truncate">Schedule</span>
12+
</Tag>
13+
);
14+
}

0 commit comments

Comments
 (0)