Merge branch 'dev' into resource-policies-restyle

This commit is contained in:
miloschwartz
2026-06-08 12:00:08 -07:00
45 changed files with 781 additions and 1468 deletions

View File

@@ -48,17 +48,46 @@ export type BrowserGatewayTargetFormProps<T extends FieldValues = FieldValues> =
export function BrowserGatewayTargetForm<T extends FieldValues>(
props: BrowserGatewayTargetFormProps<T>
) {
// 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 <Input> 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 <Input>
// 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;
@@ -141,7 +170,17 @@ export function BrowserGatewayTargetForm<T extends FieldValues>(
<FormItem>
<FormLabel>{t("destination")}</FormLabel>
<FormControl>
<Input {...field} value={field.value ?? ""} />
<Input
name={field.name}
ref={field.ref}
onBlur={field.onBlur}
onChange={field.onChange}
value={
(watchedDestination as
| string
| undefined) ?? ""
}
/>
</FormControl>
<FormMessage />
</FormItem>
@@ -158,8 +197,16 @@ export function BrowserGatewayTargetForm<T extends FieldValues>(
type="number"
min={1}
max={65535}
{...field}
value={field.value ?? ""}
name={field.name}
ref={field.ref}
onBlur={field.onBlur}
onChange={field.onChange}
value={
(watchedDestinationPort as
| string
| number
| undefined) ?? ""
}
/>
</FormControl>
<FormMessage />

View File

@@ -514,6 +514,16 @@ export default function SitesTable({
)}
</DropdownMenuItem>
</Link>
<DropdownMenuItem
onClick={() => {
setSelectedSite(siteRow);
setIsDeleteModalOpen(true);
}}
>
<span className="text-red-500">
{t("delete")}
</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<Link

View File

@@ -123,7 +123,7 @@ export function PolicyAuthStackSectionCreate({
}
allIdps={allIdps}
rolesEditor={
<FormField
<FormField<PolicyFormValues, "roles">
control={parentForm.control}
name="roles"
render={({ field }) => (
@@ -146,7 +146,7 @@ export function PolicyAuthStackSectionCreate({
/>
}
usersEditor={
<FormField
<FormField<PolicyFormValues, "users">
control={parentForm.control}
name="users"
render={({ field }) => (

View File

@@ -725,7 +725,8 @@ export function PolicyAuthStackSectionEdit({
user: headerAuth.user,
password: headerAuth.password,
extendedCompatibility:
headerAuth.extendedCompatibility
headerAuth.extendedCompatibility ??
true
}
: undefined
}