Merge pull request #2450 from Dokploy/2403-no-delete-volumes-option-when-deleting-in-bulk

feat(ui): add bulk deploy functionality for services in project dashb…
This commit is contained in:
Mauricio Siu
2025-08-23 16:59:03 -06:00
committed by GitHub

View File

@@ -10,6 +10,7 @@ import {
FolderInput,
GlobeIcon,
Loader2,
Play,
PlusIcon,
Search,
ServerIcon,
@@ -315,6 +316,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 = {
@@ -322,6 +324,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 = {
@@ -329,6 +332,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 = {
@@ -336,6 +340,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 = {
@@ -343,6 +348,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 = {
@@ -350,6 +356,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 = {
@@ -357,6 +364,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 () => {
@@ -589,6 +597,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(
@@ -732,6 +817,24 @@ const Project = (
Start
</Button>
</DialogAction>
<DialogAction
title="Deploy Services"
description={`Are you sure you want to deploy ${selectedServices.length} service${selectedServices.length !== 1 ? "s" : ""}? This will redeploy/restart the selected services.`}
onClick={handleBulkDeploy}
type="default"
disabled={
selectedServices.length === 0 ||
isBulkActionLoading
}
>
<Button
variant="ghost"
className="w-full justify-start"
>
<Play className="mr-2 h-4 w-4" />
Deploy
</Button>
</DialogAction>
<DialogAction
title="Stop Services"
description={`Are you sure you want to stop ${selectedServices.length} services?`}
@@ -904,16 +1007,16 @@ const Project = (
key={serviceId}
className="flex items-center space-x-2 text-sm"
>
<Badge variant="secondary">
<span className="px-2 py-1 text-xs bg-secondary rounded">
{service.type}
</Badge>
</span>
<span>{service.name}</span>
</div>
) : null;
})}
</div>
{/* Volume deletion option for services that support it */}
{/* Volume deletion option for compose services */}
{(() => {
const servicesWithVolumeSupport =
selectedServices.filter((serviceId) => {
@@ -975,7 +1078,7 @@ const Project = (
setIsBulkDeleteDialogOpen(false);
setDeleteVolumes(false); // Reset checkbox
}}
isLoading={isBulkActionLoading}
disabled={isBulkActionLoading}
>
Delete Services
</Button>