make form grids more consistent

This commit is contained in:
miloschwartz
2026-06-08 15:59:54 -07:00
parent 1b7c1ffa70
commit 135a5d38af
10 changed files with 1201 additions and 1010 deletions
@@ -10,6 +10,8 @@ import { formatAxiosError } from "@app/lib/api";
import { AxiosResponse } from "axios"; import { AxiosResponse } from "axios";
import { import {
SettingsContainer, SettingsContainer,
SettingsFormCell,
SettingsFormGrid,
SettingsSection, SettingsSection,
SettingsSectionHeader, SettingsSectionHeader,
SettingsSectionTitle, SettingsSectionTitle,
@@ -1324,7 +1326,8 @@ export default function BillingPage() {
</SettingsSectionDescription> </SettingsSectionDescription>
</SettingsSectionHeader> </SettingsSectionHeader>
<SettingsSectionBody> <SettingsSectionBody>
<div className="w-full md:w-1/2"> <SettingsFormGrid>
<SettingsFormCell span="half">
<div className="flex flex-col md:flex-row items-start md:items-center justify-between gap-4 border rounded-lg p-4"> <div className="flex flex-col md:flex-row items-start md:items-center justify-between gap-4 border rounded-lg p-4">
<div> <div>
<div className="text-sm text-muted-foreground mb-1"> <div className="text-sm text-muted-foreground mb-1">
@@ -1359,7 +1362,8 @@ export default function BillingPage() {
"Manage your subscription for paid self-hosted license keys and download invoices."} "Manage your subscription for paid self-hosted license keys and download invoices."}
</p> </p>
</div> </div>
</div> </SettingsFormCell>
</SettingsFormGrid>
</SettingsSectionBody> </SettingsSectionBody>
</SettingsSection> </SettingsSection>
)} )}
@@ -9,6 +9,8 @@ import {
} from "@app/components/InfoSection"; } from "@app/components/InfoSection";
import { import {
SettingsContainer, SettingsContainer,
SettingsFormCell,
SettingsFormGrid,
SettingsSection, SettingsSection,
SettingsSectionBody, SettingsSectionBody,
SettingsSectionDescription, SettingsSectionDescription,
@@ -257,9 +259,10 @@ export default function Page() {
e.preventDefault(); // block default enter refresh e.preventDefault(); // block default enter refresh
} }
}} }}
className="space-y-4 grid gap-4 grid-cols-1 md:grid-cols-2 items-start"
id="create-client-form" id="create-client-form"
> >
<SettingsFormGrid>
<SettingsFormCell span="half">
<FormField <FormField
control={form.control} control={form.control}
name="name" name="name"
@@ -283,7 +286,8 @@ export default function Page() {
</FormItem> </FormItem>
)} )}
/> />
<div className="flex items-center justify-end md:col-start-2"> </SettingsFormCell>
<SettingsFormCell className="flex items-center justify-end md:col-span-2">
<Button <Button
type="button" type="button"
variant="ghost" variant="ghost"
@@ -302,15 +306,18 @@ export default function Page() {
)} )}
{t("advancedSettings")} {t("advancedSettings")}
</Button> </Button>
</div> </SettingsFormCell>
{showAdvancedSettings && ( {showAdvancedSettings && (
<SettingsFormCell span="full">
<FormField <FormField
control={form.control} control={form.control}
name="subnet" name="subnet"
render={({ field }) => ( render={({ field }) => (
<FormItem className="md:col-start-1 md:col-span-2"> <FormItem>
<FormLabel> <FormLabel>
{t("clientAddress")} {t(
"clientAddress"
)}
</FormLabel> </FormLabel>
<FormControl> <FormControl>
<Input <Input
@@ -330,7 +337,9 @@ export default function Page() {
</FormItem> </FormItem>
)} )}
/> />
</SettingsFormCell>
)} )}
</SettingsFormGrid>
</form> </form>
</Form> </Form>
</SettingsSectionBody> </SettingsSectionBody>
@@ -20,6 +20,8 @@ import {
SettingsSectionBody, SettingsSectionBody,
SettingsSectionDescription, SettingsSectionDescription,
SettingsSectionFooter, SettingsSectionFooter,
SettingsFormCell,
SettingsFormGrid,
SettingsSectionForm, SettingsSectionForm,
SettingsSectionHeader, SettingsSectionHeader,
SettingsSectionTitle SettingsSectionTitle
@@ -616,9 +618,10 @@ export default function GeneralForm() {
<Form {...form}> <Form {...form}>
<form <form
action={formAction} action={formAction}
className="space-y-4"
id="general-settings-form" id="general-settings-form"
> >
<SettingsFormGrid>
<SettingsFormCell span="full">
<FormField <FormField
control={form.control} control={form.control}
name="enabled" name="enabled"
@@ -652,8 +655,9 @@ export default function GeneralForm() {
</FormItem> </FormItem>
)} )}
/> />
</SettingsFormCell>
<div className="grid grid-cols-2 gap-4"> <SettingsFormCell span="half">
<FormField <FormField
control={form.control} control={form.control}
name="name" name="name"
@@ -663,13 +667,17 @@ export default function GeneralForm() {
{t("name")} {t("name")}
</FormLabel> </FormLabel>
<FormControl> <FormControl>
<Input {...field} /> <Input
{...field}
/>
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
)} )}
/> />
</SettingsFormCell>
<SettingsFormCell span="half">
<FormField <FormField
control={form.control} control={form.control}
name="niceId" name="niceId"
@@ -690,12 +698,12 @@ export default function GeneralForm() {
</FormItem> </FormItem>
)} )}
/> />
</div> </SettingsFormCell>
{!["http", "ssh", "rdp", "vnc"].includes( {!["http", "ssh", "rdp", "vnc"].includes(
resource.mode resource.mode
) && ( ) && (
<> <SettingsFormCell span="half">
<FormField <FormField
control={form.control} control={form.control}
name="proxyPort" name="proxyPort"
@@ -717,9 +725,12 @@ export default function GeneralForm() {
) )
: "" : ""
} }
onChange={(e) => onChange={(
e
) =>
field.onChange( field.onChange(
e.target e
.target
.value .value
? parseInt( ? parseInt(
e e
@@ -740,17 +751,19 @@ export default function GeneralForm() {
</FormItem> </FormItem>
)} )}
/> />
</> </SettingsFormCell>
)} )}
{["http", "ssh", "rdp", "vnc"].includes( {["http", "ssh", "rdp", "vnc"].includes(
resource.mode resource.mode
) && ( ) && (
<div className="space-y-4"> <SettingsFormCell span="full">
<div id="resource-domain-picker"> <div id="resource-domain-picker">
<DomainPicker <DomainPicker
allowWildcard={true} allowWildcard={true}
key={resource.resourceId} key={
resource.resourceId
}
orgId={orgId as string} orgId={orgId as string}
cols={2} cols={2}
defaultSubdomain={ defaultSubdomain={
@@ -767,7 +780,9 @@ export default function GeneralForm() {
resourceFullDomainName || resourceFullDomainName ||
undefined undefined
} }
onDomainChange={(res) => { onDomainChange={(
res
) => {
if (res === null) { if (res === null) {
form.setValue( form.setValue(
"domainId", "domainId",
@@ -797,9 +812,10 @@ export default function GeneralForm() {
}} }}
/> />
</div> </div>
</div> </SettingsFormCell>
)} )}
{showResourcePolicy && ( {showResourcePolicy && (
<SettingsFormCell span="half">
<div className="space-y-2"> <div className="space-y-2">
<FormLabel> <FormLabel>
{t("sharedPolicy")} {t("sharedPolicy")}
@@ -810,13 +826,17 @@ export default function GeneralForm() {
"none" "none"
} }
orgId={org.org.orgId} orgId={org.org.orgId}
value={selectedSharedPolicyId} value={
selectedSharedPolicyId
}
onChange={ onChange={
setSelectedSharedPolicyId setSelectedSharedPolicyId
} }
/> />
</div> </div>
</SettingsFormCell>
)} )}
</SettingsFormGrid>
</form> </form>
</Form> </Form>
</SettingsSectionForm> </SettingsSectionForm>
@@ -5,6 +5,8 @@ import {
SettingsSection, SettingsSection,
SettingsSectionBody, SettingsSectionBody,
SettingsSectionDescription, SettingsSectionDescription,
SettingsFormCell,
SettingsFormGrid,
SettingsSectionForm, SettingsSectionForm,
SettingsSectionHeader, SettingsSectionHeader,
SettingsSectionTitle, SettingsSectionTitle,
@@ -410,17 +412,25 @@ function SshServerForm({
<Form {...form}> <Form {...form}>
<SettingsSectionBody> <SettingsSectionBody>
<SettingsSectionForm variant="half"> <SettingsSectionForm variant="half">
<SettingsFormGrid>
<SettingsFormCell span="full">
<div className="space-y-2"> <div className="space-y-2">
<p className="font-semibold text-sm">{t("sshServerMode")}</p> <p className="font-semibold text-sm">
{t("sshServerMode")}
</p>
<Badge variant="secondary"> <Badge variant="secondary">
{sshServerMode == "standard" {sshServerMode == "standard"
? t("sshServerModeStandard") ? t("sshServerModeStandard")
: t("sshServerModePangolin")} : t("sshServerModePangolin")}
</Badge> </Badge>
</div> </div>
</SettingsFormCell>
<SettingsFormCell span="full">
<div className="space-y-2"> <div className="space-y-2">
<p className="font-semibold text-sm">{t("sshAuthenticationMethod")}</p> <p className="font-semibold text-sm">
{t("sshAuthenticationMethod")}
</p>
<StrategySelect<"passthrough" | "push"> <StrategySelect<"passthrough" | "push">
value={pamMode} value={pamMode}
options={authMethodOptions} options={authMethodOptions}
@@ -432,10 +442,14 @@ function SshServerForm({
cols={2} cols={2}
/> />
</div> </div>
</SettingsFormCell>
{showDaemonLocation && ( {showDaemonLocation && (
<SettingsFormCell span="full">
<div className="space-y-2"> <div className="space-y-2">
<p className="font-semibold text-sm">{t("sshAuthDaemonLocation")}</p> <p className="font-semibold text-sm">
{t("sshAuthDaemonLocation")}
</p>
<StrategySelect<"site" | "remote"> <StrategySelect<"site" | "remote">
value={standardDaemonLocation} value={standardDaemonLocation}
options={daemonLocationOptions} options={daemonLocationOptions}
@@ -443,7 +457,9 @@ function SshServerForm({
form.setValue( form.setValue(
"standardDaemonLocation", "standardDaemonLocation",
value, value,
{ shouldValidate: true } {
shouldValidate: true
}
) )
} }
cols={2} cols={2}
@@ -461,10 +477,11 @@ function SshServerForm({
</a> </a>
</p> </p>
</div> </div>
</SettingsFormCell>
)} )}
{showDaemonPort && ( {showDaemonPort && (
<div className="w-full md:w-1/2"> <SettingsFormCell span="half">
<FormField <FormField
control={form.control} control={form.control}
name="authDaemonPort" name="authDaemonPort"
@@ -485,19 +502,24 @@ function SshServerForm({
</FormItem> </FormItem>
)} )}
/> />
</div> </SettingsFormCell>
)} )}
<div className="space-y-3"> <SettingsFormCell span="full">
<SettingsSubsectionHeader> <SettingsSubsectionHeader>
<SettingsSubsectionTitle> <SettingsSubsectionTitle>
{t("sshServerDestination")} {t("sshServerDestination")}
</SettingsSubsectionTitle> </SettingsSubsectionTitle>
<SettingsSubsectionDescription> <SettingsSubsectionDescription>
{t("sshServerDestinationDescription")} {t(
"sshServerDestinationDescription"
)}
</SettingsSubsectionDescription> </SettingsSubsectionDescription>
</SettingsSubsectionHeader> </SettingsSubsectionHeader>
</SettingsFormCell>
{isNative ? ( {isNative ? (
<SettingsFormCell span="half">
<FormField <FormField
control={form.control} control={form.control}
name="selectedNativeSite" name="selectedNativeSite"
@@ -514,7 +536,7 @@ function SshServerForm({
<Button <Button
variant="outline" variant="outline"
role="combobox" role="combobox"
className="w-full max-w-xs justify-between font-normal" className="w-full justify-between font-normal"
> >
<span className="truncate"> <span className="truncate">
{selectedNativeSite?.name ?? {selectedNativeSite?.name ??
@@ -554,7 +576,9 @@ function SshServerForm({
</FormItem> </FormItem>
)} )}
/> />
</SettingsFormCell>
) : useMultiSiteTargetForm ? ( ) : useMultiSiteTargetForm ? (
<SettingsFormCell span="full">
<BrowserGatewayTargetForm <BrowserGatewayTargetForm
control={form.control} control={form.control}
orgId={orgId} orgId={orgId}
@@ -565,7 +589,9 @@ function SshServerForm({
learnMoreHref="https://docs.pangolin.net/manage/resources/public/ssh" learnMoreHref="https://docs.pangolin.net/manage/resources/public/ssh"
defaultPort={22} defaultPort={22}
/> />
</SettingsFormCell>
) : ( ) : (
<SettingsFormCell span="full">
<BrowserGatewayTargetForm <BrowserGatewayTargetForm
control={form.control} control={form.control}
orgId={orgId} orgId={orgId}
@@ -576,8 +602,9 @@ function SshServerForm({
learnMoreHref="https://docs.pangolin.net/manage/resources/public/ssh" learnMoreHref="https://docs.pangolin.net/manage/resources/public/ssh"
defaultPort={22} defaultPort={22}
/> />
</SettingsFormCell>
)} )}
</div> </SettingsFormGrid>
</SettingsSectionForm> </SettingsSectionForm>
</SettingsSectionBody> </SettingsSectionBody>
<form action={formAction} className="flex justify-end mt-4"> <form action={formAction} className="flex justify-end mt-4">
@@ -4,6 +4,8 @@ import CopyTextBox from "@app/components/CopyTextBox";
import DomainPicker from "@app/components/DomainPicker"; import DomainPicker from "@app/components/DomainPicker";
import { import {
SettingsContainer, SettingsContainer,
SettingsFormCell,
SettingsFormGrid,
SettingsSection, SettingsSection,
SettingsSectionBody, SettingsSectionBody,
SettingsSectionDescription, SettingsSectionDescription,
@@ -806,24 +808,33 @@ export default function Page() {
</SettingsSectionHeader> </SettingsSectionHeader>
<SettingsSectionBody> <SettingsSectionBody>
<SettingsSectionForm variant="half"> <SettingsSectionForm variant="half">
{/* Name */} <SettingsFormGrid>
<SettingsFormCell span="half">
<Form {...baseForm}> <Form {...baseForm}>
<form <form
onKeyDown={(e) => { onKeyDown={(e) => {
if (e.key === "Enter") { if (
e.key ===
"Enter"
) {
e.preventDefault(); e.preventDefault();
} }
}} }}
className="grid gap-4 grid-cols-1 md:grid-cols-2 items-start"
id="base-resource-form" id="base-resource-form"
> >
<FormField <FormField
control={baseForm.control} control={
baseForm.control
}
name="name" name="name"
render={({ field }) => ( render={({
field
}) => (
<FormItem> <FormItem>
<FormLabel> <FormLabel>
{t("name")} {t(
"name"
)}
</FormLabel> </FormLabel>
<FormControl> <FormControl>
<Input <Input
@@ -841,8 +852,9 @@ export default function Page() {
/> />
</form> </form>
</Form> </Form>
</SettingsFormCell>
{/* Inline Type Selector */} <SettingsFormCell span="full">
<div className="space-y-2"> <div className="space-y-2">
<p className="text-sm font-medium"> <p className="text-sm font-medium">
{t("type")} {t("type")}
@@ -850,19 +862,26 @@ export default function Page() {
<OptionSelect<NewResourceType> <OptionSelect<NewResourceType>
options={typeOptions} options={typeOptions}
value={resourceType} value={resourceType}
onChange={setResourceType} onChange={
setResourceType
}
cols={6} cols={6}
/> />
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">
{t("resourceTypeDescription")} {t(
"resourceTypeDescription"
)}
</p> </p>
</div> </div>
</SettingsFormCell>
{/* Domain/Subdomain (HTTP-based types) */}
{isHttpResource && ( {isHttpResource && (
<SettingsFormCell span="full">
<Form {...httpForm}> <Form {...httpForm}>
<FormField <FormField
control={httpForm.control} control={
httpForm.control
}
name="domainId" name="domainId"
render={() => ( render={() => (
<FormItem> <FormItem>
@@ -880,7 +899,9 @@ export default function Page() {
onDomainChange={( onDomainChange={(
res res
) => { ) => {
if (!res) if (
!res
)
return; return;
httpForm.setValue( httpForm.setValue(
"subdomain", "subdomain",
@@ -910,18 +931,21 @@ export default function Page() {
)} )}
/> />
</Form> </Form>
</SettingsFormCell>
)} )}
{/* Proxy Port (TCP/UDP types) */}
{!isHttpResource && ( {!isHttpResource && (
<SettingsFormCell span="half">
<Form {...tcpUdpForm}> <Form {...tcpUdpForm}>
<form <form
onKeyDown={(e) => { onKeyDown={(e) => {
if (e.key === "Enter") { if (
e.key ===
"Enter"
) {
e.preventDefault(); e.preventDefault();
} }
}} }}
className="grid gap-4 grid-cols-1 md:grid-cols-2 items-start"
id="tcp-udp-settings-form" id="tcp-udp-settings-form"
> >
<FormField <FormField
@@ -929,7 +953,9 @@ export default function Page() {
tcpUdpForm.control tcpUdpForm.control
} }
name="proxyPort" name="proxyPort"
render={({ field }) => ( render={({
field
}) => (
<FormItem> <FormItem>
<FormLabel> <FormLabel>
{t( {t(
@@ -971,7 +997,9 @@ export default function Page() {
/> />
</form> </form>
</Form> </Form>
</SettingsFormCell>
)} )}
</SettingsFormGrid>
</SettingsSectionForm> </SettingsSectionForm>
</SettingsSectionBody> </SettingsSectionBody>
</SettingsSection> </SettingsSection>
@@ -1005,21 +1033,34 @@ export default function Page() {
> >
<SettingsSectionBody> <SettingsSectionBody>
<SettingsSectionForm variant="half"> <SettingsSectionForm variant="half">
{/* Mode */} <SettingsFormGrid>
<SettingsFormCell span="full">
<div className="space-y-2"> <div className="space-y-2">
<p className="font-semibold text-sm">{t("sshServerMode")}</p> <p className="font-semibold text-sm">
{t("sshServerMode")}
</p>
<StrategySelect< <StrategySelect<
"standard" | "native" "standard" | "native"
> >
value={sshServerMode} value={sshServerMode}
options={sshModeOptions} options={
onChange={setSshServerMode} sshModeOptions
}
onChange={
setSshServerMode
}
cols={2} cols={2}
/> />
</div> </div>
</SettingsFormCell>
<SettingsFormCell span="full">
<div className="space-y-2"> <div className="space-y-2">
<p className="font-semibold text-sm">{t("sshAuthenticationMethod")}</p> <p className="font-semibold text-sm">
{t(
"sshAuthenticationMethod"
)}
</p>
<StrategySelect< <StrategySelect<
"passthrough" | "push" "passthrough" | "push"
> >
@@ -1031,11 +1072,16 @@ export default function Page() {
cols={2} cols={2}
/> />
</div> </div>
</SettingsFormCell>
{/* Daemon Location (standard + push) */}
{showDaemonLocation && ( {showDaemonLocation && (
<SettingsFormCell span="full">
<div className="space-y-2"> <div className="space-y-2">
<p className="font-semibold text-sm">{t("sshAuthDaemonLocation")}</p> <p className="font-semibold text-sm">
{t(
"sshAuthDaemonLocation"
)}
</p>
<StrategySelect< <StrategySelect<
"site" | "remote" "site" | "remote"
> >
@@ -1060,17 +1106,21 @@ export default function Page() {
rel="noopener noreferrer" rel="noopener noreferrer"
className="text-primary hover:underline inline-flex items-center gap-1" className="text-primary hover:underline inline-flex items-center gap-1"
> >
{t("learnMore")} {t(
"learnMore"
)}
<ExternalLink className="size-3.5 shrink-0" /> <ExternalLink className="size-3.5 shrink-0" />
</a> </a>
</p> </p>
</div> </div>
</SettingsFormCell>
)} )}
{/* Daemon Port (standard + push + remote) */}
{showDaemonPort && ( {showDaemonPort && (
<Form {...sshDaemonPortForm}> <SettingsFormCell span="half">
<div className="w-full md:w-1/2"> <Form
{...sshDaemonPortForm}
>
<FormField <FormField
control={ control={
sshDaemonPortForm.control sshDaemonPortForm.control
@@ -1101,12 +1151,11 @@ export default function Page() {
</FormItem> </FormItem>
)} )}
/> />
</div>
</Form> </Form>
</SettingsFormCell>
)} )}
{/* Server Destination */} <SettingsFormCell span="full">
<div className="space-y-3">
<SettingsSubsectionHeader> <SettingsSubsectionHeader>
<SettingsSubsectionTitle> <SettingsSubsectionTitle>
{t( {t(
@@ -1119,18 +1168,25 @@ export default function Page() {
)} )}
</SettingsSubsectionDescription> </SettingsSubsectionDescription>
</SettingsSubsectionHeader> </SettingsSubsectionHeader>
</SettingsFormCell>
{isNative ? ( {isNative ? (
<SettingsFormCell span="half">
<Popover <Popover
open={nativeSiteOpen} open={
nativeSiteOpen
}
onOpenChange={ onOpenChange={
setNativeSiteOpen setNativeSiteOpen
} }
> >
<PopoverTrigger asChild> <PopoverTrigger
asChild
>
<Button <Button
variant="outline" variant="outline"
role="combobox" role="combobox"
className="w-full md:w-1/2 justify-between font-normal" className="w-full justify-between font-normal"
> >
<span className="truncate"> <span className="truncate">
{nativeSelectedSite?.name ?? {nativeSelectedSite?.name ??
@@ -1162,10 +1218,12 @@ export default function Page() {
/> />
</PopoverContent> </PopoverContent>
</Popover> </Popover>
</SettingsFormCell>
) : standardDaemonLocation !== ) : standardDaemonLocation !==
"site" || "site" ||
pamMode === pamMode ===
"passthrough" ? ( "passthrough" ? (
<SettingsFormCell span="full">
<Form {...bgTargetForm}> <Form {...bgTargetForm}>
<BrowserGatewayTargetForm <BrowserGatewayTargetForm
control={ control={
@@ -1182,7 +1240,9 @@ export default function Page() {
defaultPort={22} defaultPort={22}
/> />
</Form> </Form>
</SettingsFormCell>
) : ( ) : (
<SettingsFormCell span="full">
<Form {...bgTargetForm}> <Form {...bgTargetForm}>
<BrowserGatewayTargetForm <BrowserGatewayTargetForm
control={ control={
@@ -1191,7 +1251,9 @@ export default function Page() {
orgId={ orgId={
orgId as string orgId as string
} }
multiSite={false} multiSite={
false
}
siteField="selectedSite" siteField="selectedSite"
destinationField="destination" destinationField="destination"
destinationPortField="destinationPort" destinationPortField="destinationPort"
@@ -1199,8 +1261,9 @@ export default function Page() {
defaultPort={22} defaultPort={22}
/> />
</Form> </Form>
</SettingsFormCell>
)} )}
</div> </SettingsFormGrid>
</SettingsSectionForm> </SettingsSectionForm>
</SettingsSectionBody> </SettingsSectionBody>
</fieldset> </fieldset>
+23 -12
View File
@@ -2,6 +2,8 @@
import { import {
SettingsContainer, SettingsContainer,
SettingsFormCell,
SettingsFormGrid,
SettingsSection, SettingsSection,
SettingsSectionBody, SettingsSectionBody,
SettingsSectionDescription, SettingsSectionDescription,
@@ -514,9 +516,10 @@ export default function Page() {
e.preventDefault(); // block default enter refresh e.preventDefault(); // block default enter refresh
} }
}} }}
className="space-y-4 grid gap-4 grid-cols-1 md:grid-cols-2 items-start"
id="create-site-form" id="create-site-form"
> >
<SettingsFormGrid>
<SettingsFormCell span="half">
<FormField <FormField
control={form.control} control={form.control}
name="name" name="name"
@@ -540,8 +543,9 @@ export default function Page() {
</FormItem> </FormItem>
)} )}
/> />
{form.watch("method") === "newt" && ( {form.watch("method") ===
<div className="flex items-center justify-end md:col-start-2"> "newt" && (
<>
<Button <Button
type="button" type="button"
variant="ghost" variant="ghost"
@@ -551,24 +555,27 @@ export default function Page() {
!showAdvancedSettings !showAdvancedSettings
) )
} }
className="flex items-center gap-2" className="mt-2 flex items-center gap-2 -ml-3"
> >
{showAdvancedSettings ? ( {showAdvancedSettings ? (
<ChevronUp className="h-4 w-4" /> <ChevronUp className="h-4 w-4" />
) : ( ) : (
<ChevronDown className="h-4 w-4" /> <ChevronDown className="h-4 w-4" />
)} )}
{t("advancedSettings")} {t(
</Button> "advancedSettings"
</div>
)} )}
{form.watch("method") === "newt" && </Button>
showAdvancedSettings && ( {showAdvancedSettings && (
<FormField <FormField
control={form.control} control={
form.control
}
name="clientAddress" name="clientAddress"
render={({ field }) => ( render={({
<FormItem className="md:col-start-1 md:col-span-2"> field
}) => (
<FormItem className="mt-4">
<FormLabel> <FormLabel>
{t( {t(
"siteAddress" "siteAddress"
@@ -606,6 +613,10 @@ export default function Page() {
)} )}
/> />
)} )}
</>
)}
</SettingsFormCell>
</SettingsFormGrid>
</form> </form>
</Form> </Form>
</SettingsSectionBody> </SettingsSectionBody>
+43
View File
@@ -43,6 +43,49 @@ export function SettingsSectionForm({
); );
} }
export function SettingsFormGrid({
children,
className
}: {
children: React.ReactNode;
className?: string;
}) {
return (
<div
className={cn(
"grid grid-cols-1 md:grid-cols-4 gap-4 items-start",
className
)}
>
{children}
</div>
);
}
export function SettingsFormCell({
children,
span = "half",
className
}: {
children: React.ReactNode;
span?: "quarter" | "half" | "full";
className?: string;
}) {
return (
<div
className={cn(
"min-w-0",
span === "quarter" && "md:col-span-1",
span === "half" && "md:col-span-2",
span === "full" && "md:col-span-4",
className
)}
>
{children}
</div>
);
}
export function SettingsSectionTitle({ export function SettingsSectionTitle({
children children
}: { }: {
@@ -1,6 +1,8 @@
"use client"; "use client";
import { import {
SettingsFormCell,
SettingsFormGrid,
SettingsSection, SettingsSection,
SettingsSectionBody, SettingsSectionBody,
SettingsSectionDescription, SettingsSectionDescription,
@@ -111,7 +113,8 @@ export function PolicyAuthStackSectionCreate({
</SettingsSectionDescription> </SettingsSectionDescription>
</SettingsSectionHeader> </SettingsSectionHeader>
<SettingsSectionBody> <SettingsSectionBody>
<div className="w-full md:w-1/2"> <SettingsFormGrid>
<SettingsFormCell span="half">
<PolicyAuthSsoSection <PolicyAuthSsoSection
sso={Boolean(sso)} sso={Boolean(sso)}
onSsoChange={(active) => onSsoChange={(active) =>
@@ -169,7 +172,8 @@ export function PolicyAuthStackSectionCreate({
/> />
} }
/> />
</div> </SettingsFormCell>
</SettingsFormGrid>
<SettingsSubsectionHeader> <SettingsSubsectionHeader>
<SettingsSubsectionTitle> <SettingsSubsectionTitle>
@@ -1,6 +1,8 @@
"use client"; "use client";
import { import {
SettingsFormCell,
SettingsFormGrid,
SettingsSection, SettingsSection,
SettingsSectionBody, SettingsSectionBody,
SettingsSectionDescription, SettingsSectionDescription,
@@ -475,7 +477,8 @@ export function PolicyAuthStackSectionEdit({
{isResourceOverlay && ( {isResourceOverlay && (
<SharedPolicyResourceNotice section="authentication" /> <SharedPolicyResourceNotice section="authentication" />
)} )}
<div className="w-full md:w-1/2"> <SettingsFormGrid>
<SettingsFormCell span="half">
<PolicyAuthSsoSection <PolicyAuthSsoSection
sso={Boolean(sso)} sso={Boolean(sso)}
onSsoChange={(active) => onSsoChange={(active) =>
@@ -508,7 +511,9 @@ export function PolicyAuthStackSectionEdit({
} }
disabled={isLoading} disabled={isLoading}
restrictAdminRole restrictAdminRole
lockedIds={policyRoleLockedIds} lockedIds={
policyRoleLockedIds
}
/> />
) : ( ) : (
<FormField <FormField
@@ -540,9 +545,13 @@ export function PolicyAuthStackSectionEdit({
<UsersSelector <UsersSelector
orgId={orgId} orgId={orgId}
selectedUsers={overlayUsers} selectedUsers={overlayUsers}
onSelectUsers={setCombinedUsers} onSelectUsers={
setCombinedUsers
}
disabled={isLoading} disabled={isLoading}
lockedIds={policyUserLockedIds} lockedIds={
policyUserLockedIds
}
/> />
) : ( ) : (
<FormField <FormField
@@ -569,7 +578,8 @@ export function PolicyAuthStackSectionEdit({
) )
} }
/> />
</div> </SettingsFormCell>
</SettingsFormGrid>
<SettingsSubsectionHeader> <SettingsSubsectionHeader>
<SettingsSubsectionTitle> <SettingsSubsectionTitle>
+1 -1
View File
@@ -183,7 +183,7 @@ export function SharedPolicySelect({
role="combobox" role="combobox"
disabled={disabled} disabled={disabled}
className={cn( className={cn(
"w-full justify-between font-normal md:w-1/2", "w-full justify-between font-normal",
value !== null && value !== null &&
!resolvedLabel && !resolvedLabel &&
!fetchedPolicy?.name && !fetchedPolicy?.name &&