From 778d3ea3a7e41b69bf084b496676e1bc8549417e Mon Sep 17 00:00:00 2001 From: shivani170 Date: Thu, 19 Jun 2025 13:08:24 +0530 Subject: [PATCH 01/14] chore: Generalize user preferences as per resource kind --- .../Hooks/useUserPreferences/service.ts | 34 ++++++++++++++----- src/Shared/Hooks/useUserPreferences/types.ts | 20 +++++++---- .../useUserPreferences/useUserPrefrences.tsx | 20 ++++++----- src/Shared/Hooks/useUserPreferences/utils.tsx | 12 ++++--- 4 files changed, 57 insertions(+), 29 deletions(-) diff --git a/src/Shared/Hooks/useUserPreferences/service.ts b/src/Shared/Hooks/useUserPreferences/service.ts index 88d38eab6..6aac661dc 100644 --- a/src/Shared/Hooks/useUserPreferences/service.ts +++ b/src/Shared/Hooks/useUserPreferences/service.ts @@ -39,7 +39,11 @@ import { getUserPreferenceResourcesMetadata } from './utils' * @description This function fetches the user preferences from the server. It uses the `get` method to make a request to the server and retrieves the user preferences based on the `USER_PREFERENCES_ATTRIBUTE_KEY`. The result is parsed and returned as a `UserPreferencesType` object. * @throws Will throw an error if the request fails or if the result is not in the expected format. */ -export const getUserPreferences = async (): Promise => { +export const getUserPreferences = async ({ + resourceKindType, +}: { + resourceKindType: ResourceKindType +}): Promise => { const queryParamsPayload: Pick = { key: USER_PREFERENCES_ATTRIBUTE_KEY, } @@ -58,6 +62,23 @@ export const getUserPreferences = async (): Promise => { ? ViewIsPipelineRBACConfiguredRadioTabs.ACCESS_ONLY : ViewIsPipelineRBACConfiguredRadioTabs.ALL_ENVIRONMENTS + const getPreferredResourcesData = () => { + let resources + switch (resourceKindType) { + case ResourceKindType.devtronApplication: + resources = { + [UserPreferenceResourceActions.RECENTLY_VISITED]: + parsedResult.resources?.[ResourceKindType.devtronApplication]?.[ + UserPreferenceResourceActions.RECENTLY_VISITED + ] || ([] as BaseAppMetaData[]), + } + break + default: + resources = {} + } + return resources + } + return { pipelineRBACViewSelectedTab, themePreference: @@ -65,16 +86,10 @@ export const getUserPreferences = async (): Promise => { ? THEME_PREFERENCE_MAP.auto : parsedResult.computedAppTheme, resources: { - [ResourceKindType.devtronApplication]: { - [UserPreferenceResourceActions.RECENTLY_VISITED]: - parsedResult.resources?.[ResourceKindType.devtronApplication]?.[ - UserPreferenceResourceActions.RECENTLY_VISITED - ] || ([] as BaseAppMetaData[]), - }, + [resourceKindType]: getPreferredResourcesData(), }, } } - /** * @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. @@ -87,6 +102,7 @@ export const getUserPreferences = async (): Promise => { const getUserPreferencePayload = async ({ path, value, + resourceKind, }: UserPathValueMapType): Promise> => { switch (path) { case 'themePreference': @@ -102,7 +118,7 @@ const getUserPreferencePayload = async ({ case 'resources': return { - resources: getUserPreferenceResourcesMetadata(value as BaseAppMetaData[]), + resources: getUserPreferenceResourcesMetadata(value as BaseAppMetaData[], resourceKind), } default: return {} diff --git a/src/Shared/Hooks/useUserPreferences/types.ts b/src/Shared/Hooks/useUserPreferences/types.ts index 18d70f79e..14c185177 100644 --- a/src/Shared/Hooks/useUserPreferences/types.ts +++ b/src/Shared/Hooks/useUserPreferences/types.ts @@ -31,11 +31,19 @@ export enum ViewIsPipelineRBACConfiguredRadioTabs { export enum UserPreferenceResourceActions { RECENTLY_VISITED = 'recently-visited', } +export type PreferredResourceKindType = ResourceKindType.devtronApplication + +export interface UserPreferenceRecentlyVisitedAppsTypes { + appId: number + appName: string + resourceKind: PreferredResourceKindType +} + export interface UserResourceKindActionType { [UserPreferenceResourceActions.RECENTLY_VISITED]: BaseAppMetaData[] } -export interface UserPreferenceResourceType { - [ResourceKindType.devtronApplication]: UserResourceKindActionType +export type UserPreferenceResourceType = { + [key in ResourceKindType]?: UserResourceKindActionType } export interface GetUserPreferencesParsedDTO { viewPermittedEnvOnly?: boolean @@ -83,25 +91,23 @@ export type UserPathValueMapType = | { path: 'themePreference' value: Required> + resourceKind?: never } | { path: 'pipelineRBACViewSelectedTab' value: Required> + resourceKind?: never } | { path: 'resources' value: Required + resourceKind: PreferredResourceKindType } export type UserPreferenceResourceProps = UserPathValueMapType & { shouldThrowError?: boolean } -export interface UserPreferenceRecentlyVisitedAppsTypes { - appId: number - appName: string -} - export interface UserPreferenceFilteredListTypes extends UserPreferenceRecentlyVisitedAppsTypes { userPreferencesResponse: UserPreferencesType } diff --git a/src/Shared/Hooks/useUserPreferences/useUserPrefrences.tsx b/src/Shared/Hooks/useUserPreferences/useUserPrefrences.tsx index 9b108a0cd..8a4ebef91 100644 --- a/src/Shared/Hooks/useUserPreferences/useUserPrefrences.tsx +++ b/src/Shared/Hooks/useUserPreferences/useUserPrefrences.tsx @@ -18,7 +18,6 @@ import { useState } from 'react' import { ServerErrors } from '@Common/ServerError' import { useTheme } from '@Shared/Providers' -import { ResourceKindType } from '@Shared/types' import { getUserPreferences, updateUserPreferences } from './service' import { @@ -36,26 +35,31 @@ export const useUserPreferences = ({ migrateUserPreferences }: UseUserPreference const { handleThemeSwitcherDialogVisibilityChange, handleThemePreferenceChange } = useTheme() - const fetchRecentlyVisitedParsedApps = async ({ appId, appName }: UserPreferenceRecentlyVisitedAppsTypes) => { - const userPreferencesResponse = await getUserPreferences() + const fetchRecentlyVisitedParsedApps = async ({ + appId, + appName, + resourceKind, + }: UserPreferenceRecentlyVisitedAppsTypes) => { + const userPreferencesResponse = await getUserPreferences({ resourceKindType: resourceKind }) const uniqueFilteredApps = getFilteredUniqueAppList({ userPreferencesResponse, appId, appName, + resourceKind, }) setUserPreferences((prev) => ({ ...prev, resources: { ...prev?.resources, - [ResourceKindType.devtronApplication]: { - ...prev?.resources?.[ResourceKindType.devtronApplication], + [resourceKind]: { + ...prev?.resources?.[resourceKind], [UserPreferenceResourceActions.RECENTLY_VISITED]: uniqueFilteredApps, }, }, })) - await updateUserPreferences({ path: 'resources', value: uniqueFilteredApps }) + await updateUserPreferences({ path: 'resources', value: uniqueFilteredApps, resourceKind }) } const handleInitializeUserPreferencesFromResponse = (userPreferencesResponse: UserPreferencesType) => { @@ -67,10 +71,10 @@ export const useUserPreferences = ({ migrateUserPreferences }: UseUserPreference setUserPreferences(userPreferencesResponse) } - const handleFetchUserPreferences = async () => { + const handleFetchUserPreferences = async (resourceKind: UserPreferenceRecentlyVisitedAppsTypes['resourceKind']) => { try { setUserPreferencesError(null) - const userPreferencesResponse = await getUserPreferences() + const userPreferencesResponse = await getUserPreferences({ resourceKindType: resourceKind }) if (migrateUserPreferences) { const migratedUserPreferences = await migrateUserPreferences(userPreferencesResponse) handleInitializeUserPreferencesFromResponse(migratedUserPreferences) diff --git a/src/Shared/Hooks/useUserPreferences/utils.tsx b/src/Shared/Hooks/useUserPreferences/utils.tsx index 1e077272b..c1bce6705 100644 --- a/src/Shared/Hooks/useUserPreferences/utils.tsx +++ b/src/Shared/Hooks/useUserPreferences/utils.tsx @@ -3,8 +3,11 @@ import { ResourceKindType } from '@Shared/types' import { UserPreferenceFilteredListTypes, UserPreferenceResourceActions, UserPreferenceResourceType } from './types' -export const getUserPreferenceResourcesMetadata = (recentlyVisited: BaseAppMetaData[]): UserPreferenceResourceType => ({ - [ResourceKindType.devtronApplication]: { +export const getUserPreferenceResourcesMetadata = ( + recentlyVisited: BaseAppMetaData[], + resourceKind: ResourceKindType, +): UserPreferenceResourceType => ({ + [resourceKind]: { [UserPreferenceResourceActions.RECENTLY_VISITED]: recentlyVisited.map(({ appId, appName }) => ({ appId, appName, @@ -16,11 +19,10 @@ export const getFilteredUniqueAppList = ({ userPreferencesResponse, appId, appName, + resourceKind, }: UserPreferenceFilteredListTypes) => { const _recentApps = - userPreferencesResponse?.resources?.[ResourceKindType.devtronApplication]?.[ - UserPreferenceResourceActions.RECENTLY_VISITED - ] || [] + userPreferencesResponse?.resources?.[resourceKind]?.[UserPreferenceResourceActions.RECENTLY_VISITED] || [] const isInvalidApp = appId && !appName From dd3130a3fecb30c38a133bd5d9c06147399fde97 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Thu, 19 Jun 2025 13:46:43 +0530 Subject: [PATCH 02/14] chore: resource kind pass dynamically in payload --- src/Shared/Hooks/useUserPreferences/service.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Shared/Hooks/useUserPreferences/service.ts b/src/Shared/Hooks/useUserPreferences/service.ts index 6aac661dc..daebe0163 100644 --- a/src/Shared/Hooks/useUserPreferences/service.ts +++ b/src/Shared/Hooks/useUserPreferences/service.ts @@ -128,12 +128,15 @@ const getUserPreferencePayload = async ({ export const updateUserPreferences = async ({ path, value, + resourceKind, shouldThrowError = false, }: UserPreferenceResourceProps): Promise => { try { 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 } as UserPathValueMapType), + ), } await patch(`${ROUTES.ATTRIBUTES_USER}/${ROUTES.PATCH}`, payload) From a57ec47c7baeeb8c37719652c45dc5078676c12c Mon Sep 17 00:00:00 2001 From: shivani170 Date: Thu, 19 Jun 2025 16:06:02 +0530 Subject: [PATCH 03/14] chore: getUserPreferences props passed --- src/Shared/Hooks/useUserPreferences/service.ts | 4 ++-- src/Shared/Hooks/useUserPreferences/useUserPrefrences.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Shared/Hooks/useUserPreferences/service.ts b/src/Shared/Hooks/useUserPreferences/service.ts index daebe0163..a53e6b34d 100644 --- a/src/Shared/Hooks/useUserPreferences/service.ts +++ b/src/Shared/Hooks/useUserPreferences/service.ts @@ -42,7 +42,7 @@ import { getUserPreferenceResourcesMetadata } from './utils' export const getUserPreferences = async ({ resourceKindType, }: { - resourceKindType: ResourceKindType + resourceKindType?: ResourceKindType }): Promise => { const queryParamsPayload: Pick = { key: USER_PREFERENCES_ATTRIBUTE_KEY, @@ -86,7 +86,7 @@ export const getUserPreferences = async ({ ? THEME_PREFERENCE_MAP.auto : parsedResult.computedAppTheme, resources: { - [resourceKindType]: getPreferredResourcesData(), + [resourceKindType]: resourceKindType ? getPreferredResourcesData() : {}, }, } } diff --git a/src/Shared/Hooks/useUserPreferences/useUserPrefrences.tsx b/src/Shared/Hooks/useUserPreferences/useUserPrefrences.tsx index 8a4ebef91..0e62ce21a 100644 --- a/src/Shared/Hooks/useUserPreferences/useUserPrefrences.tsx +++ b/src/Shared/Hooks/useUserPreferences/useUserPrefrences.tsx @@ -71,10 +71,10 @@ export const useUserPreferences = ({ migrateUserPreferences }: UseUserPreference setUserPreferences(userPreferencesResponse) } - const handleFetchUserPreferences = async (resourceKind: UserPreferenceRecentlyVisitedAppsTypes['resourceKind']) => { + const handleFetchUserPreferences = async () => { try { setUserPreferencesError(null) - const userPreferencesResponse = await getUserPreferences({ resourceKindType: resourceKind }) + const userPreferencesResponse = await getUserPreferences({}) if (migrateUserPreferences) { const migratedUserPreferences = await migrateUserPreferences(userPreferencesResponse) handleInitializeUserPreferencesFromResponse(migratedUserPreferences) From 3d9334e7a1d69170154c6001d8c1f4ede943031b Mon Sep 17 00:00:00 2001 From: shivani170 Date: Tue, 24 Jun 2025 06:16:56 +0530 Subject: [PATCH 04/14] chore: version bump to 1.15.3-beta-3 --- package-lock.json | 4 +- package.json | 2 +- .../Hooks/useUserPreferences/service.ts | 55 +++++++++---------- src/Shared/Hooks/useUserPreferences/types.ts | 11 +++- .../useUserPreferences/useUserPrefrences.tsx | 11 +++- src/Shared/types.ts | 1 + 6 files changed, 48 insertions(+), 36 deletions(-) diff --git a/package-lock.json b/package-lock.json index 75466eab9..c71d97b45 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.15.3-pre-4", + "version": "1.15.3-beta-3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.15.3-pre-4", + "version": "1.15.3-beta-3", "hasInstallScript": true, "license": "ISC", "dependencies": { diff --git a/package.json b/package.json index be84c550e..2c660d715 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.15.3-pre-4", + "version": "1.15.3-beta-3", "description": "Supporting common component library", "type": "module", "main": "dist/index.js", diff --git a/src/Shared/Hooks/useUserPreferences/service.ts b/src/Shared/Hooks/useUserPreferences/service.ts index a53e6b34d..dea97f5b0 100644 --- a/src/Shared/Hooks/useUserPreferences/service.ts +++ b/src/Shared/Hooks/useUserPreferences/service.ts @@ -18,7 +18,6 @@ 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 { @@ -39,11 +38,7 @@ import { getUserPreferenceResourcesMetadata } from './utils' * @description This function fetches the user preferences from the server. It uses the `get` method to make a request to the server and retrieves the user preferences based on the `USER_PREFERENCES_ATTRIBUTE_KEY`. The result is parsed and returned as a `UserPreferencesType` object. * @throws Will throw an error if the request fails or if the result is not in the expected format. */ -export const getUserPreferences = async ({ - resourceKindType, -}: { - resourceKindType?: ResourceKindType -}): Promise => { +export const getUserPreferences = async (): Promise => { const queryParamsPayload: Pick = { key: USER_PREFERENCES_ATTRIBUTE_KEY, } @@ -62,32 +57,13 @@ export const getUserPreferences = async ({ ? ViewIsPipelineRBACConfiguredRadioTabs.ACCESS_ONLY : ViewIsPipelineRBACConfiguredRadioTabs.ALL_ENVIRONMENTS - const getPreferredResourcesData = () => { - let resources - switch (resourceKindType) { - case ResourceKindType.devtronApplication: - resources = { - [UserPreferenceResourceActions.RECENTLY_VISITED]: - parsedResult.resources?.[ResourceKindType.devtronApplication]?.[ - UserPreferenceResourceActions.RECENTLY_VISITED - ] || ([] as BaseAppMetaData[]), - } - break - default: - resources = {} - } - return resources - } - return { pipelineRBACViewSelectedTab, themePreference: parsedResult.computedAppTheme === 'system-dark' || parsedResult.computedAppTheme === 'system-light' ? THEME_PREFERENCE_MAP.auto : parsedResult.computedAppTheme, - resources: { - [resourceKindType]: resourceKindType ? getPreferredResourcesData() : {}, - }, + resources: parsedResult.resources, } } /** @@ -103,6 +79,7 @@ const getUserPreferencePayload = async ({ path, value, resourceKind, + userPreferencesResponse, }: UserPathValueMapType): Promise> => { switch (path) { case 'themePreference': @@ -116,10 +93,24 @@ const getUserPreferencePayload = async ({ value.pipelineRBACViewSelectedTab === ViewIsPipelineRBACConfiguredRadioTabs.ACCESS_ONLY, } - case 'resources': + case 'resources': { + const existingResources = userPreferencesResponse?.resources || {} + + const updatedResources = { + ...existingResources, + [resourceKind]: { + ...existingResources[resourceKind], + [UserPreferenceResourceActions.RECENTLY_VISITED]: + getUserPreferenceResourcesMetadata(value as BaseAppMetaData[], resourceKind)[resourceKind]?.[ + UserPreferenceResourceActions.RECENTLY_VISITED + ] || [], + }, + } + return { - resources: getUserPreferenceResourcesMetadata(value as BaseAppMetaData[], resourceKind), + resources: updatedResources, } + } default: return {} } @@ -130,12 +121,18 @@ export const updateUserPreferences = async ({ value, resourceKind, shouldThrowError = false, + userPreferencesResponse, }: UserPreferenceResourceProps): Promise => { try { const payload: UpdateUserPreferencesPayloadType = { key: USER_PREFERENCES_ATTRIBUTE_KEY, value: JSON.stringify( - await getUserPreferencePayload({ path, value, resourceKind } as UserPathValueMapType), + await getUserPreferencePayload({ + path, + value, + resourceKind, + userPreferencesResponse, + } as UserPathValueMapType), ), } diff --git a/src/Shared/Hooks/useUserPreferences/types.ts b/src/Shared/Hooks/useUserPreferences/types.ts index 14c185177..589c14795 100644 --- a/src/Shared/Hooks/useUserPreferences/types.ts +++ b/src/Shared/Hooks/useUserPreferences/types.ts @@ -31,7 +31,11 @@ export enum ViewIsPipelineRBACConfiguredRadioTabs { export enum UserPreferenceResourceActions { RECENTLY_VISITED = 'recently-visited', } -export type PreferredResourceKindType = ResourceKindType.devtronApplication +export type PreferredResourceKindType = + | ResourceKindType.devtronApplication + | ResourceKindType.job + | ResourceKindType.appGroup + | ResourceKindType.cluster export interface UserPreferenceRecentlyVisitedAppsTypes { appId: number @@ -84,6 +88,7 @@ export interface UserPreferencesType { export interface UpdatedUserPreferencesType extends UserPreferencesType, Pick {} export interface UseUserPreferencesProps { + userPreferenceResourceKind?: ResourceKindType migrateUserPreferences?: (userPreferencesResponse: UserPreferencesType) => Promise } @@ -92,20 +97,24 @@ export type UserPathValueMapType = path: 'themePreference' value: Required> resourceKind?: never + userPreferencesResponse?: never } | { path: 'pipelineRBACViewSelectedTab' value: Required> resourceKind?: never + userPreferencesResponse?: never } | { path: 'resources' value: Required resourceKind: PreferredResourceKindType + userPreferencesResponse? } export type UserPreferenceResourceProps = UserPathValueMapType & { shouldThrowError?: boolean + userPreferencesResponse? } export interface UserPreferenceFilteredListTypes extends UserPreferenceRecentlyVisitedAppsTypes { diff --git a/src/Shared/Hooks/useUserPreferences/useUserPrefrences.tsx b/src/Shared/Hooks/useUserPreferences/useUserPrefrences.tsx index 0e62ce21a..a4b116003 100644 --- a/src/Shared/Hooks/useUserPreferences/useUserPrefrences.tsx +++ b/src/Shared/Hooks/useUserPreferences/useUserPrefrences.tsx @@ -40,7 +40,7 @@ export const useUserPreferences = ({ migrateUserPreferences }: UseUserPreference appName, resourceKind, }: UserPreferenceRecentlyVisitedAppsTypes) => { - const userPreferencesResponse = await getUserPreferences({ resourceKindType: resourceKind }) + const userPreferencesResponse = await getUserPreferences() const uniqueFilteredApps = getFilteredUniqueAppList({ userPreferencesResponse, @@ -59,7 +59,12 @@ export const useUserPreferences = ({ migrateUserPreferences }: UseUserPreference }, }, })) - await updateUserPreferences({ path: 'resources', value: uniqueFilteredApps, resourceKind }) + await updateUserPreferences({ + path: 'resources', + value: uniqueFilteredApps, + resourceKind, + userPreferencesResponse, + }) } const handleInitializeUserPreferencesFromResponse = (userPreferencesResponse: UserPreferencesType) => { @@ -74,7 +79,7 @@ export const useUserPreferences = ({ migrateUserPreferences }: UseUserPreference const handleFetchUserPreferences = async () => { try { setUserPreferencesError(null) - const userPreferencesResponse = await getUserPreferences({}) + const userPreferencesResponse = await getUserPreferences() if (migrateUserPreferences) { const migratedUserPreferences = await migrateUserPreferences(userPreferencesResponse) handleInitializeUserPreferencesFromResponse(migratedUserPreferences) diff --git a/src/Shared/types.ts b/src/Shared/types.ts index 5c74fe3aa..b389c0e8b 100644 --- a/src/Shared/types.ts +++ b/src/Shared/types.ts @@ -499,6 +499,7 @@ export enum ResourceKindType { cdPipeline = 'cd-pipeline', ciPipeline = 'ci-pipeline', project = 'project', + appGroup = 'app-group', } /** From 51576c9633460f625e3637570ca57c8ab4e34de1 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Tue, 24 Jun 2025 15:08:47 +0530 Subject: [PATCH 05/14] chore: context switcher moved here --- .../ContextSwitcher/ContextSwitcher.tsx | 41 +++++++++++++++++++ .../Components/ContextSwitcher/index.ts | 3 ++ .../Components/ContextSwitcher/types.ts | 30 ++++++++++++++ .../Components/ContextSwitcher/utils.ts | 14 +++++++ src/Shared/Components/index.ts | 1 + .../Hooks/useUserPreferences/service.ts | 8 ++-- src/Shared/Hooks/useUserPreferences/types.ts | 12 +++--- .../useUserPreferences/useUserPrefrences.tsx | 4 +- src/Shared/Hooks/useUserPreferences/utils.tsx | 10 +++-- src/Shared/types.ts | 1 - 10 files changed, 107 insertions(+), 17 deletions(-) create mode 100644 src/Shared/Components/ContextSwitcher/ContextSwitcher.tsx create mode 100644 src/Shared/Components/ContextSwitcher/index.ts create mode 100644 src/Shared/Components/ContextSwitcher/types.ts create mode 100644 src/Shared/Components/ContextSwitcher/utils.ts diff --git a/src/Shared/Components/ContextSwitcher/ContextSwitcher.tsx b/src/Shared/Components/ContextSwitcher/ContextSwitcher.tsx new file mode 100644 index 000000000..e61a91dcb --- /dev/null +++ b/src/Shared/Components/ContextSwitcher/ContextSwitcher.tsx @@ -0,0 +1,41 @@ +import { getNoMatchingResultText, SelectPicker, SelectPickerVariantType } from '@Shared/Components' +import { ComponentSizeType } from '@Shared/constants' + +import { ContextSwitcherTypes } from './types' +import { customSelect, getDisabledOptions } from './utils' + +export const ContextSwitcher = ({ + inputId, + options = [], + inputValue, + onInputChange, + isLoading, + value, + onChange, + placeholder, + filterOption, + formatOptionLabel, +}: ContextSwitcherTypes) => { + const selectedOptions = options?.map((section) => ({ + ...section, + options: section?.label === 'Recently Visited' ? section.options?.slice(1) : section.options, + })) + return ( + + ) +} 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..10bf49f6f --- /dev/null +++ b/src/Shared/Components/ContextSwitcher/types.ts @@ -0,0 +1,30 @@ +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' + > { + options: GroupBase>[] + isAppDataAvailable?: boolean +} + +export interface RecentlyVisitedOptions extends SelectPickerOptionType { + isDisabled?: boolean + isRecentlyVisited?: boolean +} + +export interface RecentlyVisitedGroupedOptionsType extends GroupBase> { + 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..fa23bbda7 --- /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 customSelect: 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/index.ts b/src/Shared/Components/index.ts index b085ca5bc..c79d6a3df 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/service.ts b/src/Shared/Hooks/useUserPreferences/service.ts index dea97f5b0..5413eb016 100644 --- a/src/Shared/Hooks/useUserPreferences/service.ts +++ b/src/Shared/Hooks/useUserPreferences/service.ts @@ -17,10 +17,10 @@ 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 { USER_PREFERENCES_ATTRIBUTE_KEY } from './constants' import { + BaseRecentlyVisitedEntitiesTypes, GetUserPreferencesParsedDTO, GetUserPreferencesQueryParamsType, UpdateUserPreferencesPayloadType, @@ -101,9 +101,9 @@ const getUserPreferencePayload = async ({ [resourceKind]: { ...existingResources[resourceKind], [UserPreferenceResourceActions.RECENTLY_VISITED]: - getUserPreferenceResourcesMetadata(value as BaseAppMetaData[], resourceKind)[resourceKind]?.[ - UserPreferenceResourceActions.RECENTLY_VISITED - ] || [], + getUserPreferenceResourcesMetadata(value as BaseRecentlyVisitedEntitiesTypes[], resourceKind)[ + resourceKind + ]?.[UserPreferenceResourceActions.RECENTLY_VISITED] || [], }, } diff --git a/src/Shared/Hooks/useUserPreferences/types.ts b/src/Shared/Hooks/useUserPreferences/types.ts index 589c14795..394c645a7 100644 --- a/src/Shared/Hooks/useUserPreferences/types.ts +++ b/src/Shared/Hooks/useUserPreferences/types.ts @@ -34,7 +34,7 @@ export enum UserPreferenceResourceActions { export type PreferredResourceKindType = | ResourceKindType.devtronApplication | ResourceKindType.job - | ResourceKindType.appGroup + | 'app-group' | ResourceKindType.cluster export interface UserPreferenceRecentlyVisitedAppsTypes { @@ -46,9 +46,7 @@ export interface UserPreferenceRecentlyVisitedAppsTypes { export interface UserResourceKindActionType { [UserPreferenceResourceActions.RECENTLY_VISITED]: BaseAppMetaData[] } -export type UserPreferenceResourceType = { - [key in ResourceKindType]?: UserResourceKindActionType -} +export type UserPreferenceResourceType = Partial> export interface GetUserPreferencesParsedDTO { viewPermittedEnvOnly?: boolean /** @@ -88,7 +86,7 @@ export interface UserPreferencesType { export interface UpdatedUserPreferencesType extends UserPreferencesType, Pick {} export interface UseUserPreferencesProps { - userPreferenceResourceKind?: ResourceKindType + userPreferenceResourceKind?: PreferredResourceKindType migrateUserPreferences?: (userPreferencesResponse: UserPreferencesType) => Promise } @@ -109,12 +107,12 @@ export type UserPathValueMapType = path: 'resources' value: Required resourceKind: PreferredResourceKindType - userPreferencesResponse? + userPreferencesResponse?: UserPreferencesType } export type UserPreferenceResourceProps = UserPathValueMapType & { shouldThrowError?: boolean - userPreferencesResponse? + userPreferencesResponse?: UserPreferencesType } export interface UserPreferenceFilteredListTypes extends UserPreferenceRecentlyVisitedAppsTypes { diff --git a/src/Shared/Hooks/useUserPreferences/useUserPrefrences.tsx b/src/Shared/Hooks/useUserPreferences/useUserPrefrences.tsx index a4b116003..02498936c 100644 --- a/src/Shared/Hooks/useUserPreferences/useUserPrefrences.tsx +++ b/src/Shared/Hooks/useUserPreferences/useUserPrefrences.tsx @@ -35,7 +35,7 @@ export const useUserPreferences = ({ migrateUserPreferences }: UseUserPreference const { handleThemeSwitcherDialogVisibilityChange, handleThemePreferenceChange } = useTheme() - const fetchRecentlyVisitedParsedApps = async ({ + const fetchRecentlyVisitedParsedEntities = async ({ appId, appName, resourceKind, @@ -112,6 +112,6 @@ export const useUserPreferences = ({ migrateUserPreferences }: UseUserPreference handleFetchUserPreferences, handleUpdatePipelineRBACViewSelectedTab, handleUpdateUserThemePreference, - fetchRecentlyVisitedParsedApps, + fetchRecentlyVisitedParsedEntities, } } diff --git a/src/Shared/Hooks/useUserPreferences/utils.tsx b/src/Shared/Hooks/useUserPreferences/utils.tsx index c1bce6705..f3a56af20 100644 --- a/src/Shared/Hooks/useUserPreferences/utils.tsx +++ b/src/Shared/Hooks/useUserPreferences/utils.tsx @@ -1,11 +1,15 @@ import { BaseAppMetaData } from '@Shared/Services' -import { ResourceKindType } from '@Shared/types' -import { UserPreferenceFilteredListTypes, UserPreferenceResourceActions, UserPreferenceResourceType } from './types' +import { + PreferredResourceKindType, + UserPreferenceFilteredListTypes, + UserPreferenceResourceActions, + UserPreferenceResourceType, +} from './types' export const getUserPreferenceResourcesMetadata = ( recentlyVisited: BaseAppMetaData[], - resourceKind: ResourceKindType, + resourceKind: PreferredResourceKindType, ): UserPreferenceResourceType => ({ [resourceKind]: { [UserPreferenceResourceActions.RECENTLY_VISITED]: recentlyVisited.map(({ appId, appName }) => ({ diff --git a/src/Shared/types.ts b/src/Shared/types.ts index b389c0e8b..5c74fe3aa 100644 --- a/src/Shared/types.ts +++ b/src/Shared/types.ts @@ -499,7 +499,6 @@ export enum ResourceKindType { cdPipeline = 'cd-pipeline', ciPipeline = 'ci-pipeline', project = 'project', - appGroup = 'app-group', } /** From 92672b021846bdb158d54ad276ec64b0b14f2a99 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Tue, 24 Jun 2025 16:26:06 +0530 Subject: [PATCH 06/14] chore: recently visited apps & fetch from useUserPreferences --- .../Hooks/useUserPreferences/service.ts | 13 ++++- src/Shared/Hooks/useUserPreferences/types.ts | 18 ++++--- .../useUserPreferences/useUserPrefrences.tsx | 47 +++++++++++-------- src/Shared/Hooks/useUserPreferences/utils.tsx | 25 +++++----- 4 files changed, 64 insertions(+), 39 deletions(-) diff --git a/src/Shared/Hooks/useUserPreferences/service.ts b/src/Shared/Hooks/useUserPreferences/service.ts index 5413eb016..13d5b45ae 100644 --- a/src/Shared/Hooks/useUserPreferences/service.ts +++ b/src/Shared/Hooks/useUserPreferences/service.ts @@ -33,6 +33,17 @@ import { } from './types' import { getUserPreferenceResourcesMetadata } from './utils' +export const getParsedResourcesMap = (resources: GetUserPreferencesParsedDTO['resources']) => { + const resourcesMap = resources || {} + const parsedResourcesMap: UserPreferencesType['resources'] = {} + + Object.entries(resourcesMap).forEach(([resourceKind, resourceActions]) => { + parsedResourcesMap[resourceKind] = resourceActions + }) + + return parsedResourcesMap +} + /** * @returns UserPreferencesType * @description This function fetches the user preferences from the server. It uses the `get` method to make a request to the server and retrieves the user preferences based on the `USER_PREFERENCES_ATTRIBUTE_KEY`. The result is parsed and returned as a `UserPreferencesType` object. @@ -63,7 +74,7 @@ export const getUserPreferences = async (): Promise => { parsedResult.computedAppTheme === 'system-dark' || parsedResult.computedAppTheme === 'system-light' ? THEME_PREFERENCE_MAP.auto : parsedResult.computedAppTheme, - resources: parsedResult.resources, + resources: getParsedResourcesMap(parsedResult.resources), } } /** diff --git a/src/Shared/Hooks/useUserPreferences/types.ts b/src/Shared/Hooks/useUserPreferences/types.ts index 394c645a7..8b126a267 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 { @@ -37,14 +36,16 @@ export type PreferredResourceKindType = | 'app-group' | ResourceKindType.cluster -export interface UserPreferenceRecentlyVisitedAppsTypes { - appId: number - appName: string +export interface BaseRecentlyVisitedEntitiesTypes { + id: number + name: string +} +export interface UserPreferenceRecentlyVisitedAppsTypes extends BaseRecentlyVisitedEntitiesTypes { resourceKind: PreferredResourceKindType } export interface UserResourceKindActionType { - [UserPreferenceResourceActions.RECENTLY_VISITED]: BaseAppMetaData[] + [UserPreferenceResourceActions.RECENTLY_VISITED]: BaseRecentlyVisitedEntitiesTypes[] } export type UserPreferenceResourceType = Partial> export interface GetUserPreferencesParsedDTO { @@ -85,9 +86,14 @@ export interface UserPreferencesType { export interface UpdatedUserPreferencesType extends UserPreferencesType, Pick {} +export interface RecentlyVisitedFetchConfigType extends UserPreferenceRecentlyVisitedAppsTypes { + isDataAvailable?: boolean +} + export interface UseUserPreferencesProps { userPreferenceResourceKind?: PreferredResourceKindType migrateUserPreferences?: (userPreferencesResponse: UserPreferencesType) => Promise + recentlyVisitedFetchConfig?: RecentlyVisitedFetchConfigType } export type UserPathValueMapType = @@ -105,7 +111,7 @@ export type UserPathValueMapType = } | { path: 'resources' - value: Required + value: Required resourceKind: PreferredResourceKindType userPreferencesResponse?: UserPreferencesType } diff --git a/src/Shared/Hooks/useUserPreferences/useUserPrefrences.tsx b/src/Shared/Hooks/useUserPreferences/useUserPrefrences.tsx index 02498936c..98fa77cc3 100644 --- a/src/Shared/Hooks/useUserPreferences/useUserPrefrences.tsx +++ b/src/Shared/Hooks/useUserPreferences/useUserPrefrences.tsx @@ -16,12 +16,13 @@ import { useState } from 'react' +import { useAsync } from '@Common/Helper' import { ServerErrors } from '@Common/ServerError' import { useTheme } from '@Shared/Providers' import { getUserPreferences, updateUserPreferences } from './service' import { - UserPreferenceRecentlyVisitedAppsTypes, + BaseRecentlyVisitedEntitiesTypes, UserPreferenceResourceActions, UserPreferencesType, UseUserPreferencesProps, @@ -29,44 +30,51 @@ import { } from './types' import { getFilteredUniqueAppList } from './utils' -export const useUserPreferences = ({ migrateUserPreferences }: UseUserPreferencesProps) => { +export const useUserPreferences = ({ migrateUserPreferences, recentlyVisitedFetchConfig }: UseUserPreferencesProps) => { const [userPreferences, setUserPreferences] = useState(null) const [userPreferencesError, setUserPreferencesError] = useState(null) + const { id, name, resourceKind, isDataAvailable } = recentlyVisitedFetchConfig ?? {} + const { handleThemeSwitcherDialogVisibilityChange, handleThemePreferenceChange } = useTheme() - const fetchRecentlyVisitedParsedEntities = async ({ - appId, - appName, - resourceKind, - }: UserPreferenceRecentlyVisitedAppsTypes) => { + const fetchRecentlyVisitedParsedEntities = async (): Promise => { const userPreferencesResponse = await getUserPreferences() const uniqueFilteredApps = getFilteredUniqueAppList({ userPreferencesResponse, - appId, - appName, + id, + name, resourceKind, }) - setUserPreferences((prev) => ({ - ...prev, - resources: { - ...prev?.resources, - [resourceKind]: { - ...prev?.resources?.[resourceKind], - [UserPreferenceResourceActions.RECENTLY_VISITED]: uniqueFilteredApps, - }, - }, - })) await updateUserPreferences({ path: 'resources', value: uniqueFilteredApps, resourceKind, userPreferencesResponse, }) + + const updatedUserPreferences = { + ...userPreferencesResponse, + resources: { + ...userPreferencesResponse?.resources, + [resourceKind]: { + ...userPreferencesResponse?.resources?.[resourceKind], + [UserPreferenceResourceActions.RECENTLY_VISITED]: uniqueFilteredApps, + }, + }, + } + + return updatedUserPreferences } + const [, recentResourcesResult] = useAsync(() => fetchRecentlyVisitedParsedEntities(), [id, name], isDataAvailable) + + const recentlyVisitedResources = + recentResourcesResult?.resources?.[resourceKind]?.[UserPreferenceResourceActions.RECENTLY_VISITED] || + ([] as BaseRecentlyVisitedEntitiesTypes[]) + const handleInitializeUserPreferencesFromResponse = (userPreferencesResponse: UserPreferencesType) => { if (!userPreferencesResponse?.themePreference) { handleThemeSwitcherDialogVisibilityChange(true) @@ -113,5 +121,6 @@ export const useUserPreferences = ({ migrateUserPreferences }: UseUserPreference handleUpdatePipelineRBACViewSelectedTab, handleUpdateUserThemePreference, fetchRecentlyVisitedParsedEntities, + recentlyVisitedResources, } } diff --git a/src/Shared/Hooks/useUserPreferences/utils.tsx b/src/Shared/Hooks/useUserPreferences/utils.tsx index f3a56af20..834bf9279 100644 --- a/src/Shared/Hooks/useUserPreferences/utils.tsx +++ b/src/Shared/Hooks/useUserPreferences/utils.tsx @@ -1,6 +1,5 @@ -import { BaseAppMetaData } from '@Shared/Services' - import { + BaseRecentlyVisitedEntitiesTypes, PreferredResourceKindType, UserPreferenceFilteredListTypes, UserPreferenceResourceActions, @@ -8,35 +7,35 @@ import { } from './types' export const getUserPreferenceResourcesMetadata = ( - recentlyVisited: BaseAppMetaData[], + recentlyVisited: BaseRecentlyVisitedEntitiesTypes[], resourceKind: PreferredResourceKindType, ): UserPreferenceResourceType => ({ [resourceKind]: { - [UserPreferenceResourceActions.RECENTLY_VISITED]: recentlyVisited.map(({ appId, appName }) => ({ - appId, - appName, + [UserPreferenceResourceActions.RECENTLY_VISITED]: recentlyVisited.map(({ id, name }) => ({ + id, + name, })), }, }) export const getFilteredUniqueAppList = ({ userPreferencesResponse, - appId, - appName, + id, + name, resourceKind, }: UserPreferenceFilteredListTypes) => { const _recentApps = 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 @@ -44,7 +43,7 @@ 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 From f6c28c1b5626bc836c98bf616cbd8716a95dabad Mon Sep 17 00:00:00 2001 From: shivani170 Date: Wed, 25 Jun 2025 11:26:20 +0530 Subject: [PATCH 07/14] chore: handling error & reload in context switcher --- src/Shared/Components/ContextSwitcher/ContextSwitcher.tsx | 4 ++++ src/Shared/Components/ContextSwitcher/types.ts | 2 ++ 2 files changed, 6 insertions(+) diff --git a/src/Shared/Components/ContextSwitcher/ContextSwitcher.tsx b/src/Shared/Components/ContextSwitcher/ContextSwitcher.tsx index e61a91dcb..84be291c7 100644 --- a/src/Shared/Components/ContextSwitcher/ContextSwitcher.tsx +++ b/src/Shared/Components/ContextSwitcher/ContextSwitcher.tsx @@ -15,6 +15,8 @@ export const ContextSwitcher = ({ placeholder, filterOption, formatOptionLabel, + optionListError, + reloadOptionList, }: ContextSwitcherTypes) => { const selectedOptions = options?.map((section) => ({ ...section, @@ -36,6 +38,8 @@ export const ContextSwitcher = ({ size={ComponentSizeType.xl} filterOption={filterOption || customSelect} formatOptionLabel={formatOptionLabel} + optionListError={optionListError} + reloadOptionList={reloadOptionList} /> ) } diff --git a/src/Shared/Components/ContextSwitcher/types.ts b/src/Shared/Components/ContextSwitcher/types.ts index 10bf49f6f..aa3063e6f 100644 --- a/src/Shared/Components/ContextSwitcher/types.ts +++ b/src/Shared/Components/ContextSwitcher/types.ts @@ -14,6 +14,8 @@ export interface ContextSwitcherTypes | 'inputId' | 'formatOptionLabel' | 'filterOption' + | 'optionListError' + | 'reloadOptionList' > { options: GroupBase>[] isAppDataAvailable?: boolean From 6d0a5061841252d2322791e16b2f1aa1d5603bce Mon Sep 17 00:00:00 2001 From: shivani170 Date: Wed, 25 Jun 2025 16:53:41 +0530 Subject: [PATCH 08/14] chore: getParsedResourcesMap code refactoring --- src/Shared/Hooks/useUserPreferences/constants.ts | 11 +++++++++++ src/Shared/Hooks/useUserPreferences/service.ts | 11 ++++++----- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/Shared/Hooks/useUserPreferences/constants.ts b/src/Shared/Hooks/useUserPreferences/constants.ts index 5c55615cc..e868f5002 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 PreferredResourceKinds: PreferredResourceKindType[] = [ + ResourceKindType.devtronApplication, + ResourceKindType.job, + 'app-group', + ResourceKindType.cluster, +] diff --git a/src/Shared/Hooks/useUserPreferences/service.ts b/src/Shared/Hooks/useUserPreferences/service.ts index 13d5b45ae..2aad9f776 100644 --- a/src/Shared/Hooks/useUserPreferences/service.ts +++ b/src/Shared/Hooks/useUserPreferences/service.ts @@ -18,7 +18,7 @@ import { ROUTES } from '@Common/Constants' import { get, getUrlWithSearchParams, patch, showError } from '@Common/index' import { THEME_PREFERENCE_MAP } from '@Shared/Providers/ThemeProvider/types' -import { USER_PREFERENCES_ATTRIBUTE_KEY } from './constants' +import { PreferredResourceKinds, USER_PREFERENCES_ATTRIBUTE_KEY } from './constants' import { BaseRecentlyVisitedEntitiesTypes, GetUserPreferencesParsedDTO, @@ -33,12 +33,13 @@ import { } from './types' import { getUserPreferenceResourcesMetadata } from './utils' -export const getParsedResourcesMap = (resources: GetUserPreferencesParsedDTO['resources']) => { - const resourcesMap = resources || {} +export const getParsedResourcesMap = ( + resources: GetUserPreferencesParsedDTO['resources'], +): UserPreferencesType['resources'] => { const parsedResourcesMap: UserPreferencesType['resources'] = {} - Object.entries(resourcesMap).forEach(([resourceKind, resourceActions]) => { - parsedResourcesMap[resourceKind] = resourceActions + PreferredResourceKinds.forEach((resourceKind) => { + parsedResourcesMap[resourceKind] = resources?.[resourceKind] }) return parsedResourcesMap From ad82427cad58425aadc54a4e22263dc48e217697 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Wed, 25 Jun 2025 17:02:51 +0530 Subject: [PATCH 09/14] chore: classNamePrefix support added --- src/Shared/Components/ContextSwitcher/ContextSwitcher.tsx | 6 ++++-- src/Shared/Components/ContextSwitcher/types.ts | 1 + src/Shared/Components/ContextSwitcher/utils.ts | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Shared/Components/ContextSwitcher/ContextSwitcher.tsx b/src/Shared/Components/ContextSwitcher/ContextSwitcher.tsx index 84be291c7..b6f89a159 100644 --- a/src/Shared/Components/ContextSwitcher/ContextSwitcher.tsx +++ b/src/Shared/Components/ContextSwitcher/ContextSwitcher.tsx @@ -2,7 +2,7 @@ import { getNoMatchingResultText, SelectPicker, SelectPickerVariantType } from ' import { ComponentSizeType } from '@Shared/constants' import { ContextSwitcherTypes } from './types' -import { customSelect, getDisabledOptions } from './utils' +import { customSelectFilterOption, getDisabledOptions } from './utils' export const ContextSwitcher = ({ inputId, @@ -17,6 +17,7 @@ export const ContextSwitcher = ({ formatOptionLabel, optionListError, reloadOptionList, + classNamePrefix, }: ContextSwitcherTypes) => { const selectedOptions = options?.map((section) => ({ ...section, @@ -36,10 +37,11 @@ export const ContextSwitcher = ({ placeholder={placeholder} isOptionDisabled={getDisabledOptions} size={ComponentSizeType.xl} - filterOption={filterOption || customSelect} + filterOption={filterOption || customSelectFilterOption} formatOptionLabel={formatOptionLabel} optionListError={optionListError} reloadOptionList={reloadOptionList} + classNamePrefix={classNamePrefix} /> ) } diff --git a/src/Shared/Components/ContextSwitcher/types.ts b/src/Shared/Components/ContextSwitcher/types.ts index aa3063e6f..8ba63b827 100644 --- a/src/Shared/Components/ContextSwitcher/types.ts +++ b/src/Shared/Components/ContextSwitcher/types.ts @@ -16,6 +16,7 @@ export interface ContextSwitcherTypes | 'filterOption' | 'optionListError' | 'reloadOptionList' + | 'classNamePrefix' > { options: GroupBase>[] isAppDataAvailable?: boolean diff --git a/src/Shared/Components/ContextSwitcher/utils.ts b/src/Shared/Components/ContextSwitcher/utils.ts index fa23bbda7..ca04e7c25 100644 --- a/src/Shared/Components/ContextSwitcher/utils.ts +++ b/src/Shared/Components/ContextSwitcher/utils.ts @@ -3,7 +3,7 @@ import { RecentlyVisitedGroupedOptionsType, RecentlyVisitedOptions } from './typ export const getDisabledOptions = (option: RecentlyVisitedOptions): SelectPickerProps['isDisabled'] => option.isDisabled -export const customSelect: SelectPickerProps['filterOption'] = (option, searchText: string) => { +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()) } From 8fc790b2f1fd7e46b8a517ac7a2728f5c595e232 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Thu, 26 Jun 2025 12:52:26 +0530 Subject: [PATCH 10/14] chore: removed resource kind spreading from update resource --- src/Shared/Hooks/useUserPreferences/service.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Shared/Hooks/useUserPreferences/service.ts b/src/Shared/Hooks/useUserPreferences/service.ts index 2aad9f776..d796aa4b8 100644 --- a/src/Shared/Hooks/useUserPreferences/service.ts +++ b/src/Shared/Hooks/useUserPreferences/service.ts @@ -109,7 +109,6 @@ const getUserPreferencePayload = async ({ const existingResources = userPreferencesResponse?.resources || {} const updatedResources = { - ...existingResources, [resourceKind]: { ...existingResources[resourceKind], [UserPreferenceResourceActions.RECENTLY_VISITED]: From 5319e27f865a283f51106ae05c05404f28075999 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Thu, 26 Jun 2025 16:23:42 +0530 Subject: [PATCH 11/14] chore: payload fixes --- src/Shared/Hooks/useUserPreferences/service.ts | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/Shared/Hooks/useUserPreferences/service.ts b/src/Shared/Hooks/useUserPreferences/service.ts index d796aa4b8..767ac8509 100644 --- a/src/Shared/Hooks/useUserPreferences/service.ts +++ b/src/Shared/Hooks/useUserPreferences/service.ts @@ -25,7 +25,6 @@ import { GetUserPreferencesQueryParamsType, UpdateUserPreferencesPayloadType, UserPathValueMapType, - UserPreferenceResourceActions, UserPreferenceResourceProps, UserPreferencesPayloadValueType, UserPreferencesType, @@ -42,7 +41,7 @@ export const getParsedResourcesMap = ( parsedResourcesMap[resourceKind] = resources?.[resourceKind] }) - return parsedResourcesMap + return parsedResourcesMap || {} } /** @@ -91,7 +90,6 @@ const getUserPreferencePayload = async ({ path, value, resourceKind, - userPreferencesResponse, }: UserPathValueMapType): Promise> => { switch (path) { case 'themePreference': @@ -106,15 +104,11 @@ const getUserPreferencePayload = async ({ } case 'resources': { - const existingResources = userPreferencesResponse?.resources || {} - const updatedResources = { [resourceKind]: { - ...existingResources[resourceKind], - [UserPreferenceResourceActions.RECENTLY_VISITED]: - getUserPreferenceResourcesMetadata(value as BaseRecentlyVisitedEntitiesTypes[], resourceKind)[ - resourceKind - ]?.[UserPreferenceResourceActions.RECENTLY_VISITED] || [], + ...getUserPreferenceResourcesMetadata(value as BaseRecentlyVisitedEntitiesTypes[], resourceKind)[ + resourceKind + ], }, } From d41abb6f489d534124d13bb38dd0ae3b5b170f49 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Thu, 26 Jun 2025 17:44:26 +0530 Subject: [PATCH 12/14] chore: getParsedResourcesMap fixes --- src/Shared/Hooks/useUserPreferences/constants.ts | 12 ++++++------ src/Shared/Hooks/useUserPreferences/service.ts | 16 ++-------------- src/Shared/Hooks/useUserPreferences/utils.tsx | 15 +++++++++++++++ 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/Shared/Hooks/useUserPreferences/constants.ts b/src/Shared/Hooks/useUserPreferences/constants.ts index e868f5002..f77f48e07 100644 --- a/src/Shared/Hooks/useUserPreferences/constants.ts +++ b/src/Shared/Hooks/useUserPreferences/constants.ts @@ -20,9 +20,9 @@ import { PreferredResourceKindType } from './types' export const USER_PREFERENCES_ATTRIBUTE_KEY = 'userPreferences' -export const PreferredResourceKinds: PreferredResourceKindType[] = [ - ResourceKindType.devtronApplication, - ResourceKindType.job, - 'app-group', - ResourceKindType.cluster, -] +export const DEFAULT_RESOURCES_MAP: Record = { + [ResourceKindType.devtronApplication]: null, + [ResourceKindType.job]: null, + 'app-group': null, + [ResourceKindType.cluster]: null, +} diff --git a/src/Shared/Hooks/useUserPreferences/service.ts b/src/Shared/Hooks/useUserPreferences/service.ts index 767ac8509..84dccb813 100644 --- a/src/Shared/Hooks/useUserPreferences/service.ts +++ b/src/Shared/Hooks/useUserPreferences/service.ts @@ -18,7 +18,7 @@ import { ROUTES } from '@Common/Constants' import { get, getUrlWithSearchParams, patch, showError } from '@Common/index' import { THEME_PREFERENCE_MAP } from '@Shared/Providers/ThemeProvider/types' -import { PreferredResourceKinds, USER_PREFERENCES_ATTRIBUTE_KEY } from './constants' +import { USER_PREFERENCES_ATTRIBUTE_KEY } from './constants' import { BaseRecentlyVisitedEntitiesTypes, GetUserPreferencesParsedDTO, @@ -30,19 +30,7 @@ import { UserPreferencesType, ViewIsPipelineRBACConfiguredRadioTabs, } from './types' -import { getUserPreferenceResourcesMetadata } from './utils' - -export const getParsedResourcesMap = ( - resources: GetUserPreferencesParsedDTO['resources'], -): UserPreferencesType['resources'] => { - const parsedResourcesMap: UserPreferencesType['resources'] = {} - - PreferredResourceKinds.forEach((resourceKind) => { - parsedResourcesMap[resourceKind] = resources?.[resourceKind] - }) - - return parsedResourcesMap || {} -} +import { getParsedResourcesMap, getUserPreferenceResourcesMetadata } from './utils' /** * @returns UserPreferencesType diff --git a/src/Shared/Hooks/useUserPreferences/utils.tsx b/src/Shared/Hooks/useUserPreferences/utils.tsx index 834bf9279..e4f6a5aef 100644 --- a/src/Shared/Hooks/useUserPreferences/utils.tsx +++ b/src/Shared/Hooks/useUserPreferences/utils.tsx @@ -1,9 +1,12 @@ +import { DEFAULT_RESOURCES_MAP } from './constants' import { BaseRecentlyVisitedEntitiesTypes, + GetUserPreferencesParsedDTO, PreferredResourceKindType, UserPreferenceFilteredListTypes, UserPreferenceResourceActions, UserPreferenceResourceType, + UserPreferencesType, } from './types' export const getUserPreferenceResourcesMetadata = ( @@ -48,3 +51,15 @@ export const getFilteredUniqueAppList = ({ 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 || {} +} From 8c09454451956df0d61787508416d50ddb6a830e Mon Sep 17 00:00:00 2001 From: shivani170 Date: Tue, 1 Jul 2025 13:13:12 +0530 Subject: [PATCH 13/14] chore: locally saved user preferences payload --- src/Shared/Hooks/useUserPreferences/service.ts | 15 ++++++++++----- .../useUserPreferences/useUserPrefrences.tsx | 5 ++++- src/Shared/Hooks/useUserPreferences/utils.tsx | 15 --------------- 3 files changed, 14 insertions(+), 21 deletions(-) diff --git a/src/Shared/Hooks/useUserPreferences/service.ts b/src/Shared/Hooks/useUserPreferences/service.ts index 84dccb813..5d22ec6c2 100644 --- a/src/Shared/Hooks/useUserPreferences/service.ts +++ b/src/Shared/Hooks/useUserPreferences/service.ts @@ -20,17 +20,17 @@ import { THEME_PREFERENCE_MAP } from '@Shared/Providers/ThemeProvider/types' import { USER_PREFERENCES_ATTRIBUTE_KEY } from './constants' import { - BaseRecentlyVisitedEntitiesTypes, GetUserPreferencesParsedDTO, GetUserPreferencesQueryParamsType, UpdateUserPreferencesPayloadType, UserPathValueMapType, + UserPreferenceResourceActions, UserPreferenceResourceProps, UserPreferencesPayloadValueType, UserPreferencesType, ViewIsPipelineRBACConfiguredRadioTabs, } from './types' -import { getParsedResourcesMap, getUserPreferenceResourcesMetadata } from './utils' +import { getParsedResourcesMap } from './utils' /** * @returns UserPreferencesType @@ -78,6 +78,7 @@ const getUserPreferencePayload = async ({ path, value, resourceKind, + userPreferencesResponse, }: UserPathValueMapType): Promise> => { switch (path) { case 'themePreference': @@ -92,11 +93,15 @@ const getUserPreferencePayload = async ({ } case 'resources': { + const existingResources = userPreferencesResponse?.resources || {} const updatedResources = { + ...existingResources, [resourceKind]: { - ...getUserPreferenceResourcesMetadata(value as BaseRecentlyVisitedEntitiesTypes[], resourceKind)[ - resourceKind - ], + ...existingResources[resourceKind], + [UserPreferenceResourceActions.RECENTLY_VISITED]: value.map(({ id, name }) => ({ + id, + name, + })), }, } diff --git a/src/Shared/Hooks/useUserPreferences/useUserPrefrences.tsx b/src/Shared/Hooks/useUserPreferences/useUserPrefrences.tsx index 98fa77cc3..85a545acb 100644 --- a/src/Shared/Hooks/useUserPreferences/useUserPrefrences.tsx +++ b/src/Shared/Hooks/useUserPreferences/useUserPrefrences.tsx @@ -39,7 +39,8 @@ export const useUserPreferences = ({ migrateUserPreferences, recentlyVisitedFetc const { handleThemeSwitcherDialogVisibilityChange, handleThemePreferenceChange } = useTheme() const fetchRecentlyVisitedParsedEntities = async (): Promise => { - const userPreferencesResponse = await getUserPreferences() + // Retrieve and parse the user's saved preferences from local storage + const userPreferencesResponse = JSON.parse(localStorage.getItem('userPreferences')) const uniqueFilteredApps = getFilteredUniqueAppList({ userPreferencesResponse, @@ -66,6 +67,8 @@ export const useUserPreferences = ({ migrateUserPreferences, recentlyVisitedFetc }, } + localStorage.setItem('userPreferences', JSON.stringify(updatedUserPreferences)) + return updatedUserPreferences } diff --git a/src/Shared/Hooks/useUserPreferences/utils.tsx b/src/Shared/Hooks/useUserPreferences/utils.tsx index e4f6a5aef..489a7bd34 100644 --- a/src/Shared/Hooks/useUserPreferences/utils.tsx +++ b/src/Shared/Hooks/useUserPreferences/utils.tsx @@ -1,26 +1,11 @@ import { DEFAULT_RESOURCES_MAP } from './constants' import { - BaseRecentlyVisitedEntitiesTypes, GetUserPreferencesParsedDTO, - PreferredResourceKindType, UserPreferenceFilteredListTypes, UserPreferenceResourceActions, - UserPreferenceResourceType, UserPreferencesType, } from './types' -export const getUserPreferenceResourcesMetadata = ( - recentlyVisited: BaseRecentlyVisitedEntitiesTypes[], - resourceKind: PreferredResourceKindType, -): UserPreferenceResourceType => ({ - [resourceKind]: { - [UserPreferenceResourceActions.RECENTLY_VISITED]: recentlyVisited.map(({ id, name }) => ({ - id, - name, - })), - }, -}) - export const getFilteredUniqueAppList = ({ userPreferencesResponse, id, From 6ebd364fe9a17cae28a5a01425cfc4dfdd4a3340 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Tue, 1 Jul 2025 14:07:56 +0530 Subject: [PATCH 14/14] chore: undefined user preferences handling --- .../useUserPreferences/useUserPrefrences.tsx | 36 ++++++++++++++----- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/src/Shared/Hooks/useUserPreferences/useUserPrefrences.tsx b/src/Shared/Hooks/useUserPreferences/useUserPrefrences.tsx index 85a545acb..19054e394 100644 --- a/src/Shared/Hooks/useUserPreferences/useUserPrefrences.tsx +++ b/src/Shared/Hooks/useUserPreferences/useUserPrefrences.tsx @@ -42,6 +42,10 @@ export const useUserPreferences = ({ migrateUserPreferences, recentlyVisitedFetc // Retrieve and parse the user's saved preferences from local storage const userPreferencesResponse = JSON.parse(localStorage.getItem('userPreferences')) + if (!resourceKind) { + return userPreferencesResponse + } + const uniqueFilteredApps = getFilteredUniqueAppList({ userPreferencesResponse, id, @@ -49,13 +53,6 @@ export const useUserPreferences = ({ migrateUserPreferences, recentlyVisitedFetc resourceKind, }) - await updateUserPreferences({ - path: 'resources', - value: uniqueFilteredApps, - resourceKind, - userPreferencesResponse, - }) - const updatedUserPreferences = { ...userPreferencesResponse, resources: { @@ -66,13 +63,33 @@ export const useUserPreferences = ({ migrateUserPreferences, recentlyVisitedFetc }, }, } - localStorage.setItem('userPreferences', JSON.stringify(updatedUserPreferences)) + setUserPreferences((prev) => ({ + ...prev, + resources: { + ...prev?.resources, + [resourceKind]: { + ...prev?.resources?.[resourceKind], + [UserPreferenceResourceActions.RECENTLY_VISITED]: uniqueFilteredApps, + }, + }, + })) + await updateUserPreferences({ + path: 'resources', + value: uniqueFilteredApps, + resourceKind, + userPreferencesResponse, + }) + return updatedUserPreferences } - const [, recentResourcesResult] = useAsync(() => fetchRecentlyVisitedParsedEntities(), [id, name], isDataAvailable) + const [recentResourcesLoading, recentResourcesResult] = useAsync( + () => fetchRecentlyVisitedParsedEntities(), + [id, name], + isDataAvailable && !!resourceKind, + ) const recentlyVisitedResources = recentResourcesResult?.resources?.[resourceKind]?.[UserPreferenceResourceActions.RECENTLY_VISITED] || @@ -125,5 +142,6 @@ export const useUserPreferences = ({ migrateUserPreferences, recentlyVisitedFetc handleUpdateUserThemePreference, fetchRecentlyVisitedParsedEntities, recentlyVisitedResources, + recentResourcesLoading, } }