From d7c152af0dd12c3a0cc5268066ecffe0feb2f7dc Mon Sep 17 00:00:00 2001 From: Aditya Nandlal <73009776+bestmaa@users.noreply.github.com> Date: Thu, 20 Aug 2026 07:03:39 +0000 Subject: [PATCH] fix: preserve selected log container on refetch --- .../__test__/logs/container-selection.test.ts | 41 +++++++++++++++++++ .../application/logs/container-selection.ts | 21 ++++++++++ .../dashboard/application/logs/show.tsx | 18 ++++---- 3 files changed, 70 insertions(+), 10 deletions(-) create mode 100644 apps/dokploy/__test__/logs/container-selection.test.ts create mode 100644 apps/dokploy/components/dashboard/application/logs/container-selection.ts diff --git a/apps/dokploy/__test__/logs/container-selection.test.ts b/apps/dokploy/__test__/logs/container-selection.test.ts new file mode 100644 index 000000000..e87817556 --- /dev/null +++ b/apps/dokploy/__test__/logs/container-selection.test.ts @@ -0,0 +1,41 @@ +import { describe, expect, it } from "vitest"; +import { resolveContainerSelection } from "@/components/dashboard/application/logs/container-selection"; + +const containers = [ + { containerId: "first-container" }, + { containerId: "selected-container" }, +]; + +describe("resolveContainerSelection", () => { + it("selects the first container when no container is selected", () => { + expect(resolveContainerSelection(undefined, containers)).toBe( + "first-container", + ); + }); + + it("preserves a manual selection when refreshed data contains it", () => { + const refreshedContainers = containers.map((container) => ({ + ...container, + })); + + expect( + resolveContainerSelection("selected-container", refreshedContainers), + ).toBe("selected-container"); + }); + + it("falls back to the first container when the selection disappears", () => { + expect(resolveContainerSelection("removed-container", containers)).toBe( + "first-container", + ); + }); + + it("keeps the current selection while container data is loading", () => { + expect(resolveContainerSelection("selected-container", undefined)).toBe( + "selected-container", + ); + }); + + it("clears the selection when no containers are available", () => { + expect(resolveContainerSelection("selected-container", [])).toBeUndefined(); + }); +}); diff --git a/apps/dokploy/components/dashboard/application/logs/container-selection.ts b/apps/dokploy/components/dashboard/application/logs/container-selection.ts new file mode 100644 index 000000000..445ffb0a8 --- /dev/null +++ b/apps/dokploy/components/dashboard/application/logs/container-selection.ts @@ -0,0 +1,21 @@ +interface ContainerOption { + containerId: string; +} + +export const resolveContainerSelection = ( + currentContainerId: string | undefined, + containers: readonly ContainerOption[] | undefined, +) => { + if (!containers) { + return currentContainerId; + } + + if ( + currentContainerId && + containers.some(({ containerId }) => containerId === currentContainerId) + ) { + return currentContainerId; + } + + return containers[0]?.containerId; +}; diff --git a/apps/dokploy/components/dashboard/application/logs/show.tsx b/apps/dokploy/components/dashboard/application/logs/show.tsx index 52ab40ec4..d5db191d1 100644 --- a/apps/dokploy/components/dashboard/application/logs/show.tsx +++ b/apps/dokploy/components/dashboard/application/logs/show.tsx @@ -21,6 +21,7 @@ import { } from "@/components/ui/select"; import { Switch } from "@/components/ui/switch"; import { api } from "@/utils/api"; +import { resolveContainerSelection } from "./container-selection"; export const DockerLogs = dynamic( () => import("@/components/dashboard/docker/logs/docker-logs-id").then( @@ -79,17 +80,13 @@ export const ShowDockerLogs = ({ appName, serverId, serviceId }: Props) => { }, ); + const availableContainers = option === "native" ? containers : services; + useEffect(() => { - if (option === "native") { - if (containers && containers?.length > 0) { - setContainerId(containers[0]?.containerId); - } - } else { - if (services && services?.length > 0) { - setContainerId(services[0]?.containerId); - } - } - }, [option, services, containers]); + setContainerId((currentContainerId) => + resolveContainerSelection(currentContainerId, availableContainers), + ); + }, [availableContainers]); const isLoading = option === "native" ? containersLoading : servicesLoading; const containersLength = @@ -114,6 +111,7 @@ export const ShowDockerLogs = ({ appName, serverId, serviceId }: Props) => { { + setContainerId(undefined); setOption(checked ? "native" : "swarm"); }} />