From f26198d799bb81bf754985d457a60f8a851ea895 Mon Sep 17 00:00:00 2001 From: maria Date: Sun, 13 Sep 2026 13:34:14 -0300 Subject: [PATCH] feat(web): organize connections by environment (#11542) --- .../settings/ConnectionsSettings.tsx | 208 ++++++++++++++---- .../settings/GitHubRoutingSettings.tsx | 11 +- .../settings/LoadBalancingSettings.tsx | 95 ++++---- 3 files changed, 221 insertions(+), 93 deletions(-) diff --git a/apps/web/src/components/settings/ConnectionsSettings.tsx b/apps/web/src/components/settings/ConnectionsSettings.tsx index e8f1232b19bcfbb3ef4b3dcca2389e47892a720f..a6e29cf7231284e5be9b16702fbd77c97d883887 100644 --- a/apps/web/src/components/settings/ConnectionsSettings.tsx +++ b/apps/web/src/components/settings/ConnectionsSettings.tsx @@ -6,6 +6,7 @@ import { TerminalIcon, } from "lucide-react"; import { useAtomValue } from "@effect/atom-react"; +import { useLocation } from "@tanstack/react-router"; import { Atom } from "effect/unstable/reactivity"; import { type KeyboardEvent, @@ -41,7 +42,7 @@ import { type EnvironmentId, resolveEnvironmentMachineKind, } from "@t3tools/contracts"; -import { connectionStatusText } from "@t3tools/client-runtime/connection"; +import { connectionStatusText, connectionStatusTitle } from "@t3tools/client-runtime/connection"; import { isAtomCommandInterrupted, squashAtomCommandFailure, @@ -67,7 +68,7 @@ import { } from "./settingsLayout"; import { searchableSetting } from "./settingsSearch"; import { EnvironmentIconPicker } from "./EnvironmentIconPicker"; -import { LoadBalancingSettings } from "./LoadBalancingSettings"; +import { LoadBalancingPreference, LoadBalancingSettings } from "./LoadBalancingSettings"; import { GitHubRoutingSettings } from "./GitHubRoutingSettings"; import { Input } from "../ui/input"; import { CommandShortcut } from "../ui/command"; @@ -157,7 +158,7 @@ import { import { requestConfirmDialog } from "~/confirmDialog"; import { useAtomCommand } from "../../state/use-atom-command"; import { primaryServerKeybindingsAtom, serverEnvironment } from "~/state/server"; -import { ConnectionStatusDot } from "../ConnectionStatusDot"; +import { ConnectionStatusDot, connectionPhaseDotClassName } from "../ConnectionStatusDot"; import { ServerUpdateAction, ServerUpdateProgress, @@ -1498,37 +1499,40 @@ function SavedBackendListRow({
-
- - -

- {environment.label} -

+
+
+ + +

+ {environment.label} +

+
+ {isConnected ? ( +
+ +
+ ) : null}
{metadataBits.length > 0 ? (

{metadataBits.join(" · ")}

) : null} - {isConnected ? ( -
- -
- ) : null} {serverUpdateState.status !== "idle" ? (
@@ -1586,12 +1590,12 @@ function SavedBackendListRow({ - Managed above + Managed locally } /> - The WSL backend is managed by the WSL setting above — turn it on or off there. + Select the primary environment to turn the WSL backend on or off. ) : ( @@ -1813,6 +1817,19 @@ export function ConnectionsSettings() { const keybindings = useAtomValue(primaryServerKeybindingsAtom); const { environments } = useEnvironments(); const primaryEnvironment = usePrimaryEnvironment(); + const [selectedEnvironmentId, setSelectedEnvironmentId] = useState(null); + const searchTargetId = useLocation({ select: (location) => location.hash.replace(/^#/, "") }); + const [handledSearchTargetId, setHandledSearchTargetId] = useState(null); + if (primaryEnvironment && handledSearchTargetId !== searchTargetId) { + setHandledSearchTargetId(searchTargetId); + if (["connections-environment", "wsl-backend"].includes(searchTargetId)) { + setSelectedEnvironmentId(primaryEnvironment.environmentId); + } + } + const selectedEnvironment = + environments.find((environment) => environment.environmentId === selectedEnvironmentId) ?? + primaryEnvironment ?? + environments[0]; const connectPairing = useAtomCommand(connectPairingAtom, { reportFailure: false }); const connectSshEnvironment = useAtomCommand(connectSshEnvironmentAtom, { reportFailure: false, @@ -3235,8 +3252,8 @@ export function ConnectionsSettings() { /> ); - return ( - + const primarySettings = ( + <> {canManageLocalBackend ? ( <> )} + + ); + return ( + {savedServerUpdateTargets.length > 0 ? ( @@ -3701,21 +3725,117 @@ export function ConnectionsSettings() {
} > - {savedEnvironments.map((environment) => ( - - ))} +
+
+ +
+ {(primaryEnvironment + ? [primaryEnvironment, ...savedEnvironments] + : savedEnvironments + ).map((environment) => { + const selected = environment.environmentId === selectedEnvironment?.environmentId; + const isPrimary = environment.entry.target._tag === "PrimaryConnectionTarget"; + return ( + + ); + })} +
+
+
+ + {selectedEnvironment ? ( +
+ {selectedEnvironment.entry.target._tag === "PrimaryConnectionTarget" ? ( + primarySettings + ) : ( + + + + )} + {selectedEnvironment.entry.enabled && loadBalancingEnvironments.length > 1 ? ( + + + + ) : null} + +
+ ) : null} +
+
- ); diff --git a/apps/web/src/components/settings/GitHubRoutingSettings.tsx b/apps/web/src/components/settings/GitHubRoutingSettings.tsx index 8f83817294efc90affdcef34bc5c43065e5c9507..fad58b4f644b1e3eb7587697d9123a8649cbcc88 100644 --- a/apps/web/src/components/settings/GitHubRoutingSettings.tsx +++ b/apps/web/src/components/settings/GitHubRoutingSettings.tsx @@ -1,4 +1,5 @@ import { useAtomValue } from "@effect/atom-react"; +import type { EnvironmentId } from "@t3tools/contracts"; import { gitHubRoutingConnectionKey, gitHubRoutingPermissionFor, @@ -22,24 +23,30 @@ const options: ReadonlyArray<{ value: GitHubRoutingPermission; label: string }> export function GitHubRoutingSettings({ environments, + selectedEnvironmentId, }: { readonly environments: ReadonlyArray; + readonly selectedEnvironmentId: EnvironmentId; }) { const permissions = useAtomValue(environmentCatalog.githubRoutingPermissionsValueAtom); const catalog = useAtomValue(environmentCatalog.catalogValueAtom); const update = useAtomCommand(environmentCatalog.setGitHubRoutingPermission); const [saving, setSaving] = useState(false); + const selectedEnvironments = environments.filter( + (environment) => environment.environmentId === selectedEnvironmentId, + ); return ( - {environments.map((environment) => ( + {selectedEnvironments.map((environment) => ( } /> - {environments.map((environment) => { - const weight = settings.loadBalancingWeights[environment.environmentId] ?? 50; - // Keep saved slider weights until the user chooses a different preference. - const preference = weight === 0 ? 0 : weight < 50 ? 25 : weight === 50 ? 50 : 100; - - return ( - { - if (value !== null) { - updateSettings({ - loadBalancingWeights: { - ...settings.loadBalancingWeights, - [environment.environmentId]: value, - }, - }); - } - }} - > - - - - - {preferences.map(({ value, label }) => ( - - {label} - - ))} - - - } - /> - ); - })} ); } + +export function LoadBalancingPreference({ environment }: { environment: EnvironmentPresentation }) { + const settings = useClientSettings(); + const settingsHydrated = useClientSettingsHydrated(); + const updateSettings = useUpdateClientSettings(); + const weight = settings.loadBalancingWeights[environment.environmentId] ?? 50; + // Keep saved slider weights until the user chooses a different preference. + const preference = weight === 0 ? 0 : weight < 50 ? 25 : weight === 50 ? 50 : 100; + + return ( + { + if (value !== null) { + updateSettings({ + loadBalancingWeights: { + ...settings.loadBalancingWeights, + [environment.environmentId]: value, + }, + }); + } + }} + > + + + + + {preferences.map(({ value, label }) => ( + + {label} + + ))} + + + } + /> + ); +} -- 2.54.0