From 94fee2940e2e08d46b37caaef1494f861d522b18 Mon Sep 17 00:00:00 2001 From: Anton Timmermans Date: Tue, 8 Sep 2026 10:54:20 +0200 Subject: [PATCH] feat(web): switch scheduled tasks between environments --- .../settings/ScheduledTasksSettings.tsx | 22 +++++-- .../src/routes/settings.scheduled-tasks.tsx | 66 ++++++++++++++++++- 2 files changed, 79 insertions(+), 9 deletions(-) diff --git a/apps/web/src/components/settings/ScheduledTasksSettings.tsx b/apps/web/src/components/settings/ScheduledTasksSettings.tsx index e81a8017dd3..f5e3c00b6df 100644 --- a/apps/web/src/components/settings/ScheduledTasksSettings.tsx +++ b/apps/web/src/components/settings/ScheduledTasksSettings.tsx @@ -251,6 +251,7 @@ function statusVariant(status: ScheduledTask["lastRunStatus"]) { export function ScheduledTasksSettings(target: { readonly environmentId?: EnvironmentId; readonly taskId?: ScheduledTaskId; + readonly deviceTabs?: ReactNode; }) { const primaryEnvironmentId = usePrimaryEnvironmentId(); const environment = useEnvironment(target.environmentId ?? primaryEnvironmentId); @@ -264,17 +265,20 @@ export function ScheduledTasksSettings(target: { }) : null, ); - if (!environment || !tasksQuery.data) { + if (!environment || environment.connection.phase !== "connected" || !tasksQuery.data) { return ( + {target.deviceTabs} }>

- {tasksQuery.error ?? - (environment - ? "Loading automations…" - : target.environmentId - ? "The environment for this automation is unavailable. Reconnect to view it." - : "Connect an environment to manage automations.")} + {environment && environment.connection.phase !== "connected" + ? "This environment is unavailable. Reconnect to manage automations." + : (tasksQuery.error ?? + (environment + ? "Loading automations…" + : target.environmentId + ? "The environment for this automation is unavailable. Reconnect to view it." + : "Connect an environment to manage automations."))}

@@ -287,6 +291,7 @@ export function ScheduledTasksSettings(target: { taskId={target.taskId} tasks={tasksQuery.data.tasks} error={tasksQuery.error} + deviceTabs={target.deviceTabs} /> ); } @@ -296,11 +301,13 @@ function EnvironmentScheduledTasksSettings({ taskId, tasks, error, + deviceTabs, }: { readonly environmentId: EnvironmentId; readonly taskId: ScheduledTaskId | undefined; readonly tasks: ReadonlyArray; readonly error: string | null; + readonly deviceTabs?: ReactNode; }) { useRelativeTimeTick(15_000); const allProjects = useProjects(); @@ -465,6 +472,7 @@ function EnvironmentScheduledTasksSettings({ return ( + {deviceTabs} } diff --git a/apps/web/src/routes/settings.scheduled-tasks.tsx b/apps/web/src/routes/settings.scheduled-tasks.tsx index 3eaaef1c674..bd9d004cc6c 100644 --- a/apps/web/src/routes/settings.scheduled-tasks.tsx +++ b/apps/web/src/routes/settings.scheduled-tasks.tsx @@ -1,11 +1,73 @@ import { createFileRoute } from "@tanstack/react-router"; -import { EnvironmentId, ScheduledTaskId } from "@t3tools/contracts"; +import { EnvironmentId, ScheduledTaskId, resolveEnvironmentMachineKind } from "@t3tools/contracts"; +import { connectionStatusTitle } from "@t3tools/client-runtime/connection"; import { ScheduledTasksSettings } from "../components/settings/ScheduledTasksSettings"; +import { EnvironmentMachineIcon } from "../components/EnvironmentMachineIcon"; +import { + ConnectionStatusDot, + connectionPhaseDotClassName, +} from "../components/ConnectionStatusDot"; +import { ScrollArea } from "../components/ui/scroll-area"; +import { Toggle, ToggleGroup } from "../components/ui/toggle-group"; +import { useEnvironments, usePrimaryEnvironmentId } from "../state/environments"; function SettingsScheduledTasksRoute() { const target = Route.useSearch(); - return ; + const navigate = Route.useNavigate(); + const { environments } = useEnvironments(); + const primaryEnvironmentId = usePrimaryEnvironmentId(); + const selectedEnvironmentId = target.environmentId ?? primaryEnvironmentId; + const options = environments.toSorted((left, right) => { + if (left.environmentId === primaryEnvironmentId) return -1; + if (right.environmentId === primaryEnvironmentId) return 1; + return left.label.localeCompare(right.label); + }); + return ( + 1 || target.environmentId ? ( + + { + const environment = options.find((option) => option.environmentId === next[0]); + if (environment && environment.environmentId !== selectedEnvironmentId) { + void navigate({ search: { environmentId: environment.environmentId } }); + } + }} + > + {options.map((environment) => ( + + + {environment.label} + {environment.connection.phase !== "connected" ? ( + + ) : null} + {connectionStatusTitle(environment.connection)} + + ))} + + + ) : null + } + /> + ); } export const Route = createFileRoute("/settings/scheduled-tasks")({