From 6204415dc54631e38a22826da7c9171f8ac6aad5 Mon Sep 17 00:00:00 2001 From: Shivam Gupta <9.shivamgupta.6@gmail.com> Date: Sun, 9 Aug 2026 01:01:41 +0530 Subject: [PATCH 1/3] fix: honour metaName for page titles and show a loading state in requests Two cases where the UI silently did nothing: - DashboardLayout declared a `metaName` prop and 18 settings pages pass it, but the component only destructured `children`, so the value was discarded. None of those pages render their own either, so every settings page fell back to the default title from _app. The layout now renders a , using the whitelabeling app name so the suffix is correct on rebranded instances. - The requests table's fallback cell only rendered its message when `statsLogs?.data.length === 0`. While the query is in flight statsLogs is undefined, so that guard is false and the cell rendered nothing at all -- a blank panel, indistinguishable from a broken page. The query's isLoading flag wasn't even destructured. It now shows a spinner, matching the queue and deployments tables. --- .../dashboard/requests/requests-table.tsx | 10 ++++++++-- .../dokploy/components/layouts/dashboard-layout.tsx | 13 ++++++++++++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/apps/dokploy/components/dashboard/requests/requests-table.tsx b/apps/dokploy/components/dashboard/requests/requests-table.tsx index 802b60e46..63f674e6c 100644 --- a/apps/dokploy/components/dashboard/requests/requests-table.tsx +++ b/apps/dokploy/components/dashboard/requests/requests-table.tsx @@ -17,6 +17,7 @@ import { Download, Globe, InfoIcon, + Loader2, Server, TrendingUpIcon, } from "lucide-react"; @@ -100,7 +101,7 @@ export const RequestsTable = ({ dateRange }: RequestsTableProps) => { pageSize: 10, }); - const { data: statsLogs } = api.settings.readStatsLogs.useQuery( + const { data: statsLogs, isLoading } = api.settings.readStatsLogs.useQuery( { sort: sorting[0], page: pagination, @@ -273,7 +274,12 @@ export const RequestsTable = ({ dateRange }: RequestsTableProps) => { colSpan={columns.length} className="h-24 text-center" > - {statsLogs?.data.length === 0 && ( + {isLoading ? ( + <div className="w-full flex gap-4 items-center justify-center h-[55vh] text-muted-foreground"> + <Loader2 className="size-4 animate-spin" /> + <span>Loading requests...</span> + </div> + ) : ( <div className="w-full flex-col gap-2 flex items-center justify-center h-[55vh]"> <span className="text-muted-foreground text-lg font-medium"> No results. diff --git a/apps/dokploy/components/layouts/dashboard-layout.tsx b/apps/dokploy/components/layouts/dashboard-layout.tsx index 222f69b9e..77a63765b 100644 --- a/apps/dokploy/components/layouts/dashboard-layout.tsx +++ b/apps/dokploy/components/layouts/dashboard-layout.tsx @@ -1,4 +1,6 @@ +import Head from "next/head"; import { api } from "@/utils/api"; +import { useWhitelabeling } from "@/utils/hooks/use-whitelabeling"; import { ImpersonationBar } from "../dashboard/impersonation/impersonation-bar"; import { HubSpotWidget } from "../shared/HubSpotWidget"; import Page from "./side"; @@ -8,9 +10,11 @@ interface Props { metaName?: string; } -export const DashboardLayout = ({ children }: Props) => { +export const DashboardLayout = ({ children, metaName }: Props) => { const { data: haveRootAccess } = api.user.haveRootAccess.useQuery(); const { data: isCloud } = api.settings.isCloud.useQuery(); + const { config: whitelabeling } = useWhitelabeling(); + const appName = whitelabeling?.appName || "Dokploy"; const { data: currentPlan } = api.stripe.getCurrentPlan.useQuery(undefined, { enabled: isCloud === true, refetchOnWindowFocus: false, @@ -22,6 +26,13 @@ export const DashboardLayout = ({ children }: Props) => { return ( <> + {metaName && ( + <Head> + <title> + {metaName} | {appName} + + + )} {children} {isChatEnabled && ( <> From 7e9b77e93f9bae7a5dd8d156066339d55614dd6b Mon Sep 17 00:00:00 2001 From: Shivam Gupta <9.shivamgupta.6@gmail.com> Date: Mon, 31 Aug 2026 22:47:37 +0530 Subject: [PATCH 2/3] fix: surface the error state instead of "No results." when the log query fails The loading branch added in the previous commit split the fallback cell two ways: spinner while in flight, "No results." otherwise. That second branch also catches the failure case -- when readStatsLogs errors, statsLogs stays undefined and isLoading goes false, so a failed request renders as a successful empty response. The query's isError/error were not destructured. The cell now branches three ways and reports the error through AlertBlock, matching how ShowTraefikSystem surfaces a failed readDirectories query. --- .../dashboard/requests/requests-table.tsx | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/apps/dokploy/components/dashboard/requests/requests-table.tsx b/apps/dokploy/components/dashboard/requests/requests-table.tsx index 63f674e6c..1e496a68b 100644 --- a/apps/dokploy/components/dashboard/requests/requests-table.tsx +++ b/apps/dokploy/components/dashboard/requests/requests-table.tsx @@ -23,6 +23,7 @@ import { } from "lucide-react"; import { useMemo, useState } from "react"; import { toast } from "sonner"; +import { AlertBlock } from "@/components/shared/alert-block"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { @@ -101,7 +102,12 @@ export const RequestsTable = ({ dateRange }: RequestsTableProps) => { pageSize: 10, }); - const { data: statsLogs, isLoading } = api.settings.readStatsLogs.useQuery( + const { + data: statsLogs, + isLoading, + isError, + error, + } = api.settings.readStatsLogs.useQuery( { sort: sorting[0], page: pagination, @@ -279,6 +285,12 @@ export const RequestsTable = ({ dateRange }: RequestsTableProps) => { Loading requests... + ) : isError ? ( +
+ + {error?.message} + +
) : (
From 880bb55272bf35b3752955187a914d4f704eef70 Mon Sep 17 00:00:00 2001 From: Narciso Date: Mon, 31 Aug 2026 16:42:26 -0400 Subject: [PATCH 3/3] fix failing test --- .../__test__/setup/monitoring-setup.real.test.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/apps/dokploy/__test__/setup/monitoring-setup.real.test.ts b/apps/dokploy/__test__/setup/monitoring-setup.real.test.ts index d16a75a40..89b8b390d 100644 --- a/apps/dokploy/__test__/setup/monitoring-setup.real.test.ts +++ b/apps/dokploy/__test__/setup/monitoring-setup.real.test.ts @@ -64,6 +64,21 @@ const serviceExists = async (name: string) => { } }; +// Swarm keeps converging a service for a bit after it's created (scheduling +// tasks, resolving endpoints), which bumps Version.Index on its own. Calling +// setupMonitoring again before that settles races that internal bump, so wait +// for two consecutive reads to agree before treating the service as stable. +const waitForServiceConvergence = async (name: string, timeoutMs = 5000) => { + const deadline = Date.now() + timeoutMs; + let lastIndex: string | null = null; + while (Date.now() < deadline) { + const inspect = await docker.getService(name).inspect(); + if (inspect.Version.Index === lastIndex) return; + lastIndex = inspect.Version.Index; + await new Promise((resolve) => setTimeout(resolve, 50)); + } +}; + const swarmTaskNames = async () => { const list = await docker.listContainers({ all: true }); return list @@ -193,6 +208,7 @@ describe.skipIf(hasRealMonitoring())( expect(await containerExists(SERVICE_NAME)).toBe(false); await expect(setupMonitoring("test-server")).resolves.not.toThrow(); + await waitForServiceConvergence(SERVICE_NAME); await expect(setupMonitoring("test-server")).resolves.not.toThrow(); expect(await serviceExists(SERVICE_NAME)).toBe(true);