From 49148160b4a9c4366cdf0dc6197b4f8b58f92812 Mon Sep 17 00:00:00 2001 From: Zeno Junior Date: Fri, 24 Jul 2026 16:31:33 -0300 Subject: [PATCH] fix: prevent template dialog from closing when switching browser tabs The template dialog was rendered inside the Create Service DropdownMenuContent. When the browser tab lost and regained focus, Radix dismissed the dropdown (focus moving to the portalled dialog is treated as an outside interaction), unmounting the dialog and its state along with it. Lift the dialog out of the dropdown by making AddTemplate a controlled component and rendering it as a sibling of the DropdownMenu, with the menu item only toggling the open state. This also closes the dropdown properly when the dialog opens instead of keeping it open behind the modal. --- .../dashboard/project/add-template.tsx | 26 +++++++------------ .../environment/[environmentId].tsx | 18 ++++++++++++- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/apps/dokploy/components/dashboard/project/add-template.tsx b/apps/dokploy/components/dashboard/project/add-template.tsx index 44512d93a9..8a437a0ec8 100644 --- a/apps/dokploy/components/dashboard/project/add-template.tsx +++ b/apps/dokploy/components/dashboard/project/add-template.tsx @@ -8,7 +8,6 @@ import { LayoutGrid, List, Loader2, - PuzzleIcon, SearchIcon, } from "lucide-react"; import Link from "next/link"; @@ -42,9 +41,7 @@ import { DialogDescription, DialogHeader, DialogTitle, - DialogTrigger, } from "@/components/ui/dialog"; -import { DropdownMenuItem } from "@/components/ui/dropdown-menu"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { @@ -76,11 +73,17 @@ const TEMPLATE_BASE_URL_KEY = "dokploy_template_base_url"; interface Props { environmentId: string; baseUrl?: string; + open: boolean; + onOpenChange: (open: boolean) => void; } -export const AddTemplate = ({ environmentId, baseUrl }: Props) => { +export const AddTemplate = ({ + environmentId, + baseUrl, + open, + onOpenChange, +}: Props) => { const [query, setQuery] = useState(""); - const [open, setOpen] = useState(false); const [viewMode, setViewMode] = useState<"detailed" | "icon">("detailed"); const [selectedTags, setSelectedTags] = useState([]); const [showBookmarksOnly, setShowBookmarksOnly] = useState(false); @@ -196,16 +199,7 @@ export const AddTemplate = ({ environmentId, baseUrl }: Props) => { }; return ( - - - e.preventDefault()} - > - - Template - - +
@@ -618,7 +612,7 @@ export const AddTemplate = ({ environmentId, baseUrl }: Props) => { utils.environment.one.invalidate({ environmentId, }); - setOpen(false); + onOpenChange(false); return `${template.name} template created successfully`; }, error: () => { diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId].tsx index 4699414468..1160c4f2e4 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId].tsx @@ -12,6 +12,7 @@ import { Loader2, Play, PlusIcon, + PuzzleIcon, RefreshCw, Search, ServerIcon, @@ -90,6 +91,7 @@ import { import { DropdownMenu, DropdownMenuContent, + DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, @@ -431,6 +433,7 @@ const EnvironmentPage = ( const [openCombobox, setOpenCombobox] = useState(false); const [selectedServices, setSelectedServices] = useState([]); const [isDropdownOpen, setIsDropdownOpen] = useState(false); + const [isTemplateDialogOpen, setIsTemplateDialogOpen] = useState(false); const [isBulkDeleteDialogOpen, setIsBulkDeleteDialogOpen] = useState(false); const [deleteVolumes, setDeleteVolumes] = useState(false); const [selectedServerId, setSelectedServerId] = useState("all"); @@ -1089,7 +1092,13 @@ const EnvironmentPage = ( projectName={projectData?.name} environmentId={environmentId} /> - + setIsTemplateDialogOpen(true)} + > + + Template + )} + {permissions?.service.create && ( + + )}