1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
"use client";
import { useState } from "react";
import { ActionButton } from "@/components/ui/action-button";
import {
Dialog,
DialogContent,
DialogDescription,
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 { toast } from "@/components/ui/use-toast";
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";
const createInviteSchema = z.object({
email: z.string().email("Please enter a valid email address"),
});
interface CreateInviteDialogProps {
children: React.ReactNode;
}
export default function CreateInviteDialog({
children,
}: CreateInviteDialogProps) {
const [open, setOpen] = useState(false);
const [errorMessage, setErrorMessage] = useState("");
const form = useForm<z.infer<typeof createInviteSchema>>({
resolver: zodResolver(createInviteSchema),
defaultValues: {
email: "",
},
});
const invalidateInvitesList = api.useUtils().invites.list.invalidate;
const createInviteMutation = api.invites.create.useMutation({
onSuccess: () => {
toast({
description: "Invite sent successfully",
});
invalidateInvitesList();
setOpen(false);
form.reset();
setErrorMessage("");
},
onError: (e) => {
if (e instanceof TRPCClientError) {
setErrorMessage(e.message);
} else {
setErrorMessage("Failed to send invite");
}
},
});
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>{children}</DialogTrigger>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle>Send User Invitation</DialogTitle>
<DialogDescription>
Send an invitation to a new user to join Karakeep. They'll
receive an email with instructions to create their account and will
be assigned the "user" role.
</DialogDescription>
</DialogHeader>
<Form {...form}>
<form
onSubmit={form.handleSubmit(async (value) => {
setErrorMessage("");
await createInviteMutation.mutateAsync(value);
})}
className="space-y-4"
>
{errorMessage && (
<p className="text-sm text-destructive">{errorMessage}</p>
)}
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email Address</FormLabel>
<FormControl>
<Input
type="email"
placeholder="user@example.com"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<div className="flex justify-end space-x-2">
<ActionButton
type="button"
variant="outline"
loading={false}
onClick={() => setOpen(false)}
>
Cancel
</ActionButton>
<ActionButton
type="submit"
loading={createInviteMutation.isPending}
>
Send Invitation
</ActionButton>
</div>
</form>
</Form>
</DialogContent>
</Dialog>
);
}
|