Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { useFormContext, useWatch } from "react-hook-form";
import type { AutomationWizardSchema } from "@/components/automations/automations-wizard/automation-schema";
import {
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { PostureSelect } from "./posture-select";

export const CustomTriggerFields = () => {
const form = useFormContext<AutomationWizardSchema>();
const posture = useWatch<AutomationWizardSchema>({ name: "trigger.posture" });

return (
<div className="space-y-4">
<div className="flex items-end gap-4">
<PostureSelect />
</div>

<FormField
control={form.control}
name="trigger.expect"
render={({ field }) => {
const events = field.value ?? [];
const textValue = events.join("\n");
return (
<FormItem>
<FormLabel>Expected Events (one per line)</FormLabel>
<FormControl>
<Textarea
placeholder="prefect.flow-run.Completed"
value={textValue}
onChange={(e) => {
const lines = e.target.value.split("\n");
field.onChange(lines.length > 0 ? lines : undefined);
}}
rows={4}
/>
</FormControl>
<FormMessage />
</FormItem>
);
}}
/>

<div className="flex gap-4">
<FormField
control={form.control}
name="trigger.threshold"
render={({ field }) => (
<FormItem className="w-32">
<FormLabel>Threshold</FormLabel>
<FormControl>
<Input
type="number"
min={1}
{...field}
onChange={(e) => field.onChange(Number(e.target.value))}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>

{posture === "Proactive" && (
<FormField
control={form.control}
name="trigger.within"
render={({ field }) => (
<FormItem className="w-32">
<FormLabel>Within (seconds)</FormLabel>
<FormControl>
<Input
type="number"
min={0}
{...field}
onChange={(e) => field.onChange(Number(e.target.value))}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
)}
</div>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { useFormContext, useWatch } from "react-hook-form";
import type { AutomationWizardSchema } from "@/components/automations/automations-wizard/automation-schema";
import {
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { PostureSelect } from "./posture-select";

const DEPLOYMENT_STATUSES = [
{ value: "prefect.deployment.ready", label: "Ready" },
{ value: "prefect.deployment.not-ready", label: "Not Ready" },
];

export const DeploymentStatusTriggerFields = () => {
const form = useFormContext<AutomationWizardSchema>();
const posture = useWatch<AutomationWizardSchema>({ name: "trigger.posture" });

// Determine which field to use based on posture
const statusFieldName =
posture === "Proactive" ? "trigger.after" : "trigger.expect";

return (
<div className="space-y-4">
<div className="flex items-end gap-4">
<PostureSelect />
<FormField
control={form.control}
name={statusFieldName}
render={({ field }) => {
const selectedStatus = field.value?.[0];
return (
<FormItem className="flex-1">
<FormLabel>Status</FormLabel>
<FormControl>
<Select
value={selectedStatus ?? ""}
onValueChange={(value) => field.onChange([value])}
>
<SelectTrigger>
<SelectValue placeholder="Select status" />
</SelectTrigger>
<SelectContent>
{DEPLOYMENT_STATUSES.map((status) => (
<SelectItem key={status.value} value={status.value}>
{status.label}
</SelectItem>
))}
</SelectContent>
</Select>
</FormControl>
<FormMessage />
</FormItem>
);
}}
/>
</div>

<div className="flex gap-4">
<FormField
control={form.control}
name="trigger.threshold"
render={({ field }) => (
<FormItem className="w-32">
<FormLabel>Threshold</FormLabel>
<FormControl>
<Input
type="number"
min={1}
{...field}
onChange={(e) => field.onChange(Number(e.target.value))}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>

{posture === "Proactive" && (
<FormField
control={form.control}
name="trigger.within"
render={({ field }) => (
<FormItem className="w-32">
<FormLabel>Within (seconds)</FormLabel>
<FormControl>
<Input
type="number"
min={0}
{...field}
onChange={(e) => field.onChange(Number(e.target.value))}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
)}
</div>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
export { CustomTriggerFields } from "./custom-trigger-fields";
export { DeploymentStatusTriggerFields } from "./deployment-status-trigger-fields";
export { FlowRunStateTriggerFields } from "./flow-run-state-trigger-fields";
export { PostureSelect } from "./posture-select";
export { StateMultiSelect } from "./state-multi-select";
export { TriggerStep } from "./trigger-step";
export { getDefaultTriggerForTemplate } from "./trigger-step-utils";
export { WorkPoolStatusTriggerFields } from "./work-pool-status-trigger-fields";
export { WorkQueueStatusTriggerFields } from "./work-queue-status-trigger-fields";
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,17 @@ describe("TriggerStep", () => {
expect(screen.getByLabelText("Trigger Template")).toBeVisible();
});

it("shows placeholder text when deployment-status template is selected", async () => {
it("can select deployment-status template and shows trigger fields", async () => {
const user = userEvent.setup();

render(<TriggerStepFormContainer />);

await user.click(screen.getByLabelText("Trigger Template"));
await user.click(screen.getByRole("option", { name: "Deployment status" }));

expect(
screen.getByText("Deployment status trigger fields coming soon"),
).toBeVisible();
// Should show the DeploymentStatusTriggerFields component
expect(screen.getByLabelText("select posture")).toBeVisible();
expect(screen.getByLabelText("Threshold")).toBeVisible();
});

it("can select flow-run-state template and shows trigger fields", async () => {
Expand All @@ -68,40 +68,45 @@ describe("TriggerStep", () => {
expect(screen.getByLabelText("Threshold")).toBeVisible();
});

it("can select work-pool-status template", async () => {
it("can select work-pool-status template and shows trigger fields", async () => {
const user = userEvent.setup();

render(<TriggerStepFormContainer />);

await user.click(screen.getByLabelText("Trigger Template"));
await user.click(screen.getByRole("option", { name: "Work pool status" }));

expect(
screen.getByText("Work pool status trigger fields coming soon"),
).toBeVisible();
// Should show the WorkPoolStatusTriggerFields component
expect(screen.getByLabelText("select posture")).toBeVisible();
expect(screen.getByLabelText("Threshold")).toBeVisible();
});

it("can select work-queue-status template", async () => {
it("can select work-queue-status template and shows trigger fields", async () => {
const user = userEvent.setup();

render(<TriggerStepFormContainer />);

await user.click(screen.getByLabelText("Trigger Template"));
await user.click(screen.getByRole("option", { name: "Work queue status" }));

expect(
screen.getByText("Work queue status trigger fields coming soon"),
).toBeVisible();
// Should show the WorkQueueStatusTriggerFields component
expect(screen.getByLabelText("select posture")).toBeVisible();
expect(screen.getByLabelText("Threshold")).toBeVisible();
});

it("can select custom template", async () => {
it("can select custom template and shows trigger fields", async () => {
const user = userEvent.setup();

render(<TriggerStepFormContainer />);

await user.click(screen.getByLabelText("Trigger Template"));
await user.click(screen.getByRole("option", { name: "Custom" }));

expect(screen.getByText("Custom trigger fields coming soon")).toBeVisible();
// Should show the CustomTriggerFields component
expect(screen.getByLabelText("select posture")).toBeVisible();
expect(screen.getByLabelText("Threshold")).toBeVisible();
expect(
screen.getByLabelText("Expected Events (one per line)"),
).toBeVisible();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@ import {
AutomationsTriggerTemplateSelect,
type TriggerTemplate,
} from "@/components/automations/automations-wizard/automations-trigger-template-select";
import { CustomTriggerFields } from "./custom-trigger-fields";
import { DeploymentStatusTriggerFields } from "./deployment-status-trigger-fields";
import { FlowRunStateTriggerFields } from "./flow-run-state-trigger-fields";
import { getDefaultTriggerForTemplate } from "./trigger-step-utils";
import { WorkPoolStatusTriggerFields } from "./work-pool-status-trigger-fields";
import { WorkQueueStatusTriggerFields } from "./work-queue-status-trigger-fields";

export const TriggerStep = () => {
const form = useFormContext<AutomationWizardSchema>();
Expand Down Expand Up @@ -37,29 +41,13 @@ const TriggerTemplateFields = ({ template }: TriggerTemplateFieldsProps) => {
case "flow-run-state":
return <FlowRunStateTriggerFields />;
case "deployment-status":
return (
<div className="text-muted-foreground">
Deployment status trigger fields coming soon
</div>
);
return <DeploymentStatusTriggerFields />;
case "work-pool-status":
return (
<div className="text-muted-foreground">
Work pool status trigger fields coming soon
</div>
);
return <WorkPoolStatusTriggerFields />;
case "work-queue-status":
return (
<div className="text-muted-foreground">
Work queue status trigger fields coming soon
</div>
);
return <WorkQueueStatusTriggerFields />;
case "custom":
return (
<div className="text-muted-foreground">
Custom trigger fields coming soon
</div>
);
return <CustomTriggerFields />;
default:
return null;
}
Expand Down
Loading
Loading