From 3211ef94c9505c377f5cf95de78095d648dcef6d Mon Sep 17 00:00:00 2001 From: Yongtae Park Date: Thu, 10 Apr 2025 18:46:07 +0900 Subject: [PATCH 1/9] feat(project-folder): create project-dashboard form modal (create, update) Signed-off-by: samuel.park --- .../ProjectDashboardFolderFormModal.vue | 188 ++++++++++++++++++ .../v2/components/ProjectDetailTab.vue | 5 +- .../use-project-dashboard-folder-query.ts | 23 ++- .../project/v2/pages/ProjectMainPage.vue | 9 +- .../v2/stores/project-page-modal-store.ts | 13 ++ 5 files changed, 234 insertions(+), 4 deletions(-) create mode 100644 apps/web/src/services/project/v2/components/ProjectDashboardFolderFormModal.vue diff --git a/apps/web/src/services/project/v2/components/ProjectDashboardFolderFormModal.vue b/apps/web/src/services/project/v2/components/ProjectDashboardFolderFormModal.vue new file mode 100644 index 0000000000..5562d1ea42 --- /dev/null +++ b/apps/web/src/services/project/v2/components/ProjectDashboardFolderFormModal.vue @@ -0,0 +1,188 @@ + + + + + diff --git a/apps/web/src/services/project/v2/components/ProjectDetailTab.vue b/apps/web/src/services/project/v2/components/ProjectDetailTab.vue index c5965840ca..1a5cc15a0a 100644 --- a/apps/web/src/services/project/v2/components/ProjectDetailTab.vue +++ b/apps/web/src/services/project/v2/components/ProjectDetailTab.vue @@ -14,7 +14,7 @@ import ProjectOverview from '@/services/project/v2/components/ProjectOverview.vu 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'; import { PROJECT_ROUTE_V2 } from '@/services/project/v2/routes/route-constant'; - +import { useProjectPageModalStore } from '@/services/project/v2/stores/project-page-modal-store'; const props = defineProps<{ projectId?: string, @@ -23,6 +23,7 @@ const props = defineProps<{ }>(); const emit = defineEmits<{(e: 'update:dashboard-id', value?: string): void;}>(); const router = useRouter(); +const projectPageModalStore = useProjectPageModalStore(); const activeTab = ref('overview'); watch(() => props.dashboardId, (did) => { @@ -120,7 +121,7 @@ const handleCreateProjectDashboard = (item: string) => { }, }); } else if (item === 'folder') { - console.log('create folder'); + projectPageModalStore.openCreateFolderFormModal(); } }; diff --git a/apps/web/src/services/project/v2/composables/queries/use-project-dashboard-folder-query.ts b/apps/web/src/services/project/v2/composables/queries/use-project-dashboard-folder-query.ts index a59fccabee..5489c5ed78 100644 --- a/apps/web/src/services/project/v2/composables/queries/use-project-dashboard-folder-query.ts +++ b/apps/web/src/services/project/v2/composables/queries/use-project-dashboard-folder-query.ts @@ -2,6 +2,8 @@ import { computed, type ComputedRef, } from 'vue'; +import { useQueryClient } from '@tanstack/vue-query'; + import { useScopedQuery } from '@/api-clients/_common/composables/use-scoped-query'; import { usePublicFolderApi } from '@/api-clients/dashboard/public-folder/composables/use-public-folder-api'; import type { PublicFolderListParameters } from '@/api-clients/dashboard/public-folder/schema/api-verbs/list'; @@ -17,10 +19,11 @@ const GC_TIME = 1000 * 60 * 5; export const useProjectDashboardFolderQuery = (options: { - projectId: ComputedRef, + projectId?: ComputedRef, projectGroupId?: ComputedRef, }) => { const { publicFolderAPI } = usePublicFolderApi(); + const queryClient = useQueryClient(); const projectPageContext = computed(() => { if (options.projectGroupId?.value) { @@ -52,6 +55,11 @@ export const useProjectDashboardFolderQuery = (options: { params: computed(() => ({ project_id: options.projectId?.value, project_group_id: options.projectGroupId?.value, + query: { + filter: [ + { k: 'resource_group', v: 'PROJECT', o: 'eq' }, + ], + }, })), }); @@ -77,10 +85,23 @@ export const useProjectDashboardFolderQuery = (options: { const isLoading = computed(() => dashboardFolderSharedListQuery.isFetching.value || dashboardFolderListQuery.isFetching.value); + const setQueryData = (newData: PublicFolderModel[]) => { + console.debug('setQueryData', newData); + queryClient.setQueryData(dashboardFolderListQueryKey.value, { + results: [...(dashboardFolderListQuery.data.value ?? []), ...newData], + }); + }; + + const invalidateAllQueries = () => { + queryClient.invalidateQueries({ queryKey: dashboardFolderListQueryKey.value }); + }; + return { dashboardFolderSharedList: computed(() => dashboardFolderSharedListQuery.data.value ?? []), dashboardFolderList: computed(() => dashboardFolderListQuery.data.value ?? []), isLoading, + setQueryData, + invalidateAllQueries, }; }; diff --git a/apps/web/src/services/project/v2/pages/ProjectMainPage.vue b/apps/web/src/services/project/v2/pages/ProjectMainPage.vue index 3c534c0629..feea1017fc 100644 --- a/apps/web/src/services/project/v2/pages/ProjectMainPage.vue +++ b/apps/web/src/services/project/v2/pages/ProjectMainPage.vue @@ -32,6 +32,7 @@ const ProjectMemberInviteModal = () => import('@/services/project/v2/components/ const ProjectTagsModal = () => import('@/services/project/v2/components/ProjectTagsModal.vue'); const ProjectFormModal = () => import('@/services/project/v2/components/ProjectFormModal.vue'); const ProjectGroupFormModal = () => import('@/services/project/v2/components/ProjectGroupFormModal.vue'); +const ProjectDashboardFolderFormModal = () => import('@/services/project/v2/components/ProjectDashboardFolderFormModal.vue'); const props = defineProps<{ projectGroupOrProjectId?: string; @@ -125,7 +126,7 @@ const handleUpdateDashboardId = (id?: string) => { :target-type="props.projectGroupOrProjectId ? (projectId ? 'project' : 'projectGroup') : undefined" /> - { :target-parent-group-id="targetParentGroupId" @created="handleCreated" /> + + + + diff --git a/apps/web/src/services/project/v2/stores/project-page-modal-store.ts b/apps/web/src/services/project/v2/stores/project-page-modal-store.ts index 278cce665d..a5e52fc05f 100644 --- a/apps/web/src/services/project/v2/stores/project-page-modal-store.ts +++ b/apps/web/src/services/project/v2/stores/project-page-modal-store.ts @@ -12,6 +12,8 @@ export const useProjectPageModalStore = defineStore('project-page-modal', () => projectFormModalVisible: false, targetId: '' as string|undefined, targetType: undefined as 'project'|'projectGroup'|undefined, + + folderFormModalVisible: false, }); const actions = { @@ -109,6 +111,17 @@ export const useProjectPageModalStore = defineStore('project-page-modal', () => closeFormModal() { state.projectFormModalVisible = false; }, + openCreateFolderFormModal() { + state.targetId = undefined; + state.folderFormModalVisible = true; + }, + openEditFolderFormModal(targetFolderId: string) { + state.targetId = targetFolderId; + state.folderFormModalVisible = true; + }, + closeFolderFormModal() { + state.folderFormModalVisible = false; + }, // overal resetTarget() { state.targetId = undefined; From ff58b519ca3dd457b0693c43bbf414a9ed670d11 Mon Sep 17 00:00:00 2001 From: Yongtae Park Date: Thu, 10 Apr 2025 19:56:10 +0900 Subject: [PATCH 2/9] feat(project-dashboard): create project-dashboard action modals Signed-off-by: samuel.park --- .../v2/components/ProjectDashboard.vue | 9 +- .../ProjectDashboardChangeFolderModal.vue | 156 ++++++++++++++++++ .../ProjectDashboardDeleteModal.vue | 75 +++++++++ .../ProjectDashboardNameEditModal.vue | 128 ++++++++++++++ 4 files changed, 367 insertions(+), 1 deletion(-) create mode 100644 apps/web/src/services/project/v2/components/ProjectDashboardChangeFolderModal.vue create mode 100644 apps/web/src/services/project/v2/components/ProjectDashboardDeleteModal.vue create mode 100644 apps/web/src/services/project/v2/components/ProjectDashboardNameEditModal.vue diff --git a/apps/web/src/services/project/v2/components/ProjectDashboard.vue b/apps/web/src/services/project/v2/components/ProjectDashboard.vue index 6320f018ac..42e4cec999 100644 --- a/apps/web/src/services/project/v2/components/ProjectDashboard.vue +++ b/apps/web/src/services/project/v2/components/ProjectDashboard.vue @@ -11,6 +11,7 @@ import type { DashboardModel, DashboardFolderModel } from '@/api-clients/dashboa 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'; +import { useProjectPageModalStore } from '@/services/project/v2/stores/project-page-modal-store'; interface Props { projectId?: string; @@ -19,7 +20,7 @@ interface Props { } const props = defineProps(); - +const projectPageModalStore = useProjectPageModalStore(); /* Query */ const { dashboardList, @@ -40,6 +41,11 @@ const { const dashboardItems = computed>(() => [...dashboardSharedList.value, ...dashboardList.value]); const dashboardFolderItems = computed>(() => [...dashboardFolderSharedList.value, ...dashboardFolderList.value]); +const handleSelectToolset = (toolsetId: string|undefined) => { + if (toolsetId === 'edit') projectPageModalStore.openDashboardNameEditModal(props.dashboardId); + if (toolsetId === 'move') projectPageModalStore.openDashboardChangeFolderModal(props.dashboardId); + if (toolsetId === 'delete') projectPageModalStore.openDashboardDeleteModal(props.dashboardId); +}; diff --git a/apps/web/src/services/project/v2/components/ProjectDashboardChangeFolderModal.vue b/apps/web/src/services/project/v2/components/ProjectDashboardChangeFolderModal.vue new file mode 100644 index 0000000000..2a7cf0e710 --- /dev/null +++ b/apps/web/src/services/project/v2/components/ProjectDashboardChangeFolderModal.vue @@ -0,0 +1,156 @@ + + + + + diff --git a/apps/web/src/services/project/v2/components/ProjectDashboardDeleteModal.vue b/apps/web/src/services/project/v2/components/ProjectDashboardDeleteModal.vue new file mode 100644 index 0000000000..d72829e99d --- /dev/null +++ b/apps/web/src/services/project/v2/components/ProjectDashboardDeleteModal.vue @@ -0,0 +1,75 @@ + + + diff --git a/apps/web/src/services/project/v2/components/ProjectDashboardNameEditModal.vue b/apps/web/src/services/project/v2/components/ProjectDashboardNameEditModal.vue new file mode 100644 index 0000000000..4224ee274d --- /dev/null +++ b/apps/web/src/services/project/v2/components/ProjectDashboardNameEditModal.vue @@ -0,0 +1,128 @@ + + + + + From 61e9e182b5d9e959493dea6bb0112fc2754dccc8 Mon Sep 17 00:00:00 2001 From: Yongtae Park Date: Thu, 10 Apr 2025 19:56:44 +0900 Subject: [PATCH 3/9] feat(project-dashboard): apply action modal Signed-off-by: samuel.park --- .../project/v2/pages/ProjectMainPage.vue | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/apps/web/src/services/project/v2/pages/ProjectMainPage.vue b/apps/web/src/services/project/v2/pages/ProjectMainPage.vue index feea1017fc..44900d160a 100644 --- a/apps/web/src/services/project/v2/pages/ProjectMainPage.vue +++ b/apps/web/src/services/project/v2/pages/ProjectMainPage.vue @@ -33,7 +33,9 @@ const ProjectTagsModal = () => import('@/services/project/v2/components/ProjectT const ProjectFormModal = () => import('@/services/project/v2/components/ProjectFormModal.vue'); const ProjectGroupFormModal = () => import('@/services/project/v2/components/ProjectGroupFormModal.vue'); const ProjectDashboardFolderFormModal = () => import('@/services/project/v2/components/ProjectDashboardFolderFormModal.vue'); - +const ProjectDashboardNameEditModal = () => import('@/services/project/v2/components/ProjectDashboardNameEditModal.vue'); +const ProjectDashboardChangeFolderModal = () => import('@/services/project/v2/components/ProjectDashboardChangeFolderModal.vue'); +const ProjectDashboardDeleteModal = () => import('@/services/project/v2/components/ProjectDashboardDeleteModal.vue'); const props = defineProps<{ projectGroupOrProjectId?: string; dashboardId?: string; @@ -154,9 +156,15 @@ const handleUpdateDashboardId = (id?: string) => { - - - + + + From 0e2110b590189402d8ca43973823293a10692bec Mon Sep 17 00:00:00 2001 From: Yongtae Park Date: Thu, 10 Apr 2025 19:57:35 +0900 Subject: [PATCH 4/9] feat(project-dashboard): create project-dashboard action client state Signed-off-by: samuel.park --- .../actions/use-dashboard-delete-action.ts | 4 +-- .../queries/use-project-dashboard-query.ts | 16 +++++++++- .../v2/stores/project-page-modal-store.ts | 30 +++++++++++++++++-- 3 files changed, 44 insertions(+), 6 deletions(-) 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 index 6c94ab2e84..c02bf92fc8 100644 --- 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 @@ -41,8 +41,8 @@ export const useDashboardDeleteAction = (options: UseDashboardDeleteActionOption 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 }); + const dashboardGetQueryKey = _isPrivate ? privateDashboardGetQueryKey(_dashboardId) : publicDashboardGetQueryKey(_dashboardId); + queryClient.invalidateQueries({ queryKey: dashboardGetQueryKey }); if (onSuccess) await onSuccess(data, variables); }, onError: (error, variables) => { diff --git a/apps/web/src/services/project/v2/composables/queries/use-project-dashboard-query.ts b/apps/web/src/services/project/v2/composables/queries/use-project-dashboard-query.ts index ca766a2463..507860c9d8 100644 --- a/apps/web/src/services/project/v2/composables/queries/use-project-dashboard-query.ts +++ b/apps/web/src/services/project/v2/composables/queries/use-project-dashboard-query.ts @@ -2,6 +2,8 @@ import { computed, type ComputedRef, } from 'vue'; +import { useQueryClient } from '@tanstack/vue-query'; + import { useScopedQuery } from '@/api-clients/_common/composables/use-scoped-query'; import { usePublicDashboardApi } from '@/api-clients/dashboard/public-dashboard/composables/use-public-dashboard-api'; import type { PublicDashboardListParameters } from '@/api-clients/dashboard/public-dashboard/schema/api-verbs/list'; @@ -21,7 +23,7 @@ export const useProjectDashboardQuery = (options: { projectGroupId?: ComputedRef, }) => { const { publicDashboardAPI } = usePublicDashboardApi(); - + const queryClient = useQueryClient(); const projectPageContext = computed(() => { if (options.projectGroupId?.value) { return 'PROJECT_GROUP'; @@ -83,10 +85,22 @@ export const useProjectDashboardQuery = (options: { const isLoading = computed(() => dashboardSharedListQuery.isFetching.value || dashboardListQuery.isFetching.value); + const setQueryData = (newData: PublicDashboardModel[]) => { + queryClient.setQueryData(dashboardListQueryKey.value, { + results: [...(dashboardListQuery.data.value ?? []), ...newData], + }); + }; + + const invalidateAllQueries = () => { + queryClient.invalidateQueries({ queryKey: dashboardListQueryKey.value }); + }; + return { dashboardSharedList: computed(() => dashboardSharedListQuery.data.value ?? []), dashboardList: computed(() => dashboardListQuery.data.value ?? []), isLoading, + setQueryData, + invalidateAllQueries, }; }; diff --git a/apps/web/src/services/project/v2/stores/project-page-modal-store.ts b/apps/web/src/services/project/v2/stores/project-page-modal-store.ts index a5e52fc05f..8d82b6649f 100644 --- a/apps/web/src/services/project/v2/stores/project-page-modal-store.ts +++ b/apps/web/src/services/project/v2/stores/project-page-modal-store.ts @@ -4,16 +4,20 @@ import { defineStore } from 'pinia'; export const useProjectPageModalStore = defineStore('project-page-modal', () => { const state = reactive({ + targetId: '' as string|undefined, + targetType: undefined as 'project'|'projectGroup'|undefined, + manageMemberModalVisible: false, deleteModalVisible: false, moveModalVisible: false, inviteMemberModalVisible: false, manageTagsModalVisible: false, projectFormModalVisible: false, - targetId: '' as string|undefined, - targetType: undefined as 'project'|'projectGroup'|undefined, folderFormModalVisible: false, + dashboardNameEditModalVisible: false, + dashboardChangeFolderModalVisible: false, + dashboardDeleteModalVisible: false, }); const actions = { @@ -122,7 +126,27 @@ export const useProjectPageModalStore = defineStore('project-page-modal', () => closeFolderFormModal() { state.folderFormModalVisible = false; }, - // overal + openDashboardNameEditModal(targetDashboardId: string) { + state.targetId = targetDashboardId; + state.dashboardNameEditModalVisible = true; + }, + closeDashboardNameEditModal() { + state.dashboardNameEditModalVisible = false; + }, + openDashboardChangeFolderModal(targetDashboardId: string) { + state.targetId = targetDashboardId; + state.dashboardChangeFolderModalVisible = true; + }, + closeDashboardChangeFolderModal() { + state.dashboardChangeFolderModalVisible = false; + }, + openDashboardDeleteModal(targetDashboardId: string) { + state.targetId = targetDashboardId; + state.dashboardDeleteModalVisible = true; + }, + closeDashboardDeleteModal() { + state.dashboardDeleteModalVisible = false; + }, resetTarget() { state.targetId = undefined; state.targetType = undefined; From 741c4d9f07b02b9dbe8f09f301caaa4c8383e3c4 Mon Sep 17 00:00:00 2001 From: Yongtae Park Date: Thu, 10 Apr 2025 19:57:51 +0900 Subject: [PATCH 5/9] chore: small fix Signed-off-by: samuel.park --- .../dashboard-shared/dashboard-detail/DashboardDetailBody.vue | 3 ++- .../components/dashboard-detail/DashboardDetailHeader.vue | 2 +- .../composables/queries/use-project-dashboard-folder-query.ts | 1 - 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/web/src/services/dashboard-shared/dashboard-detail/DashboardDetailBody.vue b/apps/web/src/services/dashboard-shared/dashboard-detail/DashboardDetailBody.vue index 9cdb4f9616..fa0cfb23a4 100644 --- a/apps/web/src/services/dashboard-shared/dashboard-detail/DashboardDetailBody.vue +++ b/apps/web/src/services/dashboard-shared/dashboard-detail/DashboardDetailBody.vue @@ -42,7 +42,7 @@ import { PROJECT_ROUTE_V2 } from '@/services/project/v2/routes/route-constant'; interface Props { dashboardId: string; dashboardItems?: Array; - folderItems?: Array; + folderItems: Array; } const props = withDefaults(defineProps(), { @@ -194,6 +194,7 @@ onUnmounted(() => { diff --git a/apps/web/src/services/project/v2/stores/project-page-modal-store.ts b/apps/web/src/services/project/v2/stores/project-page-modal-store.ts index 8d82b6649f..77a951105f 100644 --- a/apps/web/src/services/project/v2/stores/project-page-modal-store.ts +++ b/apps/web/src/services/project/v2/stores/project-page-modal-store.ts @@ -18,6 +18,7 @@ export const useProjectPageModalStore = defineStore('project-page-modal', () => dashboardNameEditModalVisible: false, dashboardChangeFolderModalVisible: false, dashboardDeleteModalVisible: false, + dashboardCloneModalVisible: false, }); const actions = { @@ -147,6 +148,13 @@ export const useProjectPageModalStore = defineStore('project-page-modal', () => closeDashboardDeleteModal() { state.dashboardDeleteModalVisible = false; }, + openDashboardCloneModal(targetDashboardId: string) { + state.targetId = targetDashboardId; + state.dashboardCloneModalVisible = true; + }, + closeDashboardCloneModal() { + state.dashboardCloneModalVisible = false; + }, resetTarget() { state.targetId = undefined; state.targetType = undefined; From e423160d37e0429be73e9b82179679893b84b675 Mon Sep 17 00:00:00 2001 From: Yongtae Park Date: Thu, 10 Apr 2025 20:30:59 +0900 Subject: [PATCH 9/9] chore: small fix Signed-off-by: samuel.park --- .../project/v2/components/ProjectDashboardCloneModal.vue | 5 +++-- .../v2/composables/queries/use-project-dashboard-query.ts | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/web/src/services/project/v2/components/ProjectDashboardCloneModal.vue b/apps/web/src/services/project/v2/components/ProjectDashboardCloneModal.vue index 93f23d53c3..ec5f06f97f 100644 --- a/apps/web/src/services/project/v2/components/ProjectDashboardCloneModal.vue +++ b/apps/web/src/services/project/v2/components/ProjectDashboardCloneModal.vue @@ -9,6 +9,7 @@ import { getClonedName } from '@cloudforet/utils'; import { RESOURCE_GROUP } from '@/api-clients/_common/schema/constant'; import type { DashboardCreateParams, DashboardModel } from '@/api-clients/dashboard/_types/dashboard-type'; +import type { PublicDashboardModel } from '@/api-clients/dashboard/public-dashboard/schema/model'; import { i18n } from '@/translations'; import { showErrorMessage } from '@/lib/helper/notice-alert-helper'; @@ -38,7 +39,7 @@ const { projectGroupId, projectId } = useProjectOrGroupId(projectGroupOrProjectI const { dashboardList, dashboardSharedList, - invalidateAllQueries: invalidateDashboardList, + setQueryData, } = useProjectDashboardQuery({ projectId, projectGroupId, @@ -94,7 +95,7 @@ const handleConfirm = async () => { const { mutate, isPending: dashboardCloneLoading } = useDashboardCloneAction({ dashboardId, onSuccess: async (data: DashboardModel) => { - invalidateDashboardList(); + setQueryData([data as PublicDashboardModel]); projectPageModalStore.closeDashboardCloneModal(); await router.replace({ name: PROJECT_ROUTE_V2._NAME, diff --git a/apps/web/src/services/project/v2/composables/queries/use-project-dashboard-query.ts b/apps/web/src/services/project/v2/composables/queries/use-project-dashboard-query.ts index 507860c9d8..ad04cf35cb 100644 --- a/apps/web/src/services/project/v2/composables/queries/use-project-dashboard-query.ts +++ b/apps/web/src/services/project/v2/composables/queries/use-project-dashboard-query.ts @@ -47,6 +47,7 @@ export const useProjectDashboardQuery = (options: { { k: 'scope', v: 'PROJECT', o: 'eq' }, { k: 'shared', v: true, o: 'eq' }, ], + sort: [{ key: 'created_at', desc: false }], }, })), }); @@ -59,6 +60,7 @@ export const useProjectDashboardQuery = (options: { filter: [ { k: 'version', v: '1.0', o: 'not' }, ], + sort: [{ key: 'created_at', desc: false }], }, })), });