Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/openstatusHQ/openstatus. ๐ซ Status page with uptime monitoring & API monitoring as code ๐ซ openstatus.dev
bun drizzle-orm monitoring monitoring-as-code nextjs observability on-call open-source shadcn-ui status-page statuspage synthetic-monitoring tinybird turso uptime uptime-checker uptime-monitor
Something went wrong. Try again.
9.2 kB ยท 249 lines
TSX
at incident-api
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250"use client";
import { zodResolver } from "@hookform/resolvers/zod";import type { RouterOutputs } from "@openstatus/api";import { Checkbox } from "@openstatus/ui/components/ui/checkbox";import { Form } from "@openstatus/ui/components/ui/form";import { FormControl, FormField, FormItem, FormLabel,} from "@openstatus/ui/components/ui/form";import { Input } from "@openstatus/ui/components/ui/input";import { cn } from "@openstatus/ui/lib/utils";import { isTRPCClientError } from "@trpc/client";import { useExtracted } from "next-intl";import { useTransition } from "react";import { useForm } from "react-hook-form";import { toast } from "sonner";import { z } from "zod";
import { StatusBlankContainer, StatusBlankDescription, StatusBlankTitle,} from "../status-page/status-blank";
type Page = NonNullable<RouterOutputs["statusPage"]["get"]>;
const schema = z.object({ email: z.email(), subscribeComponents: z.boolean(), pageComponents: z.array(z.number().int().positive()),});
export type FormValues = z.infer<typeof schema>;
export function FormSubscribeEmail({ page, onSubmit, className, ...props}: Omit<React.ComponentProps<"form">, "onSubmit"> & { onSubmit: (values: FormValues) => Promise<void>; onSubmitCallback?: () => void; page?: Page | null;}) { const t = useExtracted(); const form = useForm<FormValues>({ resolver: zodResolver(schema), defaultValues: { email: "", subscribeComponents: false, pageComponents: [], }, }); const [isPending, startTransition] = useTransition();
function submitAction(values: FormValues) { if (isPending) return;
startTransition(async () => { try { const promise = onSubmit(values); toast.promise(promise, { loading: t("Subscribing..."), success: t("Subscribed"), error: (error) => { if (isTRPCClientError(error)) { return error.message; } return t("Failed to subscribe"); }, }); await promise; } catch (error) { console.error(error); } }); }
return ( <Form {...form}> <form onSubmit={form.handleSubmit(submitAction)} className={cn("flex flex-col gap-2", className)} {...props} > <FormField control={form.control} name="email" render={({ field }) => ( <FormItem> <FormLabel className="sr-only">{t("Email")}</FormLabel> <FormControl> <Input placeholder="subscribe@me.com" {...field} /> </FormControl> </FormItem> )} />
<FormField control={form.control} name="subscribeComponents" render={({ field }) => ( <FormItem className="flex items-center gap-2"> <FormControl> <Checkbox checked={field.value} onCheckedChange={field.onChange} /> </FormControl> <FormLabel>{t("Subscribe to specific components")}</FormLabel> </FormItem> )} /> {form.watch("subscribeComponents") && ( <div className="border-border bg-muted flex max-h-56 flex-col gap-2 overflow-y-auto rounded-md border p-2"> {page?.trackers && page.trackers.length > 0 ? ( page.trackers.map((tracker) => { if (tracker.type === "group") { const groupIds = tracker.components.map((c) => c.id); return ( <div key={tracker.groupId} className="flex flex-col gap-2"> <FormField control={form.control} name="pageComponents" render={({ field }) => { const allChecked = groupIds.every((id) => field.value?.includes(id), ); const someChecked = groupIds.some((id) => field.value?.includes(id), ); return ( <FormItem className="flex items-center gap-2"> <FormControl> <Checkbox checked={ allChecked ? true : someChecked ? "indeterminate" : false } onCheckedChange={(checked) => { const value = field.value ?? []; if (checked) { field.onChange([ ...new Set([...value, ...groupIds]), ]); } else { field.onChange( value.filter( (id) => !groupIds.includes(id), ), ); } }} /> </FormControl> <FormLabel>{tracker.groupName}</FormLabel> </FormItem> ); }} /> {tracker.components.map((component) => ( <FormField key={component.id} control={form.control} name="pageComponents" render={({ field }) => ( <FormItem className="flex items-center gap-2 pl-6"> <FormControl> <Checkbox checked={field.value?.includes(component.id)} onCheckedChange={(checked) => { const value = field.value ?? []; if (checked) { field.onChange([...value, component.id]); } else { field.onChange( value.filter( (id) => id !== component.id, ), ); } }} /> </FormControl> <FormLabel>{component.name}</FormLabel> </FormItem> )} /> ))} </div> ); } return ( <FormField key={tracker.component.id} control={form.control} name="pageComponents" render={({ field }) => ( <FormItem className="flex items-center gap-2"> <FormControl> <Checkbox checked={field.value?.includes( tracker.component.id, )} onCheckedChange={(checked) => { const value = field.value ?? []; if (checked) { field.onChange([ ...value, tracker.component.id, ]); } else { field.onChange( value.filter( (id) => id !== tracker.component.id, ), ); } }} /> </FormControl> <FormLabel>{tracker.component.name}</FormLabel> </FormItem> )} /> ); }) ) : ( <StatusBlankContainer> <StatusBlankTitle> {t("No components to subscribe to")} </StatusBlankTitle> <StatusBlankDescription> {t("This page has no components to subscribe to.")} </StatusBlankDescription> </StatusBlankContainer> )} </div> )} </form> </Form> );}