From ba1f4dbd3a76c0079de2ea28417a5dc243e2f577 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 23 Aug 2025 16:04:13 -0600 Subject: [PATCH 1/2] feat(ui): add bulk deploy functionality for services in project dashboard --- .../pages/dashboard/project/[projectId].tsx | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/apps/dokploy/pages/dashboard/project/[projectId].tsx b/apps/dokploy/pages/dashboard/project/[projectId].tsx index a32bae06f..65f7d064d 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId].tsx @@ -10,6 +10,7 @@ import { FolderInput, GlobeIcon, Loader2, + Play, PlusIcon, Search, ServerIcon, @@ -312,6 +313,7 @@ const Project = ( stop: api.compose.stop.useMutation(), move: api.compose.move.useMutation(), delete: api.compose.delete.useMutation(), + deploy: api.compose.deploy.useMutation(), }; const applicationActions = { @@ -319,6 +321,7 @@ const Project = ( stop: api.application.stop.useMutation(), move: api.application.move.useMutation(), delete: api.application.delete.useMutation(), + deploy: api.application.deploy.useMutation(), }; const postgresActions = { @@ -326,6 +329,7 @@ const Project = ( stop: api.postgres.stop.useMutation(), move: api.postgres.move.useMutation(), delete: api.postgres.remove.useMutation(), + deploy: api.postgres.deploy.useMutation(), }; const mysqlActions = { @@ -333,6 +337,7 @@ const Project = ( stop: api.mysql.stop.useMutation(), move: api.mysql.move.useMutation(), delete: api.mysql.remove.useMutation(), + deploy: api.mysql.deploy.useMutation(), }; const mariadbActions = { @@ -340,6 +345,7 @@ const Project = ( stop: api.mariadb.stop.useMutation(), move: api.mariadb.move.useMutation(), delete: api.mariadb.remove.useMutation(), + deploy: api.mariadb.deploy.useMutation(), }; const redisActions = { @@ -347,6 +353,7 @@ const Project = ( stop: api.redis.stop.useMutation(), move: api.redis.move.useMutation(), delete: api.redis.remove.useMutation(), + deploy: api.redis.deploy.useMutation(), }; const mongoActions = { @@ -354,6 +361,7 @@ const Project = ( stop: api.mongo.stop.useMutation(), move: api.mongo.move.useMutation(), delete: api.mongo.remove.useMutation(), + deploy: api.mongo.deploy.useMutation(), }; const handleBulkStart = async () => { @@ -586,6 +594,83 @@ const Project = ( setIsBulkActionLoading(false); }; + const handleBulkDeploy = async () => { + let success = 0; + let failed = 0; + setIsBulkActionLoading(true); + + for (const serviceId of selectedServices) { + try { + const service = filteredServices.find((s) => s.id === serviceId); + if (!service) continue; + + switch (service.type) { + case "application": + await applicationActions.deploy.mutateAsync({ + applicationId: serviceId, + }); + break; + case "compose": + await composeActions.deploy.mutateAsync({ + composeId: serviceId, + }); + + break; + case "postgres": + await postgresActions.deploy.mutateAsync({ + postgresId: serviceId, + }); + + break; + case "mysql": + await mysqlActions.deploy.mutateAsync({ + mysqlId: serviceId, + }); + + break; + case "mariadb": + await mariadbActions.deploy.mutateAsync({ + mariadbId: serviceId, + }); + + break; + case "redis": + await redisActions.deploy.mutateAsync({ + redisId: serviceId, + }); + + break; + case "mongo": + await mongoActions.deploy.mutateAsync({ + mongoId: serviceId, + }); + + break; + } + success++; + } catch (error) { + failed++; + toast.error( + `Error deploying service ${serviceId}: ${error instanceof Error ? error.message : "Unknown error"}`, + ); + } + } + if (success > 0) { + toast.success( + `${success} service${success !== 1 ? "s" : ""} deployed successfully`, + ); + } + if (failed > 0) { + toast.error( + `${failed} service${failed !== 1 ? "s" : ""} failed to deploy`, + ); + } + + setSelectedServices([]); + setIsDropdownOpen(false); + setIsBulkActionLoading(false); + }; + const filteredServices = useMemo(() => { if (!applications) return []; const filtered = applications.filter( @@ -729,6 +814,24 @@ const Project = ( Start + + + Date: Sat, 23 Aug 2025 16:06:25 -0600 Subject: [PATCH 2/2] feat(ui): implement bulk delete dialog for services in project dashboard --- .../pages/dashboard/project/[projectId].tsx | 115 +++++++++++++++++- 1 file changed, 112 insertions(+), 3 deletions(-) diff --git a/apps/dokploy/pages/dashboard/project/[projectId].tsx b/apps/dokploy/pages/dashboard/project/[projectId].tsx index 65f7d064d..3182f316a 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId].tsx @@ -290,6 +290,8 @@ const Project = ( const [openCombobox, setOpenCombobox] = useState(false); const [selectedServices, setSelectedServices] = useState([]); const [isDropdownOpen, setIsDropdownOpen] = useState(false); + const [isBulkDeleteDialogOpen, setIsBulkDeleteDialogOpen] = useState(false); + const [deleteVolumes, setDeleteVolumes] = useState(false); const handleSelectAll = () => { if (selectedServices.length === filteredServices.length) { @@ -532,7 +534,7 @@ const Project = ( setIsBulkActionLoading(false); }; - const handleBulkDelete = async () => { + const handleBulkDelete = async (deleteVolumes = false) => { let success = 0; setIsBulkActionLoading(true); for (const serviceId of selectedServices) { @@ -549,7 +551,7 @@ const Project = ( case "compose": await composeActions.delete.mutateAsync({ composeId: serviceId, - deleteVolumes: false, + deleteVolumes, }); break; case "postgres": @@ -879,7 +881,7 @@ const Project = ( disabled={ selectedServicesWithRunningStatus.length > 0 } - onClick={handleBulkDelete} + onClick={() => setIsBulkDeleteDialogOpen(true)} > + + + +