From 3910e2241259c0e5f26127c2211834200c378851 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 30 Mar 2025 02:42:35 -0600 Subject: [PATCH] Refactor Duplicate Project component and integrate with project detail page - Updated the DuplicateProject component to accept projectId and selectedServiceIds as props, enhancing its flexibility. - Removed unnecessary state management for service selection within the component. - Integrated the DuplicateProject component directly into the project detail page, allowing for easier access to duplication functionality. - Improved the user interface by utilizing DialogTrigger for initiating the duplication process and displaying selected services more clearly. --- .../dashboard/project/duplicate-project.tsx | 228 +++++++----------- .../pages/dashboard/project/[projectId].tsx | 50 ++-- 2 files changed, 113 insertions(+), 165 deletions(-) diff --git a/apps/dokploy/components/dashboard/project/duplicate-project.tsx b/apps/dokploy/components/dashboard/project/duplicate-project.tsx index 3a475232..038ddcb6 100644 --- a/apps/dokploy/components/dashboard/project/duplicate-project.tsx +++ b/apps/dokploy/components/dashboard/project/duplicate-project.tsx @@ -6,13 +6,11 @@ import { DialogFooter, 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 { Checkbox } from "@/components/ui/checkbox"; import { api } from "@/utils/api"; -import type { findProjectById } from "@dokploy/server"; import { Copy, Loader2 } from "lucide-react"; import { useRouter } from "next/router"; import { useState } from "react"; @@ -36,25 +34,27 @@ export type Services = { status?: "idle" | "running" | "done" | "error"; }; -type Project = Awaited>; - interface DuplicateProjectProps { - project: Project; + projectId: string; services: Services[]; + selectedServiceIds: string[]; } export const DuplicateProject = ({ - project, + projectId, services, + selectedServiceIds, }: DuplicateProjectProps) => { const [open, setOpen] = useState(false); const [name, setName] = useState(""); const [description, setDescription] = useState(""); - const [includeServices, setIncludeServices] = useState(true); - const [selectedServices, setSelectedServices] = useState([]); const utils = api.useUtils(); const router = useRouter(); + const selectedServices = services.filter((service) => + selectedServiceIds.includes(service.id), + ); + const { mutateAsync: duplicateProject, isLoading } = api.project.duplicate.useMutation({ onSuccess: async (newProject) => { @@ -75,140 +75,98 @@ export const DuplicateProject = ({ } await duplicateProject({ - sourceProjectId: project.projectId, + sourceProjectId: projectId, name, description, - includeServices, - selectedServices: includeServices - ? services - .filter((service) => selectedServices.includes(service.id)) - .map((service) => ({ - id: service.id, - type: service.type, - })) - : [], + includeServices: true, + selectedServices: selectedServices.map((service) => ({ + id: service.id, + type: service.type, + })), }); }; return ( - <> - { - e.preventDefault(); - setOpen(true); - }} - > - - Duplicate Project - + { + setOpen(isOpen); + if (!isOpen) { + // Reset form when closing + setName(""); + setDescription(""); + } + }} + > + + + + + + Duplicate Project + + Create a new project with the selected services + + - { - setOpen(isOpen); - if (!isOpen) { - // Reset form when closing - setName(""); - setDescription(""); - setIncludeServices(true); - setSelectedServices([]); - } - }} - > - - - Duplicate Project - - Create a new project with the same configuration - - - -
-
- - setName(e.target.value)} - placeholder="New project name" - /> -
- -
- - setDescription(e.target.value)} - placeholder="Project description (optional)" - /> -
- -
- { - setIncludeServices(checked as boolean); - if (!checked) { - setSelectedServices([]); - } - }} - /> - -
- - {includeServices && services.length > 0 && ( -
- -
- {services.map((service) => ( -
- { - setSelectedServices((prev) => - checked - ? [...prev, service.id] - : prev.filter((id) => id !== service.id), - ); - }} - /> - -
- ))} -
-
- )} +
+
+ + setName(e.target.value)} + placeholder="New project name" + />
- - - - - -
- +
+ + setDescription(e.target.value)} + placeholder="Project description (optional)" + /> +
+ +
+ +
+ {selectedServices.map((service) => ( +
+ + {service.name} ({service.type}) + +
+ ))} +
+
+ + + + + + +
+
); }; diff --git a/apps/dokploy/pages/dashboard/project/[projectId].tsx b/apps/dokploy/pages/dashboard/project/[projectId].tsx index a9ffe9c7..e3cfce16 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId].tsx @@ -78,7 +78,6 @@ import { FolderInput, GlobeIcon, Loader2, - MoreHorizontal, PlusIcon, Search, Trash2, @@ -595,22 +594,6 @@ const Project = ( - {auth?.role === "owner" && ( - - - - - - - - - )} @@ -688,20 +671,27 @@ const Project = ( {(auth?.role === "owner" || auth?.canDeleteServices) && ( - - - + + + + )}