diff --git a/package.json b/package.json index ba972db234..97bfdc5d8c 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "homepage": "/dashboard", "dependencies": { - "@devtron-labs/devtron-fe-common-lib": "0.3.21-beta-2", + "@devtron-labs/devtron-fe-common-lib": "0.3.22-beta-4", "@esbuild-plugins/node-globals-polyfill": "0.2.3", "@rjsf/core": "^5.13.3", "@rjsf/utils": "^5.13.3", diff --git a/src/assets/icons/ic-camera.svg b/src/assets/icons/ic-camera.svg new file mode 100644 index 0000000000..3068e02336 --- /dev/null +++ b/src/assets/icons/ic-camera.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/components/app/details/appDetails/AppDetails.tsx b/src/components/app/details/appDetails/AppDetails.tsx index a167f443f9..dac67e74ac 100644 --- a/src/components/app/details/appDetails/AppDetails.tsx +++ b/src/components/app/details/appDetails/AppDetails.tsx @@ -38,7 +38,7 @@ import { } from '@devtron-labs/devtron-fe-common-lib' import { Link, useParams, useHistory, useRouteMatch, generatePath, Route, useLocation } from 'react-router-dom' import Tippy from '@tippyjs/react' -import Select, { components } from 'react-select' +import Select from 'react-select' import { fetchAppDetailsInTime, fetchResourceTreeInTime } from '../../service' import { URLS, @@ -296,6 +296,7 @@ export const Details: React.FC = ({ const appDetailsAbortRef = useRef(null) const shouldFetchTimelineRef = useRef(false) + const [deploymentStatusDetailsBreakdownData, setDeploymentStatusDetailsBreakdownData] = useState({ ...(isVirtualEnvRef.current && processVirtualEnvironmentDeploymentData @@ -356,6 +357,7 @@ export const Details: React.FC = ({ ], ) + // This is called only when timeline modal is open const getDeploymentDetailStepsData = useCallback( (showTimeline?: boolean): void => { const shouldFetchTimeline = showTimeline ?? shouldFetchTimelineRef.current @@ -425,7 +427,10 @@ export const Details: React.FC = ({ } IndexStore.publishAppDetails(appDetailsRef.current, AppType.DEVTRON_APP) setAppDetails(appDetailsRef.current) - _getDeploymentStatusDetail(appDetailsRef.current.deploymentAppType) + + const isIsolatedEnv = isVirtualEnvRef.current && !!appDetailsRef.current.resourceTree + + _getDeploymentStatusDetail(appDetailsRef.current.deploymentAppType, isIsolatedEnv, isIsolatedEnv ? appDetailsRef.current?.resourceTree?.wfrId : null) if (fetchExternalLinks && response.result?.clusterId) { getExternalLinksAndTools(response.result.clusterId) @@ -470,13 +475,15 @@ export const Details: React.FC = ({ }) } - function _getDeploymentStatusDetail(deploymentAppType: DeploymentAppTypes) { + function _getDeploymentStatusDetail(deploymentAppType: DeploymentAppTypes, isIsolatedEnv: boolean, triggerIdToFetch?: number) { const shouldFetchTimeline = shouldFetchTimelineRef.current - getDeploymentStatusDetail(params.appId, params.envId, shouldFetchTimeline) + // triggerIdToFetch represents the wfrId to fetch for any specific deployment + getDeploymentStatusDetail(params.appId, params.envId, shouldFetchTimeline, triggerIdToFetch?.toString()) .then((deploymentStatusDetailRes) => { if (deploymentStatusDetailRes.result) { - if (deploymentAppType === DeploymentAppTypes.HELM) { + // Timelines are not applicable for helm deployments and air gapped envs + if (deploymentAppType === DeploymentAppTypes.HELM || isIsolatedEnv) { setDeploymentStatusDetailsBreakdownData({ ...deploymentStatusDetailsBreakdownData, deploymentStatus: @@ -617,7 +624,7 @@ export const Details: React.FC = ({ const environmentName = environmentsMap[+envId] const renderAppDetails = (): JSX.Element => { - if (isVirtualEnvRef.current && VirtualAppDetailsEmptyState) { + if (!appDetails.resourceTree && isVirtualEnvRef.current && VirtualAppDetailsEmptyState) { return } return ( @@ -627,6 +634,7 @@ export const Details: React.FC = ({ monitoringTools={externalLinksAndTools.monitoringTools} isDevtronApp isDeploymentBlocked={isDeploymentBlocked} + isVirtualEnvironment={isVirtualEnvRef.current} /> ) } @@ -942,17 +950,8 @@ export const EnvSelector = ({ value={envId ? { value: +envId, label: environmentName } : null} onChange={(selected, meta) => selectEnvironment((selected as any).value)} closeMenuOnSelect - // components={{ - // IndicatorSeparator: null, - // Option, - // GroupHeading: (props) => , - // DropdownIndicator: components.DropdownIndicator, - // ValueContainer: (props) => , - // }} - // styles={envSelectorStyle} isSearchable classNamePrefix="app-environment-select" - // formatOptionLabel={formatOptionLabel} /> diff --git a/src/components/app/details/appDetails/SourceInfo.tsx b/src/components/app/details/appDetails/SourceInfo.tsx index 0688c6cacd..eb79442030 100644 --- a/src/components/app/details/appDetails/SourceInfo.tsx +++ b/src/components/app/details/appDetails/SourceInfo.tsx @@ -14,17 +14,21 @@ * limitations under the License. */ -// @ts-nocheck - @TODO: Remove this by fixing the type issues -import React, { useEffect, useMemo, useState } from 'react' -import { useParams } from 'react-router-dom' +import { useEffect, useMemo, useState } from 'react' +import { Link, useParams } from 'react-router-dom' import Tippy from '@tippyjs/react' +import moment from 'moment' import { ConditionalWrap, + DATE_TIME_FORMATS, DeploymentAppTypes, getIsManualApprovalConfigured, + handleUTCTime, ReleaseMode, showError, + Tooltip, } from '@devtron-labs/devtron-fe-common-lib' +import { ReactComponent as ICCamera } from '@Icons/ic-camera.svg' import { URLS } from '../../../../config' import { EnvSelector } from './AppDetails' import { DeploymentAppTypeNameMapping } from '../../../../config/constantMessaging' @@ -43,6 +47,7 @@ import { ReactComponent as RotateIcon } from '../../../../assets/icons/ic-arrows import { ReactComponent as LinkIcon } from '../../../../assets/icons/ic-link.svg' import { ReactComponent as Trash } from '../../../../assets/icons/ic-delete-dots.svg' import { ReactComponent as ScaleDown } from '../../../../assets/icons/ic-scale-down.svg' +import HelmAppConfigApplyStatusCard from '@Components/v2/appDetails/sourceInfo/environmentStatus/HelmAppConfigApplyStatusCard' const AppDetailsDownloadCard = importComponentFromFELibrary('AppDetailsDownloadCard') const DeploymentWindowStatusCard = importComponentFromFELibrary('DeploymentWindowStatusCard') @@ -79,6 +84,7 @@ export const SourceInfo = ({ // helmMigratedAppNotTriggered means the app is migrated from a helm release and has not been deployed yet i.e. CD Pipeline has not been triggered const helmMigratedAppNotTriggered = appDetails?.releaseMode === ReleaseMode.MIGRATE_HELM && !appDetails?.isPipelineTriggered + const isIsolatedEnv = isVirtualEnvironment && !!appDetails?.resourceTree if ( ['progressing', 'degraded'].includes(status?.toLowerCase()) && @@ -139,6 +145,10 @@ export const SourceInfo = ({ const renderDevtronAppsEnvironmentSelector = (environment) => { // If moving to a component then move getIsApprovalConfigured with it as well with memoization. const isApprovalConfigured = getIsApprovalConfigured() + const relativeSnapshotTime = appDetails?.resourceTree?.lastSnapshotTime ? handleUTCTime( + appDetails.resourceTree.lastSnapshotTime, + true, + ) : '' return (
@@ -147,26 +157,50 @@ export const SourceInfo = ({ disabled={loadingDetails || loadingResourceTree || (params.envId && !showCommitInfo)} /> {appDetails?.deploymentAppType && ( - -
- +
+ {/* TODO: verify what appType needs to be passed */} +
- + )} {isdeploymentAppDeleting && ( -
+
Deleting deployment pipeline
)} + {/* Last snapshot time */} + {isIsolatedEnv && relativeSnapshotTime && ( + +
Last snapshot received
+

+ {moment(appDetails.resourceTree.lastSnapshotTime).format( + DATE_TIME_FORMATS.TWELVE_HOURS_FORMAT, + )} +

+
+ } + alwaysShowTippyOnHover + > +
+
+
+ +

{relativeSnapshotTime}

+
+
+ + )} {!loadingResourceTree && environment && ( <> {!isdeploymentAppDeleting && ( @@ -204,7 +238,7 @@ export const SourceInfo = ({ -
- {getNodeDetailTabs(node.kind as NodeType).map((kind, index) => { - return ( -
- {kind.toLowerCase()} -
- ) - })} -
- {node.kind !== NodeType.Containers && ( + {!appDetails.isVirtualEnvironment && ( <> -
- {node.kind.toLowerCase() == NodeType.Pod.toLowerCase() && ( +
+ {getNodeDetailTabs(node.kind as NodeType).map((kind, index) => { + return ( +
+ {kind.toLowerCase()} +
+ ) + })} +
+ {node.kind !== NodeType.Containers && ( <>
-
+ {node.kind.toLowerCase() == NodeType.Pod.toLowerCase() && ( + <> +
+
+ + )} )} @@ -496,7 +500,8 @@ const NodeComponent = ({ )}
)} - {node?.kind !== NodeType.Containers && + {!appDetails.isVirtualEnvironment && + node?.kind !== NodeType.Containers && node?.kind !== 'Endpoints' && node?.kind !== 'EndpointSlice' && ( -
+
-
+
-
-
Config apply status
+
+

Config apply status

-
- +
+
-
+

{releaseStatus.status} -

+

-
+
-
- {releaseStatus.message} -
+ +

{releaseStatus.message}

+
) : null diff --git a/src/components/v2/values/chartValuesDiff/ChartValuesView.component.tsx b/src/components/v2/values/chartValuesDiff/ChartValuesView.component.tsx index 4f50ecda2e..06acb69809 100644 --- a/src/components/v2/values/chartValuesDiff/ChartValuesView.component.tsx +++ b/src/components/v2/values/chartValuesDiff/ChartValuesView.component.tsx @@ -15,6 +15,7 @@ */ import { useEffect, useState } from 'react' +import { GroupBase } from 'react-select' import { useParams } from 'react-router-dom' import { Progressing, @@ -28,15 +29,17 @@ import { TippyTheme, GitOpsAuthModeType, SelectPicker, + SelectPickerOptionType, + SelectPickerProps, } from '@devtron-labs/devtron-fe-common-lib' import Tippy from '@tippyjs/react' import { ReactComponent as Error } from '../../../../assets/icons/ic-warning.svg' -import { ChartValuesSelect } from '../../../charts/util/ChartValueSelect' -import { importComponentFromFELibrary, Select } from '../../../common' +import { importComponentFromFELibrary } from '../../../common' import { ReactComponent as Close } from '../../assets/icons/ic-close.svg' import { ReactComponent as EditIcon } from '../../../../assets/icons/ic-pencil.svg' import { AUTO_GENERATE_GITOPS_REPO, GITOPS_REPO_REQUIRED, GITOPS_REPO_REQUIRED_FOR_ENV } from './constant' import './ChartValuesView.scss' +import { ReactComponent as ICAdd } from '@Icons/ic-add.svg' import { ActiveReadmeColumnProps, @@ -66,6 +69,8 @@ import { ReactComponent as ArgoCD } from '../../../../assets/icons/argo-cd-app.s import { ReactComponent as Helm } from '../../../../assets/icons/helm-app.svg' import { DELETE_ACTION, repoType } from '../../../../config' import UserGitRepo from '../../../gitOps/UserGitRepo' +import { getChartValuesFiltered } from '@Components/charts/charts.helper' +import { ChartValuesType } from '@Components/charts/charts.types' const VirtualEnvSelectionInfoText = importComponentFromFELibrary('VirtualEnvSelectionInfoText') const VirtualEnvHelpTippy = importComponentFromFELibrary('VirtualEnvHelpTippy') @@ -77,7 +82,7 @@ export const ChartEnvironmentSelector = ({ selectedEnvironment, handleEnvironmentSelection, environments, - invalidaEnvironment, + invalidEnvironment, isVirtualEnvironmentOnSelector, isVirtualEnvironment, }: ChartEnvironmentSelectorType): JSX.Element => { @@ -86,6 +91,8 @@ export const ChartEnvironmentSelector = ({ if (isVirtualEnvironmentOnSelector && VirtualEnvSelectionInfoText) { return } + + return null } const renderVirtualTippy = (): JSX.Element => { @@ -120,20 +127,18 @@ export const ChartEnvironmentSelector = ({ )}
) : ( -
+
- {invalidaEnvironment && renderValidationErrorLabel()} - {renderVirtualEnvironmentInfoText()}
) } @@ -447,60 +452,50 @@ export const ChartProjectSelector = ({ invalidProject, }: ChartProjectSelectorType): JSX.Element => { return ( -
+
- {invalidProject && renderValidationErrorLabel()}
) } export const ChartVersionSelector = ({ selectedVersion, - chartVersionObj, selectedVersionUpdatePage, handleVersionSelection, chartVersionsData, }: ChartVersionSelectorType) => { + const selectOptions = chartVersionsData.map(chartVersion => ({ + value: chartVersion.id, + label: chartVersion.version, + })) + + const selectedOption = selectOptions.find( + (option) => option.value === selectedVersionUpdatePage?.id || option.value === selectedVersion, + ) + return ( -
- - Chart Version - - + } + value={selectedOption} + />
) } @@ -513,21 +508,79 @@ export const ChartValuesSelector = ({ hideVersionFromLabel, hideCreateNewOption, }: ChartValuesSelectorType) => { + const filteredChartValues = getChartValuesFiltered(chartValuesList) + + const selectOptions: GroupBase>[] = [ + { + label: 'Deployed', + options: filteredChartValues.deployedChartValues.map((chartValue) => ({ + value: chartValue, + label: `${chartValue.name} ${chartValue.chartVersion}`, + description: `Deployed on: ${chartValue.environmentName || ''}`, + })), + }, + { + label: 'Preset Values', + options: filteredChartValues.savedChartValues.map((chartValue) => ({ + value: chartValue, + label: `${chartValue.name} ${chartValue.chartVersion}`, + })), + }, + { + label: 'Existing', + options: filteredChartValues.existingChartValues.map((chartValue) => ({ + value: chartValue, + label: `${chartValue.name}${hideVersionFromLabel || !chartValue.chartVersion ? '' : ` (${chartValue.chartVersion})`}`, + })), + }, + { + label: 'Default', + options: filteredChartValues.defaultChartValues.map((chartValue) => ({ + value: chartValue, + label: `${chartValue.name} ${chartValue.chartVersion}`, + })), + }, + ] + + const renderMenuListFooter = () => { + if (hideCreateNewOption) { + return null + } + + return ( + + ) + } + + const getOptionValue: SelectPickerProps['getOptionValue'] = (option) => + `${option.value.id} ${option.value.kind}` + + const handleChange: SelectPickerProps['onChange'] = (selectedOption) => + handleChartValuesSelection(selectedOption.value) + + const selectedOption = selectOptions.flatMap(groupedOption => groupedOption.options).find(option => getOptionValue(option) === getOptionValue({ + // Setting label null since the getOptionValue is not consuming it + label: null, + value: chartValues + })) + return ( -
- - Chart Values - - -
+ + inputId="chart-values-selector" + options={selectOptions} + renderMenuListFooter={renderMenuListFooter} + getOptionValue={getOptionValue} + label="Chart Values" + onChange={handleChange} + value={selectedOption} + /> ) } diff --git a/src/components/v2/values/chartValuesDiff/ChartValuesView.scss b/src/components/v2/values/chartValuesDiff/ChartValuesView.scss index fccd20160f..73ec0c7744 100644 --- a/src/components/v2/values/chartValuesDiff/ChartValuesView.scss +++ b/src/components/v2/values/chartValuesDiff/ChartValuesView.scss @@ -133,12 +133,6 @@ width: 268px; } - button.chart-values-selector { - min-height: 32px; - background-color: var(--N50); - padding: 0 8px 0 10px; - } - .chart-deprecated-wrapper { padding: 10px 12px; line-height: 18px; diff --git a/src/components/v2/values/chartValuesDiff/ChartValuesView.tsx b/src/components/v2/values/chartValuesDiff/ChartValuesView.tsx index f11d886336..0034dafde7 100644 --- a/src/components/v2/values/chartValuesDiff/ChartValuesView.tsx +++ b/src/components/v2/values/chartValuesDiff/ChartValuesView.tsx @@ -1698,7 +1698,7 @@ const ChartValuesView = ({ selectedEnvironment={commonState.selectedEnvironment} handleEnvironmentSelection={handleEnvironmentSelection} environments={commonState.environments} - invalidaEnvironment={commonState.invalidaEnvironment} + invalidEnvironment={commonState.invalidaEnvironment} isVirtualEnvironmentOnSelector={isVirtualEnvironmentOnSelector} isVirtualEnvironment={appDetails?.isVirtualEnvironment} /> diff --git a/src/components/v2/values/chartValuesDiff/ChartValuesView.type.ts b/src/components/v2/values/chartValuesDiff/ChartValuesView.type.ts index 351ae866d8..d3d33980e8 100644 --- a/src/components/v2/values/chartValuesDiff/ChartValuesView.type.ts +++ b/src/components/v2/values/chartValuesDiff/ChartValuesView.type.ts @@ -94,7 +94,7 @@ export interface ChartEnvironmentSelectorType extends ChartSelectorType { selectedEnvironment?: ChartEnvironmentOptionType handleEnvironmentSelection?: (selected: ChartEnvironmentOptionType) => void environments?: ChartEnvironmentOptionType[] | ChartEnvironmentListType[] - invalidaEnvironment: boolean + invalidEnvironment: boolean isVirtualEnvironmentOnSelector?: boolean isVirtualEnvironment?: boolean } diff --git a/src/components/v2/values/chartValuesDiff/ChartValuesView.utils.ts b/src/components/v2/values/chartValuesDiff/ChartValuesView.utils.ts index 5ab81c2520..d7a3072444 100644 --- a/src/components/v2/values/chartValuesDiff/ChartValuesView.utils.ts +++ b/src/components/v2/values/chartValuesDiff/ChartValuesView.utils.ts @@ -24,7 +24,6 @@ import { ChartDeploymentManifestDetailResponse, getDeploymentManifestDetails, } from '../../chartDeploymentHistory/chartDeploymentHistory.service' -import { groupStyle } from '../../common/ReactSelect.utils' export const getCompareValuesSelectStyles = () => ({ control: (base) => ({ @@ -178,23 +177,6 @@ export const getAndUpdateSchemaValue = ( }) } -export const envGroupStyle = { - ...groupStyle(), - control: (base) => ({ - ...base, - border: '1px solid #d6dbdf', - background: 'var(--N50)', - minHeight: '32px', - }), - dropdownIndicator: (base, state) => ({ - ...base, - color: 'var(--N400)', - padding: '0 8px', - transition: 'all .2s ease', - transform: state.selectProps.menuIsOpen ? 'rotate(180deg)' : 'rotate(0deg)', - }), -} - export const updateYamlDocument = ( operations: Operation[], json: object, diff --git a/src/css/base.scss b/src/css/base.scss index cb825268a0..74160baab5 100644 --- a/src/css/base.scss +++ b/src/css/base.scss @@ -3971,6 +3971,14 @@ textarea, overflow-x: scroll; } +.dc__overflow-initial { + overflow: initial; + + &--imp { + overflow: initial !important; + } +} + .dc__no-svg-fill { path { fill: none !important; @@ -4719,12 +4727,6 @@ textarea::placeholder { border: none; } } - - .dc__transition { - &--transform { - transition: transform 0.3s; - } - } } .dc__even-child {