From eb4fbff1b20dda42119d0d2c9d636cb7667337a3 Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Mon, 15 Dec 2025 15:17:56 -0600 Subject: [PATCH 1/3] feat(servers): enhance server management UI with button options - Added `asButton` prop to `HandleServers`, `SetupServer`, `ShowServerActions`, and `TerminalModal` components to allow rendering as buttons for improved UI flexibility. - Updated the server management interface to use buttons for actions like editing and setting up servers, enhancing user experience. - Introduced new icons for better visual representation of actions in the server management dashboard. --- .../servers/actions/show-server-actions.tsx | 20 +- .../settings/servers/handle-servers.tsx | 28 +- .../settings/servers/setup-server.tsx | 20 +- .../settings/servers/show-servers.tsx | 445 +++++++++--------- .../settings/web-server/terminal-modal.tsx | 19 +- 5 files changed, 293 insertions(+), 239 deletions(-) diff --git a/apps/dokploy/components/dashboard/settings/servers/actions/show-server-actions.tsx b/apps/dokploy/components/dashboard/settings/servers/actions/show-server-actions.tsx index 41156d35b..ea2300cc5 100644 --- a/apps/dokploy/components/dashboard/settings/servers/actions/show-server-actions.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/actions/show-server-actions.tsx @@ -1,3 +1,4 @@ +import { Activity } from "lucide-react"; import { useState } from "react"; import { Dialog, @@ -6,6 +7,7 @@ import { DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; +import { Button } from "@/components/ui/button"; import { DropdownMenuItem } from "@/components/ui/dropdown-menu"; import { ShowStorageActions } from "./show-storage-actions"; import { ShowTraefikActions } from "./show-traefik-actions"; @@ -13,20 +15,30 @@ import { ToggleDockerCleanup } from "./toggle-docker-cleanup"; interface Props { serverId: string; + asButton?: boolean; } -export const ShowServerActions = ({ serverId }: Props) => { +export const ShowServerActions = ({ serverId, asButton = false }: Props) => { const [isOpen, setIsOpen] = useState(false); return ( - + {asButton ? ( + + + + ) : ( e.preventDefault()} + onSelect={(e) => { + e.preventDefault(); + setIsOpen(true); + }} > View Actions - + )}
Web server settings diff --git a/apps/dokploy/components/dashboard/settings/servers/handle-servers.tsx b/apps/dokploy/components/dashboard/settings/servers/handle-servers.tsx index b36aec7c4..d9d1977a7 100644 --- a/apps/dokploy/components/dashboard/settings/servers/handle-servers.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/handle-servers.tsx @@ -1,5 +1,5 @@ import { zodResolver } from "@hookform/resolvers/zod"; -import { PlusIcon } from "lucide-react"; +import { PlusIcon, Pencil } from "lucide-react"; import Link from "next/link"; import { useTranslation } from "next-i18next"; import { useEffect, useState } from "react"; @@ -59,9 +59,10 @@ type Schema = z.infer; interface Props { serverId?: string; + asButton?: boolean; } -export const HandleServers = ({ serverId }: Props) => { +export const HandleServers = ({ serverId, asButton = false }: Props) => { const { t } = useTranslation("settings"); const utils = api.useUtils(); @@ -137,21 +138,32 @@ export const HandleServers = ({ serverId }: Props) => { return ( - - {serverId ? ( + {serverId ? ( + asButton ? ( + + + + ) : ( e.preventDefault()} + onSelect={(e) => { + e.preventDefault(); + setIsOpen(true); + }} > Edit Server - ) : ( + ) + ) : ( + - )} - + + )} {serverId ? "Edit" : "Create"} Server diff --git a/apps/dokploy/components/dashboard/settings/servers/setup-server.tsx b/apps/dokploy/components/dashboard/settings/servers/setup-server.tsx index 95899f20a..d88e9a3e4 100644 --- a/apps/dokploy/components/dashboard/settings/servers/setup-server.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/setup-server.tsx @@ -1,5 +1,5 @@ import copy from "copy-to-clipboard"; -import { CopyIcon, ExternalLinkIcon, ServerIcon } from "lucide-react"; +import { CopyIcon, ExternalLinkIcon, ServerIcon, Settings } from "lucide-react"; import Link from "next/link"; import { useState } from "react"; import { toast } from "sonner"; @@ -36,9 +36,10 @@ import { ValidateServer } from "./validate-server"; interface Props { serverId: string; + asButton?: boolean; } -export const SetupServer = ({ serverId }: Props) => { +export const SetupServer = ({ serverId, asButton = false }: Props) => { const [isOpen, setIsOpen] = useState(false); const { data: server } = api.server.one.useQuery( { @@ -81,14 +82,23 @@ export const SetupServer = ({ serverId }: Props) => { return ( - + {asButton ? ( + + + + ) : ( e.preventDefault()} + onSelect={(e) => { + e.preventDefault(); + setIsOpen(true); + }} > Setup Server - + )}
diff --git a/apps/dokploy/components/dashboard/settings/servers/show-servers.tsx b/apps/dokploy/components/dashboard/settings/servers/show-servers.tsx index 2f8ac24e2..2efc718ee 100644 --- a/apps/dokploy/components/dashboard/settings/servers/show-servers.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/show-servers.tsx @@ -1,5 +1,5 @@ import { format } from "date-fns"; -import { KeyIcon, Loader2, MoreHorizontal, ServerIcon } from "lucide-react"; +import { KeyIcon, Loader2, MoreHorizontal, ServerIcon, Clock, User, Key, Network, Terminal, Settings, Pencil, Trash2 } from "lucide-react"; import Link from "next/link"; import { useRouter } from "next/router"; import { useTranslation } from "next-i18next"; @@ -24,14 +24,11 @@ import { DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { - Table, - TableBody, - TableCaption, - TableCell, - TableHead, - TableHeader, - TableRow, -} from "@/components/ui/table"; + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; import { api } from "@/utils/api"; import { ShowNodesModal } from "../cluster/nodes/show-nodes-modal"; import { TerminalModal } from "../web-server/terminal-modal"; @@ -59,7 +56,7 @@ export const ShowServers = () => { return (
{query?.success && isCloud && } - +
@@ -114,57 +111,70 @@ export const ShowServers = () => {
) : ( -
- - -
- See all servers -
-
- - - Name - {isCloud && ( - - Status - - )} - - Type - - - IP Address - - - Port - - - Username - - - SSH Key - - - Created - - - Actions - - - - - {data?.map((server) => { - const canDelete = server.totalSum === 0; - const isActive = server.serverStatus === "active"; - const isBuildServer = - server.serverType === "build"; - return ( - - - {server.name} - - {isCloud && ( - +
+
+ {data?.map((server) => { + const canDelete = server.totalSum === 0; + const isActive = server.serverStatus === "active"; + const isBuildServer = + server.serverType === "build"; + return ( + + +
+
+ + + {server.name} + +
+ {isActive && server.sshKeyId && !isBuildServer && ( + + + + + + + Advanced + + + + {isCloud && ( + + )} + + + + + + )} +
+
+ {isCloud && ( { > {server.serverStatus} - - )} - + )} { > {server.serverType} - - - {server.ipAddress} - - - {server.port} - - - {server.username} - - - +
+
+ +
+ + IP: + {server.ipAddress} + Port: + {server.port} +
+
+ + User: + {server.username} +
+
+ + SSH Key: + {server.sshKeyId ? "Yes" : "No"} - - - - {format( - new Date(server.createdAt), - "PPpp", - )} +
+
+ + + Created {format(new Date(server.createdAt), "PPp")} - - - - - - - - - - Actions - - - {isActive && ( - <> - {server.sshKeyId && ( - + + {/* Compact Actions */} + {isActive && ( +
+ + {server.sshKeyId && ( + + +
+ + + +
+
+ +

Terminal

+
+
+ )} + + + +
+ - - {t( - "settings.common.enterTerminal", - )} - - - )} - + asButton={true} + /> +
+
+ +

Setup Server

+
+
- + + +
+ +
+
+ +

Edit Server

+
+
- {server.sshKeyId && - !isBuildServer && ( + {server.sshKeyId && !isBuildServer && ( + + +
- )} - - )} - - - You can not delete this server - because it has active services. - - You have active services - associated with this server, - please delete them first. -
- ) - } - onClick={async () => { - await mutateAsync({ - serverId: server.serverId, - }) - .then(() => { - refetch(); - toast.success( - `Server ${server.name} deleted successfully`, - ); - }) - .catch((err) => { - toast.error(err.message); - }); - }} - > - e.preventDefault()} - > - Delete Server - - +
+ +

Web Server Actions

+
+
+ )} + +
+ + + +
+ + You can not delete this server + because it has active services. + + You have active services + associated with this server, + please delete them first. + +
+ ) + } + onClick={async () => { + await mutateAsync({ + serverId: server.serverId, + }) + .then(() => { + refetch(); + toast.success( + `Server ${server.name} deleted successfully`, + ); + }) + .catch((err) => { + toast.error(err.message); + }); + }} + > + + +
+ + +

{canDelete ? 'Delete Server' : 'Cannot delete - has active services'}

+
+ +
+
+ )} + + + ); + })} +
- {isActive && - server.sshKeyId && - !isBuildServer && ( - <> - - - Extra - - - - - {isCloud && ( - - )} - - - - - - - )} - - - - - ); - })} - -
- -
+
{data && data?.length > 0 && (
diff --git a/apps/dokploy/components/dashboard/settings/web-server/terminal-modal.tsx b/apps/dokploy/components/dashboard/settings/web-server/terminal-modal.tsx index 58e4c9d4e..0bea05fc0 100644 --- a/apps/dokploy/components/dashboard/settings/web-server/terminal-modal.tsx +++ b/apps/dokploy/components/dashboard/settings/web-server/terminal-modal.tsx @@ -24,10 +24,12 @@ const getTerminalKey = () => { interface Props { children?: React.ReactNode; serverId: string; + asButton?: boolean; } -export const TerminalModal = ({ children, serverId }: Props) => { +export const TerminalModal = ({ children, serverId, asButton = false }: Props) => { const [terminalKey, setTerminalKey] = useState(getTerminalKey()); + const [isOpen, setIsOpen] = useState(false); const isLocalServer = serverId === "local"; const { data } = api.server.one.useQuery( @@ -43,15 +45,22 @@ export const TerminalModal = ({ children, serverId }: Props) => { }; return ( - - + + {asButton ? ( + + {children} + + ) : ( e.preventDefault()} + onSelect={(e) => { + e.preventDefault(); + setIsOpen(true); + }} > {children} - + )} event.preventDefault()} From 0ddf6b851f0d9919d73430ce384297c5c2a5887a Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Tue, 16 Dec 2025 21:05:52 -0600 Subject: [PATCH 2/3] feat(servers): add tooltip for deactivated server status in dashboard - Wrapped server status display in a TooltipProvider to provide additional context for deactivated servers. - Implemented a tooltip that informs users about the reason for deactivation and instructions for reactivation, enhancing user experience and clarity in server management. --- .../settings/servers/show-servers.tsx | 45 ++++++++++++------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/apps/dokploy/components/dashboard/settings/servers/show-servers.tsx b/apps/dokploy/components/dashboard/settings/servers/show-servers.tsx index 2efc718ee..e0aa3c54e 100644 --- a/apps/dokploy/components/dashboard/settings/servers/show-servers.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/show-servers.tsx @@ -173,26 +173,41 @@ export const ShowServers = () => { )}
-
- {isCloud && ( + +
+ {isCloud && ( + <> + {server.serverStatus === "active" ? ( + + {server.serverStatus} + + ) : ( + + + + + {server.serverStatus} + + + + +

+ This server is deactivated due to lack of payment. Please pay your invoice to reactivate it. If you think this is an error, please contact support. +

+
+
+ )} + + )} - {server.serverStatus} + {server.serverType} - )} - - {server.serverType} - -
+
+
From 3a5ac9d31f91c0d3cdb74a24be5356ef83dfbd5c Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Wed, 17 Dec 2025 03:09:23 +0000 Subject: [PATCH 3/3] [autofix.ci] apply automated fixes --- .../settings/servers/show-servers.tsx | 195 +++++++++++------- .../settings/web-server/terminal-modal.tsx | 10 +- 2 files changed, 130 insertions(+), 75 deletions(-) diff --git a/apps/dokploy/components/dashboard/settings/servers/show-servers.tsx b/apps/dokploy/components/dashboard/settings/servers/show-servers.tsx index e0aa3c54e..111f10e28 100644 --- a/apps/dokploy/components/dashboard/settings/servers/show-servers.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/show-servers.tsx @@ -1,5 +1,18 @@ import { format } from "date-fns"; -import { KeyIcon, Loader2, MoreHorizontal, ServerIcon, Clock, User, Key, Network, Terminal, Settings, Pencil, Trash2 } from "lucide-react"; +import { + KeyIcon, + Loader2, + MoreHorizontal, + ServerIcon, + Clock, + User, + Key, + Network, + Terminal, + Settings, + Pencil, + Trash2, +} from "lucide-react"; import Link from "next/link"; import { useRouter } from "next/router"; import { useTranslation } from "next-i18next"; @@ -116,10 +129,12 @@ export const ShowServers = () => { {data?.map((server) => { const canDelete = server.totalSum === 0; const isActive = server.serverStatus === "active"; - const isBuildServer = - server.serverType === "build"; + const isBuildServer = server.serverType === "build"; return ( - +
@@ -128,50 +143,52 @@ export const ShowServers = () => { {server.name}
- {isActive && server.sshKeyId && !isBuildServer && ( - - - - - - - Advanced - - - - {isCloud && ( - + + + + + + Advanced + + - )} - - - - - - )} + + {isCloud && ( + + )} + + + + + + )}
@@ -185,14 +202,24 @@ export const ShowServers = () => { - + {server.serverStatus} - +

- This server is deactivated due to lack of payment. Please pay your invoice to reactivate it. If you think this is an error, please contact support. + This server is deactivated due + to lack of payment. Please pay + your invoice to reactivate it. + If you think this is an error, + please contact support.

@@ -201,7 +228,9 @@ export const ShowServers = () => { )} {server.serverType} @@ -212,19 +241,33 @@ export const ShowServers = () => {
- IP: - {server.ipAddress} - Port: - {server.port} + + IP: + + + {server.ipAddress} + + + Port: + + + {server.port} +
- User: - {server.username} + + User: + + + {server.username} +
- SSH Key: + + SSH Key: + {server.sshKeyId ? "Yes" : "No"} @@ -232,10 +275,14 @@ export const ShowServers = () => {
- Created {format(new Date(server.createdAt), "PPp")} + Created{" "} + {format( + new Date(server.createdAt), + "PPp", + )}
- + {/* Compact Actions */} {isActive && (
@@ -248,8 +295,8 @@ export const ShowServers = () => { serverId={server.serverId} asButton={true} > - @@ -361,7 +410,11 @@ export const ShowServers = () => {
-

{canDelete ? 'Delete Server' : 'Cannot delete - has active services'}

+

+ {canDelete + ? "Delete Server" + : "Cannot delete - has active services"} +

diff --git a/apps/dokploy/components/dashboard/settings/web-server/terminal-modal.tsx b/apps/dokploy/components/dashboard/settings/web-server/terminal-modal.tsx index 0bea05fc0..2647e1dc0 100644 --- a/apps/dokploy/components/dashboard/settings/web-server/terminal-modal.tsx +++ b/apps/dokploy/components/dashboard/settings/web-server/terminal-modal.tsx @@ -27,7 +27,11 @@ interface Props { asButton?: boolean; } -export const TerminalModal = ({ children, serverId, asButton = false }: Props) => { +export const TerminalModal = ({ + children, + serverId, + asButton = false, +}: Props) => { const [terminalKey, setTerminalKey] = useState(getTerminalKey()); const [isOpen, setIsOpen] = useState(false); const isLocalServer = serverId === "local"; @@ -47,9 +51,7 @@ export const TerminalModal = ({ children, serverId, asButton = false }: Props) = return ( {asButton ? ( - - {children} - + {children} ) : (