Skip to content

Commit 2c47abe

Browse files
[UI v2] feat: Starts trigger description with deployment type trigger
1 parent 376f013 commit 2c47abe

File tree

3 files changed

+205
-0
lines changed

3 files changed

+205
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import type { Automation } from "@/api/automations";
2+
3+
export type AutomationTrigger = Extract<
4+
Automation["trigger"],
5+
{ type: "event" }
6+
>;
7+
8+
export const AUTOMATION_TRIGGER_EVENT_POSTURE_LABEL = {
9+
Proactive: "stays in",
10+
Reactive: "enters",
11+
} as const satisfies Record<AutomationTrigger["posture"], string>;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import { createFakeDeployment } from "@/mocks";
2+
import { routerDecorator } from "@/storybook/utils";
3+
import type { Meta, StoryObj } from "@storybook/react";
4+
import type { AutomationTrigger } from "./constants";
5+
import { DeploymentsStatusDetails } from "./deployments-status-details";
6+
7+
const ANY_DEPLOYMENTS_ENTER: AutomationTrigger = {
8+
type: "event",
9+
id: "bc1f8369-67fd-4b04-98ba-602bd2f8b075",
10+
match: { "prefect.resource.id": "prefect.deployment.*" },
11+
match_related: {},
12+
after: [],
13+
expect: ["prefect.deployment.not-ready"],
14+
for_each: ["prefect.resource.id"],
15+
posture: "Reactive",
16+
threshold: 1,
17+
within: 0,
18+
};
19+
20+
const ANY_DEPLOYMENTS_STAY: AutomationTrigger = {
21+
type: "event",
22+
id: "413e09b4-b7cd-42a8-b503-cddcbbd5533f",
23+
match: { "prefect.resource.id": "prefect.deployment.*" },
24+
match_related: {},
25+
after: ["prefect.deployment.not-ready"],
26+
expect: ["prefect.deployment.ready"],
27+
for_each: ["prefect.resource.id"],
28+
posture: "Proactive",
29+
threshold: 1,
30+
within: 30,
31+
};
32+
33+
const SPECIFIC_DEPLOYMENTS_ENTER: AutomationTrigger = {
34+
type: "event",
35+
id: "46d8316b-8b3f-441e-b4b5-8cb98ed81ea3",
36+
match: {
37+
"prefect.resource.id": [
38+
"prefect.deployment.18940945-9107-4d8c-8734-ab2dc839cdba",
39+
"prefect.deployment.38ce4d9e-df55-4df6-a65b-38a8f2baf975",
40+
],
41+
},
42+
match_related: {},
43+
after: [],
44+
expect: ["prefect.deployment.not-ready"],
45+
for_each: ["prefect.resource.id"],
46+
posture: "Reactive",
47+
threshold: 1,
48+
within: 0,
49+
};
50+
51+
const SPECIFIC_DEPLOYMENTS_STAY: AutomationTrigger = {
52+
type: "event",
53+
id: "3f8e54f3-13da-4880-bbca-32c42c2f7688",
54+
match: {
55+
"prefect.resource.id": [
56+
"prefect.deployment.18940945-9107-4d8c-8734-ab2dc839cdba",
57+
"prefect.deployment.38ce4d9e-df55-4df6-a65b-38a8f2baf975",
58+
"prefect.deployment.06597581-a4ed-4e1a-a451-d430bd6f33d6",
59+
],
60+
},
61+
match_related: {},
62+
after: ["prefect.deployment.not-ready"],
63+
expect: ["prefect.deployment.ready"],
64+
for_each: ["prefect.resource.id"],
65+
posture: "Proactive",
66+
threshold: 1,
67+
within: 1800,
68+
};
69+
70+
const meta = {
71+
title: "Components/Automations/DeploymentsStatusDetails",
72+
component: StoryComponent,
73+
decorators: [routerDecorator],
74+
} satisfies Meta<typeof DeploymentsStatusDetails>;
75+
76+
export default meta;
77+
78+
function StoryComponent() {
79+
return (
80+
<div className="flex flex-col gap-4">
81+
{[
82+
ANY_DEPLOYMENTS_ENTER,
83+
ANY_DEPLOYMENTS_STAY,
84+
SPECIFIC_DEPLOYMENTS_ENTER,
85+
SPECIFIC_DEPLOYMENTS_STAY,
86+
].map((trigger, i) => (
87+
<DeploymentsStatusDetails
88+
key={i}
89+
trigger={trigger}
90+
deployments={[createFakeDeployment(), createFakeDeployment()]}
91+
/>
92+
))}
93+
</div>
94+
);
95+
}
96+
97+
export const Story: StoryObj = {
98+
name: "DeploymentsStatusDetails",
99+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import type { Deployment } from "@/api/deployments";
2+
import { Icon } from "@/components/ui/icons";
3+
import { pluralize } from "@/utils";
4+
import { Link } from "@tanstack/react-router";
5+
import humanizeDuration from "humanize-duration";
6+
import {
7+
AUTOMATION_TRIGGER_EVENT_POSTURE_LABEL,
8+
type AutomationTrigger,
9+
} from "./constants";
10+
11+
const DEPLOYMENT_STATUS_LABELS = {
12+
not_ready: "not ready",
13+
ready: "ready",
14+
disabled: "disabled",
15+
} as const;
16+
type DeploymentStatus = keyof typeof DEPLOYMENT_STATUS_LABELS;
17+
18+
const PREFECT_DEPLOYMENT_STATUS = {
19+
"prefect.deployment.ready": "ready",
20+
"prefect.deployment.not-ready": "not_ready",
21+
"prefect.deployment.disabled": "disabled",
22+
} as const;
23+
type PrefectDeploymentStatus = keyof typeof PREFECT_DEPLOYMENT_STATUS;
24+
25+
const getIsAnyDeployment = (trigger: AutomationTrigger) => {
26+
return trigger.match?.["prefect.resource.id"] === "prefect.deployment.*";
27+
};
28+
29+
type DeploymentsListProps = { deployments: Array<Deployment> };
30+
const DeploymentsList = ({ deployments }: DeploymentsListProps) => {
31+
return (
32+
<div className="flex gap-2">
33+
<div>{pluralize(deployments.length, "deployment")}</div>
34+
{deployments.map((deployment, i) => {
35+
return (
36+
<div key={deployment.id} className="flex items-center gap-1">
37+
<Link
38+
className="text-xs flex items-center"
39+
to="/deployments/deployment/$id"
40+
params={{ id: deployment.id }}
41+
>
42+
<Icon id="Rocket" className="h-4 w-4 mr-1" />
43+
{deployment.name}
44+
</Link>
45+
{i < deployments.length - 1 && "or"}
46+
</div>
47+
);
48+
})}
49+
</div>
50+
);
51+
};
52+
53+
type DeploymentsStatusDetailsProps = {
54+
deployments: Array<Deployment>;
55+
trigger: AutomationTrigger;
56+
};
57+
export const DeploymentsStatusDetails = ({
58+
deployments,
59+
trigger,
60+
}: DeploymentsStatusDetailsProps) => {
61+
const status = getDeploymentTriggerStatus(trigger);
62+
return (
63+
<div className="flex items-center gap-1 text-sm">
64+
When{" "}
65+
{getIsAnyDeployment(trigger) ? (
66+
"any deployment"
67+
) : (
68+
<DeploymentsList deployments={deployments} />
69+
)}{" "}
70+
{AUTOMATION_TRIGGER_EVENT_POSTURE_LABEL[trigger.posture]}{" "}
71+
{DEPLOYMENT_STATUS_LABELS[status]}
72+
{trigger.posture === "Proactive" &&
73+
` for ${humanizeDuration(trigger.within * 1_000)}`}
74+
</div>
75+
);
76+
};
77+
78+
function getDeploymentTriggerStatus(
79+
trigger: AutomationTrigger,
80+
): DeploymentStatus {
81+
// Reactive triggers respond to the presence of the expected events
82+
if (trigger.posture === "Reactive") {
83+
const status = trigger.expect?.[0];
84+
if (!status) {
85+
throw new Error("'expect' field expected");
86+
}
87+
return PREFECT_DEPLOYMENT_STATUS[status as PrefectDeploymentStatus];
88+
}
89+
// Proactive triggers respond to the absence of those expected events.
90+
const status = trigger.after?.[0];
91+
if (!status) {
92+
throw new Error("'after' field expected");
93+
}
94+
return PREFECT_DEPLOYMENT_STATUS[status as PrefectDeploymentStatus];
95+
}

0 commit comments

Comments
 (0)