Skip to content

Commit 5affbfe

Browse files
committed
Merge branch 'develop' of github.com:devtron-labs/dashboard into fix/use-tabs
2 parents a554f60 + cad2de4 commit 5affbfe

29 files changed

+527
-339
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"private": true,
55
"homepage": "/dashboard",
66
"dependencies": {
7-
"@devtron-labs/devtron-fe-common-lib": "1.17.0-pre-8",
7+
"@devtron-labs/devtron-fe-common-lib": "1.17.0-pre-11",
88
"@esbuild-plugins/node-globals-polyfill": "0.2.3",
99
"@rjsf/core": "^5.13.3",
1010
"@rjsf/utils": "^5.13.3",

src/assets/img/img-page-blocked.webp

19.2 KB
Binary file not shown.

src/components/AppSelector/AppSelector.tsx

Lines changed: 28 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -15,84 +15,54 @@
1515
*/
1616

1717
import { useRef, useState } from 'react'
18-
import ReactGA from 'react-ga4'
1918
import { ActionMeta } from 'react-select'
2019

2120
import {
22-
AppSelectorNoOptionsMessage as appSelectorNoOptionsMessage,
23-
BaseAppMetaData,
24-
ComponentSizeType,
25-
getNoMatchingResultText,
21+
ContextSwitcher,
22+
handleAnalyticsEvent,
23+
RecentlyVisitedOptions,
2624
ResourceKindType,
27-
SelectPicker,
2825
SelectPickerOptionType,
2926
SelectPickerProps,
30-
SelectPickerVariantType,
3127
useAsync,
32-
UserPreferenceResourceActions,
3328
useUserPreferences,
3429
} from '@devtron-labs/devtron-fe-common-lib'
3530

36-
import { AppSelectorType, RecentlyVisitedOptions } from './AppSelector.types'
37-
import { appListOptions } from './AppSelectorUtil'
38-
import { APP_DETAILS_GA_EVENTS } from './constants'
31+
import { AppSelectorType } from './AppSelector.types'
32+
import { appListOptions, getAppSelectGAEvent } from './AppSelectorUtil'
3933

4034
const AppSelector = ({ onChange, appId, appName, isJobView }: AppSelectorType) => {
4135
const abortControllerRef = useRef<AbortController>(new AbortController())
36+
const isAppDataAvailable = !!appId && !!appName
4237

43-
const { userPreferences, fetchRecentlyVisitedParsedApps } = useUserPreferences({})
44-
const [inputValue, setInputValue] = useState('')
38+
const { recentlyVisitedResources } = useUserPreferences({
39+
recentlyVisitedFetchConfig: {
40+
id: appId,
41+
name: appName,
42+
resourceKind: isJobView ? ResourceKindType.job : ResourceKindType.devtronApplication,
43+
isDataAvailable: isAppDataAvailable,
44+
},
45+
})
4546

46-
const recentlyVisitedDevtronApps =
47-
userPreferences?.resources?.[ResourceKindType.devtronApplication]?.[
48-
UserPreferenceResourceActions.RECENTLY_VISITED
49-
] || ([] as BaseAppMetaData[])
47+
const [inputValue, setInputValue] = useState('')
5048

51-
const isAppDataAvailable = !!appId && !!appName
52-
const shouldFetchAppOptions = isJobView ? true : !!recentlyVisitedDevtronApps.length
49+
const shouldFetchAppOptions = !!recentlyVisitedResources.length
5350

54-
const [loading, selectOptions] = useAsync(
51+
const [loading, selectOptions, error, reload] = useAsync(
5552
() =>
5653
appListOptions({
5754
inputValue,
5855
isJobView,
5956
signal: abortControllerRef.current.signal,
60-
recentlyVisitedDevtronApps,
57+
recentlyVisitedResources,
6158
}),
62-
[inputValue, isJobView],
59+
[inputValue, appId, appName],
6360
isAppDataAvailable && shouldFetchAppOptions,
6461
)
65-
66-
// fetching recently visited apps only in case of devtron apps
67-
useAsync(
68-
() => fetchRecentlyVisitedParsedApps({ appId, appName }),
69-
[appId, appName],
70-
isAppDataAvailable && !isJobView,
71-
)
72-
7362
const onInputChange: SelectPickerProps['onInputChange'] = async (val) => {
7463
setInputValue(val)
7564
}
7665

77-
const customSelect: SelectPickerProps['filterOption'] = (option, searchText: string) => {
78-
const label = option.data.label as string
79-
return option.data.value === 0 || label.toLowerCase().includes(searchText.toLowerCase())
80-
}
81-
82-
const getDisabledOptions = (option: RecentlyVisitedOptions): SelectPickerProps['isDisabled'] => option.isDisabled
83-
84-
const noOptionsMessage = () =>
85-
isJobView
86-
? appSelectorNoOptionsMessage({
87-
inputValue,
88-
})
89-
: getNoMatchingResultText()
90-
91-
const _selectOption = selectOptions?.map((section) => ({
92-
...section,
93-
options: section.label === 'Recently Visited' ? section.options.slice(1) : section.options,
94-
}))
95-
9666
const handleChange = (
9767
selectedOption: RecentlyVisitedOptions,
9868
actionMeta: ActionMeta<SelectPickerOptionType<string | number>>,
@@ -101,30 +71,24 @@ const AppSelector = ({ onChange, appId, appName, isJobView }: AppSelectorType) =
10171

10272
onChange(selectedOption, actionMeta)
10373

104-
if (!isJobView) {
105-
ReactGA.event(
106-
selectedOption.isRecentlyVisited
107-
? APP_DETAILS_GA_EVENTS.RecentlyVisitedApps
108-
: APP_DETAILS_GA_EVENTS.SearchesAppClicked,
109-
)
110-
}
74+
handleAnalyticsEvent({
75+
category: isJobView ? 'Job Selector' : 'App Selector',
76+
action: getAppSelectGAEvent(selectedOption, isJobView),
77+
})
11178
}
11279

11380
return (
114-
<SelectPicker
115-
inputId={`${isJobView ? 'job' : 'app'}-name`}
116-
options={_selectOption || []}
81+
<ContextSwitcher
82+
inputId={`${isJobView ? `job-switcher-${appId}` : `app-switcher-${appId}`}`}
83+
options={selectOptions}
11784
inputValue={inputValue}
11885
onInputChange={onInputChange}
11986
isLoading={loading}
120-
noOptionsMessage={noOptionsMessage}
12187
onChange={handleChange}
12288
value={{ value: appId, label: appName }}
123-
variant={SelectPickerVariantType.BORDER_LESS}
12489
placeholder={appName}
125-
isOptionDisabled={getDisabledOptions}
126-
size={ComponentSizeType.xl}
127-
filterOption={customSelect}
90+
optionListError={error}
91+
reloadOptionList={reload}
12892
/>
12993
)
13094
}
Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
import { GroupBase } from 'react-select'
2-
3-
import { BaseAppMetaData, SelectPickerOptionType, SelectPickerProps } from '@devtron-labs/devtron-fe-common-lib'
1+
import { BaseRecentlyVisitedEntitiesTypes, SelectPickerProps } from '@devtron-labs/devtron-fe-common-lib'
42

53
import { AppHeaderType } from '@Components/app/types'
64

@@ -9,19 +7,20 @@ export interface AppSelectorType extends Pick<SelectPickerProps, 'onChange'>, Pi
97
isJobView?: boolean
108
}
119

12-
export interface RecentlyVisitedOptions extends SelectPickerOptionType<number> {
13-
isDisabled?: boolean
14-
isRecentlyVisited?: boolean
15-
}
16-
17-
export interface RecentlyVisitedGroupedOptionsType extends GroupBase<SelectPickerOptionType<number>> {
18-
label: string
19-
options: RecentlyVisitedOptions[]
20-
}
21-
2210
export interface AppListOptionsTypes {
2311
inputValue: string
2412
isJobView?: boolean
2513
signal?: AbortSignal
26-
recentlyVisitedDevtronApps?: BaseAppMetaData[] | []
14+
recentlyVisitedResources?: BaseRecentlyVisitedEntitiesTypes[] | []
15+
}
16+
17+
export interface ChartSelectorType {
18+
primaryKey: string // url match
19+
primaryValue: string
20+
matchedKeys: string[]
21+
api: (queryString?: string) => Promise<any>
22+
apiPrimaryKey?: string // primary key to generate map
23+
onChange?: ({ label, value }) => void
24+
formatOptionLabel?: ({ label, value, ...rest }) => React.ReactNode
25+
filterOption?: (option: any, searchString: string) => boolean
2726
}

src/components/AppSelector/AppSelectorUtil.tsx

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,26 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { BaseAppMetaData, getIsRequestAborted, ServerErrors, showError } from '@devtron-labs/devtron-fe-common-lib'
17+
import {
18+
BaseRecentlyVisitedEntitiesTypes,
19+
getIsRequestAborted,
20+
RecentlyVisitedGroupedOptionsType,
21+
RecentlyVisitedOptions,
22+
ServerErrors,
23+
showError,
24+
} from '@devtron-labs/devtron-fe-common-lib'
1825

1926
import { getAppListMin } from '../../services/service'
20-
import { AppListOptionsTypes, RecentlyVisitedGroupedOptionsType, RecentlyVisitedOptions } from './AppSelector.types'
21-
import { AllApplicationsMetaData } from './constants'
27+
import { AppListOptionsTypes } from './AppSelector.types'
28+
import { appSelectorGAEvents, getMinCharSearchPlaceholderGroup } from './constants'
2229

2330
let timeoutId
2431

2532
export const appListOptions = ({
2633
inputValue,
2734
isJobView = false,
2835
signal,
29-
recentlyVisitedDevtronApps,
36+
recentlyVisitedResources,
3037
}: AppListOptionsTypes): Promise<RecentlyVisitedGroupedOptionsType[]> => {
3138
const options = signal ? { signal } : null
3239

@@ -37,22 +44,22 @@ export const appListOptions = ({
3744
timeoutId = setTimeout(() => {
3845
if (inputValue.length < 3) {
3946
resolve(
40-
recentlyVisitedDevtronApps?.length && !isJobView
47+
recentlyVisitedResources?.length
4148
? [
4249
{
4350
label: 'Recently Visited',
44-
options: recentlyVisitedDevtronApps.map((app: BaseAppMetaData) => ({
45-
label: app.appName,
46-
value: app.appId,
51+
options: recentlyVisitedResources.map((app: BaseRecentlyVisitedEntitiesTypes) => ({
52+
label: app.name,
53+
value: app.id,
4754
isRecentlyVisited: true,
4855
})) as RecentlyVisitedOptions[],
4956
},
50-
AllApplicationsMetaData,
57+
getMinCharSearchPlaceholderGroup(isJobView ? 'Jobs' : 'Apps'),
5158
]
5259
: [],
5360
)
5461
} else {
55-
getAppListMin(null, options, inputValue, isJobView ?? false)
62+
getAppListMin(null, options, inputValue, isJobView)
5663
.then((response) => {
5764
const appList = response.result
5865
? ([
@@ -80,3 +87,16 @@ export const appListOptions = ({
8087
}, 300)
8188
})
8289
}
90+
91+
export const getAppSelectGAEvent = (selectedOption: RecentlyVisitedOptions, isJobView) => {
92+
if (isJobView) {
93+
if (selectedOption.isRecentlyVisited) {
94+
return appSelectorGAEvents.JOB_SWITCH_RECENTLY_VISITED_CLICKED
95+
}
96+
return appSelectorGAEvents.JOB_SWITCH_SEARCHED_ITEM_CLICKED
97+
}
98+
if (selectedOption.isRecentlyVisited) {
99+
return appSelectorGAEvents.DA_SWITCH_RECENTLY_VISITED_CLICKED
100+
}
101+
return appSelectorGAEvents.DA_SWITCH_SEARCHED_APP_CLICKED
102+
}

src/components/AppSelector/ChartSelector.tsx

Lines changed: 40 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,19 @@
1414
* limitations under the License.
1515
*/
1616

17-
import React from 'react'
1817
import { useParams, useHistory, generatePath, useRouteMatch } from 'react-router-dom'
19-
import Select from 'react-select'
20-
import { useAsync, APP_SELECTOR_STYLES, AppSelectorDropdownIndicator } from '@devtron-labs/devtron-fe-common-lib'
18+
import { GroupBase } from 'react-select'
19+
import {
20+
useAsync,
21+
SelectPickerOptionType,
22+
ContextSwitcher,
23+
Icon,
24+
handleAnalyticsEvent,
25+
} from '@devtron-labs/devtron-fe-common-lib'
2126
import { mapByKey } from '../common'
27+
import { ChartSelectorType } from './AppSelector.types'
28+
import { appSelectorGAEvents } from './constants'
2229

23-
interface ChartSelectorType {
24-
primaryKey: string // url match
25-
primaryValue: string
26-
matchedKeys: string[]
27-
api: () => Promise<any>
28-
apiPrimaryKey?: string // primary key to generate map
29-
onChange?: ({ label, value }) => void
30-
formatOptionLabel?: ({ label, value, ...rest }) => React.ReactNode
31-
filterOption?: (option: any, searchString: string) => boolean
32-
}
3330
export default function ChartSelector({
3431
primaryKey,
3532
primaryValue,
@@ -46,35 +43,51 @@ export default function ChartSelector({
4643
const params = useParams()
4744
const { push } = useHistory()
4845
const _primaryKey = Number(params[primaryKey])
49-
function selectApp(selected) {
46+
const selectApp = (selected) => {
5047
if (onChange) {
5148
onChange(selected)
5249
return
5350
}
51+
handleAnalyticsEvent({
52+
category: 'Chart Store',
53+
action: appSelectorGAEvents.CS_CHART_DETAIL_SWITCH_ITEM_CLICKED,
54+
})
5455
const keys = listMap.get(selected.value)
5556
const replacements = [...matchedKeys].reduce((agg, curr) => ({ ...agg, [curr]: keys[curr] }), {})
5657
const newUrl = generatePath(path, { ...replacements, [primaryKey]: selected.value })
5758
push(newUrl)
5859
}
60+
61+
const getChartsOptions = (): GroupBase<SelectPickerOptionType<string | number>>[] => [
62+
{
63+
label: 'All Charts',
64+
options:
65+
result?.result?.map((res) => ({
66+
value: res[apiPrimaryKey || primaryKey],
67+
label: res[primaryValue],
68+
description: res.chart_name || res.docker_artifact_store_id,
69+
endIcon: res.deprecated && <Icon name="ic-warning" color={null} size={16} />,
70+
})) || [],
71+
},
72+
]
73+
74+
const selectedChartLabel = listMap.has(_primaryKey) ? (listMap.get(_primaryKey)[primaryValue] as string) : ''
75+
5976
return (
60-
<Select
61-
options={result?.result?.map((res) => ({
62-
value: res[apiPrimaryKey || primaryKey],
63-
label: res[primaryValue],
64-
...res,
65-
}))}
77+
<ContextSwitcher
78+
inputId={`chart-switcher-${_primaryKey}`}
79+
options={getChartsOptions()}
80+
isLoading={loading}
81+
onChange={selectApp}
6682
value={{
6783
value: _primaryKey,
68-
label: listMap.has(_primaryKey) ? (listMap.get(_primaryKey)[primaryValue] as string) : '',
84+
label: selectedChartLabel,
6985
}}
86+
placeholder={selectedChartLabel}
7087
{...(formatOptionLabel ? { formatOptionLabel } : {})}
7188
{...(filterOption ? { filterOption } : {})}
72-
onChange={selectApp}
73-
components={{
74-
IndicatorSeparator: null,
75-
DropdownIndicator: AppSelectorDropdownIndicator,
76-
}}
77-
styles={APP_SELECTOR_STYLES}
89+
optionListError={error}
90+
reloadOptionList={reload}
7891
/>
7992
)
8093
}
Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
1-
import { RecentlyVisitedGroupedOptionsType } from './AppSelector.types'
1+
import { RecentlyVisitedGroupedOptionsType } from '@devtron-labs/devtron-fe-common-lib'
22

3-
export const AllApplicationsMetaData: RecentlyVisitedGroupedOptionsType = {
4-
label: 'All Applications',
3+
export const getMinCharSearchPlaceholderGroup = (resourceKind): RecentlyVisitedGroupedOptionsType => ({
4+
label: `All ${resourceKind}`,
55
options: [{ value: 0, label: 'Type 3 characters to search', isDisabled: true }],
6-
}
6+
})
77

8-
export const APP_DETAILS_GA_EVENTS = {
9-
RecentlyVisitedApps: {
10-
category: 'App Selector',
11-
action: 'DA_SWITCH_RECENTLY_VISITED_CLICKED',
12-
},
13-
SearchesAppClicked: {
14-
category: 'App Selector',
15-
action: 'DA_SWITCH_SEARCHED_APP_CLICKED',
16-
},
8+
export const appSelectorGAEvents = {
9+
DA_SWITCH_RECENTLY_VISITED_CLICKED: 'DA_SWITCH_RECENTLY_VISITED_CLICKED',
10+
DA_SWITCH_SEARCHED_APP_CLICKED: 'DA_SWITCH_SEARCHED_APP_CLICKED',
11+
JOB_SWITCH_RECENTLY_VISITED_CLICKED: 'JOB_SWITCH_RECENTLY_VISITED_CLICKED',
12+
JOB_SWITCH_SEARCHED_ITEM_CLICKED: 'JOB_SWITCH_SEARCHED_ITEM_CLICKED',
13+
CS_CHART_DETAIL_SWITCH_ITEM_CLICKED: 'CS_CHART_DETAIL_SWITCH_ITEM_CLICKED',
1714
}

0 commit comments

Comments
 (0)