Skip to content

Commit 48f47c7

Browse files
committed
Merge branch 'feat/license-manager' into feat/license-manager-dto
2 parents 95c75b3 + 3a9ad03 commit 48f47c7

File tree

13 files changed

+398
-1
lines changed

13 files changed

+398
-1
lines changed

src/Assets/IconV2/ic-devtron.svg

Lines changed: 1 addition & 0 deletions
Loading

src/Assets/IconV2/ic-key.svg

Lines changed: 19 additions & 0 deletions
Loading

src/Assets/IconV2/ic-timer.svg

Lines changed: 19 additions & 0 deletions
Loading

src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ const ConfirmationModalBody = ({
8282
return (
8383
<Backdrop onEscape={shouldCloseOnEscape ? handleCloseWrapper : noop}>
8484
<motion.div
85-
className={`${isLandscapeView ? 'w-500' : 'w-400'} confirmation-modal border__secondary flexbox-col br-8 bg__primary dc__m-auto mt-40 w-400`}
85+
className={`${isLandscapeView ? 'w-500' : 'w-400'} confirmation-modal border__secondary flexbox-col br-8 bg__primary dc__m-auto mt-40`}
8686
exit={{ y: 100, opacity: 0, scale: 0.75, transition: { duration: 0.35 } }}
8787
initial={{ y: 100, opacity: 0, scale: 0.75 }}
8888
animate={{ y: 0, opacity: 1, scale: 1 }}

src/Shared/Components/Icon/Icon.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import { ReactComponent as ICInfoFilled } from '@IconsV2/ic-info-filled.svg'
4545
import { ReactComponent as ICInfoOutline } from '@IconsV2/ic-info-outline.svg'
4646
import { ReactComponent as ICJobColor } from '@IconsV2/ic-job-color.svg'
4747
import { ReactComponent as ICK8sJob } from '@IconsV2/ic-k8s-job.svg'
48+
import { ReactComponent as ICKey } from '@IconsV2/ic-key.svg'
4849
import { ReactComponent as ICLdap } from '@IconsV2/ic-ldap.svg'
4950
import { ReactComponent as ICLoginDevtronLogo } from '@IconsV2/ic-login-devtron-logo.svg'
5051
import { ReactComponent as ICLogout } from '@IconsV2/ic-logout.svg'
@@ -65,6 +66,7 @@ import { ReactComponent as ICStack } from '@IconsV2/ic-stack.svg'
6566
import { ReactComponent as ICSuccess } from '@IconsV2/ic-success.svg'
6667
import { ReactComponent as ICSuspended } from '@IconsV2/ic-suspended.svg'
6768
import { ReactComponent as ICTimeoutTwoDash } from '@IconsV2/ic-timeout-two-dash.svg'
69+
import { ReactComponent as ICTimer } from '@IconsV2/ic-timer.svg'
6870
import { ReactComponent as ICUnknown } from '@IconsV2/ic-unknown.svg'
6971
import { ReactComponent as ICUserKey } from '@IconsV2/ic-user-key.svg'
7072
import { ReactComponent as ICWarning } from '@IconsV2/ic-warning.svg'
@@ -119,6 +121,7 @@ export const iconMap = {
119121
'ic-info-outline': ICInfoOutline,
120122
'ic-job-color': ICJobColor,
121123
'ic-k8s-job': ICK8sJob,
124+
'ic-key': ICKey,
122125
'ic-ldap': ICLdap,
123126
'ic-login-devtron-logo': ICLoginDevtronLogo,
124127
'ic-logout': ICLogout,
@@ -139,6 +142,7 @@ export const iconMap = {
139142
'ic-success': ICSuccess,
140143
'ic-suspended': ICSuspended,
141144
'ic-timeout-two-dash': ICTimeoutTwoDash,
145+
'ic-timer': ICTimer,
142146
'ic-unknown': ICUnknown,
143147
'ic-user-key': ICUserKey,
144148
'ic-warning': ICWarning,
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
import { ClipboardButton, DOCUMENTATION_HOME_PAGE } from '@Common/index'
2+
import { ReactComponent as ICChatSupport } from '@IconsV2/ic-chat-circle-dots.svg'
3+
import { Icon } from '../Icon'
4+
import { Button, ButtonStyleType, ButtonVariantType } from '../Button'
5+
import { InfoIconTippy } from '../InfoIconTippy'
6+
import './licenseInfoDialog.scss'
7+
8+
export enum LicenseStatus {
9+
ACTIVE = 'ACTIVE',
10+
EXPIRED = 'EXPIRED',
11+
REMINDER_THRESHOLD_REACHED = 'REMINDER_THRESHOLD_REACHED',
12+
}
13+
14+
export type DevtronLicenseCardProps = {
15+
enterpriseName: string
16+
expiryDate: string
17+
ttl: number
18+
licenseStatus: LicenseStatus
19+
isTrial: boolean
20+
} & (
21+
| {
22+
licenseKey: string
23+
licenseSuffix?: never
24+
}
25+
| {
26+
licenseKey?: never
27+
licenseSuffix: string
28+
}
29+
)
30+
31+
export const getColorAccordingToStatus = (licenseStatus: LicenseStatus) => {
32+
switch (licenseStatus) {
33+
case LicenseStatus.ACTIVE:
34+
return 'var(--G100)'
35+
case LicenseStatus.REMINDER_THRESHOLD_REACHED:
36+
return 'var(--Y100)'
37+
default:
38+
return 'var(--R100)'
39+
}
40+
}
41+
42+
export const DevtronLicenseCard = ({
43+
enterpriseName,
44+
licenseKey,
45+
licenseSuffix,
46+
expiryDate,
47+
licenseStatus,
48+
isTrial,
49+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
50+
ttl,
51+
}: DevtronLicenseCardProps) => {
52+
const colorValue = getColorAccordingToStatus(licenseStatus)
53+
54+
return (
55+
<div className="flexbox-col p-8 br-16" style={{ backgroundColor: colorValue }}>
56+
<div className="license-card flexbox-col dc__content-space br-12 p-20 h-200 bg__tertiary">
57+
<div className="flexbox dc__align-items-center dc__content-space">
58+
<span className="font-merriweather cn-9 fs-16 fw-7 lh-1-5">{enterpriseName}</span>
59+
<Icon name="ic-devtron" color="N900" size={24} />
60+
</div>
61+
<div className="flexbox-col dc__gap-4">
62+
<div className="flexbox dc__align-items-center dc__gap-6">
63+
<Icon name="ic-key" color={null} size={20} />
64+
<div className="flex dc__gap-4 cn-7 fs-16 fw-5 lh-1-5 font-ibm-plex-mono">
65+
<span>••••</span>
66+
<span>{licenseSuffix || licenseKey.slice(-8)}</span>
67+
</div>
68+
{licenseKey && <ClipboardButton content={licenseKey} />}
69+
</div>
70+
<div className="flexbox dc__align-items-center dc__gap-4">
71+
<span>{expiryDate}</span>
72+
<span></span>
73+
<span style={{ color: colorValue }}>2 months remaining</span>
74+
</div>
75+
</div>
76+
{isTrial && <div className="flexbox dc__align-items-center px-20 py-6">TRIAL LICENSE</div>}
77+
</div>
78+
{licenseStatus !== LicenseStatus.ACTIVE && (
79+
<div className="p-16 flexbox-col dc__gap-8">
80+
<div className="flexbox dc__gap-8">
81+
<span>
82+
{/* TODO: Confirm with product team */}
83+
To renew your license mail us at enterprise@devtron.ai or contact your Devtron
84+
representative.
85+
</span>
86+
<Icon name="ic-timer" color="Y500" size={16} />
87+
</div>
88+
<Button
89+
dataTestId="contact-support"
90+
startIcon={<ICChatSupport />}
91+
text="Contact support"
92+
variant={ButtonVariantType.text}
93+
/>
94+
</div>
95+
)}
96+
</div>
97+
)
98+
}
99+
100+
export const InstallationFingerprintInfo = ({
101+
installationFingerprint,
102+
showHelpTip = false,
103+
}: {
104+
installationFingerprint: string
105+
showHelpTip?: boolean
106+
}) => (
107+
<div className="flexbox-col dc__gap-6">
108+
<div className="flexbox dc__align-items-center dc__gap-4">
109+
<span className="fs-13 lh-20 cn-7 fw-4">Installation Fingerprint</span>
110+
{showHelpTip && (
111+
<InfoIconTippy
112+
heading="Installation Fingerprint"
113+
infoText="A unique fingerprint to identify your Devtron Installation. An enterprise license is generated against an installation fingerprint."
114+
documentationLinkText="Documentation"
115+
iconClassName="icon-dim-20 fcn-6"
116+
placement="right"
117+
documentationLink={DOCUMENTATION_HOME_PAGE}
118+
/>
119+
)}
120+
</div>
121+
<div className="flex dc__content-space">
122+
<span className="cn-9 fs-13 lh-1-5 fw-4">{installationFingerprint}</span>
123+
<ClipboardButton content={installationFingerprint} />
124+
</div>
125+
</div>
126+
)
127+
128+
export const LicenseInfo = ({ handleUpdateLicenseClick }: { handleUpdateLicenseClick: () => void }) => (
129+
<div className="flexbox-col dc__gap-20">
130+
<DevtronLicenseCard
131+
enterpriseName="BharatPe"
132+
licenseSuffix="4AF593V3"
133+
ttl={100}
134+
licenseStatus={LicenseStatus.ACTIVE}
135+
isTrial
136+
expiryDate="2025-05-17"
137+
/>
138+
<InstallationFingerprintInfo installationFingerprint="3ff0d8be-e7f2-4bf4-9c3f-70ec904b51f4" showHelpTip />
139+
<div className="border__primary--bottom h-1" />
140+
<div className="flex dc__content-space">
141+
<span>Have a new license?</span>
142+
<Button
143+
dataTestId="update-license"
144+
text="Update license"
145+
variant={ButtonVariantType.text}
146+
onClick={handleUpdateLicenseClick}
147+
/>
148+
</div>
149+
</div>
150+
)
151+
152+
export const AboutDevtron = () => (
153+
<div className="flexbox-col dc__align-items-center dc__gap-20">
154+
<div className="flex p-6 border__primary br-8">
155+
<Icon name="ic-devtron" color="B500" size={40} />
156+
</div>
157+
<div className="text-center">
158+
<p className="fs-16 cn-9 fw-6 lh-1-5 m-0">Devtron</p>
159+
{/* TODO: add version */}
160+
<p className="fs-13 cn-7 fw-4 lh-20 m-0">Enterprise Version (1.3.1)</p>
161+
</div>
162+
<p className="fs-13 cn-5 fw-4 lh-20 m-0">Copyright © 2025 Devtron Inc. All rights reserved.</p>
163+
{/* TODO: add links for all button below */}
164+
<div className="flexbox dc__align-items-center dc__gap-6">
165+
<Button
166+
dataTestId="terms-of-service"
167+
text="Terms of service"
168+
variant={ButtonVariantType.text}
169+
style={ButtonStyleType.neutral}
170+
/>
171+
<span></span>
172+
<Button
173+
dataTestId="privacy-policy"
174+
text="Privacy policy"
175+
variant={ButtonVariantType.text}
176+
style={ButtonStyleType.neutral}
177+
/>
178+
<span></span>
179+
<Button
180+
dataTestId="license-agreement"
181+
text="License agreement"
182+
variant={ButtonVariantType.text}
183+
style={ButtonStyleType.neutral}
184+
/>
185+
</div>
186+
</div>
187+
)
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { ChangeEvent, useState } from 'react'
2+
import { SegmentedControl } from '@Common/index'
3+
import { ComponentSizeType } from '@Shared/constants'
4+
import { AboutDevtron, LicenseInfo } from './LicenseInfoDialog.components'
5+
import { Button } from '../Button'
6+
import { LicenseInfoDialogType } from './types'
7+
import { ABOUT_DEVTRON_TABS } from './constants'
8+
import ActivateLicenseDialog from './UpdateLicenseDialog'
9+
import './licenseInfoDialog.scss'
10+
11+
const LicenseInfoDialog = () => {
12+
const [dialogType, setDialogType] = useState<LicenseInfoDialogType>(LicenseInfoDialogType.ABOUT)
13+
14+
const handleChangeDialogType = (e: ChangeEvent<HTMLInputElement>) => {
15+
setDialogType(e.target.value as LicenseInfoDialogType)
16+
}
17+
18+
const handleUpdateLicenseClick = () => {
19+
setDialogType(LicenseInfoDialogType.UPDATE)
20+
}
21+
22+
return dialogType === LicenseInfoDialogType.UPDATE ? (
23+
<ActivateLicenseDialog />
24+
) : (
25+
<div className="license-info-dialog w-400 br-12 bg__modal--primary border__primary">
26+
{/* Header */}
27+
<div className="px-24 pt-24 pb-8">
28+
<SegmentedControl
29+
name="about-devtron-segmented-control"
30+
tabs={ABOUT_DEVTRON_TABS}
31+
initialTab={dialogType}
32+
onChange={handleChangeDialogType}
33+
rootClassName="h-32 w-100"
34+
/>
35+
</div>
36+
{/* Body */}
37+
<div className="p-24 mxh-500 overflow-auto">
38+
{dialogType === LicenseInfoDialogType.ABOUT ? (
39+
<AboutDevtron />
40+
) : (
41+
<LicenseInfo handleUpdateLicenseClick={handleUpdateLicenseClick} />
42+
)}
43+
</div>
44+
{/* Footer */}
45+
<div className="flex px-24 py-20 dc__content-end">
46+
<Button dataTestId="license-info-okay" text="Okay" size={ComponentSizeType.medium} />
47+
</div>
48+
</div>
49+
)
50+
}
51+
52+
export default LicenseInfoDialog

0 commit comments

Comments
 (0)