diff --git a/ee/tabby-ui/app/(dashboard)/settings/subscription/components/subscription.tsx b/ee/tabby-ui/app/(dashboard)/settings/subscription/components/subscription.tsx index 692da344318..09d3a5ee72e 100644 --- a/ee/tabby-ui/app/(dashboard)/settings/subscription/components/subscription.tsx +++ b/ee/tabby-ui/app/(dashboard)/settings/subscription/components/subscription.tsx @@ -3,8 +3,14 @@ import { capitalize } from 'lodash-es' import moment from 'moment' -import { LicenseInfo, LicenseType } from '@/lib/gql/generates/graphql' +import { + LicenseInfo, + LicenseStatus, + LicenseType +} from '@/lib/gql/generates/graphql' import { useLicense } from '@/lib/hooks/use-license' +import { Badge } from '@/components/ui/badge' +import { IconAlertTriangle } from '@/components/ui/icons' import { Skeleton } from '@/components/ui/skeleton' import LoadingWrapper from '@/components/loading-wrapper' import { SubHeader } from '@/components/sub-header' @@ -60,11 +66,27 @@ function License({ license }: { license: LicenseInfo }) {
Expires at
-
{expiresAt}
+
+ {expiresAt} + {license.status === LicenseStatus.Expired && ( + + + Expired + + )} +
Assigned / Total Seats
-
{seatsText}
+
+ {seatsText} + {license.status === LicenseStatus.Expired && ( + + + Seats exceeded + + )} +
Current plan
diff --git a/ee/tabby-ui/components/header.tsx b/ee/tabby-ui/components/header.tsx index b89f1fdfae3..6b94f11ce01 100644 --- a/ee/tabby-ui/components/header.tsx +++ b/ee/tabby-ui/components/header.tsx @@ -3,11 +3,13 @@ import * as React from 'react' import { compare } from 'compare-versions' +import { LicenseStatus } from '@/lib/gql/generates/graphql' import { useHealth } from '@/lib/hooks/use-health' import { ReleaseInfo, useLatestRelease } from '@/lib/hooks/use-latest-release' +import { useLicenseInfo } from '@/lib/hooks/use-license' import { cn } from '@/lib/utils' import { buttonVariants } from '@/components/ui/button' -import { IconNotice } from '@/components/ui/icons' +import { IconInfoCircled, IconNotice } from '@/components/ui/icons' import { ClientOnly } from './client-only' import { ThemeToggle } from './theme-toggle' @@ -22,7 +24,7 @@ export function Header() { return (
-
+
{newVersionAvailable && ( )} +
@@ -60,3 +63,27 @@ function isNewVersionAvailable(version?: string, latestRelease?: ReleaseInfo) { return true } } + +function LicenseAlert() { + const license = useLicenseInfo() + + if (!license) return null + + if (license.status === LicenseStatus.Expired) { + return ( +
+ + Your license has expired. +
+ ) + } + + if (license.status === LicenseStatus.SeatsExceeded) { + return ( +
+ + Your seat count has exceeded the limit. +
+ ) + } +} diff --git a/ee/tabby-ui/components/license-guard.tsx b/ee/tabby-ui/components/license-guard.tsx index 5da745e80c5..c45387ebfe4 100644 --- a/ee/tabby-ui/components/license-guard.tsx +++ b/ee/tabby-ui/components/license-guard.tsx @@ -4,6 +4,7 @@ import { capitalize } from 'lodash-es' import { GetLicenseInfoQuery, + LicenseInfo, LicenseStatus, LicenseType } from '@/lib/gql/generates/graphql' @@ -18,19 +19,25 @@ import { interface LicenseGuardProps { licenses: LicenseType[] + isSeatCountRelated?: boolean children: (params: { hasValidLicense: boolean license: GetLicenseInfoQuery['license'] | undefined | null }) => React.ReactNode } -const LicenseGuard: React.FC = ({ licenses, children }) => { +const LicenseGuard: React.FC = ({ + licenses, + isSeatCountRelated, + children +}) => { const [open, setOpen] = React.useState(false) const license = useLicenseInfo() + const hasValidLicense = !!license && - license.status === LicenseStatus.Ok && - licenses.includes(license.type) + licenses.includes(license.type) && + license.status === LicenseStatus.Ok const onOpenChange = (v: boolean) => { if (hasValidLicense) return @@ -46,16 +53,11 @@ const LicenseGuard: React.FC = ({ licenses, children }) => { return ( -
- This feature is only available on Tabby's{' '} - {licenseText} plan. Upgrade to - use this feature. -
-
- - Upgrade to {licenseString} - -
+
= ({ licenses, children }) => { LicenseGuard.displayName = 'LicenseGuard' export { LicenseGuard } + +function LicenseTips({ + licenses, + license, + isSeatCountRelated +}: { + licenses: LicenseType[] + license: LicenseInfo | undefined + isSeatCountRelated: boolean +}) { + const hasSufficientLicense = !!license && licenses.includes(license.type) + const isLicenseExpired = + hasSufficientLicense && license?.status === LicenseStatus.Expired + const isSearCountRelated = + hasSufficientLicense && license?.status === LicenseStatus.SeatsExceeded + + const licenseString = capitalize(licenses[0]) + let insufficientLicenseText = licenseString + if (licenses.length == 2) { + insufficientLicenseText = `${capitalize(licenses[0])} or ${capitalize( + licenses[1] + )}` + } + + if (isSearCountRelated && isSeatCountRelated) { + return ( + <> +
+ Your seat count has exceeded the limit. Please upgrade your license to + continue using this feature. +
+
+ + Upgrade license + +
+ `` + + ) + } + + if (isLicenseExpired) { + return ( + <> +
+ Your license has expired. Please update your license to use this + feature. +
+
+ + Update license + +
+ + ) + } + + return ( + <> +
+ This feature is only available on Tabby's{' '} + {insufficientLicenseText} plan. + Upgrade to use this feature. +
+
+ + Upgrade to {licenseString} + +
+ + ) +}