Skip to content

Commit f702d1d

Browse files
authored
Merge pull request #2700 from devtron-labs/feat/retain-env-context
feat: retain env context throughout app details
2 parents 737b585 + fa0dc9f commit f702d1d

File tree

12 files changed

+151
-84
lines changed

12 files changed

+151
-84
lines changed

src/Pages/Applications/DevtronApps/Details/AppConfigurations/AppConfig.types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ export interface EnvConfigurationsNavProps extends Required<Pick<AppConfigProps,
218218
isCMSecretLocked?: boolean
219219
hideEnvSelector?: boolean
220220
appOrEnvIdToResourceApprovalConfigurationMap: AppConfigState['envIdToEnvApprovalConfigurationMap']
221+
shouldSetEnvInContext?: boolean
221222
}
222223

223224
export interface EnvConfigRouteParams {

src/Pages/Applications/DevtronApps/Details/AppConfigurations/Navigation/AppNavigation.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ export const AppNavigation = () => {
172172
isCMSecretLocked={!isUnlocked.workflowEditor}
173173
appOrEnvIdToResourceApprovalConfigurationMap={envIdToEnvApprovalConfigurationMap}
174174
isTemplateView={isTemplateView}
175+
shouldSetEnvInContext={!isTemplateView && !isJobView}
175176
/>
176177
)}
177178
</Route>

src/Pages/Applications/DevtronApps/Details/AppConfigurations/Navigation/EnvConfigurationsNav.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import { ReactComponent as ICArrowsLeftRight } from '@Icons/ic-arrows-left-right
4242
import { ReactComponent as ICBack } from '@Icons/ic-caret-left-small.svg'
4343
import { ReactComponent as ICFileCode } from '@Icons/ic-file-code.svg'
4444
import { ReactComponent as ICLocked } from '@Icons/ic-locked.svg'
45+
import { useAppContext } from '@Components/common'
4546
import { DEPLOYMENT_CONFIGURATION_RESOURCE_TYPE_ROUTE } from '@Config/constants'
4647
import { URLS } from '@Config/routes'
4748
import { ResourceConfigState } from '@Pages/Applications/DevtronApps/service.types'
@@ -306,15 +307,23 @@ export const EnvConfigurationsNav = ({
306307
compareWithURL,
307308
appOrEnvIdToResourceApprovalConfigurationMap,
308309
isTemplateView,
310+
shouldSetEnvInContext = false,
309311
}: EnvConfigurationsNavProps) => {
310312
const history = useHistory()
311313
const { isSuperAdmin } = useMainContext()
314+
const { setEnvironmentId } = useAppContext()
312315
const { pathname } = useLocation()
313316
const { path, params } = useRouteMatch<EnvConfigRouteParams>()
314317

315318
const { envId, resourceType } = params
316319
const parsedResourceId = +params[paramToCheck]
317320

321+
useEffect(() => {
322+
if (shouldSetEnvInContext && envId && !Number.isNaN(+envId)) {
323+
setEnvironmentId(+envId)
324+
}
325+
}, [envId, shouldSetEnvInContext])
326+
318327
const { isLoading } = envConfig
319328
const resourceData =
320329
resourceList.find((resource) => resource.id === parsedResourceId) ||

src/components/app/details/appDetails/AppDetails.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -840,7 +840,6 @@ const AppDetail = ({ detailsType, filteredResourceIds }: AppDetailProps) => {
840840
replace(newUrl)
841841
return
842842
}
843-
setEnvironmentId(null)
844843
return
845844
}
846845

@@ -857,7 +856,7 @@ const AppDetail = ({ detailsType, filteredResourceIds }: AppDetailProps) => {
857856
if (!params.envId || !params.appId) {
858857
return
859858
}
860-
// Setting environmentId in app context only in cse of app details and not env details
859+
// Setting environmentId in app context only in case of app details and not env details
861860
if (isAppView) {
862861
setEnvironmentId(Number(params.envId))
863862
}

src/components/app/details/cdDetails/CDDetails.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export default function CDDetails({ filteredEnvIds }: { filteredEnvIds: string }
6464
triggerId: string
6565
pipelineId: string
6666
}>()
67-
const { currentAppName } = useAppContext()
67+
const { currentAppName, environmentId, setEnvironmentId } = useAppContext()
6868
const [pagination, setPagination] = useState<{ offset: number; size: number }>({ offset: 0, size: 20 })
6969
const [hasMore, setHasMore] = useState<boolean>(false)
7070
const [hasMoreLoading, setHasMoreLoading] = useState<boolean>(false)
@@ -161,8 +161,12 @@ export default function CDDetails({ filteredEnvIds }: { filteredEnvIds: string }
161161

162162
//Result Typing to be fixed here
163163
useEffect(() => {
164+
if (!envId && environmentId) {
165+
replace(generatePath(path, { envId: environmentId, appId }))
166+
return
167+
}
164168
if (result) {
165-
if (result[1]) {
169+
if (result[1] && pipelineId) {
166170
setDeploymentAppType(
167171
result[1]['value']?.pipelines?.find((pipeline) => pipeline.id === Number(pipelineId))
168172
?.deploymentAppType,
@@ -179,6 +183,7 @@ export default function CDDetails({ filteredEnvIds }: { filteredEnvIds: string }
179183
})
180184
setSelectedEnv(_selectedEnvironment)
181185
}
186+
setEnvironmentId(+envId)
182187
}
183188

184189
return () => {
@@ -187,7 +192,7 @@ export default function CDDetails({ filteredEnvIds }: { filteredEnvIds: string }
187192
setHasMore(false)
188193
setFetchTriggerIdData(null)
189194
}
190-
}, [envId])
195+
}, [envId, result, pipelineId])
191196

192197
useEffect(() => {
193198
if (result) {
@@ -196,6 +201,7 @@ export default function CDDetails({ filteredEnvIds }: { filteredEnvIds: string }
196201
(pipeline) => pipeline.id === Number(pipelineId),
197202
)?.deploymentAppType
198203
const cdPipelinesMap = mapByKey(pipelines, 'environmentId')
204+
199205
let _selectedEnvironment
200206
let isEnvDeleted = false
201207
const filteredEnvMap = filteredEnvIds?.split(',').reduce((agg, curr) => agg.set(+curr, true), new Map())
@@ -222,9 +228,11 @@ export default function CDDetails({ filteredEnvIds }: { filteredEnvIds: string }
222228
(envOptions.length === 1 && !envId && !isEnvDeleted) ||
223229
(envId && envOptions.length && !_selectedEnvironment)
224230
) {
231+
setEnvironmentId(+envOptions[0].value)
225232
replace(generatePath(path, { appId, envId: envOptions[0].value, pipelineId: envOptions[0].pipelineId }))
226233
} else if (envId && !pipelineId && _selectedEnvironment) {
227234
// Update the pipeline id when the selected environment is available and pipeline id is not available
235+
setEnvironmentId(_selectedEnvironment.environmentId)
228236
replace(
229237
generatePath(path, {
230238
appId,

src/components/app/details/main.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,15 @@ export default function AppDetailsPage() {
405405
<Route
406406
path={`${path}/${URLS.APP_DEPLOYMENT_METRICS}/:envId(\\d+)?`}
407407
render={(props) => {
408-
return <DeploymentMetrics {...props} filteredEnvIds={_filteredEnvIds} />
408+
const envId = props.match.params.envId
409+
const match = {
410+
...props.match,
411+
params: {
412+
appId: appId,
413+
envId: envId,
414+
},
415+
}
416+
return <DeploymentMetrics {...props} match={match} filteredEnvIds={_filteredEnvIds} />
409417
}}
410418
/>
411419
<Route

src/components/app/details/metrics/DeploymentMetrics.tsx

Lines changed: 68 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -14,50 +14,55 @@
1414
* limitations under the License.
1515
*/
1616

17-
import React, { Component } from 'react'
17+
import React, { Component, useEffect } from 'react'
18+
import ReactGA from 'react-ga4'
19+
import { generatePath, useHistory, useParams, useRouteMatch } from 'react-router-dom'
20+
import Tippy from '@tippyjs/react'
21+
import moment from 'moment'
22+
import { Bar, BarChart, Label, Legend, ReferenceLine, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts'
23+
1824
import {
19-
showError,
20-
Progressing,
25+
EMPTY_STATE_STATUS,
2126
ErrorScreenManager,
2227
GenericEmptyState,
28+
Progressing,
2329
SelectPicker,
30+
showError,
2431
} from '@devtron-labs/devtron-fe-common-lib'
25-
import { generatePath } from 'react-router-dom'
26-
import { ResponsiveContainer, BarChart, Bar, XAxis, YAxis, Tooltip, Legend, Label, ReferenceLine } from 'recharts'
27-
import moment from 'moment'
28-
import Tippy from '@tippyjs/react'
29-
import ReactGA from 'react-ga4'
30-
import { getDeploymentMetrics } from './deploymentMetrics.service'
31-
import { DatePicker } from '../../../common'
32+
33+
import { ReactComponent as Success } from '@Icons/appstatus/healthy.svg'
34+
import { ReactComponent as Fail } from '@Icons/ic-error-exclamation.svg'
35+
import { ReactComponent as ICHelpOutline } from '@Icons/ic-help-outline.svg'
36+
import { ReactComponent as Deploy } from '@Icons/ic-nav-rocket.svg'
37+
38+
import AppNotDeployed from '@Images/app-not-deployed.svg'
39+
import SelectEnvImage from '@Images/[email protected]'
3240
import { ViewType } from '../../../../config'
33-
import { DeploymentTable } from './DeploymentTable'
3441
import { getAppOtherEnvironmentMin } from '../../../../services/service'
35-
import { DeploymentTableModal } from './DeploymentTableModal'
42+
import { DatePicker, useAppContext } from '../../../common'
3643
import { BenchmarkModal } from './BenchmarkModal'
44+
import { getDeploymentMetrics } from './deploymentMetrics.service'
45+
import { DeploymentMetricsProps, DeploymentMetricsState } from './deploymentMetrics.types'
3746
import {
3847
BenchmarkLine,
48+
EliteCategoryMessage,
49+
FailureLegendEmptyState,
50+
FrequencyTooltip,
3951
frequencyXAxisLabel,
52+
getGALabel,
53+
LeadTimeTooltip,
4054
leadTimeXAxisLabel,
4155
recoveryTimeLabel,
56+
RecoveryTimeTooltip,
4257
ReferenceLineLegend,
4358
renderCategoryTag,
44-
FrequencyTooltip,
45-
RecoveryTimeTooltip,
46-
LeadTimeTooltip,
47-
EliteCategoryMessage,
48-
FailureLegendEmptyState,
4959
} from './deploymentMetrics.util'
50-
import AppNotDeployed from '../../../../assets/img/app-not-deployed.svg'
51-
import SelectEnvImage from '../../../../assets/img/[email protected]'
52-
import { ReactComponent as ICHelpOutline } from '@Icons/ic-help-outline.svg'
53-
import { ReactComponent as Deploy } from '@Icons/ic-nav-rocket.svg'
54-
import { ReactComponent as Success } from '@Icons/appstatus/healthy.svg'
55-
import { ReactComponent as Fail } from '@Icons/ic-error-exclamation.svg'
60+
import { DeploymentTable } from './DeploymentTable'
61+
import { DeploymentTableModal } from './DeploymentTableModal'
62+
5663
import './deploymentMetrics.scss'
57-
import { DeploymentMetricsProps, DeploymentMetricsState } from './deploymentMetrics.types'
58-
import { EMPTY_STATE_STATUS } from '../../../../config/constantMessaging'
5964

60-
export default class DeploymentMetrics extends Component<DeploymentMetricsProps, DeploymentMetricsState> {
65+
class DeploymentMetricsComponent extends Component<DeploymentMetricsProps, DeploymentMetricsState> {
6166
constructor(props) {
6267
super(props)
6368

@@ -93,7 +98,6 @@ export default class DeploymentMetrics extends Component<DeploymentMetricsProps,
9398
endDate: undefined,
9499
},
95100
deploymentTableView: ViewType.FORM,
96-
filteredEnvironment: [],
97101
}
98102
this.handleDatesChange = this.handleDatesChange.bind(this)
99103
this.handleFocusChange = this.handleFocusChange.bind(this)
@@ -181,7 +185,6 @@ export default class DeploymentMetrics extends Component<DeploymentMetricsProps,
181185
allEnv.length && prevEnvId && (prevEnvId !== this.props.match.params.envId || !isEnvExist)
182186
this.setState({
183187
environments: allEnv,
184-
filteredEnvironment: allEnv,
185188
view: this.props.match.params.envId || redirectToNewUrl ? ViewType.LOADING : ViewType.FORM,
186189
})
187190
if (redirectToNewUrl) {
@@ -234,7 +237,7 @@ export default class DeploymentMetrics extends Component<DeploymentMetricsProps,
234237
ReactGA.event({
235238
category: 'Deployment Metrics',
236239
action: 'Deployment Status Filter Clicked',
237-
label: this.getGALabel(event.target.value),
240+
label: getGALabel(event.target.value),
238241
})
239242
this.setState({ statusFilter: Number(event.target.value), deploymentTableView: ViewType.LOADING }, () => {
240243
setTimeout(() => {
@@ -243,19 +246,6 @@ export default class DeploymentMetrics extends Component<DeploymentMetricsProps,
243246
})
244247
}
245248

246-
getGALabel(statusFilter) {
247-
switch (Number(statusFilter)) {
248-
case -1:
249-
return 'All'
250-
case 0:
251-
return 'Success'
252-
case 1:
253-
return 'Failed'
254-
default:
255-
return ''
256-
}
257-
}
258-
259249
renderInputs() {
260250
return (
261251
<div className="deployment-metrics__inputs bg__primary">
@@ -269,11 +259,11 @@ export default class DeploymentMetrics extends Component<DeploymentMetricsProps,
269259
onChange={(selected) => {
270260
this.handleEnvironmentChange(selected)
271261
}}
272-
options={this.state.filteredEnvironment.sort((a, b) => (a.label > b.label ? 1 : -1))}
262+
options={this.state.environments}
273263
/>
274264
</div>
275265
<div className="dc__align-right ">
276-
{this.props.match.params.envId ? (
266+
{this.state.selectedEnvironment ? (
277267
<DatePicker
278268
startDate={this.state.startDate}
279269
endDate={this.state.endDate}
@@ -631,21 +621,22 @@ export default class DeploymentMetrics extends Component<DeploymentMetricsProps,
631621
if (this.state.view === ViewType.FORM && this.state.environments.length === 0) {
632622
return this.renderNoEnvironmentView()
633623
}
634-
if (this.state.view === ViewType.FORM && !this.props.match.params.envId) {
624+
if (this.state.view === ViewType.FORM && (!this.props.match.params.envId || !(this.state.environments ?? []).find((env) => env.value === +this.props.match.params.envId))) {
635625
return this.renderSelectEnvironmentView()
636626
}
637627
if (this.state.view === ViewType.FORM && this.state.frequencyAndLeadTimeGraph.length === 0) {
638628
return this.renderEmptyState()
639629
}
640630

641-
let deploymentTableRows = this.state.rows
642-
if (this.state.statusFilter > -1) {
643-
deploymentTableRows = deploymentTableRows.filter((row) => {
644-
if (row.releaseStatus === this.state.statusFilter) {
645-
return row
646-
}
647-
})
648-
}
631+
const deploymentTableRows =
632+
this.state.statusFilter > -1
633+
? this.state.rows.filter((row) => {
634+
if (row.releaseStatus === this.state.statusFilter) {
635+
return row
636+
}
637+
})
638+
: this.state.rows
639+
649640
return (
650641
<div>
651642
{this.renderGraphs()}
@@ -703,7 +694,27 @@ export default class DeploymentMetrics extends Component<DeploymentMetricsProps,
703694
}
704695
}
705696

706-
export interface FrequencyGraphLegendProps {
697+
const DeploymentMetrics = (props: DeploymentMetricsProps) => {
698+
const { appId, envId } = useParams<{ appId: string; envId: string }>()
699+
const { environmentId, setEnvironmentId } = useAppContext()
700+
const { path } = useRouteMatch()
701+
const { replace } = useHistory()
702+
703+
useEffect(() => {
704+
if (envId && +envId !== environmentId) {
705+
setEnvironmentId(+envId)
706+
}
707+
if (!envId && environmentId) {
708+
replace(generatePath(path, { appId, envId: environmentId }))
709+
}
710+
}, [envId])
711+
712+
return <DeploymentMetricsComponent {...props} />
713+
}
714+
715+
export default DeploymentMetrics
716+
717+
interface FrequencyGraphLegendProps {
707718
noFailures: boolean
708719
label: string
709720
frequency: string
@@ -713,7 +724,7 @@ export interface FrequencyGraphLegendProps {
713724
setFrequencyMetric: (...args) => void
714725
setFailureMetric: (...args) => void
715726
}
716-
export class FrequencyGraphLegend extends React.Component<FrequencyGraphLegendProps, {}> {
727+
class FrequencyGraphLegend extends React.Component<FrequencyGraphLegendProps, {}> {
717728
render() {
718729
return (
719730
<div className="graph-legend">
@@ -789,15 +800,15 @@ export class FrequencyGraphLegend extends React.Component<FrequencyGraphLegendPr
789800
}
790801
}
791802

792-
export interface RecoveryAndLeadTimeGraphLegendProps {
803+
interface RecoveryAndLeadTimeGraphLegendProps {
793804
noFailures: boolean
794805
valueLabel: string
795806
label: string
796807
tooltipText: string
797808
benchmark: undefined | any
798809
setMetric: (...args) => void
799810
}
800-
export class RecoveryAndLeadTimeGraphLegend extends React.Component<RecoveryAndLeadTimeGraphLegendProps, {}> {
811+
class RecoveryAndLeadTimeGraphLegend extends React.Component<RecoveryAndLeadTimeGraphLegendProps, {}> {
801812
render() {
802813
if (this.props.noFailures) {
803814
return (

src/components/app/details/metrics/deploymentMetrics.types.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,5 +96,4 @@ export interface DeploymentMetricsState {
9696
endDate: undefined | Moment
9797
}
9898
deploymentTableView: string
99-
filteredEnvironment: Array<Environment>
10099
}

src/components/app/details/metrics/deploymentMetrics.util.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,3 +216,16 @@ export function getTimeperiod(timeInDays: number) {
216216
const timeInMinutes = 24 * 60 * (1 / timeInDays)
217217
return createTimestamp(timeInMinutes)
218218
}
219+
220+
export const getGALabel = (statusFilter) => {
221+
switch (Number(statusFilter)) {
222+
case -1:
223+
return 'All'
224+
case 0:
225+
return 'Success'
226+
case 1:
227+
return 'Failed'
228+
default:
229+
return ''
230+
}
231+
}

0 commit comments

Comments
 (0)