diff --git a/package-lock.json b/package-lock.json index 70f1111b6..0c35ce2b3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.19.0", + "version": "1.19.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.19.0", + "version": "1.19.1", "hasInstallScript": true, "license": "ISC", "dependencies": { diff --git a/package.json b/package.json index 3740eefd6..dfd5c4c4b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.19.0", + "version": "1.19.1", "description": "Supporting common component library", "type": "module", "main": "dist/index.js", diff --git a/src/Assets/IconV2/ic-upgrade-enterprise.svg b/src/Assets/IconV2/ic-upgrade-enterprise.svg new file mode 100644 index 000000000..eb7ca93cf --- /dev/null +++ b/src/Assets/IconV2/ic-upgrade-enterprise.svg @@ -0,0 +1,320 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Shared/Components/GenericModal/constants.ts b/src/Shared/Components/GenericModal/constants.ts index 6f821f0cc..bef8d8813 100644 --- a/src/Shared/Components/GenericModal/constants.ts +++ b/src/Shared/Components/GenericModal/constants.ts @@ -17,6 +17,7 @@ import { GenericModalProps } from './types' export const MODAL_WIDTH_TO_CLASS_NAME_MAP: Record = { + 450: 'w-450', 500: 'w-500', 600: 'w-600', 800: 'w-800', diff --git a/src/Shared/Components/GenericModal/types.ts b/src/Shared/Components/GenericModal/types.ts index 9a0e46c3d..9f3417cda 100644 --- a/src/Shared/Components/GenericModal/types.ts +++ b/src/Shared/Components/GenericModal/types.ts @@ -28,7 +28,7 @@ export interface GenericModalProps extends Partial { export const getHelpActionMenuOptions = ({ isEnterprise, - isTrial, + isTrialOrFreemium, }: { isEnterprise: boolean - isTrial: boolean + isTrialOrFreemium: boolean }): HelpButtonActionMenuProps['options'] => [ { items: COMMON_HELP_ACTION_MENU_ITEMS, @@ -56,7 +56,9 @@ export const getHelpActionMenuOptions = ({ ? [ { groupLabel: 'Enterprise Support', - items: isTrial ? ENTERPRISE_TRIAL_HELP_ACTION_MENU_ITEMS : ENTERPRISE_HELP_ACTION_MENU_ITEMS, + items: isTrialOrFreemium + ? ENTERPRISE_TRIAL_HELP_ACTION_MENU_ITEMS + : ENTERPRISE_HELP_ACTION_MENU_ITEMS, }, ] : [ diff --git a/src/Shared/Components/Icon/Icon.tsx b/src/Shared/Components/Icon/Icon.tsx index 410648573..50d4f62b7 100644 --- a/src/Shared/Components/Icon/Icon.tsx +++ b/src/Shared/Components/Icon/Icon.tsx @@ -212,6 +212,7 @@ import { ReactComponent as ICTravclan } from '@IconsV2/ic-travclan.svg' import { ReactComponent as ICTwoCubes } from '@IconsV2/ic-two-cubes.svg' import { ReactComponent as ICUbuntu } from '@IconsV2/ic-ubuntu.svg' import { ReactComponent as ICUnknown } from '@IconsV2/ic-unknown.svg' +import { ReactComponent as ICUpgradeEnterprise } from '@IconsV2/ic-upgrade-enterprise.svg' import { ReactComponent as ICUserCircle } from '@IconsV2/ic-user-circle.svg' import { ReactComponent as ICUserKey } from '@IconsV2/ic-user-key.svg' import { ReactComponent as ICUsers } from '@IconsV2/ic-users.svg' @@ -438,6 +439,7 @@ export const iconMap = { 'ic-two-cubes': ICTwoCubes, 'ic-ubuntu': ICUbuntu, 'ic-unknown': ICUnknown, + 'ic-upgrade-enterprise': ICUpgradeEnterprise, 'ic-user-circle': ICUserCircle, 'ic-user-key': ICUserKey, 'ic-users': ICUsers, diff --git a/src/Shared/Components/License/DevtronLicenseCard.tsx b/src/Shared/Components/License/DevtronLicenseCard.tsx index d463f0a8c..346f2cdda 100644 --- a/src/Shared/Components/License/DevtronLicenseCard.tsx +++ b/src/Shared/Components/License/DevtronLicenseCard.tsx @@ -23,6 +23,7 @@ import { ClipboardButton, getTTLInHumanReadableFormat } from '@Common/index' import { CONTACT_SUPPORT_LINK, ENTERPRISE_SUPPORT_LINK } from '@Shared/constants' import { AppThemeType } from '@Shared/Providers' import { getThemeOppositeThemeClass } from '@Shared/Providers/ThemeProvider/utils' +import { LicensingErrorCodes } from '@Shared/types' import { Button, ButtonComponentType, ButtonVariantType } from '../Button' import { Icon } from '../Icon' @@ -33,6 +34,85 @@ import './licenseCard.scss' const DAMPEN_FACTOR = 50 +const ContactSupportButton = () => ( + } + text="Contact support" + variant={ButtonVariantType.text} + component={ButtonComponentType.anchor} + anchorProps={{ href: CONTACT_SUPPORT_LINK }} + /> +) + +const LicenseCardSubText = ({ + isFreemium, + licenseStatus, + licenseStatusError, +}: Pick) => { + if (licenseStatus === LicenseStatus.ACTIVE && !isFreemium) { + return null + } + + const freemiumLimitReached = licenseStatusError?.code === LicensingErrorCodes.ClusterLimitExceeded + + if (licenseStatus === LicenseStatus.ACTIVE && isFreemium) { + return ( + + + {freemiumLimitReached ? ( + + Multiple Clusters Detected + + Your account is connected to multiple clusters, which isn’t allowed on the freemium + plan. Upgrade to an Enterprise license or contact us. + + + ) : ( + Unlimited single cluster usage + )} + + + + } + text={ENTERPRISE_SUPPORT_LINK} + variant={ButtonVariantType.text} + component={ButtonComponentType.anchor} + anchorProps={{ href: `mailto:${ENTERPRISE_SUPPORT_LINK}` }} + /> + + + + ) + } + + const isLicenseExpired = licenseStatus === LicenseStatus.EXPIRED + + return ( + + + + To renew your license mail us at + {ENTERPRISE_SUPPORT_LINK} or contact your Devtron + representative. + + + + + + ) +} + export const DevtronLicenseCard = ({ enterpriseName, licenseKey, @@ -40,14 +120,15 @@ export const DevtronLicenseCard = ({ expiryDate, licenseStatus, isTrial, + isFreemium, ttl, appTheme, handleCopySuccess, + licenseStatusError, }: DevtronLicenseCardProps) => { - const { bgColor, textColor } = getLicenseColorsAccordingToStatus(licenseStatus) + const { bgColor, textColor } = getLicenseColorsAccordingToStatus({ isFreemium, licenseStatus, licenseStatusError }) const remainingTime = getTTLInHumanReadableFormat(ttl) const remainingTimeString = ttl < 0 ? `Expired ${remainingTime} ago` : `${remainingTime} remaining` - const isLicenseValid = licenseStatus !== LicenseStatus.EXPIRED const isThemeDark = appTheme === AppThemeType.dark const cardRef = useRef(null) @@ -93,7 +174,7 @@ export const DevtronLicenseCard = ({ : useMotionTemplate`linear-gradient(55deg, transparent, rgba(255, 255, 255, ${sheenOpacity}) ${sheenPosition}%, transparent)` return ( - + - {expiryDate} - · - {remainingTimeString} + + {isFreemium ? 'VALID FOREVER' : expiryDate} + + {!isFreemium && ( + <> + · + {remainingTimeString} + > + )} - {isTrial && ( + {(isTrial || isFreemium) && ( - TRIAL LICENSE + {isFreemium ? 'FREEMIUM' : 'TRIAL'} LICENSE )} - {licenseStatus !== LicenseStatus.ACTIVE && ( - - - - To renew your license mail us at - {ENTERPRISE_SUPPORT_LINK} or contact your - Devtron representative. - - - - } - text="Contact support" - variant={ButtonVariantType.text} - component={ButtonComponentType.anchor} - anchorProps={{ href: CONTACT_SUPPORT_LINK }} - /> - - )} + ) } diff --git a/src/Shared/Components/License/constants.ts b/src/Shared/Components/License/constants.ts new file mode 100644 index 000000000..2033d8348 --- /dev/null +++ b/src/Shared/Components/License/constants.ts @@ -0,0 +1 @@ +export const ALLOWED_CLUSTER_IN_FREEMIUM = 2 diff --git a/src/Shared/Components/License/licenseCard.scss b/src/Shared/Components/License/licenseCard.scss index 2663e3b99..57710e14b 100644 --- a/src/Shared/Components/License/licenseCard.scss +++ b/src/Shared/Components/License/licenseCard.scss @@ -14,10 +14,18 @@ * limitations under the License. */ -.license-card { +.license-card-wrapper { + .license-card { + .trial-license-badge { + background-color: var(--divider-secondary-translucent); + letter-spacing: 0.55px; + } + } - .trial-license-badge { - background-color: var(--divider-secondary-translucent); - letter-spacing: 0.55px; + .mail-support { + .button { + text-transform: lowercase; + } } } + diff --git a/src/Shared/Components/License/types.ts b/src/Shared/Components/License/types.ts index 878971301..38b2be000 100644 --- a/src/Shared/Components/License/types.ts +++ b/src/Shared/Components/License/types.ts @@ -15,7 +15,7 @@ */ import { AppThemeType } from '@Shared/Providers' -import { DevtronLicenseBaseDTO, DevtronLicenseDTO } from '@Shared/types' +import { DevtronLicenseBaseDTO, DevtronLicenseDTO, LicenseErrorStruct } from '@Shared/types' export enum LicenseStatus { ACTIVE = 'ACTIVE', @@ -29,7 +29,9 @@ export type DevtronLicenseCardProps = { ttl: number licenseStatus: LicenseStatus isTrial: boolean + isFreemium: boolean appTheme: AppThemeType + licenseStatusError: LicenseErrorStruct } & ( | { licenseKey: string @@ -44,7 +46,7 @@ export type DevtronLicenseCardProps = { ) export type DevtronLicenseInfo = Omit & - Pick + Pick export interface ActivateLicenseDialogProps extends Pick { enterpriseName: string diff --git a/src/Shared/Components/License/utils.tsx b/src/Shared/Components/License/utils.tsx index fc6d78dcb..540d14613 100644 --- a/src/Shared/Components/License/utils.tsx +++ b/src/Shared/Components/License/utils.tsx @@ -18,13 +18,25 @@ import moment from 'moment' import { DATE_TIME_FORMATS } from '@Common/Constants' import { getUrlWithSearchParams } from '@Common/index' -import { DevtronLicenseDTO } from '@Shared/types' +import { DevtronLicenseDTO, LicensingErrorCodes } from '@Shared/types' +import { ALLOWED_CLUSTER_IN_FREEMIUM } from './constants' import { DevtronLicenseCardProps, DevtronLicenseInfo, LicenseStatus } from './types' -export const getLicenseColorsAccordingToStatus = ( - licenseStatus: LicenseStatus, -): { bgColor: string; textColor: string } => { +export const getLicenseColorsAccordingToStatus = ({ + isFreemium, + licenseStatus, + licenseStatusError, +}: Pick): { + bgColor: string + textColor: string +} => { + if (isFreemium) { + const freemiumLimitReached = licenseStatusError?.code === LicensingErrorCodes.ClusterLimitExceeded + return freemiumLimitReached + ? { bgColor: 'var(--R100)', textColor: 'var(--R500)' } + : { bgColor: 'var(--G100)', textColor: 'var(--G500)' } + } switch (licenseStatus) { case LicenseStatus.ACTIVE: return { bgColor: 'var(--G100)', textColor: 'var(--G500)' } @@ -54,8 +66,17 @@ export const parseDevtronLicenseDTOIntoLicenseCardData = , currentUserEmail?: isCentralDashboard extends true ? string : never, ): Omit => { - const { isTrial, expiry, ttl, reminderThreshold, organisationMetadata, license, claimedByUserDetails } = - licenseDTO || {} + const { + isTrial, + expiry, + ttl, + reminderThreshold, + organisationMetadata, + license, + claimedByUserDetails, + isFreemium, + licenseStatusError, + } = licenseDTO || {} return { enterpriseName: organisationMetadata?.name || 'Devtron Enterprise', @@ -63,6 +84,8 @@ export const parseDevtronLicenseDTOIntoLicenseCardData = = Devt } | null showLicenseData?: never licenseStatusError?: never + moduleLimits?: never } : { claimedByUserDetails?: never showLicenseData: boolean licenseStatusError?: LicenseErrorStruct + moduleLimits: { + allAllowed: boolean + maxAllowedClusters: number + } }) export type CountryISO2Type = ParsedCountry['iso2'] @@ -1177,6 +1184,7 @@ export type IconBaseSizeType = | 42 | 44 | 48 + | 64 | 72 | 80