import { getFormProps, getSelectProps, getInputProps, getTextareaProps, useForm, } from "@conform-to/react"; import { parseWithZod } from "@conform-to/zod"; import { InformationCircleIcon, ArrowUpCircleIcon } from "@heroicons/react/20/solid"; import { EnvelopeIcon, ShieldCheckIcon } from "@heroicons/react/24/solid"; import { Form, useActionData, useLocation, useNavigation, useSearchParams } from "@remix-run/react"; import { type ReactNode, useEffect, useState } from "react"; import { type FeedbackType, feedbackTypes, schema } from "~/routes/resources.feedback"; import { Button } from "./primitives/Buttons"; import { Dialog, DialogContent, DialogHeader, DialogTrigger } from "./primitives/Dialog"; import { Fieldset } from "./primitives/Fieldset"; import { FormButtons } from "./primitives/FormButtons"; import { FormError } from "./primitives/FormError"; import { Icon } from "./primitives/Icon"; import { InfoPanel } from "./primitives/InfoPanel"; import { InputGroup } from "./primitives/InputGroup"; import { Label } from "./primitives/Label"; import { Paragraph } from "./primitives/Paragraph"; import { Select, SelectItem } from "./primitives/Select"; import { TextArea } from "./primitives/TextArea"; import { TextLink } from "./primitives/TextLink"; import { DialogClose } from "@radix-ui/react-dialog"; type FeedbackProps = { button: ReactNode; defaultValue?: FeedbackType; onOpenChange?: (open: boolean) => void; }; export function Feedback({ button, defaultValue = "bug", onOpenChange }: FeedbackProps) { const [open, setOpen] = useState(false); const [searchParams, setSearchParams] = useSearchParams(); const location = useLocation(); const lastSubmission = useActionData(); const navigation = useNavigation(); const [type, setType] = useState(defaultValue); const [form, fields] = useForm({ id: "accept-invite", lastResult: lastSubmission as any, onValidate({ formData }) { return parseWithZod(formData, { schema }); }, shouldRevalidate: "onInput", }); useEffect(() => { if ( navigation.formAction === "/resources/feedback" && navigation.state === "loading" && Object.keys(form.allErrors).length === 0 ) { setOpen(false); } }, [navigation.formAction, navigation.state, form.allErrors]); // Handle URL param functionality useEffect(() => { const open = searchParams.get("feedbackPanel"); if (open) { setType(open as FeedbackType); setOpen(true); // Clone instead of mutating in place const next = new URLSearchParams(searchParams); next.delete("feedbackPanel"); setSearchParams(next); } }, [searchParams]); const handleOpenChange = (value: boolean) => { setOpen(value); onOpenChange?.(value); }; return ( {button} Contact us
How can we help? We read every message and will respond as quickly as we can.
{!( type === "feature" || type === "help" || type === "concurrency" || type === "hipaa" ) &&
}
{type === "feature" && ( All our feature requests are public and voted on by the community. The best way to submit your feature request is to{" "} post it to our feedback forum . )} {type === "help" && ( The quickest way to get answers from the Trigger.dev team and community is to{" "} ask in our Discord. )} {type === "concurrency" && ( How much extra concurrency do you need? You can add bundles of 50 for $50/month each. To help us advise you, please let us know what your tasks do, your typical run volume, and if your workload is spiky (many runs at once). )} {type === "hipaa" && ( We offer a signed Business Associate Agreement (BAA) as a paid add-on on any paid plan. To help us get back to you quickly, please include your company name, and a brief description of the PHI workload you plan to run. )} {fields.feedbackType.errors}