mirror of
https://github.com/fosrl/pangolin.git
synced 2026-03-02 00:36:38 +00:00
refactor and add tiers
This commit is contained in:
committed by
Owen Schwartz
parent
1061bb8481
commit
fa0c5c623f
@@ -11,6 +11,7 @@ import type { GetOrgResponse } from "@server/routers/org";
|
|||||||
import type { ListRolesResponse } from "@server/routers/role";
|
import type { ListRolesResponse } from "@server/routers/role";
|
||||||
import type { AxiosResponse } from "axios";
|
import type { AxiosResponse } from "axios";
|
||||||
import { getTranslations } from "next-intl/server";
|
import { getTranslations } from "next-intl/server";
|
||||||
|
import { tierMatrix } from "@server/lib/billing/tierMatrix";
|
||||||
|
|
||||||
export interface ApprovalFeedPageProps {
|
export interface ApprovalFeedPageProps {
|
||||||
params: Promise<{ orgId: string }>;
|
params: Promise<{ orgId: string }>;
|
||||||
@@ -29,10 +30,9 @@ export default async function ApprovalFeedPage(props: ApprovalFeedPageProps) {
|
|||||||
// Fetch roles to check if approvals are enabled
|
// Fetch roles to check if approvals are enabled
|
||||||
let hasApprovalsEnabled = false;
|
let hasApprovalsEnabled = false;
|
||||||
const rolesRes = await internal
|
const rolesRes = await internal
|
||||||
.get<AxiosResponse<ListRolesResponse>>(
|
.get<
|
||||||
`/org/${params.orgId}/roles`,
|
AxiosResponse<ListRolesResponse>
|
||||||
await authCookieHeader()
|
>(`/org/${params.orgId}/roles`, await authCookieHeader())
|
||||||
)
|
|
||||||
.catch((e) => {});
|
.catch((e) => {});
|
||||||
|
|
||||||
if (rolesRes && rolesRes.status === 200) {
|
if (rolesRes && rolesRes.status === 200) {
|
||||||
@@ -52,7 +52,7 @@ export default async function ApprovalFeedPage(props: ApprovalFeedPageProps) {
|
|||||||
|
|
||||||
<ApprovalsBanner />
|
<ApprovalsBanner />
|
||||||
|
|
||||||
<PaidFeaturesAlert />
|
<PaidFeaturesAlert tiers={tierMatrix.deviceApprovals} />
|
||||||
|
|
||||||
<OrgProvider org={org}>
|
<OrgProvider org={org}>
|
||||||
<div className="container mx-auto max-w-12xl">
|
<div className="container mx-auto max-w-12xl">
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ import { formatAxiosError } from "@app/lib/api";
|
|||||||
import { createApiClient } from "@app/lib/api";
|
import { createApiClient } from "@app/lib/api";
|
||||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { SwitchInput } from "@app/components/SwitchInput";
|
|
||||||
import { Alert, AlertDescription, AlertTitle } from "@app/components/ui/alert";
|
import { Alert, AlertDescription, AlertTitle } from "@app/components/ui/alert";
|
||||||
import { InfoIcon, ExternalLink } from "lucide-react";
|
import { InfoIcon, ExternalLink } from "lucide-react";
|
||||||
import {
|
import {
|
||||||
@@ -41,12 +40,13 @@ import {
|
|||||||
InfoSectionTitle
|
InfoSectionTitle
|
||||||
} from "@app/components/InfoSection";
|
} from "@app/components/InfoSection";
|
||||||
import CopyToClipboard from "@app/components/CopyToClipboard";
|
import CopyToClipboard from "@app/components/CopyToClipboard";
|
||||||
import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext";
|
|
||||||
import IdpTypeBadge from "@app/components/IdpTypeBadge";
|
import IdpTypeBadge from "@app/components/IdpTypeBadge";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { AxiosResponse } from "axios";
|
import { AxiosResponse } from "axios";
|
||||||
import { ListRolesResponse } from "@server/routers/role";
|
import { ListRolesResponse } from "@server/routers/role";
|
||||||
import AutoProvisionConfigWidget from "@app/components/AutoProvisionConfigWidget";
|
import AutoProvisionConfigWidget from "@app/components/AutoProvisionConfigWidget";
|
||||||
|
import { PaidFeaturesAlert } from "@app/components/PaidFeaturesAlert";
|
||||||
|
import { tierMatrix } from "@server/lib/billing/tierMatrix";
|
||||||
|
|
||||||
export default function GeneralPage() {
|
export default function GeneralPage() {
|
||||||
const { env } = useEnvContext();
|
const { env } = useEnvContext();
|
||||||
@@ -60,7 +60,6 @@ export default function GeneralPage() {
|
|||||||
"role" | "expression"
|
"role" | "expression"
|
||||||
>("role");
|
>("role");
|
||||||
const [variant, setVariant] = useState<"oidc" | "google" | "azure">("oidc");
|
const [variant, setVariant] = useState<"oidc" | "google" | "azure">("oidc");
|
||||||
const { isUnlocked } = useLicenseStatusContext();
|
|
||||||
|
|
||||||
const dashboardRedirectUrl = `${env.app.dashboardUrl}/auth/idp/${idpId}/oidc/callback`;
|
const dashboardRedirectUrl = `${env.app.dashboardUrl}/auth/idp/${idpId}/oidc/callback`;
|
||||||
const [redirectUrl, setRedirectUrl] = useState(
|
const [redirectUrl, setRedirectUrl] = useState(
|
||||||
@@ -499,6 +498,10 @@ export default function GeneralPage() {
|
|||||||
</SettingsSectionHeader>
|
</SettingsSectionHeader>
|
||||||
<SettingsSectionBody>
|
<SettingsSectionBody>
|
||||||
<SettingsSectionForm>
|
<SettingsSectionForm>
|
||||||
|
<PaidFeaturesAlert
|
||||||
|
tiers={tierMatrix.autoProvisioning}
|
||||||
|
/>
|
||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
onSubmit={form.handleSubmit(onSubmit)}
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import AutoProvisionConfigWidget from "@app/components/AutoProvisionConfigWidget";
|
import AutoProvisionConfigWidget from "@app/components/AutoProvisionConfigWidget";
|
||||||
|
import { PaidFeaturesAlert } from "@app/components/PaidFeaturesAlert";
|
||||||
import {
|
import {
|
||||||
SettingsContainer,
|
SettingsContainer,
|
||||||
SettingsSection,
|
SettingsSection,
|
||||||
@@ -31,6 +32,7 @@ import { usePaidStatus } from "@app/hooks/usePaidStatus";
|
|||||||
import { toast } from "@app/hooks/useToast";
|
import { toast } from "@app/hooks/useToast";
|
||||||
import { createApiClient, formatAxiosError } from "@app/lib/api";
|
import { createApiClient, formatAxiosError } from "@app/lib/api";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { tierMatrix } from "@server/lib/billing/tierMatrix";
|
||||||
import { ListRolesResponse } from "@server/routers/role";
|
import { ListRolesResponse } from "@server/routers/role";
|
||||||
import { AxiosResponse } from "axios";
|
import { AxiosResponse } from "axios";
|
||||||
import { InfoIcon } from "lucide-react";
|
import { InfoIcon } from "lucide-react";
|
||||||
@@ -50,7 +52,6 @@ export default function Page() {
|
|||||||
const [roleMappingMode, setRoleMappingMode] = useState<
|
const [roleMappingMode, setRoleMappingMode] = useState<
|
||||||
"role" | "expression"
|
"role" | "expression"
|
||||||
>("role");
|
>("role");
|
||||||
const { isUnlocked } = useLicenseStatusContext();
|
|
||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
const { isPaidUser } = usePaidStatus();
|
const { isPaidUser } = usePaidStatus();
|
||||||
|
|
||||||
@@ -363,6 +364,9 @@ export default function Page() {
|
|||||||
</SettingsSectionHeader>
|
</SettingsSectionHeader>
|
||||||
<SettingsSectionBody>
|
<SettingsSectionBody>
|
||||||
<SettingsSectionForm>
|
<SettingsSectionForm>
|
||||||
|
<PaidFeaturesAlert
|
||||||
|
tiers={tierMatrix.autoProvisioning}
|
||||||
|
/>
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
className="space-y-4"
|
className="space-y-4"
|
||||||
@@ -808,7 +812,7 @@ export default function Page() {
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={createLoading || !isPaidUser}
|
disabled={createLoading || !isPaidUser(tierMatrix.orgOidc)}
|
||||||
loading={createLoading}
|
loading={createLoading}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
// log any issues with the form
|
// log any issues with the form
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import SettingsSectionTitle from "@app/components/SettingsSectionTitle";
|
|||||||
import IdpTable, { IdpRow } from "@app/components/OrgIdpTable";
|
import IdpTable, { IdpRow } from "@app/components/OrgIdpTable";
|
||||||
import { getTranslations } from "next-intl/server";
|
import { getTranslations } from "next-intl/server";
|
||||||
import { PaidFeaturesAlert } from "@app/components/PaidFeaturesAlert";
|
import { PaidFeaturesAlert } from "@app/components/PaidFeaturesAlert";
|
||||||
|
import { tierMatrix } from "@server/lib/billing/tierMatrix";
|
||||||
|
|
||||||
type OrgIdpPageProps = {
|
type OrgIdpPageProps = {
|
||||||
params: Promise<{ orgId: string }>;
|
params: Promise<{ orgId: string }>;
|
||||||
@@ -35,7 +36,7 @@ export default async function OrgIdpPage(props: OrgIdpPageProps) {
|
|||||||
description={t("idpManageDescription")}
|
description={t("idpManageDescription")}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<PaidFeaturesAlert />
|
<PaidFeaturesAlert tiers={tierMatrix.orgOidc} />
|
||||||
|
|
||||||
<IdpTable idps={idps} orgId={params.orgId} />
|
<IdpTable idps={idps} orgId={params.orgId} />
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -129,7 +129,9 @@ export default function CredentialsPage() {
|
|||||||
</SettingsSectionDescription>
|
</SettingsSectionDescription>
|
||||||
</SettingsSectionHeader>
|
</SettingsSectionHeader>
|
||||||
<SettingsSectionBody>
|
<SettingsSectionBody>
|
||||||
<PaidFeaturesAlert />
|
<PaidFeaturesAlert
|
||||||
|
tiers={tierMatrix.rotateCredentials}
|
||||||
|
/>
|
||||||
|
|
||||||
<InfoSections cols={3}>
|
<InfoSections cols={3}>
|
||||||
<InfoSection>
|
<InfoSection>
|
||||||
@@ -194,7 +196,9 @@ export default function CredentialsPage() {
|
|||||||
setShouldDisconnect(false);
|
setShouldDisconnect(false);
|
||||||
setModalOpen(true);
|
setModalOpen(true);
|
||||||
}}
|
}}
|
||||||
disabled={isPaidUser(tierMatrix.rotateCredentials)}
|
disabled={
|
||||||
|
!isPaidUser(tierMatrix.rotateCredentials)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{t("regenerateCredentialsButton")}
|
{t("regenerateCredentialsButton")}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -203,7 +207,9 @@ export default function CredentialsPage() {
|
|||||||
setShouldDisconnect(true);
|
setShouldDisconnect(true);
|
||||||
setModalOpen(true);
|
setModalOpen(true);
|
||||||
}}
|
}}
|
||||||
disabled={isPaidUser(tierMatrix.rotateCredentials)}
|
disabled={
|
||||||
|
!isPaidUser(tierMatrix.rotateCredentials)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{t("remoteExitNodeRegenerateAndDisconnect")}
|
{t("remoteExitNodeRegenerateAndDisconnect")}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -119,7 +119,9 @@ export default function CredentialsPage() {
|
|||||||
</SettingsSectionDescription>
|
</SettingsSectionDescription>
|
||||||
</SettingsSectionHeader>
|
</SettingsSectionHeader>
|
||||||
<SettingsSectionBody>
|
<SettingsSectionBody>
|
||||||
<PaidFeaturesAlert />
|
<PaidFeaturesAlert
|
||||||
|
tiers={tierMatrix.rotateCredentials}
|
||||||
|
/>
|
||||||
|
|
||||||
<InfoSections cols={3}>
|
<InfoSections cols={3}>
|
||||||
<InfoSection>
|
<InfoSection>
|
||||||
@@ -180,7 +182,9 @@ export default function CredentialsPage() {
|
|||||||
setShouldDisconnect(false);
|
setShouldDisconnect(false);
|
||||||
setModalOpen(true);
|
setModalOpen(true);
|
||||||
}}
|
}}
|
||||||
disabled={isPaidUser(tierMatrix.rotateCredentials)}
|
disabled={
|
||||||
|
!isPaidUser(tierMatrix.rotateCredentials)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{t("regenerateCredentialsButton")}
|
{t("regenerateCredentialsButton")}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -189,7 +193,9 @@ export default function CredentialsPage() {
|
|||||||
setShouldDisconnect(true);
|
setShouldDisconnect(true);
|
||||||
setModalOpen(true);
|
setModalOpen(true);
|
||||||
}}
|
}}
|
||||||
disabled={isPaidUser(tierMatrix.rotateCredentials)}
|
disabled={
|
||||||
|
!isPaidUser(tierMatrix.rotateCredentials)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{t("clientRegenerateAndDisconnect")}
|
{t("clientRegenerateAndDisconnect")}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -155,13 +155,11 @@ export default function GeneralPage() {
|
|||||||
const [, startTransition] = useTransition();
|
const [, startTransition] = useTransition();
|
||||||
const { env } = useEnvContext();
|
const { env } = useEnvContext();
|
||||||
|
|
||||||
const showApprovalFeatures = build !== "oss" && isPaidUser;
|
const showApprovalFeatures =
|
||||||
|
build !== "oss" && isPaidUser(tierMatrix.deviceApprovals);
|
||||||
|
|
||||||
const formatPostureValue = (
|
const formatPostureValue = (value: boolean | null | undefined | "-") => {
|
||||||
value: boolean | null | undefined | "-"
|
if (value === null || value === undefined || value === "-") return "-";
|
||||||
) => {
|
|
||||||
if (value === null || value === undefined || value === "-")
|
|
||||||
return "-";
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{value ? (
|
{value ? (
|
||||||
@@ -584,7 +582,8 @@ export default function GeneralPage() {
|
|||||||
</SettingsSectionHeader>
|
</SettingsSectionHeader>
|
||||||
|
|
||||||
<SettingsSectionBody>
|
<SettingsSectionBody>
|
||||||
<PaidFeaturesAlert />
|
<PaidFeaturesAlert tiers={tierMatrix.devicePosture} />
|
||||||
|
|
||||||
{client.posture &&
|
{client.posture &&
|
||||||
Object.keys(client.posture).length > 0 ? (
|
Object.keys(client.posture).length > 0 ? (
|
||||||
<>
|
<>
|
||||||
@@ -598,7 +597,9 @@ export default function GeneralPage() {
|
|||||||
{t("biometricsEnabled")}
|
{t("biometricsEnabled")}
|
||||||
</InfoSectionTitle>
|
</InfoSectionTitle>
|
||||||
<InfoSectionContent>
|
<InfoSectionContent>
|
||||||
{isPaidUser(tierMatrix.devicePosture)
|
{isPaidUser(
|
||||||
|
tierMatrix.devicePosture
|
||||||
|
)
|
||||||
? formatPostureValue(
|
? formatPostureValue(
|
||||||
client.posture
|
client.posture
|
||||||
.biometricsEnabled
|
.biometricsEnabled
|
||||||
@@ -616,7 +617,9 @@ export default function GeneralPage() {
|
|||||||
{t("diskEncrypted")}
|
{t("diskEncrypted")}
|
||||||
</InfoSectionTitle>
|
</InfoSectionTitle>
|
||||||
<InfoSectionContent>
|
<InfoSectionContent>
|
||||||
{isPaidUser(tierMatrix.devicePosture)
|
{isPaidUser(
|
||||||
|
tierMatrix.devicePosture
|
||||||
|
)
|
||||||
? formatPostureValue(
|
? formatPostureValue(
|
||||||
client.posture
|
client.posture
|
||||||
.diskEncrypted
|
.diskEncrypted
|
||||||
@@ -634,7 +637,9 @@ export default function GeneralPage() {
|
|||||||
{t("firewallEnabled")}
|
{t("firewallEnabled")}
|
||||||
</InfoSectionTitle>
|
</InfoSectionTitle>
|
||||||
<InfoSectionContent>
|
<InfoSectionContent>
|
||||||
{isPaidUser(tierMatrix.devicePosture)
|
{isPaidUser(
|
||||||
|
tierMatrix.devicePosture
|
||||||
|
)
|
||||||
? formatPostureValue(
|
? formatPostureValue(
|
||||||
client.posture
|
client.posture
|
||||||
.firewallEnabled
|
.firewallEnabled
|
||||||
@@ -653,7 +658,9 @@ export default function GeneralPage() {
|
|||||||
{t("autoUpdatesEnabled")}
|
{t("autoUpdatesEnabled")}
|
||||||
</InfoSectionTitle>
|
</InfoSectionTitle>
|
||||||
<InfoSectionContent>
|
<InfoSectionContent>
|
||||||
{isPaidUser(tierMatrix.devicePosture)
|
{isPaidUser(
|
||||||
|
tierMatrix.devicePosture
|
||||||
|
)
|
||||||
? formatPostureValue(
|
? formatPostureValue(
|
||||||
client.posture
|
client.posture
|
||||||
.autoUpdatesEnabled
|
.autoUpdatesEnabled
|
||||||
@@ -671,7 +678,9 @@ export default function GeneralPage() {
|
|||||||
{t("tpmAvailable")}
|
{t("tpmAvailable")}
|
||||||
</InfoSectionTitle>
|
</InfoSectionTitle>
|
||||||
<InfoSectionContent>
|
<InfoSectionContent>
|
||||||
{isPaidUser(tierMatrix.devicePosture)
|
{isPaidUser(
|
||||||
|
tierMatrix.devicePosture
|
||||||
|
)
|
||||||
? formatPostureValue(
|
? formatPostureValue(
|
||||||
client.posture
|
client.posture
|
||||||
.tpmAvailable
|
.tpmAvailable
|
||||||
@@ -693,7 +702,9 @@ export default function GeneralPage() {
|
|||||||
)}
|
)}
|
||||||
</InfoSectionTitle>
|
</InfoSectionTitle>
|
||||||
<InfoSectionContent>
|
<InfoSectionContent>
|
||||||
{isPaidUser(tierMatrix.devicePosture)
|
{isPaidUser(
|
||||||
|
tierMatrix.devicePosture
|
||||||
|
)
|
||||||
? formatPostureValue(
|
? formatPostureValue(
|
||||||
client.posture
|
client.posture
|
||||||
.windowsAntivirusEnabled
|
.windowsAntivirusEnabled
|
||||||
@@ -711,7 +722,9 @@ export default function GeneralPage() {
|
|||||||
{t("macosSipEnabled")}
|
{t("macosSipEnabled")}
|
||||||
</InfoSectionTitle>
|
</InfoSectionTitle>
|
||||||
<InfoSectionContent>
|
<InfoSectionContent>
|
||||||
{isPaidUser(tierMatrix.devicePosture)
|
{isPaidUser(
|
||||||
|
tierMatrix.devicePosture
|
||||||
|
)
|
||||||
? formatPostureValue(
|
? formatPostureValue(
|
||||||
client.posture
|
client.posture
|
||||||
.macosSipEnabled
|
.macosSipEnabled
|
||||||
@@ -733,7 +746,9 @@ export default function GeneralPage() {
|
|||||||
)}
|
)}
|
||||||
</InfoSectionTitle>
|
</InfoSectionTitle>
|
||||||
<InfoSectionContent>
|
<InfoSectionContent>
|
||||||
{isPaidUser(tierMatrix.devicePosture)
|
{isPaidUser(
|
||||||
|
tierMatrix.devicePosture
|
||||||
|
)
|
||||||
? formatPostureValue(
|
? formatPostureValue(
|
||||||
client.posture
|
client.posture
|
||||||
.macosGatekeeperEnabled
|
.macosGatekeeperEnabled
|
||||||
@@ -755,7 +770,9 @@ export default function GeneralPage() {
|
|||||||
)}
|
)}
|
||||||
</InfoSectionTitle>
|
</InfoSectionTitle>
|
||||||
<InfoSectionContent>
|
<InfoSectionContent>
|
||||||
{isPaidUser(tierMatrix.devicePosture)
|
{isPaidUser(
|
||||||
|
tierMatrix.devicePosture
|
||||||
|
)
|
||||||
? formatPostureValue(
|
? formatPostureValue(
|
||||||
client.posture
|
client.posture
|
||||||
.macosFirewallStealthMode
|
.macosFirewallStealthMode
|
||||||
@@ -774,7 +791,9 @@ export default function GeneralPage() {
|
|||||||
{t("linuxAppArmorEnabled")}
|
{t("linuxAppArmorEnabled")}
|
||||||
</InfoSectionTitle>
|
</InfoSectionTitle>
|
||||||
<InfoSectionContent>
|
<InfoSectionContent>
|
||||||
{isPaidUser(tierMatrix.devicePosture)
|
{isPaidUser(
|
||||||
|
tierMatrix.devicePosture
|
||||||
|
)
|
||||||
? formatPostureValue(
|
? formatPostureValue(
|
||||||
client.posture
|
client.posture
|
||||||
.linuxAppArmorEnabled
|
.linuxAppArmorEnabled
|
||||||
@@ -793,7 +812,9 @@ export default function GeneralPage() {
|
|||||||
{t("linuxSELinuxEnabled")}
|
{t("linuxSELinuxEnabled")}
|
||||||
</InfoSectionTitle>
|
</InfoSectionTitle>
|
||||||
<InfoSectionContent>
|
<InfoSectionContent>
|
||||||
{isPaidUser(tierMatrix.devicePosture)
|
{isPaidUser(
|
||||||
|
tierMatrix.devicePosture
|
||||||
|
)
|
||||||
? formatPostureValue(
|
? formatPostureValue(
|
||||||
client.posture
|
client.posture
|
||||||
.linuxSELinuxEnabled
|
.linuxSELinuxEnabled
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ import { SwitchInput } from "@app/components/SwitchInput";
|
|||||||
import { PaidFeaturesAlert } from "@app/components/PaidFeaturesAlert";
|
import { PaidFeaturesAlert } from "@app/components/PaidFeaturesAlert";
|
||||||
import { usePaidStatus } from "@app/hooks/usePaidStatus";
|
import { usePaidStatus } from "@app/hooks/usePaidStatus";
|
||||||
import type { OrgContextType } from "@app/contexts/orgContext";
|
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
|
// Session length options in hours
|
||||||
const SESSION_LENGTH_OPTIONS = [
|
const SESSION_LENGTH_OPTIONS = [
|
||||||
@@ -244,13 +246,17 @@ function LogRetentionSectionForm({ org }: SectionFormProps) {
|
|||||||
|
|
||||||
{!env.flags.disableEnterpriseFeatures && (
|
{!env.flags.disableEnterpriseFeatures && (
|
||||||
<>
|
<>
|
||||||
<PaidFeaturesAlert />
|
<PaidFeaturesAlert
|
||||||
|
tiers={tierMatrix.accessLogs}
|
||||||
|
/>
|
||||||
|
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="settingsLogRetentionDaysAccess"
|
name="settingsLogRetentionDaysAccess"
|
||||||
render={({ field }) => {
|
render={({ field }) => {
|
||||||
const isDisabled = !isPaidUser;
|
const isDisabled = !isPaidUser(
|
||||||
|
tierMatrix.accessLogs
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
@@ -316,7 +322,9 @@ function LogRetentionSectionForm({ org }: SectionFormProps) {
|
|||||||
control={form.control}
|
control={form.control}
|
||||||
name="settingsLogRetentionDaysAction"
|
name="settingsLogRetentionDaysAction"
|
||||||
render={({ field }) => {
|
render={({ field }) => {
|
||||||
const isDisabled = !isPaidUser;
|
const isDisabled = !isPaidUser(
|
||||||
|
tierMatrix.actionLogs
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
@@ -521,12 +529,17 @@ function SecuritySettingsSectionForm({ org }: SectionFormProps) {
|
|||||||
id="security-settings-section-form"
|
id="security-settings-section-form"
|
||||||
className="space-y-4"
|
className="space-y-4"
|
||||||
>
|
>
|
||||||
<PaidFeaturesAlert />
|
<PaidFeaturesAlert
|
||||||
|
tiers={tierMatrix.twoFactorEnforcement}
|
||||||
|
/>
|
||||||
|
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="requireTwoFactor"
|
name="requireTwoFactor"
|
||||||
render={({ field }) => {
|
render={({ field }) => {
|
||||||
const isDisabled = !isPaidUser;
|
const isDisabled = !isPaidUser(
|
||||||
|
tierMatrix.twoFactorEnforcement
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormItem className="col-span-2">
|
<FormItem className="col-span-2">
|
||||||
@@ -573,7 +586,9 @@ function SecuritySettingsSectionForm({ org }: SectionFormProps) {
|
|||||||
control={form.control}
|
control={form.control}
|
||||||
name="maxSessionLengthHours"
|
name="maxSessionLengthHours"
|
||||||
render={({ field }) => {
|
render={({ field }) => {
|
||||||
const isDisabled = !isPaidUser;
|
const isDisabled = !isPaidUser(
|
||||||
|
tierMatrix.sessionDurationPolicies
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormItem className="col-span-2">
|
<FormItem className="col-span-2">
|
||||||
@@ -653,7 +668,9 @@ function SecuritySettingsSectionForm({ org }: SectionFormProps) {
|
|||||||
control={form.control}
|
control={form.control}
|
||||||
name="passwordExpiryDays"
|
name="passwordExpiryDays"
|
||||||
render={({ field }) => {
|
render={({ field }) => {
|
||||||
const isDisabled = !isPaidUser;
|
const isDisabled = !isPaidUser(
|
||||||
|
tierMatrix.passwordExpirationPolicies
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormItem className="col-span-2">
|
<FormItem className="col-span-2">
|
||||||
@@ -739,7 +756,12 @@ function SecuritySettingsSectionForm({ org }: SectionFormProps) {
|
|||||||
type="submit"
|
type="submit"
|
||||||
form="security-settings-section-form"
|
form="security-settings-section-form"
|
||||||
loading={loadingSave}
|
loading={loadingSave}
|
||||||
disabled={loadingSave || !isPaidUser}
|
disabled={
|
||||||
|
loadingSave ||
|
||||||
|
!isPaidUser(tierMatrix.twoFactorEnforcement) ||
|
||||||
|
!isPaidUser(tierMatrix.sessionDurationPolicies) ||
|
||||||
|
!isPaidUser(tierMatrix.passwordExpirationPolicies)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{t("saveSettings")}
|
{t("saveSettings")}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -608,7 +608,7 @@ export default function GeneralPage() {
|
|||||||
description={t("accessLogsDescription")}
|
description={t("accessLogsDescription")}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<PaidFeaturesAlert />
|
<PaidFeaturesAlert tiers={tierMatrix.accessLogs} />
|
||||||
|
|
||||||
<LogDataTable
|
<LogDataTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
@@ -618,6 +618,9 @@ export default function GeneralPage() {
|
|||||||
isRefreshing={isRefreshing}
|
isRefreshing={isRefreshing}
|
||||||
onExport={() => startTransition(exportData)}
|
onExport={() => startTransition(exportData)}
|
||||||
isExporting={isExporting}
|
isExporting={isExporting}
|
||||||
|
isExportDisabled={
|
||||||
|
!isPaidUser(tierMatrix.accessLogs) || build === "oss"
|
||||||
|
}
|
||||||
onDateRangeChange={handleDateRangeChange}
|
onDateRangeChange={handleDateRangeChange}
|
||||||
dateRange={{
|
dateRange={{
|
||||||
start: dateRange.startDate,
|
start: dateRange.startDate,
|
||||||
|
|||||||
@@ -461,7 +461,7 @@ export default function GeneralPage() {
|
|||||||
description={t("actionLogsDescription")}
|
description={t("actionLogsDescription")}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<PaidFeaturesAlert />
|
<PaidFeaturesAlert tiers={tierMatrix.actionLogs} />
|
||||||
|
|
||||||
<LogDataTable
|
<LogDataTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
@@ -472,6 +472,9 @@ export default function GeneralPage() {
|
|||||||
onRefresh={refreshData}
|
onRefresh={refreshData}
|
||||||
isRefreshing={isRefreshing}
|
isRefreshing={isRefreshing}
|
||||||
onExport={() => startTransition(exportData)}
|
onExport={() => startTransition(exportData)}
|
||||||
|
isExportDisabled={
|
||||||
|
!isPaidUser(tierMatrix.logExport) || build === "oss"
|
||||||
|
}
|
||||||
isExporting={isExporting}
|
isExporting={isExporting}
|
||||||
onDateRangeChange={handleDateRangeChange}
|
onDateRangeChange={handleDateRangeChange}
|
||||||
dateRange={{
|
dateRange={{
|
||||||
|
|||||||
@@ -178,13 +178,15 @@ function MaintenanceSectionForm({
|
|||||||
className="space-y-4"
|
className="space-y-4"
|
||||||
id="maintenance-settings-form"
|
id="maintenance-settings-form"
|
||||||
>
|
>
|
||||||
<PaidFeaturesAlert />
|
<PaidFeaturesAlert
|
||||||
|
tiers={tierMatrix.maintencePage}
|
||||||
|
/>
|
||||||
<FormField
|
<FormField
|
||||||
control={maintenanceForm.control}
|
control={maintenanceForm.control}
|
||||||
name="maintenanceModeEnabled"
|
name="maintenanceModeEnabled"
|
||||||
render={({ field }) => {
|
render={({ field }) => {
|
||||||
const isDisabled =
|
const isDisabled =
|
||||||
isPaidUser(tierMatrix.maintencePage) ||
|
!isPaidUser(tierMatrix.maintencePage) ||
|
||||||
resource.http === false;
|
resource.http === false;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -251,7 +253,11 @@ function MaintenanceSectionForm({
|
|||||||
defaultValue={
|
defaultValue={
|
||||||
field.value
|
field.value
|
||||||
}
|
}
|
||||||
disabled={isPaidUser(tierMatrix.maintencePage)}
|
disabled={
|
||||||
|
!isPaidUser(
|
||||||
|
tierMatrix.maintencePage
|
||||||
|
)
|
||||||
|
}
|
||||||
className="flex flex-col space-y-1"
|
className="flex flex-col space-y-1"
|
||||||
>
|
>
|
||||||
<FormItem className="flex items-start space-x-3 space-y-0">
|
<FormItem className="flex items-start space-x-3 space-y-0">
|
||||||
@@ -324,7 +330,11 @@ function MaintenanceSectionForm({
|
|||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
{...field}
|
{...field}
|
||||||
disabled={isPaidUser(tierMatrix.maintencePage)}
|
disabled={
|
||||||
|
!isPaidUser(
|
||||||
|
tierMatrix.maintencePage
|
||||||
|
)
|
||||||
|
}
|
||||||
placeholder="We'll be back soon!"
|
placeholder="We'll be back soon!"
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -350,7 +360,11 @@ function MaintenanceSectionForm({
|
|||||||
<Textarea
|
<Textarea
|
||||||
{...field}
|
{...field}
|
||||||
rows={4}
|
rows={4}
|
||||||
disabled={isPaidUser(tierMatrix.maintencePage)}
|
disabled={
|
||||||
|
!isPaidUser(
|
||||||
|
tierMatrix.maintencePage
|
||||||
|
)
|
||||||
|
}
|
||||||
placeholder={t(
|
placeholder={t(
|
||||||
"maintenancePageMessagePlaceholder"
|
"maintenancePageMessagePlaceholder"
|
||||||
)}
|
)}
|
||||||
@@ -379,7 +393,11 @@ function MaintenanceSectionForm({
|
|||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
{...field}
|
{...field}
|
||||||
disabled={isPaidUser(tierMatrix.maintencePage)}
|
disabled={
|
||||||
|
!isPaidUser(
|
||||||
|
tierMatrix.maintencePage
|
||||||
|
)
|
||||||
|
}
|
||||||
placeholder={t(
|
placeholder={t(
|
||||||
"maintenanceTime"
|
"maintenanceTime"
|
||||||
)}
|
)}
|
||||||
@@ -405,7 +423,10 @@ function MaintenanceSectionForm({
|
|||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
loading={maintenanceSaveLoading}
|
loading={maintenanceSaveLoading}
|
||||||
disabled={maintenanceSaveLoading || !isPaidUser}
|
disabled={
|
||||||
|
maintenanceSaveLoading ||
|
||||||
|
!isPaidUser(tierMatrix.maintencePage)
|
||||||
|
}
|
||||||
form="maintenance-settings-form"
|
form="maintenance-settings-form"
|
||||||
>
|
>
|
||||||
{t("saveSettings")}
|
{t("saveSettings")}
|
||||||
|
|||||||
@@ -196,7 +196,9 @@ export default function CredentialsPage() {
|
|||||||
</SettingsSectionDescription>
|
</SettingsSectionDescription>
|
||||||
</SettingsSectionHeader>
|
</SettingsSectionHeader>
|
||||||
|
|
||||||
<PaidFeaturesAlert />
|
<PaidFeaturesAlert
|
||||||
|
tiers={tierMatrix.rotateCredentials}
|
||||||
|
/>
|
||||||
|
|
||||||
<SettingsSectionBody>
|
<SettingsSectionBody>
|
||||||
<InfoSections cols={3}>
|
<InfoSections cols={3}>
|
||||||
@@ -268,7 +270,11 @@ export default function CredentialsPage() {
|
|||||||
setShouldDisconnect(false);
|
setShouldDisconnect(false);
|
||||||
setModalOpen(true);
|
setModalOpen(true);
|
||||||
}}
|
}}
|
||||||
disabled={isPaidUser(tierMatrix.rotateCredentials)}
|
disabled={
|
||||||
|
!isPaidUser(
|
||||||
|
tierMatrix.rotateCredentials
|
||||||
|
)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{t("regenerateCredentialsButton")}
|
{t("regenerateCredentialsButton")}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -277,7 +283,11 @@ export default function CredentialsPage() {
|
|||||||
setShouldDisconnect(true);
|
setShouldDisconnect(true);
|
||||||
setModalOpen(true);
|
setModalOpen(true);
|
||||||
}}
|
}}
|
||||||
disabled={isPaidUser(tierMatrix.rotateCredentials)}
|
disabled={
|
||||||
|
!isPaidUser(
|
||||||
|
tierMatrix.rotateCredentials
|
||||||
|
)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{t("siteRegenerateAndDisconnect")}
|
{t("siteRegenerateAndDisconnect")}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -304,7 +314,9 @@ export default function CredentialsPage() {
|
|||||||
</SettingsSectionDescription>
|
</SettingsSectionDescription>
|
||||||
</SettingsSectionHeader>
|
</SettingsSectionHeader>
|
||||||
|
|
||||||
<PaidFeaturesAlert />
|
<PaidFeaturesAlert
|
||||||
|
tiers={tierMatrix.rotateCredentials}
|
||||||
|
/>
|
||||||
|
|
||||||
<SettingsSectionBody>
|
<SettingsSectionBody>
|
||||||
{!loadingDefaults && (
|
{!loadingDefaults && (
|
||||||
@@ -378,7 +390,11 @@ export default function CredentialsPage() {
|
|||||||
<SettingsSectionFooter>
|
<SettingsSectionFooter>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => setModalOpen(true)}
|
onClick={() => setModalOpen(true)}
|
||||||
disabled={isPaidUser(tierMatrix.rotateCredentials)}
|
disabled={
|
||||||
|
!isPaidUser(
|
||||||
|
tierMatrix.rotateCredentials
|
||||||
|
)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{t("siteRegenerateAndDisconnect")}
|
{t("siteRegenerateAndDisconnect")}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import { usePaidStatus } from "@app/hooks/usePaidStatus";
|
|||||||
import { build } from "@server/build";
|
import { build } from "@server/build";
|
||||||
import { PaidFeaturesAlert } from "./PaidFeaturesAlert";
|
import { PaidFeaturesAlert } from "./PaidFeaturesAlert";
|
||||||
import { Alert, AlertDescription, AlertTitle } from "./ui/alert";
|
import { Alert, AlertDescription, AlertTitle } from "./ui/alert";
|
||||||
|
import { tierMatrix } from "@server/lib/billing/tierMatrix";
|
||||||
|
|
||||||
export type AuthPageCustomizationProps = {
|
export type AuthPageCustomizationProps = {
|
||||||
orgId: string;
|
orgId: string;
|
||||||
@@ -139,7 +140,7 @@ export default function AuthPageBrandingForm({
|
|||||||
`Choose your preferred authentication method for {{resourceName}}`,
|
`Choose your preferred authentication method for {{resourceName}}`,
|
||||||
primaryColor: branding?.primaryColor ?? `#f36117` // default pangolin primary color
|
primaryColor: branding?.primaryColor ?? `#f36117` // default pangolin primary color
|
||||||
},
|
},
|
||||||
disabled: !isPaidUser
|
disabled: !isPaidUser(tierMatrix.loginPageBranding)
|
||||||
});
|
});
|
||||||
|
|
||||||
async function updateBranding() {
|
async function updateBranding() {
|
||||||
@@ -221,7 +222,9 @@ export default function AuthPageBrandingForm({
|
|||||||
|
|
||||||
<SettingsSectionBody>
|
<SettingsSectionBody>
|
||||||
<SettingsSectionForm>
|
<SettingsSectionForm>
|
||||||
<PaidFeaturesAlert />
|
<PaidFeaturesAlert
|
||||||
|
tiers={tierMatrix.loginPageBranding}
|
||||||
|
/>
|
||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
|
|||||||
@@ -285,7 +285,7 @@ function AuthPageSettings({
|
|||||||
</SettingsSectionHeader>
|
</SettingsSectionHeader>
|
||||||
<SettingsSectionBody>
|
<SettingsSectionBody>
|
||||||
<SettingsSectionForm>
|
<SettingsSectionForm>
|
||||||
<PaidFeaturesAlert />
|
<PaidFeaturesAlert tiers={tierMatrix.loginPageDomain} />
|
||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ import {
|
|||||||
import { Input } from "@app/components/ui/input";
|
import { Input } from "@app/components/ui/input";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { Control, FieldValues, Path } from "react-hook-form";
|
import { Control, FieldValues, Path } from "react-hook-form";
|
||||||
|
import { usePaidStatus } from "@app/hooks/usePaidStatus";
|
||||||
|
import { tierMatrix } from "@server/lib/billing/tierMatrix";
|
||||||
|
|
||||||
type Role = {
|
type Role = {
|
||||||
roleId: number;
|
roleId: number;
|
||||||
@@ -49,6 +51,8 @@ export default function AutoProvisionConfigWidget<T extends FieldValues>({
|
|||||||
}: AutoProvisionConfigWidgetProps<T>) {
|
}: AutoProvisionConfigWidgetProps<T>) {
|
||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
|
|
||||||
|
const { isPaidUser } = usePaidStatus();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
@@ -57,6 +61,7 @@ export default function AutoProvisionConfigWidget<T extends FieldValues>({
|
|||||||
label={t("idpAutoProvisionUsers")}
|
label={t("idpAutoProvisionUsers")}
|
||||||
defaultChecked={autoProvision}
|
defaultChecked={autoProvision}
|
||||||
onCheckedChange={onAutoProvisionChange}
|
onCheckedChange={onAutoProvisionChange}
|
||||||
|
disabled={!isPaidUser(tierMatrix.autoProvisioning)}
|
||||||
/>
|
/>
|
||||||
<span className="text-sm text-muted-foreground">
|
<span className="text-sm text-muted-foreground">
|
||||||
{t("idpAutoProvisionUsersDescription")}
|
{t("idpAutoProvisionUsersDescription")}
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import { useForm } from "react-hook-form";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { PaidFeaturesAlert } from "./PaidFeaturesAlert";
|
import { PaidFeaturesAlert } from "./PaidFeaturesAlert";
|
||||||
import { CheckboxWithLabel } from "./ui/checkbox";
|
import { CheckboxWithLabel } from "./ui/checkbox";
|
||||||
|
import { tierMatrix } from "@server/lib/billing/tierMatrix";
|
||||||
|
|
||||||
type CreateRoleFormProps = {
|
type CreateRoleFormProps = {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@@ -164,7 +165,9 @@ export default function CreateRoleForm({
|
|||||||
|
|
||||||
{!env.flags.disableEnterpriseFeatures && (
|
{!env.flags.disableEnterpriseFeatures && (
|
||||||
<>
|
<>
|
||||||
<PaidFeaturesAlert />
|
<PaidFeaturesAlert
|
||||||
|
tiers={tierMatrix.deviceApprovals}
|
||||||
|
/>
|
||||||
|
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
@@ -175,7 +178,9 @@ export default function CreateRoleForm({
|
|||||||
<CheckboxWithLabel
|
<CheckboxWithLabel
|
||||||
{...field}
|
{...field}
|
||||||
disabled={
|
disabled={
|
||||||
!isPaidUser
|
!isPaidUser(
|
||||||
|
tierMatrix.deviceApprovals
|
||||||
|
)
|
||||||
}
|
}
|
||||||
value="on"
|
value="on"
|
||||||
checked={form.watch(
|
checked={form.watch(
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ import { useForm } from "react-hook-form";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { PaidFeaturesAlert } from "./PaidFeaturesAlert";
|
import { PaidFeaturesAlert } from "./PaidFeaturesAlert";
|
||||||
import { CheckboxWithLabel } from "./ui/checkbox";
|
import { CheckboxWithLabel } from "./ui/checkbox";
|
||||||
|
import { tierMatrix } from "@server/lib/billing/tierMatrix";
|
||||||
|
|
||||||
type CreateRoleFormProps = {
|
type CreateRoleFormProps = {
|
||||||
role: Role;
|
role: Role;
|
||||||
@@ -172,7 +173,9 @@ export default function EditRoleForm({
|
|||||||
|
|
||||||
{!env.flags.disableEnterpriseFeatures && (
|
{!env.flags.disableEnterpriseFeatures && (
|
||||||
<>
|
<>
|
||||||
<PaidFeaturesAlert />
|
<PaidFeaturesAlert
|
||||||
|
tiers={tierMatrix.deviceApprovals}
|
||||||
|
/>
|
||||||
|
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
@@ -183,7 +186,9 @@ export default function EditRoleForm({
|
|||||||
<CheckboxWithLabel
|
<CheckboxWithLabel
|
||||||
{...field}
|
{...field}
|
||||||
disabled={
|
disabled={
|
||||||
!isPaidUser
|
!isPaidUser(
|
||||||
|
tierMatrix.deviceApprovals
|
||||||
|
)
|
||||||
}
|
}
|
||||||
value="on"
|
value="on"
|
||||||
checked={form.watch(
|
checked={form.watch(
|
||||||
|
|||||||
@@ -120,6 +120,7 @@ type DataTableProps<TData, TValue> = {
|
|||||||
// Row expansion props
|
// Row expansion props
|
||||||
expandable?: boolean;
|
expandable?: boolean;
|
||||||
renderExpandedRow?: (row: TData) => React.ReactNode;
|
renderExpandedRow?: (row: TData) => React.ReactNode;
|
||||||
|
isExportDisabled?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function LogDataTable<TData, TValue>({
|
export function LogDataTable<TData, TValue>({
|
||||||
@@ -145,7 +146,8 @@ export function LogDataTable<TData, TValue>({
|
|||||||
isLoading = false,
|
isLoading = false,
|
||||||
expandable = false,
|
expandable = false,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
renderExpandedRow
|
renderExpandedRow,
|
||||||
|
isExportDisabled
|
||||||
}: DataTableProps<TData, TValue>) {
|
}: DataTableProps<TData, TValue>) {
|
||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
|
|
||||||
@@ -403,7 +405,7 @@ export function LogDataTable<TData, TValue>({
|
|||||||
onClick={() =>
|
onClick={() =>
|
||||||
!disabled && onExport()
|
!disabled && onExport()
|
||||||
}
|
}
|
||||||
disabled={isExporting || disabled}
|
disabled={isExporting || disabled || isExportDisabled}
|
||||||
>
|
>
|
||||||
{isExporting ? (
|
{isExporting ? (
|
||||||
<Loader className="mr-2 size-4 animate-spin" />
|
<Loader className="mr-2 size-4 animate-spin" />
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { Card, CardContent } from "@app/components/ui/card";
|
import { Card, CardContent } from "@app/components/ui/card";
|
||||||
import { build } from "@server/build";
|
import { build } from "@server/build";
|
||||||
import { usePaidStatus } from "@app/hooks/usePaidStatus";
|
import { usePaidStatus } from "@app/hooks/usePaidStatus";
|
||||||
@@ -6,6 +7,7 @@ import { ExternalLink, KeyRound, Sparkles } from "lucide-react";
|
|||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||||
|
import { Tier } from "@server/types/Tiers";
|
||||||
|
|
||||||
const bannerClassName =
|
const bannerClassName =
|
||||||
"mb-6 border-primary/30 bg-linear-to-br from-primary/10 via-background to-background overflow-hidden";
|
"mb-6 border-primary/30 bg-linear-to-br from-primary/10 via-background to-background overflow-hidden";
|
||||||
@@ -13,7 +15,11 @@ const bannerContentClassName = "py-3 px-4";
|
|||||||
const bannerRowClassName =
|
const bannerRowClassName =
|
||||||
"flex items-center gap-2.5 text-sm text-muted-foreground";
|
"flex items-center gap-2.5 text-sm text-muted-foreground";
|
||||||
|
|
||||||
export function PaidFeaturesAlert() {
|
type Props = {
|
||||||
|
tiers: Tier[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export function PaidFeaturesAlert({ tiers }: Props) {
|
||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
const { hasSaasSubscription, hasEnterpriseLicense } = usePaidStatus();
|
const { hasSaasSubscription, hasEnterpriseLicense } = usePaidStatus();
|
||||||
const { env } = useEnvContext();
|
const { env } = useEnvContext();
|
||||||
@@ -24,7 +30,7 @@ export function PaidFeaturesAlert() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{build === "saas" && !hasSaasSubscription ? (
|
{build === "saas" && !hasSaasSubscription(tiers) ? (
|
||||||
<Card className={bannerClassName}>
|
<Card className={bannerClassName}>
|
||||||
<CardContent className={bannerContentClassName}>
|
<CardContent className={bannerContentClassName}>
|
||||||
<div className={bannerRowClassName}>
|
<div className={bannerRowClassName}>
|
||||||
|
|||||||
@@ -5,16 +5,12 @@ import DeleteRoleForm from "@app/components/DeleteRoleForm";
|
|||||||
import { RolesDataTable } from "@app/components/RolesDataTable";
|
import { RolesDataTable } from "@app/components/RolesDataTable";
|
||||||
import { Button } from "@app/components/ui/button";
|
import { Button } from "@app/components/ui/button";
|
||||||
import { ExtendedColumnDef } from "@app/components/ui/data-table";
|
import { ExtendedColumnDef } from "@app/components/ui/data-table";
|
||||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
|
||||||
import { useOrgContext } from "@app/hooks/useOrgContext";
|
|
||||||
import { toast } from "@app/hooks/useToast";
|
import { toast } from "@app/hooks/useToast";
|
||||||
import { createApiClient } from "@app/lib/api";
|
|
||||||
import { Role } from "@server/db";
|
import { Role } from "@server/db";
|
||||||
import { ArrowRight, ArrowUpDown, Link, MoreHorizontal } from "lucide-react";
|
import { ArrowUpDown, MoreHorizontal } from "lucide-react";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { useState, useTransition } from "react";
|
import { useState, useTransition } from "react";
|
||||||
import { usePaidStatus } from "@app/hooks/usePaidStatus";
|
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
@@ -38,11 +34,6 @@ export default function UsersTable({ roles }: RolesTableProps) {
|
|||||||
|
|
||||||
const [roleToRemove, setRoleToRemove] = useState<RoleRow | null>(null);
|
const [roleToRemove, setRoleToRemove] = useState<RoleRow | null>(null);
|
||||||
|
|
||||||
const api = createApiClient(useEnvContext());
|
|
||||||
|
|
||||||
const { org } = useOrgContext();
|
|
||||||
const { isPaidUser } = usePaidStatus();
|
|
||||||
|
|
||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
const [isRefreshing, startTransition] = useTransition();
|
const [isRefreshing, startTransition] = useTransition();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user