From 1c25ab4303001570f159dc45d6a3d4611a8ce999 Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Tue, 17 Feb 2026 01:47:27 -0600 Subject: [PATCH] feat(patch-dialog): add EditPatchDialog component for editing patches - Introduced the EditPatchDialog component to facilitate patch editing within the dashboard. - Integrated the dialog into the ShowPatches component, allowing users to edit patches directly from the list view. - Enhanced user experience with loading indicators and success/error notifications during patch updates. - Updated the UI to ensure consistent styling and behavior across patch management features. --- .../application/patches/edit-patch-dialog.tsx | 102 ++++++++++++++++++ .../application/patches/show-patches.tsx | 49 +++++---- apps/dokploy/server/api/routers/patch.ts | 2 - 3 files changed, 131 insertions(+), 22 deletions(-) create mode 100644 apps/dokploy/components/dashboard/application/patches/edit-patch-dialog.tsx diff --git a/apps/dokploy/components/dashboard/application/patches/edit-patch-dialog.tsx b/apps/dokploy/components/dashboard/application/patches/edit-patch-dialog.tsx new file mode 100644 index 000000000..284b62d10 --- /dev/null +++ b/apps/dokploy/components/dashboard/application/patches/edit-patch-dialog.tsx @@ -0,0 +1,102 @@ +import { Loader2, Pencil } from "lucide-react"; +import { useEffect, useState } from "react"; +import { toast } from "sonner"; +import { CodeEditor } from "@/components/shared/code-editor"; +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { api } from "@/utils/api"; + +interface Props { + patchId: string; + entityId: string; + type: "application" | "compose"; + onSuccess?: () => void; +} + +export const EditPatchDialog = ({ + patchId, + entityId, + type, + onSuccess, +}: Props) => { + const { data: patch, isLoading: isPatchLoading } = api.patch.one.useQuery( + { patchId }, + { enabled: !!patchId }, + ); + const [content, setContent] = useState(""); + + useEffect(() => { + if (patch) { + setContent(patch.content); + } + }, [patch]); + + const utils = api.useUtils(); + const updatePatch = api.patch.update.useMutation(); + + const handleSave = () => { + updatePatch + .mutateAsync({ patchId, content }) + .then(() => { + toast.success("Patch saved"); + utils.patch.byEntityId.invalidate({ id: entityId, type }); + onSuccess?.(); + }) + .catch((err) => { + toast.error(err.message); + }); + }; + + return ( + + + + + + + Edit Patch + + {patch ? `Editing: ${patch.filePath}` : "Loading patch..."} + + + {isPatchLoading ? ( +
+ +
+ ) : ( +
+ setContent(value ?? "")} + className="h-[400px] w-full" + wrapperClassName="h-[400px]" + lineWrapping + /> +
+ )} + + + + + + +
+
+ ); +}; diff --git a/apps/dokploy/components/dashboard/application/patches/show-patches.tsx b/apps/dokploy/components/dashboard/application/patches/show-patches.tsx index 2a090e39f..eace7d820 100644 --- a/apps/dokploy/components/dashboard/application/patches/show-patches.tsx +++ b/apps/dokploy/components/dashboard/application/patches/show-patches.tsx @@ -19,6 +19,7 @@ import { TableRow, } from "@/components/ui/table"; import { api } from "@/utils/api"; +import { EditPatchDialog } from "./edit-patch-dialog"; import { PatchEditor } from "./patch-editor"; interface Props { @@ -82,7 +83,7 @@ export const ShowPatches = ({ id, type }: Props) => { return ( - +
Patches @@ -129,7 +130,7 @@ export const ShowPatches = ({ id, type }: Props) => { File Path Enabled - Actions + Actions @@ -167,25 +168,33 @@ export const ShowPatches = ({ id, type }: Props) => { /> - + }} + title="Delete patch" + > + + +
))} diff --git a/apps/dokploy/server/api/routers/patch.ts b/apps/dokploy/server/api/routers/patch.ts index 1c9eb4fe3..0279c8916 100644 --- a/apps/dokploy/server/api/routers/patch.ts +++ b/apps/dokploy/server/api/routers/patch.ts @@ -29,11 +29,9 @@ import { } from "@/server/db/schema"; export const patchRouter = createTRPCRouter({ - // CRUD Operations create: protectedProcedure .input(apiCreatePatch) .mutation(async ({ input, ctx }) => { - // Verify access if (input.applicationId) { const app = await findApplicationById(input.applicationId); if (