mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
feat: validating app name before delete
This commit is contained in:
@@ -1,63 +1,143 @@
|
|||||||
import {
|
|
||||||
AlertDialog,
|
|
||||||
AlertDialogAction,
|
|
||||||
AlertDialogCancel,
|
|
||||||
AlertDialogContent,
|
|
||||||
AlertDialogDescription,
|
|
||||||
AlertDialogFooter,
|
|
||||||
AlertDialogHeader,
|
|
||||||
AlertDialogTitle,
|
|
||||||
AlertDialogTrigger,
|
|
||||||
} from "@/components/ui/alert-dialog";
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from "@/components/ui/form";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { TrashIcon } from "lucide-react";
|
import { TrashIcon } from "lucide-react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
const deleteApplicationSchema = z.object({
|
||||||
|
projectName: z.string().min(1, {
|
||||||
|
message: "Application name is required",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
type DeleteApplication = z.infer<typeof deleteApplicationSchema>;
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
applicationId: string;
|
applicationId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DeleteApplication = ({ applicationId }: Props) => {
|
export const DeleteApplication = ({ applicationId }: Props) => {
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const { mutateAsync, isLoading } = api.application.delete.useMutation();
|
const { mutateAsync, isLoading } = api.application.delete.useMutation();
|
||||||
|
const { data } = api.application.one.useQuery(
|
||||||
|
{ applicationId },
|
||||||
|
{ enabled: !!applicationId },
|
||||||
|
);
|
||||||
const { push } = useRouter();
|
const { push } = useRouter();
|
||||||
|
const form = useForm<DeleteApplication>({
|
||||||
|
defaultValues: {
|
||||||
|
projectName: "",
|
||||||
|
},
|
||||||
|
resolver: zodResolver(deleteApplicationSchema),
|
||||||
|
});
|
||||||
|
|
||||||
|
const onSubmit = async (formData: DeleteApplication) => {
|
||||||
|
const expectedName = `${data?.name}/${data?.appName}`;
|
||||||
|
if (formData.projectName === expectedName) {
|
||||||
|
await mutateAsync({
|
||||||
|
applicationId,
|
||||||
|
})
|
||||||
|
.then((data) => {
|
||||||
|
push(`/dashboard/project/${data?.projectId}`);
|
||||||
|
toast.success("Application deleted successfully");
|
||||||
|
setIsOpen(false);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
toast.error("Error deleting the application");
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
form.setError("projectName", {
|
||||||
|
message: "Project name does not match",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AlertDialog>
|
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
||||||
<AlertDialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<Button variant="ghost" isLoading={isLoading}>
|
<Button variant="ghost" isLoading={isLoading}>
|
||||||
<TrashIcon className="size-4 text-muted-foreground" />
|
<TrashIcon className="size-4 text-muted-foreground" />
|
||||||
</Button>
|
</Button>
|
||||||
</AlertDialogTrigger>
|
</DialogTrigger>
|
||||||
<AlertDialogContent>
|
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-lg">
|
||||||
<AlertDialogHeader>
|
<DialogHeader>
|
||||||
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
|
<DialogTitle>Are you absolutely sure?</DialogTitle>
|
||||||
<AlertDialogDescription>
|
<DialogDescription>
|
||||||
This action cannot be undone. This will permanently delete the
|
This action cannot be undone. This will permanently delete the
|
||||||
application
|
application. If you are sure please enter the application name to
|
||||||
</AlertDialogDescription>
|
delete this application.
|
||||||
</AlertDialogHeader>
|
</DialogDescription>
|
||||||
<AlertDialogFooter>
|
</DialogHeader>
|
||||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
<div className="grid gap-4">
|
||||||
<AlertDialogAction
|
<Form {...form}>
|
||||||
onClick={async () => {
|
<form
|
||||||
await mutateAsync({
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
applicationId,
|
id="hook-form-delete-application"
|
||||||
})
|
className="grid w-full gap-4"
|
||||||
.then((data) => {
|
>
|
||||||
push(`/dashboard/project/${data?.projectId}`);
|
<FormField
|
||||||
|
control={form.control}
|
||||||
toast.success("Application delete succesfully");
|
name="projectName"
|
||||||
})
|
render={({ field }) => (
|
||||||
.catch(() => {
|
<FormItem>
|
||||||
toast.error("Error to delete Application");
|
<FormLabel>
|
||||||
});
|
To confirm, type "{data?.name}/{data?.appName}" in the box
|
||||||
|
below
|
||||||
|
</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
placeholder="Enter application name to confirm"
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
<DialogFooter>
|
||||||
|
<Button
|
||||||
|
variant="secondary"
|
||||||
|
onClick={() => {
|
||||||
|
setIsOpen(false);
|
||||||
}}
|
}}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
isLoading={isLoading}
|
||||||
|
form="hook-form-delete-application"
|
||||||
|
type="submit"
|
||||||
|
variant="destructive"
|
||||||
>
|
>
|
||||||
Confirm
|
Confirm
|
||||||
</AlertDialogAction>
|
</Button>
|
||||||
</AlertDialogFooter>
|
</DialogFooter>
|
||||||
</AlertDialogContent>
|
</DialogContent>
|
||||||
</AlertDialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,63 +1,141 @@
|
|||||||
import {
|
|
||||||
AlertDialog,
|
|
||||||
AlertDialogAction,
|
|
||||||
AlertDialogCancel,
|
|
||||||
AlertDialogContent,
|
|
||||||
AlertDialogDescription,
|
|
||||||
AlertDialogFooter,
|
|
||||||
AlertDialogHeader,
|
|
||||||
AlertDialogTitle,
|
|
||||||
AlertDialogTrigger,
|
|
||||||
} from "@/components/ui/alert-dialog";
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from "@/components/ui/form";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { TrashIcon } from "lucide-react";
|
import { TrashIcon } from "lucide-react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
const deleteComposeSchema = z.object({
|
||||||
|
projectName: z.string().min(1, {
|
||||||
|
message: "Compose name is required",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
type DeleteCompose = z.infer<typeof deleteComposeSchema>;
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
composeId: string;
|
composeId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DeleteCompose = ({ composeId }: Props) => {
|
export const DeleteCompose = ({ composeId }: Props) => {
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const { mutateAsync, isLoading } = api.compose.delete.useMutation();
|
const { mutateAsync, isLoading } = api.compose.delete.useMutation();
|
||||||
|
const { data } = api.compose.one.useQuery(
|
||||||
|
{ composeId },
|
||||||
|
{ enabled: !!composeId },
|
||||||
|
);
|
||||||
const { push } = useRouter();
|
const { push } = useRouter();
|
||||||
|
const form = useForm<DeleteCompose>({
|
||||||
|
defaultValues: {
|
||||||
|
projectName: "",
|
||||||
|
},
|
||||||
|
resolver: zodResolver(deleteComposeSchema),
|
||||||
|
});
|
||||||
|
|
||||||
|
const onSubmit = async (formData: DeleteCompose) => {
|
||||||
|
const expectedName = `${data?.name}/${data?.appName}`;
|
||||||
|
if (formData.projectName === expectedName) {
|
||||||
|
await mutateAsync({ composeId })
|
||||||
|
.then((result) => {
|
||||||
|
push(`/dashboard/project/${result?.projectId}`);
|
||||||
|
toast.success("Compose deleted successfully");
|
||||||
|
setIsOpen(false);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
toast.error("Error deleting the compose");
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
form.setError("projectName", {
|
||||||
|
message: `Project name must match "${expectedName}"`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AlertDialog>
|
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
||||||
<AlertDialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<Button variant="ghost" isLoading={isLoading}>
|
<Button variant="ghost" isLoading={isLoading}>
|
||||||
<TrashIcon className="size-4 text-muted-foreground" />
|
<TrashIcon className="size-4 text-muted-foreground" />
|
||||||
</Button>
|
</Button>
|
||||||
</AlertDialogTrigger>
|
</DialogTrigger>
|
||||||
<AlertDialogContent>
|
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-lg">
|
||||||
<AlertDialogHeader>
|
<DialogHeader>
|
||||||
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
|
<DialogTitle>Are you absolutely sure?</DialogTitle>
|
||||||
<AlertDialogDescription>
|
<DialogDescription>
|
||||||
This action cannot be undone. This will permanently delete the
|
This action cannot be undone. This will permanently delete the
|
||||||
compose and all its services.
|
compose. If you are sure please enter the compose name to delete
|
||||||
</AlertDialogDescription>
|
this compose.
|
||||||
</AlertDialogHeader>
|
</DialogDescription>
|
||||||
<AlertDialogFooter>
|
</DialogHeader>
|
||||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
<div className="grid gap-4">
|
||||||
<AlertDialogAction
|
<Form {...form}>
|
||||||
onClick={async () => {
|
<form
|
||||||
await mutateAsync({
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
composeId,
|
id="hook-form-delete-compose"
|
||||||
})
|
className="grid w-full gap-4"
|
||||||
.then((data) => {
|
>
|
||||||
push(`/dashboard/project/${data?.projectId}`);
|
<FormField
|
||||||
|
control={form.control}
|
||||||
toast.success("Compose delete succesfully");
|
name="projectName"
|
||||||
})
|
render={({ field }) => (
|
||||||
.catch(() => {
|
<FormItem>
|
||||||
toast.error("Error to delete the compose");
|
<FormLabel>
|
||||||
});
|
To confirm, type "{data?.name}/{data?.appName}" in the box
|
||||||
|
below
|
||||||
|
</FormLabel>{" "}
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
placeholder="Enter compose name to confirm"
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
<DialogFooter>
|
||||||
|
<Button
|
||||||
|
variant="secondary"
|
||||||
|
onClick={() => {
|
||||||
|
setIsOpen(false);
|
||||||
}}
|
}}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
isLoading={isLoading}
|
||||||
|
form="hook-form-delete-compose"
|
||||||
|
type="submit"
|
||||||
|
variant="destructive"
|
||||||
>
|
>
|
||||||
Confirm
|
Confirm
|
||||||
</AlertDialogAction>
|
</Button>
|
||||||
</AlertDialogFooter>
|
</DialogFooter>
|
||||||
</AlertDialogContent>
|
</DialogContent>
|
||||||
</AlertDialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,62 +1,140 @@
|
|||||||
import {
|
|
||||||
AlertDialog,
|
|
||||||
AlertDialogAction,
|
|
||||||
AlertDialogCancel,
|
|
||||||
AlertDialogContent,
|
|
||||||
AlertDialogDescription,
|
|
||||||
AlertDialogFooter,
|
|
||||||
AlertDialogHeader,
|
|
||||||
AlertDialogTitle,
|
|
||||||
AlertDialogTrigger,
|
|
||||||
} from "@/components/ui/alert-dialog";
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from "@/components/ui/form";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { TrashIcon } from "lucide-react";
|
import { TrashIcon } from "lucide-react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
const deleteMariadbSchema = z.object({
|
||||||
|
projectName: z.string().min(1, {
|
||||||
|
message: "Database name is required",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
type DeleteMariadb = z.infer<typeof deleteMariadbSchema>;
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
mariadbId: string;
|
mariadbId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DeleteMariadb = ({ mariadbId }: Props) => {
|
export const DeleteMariadb = ({ mariadbId }: Props) => {
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const { mutateAsync, isLoading } = api.mariadb.remove.useMutation();
|
const { mutateAsync, isLoading } = api.mariadb.remove.useMutation();
|
||||||
|
const { data } = api.mariadb.one.useQuery(
|
||||||
|
{ mariadbId },
|
||||||
|
{ enabled: !!mariadbId },
|
||||||
|
);
|
||||||
const { push } = useRouter();
|
const { push } = useRouter();
|
||||||
|
const form = useForm<DeleteMariadb>({
|
||||||
|
defaultValues: {
|
||||||
|
projectName: "",
|
||||||
|
},
|
||||||
|
resolver: zodResolver(deleteMariadbSchema),
|
||||||
|
});
|
||||||
|
|
||||||
|
const onSubmit = async (formData: DeleteMariadb) => {
|
||||||
|
const expectedName = `${data?.name}/${data?.appName}`;
|
||||||
|
if (formData.projectName === expectedName) {
|
||||||
|
await mutateAsync({ mariadbId })
|
||||||
|
.then((data) => {
|
||||||
|
push(`/dashboard/project/${data?.projectId}`);
|
||||||
|
toast.success("Database deleted successfully");
|
||||||
|
setIsOpen(false);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
toast.error("Error deleting the database");
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
form.setError("projectName", {
|
||||||
|
message: "Database name does not match",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AlertDialog>
|
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
||||||
<AlertDialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<Button variant="ghost" isLoading={isLoading}>
|
<Button variant="ghost" isLoading={isLoading}>
|
||||||
<TrashIcon className="size-4 text-muted-foreground " />
|
<TrashIcon className="size-4 text-muted-foreground" />
|
||||||
</Button>
|
</Button>
|
||||||
</AlertDialogTrigger>
|
</DialogTrigger>
|
||||||
<AlertDialogContent>
|
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-lg">
|
||||||
<AlertDialogHeader>
|
<DialogHeader>
|
||||||
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
|
<DialogTitle>Are you absolutely sure?</DialogTitle>
|
||||||
<AlertDialogDescription>
|
<DialogDescription>
|
||||||
This action cannot be undone. This will permanently delete the
|
This action cannot be undone. This will permanently delete the
|
||||||
database
|
database. If you are sure please enter the database name to delete
|
||||||
</AlertDialogDescription>
|
this database.
|
||||||
</AlertDialogHeader>
|
</DialogDescription>
|
||||||
<AlertDialogFooter>
|
</DialogHeader>
|
||||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
<div className="grid gap-4">
|
||||||
<AlertDialogAction
|
<Form {...form}>
|
||||||
onClick={async () => {
|
<form
|
||||||
await mutateAsync({
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
mariadbId,
|
id="hook-form-delete-mariadb"
|
||||||
})
|
className="grid w-full gap-4"
|
||||||
.then((data) => {
|
>
|
||||||
push(`/dashboard/project/${data?.projectId}`);
|
<FormField
|
||||||
toast.success("Database delete succesfully");
|
control={form.control}
|
||||||
})
|
name="projectName"
|
||||||
.catch(() => {
|
render={({ field }) => (
|
||||||
toast.error("Error to delete the database");
|
<FormItem>
|
||||||
});
|
<FormLabel>
|
||||||
|
To confirm, type "{data?.name}/{data?.appName}" in the box
|
||||||
|
below
|
||||||
|
</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
placeholder="Enter database name to confirm"
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
<DialogFooter>
|
||||||
|
<Button
|
||||||
|
variant="secondary"
|
||||||
|
onClick={() => {
|
||||||
|
setIsOpen(false);
|
||||||
}}
|
}}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
isLoading={isLoading}
|
||||||
|
form="hook-form-delete-mariadb"
|
||||||
|
type="submit"
|
||||||
|
variant="destructive"
|
||||||
>
|
>
|
||||||
Confirm
|
Confirm
|
||||||
</AlertDialogAction>
|
</Button>
|
||||||
</AlertDialogFooter>
|
</DialogFooter>
|
||||||
</AlertDialogContent>
|
</DialogContent>
|
||||||
</AlertDialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,62 +1,139 @@
|
|||||||
import {
|
|
||||||
AlertDialog,
|
|
||||||
AlertDialogAction,
|
|
||||||
AlertDialogCancel,
|
|
||||||
AlertDialogContent,
|
|
||||||
AlertDialogDescription,
|
|
||||||
AlertDialogFooter,
|
|
||||||
AlertDialogHeader,
|
|
||||||
AlertDialogTitle,
|
|
||||||
AlertDialogTrigger,
|
|
||||||
} from "@/components/ui/alert-dialog";
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from "@/components/ui/form";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { TrashIcon } from "lucide-react";
|
import { TrashIcon } from "lucide-react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
const deleteMongoSchema = z.object({
|
||||||
|
projectName: z.string().min(1, {
|
||||||
|
message: "Database name is required",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
type DeleteMongo = z.infer<typeof deleteMongoSchema>;
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
mongoId: string;
|
mongoId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// commen
|
||||||
|
|
||||||
export const DeleteMongo = ({ mongoId }: Props) => {
|
export const DeleteMongo = ({ mongoId }: Props) => {
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const { mutateAsync, isLoading } = api.mongo.remove.useMutation();
|
const { mutateAsync, isLoading } = api.mongo.remove.useMutation();
|
||||||
|
const { data } = api.mongo.one.useQuery({ mongoId }, { enabled: !!mongoId });
|
||||||
const { push } = useRouter();
|
const { push } = useRouter();
|
||||||
|
const form = useForm<DeleteMongo>({
|
||||||
|
defaultValues: {
|
||||||
|
projectName: "",
|
||||||
|
},
|
||||||
|
resolver: zodResolver(deleteMongoSchema),
|
||||||
|
});
|
||||||
|
|
||||||
|
const onSubmit = async (formData: DeleteMongo) => {
|
||||||
|
const expectedName = `${data?.name}/${data?.appName}`;
|
||||||
|
if (formData.projectName === expectedName) {
|
||||||
|
await mutateAsync({ mongoId })
|
||||||
|
.then((data) => {
|
||||||
|
push(`/dashboard/project/${data?.projectId}`);
|
||||||
|
toast.success("Database deleted successfully");
|
||||||
|
setIsOpen(false);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
toast.error("Error deleting the database");
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
form.setError("projectName", {
|
||||||
|
message: "Database name does not match",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<AlertDialog>
|
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
||||||
<AlertDialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<Button variant="ghost" isLoading={isLoading}>
|
<Button variant="ghost" isLoading={isLoading}>
|
||||||
<TrashIcon className="size-4 text-muted-foreground " />
|
<TrashIcon className="size-4 text-muted-foreground" />
|
||||||
</Button>
|
</Button>
|
||||||
</AlertDialogTrigger>
|
</DialogTrigger>
|
||||||
<AlertDialogContent>
|
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-lg">
|
||||||
<AlertDialogHeader>
|
<DialogHeader>
|
||||||
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
|
<DialogTitle>Are you absolutely sure?</DialogTitle>
|
||||||
<AlertDialogDescription>
|
<DialogDescription>
|
||||||
This action cannot be undone. This will permanently delete the
|
This action cannot be undone. This will permanently delete the
|
||||||
database
|
database. If you are sure please enter the database name to delete
|
||||||
</AlertDialogDescription>
|
this database.
|
||||||
</AlertDialogHeader>
|
</DialogDescription>
|
||||||
<AlertDialogFooter>
|
</DialogHeader>
|
||||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
<div className="grid gap-4">
|
||||||
<AlertDialogAction
|
<Form {...form}>
|
||||||
onClick={async () => {
|
<form
|
||||||
await mutateAsync({
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
mongoId,
|
id="hook-form-delete-mongo"
|
||||||
})
|
className="grid w-full gap-4"
|
||||||
.then((data) => {
|
>
|
||||||
push(`/dashboard/project/${data?.projectId}`);
|
<FormField
|
||||||
toast.success("Database delete succesfully");
|
control={form.control}
|
||||||
})
|
name="projectName"
|
||||||
.catch(() => {
|
render={({ field }) => (
|
||||||
toast.error("Error to delete the database");
|
<FormItem>
|
||||||
});
|
<FormLabel>
|
||||||
|
To confirm, type "{data?.name}/{data?.appName}" in the box
|
||||||
|
below
|
||||||
|
</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
placeholder="Enter database name to confirm"
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
<DialogFooter>
|
||||||
|
<Button
|
||||||
|
variant="secondary"
|
||||||
|
onClick={() => {
|
||||||
|
setIsOpen(false);
|
||||||
}}
|
}}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
isLoading={isLoading}
|
||||||
|
form="hook-form-delete-mongo"
|
||||||
|
type="submit"
|
||||||
|
variant="destructive"
|
||||||
>
|
>
|
||||||
Confirm
|
Confirm
|
||||||
</AlertDialogAction>
|
</Button>
|
||||||
</AlertDialogFooter>
|
</DialogFooter>
|
||||||
</AlertDialogContent>
|
</DialogContent>
|
||||||
</AlertDialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,62 +1,138 @@
|
|||||||
import {
|
|
||||||
AlertDialog,
|
|
||||||
AlertDialogAction,
|
|
||||||
AlertDialogCancel,
|
|
||||||
AlertDialogContent,
|
|
||||||
AlertDialogDescription,
|
|
||||||
AlertDialogFooter,
|
|
||||||
AlertDialogHeader,
|
|
||||||
AlertDialogTitle,
|
|
||||||
AlertDialogTrigger,
|
|
||||||
} from "@/components/ui/alert-dialog";
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from "@/components/ui/form";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { TrashIcon } from "lucide-react";
|
import { TrashIcon } from "lucide-react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
const deleteMysqlSchema = z.object({
|
||||||
|
projectName: z.string().min(1, {
|
||||||
|
message: "Database name is required",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
type DeleteMysql = z.infer<typeof deleteMysqlSchema>;
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
mysqlId: string;
|
mysqlId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DeleteMysql = ({ mysqlId }: Props) => {
|
export const DeleteMysql = ({ mysqlId }: Props) => {
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const { mutateAsync, isLoading } = api.mysql.remove.useMutation();
|
const { mutateAsync, isLoading } = api.mysql.remove.useMutation();
|
||||||
|
const { data } = api.mysql.one.useQuery({ mysqlId }, { enabled: !!mysqlId });
|
||||||
const { push } = useRouter();
|
const { push } = useRouter();
|
||||||
|
const form = useForm<DeleteMysql>({
|
||||||
|
defaultValues: {
|
||||||
|
projectName: "",
|
||||||
|
},
|
||||||
|
resolver: zodResolver(deleteMysqlSchema),
|
||||||
|
});
|
||||||
|
|
||||||
|
const onSubmit = async (formData: DeleteMysql) => {
|
||||||
|
const expectedName = `${data?.name}/${data?.appName}`;
|
||||||
|
if (formData.projectName === expectedName) {
|
||||||
|
await mutateAsync({ mysqlId })
|
||||||
|
.then((data) => {
|
||||||
|
push(`/dashboard/project/${data?.projectId}`);
|
||||||
|
toast.success("Database deleted successfully");
|
||||||
|
setIsOpen(false);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
toast.error("Error deleting the database");
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
form.setError("projectName", {
|
||||||
|
message: "Database name does not match",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AlertDialog>
|
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
||||||
<AlertDialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<Button variant="ghost" isLoading={isLoading}>
|
<Button variant="ghost" isLoading={isLoading}>
|
||||||
<TrashIcon className="size-4 text-muted-foreground " />
|
<TrashIcon className="size-4 text-muted-foreground" />
|
||||||
</Button>
|
</Button>
|
||||||
</AlertDialogTrigger>
|
</DialogTrigger>
|
||||||
<AlertDialogContent>
|
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-lg">
|
||||||
<AlertDialogHeader>
|
<DialogHeader>
|
||||||
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
|
<DialogTitle>Are you absolutely sure?</DialogTitle>
|
||||||
<AlertDialogDescription>
|
<DialogDescription>
|
||||||
This action cannot be undone. This will permanently delete the
|
This action cannot be undone. This will permanently delete the
|
||||||
database
|
database. If you are sure please enter the database name to delete
|
||||||
</AlertDialogDescription>
|
this database.
|
||||||
</AlertDialogHeader>
|
</DialogDescription>
|
||||||
<AlertDialogFooter>
|
</DialogHeader>
|
||||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
<div className="grid gap-4">
|
||||||
<AlertDialogAction
|
<Form {...form}>
|
||||||
onClick={async () => {
|
<form
|
||||||
await mutateAsync({
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
mysqlId,
|
id="hook-form-delete-mysql"
|
||||||
})
|
className="grid w-full gap-4"
|
||||||
.then((data) => {
|
>
|
||||||
push(`/dashboard/project/${data?.projectId}`);
|
<FormField
|
||||||
toast.success("Database delete succesfully");
|
control={form.control}
|
||||||
})
|
name="projectName"
|
||||||
.catch(() => {
|
render={({ field }) => (
|
||||||
toast.error("Error to delete the database");
|
<FormItem>
|
||||||
});
|
<FormLabel>
|
||||||
|
To confirm, type "{data?.name}/{data?.appName}" in the box
|
||||||
|
below
|
||||||
|
</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
placeholder="Enter database name to confirm"
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
<DialogFooter>
|
||||||
|
<Button
|
||||||
|
variant="secondary"
|
||||||
|
onClick={() => {
|
||||||
|
setIsOpen(false);
|
||||||
}}
|
}}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
isLoading={isLoading}
|
||||||
|
form="hook-form-delete-mysql"
|
||||||
|
type="submit"
|
||||||
|
variant="destructive"
|
||||||
>
|
>
|
||||||
Confirm
|
Confirm
|
||||||
</AlertDialogAction>
|
</Button>
|
||||||
</AlertDialogFooter>
|
</DialogFooter>
|
||||||
</AlertDialogContent>
|
</DialogContent>
|
||||||
</AlertDialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,62 +1,141 @@
|
|||||||
import {
|
|
||||||
AlertDialog,
|
|
||||||
AlertDialogAction,
|
|
||||||
AlertDialogCancel,
|
|
||||||
AlertDialogContent,
|
|
||||||
AlertDialogDescription,
|
|
||||||
AlertDialogFooter,
|
|
||||||
AlertDialogHeader,
|
|
||||||
AlertDialogTitle,
|
|
||||||
AlertDialogTrigger,
|
|
||||||
} from "@/components/ui/alert-dialog";
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from "@/components/ui/form";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { TrashIcon } from "lucide-react";
|
import { TrashIcon } from "lucide-react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
const deletePostgresSchema = z.object({
|
||||||
|
projectName: z.string().min(1, {
|
||||||
|
message: "Database name is required",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
type DeletePostgres = z.infer<typeof deletePostgresSchema>;
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
postgresId: string;
|
postgresId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DeletePostgres = ({ postgresId }: Props) => {
|
export const DeletePostgres = ({ postgresId }: Props) => {
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const { mutateAsync, isLoading } = api.postgres.remove.useMutation();
|
const { mutateAsync, isLoading } = api.postgres.remove.useMutation();
|
||||||
|
const { data } = api.postgres.one.useQuery(
|
||||||
|
{ postgresId },
|
||||||
|
{ enabled: !!postgresId },
|
||||||
|
);
|
||||||
const { push } = useRouter();
|
const { push } = useRouter();
|
||||||
|
const form = useForm<DeletePostgres>({
|
||||||
|
defaultValues: {
|
||||||
|
projectName: "",
|
||||||
|
},
|
||||||
|
resolver: zodResolver(deletePostgresSchema),
|
||||||
|
});
|
||||||
|
|
||||||
|
const onSubmit = async (formData: DeletePostgres) => {
|
||||||
|
const expectedName = `${data?.name}/${data?.appName}`;
|
||||||
|
if (formData.projectName === expectedName) {
|
||||||
|
await mutateAsync({ postgresId })
|
||||||
|
.then((data) => {
|
||||||
|
push(`/dashboard/project/${data?.projectId}`);
|
||||||
|
toast.success("Database deleted successfully");
|
||||||
|
setIsOpen(false);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
toast.error("Error deleting the database");
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
form.setError("projectName", {
|
||||||
|
message: "Database name does not match",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AlertDialog>
|
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
||||||
<AlertDialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<Button variant="ghost" isLoading={isLoading}>
|
<Button variant="ghost" isLoading={isLoading}>
|
||||||
<TrashIcon className="size-4 text-muted-foreground " />
|
<TrashIcon className="size-4 text-muted-foreground" />
|
||||||
</Button>
|
</Button>
|
||||||
</AlertDialogTrigger>
|
</DialogTrigger>
|
||||||
<AlertDialogContent>
|
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-lg">
|
||||||
<AlertDialogHeader>
|
<DialogHeader>
|
||||||
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
|
<DialogTitle>Are you absolutely sure?</DialogTitle>
|
||||||
<AlertDialogDescription>
|
<DialogDescription>
|
||||||
This action cannot be undone. This will permanently delete the
|
This action cannot be undone. This will permanently delete the
|
||||||
database
|
database. If you are sure please enter the database name to delete
|
||||||
</AlertDialogDescription>
|
this database.
|
||||||
</AlertDialogHeader>
|
</DialogDescription>
|
||||||
<AlertDialogFooter>
|
</DialogHeader>
|
||||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
<div className="grid gap-4">
|
||||||
<AlertDialogAction
|
<Form {...form}>
|
||||||
onClick={async () => {
|
<form
|
||||||
await mutateAsync({
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
postgresId,
|
id="hook-form-delete-postgres"
|
||||||
})
|
className="grid w-full gap-4"
|
||||||
.then((data) => {
|
>
|
||||||
push(`/dashboard/project/${data?.projectId}`);
|
<FormField
|
||||||
toast.success("Database delete succesfully");
|
control={form.control}
|
||||||
})
|
name="projectName"
|
||||||
.catch(() => {
|
render={({ field }) => (
|
||||||
toast.error("Error to delete the database");
|
<FormItem>
|
||||||
});
|
<FormLabel>
|
||||||
|
To confirm, type "{data?.name}/{data?.appName}" in the box
|
||||||
|
below
|
||||||
|
</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
placeholder="Enter database name to confirm"
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
<DialogFooter>
|
||||||
|
<Button
|
||||||
|
variant="secondary"
|
||||||
|
onClick={() => {
|
||||||
|
setIsOpen(false);
|
||||||
}}
|
}}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
isLoading={isLoading}
|
||||||
|
form="hook-form-delete-postgres"
|
||||||
|
type="submit"
|
||||||
|
variant="destructive"
|
||||||
>
|
>
|
||||||
Confirm
|
Confirm
|
||||||
</AlertDialogAction>
|
</Button>
|
||||||
</AlertDialogFooter>
|
</DialogFooter>
|
||||||
</AlertDialogContent>
|
</DialogContent>
|
||||||
</AlertDialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,62 +1,138 @@
|
|||||||
import {
|
|
||||||
AlertDialog,
|
|
||||||
AlertDialogAction,
|
|
||||||
AlertDialogCancel,
|
|
||||||
AlertDialogContent,
|
|
||||||
AlertDialogDescription,
|
|
||||||
AlertDialogFooter,
|
|
||||||
AlertDialogHeader,
|
|
||||||
AlertDialogTitle,
|
|
||||||
AlertDialogTrigger,
|
|
||||||
} from "@/components/ui/alert-dialog";
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from "@/components/ui/form";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { TrashIcon } from "lucide-react";
|
import { TrashIcon } from "lucide-react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
const deleteRedisSchema = z.object({
|
||||||
|
projectName: z.string().min(1, {
|
||||||
|
message: "Database name is required",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
type DeleteRedis = z.infer<typeof deleteRedisSchema>;
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
redisId: string;
|
redisId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DeleteRedis = ({ redisId }: Props) => {
|
export const DeleteRedis = ({ redisId }: Props) => {
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const { mutateAsync, isLoading } = api.redis.remove.useMutation();
|
const { mutateAsync, isLoading } = api.redis.remove.useMutation();
|
||||||
|
const { data } = api.redis.one.useQuery({ redisId }, { enabled: !!redisId });
|
||||||
const { push } = useRouter();
|
const { push } = useRouter();
|
||||||
|
const form = useForm<DeleteRedis>({
|
||||||
|
defaultValues: {
|
||||||
|
projectName: "",
|
||||||
|
},
|
||||||
|
resolver: zodResolver(deleteRedisSchema),
|
||||||
|
});
|
||||||
|
|
||||||
|
const onSubmit = async (formData: DeleteRedis) => {
|
||||||
|
const expectedName = `${data?.name}/${data?.appName}`;
|
||||||
|
if (formData.projectName === expectedName) {
|
||||||
|
await mutateAsync({ redisId })
|
||||||
|
.then((data) => {
|
||||||
|
push(`/dashboard/project/${data?.projectId}`);
|
||||||
|
toast.success("Database deleted successfully");
|
||||||
|
setIsOpen(false);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
toast.error("Error deleting the database");
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
form.setError("projectName", {
|
||||||
|
message: "Database name does not match",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AlertDialog>
|
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
||||||
<AlertDialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<Button variant="ghost" isLoading={isLoading}>
|
<Button variant="ghost" isLoading={isLoading}>
|
||||||
<TrashIcon className="size-4 text-muted-foreground " />
|
<TrashIcon className="size-4 text-muted-foreground" />
|
||||||
</Button>
|
</Button>
|
||||||
</AlertDialogTrigger>
|
</DialogTrigger>
|
||||||
<AlertDialogContent>
|
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-lg">
|
||||||
<AlertDialogHeader>
|
<DialogHeader>
|
||||||
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
|
<DialogTitle>Are you absolutely sure?</DialogTitle>
|
||||||
<AlertDialogDescription>
|
<DialogDescription>
|
||||||
This action cannot be undone. This will permanently delete the
|
This action cannot be undone. This will permanently delete the
|
||||||
database
|
database. If you are sure please enter the database name to delete
|
||||||
</AlertDialogDescription>
|
this database.
|
||||||
</AlertDialogHeader>
|
</DialogDescription>
|
||||||
<AlertDialogFooter>
|
</DialogHeader>
|
||||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
<div className="grid gap-4">
|
||||||
<AlertDialogAction
|
<Form {...form}>
|
||||||
onClick={async () => {
|
<form
|
||||||
await mutateAsync({
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
redisId,
|
id="hook-form-delete-redis"
|
||||||
})
|
className="grid w-full gap-4"
|
||||||
.then((data) => {
|
>
|
||||||
push(`/dashboard/project/${data?.projectId}`);
|
<FormField
|
||||||
toast.success("Database delete succesfully");
|
control={form.control}
|
||||||
})
|
name="projectName"
|
||||||
.catch(() => {
|
render={({ field }) => (
|
||||||
toast.error("Error to delete the database");
|
<FormItem>
|
||||||
});
|
<FormLabel>
|
||||||
|
To confirm, type "{data?.name}/{data?.appName}" in the box
|
||||||
|
below
|
||||||
|
</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
placeholder="Enter database name to confirm"
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
<DialogFooter>
|
||||||
|
<Button
|
||||||
|
variant="secondary"
|
||||||
|
onClick={() => {
|
||||||
|
setIsOpen(false);
|
||||||
}}
|
}}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
isLoading={isLoading}
|
||||||
|
form="hook-form-delete-redis"
|
||||||
|
type="submit"
|
||||||
|
variant="destructive"
|
||||||
>
|
>
|
||||||
Confirm
|
Confirm
|
||||||
</AlertDialogAction>
|
</Button>
|
||||||
</AlertDialogFooter>
|
</DialogFooter>
|
||||||
</AlertDialogContent>
|
</DialogContent>
|
||||||
</AlertDialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user