feat(services): add bulk delete functionality for services

- Implement bulk delete feature for applications, compose, and various database services
- Add delete mutation endpoints for each service type
- Provide user-friendly bulk delete action with error handling and success notifications
- Integrate Trash2 icon for delete action in bulk service management
This commit is contained in:
Mauricio Siu 2025-03-08 18:43:37 -06:00
parent bf0668c319
commit 7dda252b7c

View File

@ -65,6 +65,7 @@ import {
PlusIcon,
Search,
X,
Trash2,
} from "lucide-react";
import type {
GetServerSidePropsContext,
@ -276,30 +277,37 @@ const Project = (
start: api.compose.start.useMutation(),
stop: api.compose.stop.useMutation(),
move: api.compose.move.useMutation(),
delete: api.compose.delete.useMutation(),
};
const applicationActions = {
move: api.application.move.useMutation(),
delete: api.application.delete.useMutation(),
};
const postgresActions = {
move: api.postgres.move.useMutation(),
delete: api.postgres.remove.useMutation(),
};
const mysqlActions = {
move: api.mysql.move.useMutation(),
delete: api.mysql.remove.useMutation(),
};
const mariadbActions = {
move: api.mariadb.move.useMutation(),
delete: api.mariadb.remove.useMutation(),
};
const redisActions = {
move: api.redis.move.useMutation(),
delete: api.redis.remove.useMutation(),
};
const mongoActions = {
move: api.mongo.move.useMutation(),
delete: api.mongo.remove.useMutation(),
};
const handleBulkStart = async () => {
@ -416,6 +424,68 @@ const Project = (
setIsBulkActionLoading(false);
};
const handleBulkDelete = async () => {
let success = 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.delete.mutateAsync({
applicationId: serviceId,
});
break;
case "compose":
await composeActions.delete.mutateAsync({
composeId: serviceId,
deleteVolumes: false,
});
break;
case "postgres":
await postgresActions.delete.mutateAsync({
postgresId: serviceId,
});
break;
case "mysql":
await mysqlActions.delete.mutateAsync({
mysqlId: serviceId,
});
break;
case "mariadb":
await mariadbActions.delete.mutateAsync({
mariadbId: serviceId,
});
break;
case "redis":
await redisActions.delete.mutateAsync({
redisId: serviceId,
});
break;
case "mongo":
await mongoActions.delete.mutateAsync({
mongoId: serviceId,
});
break;
}
success++;
} catch (error) {
toast.error(
`Error deleting service ${serviceId}: ${error instanceof Error ? error.message : "Unknown error"}`,
);
}
}
if (success > 0) {
toast.success(`${success} services deleted successfully`);
refetch();
}
setSelectedServices([]);
setIsDropdownOpen(false);
setIsBulkActionLoading(false);
};
const filteredServices = useMemo(() => {
if (!applications) return [];
return applications.filter(
@ -565,6 +635,20 @@ const Project = (
Stop
</Button>
</DialogAction>
<DialogAction
title="Delete Services"
description={`Are you sure you want to delete ${selectedServices.length} services? This action cannot be undone.`}
type="destructive"
onClick={handleBulkDelete}
>
<Button
variant="ghost"
className="w-full justify-start text-destructive"
>
<Trash2 className="mr-2 h-4 w-4" />
Delete
</Button>
</DialogAction>
<Dialog
open={isMoveDialogOpen}
onOpenChange={setIsMoveDialogOpen}