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.
5.6 kB ยท 192 lines
TSX
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193"use client";
import { zodResolver } from "@hookform/resolvers/zod";import { Check, More, Blog, Search as SearchIcon, AI, Team,} from "@openstatus/icons";import { BlueskyIcon, GitHubIcon, LinkedInIcon, XIcon, YouTubeIcon,} from "@openstatus/icons/brand";import { Badge } from "@openstatus/ui/components/ui/badge";import { Button } from "@openstatus/ui/components/ui/button";import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage,} from "@openstatus/ui/components/ui/form";import { Input } from "@openstatus/ui/components/ui/input";import { RadioGroup, RadioGroupItem,} from "@openstatus/ui/components/ui/radio-group";import { cn } from "@openstatus/ui/lib/utils";import { useTransition } from "react";import { useForm } from "react-hook-form";import { toast } from "sonner";import { z } from "zod";
import { Note } from "@/components/common/note";
const sources = [ { id: "search-engine", title: "Search Engine", icon: SearchIcon }, { id: "github", title: "GitHub", icon: GitHubIcon }, { id: "linkedin", title: "LinkedIn", icon: LinkedInIcon }, { id: "bluesky", title: "Bluesky", icon: BlueskyIcon }, { id: "twitter", title: "X / Twitter", icon: XIcon }, { id: "ai-chat", title: "AI Chat", icon: AI }, { id: "recommendation", title: "Friend / Colleague", icon: Team }, { id: "youtube", title: "YouTube", icon: YouTubeIcon }, { id: "blog", title: "Blog / Article", icon: Blog }, { id: "other", title: "Other", icon: More },] as const;
const schema = z.object({ source: z.string().min(1, "Please select an option"), other: z.string().optional(),});
export type FormValues = z.infer<typeof schema>;
export function QuestionForm({ onSubmit, defaultValues, className, ...props}: Omit<React.ComponentProps<"form">, "onSubmit"> & { defaultValues?: FormValues; onSubmit: (values: FormValues) => Promise<void>;}) { const [isPending, startTransition] = useTransition(); const form = useForm<FormValues>({ resolver: zodResolver(schema), defaultValues: defaultValues ?? { source: "", other: "", }, }); const watchSource = form.watch("source");
function handleSubmit(values: FormValues) { if (isPending) return;
startTransition(async () => { try { const promise = onSubmit(values); toast.promise(promise, { loading: "Submitting...", success: () => "Submitted", error: "Failed to submit", }); await promise; } catch (error) { console.error(error); } }); }
if (!isPending && form.formState.isSubmitSuccessful) { return ( <Note color="success"> <Check /> Thank you for your feedback! </Note> ); }
return ( <Form {...form}> <form onSubmit={form.handleSubmit(handleSubmit)} className={cn("space-y-3", className)} {...props} > <FormField control={form.control} name="source" render={({ field }) => ( <FormItem> <FormControl> <RadioGroup onValueChange={field.onChange} defaultValue={field.value} className="flex flex-wrap gap-2" > {sources.map((item) => { const selected = field.value === item.id; return ( <FormItem key={item.id} className="space-y-0"> <FormControl> <RadioGroupItem value={item.id} id={item.id} className="sr-only" /> </FormControl> <FormLabel htmlFor={item.id} className="cursor-pointer font-normal" > <Badge variant={selected ? "default" : "outline"} className={cn( "transition-colors", !selected && "text-muted-foreground hover:border-foreground hover:text-foreground", )} > <item.icon className="size-3" /> {item.title} </Badge> </FormLabel> </FormItem> ); })} </RadioGroup> </FormControl> <FormMessage /> </FormItem> )} /> <div className="flex items-center gap-2"> {watchSource === "other" && ( <FormField control={form.control} name="other" render={({ field }) => ( <FormItem> <FormControl> <Input placeholder="Please specify..." className="w-full" {...field} /> </FormControl> </FormItem> )} /> )} <Button size="sm" type="submit" disabled={isPending || !watchSource} variant="default" > {isPending ? "Submitting..." : "Submit"} </Button> </div> </form> </Form> );}