refactor: add back delete with confirmation

This commit is contained in:
Mauricio Siu 2025-01-26 17:40:28 -06:00
parent 13551f6065
commit eeb97645b5
8 changed files with 86 additions and 206 deletions

View File

@ -20,6 +20,7 @@ import {
} from "@/components/ui/form"; } from "@/components/ui/form";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { api } from "@/utils/api"; import { api } from "@/utils/api";
import type { ServiceType } from "@dokploy/server/db/schema";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { Copy, Trash2 } from "lucide-react"; import { Copy, Trash2 } from "lucide-react";
import { TrashIcon } from "lucide-react"; import { TrashIcon } from "lucide-react";
@ -39,16 +40,42 @@ const deleteComposeSchema = z.object({
type DeleteCompose = z.infer<typeof deleteComposeSchema>; type DeleteCompose = z.infer<typeof deleteComposeSchema>;
interface Props { interface Props {
composeId: string; id: string;
type: ServiceType | "application";
} }
export const DeleteCompose = ({ composeId }: Props) => { export const DeleteService = ({ id, type }: Props) => {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const { mutateAsync, isLoading } = api.compose.delete.useMutation();
const { data } = api.compose.one.useQuery( const queryMap = {
{ composeId }, postgres: () =>
{ enabled: !!composeId }, api.postgres.one.useQuery({ postgresId: id }, { enabled: !!id }),
); redis: () => api.redis.one.useQuery({ redisId: id }, { enabled: !!id }),
mysql: () => api.mysql.one.useQuery({ mysqlId: id }, { enabled: !!id }),
mariadb: () =>
api.mariadb.one.useQuery({ mariadbId: id }, { enabled: !!id }),
application: () =>
api.application.one.useQuery({ applicationId: id }, { enabled: !!id }),
mongo: () => api.mongo.one.useQuery({ mongoId: id }, { enabled: !!id }),
compose: () =>
api.compose.one.useQuery({ composeId: id }, { enabled: !!id }),
};
const { data, refetch } = queryMap[type]
? queryMap[type]()
: api.mongo.one.useQuery({ mongoId: id }, { enabled: !!id });
const mutationMap = {
postgres: () => api.postgres.remove.useMutation(),
redis: () => api.redis.remove.useMutation(),
mysql: () => api.mysql.remove.useMutation(),
mariadb: () => api.mariadb.remove.useMutation(),
application: () => api.application.delete.useMutation(),
mongo: () => api.mongo.remove.useMutation(),
compose: () => api.compose.delete.useMutation(),
};
const { mutateAsync, isLoading } = mutationMap[type]
? mutationMap[type]()
: api.mongo.remove.useMutation();
const { push } = useRouter(); const { push } = useRouter();
const form = useForm<DeleteCompose>({ const form = useForm<DeleteCompose>({
defaultValues: { defaultValues: {
@ -62,14 +89,23 @@ export const DeleteCompose = ({ composeId }: Props) => {
const expectedName = `${data?.name}/${data?.appName}`; const expectedName = `${data?.name}/${data?.appName}`;
if (formData.projectName === expectedName) { if (formData.projectName === expectedName) {
const { deleteVolumes } = formData; const { deleteVolumes } = formData;
await mutateAsync({ composeId, deleteVolumes }) await mutateAsync({
mongoId: id || "",
postgresId: id || "",
redisId: id || "",
mysqlId: id || "",
mariadbId: id || "",
applicationId: id || "",
composeId: id || "",
deleteVolumes,
})
.then((result) => { .then((result) => {
push(`/dashboard/project/${result?.projectId}`); push(`/dashboard/project/${result?.projectId}`);
toast.success("Compose deleted successfully"); toast.success("deleted successfully");
setIsOpen(false); setIsOpen(false);
}) })
.catch(() => { .catch(() => {
toast.error("Error deleting the compose"); toast.error("Error deleting the service");
}); });
} else { } else {
form.setError("projectName", { form.setError("projectName", {
@ -95,8 +131,8 @@ export const DeleteCompose = ({ composeId }: Props) => {
<DialogTitle>Are you absolutely sure?</DialogTitle> <DialogTitle>Are you absolutely sure?</DialogTitle>
<DialogDescription> <DialogDescription>
This action cannot be undone. This will permanently delete the This action cannot be undone. This will permanently delete the
compose. If you are sure please enter the compose name to delete service. If you are sure please enter the service name to delete
this compose. this service.
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<div className="grid gap-4"> <div className="grid gap-4">
@ -142,27 +178,29 @@ export const DeleteCompose = ({ composeId }: Props) => {
</FormItem> </FormItem>
)} )}
/> />
<FormField {type === "compose" && (
control={form.control} <FormField
name="deleteVolumes" control={form.control}
render={({ field }) => ( name="deleteVolumes"
<FormItem> render={({ field }) => (
<div className="flex items-center"> <FormItem>
<FormControl> <div className="flex items-center">
<Checkbox <FormControl>
checked={field.value} <Checkbox
onCheckedChange={field.onChange} checked={field.value}
/> onCheckedChange={field.onChange}
</FormControl> />
</FormControl>
<FormLabel className="ml-2"> <FormLabel className="ml-2">
Delete volumes associated with this compose Delete volumes associated with this compose
</FormLabel> </FormLabel>
</div> </div>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
)} )}
/> />
)}
</form> </form>
</Form> </Form>
</div> </div>

View File

@ -13,6 +13,7 @@ import { ShowGeneralApplication } from "@/components/dashboard/application/gener
import { ShowDockerLogs } from "@/components/dashboard/application/logs/show"; import { ShowDockerLogs } from "@/components/dashboard/application/logs/show";
import { ShowPreviewDeployments } from "@/components/dashboard/application/preview-deployments/show-preview-deployments"; import { ShowPreviewDeployments } from "@/components/dashboard/application/preview-deployments/show-preview-deployments";
import { UpdateApplication } from "@/components/dashboard/application/update-application"; import { UpdateApplication } from "@/components/dashboard/application/update-application";
import { DeleteService } from "@/components/dashboard/compose/delete-service";
import { DockerMonitoring } from "@/components/dashboard/monitoring/docker/show"; import { DockerMonitoring } from "@/components/dashboard/monitoring/docker/show";
import { ProjectLayout } from "@/components/layouts/project-layout"; import { ProjectLayout } from "@/components/layouts/project-layout";
import { BreadcrumbSidebar } from "@/components/shared/breadcrumb-sidebar"; import { BreadcrumbSidebar } from "@/components/shared/breadcrumb-sidebar";
@ -82,8 +83,6 @@ const Service = (
}, },
); );
const { mutateAsync, isLoading: isRemoving } =
api.application.delete.useMutation();
const { data: auth } = api.auth.get.useQuery(); const { data: auth } = api.auth.get.useQuery();
const { data: user } = api.user.byAuthId.useQuery( const { data: user } = api.user.byAuthId.useQuery(
{ {
@ -177,34 +176,7 @@ const Service = (
<div className="flex flex-row gap-2 justify-end"> <div className="flex flex-row gap-2 justify-end">
<UpdateApplication applicationId={applicationId} /> <UpdateApplication applicationId={applicationId} />
{(auth?.rol === "admin" || user?.canDeleteServices) && ( {(auth?.rol === "admin" || user?.canDeleteServices) && (
<DialogAction <DeleteService id={applicationId} type="application" />
title="Delete Application"
description="Are you sure you want to delete this application?"
type="destructive"
onClick={async () => {
await mutateAsync({
applicationId: applicationId,
})
.then(() => {
router.push(
`/dashboard/project/${data?.projectId}`,
);
toast.success("Application deleted successfully");
})
.catch(() => {
toast.error("Error deleting application");
});
}}
>
<Button
variant="ghost"
size="icon"
className="group hover:bg-red-500/10 "
isLoading={isRemoving}
>
<Trash2 className="size-4 text-primary group-hover:text-red-500" />
</Button>
</DialogAction>
)} )}
</div> </div>
</div> </div>

View File

@ -1,7 +1,7 @@
import { ShowVolumes } from "@/components/dashboard/application/advanced/volumes/show-volumes"; import { ShowVolumes } from "@/components/dashboard/application/advanced/volumes/show-volumes";
import { ShowEnvironment } from "@/components/dashboard/application/environment/show-enviroment"; import { ShowEnvironment } from "@/components/dashboard/application/environment/show-enviroment";
import { AddCommandCompose } from "@/components/dashboard/compose/advanced/add-command"; import { AddCommandCompose } from "@/components/dashboard/compose/advanced/add-command";
import { DeleteCompose } from "@/components/dashboard/compose/delete-compose"; import { DeleteService } from "@/components/dashboard/compose/delete-service";
import { ShowDeploymentsCompose } from "@/components/dashboard/compose/deployments/show-deployments-compose"; import { ShowDeploymentsCompose } from "@/components/dashboard/compose/deployments/show-deployments-compose";
import { ShowDomainsCompose } from "@/components/dashboard/compose/domains/show-domains"; import { ShowDomainsCompose } from "@/components/dashboard/compose/domains/show-domains";
import { ShowGeneralCompose } from "@/components/dashboard/compose/general/show"; import { ShowGeneralCompose } from "@/components/dashboard/compose/general/show";
@ -168,7 +168,7 @@ const Service = (
<UpdateCompose composeId={composeId} /> <UpdateCompose composeId={composeId} />
{(auth?.rol === "admin" || user?.canDeleteServices) && ( {(auth?.rol === "admin" || user?.canDeleteServices) && (
<DeleteCompose composeId={composeId} /> <DeleteService id={composeId} type="compose" />
)} )}
</div> </div>
</div> </div>

View File

@ -2,6 +2,7 @@ import { ShowResources } from "@/components/dashboard/application/advanced/show-
import { ShowVolumes } from "@/components/dashboard/application/advanced/volumes/show-volumes"; import { ShowVolumes } from "@/components/dashboard/application/advanced/volumes/show-volumes";
import { ShowEnvironment } from "@/components/dashboard/application/environment/show-enviroment"; import { ShowEnvironment } from "@/components/dashboard/application/environment/show-enviroment";
import { ShowDockerLogs } from "@/components/dashboard/application/logs/show"; import { ShowDockerLogs } from "@/components/dashboard/application/logs/show";
import { DeleteService } from "@/components/dashboard/compose/delete-service";
import { ShowBackups } from "@/components/dashboard/database/backups/show-backups"; import { ShowBackups } from "@/components/dashboard/database/backups/show-backups";
import { ShowExternalMariadbCredentials } from "@/components/dashboard/mariadb/general/show-external-mariadb-credentials"; import { ShowExternalMariadbCredentials } from "@/components/dashboard/mariadb/general/show-external-mariadb-credentials";
import { ShowGeneralMariadb } from "@/components/dashboard/mariadb/general/show-general-mariadb"; import { ShowGeneralMariadb } from "@/components/dashboard/mariadb/general/show-general-mariadb";
@ -67,8 +68,7 @@ const Mariadb = (
enabled: !!auth?.id && auth?.rol === "user", enabled: !!auth?.id && auth?.rol === "user",
}, },
); );
const { mutateAsync: remove, isLoading: isRemoving } =
api.mariadb.remove.useMutation();
return ( return (
<div className="pb-10"> <div className="pb-10">
<BreadcrumbSidebar <BreadcrumbSidebar
@ -148,35 +148,10 @@ const Mariadb = (
</TooltipProvider> </TooltipProvider>
)} )}
</div> </div>
<div className="flex flex-row gap-2"> <div className="flex flex-row gap-2 justify-end">
<UpdateMariadb mariadbId={mariadbId} /> <UpdateMariadb mariadbId={mariadbId} />
{(auth?.rol === "admin" || user?.canDeleteServices) && ( {(auth?.rol === "admin" || user?.canDeleteServices) && (
<DialogAction <DeleteService id={mariadbId} type="mariadb" />
title="Remove Mariadb"
description="Are you sure you want to delete this mariadb?"
type="destructive"
onClick={async () => {
await remove({ mariadbId })
.then(() => {
router.push(
`/dashboard/project/${data?.projectId}`,
);
toast.success("Mariadb deleted successfully");
})
.catch(() => {
toast.error("Error deleting the mariadb");
});
}}
>
<Button
variant="ghost"
size="icon"
className="group hover:bg-red-500/10 "
isLoading={isRemoving}
>
<Trash2 className="size-4 text-primary group-hover:text-red-500" />
</Button>
</DialogAction>
)} )}
</div> </div>
</div> </div>

View File

@ -2,6 +2,7 @@ import { ShowResources } from "@/components/dashboard/application/advanced/show-
import { ShowVolumes } from "@/components/dashboard/application/advanced/volumes/show-volumes"; import { ShowVolumes } from "@/components/dashboard/application/advanced/volumes/show-volumes";
import { ShowEnvironment } from "@/components/dashboard/application/environment/show-enviroment"; import { ShowEnvironment } from "@/components/dashboard/application/environment/show-enviroment";
import { ShowDockerLogs } from "@/components/dashboard/application/logs/show"; import { ShowDockerLogs } from "@/components/dashboard/application/logs/show";
import { DeleteService } from "@/components/dashboard/compose/delete-service";
import { ShowBackups } from "@/components/dashboard/database/backups/show-backups"; import { ShowBackups } from "@/components/dashboard/database/backups/show-backups";
import { ShowExternalMongoCredentials } from "@/components/dashboard/mongo/general/show-external-mongo-credentials"; import { ShowExternalMongoCredentials } from "@/components/dashboard/mongo/general/show-external-mongo-credentials";
import { ShowGeneralMongo } from "@/components/dashboard/mongo/general/show-general-mongo"; import { ShowGeneralMongo } from "@/components/dashboard/mongo/general/show-general-mongo";
@ -69,8 +70,6 @@ const Mongo = (
enabled: !!auth?.id && auth?.rol === "user", enabled: !!auth?.id && auth?.rol === "user",
}, },
); );
const { mutateAsync: remove, isLoading: isRemoving } =
api.mongo.remove.useMutation();
return ( return (
<div className="pb-10"> <div className="pb-10">
@ -155,32 +154,7 @@ const Mongo = (
<div className="flex flex-row gap-2 justify-end"> <div className="flex flex-row gap-2 justify-end">
<UpdateMongo mongoId={mongoId} /> <UpdateMongo mongoId={mongoId} />
{(auth?.rol === "admin" || user?.canDeleteServices) && ( {(auth?.rol === "admin" || user?.canDeleteServices) && (
<DialogAction <DeleteService id={mongoId} type="mongo" />
title="Remove mongo"
description="Are you sure you want to delete this mongo?"
type="destructive"
onClick={async () => {
await remove({ mongoId })
.then(() => {
router.push(
`/dashboard/project/${data?.projectId}`,
);
toast.success("Mongo deleted successfully");
})
.catch(() => {
toast.error("Error deleting the mongo");
});
}}
>
<Button
variant="ghost"
size="icon"
className="group hover:bg-red-500/10 "
isLoading={isRemoving}
>
<Trash2 className="size-4 text-primary group-hover:text-red-500" />
</Button>
</DialogAction>
)} )}
</div> </div>
</div> </div>

View File

@ -2,6 +2,7 @@ import { ShowResources } from "@/components/dashboard/application/advanced/show-
import { ShowVolumes } from "@/components/dashboard/application/advanced/volumes/show-volumes"; import { ShowVolumes } from "@/components/dashboard/application/advanced/volumes/show-volumes";
import { ShowEnvironment } from "@/components/dashboard/application/environment/show-enviroment"; import { ShowEnvironment } from "@/components/dashboard/application/environment/show-enviroment";
import { ShowDockerLogs } from "@/components/dashboard/application/logs/show"; import { ShowDockerLogs } from "@/components/dashboard/application/logs/show";
import { DeleteService } from "@/components/dashboard/compose/delete-service";
import { ShowBackups } from "@/components/dashboard/database/backups/show-backups"; import { ShowBackups } from "@/components/dashboard/database/backups/show-backups";
import { DockerMonitoring } from "@/components/dashboard/monitoring/docker/show"; import { DockerMonitoring } from "@/components/dashboard/monitoring/docker/show";
import { ShowExternalMysqlCredentials } from "@/components/dashboard/mysql/general/show-external-mysql-credentials"; import { ShowExternalMysqlCredentials } from "@/components/dashboard/mysql/general/show-external-mysql-credentials";
@ -68,8 +69,6 @@ const MySql = (
}, },
); );
const { mutateAsync: remove, isLoading: isRemoving } =
api.mysql.remove.useMutation();
return ( return (
<div className="pb-10"> <div className="pb-10">
<BreadcrumbSidebar <BreadcrumbSidebar
@ -154,32 +153,7 @@ const MySql = (
<div className="flex flex-row gap-2 justify-end"> <div className="flex flex-row gap-2 justify-end">
<UpdateMysql mysqlId={mysqlId} /> <UpdateMysql mysqlId={mysqlId} />
{(auth?.rol === "admin" || user?.canDeleteServices) && ( {(auth?.rol === "admin" || user?.canDeleteServices) && (
<DialogAction <DeleteService id={mysqlId} type="mysql" />
title="Remove Mysql"
description="Are you sure you want to delete this mysql?"
type="destructive"
onClick={async () => {
await remove({ mysqlId })
.then(() => {
router.push(
`/dashboard/project/${data?.projectId}`,
);
toast.success("Mysql deleted successfully");
})
.catch(() => {
toast.error("Error deleting the mysql");
});
}}
>
<Button
variant="ghost"
size="icon"
className="group hover:bg-red-500/10 "
isLoading={isRemoving}
>
<Trash2 className="size-4 text-primary group-hover:text-red-500" />
</Button>
</DialogAction>
)} )}
</div> </div>
</div> </div>

View File

@ -2,6 +2,7 @@ import { ShowResources } from "@/components/dashboard/application/advanced/show-
import { ShowVolumes } from "@/components/dashboard/application/advanced/volumes/show-volumes"; import { ShowVolumes } from "@/components/dashboard/application/advanced/volumes/show-volumes";
import { ShowEnvironment } from "@/components/dashboard/application/environment/show-enviroment"; import { ShowEnvironment } from "@/components/dashboard/application/environment/show-enviroment";
import { ShowDockerLogs } from "@/components/dashboard/application/logs/show"; import { ShowDockerLogs } from "@/components/dashboard/application/logs/show";
import { DeleteService } from "@/components/dashboard/compose/delete-service";
import { ShowBackups } from "@/components/dashboard/database/backups/show-backups"; import { ShowBackups } from "@/components/dashboard/database/backups/show-backups";
import { DockerMonitoring } from "@/components/dashboard/monitoring/docker/show"; import { DockerMonitoring } from "@/components/dashboard/monitoring/docker/show";
import { ShowCustomCommand } from "@/components/dashboard/postgres/advanced/show-custom-command"; import { ShowCustomCommand } from "@/components/dashboard/postgres/advanced/show-custom-command";
@ -70,9 +71,6 @@ const Postgresql = (
}, },
); );
const { mutateAsync: remove, isLoading: isRemoving } =
api.postgres.remove.useMutation();
return ( return (
<div className="pb-10"> <div className="pb-10">
<BreadcrumbSidebar <BreadcrumbSidebar
@ -156,32 +154,7 @@ const Postgresql = (
<div className="flex flex-row gap-2 justify-end"> <div className="flex flex-row gap-2 justify-end">
<UpdatePostgres postgresId={postgresId} /> <UpdatePostgres postgresId={postgresId} />
{(auth?.rol === "admin" || user?.canDeleteServices) && ( {(auth?.rol === "admin" || user?.canDeleteServices) && (
<DialogAction <DeleteService id={postgresId} type="postgres" />
title="Remove Postgres"
description="Are you sure you want to delete this postgres?"
type="destructive"
onClick={async () => {
await remove({ postgresId })
.then(() => {
router.push(
`/dashboard/project/${data?.projectId}`,
);
toast.success("Postgres deleted successfully");
})
.catch(() => {
toast.error("Error deleting the postgres");
});
}}
>
<Button
variant="ghost"
size="icon"
className="group hover:bg-red-500/10 "
isLoading={isRemoving}
>
<Trash2 className="size-4 text-primary group-hover:text-red-500" />
</Button>
</DialogAction>
)} )}
</div> </div>
</div> </div>

View File

@ -2,6 +2,7 @@ import { ShowResources } from "@/components/dashboard/application/advanced/show-
import { ShowVolumes } from "@/components/dashboard/application/advanced/volumes/show-volumes"; import { ShowVolumes } from "@/components/dashboard/application/advanced/volumes/show-volumes";
import { ShowEnvironment } from "@/components/dashboard/application/environment/show-enviroment"; import { ShowEnvironment } from "@/components/dashboard/application/environment/show-enviroment";
import { ShowDockerLogs } from "@/components/dashboard/application/logs/show"; import { ShowDockerLogs } from "@/components/dashboard/application/logs/show";
import { DeleteService } from "@/components/dashboard/compose/delete-service";
import { DockerMonitoring } from "@/components/dashboard/monitoring/docker/show"; import { DockerMonitoring } from "@/components/dashboard/monitoring/docker/show";
import { ShowCustomCommand } from "@/components/dashboard/postgres/advanced/show-custom-command"; import { ShowCustomCommand } from "@/components/dashboard/postgres/advanced/show-custom-command";
import { ShowExternalRedisCredentials } from "@/components/dashboard/redis/general/show-external-redis-credentials"; import { ShowExternalRedisCredentials } from "@/components/dashboard/redis/general/show-external-redis-credentials";
@ -68,8 +69,6 @@ const Redis = (
}, },
); );
const { mutateAsync: remove, isLoading: isRemoving } =
api.redis.remove.useMutation();
return ( return (
<div className="pb-10"> <div className="pb-10">
<BreadcrumbSidebar <BreadcrumbSidebar
@ -153,32 +152,7 @@ const Redis = (
<div className="flex flex-row gap-2 justify-end"> <div className="flex flex-row gap-2 justify-end">
<UpdateRedis redisId={redisId} /> <UpdateRedis redisId={redisId} />
{(auth?.rol === "admin" || user?.canDeleteServices) && ( {(auth?.rol === "admin" || user?.canDeleteServices) && (
<DialogAction <DeleteService id={redisId} type="redis" />
title="Remove Redis"
description="Are you sure you want to delete this redis?"
type="destructive"
onClick={async () => {
await remove({ redisId })
.then(() => {
router.push(
`/dashboard/project/${data?.projectId}`,
);
toast.success("Redis deleted successfully");
})
.catch(() => {
toast.error("Error deleting the redis");
});
}}
>
<Button
variant="ghost"
size="icon"
className="group hover:bg-red-500/10 "
isLoading={isRemoving}
>
<Trash2 className="size-4 text-primary group-hover:text-red-500" />
</Button>
</DialogAction>
)} )}
</div> </div>
</div> </div>