diff --git a/apps/web/src/common/modules/navigations/gnb/GNBNavigationRail.vue b/apps/web/src/common/modules/navigations/gnb/GNBNavigationRail.vue index 405a484a6e..4e6b099f33 100644 --- a/apps/web/src/common/modules/navigations/gnb/GNBNavigationRail.vue +++ b/apps/web/src/common/modules/navigations/gnb/GNBNavigationRail.vue @@ -12,6 +12,7 @@ import { } from '@cloudforet/mirinae'; import type { ContextMenuType } from '@cloudforet/mirinae/src/controls/context-menu/type'; +import { useAppContextStore } from '@/store/app-context/app-context-store'; import { useUserWorkspaceStore } from '@/store/app-context/workspace/user-workspace-store'; import { useDisplayStore } from '@/store/display/display-store'; import type { DisplayMenu } from '@/store/display/type'; @@ -25,9 +26,9 @@ 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 { useCurrentMenuId } from '@/common/composables/current-menu-id'; -import { useProperRouteLocation } from '@/common/composables/proper-route-location'; import { useGnbStore } from '@/common/modules/navigations/stores/gnb-store'; +import { ADMIN_COST_EXPLORER_ROUTE } from '@/services/cost-explorer/routes/admin/route-constant'; import { COST_EXPLORER_ROUTE } from '@/services/cost-explorer/routes/route-constant'; interface GNBMenuType extends DisplayMenu { @@ -38,7 +39,7 @@ interface GNBMenuType extends DisplayMenu { const allReferenceStore = useAllReferenceStore(); const allReferenceGetters = allReferenceStore.getters; -const { getProperRouteLocation } = useProperRouteLocation(); +const appContextStore = useAppContextStore(); const gnbStore = useGnbStore(); const gnbGetters = gnbStore.getters; const userWorkspaceStore = useUserWorkspaceStore(); @@ -52,6 +53,7 @@ const { width } = useWindowSize(); const { currentMenuId } = useCurrentMenuId(); const storeState = reactive({ + isAdminMode: computed(() => appContextStore.getters.isAdminMode), isHideNavRail: computed(() => gnbGetters.isHideNavRail), isMinimizeNavRail: computed(() => gnbGetters.isMinimizeNavRail), currentWorkspaceId: computed(() => userWorkspaceGetters.currentWorkspaceId), @@ -106,9 +108,9 @@ const handleMouseEvent = (value: boolean) => { const handleMenuDescription = (value?: boolean) => { state.isMenuDescription = value; if (value) { - router.push(getProperRouteLocation({ - name: COST_EXPLORER_ROUTE.LANDING._NAME, - })); + router.push({ + name: storeState.isAdminMode ? ADMIN_COST_EXPLORER_ROUTE.LANDING._NAME : COST_EXPLORER_ROUTE.LANDING._NAME, + }).catch(() => {}); } }; const handleMinimizedGnbRail = () => { diff --git a/apps/web/src/common/modules/navigations/top-bar/TopBarMyPage.vue b/apps/web/src/common/modules/navigations/top-bar/TopBarMyPage.vue index c4f142ab18..2bb5ce1c5c 100644 --- a/apps/web/src/common/modules/navigations/top-bar/TopBarMyPage.vue +++ b/apps/web/src/common/modules/navigations/top-bar/TopBarMyPage.vue @@ -41,14 +41,14 @@ const handleBackToWorkspace = () => { return; } if (state.beforeWorkspace === 'landing') { - router.push({ name: ROOT_ROUTE.WORKSPACE._NAME }); + router.push({ name: ROOT_ROUTE.WORKSPACE._NAME }).catch(() => {}); return; } if (state.beforeWorkspace) { router.push({ name: ROOT_ROUTE.WORKSPACE._NAME, params: { workspaceId: state.beforeWorkspace } }); return; } - router.push({ name: ROOT_ROUTE._NAME }); + router.push({ name: ROOT_ROUTE._NAME }).catch(() => {}); }; diff --git a/apps/web/src/common/modules/project/ProjectLinkButton.vue b/apps/web/src/common/modules/project/ProjectLinkButton.vue index e98d1d0944..d867427f15 100644 --- a/apps/web/src/common/modules/project/ProjectLinkButton.vue +++ b/apps/web/src/common/modules/project/ProjectLinkButton.vue @@ -1,14 +1,14 @@ diff --git a/apps/web/src/common/modules/widgets/_composables/use-widget-frame.ts b/apps/web/src/common/modules/widgets/_composables/use-widget-frame.ts index f94e2f788e..c59c0fef64 100644 --- a/apps/web/src/common/modules/widgets/_composables/use-widget-frame.ts +++ b/apps/web/src/common/modules/widgets/_composables/use-widget-frame.ts @@ -11,9 +11,12 @@ import type { WidgetConfig } from '@/api-clients/dashboard/_types/widget-type'; import type { PrivateDataTableModel } from '@/api-clients/dashboard/private-data-table/schema/model'; import type { PublicDataTableModel } from '@/api-clients/dashboard/public-data-table/schema/model'; +import { useAppContextStore } from '@/store/app-context/app-context-store'; +import { useUserWorkspaceStore } from '@/store/app-context/workspace/user-workspace-store'; + import { arrayToQueryString, objectToQueryString, primitiveToQueryString } from '@/lib/router-query-string'; -import { useProperRouteLocation } from '@/common/composables/proper-route-location'; + import { useWidgetFormQuery } from '@/common/modules/widgets/_composables/use-widget-form-query'; import { DATA_SOURCE_DOMAIN, DATA_TABLE_TYPE } from '@/common/modules/widgets/_constants/data-table-constant'; import { getWidgetConfig } from '@/common/modules/widgets/_helpers/widget-config-helper'; @@ -25,10 +28,12 @@ import type { } from '@/common/modules/widgets/types/widget-display-type'; import type { FullDataLink, WidgetFrameProps } from '@/common/modules/widgets/types/widget-frame-type'; +import { ADMIN_ASSET_INVENTORY_ROUTE_V1 } from '@/services/asset-inventory-v1/routes/admin/route-constant'; import { ASSET_INVENTORY_ROUTE_V1 } from '@/services/asset-inventory-v1/routes/route-constant'; import type { MetricFilter } from '@/services/asset-inventory-v1/types/asset-analysis-type'; import { UNIFIED_COST_KEY } from '@/services/cost-explorer/constants/cost-explorer-constant'; import { DYNAMIC_COST_QUERY_SET_PARAMS } from '@/services/cost-explorer/constants/managed-cost-analysis-query-sets'; +import { ADMIN_COST_EXPLORER_ROUTE } from '@/services/cost-explorer/routes/admin/route-constant'; import { COST_EXPLORER_ROUTE } from '@/services/cost-explorer/routes/route-constant'; @@ -40,7 +45,6 @@ interface OverridableWidgetFrameState { noData?: ComputedRef; } type DataTableModel = PublicDataTableModel | PrivateDataTableModel; -const { getProperRouteLocation } = useProperRouteLocation(); const convertDashboardVarsToConsoleFilters = (dashboardVars?: DashboardVars): ConsoleFilter[] => { if (!dashboardVars) return []; @@ -75,7 +79,14 @@ const getRecursiveDataTableIds = (prevValue: string[] = [], dataTable: DataTable } return prevValue.concat(dataTable.data_table_id); }; -const getFullDataLocation = (dataTable: DataTableModel, widgetOptions?: WidgetProps['widgetOptions'], dateRange?: DateRange, dashboardVars?: DashboardVars): Location|undefined => { +const getFullDataLocation = ( + dataTable: DataTableModel, + widgetOptions?: WidgetProps['widgetOptions'], + dateRange?: DateRange, + dashboardVars?: DashboardVars, + isAdminMode: boolean, + workspaceId?: string, +): Location|undefined => { const _granularity = (widgetOptions?.granularity?.value as GranularityValue)?.granularity || 'MONTHLY'; const _groupBy: string[] = dataTable?.options?.group_by?.map((d) => d.key); const _costFilters = [ @@ -98,35 +109,38 @@ const getFullDataLocation = (dataTable: DataTableModel, widgetOptions?: WidgetPr }; if (dataTable?.source_type === DATA_SOURCE_DOMAIN.COST) { const _costDataSourceId = dataTable?.options?.[DATA_SOURCE_DOMAIN.COST]?.data_source_id; - return getProperRouteLocation({ - name: COST_EXPLORER_ROUTE.COST_ANALYSIS.QUERY_SET._NAME, + return { + name: isAdminMode ? ADMIN_COST_EXPLORER_ROUTE.COST_ANALYSIS.QUERY_SET._NAME : COST_EXPLORER_ROUTE.COST_ANALYSIS.QUERY_SET._NAME, params: { + workspaceId: isAdminMode ? undefined : workspaceId, dataSourceId: _costDataSourceId ?? '', costQuerySetId: DYNAMIC_COST_QUERY_SET_PARAMS, }, query: _query, - }); + }; } if (dataTable?.source_type === DATA_SOURCE_DOMAIN.UNIFIED_COST) { - return getProperRouteLocation({ - name: COST_EXPLORER_ROUTE.COST_ANALYSIS.QUERY_SET._NAME, + return { + name: isAdminMode ? ADMIN_COST_EXPLORER_ROUTE.COST_ANALYSIS.QUERY_SET._NAME : COST_EXPLORER_ROUTE.COST_ANALYSIS.QUERY_SET._NAME, params: { + workspaceId: isAdminMode ? undefined : workspaceId, dataSourceId: UNIFIED_COST_KEY, costQuerySetId: DYNAMIC_COST_QUERY_SET_PARAMS, }, query: _query, - }); + }; } if (dataTable?.source_type === DATA_SOURCE_DOMAIN.ASSET) { const _metricId = dataTable?.options?.[DATA_SOURCE_DOMAIN.ASSET]?.metric_id; if (_metricId) { - return getProperRouteLocation({ - name: ASSET_INVENTORY_ROUTE_V1.METRIC_EXPLORER.DETAIL._NAME, + return { + name: isAdminMode ? ADMIN_ASSET_INVENTORY_ROUTE_V1.METRIC_EXPLORER.DETAIL._NAME : ASSET_INVENTORY_ROUTE_V1.METRIC_EXPLORER.DETAIL._NAME, params: { + workspaceId: isAdminMode ? undefined : workspaceId, metricId: _metricId, }, query: _query, - }); + }; } } return undefined; @@ -142,9 +156,13 @@ export const useWidgetFrame = ( } = useWidgetFormQuery({ widgetId: computed(() => props.widgetId), }); + const appContextStore = useAppContextStore(); + const userWorkspaceStore = useUserWorkspaceStore(); const _state = reactive({ + isAdminMode: computed(() => appContextStore.getters.isAdminMode), + currentWorkspaceId: computed(() => userWorkspaceStore.getters.currentWorkspaceId), widgetConfig: computed(() => getWidgetConfig(props.widgetName)), showWidgetHeader: computed(() => props.widgetOptions?.widgetHeader?.value?.toggleValue || false), title: computed(() => { @@ -182,7 +200,7 @@ export const useWidgetFrame = ( const _dataTables = dataTableList.value.filter((d) => _dataTableIds.includes(d.data_table_id)); const _result: FullDataLink[] = []; _dataTables.forEach((d) => { - const _location = getFullDataLocation(d, props.widgetOptions, overrides.dateRange?.value, props.dashboardVars); + const _location = getFullDataLocation(d, props.widgetOptions, overrides.dateRange?.value, props.dashboardVars, _state.isAdminMode, _state.currentWorkspaceId); if (_location) { _result.push({ name: d.options?.data_name, diff --git a/apps/web/src/lib/reference/referenceRouter.ts b/apps/web/src/lib/reference/referenceRouter.ts index 46da700733..663c16cd34 100644 --- a/apps/web/src/lib/reference/referenceRouter.ts +++ b/apps/web/src/lib/reference/referenceRouter.ts @@ -7,6 +7,7 @@ import { QueryHelper } from '@cloudforet/core-lib/query'; import type { Reference, ResourceType } from '@/lib/reference/type'; +import { ADMIN_ASSET_INVENTORY_ROUTE_V1 } from '@/services/asset-inventory-v1/routes/admin/route-constant'; import { ASSET_INVENTORY_ROUTE_V1 } from '@/services/asset-inventory-v1/routes/route-constant'; import { PROJECT_ROUTE } from '@/services/project/routes/route-constant'; @@ -99,7 +100,7 @@ const cloudServiceTypeLinkFormatter: LinkFormatter = (name, data, reference, que type RouterMap = Record; -const routerMap: RouterMap = { +const routerMap = (isAdminMode?: boolean): RouterMap => ({ 'inventory.Server': { name: ASSET_INVENTORY_ROUTE_V1.SERVER._NAME, @@ -117,34 +118,34 @@ const routerMap: RouterMap = { }, 'inventory.Collector': { - name: ASSET_INVENTORY_ROUTE_V1.COLLECTOR._NAME, + name: isAdminMode ? ADMIN_ASSET_INVENTORY_ROUTE_V1.COLLECTOR._NAME : ASSET_INVENTORY_ROUTE_V1.COLLECTOR._NAME, formatter: collectorLinkFormatter, }, 'identity.ServiceAccount': { - name: ASSET_INVENTORY_ROUTE_V1.SERVICE_ACCOUNT.DETAIL._NAME, + name: isAdminMode ? ADMIN_ASSET_INVENTORY_ROUTE_V1.SERVICE_ACCOUNT.DETAIL._NAME : ASSET_INVENTORY_ROUTE_V1.SERVICE_ACCOUNT.DETAIL._NAME, formatter: serviceAccountLinkFormatter, }, 'identity.TrustedAccount': { - name: ASSET_INVENTORY_ROUTE_V1.SERVICE_ACCOUNT.DETAIL._NAME, + name: isAdminMode ? ADMIN_ASSET_INVENTORY_ROUTE_V1.SERVICE_ACCOUNT.DETAIL._NAME : ASSET_INVENTORY_ROUTE_V1.SERVICE_ACCOUNT.DETAIL._NAME, formatter: serviceAccountLinkFormatter, }, 'inventory.CloudService': { - name: ASSET_INVENTORY_ROUTE_V1.CLOUD_SERVICE.SEARCH._NAME, + name: isAdminMode ? ADMIN_ASSET_INVENTORY_ROUTE_V1.CLOUD_SERVICE.SEARCH._NAME : ASSET_INVENTORY_ROUTE_V1.CLOUD_SERVICE.SEARCH._NAME, formatter: cloudServiceLinkFormatter, }, 'inventory.CloudServiceType': { - name: ASSET_INVENTORY_ROUTE_V1.CLOUD_SERVICE.TYPE_SEARCH._NAME, + name: isAdminMode ? ADMIN_ASSET_INVENTORY_ROUTE_V1.CLOUD_SERVICE.TYPE_SEARCH._NAME : ASSET_INVENTORY_ROUTE_V1.CLOUD_SERVICE.TYPE_SEARCH._NAME, formatter: cloudServiceTypeLinkFormatter, }, -}; +}); export const referenceRouter = (data: string, reference: Reference, query?: Location['query']): Location => { - if (routerMap[reference.resource_type]) { - const { name, formatter } = routerMap[reference.resource_type]; + if (routerMap(reference.isAdminMode)[reference.resource_type]) { + const { name, formatter } = routerMap(reference.isAdminMode)[reference.resource_type]; const location = formatter(name, data, reference, query); if (reference.workspace_id) { location.params = { diff --git a/apps/web/src/lib/reference/type.ts b/apps/web/src/lib/reference/type.ts index 058f6505af..8f8c62c4ff 100644 --- a/apps/web/src/lib/reference/type.ts +++ b/apps/web/src/lib/reference/type.ts @@ -29,4 +29,5 @@ export interface Reference { resource_type: ResourceType; reference_key?: string; workspace_id?: string; + isAdminMode?: boolean; } diff --git a/apps/web/src/router/index.ts b/apps/web/src/router/index.ts index d63d092b56..3029bfdf87 100755 --- a/apps/web/src/router/index.ts +++ b/apps/web/src/router/index.ts @@ -1,5 +1,5 @@ import Vue from 'vue'; -import type { RouteConfig } from 'vue-router'; +import type { NavigationGuardNext, Route, RouteConfig } from 'vue-router'; import VueRouter from 'vue-router'; import { clone } from 'lodash'; @@ -93,6 +93,11 @@ export class SpaceRouter { }); SpaceRouter.router.beforeEach(async (to, from, next) => { + /* Redirection to proper route */ + const currentWorkspaceId = userWorkspaceStore.getters.currentWorkspaceId; + const redirected = SpaceRouter.handleWorkspaceRouteRedirection(to, currentWorkspaceId, next); + if (redirected) return; + const { rol: prevRole, wid: prevWorkspaceId } = getDecodedDataFromAccessToken(); const routeScope = getRouteScope(to); @@ -169,4 +174,24 @@ export class SpaceRouter { return SpaceRouter.router; } + + + static handleWorkspaceRouteRedirection(to: Route, currentWorkspaceId: string|undefined, next: NavigationGuardNext): boolean { + const targetRouteScope = getRouteScope(to); + const needsWorkspaceId = targetRouteScope === ROUTE_SCOPE.WORKSPACE && !to.params.workspaceId && currentWorkspaceId; + + if (to.name && needsWorkspaceId) { + next({ + ...to, + name: to.name, + params: { + ...to.params, + workspaceId: currentWorkspaceId, + }, + }); + return true; + } + + return false; + } } diff --git a/apps/web/src/services/alert-manager-v1/components/AlertDetailInfoTable.vue b/apps/web/src/services/alert-manager-v1/components/AlertDetailInfoTable.vue index 608680f52d..9545e706a0 100644 --- a/apps/web/src/services/alert-manager-v1/components/AlertDetailInfoTable.vue +++ b/apps/web/src/services/alert-manager-v1/components/AlertDetailInfoTable.vue @@ -8,18 +8,18 @@ import { import { ACTION_ICON } from '@cloudforet/mirinae/src/navigation/link/type'; import { iso8601Formatter } from '@cloudforet/utils'; -import type { AlertModel } from '@/schema/monitoring/alert/model'; +import type { AlertModel } from '@/schema/alert-manager/alert/model'; import type { EscalationPolicyGetParameters } from '@/schema/monitoring/escalation-policy/api-verbs/get'; import type { EscalationPolicyModel } from '@/schema/monitoring/escalation-policy/model'; import { i18n } from '@/translations'; +import { useUserWorkspaceStore } from '@/store/app-context/workspace/user-workspace-store'; import { useAllReferenceStore } from '@/store/reference/all-reference-store'; import type { UserReferenceMap } from '@/store/reference/user-reference-store'; import type { WebhookReferenceMap } from '@/store/reference/webhook-reference-store'; import { useUserStore } from '@/store/user/user-store'; import ErrorHandler from '@/common/composables/error/errorHandler'; -import { useProperRouteLocation } from '@/common/composables/proper-route-location'; import AlertDetailInfoTableDescription from '@/services/alert-manager-v1/components/AlertDetailInfoTableDescription.vue'; import AlertDetailInfoTableProject from '@/services/alert-manager-v1/components/AlertDetailInfoTableProject.vue'; @@ -36,10 +36,10 @@ const props = defineProps<{ }>(); const allReferenceStore = useAllReferenceStore(); +const userWorkspaceStore = useUserWorkspaceStore(); const alertPageStore = useAlertPageStore(); const alertPageState = alertPageStore.state; const userStore = useUserStore(); -const { getProperRouteLocation } = useProperRouteLocation(); const state = reactive({ @@ -127,7 +127,7 @@ const getEscalationPolicy = async () => {