diff options
Diffstat (limited to 'apps/web/components/admin/AddUserDialog.tsx')
| -rw-r--r-- | apps/web/components/admin/AddUserDialog.tsx | 430 |
1 files changed, 217 insertions, 213 deletions
diff --git a/apps/web/components/admin/AddUserDialog.tsx b/apps/web/components/admin/AddUserDialog.tsx index 3c578eca..2e29c6da 100644 --- a/apps/web/components/admin/AddUserDialog.tsx +++ b/apps/web/components/admin/AddUserDialog.tsx @@ -1,213 +1,217 @@ -import { useEffect, useState } from "react";
-import { ActionButton } from "@/components/ui/action-button";
-import { Button } from "@/components/ui/button";
-import {
- Dialog,
- DialogClose,
- DialogContent,
- 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 {
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
- SelectValue,
-} from "@/components/ui/select";
-import { toast } from "@/components/ui/sonner";
-import { api } from "@/lib/trpc";
-import { zodResolver } from "@hookform/resolvers/zod";
-import { TRPCClientError } from "@trpc/client";
-import { useForm } from "react-hook-form";
-import { z } from "zod";
-
-import { zAdminCreateUserSchema } from "@karakeep/shared/types/admin";
-
-type AdminCreateUserSchema = z.infer<typeof zAdminCreateUserSchema>;
-
-export default function AddUserDialog({
- children,
-}: {
- children?: React.ReactNode;
-}) {
- const apiUtils = api.useUtils();
- const [isOpen, onOpenChange] = useState(false);
- const form = useForm<AdminCreateUserSchema>({
- resolver: zodResolver(zAdminCreateUserSchema),
- defaultValues: {
- name: "",
- email: "",
- password: "",
- confirmPassword: "",
- role: "user",
- },
- });
- const { mutate, isPending } = api.admin.createUser.useMutation({
- onSuccess: () => {
- toast({
- description: "User created successfully",
- });
- onOpenChange(false);
- apiUtils.users.list.invalidate();
- apiUtils.admin.userStats.invalidate();
- },
- onError: (error) => {
- if (error instanceof TRPCClientError) {
- toast({
- variant: "destructive",
- description: error.message,
- });
- } else {
- toast({
- variant: "destructive",
- description: "Failed to create user",
- });
- }
- },
- });
-
- useEffect(() => {
- if (!isOpen) {
- form.reset();
- }
- }, [isOpen, form]);
-
- return (
- <Dialog open={isOpen} onOpenChange={onOpenChange}>
- <DialogTrigger asChild>{children}</DialogTrigger>
- <DialogContent>
- <DialogHeader>
- <DialogTitle>Add User</DialogTitle>
- </DialogHeader>
- <Form {...form}>
- <form onSubmit={form.handleSubmit((val) => mutate(val))}>
- <div className="flex w-full flex-col space-y-2">
- <FormField
- control={form.control}
- name="name"
- render={({ field }) => (
- <FormItem>
- <FormLabel>Name</FormLabel>
- <FormControl>
- <Input
- type="text"
- placeholder="Name"
- {...field}
- className="w-full rounded border p-2"
- />
- </FormControl>
- <FormMessage />
- </FormItem>
- )}
- />
- <FormField
- control={form.control}
- name="email"
- render={({ field }) => (
- <FormItem>
- <FormLabel>Email</FormLabel>
- <FormControl>
- <Input
- type="email"
- placeholder="Email"
- {...field}
- className="w-full rounded border p-2"
- />
- </FormControl>
- <FormMessage />
- </FormItem>
- )}
- />
- <FormField
- control={form.control}
- name="password"
- render={({ field }) => (
- <FormItem>
- <FormLabel>Password</FormLabel>
- <FormControl>
- <Input
- type="password"
- placeholder="Password"
- {...field}
- className="w-full rounded border p-2"
- />
- </FormControl>
- <FormMessage />
- </FormItem>
- )}
- />
- <FormField
- control={form.control}
- name="confirmPassword"
- render={({ field }) => (
- <FormItem>
- <FormLabel>Confirm Password</FormLabel>
- <FormControl>
- <Input
- type="password"
- placeholder="Confirm Password"
- {...field}
- className="w-full rounded border p-2"
- />
- </FormControl>
- <FormMessage />
- </FormItem>
- )}
- />
- <FormField
- control={form.control}
- name="role"
- render={({ field }) => (
- <FormItem>
- <FormLabel>Role</FormLabel>
- <FormControl>
- <Select
- value={field.value}
- onValueChange={field.onChange}
- >
- <SelectTrigger className="w-full">
- <SelectValue />
- </SelectTrigger>
- <SelectContent>
- <SelectItem value="user">User</SelectItem>
- <SelectItem value="admin">Admin</SelectItem>
- </SelectContent>
- </Select>
- </FormControl>
- <FormMessage />
- </FormItem>
- )}
- />
- <DialogFooter className="sm:justify-end">
- <DialogClose asChild>
- <Button type="button" variant="secondary">
- Close
- </Button>
- </DialogClose>
- <ActionButton
- type="submit"
- loading={isPending}
- disabled={isPending}
- >
- Create
- </ActionButton>
- </DialogFooter>
- </div>
- </form>
- </Form>
- </DialogContent>
- </Dialog>
- );
-}
+import { useEffect, useState } from "react"; +import { ActionButton } from "@/components/ui/action-button"; +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogClose, + DialogContent, + 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 { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { toast } from "@/components/ui/sonner"; +import { useTRPC } from "@/lib/trpc"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { TRPCClientError } from "@trpc/client"; +import { useForm } from "react-hook-form"; +import { z } from "zod"; + +import { zAdminCreateUserSchema } from "@karakeep/shared/types/admin"; + +type AdminCreateUserSchema = z.infer<typeof zAdminCreateUserSchema>; + +export default function AddUserDialog({ + children, +}: { + children?: React.ReactNode; +}) { + const api = useTRPC(); + const queryClient = useQueryClient(); + const [isOpen, onOpenChange] = useState(false); + const form = useForm<AdminCreateUserSchema>({ + resolver: zodResolver(zAdminCreateUserSchema), + defaultValues: { + name: "", + email: "", + password: "", + confirmPassword: "", + role: "user", + }, + }); + const { mutate, isPending } = useMutation( + api.admin.createUser.mutationOptions({ + onSuccess: () => { + toast({ + description: "User created successfully", + }); + onOpenChange(false); + queryClient.invalidateQueries(api.users.list.pathFilter()); + queryClient.invalidateQueries(api.admin.userStats.pathFilter()); + }, + onError: (error) => { + if (error instanceof TRPCClientError) { + toast({ + variant: "destructive", + description: error.message, + }); + } else { + toast({ + variant: "destructive", + description: "Failed to create user", + }); + } + }, + }), + ); + + useEffect(() => { + if (!isOpen) { + form.reset(); + } + }, [isOpen, form]); + + return ( + <Dialog open={isOpen} onOpenChange={onOpenChange}> + <DialogTrigger asChild>{children}</DialogTrigger> + <DialogContent> + <DialogHeader> + <DialogTitle>Add User</DialogTitle> + </DialogHeader> + <Form {...form}> + <form onSubmit={form.handleSubmit((val) => mutate(val))}> + <div className="flex w-full flex-col space-y-2"> + <FormField + control={form.control} + name="name" + render={({ field }) => ( + <FormItem> + <FormLabel>Name</FormLabel> + <FormControl> + <Input + type="text" + placeholder="Name" + {...field} + className="w-full rounded border p-2" + /> + </FormControl> + <FormMessage /> + </FormItem> + )} + /> + <FormField + control={form.control} + name="email" + render={({ field }) => ( + <FormItem> + <FormLabel>Email</FormLabel> + <FormControl> + <Input + type="email" + placeholder="Email" + {...field} + className="w-full rounded border p-2" + /> + </FormControl> + <FormMessage /> + </FormItem> + )} + /> + <FormField + control={form.control} + name="password" + render={({ field }) => ( + <FormItem> + <FormLabel>Password</FormLabel> + <FormControl> + <Input + type="password" + placeholder="Password" + {...field} + className="w-full rounded border p-2" + /> + </FormControl> + <FormMessage /> + </FormItem> + )} + /> + <FormField + control={form.control} + name="confirmPassword" + render={({ field }) => ( + <FormItem> + <FormLabel>Confirm Password</FormLabel> + <FormControl> + <Input + type="password" + placeholder="Confirm Password" + {...field} + className="w-full rounded border p-2" + /> + </FormControl> + <FormMessage /> + </FormItem> + )} + /> + <FormField + control={form.control} + name="role" + render={({ field }) => ( + <FormItem> + <FormLabel>Role</FormLabel> + <FormControl> + <Select + value={field.value} + onValueChange={field.onChange} + > + <SelectTrigger className="w-full"> + <SelectValue /> + </SelectTrigger> + <SelectContent> + <SelectItem value="user">User</SelectItem> + <SelectItem value="admin">Admin</SelectItem> + </SelectContent> + </Select> + </FormControl> + <FormMessage /> + </FormItem> + )} + /> + <DialogFooter className="sm:justify-end"> + <DialogClose asChild> + <Button type="button" variant="secondary"> + Close + </Button> + </DialogClose> + <ActionButton + type="submit" + loading={isPending} + disabled={isPending} + > + Create + </ActionButton> + </DialogFooter> + </div> + </form> + </Form> + </DialogContent> + </Dialog> + ); +} |
