From 17a09f0bca22262fbd3fa27fbd12a65e6a1ac751 Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Fri, 16 Oct 2026 11:10:59 -0400 Subject: [PATCH] use form grid --- src/app/[orgId]/settings/general/page.tsx | 137 +- .../settings/general/security/page.tsx | 1656 +++++++++-------- src/components/AuthPageBrandingForm.tsx | 414 +++-- src/components/AuthPageSettings.tsx | 275 +-- 4 files changed, 1317 insertions(+), 1165 deletions(-) diff --git a/src/app/[orgId]/settings/general/page.tsx b/src/app/[orgId]/settings/general/page.tsx index 518decf81..d10e600f0 100644 --- a/src/app/[orgId]/settings/general/page.tsx +++ b/src/app/[orgId]/settings/general/page.tsx @@ -36,7 +36,9 @@ import { SettingsSectionDescription, SettingsSectionBody, SettingsSectionForm, - SettingsSectionFooter + SettingsSectionFooter, + SettingsFormGrid, + SettingsFormCell } from "@app/components/Settings"; import { useUserContext } from "@app/hooks/useUserContext"; import { useTranslations } from "next-intl"; @@ -242,7 +244,7 @@ function GeneralSectionForm({ org }: SectionFormProps) { - +
{ @@ -251,71 +253,86 @@ function GeneralSectionForm({ org }: SectionFormProps) { formAction(); }); }} - className="grid gap-4" id="org-general-settings-form" > - ( - - {t("name")} - - - - - - {t("orgDisplayName")} - - - )} - /> + + + ( + + + {t("name")} + + + + + + + {t("orgDisplayName")} + + + )} + /> + - - {!env.flags.disableEnterpriseFeatures && ( - ( - - - - - - {t("newtAutoUpdateDescription")}{" "} - - {t("learnMore")} - - - - - - )} - /> - )} + + + + {!env.flags.disableEnterpriseFeatures && ( + + ( + + + + + + {t( + "newtAutoUpdateDescription" + )}{" "} + + {t("learnMore")} + + + + + + )} + /> + + )} +
-
+ -
+ ); } diff --git a/src/app/[orgId]/settings/general/security/page.tsx b/src/app/[orgId]/settings/general/security/page.tsx index 9c1d012c9..fa8561f3d 100644 --- a/src/app/[orgId]/settings/general/security/page.tsx +++ b/src/app/[orgId]/settings/general/security/page.tsx @@ -41,7 +41,10 @@ import { SettingsSectionTitle, SettingsSectionDescription, SettingsSectionBody, - SettingsSectionForm + SettingsSectionForm, + SettingsSectionFooter, + SettingsFormGrid, + SettingsFormCell } from "@app/components/Settings"; import { useTranslations } from "next-intl"; import { build } from "@server/build"; @@ -151,7 +154,15 @@ function LogRetentionSectionForm({ org }: SectionFormProps) { const router = useRouter(); const t = useTranslations(); - const { isPaidUser, subscriptionTier } = usePaidStatus(); + const { + isPaidUser, + subscriptionTier, + hasSaasSubscription, + hasEnterpriseLicense + } = usePaidStatus(); + const showAccessLogsAlert = + (build === "saas" && !hasSaasSubscription(tierMatrix.accessLogs)) || + ((build === "enterprise" || build === "oss") && !hasEnterpriseLicense); const [, formAction, loadingSave] = useActionState(performSave, null); const { env } = useEnvContext(); @@ -208,7 +219,7 @@ function LogRetentionSectionForm({ org }: SectionFormProps) { - +
{ @@ -217,618 +228,650 @@ function LogRetentionSectionForm({ org }: SectionFormProps) { formAction(); }); }} - className="grid gap-4" id="org-log-retention-settings-form" + className="space-y-4" > - ( - - - {t("logRetentionRequestLabel")} - - - - - - + {!env.flags.disableEnterpriseFeatures && + showAccessLogsAlert && ( +
+ +
)} - /> - - {!env.flags.disableEnterpriseFeatures && ( - <> - - + + { - const isDisabled = !isPaidUser( - tierMatrix.accessLogs - ); - - return ( - - - {t( - "logRetentionAccessLabel" - )} - - - + field.onChange( + parseInt( + value, + 10 + ) + ) + } + > + + + + + {LOG_RETENTION_OPTIONS.filter( + (option) => { + if ( + build != + "saas" + ) { return true; } - ).map( - ( - option - ) => ( - - {t( - option.label - )} - - ) - )} - - - - - - ); - }} - /> - { - const isDisabled = !isPaidUser( - tierMatrix.actionLogs - ); - return ( - - - {t( - "logRetentionActionLabel" - )} - - - + + + + )} + /> + + + {!env.flags.disableEnterpriseFeatures && ( + <> + + { + const isDisabled = + !isPaidUser( + tierMatrix.accessLogs + ); + + return ( + + + {t( + "logRetentionAccessLabel" + )} + + + - - - - ); - }} - /> - { - const isDisabled = !isPaidUser( - tierMatrix.connectionLogs - ); + /> + + + {LOG_RETENTION_OPTIONS.filter( + ( + option + ) => { + if ( + build != + "saas" + ) { + return true; + } - return ( - - - {t( - "logRetentionConnectionLabel" - )} - - - + + + + ); + }} + /> + + + { + const isDisabled = + !isPaidUser( + tierMatrix.actionLogs + ); + + return ( + + + {t( + "logRetentionActionLabel" + )} + + + - - - - ); - }} - /> - { - const isDisabled = !isPaidUser( - tierMatrix.aiSessionLogs - ); + /> + + + {LOG_RETENTION_OPTIONS.filter( + ( + option + ) => { + if ( + build != + "saas" + ) { + return true; + } - return ( - - - {t( - "logRetentionAISessionsLabel" - )} - - - + + + + ); + }} + /> + + + { + const isDisabled = + !isPaidUser( + tierMatrix.connectionLogs + ); + + return ( + + + {t( + "logRetentionConnectionLabel" + )} + + + + + + + ); + }} + /> + + + { + const isDisabled = + !isPaidUser( + tierMatrix.aiSessionLogs + ); + + return ( + + + {t( + "logRetentionAISessionsLabel" )} - - - - - - ); - }} - /> - - )} + + + + + + + ); + }} + /> + + + )} +
-
+ -
+ ); } @@ -962,7 +1005,7 @@ function SecuritySettingsSectionForm({ org }: SectionFormProps) { - +
{ @@ -973,231 +1016,258 @@ function SecuritySettingsSectionForm({ org }: SectionFormProps) { }} ref={formRef} id="security-settings-section-form" - className="space-y-4" > - + + + + - { - const isDisabled = !isPaidUser( - tierMatrix.twoFactorEnforcement - ); + + { + const isDisabled = !isPaidUser( + tierMatrix.twoFactorEnforcement + ); - return ( - -
- - { - if ( - !isDisabled - ) { - form.setValue( - "requireTwoFactor", + return ( + +
+ + { + if ( + !isDisabled + ) { + form.setValue( + "requireTwoFactor", + val + ); + } + }} + /> + +
+ + + {t( + "requireTwoFactorDescription" + )} + +
+ ); + }} + /> + + + { + const isDisabled = !isPaidUser( + tierMatrix.sessionDurationPolicies + ); + + return ( + + + {t( + "maxSessionLength" + )} + + + { - if (!isDisabled) { - const numValue = - value === - "null" - ? null - : parseInt( - value, - 10 - ); - form.setValue( - "maxSessionLengthHours", - numValue - ); - } - }} - disabled={isDisabled} - > - - - - - {SESSION_LENGTH_OPTIONS.map( - (option) => ( - - {t( - option.labelKey + onValueChange={( + value + ) => { + if ( + !isDisabled + ) { + const numValue = + value === + "null" + ? null + : parseInt( + value, + 10 + ); + form.setValue( + "maxSessionLengthHours", + numValue + ); + } + }} + disabled={ + isDisabled + } + > + + - ) + /> + + + {SESSION_LENGTH_OPTIONS.map( + ( + option + ) => ( + + {t( + option.labelKey + )} + + ) + )} + + + + + + {t( + "maxSessionLengthDescription" )} - - -
- - - {t( - "maxSessionLengthDescription" - )} - - - ); - }} - /> - { - const isDisabled = !isPaidUser( - tierMatrix.passwordExpirationPolicies - ); + + + ); + }} + /> + + + { + const isDisabled = !isPaidUser( + tierMatrix.passwordExpirationPolicies + ); - return ( - - - {t("passwordExpiryDays")} - - - - - - - {t( - "editPasswordExpiryDescription" - )} - - - ); - }} - /> + + + + + + + {t( + "editPasswordExpiryDescription" + )} + + + ); + }} + /> + + -
+ -
+ ); diff --git a/src/components/AuthPageBrandingForm.tsx b/src/components/AuthPageBrandingForm.tsx index 5561df314..b46699311 100644 --- a/src/components/AuthPageBrandingForm.tsx +++ b/src/components/AuthPageBrandingForm.tsx @@ -15,12 +15,18 @@ import { useActionState } from "react"; import { useForm } from "react-hook-form"; import z from "zod"; import { + SettingsFormCell, + SettingsFormGrid, SettingsSection, SettingsSectionBody, SettingsSectionDescription, + SettingsSectionFooter, SettingsSectionForm, SettingsSectionHeader, - SettingsSectionTitle + SettingsSectionTitle, + SettingsSubsectionDescription, + SettingsSubsectionHeader, + SettingsSubsectionTitle } from "./Settings"; import { useEnvContext } from "@app/hooks/useEnvContext"; @@ -176,89 +182,228 @@ export default function AuthPageBrandingForm({ - - - +
- ( - - - {t("brandingPrimaryColor")} - + + + + -
- - - - -
- - -
- )} - /> - -
- ( - - - {build === "enterprise" - ? t( - "brandingLogoURLOrPath" - ) - : t("brandingLogoURL")} - - - - - - - {build === "enterprise" - ? t( - "brandingLogoPathDescription" - ) - : t( - "brandingLogoURLDescription" - )} - - - )} - /> -
+ ( - + - {t("brandingLogoWidth")} + {t( + "brandingPrimaryColor" + )} + +
+ + + + +
+ + +
+ )} + /> +
+ + + ( + + + {build === "enterprise" + ? t( + "brandingLogoURLOrPath" + ) + : t( + "brandingLogoURL" + )} + + + + + + + {build === "enterprise" + ? t( + "brandingLogoPathDescription" + ) + : t( + "brandingLogoURLDescription" + )} + + + )} + /> + + + +
+ ( + + + {t( + "brandingLogoWidth" + )} + + + + + + + )} + /> + + + + + + ( + + + {t( + "brandingLogoHeight" + )} + + + + + + + )} + /> +
+
+ + {build === "saas" || + env.env.app.identityProviderMode === + "org" ? ( + <> + + + + {t( + "organizationLoginPageTitle" + )} + + + {t( + "organizationLoginPageDescription" + )} + + + + + + ( + + + {t( + "brandingOrgTitle" + )} + + + + + + + + )} + /> + + + ( + + + {t( + "brandingOrgSubtitle" + )} + + + + + + + + )} + /> + + + ) : null} + + + + + {t("resourceLoginPageTitle")} + + + {t( + "resourceLoginPageDescription" + )} + + + + + + ( + + + {t( + "brandingResourceTitle" + )} + + @@ -266,19 +411,16 @@ export default function AuthPageBrandingForm({ )} /> - - - - - + + ( - + {t( - "brandingLogoHeight" + "brandingResourceSubtitle" )} @@ -288,116 +430,14 @@ export default function AuthPageBrandingForm({ )} /> -
-
- - {build === "saas" || - env.env.app.identityProviderMode === "org" ? ( - <> -
- - {t( - "organizationLoginPageTitle" - )} - - - {t( - "organizationLoginPageDescription" - )} - -
- -
- ( - - - {t( - "brandingOrgTitle" - )} - - - - - - - - )} - /> - ( - - - {t( - "brandingOrgSubtitle" - )} - - - - - - - - )} - /> -
- - ) : null} - -
- - {t("resourceLoginPageTitle")} - - - {t("resourceLoginPageDescription")} - -
- -
- ( - - - {t("brandingResourceTitle")} - - - - - - - - )} - /> - ( - - - {t( - "brandingResourceSubtitle" - )} - - - - - - - )} - /> -
+ +
-
+ {branding && (
-
+ ); diff --git a/src/components/AuthPageSettings.tsx b/src/components/AuthPageSettings.tsx index a76f13232..d5c75d69e 100644 --- a/src/components/AuthPageSettings.tsx +++ b/src/components/AuthPageSettings.tsx @@ -20,7 +20,10 @@ import { SettingsSectionTitle, SettingsSectionDescription, SettingsSectionBody, - SettingsSectionForm + SettingsSectionForm, + SettingsSectionFooter, + SettingsFormGrid, + SettingsFormCell } from "@app/components/Settings"; import { useTranslations } from "next-intl"; import { GetLoginPageResponse } from "@server/routers/loginPage/types"; @@ -284,145 +287,167 @@ function AuthPageSettings({ - - - + -
- -
- - - {loginPage && - !loginPage.domainId ? ( - + + + + +
+ +
+ + + {loginPage && + !loginPage.domainId ? ( + + ) : loginPage?.fullDomain ? ( + + {`${window.location.protocol}//${loginPage.fullDomain}`} + + ) : form.watch( + "authPageDomainId" + ) ? ( + // Show selected domain from form state when no loginPage exists yet + (() => { + const selectedDomainId = + form.watch( + "authPageDomainId" + ); + const selectedSubdomain = + form.watch( + "authPageSubdomain" + ); + const domain = + baseDomains.find( + (d) => + d.domainId === + selectedDomainId + ); + if (domain) { + const sanitizedSubdomain = + selectedSubdomain + ? finalizeSubdomainSanitize( + selectedSubdomain + ) + : ""; + const fullDomain = + sanitizedSubdomain + ? `${sanitizedSubdomain}.${domain.baseDomain}` + : domain.baseDomain; + return fullDomain; + } + return t( + "noDomainSet" + ); + })() + ) : ( + t("noDomainSet") )} - text={t("domainNotFound")} - /> - ) : loginPage?.fullDomain ? ( - - {`${window.location.protocol}//${loginPage.fullDomain}`} - - ) : form.watch( - "authPageDomainId" - ) ? ( - // Show selected domain from form state when no loginPage exists yet - (() => { - const selectedDomainId = - form.watch( + +
+ - {form.watch("authPageDomainId") && ( - - )} -
-
+ ? t("changeDomain") + : t("selectDomain")} + + {form.watch( + "authPageDomainId" + ) && ( + + )} +
+
- {!form.watch("authPageDomainId") && ( -
- {t( - "addDomainToEnableCustomAuthPages" + {!form.watch( + "authPageDomainId" + ) && ( +
+ {t( + "addDomainToEnableCustomAuthPages" + )} +
)} -
- )} - {(build === "enterprise" || - !isPaidUser( - tierMatrix.loginPageDomain - )) && - loginPage?.domainId && - loginPage?.fullDomain && - !hasUnsavedChanges && ( - - )} -
+ {(build === "enterprise" || + !isPaidUser( + tierMatrix.loginPageDomain + )) && + loginPage?.domainId && + loginPage?.fullDomain && + !hasUnsavedChanges && ( + + )} +
+
+
-
+ -
+ {/* Domain Picker Modal */}