diff --git a/apps/fabro-web/app/components/automation-form.tsx b/apps/fabro-web/app/components/automation-form.tsx index 84831c9a6..55f29d832 100644 --- a/apps/fabro-web/app/components/automation-form.tsx +++ b/apps/fabro-web/app/components/automation-form.tsx @@ -1,8 +1,10 @@ import { useRef, type ReactNode } from "react"; +import { Link } from "react-router"; import { Switch } from "@headlessui/react"; import type { Automation, AutomationTrigger, + Environment, Run, RunProjection, WorkflowSettings, @@ -22,6 +24,7 @@ export interface AutomationFormValues { id: string; name: string; description: string; + environmentId: string; repository: string; branch: string; tag: string; @@ -36,6 +39,7 @@ export const EMPTY_AUTOMATION_FORM: AutomationFormValues = { id: "", name: "", description: "", + environmentId: "", repository: "", branch: "main", tag: "", @@ -61,6 +65,7 @@ export function automationToFormValues(automation: Automation): AutomationFormVa id: automation.id, name: automation.name, description: automation.description ?? "", + environmentId: automation.environment_id ?? "", repository: target?.repo ?? "", branch: target?.branch ?? EMPTY_AUTOMATION_FORM.branch, tag: target?.tag ?? "", @@ -76,6 +81,7 @@ export function automationFormValuesFromRun( run: Run, runState?: RunProjection | null, settings?: WorkflowSettings | null, + environments?: Environment[], ): AutomationFormValues { const name = firstPresentString( run.title, @@ -96,10 +102,17 @@ export function automationFormValuesFromRun( ?? githubRepositoryFromOriginUrl(run.repository?.origin_url) ?? ""; const cloneBranch = sandboxRuntime(run.sandbox)?.clone_branch; + const sourceEnvironment = settings?.run?.environment; + const environmentId = sourceEnvironment + && sourceEnvironment.provider !== "local" + && environments?.some((environment) => environment.id === sourceEnvironment.id) + ? sourceEnvironment.id + : ""; return { ...EMPTY_AUTOMATION_FORM, id: kebabify(name), name, + environmentId, repository, branch: canonicalTarget?.branch ?? cloneBranch @@ -130,6 +143,7 @@ export function isFormValid(values: AutomationFormValues): boolean { return ( values.id.trim() !== "" && values.name.trim() !== "" && + values.environmentId.trim() !== "" && values.repository.trim() !== "" && values.branch.trim() !== "" && isOptionalShaValid(values.sha) && @@ -172,6 +186,10 @@ function firstPresentString(...values: Array): string return ""; } +function providerLabel(provider: string): string { + return provider.charAt(0).toUpperCase() + provider.slice(1); +} + function githubRepositoryFromSettings( settings?: WorkflowSettings | null, ): string | null { @@ -226,15 +244,26 @@ interface AutomationFormFieldsProps { values: AutomationFormValues; onChange: (values: AutomationFormValues) => void; lockIdAndTarget?: boolean; + environments?: Environment[]; + environmentsLoading?: boolean; + environmentsError?: boolean; } export function AutomationFormFields({ values, onChange, lockIdAndTarget = false, + environments = [], + environmentsLoading = false, + environmentsError = false, }: AutomationFormFieldsProps) { const slugTouchedRef = useRef(values.id.length > 0); const shaValid = isOptionalShaValid(values.sha); + const compatibleEnvironments = environments + .filter((environment) => environment.provider === "docker" || environment.provider === "daytona") + .sort((left, right) => left.id.localeCompare(right.id)); + const selectedEnvironmentMissing = values.environmentId !== "" + && !compatibleEnvironments.some((environment) => environment.id === values.environmentId); function patch(partial: Partial) { onChange({ ...values, ...partial }); @@ -304,6 +333,55 @@ export function AutomationFormFields({ + + Environment} + help="Server-managed Docker or Daytona environment used whenever this automation runs." + > +
+ + {environmentsError ? ( +

+ Couldn't load environments. Refresh the page and try again. +

+ ) : !environmentsLoading && compatibleEnvironments.length === 0 ? ( +

+ No Docker or Daytona environments are available.{" "} + + Create an environment + {" "} + before saving this automation. +

+ ) : selectedEnvironmentMissing ? ( +

+ This environment is no longer available. Choose another environment before saving. +

+ ) : null} +
+
+
+ Repository} help="GitHub repository in owner/repo form."> {automation.workflow} + + {automation.environment_id ?? ( + Environment required + )} + {scheduleTrigger ? ( {scheduleTrigger.expression} ) : null} @@ -164,6 +170,11 @@ function AutomationHeader({ automation }: { automation: Automation }) { {automation.description}

) : null} + {automation.last_error ? ( +

+ Last scheduled run failed: {automation.last_error} +

+ ) : null}
@@ -177,7 +188,13 @@ function AutomationHeader({ automation }: { automation: Automation }) { type="button" onClick={onRun} disabled={!canRun || running} - title={canRun ? undefined : "Enable the API trigger to run it"} + title={ + canRun + ? undefined + : automation.environment_id === null + ? "Select an environment before running this automation" + : "Enable the API trigger to run it" + } className={PRIMARY_BUTTON_CLASS} >