diff --git a/src/app/[orgId]/settings/general/page.tsx b/src/app/[orgId]/settings/general/page.tsx index ff8a103d..6bfb3013 100644 --- a/src/app/[orgId]/settings/general/page.tsx +++ b/src/app/[orgId]/settings/general/page.tsx @@ -53,6 +53,7 @@ import { SwitchInput } from "@app/components/SwitchInput"; import { SecurityFeaturesAlert } from "@app/components/SecurityFeaturesAlert"; import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext"; import { useSubscriptionStatusContext } from "@app/hooks/useSubscriptionStatusContext"; +import { usePaidStatus } from "@app/hooks/usePaidStatus"; // Session length options in hours const SESSION_LENGTH_OPTIONS = [ @@ -875,3 +876,520 @@ export default function GeneralPage() { ); } + +function GeneralSectionForm() { + const { org } = useOrgContext(); + const form = useForm({ + resolver: zodResolver(GeneralFormSchema), + defaultValues: { + name: org?.org.name + }, + mode: "onChange" + }); + const t = useTranslations(); + const subscription = useSubscriptionStatusContext(); + const { isUnlocked } = useLicenseStatusContext(); + const { isPaidUser } = usePaidStatus(); + return ( + <> + + + {t("general")} + + {t("orgGeneralSettingsDescription")} + + + + + ( + + {t("name")} + + + + + + {t("orgDisplayName")} + + + )} + /> + ( + + {t("subnet")} + + + + + + {t("subnetDescription")} + + + )} + /> + + + + + + {t("saveSettings")} + + + + > + ); +} + +function LogRetentionSectionForm() { + const { org } = useOrgContext(); + const form = useForm({ + resolver: zodResolver(GeneralFormSchema), + defaultValues: { + name: org?.org.name + }, + mode: "onChange" + }); + const t = useTranslations(); + const subscription = useSubscriptionStatusContext(); + const { isUnlocked } = useLicenseStatusContext(); + const { isPaidUser } = usePaidStatus(); + + return ( + + + {t("logRetention")} + + {t("logRetentionDescription")} + + + + + ( + + + {t("logRetentionRequestLabel")} + + + + field.onChange(parseInt(value, 10)) + } + > + + + + + {LOG_RETENTION_OPTIONS.filter( + (option) => { + if ( + build == "saas" && + !subscription?.subscribed && + option.value > 30 + ) { + return false; + } + return true; + } + ).map((option) => ( + + {t(option.label)} + + ))} + + + + + + )} + /> + + {build != "oss" && ( + <> + + + { + const isDisabled = + (build == "saas" && + !subscription?.subscribed) || + (build == "enterprise" && + !isUnlocked()); + + return ( + + + {t("logRetentionAccessLabel")} + + + { + if (!isDisabled) { + field.onChange( + parseInt( + value, + 10 + ) + ); + } + }} + disabled={isDisabled} + > + + + + + {LOG_RETENTION_OPTIONS.map( + (option) => ( + + {t( + option.label + )} + + ) + )} + + + + + + ); + }} + /> + { + const isDisabled = + (build == "saas" && + !subscription?.subscribed) || + (build == "enterprise" && + !isUnlocked()); + + return ( + + + {t("logRetentionActionLabel")} + + + { + if (!isDisabled) { + field.onChange( + parseInt( + value, + 10 + ) + ); + } + }} + disabled={isDisabled} + > + + + + + {LOG_RETENTION_OPTIONS.map( + (option) => ( + + {t( + option.label + )} + + ) + )} + + + + + + ); + }} + /> + > + )} + + + + ); +} + +function SectionForm() { + const { org } = useOrgContext(); + const form = useForm({ + resolver: zodResolver(GeneralFormSchema), + defaultValues: { + name: org?.org.name + }, + mode: "onChange" + }); + const t = useTranslations(); + const subscription = useSubscriptionStatusContext(); + const { isUnlocked } = useLicenseStatusContext(); + const { isPaidUser } = usePaidStatus(); + + return ( + + {build !== "oss" && ( + <> + + + + {t("securitySettings")} + + + {t("securitySettingsDescription")} + + + + + + { + const isDisabled = + isSecurityFeatureDisabled(); + + return ( + + + + { + if (!isDisabled) { + form.setValue( + "requireTwoFactor", + val + ); + } + }} + /> + + + + + {t( + "requireTwoFactorDescription" + )} + + + ); + }} + /> + { + const isDisabled = + isSecurityFeatureDisabled(); + + 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 + )} + + ) + )} + + + + + + {t( + "maxSessionLengthDescription" + )} + + + ); + }} + /> + { + const isDisabled = + isSecurityFeatureDisabled(); + + return ( + + + {t("passwordExpiryDays")} + + + { + if (!isDisabled) { + const numValue = + value === "null" + ? null + : parseInt( + value, + 10 + ); + form.setValue( + "passwordExpiryDays", + numValue + ); + } + }} + disabled={isDisabled} + > + + + + + {PASSWORD_EXPIRY_OPTIONS.map( + (option) => ( + + {t( + option.labelKey + )} + + ) + )} + + + + + + {t( + "editPasswordExpiryDescription" + )} + + + ); + }} + /> + + + > + )} + + ); +} diff --git a/src/hooks/usePaidStatus.ts b/src/hooks/usePaidStatus.ts new file mode 100644 index 00000000..d8173e6e --- /dev/null +++ b/src/hooks/usePaidStatus.ts @@ -0,0 +1,21 @@ +import { build } from "@server/build"; +import { useLicenseStatusContext } from "./useLicenseStatusContext"; +import { useSubscriptionStatusContext } from "./useSubscriptionStatusContext"; + +export function usePaidStatus() { + const { isUnlocked } = useLicenseStatusContext(); + const subscription = useSubscriptionStatusContext(); + + // Check if features are disabled due to licensing/subscription + const hasEnterpriseLicense = build === "enterprise" && isUnlocked(); + const hasSaasSubscription = + build === "saas" && + subscription?.isSubscribed() && + subscription.isActive(); + + return { + hasEnterpriseLicense, + hasSaasSubscription, + isPaidUser: hasEnterpriseLicense || hasSaasSubscription + }; +}