Openstatus
www.openstatus.dev
1"use client";
2
3import { Link } from "@/components/common/link";
4import {
5 FormCard,
6 FormCardContent,
7 FormCardDescription,
8 FormCardFooter,
9 FormCardFooterInfo,
10 FormCardHeader,
11 FormCardTitle,
12} from "@/components/forms/form-card";
13import { Button } from "@/components/ui/button";
14import {
15 Form,
16 FormControl,
17 FormDescription,
18 FormField,
19 FormItem,
20 FormLabel,
21 FormMessage,
22} from "@/components/ui/form";
23import { Input } from "@/components/ui/input";
24import { zodResolver } from "@hookform/resolvers/zod";
25import { useTransition } from "react";
26import { useForm } from "react-hook-form";
27import { toast } from "sonner";
28import { z } from "zod";
29
30const RETRY_MIN = 1;
31const RETRY_MAX = 10;
32export const RETRY_DEFAULT = 3;
33
34const schema = z.object({
35 retry: z.coerce
36 .number<number>()
37 .min(RETRY_MIN)
38 .max(RETRY_MAX)
39 .prefault(RETRY_DEFAULT),
40});
41
42type FormValues = z.input<typeof schema>;
43
44export function FormRetry({
45 defaultValues,
46 onSubmit,
47 ...props
48}: Omit<React.ComponentProps<"form">, "onSubmit"> & {
49 defaultValues?: FormValues;
50 onSubmit: (values: FormValues) => Promise<void>;
51}) {
52 const form = useForm<FormValues>({
53 resolver: zodResolver(schema),
54 defaultValues: defaultValues ?? {
55 retry: RETRY_DEFAULT,
56 },
57 });
58 const [isPending, startTransition] = useTransition();
59
60 function submitAction(values: FormValues) {
61 if (isPending) return;
62
63 startTransition(async () => {
64 try {
65 const promise = onSubmit(values);
66 toast.promise(promise, {
67 loading: "Saving...",
68 success: "Saved",
69 error: "Failed to save",
70 });
71 await promise;
72 } catch (error) {
73 console.error(error);
74 }
75 });
76 }
77
78 return (
79 <Form {...form}>
80 <form onSubmit={form.handleSubmit(submitAction)} {...props}>
81 <FormCard>
82 <FormCardHeader>
83 <FormCardTitle>Retry Policy</FormCardTitle>
84 <FormCardDescription>
85 Configure the retry policy for your monitor.
86 </FormCardDescription>
87 </FormCardHeader>
88 <FormCardContent className="grid gap-4 sm:grid-cols-2">
89 <FormField
90 control={form.control}
91 name="retry"
92 render={({ field }) => (
93 <FormItem>
94 <FormLabel>Retry</FormLabel>
95 <FormControl>
96 <Input
97 min={RETRY_MIN}
98 max={RETRY_MAX}
99 step={1}
100 type="number"
101 {...field}
102 />
103 </FormControl>
104 <FormMessage />
105 <FormDescription>
106 The retry policy is exponential backoff.
107 </FormDescription>
108 </FormItem>
109 )}
110 />
111 </FormCardContent>
112 <FormCardFooter>
113 <FormCardFooterInfo>
114 Learn more about{" "}
115 <Link
116 href="https://docs.openstatus.dev/reference/http-monitor/#retry"
117 rel="noreferrer"
118 target="_blank"
119 >
120 retries
121 </Link>
122 .
123 </FormCardFooterInfo>
124 <Button type="submit" disabled={isPending}>
125 {isPending ? "Submitting..." : "Submit"}
126 </Button>
127 </FormCardFooter>
128 </FormCard>
129 </form>
130 </Form>
131 );
132}