diff --git a/apps/web/src/api-clients/dashboard/private-dashboard/schema/api-verbs/list.ts b/apps/web/src/api-clients/dashboard/private-dashboard/schema/api-verbs/list.ts index 2656ae2b91..ab0ab87fd6 100644 --- a/apps/web/src/api-clients/dashboard/private-dashboard/schema/api-verbs/list.ts +++ b/apps/web/src/api-clients/dashboard/private-dashboard/schema/api-verbs/list.ts @@ -5,4 +5,5 @@ export interface PrivateDashboardListParameters { query?: Query; dashboard_id?: string; name?: string; + folder_id?: string; } diff --git a/apps/web/src/api-clients/dashboard/public-dashboard/schema/api-verbs/list.ts b/apps/web/src/api-clients/dashboard/public-dashboard/schema/api-verbs/list.ts index 3987bb3b5f..8fe6638e86 100644 --- a/apps/web/src/api-clients/dashboard/public-dashboard/schema/api-verbs/list.ts +++ b/apps/web/src/api-clients/dashboard/public-dashboard/schema/api-verbs/list.ts @@ -8,4 +8,5 @@ export interface PublicDashboardListParameters { workspace_id?: string; project_id?: string; project_group_id?: string; + folder_id?: string; } diff --git a/apps/web/src/services/dashboard-shared/core/actions/use-dashboard-change-folder-action.ts b/apps/web/src/services/dashboard-shared/core/actions/use-dashboard-change-folder-action.ts new file mode 100644 index 0000000000..c71023334c --- /dev/null +++ b/apps/web/src/services/dashboard-shared/core/actions/use-dashboard-change-folder-action.ts @@ -0,0 +1,55 @@ +import type { ComputedRef } from 'vue'; +import { computed } from 'vue'; + +import { + useMutation, useQueryClient, +} from '@tanstack/vue-query'; + +import type { DashboardChangeFolderParams, 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/query-key/use-service-query-key'; + +interface UseDashboardChangeFolderActionOptions { + dashboardId: ComputedRef; + onSuccess?: (data: DashboardModel, variables: DashboardChangeFolderParams) => void|Promise; + onError?: (error: Error, variables: DashboardChangeFolderParams) => void|Promise; + onSettled?: (data: DashboardModel|undefined, error: Error|null, variables: DashboardChangeFolderParams) => void|Promise; +} + +export const useDashboardChangeFolderAction = (options: UseDashboardChangeFolderActionOptions) => { + const { publicDashboardAPI } = usePublicDashboardApi(); + const { privateDashboardAPI } = usePrivateDashboardApi(); + const queryClient = useQueryClient(); + const { withSuffix: publicDashboardGetQueryKey } = useServiceQueryKey('dashboard', 'public-dashboard', 'get'); + const { withSuffix: privateDashboardGetQueryKey } = useServiceQueryKey('dashboard', 'private-dashboard', 'get'); + + const { + dashboardId, onSuccess, onError, onSettled, + } = options; + + const isPrivate = computed(() => dashboardId.value?.startsWith('private')); + + const changeFolderFn = (params: DashboardChangeFolderParams): Promise => { + if (!dashboardId.value) throw new Error('Dashboard ID is not provided'); + const fetcher = isPrivate.value ? privateDashboardAPI.changeFolder : publicDashboardAPI.changeFolder; + return fetcher(params); + }; + + return useMutation({ + mutationFn: changeFolderFn, + onSuccess: async (data, variables) => { + const _dashboardId = variables.dashboard_id; + const _isPrivate = _dashboardId.startsWith('private'); + const dashboardListQueryKey = _isPrivate ? privateDashboardGetQueryKey(_dashboardId) : publicDashboardGetQueryKey(_dashboardId); + queryClient.invalidateQueries({ queryKey: dashboardListQueryKey }); + if (onSuccess) await onSuccess(data, variables); + }, + onError: (error, variables) => { + if (onError) onError(error, variables); + }, + onSettled: (data, error, variables) => { + if (onSettled) onSettled(data, error, variables); + }, + }); +}; diff --git a/apps/web/src/services/dashboard-shared/core/actions/use-dashboard-clone-action.ts b/apps/web/src/services/dashboard-shared/core/actions/use-dashboard-clone-action.ts new file mode 100644 index 0000000000..ce2442c18c --- /dev/null +++ b/apps/web/src/services/dashboard-shared/core/actions/use-dashboard-clone-action.ts @@ -0,0 +1,118 @@ +import type { ComputedRef } from 'vue'; + +import { useMutation } from '@tanstack/vue-query'; + +import type { ListResponse } from '@/api-clients/_common/schema/api-verbs/list'; +import { RESOURCE_GROUP } from '@/api-clients/_common/schema/constant'; +import type { DashboardCreateParams, DashboardModel } from '@/api-clients/dashboard/_types/dashboard-type'; +import type { WidgetModel } from '@/api-clients/dashboard/_types/widget-type'; +import { usePrivateDashboardApi } from '@/api-clients/dashboard/private-dashboard/composables/use-private-dashboard-api'; +import type { PrivateDashboardCreateParameters } from '@/api-clients/dashboard/private-dashboard/schema/api-verbs/create'; +import { usePrivateWidgetApi } from '@/api-clients/dashboard/private-widget/composables/use-private-widget-api'; +import { usePublicDashboardApi } from '@/api-clients/dashboard/public-dashboard/composables/use-public-dashboard-api'; +import type { PublicDashboardCreateParameters } from '@/api-clients/dashboard/public-dashboard/schema/api-verbs/create'; +import { usePublicWidgetApi } from '@/api-clients/dashboard/public-widget/composables/use-public-widget-api'; + +import { useAllReferenceStore } from '@/store/reference/all-reference-store'; +import { useUserStore } from '@/store/user/user-store'; + +import { useDashboardRouteContext } from '@/services/dashboard-shared/core/composables/use-dashboard-route-context'; +import { getSharedDashboardLayouts } from '@/services/dashboard-shared/core/helpers/dashboard-share-helper'; + + +interface UseDashboardCloneActionOptions { + dashboardId: ComputedRef; + isPrivate?: ComputedRef; + onSuccess?: (data: DashboardModel, variables: DashboardCreateParams) => void|Promise; + onError?: (error: Error, variables: DashboardCreateParams) => void|Promise; + onSettled?: (data: DashboardModel|undefined, error: Error|null, variables: DashboardCreateParams) => void|Promise; +} + +export const useDashboardCloneAction = (options: UseDashboardCloneActionOptions) => { + const { publicDashboardAPI } = usePublicDashboardApi(); + const { privateDashboardAPI } = usePrivateDashboardApi(); + const { privateWidgetAPI } = usePrivateWidgetApi(); + const { publicWidgetAPI } = usePublicWidgetApi(); + const allReferenceStore = useAllReferenceStore(); + const userStore = useUserStore(); + const { + entryPoint, + } = useDashboardRouteContext(); + + const { + dashboardId, isPrivate, onSuccess, onError, onSettled, + } = options; + + + const getDashboardFn = async (): Promise => { + if (!dashboardId.value) throw new Error('Dashboard id not found'); + try { + if (dashboardId.value.startsWith('private')) { + return privateDashboardAPI.get({ dashboard_id: dashboardId.value }); + } + return publicDashboardAPI.get({ dashboard_id: dashboardId.value }); + } catch (error) { + throw new Error('Dashboard not found'); + } + }; + + const listWidgetFn = async (): Promise> => { + if (!dashboardId.value) throw new Error('Dashboard id not found'); + try { + if (dashboardId.value.startsWith('private')) { + return privateWidgetAPI.list({ dashboard_id: dashboardId.value }); + } + return publicWidgetAPI.list({ dashboard_id: dashboardId.value }); + } catch (error) { + throw new Error('Widget list not found'); + } + }; + + + const cloneDashboardFn = async (params: DashboardCreateParams): Promise => { + if (!dashboardId.value) throw new Error('Dashboard id not found'); + + const dashboard = await getDashboardFn(); + const widgetList = await listWidgetFn(); + + const _sharedLayouts = await getSharedDashboardLayouts(dashboard.layouts, widgetList.results || [], allReferenceStore.getters.costDataSource); + + const _sharedDashboard: DashboardCreateParams = { + name: params.name, + layouts: _sharedLayouts, + options: dashboard.options || {}, + labels: dashboard.labels || [], + tags: { created_by: userStore.state.userId }, + vars: dashboard.vars, + vars_schema: dashboard.vars_schema, + }; + if (entryPoint.value === 'ADMIN') { + (_sharedDashboard as PublicDashboardCreateParameters).resource_group = RESOURCE_GROUP.DOMAIN; + } else if (entryPoint.value === 'WORKSPACE') { + if (!isPrivate?.value) { + (_sharedDashboard as PublicDashboardCreateParameters).resource_group = RESOURCE_GROUP.WORKSPACE; + } + } else if (entryPoint.value === 'PROJECT') { + (_sharedDashboard as PublicDashboardCreateParameters).resource_group = RESOURCE_GROUP.PROJECT; + } + + + if (isPrivate?.value) { + return privateDashboardAPI.create(_sharedDashboard as PrivateDashboardCreateParameters); + } + return publicDashboardAPI.create(_sharedDashboard as PublicDashboardCreateParameters); + }; + + return useMutation({ + mutationFn: cloneDashboardFn, + onSuccess: async (data, variables) => { + if (onSuccess) await onSuccess(data, variables); + }, + onError: (error, variables) => { + if (onError) onError(error, variables); + }, + onSettled: (data, error, variables) => { + if (onSettled) onSettled(data, error, variables); + }, + }); +}; diff --git a/apps/web/src/services/dashboard-shared/core/actions/use-dashboard-delete-action.ts b/apps/web/src/services/dashboard-shared/core/actions/use-dashboard-delete-action.ts new file mode 100644 index 0000000000..6c94ab2e84 --- /dev/null +++ b/apps/web/src/services/dashboard-shared/core/actions/use-dashboard-delete-action.ts @@ -0,0 +1,55 @@ +import type { ComputedRef } from 'vue'; +import { computed } from 'vue'; + +import { + useMutation, useQueryClient, +} from '@tanstack/vue-query'; + +import type { DashboardDeleteParams } 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/query-key/use-service-query-key'; + +interface UseDashboardDeleteActionOptions { + dashboardId: ComputedRef; + onSuccess?: (data: unknown, variables: DashboardDeleteParams) => void|Promise; + onError?: (error: Error, variables: DashboardDeleteParams) => void|Promise; + onSettled?: (data: unknown|undefined, error: Error|null, variables: DashboardDeleteParams) => void|Promise; +} + +export const useDashboardDeleteAction = (options: UseDashboardDeleteActionOptions) => { + const { publicDashboardAPI } = usePublicDashboardApi(); + const { privateDashboardAPI } = usePrivateDashboardApi(); + const queryClient = useQueryClient(); + const { withSuffix: publicDashboardGetQueryKey } = useServiceQueryKey('dashboard', 'public-dashboard', 'get'); + const { withSuffix: privateDashboardGetQueryKey } = useServiceQueryKey('dashboard', 'private-dashboard', 'get'); + + const { + dashboardId, onSuccess, onError, onSettled, + } = options; + + const isPrivate = computed(() => dashboardId.value?.startsWith('private')); + + const deleteDashboardFn = (params: DashboardDeleteParams) => { + if (!dashboardId.value) throw new Error('Dashboard ID is not provided'); + const fetcher = isPrivate.value ? privateDashboardAPI.delete : publicDashboardAPI.delete; + return fetcher(params); + }; + + return useMutation({ + mutationFn: deleteDashboardFn, + onSuccess: async (data, variables) => { + const _dashboardId = variables.dashboard_id; + const _isPrivate = _dashboardId.startsWith('private'); + const dashboardListQueryKey = _isPrivate ? privateDashboardGetQueryKey(_dashboardId) : publicDashboardGetQueryKey(_dashboardId); + queryClient.invalidateQueries({ queryKey: dashboardListQueryKey }); + if (onSuccess) await onSuccess(data, variables); + }, + onError: (error, variables) => { + if (onError) onError(error, variables); + }, + onSettled: (data, error, variables) => { + if (onSettled) onSettled(data, error, variables); + }, + }); +}; diff --git a/apps/web/src/services/dashboard-shared/core/actions/use-dashboard-folder-share-action.ts b/apps/web/src/services/dashboard-shared/core/actions/use-dashboard-folder-share-action.ts new file mode 100644 index 0000000000..1499603444 --- /dev/null +++ b/apps/web/src/services/dashboard-shared/core/actions/use-dashboard-folder-share-action.ts @@ -0,0 +1,77 @@ +import type { ComputedRef } from 'vue'; + +import { + useMutation, useQueryClient, +} from '@tanstack/vue-query'; + +import { usePublicDashboardApi } from '@/api-clients/dashboard/public-dashboard/composables/use-public-dashboard-api'; +import { usePublicFolderApi } from '@/api-clients/dashboard/public-folder/composables/use-public-folder-api'; +import type { PublicFolderShareParameters } from '@/api-clients/dashboard/public-folder/schema/api-verbs/share'; +import type { PublicFolderUnshareParameters } from '@/api-clients/dashboard/public-folder/schema/api-verbs/unshare'; +import type { PublicFolderModel } from '@/api-clients/dashboard/public-folder/schema/model'; +import { useServiceQueryKey } from '@/query/query-key/use-service-query-key'; + +interface UseDashboardFolderShareActionOptions { + folderId: ComputedRef; + isShared: ComputedRef; + onSuccess?: (data: PublicFolderModel, variables: PublicFolderShareParameters|PublicFolderUnshareParameters) => void|Promise; + onError?: (error: Error, variables: PublicFolderShareParameters|PublicFolderUnshareParameters) => void|Promise; + onSettled?: (data: PublicFolderModel|undefined, error: Error|null, variables: PublicFolderShareParameters|PublicFolderUnshareParameters) => void|Promise; +} + +export const useDashboardFolderShareAction = (options: UseDashboardFolderShareActionOptions) => { + const { publicFolderAPI } = usePublicFolderApi(); + const { publicDashboardAPI } = usePublicDashboardApi(); + const queryClient = useQueryClient(); + const { withSuffix: publicFolderGetQueryKey } = useServiceQueryKey('dashboard', 'public-folder', 'get'); + const { withSuffix: publicDashboardGetQueryKey } = useServiceQueryKey('dashboard', 'public-dashboard', 'get'); + const { + folderId, isShared, onSuccess, onError, onSettled, + } = options; + + + const listPublicDashboard = async () => { + if (!folderId.value) throw new Error('Folder ID is not provided'); + try { + const _dashboardList = await publicDashboardAPI.list({ + folder_id: folderId.value, + }); + return _dashboardList; + } catch (error) { + console.error(error); + return { + results: [], + }; + } + }; + + const shareFolderFn = async (params: PublicFolderShareParameters|PublicFolderUnshareParameters): Promise => { + if (!folderId.value) throw new Error('Folder ID is not provided'); + if (isShared.value) return publicFolderAPI.unshare(params as PublicFolderUnshareParameters); + return publicFolderAPI.share(params as PublicFolderShareParameters); + }; + + return useMutation({ + mutationFn: shareFolderFn, + onSuccess: async (data, variables) => { + const _folderId = variables.folder_id; + queryClient.invalidateQueries({ queryKey: publicFolderGetQueryKey(_folderId) }); + if (!isShared.value) { + const _dashboardList = await listPublicDashboard(); + const _dashboardIds = _dashboardList.results?.map((dashboard) => dashboard.dashboard_id); + if (_dashboardIds) { + await Promise.all( + _dashboardIds.map((dashboardId) => queryClient.invalidateQueries({ queryKey: publicDashboardGetQueryKey(dashboardId) })), + ); + } + } + if (onSuccess) await onSuccess(data, variables); + }, + onError: (error, variables) => { + if (onError) onError(error, variables); + }, + onSettled: (data, error, variables) => { + if (onSettled) onSettled(data, error, variables); + }, + }); +}; diff --git a/apps/web/src/services/dashboard-shared/core/actions/use-dashboard-share-action.ts b/apps/web/src/services/dashboard-shared/core/actions/use-dashboard-share-action.ts new file mode 100644 index 0000000000..6b054efe9d --- /dev/null +++ b/apps/web/src/services/dashboard-shared/core/actions/use-dashboard-share-action.ts @@ -0,0 +1,50 @@ +import type { ComputedRef } from 'vue'; + +import { + useMutation, useQueryClient, +} from '@tanstack/vue-query'; + +import type { DashboardModel } from '@/api-clients/dashboard/_types/dashboard-type'; +import { usePublicDashboardApi } from '@/api-clients/dashboard/public-dashboard/composables/use-public-dashboard-api'; +import type { PublicDashboardShareParameters } from '@/api-clients/dashboard/public-dashboard/schema/api-verbs/share'; +import type { PublicDashboardUnshareParameters } from '@/api-clients/dashboard/public-dashboard/schema/api-verbs/unshare'; +import { useServiceQueryKey } from '@/query/query-key/use-service-query-key'; + +interface UseDashboardShareActionOptions { + dashboardId: ComputedRef; + isShared: ComputedRef; + onSuccess?: (data: DashboardModel, variables: PublicDashboardShareParameters|PublicDashboardUnshareParameters) => void|Promise; + onError?: (error: Error, variables: PublicDashboardShareParameters|PublicDashboardUnshareParameters) => void|Promise; + onSettled?: (data: DashboardModel|undefined, error: Error|null, variables: PublicDashboardShareParameters|PublicDashboardUnshareParameters) => void|Promise; +} + +export const useDashboardShareAction = (options: UseDashboardShareActionOptions) => { + const { publicDashboardAPI } = usePublicDashboardApi(); + const queryClient = useQueryClient(); + const { withSuffix: publicDashboardGetQueryKey } = useServiceQueryKey('dashboard', 'public-dashboard', 'get'); + + const { + dashboardId, isShared, onSuccess, onError, onSettled, + } = options; + + const shareDashboardFn = (params: PublicDashboardShareParameters|PublicDashboardUnshareParameters): Promise => { + if (!dashboardId.value) throw new Error('Dashboard ID is not provided'); + if (isShared.value) return publicDashboardAPI.unshare(params as PublicDashboardUnshareParameters); + return publicDashboardAPI.share(params as PublicDashboardShareParameters); + }; + + return useMutation({ + mutationFn: shareDashboardFn, + onSuccess: async (data, variables) => { + const _dashboardId = variables.dashboard_id; + queryClient.invalidateQueries({ queryKey: publicDashboardGetQueryKey(_dashboardId) }); + if (onSuccess) await onSuccess(data, variables); + }, + onError: (error, variables) => { + if (onError) onError(error, variables); + }, + onSettled: (data, error, variables) => { + if (onSettled) onSettled(data, error, variables); + }, + }); +}; diff --git a/apps/web/src/services/dashboard-shared/core/actions/use-dashboard-update-action.ts b/apps/web/src/services/dashboard-shared/core/actions/use-dashboard-update-action.ts new file mode 100644 index 0000000000..ce89341069 --- /dev/null +++ b/apps/web/src/services/dashboard-shared/core/actions/use-dashboard-update-action.ts @@ -0,0 +1,55 @@ +import type { ComputedRef } from 'vue'; +import { computed } from 'vue'; + +import { + useMutation, useQueryClient, +} from '@tanstack/vue-query'; + +import type { DashboardModel, DashboardUpdateParams } 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/query-key/use-service-query-key'; + +interface UseDashboardUpdateActionOptions { + dashboardId: ComputedRef; + onSuccess?: (data: DashboardModel, variables: DashboardUpdateParams) => void|Promise; + onError?: (error: Error, variables: DashboardUpdateParams) => void|Promise; + onSettled?: (data: DashboardModel|undefined, error: Error|null, variables: DashboardUpdateParams) => void|Promise; +} + +export const useDashboardUpdateAction = (options: UseDashboardUpdateActionOptions) => { + const { publicDashboardAPI } = usePublicDashboardApi(); + const { privateDashboardAPI } = usePrivateDashboardApi(); + const queryClient = useQueryClient(); + const { withSuffix: publicDashboardGetQueryKey } = useServiceQueryKey('dashboard', 'public-dashboard', 'get'); + const { withSuffix: privateDashboardGetQueryKey } = useServiceQueryKey('dashboard', 'private-dashboard', 'get'); + + const { + dashboardId, onSuccess, onError, onSettled, + } = options; + + const isPrivate = computed(() => dashboardId.value?.startsWith('private')); + + const updateDashboardFn = (params: DashboardUpdateParams): Promise => { + if (!dashboardId.value) throw new Error('Dashboard ID is not provided'); + const fetcher = isPrivate.value ? privateDashboardAPI.update : publicDashboardAPI.update; + return fetcher(params); + }; + + return useMutation({ + mutationFn: updateDashboardFn, + onSuccess: async (data, variables) => { + const _dashboardId = variables.dashboard_id; + const _isPrivate = _dashboardId.startsWith('private'); + const dashboardListQueryKey = _isPrivate ? privateDashboardGetQueryKey(_dashboardId) : publicDashboardGetQueryKey(_dashboardId); + queryClient.invalidateQueries({ queryKey: dashboardListQueryKey }); + if (onSuccess) await onSuccess(data, variables); + }, + onError: (error, variables) => { + if (onError) onError(error, variables); + }, + onSettled: (data, error, variables) => { + if (onSettled) onSettled(data, error, variables); + }, + }); +}; diff --git a/apps/web/src/services/dashboard-shared/core/composables/use-dashboard-manageable.ts b/apps/web/src/services/dashboard-shared/core/composables/use-dashboard-manageable.ts new file mode 100644 index 0000000000..6d4f836f11 --- /dev/null +++ b/apps/web/src/services/dashboard-shared/core/composables/use-dashboard-manageable.ts @@ -0,0 +1,84 @@ +import type { ComputedRef } from 'vue'; +import { computed, reactive } from 'vue'; + +import { RESOURCE_GROUP } from '@/api-clients/_common/schema/constant'; +import type { DashboardModel } from '@/api-clients/dashboard/_types/dashboard-type'; +import type { FolderModel } from '@/api-clients/dashboard/_types/folder-type'; +import type { PublicDashboardModel } from '@/api-clients/dashboard/public-dashboard/schema/model'; +import type { PublicFolderModel } from '@/api-clients/dashboard/public-folder/schema/model'; +import { ROLE_TYPE } from '@/api-clients/identity/role/constant'; + +import { useUserStore } from '@/store/user/user-store'; + +import { useDashboardRouteContext } from '@/services/dashboard-shared/core/composables/use-dashboard-route-context'; +import { useDashboardGetQuery } from '@/services/dashboard-shared/dashboard-detail/composables/use-dashboard-get-query'; + +interface UseDashboardManageableOptions { + dashboardId: ComputedRef; +} + +interface UseDashboardManageableReturn { + isManageable: ComputedRef; + getDashboardManageable: (_dashboard?: DashboardModel) => boolean; + getFolderManageable: (_folder?: FolderModel) => boolean; +} + +export const useDashboardManageable = (options?: UseDashboardManageableOptions): UseDashboardManageableReturn => { + const userStore = useUserStore(); + const { dashboardId } = options ?? {}; + + const { entryPoint } = useDashboardRouteContext(); + + + const storeState = reactive({ + isWorkspaceOwner: computed(() => userStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_OWNER), + }); + + const { dashboard } = useDashboardGetQuery({ + dashboardId: computed(() => dashboardId?.value), + }); + + const isManageable = computed(() => getDashboardManageable(dashboard.value)); + + const getDashboardManageable = (_dashboard?: DashboardModel): boolean => { + if (!_dashboard) return false; + if (entryPoint.value === 'ADMIN') return true; + if (entryPoint.value === 'WORKSPACE') { + if (_dashboard.dashboard_id?.startsWith('private')) return true; + const publicDashboard = _dashboard as PublicDashboardModel; + if (publicDashboard?.shared && publicDashboard?.resource_group === RESOURCE_GROUP.DOMAIN) return false; + if (storeState.isWorkspaceOwner) return true; + return false; + } + if (entryPoint.value === 'PROJECT') { + const publicDashboard = _dashboard as PublicDashboardModel; + if (publicDashboard?.shared) return false; + return true; + } + return false; + }; + const getFolderManageable = (_folder?: FolderModel): boolean => { + if (!_folder) return false; + if (entryPoint.value === 'ADMIN') return true; + if (entryPoint.value === 'WORKSPACE') { + if (_folder.folder_id?.startsWith('private')) return true; + const publicFolder = _folder as PublicFolderModel; + if (publicFolder?.shared && publicFolder?.resource_group === RESOURCE_GROUP.DOMAIN) return false; + if (storeState.isWorkspaceOwner) return true; + return false; + } + if (entryPoint.value === 'PROJECT') { + const publicFolder = _folder as PublicFolderModel; + if (publicFolder?.shared) return false; + return true; + } + return false; + }; + + + return { + isManageable, + getDashboardManageable, + getFolderManageable, + }; +}; diff --git a/apps/web/src/services/dashboard-shared/_composables/use-dashboard-route-context.ts b/apps/web/src/services/dashboard-shared/core/composables/use-dashboard-route-context.ts similarity index 100% rename from apps/web/src/services/dashboard-shared/_composables/use-dashboard-route-context.ts rename to apps/web/src/services/dashboard-shared/core/composables/use-dashboard-route-context.ts diff --git a/apps/web/src/services/dashboard-shared/core/helpers/dashboard-control-menu-helper.ts b/apps/web/src/services/dashboard-shared/core/helpers/dashboard-control-menu-helper.ts new file mode 100644 index 0000000000..ee98cf8fca --- /dev/null +++ b/apps/web/src/services/dashboard-shared/core/helpers/dashboard-control-menu-helper.ts @@ -0,0 +1,97 @@ +import type { MenuItem } from '@cloudforet/mirinae/types/controls/context-menu/type'; + +import type { DashboardModel } from '@/api-clients/dashboard/_types/dashboard-type'; +import type { FolderModel } from '@/api-clients/dashboard/_types/folder-type'; +import type { PublicDashboardModel } from '@/api-clients/dashboard/public-dashboard/schema/model'; +import type { PublicFolderModel } from '@/api-clients/dashboard/public-folder/schema/model'; +import { i18n } from '@/translations'; + +const _getShareMenuItems = (isShared: boolean): MenuItem[] => [ + { + type: 'item', + name: 'share', + label: isShared ? i18n.t('DASHBOARDS.ALL_DASHBOARDS.UNSHARE') : i18n.t('DASHBOARDS.ALL_DASHBOARDS.SHARE'), + icon: 'ic_share', + }, +]; + +export const getControlDashboardMenuItems = ( + dashboardId: string, + manageable: boolean, + dashboard: DashboardModel, + isProject = false, +): MenuItem[] => { + const _isPrivate = dashboardId.startsWith('private'); + const _isDeprecated = dashboard.version === '1.0'; + const _isShared = !!(dashboard as PublicDashboardModel)?.shared; + if (!manageable) { + if (_isDeprecated) return []; + return [ + { + type: 'item', name: 'clone', label: i18n.t('DASHBOARDS.DETAIL.CLONE'), icon: 'ic_clone', + }, + ]; + } + if (_isDeprecated) { + return [ + { + type: 'item', name: 'delete', label: i18n.t('DASHBOARDS.ALL_DASHBOARDS.DELETE'), icon: 'ic_delete', + }, + ]; + } + + const _shareContextMenuItems: MenuItem[] = [ + { + type: 'item', name: 'shareWithCode', label: i18n.t('DASHBOARDS.DETAIL.SHARE_WITH_CODE'), icon: 'ic_share-code', + }, + ...(_isPrivate ? [] : _getShareMenuItems(_isShared)), + { type: 'divider', name: 'divider' }, + ]; + + return [ + { + type: 'item', + name: 'edit', + label: i18n.t('DASHBOARDS.ALL_DASHBOARDS.EDIT_NAME'), + icon: 'ic_edit-text', + }, + { + type: 'item', name: 'clone', label: i18n.t('DASHBOARDS.DETAIL.CLONE'), icon: 'ic_clone', + }, + { + type: 'item', name: 'move', label: i18n.t('DASHBOARDS.DETAIL.MOVE'), icon: 'ic_move', + }, + { type: 'divider', name: 'divider' }, + ...(isProject ? [] : _shareContextMenuItems), + { + type: 'item', name: 'delete', label: i18n.t('DASHBOARDS.ALL_DASHBOARDS.DELETE'), icon: 'ic_delete', + }, + ]; +}; + +export const getControlFolderMenuItems = (folderId: string, manageable: boolean, folder: FolderModel): MenuItem[] => { + const _isPrivate = folderId.startsWith('private'); + const _isShared = !!(folder as PublicFolderModel)?.shared; + if (!manageable) { + return [ + { + type: 'item', name: 'clone', label: i18n.t('DASHBOARDS.DETAIL.CLONE'), icon: 'ic_clone', + }, + ]; + } + + return [ + { + type: 'item', name: 'edit', label: i18n.t('DASHBOARDS.ALL_DASHBOARDS.EDIT_NAME'), icon: 'ic_edit-text', + }, + { + type: 'item', name: 'clone', label: i18n.t('DASHBOARDS.DETAIL.CLONE'), icon: 'ic_clone', + }, + { type: 'divider', name: 'divider' }, + ...(_isPrivate ? [] : _getShareMenuItems(_isShared)), + { type: 'divider', name: 'divider' }, + { + type: 'item', name: 'delete', label: i18n.t('DASHBOARDS.ALL_DASHBOARDS.DELETE'), icon: 'ic_delete', + }, + ]; +}; diff --git a/apps/web/src/services/dashboards/helpers/dashboard-share-helper.ts b/apps/web/src/services/dashboard-shared/core/helpers/dashboard-share-helper.ts similarity index 100% rename from apps/web/src/services/dashboards/helpers/dashboard-share-helper.ts rename to apps/web/src/services/dashboard-shared/core/helpers/dashboard-share-helper.ts diff --git a/apps/web/src/services/dashboard-shared/dashboard-create/DashboardCreateBody.vue b/apps/web/src/services/dashboard-shared/dashboard-create/DashboardCreateBody.vue index 08ebaad752..105593b769 100644 --- a/apps/web/src/services/dashboard-shared/dashboard-create/DashboardCreateBody.vue +++ b/apps/web/src/services/dashboard-shared/dashboard-create/DashboardCreateBody.vue @@ -35,6 +35,7 @@ import { i18n } from '@/translations'; import ConfirmBackModal from '@/common/components/modals/ConfirmBackModal.vue'; import { useGoBack } from '@/common/composables/go-back'; +import { useDashboardRouteContext } from '@/services/dashboard-shared/core/composables/use-dashboard-route-context'; import DashboardCreateStep1 from '@/services/dashboard-shared/dashboard-create/components/DashboardCreateStep1.vue'; import DashboardCreateStep2 from '@/services/dashboard-shared/dashboard-create/components/DashboardCreateStep2.vue'; import { useDashboardCreatePageStore } from '@/services/dashboard-shared/dashboard-create/stores/dashboard-create-page-store'; @@ -42,7 +43,6 @@ import { ADMIN_DASHBOARDS_ROUTE } from '@/services/dashboards/routes/admin/route import { DASHBOARDS_ROUTE } from '@/services/dashboards/routes/route-constant'; import { PROJECT_ROUTE_V2 } from '@/services/project/v2/routes/route-constant'; -import { useDashboardRouteContext } from '../_composables/use-dashboard-route-context'; interface Step { step: number; diff --git a/apps/web/src/services/dashboard-shared/dashboard-create/components/DashboardCreateScopeForm.vue b/apps/web/src/services/dashboard-shared/dashboard-create/components/DashboardCreateScopeForm.vue index 7ad4678a96..0d23d94df7 100644 --- a/apps/web/src/services/dashboard-shared/dashboard-create/components/DashboardCreateScopeForm.vue +++ b/apps/web/src/services/dashboard-shared/dashboard-create/components/DashboardCreateScopeForm.vue @@ -18,10 +18,10 @@ import { useUserStore } from '@/store/user/user-store'; import WorkspaceLogoIcon from '@/common/modules/navigations/top-bar/modules/top-bar-header/WorkspaceLogoIcon.vue'; +import { useDashboardRouteContext } from '@/services/dashboard-shared/core/composables/use-dashboard-route-context'; import { useDashboardCreatePageStore } from '@/services/dashboard-shared/dashboard-create/stores/dashboard-create-page-store'; - interface BoardSet { value: 'WORKSPACE' | 'PRIVATE'; title: TranslateResult; @@ -39,6 +39,11 @@ const storeState = reactive({ isWorkspaceOwner: computed(() => userStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_OWNER), selectedWorkspace: computed(() => userWorkspaceState.getters.currentWorkspace), }); + +const { + entryPoint, +} = useDashboardRouteContext(); + const state = reactive({ dashboardScopeBoardSets: computed(() => { const boardSets: BoardSet[] = [ @@ -79,7 +84,9 @@ onMounted(() => { {{ $t('DASHBOARDS.CREATE.DASHBOARD_SCOPE') }} -
+
{
+
+ + + Only Public + {{ $t('(Everyone in this workspace)') }} + +
diff --git a/apps/web/src/services/dashboards/components/dashboard-detail/DashboardControlButtons.vue b/apps/web/src/services/dashboard-shared/dashboard-detail/components/DashboardControlButtons.vue similarity index 100% rename from apps/web/src/services/dashboards/components/dashboard-detail/DashboardControlButtons.vue rename to apps/web/src/services/dashboard-shared/dashboard-detail/components/DashboardControlButtons.vue diff --git a/apps/web/src/services/dashboard-shared/dashboard-detail/components/DashboardDetailHeader.vue b/apps/web/src/services/dashboard-shared/dashboard-detail/components/DashboardDetailHeader.vue new file mode 100644 index 0000000000..bad14b2b08 --- /dev/null +++ b/apps/web/src/services/dashboard-shared/dashboard-detail/components/DashboardDetailHeader.vue @@ -0,0 +1,194 @@ + + + + + diff --git a/apps/web/src/services/dashboard-shared/dashboard-detail/components/DashboardLabelsButton.vue b/apps/web/src/services/dashboard-shared/dashboard-detail/components/DashboardLabelsButton.vue index 1fa7355318..a86228dac1 100644 --- a/apps/web/src/services/dashboard-shared/dashboard-detail/components/DashboardLabelsButton.vue +++ b/apps/web/src/services/dashboard-shared/dashboard-detail/components/DashboardLabelsButton.vue @@ -2,18 +2,15 @@ import { onClickOutside } from '@vueuse/core'; import { computed, reactive, ref } from 'vue'; -import { useMutation, useQueryClient } from '@tanstack/vue-query'; - import { PButton, PBadge, PPopover } from '@cloudforet/mirinae'; -import type { PrivateDashboardModel } from '@/api-clients/dashboard/private-dashboard/schema/model'; -import type { PublicDashboardModel } from '@/api-clients/dashboard/public-dashboard/schema/model'; import ErrorHandler from '@/common/composables/error/errorHandler'; +import { useDashboardUpdateAction } from '@/services/dashboard-shared/core/actions/use-dashboard-update-action'; +import { useDashboardManageable } from '@/services/dashboard-shared/core/composables/use-dashboard-manageable'; import DashboardLabels from '@/services/dashboard-shared/dashboard-detail/components/DashboardLabels.vue'; import { useDashboardGetQuery } from '@/services/dashboard-shared/dashboard-detail/composables/use-dashboard-get-query'; -import { useDashboardManageable } from '@/services/dashboards/composables/use-dashboard-manageable'; @@ -26,15 +23,12 @@ const labelPopoverRef = ref(null); /* Query */ const { dashboard, - fetcher, - keys, } = useDashboardGetQuery({ dashboardId: computed(() => props.dashboardId), }); const { isManageable } = useDashboardManageable({ dashboardId: computed(() => props.dashboardId), }); -const queryClient = useQueryClient(); const state = reactive({ visible: false, dashboardLabels: computed(() => dashboard.value?.labels || []), @@ -42,20 +36,15 @@ const state = reactive({ }); const handleUpdateLabels = async (labels: string[]) => { - mutate({ + updateDashboard({ dashboard_id: props.dashboardId, labels, }); }; -const { mutate } = useMutation( +const { mutate: updateDashboard } = useDashboardUpdateAction( { - mutationFn: fetcher.updateDashboardFn, - onSuccess: (_dashboard: PublicDashboardModel|PrivateDashboardModel) => { - const isPrivate = _dashboard.dashboard_id.startsWith('private'); - const dashboardQueryKey = isPrivate ? keys.privateDashboardGetQueryKey : keys.publicDashboardGetQueryKey; - queryClient.invalidateQueries({ queryKey: dashboardQueryKey.value }); - }, + dashboardId: computed(() => props.dashboardId), onError: (e) => { ErrorHandler.handleError(e); }, diff --git a/apps/web/src/services/dashboard-shared/dashboard-detail/components/DashboardManageVariableOverlay.vue b/apps/web/src/services/dashboard-shared/dashboard-detail/components/DashboardManageVariableOverlay.vue index 707ac89f7d..5226bc5062 100644 --- a/apps/web/src/services/dashboard-shared/dashboard-detail/components/DashboardManageVariableOverlay.vue +++ b/apps/web/src/services/dashboard-shared/dashboard-detail/components/DashboardManageVariableOverlay.vue @@ -26,7 +26,7 @@ import { showSuccessMessage } from '@/lib/helper/notice-alert-helper'; import DeleteModal from '@/common/components/modals/DeleteModal.vue'; import ErrorHandler from '@/common/composables/error/errorHandler'; -import { useDashboardRouteContext } from '@/services/dashboard-shared/_composables/use-dashboard-route-context'; +import { useDashboardRouteContext } from '@/services/dashboard-shared/core/composables/use-dashboard-route-context'; import DashboardVariablesFormModal from '@/services/dashboard-shared/dashboard-detail/components/DashboardVariablesFormModal.vue'; import { useDashboardGetQuery } from '@/services/dashboard-shared/dashboard-detail/composables/use-dashboard-get-query'; diff --git a/apps/web/src/services/dashboard-shared/dashboard-detail/components/DashboardRefreshDropdown.vue b/apps/web/src/services/dashboard-shared/dashboard-detail/components/DashboardRefreshDropdown.vue index 7f01b154e3..332403943f 100644 --- a/apps/web/src/services/dashboard-shared/dashboard-detail/components/DashboardRefreshDropdown.vue +++ b/apps/web/src/services/dashboard-shared/dashboard-detail/components/DashboardRefreshDropdown.vue @@ -16,9 +16,9 @@ import type { PrivateDashboardModel } from '@/api-clients/dashboard/private-dash import type { PublicDashboardModel } from '@/api-clients/dashboard/public-dashboard/schema/model'; import { i18n } from '@/translations'; +import { useDashboardManageable } from '@/services/dashboard-shared/core/composables/use-dashboard-manageable'; import { useDashboardGetQuery } from '@/services/dashboard-shared/dashboard-detail/composables/use-dashboard-get-query'; import { useDashboardDetailInfoStore } from '@/services/dashboard-shared/dashboard-detail/stores/dashboard-detail-info-store'; -import { useDashboardManageable } from '@/services/dashboards/composables/use-dashboard-manageable'; const REFRESH_INTERVAL_OPTIONS = Object.keys(REFRESH_INTERVAL_OPTIONS_MAP); diff --git a/apps/web/src/services/dashboard-shared/dashboard-detail/components/DashboardToolsetDateDropdown.vue b/apps/web/src/services/dashboard-shared/dashboard-detail/components/DashboardToolsetDateDropdown.vue index bdd7ba12a4..1099029ba5 100644 --- a/apps/web/src/services/dashboard-shared/dashboard-detail/components/DashboardToolsetDateDropdown.vue +++ b/apps/web/src/services/dashboard-shared/dashboard-detail/components/DashboardToolsetDateDropdown.vue @@ -18,11 +18,11 @@ import { i18n } from '@/translations'; import { useI18nDayjs } from '@/common/composables/i18n-dayjs'; +import { useDashboardManageable } from '@/services/dashboard-shared/core/composables/use-dashboard-manageable'; import DashboardToolsetDateCustomModal from '@/services/dashboard-shared/dashboard-detail/components/DashboardToolsetDateCustomModal.vue'; import { useDashboardGetQuery } from '@/services/dashboard-shared/dashboard-detail/composables/use-dashboard-get-query'; import { useDashboardDetailInfoStore } from '@/services/dashboard-shared/dashboard-detail/stores/dashboard-detail-info-store'; -import { useDashboardManageable } from '@/services/dashboards/composables/use-dashboard-manageable'; interface Props { dateRange?: DateRange; diff --git a/apps/web/src/services/dashboard-shared/dashboard-detail/components/DashboardVariablesFormDynamic.vue b/apps/web/src/services/dashboard-shared/dashboard-detail/components/DashboardVariablesFormDynamic.vue index 99d3d3cb6c..dca17cdd39 100644 --- a/apps/web/src/services/dashboard-shared/dashboard-detail/components/DashboardVariablesFormDynamic.vue +++ b/apps/web/src/services/dashboard-shared/dashboard-detail/components/DashboardVariablesFormDynamic.vue @@ -27,7 +27,7 @@ import { } from '@/common/composables/data-source/use-cost-data-source-filter-menu-items'; import { useProxyValue } from '@/common/composables/proxy-state'; -import { useDashboardRouteContext } from '@/services/dashboard-shared/_composables/use-dashboard-route-context'; +import { useDashboardRouteContext } from '@/services/dashboard-shared/core/composables/use-dashboard-route-context'; import { DASHBOARD_GLOBAL_VARIABLES_PRESET_LIST, } from '@/services/dashboard-shared/dashboard-detail/constants/dashboard-global-variable-preset'; diff --git a/apps/web/src/services/dashboard-shared/dashboard-detail/components/DashboardVariablesMoreButton.vue b/apps/web/src/services/dashboard-shared/dashboard-detail/components/DashboardVariablesMoreButton.vue index bdaedb3095..6080291d53 100644 --- a/apps/web/src/services/dashboard-shared/dashboard-detail/components/DashboardVariablesMoreButton.vue +++ b/apps/web/src/services/dashboard-shared/dashboard-detail/components/DashboardVariablesMoreButton.vue @@ -25,7 +25,7 @@ import { showSuccessMessage } from '@/lib/helper/notice-alert-helper'; import ErrorHandler from '@/common/composables/error/errorHandler'; -import { useDashboardRouteContext } from '@/services/dashboard-shared/_composables/use-dashboard-route-context'; +import { useDashboardRouteContext } from '@/services/dashboard-shared/core/composables/use-dashboard-route-context'; import { useDashboardGetQuery } from '@/services/dashboard-shared/dashboard-detail/composables/use-dashboard-get-query'; import { MANAGE_VARIABLES_HASH_NAME } from '@/services/dashboard-shared/dashboard-detail/constants/manage-variable-overlay-constant'; import { getOrderedGlobalVariables } from '@/services/dashboard-shared/dashboard-detail/helpers/dashboard-global-variables-helper'; diff --git a/apps/web/src/services/dashboard-shared/dashboard-detail/components/DashboardVariablesV2.vue b/apps/web/src/services/dashboard-shared/dashboard-detail/components/DashboardVariablesV2.vue index d5f634489e..b09b4f6f3a 100644 --- a/apps/web/src/services/dashboard-shared/dashboard-detail/components/DashboardVariablesV2.vue +++ b/apps/web/src/services/dashboard-shared/dashboard-detail/components/DashboardVariablesV2.vue @@ -14,7 +14,7 @@ import type { import ChangedMark from '@/common/components/marks/ChangedMark.vue'; -import { useDashboardRouteContext } from '@/services/dashboard-shared/_composables/use-dashboard-route-context'; +import { useDashboardRouteContext } from '@/services/dashboard-shared/core/composables/use-dashboard-route-context'; import DashboardGlobalVariableFilter from '@/services/dashboard-shared/dashboard-detail/components/DashboardGlobalVariableFilter.vue'; import DashboardManageVariableImportModal diff --git a/apps/web/src/services/dashboard-shared/dashboard-detail/components/DashboardWidgetContainerV2.vue b/apps/web/src/services/dashboard-shared/dashboard-detail/components/DashboardWidgetContainerV2.vue index 30b28a5d94..b6dc9da4dd 100644 --- a/apps/web/src/services/dashboard-shared/dashboard-detail/components/DashboardWidgetContainerV2.vue +++ b/apps/web/src/services/dashboard-shared/dashboard-detail/components/DashboardWidgetContainerV2.vue @@ -45,6 +45,7 @@ import type { WidgetExpose, WidgetProps, WidgetSize, WidgetOverlayType, } from '@/common/modules/widgets/types/widget-display-type'; +import { useDashboardManageable } from '@/services/dashboard-shared/core/composables/use-dashboard-manageable'; import DashboardReorderSidebar from '@/services/dashboard-shared/dashboard-detail/components/DashboardReorderSidebar.vue'; import { @@ -53,7 +54,6 @@ import { import { useDashboardGetQuery } from '@/services/dashboard-shared/dashboard-detail/composables/use-dashboard-get-query'; import { useDashboardWidgetListQuery } from '@/services/dashboard-shared/dashboard-detail/composables/use-dashboard-widget-list-query'; import { useDashboardDetailInfoStore } from '@/services/dashboard-shared/dashboard-detail/stores/dashboard-detail-info-store'; -import { useDashboardManageable } from '@/services/dashboards/composables/use-dashboard-manageable'; import type { AllReferenceTypeInfo } from '@/services/dashboards/stores/all-reference-type-info-store'; import { useAllReferenceTypeInfoStore, diff --git a/apps/web/src/services/dashboards/components/DashboardDeleteModal.vue b/apps/web/src/services/dashboards/components/DashboardDeleteModal.vue index da243f1dce..48e6ca923f 100644 --- a/apps/web/src/services/dashboards/components/DashboardDeleteModal.vue +++ b/apps/web/src/services/dashboards/components/DashboardDeleteModal.vue @@ -2,10 +2,8 @@ import { computed, reactive } from 'vue'; import { useRouter } from 'vue-router/composables'; -import { useMutation, useQueryClient } from '@tanstack/vue-query'; +import { useQueryClient } from '@tanstack/vue-query'; -import type { PrivateDashboardDeleteParameters } from '@/api-clients/dashboard/private-dashboard/schema/api-verbs/delete'; -import type { PublicDashboardDeleteParameters } from '@/api-clients/dashboard/public-dashboard/schema/api-verbs/delete'; import { i18n } from '@/translations'; import { useAppContextStore } from '@/store/app-context/app-context-store'; @@ -19,6 +17,7 @@ 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 { useDashboardDeleteAction } from '@/services/dashboard-shared/core/actions/use-dashboard-delete-action'; import { useDashboardQuery } from '@/services/dashboards/composables/use-dashboard-query'; import { ADMIN_DASHBOARDS_ROUTE } from '@/services/dashboards/routes/admin/route-constant'; import { DASHBOARDS_ROUTE } from '@/services/dashboards/routes/route-constant'; @@ -43,7 +42,6 @@ const appContextStore = useAppContextStore(); const router = useRouter(); /* Query */ const { - api, keys, } = useDashboardQuery(); const queryClient = useQueryClient(); @@ -68,44 +66,34 @@ const handleDeleteDashboardConfirm = async () => { }; /* Api */ -const deleteDashboardFn = (params: PublicDashboardDeleteParameters|PrivateDashboardDeleteParameters): Promise => { - const _isPrivate = params.dashboard_id.startsWith('private'); - if (_isPrivate) { - return api.privateDashboardAPI.delete(params as PrivateDashboardDeleteParameters); - } - return api.publicDashboardAPI.delete(params as PublicDashboardDeleteParameters); -}; - -const { mutate: deleteDashboard, isPending: loading } = useMutation( - { - mutationFn: deleteDashboardFn, - onSuccess: async (_, params) => { - const _isPrivate = params.dashboard_id.startsWith('private'); - const dashboardListQueryKey = _isPrivate ? keys.privateDashboardListQueryKey : keys.publicDashboardListQueryKey; - await queryClient.invalidateQueries({ queryKey: dashboardListQueryKey.value }); - await recentStore.deleteRecent({ - type: RECENT_TYPE.DASHBOARD, - itemId: props.dashboardId, +const { mutate: deleteDashboard, isPending: loading } = useDashboardDeleteAction({ + dashboardId: computed(() => props.dashboardId), + onSuccess: async (_, params) => { + const _isPrivate = params.dashboard_id.startsWith('private'); + const dashboardListQueryKey = _isPrivate ? keys.privateDashboardListQueryKey : keys.publicDashboardListQueryKey; + await queryClient.invalidateQueries({ queryKey: dashboardListQueryKey.value }); + await recentStore.deleteRecent({ + type: RECENT_TYPE.DASHBOARD, + itemId: props.dashboardId, + }); + const isFavoriteItem = favoriteGetters.dashboardItems.find((item) => item.itemId === params.dashboard_id); + if (isFavoriteItem) { + await favoriteStore.deleteFavorite({ + itemType: FAVORITE_TYPE.DASHBOARD, + workspaceId: storeState.currentWorkspaceId || '', + itemId: params.dashboard_id, }); - const isFavoriteItem = favoriteGetters.dashboardItems.find((item) => item.itemId === params.dashboard_id); - if (isFavoriteItem) { - await favoriteStore.deleteFavorite({ - itemType: FAVORITE_TYPE.DASHBOARD, - workspaceId: storeState.currentWorkspaceId || '', - itemId: params.dashboard_id, - }); - } - state.proxyVisible = false; - const dashboardRouteName = storeState.isAdminMode - ? ADMIN_DASHBOARDS_ROUTE._NAME - : DASHBOARDS_ROUTE._NAME; - await router.replace({ name: dashboardRouteName }).catch(() => {}); - }, - onError(error) { - ErrorHandler.handleRequestError(error, i18n.t('DASHBOARDS.FORM.ALT_E_DELETE_DASHBOARD')); - }, + } + state.proxyVisible = false; + const dashboardRouteName = storeState.isAdminMode + ? ADMIN_DASHBOARDS_ROUTE._NAME + : DASHBOARDS_ROUTE._NAME; + await router.replace({ name: dashboardRouteName }).catch(() => {}); }, -); + onError: (error) => { + ErrorHandler.handleRequestError(error, i18n.t('DASHBOARDS.FORM.ALT_E_DELETE_DASHBOARD')); + }, +}); diff --git a/apps/web/src/services/dashboards/components/dashboard-detail/DashboardCloneModal.vue b/apps/web/src/services/dashboards/components/dashboard-detail/DashboardCloneModal.vue index af97f93af3..de91b8140f 100644 --- a/apps/web/src/services/dashboards/components/dashboard-detail/DashboardCloneModal.vue +++ b/apps/web/src/services/dashboards/components/dashboard-detail/DashboardCloneModal.vue @@ -2,20 +2,14 @@ import { computed, reactive, watch } from 'vue'; import { useRouter } from 'vue-router/composables'; -import { useMutation, useQueryClient } from '@tanstack/vue-query'; +import { useQueryClient } from '@tanstack/vue-query'; import { PButtonModal, PFieldGroup, PTextInput, PToggleButton, } from '@cloudforet/mirinae'; import { getClonedName } from '@cloudforet/utils'; -import { RESOURCE_GROUP } from '@/api-clients/_common/schema/constant'; import type { DashboardCreateParams, DashboardModel, DashboardType } from '@/api-clients/dashboard/_types/dashboard-type'; -import type { WidgetModel } from '@/api-clients/dashboard/_types/widget-type'; -import type { PrivateDashboardCreateParameters } from '@/api-clients/dashboard/private-dashboard/schema/api-verbs/create'; -import { usePrivateWidgetApi } from '@/api-clients/dashboard/private-widget/composables/use-private-widget-api'; -import type { PublicDashboardCreateParameters } from '@/api-clients/dashboard/public-dashboard/schema/api-verbs/create'; -import { usePublicWidgetApi } from '@/api-clients/dashboard/public-widget/composables/use-public-widget-api'; import { ROLE_TYPE } from '@/api-clients/identity/role/constant'; import { i18n } from '@/translations'; @@ -26,12 +20,11 @@ import { useUserStore } from '@/store/user/user-store'; import { showErrorMessage } from '@/lib/helper/notice-alert-helper'; -import ErrorHandler from '@/common/composables/error/errorHandler'; import { useFormValidator } from '@/common/composables/form-validator'; import { useProxyValue } from '@/common/composables/proxy-state'; +import { useDashboardCloneAction } from '@/services/dashboard-shared/core/actions/use-dashboard-clone-action'; import { useDashboardQuery } from '@/services/dashboards/composables/use-dashboard-query'; -import { getSharedDashboardLayouts } from '@/services/dashboards/helpers/dashboard-share-helper'; import { ADMIN_DASHBOARDS_ROUTE } from '@/services/dashboards/routes/admin/route-constant'; import { DASHBOARDS_ROUTE } from '@/services/dashboards/routes/route-constant'; @@ -47,15 +40,12 @@ const props = withDefaults(defineProps(), { }); const emit = defineEmits<{(e: 'update:visible', value: boolean): void; }>(); -const { privateWidgetAPI } = usePrivateWidgetApi(); -const { publicWidgetAPI } = usePublicWidgetApi(); /* Query */ const { publicDashboardList, privateDashboardList, keys, - api, } = useDashboardQuery(); const queryClient = useQueryClient(); @@ -111,22 +101,6 @@ const state = reactive({ }), }); -/* Api */ -const listDashboardWidgets = async (dashboardId: string): Promise => { - try { - const isPrivate = dashboardId.startsWith('private'); - const fetcher = isPrivate - ? privateWidgetAPI.list - : publicWidgetAPI.list; - const res = await fetcher({ - dashboard_id: dashboardId, - }); - return res.results || []; - } catch (e) { - ErrorHandler.handleError(e); - return []; - } -}; const handleUpdateVisible = (visible) => { state.proxyVisible = visible; @@ -138,62 +112,42 @@ const handleChangePrivate = (val: boolean) => { }; const handleConfirm = async () => { if (!isAllValid) return; - const _dashboardWidgets = await listDashboardWidgets(props.dashboardId); - const _sharedLayouts = await getSharedDashboardLayouts(state.targetDashboard.layouts, _dashboardWidgets, storeState.costDataSource); const _sharedDashboard: DashboardCreateParams = { name: name.value, - layouts: _sharedLayouts, - options: state.targetDashboard.options || {}, - labels: state.targetDashboard.labels || [], - tags: { created_by: userStore.state.userId }, - vars: state.targetDashboard.vars, - vars_schema: state.targetDashboard.vars_schema, }; if (storeState.isWorkspaceMember) { state.isPrivate = true; } else if (storeState.isAdminMode) { state.isPrivate = false; - (_sharedDashboard as PublicDashboardCreateParameters).resource_group = RESOURCE_GROUP.DOMAIN; - } else if (!state.isPrivate) { - (_sharedDashboard as PublicDashboardCreateParameters).resource_group = state.targetDashboard?.resource_group || RESOURCE_GROUP.WORKSPACE; - } - - mutate(_sharedDashboard as PrivateDashboardCreateParameters); -}; - -const createDashboard = (params: DashboardCreateParams): Promise => { - if (state.isPrivate) { - return api.privateDashboardAPI.create(params as PrivateDashboardCreateParameters); } - return api.publicDashboardAPI.create(params as PublicDashboardCreateParameters); + mutate(_sharedDashboard as DashboardCreateParams); }; -const { mutate, isPending: dashboardCloneLoading } = useMutation( - { - mutationFn: createDashboard, - onSuccess: (dashboard: DashboardModel) => { - const isPrivate = dashboard.dashboard_id.startsWith('private'); - const dashboardListQueryKey = isPrivate ? keys.privateDashboardListQueryKey : keys.publicDashboardListQueryKey; - queryClient.invalidateQueries({ queryKey: dashboardListQueryKey.value }); - }, - onError: (e) => { - showErrorMessage(i18n.t('DASHBOARDS.FORM.ALT_E_CLONE_DASHBOARD'), e); - }, - onSettled(data) { - state.proxyVisible = false; - if (data?.dashboard_id) { - const dashboardDetailRouteName = storeState.isAdminMode - ? ADMIN_DASHBOARDS_ROUTE.DETAIL._NAME - : DASHBOARDS_ROUTE.DETAIL._NAME; - router.push({ - name: dashboardDetailRouteName, - params: { dashboardId: data.dashboard_id }, - }).catch(() => {}); - } - }, +const { mutate, isPending: dashboardCloneLoading } = useDashboardCloneAction({ + dashboardId: computed(() => props.dashboardId), + isPrivate: computed(() => state.isPrivate), + onSuccess: (dashboard: DashboardModel) => { + const isPrivate = dashboard.dashboard_id.startsWith('private'); + const dashboardListQueryKey = isPrivate ? keys.privateDashboardListQueryKey : keys.publicDashboardListQueryKey; + queryClient.invalidateQueries({ queryKey: dashboardListQueryKey.value }); + }, + onError: (e) => { + showErrorMessage(i18n.t('DASHBOARDS.FORM.ALT_E_CLONE_DASHBOARD'), e); }, -); + onSettled(data) { + state.proxyVisible = false; + if (data?.dashboard_id) { + const dashboardDetailRouteName = storeState.isAdminMode + ? ADMIN_DASHBOARDS_ROUTE.DETAIL._NAME + : DASHBOARDS_ROUTE.DETAIL._NAME; + router.push({ + name: dashboardDetailRouteName, + params: { dashboardId: data.dashboard_id }, + }).catch(() => {}); + } + }, +}); watch(() => props.visible, (visible) => { if (visible) { diff --git a/apps/web/src/services/dashboards/components/dashboard-detail/DashboardDetailHeader.vue b/apps/web/src/services/dashboards/components/dashboard-detail/DashboardDetailHeader.vue index 3a2dd74a0f..bad14b2b08 100644 --- a/apps/web/src/services/dashboards/components/dashboard-detail/DashboardDetailHeader.vue +++ b/apps/web/src/services/dashboards/components/dashboard-detail/DashboardDetailHeader.vue @@ -7,45 +7,30 @@ import { import type { MenuItem } from '@cloudforet/mirinae/types/controls/context-menu/type'; import type { BadgeStyleType, BadgeType } from '@cloudforet/mirinae/types/data-display/badge/type'; -import type { DashboardScope } from '@/api-clients/dashboard/_types/dashboard-type'; -import { ROLE_TYPE } from '@/api-clients/identity/role/constant'; +import { RESOURCE_GROUP } from '@/api-clients/_common/schema/constant'; +import type { FolderModel } from '@/api-clients/dashboard/_types/folder-type'; import { i18n } from '@/translations'; -import { useAppContextStore } from '@/store/app-context/app-context-store'; -import { useUserStore } from '@/store/user/user-store'; import { gray } from '@/styles/colors'; +import { useDashboardManageable } from '@/services/dashboard-shared/core/composables/use-dashboard-manageable'; +import { useDashboardRouteContext } from '@/services/dashboard-shared/core/composables/use-dashboard-route-context'; +import { getControlDashboardMenuItems } from '@/services/dashboard-shared/core/helpers/dashboard-control-menu-helper'; +import DashboardControlButtons from '@/services/dashboard-shared/dashboard-detail/components/DashboardControlButtons.vue'; import DashboardLabelsButton from '@/services/dashboard-shared/dashboard-detail/components/DashboardLabelsButton.vue'; import { useDashboardGetQuery } from '@/services/dashboard-shared/dashboard-detail/composables/use-dashboard-get-query'; -import DashboardControlButtons from '@/services/dashboards/components/dashboard-detail/DashboardControlButtons.vue'; -import { useDashboardControlMenuItems } from '@/services/dashboards/composables/use-dashboard-control-menu-items'; -import { useDashboardFolderQuery } from '@/services/dashboards/composables/use-dashboard-folder-query'; -import { useDashboardManageable } from '@/services/dashboards/composables/use-dashboard-manageable'; -import { useDashboardQuery } from '@/services/dashboards/composables/use-dashboard-query'; -import { useDashboardPageControlStore } from '@/services/dashboards/stores/dashboard-page-control-store'; - - interface Props { dashboardId: string; - templateName?: string; + folderItems?: Array; } -const props = defineProps(); -const dashboardPageControlStore = useDashboardPageControlStore(); -const appContextStore = useAppContextStore(); -const userStore = useUserStore(); - -/* Query */ -const { - publicDashboardList, - privateDashboardList, -} = useDashboardQuery(); -const { - publicFolderList, - privateFolderList, -} = useDashboardFolderQuery(); - +const props = withDefaults(defineProps(), { + folderItems: () => [], +}); +const emit = defineEmits<{(e: 'select-toolset', toolsetId: string|undefined): void; +}>(); +const { entryPoint } = useDashboardRouteContext(); const { dashboard } = useDashboardGetQuery({ dashboardId: computed(() => props.dashboardId), @@ -54,35 +39,14 @@ const { isManageable } = useDashboardManageable({ dashboardId: computed(() => props.dashboardId), }); -const queryState = reactive({ - publicDashboardItems: computed(() => { - const _v2DashboardItems = publicDashboardList.value.filter((d) => d.version !== '1.0'); - if (storeState.isAdminMode) return _v2DashboardItems; - return _v2DashboardItems.filter((d) => !(d.resource_group === 'DOMAIN' && !!d.shared && d.scope === 'PROJECT')); - }), - privateDashboardItems: computed(() => privateDashboardList.value.filter((d) => d.version !== '1.0')), - allDashboardItems: computed(() => [...queryState.publicDashboardItems, ...queryState.privateDashboardItems]), - publicFolderItems: computed(() => { - if (storeState.isAdminMode) return publicFolderList.value; - return publicFolderList.value.filter((d) => !(d.resource_group === 'DOMAIN' && !!d.shared && d.scope === 'PROJECT')); - }), - privateFolderItems: computed(() => privateFolderList.value), - allFolderItems: computed(() => [...queryState.publicFolderItems, ...queryState.privateFolderItems]), +const controlMenuItems = computed(() => { + if (!dashboard.value) return []; + return getControlDashboardMenuItems(props.dashboardId, isManageable.value, dashboard.value, entryPoint.value === 'PROJECT'); }); -const storeState = reactive({ - isAdminMode: computed(() => appContextStore.getters.isAdminMode), - isWorkspaceOwner: computed(() => userStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_OWNER), -}); -const { getControlMenuItems } = useDashboardControlMenuItems({ - isAdminMode: computed(() => storeState.isAdminMode), - isWorkspaceOwner: computed(() => storeState.isWorkspaceOwner), -}); const state = reactive({ isPrivate: computed(() => !!dashboard.value?.dashboard_id.startsWith('private')), isSharedDashboard: computed(() => !!dashboard.value?.shared), - sharedScope: computed(() => dashboard.value?.scope), - selectedSharedScope: 'WORKSPACE' as DashboardScope, showBadge: computed(() => { if (dashboard.value?.user_id) return true; return state.isSharedDashboard; @@ -90,43 +54,46 @@ const state = reactive({ badgeType: computed(() => 'subtle'), badgeStyleType: computed(() => { if (state.isPrivate) return 'gray150'; - if (state.sharedScope === 'PROJECT') return 'primary3'; return 'indigo100'; }), badgeText: computed(() => { if (props.dashboardId?.startsWith('private')) return i18n.t('DASHBOARDS.ALL_DASHBOARDS.PRIVATE'); if (state.isSharedDashboard) { - if (storeState.isAdminMode) { + if (entryPoint.value === 'ADMIN') { if (dashboard.value?.scope === 'PROJECT') { return i18n.t('DASHBOARDS.DETAIL.SHARED_TO_ALL_PROJECTS'); } return i18n.t('DASHBOARDS.DETAIL.SHARED_TO_WORKSPACES'); } - if (state.sharedScope === 'PROJECT') return i18n.t('DASHBOARDS.DETAIL.SHARED_TO_ALL_PROJECTS'); - return i18n.t('DASHBOARDS.DETAIL.SHARED_BY_ADMIN'); + if (entryPoint.value === 'WORKSPACE') { + return i18n.t('DASHBOARDS.DETAIL.SHARED_BY_ADMIN'); + } + if (entryPoint.value === 'PROJECT') { + if (dashboard.value?.resource_group === RESOURCE_GROUP.DOMAIN) return i18n.t('DASHBOARDS.DETAIL.SHARED_BY_ADMIN'); + return i18n.t('DASHBOARDS.DETAIL.SHARED_BY_WORKSPACE'); + } } return ''; }), folderName: computed(() => { - const _folderId = queryState.allDashboardItems.find((d) => d.dashboard_id === props.dashboardId)?.folder_id; - const folder = queryState.allFolderItems.find((d) => d.folder_id === _folderId); - return folder?.name; + if (props.folderItems.length > 0) { + const currentFolder = props.folderItems.find((d) => d.folder_id === dashboard.value?.folder_id); + return currentFolder?.name; + } + return undefined; }), }); /* Event */ const handleSelectItem = (item: MenuItem) => { - if (item.name === 'edit') dashboardPageControlStore.openEditNameModal(props.dashboardId); - if (item.name === 'clone') dashboardPageControlStore.openCloneModal(props.dashboardId); - if (item.name === 'move') dashboardPageControlStore.openMoveModal(props.dashboardId); - if (item.name === 'share') dashboardPageControlStore.openShareModal(props.dashboardId); - if (item.name === 'shareWithCode') dashboardPageControlStore.openShareWithCodeModal(props.dashboardId); - if (item.name === 'delete') dashboardPageControlStore.openDeleteModal(props.dashboardId); + emit('select-toolset', item.name); }; diff --git a/apps/web/src/services/dashboards/types/dashboard-folder-type.ts b/apps/web/src/services/dashboards/types/dashboard-folder-type.ts index 0cda85e08e..81024dd7ab 100644 --- a/apps/web/src/services/dashboards/types/dashboard-folder-type.ts +++ b/apps/web/src/services/dashboards/types/dashboard-folder-type.ts @@ -1,16 +1,22 @@ import type { TreeData } from '@cloudforet/mirinae/types/data-display/tree/tree-view/type'; +import type { DashboardModel } from '@/api-clients/dashboard/_types/dashboard-type'; +import type { FolderModel } from '@/api-clients/dashboard/_types/folder-type'; -export interface DashboardTreeDataType extends TreeData { + +export type DashboardTreeDataType = TreeDataDashboard | TreeDataFolder; + + +type TreeDataDashboard = TreeDataBase & DashboardModel & { + type: 'DASHBOARD'; +}; +type TreeDataFolder = TreeDataBase & FolderModel & { + type: 'FOLDER'; +}; +export interface TreeDataBase extends TreeData { name: string; type: 'FOLDER'| 'DASHBOARD'; id: string; - folderId?: string; - shared?: boolean; - scope?: string; - projectId?: string; - workspaceId?: string; - userId?: string; createdBy?: string; isNew?: boolean; labels?: string[]; diff --git a/apps/web/src/services/project/v2/components/ProjectDashboard.vue b/apps/web/src/services/project/v2/components/ProjectDashboard.vue index 053fa8e92f..6320f018ac 100644 --- a/apps/web/src/services/project/v2/components/ProjectDashboard.vue +++ b/apps/web/src/services/project/v2/components/ProjectDashboard.vue @@ -3,13 +3,11 @@ import { computed, } from 'vue'; -import { PSkeleton } from '@cloudforet/mirinae'; import type { DashboardModel, DashboardFolderModel } from '@/api-clients/dashboard/_types/dashboard-type'; -import BetaMark from '@/common/components/marks/BetaMark.vue'; -import DashboardLabelsButton from '@/services/dashboard-shared/dashboard-detail/components/DashboardLabelsButton.vue'; + import DashboardDetailBody from '@/services/dashboard-shared/dashboard-detail/DashboardDetailBody.vue'; import { useProjectDashboardFolderQuery } from '@/services/project/v2/composables/queries/use-project-dashboard-folder-query'; import { useProjectDashboardQuery } from '@/services/project/v2/composables/queries/use-project-dashboard-query'; @@ -41,35 +39,15 @@ const { const dashboardItems = computed>(() => [...dashboardSharedList.value, ...dashboardList.value]); const dashboardFolderItems = computed>(() => [...dashboardFolderSharedList.value, ...dashboardFolderList.value]); -const currentDashboard = computed(() => dashboardItems.value.find((d) => d.dashboard_id === props.dashboardId)); diff --git a/apps/web/src/services/project/v2/components/ProjectOverview.vue b/apps/web/src/services/project/v2/components/ProjectOverview.vue index 7ce33c9fe1..c415a26b58 100644 --- a/apps/web/src/services/project/v2/components/ProjectOverview.vue +++ b/apps/web/src/services/project/v2/components/ProjectOverview.vue @@ -1,4 +1,6 @@ diff --git a/apps/web/src/services/workspace-home/constants/workspace-home-constant.ts b/apps/web/src/services/workspace-home/constants/workspace-home-constant.ts index c7dc27d53b..a0651d72d7 100644 --- a/apps/web/src/services/workspace-home/constants/workspace-home-constant.ts +++ b/apps/web/src/services/workspace-home/constants/workspace-home-constant.ts @@ -55,16 +55,6 @@ export const WORKSPACE_HOME_DATA_TYPE = { BILLING: 'Billing', } as const; -export const SUMMARY_DATA_TYPE = { - ASSET: 'Asset', - COST: 'Cost', - ACCOUNT: 'Account', -} as const; -export const COST_SUMMARY_STATE_TYPE = { - AGGREGATING: 'AGGREGATING', - CONFIRM: 'CONFIRM', - ESTIMATED: 'ESTIMATED', -} as const; export const SERVICE_ACCOUNT_SUMMARY_STATE_COLOR = { ACTIVE: { iconColor: styles.green[600], diff --git a/apps/web/src/services/workspace-home/shared/components/AccountSummary.vue b/apps/web/src/services/workspace-home/shared/components/AccountSummary.vue index fc26ac9cbd..5959732696 100644 --- a/apps/web/src/services/workspace-home/shared/components/AccountSummary.vue +++ b/apps/web/src/services/workspace-home/shared/components/AccountSummary.vue @@ -8,15 +8,16 @@ import type { EChartsType } from 'echarts/core'; import { init } from 'echarts/core'; import { countBy, isEmpty, map } from 'lodash'; -import { SpaceConnector } from '@cloudforet/core-lib/space-connector'; import { PStatus, PFieldTitle, PLazyImg, PDivider, PLink, PSpinner, PProgressBar, } from '@cloudforet/mirinae'; import { numberFormatter } from '@cloudforet/utils'; -import type { ListResponse } from '@/api-clients/_common/schema/api-verbs/list'; +import { useScopedQuery } from '@/api-clients/_common/composables/use-scoped-query'; +import { useServiceAccountApi } from '@/api-clients/identity/service-account/composables/use-service-account-api'; import type { ServiceAccountListParameters } from '@/api-clients/identity/service-account/schema/api-verbs/list'; import type { ServiceAccountModel } from '@/api-clients/identity/service-account/schema/model'; +import { useServiceQueryKey } from '@/query/query-key/use-service-query-key'; import { i18n } from '@/translations'; import { useUserWorkspaceStore } from '@/store/app-context/workspace/user-workspace-store'; @@ -29,22 +30,18 @@ import type { PageAccessMap } from '@/lib/access-control/config'; import { assetUrlConverter } from '@/lib/helper/asset-helper'; import { MENU_ID } from '@/lib/menu/config'; -import ErrorHandler from '@/common/composables/error/errorHandler'; - import { SERVICE_ACCOUNT_ROUTE } from '@/services/service-account/routes/route-constant'; import { serviceAccountStateSummaryFormatter } from '@/services/workspace-home/composables/use-workspace-home'; -import { SUMMARY_DATA_TYPE } from '@/services/workspace-home/constants/workspace-home-constant'; import EmptySummaryData from '@/services/workspace-home/shared/components/EmptySummaryData.vue'; +import { SUMMARY_DATA_TYPE } from '@/services/workspace-home/shared/constants/summary-type-constant'; +import type { EmptyData } from '@/services/workspace-home/shared/types/empty-data-type'; import type { WidgetStyleType } from '@/services/workspace-home/shared/types/widget-style-type'; -import type { EmptyData } from '@/services/workspace-home/types/workspace-home-type'; const props = withDefaults(defineProps<{ - projectGroupId?: string; - projectId?: string; + projectIds?: string[]; styleType?: WidgetStyleType; }>(), { - projectGroupId: undefined, - projectId: undefined, + projectIds: undefined, styleType: 'default', }); @@ -64,8 +61,27 @@ const storeState = reactive({ provider: computed(() => allReferenceGetters.provider), pageAccessPermissionMap: computed(() => userStore.getters.pageAccessPermissionMap), }); + +const { serviceAccountAPI } = useServiceAccountApi(); +const { key: serviceAccountQueryKey, params } = useServiceQueryKey('identity', 'service-account', 'list', { + params: computed(() => ({ + query: { + filter: props.projectIds?.length ? [{ + k: 'project_id', + v: props.projectIds, + o: 'in', + }] : undefined, + }, + })), +}); +const { data: serviceAccountData, isLoading: isLoadingServiceAccount } = useScopedQuery({ + queryKey: serviceAccountQueryKey, + queryFn: () => serviceAccountAPI.list(params.value), + staleTime: 1000 * 60 * 5, // 5 minutes + gcTime: 1000 * 60 * 30, // 30 minutes +}, ['WORKSPACE']); + const state = reactive({ - loading: true, accessLink: computed(() => !isEmpty(storeState.pageAccessPermissionMap[MENU_ID.SERVICE_ACCOUNT])), writableServiceAccount: computed(() => storeState.pageAccessPermissionMap[MENU_ID.SERVICE_ACCOUNT].write), emptyData: computed(() => { @@ -80,8 +96,8 @@ const state = reactive({ } return result; }), - items: [] as ServiceAccountModel[], - itemsTotalCount: 0, + items: computed(() => serviceAccountData.value?.results || []), + itemsTotalCount: computed(() => serviceAccountData.value?.total_count || 0), itemsByState: computed(() => { const stateCounts = countBy(state.items, 'state'); return map(stateCounts, (count, i) => ({ @@ -136,11 +152,11 @@ const state = reactive({ tooltip: { trigger: 'item', position: 'right', - formatter: (params) => { - const _name = storeState.provider[params.name].label; - const _value = numberFormatter(params.value) || ''; - const percent = getPercent(params.value, state.itemsTotalCount); - return `${params.marker} ${_name}: ${_value} (${percent}%)`; + formatter: (p) => { + const _name = storeState.provider[p.name].label; + const _value = numberFormatter(p.value) || ''; + const percent = getPercent(p.value, state.itemsTotalCount); + return `${p.marker} ${_name}: ${_value} (${percent}%)`; }, }, series: [ @@ -164,20 +180,6 @@ const getPercent = (value: number, total: number) => { const roundedValue = Math.ceil(_value * 100) / 100; return parseFloat(roundedValue.toFixed(2)); }; -const fetchServiceAccountList = async () => { - state.loading = true; - try { - const { results, total_count } = await SpaceConnector.clientV2.identity.serviceAccount.list>(); - state.items = results || []; - state.itemsTotalCount = total_count; - } catch (e) { - ErrorHandler.handleError(e); - state.items = []; - state.itemsTotalCount = 0; - } finally { - state.loading = false; - } -}; watch([() => state.totalChartData, () => totalChartContext.value], ([, chartCtx]) => { if (chartCtx) { @@ -192,10 +194,6 @@ watch([() => state.providerChartData, () => providerChartContext.value], ([, cha } }, { immediate: true }); -watch([() => storeState.currentWorkspaceId, () => storeState.provider], async ([currentWorkspaceId]) => { - if (!currentWorkspaceId) return; - await fetchServiceAccountList(); -}, { immediate: true });