Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
e5e0be3
chore: git repo url handling
shivani170 Aug 12, 2025
d7a949f
Merge branch 'develop' into feat/git-icon-replacement
shivani170 Aug 12, 2025
55cf9b5
chore: code refactoring
shivani170 Aug 13, 2025
eece861
feat: add support for freemium license
arunjaindev Aug 13, 2025
52ddf77
chore: version bump
arunjaindev Aug 13, 2025
70e544d
chore: type modified
shivani170 Aug 13, 2025
27ecf02
chore: add gradient
arunjaindev Aug 13, 2025
705d2e7
feat: add module limits in freemium
arunjaindev Aug 14, 2025
2f2fc0f
chore: version bump
arunjaindev Aug 14, 2025
5489a64
chore: add check for license data
arunjaindev Aug 14, 2025
ddd2c23
fix: use all clusters for count
arunjaindev Aug 14, 2025
b849663
chore: version bump
arunjaindev Aug 14, 2025
dcbbbf6
Merge branch 'develop' into feat/git-icon-replacement
shivani170 Aug 18, 2025
d258e21
chore: version bump
shivani170 Aug 18, 2025
048b271
chore: console removed
shivani170 Aug 18, 2025
cc854a4
feat: use icon from icon component
arunjaindev Aug 19, 2025
b00ed98
chore: version bump
arunjaindev Aug 19, 2025
5955bd5
chore: update url for upgrade contact
arunjaindev Aug 19, 2025
bdc7d8c
Merge pull request #2882 from devtron-labs/feat/freemium
arunjaindev Aug 19, 2025
f0f0241
feat: update messaging for 2 cluster in freemium
arunjaindev Aug 20, 2025
cbd919c
chore: version bump
arunjaindev Aug 20, 2025
ff043cc
Merge pull request #2886 from devtron-labs/feat/freemium-messaging
arunjaindev Aug 20, 2025
d7c1b1f
chore: code feedback fixes
shivani170 Aug 21, 2025
de1a3c9
chore: unnecessary code removed
shivani170 Aug 21, 2025
f3a9e94
Merge branch main into feat/git-icon-replacement
shivani170 Aug 21, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"private": true,
"homepage": "/dashboard",
"dependencies": {
"@devtron-labs/devtron-fe-common-lib": "1.19.0-pre-5",
"@devtron-labs/devtron-fe-common-lib": "1.19.4-pre-0",
"@esbuild-plugins/node-globals-polyfill": "0.2.3",
"@rjsf/core": "^5.13.3",
"@rjsf/utils": "^5.13.3",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ export const CIStepperContent = ({
<InfoBlock variant="success" description="Build pipeline is created" size={ComponentSizeType.medium} />
)}
{materials.map((material, index) => {
const { id, name, type, isRegex, value, regex, gitMaterialId } = material
const { id, name, type, isRegex, value, regex, gitMaterialId, url } = material

const isBranchRegex = type === SourceTypeMap.BranchRegex || isRegex
const isBranchFixed = type === SourceTypeMap.BranchFixed && !isRegex
Expand All @@ -183,6 +183,7 @@ export const CIStepperContent = ({
<Fragment key={id}>
<SourceMaterialsSelector
repoName={name}
gitURL={url}
sourceTypePickerProps={{
inputId: 'ci-pipeline-sourceType',
label: 'Source Type',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,20 @@
* limitations under the License.
*/

import { ComponentSizeType, CustomInput, Icon, SelectPicker } from '@devtron-labs/devtron-fe-common-lib'
import { ComponentSizeType, CustomInput, GitProviderIcon, SelectPicker } from '@devtron-labs/devtron-fe-common-lib'

import { SourceMaterialsSelectorProps } from './types'

export const SourceMaterialsSelector = ({
repoName,
sourceTypePickerProps,
branchInputProps,
gitURL,
}: SourceMaterialsSelectorProps) => (
<div className="flexbox-col dc__gap-8">
{repoName && (
<div className="flex left dc__gap-8">
<Icon name="ic-git" color={null} size={24} />
<GitProviderIcon gitRepoUrl={gitURL} size={24} />
<p className="m-0 fs-13 lh-20 fw-6 cn-9 dc__truncate">{repoName}</p>
</div>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,9 @@
* limitations under the License.
*/

import { CustomInputProps, SelectPickerProps } from '@devtron-labs/devtron-fe-common-lib'
import { CustomInputProps, MaterialType, SelectPickerProps } from '@devtron-labs/devtron-fe-common-lib'

export interface SourceMaterialsSelectorProps {
repoName?: string
type CommonSourceTypeProps = {
sourceTypePickerProps: Omit<
SelectPickerProps<string | number, false>,
'required' | 'isClearable' | 'closeMenuOnSelect' | 'size'
Expand All @@ -26,3 +25,13 @@ export interface SourceMaterialsSelectorProps {
hideInput?: boolean
}
}

export type SourceMaterialsSelectorProps =
| (CommonSourceTypeProps & {
repoName: string
gitURL: MaterialType['url']
})
| (CommonSourceTypeProps & {
repoName?: never
gitURL?: never
})
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ import {
GenericFilterEmptyState,
getSelectPickerOptionByValue,
Icon,
noop,
numberComparatorBySortOrder,
OptionType,
PaginationEnum,
Expand All @@ -55,6 +54,7 @@ import { importComponentFromFELibrary } from '@Components/common'
import { URLS } from '@Config/routes'
import CreateCluster from '@Pages/GlobalConfigurations/ClustersAndEnvironments/CreateCluster/CreateCluster.component'
import { CreateClusterTypeEnum } from '@Pages/GlobalConfigurations/ClustersAndEnvironments/CreateCluster/types'
import { UpgradeToEnterpriseDialog } from '@Pages/Shared/UpgradeToEnterprise'

import { getClusterList, getEnvironmentList } from './cluster.service'
import {
Expand Down Expand Up @@ -84,8 +84,9 @@ const PodSpreadModal = importComponentFromFELibrary('PodSpreadModal', null, 'fun
const HibernationRulesModal = importComponentFromFELibrary('HibernationRulesModal', null, 'function')

const ClusterList = () => {
const { isSuperAdmin } = useMainContext()
const { isSuperAdmin, licenseData } = useMainContext()
const isK8sClient = window._env_.K8S_CLIENT
const isFreemium = licenseData?.isFreemium ?? false

const { push } = useHistory()
const { search } = useLocation()
Expand Down Expand Up @@ -121,6 +122,7 @@ const ClusterList = () => {
)

const [showUnmappedEnvs, setShowUnmappedEnvs] = useState(false)
const [showUpgradeToEnterprise, setShowUpgradeToEnterprise] = useState(false)

const clusterIdVsEnvMap: Record<number, Environment[]> = useMemo(
() =>
Expand Down Expand Up @@ -244,6 +246,8 @@ const ClusterList = () => {

const isEnvironmentsView = selectedTab === ClusterEnvTabs.ENVIRONMENTS
const isClusterEnvListLoading = clusterListLoading || envListLoading
const isClusterAdditionAllowed =
!isFreemium || clusterListResult?.length < licenseData?.moduleLimits.maxAllowedClusters

// Early return for non super admin users
if (!isK8sClient && !isSuperAdmin) {
Expand Down Expand Up @@ -338,15 +342,76 @@ const ClusterList = () => {
)
}

const handleOpenUpgradeDialog = () => {
setShowUpgradeToEnterprise(true)
}

const handleCloseUpgradeDialog = () => {
setShowUpgradeToEnterprise(false)
}

const renderAddEnvButton = () => (
<Button
dataTestId="add-environment-button"
linkProps={{
to: {
pathname: `${URLS.GLOBAL_CONFIG_CLUSTER}${URLS.CREATE_ENVIRONMENT}`,
search,
},
}}
component={ButtonComponentType.link}
startIcon={<Icon name="ic-add" color={null} />}
size={ComponentSizeType.medium}
text="Add Environment"
/>
)

const renderAddClusterButton = () => (
<Button
dataTestId="add-cluster-button"
component={ButtonComponentType.link}
startIcon={<Icon name="ic-link" color={null} />}
size={ComponentSizeType.medium}
text="Connect Cluster"
{...(isClusterAdditionAllowed
? {
component: ButtonComponentType.link,
linkProps: {
to: generatePath(URLS.GLOBAL_CONFIG_CREATE_CLUSTER, {
type: CreateClusterTypeEnum.CONNECT_CLUSTER,
}),
search,
},
}
: {
component: ButtonComponentType.button,
onClick: handleOpenUpgradeDialog,
})}
/>
)

const renderAddClusterRoute = () =>
isClusterAdditionAllowed ? (
<Route path={URLS.GLOBAL_CONFIG_CREATE_CLUSTER}>
<CreateCluster
handleReloadClusterList={reloadClusterList}
handleRedirectOnModalClose={handleRedirectToClusterList}
/>
</Route>
) : null

if (clusterListResult && !clusterListResult.length) {
return (
<GenericEmptyState
title="Manage Clusters and Environments"
subTitle="It looks like you haven't set up any Kubernetes clusters yet. Start by adding your first cluster and environment."
isButtonAvailable
renderButton={noop}
image={NoClusterImg}
/>
<>
<GenericEmptyState
title="Manage Clusters and Environments"
subTitle="It looks like you haven't set up any Kubernetes clusters yet. Start by adding your first cluster and environment."
isButtonAvailable
renderButton={renderAddClusterButton}
image={NoClusterImg}
/>
{renderAddClusterRoute()}
</>
)
}

Expand Down Expand Up @@ -394,23 +459,7 @@ const ClusterList = () => {
keyboardShortcut="/"
/>
{ManageCategoryButton && <ManageCategoryButton search={search} />}
<Button
dataTestId={isEnvironmentsView ? 'add-environment-button' : 'add-cluster-button'}
linkProps={{
to: {
pathname: isEnvironmentsView
? `${URLS.GLOBAL_CONFIG_CLUSTER}${URLS.CREATE_ENVIRONMENT}`
: generatePath(URLS.GLOBAL_CONFIG_CREATE_CLUSTER, {
type: CreateClusterTypeEnum.CONNECT_CLUSTER,
}),
search,
},
}}
component={ButtonComponentType.link}
startIcon={<Icon name={isEnvironmentsView ? 'ic-add' : 'ic-link'} color={null} />}
size={ComponentSizeType.medium}
text={isEnvironmentsView ? 'Add Environment' : 'Connect Cluster'}
/>
{isEnvironmentsView ? renderAddEnvButton() : renderAddClusterButton()}
{isEnvironmentsView && (
<ActionMenu
id="additional-options-action-menu"
Expand Down Expand Up @@ -451,13 +500,9 @@ const ClusterList = () => {
{/* Body */}
{renderList()}
{/* Modals and Routes */}
<UpgradeToEnterpriseDialog open={showUpgradeToEnterprise} handleClose={handleCloseUpgradeDialog} />
{ManageCategories && <ManageCategories />}
<Route path={URLS.GLOBAL_CONFIG_CREATE_CLUSTER}>
<CreateCluster
handleReloadClusterList={reloadClusterList}
handleRedirectOnModalClose={handleRedirectToClusterList}
/>
</Route>
{renderAddClusterRoute()}
<Route path={COMMON_URLS.GLOBAL_CONFIG_EDIT_CLUSTER}>
<EditCluster
clusterList={clusterListResult ?? []}
Expand Down
16 changes: 12 additions & 4 deletions src/Pages/License/ActivateLicense.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,12 @@ const ActivateLicense = () => {

// licenseDataError.code === 404 means, oss and licensing does not exist
// In case licenseStatusError is null, license is valid
if (licenseDataError?.code === API_STATUS_CODES.NOT_FOUND || !licenseData.licenseStatusError) {
if (licenseDataError?.code === API_STATUS_CODES.NOT_FOUND || !licenseData?.licenseStatusError) {
redirectToLogin()
return
}

if (licenseData?.licenseStatusError.code === LicensingErrorCodes.LicKeyNotFound) {
if (licenseData?.licenseStatusError?.code === LicensingErrorCodes.LicKeyNotFound) {
setShowActivateDialog(true)
}
}, [isLoading, licenseData])
Expand Down Expand Up @@ -91,11 +91,17 @@ const ActivateLicense = () => {
<div className="fs-20 lh-1-5 fw-7 cn-9 font-merriweather dc__truncate">
{licenseData.enterpriseName}
</div>
<div className="fs-16 lh-1-5 cr-5 fw-4">Your license key is no longer valid</div>
<div className="fs-16 lh-1-5 cr-5 fw-4">
{licenseData.isFreemium &&
licenseData.licenseStatusError?.code === LicensingErrorCodes.ClusterLimitExceeded
? 'Freemium Limit Reached'
: 'Your license key is no longer valid'}
</div>
</div>
</div>
{licenseData.licenseStatusError &&
licenseData.licenseStatusError.code !== LicensingErrorCodes.LicenseExpired ? (
licenseData.licenseStatusError.code !== LicensingErrorCodes.LicenseExpired &&
licenseData.licenseStatusError.code !== LicensingErrorCodes.ClusterLimitExceeded ? (
<InfoBlock
heading="Need help?"
description={
Expand Down Expand Up @@ -124,7 +130,9 @@ const ActivateLicense = () => {
licenseStatus={licenseData.licenseStatus}
isTrial={licenseData.isTrial}
licenseSuffix={licenseData.licenseSuffix}
isFreemium={licenseData.isFreemium}
appTheme={appTheme}
licenseStatusError={licenseData.licenseStatusError}
/>
)}
<div className="flex dc__content-space">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import {
Button,
ButtonComponentType,
ButtonStyleType,
ButtonVariantType,
ComponentSizeType,
CONTACT_SALES_LINK,
GenericModal,
Icon,
} from '@devtron-labs/devtron-fe-common-lib'

const ENTERPRISE_PLAN_OFFERINGS = [
'Unlimited clusters',
'Managed Devtron installation',
'Priority support',
'Contribute to our roadmap',
'Early access to enterprise features & more…',
]

const UpgradeToEnterpriseDialog = ({ open, handleClose }: { open: boolean; handleClose: () => void }) => (
<GenericModal
name="upgrade-to-enterprise"
open={open}
width={450}
borderRadius={16}
onClose={handleClose}
onEscape={handleClose}
>
<GenericModal.Body>
<div
className="p-32 flexbox-col dc__gap-32"
style={{
background:
'linear-gradient(183deg, var(--bg-primary) 5.64%, var(--bg-primary, #FFF) 41.13%, var(--B100) 76.62%, var(--V200) 94.36%)',
}}
>
<div className="flexbox-col dc__gap-16">
<div className="flexbox dc__content-space dc__align-start">
<Icon name="ic-upgrade-enterprise" size={64} color={null} />
<Button
dataTestId="close-upgrade-dialog"
icon={<Icon name="ic-close-large" color={null} />}
variant={ButtonVariantType.secondary}
style={ButtonStyleType.negativeGrey}
onClick={handleClose}
ariaLabel="close-upgrade-dialog"
showAriaLabelInTippy={false}
size={ComponentSizeType.medium}
/>
</div>
<div className="flexbox-col dc__gap-8">
<h1 className="fs-24 lh-1-5 fw-7 cn-9 m-0 font-merriweather">Upgrade to Enterprise Plan</h1>
<div className="flexbox-col dc__gap-20 fs-16 fw-4 cn-9 lh-1-5">
<span>
Freemium plan allows managing the Devtron host cluster along with one additional
cluster.
</span>
<span>Switch to Enterprise plan to scale without limits.</span>
</div>
</div>
</div>
<div className="flexbox-col border__primary-translucent bg__primary br-12 shadow__card--10">
<div className="flexbox-col dc__gap-16 p-20">
<span className="fs-15 fw-6 lh-1-5 cn-9">What’s included</span>
<div className="flexbox-col dc__gap-8">
{ENTERPRISE_PLAN_OFFERINGS.map((description) => (
<div key={description} className="flexbox dc__gap-8 dc__align-items-center">
<Icon name="ic-check" color="G500" size={20} />
<span className="fs-13 lh-20 fw-4 cn-9">{description}</span>
</div>
))}
</div>
</div>
<div className="divider__secondary--horizontal" />
<div className="p-20 flexbox-col dc__gap-20">
<div className="flexbox-col dc__gap-4">
<span className="fs-15 fw-6 lh-1-5 cn-9">Unlock Devtron&apos;s Full Potential</span>
<span className="fs-13 hw-4 lh-1-5 cn-9">
Scale your infrastructure, accelerate your teams, and get the resources you need to
grow.
</span>
</div>
<Button
dataTestId="upgrade-to-enterprise"
text="Contact to upgrade"
endIcon={<Icon name="ic-arrow-right" color={null} />}
component={ButtonComponentType.anchor}
anchorProps={{
href: CONTACT_SALES_LINK,
}}
/>
</div>
</div>
</div>
</GenericModal.Body>
</GenericModal>
)

export default UpgradeToEnterpriseDialog
1 change: 1 addition & 0 deletions src/Pages/Shared/UpgradeToEnterprise/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as UpgradeToEnterpriseDialog } from './UpgradeToEnterpriseDialog'
Loading
Loading