feat(ui): implement bulk delete dialog for services in project dashboard

This commit is contained in:
Mauricio Siu
2025-08-23 16:06:25 -06:00
parent ba1f4dbd3a
commit 2e3d4f1021

View File

@@ -290,6 +290,8 @@ const Project = (
const [openCombobox, setOpenCombobox] = useState(false);
const [selectedServices, setSelectedServices] = useState<string[]>([]);
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)}
>
<Button
variant="ghost"
@@ -975,6 +977,113 @@ const Project = (
</DialogFooter>
</DialogContent>
</Dialog>
{/* Bulk Delete Dialog */}
<Dialog
open={isBulkDeleteDialogOpen}
onOpenChange={setIsBulkDeleteDialogOpen}
>
<DialogContent>
<DialogHeader>
<DialogTitle>Delete Services</DialogTitle>
<DialogDescription>
Are you sure you want to delete{" "}
{selectedServices.length} service
{selectedServices.length !== 1 ? "s" : ""}?
This action cannot be undone.
</DialogDescription>
</DialogHeader>
<div className="space-y-4">
{/* Show services to be deleted */}
<div className="max-h-40 overflow-y-auto space-y-2">
{selectedServices.map((serviceId) => {
const service = filteredServices.find(
(s) => s.id === serviceId,
);
return service ? (
<div
key={serviceId}
className="flex items-center space-x-2 text-sm"
>
<span className="px-2 py-1 text-xs bg-secondary rounded">
{service.type}
</span>
<span>{service.name}</span>
</div>
) : null;
})}
</div>
{/* Volume deletion option for compose services */}
{(() => {
const servicesWithVolumeSupport =
selectedServices.filter((serviceId) => {
const service = filteredServices.find(
(s) => s.id === serviceId,
);
// Currently only compose services support volume deletion
return service?.type === "compose";
});
if (servicesWithVolumeSupport.length === 0)
return null;
return (
<div className="space-y-2">
<div className="flex items-center space-x-2">
<Checkbox
id="deleteVolumes"
checked={deleteVolumes}
onCheckedChange={(checked) =>
setDeleteVolumes(checked === true)
}
/>
<label
htmlFor="deleteVolumes"
className="text-sm font-medium"
>
Delete volumes associated with
services
</label>
</div>
<p className="text-xs text-muted-foreground">
Volume deletion is available for:{" "}
{servicesWithVolumeSupport.length}{" "}
compose service
{servicesWithVolumeSupport.length !== 1
? "s"
: ""}
</p>
</div>
);
})()}
</div>
<DialogFooter>
<Button
variant="outline"
onClick={() => {
setIsBulkDeleteDialogOpen(false);
setDeleteVolumes(false); // Reset checkbox
}}
>
Cancel
</Button>
<Button
variant="destructive"
onClick={() => {
handleBulkDelete(deleteVolumes);
setIsBulkDeleteDialogOpen(false);
setDeleteVolumes(false); // Reset checkbox
}}
disabled={isBulkActionLoading}
>
Delete Services
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</DropdownMenuContent>
</DropdownMenu>
</div>