diff --git a/apps/web/src/api-clients/add-ons/favorite/schema/list.ts b/apps/web/src/api-clients/add-ons/favorite/schema/list.ts index 58ace2baa1..602911ee80 100644 --- a/apps/web/src/api-clients/add-ons/favorite/schema/list.ts +++ b/apps/web/src/api-clients/add-ons/favorite/schema/list.ts @@ -1,7 +1,7 @@ import type { ListResponse } from '@/api-clients/_common/schema/api-verbs/list'; import type { FavoriteModel } from '@/api-clients/add-ons/favorite/schema/model'; -import type { FavoriteType } from '@/common/modules/favorites/favorite-button/type'; +import type { FavoriteType } from '@/common/modules/user-config/favorite/favorite-button/type'; export interface FavoriteListParameters { type: FavoriteType; diff --git a/apps/web/src/api-clients/add-ons/favorite/schema/model.ts b/apps/web/src/api-clients/add-ons/favorite/schema/model.ts index 1959b6e297..8cb7c843bc 100644 --- a/apps/web/src/api-clients/add-ons/favorite/schema/model.ts +++ b/apps/web/src/api-clients/add-ons/favorite/schema/model.ts @@ -1,6 +1,6 @@ import type { UserConfigModel } from '@/api-clients/config/user-config/schema/model'; -import type { FavoriteType } from '@/common/modules/favorites/favorite-button/type'; +import type { FavoriteType } from '@/common/modules/user-config/favorite/favorite-button/type'; export type FavoriteModel = UserConfigModel<{ workspace_id: string; diff --git a/apps/web/src/api-clients/config/user-config/composables/use-user-config-api.ts b/apps/web/src/api-clients/config/user-config/composables/use-user-config-api.ts index 2f25742c73..4e266668a9 100644 --- a/apps/web/src/api-clients/config/user-config/composables/use-user-config-api.ts +++ b/apps/web/src/api-clients/config/user-config/composables/use-user-config-api.ts @@ -16,7 +16,7 @@ export const useUserConfigApi = () => { delete: SpaceConnector.clientV2.config.userConfig.delete, get: >(params: UserConfigGetParameters) => SpaceConnector.clientV2.config.userConfig.get>(params), list: >(params: UserConfigListParameters) => SpaceConnector.clientV2.config.userConfig.list>>(params), - set: SpaceConnector.clientV2.config.userConfig.set, + set: >(params: UserConfigSetParameters) => SpaceConnector.clientV2.config.userConfig.set>(params), }; return { diff --git a/apps/web/src/api-clients/identity/user-profile/composables/use-user-profile-api.ts b/apps/web/src/api-clients/identity/user-profile/composables/use-user-profile-api.ts index ca74db5831..4122ee08d0 100644 --- a/apps/web/src/api-clients/identity/user-profile/composables/use-user-profile-api.ts +++ b/apps/web/src/api-clients/identity/user-profile/composables/use-user-profile-api.ts @@ -1,5 +1,6 @@ import { SpaceConnector } from '@cloudforet/core-lib/space-connector'; +import type { ListResponse } from '@/api-clients/_common/schema/api-verbs/list'; import type { UserProfileConfirmEmailParameters } from '@/api-clients/identity/user-profile/schema/api-verbs/confirm-email'; import type { UserProfileConfirmMfaParameters } from '@/api-clients/identity/user-profile/schema/api-verbs/confirm-mfa'; import type { UserProfileDisableMfaParameters } from '@/api-clients/identity/user-profile/schema/api-verbs/disable-mfa'; @@ -10,7 +11,7 @@ import type { UserProfileUpdateParameters } from '@/api-clients/identity/user-pr import type { UserProfileUpdatePasswordParameters } from '@/api-clients/identity/user-profile/schema/api-verbs/update-password'; import type { UserProfileVerifyEmailParameters } from '@/api-clients/identity/user-profile/schema/api-verbs/verify-email'; import type { UserModel } from '@/api-clients/identity/user/schema/model'; - +import type { WorkspaceModel } from '@/api-clients/identity/workspace/schema/model'; export const useUserProfileApi = () => { @@ -23,7 +24,7 @@ export const useUserProfileApi = () => { disableMfa: SpaceConnector.clientV2.identity.userProfile.disableMfa, enableMfa: SpaceConnector.clientV2.identity.userProfile.enableMfa, confirmMfa: SpaceConnector.clientV2.identity.userProfile.confirmMfa, - getWorkspaces: SpaceConnector.clientV2.identity.userProfile.getWorkspaces, + getWorkspaces: SpaceConnector.clientV2.identity.userProfile.getWorkspaces>, getWorkspaceGroups: SpaceConnector.clientV2.identity.userProfile.getWorkspaceGroups, }; diff --git a/apps/web/src/common/modules/favorites/favorite-button/FavoriteButton.vue b/apps/web/src/common/modules/favorites/favorite-button/FavoriteButton.vue deleted file mode 100644 index 24f50751af..0000000000 --- a/apps/web/src/common/modules/favorites/favorite-button/FavoriteButton.vue +++ /dev/null @@ -1,194 +0,0 @@ - - - - - diff --git a/apps/web/src/common/modules/navigations/gnb/GNBToolbox.vue b/apps/web/src/common/modules/navigations/gnb/GNBToolbox.vue index 48e6b0a6ec..3928815f55 100644 --- a/apps/web/src/common/modules/navigations/gnb/GNBToolbox.vue +++ b/apps/web/src/common/modules/navigations/gnb/GNBToolbox.vue @@ -24,12 +24,11 @@ import type { MenuId } from '@/lib/menu/config'; import { MENU_ID } from '@/lib/menu/config'; import { useBreadcrumbs } from '@/common/composables/breadcrumbs'; -import FavoriteButton from '@/common/modules/favorites/favorite-button/FavoriteButton.vue'; -import { useFavoriteStore } from '@/common/modules/favorites/favorite-button/store/favorite-store'; -import { FAVORITE_TYPE } from '@/common/modules/favorites/favorite-button/type'; -import type { FavoriteOptions } from '@/common/modules/favorites/favorite-button/type'; import { useGnbStore } from '@/common/modules/navigations/stores/gnb-store'; import type { Breadcrumb } from '@/common/modules/page-layouts/type'; +import FavoriteButton from '@/common/modules/user-config/favorite/favorite-button/FavoriteButton.vue'; +import { FAVORITE_TYPE } from '@/common/modules/user-config/favorite/favorite-button/type'; +import type { FavoriteOptions } from '@/common/modules/user-config/favorite/favorite-button/type'; import { LANDING_ROUTE } from '@/services/landing/routes/route-constant'; import { MY_PAGE_ROUTE } from '@/services/my-page/routes/route-constant'; @@ -38,7 +37,6 @@ const userWorkspaceStore = useUserWorkspaceStore(); const userWorkspaceGetters = userWorkspaceStore.getters; const gnbStore = useGnbStore(); const gnbGetters = gnbStore.getters; -const favoriteStore = useFavoriteStore(); const appContextStore = useAppContextStore(); const appContextGetters = appContextStore.getters; @@ -112,7 +110,6 @@ const handleClickBreadcrumbsDropdownItem = (item: MenuItem) => { watch(() => state.selectedMenuId, async (selectedMenuId) => { await gnbStore.initState(); - await favoriteStore.fetchFavorite(); if (selectedMenuId === MENU_ID.COST_ANALYSIS) { await gnbStore.fetchCostQuerySet(); diff --git a/apps/web/src/common/modules/navigations/lsb/modules/LSBRouterMenuItem.vue b/apps/web/src/common/modules/navigations/lsb/modules/LSBRouterMenuItem.vue index 3c91aafac5..51c7dc123a 100644 --- a/apps/web/src/common/modules/navigations/lsb/modules/LSBRouterMenuItem.vue +++ b/apps/web/src/common/modules/navigations/lsb/modules/LSBRouterMenuItem.vue @@ -18,9 +18,9 @@ import type { MenuId } from '@/lib/menu/config'; import BetaMark from '@/common/components/marks/BetaMark.vue'; import NewMark from '@/common/components/marks/NewMark.vue'; import UpdateMark from '@/common/components/marks/UpdateMark.vue'; -import FavoriteButton from '@/common/modules/favorites/favorite-button/FavoriteButton.vue'; -import { FAVORITE_TYPE } from '@/common/modules/favorites/favorite-button/type'; import type { LSBIcon, LSBItem } from '@/common/modules/navigations/lsb/type'; +import FavoriteButton from '@/common/modules/user-config/favorite/favorite-button/FavoriteButton.vue'; +import { FAVORITE_TYPE } from '@/common/modules/user-config/favorite/favorite-button/type'; interface Props { item: LSBItem; diff --git a/apps/web/src/common/modules/navigations/lsb/type.ts b/apps/web/src/common/modules/navigations/lsb/type.ts index 78065489e8..65fd6bb64f 100644 --- a/apps/web/src/common/modules/navigations/lsb/type.ts +++ b/apps/web/src/common/modules/navigations/lsb/type.ts @@ -6,7 +6,7 @@ import type { MenuItem } from '@cloudforet/mirinae/types/controls/context-menu/t import type { MenuId } from '@/lib/menu/config'; -import type { FavoriteOptions } from '@/common/modules/favorites/favorite-button/type'; +import type { FavoriteOptions } from '@/common/modules/user-config/favorite/favorite-button/type'; export const MENU_ITEM_TYPE = { diff --git a/apps/web/src/common/modules/navigations/new-lsb/LSBItem.vue b/apps/web/src/common/modules/navigations/new-lsb/LSBItem.vue index acb1bd8408..d316364667 100644 --- a/apps/web/src/common/modules/navigations/new-lsb/LSBItem.vue +++ b/apps/web/src/common/modules/navigations/new-lsb/LSBItem.vue @@ -4,9 +4,9 @@ import { PTreeNode } from '@cloudforet/mirinae'; import BetaMark from '@/common/components/marks/BetaMark.vue'; import NewMark from '@/common/components/marks/NewMark.vue'; import UpdateMark from '@/common/components/marks/UpdateMark.vue'; -import FavoriteButton from '@/common/modules/favorites/favorite-button/FavoriteButton.vue'; -import { FAVORITE_TYPE } from '@/common/modules/favorites/favorite-button/type'; import type { LSBItemProps } from '@/common/modules/navigations/new-lsb/type'; +import FavoriteButton from '@/common/modules/user-config/favorite/favorite-button/FavoriteButton.vue'; +import { FAVORITE_TYPE } from '@/common/modules/user-config/favorite/favorite-button/type'; const props = withDefaults(defineProps(), { icon: undefined, diff --git a/apps/web/src/common/modules/navigations/new-lsb/type.ts b/apps/web/src/common/modules/navigations/new-lsb/type.ts index e05ca944e4..ad09a4faab 100644 --- a/apps/web/src/common/modules/navigations/new-lsb/type.ts +++ b/apps/web/src/common/modules/navigations/new-lsb/type.ts @@ -3,7 +3,7 @@ import type { Location, Route } from 'vue-router'; import type { TreeNodeDisplayType, TreeNodeIcon, TreeNodeLink } from '@cloudforet/mirinae/types/data-display/tree/new-tree/type'; -import type { FavoriteOptions } from '@/common/modules/favorites/favorite-button/type'; +import type { FavoriteOptions } from '@/common/modules/user-config/favorite/favorite-button/type'; export type HighlightTagType = 'new' | 'beta' | 'update'; export type LSBRouterPredicate = (to: Location, currentRoute: Route) => boolean; diff --git a/apps/web/src/common/modules/navigations/stores/gnb-store.ts b/apps/web/src/common/modules/navigations/stores/gnb-store.ts index 1f634ec6aa..907d91401d 100644 --- a/apps/web/src/common/modules/navigations/stores/gnb-store.ts +++ b/apps/web/src/common/modules/navigations/stores/gnb-store.ts @@ -18,8 +18,8 @@ import type { CostDataSourceReferenceMap } from '@/store/reference/cost-data-sou import { useUserStore } from '@/store/user/user-store'; import ErrorHandler from '@/common/composables/error/errorHandler'; -import type { FavoriteOptions } from '@/common/modules/favorites/favorite-button/type'; import type { Breadcrumb } from '@/common/modules/page-layouts/type'; +import type { FavoriteOptions } from '@/common/modules/user-config/favorite/favorite-button/type'; interface GnbStoreState { breadcrumbs: Breadcrumb[]; diff --git a/apps/web/src/common/modules/navigations/top-bar/modules/TopBarSuggestionList.vue b/apps/web/src/common/modules/navigations/top-bar/modules/TopBarSuggestionList.vue index 217117ecc1..c7bf1203de 100644 --- a/apps/web/src/common/modules/navigations/top-bar/modules/TopBarSuggestionList.vue +++ b/apps/web/src/common/modules/navigations/top-bar/modules/TopBarSuggestionList.vue @@ -9,14 +9,14 @@ import { import { i18n } from '@/translations'; -import FavoriteButton from '@/common/modules/favorites/favorite-button/FavoriteButton.vue'; -import type { FavoriteType } from '@/common/modules/favorites/favorite-button/type'; -import { FAVORITE_TYPE } from '@/common/modules/favorites/favorite-button/type'; import type { SuggestionItem } from '@/common/modules/navigations/top-bar/modules/top-bar-search/config'; import type { FocusingDirection } from '@/common/modules/navigations/top-bar/modules/top-bar-search/type'; import type { FavoriteMenuItem, } from '@/common/modules/navigations/top-bar/modules/top-bar-toolset/modules/top-bar-favorite/modules/TopBarFavoriteContextMenu.vue'; +import FavoriteButton from '@/common/modules/user-config/favorite/favorite-button/FavoriteButton.vue'; +import type { FavoriteType } from '@/common/modules/user-config/favorite/favorite-button/type'; +import { FAVORITE_TYPE } from '@/common/modules/user-config/favorite/favorite-button/type'; import { gray, indigo, peacock } from '@/styles/colors'; diff --git a/apps/web/src/common/modules/navigations/top-bar/modules/top-bar-header/TopBarWorkspaces.vue b/apps/web/src/common/modules/navigations/top-bar/modules/top-bar-header/TopBarWorkspaces.vue index 848fc2959c..ab6079b868 100644 --- a/apps/web/src/common/modules/navigations/top-bar/modules/top-bar-header/TopBarWorkspaces.vue +++ b/apps/web/src/common/modules/navigations/top-bar/modules/top-bar-header/TopBarWorkspaces.vue @@ -1,6 +1,6 @@ - +
(null); const dropdownSize = useElementSize(dropdownRef); const tabRef = ref(null); +/* Recent */ +const { mutateAsync: createRecent } = useRecentCreate(); + const getTabHeaderHeight = () => { const tabHeaderHeight = tabRef.value?.$el.firstElementChild?.clientHeight; @@ -117,10 +119,10 @@ const handleUpdateContentsSize = debounce((height: number) => { if (state.contentsHeight !== height) state.contentsHeight = height; }, 100); -const handleSelect = (item) => { +const handleSelect = async (item) => { if (topBarSearchStore.state.activeTab === SEARCH_TAB.CLOUD_SERVICE) { router.push(topBarSearchReferenceRouter(topBarSearchStore.state.activeTab, item.resource_id, item.workspace_id, item?.tags)); - recentStore.createRecent({ + await createRecent({ workspaceId: item?.workspace_id, type: RECENT_TYPE.CLOUD_SERVICE, id: item?.resource_id, @@ -145,7 +147,7 @@ const handleSelect = (item) => { diff --git a/apps/web/src/common/modules/favorites/favorite-button/store/favorite-store.ts b/apps/web/src/common/modules/user-config/favorite/favorite-button/store/favorite-store.ts similarity index 98% rename from apps/web/src/common/modules/favorites/favorite-button/store/favorite-store.ts rename to apps/web/src/common/modules/user-config/favorite/favorite-button/store/favorite-store.ts index daa94a742d..db9bf182da 100644 --- a/apps/web/src/common/modules/favorites/favorite-button/store/favorite-store.ts +++ b/apps/web/src/common/modules/user-config/favorite/favorite-button/store/favorite-store.ts @@ -17,7 +17,7 @@ import { useUserStore } from '@/store/user/user-store'; import type { ReferenceData, ConfigData } from '@/lib/helper/config-data-helper'; import ErrorHandler from '@/common/composables/error/errorHandler'; -import { FAVORITE_TYPE } from '@/common/modules/favorites/favorite-button/type'; +import { FAVORITE_TYPE } from '@/common/modules/user-config/favorite/favorite-button/type'; diff --git a/apps/web/src/common/modules/favorites/favorite-button/type.ts b/apps/web/src/common/modules/user-config/favorite/favorite-button/type.ts similarity index 100% rename from apps/web/src/common/modules/favorites/favorite-button/type.ts rename to apps/web/src/common/modules/user-config/favorite/favorite-button/type.ts diff --git a/apps/web/src/common/modules/user-config/recent/use-recent-create.ts b/apps/web/src/common/modules/user-config/recent/use-recent-create.ts new file mode 100644 index 0000000000..a18c598027 --- /dev/null +++ b/apps/web/src/common/modules/user-config/recent/use-recent-create.ts @@ -0,0 +1,56 @@ +import { useMutation, useQueryClient } from '@tanstack/vue-query'; + +import type { ListResponse } from '@/api-clients/_common/schema/api-verbs/list'; +import { useUserConfigApi } from '@/api-clients/config/user-config/composables/use-user-config-api'; +import { useServiceQueryKey } from '@/query/core/query-key/use-service-query-key'; + +import ErrorHandler from '@/common/composables/error/errorHandler'; +import type { RecentType } from '@/common/modules/navigations/type'; +import type { RecentItem } from '@/common/modules/user-config/recent/use-recent-list'; + + +interface RecentConfigCreateParams { + type: RecentType; + workspaceId: string; + id: string; + options?: {[key:string]: any}; +} + +export const useRecentCreate = () => { + const queryClient = useQueryClient(); + const { userConfigAPI } = useUserConfigApi(); + const { withSuffix } = useServiceQueryKey('config', 'user-config', 'list'); + + return useMutation({ + mutationFn: (params: RecentConfigCreateParams) => { + const { + workspaceId, id, type, ...options + } = params; + + return userConfigAPI.set({ + name: `console:recent:${type}:${workspaceId}:${id}`, + data: { + id, + workspace_id: workspaceId, + type, + ...options, + }, + }); + }, + onSuccess: (_, variables) => { + queryClient.setQueryData( + withSuffix(`console:recent:${variables.type}`), + (oldData: ListResponse) => { + const newData = [...(oldData?.results ?? []), variables]; + return { + ...oldData, + results: newData, + }; + }, + ); + }, + onError: (error) => { + ErrorHandler.handleError(error); + }, + }); +}; diff --git a/apps/web/src/common/modules/user-config/recent/use-recent-delete.ts b/apps/web/src/common/modules/user-config/recent/use-recent-delete.ts new file mode 100644 index 0000000000..00be05848a --- /dev/null +++ b/apps/web/src/common/modules/user-config/recent/use-recent-delete.ts @@ -0,0 +1,71 @@ +import { computed } from 'vue'; + +import { useMutation, useQueryClient } from '@tanstack/vue-query'; + +import type { ListResponse } from '@/api-clients/_common/schema/api-verbs/list'; +import { useUserConfigApi } from '@/api-clients/config/user-config/composables/use-user-config-api'; +import { useServiceQueryKey } from '@/query/core/query-key/use-service-query-key'; + +import { useUserWorkspaceStore } from '@/store/app-context/workspace/user-workspace-store'; + +import ErrorHandler from '@/common/composables/error/errorHandler'; +import type { RecentType } from '@/common/modules/navigations/type'; +import type { RecentItem } from '@/common/modules/user-config/recent/use-recent-list'; + + + +// NOTE: only name or type/itemId is required +interface RecentConfigDeleteParams { + name?: string; + type?: RecentType; + itemId?: string; +} + +export const useRecentDelete = () => { + const userWorkspaceStore = useUserWorkspaceStore(); + const currentWorkspaceId = computed(() => userWorkspaceStore.getters.currentWorkspaceId); + + const queryClient = useQueryClient(); + const { userConfigAPI } = useUserConfigApi(); + const { withSuffix } = useServiceQueryKey('config', 'user-config', 'list'); + + return useMutation({ + mutationFn: (params: RecentConfigDeleteParams) => { + const { name, type, itemId } = params; + const _name = name ?? `console:recent:${type}:${currentWorkspaceId.value}:${itemId}`; + + return userConfigAPI.delete({ + name: _name, + }); + }, + onSuccess: (_, variables) => { + if (variables.name) { + const _type = variables.name.split(':')[2] as RecentType; + queryClient.setQueryData( + withSuffix(`console:recent:${_type}`), + (oldData: ListResponse) => { + const newData = (oldData?.results ?? []).filter((item) => item.name !== variables.name); + return { + ...oldData, + results: newData, + }; + }, + ); + } else { + queryClient.setQueryData( + withSuffix(`console:recent:${variables.type}`), + (oldData: ListResponse) => { + const newData = (oldData?.results ?? []).filter((item) => item.data.id !== variables.itemId); + return { + ...oldData, + results: newData, + }; + }, + ); + } + }, + onError: (error) => { + ErrorHandler.handleError(error); + }, + }); +}; diff --git a/apps/web/src/common/modules/user-config/recent/use-recent-list.ts b/apps/web/src/common/modules/user-config/recent/use-recent-list.ts new file mode 100644 index 0000000000..b24ae4bc14 --- /dev/null +++ b/apps/web/src/common/modules/user-config/recent/use-recent-list.ts @@ -0,0 +1,258 @@ +import type { ComputedRef } from 'vue'; +import { computed } from 'vue'; + +import { ApiQueryHelper } from '@cloudforet/core-lib/space-connector/helper'; + +import { useUserConfigApi } from '@/api-clients/config/user-config/composables/use-user-config-api'; +import type { UserConfigListParameters } from '@/api-clients/config/user-config/schema/api-verbs/list'; +import type { UserConfigModel } from '@/api-clients/config/user-config/schema/model'; +import { useServiceQueryKey } from '@/query/core/query-key/use-service-query-key'; +import { useScopedQuery } from '@/query/service-query/use-scoped-query'; + +import { useUserWorkspaceStore } from '@/store/app-context/workspace/user-workspace-store'; +import { useUserStore } from '@/store/user/user-store'; + + +import { MENU_ID } from '@/lib/menu/config'; + +import type { RecentType } from '@/common/modules/navigations/type'; +import { RECENT_TYPE } from '@/common/modules/navigations/type'; + + +export interface RecentOriginConfig { + id: string; + label: string; + type: RecentType; + workspace_id: string; + [key: string]: any; +} + +export type RecentItem = UserConfigModel; + + +interface UseRecentListOptions { + limit?: number; +} + +const recentListBaseApiQuery = new ApiQueryHelper().setSort('updated_at', true); + +export const useRecentList = ({ limit = 15 }: UseRecentListOptions = {}) => { + const userStore = useUserStore(); + const userWorkspaceStore = useUserWorkspaceStore(); + const userId = computed(() => userStore.state.userId); + const currentWorkspaceId = computed(() => userWorkspaceStore.getters.currentWorkspaceId); + + const getRecentListParams = computed(() => { + recentListBaseApiQuery.setFilters([ + { k: 'user_id', v: userId.value || '', o: '=' }, + ]); + return (type: RecentType) => { + const recentListApiQuery = new ApiQueryHelper().setFilters([ + ...recentListBaseApiQuery.filters, + { k: 'name', v: `console:recent:${type}:`, o: '' }, + ]).setPageLimit(limit); + + if (type !== RECENT_TYPE.WORKSPACE && currentWorkspaceId.value) { + recentListApiQuery.addFilter({ k: 'data.workspace_id', v: currentWorkspaceId.value, o: '=' }); + } + return { + query: recentListApiQuery.data, + }; + }; + }); + + const { data: menuRecentList, isFetching: isFetchingMenuRecentList } = useRecentQueryByType({ + params: computed(() => { + const recentListApiQuery = new ApiQueryHelper().setFilters([ + ...recentListBaseApiQuery.filters, + { k: 'name', v: 'console:recent:', o: '' }, + { k: 'data.type', v: RECENT_TYPE.WORKSPACE, o: '!=' }, + { k: 'data.id', v: MENU_ID.WORKSPACE_HOME, o: '!=' }, + // NOTE: Code corresponding to data stored as 'home-dashboard' + { k: 'data.id', v: 'home-dashboard', o: '!=' }, + ]).setPageLimit(limit); + + if (currentWorkspaceId.value) { + recentListApiQuery.addFilter({ k: 'data.workspace_id', v: currentWorkspaceId.value, o: '=' }); + } + return { + query: recentListApiQuery.data, + }; + }), + enabled: computed(() => { + if (!userId.value) return false; + return true; + }), + }); + + + const { data: workspaceRecentList, isFetching: isFetchingWorkspaceRecentList } = useRecentQueryByType({ + type: RECENT_TYPE.WORKSPACE, + params: computed(() => getRecentListParams.value(RECENT_TYPE.WORKSPACE)), + enabled: computed(() => { + if (!userId.value) return false; + return true; + }), + }); + + const { data: serviceRecentList, isFetching: isFetchingServiceRecentList } = useRecentQueryByType({ + type: RECENT_TYPE.SERVICE, + params: computed(() => getRecentListParams.value(RECENT_TYPE.SERVICE)), + enabled: computed(() => { + if (!userId.value) return false; + if (!currentWorkspaceId.value) return false; + return true; + }), + }); + + const { data: serviceAccountRecentList, isFetching: isFetchingServiceAccountRecentList } = useRecentQueryByType({ + type: RECENT_TYPE.SERVICE_ACCOUNT, + params: computed(() => getRecentListParams.value(RECENT_TYPE.SERVICE_ACCOUNT)), + enabled: computed(() => { + if (!userId.value) return false; + if (!currentWorkspaceId.value) return false; + return true; + }), + }); + + const { data: trustedAccountRecentList, isFetching: isFetchingTrustedAccountRecentList } = useRecentQueryByType({ + type: RECENT_TYPE.TRUSTED_ACCOUNT, + params: computed(() => getRecentListParams.value(RECENT_TYPE.TRUSTED_ACCOUNT)), + enabled: computed(() => { + if (!userId.value) return false; + return true; + }), + }); + + const { data: projectRecentList, isFetching: isFetchingProjectRecentList } = useRecentQueryByType({ + type: RECENT_TYPE.PROJECT, + params: computed(() => getRecentListParams.value(RECENT_TYPE.PROJECT)), + enabled: computed(() => { + if (!userId.value) return false; + if (!currentWorkspaceId.value) return false; + return true; + }), + }); + + const { data: projectGroupRecentList, isFetching: isFetchingProjectGroupRecentList } = useRecentQueryByType({ + type: RECENT_TYPE.PROJECT_GROUP, + params: computed(() => getRecentListParams.value(RECENT_TYPE.PROJECT_GROUP)), + enabled: computed(() => { + if (!userId.value) return false; + if (!currentWorkspaceId.value) return false; + return true; + }), + }); + + const { data: dashboardRecentList, isFetching: isFetchingDashboardRecentList } = useRecentQueryByType({ + type: RECENT_TYPE.DASHBOARD, + params: computed(() => getRecentListParams.value(RECENT_TYPE.DASHBOARD)), + enabled: computed(() => { + if (!userId.value) return false; + if (!currentWorkspaceId.value) return false; + return true; + }), + }); + + const { data: cloudServiceRecentList, isFetching: isFetchingCloudServiceRecentList } = useRecentQueryByType({ + type: RECENT_TYPE.CLOUD_SERVICE, + params: computed(() => getRecentListParams.value(RECENT_TYPE.CLOUD_SERVICE)), + enabled: computed(() => { + if (!userId.value) return false; + if (!currentWorkspaceId.value) return false; + return true; + }), + }); + + const { data: cloudServiceTypeRecentList, isFetching: isFetchingCloudServiceTypeRecentList } = useRecentQueryByType({ + type: RECENT_TYPE.CLOUD_SERVICE_TYPE, + params: computed(() => getRecentListParams.value(RECENT_TYPE.CLOUD_SERVICE_TYPE)), + enabled: computed(() => { + if (!userId.value) return false; + if (!currentWorkspaceId.value) return false; + return true; + }), + }); + + const { data: costAnalysisRecentList, isFetching: isFetchingCostAnalysisRecentList } = useRecentQueryByType({ + type: RECENT_TYPE.COST_ANALYSIS, + params: computed(() => getRecentListParams.value(RECENT_TYPE.COST_ANALYSIS)), + enabled: computed(() => { + if (!userId.value) return false; + if (!currentWorkspaceId.value) return false; + return true; + }), + }); + + const { data: metricExplorerRecentList, isFetching: isFetchingMetricExplorerRecentList } = useRecentQueryByType({ + type: RECENT_TYPE.METRIC_EXPLORER, + params: computed(() => getRecentListParams.value(RECENT_TYPE.METRIC_EXPLORER)), + enabled: computed(() => { + if (!userId.value) return false; + if (!currentWorkspaceId.value) return false; + return true; + }), + }); + + const { data: securityRecentList, isFetching: isFetchingSecurityRecentList } = useRecentQueryByType({ + type: RECENT_TYPE.SECURITY, + params: computed(() => getRecentListParams.value(RECENT_TYPE.SECURITY)), + enabled: computed(() => { + if (!userId.value) return false; + if (!currentWorkspaceId.value) return false; + return true; + }), + }); + + return { + menuRecentList, + workspaceRecentList, + serviceRecentList, + serviceAccountRecentList, + trustedAccountRecentList, + projectRecentList, + projectGroupRecentList, + dashboardRecentList, + cloudServiceRecentList, + cloudServiceTypeRecentList, + costAnalysisRecentList, + metricExplorerRecentList, + securityRecentList, + workspaceRecentLoading: isFetchingWorkspaceRecentList, + recentLoading: computed(() => isFetchingMenuRecentList.value + || isFetchingWorkspaceRecentList.value + || isFetchingServiceRecentList.value + || isFetchingServiceAccountRecentList.value + || isFetchingTrustedAccountRecentList.value + || isFetchingProjectRecentList.value + || isFetchingProjectGroupRecentList.value + || isFetchingDashboardRecentList.value + || isFetchingCloudServiceRecentList.value + || isFetchingCloudServiceTypeRecentList.value + || isFetchingCostAnalysisRecentList.value + || isFetchingMetricExplorerRecentList.value + || isFetchingSecurityRecentList.value), + }; +}; + +interface UseRecentListByTypeOptions { + type?: RecentType; + params: ComputedRef; + enabled: ComputedRef; +} + +const useRecentQueryByType = ({ type, params, enabled }: UseRecentListByTypeOptions) => { + const { userConfigAPI } = useUserConfigApi(); + + const { key, params: queryParams } = useServiceQueryKey('config', 'user-config', 'list', { + contextKey: type ? `console:recent:${type}:` : 'console:recent:', + params, + }); + + return useScopedQuery({ + queryKey: key, + queryFn: () => userConfigAPI.list(queryParams.value), + select: (data) => data?.results ?? [], + enabled, + }, ['WORKSPACE', 'USER']); +}; diff --git a/apps/web/src/common/modules/user-config/shared/_internal/use-cloud-service-type-map.ts b/apps/web/src/common/modules/user-config/shared/_internal/use-cloud-service-type-map.ts new file mode 100644 index 0000000000..9ce661b0ac --- /dev/null +++ b/apps/web/src/common/modules/user-config/shared/_internal/use-cloud-service-type-map.ts @@ -0,0 +1,38 @@ +import { computed } from 'vue'; + +import { useCloudServiceTypeApi } from '@/api-clients/inventory/cloud-service-type/composables/use-cloud-service-type-api'; +import type { CloudServiceTypeModel } from '@/api-clients/inventory/cloud-service-type/schema/model'; +import { useServiceQueryKey } from '@/query/core/query-key/use-service-query-key'; +import { useScopedQuery } from '@/query/service-query/use-scoped-query'; + +export const useCloudServiceTypeMap = () => { + const { cloudServiceTypeAPI } = useCloudServiceTypeApi(); + + const { key, params } = useServiceQueryKey('inventory', 'cloud-service-type', 'list', { + params: { + query: { + only: ['name', 'group', 'provider', 'cloud_service_type_key', 'tags'], + }, + }, + }); + const { data: cloudServiceTypeList, isFetching: isFetchingCloudServiceTypeList } = useScopedQuery({ + queryKey: key, + queryFn: () => cloudServiceTypeAPI.list(params.value), + select: (data) => data.results || [], + staleTime: 1000 * 60 * 5, + gcTime: 1000 * 60 * 10, + }, ['WORKSPACE']); + + + return { + map: computed(() => { + const cloudServiceTypeMap = new Map(); + cloudServiceTypeList.value?.forEach((d) => { + cloudServiceTypeMap.set(d.cloud_service_type_key, d); + }); + + return cloudServiceTypeMap; + }), + loading: isFetchingCloudServiceTypeList, + }; +}; diff --git a/apps/web/src/common/modules/user-config/shared/_internal/use-cost-query-set-map.ts b/apps/web/src/common/modules/user-config/shared/_internal/use-cost-query-set-map.ts new file mode 100644 index 0000000000..bdc7e2f4b8 --- /dev/null +++ b/apps/web/src/common/modules/user-config/shared/_internal/use-cost-query-set-map.ts @@ -0,0 +1,84 @@ +import { + reactive, watch, readonly, ref, + computed, +} from 'vue'; + +import { useQueryClient } from '@tanstack/vue-query'; + +import type { ListResponse } from '@/api-clients/_common/schema/api-verbs/list'; +import { useCostQuerySetApi } from '@/api-clients/cost-analysis/cost-query-set/composables/use-cost-query-set-api'; +import type { CostQuerySetModel } from '@/api-clients/cost-analysis/cost-query-set/schema/model'; +import { useDataSourceApi } from '@/api-clients/cost-analysis/data-source/composables/use-data-source-api'; +import { useServiceQueryKey } from '@/query/core/query-key/use-service-query-key'; +import { useScopedQuery } from '@/query/service-query/use-scoped-query'; + +export const useCostQuerySetMap = () => { + const queryClient = useQueryClient(); + const { dataSourceAPI } = useDataSourceApi(); + const { costQuerySetAPI } = useCostQuerySetApi(); + const loading = ref(false); + + const { key: dataSourceKey, params: dataSourceParams } = useServiceQueryKey('cost-analysis', 'data-source', 'list', { + params: { + query: { + only: ['data_source_id'], + }, + }, + }); + const { withSuffix } = useServiceQueryKey('cost-analysis', 'cost-query-set', 'list', { + params: { + query: { + only: ['cost_query_set_id', 'name', 'data_source_id'], + }, + }, + }); + + const { data: dataSourceIds, isFetching: isFetchingDataSourceIds } = useScopedQuery({ + queryKey: dataSourceKey, + queryFn: () => dataSourceAPI.list(dataSourceParams.value), + select: (data) => (data.results || []).map((d) => d.data_source_id), + staleTime: 1000 * 60 * 5, + gcTime: 1000 * 60 * 10, + }, ['WORKSPACE']); + + const staticQueryParams = { + query: { + only: ['cost_query_set_id', 'name', 'data_source_id'], + }, + }; + const costQuerySetFetcher = async (dataSourceId: string) => { + const costQuerySetParams = { + data_source_id: dataSourceId, + ...staticQueryParams, + }; + const response = await queryClient.ensureQueryData>({ + queryKey: withSuffix([dataSourceId, costQuerySetParams]), + queryFn: () => costQuerySetAPI.list(costQuerySetParams), + staleTime: 1000 * 60 * 5, + gcTime: 1000 * 60 * 10, + }); + + return response.results || []; + }; + + const costQuerySetMap = reactive>({}); + + + watch(dataSourceIds, async () => { + if (!dataSourceIds.value?.length) return; + loading.value = true; + const promises = dataSourceIds.value.map((dataSourceId) => costQuerySetFetcher(dataSourceId)); + const results = await Promise.all(promises); + results.forEach((data) => { + data.forEach((d) => { + costQuerySetMap[d.cost_query_set_id] = d; + }); + }); + loading.value = false; + }); + + return { + map: readonly(costQuerySetMap), + loading: computed(() => loading.value || isFetchingDataSourceIds.value), + }; +}; diff --git a/apps/web/src/common/modules/user-config/shared/_internal/use-dashboard-map.ts b/apps/web/src/common/modules/user-config/shared/_internal/use-dashboard-map.ts new file mode 100644 index 0000000000..2c41b68d7a --- /dev/null +++ b/apps/web/src/common/modules/user-config/shared/_internal/use-dashboard-map.ts @@ -0,0 +1,64 @@ +import { computed } from 'vue'; + +import { ApiQueryHelper } from '@cloudforet/core-lib/space-connector/helper'; + +import type { DashboardModel } from '@/api-clients/dashboard/_types/dashboard-type'; +import { usePrivateDashboardApi } from '@/api-clients/dashboard/private-dashboard/composables/use-private-dashboard-api'; +import { usePublicDashboardApi } from '@/api-clients/dashboard/public-dashboard/composables/use-public-dashboard-api'; +import { useServiceQueryKey } from '@/query/core/query-key/use-service-query-key'; +import { useScopedQuery } from '@/query/service-query/use-scoped-query'; + +const privateDashboardListApiQueryHelper = new ApiQueryHelper().setOnly('dashboard_id', 'name'); +const publicDashboardListApiQueryHelper = new ApiQueryHelper().setFilters([ + { k: 'resource_group', v: 'WORKSPACE', o: '=' }, +]).setOnly('dashboard_id', 'name'); + + +export const useDashboardMap = () => { + const { publicDashboardAPI } = usePublicDashboardApi(); + const { privateDashboardAPI } = usePrivateDashboardApi(); + + const { key: publicDashboardKey, params: publicDashboardParams } = useServiceQueryKey('dashboard', 'public-dashboard', 'list', { + params: { + query: publicDashboardListApiQueryHelper.data, + }, + }); + + const { key: privateDashboardKey, params: privateDashboardParams } = useServiceQueryKey('dashboard', 'private-dashboard', 'list', { + params: { + query: privateDashboardListApiQueryHelper.data, + }, + }); + const { data: publicDashboardList, isFetching: isFetchingPublicDashboardList } = useScopedQuery({ + queryKey: publicDashboardKey, + queryFn: () => publicDashboardAPI.list(publicDashboardParams.value), + select: (data) => data.results || [], + staleTime: 1000 * 60 * 5, + gcTime: 1000 * 60 * 10, + }, ['WORKSPACE']); + + const { data: privateDashboardList, isFetching: isFetchingPrivateDashboardList } = useScopedQuery({ + queryKey: privateDashboardKey, + queryFn: () => privateDashboardAPI.list(privateDashboardParams.value), + select: (data) => data.results || [], + staleTime: 1000 * 60 * 5, + gcTime: 1000 * 60 * 10, + }, ['WORKSPACE']); + + + return { + map: computed(() => { + const dashboardMap = new Map(); + publicDashboardList.value?.forEach((d) => { + dashboardMap.set(d.dashboard_id, d); + }); + + privateDashboardList.value?.forEach((d) => { + dashboardMap.set(d.dashboard_id, d); + }); + + return dashboardMap; + }), + loading: computed(() => isFetchingPublicDashboardList.value || isFetchingPrivateDashboardList.value), + }; +}; diff --git a/apps/web/src/common/modules/user-config/shared/_internal/use-metric-example-map.ts b/apps/web/src/common/modules/user-config/shared/_internal/use-metric-example-map.ts new file mode 100644 index 0000000000..120511a9aa --- /dev/null +++ b/apps/web/src/common/modules/user-config/shared/_internal/use-metric-example-map.ts @@ -0,0 +1,36 @@ +import { computed } from 'vue'; + +import { useMetricExampleApi } from '@/api-clients/inventory/metric-example/composables/use-metric-example-api'; +import type { MetricExampleModel } from '@/api-clients/inventory/metric-example/schema/model'; +import { useServiceQueryKey } from '@/query/core/query-key/use-service-query-key'; +import { useScopedQuery } from '@/query/service-query/use-scoped-query'; + +export const useMetricExampleMap = () => { + const { metricExampleAPI } = useMetricExampleApi(); + + const { key, params: queryParams } = useServiceQueryKey('inventory', 'metric-example', 'list', { + params: { + query: { + only: ['example_id', 'name'], + }, + }, + }); + const { data: metricExampleList, isFetching: isFetchingMetricExampleList } = useScopedQuery({ + queryKey: key, + queryFn: () => metricExampleAPI.list(queryParams.value), + select: (data) => data.results ?? [], + staleTime: 1000 * 60 * 5, + gcTime: 1000 * 60 * 10, + }, ['WORKSPACE']); + + return { + map: computed>(() => { + const metricExampleMap = new Map(); + metricExampleList.value?.forEach((item) => { + metricExampleMap.set(item.example_id, item); + }); + return metricExampleMap; + }), + loading: isFetchingMetricExampleList, + }; +}; diff --git a/apps/web/src/common/modules/user-config/shared/_internal/use-workspace-map.ts b/apps/web/src/common/modules/user-config/shared/_internal/use-workspace-map.ts new file mode 100644 index 0000000000..de3d670fc5 --- /dev/null +++ b/apps/web/src/common/modules/user-config/shared/_internal/use-workspace-map.ts @@ -0,0 +1,31 @@ +import { computed } from 'vue'; + +import { useUserProfileApi } from '@/api-clients/identity/user-profile/composables/use-user-profile-api'; +import type { WorkspaceModel } from '@/api-clients/identity/workspace/schema/model'; +import { useServiceQueryKey } from '@/query/core/query-key/use-service-query-key'; +import { useScopedQuery } from '@/query/service-query/use-scoped-query'; + +export const useWorkspaceMap = () => { + const { userProfileAPI } = useUserProfileApi(); + + const { key, params } = useServiceQueryKey('identity', 'user-profile', 'get-workspaces'); + const { data: workspaceList, isFetching: isFetchingWorkspaceList } = useScopedQuery({ + queryKey: key, + queryFn: () => userProfileAPI.getWorkspaces(params.value), + select: (data) => data.results || [], + staleTime: 1000 * 60 * 5, + gcTime: 1000 * 60 * 10, + }, ['WORKSPACE', 'USER']); + + + return { + map: computed(() => { + const workspaceMap = new Map(); + workspaceList.value?.forEach((workspace) => { + workspaceMap.set(workspace.workspace_id, workspace); + }); + return workspaceMap; + }), + loading: isFetchingWorkspaceList, + }; +}; diff --git a/apps/web/src/common/modules/user-config/shared/use-convert-referenced-config-data.ts b/apps/web/src/common/modules/user-config/shared/use-convert-referenced-config-data.ts new file mode 100644 index 0000000000..bc38ee3ed8 --- /dev/null +++ b/apps/web/src/common/modules/user-config/shared/use-convert-referenced-config-data.ts @@ -0,0 +1,275 @@ +import type { ComputedRef } from 'vue'; +import { computed } from 'vue'; +import { useRoute, useRouter } from 'vue-router/composables'; + +import { cloneDeep, find } from 'lodash'; + +import { useAllReferenceDataModel } from '@/query/resource-query/reference-data-model'; + +import type { DisplayMenu } from '@/store/menu/type'; + +import { assetUrlConverter } from '@/lib/helper/asset-helper'; +import { getParsedKeysWithManagedCostQueryFavoriteKey } from '@/lib/helper/config-data-helper'; +import { getAllSuggestionMenuList } from '@/lib/helper/menu-suggestion-helper'; +import { useAllMenuList } from '@/lib/menu/use-all-menu-list'; + +import type { RecentConfig, RecentType } from '@/common/modules/navigations/type'; +import { + FAVORITE_TYPE, type FavoriteConfig, type FavoriteType, type FavoriteItem, +} from '@/common/modules/user-config/favorite/favorite-button/type'; +import { useCloudServiceTypeMap } from '@/common/modules/user-config/shared/_internal/use-cloud-service-type-map'; +import { useCostQuerySetMap } from '@/common/modules/user-config/shared/_internal/use-cost-query-set-map'; +import { useDashboardMap } from '@/common/modules/user-config/shared/_internal/use-dashboard-map'; +import { useMetricExampleMap } from '@/common/modules/user-config/shared/_internal/use-metric-example-map'; +import { useWorkspaceMap } from '@/common/modules/user-config/shared/_internal/use-workspace-map'; + + +export interface ConfigData extends Omit, Omit { + itemType: RecentType|FavoriteType; +} + +export interface ReferenceData extends Omit { + itemType: RecentType|FavoriteType; +} + +interface UseConvertReferencedConfigDataOptions { + allConfigList?: ComputedRef; + projectConfigList?: ComputedRef; + projectGroupConfigList?: ComputedRef; + metricConfigList?: ComputedRef; + metricExampleConfigList?: ComputedRef; + serviceConfigList?: ComputedRef; + dashboardConfigList?: ComputedRef; + costQuerySetConfigList?: ComputedRef; + cloudServiceConfigList?: ComputedRef; + workspaceConfigList?: ComputedRef; +} + +export const useConvertReferencedConfigData = ({ + allConfigList, + projectConfigList, + projectGroupConfigList, + metricConfigList, + metricExampleConfigList, + serviceConfigList, + dashboardConfigList, + costQuerySetConfigList, + cloudServiceConfigList, + workspaceConfigList, +}: UseConvertReferencedConfigDataOptions) => { + /* Reference */ + const referenceMap = useAllReferenceDataModel(); + const projectMap = referenceMap.project; + const projectGroupMap = referenceMap.projectGroup; + const metricMap = referenceMap.metric; + const serviceMap = referenceMap.service; + const costDataSourceMap = referenceMap.costDataSource; + const { map: cloudServiceTypeMap, loading: isLoadingCloudServiceTypeMap } = useCloudServiceTypeMap(); + const { map: dashboardMap, loading: isLoadingDashboardMap } = useDashboardMap(); + const { map: costQuerySetMap, loading: isLoadingCostQuerySetMap } = useCostQuerySetMap(); + const { map: metricExampleMap, loading: isLoadingMetricExampleMap } = useMetricExampleMap(); + const { map: workspaceMap, loading: isLoadingWorkspaceMap } = useWorkspaceMap(); + + /* Menu */ + const { getAllMenuList } = useAllMenuList(); + const router = useRouter(); + const route = useRoute(); + const allMenuList = computed(() => getAllMenuList(route, router)); + + /* Converted */ + const convertedMenuList = computed(() => { + const convertMenuList = cloneDeep(allMenuList.value); + + const suggestionMenuList = getAllSuggestionMenuList(convertMenuList); + const results: ReferenceData[] = []; + if (!allConfigList?.value) return results; + + const reorderedConfig: ConfigData[] = []; + suggestionMenuList.forEach((menu) => { + const configItem = allConfigList?.value.find((item) => item.itemId === menu.id); + if (configItem) { + reorderedConfig.push(configItem); + } + }); + + return reorderedConfig.map((d) => { + const menu = find(suggestionMenuList, { id: d.itemId }); + return { + ...d, + itemType: FAVORITE_TYPE.MENU, + itemId: menu?.id || d.itemId, + name: menu?.id || d.itemId, + label: menu?.label || d.itemId, + icon: menu?.parents?.[0]?.icon || menu?.icon || undefined, + parents: menu?.parents || [], + updatedAt: d?.updatedAt, + isDeleted: !menu, + }; + }); + }); + + const convertedProjectList = computed(() => (projectConfigList?.value ?? []).map((d) => { + const project = projectMap[d.itemId]; + const parents = project?.data?.groupInfo?.id ? [{ + name: project.data?.groupInfo?.id, + label: project.data?.groupInfo?.name, + }] : undefined; + return { + ...d, + name: d.itemId, + label: project?.name || d.itemId, + icon: 'ic_document-filled', + updatedAt: d?.updatedAt, + parents, + isDeleted: !project, + }; + })); + + const convertedProjectGroupList = computed(() => (projectGroupConfigList?.value ?? []).map((d) => { + const projectGroup = projectGroupMap[d.itemId]; + const parents = projectGroup?.data?.parentGroupInfo?.id ? [{ + name: projectGroup.data?.parentGroupInfo?.id, + label: projectGroup.data?.parentGroupInfo?.name, + }] : undefined; + return { + ...d, + name: d.itemId, + label: projectGroup?.name || d.itemId, + icon: 'ic_folder-filled', + parents, + isDeleted: !projectGroup, + }; + })); + + const convertedCloudServiceTypeList = computed(() => (cloudServiceConfigList?.value ?? []).map((d) => { + const cloudServiceType = cloudServiceTypeMap.value.get(d.itemId); + return { + ...d, + name: d.itemId, + label: cloudServiceType?.name || d.itemId, + icon: assetUrlConverter(cloudServiceType?.tags['spaceone:icon']), + provider: cloudServiceType?.provider, + parents: [{ + name: cloudServiceType?.group, + label: cloudServiceType?.group, + }], + updatedAt: d.updatedAt, + isDeleted: !cloudServiceType, + }; + })); + + const convertedMetricList = computed(() => (metricConfigList?.value ?? []).map((d) => { + const metric = metricMap[d.itemId]; + return { + ...d, + name: d.itemId, + label: metric?.name || d.itemId, + icon: d.itemId.startsWith('metric-managed-') ? 'ic_main-filled' : 'ic_sub', + updatedAt: d?.updatedAt, + isDeleted: !metric, + }; + })); + + const convertedMetricExampleList = computed(() => (metricExampleConfigList?.value ?? []).map((d) => { + const metricExample = metricExampleMap.value.get(d.itemId); + return { + ...d, + name: d.itemId, + label: metricExample?.name || d.itemId, + icon: 'ic_example-filled', + updatedAt: d?.updatedAt, + isDeleted: !metricExample, + }; + })); + + const convertedServiceList = computed(() => (serviceConfigList?.value ?? []).map((d) => { + const service = serviceMap[d.itemId]; + return { + ...d, + itemType: FAVORITE_TYPE.SERVICE, + itemId: service?.name || d.itemId, + name: service?.name || d.itemId, + label: service?.label || d.itemId, + icon: 'ic_service_alert', + updatedAt: d?.updatedAt, + isDeleted: !service, + }; + })); + + const convertedDashboardList = computed(() => (dashboardConfigList?.value ?? []).map((d) => { + const dashboard = dashboardMap.value.get(d.itemId); + return { + ...d, + name: dashboard?.dashboard_id || d.itemId, + label: dashboard?.name || d.itemId, + icon: 'ic_service_dashboard', + updatedAt: d.updatedAt, + isDeleted: !dashboard, + }; + })); + + const convertedCostQuerySetList = computed(() => (costQuerySetConfigList?.value ?? []).map((d) => { + const parsedKeys = getParsedKeysWithManagedCostQueryFavoriteKey(d.itemId); + if (parsedKeys) { + const [dataSourceId, costQuerySetId] = parsedKeys; + const dataSource = costDataSourceMap[dataSourceId]; + return { + ...d, + name: d.itemId, + label: costQuerySetId || d.itemId, + updatedAt: d.updatedAt, + icon: 'ic_service_cost-explorer', + dataSourceId, + parents: [{ + name: dataSourceId, + label: dataSource?.label, + }], + isDeleted: false, + }; + } + const costQuerySet = costQuerySetMap[d.itemId]; + return { + ...d, + name: costQuerySet?.cost_query_set_id || d.itemId, + label: costQuerySet?.name || d.itemId, + updatedAt: d.updatedAt, + icon: 'ic_service_cost-explorer', + dataSourceId: costQuerySet?.data_source_id, + parents: [{ + name: costQuerySet?.data_source_id, + label: costDataSourceMap[costQuerySet?.data_source_id]?.label, + }], + isDeleted: !costQuerySet, + }; + })); + + const convertedWorkspaceList = computed(() => (workspaceConfigList?.value ?? []).map((d) => { + const workspace = workspaceMap.value.get(d.itemId); + return { + ...d, + itemType: FAVORITE_TYPE.WORKSPACE, + itemId: workspace?.workspace_id || d.itemId, + name: workspace?.workspace_id || d.itemId, + label: workspace?.name || d.itemId, + tags: workspace?.tags, + isDeleted: !workspace, + }; + })); + + + return { + convertedMenu: convertedMenuList, + convertedProject: convertedProjectList, + convertedProjectGroup: convertedProjectGroupList, + convertedMetric: convertedMetricList, + convertedMetricExample: convertedMetricExampleList, + convertedService: convertedServiceList, + convertedCloudServiceType: convertedCloudServiceTypeList, + convertedDashboard: convertedDashboardList, + convertedCostQuerySet: convertedCostQuerySetList, + convertedWorkspace: convertedWorkspaceList, + loading: computed(() => isLoadingCloudServiceTypeMap.value || isLoadingDashboardMap.value || isLoadingCostQuerySetMap.value || isLoadingMetricExampleMap.value || isLoadingWorkspaceMap.value), + }; +}; + + diff --git a/apps/web/src/lib/helper/config-data-helper.ts b/apps/web/src/lib/helper/config-data-helper.ts index 219a290f86..5fc945227a 100644 --- a/apps/web/src/lib/helper/config-data-helper.ts +++ b/apps/web/src/lib/helper/config-data-helper.ts @@ -17,9 +17,9 @@ import type { ServiceReferenceMap } from '@/store/reference/service-reference-st import { getAllSuggestionMenuList } from '@/lib/helper/menu-suggestion-helper'; -import { FAVORITE_TYPE } from '@/common/modules/favorites/favorite-button/type'; -import type { FavoriteItem, FavoriteType, FavoriteConfig } from '@/common/modules/favorites/favorite-button/type'; import type { RecentType, RecentConfig } from '@/common/modules/navigations/type'; +import { FAVORITE_TYPE } from '@/common/modules/user-config/favorite/favorite-button/type'; +import type { FavoriteItem, FavoriteType, FavoriteConfig } from '@/common/modules/user-config/favorite/favorite-button/type'; export interface ConfigData extends Omit, Omit { itemType: RecentType|FavoriteType; @@ -183,6 +183,9 @@ export const convertMetricConfigToReferenceData = (config: ConfigData[]|null, me } else { results.push({ ...d, + name: d.itemId, + label: d.itemId, + icon: d.itemId.startsWith('metric-managed-') ? 'ic_main-filled' : 'ic_sub', isDeleted: !resource, }); } diff --git a/apps/web/src/query/clients.ts b/apps/web/src/query/clients.ts index 3efb0ec11d..60ecb37987 100644 --- a/apps/web/src/query/clients.ts +++ b/apps/web/src/query/clients.ts @@ -12,4 +12,11 @@ export const serviceQueryClient = new QueryClient(); * This client is used internally by the reference data system for managing its own cache. * It's not meant to be used directly in service context. */ -export const referenceQueryClient = new QueryClient(); +export const resourceQueryClient = new QueryClient(); + +/** + * Dedicated query client for the config feature. + * This client is used internally by the config feature for managing its own cache. + * It's not meant to be used directly in service context. + */ +export const configFeatureQueryClient = new QueryClient(); diff --git a/apps/web/src/query/resource-query/reference-data-model/adaptors/implementations/use-service-reference-data-model.ts b/apps/web/src/query/resource-query/reference-data-model/adaptors/implementations/use-service-reference-data-model.ts index b64daf19d1..cb02faa8fd 100644 --- a/apps/web/src/query/resource-query/reference-data-model/adaptors/implementations/use-service-reference-data-model.ts +++ b/apps/web/src/query/resource-query/reference-data-model/adaptors/implementations/use-service-reference-data-model.ts @@ -25,7 +25,7 @@ export const useServiceReferenceDataModel: ReferenceDataModelImplementationAdapt } = useReferenceDataModel( RESOURCE_CONFIG_MAP.service.resourceKey, (serviceInfo: ServiceModel) => ({ - key: serviceInfo.service_key, + key: serviceInfo.service_id, label: serviceInfo.name, name: serviceInfo.service_id, data: serviceInfo, diff --git a/apps/web/src/query/resource-query/reference-data-model/composables/use-reference-data-model.ts b/apps/web/src/query/resource-query/reference-data-model/composables/use-reference-data-model.ts index fedee5b5eb..51bec6c38f 100644 --- a/apps/web/src/query/resource-query/reference-data-model/composables/use-reference-data-model.ts +++ b/apps/web/src/query/resource-query/reference-data-model/composables/use-reference-data-model.ts @@ -3,7 +3,7 @@ import { ref, } from 'vue'; -import { referenceQueryClient as queryClient } from '@/query/clients'; +import { resourceQueryClient as queryClient } from '@/query/clients'; import { useResourceQueryKey } from '@/query/core/query-key/use-resource-query-key'; import { useReferenceReactiveCache } from '@/query/resource-query/reference-data-model/composables/_internal/use-reference-reactive-cache'; import { getRepository } from '@/query/resource-query/reference-data-model/core/repository-registry'; diff --git a/apps/web/src/query/resource-query/resource-menu-handler/core/use-base-resource-menu-handler.ts b/apps/web/src/query/resource-query/resource-menu-handler/core/use-base-resource-menu-handler.ts index db1867802f..2b796411fc 100644 --- a/apps/web/src/query/resource-query/resource-menu-handler/core/use-base-resource-menu-handler.ts +++ b/apps/web/src/query/resource-query/resource-menu-handler/core/use-base-resource-menu-handler.ts @@ -1,6 +1,6 @@ import type { MenuAttachHandler } from '@cloudforet/mirinae'; -import { referenceQueryClient as queryClient } from '@/query/clients'; +import { resourceQueryClient as queryClient } from '@/query/clients'; import { useResourceQueryKey } from '@/query/core/query-key/use-resource-query-key'; import { RESOURCE_MENU_FETCH_CONFIG } from '@/query/resource-query/resource-menu-handler/config/resource-menu-fetch-config'; import { generateMenuQueryHandler } from '@/query/resource-query/resource-menu-handler/core/menu-query-handler.generator'; diff --git a/apps/web/src/query/resource-query/shared/composable/__tests__/use-resource-cache-sync.test.ts b/apps/web/src/query/resource-query/shared/composable/__tests__/use-resource-cache-sync.test.ts index 524954e63f..b56e2e24f0 100644 --- a/apps/web/src/query/resource-query/shared/composable/__tests__/use-resource-cache-sync.test.ts +++ b/apps/web/src/query/resource-query/shared/composable/__tests__/use-resource-cache-sync.test.ts @@ -4,6 +4,7 @@ import { describe, it, expect, vi, beforeEach, } from 'vitest'; +import { resourceQueryClient } from '@/query/clients'; import { useResourceCacheSync } from '@/query/resource-query/shared/composable/use-resource-cache-sync'; vi.mock('@tanstack/vue-query', async (importOriginal) => { @@ -61,7 +62,7 @@ describe('useResourceCacheSync', () => { const { wrapResourceCacheRefresh } = useResourceCacheSync('project'); const mockApiFn = vi.fn().mockResolvedValue({ status: 'success' }); const wrappedFn = wrapResourceCacheRefresh(mockApiFn); - const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries'); + const invalidateSpy = vi.spyOn(resourceQueryClient, 'invalidateQueries'); // Act await wrappedFn({ name: 'new-project' }); @@ -81,7 +82,7 @@ describe('useResourceCacheSync', () => { const { wrapResourceCacheRefresh } = useResourceCacheSync('project'); const mockApiFn = vi.fn().mockRejectedValue(new Error('API Error')); const wrappedFn = wrapResourceCacheRefresh(mockApiFn); - const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries'); + const invalidateSpy = vi.spyOn(resourceQueryClient, 'invalidateQueries'); // Act & Assert await expect(wrappedFn()).rejects.toThrow('API Error'); @@ -93,48 +94,48 @@ describe('useResourceCacheSync', () => { }); }); - describe('2. wrapResourceCacheUpdate', () => { - const resourceType = 'project'; - const resourceQueryKey = ['workspace', 'workspace-123', resourceType]; - - it('should create a new cache with the new data and invalidate the list/stat queries', async () => { - // Arrange - const { wrapResourceCacheUpdate } = useResourceCacheSync(resourceType); - const newProjectData = { project_id: 'project-new', name: 'New Project' }; - const mockApiFn = vi.fn().mockResolvedValue(newProjectData); - const wrappedFn = wrapResourceCacheUpdate(mockApiFn); - const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries'); - - // Act - await wrappedFn(newProjectData); - - // Assert - const cachedData = queryClient.getQueryData>(resourceQueryKey); - expect(cachedData).toEqual({ 'project-new': newProjectData }); - - expect(invalidateSpy).toHaveBeenCalledTimes(2); - expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: [...resourceQueryKey, 'stat'] }); - expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: [...resourceQueryKey, 'list'] }); - }); - - it('should update the existing cache with the new data', async () => { - // Arrange - const { wrapResourceCacheUpdate } = useResourceCacheSync(resourceType); - const initialData = { project_id: 'project-123', name: 'Old Name' }; - const updatedData = { project_id: 'project-123', name: 'Updated Name' }; - - queryClient.setQueryData(resourceQueryKey, { 'project-123': initialData }); - - const mockApiFn = vi.fn().mockResolvedValue(updatedData); - const wrappedFn = wrapResourceCacheUpdate(mockApiFn); - - // Act - await wrappedFn(updatedData); - - // Assert - const cachedData = queryClient.getQueryData>(resourceQueryKey); - expect(cachedData?.[updatedData.project_id]).toEqual(updatedData); - expect(Object.keys(cachedData ?? {}).length).toBe(1); - }); - }); + // describe('2. wrapResourceCacheUpdate', () => { + // const resourceType = 'project'; + // const resourceQueryKey = ['workspace', 'workspace-123', resourceType]; + + // it('should create a new cache with the new data and invalidate the list/stat queries', async () => { + // // Arrange + // const { wrapResourceCacheUpdate } = useResourceCacheSync(resourceType); + // const newProjectData = { project_id: 'project-new', name: 'New Project' }; + // const mockApiFn = vi.fn().mockResolvedValue(newProjectData); + // const wrappedFn = wrapResourceCacheUpdate(mockApiFn); + // const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries'); + + // // Act + // await wrappedFn(newProjectData); + + // // Assert + // const cachedData = queryClient.getQueryData>(resourceQueryKey); + // expect(cachedData).toEqual({ 'project-new': newProjectData }); + + // expect(invalidateSpy).toHaveBeenCalledTimes(2); + // expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: [...resourceQueryKey, 'stat'] }); + // expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: [...resourceQueryKey, 'list'] }); + // }); + + // it('should update the existing cache with the new data', async () => { + // // Arrange + // const { wrapResourceCacheUpdate } = useResourceCacheSync(resourceType); + // const initialData = { project_id: 'project-123', name: 'Old Name' }; + // const updatedData = { project_id: 'project-123', name: 'Updated Name' }; + + // queryClient.setQueryData(resourceQueryKey, { 'project-123': initialData }); + + // const mockApiFn = vi.fn().mockResolvedValue(updatedData); + // const wrappedFn = wrapResourceCacheUpdate(mockApiFn); + + // // Act + // await wrappedFn(updatedData); + + // // Assert + // const cachedData = queryClient.getQueryData>(resourceQueryKey); + // expect(cachedData?.[updatedData.project_id]).toEqual(updatedData); + // expect(Object.keys(cachedData ?? {}).length).toBe(1); + // }); + // }); }); diff --git a/apps/web/src/query/resource-query/shared/composable/use-resource-cache-sync.ts b/apps/web/src/query/resource-query/shared/composable/use-resource-cache-sync.ts index 769efcdab4..5cb8894f3c 100644 --- a/apps/web/src/query/resource-query/shared/composable/use-resource-cache-sync.ts +++ b/apps/web/src/query/resource-query/shared/composable/use-resource-cache-sync.ts @@ -1,20 +1,14 @@ -import type { QueryClient } from '@tanstack/vue-query'; -import { useQueryClient } from '@tanstack/vue-query'; - +import { resourceQueryClient as queryClient } from '@/query/clients'; import { useResourceQueryKey } from '@/query/core/query-key/use-resource-query-key'; -import { RESOURCE_CONFIG_MAP } from '@/query/resource-query/shared/contants/resource-config-map'; -import type { ResourceCacheType, ResourceKeyType } from '@/query/resource-query/shared/types/resource-type'; +import type { ResourceKeyType } from '@/query/resource-query/shared/types/resource-type'; -type Obj = Record; type AnyFn = (...args: any[]) => any; type AwaitedRet = Awaited>; export const useResourceCacheSync = (resourceType: ResourceKeyType) => { - const queryClient = useQueryClient(); - const wrapResourceCacheRefresh = (fn: F) => async (...args: Parameters): Promise> => { const { key: referenceQueryKey } = useResourceQueryKey(resourceType); try { @@ -25,40 +19,44 @@ export const useResourceCacheSync = (resourceType: ResourceKeyType) => { } }; - const wrapResourceCacheUpdate = (fn: F) => async (...args: Parameters): Promise> => { - const res = await Promise.resolve(fn(...args)); - await _updateResourceCache(resourceType, res as Obj, queryClient); - return res; - }; + // TODO: annotaion + // const wrapResourceCacheUpdate = (fn: F) => async (...args: Parameters): Promise> => { + // const res = await Promise.resolve(fn(...args)); + // await _updateResourceCache(resourceType, res as Obj, queryClient); + // return res; + // }; - return { wrapResourceCacheRefresh, wrapResourceCacheUpdate }; + return { + wrapResourceCacheRefresh, + // wrapResourceCacheUpdate, + }; }; -const _updateResourceCache = async ( - resourceType: ResourceKeyType, - newData: T, - queryClient: QueryClient, -) => { - const { key: referenceQueryKey, withSuffix } = useResourceQueryKey(resourceType); - const idKey = RESOURCE_CONFIG_MAP[resourceType].idKey as keyof T; - - if (!idKey || typeof newData !== 'object' || newData === null || !(idKey in newData)) { - throw new Error(`Invalid resource key or data for type: ${resourceType}`); - } - - queryClient.setQueryData>(referenceQueryKey.value, (oldData) => { - const currentResults = (oldData ?? {}) as ResourceCacheType; - const newDataId = newData[idKey] as PropertyKey; - - if (newDataId in currentResults) { - return { ...currentResults, [newDataId]: newData }; - } - return { ...currentResults, [newDataId]: newData }; - }); - - await Promise.all([ - queryClient.invalidateQueries({ queryKey: withSuffix('stat') }), - queryClient.invalidateQueries({ queryKey: withSuffix('list') }), - ]); -}; +// const _updateResourceCache = async ( +// resourceType: ResourceKeyType, +// newData: T, +// queryClient: QueryClient, +// ) => { +// const { key: referenceQueryKey, withSuffix } = useResourceQueryKey(resourceType); +// const idKey = RESOURCE_CONFIG_MAP[resourceType].idKey as keyof T; + +// if (!idKey || typeof newData !== 'object' || newData === null || !(idKey in newData)) { +// throw new Error(`Invalid resource key or data for type: ${resourceType}`); +// } + +// queryClient.setQueryData>(referenceQueryKey.value, (oldData) => { +// const currentResults = (oldData ?? {}) as ResourceCacheType; +// const newDataId = newData[idKey] as PropertyKey; + +// if (newDataId in currentResults) { +// return { ...currentResults, [newDataId]: newData }; +// } +// return { ...currentResults, [newDataId]: newData }; +// }); + +// await Promise.all([ +// queryClient.invalidateQueries({ queryKey: withSuffix('stat') }), +// queryClient.invalidateQueries({ queryKey: withSuffix('list') }), +// ]); +// }; diff --git a/apps/web/src/query/shared/composables/use-watched-query-cache.ts b/apps/web/src/query/shared/composables/use-watched-query-cache.ts index 51beefadcd..e40f63f0f2 100644 --- a/apps/web/src/query/shared/composables/use-watched-query-cache.ts +++ b/apps/web/src/query/shared/composables/use-watched-query-cache.ts @@ -3,7 +3,7 @@ import { ref } from 'vue'; import { hashKey } from '@tanstack/vue-query'; -import { referenceQueryClient as queryClient } from '@/query/clients'; +import { resourceQueryClient as queryClient } from '@/query/clients'; import type { QueryKeyArray } from '@/query/core/query-key/types/query-key-type'; export const useWatchedQueryCache = (queryKey: QueryKeyArray) => { diff --git a/apps/web/src/services/alert-manager/v2/pages/ServiceDetailPage.vue b/apps/web/src/services/alert-manager/v2/pages/ServiceDetailPage.vue index 48176f3afc..1d952ed57f 100644 --- a/apps/web/src/services/alert-manager/v2/pages/ServiceDetailPage.vue +++ b/apps/web/src/services/alert-manager/v2/pages/ServiceDetailPage.vue @@ -10,9 +10,9 @@ import type { Route } from '@cloudforet/mirinae/types/navigation/breadcrumbs/typ import { useAllReferenceDataModel } from '@/query/resource-query/reference-data-model'; import { i18n } from '@/translations'; -import type { FavoriteOptions } from '@/common/modules/favorites/favorite-button/type'; -import { FAVORITE_TYPE } from '@/common/modules/favorites/favorite-button/type'; import { useGnbStore } from '@/common/modules/navigations/stores/gnb-store'; +import type { FavoriteOptions } from '@/common/modules/user-config/favorite/favorite-button/type'; +import { FAVORITE_TYPE } from '@/common/modules/user-config/favorite/favorite-button/type'; import ServiceDetailHeader from '@/services/alert-manager/v2/components/ServiceDetailHeader.vue'; import ServiceDetailTabs from '@/services/alert-manager/v2/components/ServiceDetailTabs.vue'; diff --git a/apps/web/src/services/asset-inventory/components/MetricExplorerLSB.vue b/apps/web/src/services/asset-inventory/components/MetricExplorerLSB.vue index 5407db4861..c0e9c20c96 100644 --- a/apps/web/src/services/asset-inventory/components/MetricExplorerLSB.vue +++ b/apps/web/src/services/asset-inventory/components/MetricExplorerLSB.vue @@ -19,14 +19,14 @@ import { i18n } from '@/translations'; import { useAppContextStore } from '@/store/app-context/app-context-store'; import { useAllReferenceStore } from '@/store/reference/all-reference-store'; -import { useFavoriteStore } from '@/common/modules/favorites/favorite-button/store/favorite-store'; -import { FAVORITE_TYPE } from '@/common/modules/favorites/favorite-button/type'; import LSB from '@/common/modules/navigations/lsb/LSB.vue'; import LSBCollapsibleMenuItem from '@/common/modules/navigations/lsb/modules/LSBCollapsibleMenuItem.vue'; import LSBMenuItem from '@/common/modules/navigations/lsb/modules/LSBMenuItem.vue'; import LSBRouterMenuItem from '@/common/modules/navigations/lsb/modules/LSBRouterMenuItem.vue'; import type { LSBCollapsibleItem, LSBItem } from '@/common/modules/navigations/lsb/type'; import { MENU_ITEM_TYPE } from '@/common/modules/navigations/lsb/type'; +import { useFavoriteStore } from '@/common/modules/user-config/favorite/favorite-button/store/favorite-store'; +import { FAVORITE_TYPE } from '@/common/modules/user-config/favorite/favorite-button/type'; import { gray, yellow } from '@/styles/colors'; diff --git a/apps/web/src/services/asset-inventory/components/MetricExplorerLSBMetricTree.vue b/apps/web/src/services/asset-inventory/components/MetricExplorerLSBMetricTree.vue index e1b3588fe7..3fe0760996 100644 --- a/apps/web/src/services/asset-inventory/components/MetricExplorerLSBMetricTree.vue +++ b/apps/web/src/services/asset-inventory/components/MetricExplorerLSBMetricTree.vue @@ -8,8 +8,8 @@ import type { TreeDisplayMap, TreeNode } from '@cloudforet/mirinae/types/data-di import { useAppContextStore } from '@/store/app-context/app-context-store'; -import FavoriteButton from '@/common/modules/favorites/favorite-button/FavoriteButton.vue'; -import { FAVORITE_TYPE } from '@/common/modules/favorites/favorite-button/type'; +import FavoriteButton from '@/common/modules/user-config/favorite/favorite-button/FavoriteButton.vue'; +import { FAVORITE_TYPE } from '@/common/modules/user-config/favorite/favorite-button/type'; import { gray } from '@/styles/colors'; diff --git a/apps/web/src/services/asset-inventory/pages/MetricExplorerDetailPage.vue b/apps/web/src/services/asset-inventory/pages/MetricExplorerDetailPage.vue index 35085e454c..b52146ec7d 100644 --- a/apps/web/src/services/asset-inventory/pages/MetricExplorerDetailPage.vue +++ b/apps/web/src/services/asset-inventory/pages/MetricExplorerDetailPage.vue @@ -15,9 +15,9 @@ import { useAllReferenceDataModel } from '@/query/resource-query/reference-data- import { queryStringToArray, queryStringToObject, queryStringToString } from '@/lib/router-query-string'; import { useBreadcrumbs } from '@/common/composables/breadcrumbs'; -import type { FavoriteOptions } from '@/common/modules/favorites/favorite-button/type'; -import { FAVORITE_TYPE } from '@/common/modules/favorites/favorite-button/type'; import { useGnbStore } from '@/common/modules/navigations/stores/gnb-store'; +import type { FavoriteOptions } from '@/common/modules/user-config/favorite/favorite-button/type'; +import { FAVORITE_TYPE } from '@/common/modules/user-config/favorite/favorite-button/type'; import MetricExplorerChart from '@/services/asset-inventory/components/MetricExplorerChart.vue'; import MetricExplorerDataTable from '@/services/asset-inventory/components/MetricExplorerDataTable.vue'; diff --git a/apps/web/src/services/cost-explorer/components/CostAnalysisHeader.vue b/apps/web/src/services/cost-explorer/components/CostAnalysisHeader.vue index 32003db877..767fddc534 100644 --- a/apps/web/src/services/cost-explorer/components/CostAnalysisHeader.vue +++ b/apps/web/src/services/cost-explorer/components/CostAnalysisHeader.vue @@ -23,10 +23,10 @@ import { showSuccessMessage } from '@/lib/helper/notice-alert-helper'; import ErrorHandler from '@/common/composables/error/errorHandler'; import { usePageEditableStatus } from '@/common/composables/page-editable-status'; -import { useFavoriteStore } from '@/common/modules/favorites/favorite-button/store/favorite-store'; -import { FAVORITE_TYPE } from '@/common/modules/favorites/favorite-button/type'; -import type { FavoriteOptions } from '@/common/modules/favorites/favorite-button/type'; import { useGnbStore } from '@/common/modules/navigations/stores/gnb-store'; +import { useFavoriteStore } from '@/common/modules/user-config/favorite/favorite-button/store/favorite-store'; +import { FAVORITE_TYPE } from '@/common/modules/user-config/favorite/favorite-button/type'; +import type { FavoriteOptions } from '@/common/modules/user-config/favorite/favorite-button/type'; import { gray } from '@/styles/colors'; diff --git a/apps/web/src/services/cost-explorer/components/CostAnalysisLSB.vue b/apps/web/src/services/cost-explorer/components/CostAnalysisLSB.vue index 7c176c7e1e..438e083ad5 100644 --- a/apps/web/src/services/cost-explorer/components/CostAnalysisLSB.vue +++ b/apps/web/src/services/cost-explorer/components/CostAnalysisLSB.vue @@ -18,13 +18,13 @@ import { useUserStore } from '@/store/user/user-store'; import { getCompoundKeyWithManagedCostQuerySetFavoriteKey } from '@/lib/helper/config-data-helper'; -import { useFavoriteStore } from '@/common/modules/favorites/favorite-button/store/favorite-store'; -import { FAVORITE_TYPE } from '@/common/modules/favorites/favorite-button/type'; -import type { FavoriteConfig } from '@/common/modules/favorites/favorite-button/type'; import LSB from '@/common/modules/navigations/lsb/LSB.vue'; import LSBRouterMenuItem from '@/common/modules/navigations/lsb/modules/LSBRouterMenuItem.vue'; import type { LSBItem, LSBMenu } from '@/common/modules/navigations/lsb/type'; import { MENU_ITEM_TYPE } from '@/common/modules/navigations/lsb/type'; +import { useFavoriteStore } from '@/common/modules/user-config/favorite/favorite-button/store/favorite-store'; +import type { FavoriteConfig } from '@/common/modules/user-config/favorite/favorite-button/type'; +import { FAVORITE_TYPE } from '@/common/modules/user-config/favorite/favorite-button/type'; import { yellow, gray, diff --git a/apps/web/src/services/dashboards/DashboardsLSB.vue b/apps/web/src/services/dashboards/DashboardsLSB.vue index 7f2ac62643..31b2a1eea4 100644 --- a/apps/web/src/services/dashboards/DashboardsLSB.vue +++ b/apps/web/src/services/dashboards/DashboardsLSB.vue @@ -19,15 +19,15 @@ import { useAuthorizationStore } from '@/store/authorization/authorization-store import { MENU_ID } from '@/lib/menu/config'; import { usePageEditableStatus } from '@/common/composables/page-editable-status'; -import { useFavoriteStore } from '@/common/modules/favorites/favorite-button/store/favorite-store'; -import { FAVORITE_TYPE } from '@/common/modules/favorites/favorite-button/type'; -import type { FavoriteConfig } from '@/common/modules/favorites/favorite-button/type'; import LSB from '@/common/modules/navigations/lsb/LSB.vue'; import LSBCollapsibleMenuItem from '@/common/modules/navigations/lsb/modules/LSBCollapsibleMenuItem.vue'; import LSBMenuItem from '@/common/modules/navigations/lsb/modules/LSBMenuItem.vue'; import LSBRouterMenuItem from '@/common/modules/navigations/lsb/modules/LSBRouterMenuItem.vue'; import type { LSBItem, LSBMenu } from '@/common/modules/navigations/lsb/type'; import { MENU_ITEM_TYPE } from '@/common/modules/navigations/lsb/type'; +import { useFavoriteStore } from '@/common/modules/user-config/favorite/favorite-button/store/favorite-store'; +import type { FavoriteConfig } from '@/common/modules/user-config/favorite/favorite-button/type'; +import { FAVORITE_TYPE } from '@/common/modules/user-config/favorite/favorite-button/type'; import { gray } from '@/styles/colors'; diff --git a/apps/web/src/services/dashboards/components/DashboardDeleteModal.vue b/apps/web/src/services/dashboards/components/DashboardDeleteModal.vue index 6a1fe619ab..e9a94f085d 100644 --- a/apps/web/src/services/dashboards/components/DashboardDeleteModal.vue +++ b/apps/web/src/services/dashboards/components/DashboardDeleteModal.vue @@ -12,10 +12,10 @@ import { useUserWorkspaceStore } from '@/store/app-context/workspace/user-worksp import DeleteModal from '@/common/components/modals/DeleteModal.vue'; import ErrorHandler from '@/common/composables/error/errorHandler'; import { useProxyValue } from '@/common/composables/proxy-state'; -import { useFavoriteStore } from '@/common/modules/favorites/favorite-button/store/favorite-store'; -import { FAVORITE_TYPE } from '@/common/modules/favorites/favorite-button/type'; import { useRecentStore } from '@/common/modules/navigations/stores/recent-store'; import { RECENT_TYPE } from '@/common/modules/navigations/type'; +import { useFavoriteStore } from '@/common/modules/user-config/favorite/favorite-button/store/favorite-store'; +import { FAVORITE_TYPE } from '@/common/modules/user-config/favorite/favorite-button/type'; import { useDashboardDeleteMutation } from '@/services/_shared/dashboard/core/composables/mutations/use-dashboard-delete-mutation'; import { useDashboardQuery } from '@/services/dashboards/composables/use-dashboard-query'; diff --git a/apps/web/src/services/dashboards/components/dashboard-folder/DashboardFolderTreeItem.vue b/apps/web/src/services/dashboards/components/dashboard-folder/DashboardFolderTreeItem.vue index 48de417d25..a67359730d 100644 --- a/apps/web/src/services/dashboards/components/dashboard-folder/DashboardFolderTreeItem.vue +++ b/apps/web/src/services/dashboards/components/dashboard-folder/DashboardFolderTreeItem.vue @@ -19,8 +19,8 @@ import { useAppContextStore } from '@/store/app-context/app-context-store'; import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import NewMark from '@/common/components/marks/NewMark.vue'; -import FavoriteButton from '@/common/modules/favorites/favorite-button/FavoriteButton.vue'; -import { FAVORITE_TYPE } from '@/common/modules/favorites/favorite-button/type'; +import FavoriteButton from '@/common/modules/user-config/favorite/favorite-button/FavoriteButton.vue'; +import { FAVORITE_TYPE } from '@/common/modules/user-config/favorite/favorite-button/type'; import { gray, indigo, violet } from '@/styles/colors'; diff --git a/apps/web/src/services/dashboards/components/dashboard-main/DashboardLSBTree.vue b/apps/web/src/services/dashboards/components/dashboard-main/DashboardLSBTree.vue index 72a751db81..c4a0931f35 100644 --- a/apps/web/src/services/dashboards/components/dashboard-main/DashboardLSBTree.vue +++ b/apps/web/src/services/dashboards/components/dashboard-main/DashboardLSBTree.vue @@ -17,8 +17,8 @@ import { ROLE_TYPE } from '@/api-clients/identity/role/constant'; import { useAppContextStore } from '@/store/app-context/app-context-store'; import { useAuthorizationStore } from '@/store/authorization/authorization-store'; -import FavoriteButton from '@/common/modules/favorites/favorite-button/FavoriteButton.vue'; -import { FAVORITE_TYPE } from '@/common/modules/favorites/favorite-button/type'; +import FavoriteButton from '@/common/modules/user-config/favorite/favorite-button/FavoriteButton.vue'; +import { FAVORITE_TYPE } from '@/common/modules/user-config/favorite/favorite-button/type'; import { gray } from '@/styles/colors'; diff --git a/apps/web/src/services/dashboards/components/dashboard-main/DashboardMainBoardList.vue b/apps/web/src/services/dashboards/components/dashboard-main/DashboardMainBoardList.vue index 9f665c8da7..e8f6048068 100644 --- a/apps/web/src/services/dashboards/components/dashboard-main/DashboardMainBoardList.vue +++ b/apps/web/src/services/dashboards/components/dashboard-main/DashboardMainBoardList.vue @@ -16,8 +16,8 @@ import { i18n } from '@/translations'; import { useAppContextStore } from '@/store/app-context/app-context-store'; -import FavoriteButton from '@/common/modules/favorites/favorite-button/FavoriteButton.vue'; -import { FAVORITE_TYPE } from '@/common/modules/favorites/favorite-button/type'; +import FavoriteButton from '@/common/modules/user-config/favorite/favorite-button/FavoriteButton.vue'; +import { FAVORITE_TYPE } from '@/common/modules/user-config/favorite/favorite-button/type'; import DashboardDeleteModal from '@/services/dashboards/components/DashboardDeleteModal.vue'; import { ADMIN_DASHBOARDS_ROUTE } from '@/services/dashboards/routes/admin/route-constant'; diff --git a/apps/web/src/services/dashboards/composables/use-dashboard-bundle-delete-workflow.ts b/apps/web/src/services/dashboards/composables/use-dashboard-bundle-delete-workflow.ts index 6b5653e68f..8f16a97b7e 100644 --- a/apps/web/src/services/dashboards/composables/use-dashboard-bundle-delete-workflow.ts +++ b/apps/web/src/services/dashboards/composables/use-dashboard-bundle-delete-workflow.ts @@ -6,8 +6,8 @@ import { useServiceQueryKey } from '@/query/core/query-key/use-service-query-key import { useUserWorkspaceStore } from '@/store/app-context/workspace/user-workspace-store'; -import { useFavoriteStore } from '@/common/modules/favorites/favorite-button/store/favorite-store'; -import { FAVORITE_TYPE } from '@/common/modules/favorites/favorite-button/type'; +import { useFavoriteStore } from '@/common/modules/user-config/favorite/favorite-button/store/favorite-store'; +import { FAVORITE_TYPE } from '@/common/modules/user-config/favorite/favorite-button/type'; import { useDashboardDeleteMutation } from '@/services/_shared/dashboard/core/composables/mutations/use-dashboard-delete-mutation'; import { useDashboardFolderDeleteMutation } from '@/services/_shared/dashboard/core/composables/mutations/use-dashboard-folder-delete-mutation'; diff --git a/apps/web/src/services/dashboards/pages/DashboardDetailPage.vue b/apps/web/src/services/dashboards/pages/DashboardDetailPage.vue index 46a69d52f5..3a84d58a21 100644 --- a/apps/web/src/services/dashboards/pages/DashboardDetailPage.vue +++ b/apps/web/src/services/dashboards/pages/DashboardDetailPage.vue @@ -9,9 +9,9 @@ import { } from '@cloudforet/mirinae'; import { useBreadcrumbs } from '@/common/composables/breadcrumbs'; -import type { FavoriteOptions } from '@/common/modules/favorites/favorite-button/type'; -import { FAVORITE_TYPE } from '@/common/modules/favorites/favorite-button/type'; import { useGnbStore } from '@/common/modules/navigations/stores/gnb-store'; +import type { FavoriteOptions } from '@/common/modules/user-config/favorite/favorite-button/type'; +import { FAVORITE_TYPE } from '@/common/modules/user-config/favorite/favorite-button/type'; import { DASHBOARD_SHARED_ENTRY_POINT } from '@/services/_shared/dashboard/core/constants/dashboard-shared-constant'; import DashboardDetailLayout from '@/services/_shared/dashboard/dashboard-detail/DashboardDetailLayout.vue'; diff --git a/apps/web/src/services/landing/components/workspace-landing/LandingContents.vue b/apps/web/src/services/landing/components/workspace-landing/LandingContents.vue index 1073f33bb4..607ca5be57 100644 --- a/apps/web/src/services/landing/components/workspace-landing/LandingContents.vue +++ b/apps/web/src/services/landing/components/workspace-landing/LandingContents.vue @@ -1,7 +1,7 @@