Openstatus
www.openstatus.dev
1"use client";
2
3import { Form } 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.email(),
20});
21
22type FormValues = z.infer<typeof schema>;
23
24export function FormSubscribeEmail({
25 onSubmit,
26 ...props
27}: Omit<React.ComponentProps<"form">, "onSubmit"> & {
28 onSubmit: (values: FormValues) => Promise<void>;
29 onSubmitCallback?: () => void;
30}) {
31 const form = useForm<FormValues>({
32 resolver: zodResolver(schema),
33 defaultValues: {
34 email: "",
35 },
36 });
37 const [isPending, startTransition] = useTransition();
38
39 function submitAction(values: FormValues) {
40 if (isPending) return;
41
42 startTransition(async () => {
43 try {
44 const promise = onSubmit(values);
45 toast.promise(promise, {
46 loading: "Subscribing...",
47 success: "Subscribed",
48 error: (error) => {
49 if (isTRPCClientError(error)) {
50 return error.message;
51 }
52 return "Failed to subscribe";
53 },
54 });
55 await promise;
56 } catch (error) {
57 console.error(error);
58 }
59 });
60 }
61
62 return (
63 <Form {...form}>
64 <form onSubmit={form.handleSubmit(submitAction)} {...props}>
65 <FormField
66 control={form.control}
67 name="email"
68 render={({ field }) => (
69 <FormItem>
70 <FormLabel className="sr-only">Email</FormLabel>
71 <FormControl>
72 <Input placeholder="subscribe@me.com" {...field} />
73 </FormControl>
74 </FormItem>
75 )}
76 />
77 </form>
78 </Form>
79 );
80}