Skip to content

Commit ca2acd1

Browse files
committed
feat: add support for freemium license
1 parent b4c9dad commit ca2acd1

8 files changed

Lines changed: 108 additions & 38 deletions

File tree

src/Shared/Components/GenericModal/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import { GenericModalProps } from './types'
1818

1919
export const MODAL_WIDTH_TO_CLASS_NAME_MAP: Record<GenericModalProps['width'], string> = {
20+
450: 'w-450',
2021
500: 'w-500',
2122
600: 'w-600',
2223
800: 'w-800',

src/Shared/Components/GenericModal/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export interface GenericModalProps extends Partial<Pick<BackdropProps, 'onEscape
2828
* Width of the modal (in pixels).
2929
* @default 600
3030
*/
31-
width?: 500 | 600 | 800
31+
width?: 450 | 500 | 600 | 800
3232
/**
3333
* Determines if the modal should close when the user clicks outside of it (on the backdrop).
3434
* @default false

src/Shared/Components/Header/HelpButton.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ export const HelpButton = ({ serverInfo, fetchingServerInfo, onClick, hideGettin
148148
alignment="end"
149149
width={220}
150150
options={getHelpActionMenuOptions({
151-
isTrial: licenseData?.isTrial ?? false,
151+
isTrialOrFreemium: (licenseData?.isTrial || licenseData?.isFreemium) ?? false,
152152
isEnterprise,
153153
})}
154154
onClick={handleActionMenuClick}

src/Shared/Components/Header/utils.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,10 @@ export const setActionWithExpiry = (key: string, days: number): void => {
4444

4545
export const getHelpActionMenuOptions = ({
4646
isEnterprise,
47-
isTrial,
47+
isTrialOrFreemium,
4848
}: {
4949
isEnterprise: boolean
50-
isTrial: boolean
50+
isTrialOrFreemium: boolean
5151
}): HelpButtonActionMenuProps['options'] => [
5252
{
5353
items: COMMON_HELP_ACTION_MENU_ITEMS,
@@ -56,7 +56,9 @@ export const getHelpActionMenuOptions = ({
5656
? [
5757
{
5858
groupLabel: 'Enterprise Support',
59-
items: isTrial ? ENTERPRISE_TRIAL_HELP_ACTION_MENU_ITEMS : ENTERPRISE_HELP_ACTION_MENU_ITEMS,
59+
items: isTrialOrFreemium
60+
? ENTERPRISE_TRIAL_HELP_ACTION_MENU_ITEMS
61+
: ENTERPRISE_HELP_ACTION_MENU_ITEMS,
6062
},
6163
]
6264
: [

src/Shared/Components/License/DevtronLicenseCard.tsx

Lines changed: 82 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { ClipboardButton, getTTLInHumanReadableFormat } from '@Common/index'
2323
import { CONTACT_SUPPORT_LINK, ENTERPRISE_SUPPORT_LINK } from '@Shared/constants'
2424
import { AppThemeType } from '@Shared/Providers'
2525
import { getThemeOppositeThemeClass } from '@Shared/Providers/ThemeProvider/utils'
26+
import { LicensingErrorCodes } from '@Shared/types'
2627

2728
import { Button, ButtonComponentType, ButtonVariantType } from '../Button'
2829
import { Icon } from '../Icon'
@@ -33,21 +34,85 @@ import './licenseCard.scss'
3334

3435
const DAMPEN_FACTOR = 50
3536

37+
const LicenseCardSubText = ({
38+
isFreemium,
39+
licenseStatus,
40+
licenseStatusError,
41+
}: Pick<DevtronLicenseCardProps, 'isFreemium' | 'licenseStatus' | 'licenseStatusError'>) => {
42+
if (licenseStatus === LicenseStatus.ACTIVE && !isFreemium) {
43+
return null
44+
}
45+
46+
const isLicenseValid = licenseStatus !== LicenseStatus.EXPIRED && !licenseStatusError
47+
const freemiumLimitReached = licenseStatusError?.code === LicensingErrorCodes.ClusterLimitExceeded
48+
49+
return licenseStatus !== LicenseStatus.ACTIVE ? (
50+
<div className="p-16 fs-13 lh-1-5 flexbox-col dc__gap-8">
51+
<div className="flexbox dc__gap-8">
52+
{freemiumLimitReached ? (
53+
<div className="flexbox-col dc__gap-4 fs-13 fw-4 lh-20 cn-9">
54+
<span className="fw-6">Multiple Clusters Detected</span>
55+
<span>
56+
Your account is connected to multiple clusters, which isn’t allowed on the freemium plan.
57+
Upgrade to an Enterprise license or contact us.
58+
</span>
59+
</div>
60+
) : (
61+
<span>
62+
To renew your license mail us at&nbsp;
63+
<a href={`mailto:${ENTERPRISE_SUPPORT_LINK}`}>{ENTERPRISE_SUPPORT_LINK}</a> or contact your
64+
Devtron representative.
65+
</span>
66+
)}
67+
<Icon
68+
name={isLicenseValid ? 'ic-timer' : 'ic-error'}
69+
color={isLicenseValid ? 'Y500' : 'R500'}
70+
size={16}
71+
/>
72+
</div>
73+
{freemiumLimitReached && (
74+
<Button
75+
dataTestId="mail-support"
76+
startIcon={<Icon name="ic-email" color={null} />}
77+
text={ENTERPRISE_SUPPORT_LINK}
78+
variant={ButtonVariantType.text}
79+
component={ButtonComponentType.anchor}
80+
anchorProps={{ href: `mailto:${ENTERPRISE_SUPPORT_LINK}` }}
81+
/>
82+
)}
83+
<Button
84+
dataTestId="contact-support"
85+
startIcon={<ICChatSupport />}
86+
text="Contact support"
87+
variant={ButtonVariantType.text}
88+
component={ButtonComponentType.anchor}
89+
anchorProps={{ href: CONTACT_SUPPORT_LINK }}
90+
/>
91+
</div>
92+
) : (
93+
<div className="flexbox p-16 cn-9 fw-6 fs-13 lh-1-5 dc__align-items-center dc__content-space">
94+
<span>Unlimited single cluster usage</span>
95+
<Icon name="ic-success" size={20} color={null} />
96+
</div>
97+
)
98+
}
99+
36100
export const DevtronLicenseCard = ({
37101
enterpriseName,
38102
licenseKey,
39103
licenseSuffix,
40104
expiryDate,
41105
licenseStatus,
42106
isTrial,
107+
isFreemium,
43108
ttl,
44109
appTheme,
45110
handleCopySuccess,
111+
licenseStatusError,
46112
}: DevtronLicenseCardProps) => {
47113
const { bgColor, textColor } = getLicenseColorsAccordingToStatus(licenseStatus)
48114
const remainingTime = getTTLInHumanReadableFormat(ttl)
49115
const remainingTimeString = ttl < 0 ? `Expired ${remainingTime} ago` : `${remainingTime} remaining`
50-
const isLicenseValid = licenseStatus !== LicenseStatus.EXPIRED
51116
const isThemeDark = appTheme === AppThemeType.dark
52117

53118
const cardRef = useRef<HTMLDivElement>(null)
@@ -130,43 +195,30 @@ export const DevtronLicenseCard = ({
130195
)}
131196
</div>
132197
<div className="flexbox dc__align-items-center dc__gap-4 flex-wrap fs-12">
133-
<span className="font-ibm-plex-mono cn-9">{expiryDate}</span>
134-
<span className="cn-9">·</span>
135-
<span style={{ color: textColor }}>{remainingTimeString}</span>
198+
<span className="font-ibm-plex-mono cn-9">
199+
{isFreemium ? 'VALID FOREVER' : expiryDate}
200+
</span>
201+
{!isFreemium && (
202+
<>
203+
<span className="cn-9">·</span>
204+
<span style={{ color: textColor }}>{remainingTimeString}</span>
205+
</>
206+
)}
136207
</div>
137208
</div>
138209
</div>
139-
{isTrial && (
210+
{(isTrial || isFreemium) && (
140211
<span className="trial-license-badge flexbox dc__align-items-center px-20 py-6 cn-9 fs-11 fw-5 lh-1-5 dc__zi-2">
141-
TRIAL LICENSE
212+
{isFreemium ? 'FREEMIUM' : 'TRIAL'} LICENSE
142213
</span>
143214
)}
144215
</motion.div>
145216
</div>
146-
{licenseStatus !== LicenseStatus.ACTIVE && (
147-
<div className="p-16 fs-13 lh-1-5 flexbox-col dc__gap-8">
148-
<div className="flexbox dc__gap-8">
149-
<span>
150-
To renew your license mail us at&nbsp;
151-
<a href={`mailto:${ENTERPRISE_SUPPORT_LINK}`}>{ENTERPRISE_SUPPORT_LINK}</a> or contact your
152-
Devtron representative.
153-
</span>
154-
<Icon
155-
name={isLicenseValid ? 'ic-timer' : 'ic-error'}
156-
color={isLicenseValid ? 'Y500' : 'R500'}
157-
size={16}
158-
/>
159-
</div>
160-
<Button
161-
dataTestId="contact-support"
162-
startIcon={<ICChatSupport />}
163-
text="Contact support"
164-
variant={ButtonVariantType.text}
165-
component={ButtonComponentType.anchor}
166-
anchorProps={{ href: CONTACT_SUPPORT_LINK }}
167-
/>
168-
</div>
169-
)}
217+
<LicenseCardSubText
218+
isFreemium={isFreemium}
219+
licenseStatusError={licenseStatusError}
220+
licenseStatus={licenseStatus}
221+
/>
170222
</div>
171223
)
172224
}

src/Shared/Components/License/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*/
1616

1717
import { AppThemeType } from '@Shared/Providers'
18-
import { DevtronLicenseBaseDTO, DevtronLicenseDTO } from '@Shared/types'
18+
import { DevtronLicenseBaseDTO, DevtronLicenseDTO, LicenseErrorStruct } from '@Shared/types'
1919

2020
export enum LicenseStatus {
2121
ACTIVE = 'ACTIVE',
@@ -29,7 +29,9 @@ export type DevtronLicenseCardProps = {
2929
ttl: number
3030
licenseStatus: LicenseStatus
3131
isTrial: boolean
32+
isFreemium: boolean
3233
appTheme: AppThemeType
34+
licenseStatusError: LicenseErrorStruct
3335
} & (
3436
| {
3537
licenseKey: string

src/Shared/Components/License/utils.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,15 +54,26 @@ export const parseDevtronLicenseDTOIntoLicenseCardData = <isCentralDashboard ext
5454
licenseDTO: DevtronLicenseDTO<isCentralDashboard>,
5555
currentUserEmail?: isCentralDashboard extends true ? string : never,
5656
): Omit<DevtronLicenseCardProps, 'appTheme'> => {
57-
const { isTrial, expiry, ttl, reminderThreshold, organisationMetadata, license, claimedByUserDetails } =
58-
licenseDTO || {}
57+
const {
58+
isTrial,
59+
expiry,
60+
ttl,
61+
reminderThreshold,
62+
organisationMetadata,
63+
license,
64+
claimedByUserDetails,
65+
isFreemium,
66+
licenseStatusError,
67+
} = licenseDTO || {}
5968

6069
return {
6170
enterpriseName: organisationMetadata?.name || 'Devtron Enterprise',
6271
expiryDate: expiry ? moment(expiry).format(DATE_TIME_FORMATS['DD/MM/YYYY']) : '',
6372
ttl,
6473
licenseStatus: getDevtronLicenseStatus({ ttl, reminderThreshold }),
6574
isTrial,
75+
isFreemium,
76+
licenseStatusError,
6677
...(currentUserEmail && currentUserEmail === claimedByUserDetails?.email
6778
? { licenseKey: license }
6879
: { licenseSuffix: license }),

src/Shared/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1099,6 +1099,7 @@ export enum LicensingErrorCodes {
10991099
LicKeyMismatch = '11006',
11001100
NoCertFound = '11007',
11011101
LicKeyNotFound = '11008',
1102+
ClusterLimitExceeded = '11011',
11021103
}
11031104

11041105
export interface LicenseErrorStruct {
@@ -1109,6 +1110,7 @@ export interface LicenseErrorStruct {
11091110
export interface DevtronLicenseBaseDTO {
11101111
fingerprint: string | null
11111112
isTrial: boolean | null
1113+
isFreemium: boolean | null
11121114
/**
11131115
* In timestamp format
11141116
*/

0 commit comments

Comments
 (0)