"use client"; import { cn } from "@app/lib/cn"; import { ChevronsUpDown, ExternalLink } from "lucide-react"; import { useTranslations } from "next-intl"; import { useState } from "react"; import type { Control, FieldValues, Path } from "react-hook-form"; import { useWatch } from "react-hook-form"; import { MultiSitesSelector, formatMultiSitesSelectorLabel } from "./multi-site-selector"; import { SitesSelector, type Selectedsite } from "./site-selector"; import { Button } from "./ui/button"; import { FormControl, FormField, FormItem, FormLabel, FormMessage } from "./ui/form"; import { Input } from "./ui/input"; import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover"; type BaseProps = { control: Control; orgId: string; destinationField: Path; destinationPortField: Path; learnMoreHref?: string; defaultPort: number; }; type MultiSiteFormProps = BaseProps & { multiSite: true; sitesField: Path; }; type SingleSiteFormProps = BaseProps & { multiSite?: false; siteField: Path; }; export type BrowserGatewayTargetFormProps = | MultiSiteFormProps | SingleSiteFormProps; export function BrowserGatewayTargetForm( props: BrowserGatewayTargetFormProps ) { // IDK MAN REMOVING THIS SEEMS TO CAUSE ISSUES // Opt out of the React Compiler for this component. // // The parent (create page) shares a single `bgTargetForm` instance across // multiple conditionally-rendered Form sections (SSH passthrough/push, RDP, // VNC) and calls `bgTargetForm.reset(...)` in a useEffect when the // resource type changes. react-hook-form's Controller uses an external // subscription that the React Compiler cannot statically reason about, so // with `reactCompiler: true` (see next.config.ts) the Compiler can memoize // the render prop and skip re-rendering the elements when their // bound form values change. The visible symptom is that typing into the // destination/port inputs updates form state but the input itself never // visually updates. The escape hatch is the canonical fix here. "use no memo"; const t = useTranslations(); const [siteOpen, setSiteOpen] = useState(false); const sitesFieldName = props.multiSite === true ? props.sitesField : props.siteField; // Subscribe to field values via useWatch and drive the controlled // elements from these values rather than from the `field.value` returned // by the Controller render prop. Combined with the "use no memo" directive // above, this makes the inputs reliably re-render when their bound form // values change. const watchedSites = useWatch({ control: props.control, name: sitesFieldName }); const watchedDestination = useWatch({ control: props.control, name: props.destinationField }); const watchedDestinationPort = useWatch({ control: props.control, name: props.destinationPortField }); const showMultiSiteDisclaimer = props.multiSite === true && ((watchedSites as Selectedsite[] | undefined)?.length ?? 0) > 1; return (
( {t("sites")} {props.multiSite === true ? ( ) : ( { field.onChange(site); setSiteOpen(false); }} filterTypes={["newt"]} /> )} )} /> ( {t("destination")} )} /> ( {t("port")} )} />
{showMultiSiteDisclaimer && (

{t("bgTargetMultiSiteDisclaimer")}{" "} {t("learnMore")}

)}
); }