Skip to content

Commit 1dae146

Browse files
authored
Merge pull request #703 from devtron-labs/chore/sync-activate-license
chore: sync activate license from main
2 parents 2bafdc7 + 87866ca commit 1dae146

16 files changed

+322
-40
lines changed

package-lock.json

Lines changed: 12 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@devtron-labs/devtron-fe-common-lib",
3-
"version": "1.11.0-pre-6",
3+
"version": "1.11.0-pre-7",
44
"description": "Supporting common component library",
55
"type": "module",
66
"main": "dist/index.js",
@@ -121,6 +121,7 @@
121121
"jsonpath-plus": "^10.3.0",
122122
"marked": "^13.0.3",
123123
"nanoid": "^3.3.8",
124+
"qrcode.react": "^4.2.0",
124125
"react-canvas-confetti": "^2.0.7",
125126
"react-dates": "^21.8.0",
126127
"react-diff-viewer-continued": "^3.4.0",

src/Common/Constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ export const ROUTES = {
156156
RESOURCE_TEMPLATE: 'resource/template',
157157
ENVIRONMENT_DATA: 'global/environment-variables',
158158
DASHBOARD_EVENT: 'dashboard-event',
159+
LICENSE_DATA: 'license/data',
159160
} as const
160161

161162
export enum KEY_VALUE {

src/Shared/Components/DevtronLicenseCard/InstallationFingerprintInfo.tsx

Lines changed: 0 additions & 29 deletions
This file was deleted.

src/Shared/Components/DevtronLicenseCard/index.tsx

Lines changed: 0 additions & 4 deletions
This file was deleted.

src/Shared/Components/Header/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { ReactComponent as Discord } from '@Icons/ic-discord-fill.svg'
1818
import { ReactComponent as File } from '@Icons/ic-file-text.svg'
1919

2020
import { DISCORD_LINK, DOCUMENTATION_HOME_PAGE, LOGIN_COUNT } from '../../../Common'
21-
import { DevtronLicenseInfo, LicenseStatus } from '../DevtronLicenseCard'
21+
import { DevtronLicenseInfo, LicenseStatus } from '../License'
2222
import { EnterpriseHelpOptions, OSSHelpOptions, TrialHelpOptions } from './constants'
2323
import { updatePostHogEvent } from './service'
2424

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import { ChangeEvent, SyntheticEvent, useState } from 'react'
2+
3+
import { ReactComponent as ICCross } from '@Icons/ic-cross.svg'
4+
import { API_STATUS_CODES } from '@Common/Constants'
5+
import { showError } from '@Common/index'
6+
import {
7+
ActivateLicenseDialogProps,
8+
Button,
9+
ButtonStyleType,
10+
ButtonVariantType,
11+
getHandleOpenURL,
12+
handleSendAnalyticsEventToServer,
13+
InstallationFingerprintInfo,
14+
requiredField,
15+
ServerAnalyticsEventType,
16+
Textarea,
17+
ToastManager,
18+
ToastVariantType,
19+
} from '@Shared/index'
20+
21+
import { GatekeeperQRDialog, ICDevtronWithBorder } from './License.components'
22+
import { activateLicense } from './services'
23+
import { getGateKeeperUrl } from './utils'
24+
25+
const ActivateLicenseDialog = ({
26+
fingerprint,
27+
enterpriseName,
28+
handleClose,
29+
handleLicenseActivateSuccess,
30+
}: ActivateLicenseDialogProps) => {
31+
const [showQRDialog, setShowQRDialog] = useState<boolean>(false)
32+
const [licenseKey, setLicenseKey] = useState<string>('')
33+
const [activatingLicense, setActivatingLicense] = useState<boolean>(false)
34+
const [error, setError] = useState<string>('')
35+
36+
const handleGetLicense = () => {
37+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
38+
handleSendAnalyticsEventToServer(ServerAnalyticsEventType.GET_LICENSE_CLICKED, true)
39+
const gateKeeperURL = getGateKeeperUrl(fingerprint)
40+
41+
getHandleOpenURL(gateKeeperURL)()
42+
setShowQRDialog(true)
43+
}
44+
45+
const handleCloseQRDialog = () => {
46+
setShowQRDialog(false)
47+
}
48+
49+
const validateForm = (updatedLicenseKey: string) => {
50+
const errorMessage = requiredField(updatedLicenseKey).message
51+
setError(errorMessage || '')
52+
return !errorMessage
53+
}
54+
55+
const handleLicenseKeyChange = (e: ChangeEvent<HTMLTextAreaElement>) => {
56+
const { value } = e.target
57+
setLicenseKey(value)
58+
validateForm(value)
59+
}
60+
61+
const handleActivateLicense = async (e: SyntheticEvent) => {
62+
e.preventDefault()
63+
const isFormValid = validateForm(licenseKey)
64+
if (isFormValid) {
65+
setActivatingLicense(true)
66+
try {
67+
await activateLicense(licenseKey)
68+
ToastManager.showToast({
69+
description: 'License activated successfully',
70+
variant: ToastVariantType.success,
71+
})
72+
handleLicenseActivateSuccess()
73+
} catch (err) {
74+
if (err.code === API_STATUS_CODES.BAD_REQUEST) {
75+
setError('Invalid license key')
76+
}
77+
showError(err)
78+
} finally {
79+
setActivatingLicense(false)
80+
}
81+
}
82+
}
83+
84+
return (
85+
<div className="flexbox-col p-36 dc__gap-32 border__primary w-400 br-12 bg__primary mxh-600 dc__overflow-auto">
86+
<div className="flexbox-col dc__gap-20">
87+
<div className="flexbox dc__content-space">
88+
<ICDevtronWithBorder />
89+
{handleClose && (
90+
<Button
91+
dataTestId="close-dialog"
92+
variant={ButtonVariantType.borderLess}
93+
ariaLabel="close activate license dialog"
94+
onClick={handleClose}
95+
style={ButtonStyleType.negativeGrey}
96+
icon={<ICCross />}
97+
showAriaLabelInTippy={false}
98+
/>
99+
)}
100+
</div>
101+
<div className="flexbox-col dc__gap-4">
102+
<div className="fs-20 lh-1-5 fw-7 cn-9 font-merriweather dc__truncate">{enterpriseName}</div>
103+
<div className="fs-16 lh-1-5 cn-8 fw-4">Enter new enterprise license key</div>
104+
</div>
105+
</div>
106+
<div className="flexbox-col dc__gap-16">
107+
<InstallationFingerprintInfo fingerprint={fingerprint} />
108+
<Textarea
109+
placeholder="Enter license key"
110+
name="license-key"
111+
onChange={handleLicenseKeyChange}
112+
value={licenseKey}
113+
required
114+
label="License Key"
115+
error={error}
116+
/>
117+
</div>
118+
<div className="flexbox-col dc__gap-16">
119+
<Button
120+
dataTestId="activate-license"
121+
text="Activate"
122+
fullWidth
123+
isLoading={activatingLicense}
124+
onClick={handleActivateLicense}
125+
/>
126+
<div className="flexbox dc__align-items-center dc__content-space">
127+
<span className="fs-13 cn-9 lh-20 fw-4">Don’t have license key?</span>
128+
<Button
129+
dataTestId="get-license"
130+
text="Get license"
131+
variant={ButtonVariantType.text}
132+
onClick={handleGetLicense}
133+
/>
134+
</div>
135+
</div>
136+
{showQRDialog && <GatekeeperQRDialog fingerprint={fingerprint} handleClose={handleCloseQRDialog} />}
137+
</div>
138+
)
139+
}
140+
141+
export default ActivateLicenseDialog
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import { useEffect, useRef, useState } from 'react'
2+
3+
import { ReactComponent as ICCheck } from '@Icons/ic-check.svg'
4+
import { ReactComponent as ICClipboard } from '@Icons/ic-copy.svg'
5+
import { DOCUMENTATION } from '@Common/Constants'
6+
import { ClipboardButton, copyToClipboard, showError } from '@Common/index'
7+
8+
import { Backdrop, Button, ButtonStyleType, ButtonVariantType, Icon, InfoIconTippy, QRCode } from '..'
9+
import { CopyButtonProps, GatekeeperQRDialogProps, InstallFingerprintInfoProps } from './types'
10+
import { getGateKeeperUrl } from './utils'
11+
12+
const CopyButton = ({ copyContent }: CopyButtonProps) => {
13+
const [clicked, setClicked] = useState<boolean>(false)
14+
const timeoutId = useRef<ReturnType<typeof setTimeout> | null>(null)
15+
16+
const handleCopy = async () => {
17+
try {
18+
await copyToClipboard(copyContent)
19+
setClicked(true)
20+
timeoutId.current = setTimeout(() => {
21+
setClicked(false)
22+
}, 1000)
23+
} catch (err) {
24+
showError(err)
25+
}
26+
}
27+
28+
useEffect(
29+
() => () => {
30+
clearTimeout(timeoutId.current)
31+
},
32+
[],
33+
)
34+
35+
return (
36+
<Button
37+
dataTestId="copy-button"
38+
text="Copy link"
39+
startIcon={clicked ? <ICCheck /> : <ICClipboard />}
40+
style={ButtonStyleType.neutral}
41+
variant={ButtonVariantType.borderLess}
42+
onClick={handleCopy}
43+
/>
44+
)
45+
}
46+
47+
export const GatekeeperQRDialog = ({ handleClose, fingerprint }: GatekeeperQRDialogProps) => {
48+
const gateKeeperURL = getGateKeeperUrl(fingerprint)
49+
50+
return (
51+
<Backdrop onEscape={handleClose}>
52+
<div className="dc__m-auto bg__menu--primary br-12 w-400 border__primary mt-40">
53+
<div className="p-24 flexbox-col dc__gap-20">
54+
<div className="flexbox-col cn-9 lh-1-5 dc__gap-4">
55+
<div className="fs-16 fw-6">Get Devtron Enterprise License</div>
56+
<div className="fs-13 fw-4">
57+
Scan the below QR to visit the license dashboard and generate a license key for your Devtron
58+
installation.
59+
</div>
60+
</div>
61+
<div className="flex">
62+
<div className="p-20 br-12 border-secondary bg__modal--secondary">
63+
<QRCode value={gateKeeperURL} size={310} bgColor="N50" fgColor="N900" />
64+
</div>
65+
</div>
66+
</div>
67+
<div className="px-24 py-20 flexbox dc__align-items-center dc__content-space">
68+
<CopyButton copyContent={gateKeeperURL} />
69+
<Button dataTestId="qr-dialog-got-it" text="Got it" onClick={handleClose} />
70+
</div>
71+
</div>
72+
</Backdrop>
73+
)
74+
}
75+
76+
export const ICDevtronWithBorder = () => (
77+
<div className="flex p-6 border__primary br-8 dc__w-fit-content">
78+
<Icon name="ic-devtron" color="B500" size={40} />
79+
</div>
80+
)
81+
82+
const InstallationFingerprintInfo = ({ fingerprint, showHelpTooltip = false }: InstallFingerprintInfoProps) => (
83+
<div className="flexbox-col dc__gap-6">
84+
<div className="flexbox dc__align-items-center dc__gap-4">
85+
<span className="fs-13 lh-20 cn-7 fw-4">Installation Fingerprint</span>
86+
{showHelpTooltip && (
87+
<InfoIconTippy
88+
heading="Installation Fingerprint"
89+
infoText="A unique fingerprint to identify your Devtron Installation. An enterprise license is generated against an installation fingerprint."
90+
documentationLinkText="Documentation"
91+
iconClassName="icon-dim-20 fcn-6"
92+
placement="right"
93+
documentationLink={DOCUMENTATION.ENTERPRISE_LICENSE}
94+
/>
95+
)}
96+
</div>
97+
<div className="flex dc__gap-8">
98+
<span className="cn-9 fs-13 lh-1-5 fw-4 dc__truncate">{fingerprint}</span>
99+
<ClipboardButton content={fingerprint} />
100+
</div>
101+
</div>
102+
)
103+
104+
export default InstallationFingerprintInfo
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export { default as ActivateLicenseDialog } from './ActivateLicenseDialog'
2+
export { default as DevtronLicenseCard } from './DevtronLicenseCard'
3+
export { ICDevtronWithBorder, default as InstallationFingerprintInfo } from './License.components'
4+
export * from './types'
5+
export { parseDevtronLicenseData } from './utils'

0 commit comments

Comments
 (0)