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}
+
+
+ >
+ )
+}