mirror of
https://github.com/fosrl/pangolin.git
synced 2026-06-18 13:22:03 +00:00
fix form responsiveness
This commit is contained in:
@@ -17,7 +17,8 @@ import {
|
|||||||
SettingsSectionTitle,
|
SettingsSectionTitle,
|
||||||
SettingsSectionDescription,
|
SettingsSectionDescription,
|
||||||
SettingsSectionBody,
|
SettingsSectionBody,
|
||||||
SettingsSectionFooter
|
SettingsSectionFooter,
|
||||||
|
SettingsSectionForm
|
||||||
} from "@app/components/Settings";
|
} from "@app/components/Settings";
|
||||||
import {
|
import {
|
||||||
InfoSection,
|
InfoSection,
|
||||||
@@ -1326,44 +1327,46 @@ export default function BillingPage() {
|
|||||||
</SettingsSectionDescription>
|
</SettingsSectionDescription>
|
||||||
</SettingsSectionHeader>
|
</SettingsSectionHeader>
|
||||||
<SettingsSectionBody>
|
<SettingsSectionBody>
|
||||||
<SettingsFormGrid>
|
<SettingsSectionForm variant="half">
|
||||||
<SettingsFormCell span="half">
|
<SettingsFormGrid>
|
||||||
<div className="flex flex-col md:flex-row items-start md:items-center justify-between gap-4 border rounded-lg p-4">
|
<SettingsFormCell span="full">
|
||||||
<div>
|
<div className="flex flex-col md:flex-row items-start md:items-center justify-between gap-4 border rounded-lg p-4">
|
||||||
<div className="text-sm text-muted-foreground mb-1">
|
<div>
|
||||||
{t("billingCurrentKeys") ||
|
<div className="text-sm text-muted-foreground mb-1">
|
||||||
"Current Keys"}
|
{t("billingCurrentKeys") ||
|
||||||
</div>
|
"Current Keys"}
|
||||||
<div className="flex items-baseline gap-2">
|
</div>
|
||||||
<span className="text-3xl font-semibold">
|
<div className="flex items-baseline gap-2">
|
||||||
{getLicenseKeyCount()}
|
<span className="text-3xl font-semibold">
|
||||||
</span>
|
{getLicenseKeyCount()}
|
||||||
<span className="text-lg">
|
</span>
|
||||||
{getLicenseKeyCount() === 1
|
<span className="text-lg">
|
||||||
? "key"
|
{getLicenseKeyCount() === 1
|
||||||
: "keys"}
|
? "key"
|
||||||
</span>
|
: "keys"}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={handleModifySubscription}
|
||||||
|
disabled={isLoading}
|
||||||
|
loading={isLoading}
|
||||||
|
>
|
||||||
|
<CreditCard className="mr-2 h-4 w-4" />
|
||||||
|
{t("billingModifyCurrentPlan") ||
|
||||||
|
"Modify Current Plan"}
|
||||||
|
</Button>
|
||||||
|
<p className="text-sm text-muted-foreground mt-2">
|
||||||
|
{t(
|
||||||
|
"billingManageLicenseSubscriptionDescription"
|
||||||
|
) ||
|
||||||
|
"Manage your subscription for paid self-hosted license keys and download invoices."}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
</SettingsFormCell>
|
||||||
variant="outline"
|
</SettingsFormGrid>
|
||||||
onClick={handleModifySubscription}
|
</SettingsSectionForm>
|
||||||
disabled={isLoading}
|
|
||||||
loading={isLoading}
|
|
||||||
>
|
|
||||||
<CreditCard className="mr-2 h-4 w-4" />
|
|
||||||
{t("billingModifyCurrentPlan") ||
|
|
||||||
"Modify Current Plan"}
|
|
||||||
</Button>
|
|
||||||
<p className="text-sm text-muted-foreground mt-2">
|
|
||||||
{t(
|
|
||||||
"billingManageLicenseSubscriptionDescription"
|
|
||||||
) ||
|
|
||||||
"Manage your subscription for paid self-hosted license keys and download invoices."}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</SettingsFormCell>
|
|
||||||
</SettingsFormGrid>
|
|
||||||
</SettingsSectionBody>
|
</SettingsSectionBody>
|
||||||
</SettingsSection>
|
</SettingsSection>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
SettingsSection,
|
SettingsSection,
|
||||||
SettingsSectionBody,
|
SettingsSectionBody,
|
||||||
SettingsSectionDescription,
|
SettingsSectionDescription,
|
||||||
|
SettingsSectionForm,
|
||||||
SettingsSectionHeader,
|
SettingsSectionHeader,
|
||||||
SettingsSectionTitle
|
SettingsSectionTitle
|
||||||
} from "@app/components/Settings";
|
} from "@app/components/Settings";
|
||||||
@@ -252,96 +253,102 @@ export default function Page() {
|
|||||||
</SettingsSectionTitle>
|
</SettingsSectionTitle>
|
||||||
</SettingsSectionHeader>
|
</SettingsSectionHeader>
|
||||||
<SettingsSectionBody>
|
<SettingsSectionBody>
|
||||||
<Form {...form}>
|
<SettingsSectionForm variant="half">
|
||||||
<form
|
<Form {...form}>
|
||||||
onKeyDown={(e) => {
|
<form
|
||||||
if (e.key === "Enter") {
|
onKeyDown={(e) => {
|
||||||
e.preventDefault(); // block default enter refresh
|
if (e.key === "Enter") {
|
||||||
}
|
e.preventDefault(); // block default enter refresh
|
||||||
}}
|
}
|
||||||
id="create-client-form"
|
}}
|
||||||
>
|
id="create-client-form"
|
||||||
<SettingsFormGrid>
|
>
|
||||||
<SettingsFormCell span="quarter">
|
<SettingsFormGrid>
|
||||||
<FormField
|
<SettingsFormCell span="half">
|
||||||
control={form.control}
|
|
||||||
name="name"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>
|
|
||||||
{t("name")}
|
|
||||||
</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
autoComplete="off"
|
|
||||||
{...field}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
<FormDescription>
|
|
||||||
{t(
|
|
||||||
"clientNameDescription"
|
|
||||||
)}
|
|
||||||
</FormDescription>
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</SettingsFormCell>
|
|
||||||
<SettingsFormCell span="full">
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
variant="ghost"
|
|
||||||
size="sm"
|
|
||||||
onClick={() =>
|
|
||||||
setShowAdvancedSettings(
|
|
||||||
!showAdvancedSettings
|
|
||||||
)
|
|
||||||
}
|
|
||||||
className="flex items-center gap-2 -ml-3"
|
|
||||||
>
|
|
||||||
{showAdvancedSettings ? (
|
|
||||||
<ChevronUp className="h-4 w-4" />
|
|
||||||
) : (
|
|
||||||
<ChevronDown className="h-4 w-4" />
|
|
||||||
)}
|
|
||||||
{t("advancedSettings")}
|
|
||||||
</Button>
|
|
||||||
</SettingsFormCell>
|
|
||||||
{showAdvancedSettings && (
|
|
||||||
<SettingsFormCell span="quarter">
|
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="subnet"
|
name="name"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>
|
<FormLabel>
|
||||||
{t(
|
{t("name")}
|
||||||
"clientAddress"
|
|
||||||
)}
|
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
placeholder={t(
|
|
||||||
"subnetPlaceholder"
|
|
||||||
)}
|
|
||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
{t(
|
{t(
|
||||||
"addressDescription"
|
"clientNameDescription"
|
||||||
)}
|
)}
|
||||||
</FormDescription>
|
</FormDescription>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</SettingsFormCell>
|
</SettingsFormCell>
|
||||||
)}
|
<SettingsFormCell span="full">
|
||||||
</SettingsFormGrid>
|
<Button
|
||||||
</form>
|
type="button"
|
||||||
</Form>
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
onClick={() =>
|
||||||
|
setShowAdvancedSettings(
|
||||||
|
!showAdvancedSettings
|
||||||
|
)
|
||||||
|
}
|
||||||
|
className="flex items-center gap-2 -ml-3"
|
||||||
|
>
|
||||||
|
{showAdvancedSettings ? (
|
||||||
|
<ChevronUp className="h-4 w-4" />
|
||||||
|
) : (
|
||||||
|
<ChevronDown className="h-4 w-4" />
|
||||||
|
)}
|
||||||
|
{t("advancedSettings")}
|
||||||
|
</Button>
|
||||||
|
</SettingsFormCell>
|
||||||
|
{showAdvancedSettings && (
|
||||||
|
<SettingsFormCell span="half">
|
||||||
|
<FormField
|
||||||
|
control={
|
||||||
|
form.control
|
||||||
|
}
|
||||||
|
name="subnet"
|
||||||
|
render={({
|
||||||
|
field
|
||||||
|
}) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>
|
||||||
|
{t(
|
||||||
|
"clientAddress"
|
||||||
|
)}
|
||||||
|
</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
autoComplete="off"
|
||||||
|
placeholder={t(
|
||||||
|
"subnetPlaceholder"
|
||||||
|
)}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
<FormDescription>
|
||||||
|
{t(
|
||||||
|
"addressDescription"
|
||||||
|
)}
|
||||||
|
</FormDescription>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</SettingsFormCell>
|
||||||
|
)}
|
||||||
|
</SettingsFormGrid>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</SettingsSectionForm>
|
||||||
</SettingsSectionBody>
|
</SettingsSectionBody>
|
||||||
</SettingsSection>
|
</SettingsSection>
|
||||||
|
|
||||||
|
|||||||
@@ -243,7 +243,7 @@ function ProxyResourceProtocolForm({
|
|||||||
{proxySettingsForm.watch("proxyProtocol") && (
|
{proxySettingsForm.watch("proxyProtocol") && (
|
||||||
<>
|
<>
|
||||||
<SettingsFormCell span="full">
|
<SettingsFormCell span="full">
|
||||||
<Alert className="[&>svg]:self-start">
|
<Alert className="[&>svg]:self-start" variant="neutral">
|
||||||
<AlertTriangle className="h-4 w-4" />
|
<AlertTriangle className="h-4 w-4" />
|
||||||
<AlertDescription>
|
<AlertDescription>
|
||||||
<strong>
|
<strong>
|
||||||
|
|||||||
@@ -1454,7 +1454,8 @@ export default function Page() {
|
|||||||
</SettingsSectionDescription>
|
</SettingsSectionDescription>
|
||||||
</SettingsSectionHeader>
|
</SettingsSectionHeader>
|
||||||
<SettingsSectionBody>
|
<SettingsSectionBody>
|
||||||
<SettingsFormGrid>
|
<SettingsSectionForm variant="half">
|
||||||
|
<SettingsFormGrid>
|
||||||
<SettingsFormCell span="full">
|
<SettingsFormCell span="full">
|
||||||
<SettingsSubsectionHeader>
|
<SettingsSubsectionHeader>
|
||||||
<SettingsSubsectionTitle>
|
<SettingsSubsectionTitle>
|
||||||
@@ -1496,6 +1497,7 @@ export default function Page() {
|
|||||||
/>
|
/>
|
||||||
</SettingsFormCell>
|
</SettingsFormCell>
|
||||||
</SettingsFormGrid>
|
</SettingsFormGrid>
|
||||||
|
</SettingsSectionForm>
|
||||||
</SettingsSectionBody>
|
</SettingsSectionBody>
|
||||||
</SettingsSection>
|
</SettingsSection>
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
SettingsSection,
|
SettingsSection,
|
||||||
SettingsSectionBody,
|
SettingsSectionBody,
|
||||||
SettingsSectionDescription,
|
SettingsSectionDescription,
|
||||||
|
SettingsSectionForm,
|
||||||
SettingsSectionHeader,
|
SettingsSectionHeader,
|
||||||
SettingsSectionTitle
|
SettingsSectionTitle
|
||||||
} from "@app/components/Settings";
|
} from "@app/components/Settings";
|
||||||
@@ -507,121 +508,122 @@ export default function Page() {
|
|||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
<SettingsSectionForm variant="half">
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
onKeyDown={(e) => {
|
onKeyDown={(e) => {
|
||||||
if (e.key === "Enter") {
|
if (e.key === "Enter") {
|
||||||
e.preventDefault(); // block default enter refresh
|
e.preventDefault(); // block default enter refresh
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
id="create-site-form"
|
id="create-site-form"
|
||||||
>
|
>
|
||||||
<SettingsFormGrid>
|
<SettingsFormGrid>
|
||||||
<SettingsFormCell span="quarter">
|
<SettingsFormCell span="half">
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="name"
|
name="name"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>
|
<FormLabel>
|
||||||
{t("name")}
|
{t("name")}w
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
{t(
|
{t(
|
||||||
"siteNameDescription"
|
"siteNameDescription"
|
||||||
)}
|
)}
|
||||||
</FormDescription>
|
</FormDescription>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</SettingsFormCell>
|
</SettingsFormCell>
|
||||||
{form.watch("method") ===
|
{form.watch("method") ===
|
||||||
"newt" && (
|
"newt" && (
|
||||||
<>
|
<>
|
||||||
<SettingsFormCell span="full">
|
<SettingsFormCell span="full">
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
setShowAdvancedSettings(
|
setShowAdvancedSettings(
|
||||||
!showAdvancedSettings
|
!showAdvancedSettings
|
||||||
)
|
)
|
||||||
}
|
|
||||||
className="mt-2 flex items-center gap-2 -ml-3"
|
|
||||||
>
|
|
||||||
{showAdvancedSettings ? (
|
|
||||||
<ChevronUp className="h-4 w-4" />
|
|
||||||
) : (
|
|
||||||
<ChevronDown className="h-4 w-4" />
|
|
||||||
)}
|
|
||||||
{t(
|
|
||||||
"advancedSettings"
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
</SettingsFormCell>
|
|
||||||
{showAdvancedSettings && (
|
|
||||||
<SettingsFormCell span="quarter">
|
|
||||||
<FormField
|
|
||||||
control={
|
|
||||||
form.control
|
|
||||||
}
|
}
|
||||||
name="clientAddress"
|
className="mt-2 flex items-center gap-2 -ml-3"
|
||||||
render={({
|
>
|
||||||
field
|
{showAdvancedSettings ? (
|
||||||
}) => (
|
<ChevronUp className="h-4 w-4" />
|
||||||
<FormItem>
|
) : (
|
||||||
<FormLabel>
|
<ChevronDown className="h-4 w-4" />
|
||||||
{t(
|
|
||||||
"siteAddress"
|
|
||||||
)}
|
|
||||||
</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
autoComplete="off"
|
|
||||||
value={
|
|
||||||
clientAddress
|
|
||||||
}
|
|
||||||
onChange={(
|
|
||||||
e
|
|
||||||
) => {
|
|
||||||
setClientAddress(
|
|
||||||
e
|
|
||||||
.target
|
|
||||||
.value
|
|
||||||
);
|
|
||||||
field.onChange(
|
|
||||||
e
|
|
||||||
.target
|
|
||||||
.value
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
<FormDescription>
|
|
||||||
{t(
|
|
||||||
"siteAddressDescription"
|
|
||||||
)}
|
|
||||||
</FormDescription>
|
|
||||||
</FormItem>
|
|
||||||
)}
|
)}
|
||||||
/>
|
{t(
|
||||||
|
"advancedSettings"
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
</SettingsFormCell>
|
</SettingsFormCell>
|
||||||
)}
|
{showAdvancedSettings && (
|
||||||
</>
|
<SettingsFormCell span="quarter">
|
||||||
)}
|
<FormField
|
||||||
</SettingsFormGrid>
|
control={
|
||||||
</form>
|
form.control
|
||||||
</Form>
|
}
|
||||||
|
name="clientAddress"
|
||||||
|
render={({
|
||||||
|
field
|
||||||
|
}) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>
|
||||||
|
{t(
|
||||||
|
"siteAddress"
|
||||||
|
)}
|
||||||
|
</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
autoComplete="off"
|
||||||
|
value={
|
||||||
|
clientAddress
|
||||||
|
}
|
||||||
|
onChange={(
|
||||||
|
e
|
||||||
|
) => {
|
||||||
|
setClientAddress(
|
||||||
|
e
|
||||||
|
.target
|
||||||
|
.value
|
||||||
|
);
|
||||||
|
field.onChange(
|
||||||
|
e
|
||||||
|
.target
|
||||||
|
.value
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
<FormDescription>
|
||||||
|
{t(
|
||||||
|
"siteAddressDescription"
|
||||||
|
)}
|
||||||
|
</FormDescription>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</SettingsFormCell>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</SettingsFormGrid>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</SettingsSectionForm>
|
||||||
</SettingsSectionBody>
|
</SettingsSectionBody>
|
||||||
</SettingsSection>
|
</SettingsSection>
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
SettingsSection,
|
SettingsSection,
|
||||||
SettingsSectionBody,
|
SettingsSectionBody,
|
||||||
SettingsSectionDescription,
|
SettingsSectionDescription,
|
||||||
|
SettingsSectionForm,
|
||||||
SettingsSectionHeader,
|
SettingsSectionHeader,
|
||||||
SettingsSectionTitle
|
SettingsSectionTitle
|
||||||
} from "@app/components/Settings";
|
} from "@app/components/Settings";
|
||||||
@@ -200,25 +201,27 @@ export function CreatePolicyForm({}: CreatePolicyFormProps) {
|
|||||||
</SettingsSectionDescription>
|
</SettingsSectionDescription>
|
||||||
</SettingsSectionHeader>
|
</SettingsSectionHeader>
|
||||||
<SettingsSectionBody>
|
<SettingsSectionBody>
|
||||||
<SettingsFormGrid>
|
<SettingsSectionForm variant="half">
|
||||||
<SettingsFormCell span="quarter">
|
<SettingsFormGrid>
|
||||||
<FormField
|
<SettingsFormCell span="half">
|
||||||
control={form.control}
|
<FormField
|
||||||
name="name"
|
control={form.control}
|
||||||
render={({ field }) => (
|
name="name"
|
||||||
<FormItem>
|
render={({ field }) => (
|
||||||
<FormLabel>
|
<FormItem>
|
||||||
{t("name")}
|
<FormLabel>
|
||||||
</FormLabel>
|
{t("name")}
|
||||||
<FormControl>
|
</FormLabel>
|
||||||
<Input {...field} />
|
<FormControl>
|
||||||
</FormControl>
|
<Input {...field} />
|
||||||
<FormMessage />
|
</FormControl>
|
||||||
</FormItem>
|
<FormMessage />
|
||||||
)}
|
</FormItem>
|
||||||
/>
|
)}
|
||||||
</SettingsFormCell>
|
/>
|
||||||
</SettingsFormGrid>
|
</SettingsFormCell>
|
||||||
|
</SettingsFormGrid>
|
||||||
|
</SettingsSectionForm>
|
||||||
</SettingsSectionBody>
|
</SettingsSectionBody>
|
||||||
</SettingsSection>
|
</SettingsSection>
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
SettingsSectionBody,
|
SettingsSectionBody,
|
||||||
SettingsSectionDescription,
|
SettingsSectionDescription,
|
||||||
SettingsSectionFooter,
|
SettingsSectionFooter,
|
||||||
|
SettingsSectionForm,
|
||||||
SettingsSectionHeader,
|
SettingsSectionHeader,
|
||||||
SettingsSectionTitle
|
SettingsSectionTitle
|
||||||
} from "@app/components/Settings";
|
} from "@app/components/Settings";
|
||||||
@@ -148,10 +149,10 @@ function PolicyAccessRulesSectionLayout({
|
|||||||
</SettingsSectionDescription>
|
</SettingsSectionDescription>
|
||||||
</SettingsSectionHeader>
|
</SettingsSectionHeader>
|
||||||
<SettingsSectionBody>
|
<SettingsSectionBody>
|
||||||
|
{resourceOverlayMode && (
|
||||||
|
<SharedPolicyResourceNotice section="rules" />
|
||||||
|
)}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{resourceOverlayMode && (
|
|
||||||
<SharedPolicyResourceNotice section="rules" />
|
|
||||||
)}
|
|
||||||
<PolicyAccessRulesIntro
|
<PolicyAccessRulesIntro
|
||||||
rulesEnabled={rulesEnabled}
|
rulesEnabled={rulesEnabled}
|
||||||
onRulesEnabledChange={onRulesEnabledChange}
|
onRulesEnabledChange={onRulesEnabledChange}
|
||||||
|
|||||||
120
src/components/resource-policy/PolicyAuthOtherMethodsSection.tsx
Normal file
120
src/components/resource-policy/PolicyAuthOtherMethodsSection.tsx
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import {
|
||||||
|
SettingsFormCell,
|
||||||
|
SettingsFormGrid,
|
||||||
|
SettingsSubsectionDescription,
|
||||||
|
SettingsSubsectionHeader,
|
||||||
|
SettingsSubsectionTitle
|
||||||
|
} from "@app/components/Settings";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
import { PolicyAuthMethodRow } from "./PolicyAuthMethodRow";
|
||||||
|
import type { PolicyAuthMethodId } from "./policy-auth-method-id";
|
||||||
|
import {
|
||||||
|
getEmailWhitelistSummary,
|
||||||
|
getHeaderAuthSummary,
|
||||||
|
getPasscodeSummary,
|
||||||
|
getPincodeSummary
|
||||||
|
} from "./policy-auth-summaries";
|
||||||
|
|
||||||
|
export type PolicyAuthOtherMethodsSectionProps = {
|
||||||
|
pinActive: boolean;
|
||||||
|
passcodeActive: boolean;
|
||||||
|
emailWhitelistEnabled: boolean;
|
||||||
|
headerAuthActive: boolean;
|
||||||
|
headerAuthUser: string;
|
||||||
|
emailCount: number;
|
||||||
|
emailEnabled: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
onConfigure: (method: PolicyAuthMethodId) => void;
|
||||||
|
onTogglePincode: (active: boolean) => void;
|
||||||
|
onTogglePasscode: (active: boolean) => void;
|
||||||
|
onToggleEmail: (active: boolean) => void;
|
||||||
|
onToggleHeaderAuth: (active: boolean) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function PolicyAuthOtherMethodsSection({
|
||||||
|
pinActive,
|
||||||
|
passcodeActive,
|
||||||
|
emailWhitelistEnabled,
|
||||||
|
headerAuthActive,
|
||||||
|
headerAuthUser,
|
||||||
|
emailCount,
|
||||||
|
emailEnabled,
|
||||||
|
disabled,
|
||||||
|
onConfigure,
|
||||||
|
onTogglePincode,
|
||||||
|
onTogglePasscode,
|
||||||
|
onToggleEmail,
|
||||||
|
onToggleHeaderAuth
|
||||||
|
}: PolicyAuthOtherMethodsSectionProps) {
|
||||||
|
const t = useTranslations();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SettingsFormGrid>
|
||||||
|
<SettingsFormCell span="full">
|
||||||
|
<SettingsSubsectionHeader>
|
||||||
|
<SettingsSubsectionTitle>
|
||||||
|
{t("policyAuthOtherMethodsTitle")}
|
||||||
|
</SettingsSubsectionTitle>
|
||||||
|
<SettingsSubsectionDescription>
|
||||||
|
{t("policyAuthOtherMethodsDescription")}
|
||||||
|
</SettingsSubsectionDescription>
|
||||||
|
</SettingsSubsectionHeader>
|
||||||
|
</SettingsFormCell>
|
||||||
|
<SettingsFormCell span="full">
|
||||||
|
<div className="flex flex-col gap-3">
|
||||||
|
<PolicyAuthMethodRow
|
||||||
|
id="pincode"
|
||||||
|
title={t("policyAuthPincodeTitle")}
|
||||||
|
description={t("policyAuthPincodeDescription")}
|
||||||
|
summary={getPincodeSummary({ t })}
|
||||||
|
active={pinActive}
|
||||||
|
onConfigure={() => onConfigure("pincode")}
|
||||||
|
onToggle={onTogglePincode}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<PolicyAuthMethodRow
|
||||||
|
id="passcode"
|
||||||
|
title={t("policyAuthPasscodeTitle")}
|
||||||
|
description={t("policyAuthPasscodeDescription")}
|
||||||
|
summary={getPasscodeSummary({ t })}
|
||||||
|
active={passcodeActive}
|
||||||
|
onConfigure={() => onConfigure("passcode")}
|
||||||
|
onToggle={onTogglePasscode}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<PolicyAuthMethodRow
|
||||||
|
id="email"
|
||||||
|
title={t("policyAuthEmailTitle")}
|
||||||
|
description={t("policyAuthEmailDescription")}
|
||||||
|
summary={getEmailWhitelistSummary({
|
||||||
|
t,
|
||||||
|
count: emailCount
|
||||||
|
})}
|
||||||
|
active={emailWhitelistEnabled}
|
||||||
|
onConfigure={() => onConfigure("email")}
|
||||||
|
onToggle={onToggleEmail}
|
||||||
|
disabled={disabled || !emailEnabled}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<PolicyAuthMethodRow
|
||||||
|
id="header-auth"
|
||||||
|
title={t("policyAuthHeaderAuthTitle")}
|
||||||
|
description={t("policyAuthHeaderAuthDescription")}
|
||||||
|
summary={getHeaderAuthSummary({
|
||||||
|
t,
|
||||||
|
headerName: headerAuthUser
|
||||||
|
})}
|
||||||
|
active={headerAuthActive}
|
||||||
|
onConfigure={() => onConfigure("headerAuth")}
|
||||||
|
onToggle={onToggleHeaderAuth}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</SettingsFormCell>
|
||||||
|
</SettingsFormGrid>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,10 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import {
|
import { SettingsFormCell, SettingsFormGrid } from "@app/components/Settings";
|
||||||
SettingsFormCell,
|
|
||||||
SettingsFormGrid,
|
|
||||||
SettingsSectionForm
|
|
||||||
} from "@app/components/Settings";
|
|
||||||
import { SwitchInput } from "@app/components/SwitchInput";
|
import { SwitchInput } from "@app/components/SwitchInput";
|
||||||
import { Button } from "@app/components/ui/button";
|
import { Button } from "@app/components/ui/button";
|
||||||
import {
|
import {
|
||||||
@@ -58,106 +54,100 @@ export function PolicyAuthSsoSection({
|
|||||||
const idpSelectDisabled = idpDisabled ?? disabled;
|
const idpSelectDisabled = idpDisabled ?? disabled;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingsSectionForm variant="half">
|
<SettingsFormGrid>
|
||||||
<SettingsFormGrid>
|
<SettingsFormCell span="full">
|
||||||
<SettingsFormCell span="full">
|
<SwitchInput
|
||||||
<SwitchInput
|
id="policy-auth-sso"
|
||||||
id="policy-auth-sso"
|
label={t("policyAuthSsoTitle")}
|
||||||
label={t("policyAuthSsoTitle")}
|
description={t("policyAuthSsoDescription")}
|
||||||
description={t("policyAuthSsoDescription")}
|
checked={sso}
|
||||||
checked={sso}
|
disabled={disabled}
|
||||||
disabled={disabled}
|
onCheckedChange={onSsoChange}
|
||||||
onCheckedChange={onSsoChange}
|
/>
|
||||||
/>
|
</SettingsFormCell>
|
||||||
</SettingsFormCell>
|
|
||||||
|
|
||||||
{sso && (
|
{sso && (
|
||||||
<>
|
<>
|
||||||
<SettingsFormCell span="full">
|
<SettingsFormCell span="full">
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t("roles")}</FormLabel>
|
<FormLabel>{t("roles")}</FormLabel>
|
||||||
{rolesEditor}
|
{rolesEditor}
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</SettingsFormCell>
|
</SettingsFormCell>
|
||||||
<SettingsFormCell span="full">
|
<SettingsFormCell span="full">
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t("users")}</FormLabel>
|
<FormLabel>{t("users")}</FormLabel>
|
||||||
{usersEditor}
|
{usersEditor}
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</SettingsFormCell>
|
</SettingsFormCell>
|
||||||
{allIdps.length > 0 && (
|
{allIdps.length > 0 && (
|
||||||
<SettingsFormCell span="half">
|
<SettingsFormCell span="half">
|
||||||
{skipToIdpId == null && !showIdpSelect ? (
|
{skipToIdpId == null && !showIdpSelect ? (
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="text"
|
variant="text"
|
||||||
size="sm"
|
size="sm"
|
||||||
className="h-auto px-0"
|
className="h-auto px-0"
|
||||||
|
disabled={idpSelectDisabled}
|
||||||
|
onClick={() => setShowIdpSelect(true)}
|
||||||
|
>
|
||||||
|
{t("policyAuthAddDefaultIdentityProvider")}
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>
|
||||||
|
{t("defaultIdentityProvider")}
|
||||||
|
</FormLabel>
|
||||||
|
<Select
|
||||||
disabled={idpSelectDisabled}
|
disabled={idpSelectDisabled}
|
||||||
onClick={() => setShowIdpSelect(true)}
|
onValueChange={(value) => {
|
||||||
>
|
if (value === "none") {
|
||||||
{t(
|
onSkipToIdpChange(null);
|
||||||
"policyAuthAddDefaultIdentityProvider"
|
setShowIdpSelect(false);
|
||||||
)}
|
return;
|
||||||
</Button>
|
|
||||||
) : (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>
|
|
||||||
{t("defaultIdentityProvider")}
|
|
||||||
</FormLabel>
|
|
||||||
<Select
|
|
||||||
disabled={idpSelectDisabled}
|
|
||||||
onValueChange={(value) => {
|
|
||||||
if (value === "none") {
|
|
||||||
onSkipToIdpChange(null);
|
|
||||||
setShowIdpSelect(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
onSkipToIdpChange(
|
|
||||||
parseInt(value)
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
value={
|
|
||||||
skipToIdpId
|
|
||||||
? skipToIdpId.toString()
|
|
||||||
: "none"
|
|
||||||
}
|
}
|
||||||
>
|
onSkipToIdpChange(parseInt(value));
|
||||||
<FormControl>
|
}}
|
||||||
<SelectTrigger>
|
value={
|
||||||
<SelectValue
|
skipToIdpId
|
||||||
placeholder={t(
|
? skipToIdpId.toString()
|
||||||
"selectIdpPlaceholder"
|
: "none"
|
||||||
)}
|
}
|
||||||
/>
|
>
|
||||||
</SelectTrigger>
|
<FormControl>
|
||||||
</FormControl>
|
<SelectTrigger>
|
||||||
<SelectContent>
|
<SelectValue
|
||||||
<SelectItem value="none">
|
placeholder={t(
|
||||||
{t("none")}
|
"selectIdpPlaceholder"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="none">
|
||||||
|
{t("none")}
|
||||||
|
</SelectItem>
|
||||||
|
{allIdps.map((idp) => (
|
||||||
|
<SelectItem
|
||||||
|
key={idp.id}
|
||||||
|
value={idp.id.toString()}
|
||||||
|
>
|
||||||
|
{idp.text}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
{allIdps.map((idp) => (
|
))}
|
||||||
<SelectItem
|
</SelectContent>
|
||||||
key={idp.id}
|
</Select>
|
||||||
value={idp.id.toString()}
|
<FormDescription>
|
||||||
>
|
{t(
|
||||||
{idp.text}
|
"defaultIdentityProviderDescription"
|
||||||
</SelectItem>
|
)}
|
||||||
))}
|
</FormDescription>
|
||||||
</SelectContent>
|
</FormItem>
|
||||||
</Select>
|
)}
|
||||||
<FormDescription>
|
</SettingsFormCell>
|
||||||
{t(
|
)}
|
||||||
"defaultIdentityProviderDescription"
|
</>
|
||||||
)}
|
)}
|
||||||
</FormDescription>
|
</SettingsFormGrid>
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
</SettingsFormCell>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</SettingsFormGrid>
|
|
||||||
</SettingsSectionForm>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,11 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
SettingsFormCell,
|
|
||||||
SettingsFormGrid,
|
|
||||||
SettingsSection,
|
SettingsSection,
|
||||||
SettingsSectionBody,
|
SettingsSectionBody,
|
||||||
SettingsSectionDescription,
|
SettingsSectionDescription,
|
||||||
|
SettingsSectionForm,
|
||||||
SettingsSectionHeader,
|
SettingsSectionHeader,
|
||||||
SettingsSubsectionDescription,
|
|
||||||
SettingsSubsectionHeader,
|
|
||||||
SettingsSubsectionTitle,
|
|
||||||
SettingsSectionTitle
|
SettingsSectionTitle
|
||||||
} from "@app/components/Settings";
|
} from "@app/components/Settings";
|
||||||
import { RolesSelector } from "@app/components/roles-selector";
|
import { RolesSelector } from "@app/components/roles-selector";
|
||||||
@@ -25,15 +21,9 @@ import {
|
|||||||
PasscodeCredenza,
|
PasscodeCredenza,
|
||||||
PincodeCredenza
|
PincodeCredenza
|
||||||
} from "./PolicyAuthMethodCredenzas";
|
} from "./PolicyAuthMethodCredenzas";
|
||||||
import { PolicyAuthMethodRow } from "./PolicyAuthMethodRow";
|
import { PolicyAuthOtherMethodsSection } from "./PolicyAuthOtherMethodsSection";
|
||||||
import { PolicyAuthSsoSection } from "./PolicyAuthSsoSection";
|
import { PolicyAuthSsoSection } from "./PolicyAuthSsoSection";
|
||||||
import type { PolicyAuthMethodId } from "./policy-auth-method-id";
|
import type { PolicyAuthMethodId } from "./policy-auth-method-id";
|
||||||
import {
|
|
||||||
getEmailWhitelistSummary,
|
|
||||||
getHeaderAuthSummary,
|
|
||||||
getPasscodeSummary,
|
|
||||||
getPincodeSummary
|
|
||||||
} from "./policy-auth-summaries";
|
|
||||||
|
|
||||||
export type PolicyAuthStackSectionCreateProps = {
|
export type PolicyAuthStackSectionCreateProps = {
|
||||||
form: UseFormReturn<PolicyFormValues, any, any>;
|
form: UseFormReturn<PolicyFormValues, any, any>;
|
||||||
@@ -105,144 +95,90 @@ export function PolicyAuthStackSectionCreate({
|
|||||||
</SettingsSectionDescription>
|
</SettingsSectionDescription>
|
||||||
</SettingsSectionHeader>
|
</SettingsSectionHeader>
|
||||||
<SettingsSectionBody>
|
<SettingsSectionBody>
|
||||||
<SettingsFormGrid>
|
<SettingsSectionForm variant="half">
|
||||||
<SettingsFormCell span="half">
|
<PolicyAuthSsoSection
|
||||||
<PolicyAuthSsoSection
|
sso={Boolean(sso)}
|
||||||
sso={Boolean(sso)}
|
onSsoChange={(active) =>
|
||||||
onSsoChange={(active) =>
|
parentForm.setValue("sso", active)
|
||||||
parentForm.setValue("sso", active)
|
}
|
||||||
}
|
skipToIdpId={skipToIdpId}
|
||||||
skipToIdpId={skipToIdpId}
|
onSkipToIdpChange={(id) =>
|
||||||
onSkipToIdpChange={(id) =>
|
parentForm.setValue("skipToIdpId", id)
|
||||||
parentForm.setValue("skipToIdpId", id)
|
}
|
||||||
}
|
allIdps={allIdps}
|
||||||
allIdps={allIdps}
|
rolesEditor={
|
||||||
rolesEditor={
|
<FormField<PolicyFormValues, "roles">
|
||||||
<FormField<PolicyFormValues, "roles">
|
control={parentForm.control}
|
||||||
control={parentForm.control}
|
name="roles"
|
||||||
name="roles"
|
render={({ field }) => (
|
||||||
render={({ field }) => (
|
<RolesSelector
|
||||||
<RolesSelector
|
orgId={orgId}
|
||||||
orgId={orgId}
|
selectedRoles={field.value}
|
||||||
selectedRoles={field.value}
|
onSelectRoles={(selected) =>
|
||||||
onSelectRoles={(selected) =>
|
parentForm.setValue(
|
||||||
parentForm.setValue(
|
"roles",
|
||||||
"roles",
|
selected
|
||||||
selected
|
)
|
||||||
)
|
}
|
||||||
}
|
restrictAdminRole
|
||||||
restrictAdminRole
|
/>
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
usersEditor={
|
|
||||||
<FormField<PolicyFormValues, "users">
|
|
||||||
control={parentForm.control}
|
|
||||||
name="users"
|
|
||||||
render={({ field }) => (
|
|
||||||
<UsersSelector
|
|
||||||
orgId={orgId}
|
|
||||||
selectedUsers={field.value}
|
|
||||||
onSelectUsers={(selected) =>
|
|
||||||
parentForm.setValue(
|
|
||||||
"users",
|
|
||||||
selected
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</SettingsFormCell>
|
|
||||||
</SettingsFormGrid>
|
|
||||||
|
|
||||||
<SettingsFormGrid>
|
|
||||||
<SettingsFormCell span="full">
|
|
||||||
<SettingsSubsectionHeader>
|
|
||||||
<SettingsSubsectionTitle>
|
|
||||||
{t("policyAuthOtherMethodsTitle")}
|
|
||||||
</SettingsSubsectionTitle>
|
|
||||||
<SettingsSubsectionDescription>
|
|
||||||
{t("policyAuthOtherMethodsDescription")}
|
|
||||||
</SettingsSubsectionDescription>
|
|
||||||
</SettingsSubsectionHeader>
|
|
||||||
</SettingsFormCell>
|
|
||||||
<SettingsFormCell span="half">
|
|
||||||
<div className="flex flex-col gap-3">
|
|
||||||
<PolicyAuthMethodRow
|
|
||||||
id="pincode"
|
|
||||||
title={t("policyAuthPincodeTitle")}
|
|
||||||
description={t("policyAuthPincodeDescription")}
|
|
||||||
summary={getPincodeSummary({ t })}
|
|
||||||
active={pinActive}
|
|
||||||
onConfigure={() => setEditingMethod("pincode")}
|
|
||||||
onToggle={(active) =>
|
|
||||||
handleToggle("pincode", active, () =>
|
|
||||||
parentForm.setValue("pincode", null)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<PolicyAuthMethodRow
|
|
||||||
id="passcode"
|
|
||||||
title={t("policyAuthPasscodeTitle")}
|
|
||||||
description={t("policyAuthPasscodeDescription")}
|
|
||||||
summary={getPasscodeSummary({ t })}
|
|
||||||
active={passcodeActive}
|
|
||||||
onConfigure={() => setEditingMethod("passcode")}
|
|
||||||
onToggle={(active) =>
|
|
||||||
handleToggle("passcode", active, () =>
|
|
||||||
parentForm.setValue("password", null)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<PolicyAuthMethodRow
|
|
||||||
id="email"
|
|
||||||
title={t("policyAuthEmailTitle")}
|
|
||||||
description={t("policyAuthEmailDescription")}
|
|
||||||
summary={getEmailWhitelistSummary({
|
|
||||||
t,
|
|
||||||
count: emails.length
|
|
||||||
})}
|
|
||||||
active={Boolean(emailWhitelistEnabled)}
|
|
||||||
onConfigure={() => setEditingMethod("email")}
|
|
||||||
onToggle={(active) =>
|
|
||||||
handleToggle("email", active, () =>
|
|
||||||
parentForm.setValue(
|
|
||||||
"emailWhitelistEnabled",
|
|
||||||
false
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
disabled={!emailEnabled}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<PolicyAuthMethodRow
|
|
||||||
id="header-auth"
|
|
||||||
title={t("policyAuthHeaderAuthTitle")}
|
|
||||||
description={t(
|
|
||||||
"policyAuthHeaderAuthDescription"
|
|
||||||
)}
|
)}
|
||||||
summary={getHeaderAuthSummary({
|
|
||||||
t,
|
|
||||||
headerName: headerAuth?.user ?? ""
|
|
||||||
})}
|
|
||||||
active={headerAuthActive}
|
|
||||||
onConfigure={() =>
|
|
||||||
setEditingMethod("headerAuth")
|
|
||||||
}
|
|
||||||
onToggle={(active) =>
|
|
||||||
handleToggle("headerAuth", active, () =>
|
|
||||||
parentForm.setValue("headerAuth", null)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
}
|
||||||
</SettingsFormCell>
|
usersEditor={
|
||||||
</SettingsFormGrid>
|
<FormField<PolicyFormValues, "users">
|
||||||
|
control={parentForm.control}
|
||||||
|
name="users"
|
||||||
|
render={({ field }) => (
|
||||||
|
<UsersSelector
|
||||||
|
orgId={orgId}
|
||||||
|
selectedUsers={field.value}
|
||||||
|
onSelectUsers={(selected) =>
|
||||||
|
parentForm.setValue(
|
||||||
|
"users",
|
||||||
|
selected
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<PolicyAuthOtherMethodsSection
|
||||||
|
pinActive={pinActive}
|
||||||
|
passcodeActive={passcodeActive}
|
||||||
|
emailWhitelistEnabled={Boolean(emailWhitelistEnabled)}
|
||||||
|
headerAuthActive={headerAuthActive}
|
||||||
|
headerAuthUser={headerAuth?.user ?? ""}
|
||||||
|
emailCount={emails.length}
|
||||||
|
emailEnabled={emailEnabled}
|
||||||
|
onConfigure={setEditingMethod}
|
||||||
|
onTogglePincode={(active) =>
|
||||||
|
handleToggle("pincode", active, () =>
|
||||||
|
parentForm.setValue("pincode", null)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
onTogglePasscode={(active) =>
|
||||||
|
handleToggle("passcode", active, () =>
|
||||||
|
parentForm.setValue("password", null)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
onToggleEmail={(active) =>
|
||||||
|
handleToggle("email", active, () =>
|
||||||
|
parentForm.setValue(
|
||||||
|
"emailWhitelistEnabled",
|
||||||
|
false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
onToggleHeaderAuth={(active) =>
|
||||||
|
handleToggle("headerAuth", active, () =>
|
||||||
|
parentForm.setValue("headerAuth", null)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</SettingsSectionForm>
|
||||||
|
|
||||||
<PincodeCredenza
|
<PincodeCredenza
|
||||||
open={editingMethod === "pincode"}
|
open={editingMethod === "pincode"}
|
||||||
|
|||||||
@@ -1,16 +1,12 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
SettingsFormCell,
|
|
||||||
SettingsFormGrid,
|
|
||||||
SettingsSection,
|
SettingsSection,
|
||||||
SettingsSectionBody,
|
SettingsSectionBody,
|
||||||
SettingsSectionDescription,
|
SettingsSectionDescription,
|
||||||
|
SettingsSectionForm,
|
||||||
SettingsSectionFooter,
|
SettingsSectionFooter,
|
||||||
SettingsSectionHeader,
|
SettingsSectionHeader,
|
||||||
SettingsSubsectionDescription,
|
|
||||||
SettingsSubsectionHeader,
|
|
||||||
SettingsSubsectionTitle,
|
|
||||||
SettingsSectionTitle
|
SettingsSectionTitle
|
||||||
} from "@app/components/Settings";
|
} from "@app/components/Settings";
|
||||||
import {
|
import {
|
||||||
@@ -50,15 +46,9 @@ import {
|
|||||||
PasscodeCredenza,
|
PasscodeCredenza,
|
||||||
PincodeCredenza
|
PincodeCredenza
|
||||||
} from "./PolicyAuthMethodCredenzas";
|
} from "./PolicyAuthMethodCredenzas";
|
||||||
import { PolicyAuthMethodRow } from "./PolicyAuthMethodRow";
|
import { PolicyAuthOtherMethodsSection } from "./PolicyAuthOtherMethodsSection";
|
||||||
import { PolicyAuthSsoSection } from "./PolicyAuthSsoSection";
|
import { PolicyAuthSsoSection } from "./PolicyAuthSsoSection";
|
||||||
import type { PolicyAuthMethodId } from "./policy-auth-method-id";
|
import type { PolicyAuthMethodId } from "./policy-auth-method-id";
|
||||||
import {
|
|
||||||
getEmailWhitelistSummary,
|
|
||||||
getHeaderAuthSummary,
|
|
||||||
getPasscodeSummary,
|
|
||||||
getPincodeSummary
|
|
||||||
} from "./policy-auth-summaries";
|
|
||||||
import { SharedPolicyResourceNotice } from "./SharedPolicyResourceNotice";
|
import { SharedPolicyResourceNotice } from "./SharedPolicyResourceNotice";
|
||||||
import z from "zod";
|
import z from "zod";
|
||||||
|
|
||||||
@@ -528,255 +518,133 @@ export function PolicyAuthStackSectionEdit({
|
|||||||
</SettingsSectionDescription>
|
</SettingsSectionDescription>
|
||||||
</SettingsSectionHeader>
|
</SettingsSectionHeader>
|
||||||
<SettingsSectionBody>
|
<SettingsSectionBody>
|
||||||
<div className="space-y-4">
|
{isResourceOverlay && (
|
||||||
{isResourceOverlay && (
|
<SharedPolicyResourceNotice section="authentication" />
|
||||||
<SharedPolicyResourceNotice section="authentication" />
|
)}
|
||||||
)}
|
<SettingsSectionForm variant="half">
|
||||||
<SettingsFormGrid>
|
<PolicyAuthSsoSection
|
||||||
<SettingsFormCell span="half">
|
sso={Boolean(sso)}
|
||||||
<PolicyAuthSsoSection
|
onSsoChange={(active) =>
|
||||||
sso={Boolean(sso)}
|
form.setValue("sso", active)
|
||||||
onSsoChange={(active) =>
|
}
|
||||||
form.setValue("sso", active)
|
skipToIdpId={skipToIdpId}
|
||||||
}
|
onSkipToIdpChange={(id) =>
|
||||||
skipToIdpId={skipToIdpId}
|
form.setValue("skipToIdpId", id)
|
||||||
onSkipToIdpChange={(id) =>
|
}
|
||||||
form.setValue("skipToIdpId", id)
|
allIdps={allIdps}
|
||||||
}
|
disabled={authReadonly}
|
||||||
allIdps={allIdps}
|
idpDisabled={authReadonly}
|
||||||
disabled={authReadonly}
|
rolesEditor={
|
||||||
idpDisabled={authReadonly}
|
isResourceOverlay ? (
|
||||||
rolesEditor={
|
<RolesSelector
|
||||||
isResourceOverlay ? (
|
orgId={orgId}
|
||||||
|
selectedRoles={overlayRoles}
|
||||||
|
onSelectRoles={(selected) =>
|
||||||
|
setCombinedRoles(
|
||||||
|
selected.map((role) => ({
|
||||||
|
...role,
|
||||||
|
isAdmin: Boolean(
|
||||||
|
role.isAdmin
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
disabled={isLoading}
|
||||||
|
restrictAdminRole
|
||||||
|
lockedIds={policyRoleLockedIds}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="roles"
|
||||||
|
render={({ field }) => (
|
||||||
<RolesSelector
|
<RolesSelector
|
||||||
orgId={orgId}
|
orgId={orgId}
|
||||||
selectedRoles={overlayRoles}
|
selectedRoles={field.value}
|
||||||
onSelectRoles={(selected) =>
|
onSelectRoles={(selected) =>
|
||||||
setCombinedRoles(
|
form.setValue(
|
||||||
selected.map(
|
"roles",
|
||||||
(role) => ({
|
selected
|
||||||
...role,
|
|
||||||
isAdmin:
|
|
||||||
Boolean(
|
|
||||||
role.isAdmin
|
|
||||||
)
|
|
||||||
})
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
disabled={isLoading}
|
disabled={readonly}
|
||||||
restrictAdminRole
|
restrictAdminRole
|
||||||
lockedIds={
|
|
||||||
policyRoleLockedIds
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
) : (
|
)}
|
||||||
<FormField
|
/>
|
||||||
control={form.control}
|
)
|
||||||
name="roles"
|
}
|
||||||
render={({ field }) => (
|
usersEditor={
|
||||||
<RolesSelector
|
isResourceOverlay ? (
|
||||||
orgId={orgId}
|
<UsersSelector
|
||||||
selectedRoles={
|
orgId={orgId}
|
||||||
field.value
|
selectedUsers={overlayUsers}
|
||||||
}
|
onSelectUsers={setCombinedUsers}
|
||||||
onSelectRoles={(
|
disabled={isLoading}
|
||||||
selected
|
lockedIds={policyUserLockedIds}
|
||||||
) =>
|
/>
|
||||||
form.setValue(
|
) : (
|
||||||
"roles",
|
<FormField
|
||||||
selected
|
control={form.control}
|
||||||
)
|
name="users"
|
||||||
}
|
render={({ field }) => (
|
||||||
disabled={readonly}
|
|
||||||
restrictAdminRole
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
usersEditor={
|
|
||||||
isResourceOverlay ? (
|
|
||||||
<UsersSelector
|
<UsersSelector
|
||||||
orgId={orgId}
|
orgId={orgId}
|
||||||
selectedUsers={overlayUsers}
|
selectedUsers={field.value}
|
||||||
onSelectUsers={
|
onSelectUsers={(selected) =>
|
||||||
setCombinedUsers
|
|
||||||
}
|
|
||||||
disabled={isLoading}
|
|
||||||
lockedIds={
|
|
||||||
policyUserLockedIds
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="users"
|
|
||||||
render={({ field }) => (
|
|
||||||
<UsersSelector
|
|
||||||
orgId={orgId}
|
|
||||||
selectedUsers={
|
|
||||||
field.value
|
|
||||||
}
|
|
||||||
onSelectUsers={(
|
|
||||||
selected
|
|
||||||
) =>
|
|
||||||
form.setValue(
|
|
||||||
"users",
|
|
||||||
selected
|
|
||||||
)
|
|
||||||
}
|
|
||||||
disabled={readonly}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</SettingsFormCell>
|
|
||||||
</SettingsFormGrid>
|
|
||||||
|
|
||||||
<SettingsFormGrid>
|
|
||||||
<SettingsFormCell span="full">
|
|
||||||
<SettingsSubsectionHeader>
|
|
||||||
<SettingsSubsectionTitle>
|
|
||||||
{t("policyAuthOtherMethodsTitle")}
|
|
||||||
</SettingsSubsectionTitle>
|
|
||||||
<SettingsSubsectionDescription>
|
|
||||||
{t(
|
|
||||||
"policyAuthOtherMethodsDescription"
|
|
||||||
)}
|
|
||||||
</SettingsSubsectionDescription>
|
|
||||||
</SettingsSubsectionHeader>
|
|
||||||
</SettingsFormCell>
|
|
||||||
<SettingsFormCell span="half">
|
|
||||||
<div className="flex flex-col gap-3">
|
|
||||||
<PolicyAuthMethodRow
|
|
||||||
id="pincode"
|
|
||||||
title={t("policyAuthPincodeTitle")}
|
|
||||||
description={t(
|
|
||||||
"policyAuthPincodeDescription"
|
|
||||||
)}
|
|
||||||
summary={getPincodeSummary({ t })}
|
|
||||||
active={pinActive}
|
|
||||||
onConfigure={() =>
|
|
||||||
openMethodEditor("pincode")
|
|
||||||
}
|
|
||||||
onToggle={(active) =>
|
|
||||||
handleToggle(
|
|
||||||
"pincode",
|
|
||||||
active,
|
|
||||||
() => {
|
|
||||||
setPinActive(false);
|
|
||||||
form.setValue(
|
form.setValue(
|
||||||
"pincode",
|
"users",
|
||||||
null
|
selected
|
||||||
);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
disabled={authReadonly}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<PolicyAuthMethodRow
|
|
||||||
id="passcode"
|
|
||||||
title={t("policyAuthPasscodeTitle")}
|
|
||||||
description={t(
|
|
||||||
"policyAuthPasscodeDescription"
|
|
||||||
)}
|
|
||||||
summary={getPasscodeSummary({ t })}
|
|
||||||
active={passcodeActive}
|
|
||||||
onConfigure={() =>
|
|
||||||
openMethodEditor("passcode")
|
|
||||||
}
|
|
||||||
onToggle={(active) =>
|
|
||||||
handleToggle(
|
|
||||||
"passcode",
|
|
||||||
active,
|
|
||||||
() => {
|
|
||||||
setPasscodeActive(
|
|
||||||
false
|
|
||||||
);
|
|
||||||
form.setValue(
|
|
||||||
"password",
|
|
||||||
null
|
|
||||||
);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
disabled={authReadonly}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<PolicyAuthMethodRow
|
|
||||||
id="email"
|
|
||||||
title={t("policyAuthEmailTitle")}
|
|
||||||
description={t(
|
|
||||||
"policyAuthEmailDescription"
|
|
||||||
)}
|
|
||||||
summary={getEmailWhitelistSummary({
|
|
||||||
t,
|
|
||||||
count: emails.length
|
|
||||||
})}
|
|
||||||
active={Boolean(
|
|
||||||
emailWhitelistEnabled
|
|
||||||
)}
|
|
||||||
onConfigure={() =>
|
|
||||||
openMethodEditor("email")
|
|
||||||
}
|
|
||||||
onToggle={(active) =>
|
|
||||||
handleToggle(
|
|
||||||
"email",
|
|
||||||
active,
|
|
||||||
() =>
|
|
||||||
form.setValue(
|
|
||||||
"emailWhitelistEnabled",
|
|
||||||
false
|
|
||||||
)
|
)
|
||||||
)
|
|
||||||
}
|
|
||||||
disabled={
|
|
||||||
authReadonly || !emailEnabled
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<PolicyAuthMethodRow
|
|
||||||
id="header-auth"
|
|
||||||
title={t(
|
|
||||||
"policyAuthHeaderAuthTitle"
|
|
||||||
)}
|
|
||||||
description={t(
|
|
||||||
"policyAuthHeaderAuthDescription"
|
|
||||||
)}
|
|
||||||
summary={getHeaderAuthSummary({
|
|
||||||
t,
|
|
||||||
headerName:
|
|
||||||
headerAuth?.user ?? ""
|
|
||||||
})}
|
|
||||||
active={headerAuthActive}
|
|
||||||
onConfigure={() =>
|
|
||||||
openMethodEditor("headerAuth")
|
|
||||||
}
|
|
||||||
onToggle={(active) =>
|
|
||||||
handleToggle(
|
|
||||||
"headerAuth",
|
|
||||||
active,
|
|
||||||
() => {
|
|
||||||
setHeaderAuthActive(
|
|
||||||
false
|
|
||||||
);
|
|
||||||
form.setValue(
|
|
||||||
"headerAuth",
|
|
||||||
null
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
)
|
disabled={readonly}
|
||||||
}
|
/>
|
||||||
disabled={authReadonly}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
)
|
||||||
</SettingsFormCell>
|
}
|
||||||
</SettingsFormGrid>
|
/>
|
||||||
</div>
|
|
||||||
|
<PolicyAuthOtherMethodsSection
|
||||||
|
pinActive={pinActive}
|
||||||
|
passcodeActive={passcodeActive}
|
||||||
|
emailWhitelistEnabled={Boolean(
|
||||||
|
emailWhitelistEnabled
|
||||||
|
)}
|
||||||
|
headerAuthActive={headerAuthActive}
|
||||||
|
headerAuthUser={headerAuth?.user ?? ""}
|
||||||
|
emailCount={emails.length}
|
||||||
|
emailEnabled={emailEnabled}
|
||||||
|
disabled={authReadonly}
|
||||||
|
onConfigure={openMethodEditor}
|
||||||
|
onTogglePincode={(active) =>
|
||||||
|
handleToggle("pincode", active, () => {
|
||||||
|
setPinActive(false);
|
||||||
|
form.setValue("pincode", null);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
onTogglePasscode={(active) =>
|
||||||
|
handleToggle("passcode", active, () => {
|
||||||
|
setPasscodeActive(false);
|
||||||
|
form.setValue("password", null);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
onToggleEmail={(active) =>
|
||||||
|
handleToggle("email", active, () =>
|
||||||
|
form.setValue(
|
||||||
|
"emailWhitelistEnabled",
|
||||||
|
false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
onToggleHeaderAuth={(active) =>
|
||||||
|
handleToggle("headerAuth", active, () => {
|
||||||
|
setHeaderAuthActive(false);
|
||||||
|
form.setValue("headerAuth", null);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</SettingsSectionForm>
|
||||||
|
|
||||||
<PincodeCredenza
|
<PincodeCredenza
|
||||||
open={editingMethod === "pincode"}
|
open={editingMethod === "pincode"}
|
||||||
|
|||||||
Reference in New Issue
Block a user