{label && (
diff --git a/src/Shared/Components/DocLink/constants.ts b/src/Shared/Components/DocLink/constants.ts
index 029e3d3af..22ca51192 100644
--- a/src/Shared/Components/DocLink/constants.ts
+++ b/src/Shared/Components/DocLink/constants.ts
@@ -142,9 +142,6 @@ export const DOCUMENTATION = {
// Security Center
SECURITY_CENTER: 'docs/user-guide/security-features',
- // AI Recommendations
- AI_RECOMMENDATIONS: 'docs/user-guide/ai-recommendations',
-
// Automation & Enablement
AUTOMATION_AND_ENABLEMENT: 'docs/user-guide/automation',
} as const
diff --git a/src/Shared/Components/GenericModal/GenericModal.component.tsx b/src/Shared/Components/GenericModal/GenericModal.component.tsx
index 3be60dab8..2f52309f5 100644
--- a/src/Shared/Components/GenericModal/GenericModal.component.tsx
+++ b/src/Shared/Components/GenericModal/GenericModal.component.tsx
@@ -21,7 +21,7 @@ import { noop, stopPropagation } from '@Common/Helper'
import { Backdrop, Button, ButtonStyleType, ButtonVariantType, Icon } from '@Shared/Components'
import { ComponentSizeType } from '@Shared/constants'
-import { MODAL_WIDTH_TO_CLASS_NAME_MAP } from './constants'
+import { BORDER_VARIANT_TO_CLASS_NAME_MAP, MODAL_WIDTH_TO_CLASS_NAME_MAP } from './constants'
import { GenericModalProvider, useGenericModalContext } from './GenericModal.context'
import { GenericModalFooterProps, GenericModalHeaderProps, GenericModalProps } from './types'
@@ -87,13 +87,20 @@ const GenericModal = ({
onEscape = noop,
closeOnBackdropClick = false,
children,
+ avoidFocusTrap = false,
+ borderVariant = 'secondary',
+ alignCenter = false,
}: PropsWithChildren
) => (
{open && (
-
+
= {
450: 'w-450',
@@ -22,3 +22,9 @@ export const MODAL_WIDTH_TO_CLASS_NAME_MAP: Record = {
+ secondary: 'border__secondary',
+ none: '',
+ 'secondary-translucent': 'border__secondary-translucent',
+}
diff --git a/src/Shared/Components/GenericModal/types.ts b/src/Shared/Components/GenericModal/types.ts
index e5993b388..a71884e9e 100644
--- a/src/Shared/Components/GenericModal/types.ts
+++ b/src/Shared/Components/GenericModal/types.ts
@@ -17,7 +17,9 @@
import { BackdropProps } from '../Backdrop'
import { ButtonProps } from '../Button'
-export interface GenericModalProps extends Partial> {
+export type BorderVariantType = 'secondary' | 'none' | 'secondary-translucent'
+
+export interface GenericModalProps extends Partial> {
/** Unique identifier for the modal */
name: string
/** Controls whether the modal is visible or hidden */
@@ -35,6 +37,8 @@ export interface GenericModalProps extends Partial {}
diff --git a/src/Shared/Components/Header/PageHeader.tsx b/src/Shared/Components/Header/PageHeader.tsx
index fd3f84b5d..7d0c48c9b 100644
--- a/src/Shared/Components/Header/PageHeader.tsx
+++ b/src/Shared/Components/Header/PageHeader.tsx
@@ -52,8 +52,14 @@ const PageHeader = ({
closeIcon,
docPath,
}: PageHeaderType) => {
- const { setLoginCount, setShowGettingStartedCard, setSidePanelConfig, sidePanelConfig, tempAppWindowConfig } =
- useMainContext()
+ const {
+ setLoginCount,
+ setShowGettingStartedCard,
+ setSidePanelConfig,
+ sidePanelConfig,
+ tempAppWindowConfig,
+ featureAskDevtronExpert,
+ } = useMainContext()
const { showSwitchThemeLocationTippy, handleShowSwitchThemeLocationTippyChange } = useTheme()
const {
@@ -133,27 +139,26 @@ const PageHeader = ({
const onAskButtonClick = () => {
handleAnalyticsEvent({
category: 'AI',
- action: `HELP_ASK_DEVTRON_AI`,
+ action: 'HELP_ASK_DEVTRON_AI',
})
setSidePanelConfig((prev) => ({ ...prev, state: SidePanelTab.ASK_DEVTRON }))
}
const renderLogoutHelpSection = () => (
<>
- {window._env_?.FEATURE_ASK_DEVTRON_EXPERT &&
- sidePanelConfig.state === 'closed' &&
- !tempAppWindowConfig.open && (
-
-
-
- )}
+ {featureAskDevtronExpert && sidePanelConfig.state === 'closed' && !tempAppWindowConfig.open && (
+
+
+
+ )}
+
) => {
- if (isFreemium) {
- const freemiumLimitReached = licenseStatusError?.code === LicensingErrorCodes.ClusterLimitExceeded
+ isFreeForever,
+}: LicenseCardSubTextProps) => {
+ const freemiumLimitReached = isFreemium && licenseStatusError?.code === LicensingErrorCodes.ClusterLimitExceeded
+ const showFreemiumMessage =
+ isFreeForever || freemiumLimitReached || (isFreemium && licenseStatus === LicenseStatus.ACTIVE)
+ if (showFreemiumMessage) {
return (
@@ -130,10 +133,17 @@ export const DevtronLicenseCard = ({
appTheme,
handleCopySuccess,
licenseStatusError,
+ isSaasInstance,
}: DevtronLicenseCardProps) => {
- const { bgColor, textColor } = getLicenseColorsAccordingToStatus({ isFreemium, licenseStatus, licenseStatusError })
- const remainingTime = getTTLInHumanReadableFormat(ttl)
- const remainingTimeString = ttl < 0 ? `Expired ${remainingTime} ago` : `${remainingTime} remaining`
+ const isFreeForever = isFreemium && !isSaasInstance
+
+ const { bgColor, textColor } = getLicenseColorsAccordingToStatus({
+ isFreemium,
+ licenseStatus,
+ licenseStatusError,
+ isSaasInstance,
+ })
+
const isThemeDark = appTheme === AppThemeType.dark
const cardRef = useRef
(null)
@@ -178,6 +188,15 @@ export const DevtronLicenseCard = ({
? useMotionTemplate`linear-gradient(55deg, transparent, rgba(122, 127, 131, ${sheenOpacity}) ${sheenPosition}%, transparent)`
: useMotionTemplate`linear-gradient(55deg, transparent, rgba(255, 255, 255, ${sheenOpacity}) ${sheenPosition}%, transparent)`
+ const getRemainingTimeString = () => {
+ if (isFreeForever) {
+ return null
+ }
+
+ const remainingTime = getTTLInHumanReadableFormat(ttl)
+ return ttl < 0 ? `Expired ${remainingTime} ago` : `${remainingTime} remaining`
+ }
+
return (
@@ -217,12 +236,12 @@ export const DevtronLicenseCard = ({
- {isFreemium ? 'VALID FOREVER' : expiryDate}
+ {isFreeForever ? 'VALID FOREVER' : expiryDate}
- {!isFreemium && (
+ {!isFreeForever && (
<>
ยท
- {remainingTimeString}
+ {getRemainingTimeString()}
>
)}
@@ -239,6 +258,7 @@ export const DevtronLicenseCard = ({
isFreemium={isFreemium}
licenseStatusError={licenseStatusError}
licenseStatus={licenseStatus}
+ isFreeForever={isFreeForever}
/>
)
diff --git a/src/Shared/Components/License/index.tsx b/src/Shared/Components/License/index.tsx
index 75fa48db5..52c765f60 100644
--- a/src/Shared/Components/License/index.tsx
+++ b/src/Shared/Components/License/index.tsx
@@ -17,5 +17,6 @@
export { default as ActivateLicenseDialog } from './ActivateLicenseDialog'
export { default as DevtronLicenseCard } from './DevtronLicenseCard'
export { ICDevtronWithBorder, default as InstallationFingerprintInfo } from './License.components'
+export { activateLicense } from './services'
export * from './types'
export { parseDevtronLicenseData, parseDevtronLicenseDTOIntoLicenseCardData } from './utils'
diff --git a/src/Shared/Components/License/types.ts b/src/Shared/Components/License/types.ts
index 38b2be000..4c2d639ae 100644
--- a/src/Shared/Components/License/types.ts
+++ b/src/Shared/Components/License/types.ts
@@ -32,6 +32,7 @@ export type DevtronLicenseCardProps = {
isFreemium: boolean
appTheme: AppThemeType
licenseStatusError: LicenseErrorStruct
+ isSaasInstance: boolean
} & (
| {
licenseKey: string
@@ -45,6 +46,11 @@ export type DevtronLicenseCardProps = {
}
)
+export interface LicenseCardSubTextProps
+ extends Pick {
+ isFreeForever: boolean
+}
+
export type DevtronLicenseInfo = Omit &
Pick
diff --git a/src/Shared/Components/License/utils.tsx b/src/Shared/Components/License/utils.tsx
index 540d14613..423eb9648 100644
--- a/src/Shared/Components/License/utils.tsx
+++ b/src/Shared/Components/License/utils.tsx
@@ -27,16 +27,22 @@ export const getLicenseColorsAccordingToStatus = ({
isFreemium,
licenseStatus,
licenseStatusError,
-}: Pick): {
+ isSaasInstance,
+}: 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)' }
+ if (freemiumLimitReached) {
+ return { bgColor: 'var(--R100)', textColor: 'var(--R500)' }
+ }
+
+ if (!isSaasInstance) {
+ return { bgColor: 'var(--G100)', textColor: 'var(--G500)' }
+ }
}
+
switch (licenseStatus) {
case LicenseStatus.ACTIVE:
return { bgColor: 'var(--G100)', textColor: 'var(--G500)' }
@@ -68,16 +74,24 @@ export const parseDevtronLicenseDTOIntoLicenseCardData = => {
const {
isTrial,
- expiry,
- ttl,
+ expiry: onPremExpiry,
+ ttl: onPremTTL,
reminderThreshold,
organisationMetadata,
license,
claimedByUserDetails,
isFreemium,
licenseStatusError,
+ isSaasInstance,
+ timeElapsedSinceCreation,
+ creationTime,
} = licenseDTO || {}
+ // In case of Saas expiry date is 30 days from creation time
+ const expiry = isSaasInstance && creationTime ? moment(creationTime).add(30, 'days').toISOString() : onPremExpiry
+ // For TTL will use timeElapsedSinceCreation to calculate remaining time for Saas license with 30 days validity, since browser time may differ from server time
+ const ttl = isSaasInstance && timeElapsedSinceCreation ? 30 * 24 * 60 * 60 - timeElapsedSinceCreation : onPremTTL
+
return {
enterpriseName: organisationMetadata?.name || 'Devtron Enterprise',
expiryDate: expiry ? moment(expiry).format(DATE_TIME_FORMATS['DD/MM/YYYY']) : '',
@@ -86,6 +100,7 @@ export const parseDevtronLicenseDTOIntoLicenseCardData =
+export enum AIAgentContextSourceType {
+ APP_DETAILS = 'app-details',
+ RESOURCE_BROWSER_CLUSTER = 'resource-browser-cluster',
+}
+
+export type AIAgentAppType =
+ | 'devtronApp'
+ | 'devtronHelmChart'
+ | 'externalHelmChart'
+ | 'externalArgoApp'
+ | 'externalFluxApp'
+
+type AIAgentAppDataMasterType = {
+ appId: number | string
+ appName: string
+ envId: number
+ envName: string
+ clusterId: number
+ namespace: string
+ appType: AIAgentAppType
+ fluxAppDeploymentType: string
+}
+
+type AIAgentAppDataType = Pick<
+ AIAgentAppDataMasterType,
+ TRequiredFields
+> & {
+ [K in Exclude]?: never
+} & {
+ appType: TAppType
+}
+
+export type AIAgentContextType =
+ | {
+ source: AIAgentContextSourceType.APP_DETAILS
+ data:
+ | AIAgentAppDataType<
+ 'devtronApp' | 'devtronHelmChart',
+ 'appId' | 'appName' | 'envId' | 'envName' | 'clusterId'
+ >
+ | AIAgentAppDataType<'externalHelmChart', 'appId' | 'appName' | 'clusterId' | 'namespace'>
+ | AIAgentAppDataType<'externalArgoApp', 'appName' | 'clusterId' | 'namespace'>
+ | (AIAgentAppDataType<
+ 'externalFluxApp',
+ 'appName' | 'clusterId' | 'namespace' | 'fluxAppDeploymentType'
+ > &
+ Record)
+ }
+ | {
+ source: AIAgentContextSourceType.RESOURCE_BROWSER_CLUSTER
+ data: {
+ clusterId: number
+ clusterName: string
+ } & Record
+ }
+
+export type DebugAgentContextType = AIAgentContextType & {
+ prompt?: string
}
export interface TempAppWindowConfig {
@@ -118,6 +174,8 @@ type CommonMainContextProps = {
setLicenseData: Dispatch>
canFetchHelmAppStatus: boolean
setIntelligenceConfig: Dispatch>
+ debugAgentContext: DebugAgentContextType | null
+ setDebugAgentContext: (aiAgentContext: DebugAgentContextType | null) => void
setAIAgentContext: (aiAgentContext: AIAgentContextType) => void
setSidePanelConfig: Dispatch>
} & Pick
@@ -152,6 +210,8 @@ export type MainContext = CommonMainContextProps &
aiAgentContext: AIAgentContextType
tempAppWindowConfig: TempAppWindowConfig
setTempAppWindowConfig: Dispatch>
+ AIRecommendations?: FunctionComponent
+ featureAskDevtronExpert: EnvironmentDataValuesDTO['featureAskDevtronExpert']
}
| {
isLicenseDashboard: true
@@ -170,6 +230,8 @@ export type MainContext = CommonMainContextProps &
aiAgentContext: null
tempAppWindowConfig: null
setTempAppWindowConfig: null
+ AIRecommendations?: null
+ featureAskDevtronExpert?: null
}
)
diff --git a/src/Shared/Services/types.ts b/src/Shared/Services/types.ts
index dd0ba0e8e..7a2fdf857 100644
--- a/src/Shared/Services/types.ts
+++ b/src/Shared/Services/types.ts
@@ -61,6 +61,7 @@ export interface EnvironmentDataValuesDTO extends Pick = {
[RemoteConnectionType.Direct]: 'Direct Connection',
diff --git a/src/Shared/types.ts b/src/Shared/types.ts
index 76a6859f3..5084811d2 100644
--- a/src/Shared/types.ts
+++ b/src/Shared/types.ts
@@ -1106,18 +1106,10 @@ export interface LicenseErrorStruct {
userMessage: string
}
-export interface DevtronLicenseBaseDTO {
+export type DevtronLicenseBaseDTO = {
fingerprint: string | null
isTrial: boolean | null
isFreemium: boolean | null
- /**
- * In timestamp format
- */
- expiry: string | null
- /**
- * Can be negative, depicts time left in seconds for license to expire
- */
- ttl: number | null
/**
* Show a reminder after these many DAYS left for license to expire, i.e,
* Show if `ttl` is less than `reminderThreshold` [converted to seconds]
@@ -1128,7 +1120,31 @@ export interface DevtronLicenseBaseDTO {
domain: string | null
} | null
license: string | null
-}
+} & (
+ | {
+ isSaasInstance: true
+ /**
+ * In seconds
+ */
+ timeElapsedSinceCreation: number
+ creationTime: string
+ ttl?: never
+ expiry?: never
+ }
+ | {
+ isSaasInstance?: false
+ timeElapsedSinceCreation?: never
+ creationTime?: never
+ /**
+ * Can be negative, depicts time left in seconds for license to expire
+ */
+ ttl: number | null
+ /**
+ * In timestamp format
+ */
+ expiry: string | null
+ }
+)
export type DevtronLicenseDTO = DevtronLicenseBaseDTO &
(isCentralDashboard extends true
@@ -1141,9 +1157,14 @@ export type DevtronLicenseDTO = Devt
showLicenseData?: never
licenseStatusError?: never
moduleLimits?: never
+ instanceData: {
+ devtronUrl: string
+ devtronPassword: string
+ } | null
}
: {
claimedByUserDetails?: never
+ instanceData?: never
showLicenseData: boolean
licenseStatusError?: LicenseErrorStruct
moduleLimits: {
@@ -1327,7 +1348,7 @@ export interface DeploymentStatusDetailsBreakdownDataType {
export interface IntelligenceConfig {
clusterId: number
- metadata: Record
+ metadata: Record
prompt: string
analyticsCategory: string
}
diff --git a/src/index.ts b/src/index.ts
index fbff34eac..a33489fd6 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -155,7 +155,6 @@ export interface customEnv {
GATEKEEPER_URL?: string
FEATURE_AI_INTEGRATION_ENABLE?: boolean
LOGIN_PAGE_IMAGE?: string
- FEATURE_ASK_DEVTRON_EXPERT?: boolean
/**
* If true, the manage traffic feature is enabled in apps & app groups.
*
@@ -188,6 +187,7 @@ export interface customEnv {
* @default false
*/
FEATURE_STORAGE_ENABLE?: boolean
+ FEATURE_ATHENA_DEBUG_MODE_ENABLE?: boolean
}
declare global {
interface Window {