diff --git a/src/components/ApplicationGroup/AppGroup.types.ts b/src/components/ApplicationGroup/AppGroup.types.ts index 04c3116790..7d94283348 100644 --- a/src/components/ApplicationGroup/AppGroup.types.ts +++ b/src/components/ApplicationGroup/AppGroup.types.ts @@ -335,6 +335,7 @@ export interface CIConfigListType { } export interface AppGroupAppFilterContextType { + resourceId: string appListOptions: OptionType[] selectedAppList: MultiValue setSelectedAppList: React.Dispatch>> diff --git a/src/components/ApplicationGroup/AppGroup.utils.ts b/src/components/ApplicationGroup/AppGroup.utils.ts index 730e64afd9..55383b582b 100644 --- a/src/components/ApplicationGroup/AppGroup.utils.ts +++ b/src/components/ApplicationGroup/AppGroup.utils.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import React from 'react' import { ServerErrors, showError, @@ -24,6 +23,7 @@ import { WorkflowType, getIsRequestAborted, CIMaterialType, + OptionType, } from '@devtron-labs/devtron-fe-common-lib' import { DEFAULT_GIT_BRANCH_VALUE, DOCKER_FILE_ERROR_TITLE, SOURCE_NOT_CONFIGURED } from '../../config' import { getEnvAppList } from './AppGroup.service' @@ -32,8 +32,12 @@ import { CDWorkflowStatusType, CIWorkflowStatusType, ProcessWorkFlowStatusType, + FilterParentType, + GroupOptionType, } from './AppGroup.types' import { getParsedBranchValuesForPlugin } from '@Components/common' +import { MultiValue } from 'react-select' +import { APP_GROUP_LOCAL_STORAGE_KEY, ENV_GROUP_LOCAL_STORAGE_KEY } from './Constants' let timeoutId @@ -290,3 +294,25 @@ export const processConsequenceData = (data: BlockedStateData): ConsequenceType export const parseSearchParams = (searchParams: URLSearchParams) => ({ [AppGroupUrlFilters.cluster]: searchParams.getAll(AppGroupUrlFilters.cluster), }) + +export const setFilterInLocalStorage = ( + filterParentType: FilterParentType, + resourceId: string, + resourceList: MultiValue, + groupList: MultiValue, +) => { + const localStorageKey = + filterParentType === FilterParentType.app ? ENV_GROUP_LOCAL_STORAGE_KEY : APP_GROUP_LOCAL_STORAGE_KEY + const localStorageValue = localStorage.getItem(localStorageKey) + if (!localStorageValue) { + const resourceIdVsValuesMap = new Map() + resourceIdVsValuesMap.set(resourceId, [resourceList, groupList]) + // Set filter in local storage as Array from Map of resourceId vs [selectedAppList, selectedGroupFilter] + localStorage.setItem(localStorageKey, JSON.stringify(Array.from(resourceIdVsValuesMap))) + } else { + // update or set value for current resource + const localStoredMap = new Map(JSON.parse(localStorageValue)) + localStoredMap.set(resourceId, [resourceList, groupList]) + localStorage.setItem(localStorageKey, JSON.stringify(Array.from(localStoredMap))) + } +} diff --git a/src/components/ApplicationGroup/AppGroupAppFilter.components.tsx b/src/components/ApplicationGroup/AppGroupAppFilter.components.tsx index eeb98ef397..89321994f5 100644 --- a/src/components/ApplicationGroup/AppGroupAppFilter.components.tsx +++ b/src/components/ApplicationGroup/AppGroupAppFilter.components.tsx @@ -29,7 +29,7 @@ import { ReactComponent as Edit } from '../../assets/icons/ic-pencil.svg' import { ReactComponent as Trash } from '../../assets/icons/ic-delete-interactive.svg' import { ReactComponent as CheckIcon } from '../../assets/icons/ic-check.svg' import { AppGroupAppFilterContextType, FilterParentType } from './AppGroup.types' -import { AppFilterTabs } from './Constants' +import { APP_GROUP_LOCAL_STORAGE_KEY, AppFilterTabs, ENV_GROUP_LOCAL_STORAGE_KEY } from './Constants' import { ShortcutKeyBadge } from '@Components/common/formFields/Widgets/Widgets' export const ValueContainer = (props): JSX.Element => { @@ -182,6 +182,11 @@ export const MenuList = (props: any): JSX.Element => { const clearSelection = (): void => { setSelectedAppList([]) setSelectedGroupFilter([]) + if (filterParentType === FilterParentType.app) { + localStorage.setItem(ENV_GROUP_LOCAL_STORAGE_KEY, '') + } else { + localStorage.setItem(APP_GROUP_LOCAL_STORAGE_KEY, '') + } } const onTabChange = (e): void => { setSelectedFilterTab(e.currentTarget.dataset.selectedTab) diff --git a/src/components/ApplicationGroup/AppGroupAppFilter.tsx b/src/components/ApplicationGroup/AppGroupAppFilter.tsx index 5b8acdcd8e..718cade2a4 100644 --- a/src/components/ApplicationGroup/AppGroupAppFilter.tsx +++ b/src/components/ApplicationGroup/AppGroupAppFilter.tsx @@ -17,14 +17,15 @@ import { useEffect, useRef, useState } from 'react' import ReactSelect, { SelectInstance } from 'react-select' import { useAppGroupAppFilterContext } from './AppGroupDetailsRoute' -import { appGroupAppSelectorStyle } from './AppGroup.utils' +import { appGroupAppSelectorStyle, setFilterInLocalStorage } from './AppGroup.utils' import { AppGroupAppFilterContextType, FilterParentType } from './AppGroup.types' -import { AppFilterTabs } from './Constants' +import { APP_GROUP_LOCAL_STORAGE_KEY, AppFilterTabs, ENV_GROUP_LOCAL_STORAGE_KEY } from './Constants' import { MenuList, Option, ValueContainer } from './AppGroupAppFilter.components' import { OptionType, ReactSelectInputAction, useRegisterShortcut } from '@devtron-labs/devtron-fe-common-lib' export default function AppGroupAppFilter() { const { + resourceId, appListOptions, selectedAppList, setSelectedAppList, @@ -87,11 +88,14 @@ export default function AppGroupAppFilter() { if (selectedFilterTab === AppFilterTabs.APP_FILTER) { setSelectedAppList(selectedValue) setSelectedGroupFilter([]) + setFilterInLocalStorage(filterParentType, resourceId, selectedValue, []) } else { const _selectedGroup = selectedValue.pop() setSelectedGroupFilter([_selectedGroup]) if (_selectedGroup) { - setSelectedAppList(appListOptions.filter((app) => _selectedGroup.appIds.indexOf(+app.value) >= 0)) + const updatedAppList = appListOptions.filter((app) => _selectedGroup.appIds.indexOf(+app.value) >= 0) + setSelectedAppList(updatedAppList) + setFilterInLocalStorage(filterParentType, resourceId, updatedAppList, [_selectedGroup]) } } } @@ -108,6 +112,51 @@ export default function AppGroupAppFilter() { appGroupFilterRef.current.onMenuOpen() } + useEffect(() => { + if (!appListOptions || !groupFilterOptions) { + return + } + + const getAndSetItem = (localStorageKey: `${string}__filter`) => { + const localStorageValue = localStorage.getItem(localStorageKey) + if (!localStorageValue) { + return + } + const valueForCurrentResource = new Map(JSON.parse(localStorageValue)).get(resourceId) + // local storage value for app list/ env list + const localStorageResourceList = valueForCurrentResource?.[0] + // local storage value for group filter + const localStorageGroupList = valueForCurrentResource?.[1] + + const appListOptionsMap = appListOptions.reduce>((agg, curr) => { + agg[curr.value] = true + return agg + }, {}) + + const groupFilterOptionsMap = groupFilterOptions.reduce>((agg, curr) => { + agg[curr.value] = true + return agg + }, {}) + + // filtering local storage lists acc to new appList/ envList or groupFilterList as local values might be deleted or does not exist anymore + const filteredLocalStorageResourceList = localStorageResourceList?.filter(({value}) => appListOptionsMap[value]) + const filteredLocalStorageGroupList = localStorageGroupList?.filter(({value}) => groupFilterOptionsMap[value]) + + if (filteredLocalStorageResourceList) { + setSelectedAppList(filteredLocalStorageResourceList) + } + if (filteredLocalStorageGroupList) { + setSelectedGroupFilter(filteredLocalStorageGroupList) + } + } + + if (filterParentType === FilterParentType.app) { + getAndSetItem(ENV_GROUP_LOCAL_STORAGE_KEY) + } else { + getAndSetItem(APP_GROUP_LOCAL_STORAGE_KEY) + } + }, [appListOptions, groupFilterOptions]) + useEffect(() => { registerShortcut({ keys: ['F'], callback: handleFilterFocus }) diff --git a/src/components/ApplicationGroup/AppGroupDetailsRoute.tsx b/src/components/ApplicationGroup/AppGroupDetailsRoute.tsx index 4f9616f1fb..62283d65a0 100644 --- a/src/components/ApplicationGroup/AppGroupDetailsRoute.tsx +++ b/src/components/ApplicationGroup/AppGroupDetailsRoute.tsx @@ -505,6 +505,7 @@ export const EnvHeader = ({ const contextValue = useMemo( () => ({ + resourceId: envId, appListOptions, isMenuOpen, setMenuOpen, @@ -528,6 +529,7 @@ export const EnvHeader = ({ groupFilterOptions, selectedGroupFilter, isSuperAdmin, + envId, ], ) diff --git a/src/components/ApplicationGroup/Constants.ts b/src/components/ApplicationGroup/Constants.ts index 74168a604a..597562e40c 100644 --- a/src/components/ApplicationGroup/Constants.ts +++ b/src/components/ApplicationGroup/Constants.ts @@ -229,3 +229,6 @@ export const BULK_CI_BUILD_STATUS = (noOfApps) => ({ title: `Verifying selected code sources for ${noOfApps} Applications & initiating build pipelines Applications`, subTitle: 'It might take some time depending upon the number of applications', }) + +export const ENV_GROUP_LOCAL_STORAGE_KEY: `${string}__filter` = 'envGroup__filter' +export const APP_GROUP_LOCAL_STORAGE_KEY: `${string}__filter` = 'appGroup__filter' \ No newline at end of file diff --git a/src/components/app/details/AppHeader.tsx b/src/components/app/details/AppHeader.tsx index 6b4c4e9825..629215b966 100644 --- a/src/components/app/details/AppHeader.tsx +++ b/src/components/app/details/AppHeader.tsx @@ -59,6 +59,7 @@ export const AppHeader = ({ const contextValue = useMemo( () => ({ + resourceId: appId, appListOptions, isMenuOpen, setMenuOpen, @@ -82,6 +83,7 @@ export const AppHeader = ({ groupFilterOptions, selectedGroupFilter, isSuperAdmin, + appId, ], )