From d27482e812c03aa8a8fcca2d650c982500350515 Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Tue, 10 Feb 2026 10:27:10 -0800 Subject: [PATCH] refactor and add tiers --- .../(private)/access/approvals/page.tsx | 10 ++-- .../(private)/idp/[idpId]/general/page.tsx | 9 ++- .../settings/(private)/idp/create/page.tsx | 8 ++- .../[orgId]/settings/(private)/idp/page.tsx | 3 +- .../[remoteExitNodeId]/credentials/page.tsx | 12 +++- .../machine/[niceId]/credentials/page.tsx | 12 +++- .../clients/user/[niceId]/general/page.tsx | 57 +++++++++++++------ .../settings/general/security/page.tsx | 38 ++++++++++--- src/app/[orgId]/settings/logs/access/page.tsx | 5 +- src/app/[orgId]/settings/logs/action/page.tsx | 5 +- .../resources/proxy/[niceId]/general/page.tsx | 35 +++++++++--- .../sites/[niceId]/credentials/page.tsx | 26 +++++++-- src/components/AuthPageBrandingForm.tsx | 7 ++- src/components/AuthPageSettings.tsx | 2 +- src/components/AutoProvisionConfigWidget.tsx | 5 ++ src/components/CreateRoleForm.tsx | 9 ++- src/components/EditRoleForm.tsx | 9 ++- src/components/LogDataTable.tsx | 6 +- src/components/PaidFeaturesAlert.tsx | 10 +++- src/components/RolesTable.tsx | 11 +--- 20 files changed, 201 insertions(+), 78 deletions(-) diff --git a/src/app/[orgId]/settings/(private)/access/approvals/page.tsx b/src/app/[orgId]/settings/(private)/access/approvals/page.tsx index 5e45be8e..de69de04 100644 --- a/src/app/[orgId]/settings/(private)/access/approvals/page.tsx +++ b/src/app/[orgId]/settings/(private)/access/approvals/page.tsx @@ -11,6 +11,7 @@ import type { GetOrgResponse } from "@server/routers/org"; import type { ListRolesResponse } from "@server/routers/role"; import type { AxiosResponse } from "axios"; import { getTranslations } from "next-intl/server"; +import { tierMatrix } from "@server/lib/billing/tierMatrix"; export interface ApprovalFeedPageProps { params: Promise<{ orgId: string }>; @@ -29,10 +30,9 @@ export default async function ApprovalFeedPage(props: ApprovalFeedPageProps) { // Fetch roles to check if approvals are enabled let hasApprovalsEnabled = false; const rolesRes = await internal - .get>( - `/org/${params.orgId}/roles`, - await authCookieHeader() - ) + .get< + AxiosResponse + >(`/org/${params.orgId}/roles`, await authCookieHeader()) .catch((e) => {}); if (rolesRes && rolesRes.status === 200) { @@ -52,7 +52,7 @@ export default async function ApprovalFeedPage(props: ApprovalFeedPageProps) { - +
diff --git a/src/app/[orgId]/settings/(private)/idp/[idpId]/general/page.tsx b/src/app/[orgId]/settings/(private)/idp/[idpId]/general/page.tsx index cdbe5f0e..7d4bece1 100644 --- a/src/app/[orgId]/settings/(private)/idp/[idpId]/general/page.tsx +++ b/src/app/[orgId]/settings/(private)/idp/[idpId]/general/page.tsx @@ -31,7 +31,6 @@ import { formatAxiosError } from "@app/lib/api"; import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; import { useState, useEffect } from "react"; -import { SwitchInput } from "@app/components/SwitchInput"; import { Alert, AlertDescription, AlertTitle } from "@app/components/ui/alert"; import { InfoIcon, ExternalLink } from "lucide-react"; import { @@ -41,12 +40,13 @@ import { InfoSectionTitle } from "@app/components/InfoSection"; import CopyToClipboard from "@app/components/CopyToClipboard"; -import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext"; import IdpTypeBadge from "@app/components/IdpTypeBadge"; import { useTranslations } from "next-intl"; import { AxiosResponse } from "axios"; import { ListRolesResponse } from "@server/routers/role"; import AutoProvisionConfigWidget from "@app/components/AutoProvisionConfigWidget"; +import { PaidFeaturesAlert } from "@app/components/PaidFeaturesAlert"; +import { tierMatrix } from "@server/lib/billing/tierMatrix"; export default function GeneralPage() { const { env } = useEnvContext(); @@ -60,7 +60,6 @@ export default function GeneralPage() { "role" | "expression" >("role"); const [variant, setVariant] = useState<"oidc" | "google" | "azure">("oidc"); - const { isUnlocked } = useLicenseStatusContext(); const dashboardRedirectUrl = `${env.app.dashboardUrl}/auth/idp/${idpId}/oidc/callback`; const [redirectUrl, setRedirectUrl] = useState( @@ -499,6 +498,10 @@ export default function GeneralPage() { + +
("role"); - const { isUnlocked } = useLicenseStatusContext(); const t = useTranslations(); const { isPaidUser } = usePaidStatus(); @@ -363,6 +364,9 @@ export default function Page() { + @@ -203,7 +207,9 @@ export default function CredentialsPage() { setShouldDisconnect(true); setModalOpen(true); }} - disabled={isPaidUser(tierMatrix.rotateCredentials)} + disabled={ + !isPaidUser(tierMatrix.rotateCredentials) + } > {t("remoteExitNodeRegenerateAndDisconnect")} diff --git a/src/app/[orgId]/settings/clients/machine/[niceId]/credentials/page.tsx b/src/app/[orgId]/settings/clients/machine/[niceId]/credentials/page.tsx index 17c958ce..634994b2 100644 --- a/src/app/[orgId]/settings/clients/machine/[niceId]/credentials/page.tsx +++ b/src/app/[orgId]/settings/clients/machine/[niceId]/credentials/page.tsx @@ -119,7 +119,9 @@ export default function CredentialsPage() { - + @@ -180,7 +182,9 @@ export default function CredentialsPage() { setShouldDisconnect(false); setModalOpen(true); }} - disabled={isPaidUser(tierMatrix.rotateCredentials)} + disabled={ + !isPaidUser(tierMatrix.rotateCredentials) + } > {t("regenerateCredentialsButton")} @@ -189,7 +193,9 @@ export default function CredentialsPage() { setShouldDisconnect(true); setModalOpen(true); }} - disabled={isPaidUser(tierMatrix.rotateCredentials)} + disabled={ + !isPaidUser(tierMatrix.rotateCredentials) + } > {t("clientRegenerateAndDisconnect")} diff --git a/src/app/[orgId]/settings/clients/user/[niceId]/general/page.tsx b/src/app/[orgId]/settings/clients/user/[niceId]/general/page.tsx index d39b5c5b..7acf52b2 100644 --- a/src/app/[orgId]/settings/clients/user/[niceId]/general/page.tsx +++ b/src/app/[orgId]/settings/clients/user/[niceId]/general/page.tsx @@ -155,13 +155,11 @@ export default function GeneralPage() { const [, startTransition] = useTransition(); const { env } = useEnvContext(); - const showApprovalFeatures = build !== "oss" && isPaidUser; + const showApprovalFeatures = + build !== "oss" && isPaidUser(tierMatrix.deviceApprovals); - const formatPostureValue = ( - value: boolean | null | undefined | "-" - ) => { - if (value === null || value === undefined || value === "-") - return "-"; + const formatPostureValue = (value: boolean | null | undefined | "-") => { + if (value === null || value === undefined || value === "-") return "-"; return (
{value ? ( @@ -584,7 +582,8 @@ export default function GeneralPage() { - + + {client.posture && Object.keys(client.posture).length > 0 ? ( <> @@ -598,7 +597,9 @@ export default function GeneralPage() { {t("biometricsEnabled")} - {isPaidUser(tierMatrix.devicePosture) + {isPaidUser( + tierMatrix.devicePosture + ) ? formatPostureValue( client.posture .biometricsEnabled @@ -616,7 +617,9 @@ export default function GeneralPage() { {t("diskEncrypted")} - {isPaidUser(tierMatrix.devicePosture) + {isPaidUser( + tierMatrix.devicePosture + ) ? formatPostureValue( client.posture .diskEncrypted @@ -634,7 +637,9 @@ export default function GeneralPage() { {t("firewallEnabled")} - {isPaidUser(tierMatrix.devicePosture) + {isPaidUser( + tierMatrix.devicePosture + ) ? formatPostureValue( client.posture .firewallEnabled @@ -653,7 +658,9 @@ export default function GeneralPage() { {t("autoUpdatesEnabled")} - {isPaidUser(tierMatrix.devicePosture) + {isPaidUser( + tierMatrix.devicePosture + ) ? formatPostureValue( client.posture .autoUpdatesEnabled @@ -671,7 +678,9 @@ export default function GeneralPage() { {t("tpmAvailable")} - {isPaidUser(tierMatrix.devicePosture) + {isPaidUser( + tierMatrix.devicePosture + ) ? formatPostureValue( client.posture .tpmAvailable @@ -693,7 +702,9 @@ export default function GeneralPage() { )} - {isPaidUser(tierMatrix.devicePosture) + {isPaidUser( + tierMatrix.devicePosture + ) ? formatPostureValue( client.posture .windowsAntivirusEnabled @@ -711,7 +722,9 @@ export default function GeneralPage() { {t("macosSipEnabled")} - {isPaidUser(tierMatrix.devicePosture) + {isPaidUser( + tierMatrix.devicePosture + ) ? formatPostureValue( client.posture .macosSipEnabled @@ -733,7 +746,9 @@ export default function GeneralPage() { )} - {isPaidUser(tierMatrix.devicePosture) + {isPaidUser( + tierMatrix.devicePosture + ) ? formatPostureValue( client.posture .macosGatekeeperEnabled @@ -755,7 +770,9 @@ export default function GeneralPage() { )} - {isPaidUser(tierMatrix.devicePosture) + {isPaidUser( + tierMatrix.devicePosture + ) ? formatPostureValue( client.posture .macosFirewallStealthMode @@ -774,7 +791,9 @@ export default function GeneralPage() { {t("linuxAppArmorEnabled")} - {isPaidUser(tierMatrix.devicePosture) + {isPaidUser( + tierMatrix.devicePosture + ) ? formatPostureValue( client.posture .linuxAppArmorEnabled @@ -793,7 +812,9 @@ export default function GeneralPage() { {t("linuxSELinuxEnabled")} - {isPaidUser(tierMatrix.devicePosture) + {isPaidUser( + tierMatrix.devicePosture + ) ? formatPostureValue( client.posture .linuxSELinuxEnabled diff --git a/src/app/[orgId]/settings/general/security/page.tsx b/src/app/[orgId]/settings/general/security/page.tsx index 55aa9d57..5b98bfa6 100644 --- a/src/app/[orgId]/settings/general/security/page.tsx +++ b/src/app/[orgId]/settings/general/security/page.tsx @@ -43,6 +43,8 @@ import { SwitchInput } from "@app/components/SwitchInput"; import { PaidFeaturesAlert } from "@app/components/PaidFeaturesAlert"; import { usePaidStatus } from "@app/hooks/usePaidStatus"; import type { OrgContextType } from "@app/contexts/orgContext"; +import { tierMatrix } from "@server/lib/billing/tierMatrix"; +import { isAppPageRouteDefinition } from "next/dist/server/route-definitions/app-page-route-definition"; // Session length options in hours const SESSION_LENGTH_OPTIONS = [ @@ -244,13 +246,17 @@ function LogRetentionSectionForm({ org }: SectionFormProps) { {!env.flags.disableEnterpriseFeatures && ( <> - + { - const isDisabled = !isPaidUser; + const isDisabled = !isPaidUser( + tierMatrix.accessLogs + ); return ( @@ -316,7 +322,9 @@ function LogRetentionSectionForm({ org }: SectionFormProps) { control={form.control} name="settingsLogRetentionDaysAction" render={({ field }) => { - const isDisabled = !isPaidUser; + const isDisabled = !isPaidUser( + tierMatrix.actionLogs + ); return ( @@ -521,12 +529,17 @@ function SecuritySettingsSectionForm({ org }: SectionFormProps) { id="security-settings-section-form" className="space-y-4" > - + + { - const isDisabled = !isPaidUser; + const isDisabled = !isPaidUser( + tierMatrix.twoFactorEnforcement + ); return ( @@ -573,7 +586,9 @@ function SecuritySettingsSectionForm({ org }: SectionFormProps) { control={form.control} name="maxSessionLengthHours" render={({ field }) => { - const isDisabled = !isPaidUser; + const isDisabled = !isPaidUser( + tierMatrix.sessionDurationPolicies + ); return ( @@ -653,7 +668,9 @@ function SecuritySettingsSectionForm({ org }: SectionFormProps) { control={form.control} name="passwordExpiryDays" render={({ field }) => { - const isDisabled = !isPaidUser; + const isDisabled = !isPaidUser( + tierMatrix.passwordExpirationPolicies + ); return ( @@ -739,7 +756,12 @@ function SecuritySettingsSectionForm({ org }: SectionFormProps) { type="submit" form="security-settings-section-form" loading={loadingSave} - disabled={loadingSave || !isPaidUser} + disabled={ + loadingSave || + !isPaidUser(tierMatrix.twoFactorEnforcement) || + !isPaidUser(tierMatrix.sessionDurationPolicies) || + !isPaidUser(tierMatrix.passwordExpirationPolicies) + } > {t("saveSettings")} diff --git a/src/app/[orgId]/settings/logs/access/page.tsx b/src/app/[orgId]/settings/logs/access/page.tsx index 13f31952..c48c5c27 100644 --- a/src/app/[orgId]/settings/logs/access/page.tsx +++ b/src/app/[orgId]/settings/logs/access/page.tsx @@ -608,7 +608,7 @@ export default function GeneralPage() { description={t("accessLogsDescription")} /> - + startTransition(exportData)} isExporting={isExporting} + isExportDisabled={ + !isPaidUser(tierMatrix.accessLogs) || build === "oss" + } onDateRangeChange={handleDateRangeChange} dateRange={{ start: dateRange.startDate, diff --git a/src/app/[orgId]/settings/logs/action/page.tsx b/src/app/[orgId]/settings/logs/action/page.tsx index 515fd2b9..3371deed 100644 --- a/src/app/[orgId]/settings/logs/action/page.tsx +++ b/src/app/[orgId]/settings/logs/action/page.tsx @@ -461,7 +461,7 @@ export default function GeneralPage() { description={t("actionLogsDescription")} /> - + startTransition(exportData)} + isExportDisabled={ + !isPaidUser(tierMatrix.logExport) || build === "oss" + } isExporting={isExporting} onDateRangeChange={handleDateRangeChange} dateRange={{ diff --git a/src/app/[orgId]/settings/resources/proxy/[niceId]/general/page.tsx b/src/app/[orgId]/settings/resources/proxy/[niceId]/general/page.tsx index 303b3c46..9589f6a2 100644 --- a/src/app/[orgId]/settings/resources/proxy/[niceId]/general/page.tsx +++ b/src/app/[orgId]/settings/resources/proxy/[niceId]/general/page.tsx @@ -178,13 +178,15 @@ function MaintenanceSectionForm({ className="space-y-4" id="maintenance-settings-form" > - + { const isDisabled = - isPaidUser(tierMatrix.maintencePage) || + !isPaidUser(tierMatrix.maintencePage) || resource.http === false; return ( @@ -251,7 +253,11 @@ function MaintenanceSectionForm({ defaultValue={ field.value } - disabled={isPaidUser(tierMatrix.maintencePage)} + disabled={ + !isPaidUser( + tierMatrix.maintencePage + ) + } className="flex flex-col space-y-1" > @@ -324,7 +330,11 @@ function MaintenanceSectionForm({ @@ -350,7 +360,11 @@ function MaintenanceSectionForm({