Openstatus
www.openstatus.dev
1"use client";
2
3import { Form, FormMessage } from "@/components/ui/form";
4import {
5 FormControl,
6 FormField,
7 FormItem,
8 FormLabel,
9} from "@/components/ui/form";
10import { Input } from "@/components/ui/input";
11import { zodResolver } from "@hookform/resolvers/zod";
12import { isTRPCClientError } from "@trpc/client";
13import { useTransition } from "react";
14import { useForm } from "react-hook-form";
15import { toast } from "sonner";
16import { z } from "zod";
17
18const schema = z.object({
19 email: z.string().email(),
20});
21
22export type FormValues = z.infer<typeof schema>;
23
24export function FormEmail({
25 onSubmit,
26 ...props
27}: Omit<React.ComponentProps<"form">, "onSubmit"> & {
28 onSubmit: (values: FormValues) => Promise<void>;
29}) {
30 const form = useForm<FormValues>({
31 resolver: zodResolver(schema),
32 defaultValues: {
33 email: "",
34 },
35 });
36 const [isPending, startTransition] = useTransition();
37
38 function submitAction(values: FormValues) {
39 if (isPending) return;
40
41 startTransition(async () => {
42 try {
43 const promise = onSubmit(values);
44 toast.promise(promise, {
45 loading: "Confirming...",
46 success: "Confirmed",
47 error: (error) => {
48 console.error(error);
49 if (isTRPCClientError(error)) {
50 form.setError("email", { message: error.message });
51 return error.message;
52 }
53 if (error instanceof Error) {
54 form.setError("email", { message: error.message });
55 return error.message;
56 }
57 return "Failed to confirm";
58 },
59 });
60 await promise;
61 } catch (error) {
62 console.error(error);
63 }
64 });
65 }
66
67 return (
68 <Form {...form}>
69 <form onSubmit={form.handleSubmit(submitAction)} {...props}>
70 <FormField
71 control={form.control}
72 name="email"
73 render={({ field }) => (
74 <FormItem>
75 <FormLabel>Email</FormLabel>
76 <FormControl>
77 <Input type="email" {...field} />
78 </FormControl>
79 <FormMessage />
80 </FormItem>
81 )}
82 />
83 </form>
84 </Form>
85 );
86}