diff --git a/package-lock.json b/package-lock.json index 67b691720..0c18c5229 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.17.0-pre-7", + "version": "1.17.0-pre-9", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.17.0-pre-7", + "version": "1.17.0-pre-9", "hasInstallScript": true, "license": "ISC", "dependencies": { diff --git a/package.json b/package.json index b9d607701..672a1a762 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.17.0-pre-7", + "version": "1.17.0-pre-9", "description": "Supporting common component library", "type": "module", "main": "dist/index.js", diff --git a/src/Shared/Components/ContextSwitcher/ContextSwitcher.tsx b/src/Shared/Components/ContextSwitcher/ContextSwitcher.tsx new file mode 100644 index 000000000..b6f89a159 --- /dev/null +++ b/src/Shared/Components/ContextSwitcher/ContextSwitcher.tsx @@ -0,0 +1,47 @@ +import { getNoMatchingResultText, SelectPicker, SelectPickerVariantType } from '@Shared/Components' +import { ComponentSizeType } from '@Shared/constants' + +import { ContextSwitcherTypes } from './types' +import { customSelectFilterOption, getDisabledOptions } from './utils' + +export const ContextSwitcher = ({ + inputId, + options = [], + inputValue, + onInputChange, + isLoading, + value, + onChange, + placeholder, + filterOption, + formatOptionLabel, + optionListError, + reloadOptionList, + classNamePrefix, +}: ContextSwitcherTypes) => { + const selectedOptions = options?.map((section) => ({ + ...section, + options: section?.label === 'Recently Visited' ? section.options?.slice(1) : section.options, + })) + return ( + <SelectPicker + inputId={inputId} + options={selectedOptions || []} + inputValue={inputValue} + onInputChange={onInputChange} + isLoading={isLoading} + noOptionsMessage={getNoMatchingResultText} + onChange={onChange} + value={value} + variant={SelectPickerVariantType.BORDER_LESS} + placeholder={placeholder} + isOptionDisabled={getDisabledOptions} + size={ComponentSizeType.xl} + filterOption={filterOption || customSelectFilterOption} + formatOptionLabel={formatOptionLabel} + optionListError={optionListError} + reloadOptionList={reloadOptionList} + classNamePrefix={classNamePrefix} + /> + ) +} diff --git a/src/Shared/Components/ContextSwitcher/index.ts b/src/Shared/Components/ContextSwitcher/index.ts new file mode 100644 index 000000000..43181ffa6 --- /dev/null +++ b/src/Shared/Components/ContextSwitcher/index.ts @@ -0,0 +1,3 @@ +export { ContextSwitcher } from './ContextSwitcher' +export type { ContextSwitcherTypes, RecentlyVisitedGroupedOptionsType, RecentlyVisitedOptions } from './types' +export { getMinCharSearchPlaceholderGroup } from './utils' diff --git a/src/Shared/Components/ContextSwitcher/types.ts b/src/Shared/Components/ContextSwitcher/types.ts new file mode 100644 index 000000000..8ba63b827 --- /dev/null +++ b/src/Shared/Components/ContextSwitcher/types.ts @@ -0,0 +1,33 @@ +import { GroupBase } from 'react-select' + +import { SelectPickerOptionType, SelectPickerProps } from '../SelectPicker' + +export interface ContextSwitcherTypes + extends Pick< + SelectPickerProps, + | 'placeholder' + | 'onChange' + | 'value' + | 'isLoading' + | 'onInputChange' + | 'inputValue' + | 'inputId' + | 'formatOptionLabel' + | 'filterOption' + | 'optionListError' + | 'reloadOptionList' + | 'classNamePrefix' + > { + options: GroupBase<SelectPickerOptionType<string | number>>[] + isAppDataAvailable?: boolean +} + +export interface RecentlyVisitedOptions extends SelectPickerOptionType<number> { + isDisabled?: boolean + isRecentlyVisited?: boolean +} + +export interface RecentlyVisitedGroupedOptionsType extends GroupBase<SelectPickerOptionType<number>> { + label: string + options: RecentlyVisitedOptions[] +} diff --git a/src/Shared/Components/ContextSwitcher/utils.ts b/src/Shared/Components/ContextSwitcher/utils.ts new file mode 100644 index 000000000..ca04e7c25 --- /dev/null +++ b/src/Shared/Components/ContextSwitcher/utils.ts @@ -0,0 +1,14 @@ +import { SelectPickerProps } from '../SelectPicker' +import { RecentlyVisitedGroupedOptionsType, RecentlyVisitedOptions } from './types' + +export const getDisabledOptions = (option: RecentlyVisitedOptions): SelectPickerProps['isDisabled'] => option.isDisabled + +export const customSelectFilterOption: SelectPickerProps['filterOption'] = (option, searchText: string) => { + const label = option.data.label as string + return option.data.value === 0 || label.toLowerCase().includes(searchText.toLowerCase()) +} + +export const getMinCharSearchPlaceholderGroup = (resourceKind: string): RecentlyVisitedGroupedOptionsType => ({ + label: `All ${resourceKind}`, + options: [{ value: 0, label: 'Type 3 characters to search', isDisabled: true }], +}) diff --git a/src/Shared/Components/DocLink/utils.tsx b/src/Shared/Components/DocLink/utils.tsx index 84a1c48a9..2124c59f4 100644 --- a/src/Shared/Components/DocLink/utils.tsx +++ b/src/Shared/Components/DocLink/utils.tsx @@ -19,7 +19,9 @@ export const getDocumentationUrl = <T extends boolean = false>({ return docPath } - const utmPath = !isLicenseDashboard ? `?utm-source=product_${isEnterprise ? 'ent' : 'oss'}` : '' + const utmPath = !isLicenseDashboard + ? `?utm_source=product_${isEnterprise ? 'ent' : 'oss'}&utm_medium=product_app&utm_campaign=docs_navigation` + : '' return `${DOCUMENTATION_HOME_PAGE}${DOCUMENTATION_VERSION}/${docPath || ''}${utmPath}` } diff --git a/src/Shared/Components/Header/ProfileMenu.tsx b/src/Shared/Components/Header/ProfileMenu.tsx index 1b84c759e..43cfa2bfa 100644 --- a/src/Shared/Components/Header/ProfileMenu.tsx +++ b/src/Shared/Components/Header/ProfileMenu.tsx @@ -57,7 +57,6 @@ export const ProfileMenu = ({ user, onClick }: ProfileMenuProps) => { <div className="flex dc__content-space dc__gap-8 px-8 py-6"> <div> <p className="m-0 fs-13 lh-1-5 fw-4 cn-9 dc__truncate">{user}</p> - <p className="m-0 fs-12 lh-1-5 fw-4 cn-7 dc__truncate">{user}</p> </div> {getAlphabetIcon(user, 'dc__no-shrink m-0-imp fs-16 icon-dim-36')} </div> diff --git a/src/Shared/Components/index.ts b/src/Shared/Components/index.ts index 15b5d93f0..1abcdae22 100644 --- a/src/Shared/Components/index.ts +++ b/src/Shared/Components/index.ts @@ -37,6 +37,7 @@ export * from './CollapsibleList' export * from './CommitChipCell' export * from './Confetti' export * from './ConfirmationModal' +export * from './ContextSwitcher' export * from './CountrySelect' export * from './CustomInput' export * from './DatePicker' diff --git a/src/Shared/Hooks/useUserPreferences/constants.ts b/src/Shared/Hooks/useUserPreferences/constants.ts index 5c55615cc..f77f48e07 100644 --- a/src/Shared/Hooks/useUserPreferences/constants.ts +++ b/src/Shared/Hooks/useUserPreferences/constants.ts @@ -14,4 +14,15 @@ * limitations under the License. */ +import { ResourceKindType } from '@Shared/types' + +import { PreferredResourceKindType } from './types' + export const USER_PREFERENCES_ATTRIBUTE_KEY = 'userPreferences' + +export const DEFAULT_RESOURCES_MAP: Record<PreferredResourceKindType, null> = { + [ResourceKindType.devtronApplication]: null, + [ResourceKindType.job]: null, + 'app-group': null, + [ResourceKindType.cluster]: null, +} diff --git a/src/Shared/Hooks/useUserPreferences/index.ts b/src/Shared/Hooks/useUserPreferences/index.ts index ccf3ce08b..461674b2a 100644 --- a/src/Shared/Hooks/useUserPreferences/index.ts +++ b/src/Shared/Hooks/useUserPreferences/index.ts @@ -15,6 +15,6 @@ */ export * from './constants' -export { getUserPreferences, updateUserPreferences } from './service' +export { getUserPreferences, updateAndPersistUserPreferences, updateUserPreferences } from './service' export * from './types' export { useUserPreferences } from './useUserPrefrences' diff --git a/src/Shared/Hooks/useUserPreferences/service.ts b/src/Shared/Hooks/useUserPreferences/service.ts index 88d38eab6..eb801cf98 100644 --- a/src/Shared/Hooks/useUserPreferences/service.ts +++ b/src/Shared/Hooks/useUserPreferences/service.ts @@ -17,22 +17,22 @@ import { ROUTES } from '@Common/Constants' import { get, getUrlWithSearchParams, patch, showError } from '@Common/index' import { THEME_PREFERENCE_MAP } from '@Shared/Providers/ThemeProvider/types' -import { BaseAppMetaData } from '@Shared/Services' -import { ResourceKindType } from '@Shared/types' import { USER_PREFERENCES_ATTRIBUTE_KEY } from './constants' import { GetUserPreferencesParsedDTO, GetUserPreferencesQueryParamsType, + PreferredResourceKindType, UpdateUserPreferencesPayloadType, UserPathValueMapType, + UserPreferenceFilteredListTypes, UserPreferenceResourceActions, UserPreferenceResourceProps, UserPreferencesPayloadValueType, UserPreferencesType, ViewIsPipelineRBACConfiguredRadioTabs, } from './types' -import { getUserPreferenceResourcesMetadata } from './utils' +import { getFilteredUniqueAppList, getParsedResourcesMap } from './utils' /** * @returns UserPreferencesType @@ -64,29 +64,45 @@ export const getUserPreferences = async (): Promise<UserPreferencesType> => { parsedResult.computedAppTheme === 'system-dark' || parsedResult.computedAppTheme === 'system-light' ? THEME_PREFERENCE_MAP.auto : parsedResult.computedAppTheme, - resources: { - [ResourceKindType.devtronApplication]: { - [UserPreferenceResourceActions.RECENTLY_VISITED]: - parsedResult.resources?.[ResourceKindType.devtronApplication]?.[ - UserPreferenceResourceActions.RECENTLY_VISITED - ] || ([] as BaseAppMetaData[]), - }, + resources: getParsedResourcesMap(parsedResult.resources), + } +} +/** + * Centralized function to build updated resources map + * This eliminates the need to manually update resources in multiple places + */ +const buildUpdatedResourcesMap = ( + existingResources: UserPreferencesType['resources'], + resourceKind: string, + value: any, +) => { + const baseResources = existingResources || {} + + return { + ...baseResources, + [resourceKind]: { + ...baseResources[resourceKind], + [UserPreferenceResourceActions.RECENTLY_VISITED]: Array.isArray(value) + ? value.map(({ id, name }) => ({ id, name })) + : [], }, } } /** - * @description This function updates the user preferences in the server. It constructs a payload with the updated user preferences and sends a PATCH request to the server. If the request is successful, it returns true. If an error occurs, it shows an error message and returns false. - * @param updatedUserPreferences - The updated user preferences to be sent to the server. - * @param recentlyVisitedDevtronApps - The recently visited Devtron apps to be sent to the server. - * @param shouldThrowError - A boolean indicating whether to throw an error if the request fails. Default is false. - * @returns A promise that resolves to true if the request is successful, or false if an error occurs. - * @throws Will throw an error if `shouldThrowError` is true and the request fails. + * @description This function updates the user preferences in the server. + * @param path - The path of the user preference to be updated. + * @param value - The value of the user preference to be updated. + * @param resourceKind - The resource kind to be updated. + * @param userPreferencesResponse - The current user preferences response. If not provided, it will be fetched from the server. + * @returns */ const getUserPreferencePayload = async ({ path, value, + resourceKind, + userPreferencesResponse, }: UserPathValueMapType): Promise<Partial<UserPreferencesPayloadValueType>> => { switch (path) { case 'themePreference': @@ -100,10 +116,11 @@ const getUserPreferencePayload = async ({ value.pipelineRBACViewSelectedTab === ViewIsPipelineRBACConfiguredRadioTabs.ACCESS_ONLY, } - case 'resources': + case 'resources': { return { - resources: getUserPreferenceResourcesMetadata(value as BaseAppMetaData[]), + resources: buildUpdatedResourcesMap(userPreferencesResponse?.resources, resourceKind, value), } + } default: return {} } @@ -112,12 +129,24 @@ const getUserPreferencePayload = async ({ export const updateUserPreferences = async ({ path, value, + resourceKind, shouldThrowError = false, + userPreferencesResponse, }: UserPreferenceResourceProps): Promise<boolean> => { try { + // If userPreferencesResponse is not provided, fetch current preferences + const currentUserPreferences = userPreferencesResponse || (await getUserPreferences()) + const payload: UpdateUserPreferencesPayloadType = { key: USER_PREFERENCES_ATTRIBUTE_KEY, - value: JSON.stringify(await getUserPreferencePayload({ path, value } as UserPathValueMapType)), + value: JSON.stringify( + await getUserPreferencePayload({ + path, + value, + resourceKind, + userPreferencesResponse: currentUserPreferences, + } as UserPathValueMapType), + ), } await patch(`${ROUTES.ATTRIBUTES_USER}/${ROUTES.PATCH}`, payload) @@ -131,3 +160,100 @@ export const updateUserPreferences = async ({ return false } } + +/** + * Optimized function to get updated user preferences with resource filtering + * Can work with provided userPreferences or fetch from server/localStorage + * Centralizes all resource updating logic + */ +export const getUpdatedUserPreferences = async ({ + id, + name, + resourceKind, + userPreferencesResponse, +}: UserPreferenceFilteredListTypes & { + userPreferencesResponse?: UserPreferencesType +}): Promise<UserPreferencesType> => { + // Get base user preferences from multiple sources (priority: provided > localStorage > server) + let baseUserPreferences: UserPreferencesType + + if (userPreferencesResponse) { + baseUserPreferences = userPreferencesResponse + } else { + try { + const localStorageData = localStorage.getItem('userPreferences') + if (localStorageData) { + baseUserPreferences = JSON.parse(localStorageData) + } else { + baseUserPreferences = await getUserPreferences() + } + } catch { + baseUserPreferences = await getUserPreferences() + } + } + + // If no resourceKind provided, return base preferences + if (!resourceKind) { + return baseUserPreferences + } + + // Get filtered unique apps using centralized logic + const uniqueFilteredApps = getFilteredUniqueAppList({ + userPreferencesResponse: baseUserPreferences, + id, + name, + resourceKind, + }) + + // Use centralized resource building function + const updatedResources = buildUpdatedResourcesMap(baseUserPreferences?.resources, resourceKind, uniqueFilteredApps) + + return { + ...baseUserPreferences, + resources: updatedResources, + } +} + +/** + * Centralized function to update and persist user preferences + * Handles both local state and server updates automatically + * Eliminates the need for manual resource management in multiple places + */ +export const updateAndPersistUserPreferences = async ({ + id, + name, + resourceKind, + shouldThrowError = false, + updateLocalStorage = true, +}: { + id?: number + name?: string + resourceKind?: PreferredResourceKindType + shouldThrowError?: boolean + updateLocalStorage?: boolean +}): Promise<UserPreferencesType> => { + // Get updated preferences with filtered resources + const updatedPreferences = await getUpdatedUserPreferences({ + id, + name, + resourceKind, + }) + + // Update localStorage if requested + if (updateLocalStorage) { + localStorage.setItem('userPreferences', JSON.stringify(updatedPreferences)) + } + + // Update server with the new resource list if resourceKind is provided + if (resourceKind && updatedPreferences.resources?.[resourceKind]) { + await updateUserPreferences({ + path: 'resources', + value: updatedPreferences.resources[resourceKind][UserPreferenceResourceActions.RECENTLY_VISITED], + resourceKind, + shouldThrowError, + userPreferencesResponse: updatedPreferences, + }) + } + + return updatedPreferences +} diff --git a/src/Shared/Hooks/useUserPreferences/types.ts b/src/Shared/Hooks/useUserPreferences/types.ts index 18d70f79e..4a4f19c17 100644 --- a/src/Shared/Hooks/useUserPreferences/types.ts +++ b/src/Shared/Hooks/useUserPreferences/types.ts @@ -16,7 +16,6 @@ import { USER_PREFERENCES_ATTRIBUTE_KEY } from '@Shared/Hooks/useUserPreferences/constants' import { AppThemeType, ThemeConfigType, ThemePreferenceType } from '@Shared/Providers/ThemeProvider/types' -import { BaseAppMetaData } from '@Shared/Services' import { ResourceKindType } from '@Shared/types' export interface GetUserPreferencesQueryParamsType { @@ -31,12 +30,24 @@ export enum ViewIsPipelineRBACConfiguredRadioTabs { export enum UserPreferenceResourceActions { RECENTLY_VISITED = 'recently-visited', } -export interface UserResourceKindActionType { - [UserPreferenceResourceActions.RECENTLY_VISITED]: BaseAppMetaData[] +export type PreferredResourceKindType = + | ResourceKindType.devtronApplication + | ResourceKindType.job + | 'app-group' + | ResourceKindType.cluster + +export interface BaseRecentlyVisitedEntitiesTypes { + id: number + name: string +} +export interface UserPreferenceRecentlyVisitedAppsTypes extends BaseRecentlyVisitedEntitiesTypes { + resourceKind: PreferredResourceKindType } -export interface UserPreferenceResourceType { - [ResourceKindType.devtronApplication]: UserResourceKindActionType + +export interface UserResourceKindActionType { + [UserPreferenceResourceActions.RECENTLY_VISITED]: BaseRecentlyVisitedEntitiesTypes[] } +export type UserPreferenceResourceType = Partial<Record<PreferredResourceKindType, UserResourceKindActionType>> export interface GetUserPreferencesParsedDTO { viewPermittedEnvOnly?: boolean /** @@ -75,33 +86,41 @@ export interface UserPreferencesType { export interface UpdatedUserPreferencesType extends UserPreferencesType, Pick<ThemeConfigType, 'appTheme'> {} +export interface RecentlyVisitedFetchConfigType extends UserPreferenceRecentlyVisitedAppsTypes { + isDataAvailable?: boolean +} + export interface UseUserPreferencesProps { + userPreferenceResourceKind?: PreferredResourceKindType migrateUserPreferences?: (userPreferencesResponse: UserPreferencesType) => Promise<UserPreferencesType> + recentlyVisitedFetchConfig?: RecentlyVisitedFetchConfigType } export type UserPathValueMapType = | { path: 'themePreference' value: Required<Pick<UpdatedUserPreferencesType, 'themePreference' | 'appTheme'>> + resourceKind?: never + userPreferencesResponse?: never } | { path: 'pipelineRBACViewSelectedTab' value: Required<Pick<UserPreferencesType, 'pipelineRBACViewSelectedTab'>> + resourceKind?: never + userPreferencesResponse?: never } | { path: 'resources' - value: Required<BaseAppMetaData[]> + value: Required<BaseRecentlyVisitedEntitiesTypes[]> + resourceKind: PreferredResourceKindType + userPreferencesResponse?: UserPreferencesType } export type UserPreferenceResourceProps = UserPathValueMapType & { shouldThrowError?: boolean -} - -export interface UserPreferenceRecentlyVisitedAppsTypes { - appId: number - appName: string + userPreferencesResponse?: UserPreferencesType } export interface UserPreferenceFilteredListTypes extends UserPreferenceRecentlyVisitedAppsTypes { - userPreferencesResponse: UserPreferencesType + userPreferencesResponse?: UserPreferencesType } diff --git a/src/Shared/Hooks/useUserPreferences/useUserPrefrences.tsx b/src/Shared/Hooks/useUserPreferences/useUserPrefrences.tsx index 9b108a0cd..3b8b08e07 100644 --- a/src/Shared/Hooks/useUserPreferences/useUserPrefrences.tsx +++ b/src/Shared/Hooks/useUserPreferences/useUserPrefrences.tsx @@ -16,48 +16,53 @@ import { useState } from 'react' +import { useAsync } from '@Common/Helper' import { ServerErrors } from '@Common/ServerError' import { useTheme } from '@Shared/Providers' -import { ResourceKindType } from '@Shared/types' -import { getUserPreferences, updateUserPreferences } from './service' +import { getUserPreferences, updateAndPersistUserPreferences } from './service' import { - UserPreferenceRecentlyVisitedAppsTypes, + BaseRecentlyVisitedEntitiesTypes, UserPreferenceResourceActions, UserPreferencesType, UseUserPreferencesProps, ViewIsPipelineRBACConfiguredRadioTabs, } from './types' -import { getFilteredUniqueAppList } from './utils' -export const useUserPreferences = ({ migrateUserPreferences }: UseUserPreferencesProps) => { +export const useUserPreferences = ({ migrateUserPreferences, recentlyVisitedFetchConfig }: UseUserPreferencesProps) => { const [userPreferences, setUserPreferences] = useState<UserPreferencesType>(null) const [userPreferencesError, setUserPreferencesError] = useState<ServerErrors>(null) - const { handleThemeSwitcherDialogVisibilityChange, handleThemePreferenceChange } = useTheme() + const { id, name, resourceKind, isDataAvailable } = recentlyVisitedFetchConfig ?? {} - const fetchRecentlyVisitedParsedApps = async ({ appId, appName }: UserPreferenceRecentlyVisitedAppsTypes) => { - const userPreferencesResponse = await getUserPreferences() + const { handleThemeSwitcherDialogVisibilityChange, handleThemePreferenceChange } = useTheme() - const uniqueFilteredApps = getFilteredUniqueAppList({ - userPreferencesResponse, - appId, - appName, + const fetchRecentlyVisitedParsedEntities = async (): Promise<UserPreferencesType> => { + // Use the optimized centralized function that handles everything + const updatedUserPreferences = await updateAndPersistUserPreferences({ + id, + name, + resourceKind, + updateLocalStorage: true, + shouldThrowError: false, }) - setUserPreferences((prev) => ({ - ...prev, - resources: { - ...prev?.resources, - [ResourceKindType.devtronApplication]: { - ...prev?.resources?.[ResourceKindType.devtronApplication], - [UserPreferenceResourceActions.RECENTLY_VISITED]: uniqueFilteredApps, - }, - }, - })) - await updateUserPreferences({ path: 'resources', value: uniqueFilteredApps }) + // Update local state + setUserPreferences(updatedUserPreferences) + + return updatedUserPreferences } + const [recentResourcesLoading, recentResourcesResult] = useAsync( + () => fetchRecentlyVisitedParsedEntities(), + [id, name], + isDataAvailable && !!resourceKind, + ) + + const recentlyVisitedResources = + recentResourcesResult?.resources?.[resourceKind]?.[UserPreferenceResourceActions.RECENTLY_VISITED] || + ([] as BaseRecentlyVisitedEntitiesTypes[]) + const handleInitializeUserPreferencesFromResponse = (userPreferencesResponse: UserPreferencesType) => { if (!userPreferencesResponse?.themePreference) { handleThemeSwitcherDialogVisibilityChange(true) @@ -103,6 +108,8 @@ export const useUserPreferences = ({ migrateUserPreferences }: UseUserPreference handleFetchUserPreferences, handleUpdatePipelineRBACViewSelectedTab, handleUpdateUserThemePreference, - fetchRecentlyVisitedParsedApps, + fetchRecentlyVisitedParsedEntities, + recentlyVisitedResources, + recentResourcesLoading, } } diff --git a/src/Shared/Hooks/useUserPreferences/utils.tsx b/src/Shared/Hooks/useUserPreferences/utils.tsx index 1e077272b..0d27875dd 100644 --- a/src/Shared/Hooks/useUserPreferences/utils.tsx +++ b/src/Shared/Hooks/useUserPreferences/utils.tsx @@ -1,36 +1,29 @@ -import { BaseAppMetaData } from '@Shared/Services' -import { ResourceKindType } from '@Shared/types' - -import { UserPreferenceFilteredListTypes, UserPreferenceResourceActions, UserPreferenceResourceType } from './types' - -export const getUserPreferenceResourcesMetadata = (recentlyVisited: BaseAppMetaData[]): UserPreferenceResourceType => ({ - [ResourceKindType.devtronApplication]: { - [UserPreferenceResourceActions.RECENTLY_VISITED]: recentlyVisited.map(({ appId, appName }) => ({ - appId, - appName, - })), - }, -}) +import { DEFAULT_RESOURCES_MAP } from './constants' +import { + GetUserPreferencesParsedDTO, + UserPreferenceFilteredListTypes, + UserPreferenceResourceActions, + UserPreferencesType, +} from './types' export const getFilteredUniqueAppList = ({ userPreferencesResponse, - appId, - appName, + id, + name, + resourceKind, }: UserPreferenceFilteredListTypes) => { const _recentApps = - userPreferencesResponse?.resources?.[ResourceKindType.devtronApplication]?.[ - UserPreferenceResourceActions.RECENTLY_VISITED - ] || [] + userPreferencesResponse?.resources?.[resourceKind]?.[UserPreferenceResourceActions.RECENTLY_VISITED] || [] - const isInvalidApp = appId && !appName + const isInvalidApp = id && !name - const validApps = _recentApps.filter((app) => { - if (!app?.appId || !app?.appName) { + const validEntities = _recentApps.filter((app) => { + if (!app?.id || !app.name) { return false } if (isInvalidApp) { - return app.appId !== appId + return app.id !== id } return true @@ -38,8 +31,20 @@ export const getFilteredUniqueAppList = ({ // Convert to a Map for uniqueness while maintaining stacking order const uniqueApps = ( - appId && appName ? [{ appId, appName }, ...validApps.filter((app) => app.appId !== appId)] : validApps + id && name ? [{ id, name }, ...validEntities.filter((entity) => entity.id !== id)] : validEntities ).slice(0, 6) // Limit to 6 items return uniqueApps } + +export const getParsedResourcesMap = ( + resources: GetUserPreferencesParsedDTO['resources'], +): UserPreferencesType['resources'] => { + const parsedResourcesMap: UserPreferencesType['resources'] = {} + + Object.keys(DEFAULT_RESOURCES_MAP).forEach((resourceKind) => { + parsedResourcesMap[resourceKind] = resources?.[resourceKind] + }) + + return parsedResourcesMap || {} +}