@@ -303,10 +298,10 @@ watch([() => state.providerChartData, () => providerChartContext.value], ([, cha diff --git a/apps/web/src/services/project/v2/pages/ProjectMainPage.vue b/apps/web/src/services/project/v2/pages/ProjectMainPage.vue index 19b61c88af..d2d813a33a 100644 --- a/apps/web/src/services/project/v2/pages/ProjectMainPage.vue +++ b/apps/web/src/services/project/v2/pages/ProjectMainPage.vue @@ -123,7 +123,7 @@ const handleUpdateDashboardId = (id?: string) => { :target-type="props.projectGroupOrProjectId ? (projectId ? 'project' : 'projectGroup') : undefined" /> - Date: Thu, 10 Apr 2025 23:01:21 +0900 Subject: [PATCH 56/90] refactor(widget-load): optimize widget load query Signed-off-by: samuel.park --- ...ry-context.ts => use-widget-load-query.ts} | 91 ++++++++++--------- .../ClusteredColumnChart.vue | 21 ++--- .../color-coded-heatmap/ColorCodedHeatmap.vue | 21 ++--- .../ColorCodedTableHeatmap.vue | 22 ++--- .../modules/widgets/_widgets/gauge/Gauge.vue | 20 ++-- .../widgets/_widgets/geo-map/GeoMap.vue | 20 ++-- .../widgets/_widgets/heatmap/Heatmap.vue | 21 ++--- .../widgets/_widgets/line-chart/LineChart.vue | 23 ++--- .../_widgets/number-card/NumberCard.vue | 40 +++----- .../widgets/_widgets/pie-chart/PieChart.vue | 23 ++--- .../_widgets/sankey-chart/SankeyChart.vue | 22 ++--- .../StackedColumnChart.vue | 22 ++--- .../StackedHorizontalBarChart.vue | 23 ++--- .../modules/widgets/_widgets/table/Table.vue | 64 +++++-------- .../widgets/_widgets/treemap/Treemap.vue | 23 ++--- .../use-dashboard-route-context.ts | 2 +- .../components/DashboardDetailHeader.vue | 2 +- 17 files changed, 174 insertions(+), 286 deletions(-) rename apps/web/src/common/modules/widgets/_composables/{use-widget-load-query-context.ts => use-widget-load-query.ts} (51%) diff --git a/apps/web/src/common/modules/widgets/_composables/use-widget-load-query-context.ts b/apps/web/src/common/modules/widgets/_composables/use-widget-load-query.ts similarity index 51% rename from apps/web/src/common/modules/widgets/_composables/use-widget-load-query-context.ts rename to apps/web/src/common/modules/widgets/_composables/use-widget-load-query.ts index 6ec6838275..5b52331f1a 100644 --- a/apps/web/src/common/modules/widgets/_composables/use-widget-load-query-context.ts +++ b/apps/web/src/common/modules/widgets/_composables/use-widget-load-query.ts @@ -1,30 +1,29 @@ import { computed, type ComputedRef } from 'vue'; -import type { WidgetLoadParams, WidgetLoadResponse, WidgetLoadSumParams } from '@/api-clients/dashboard/_types/widget-type'; +import { useScopedQuery } from '@/api-clients/_common/composables/use-scoped-query'; +import type { WidgetLoadParams, WidgetLoadSumParams } from '@/api-clients/dashboard/_types/widget-type'; import { usePrivateWidgetApi } from '@/api-clients/dashboard/private-widget/composables/use-private-widget-api'; import { usePublicWidgetApi } from '@/api-clients/dashboard/public-widget/composables/use-public-widget-api'; -import type { QueryKeyArray } from '@/query/query-key/_types/query-key-type'; import { useServiceQueryKey } from '@/query/query-key/use-service-query-key'; +import { WIDGET_LOAD_STALE_TIME } from '@/common/modules/widgets/_constants/widget-constant'; -interface UseWidgetLoadQueryContextOptions { + +interface UseWidgetLoadQueryOptions { widgetId: ComputedRef; params: ComputedRef; additionalDeps?: ComputedRef; -} - -interface UseWidgetLoadQueryContextResult { - fetcher: () => Promise; - key: ComputedRef; + enabled?: ComputedRef; } -export const useWidgetLoadQueryContext = ({ +export const useWidgetLoadQuery = ({ widgetId, params, additionalDeps, -}: UseWidgetLoadQueryContextOptions): UseWidgetLoadQueryContextResult => { + enabled, +}: UseWidgetLoadQueryOptions) => { const { publicWidgetAPI } = usePublicWidgetApi(); const { privateWidgetAPI } = usePrivateWidgetApi(); @@ -46,37 +45,39 @@ export const useWidgetLoadQueryContext = ({ params, }); - const fetcher = () => { - if (!params.value) { - throw new Error('Widget parameters are required'); - } - return isPrivate.value - ? privateWidgetAPI.load(privateWidgetLoadParams.value) - : publicWidgetAPI.load(publicWidgetLoadParams.value); - }; - - return { - fetcher, - key: isPrivate.value ? privateWidgetLoadQueryKey : publicWidgetLoadQueryKey, - }; + return useScopedQuery({ + queryKey: isPrivate.value ? privateWidgetLoadQueryKey : publicWidgetLoadQueryKey, + queryFn: () => { + if (isPrivate.value) { + if (!privateWidgetLoadParams.value) { + throw new Error('Widget parameters are required'); + } + return privateWidgetAPI.load(privateWidgetLoadParams.value); + } + if (!publicWidgetLoadParams.value) { + throw new Error('Widget parameters are required'); + } + return publicWidgetAPI.load(publicWidgetLoadParams.value); + }, + enabled, + staleTime: WIDGET_LOAD_STALE_TIME, + }, ['DOMAIN', 'WORKSPACE']); }; -interface UseWidgetLoadSumQueryContextOptions { +interface UseWidgetLoadSumQueryOptions { widgetId: ComputedRef; params: ComputedRef; additionalDeps?: ComputedRef; -} -interface UseWidgetLoadSumQueryContextResult { - fetcher: () => Promise; - key: ComputedRef; + enabled?: ComputedRef; } -export const useWidgetLoadSumQueryContext = ({ +export const useWidgetLoadSumQuery = ({ widgetId, params, additionalDeps, -}: UseWidgetLoadSumQueryContextOptions): UseWidgetLoadSumQueryContextResult => { + enabled, +}: UseWidgetLoadSumQueryOptions) => { const { publicWidgetAPI } = usePublicWidgetApi(); const { privateWidgetAPI } = usePrivateWidgetApi(); @@ -98,17 +99,21 @@ export const useWidgetLoadSumQueryContext = ({ params, }); - const fetcher = () => { - if (!params.value) { - throw new Error('Widget parameters are required'); - } - return isPrivate.value - ? privateWidgetAPI.loadSum(privateWidgetLoadSumParams.value) - : publicWidgetAPI.loadSum(publicWidgetLoadSumParams.value); - }; - - return { - fetcher, - key: isPrivate.value ? privateWidgetLoadSumQueryKey : publicWidgetLoadSumQueryKey, - }; + return useScopedQuery({ + queryKey: isPrivate.value ? privateWidgetLoadSumQueryKey : publicWidgetLoadSumQueryKey, + queryFn: () => { + if (isPrivate.value) { + if (!privateWidgetLoadSumParams.value) { + throw new Error('Widget parameters are required'); + } + return privateWidgetAPI.loadSum(privateWidgetLoadSumParams.value); + } + if (!publicWidgetLoadSumParams.value) { + throw new Error('Widget parameters are required'); + } + return publicWidgetAPI.loadSum(publicWidgetLoadSumParams.value); + }, + enabled, + staleTime: WIDGET_LOAD_STALE_TIME, + }, ['DOMAIN', 'WORKSPACE']); }; diff --git a/apps/web/src/common/modules/widgets/_widgets/clustered-column-chart/ClusteredColumnChart.vue b/apps/web/src/common/modules/widgets/_widgets/clustered-column-chart/ClusteredColumnChart.vue index c85c658a34..3f38a00046 100644 --- a/apps/web/src/common/modules/widgets/_widgets/clustered-column-chart/ClusteredColumnChart.vue +++ b/apps/web/src/common/modules/widgets/_widgets/clustered-column-chart/ClusteredColumnChart.vue @@ -4,7 +4,6 @@ import { computed, defineExpose, onMounted, reactive, ref, watch, } from 'vue'; -import { useQuery } from '@tanstack/vue-query'; import dayjs from 'dayjs'; import type { BarSeriesOption } from 'echarts/charts'; import { init } from 'echarts/core'; @@ -24,9 +23,9 @@ import WidgetFrame from '@/common/modules/widgets/_components/WidgetFrame.vue'; import { useWidgetDataTableQuery } from '@/common/modules/widgets/_composables/use-widget-data-table-query'; import { useWidgetDateRange } from '@/common/modules/widgets/_composables/use-widget-date-range'; import { useWidgetFrame } from '@/common/modules/widgets/_composables/use-widget-frame'; -import { useWidgetLoadQueryContext } from '@/common/modules/widgets/_composables/use-widget-load-query-context'; +import { useWidgetLoadQuery } from '@/common/modules/widgets/_composables/use-widget-load-query'; import { DATA_TABLE_OPERATOR } from '@/common/modules/widgets/_constants/data-table-constant'; -import { DATE_FIELD, WIDGET_LOAD_STALE_TIME } from '@/common/modules/widgets/_constants/widget-constant'; +import { DATE_FIELD } from '@/common/modules/widgets/_constants/widget-constant'; import { DATE_FORMAT, SUB_TOTAL_NAME } from '@/common/modules/widgets/_constants/widget-field-constant'; import { getReferenceLabel, @@ -75,7 +74,7 @@ const state = reactive({ isPrivateWidget: computed(() => props.widgetId.startsWith('private')), unitMap: computed>(() => widgetFrameProps.value.unitMap || {}), - data: computed(() => queryResult.data?.value ?? null), + data: computed(() => loadQuery.data?.value ?? null), chart: null as EChartsType | null, dataField: computed(() => widgetOptionsState.dataFieldInfo?.data?.[0] || ''), xAxisData: computed(() => { @@ -175,7 +174,7 @@ const widgetOptionsState = reactive({ }); /* Api */ -const { fetcher: queryFn, key: queryKey } = useWidgetLoadQueryContext({ +const loadQuery = useWidgetLoadQuery({ widgetId: computed(() => props.widgetId), params: computed(() => ({ widget_id: props.widgetId, @@ -190,19 +189,13 @@ const { fetcher: queryFn, key: queryKey } = useWidgetLoadQueryContext({ widgetName: props.widgetName, dataTableId: props.dataTableId, })), -}); - -const queryResult = useQuery({ - queryKey, - queryFn, enabled: computed(() => props.widgetState !== 'INACTIVE' && !!dataTable.value && !props.loadDisabled), - staleTime: WIDGET_LOAD_STALE_TIME, }); -const widgetLoading = computed(() => queryResult.isFetching.value || dataTableLoading.value); +const widgetLoading = computed(() => loadQuery.isFetching.value || dataTableLoading.value); const errorMessage = computed(() => { if (!dataTable.value) return i18n.t('COMMON.WIDGETS.NO_DATA_TABLE_ERROR_MESSAGE') as string; - return queryResult.error?.value?.message as string; + return loadQuery.error?.value?.message as string; }); /* Util */ @@ -295,7 +288,7 @@ useResizeObserver(chartContext, throttle(() => { defineExpose({ loadWidget: () => { - queryResult.refetch(); + loadQuery.refetch(); }, }); onMounted(() => { diff --git a/apps/web/src/common/modules/widgets/_widgets/color-coded-heatmap/ColorCodedHeatmap.vue b/apps/web/src/common/modules/widgets/_widgets/color-coded-heatmap/ColorCodedHeatmap.vue index f4bcc66a3f..c2d47f3a69 100644 --- a/apps/web/src/common/modules/widgets/_widgets/color-coded-heatmap/ColorCodedHeatmap.vue +++ b/apps/web/src/common/modules/widgets/_widgets/color-coded-heatmap/ColorCodedHeatmap.vue @@ -3,7 +3,6 @@ import { computed, defineExpose, onMounted, reactive, watch, } from 'vue'; -import { useQuery } from '@tanstack/vue-query'; import { orderBy } from 'lodash'; import { @@ -18,8 +17,7 @@ import WidgetFrame from '@/common/modules/widgets/_components/WidgetFrame.vue'; import { useWidgetDataTableQuery } from '@/common/modules/widgets/_composables/use-widget-data-table-query'; import { useWidgetDateRange } from '@/common/modules/widgets/_composables/use-widget-date-range'; import { useWidgetFrame } from '@/common/modules/widgets/_composables/use-widget-frame'; -import { useWidgetLoadQueryContext } from '@/common/modules/widgets/_composables/use-widget-load-query-context'; -import { WIDGET_LOAD_STALE_TIME } from '@/common/modules/widgets/_constants/widget-constant'; +import { useWidgetLoadQuery } from '@/common/modules/widgets/_composables/use-widget-load-query'; import { getReferenceLabel, } from '@/common/modules/widgets/_helpers/widget-date-helper'; @@ -68,7 +66,7 @@ const widgetOptionsState = reactive({ }); /* Api */ -const { fetcher: queryFn, key: queryKey } = useWidgetLoadQueryContext({ +const loadQuery = useWidgetLoadQuery({ widgetId: computed(() => props.widgetId), params: computed(() => ({ widget_id: props.widgetId, @@ -88,23 +86,18 @@ const { fetcher: queryFn, key: queryKey } = useWidgetLoadQueryContext({ widgetName: props.widgetName, dataTableId: props.dataTableId, })), -}); - -const queryResult = useQuery({ - queryKey, - queryFn, enabled: computed(() => props.widgetState !== 'INACTIVE' && !!dataTable.value && !props.loadDisabled), - staleTime: WIDGET_LOAD_STALE_TIME, }); -const widgetLoading = computed(() => queryResult.isFetching.value || dataTableLoading.value); + +const widgetLoading = computed(() => loadQuery.isFetching.value || dataTableLoading.value); const errorMessage = computed(() => { if (!dataTable.value) return i18n.t('COMMON.WIDGETS.NO_DATA_TABLE_ERROR_MESSAGE') as string; - return queryResult.error?.value?.message as string; + return loadQuery.error?.value?.message as string; }); const refinedData = computed(() => { - const data = queryResult.data?.value; + const data = loadQuery.data?.value; if (!data?.results || !widgetOptionsState.groupByInfo?.data || !widgetOptionsState.formatRulesInfo?.field) return []; const groupByField = widgetOptionsState.groupByInfo.data as string; const formatRulesField = widgetOptionsState.formatRulesInfo.field as string; @@ -153,7 +146,7 @@ watch(() => widgetOptionsState.formatRulesInfo, async () => { defineExpose({ loadWidget: () => { - queryResult.refetch(); + loadQuery.refetch(); }, }); onMounted(() => { diff --git a/apps/web/src/common/modules/widgets/_widgets/color-coded-table-heatmap/ColorCodedTableHeatmap.vue b/apps/web/src/common/modules/widgets/_widgets/color-coded-table-heatmap/ColorCodedTableHeatmap.vue index 0996b2bebf..49110e10ca 100644 --- a/apps/web/src/common/modules/widgets/_widgets/color-coded-table-heatmap/ColorCodedTableHeatmap.vue +++ b/apps/web/src/common/modules/widgets/_widgets/color-coded-table-heatmap/ColorCodedTableHeatmap.vue @@ -4,7 +4,6 @@ import { computed, defineExpose, onMounted, reactive, ref, watch, } from 'vue'; -import { useQuery } from '@tanstack/vue-query'; import { cloneDeep, throttle } from 'lodash'; import { PTooltip } from '@cloudforet/mirinae'; @@ -18,9 +17,9 @@ import WidgetFrame from '@/common/modules/widgets/_components/WidgetFrame.vue'; import { useWidgetDataTableQuery } from '@/common/modules/widgets/_composables/use-widget-data-table-query'; import { useWidgetDateRange } from '@/common/modules/widgets/_composables/use-widget-date-range'; import { useWidgetFrame } from '@/common/modules/widgets/_composables/use-widget-frame'; -import { useWidgetLoadQueryContext } from '@/common/modules/widgets/_composables/use-widget-load-query-context'; +import { useWidgetLoadQuery } from '@/common/modules/widgets/_composables/use-widget-load-query'; import { DATA_TABLE_OPERATOR } from '@/common/modules/widgets/_constants/data-table-constant'; -import { DATE_FIELD, WIDGET_LOAD_STALE_TIME } from '@/common/modules/widgets/_constants/widget-constant'; +import { DATE_FIELD } from '@/common/modules/widgets/_constants/widget-constant'; import { SUB_TOTAL_NAME } from '@/common/modules/widgets/_constants/widget-field-constant'; import { getReferenceLabel, @@ -68,7 +67,7 @@ const { dateRange } = useWidgetDateRange({ const state = reactive({ isPrivateWidget: computed(() => props.widgetId.startsWith('private')), - data: computed(() => queryResult.data?.value), + data: computed(() => loadQuery.data?.value), dataField: computed(() => widgetOptionsState.dataFieldInfo?.data?.[0] || ''), // unit: computed(() => widgetFrameProps.value.unitMap?.[state.dataField]), boxWidth: BOX_MIN_WIDTH, @@ -111,7 +110,7 @@ const widgetOptionsState = reactive({ /* Api */ -const { fetcher: queryFn, key: queryKey } = useWidgetLoadQueryContext({ +const loadQuery = useWidgetLoadQuery({ widgetId: computed(() => props.widgetId), params: computed(() => ({ widget_id: props.widgetId, @@ -125,19 +124,14 @@ const { fetcher: queryFn, key: queryKey } = useWidgetLoadQueryContext({ widgetName: props.widgetName, dataTableId: props.dataTableId, })), -}); - -const queryResult = useQuery({ - queryKey, - queryFn, enabled: computed(() => props.widgetState !== 'INACTIVE' && !!dataTable.value && !props.loadDisabled), - staleTime: WIDGET_LOAD_STALE_TIME, }); -const widgetLoading = computed(() => queryResult.isFetching.value || dataTableLoading.value); + +const widgetLoading = computed(() => loadQuery.isFetching.value || dataTableLoading.value); const errorMessage = computed(() => { if (!dataTable.value) return i18n.t('COMMON.WIDGETS.NO_DATA_TABLE_ERROR_MESSAGE') as string; - return queryResult.error?.value?.message as string; + return loadQuery.error?.value?.message as string; }); /* Util */ @@ -219,7 +213,7 @@ watch(() => widgetOptionsState, () => { /* Lifecycle */ defineExpose({ loadWidget: () => { - queryResult.refetch(); + loadQuery.refetch(); }, }); onMounted(() => { diff --git a/apps/web/src/common/modules/widgets/_widgets/gauge/Gauge.vue b/apps/web/src/common/modules/widgets/_widgets/gauge/Gauge.vue index 6edf530f7e..ab94c99bba 100644 --- a/apps/web/src/common/modules/widgets/_widgets/gauge/Gauge.vue +++ b/apps/web/src/common/modules/widgets/_widgets/gauge/Gauge.vue @@ -4,7 +4,6 @@ import { computed, defineExpose, onMounted, reactive, ref, watch, } from 'vue'; -import { useQuery } from '@tanstack/vue-query'; import type { GaugeSeriesOption } from 'echarts/charts'; import type { EChartsType, @@ -20,8 +19,7 @@ import WidgetFrame from '@/common/modules/widgets/_components/WidgetFrame.vue'; import { useWidgetDataTableQuery } from '@/common/modules/widgets/_composables/use-widget-data-table-query'; import { useWidgetDateRange } from '@/common/modules/widgets/_composables/use-widget-date-range'; import { useWidgetFrame } from '@/common/modules/widgets/_composables/use-widget-frame'; -import { useWidgetLoadQueryContext } from '@/common/modules/widgets/_composables/use-widget-load-query-context'; -import { WIDGET_LOAD_STALE_TIME } from '@/common/modules/widgets/_constants/widget-constant'; +import { useWidgetLoadQuery } from '@/common/modules/widgets/_composables/use-widget-load-query'; import { getFormattedNumber } from '@/common/modules/widgets/_helpers/widget-helper'; import type { DataFieldValue } from '@/common/modules/widgets/_widget-fields/data-field/type'; import type { DateRangeValue } from '@/common/modules/widgets/_widget-fields/date-range/type'; @@ -58,7 +56,7 @@ const state = reactive({ if (!widgetOptionsState.dataFieldInfo?.data) return undefined; return widgetFrameProps.value.unitMap?.[widgetOptionsState.dataFieldInfo.data as string]; }), - data: computed(() => queryResult?.data?.value || null), + data: computed(() => loadQuery.data?.value || null), chart: null as EChartsType | null, chartData: undefined as undefined|number, chartOptions: computed<{series: GaugeSeriesOption[]}>(() => ({ @@ -135,7 +133,7 @@ const widgetOptionsState = reactive({ /* Api */ -const { fetcher: queryFn, key: queryKey } = useWidgetLoadQueryContext({ +const loadQuery = useWidgetLoadQuery({ widgetId: computed(() => props.widgetId), params: computed(() => ({ widget_id: props.widgetId, @@ -148,19 +146,13 @@ const { fetcher: queryFn, key: queryKey } = useWidgetLoadQueryContext({ widgetName: props.widgetName, dataTableId: props.dataTableId, })), -}); - -const queryResult = useQuery({ - queryKey, - queryFn, enabled: computed(() => props.widgetState !== 'INACTIVE' && !!dataTable.value && !props.loadDisabled), - staleTime: WIDGET_LOAD_STALE_TIME, }); -const widgetLoading = computed(() => queryResult.isFetching.value || dataTableLoading.value); +const widgetLoading = computed(() => loadQuery.isFetching.value || dataTableLoading.value); const errorMessage = computed(() => { if (!dataTable.value) return i18n.t('COMMON.WIDGETS.NO_DATA_TABLE_ERROR_MESSAGE') as string; - return queryResult.error?.value?.message as string; + return loadQuery.error?.value?.message as string; }); /* Util */ @@ -189,7 +181,7 @@ watch([() => state.data, () => props.widgetOptions, dataTable], ([newData,, _dat defineExpose({ loadWidget: () => { - queryResult.refetch(); + loadQuery.refetch(); }, }); onMounted(() => { diff --git a/apps/web/src/common/modules/widgets/_widgets/geo-map/GeoMap.vue b/apps/web/src/common/modules/widgets/_widgets/geo-map/GeoMap.vue index 8bd5bbe456..32e2c3d410 100644 --- a/apps/web/src/common/modules/widgets/_widgets/geo-map/GeoMap.vue +++ b/apps/web/src/common/modules/widgets/_widgets/geo-map/GeoMap.vue @@ -4,7 +4,6 @@ import { computed, defineExpose, onMounted, reactive, ref, watch, } from 'vue'; -import { useQuery } from '@tanstack/vue-query'; import axios from 'axios'; import type { MapSeriesOption } from 'echarts/charts'; import { init, registerMap } from 'echarts/core'; @@ -25,8 +24,7 @@ import WidgetFrame from '@/common/modules/widgets/_components/WidgetFrame.vue'; import { useWidgetDataTableQuery } from '@/common/modules/widgets/_composables/use-widget-data-table-query'; import { useWidgetDateRange } from '@/common/modules/widgets/_composables/use-widget-date-range'; import { useWidgetFrame } from '@/common/modules/widgets/_composables/use-widget-frame'; -import { useWidgetLoadQueryContext } from '@/common/modules/widgets/_composables/use-widget-load-query-context'; -import { WIDGET_LOAD_STALE_TIME } from '@/common/modules/widgets/_constants/widget-constant'; +import { useWidgetLoadQuery } from '@/common/modules/widgets/_composables/use-widget-load-query'; import type { DataFieldValue } from '@/common/modules/widgets/_widget-fields/data-field/type'; import type { DateRangeValue } from '@/common/modules/widgets/_widget-fields/date-range/type'; import type { GranularityValue } from '@/common/modules/widgets/_widget-fields/granularity/type'; @@ -62,7 +60,7 @@ const state = reactive({ mapLoaded: false, isPrivateWidget: computed(() => props.widgetId.startsWith('private')), - data: computed(() => queryResult?.data?.value || null), + data: computed(() => loadQuery.data?.value || null), chart: null as EChartsType | null, chartData: [], unit: computed(() => widgetFrameProps.value.unitMap?.[widgetOptionsState.dataFieldInfo?.data as string]), @@ -110,7 +108,7 @@ const widgetOptionsState = reactive({ }); /* Api */ -const { fetcher: queryFn, key: queryKey } = useWidgetLoadQueryContext({ +const loadQuery = useWidgetLoadQuery({ widgetId: computed(() => props.widgetId), params: computed(() => ({ widget_id: props.widgetId, @@ -124,19 +122,13 @@ const { fetcher: queryFn, key: queryKey } = useWidgetLoadQueryContext({ widgetName: props.widgetName, dataTableId: props.dataTableId, })), -}); - -const queryResult = useQuery({ - queryKey, - queryFn, enabled: computed(() => props.widgetState !== 'INACTIVE' && !!dataTable.value && !props.loadDisabled), - staleTime: WIDGET_LOAD_STALE_TIME, }); -const widgetLoading = computed(() => queryResult.isFetching.value || dataTableLoading.value); +const widgetLoading = computed(() => loadQuery.isFetching.value || dataTableLoading.value); const errorMessage = computed(() => { if (!dataTable.value) return i18n.t('COMMON.WIDGETS.NO_DATA_TABLE_ERROR_MESSAGE') as string; - return queryResult.error?.value?.message; + return loadQuery.error?.value?.message; }); /* Util */ @@ -194,7 +186,7 @@ useResizeObserver(chartContext, throttle(() => { defineExpose({ loadWidget: () => { - queryResult.refetch(); + loadQuery.refetch(); }, }); onMounted(() => { diff --git a/apps/web/src/common/modules/widgets/_widgets/heatmap/Heatmap.vue b/apps/web/src/common/modules/widgets/_widgets/heatmap/Heatmap.vue index e0d86cd826..751f417581 100644 --- a/apps/web/src/common/modules/widgets/_widgets/heatmap/Heatmap.vue +++ b/apps/web/src/common/modules/widgets/_widgets/heatmap/Heatmap.vue @@ -4,7 +4,6 @@ import { computed, defineExpose, onMounted, reactive, ref, watch, } from 'vue'; -import { useQuery } from '@tanstack/vue-query'; import dayjs from 'dayjs'; import type { HeatmapSeriesOption } from 'echarts/charts'; import type { EChartsType } from 'echarts/core'; @@ -20,9 +19,9 @@ import WidgetFrame from '@/common/modules/widgets/_components/WidgetFrame.vue'; import { useWidgetDataTableQuery } from '@/common/modules/widgets/_composables/use-widget-data-table-query'; import { useWidgetDateRange } from '@/common/modules/widgets/_composables/use-widget-date-range'; import { useWidgetFrame } from '@/common/modules/widgets/_composables/use-widget-frame'; -import { useWidgetLoadQueryContext } from '@/common/modules/widgets/_composables/use-widget-load-query-context'; +import { useWidgetLoadQuery } from '@/common/modules/widgets/_composables/use-widget-load-query'; import { DATA_TABLE_OPERATOR } from '@/common/modules/widgets/_constants/data-table-constant'; -import { DATE_FIELD, WIDGET_LOAD_STALE_TIME } from '@/common/modules/widgets/_constants/widget-constant'; +import { DATE_FIELD } from '@/common/modules/widgets/_constants/widget-constant'; import { SUB_TOTAL_NAME } from '@/common/modules/widgets/_constants/widget-field-constant'; import { getReferenceLabel, @@ -65,7 +64,7 @@ const chartContext = ref(null); const state = reactive({ isPrivateWidget: computed(() => props.widgetId.startsWith('private')), - data: computed(() => queryResult.data?.value ?? null), + data: computed(() => loadQuery.data?.value ?? null), dataField: computed(() => widgetOptionsState.dataFieldInfo?.data?.[0] || ''), chart: null as EChartsType | null, xAxisData: computed(() => { @@ -176,7 +175,7 @@ const widgetOptionsState = reactive({ }); /* Api */ -const { fetcher: queryFn, key: queryKey } = useWidgetLoadQueryContext({ +const loadQuery = useWidgetLoadQuery({ widgetId: computed(() => props.widgetId), params: computed(() => ({ widget_id: props.widgetId, @@ -190,19 +189,13 @@ const { fetcher: queryFn, key: queryKey } = useWidgetLoadQueryContext({ widgetName: props.widgetName, dataTableId: props.dataTableId, })), -}); - -const queryResult = useQuery({ - queryKey, - queryFn, enabled: computed(() => props.widgetState !== 'INACTIVE' && !!dataTable.value && !props.loadDisabled), - staleTime: WIDGET_LOAD_STALE_TIME, }); -const widgetLoading = computed(() => queryResult.isFetching.value || dataTableLoading.value); +const widgetLoading = computed(() => loadQuery.isFetching.value || dataTableLoading.value); const errorMessage = computed(() => { if (!dataTable.value) return i18n.t('COMMON.WIDGETS.NO_DATA_TABLE_ERROR_MESSAGE') as string; - return queryResult.error?.value?.message as string; + return loadQuery.error?.value?.message as string; }); @@ -248,7 +241,7 @@ useResizeObserver(chartContext, throttle(() => { defineExpose({ loadWidget: () => { - queryResult.refetch(); + loadQuery.refetch(); }, }); onMounted(() => { diff --git a/apps/web/src/common/modules/widgets/_widgets/line-chart/LineChart.vue b/apps/web/src/common/modules/widgets/_widgets/line-chart/LineChart.vue index 9fcf4a42d3..2b51a5851a 100644 --- a/apps/web/src/common/modules/widgets/_widgets/line-chart/LineChart.vue +++ b/apps/web/src/common/modules/widgets/_widgets/line-chart/LineChart.vue @@ -4,7 +4,6 @@ import { computed, defineExpose, onMounted, reactive, ref, watch, } from 'vue'; -import { useQuery } from '@tanstack/vue-query'; import dayjs from 'dayjs'; import type { LineSeriesOption } from 'echarts/charts'; import { init } from 'echarts/core'; @@ -24,9 +23,9 @@ import WidgetFrame from '@/common/modules/widgets/_components/WidgetFrame.vue'; import { useWidgetDataTableQuery } from '@/common/modules/widgets/_composables/use-widget-data-table-query'; import { useWidgetDateRange } from '@/common/modules/widgets/_composables/use-widget-date-range'; import { useWidgetFrame } from '@/common/modules/widgets/_composables/use-widget-frame'; -import { useWidgetLoadQueryContext } from '@/common/modules/widgets/_composables/use-widget-load-query-context'; +import { useWidgetLoadQuery } from '@/common/modules/widgets/_composables/use-widget-load-query'; import { DATA_TABLE_OPERATOR } from '@/common/modules/widgets/_constants/data-table-constant'; -import { DATE_FIELD, WIDGET_LOAD_STALE_TIME } from '@/common/modules/widgets/_constants/widget-constant'; +import { DATE_FIELD } from '@/common/modules/widgets/_constants/widget-constant'; import { DATE_FORMAT, SUB_TOTAL_NAME } from '@/common/modules/widgets/_constants/widget-field-constant'; import { getReferenceLabel, getWidgetDateFields, getWidgetDateRange, @@ -74,7 +73,7 @@ const chartContext = ref(null); const state = reactive({ isPrivateWidget: computed(() => props.widgetId.startsWith('private')), - data: computed(() => queryResult?.data?.value || null), + data: computed(() => loadQuery.data?.value || null), chart: null as EChartsType | null, dataField: computed(() => widgetOptionsState.dataFieldInfo?.data?.[0] || ''), xAxisData: computed(() => { @@ -181,7 +180,7 @@ const widgetOptionsState = reactive({ /* Api */ -const { fetcher: queryFn, key: queryKey } = useWidgetLoadQueryContext({ +const loadQuery = useWidgetLoadQuery({ widgetId: computed(() => props.widgetId), params: computed(() => ({ widget_id: props.widgetId, @@ -196,20 +195,14 @@ const { fetcher: queryFn, key: queryKey } = useWidgetLoadQueryContext({ widgetName: props.widgetName, dataTableId: props.dataTableId, })), -}); - - -const queryResult = useQuery({ - queryKey, - queryFn, enabled: computed(() => props.widgetState !== 'INACTIVE' && !!dataTable.value && !props.loadDisabled), - staleTime: WIDGET_LOAD_STALE_TIME, }); -const widgetLoading = computed(() => queryResult.isFetching.value || dataTableLoading.value); + +const widgetLoading = computed(() => loadQuery.isFetching.value || dataTableLoading.value); const errorMessage = computed(() => { if (!dataTable.value) return i18n.t('COMMON.WIDGETS.NO_DATA_TABLE_ERROR_MESSAGE') as string; - return queryResult.error?.value?.message as string; + return loadQuery.error?.value?.message as string; }); /* Util */ @@ -280,7 +273,7 @@ useResizeObserver(chartContext, throttle(() => { defineExpose({ loadWidget: () => { - queryResult.refetch(); + loadQuery.refetch(); }, }); onMounted(() => { diff --git a/apps/web/src/common/modules/widgets/_widgets/number-card/NumberCard.vue b/apps/web/src/common/modules/widgets/_widgets/number-card/NumberCard.vue index 959d495bfb..357f36f0aa 100644 --- a/apps/web/src/common/modules/widgets/_widgets/number-card/NumberCard.vue +++ b/apps/web/src/common/modules/widgets/_widgets/number-card/NumberCard.vue @@ -4,7 +4,6 @@ import { computed, defineExpose, onMounted, reactive, ref, } from 'vue'; -import { useQueries } from '@tanstack/vue-query'; import { debounce, throttle } from 'lodash'; import { @@ -19,9 +18,8 @@ import WidgetFrame from '@/common/modules/widgets/_components/WidgetFrame.vue'; import { useWidgetDataTableQuery } from '@/common/modules/widgets/_composables/use-widget-data-table-query'; import { useWidgetDateRange } from '@/common/modules/widgets/_composables/use-widget-date-range'; import { useWidgetFrame } from '@/common/modules/widgets/_composables/use-widget-frame'; -import { useWidgetLoadSumQueryContext } from '@/common/modules/widgets/_composables/use-widget-load-query-context'; +import { useWidgetLoadSumQuery } from '@/common/modules/widgets/_composables/use-widget-load-query'; import { DATA_TABLE_OPERATOR } from '@/common/modules/widgets/_constants/data-table-constant'; -import { WIDGET_LOAD_STALE_TIME } from '@/common/modules/widgets/_constants/widget-constant'; import { SUB_TOTAL_NAME } from '@/common/modules/widgets/_constants/widget-field-constant'; import { getPreviousDateRange, @@ -63,8 +61,8 @@ const state = reactive({ isPrivateWidget: computed(() => props.widgetId.startsWith('private')), previousLoading: false, - data: computed(() => queryResults.value?.[0].data || null), - previousData: computed(() => queryResults.value?.[1]?.data || null), + data: computed(() => loadQuery.data?.value || null), + previousData: computed(() => comparisonQuery.data?.value || null), unit: computed(() => widgetFrameProps.value.unitMap?.[widgetOptionsState.dataFieldInfo?.data as string]), previousValue: computed(() => { if (isPivotDataTable.value) return state.previousData?.results?.[0]?.[SUB_TOTAL_NAME] ?? 0; @@ -151,7 +149,7 @@ const setValueTextFontSize = debounce(() => { }, 300); /* Api */ -const { fetcher: baseQueryFn, key: baseQueryKey } = useWidgetLoadSumQueryContext({ +const loadQuery = useWidgetLoadSumQuery({ widgetId: computed(() => props.widgetId), params: computed(() => ({ widget_id: props.widgetId, @@ -164,10 +162,11 @@ const { fetcher: baseQueryFn, key: baseQueryKey } = useWidgetLoadSumQueryContext widgetName: props.widgetName, dataTableId: props.dataTableId, })), + enabled: computed(() => props.widgetState !== 'INACTIVE' && !!dataTable.value && !props.loadDisabled), }); -const { fetcher: comparisonQueryFn, key: comparisonQueryKey } = useWidgetLoadSumQueryContext({ +const comparisonQuery = useWidgetLoadSumQuery({ widgetId: computed(() => props.widgetId), params: computed(() => ({ widget_id: props.widgetId, @@ -180,30 +179,15 @@ const { fetcher: comparisonQueryFn, key: comparisonQueryKey } = useWidgetLoadSum widgetName: props.widgetName, dataTableId: props.dataTableId, })), + enabled: computed(() => props.widgetState !== 'INACTIVE' && !!dataTable.value && widgetOptionsState.comparisonInfo?.toggleValue && !props.loadDisabled), }); -const queryResults = useQueries({ - queries: [ - { - queryKey: baseQueryKey, - queryFn: baseQueryFn, - enabled: computed(() => props.widgetState !== 'INACTIVE' && !!dataTable.value && !props.loadDisabled), - staleTime: WIDGET_LOAD_STALE_TIME, - }, - { - queryKey: comparisonQueryKey, - queryFn: comparisonQueryFn, - enabled: computed(() => props.widgetState !== 'INACTIVE' && !!dataTable.value && widgetOptionsState.comparisonInfo?.toggleValue && !props.loadDisabled), - staleTime: WIDGET_LOAD_STALE_TIME, - }, - ], -}); -const widgetLoading = computed(() => queryResults.value?.[0].isFetching || dataTableLoading.value); -const previousLoading = computed(() => queryResults.value?.[1].isFetching || dataTableLoading.value); +const widgetLoading = computed(() => loadQuery.isFetching.value || dataTableLoading.value); +const previousLoading = computed(() => comparisonQuery.isFetching.value || dataTableLoading.value); const errorMessage = computed(() => { if (!dataTable.value) return i18n.t('COMMON.WIDGETS.NO_DATA_TABLE_ERROR_MESSAGE') as string; - return queryResults.value?.[0].error?.message as string; + return loadQuery.error?.value?.message as string; }); const { widgetFrameProps, widgetFrameEventHandlers } = useWidgetFrame(props, emit, { @@ -219,8 +203,8 @@ useResizeObserver(valueTextRef, throttle(() => { defineExpose({ loadWidget: () => { - queryResults.value?.[0].refetch(); - queryResults.value?.[1].refetch(); + loadQuery.refetch(); + comparisonQuery.refetch(); }, }); onMounted(() => { diff --git a/apps/web/src/common/modules/widgets/_widgets/pie-chart/PieChart.vue b/apps/web/src/common/modules/widgets/_widgets/pie-chart/PieChart.vue index 9884540257..187016e3e2 100644 --- a/apps/web/src/common/modules/widgets/_widgets/pie-chart/PieChart.vue +++ b/apps/web/src/common/modules/widgets/_widgets/pie-chart/PieChart.vue @@ -5,7 +5,6 @@ import { reactive, ref, watch, } from 'vue'; -import { useQuery } from '@tanstack/vue-query'; import dayjs from 'dayjs'; import type { PieSeriesOption } from 'echarts/charts'; import { init } from 'echarts/core'; @@ -26,8 +25,8 @@ import WidgetFrame from '@/common/modules/widgets/_components/WidgetFrame.vue'; import { useWidgetDataTableQuery } from '@/common/modules/widgets/_composables/use-widget-data-table-query'; import { useWidgetDateRange } from '@/common/modules/widgets/_composables/use-widget-date-range'; import { useWidgetFrame } from '@/common/modules/widgets/_composables/use-widget-frame'; -import { useWidgetLoadQueryContext } from '@/common/modules/widgets/_composables/use-widget-load-query-context'; -import { DATE_FIELD, WIDGET_LOAD_STALE_TIME } from '@/common/modules/widgets/_constants/widget-constant'; +import { useWidgetLoadQuery } from '@/common/modules/widgets/_composables/use-widget-load-query'; +import { DATE_FIELD } from '@/common/modules/widgets/_constants/widget-constant'; import { DATE_FORMAT } from '@/common/modules/widgets/_constants/widget-field-constant'; import { getReferenceLabel, @@ -75,7 +74,7 @@ const { dateRange } = useWidgetDateRange({ const chartContext = ref(null); const state = reactive({ isPrivateWidget: computed(() => props.widgetId.startsWith('private')), - data: computed(() => queryResult.data?.value || null), + data: computed(() => loadQuery.data?.value || null), chart: null as EChartsType | null, chartData: [] as ChartData[], unit: computed(() => widgetFrameProps.value.unitMap?.[widgetOptionsState.dataFieldInfo?.data as string]), @@ -190,7 +189,7 @@ const widgetOptionsState = reactive({ /* Api */ -const { fetcher: queryFn, key: queryKey } = useWidgetLoadQueryContext({ +const loadQuery = useWidgetLoadQuery({ widgetId: computed(() => props.widgetId), params: computed(() => ({ widget_id: props.widgetId, @@ -205,20 +204,14 @@ const { fetcher: queryFn, key: queryKey } = useWidgetLoadQueryContext({ widgetName: props.widgetName, dataTableId: props.dataTableId, })), -}); - - -const queryResult = useQuery({ - queryKey, - queryFn, enabled: computed(() => props.widgetState !== 'INACTIVE' && !!dataTable.value && !props.loadDisabled), - staleTime: WIDGET_LOAD_STALE_TIME, }); -const widgetLoading = computed(() => queryResult.isFetching.value || dataTableLoading.value); + +const widgetLoading = computed(() => loadQuery.isFetching.value || dataTableLoading.value); const errorMessage = computed(() => { if (!dataTable.value) return i18n.t('COMMON.WIDGETS.NO_DATA_TABLE_ERROR_MESSAGE') as string; - return queryResult.error?.value?.message; + return loadQuery.error?.value?.message as string; }); const drawChart = (rawData: WidgetLoadResponse|null) => { @@ -270,7 +263,7 @@ useResizeObserver(chartContext, throttle(() => { }, 500)); defineExpose({ loadWidget: async () => { - queryResult.refetch(); + loadQuery.refetch(); }, }); onMounted(() => { diff --git a/apps/web/src/common/modules/widgets/_widgets/sankey-chart/SankeyChart.vue b/apps/web/src/common/modules/widgets/_widgets/sankey-chart/SankeyChart.vue index 00fb631682..6360c52a5d 100644 --- a/apps/web/src/common/modules/widgets/_widgets/sankey-chart/SankeyChart.vue +++ b/apps/web/src/common/modules/widgets/_widgets/sankey-chart/SankeyChart.vue @@ -5,7 +5,6 @@ import { reactive, ref, watch, } from 'vue'; -import { useQuery } from '@tanstack/vue-query'; import dayjs from 'dayjs'; import type { SankeySeriesOption } from 'echarts'; import { init } from 'echarts/core'; @@ -26,8 +25,8 @@ import WidgetFrame from '@/common/modules/widgets/_components/WidgetFrame.vue'; import { useWidgetDataTableQuery } from '@/common/modules/widgets/_composables/use-widget-data-table-query'; import { useWidgetDateRange } from '@/common/modules/widgets/_composables/use-widget-date-range'; import { useWidgetFrame } from '@/common/modules/widgets/_composables/use-widget-frame'; -import { useWidgetLoadQueryContext } from '@/common/modules/widgets/_composables/use-widget-load-query-context'; -import { DATE_FIELD, WIDGET_LOAD_STALE_TIME } from '@/common/modules/widgets/_constants/widget-constant'; +import { useWidgetLoadQuery } from '@/common/modules/widgets/_composables/use-widget-load-query'; +import { DATE_FIELD } from '@/common/modules/widgets/_constants/widget-constant'; import { DATE_FORMAT } from '@/common/modules/widgets/_constants/widget-field-constant'; import { getReferenceLabel } from '@/common/modules/widgets/_helpers/widget-date-helper'; import { getFormattedNumber } from '@/common/modules/widgets/_helpers/widget-helper'; @@ -65,7 +64,7 @@ const { dateRange } = useWidgetDateRange({ const chartContext = ref(null); const state = reactive({ isPrivateWidget: computed(() => props.widgetId.startsWith('private')), - data: computed(() => queryResult?.data?.value || null), + data: computed(() => loadQuery.data?.value || null), chart: null as EChartsType | null, chartData: [] as SankeySeriesOption['data'], links: [] as SankeySeriesOption['links'], @@ -132,7 +131,7 @@ const widgetOptionsState = reactive({ /* Api */ -const { fetcher: queryFn, key: queryKey } = useWidgetLoadQueryContext({ +const loadQuery = useWidgetLoadQuery({ widgetId: computed(() => props.widgetId), params: computed(() => ({ widget_id: props.widgetId, @@ -147,19 +146,14 @@ const { fetcher: queryFn, key: queryKey } = useWidgetLoadQueryContext({ widgetName: props.widgetName, dataTableId: props.dataTableId, })), + enabled: computed(() => props.widgetState !== 'INACTIVE' && !!dataTable.value && !props.loadDisabled), }); -const queryResult = useQuery({ - queryKey, - queryFn, - enabled: computed(() => props.widgetState !== 'INACTIVE' && !!dataTable.value && !props.loadDisabled), - staleTime: WIDGET_LOAD_STALE_TIME, -}); +const widgetLoading = computed(() => loadQuery.isFetching.value || dataTableLoading.value); -const widgetLoading = computed(() => queryResult.isFetching.value || dataTableLoading.value); const errorMessage = computed(() => { if (!dataTable.value) return i18n.t('COMMON.WIDGETS.NO_DATA_TABLE_ERROR_MESSAGE') as string; - return queryResult.error?.value?.message; + return loadQuery.error?.value?.message as string; }); const drawChart = (rawData: WidgetLoadResponse|null) => { @@ -219,7 +213,7 @@ useResizeObserver(chartContext, throttle(() => { defineExpose({ loadWidget: () => { - queryResult.refetch(); + loadQuery.refetch(); }, }); onMounted(() => { diff --git a/apps/web/src/common/modules/widgets/_widgets/stacked-column-chart/StackedColumnChart.vue b/apps/web/src/common/modules/widgets/_widgets/stacked-column-chart/StackedColumnChart.vue index ae22ce526a..c396d061c4 100644 --- a/apps/web/src/common/modules/widgets/_widgets/stacked-column-chart/StackedColumnChart.vue +++ b/apps/web/src/common/modules/widgets/_widgets/stacked-column-chart/StackedColumnChart.vue @@ -4,7 +4,6 @@ import { computed, defineExpose, onMounted, reactive, ref, watch, } from 'vue'; -import { useQuery } from '@tanstack/vue-query'; import dayjs from 'dayjs'; import type { BarSeriesOption } from 'echarts/charts'; import { init } from 'echarts/core'; @@ -24,9 +23,9 @@ import WidgetFrame from '@/common/modules/widgets/_components/WidgetFrame.vue'; import { useWidgetDataTableQuery } from '@/common/modules/widgets/_composables/use-widget-data-table-query'; import { useWidgetDateRange } from '@/common/modules/widgets/_composables/use-widget-date-range'; import { useWidgetFrame } from '@/common/modules/widgets/_composables/use-widget-frame'; -import { useWidgetLoadQueryContext } from '@/common/modules/widgets/_composables/use-widget-load-query-context'; +import { useWidgetLoadQuery } from '@/common/modules/widgets/_composables/use-widget-load-query'; import { DATA_TABLE_OPERATOR } from '@/common/modules/widgets/_constants/data-table-constant'; -import { DATE_FIELD, WIDGET_LOAD_STALE_TIME } from '@/common/modules/widgets/_constants/widget-constant'; +import { DATE_FIELD } from '@/common/modules/widgets/_constants/widget-constant'; import { DATE_FORMAT, SUB_TOTAL_NAME } from '@/common/modules/widgets/_constants/widget-field-constant'; import { getReferenceLabel, @@ -72,7 +71,7 @@ const chartContext = ref(null); const state = reactive({ isPrivateWidget: computed(() => props.widgetId.startsWith('private')), - data: computed(() => queryResult?.data?.value || null), + data: computed(() => loadQuery.data?.value || null), xAxisData: computed(() => { if (!state.data?.results?.length) return []; if (isDateField(widgetOptionsState.xAxisInfo?.data)) { @@ -172,7 +171,7 @@ const widgetOptionsState = reactive({ /* Api */ -const { fetcher: queryFn, key: queryKey } = useWidgetLoadQueryContext({ +const loadQuery = useWidgetLoadQuery({ widgetId: computed(() => props.widgetId), params: computed(() => ({ widget_id: props.widgetId, @@ -187,19 +186,14 @@ const { fetcher: queryFn, key: queryKey } = useWidgetLoadQueryContext({ widgetName: props.widgetName, dataTableId: props.dataTableId, })), + enabled: computed(() => props.widgetState !== 'INACTIVE' && !!dataTable.value && !props.loadDisabled), }); -const queryResult = useQuery({ - queryKey, - queryFn, - enabled: computed(() => props.widgetState !== 'INACTIVE' && !!dataTable.value && !props.loadDisabled), - staleTime: WIDGET_LOAD_STALE_TIME, -}); +const widgetLoading = computed(() => loadQuery.isFetching.value || dataTableLoading.value); -const widgetLoading = computed(() => queryResult.isFetching.value || dataTableLoading.value); const errorMessage = computed(() => { if (!dataTable.value) return i18n.t('COMMON.WIDGETS.NO_DATA_TABLE_ERROR_MESSAGE') as string; - return queryResult.error?.value?.message; + return loadQuery.error?.value?.message as string; }); @@ -267,7 +261,7 @@ useResizeObserver(chartContext, throttle(() => { defineExpose({ loadWidget: () => { - queryResult.refetch(); + loadQuery.refetch(); }, }); onMounted(() => { diff --git a/apps/web/src/common/modules/widgets/_widgets/stacked-horizontal-bar-chart/StackedHorizontalBarChart.vue b/apps/web/src/common/modules/widgets/_widgets/stacked-horizontal-bar-chart/StackedHorizontalBarChart.vue index 5bec2d5232..cf4120c30b 100644 --- a/apps/web/src/common/modules/widgets/_widgets/stacked-horizontal-bar-chart/StackedHorizontalBarChart.vue +++ b/apps/web/src/common/modules/widgets/_widgets/stacked-horizontal-bar-chart/StackedHorizontalBarChart.vue @@ -4,7 +4,6 @@ import { computed, defineExpose, onMounted, reactive, ref, watch, } from 'vue'; -import { useQuery } from '@tanstack/vue-query'; import dayjs from 'dayjs'; import type { BarSeriesOption } from 'echarts/charts'; import type { EChartsType } from 'echarts/core'; @@ -22,9 +21,9 @@ import WidgetFrame from '@/common/modules/widgets/_components/WidgetFrame.vue'; import { useWidgetDataTableQuery } from '@/common/modules/widgets/_composables/use-widget-data-table-query'; import { useWidgetDateRange } from '@/common/modules/widgets/_composables/use-widget-date-range'; import { useWidgetFrame } from '@/common/modules/widgets/_composables/use-widget-frame'; -import { useWidgetLoadQueryContext } from '@/common/modules/widgets/_composables/use-widget-load-query-context'; +import { useWidgetLoadQuery } from '@/common/modules/widgets/_composables/use-widget-load-query'; import { DATA_TABLE_OPERATOR } from '@/common/modules/widgets/_constants/data-table-constant'; -import { DATE_FIELD, WIDGET_LOAD_STALE_TIME } from '@/common/modules/widgets/_constants/widget-constant'; +import { DATE_FIELD } from '@/common/modules/widgets/_constants/widget-constant'; import { DATE_FORMAT, SUB_TOTAL_NAME } from '@/common/modules/widgets/_constants/widget-field-constant'; import { getReferenceLabel, @@ -70,7 +69,7 @@ const chartContext = ref(null); const state = reactive({ isPrivateWidget: computed(() => props.widgetId.startsWith('private')), - data: computed(() => queryResult?.data?.value || null), + data: computed(() => loadQuery.data?.value || null), yAxisData: computed(() => { if (!state.data?.results?.length) return []; if (isDateField(widgetOptionsState.yAxisInfo?.data)) { @@ -167,7 +166,7 @@ const widgetOptionsState = reactive({ }); /* Api */ -const { fetcher: queryFn, key: queryKey } = useWidgetLoadQueryContext({ +const loadQuery = useWidgetLoadQuery({ widgetId: computed(() => props.widgetId), params: computed(() => ({ widget_id: props.widgetId, @@ -182,19 +181,13 @@ const { fetcher: queryFn, key: queryKey } = useWidgetLoadQueryContext({ widgetName: props.widgetName, dataTableId: props.dataTableId, })), + enabled: computed(() => props.widgetState !== 'INACTIVE' && !!dataTable.value && !props.loadDisabled), }); -const queryResult = useQuery({ - queryKey, - queryFn, - enabled: computed(() => props.widgetState !== 'INACTIVE' && !!dataTable.value && !props.loadDisabled), - staleTime: WIDGET_LOAD_STALE_TIME, -}); - -const widgetLoading = computed(() => queryResult.isFetching.value || dataTableLoading.value); +const widgetLoading = computed(() => loadQuery.isFetching.value || dataTableLoading.value); const errorMessage = computed(() => { if (!dataTable.value) return i18n.t('COMMON.WIDGETS.NO_DATA_TABLE_ERROR_MESSAGE') as string; - return queryResult.error?.value?.message; + return loadQuery.error?.value?.message as string; }); /* Util */ @@ -261,7 +254,7 @@ useResizeObserver(chartContext, throttle(() => { defineExpose({ loadWidget: () => { - queryResult.refetch(); + loadQuery.refetch(); }, }); onMounted(() => { diff --git a/apps/web/src/common/modules/widgets/_widgets/table/Table.vue b/apps/web/src/common/modules/widgets/_widgets/table/Table.vue index 5aaa0cd514..7f93bc1b3b 100644 --- a/apps/web/src/common/modules/widgets/_widgets/table/Table.vue +++ b/apps/web/src/common/modules/widgets/_widgets/table/Table.vue @@ -3,7 +3,6 @@ import { defineExpose, reactive, computed, onMounted, } from 'vue'; -import { useQueries } from '@tanstack/vue-query'; import { sortBy } from 'lodash'; import type { Sort } from '@cloudforet/core-lib/space-connector/type'; @@ -15,9 +14,8 @@ import { i18n } from '@/translations'; import WidgetFrame from '@/common/modules/widgets/_components/WidgetFrame.vue'; import { useWidgetDateRange } from '@/common/modules/widgets/_composables/use-widget-date-range'; import { useWidgetFrame } from '@/common/modules/widgets/_composables/use-widget-frame'; -import { useWidgetLoadQueryContext, useWidgetLoadSumQueryContext } from '@/common/modules/widgets/_composables/use-widget-load-query-context'; +import { useWidgetLoadQuery, useWidgetLoadSumQuery } from '@/common/modules/widgets/_composables/use-widget-load-query'; import { DATA_TABLE_OPERATOR } from '@/common/modules/widgets/_constants/data-table-constant'; -import { WIDGET_LOAD_STALE_TIME } from '@/common/modules/widgets/_constants/widget-constant'; import { SUB_TOTAL_NAME } from '@/common/modules/widgets/_constants/widget-field-constant'; import type { CustomTableColumnWidthValue } from '@/common/modules/widgets/_widget-fields/custom-table-column-width/type'; import type { DataFieldHeatmapColorValue } from '@/common/modules/widgets/_widget-fields/data-field-heatmap-color/type'; @@ -138,7 +136,7 @@ const getTableDefaultSortBy = (_sortBy: Sort[]) => { return defaultSortBy; }; -const { fetcher: loadFetcher, key: loadKey } = useWidgetLoadQueryContext({ +const loadQuery = useWidgetLoadQuery({ widgetId: computed(() => props.widgetId), params: computed(() => ({ widget_id: props.widgetId, @@ -157,9 +155,15 @@ const { fetcher: loadFetcher, key: loadKey } = useWidgetLoadQueryContext({ widgetName: props.widgetName, dataTableId: props.dataTableId, })), + enabled: computed(() => { + const widgetActive = props.widgetState !== 'INACTIVE'; + const dataTableReady = !!dataTable.value; + const loadDisabled = props.loadDisabled; + return widgetActive && dataTableReady && !loadDisabled; + }), }); -const { fetcher: loadSumFetcher, key: loadSumKey } = useWidgetLoadSumQueryContext({ +const loadSumQuery = useWidgetLoadSumQuery({ widgetId: computed(() => props.widgetId), params: computed(() => ({ widget_id: props.widgetId, @@ -173,47 +177,25 @@ const { fetcher: loadSumFetcher, key: loadSumKey } = useWidgetLoadSumQueryContex dataTableId: props.dataTableId, enabledTotal: !!widgetOptionsState.totalInfo?.toggleValue, })), + enabled: computed(() => { + const widgetActive = props.widgetState !== 'INACTIVE'; + const dataTableReady = !!dataTable.value; + const totalEnabled = !!widgetOptionsState.totalInfo?.toggleValue; + const loadDisabled = props.loadDisabled; + return widgetActive && dataTableReady && totalEnabled && !loadDisabled; + }), }); -const queryResults = useQueries({ - queries: [ - { - queryKey: loadKey, - queryFn: loadFetcher, - enabled: computed(() => { - const widgetActive = props.widgetState !== 'INACTIVE'; - const dataTableReady = !!dataTable.value; - const loadDisabled = props.loadDisabled; - return widgetActive && dataTableReady && !loadDisabled; - }), - staleTime: WIDGET_LOAD_STALE_TIME, - }, - { - queryKey: loadSumKey, - queryFn: loadSumFetcher, - enabled: computed(() => { - const widgetActive = props.widgetState !== 'INACTIVE'; - const dataTableReady = !!dataTable.value; - const totalEnabled = !!widgetOptionsState.totalInfo?.toggleValue; - const loadDisabled = props.loadDisabled; - - return widgetActive && dataTableReady && totalEnabled && !loadDisabled; - }), - staleTime: WIDGET_LOAD_STALE_TIME, - }, - ], -}); - -const widgetLoading = computed(() => queryResults.value?.[0].isFetching || queryResults.value?.[1].isFetching || dataTableLoading.value); +const widgetLoading = computed(() => loadQuery.isFetching.value || loadSumQuery.isFetching.value || dataTableLoading.value); const errorMessage = computed(() => { if (!dataTable.value) return i18n.t('COMMON.WIDGETS.NO_DATA_TABLE_ERROR_MESSAGE') as string; - return queryResults.value?.[0].error?.message as string || queryResults.value?.[1].error?.message as string; + return loadQuery.error?.value?.message as string || loadSumQuery.error?.value?.message as string; }); const refinedData = computed(() => { - const data = queryResults.value?.[0].data; - const totalData = queryResults.value?.[1].data; + const data = loadQuery.data?.value; + const totalData = loadSumQuery.data?.value; if (!data) return null; @@ -243,7 +225,7 @@ const { widgetFrameProps, widgetFrameEventHandlers } = useWidgetFrame(props, emi dateRange, errorMessage, widgetLoading, - noData: computed(() => (queryResults.value?.[0].data ? !(queryResults.value?.[0].data?.results?.length) : false)), + noData: computed(() => (loadQuery.data?.value ? !(loadQuery.data?.value?.results?.length) : false)), }); const handleUpdateThisPage = async (newPage: number) => { @@ -252,8 +234,8 @@ const handleUpdateThisPage = async (newPage: number) => { defineExpose({ loadWidget: () => { - queryResults.value?.[0].refetch(); - queryResults.value?.[1].refetch(); + loadQuery.refetch(); + loadSumQuery.refetch(); }, }); onMounted(() => { diff --git a/apps/web/src/common/modules/widgets/_widgets/treemap/Treemap.vue b/apps/web/src/common/modules/widgets/_widgets/treemap/Treemap.vue index d2abed421e..21846787b7 100644 --- a/apps/web/src/common/modules/widgets/_widgets/treemap/Treemap.vue +++ b/apps/web/src/common/modules/widgets/_widgets/treemap/Treemap.vue @@ -4,7 +4,6 @@ import { computed, defineExpose, onMounted, reactive, ref, watch, } from 'vue'; -import { useQuery } from '@tanstack/vue-query'; import dayjs from 'dayjs'; import type { TreemapSeriesOption } from 'echarts/charts'; import { init } from 'echarts/core'; @@ -24,8 +23,8 @@ import WidgetFrame from '@/common/modules/widgets/_components/WidgetFrame.vue'; import { useWidgetDataTableQuery } from '@/common/modules/widgets/_composables/use-widget-data-table-query'; import { useWidgetDateRange } from '@/common/modules/widgets/_composables/use-widget-date-range'; import { useWidgetFrame } from '@/common/modules/widgets/_composables/use-widget-frame'; -import { useWidgetLoadQueryContext } from '@/common/modules/widgets/_composables/use-widget-load-query-context'; -import { DATE_FIELD, WIDGET_LOAD_STALE_TIME } from '@/common/modules/widgets/_constants/widget-constant'; +import { useWidgetLoadQuery } from '@/common/modules/widgets/_composables/use-widget-load-query'; +import { DATE_FIELD } from '@/common/modules/widgets/_constants/widget-constant'; import { DATE_FORMAT } from '@/common/modules/widgets/_constants/widget-field-constant'; import { getReferenceLabel, @@ -70,7 +69,7 @@ const chartContext = ref(null); const state = reactive({ isPrivateWidget: computed(() => props.widgetId.startsWith('private')), - data: computed(() => queryResult?.data?.value || null), + data: computed(() => loadQuery.data?.value || null), chart: null as EChartsType | null, chartData: [] as ChartData[], unit: computed(() => widgetFrameProps.value.unitMap?.[widgetOptionsState.dataFieldInfo?.data as string]), @@ -133,7 +132,7 @@ const widgetOptionsState = reactive({ }); /* Api */ -const { fetcher: queryFn, key: queryKey } = useWidgetLoadQueryContext({ +const loadQuery = useWidgetLoadQuery({ widgetId: computed(() => props.widgetId), params: computed(() => ({ widget_id: props.widgetId, @@ -148,19 +147,13 @@ const { fetcher: queryFn, key: queryKey } = useWidgetLoadQueryContext({ widgetName: props.widgetName, dataTableId: props.dataTableId, })), + enabled: computed(() => props.widgetState !== 'INACTIVE' && !!dataTable.value), }); -const queryResult = useQuery({ - queryKey, - queryFn, - enabled: computed(() => props.widgetState !== 'INACTIVE' && !!dataTable.value), - staleTime: WIDGET_LOAD_STALE_TIME, -}); - -const widgetLoading = computed(() => queryResult.isFetching.value || dataTableLoading.value); +const widgetLoading = computed(() => loadQuery.isFetching.value || dataTableLoading.value); const errorMessage = computed(() => { if (!dataTable.value) return i18n.t('COMMON.WIDGETS.NO_DATA_TABLE_ERROR_MESSAGE') as string; - return queryResult.error?.value?.message; + return loadQuery.error?.value?.message as string; }); const drawChart = (rawData: WidgetLoadResponse|null) => { @@ -222,7 +215,7 @@ useResizeObserver(chartContext, throttle(() => { defineExpose({ loadWidget: () => { - queryResult.refetch(); + loadQuery.refetch(); }, }); onMounted(() => { diff --git a/apps/web/src/services/dashboard-shared/core/composables/use-dashboard-route-context.ts b/apps/web/src/services/dashboard-shared/core/composables/use-dashboard-route-context.ts index 1f47623d60..58957c90b9 100644 --- a/apps/web/src/services/dashboard-shared/core/composables/use-dashboard-route-context.ts +++ b/apps/web/src/services/dashboard-shared/core/composables/use-dashboard-route-context.ts @@ -40,7 +40,7 @@ export const useDashboardRouteContext = () => { }); const projectContextType = computed(() => { - const id = projectGroupOrProjectId.value; + const id = route.params.projectGroupOrProjectId; if (!id) return undefined; if (id.startsWith('pg-')) return 'PROJECT_GROUP'; if (id.startsWith('project-')) return 'PROJECT'; 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 index bad14b2b08..4c9f6cee20 100644 --- a/apps/web/src/services/dashboard-shared/dashboard-detail/components/DashboardDetailHeader.vue +++ b/apps/web/src/services/dashboard-shared/dashboard-detail/components/DashboardDetailHeader.vue @@ -23,7 +23,7 @@ import { useDashboardGetQuery } from '@/services/dashboard-shared/dashboard-deta interface Props { dashboardId: string; - folderItems?: Array; + folderItems: Array; } const props = withDefaults(defineProps(), { folderItems: () => [], From ae1288729bc2667a81625c6e97e0037d012a468b Mon Sep 17 00:00:00 2001 From: Yongtae Park Date: Thu, 10 Apr 2025 23:03:26 +0900 Subject: [PATCH 57/90] chore: edit usage of dashboard temp vars Signed-off-by: samuel.park --- .../components/DashboardVariablesV2.vue | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) 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 b09b4f6f3a..6813068724 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 @@ -30,9 +30,7 @@ import { useDashboardDetailInfoStore } from '@/services/dashboard-shared/dashboa import { useAllReferenceTypeInfoStore } from '@/services/dashboards/stores/all-reference-type-info-store'; - interface Props { - isProjectDashboard?: boolean; loading?: boolean; disableSaveButton?: boolean; originVars?: DashboardVars; @@ -68,8 +66,8 @@ const state = reactive({ globalVariables: computed(() => { const properties = state.dashboardVarsSchemaProperties as Record; const _usedProperties: DashboardGlobalVariable[] = Object.values(properties).filter((d) => d.use); - if (props.isProjectDashboard) { - const _usedPropertiesWithoutProject = getOrderedGlobalVariables(_usedProperties).filter((d) => d.key !== 'project_id'); + if (entryPoint.value === 'PROJECT') { + const _usedPropertiesWithoutProject = getOrderedGlobalVariables(_usedProperties).filter((d) => d.key !== 'project_id' && d.key !== 'project_group_id'); return getOrderedGlobalVariables(_usedPropertiesWithoutProject); } return getOrderedGlobalVariables(_usedProperties); @@ -93,9 +91,8 @@ const handleClickSaveButton = () => { emit('update', { vars: state.tempVars }); }; const handleResetVariables = () => { - const _originVars = state.dashboardVars; - state.tempVars = { ..._originVars }; - dashboardDetailStore.setVars(_originVars); + state.tempVars = state.dashboardVars; + // dashboardDetailStore.setVars(); }; watch(() => dashboard.value?.vars, (_vars) => { From 5620c848e6d73083273a61738398b800cf5e6787 Mon Sep 17 00:00:00 2001 From: Yongtae Park Date: Thu, 10 Apr 2025 23:03:50 +0900 Subject: [PATCH 58/90] chore: refactor query key composable Signed-off-by: samuel.park --- apps/web/src/query/query-key/use-service-query-key.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/web/src/query/query-key/use-service-query-key.ts b/apps/web/src/query/query-key/use-service-query-key.ts index 959900f30c..92796230dc 100644 --- a/apps/web/src/query/query-key/use-service-query-key.ts +++ b/apps/web/src/query/query-key/use-service-query-key.ts @@ -57,20 +57,21 @@ export const useServiceQueryKey = = {}, ): UseServiceQueryKeyResult => { + const { params, contextKey } = options; + // Runtime validation for development environment if (import.meta.env.DEV) { if (!service || !resource || !verb) { console.warn('Required parameters (service, resource, verb) must be provided'); } - if (options.params) { - const rawParams = toValue(options.params); + if (params) { + const rawParams = toValue(params); if (rawParams === null || typeof rawParams !== 'object') { console.warn('params must be a non-null object'); } } } - const { params, contextKey } = options; const queryKeyAppContext = useQueryKeyAppContext(); @@ -102,7 +103,7 @@ export const useServiceQueryKey = { const resolvedParams = toValue(params); - return resolvedParams; + return createImmutableObjectKeyItem(resolvedParams); }), withSuffix: (arg) => { if (typeof arg === 'object' && arg !== null) { From 575655d2dc8779634c69acf89f864950f93a33ca Mon Sep 17 00:00:00 2001 From: Yongtae Park Date: Thu, 10 Apr 2025 23:15:58 +0900 Subject: [PATCH 59/90] feat(project-page-context): create project-page-context composable Signed-off-by: samuel.park --- .../v2/components/ProjectDashboardCloneModal.vue | 15 +++++++-------- .../ProjectDashboardFolderFormModal.vue | 13 ++++++------- .../use-project-dashboard-folder-query.ts | 14 ++++---------- .../queries/use-project-dashboard-query.ts | 15 +++++---------- .../v2/composables/use-proejct-page-context.ts | 16 ++++++++++++++++ 5 files changed, 38 insertions(+), 35 deletions(-) create mode 100644 apps/web/src/services/project/v2/composables/use-proejct-page-context.ts diff --git a/apps/web/src/services/project/v2/components/ProjectDashboardCloneModal.vue b/apps/web/src/services/project/v2/components/ProjectDashboardCloneModal.vue index ec5f06f97f..07a2ed82c9 100644 --- a/apps/web/src/services/project/v2/components/ProjectDashboardCloneModal.vue +++ b/apps/web/src/services/project/v2/components/ProjectDashboardCloneModal.vue @@ -19,10 +19,10 @@ import { useFormValidator } from '@/common/composables/form-validator'; import { useDashboardCloneAction } from '@/services/dashboard-shared/core/actions/use-dashboard-clone-action'; import { useProjectDashboardQuery } from '@/services/project/v2/composables/queries/use-project-dashboard-query'; +import { useProjectPageContext } from '@/services/project/v2/composables/use-proejct-page-context'; import { useProjectOrGroupId } from '@/services/project/v2/composables/use-project-or-group-id'; import { PROJECT_ROUTE_V2 } from '@/services/project/v2/routes/route-constant'; import { useProjectPageModalStore } from '@/services/project/v2/stores/project-page-modal-store'; -import type { ProjectPageContextType } from '@/services/project/v2/types/project-page-context-type'; interface Props { projectGroupOrProjectId: string; @@ -45,10 +45,9 @@ const { projectGroupId, }); -const projectContext = computed(() => { - if (projectGroupId.value) return 'PROJECT_GROUP'; - if (projectId.value) return 'PROJECT'; - return undefined; +const projectPageContext = useProjectPageContext({ + projectGroupId, + projectId, }); const existingNameList = computed(() => dashboardList.value.map((d) => d.name)); const currentDashboard = computed(() => [...dashboardList.value, ...dashboardSharedList.value].find((d) => d.dashboard_id === dashboardId.value)); @@ -80,12 +79,12 @@ const handleConfirm = async () => { const _sharedDashboard: DashboardCreateParams = { name: name.value, - resource_group: projectContext.value === 'PROJECT_GROUP' ? RESOURCE_GROUP.WORKSPACE : RESOURCE_GROUP.PROJECT, + resource_group: projectPageContext.value === 'PROJECT_GROUP' ? RESOURCE_GROUP.WORKSPACE : RESOURCE_GROUP.PROJECT, }; - if (projectContext.value === 'PROJECT_GROUP') { + if (projectPageContext.value === 'PROJECT_GROUP') { _sharedDashboard.project_group_id = projectGroupId.value; - } else if (projectContext.value === 'PROJECT') { + } else if (projectPageContext.value === 'PROJECT') { _sharedDashboard.project_id = projectId.value; } diff --git a/apps/web/src/services/project/v2/components/ProjectDashboardFolderFormModal.vue b/apps/web/src/services/project/v2/components/ProjectDashboardFolderFormModal.vue index 5562d1ea42..3539af959d 100644 --- a/apps/web/src/services/project/v2/components/ProjectDashboardFolderFormModal.vue +++ b/apps/web/src/services/project/v2/components/ProjectDashboardFolderFormModal.vue @@ -25,9 +25,9 @@ import ErrorHandler from '@/common/composables/error/errorHandler'; import { useFormValidator } from '@/common/composables/form-validator'; import { useProjectDashboardFolderQuery } from '@/services/project/v2/composables/queries/use-project-dashboard-folder-query'; +import { useProjectPageContext } from '@/services/project/v2/composables/use-proejct-page-context'; import { useProjectOrGroupId } from '@/services/project/v2/composables/use-project-or-group-id'; import { useProjectPageModalStore } from '@/services/project/v2/stores/project-page-modal-store'; -import type { ProjectPageContextType } from '@/services/project/v2/types/project-page-context-type'; interface Props { projectGroupOrProjectId?: string; @@ -52,10 +52,9 @@ const { projectGroupId, }); -const projectContext = computed(() => { - if (projectGroupId.value) return 'PROJECT_GROUP'; - if (projectId.value) return 'PROJECT'; - return undefined; +const projectPageContext = useProjectPageContext({ + projectGroupId, + projectId, }); const existingNameList = computed(() => dashboardFolderList.value.map((d) => d.name)); @@ -93,9 +92,9 @@ const handleFormConfirm = async () => { const params: FolderCreateParams = { name: name.value as string, tags: { created_by: userStore.state.userId }, - resource_group: projectContext.value === 'PROJECT_GROUP' ? RESOURCE_GROUP.WORKSPACE : RESOURCE_GROUP.PROJECT, + resource_group: projectPageContext.value === 'PROJECT_GROUP' ? RESOURCE_GROUP.WORKSPACE : RESOURCE_GROUP.PROJECT, }; - if (projectContext.value === 'PROJECT_GROUP') { + if (projectPageContext.value === 'PROJECT_GROUP') { params.project_group_id = projectGroupId.value; } else { params.project_id = projectId.value; 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 88efc11c10..0759c51ffe 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 @@ -10,7 +10,7 @@ import type { PublicFolderListParameters } from '@/api-clients/dashboard/public- import type { PublicFolderModel } from '@/api-clients/dashboard/public-folder/schema/model'; import { useServiceQueryKey } from '@/query/query-key/use-service-query-key'; -import type { ProjectPageContextType } from '@/services/project/v2/types/project-page-context-type'; +import { useProjectPageContext } from '@/services/project/v2/composables/use-proejct-page-context'; const STALE_TIME = 1000 * 60 * 5; const GC_TIME = 1000 * 60 * 5; @@ -25,17 +25,11 @@ export const useProjectDashboardFolderQuery = (options: { const { publicFolderAPI } = usePublicFolderApi(); const queryClient = useQueryClient(); - const projectPageContext = computed(() => { - if (options.projectGroupId?.value) { - return 'PROJECT_GROUP'; - } - if (options.projectId?.value) { - return 'PROJECT'; - } - return undefined; + const projectPageContext = useProjectPageContext({ + projectGroupId: options.projectGroupId, + projectId: options.projectId, }); - /* Query Keys */ const { key: dashboardFolderSharedListQueryKey, params: dashboardFolderSharedListParams } = useServiceQueryKey('dashboard', 'public-folder', 'list', { contextKey: projectPageContext.value, 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 ad04cf35cb..0d26fd206d 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 @@ -10,7 +10,7 @@ import type { PublicDashboardListParameters } from '@/api-clients/dashboard/publ import type { PublicDashboardModel } from '@/api-clients/dashboard/public-dashboard/schema/model'; import { useServiceQueryKey } from '@/query/query-key/use-service-query-key'; -import type { ProjectPageContextType } from '@/services/project/v2/types/project-page-context-type'; +import { useProjectPageContext } from '@/services/project/v2/composables/use-proejct-page-context'; const STALE_TIME = 1000 * 60 * 5; const GC_TIME = 1000 * 60 * 5; @@ -24,16 +24,11 @@ export const useProjectDashboardQuery = (options: { }) => { const { publicDashboardAPI } = usePublicDashboardApi(); const queryClient = useQueryClient(); - const projectPageContext = computed(() => { - if (options.projectGroupId?.value) { - return 'PROJECT_GROUP'; - } - if (options.projectId?.value) { - return 'PROJECT'; - } - return undefined; - }); + const projectPageContext = useProjectPageContext({ + projectGroupId: options.projectGroupId, + projectId: options.projectId, + }); /* Query Keys */ const { key: dashboardSharedListQueryKey, params: dashboardSharedListParams } = useServiceQueryKey('dashboard', 'public-dashboard', 'list', { diff --git a/apps/web/src/services/project/v2/composables/use-proejct-page-context.ts b/apps/web/src/services/project/v2/composables/use-proejct-page-context.ts new file mode 100644 index 0000000000..08f20d3292 --- /dev/null +++ b/apps/web/src/services/project/v2/composables/use-proejct-page-context.ts @@ -0,0 +1,16 @@ +import { computed, type ComputedRef } from 'vue'; + +import type { ProjectPageContextType } from '@/services/project/v2/types/project-page-context-type'; + +export const useProjectPageContext = (options: { + projectGroupId?: ComputedRef, + projectId?: ComputedRef, +}) => computed(() => { + if (options.projectGroupId?.value) { + return 'PROJECT_GROUP'; + } + if (options.projectId?.value) { + return 'PROJECT'; + } + return undefined; +}); From 0b396f7993bc54cd00501bf068a4dabf4af51e7b Mon Sep 17 00:00:00 2001 From: Yongtae Park Date: Thu, 10 Apr 2025 23:56:22 +0900 Subject: [PATCH 60/90] fix(p-tab): add missing spec & solve bug Signed-off-by: samuel.park --- .../mirinae/src/navigation/tabs/tab/PTab.vue | 25 +++++++++++++++++-- .../tabs/tab/components/FolderTab.vue | 13 +++++++++- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/packages/mirinae/src/navigation/tabs/tab/PTab.vue b/packages/mirinae/src/navigation/tabs/tab/PTab.vue index 5093b7bc64..c4b0c4802c 100644 --- a/packages/mirinae/src/navigation/tabs/tab/PTab.vue +++ b/packages/mirinae/src/navigation/tabs/tab/PTab.vue @@ -1,6 +1,6 @@ @@ -219,6 +229,13 @@ onClickOutside(hiddenTabsMenuRef, hideHiddenTabs); @click="handleSelectTab(tab, idx)" >
+ {{ tab.label }} @@ -321,7 +338,7 @@ onClickOutside(hiddenTabsMenuRef, hideHiddenTabs); width: 100%; flex: 1; li { - @apply relative text-gray-400 cursor-pointer; + @apply relative text-gray-600 cursor-pointer; height: 2.5rem; min-height: 2.5rem; max-width: 10rem; @@ -343,6 +360,10 @@ onClickOutside(hiddenTabsMenuRef, hideHiddenTabs); } .content-wrapper { @apply flex items-center justify-center w-full h-full; + .tab-icon { + min-width: 0.875rem; + margin-right: 0.25rem; + } .label { @apply w-full text-label-md block truncate; } diff --git a/packages/mirinae/src/navigation/tabs/tab/components/FolderTab.vue b/packages/mirinae/src/navigation/tabs/tab/components/FolderTab.vue index ab9aff9b8a..0c6618d24b 100644 --- a/packages/mirinae/src/navigation/tabs/tab/components/FolderTab.vue +++ b/packages/mirinae/src/navigation/tabs/tab/components/FolderTab.vue @@ -84,6 +84,13 @@ onClickOutside(folderTabRef, () => { @click="handleSelectTab" >
+ {{ tab.label }} @@ -91,7 +98,7 @@ onClickOutside(folderTabRef, () => { :name="state.visible ? 'ic_chevron-up' : 'ic_chevron-down'" width="1.25rem" height="1.25rem" - color="inherit" + color="gray-600" />
{ } .content-wrapper { @apply flex items-center justify-center w-full h-full; + .folder-tab-icon { + min-width: 0.875rem; + margin-right: 0.25rem; + } .label { @apply w-full text-label-md block truncate; } From de8b3521d378d6d0efeb6714c2c3a80be7cb7df8 Mon Sep 17 00:00:00 2001 From: Yongtae Park Date: Thu, 10 Apr 2025 23:56:50 +0900 Subject: [PATCH 61/90] chore: small fix Signed-off-by: samuel.park --- .../composables/use-dashboard-get-query.ts | 14 ++++++++++++-- .../project/v2/components/ProjectDetailTab.vue | 2 +- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/apps/web/src/services/dashboard-shared/dashboard-detail/composables/use-dashboard-get-query.ts b/apps/web/src/services/dashboard-shared/dashboard-detail/composables/use-dashboard-get-query.ts index ac013223ad..3546538266 100644 --- a/apps/web/src/services/dashboard-shared/dashboard-detail/composables/use-dashboard-get-query.ts +++ b/apps/web/src/services/dashboard-shared/dashboard-detail/composables/use-dashboard-get-query.ts @@ -64,13 +64,23 @@ export const useDashboardGetQuery = ({ /* Querys */ const publicDashboardQuery = useScopedQuery({ queryKey: publicDashboardGetQueryKey, - queryFn: () => publicDashboardAPI.get(publicDashboardGetParams.value), + queryFn: () => { + if (!publicDashboardGetParams.value.dashboard_id) { + throw new Error('dashboard_id is required'); + } + return publicDashboardAPI.get(publicDashboardGetParams.value); + }, enabled: computed(() => !!dashboardId.value && !isPrivate.value), staleTime: STALE_TIME, }, ['DOMAIN', 'WORKSPACE']); const privateDashboardQuery = useScopedQuery({ queryKey: privateDashboardGetQueryKey, - queryFn: () => privateDashboardAPI.get(privateDashboardGetParams.value), + queryFn: () => { + if (!privateDashboardGetParams.value.dashboard_id) { + throw new Error('dashboard_id is required'); + } + return privateDashboardAPI.get(privateDashboardGetParams.value); + }, enabled: computed(() => !!dashboardId.value && isPrivate.value), staleTime: STALE_TIME, }, ['WORKSPACE']); diff --git a/apps/web/src/services/project/v2/components/ProjectDetailTab.vue b/apps/web/src/services/project/v2/components/ProjectDetailTab.vue index 2a810fb874..7f9b11e001 100644 --- a/apps/web/src/services/project/v2/components/ProjectDetailTab.vue +++ b/apps/web/src/services/project/v2/components/ProjectDetailTab.vue @@ -121,7 +121,7 @@ const handleCreateProjectDashboard = (item: string|number|SelectDropdownMenuItem params: { projectGroupOrProjectId, }, - }); + }).catch(() => {}); } else if (item === 'folder') { projectPageModalStore.openCreateFolderFormModal(); } From a2dfbbacfc9a6834d8575ff1cd9d40227ce203e4 Mon Sep 17 00:00:00 2001 From: Wanjin Noh Date: Fri, 11 Apr 2025 11:00:35 +0900 Subject: [PATCH 62/90] feat(api-client-templates): add new API client generation rules and templates Signed-off-by: Wanjin Noh --- .cursor/rules/generate-new-api-clients.mdc | 140 +++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 .cursor/rules/generate-new-api-clients.mdc diff --git a/.cursor/rules/generate-new-api-clients.mdc b/.cursor/rules/generate-new-api-clients.mdc new file mode 100644 index 0000000000..7c96c427ac --- /dev/null +++ b/.cursor/rules/generate-new-api-clients.mdc @@ -0,0 +1,140 @@ +--- +description: +globs: +alwaysApply: false +--- + + +## Directory Structure +``` +api-clients/ +├── _common/ +│ ├── composables/ +│ │ └── use-api-query-key.ts +│ └── schema/ +│ ├── api-verbs/ +│ └── model.ts +└── {service-name}/ + ├── {resource}/ + │ ├── composables/ + │ │ └── use-{resource}-api.ts + │ └── schema/ + │ ├── api-verbs/ + │ │ ├── {verb}.ts + │ └── model.ts + └── index.ts +``` + +## API Client Composable Template +```typescript +// use-{resource}-api.ts template +import { SpaceConnector } from '@cloudforet/core-lib/space-connector'; + +import { useAPIQueryKey } from '@/api-clients/_common/composables/use-api-query-key'; +import type { ListResponse } from '@/api-clients/_common/schema/api-verbs/list'; +import type { {Resource}CreateParameters } from '@/api-clients/{service}/{resource}/schema/api-verbs/create'; +import type { {Resource}Model } from '@/api-clients/{service}/{resource}/schema/model'; + +export const use{Resource}Api = () => { + // Define API actions + const actions = { + create: SpaceConnector.clientV2.{service}.{resource}.create<{Resource}CreateParameters, {Resource}Model>, + list: SpaceConnector.clientV2.{service}.{resource}.list<{Resource}ListParameters, ListResponse<{Resource}Model>>, + // ... other actions + }; + + return { + {resource}QueryKey, + {resource}ListQueryKey, + {resource}API: actions, + }; +}; +``` + +## Schema Templates + +### Model Definition +```typescript +// model.ts template +export interface {Resource}Model { + // Define resource properties + resource_id: string; + name: string; + // ... other properties +} +``` + +### API Verb Parameters +```typescript +// create.ts template +export interface {Resource}CreateParameters { + // Define create operation parameters + name: string; + // ... other parameters +} + +// list.ts template +export interface {Resource}ListParameters { + query?: { + filter?: Array<{ + k: string; + v: any; + o: string; + }>; + // ... other query parameters + }; +} +``` + +## Usage Rules + +1. **Naming Conventions** + - Use PascalCase for interface names: `{Resource}Model`, `{Resource}{Verb}Parameters` + - Use camelCase for variables and functions: `use{Resource}Api` + - Follow existing naming patterns in the codebase + +2. **Type Safety** + - Always define proper TypeScript interfaces for all parameters and responses + - Use generics with SpaceConnector client methods + - Define all possible API parameters in schema files + +3. **Query Key Management** + - Use `useAPIQueryKey` for generating consistent query keys + - Create separate query keys for different operations + - Include contextual information in query keys when needed + +4. **Code Organization** + - Keep schema definitions separate from API logic + - Group related files in appropriate directories + - Follow the established directory structure + +5. **Documentation** + - Add JSDoc comments for public interfaces and functions + - Document any special behaviors or requirements + - Include examples for complex parameter structures + +## Example Usage in Components +```typescript +const { {resource}API } = use{Resource}Api(); + +const { key, params } = useServiceKey(service, resource, verb, { + params: ... +}) + +// In composables +const query = useScopedQuery({ + queryKey: {resource}QueryKey.value, + queryFn: () => {resource}API.{verb}(params.value), + // ... other options +}, ['WORKSPACE', 'ADMIN']); +``` + +## Notes +- Always check existing API clients for consistent patterns +- Consider reusability and maintainability +- Follow the service's API documentation for accurate parameter definitions +- Use appropriate error handling and loading states +- Consider implementing proper caching strategies +- Add comments only when it is really complex. (English only) + + From 2077b3c7a87efba25c3cd709eeef81b9d13c1284 Mon Sep 17 00:00:00 2001 From: Yongtae Park Date: Fri, 11 Apr 2025 12:08:52 +0900 Subject: [PATCH 63/90] fix(dashboard-control-menu): create control menu composable Signed-off-by: samuel.park --- .../use-dashboard-control-menu-helper.ts | 105 ++++++++++++++++++ .../helpers/dashboard-control-menu-helper.ts | 97 ---------------- 2 files changed, 105 insertions(+), 97 deletions(-) create mode 100644 apps/web/src/services/dashboard-shared/core/composables/use-dashboard-control-menu-helper.ts delete mode 100644 apps/web/src/services/dashboard-shared/core/helpers/dashboard-control-menu-helper.ts diff --git a/apps/web/src/services/dashboard-shared/core/composables/use-dashboard-control-menu-helper.ts b/apps/web/src/services/dashboard-shared/core/composables/use-dashboard-control-menu-helper.ts new file mode 100644 index 0000000000..a94893e9e1 --- /dev/null +++ b/apps/web/src/services/dashboard-shared/core/composables/use-dashboard-control-menu-helper.ts @@ -0,0 +1,105 @@ +import { computed } from 'vue'; + +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'; + +export const useDashboardControlMenuHelper = () => { + const CLONE_MENU_ITEM = computed(() => ({ + type: 'item', + name: 'clone', + label: i18n.t('DASHBOARDS.DETAIL.CLONE'), + icon: 'ic_clone', + })); + + const DELETE_MENU_ITEM = computed(() => ({ + type: 'item', + name: 'delete', + label: i18n.t('DASHBOARDS.ALL_DASHBOARDS.DELETE'), + icon: 'ic_delete', + })); + + 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', + }, + ]; + + 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) return isDeprecated ? [] : [CLONE_MENU_ITEM.value]; + if (isDeprecated) return [DELETE_MENU_ITEM.value]; + + 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', + }, + CLONE_MENU_ITEM.value, + { + type: 'item', + name: 'move', + label: i18n.t('DASHBOARDS.DETAIL.MOVE'), + icon: 'ic_move', + }, + { type: 'divider', name: 'divider' }, + ...(!isProject ? shareContextMenuItems : []), + DELETE_MENU_ITEM.value, + ]; + }; + + const getControlFolderMenuItems = (folderId: string, manageable: boolean, folder: FolderModel): MenuItem[] => { + const isPrivate = folderId.startsWith('private'); + const isShared = !!(folder as PublicFolderModel)?.shared; + + if (!manageable) return [CLONE_MENU_ITEM.value]; + + return [ + { + type: 'item', + name: 'edit', + label: i18n.t('DASHBOARDS.ALL_DASHBOARDS.EDIT_NAME'), + icon: 'ic_edit-text', + }, + CLONE_MENU_ITEM.value, + { type: 'divider', name: 'divider' }, + ...(!isPrivate ? getShareMenuItems(isShared) : []), + { type: 'divider', name: 'divider' }, + DELETE_MENU_ITEM.value, + ]; + }; + + return { + getControlDashboardMenuItems, + getControlFolderMenuItems, + }; +}; + 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 deleted file mode 100644 index ee98cf8fca..0000000000 --- a/apps/web/src/services/dashboard-shared/core/helpers/dashboard-control-menu-helper.ts +++ /dev/null @@ -1,97 +0,0 @@ -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', - }, - ]; -}; From ef1b237762b707c21a5c2b45cd0326e59b2804b8 Mon Sep 17 00:00:00 2001 From: Yongtae Park Date: Fri, 11 Apr 2025 12:12:52 +0900 Subject: [PATCH 64/90] refactor(dashboard-manageable): refactor and remove query concern Signed-off-by: samuel.park --- .../composables/use-dashboard-manageable.ts | 65 +++++++------------ 1 file changed, 24 insertions(+), 41 deletions(-) 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 index 6d4f836f11..3134d0dae3 100644 --- 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 @@ -1,4 +1,3 @@ -import type { ComputedRef } from 'vue'; import { computed, reactive } from 'vue'; import { RESOURCE_GROUP } from '@/api-clients/_common/schema/constant'; @@ -11,73 +10,57 @@ 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 => { +export const useDashboardManageable = (): 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; + const _isManageable = (isPrivate: boolean, isShared: boolean, resourceGroup: string | undefined): boolean => { 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 (isPrivate) return true; + if (isShared && resourceGroup === 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 !isShared; } return false; }; + + + + const getDashboardManageable = (_dashboard?: DashboardModel): boolean => { + if (!_dashboard) return false; + const publicDashboard = _dashboard as PublicDashboardModel; + return _isManageable( + _dashboard.dashboard_id?.startsWith('private') || false, + publicDashboard?.shared || false, + publicDashboard?.resource_group, + ); + }; + 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; + const publicFolder = _folder as PublicFolderModel; + return _isManageable( + _folder.folder_id?.startsWith('private') || false, + publicFolder?.shared || false, + publicFolder?.resource_group, + ); }; - return { - isManageable, getDashboardManageable, getFolderManageable, }; From c6786a6c5909b95a4438ce324d6e6306f039587f Mon Sep 17 00:00:00 2001 From: Yongtae Park Date: Fri, 11 Apr 2025 12:13:22 +0900 Subject: [PATCH 65/90] fix: apply changed composables Signed-off-by: samuel.park --- .../dashboard-detail/DashboardDetailBody.vue | 6 ++---- .../components/DashboardDetailHeader.vue | 15 +++++++-------- .../components/DashboardLabelsButton.vue | 8 ++++---- .../components/DashboardRefreshDropdown.vue | 7 +++---- .../components/DashboardToolsetDateDropdown.vue | 7 +++---- .../components/DashboardWidgetContainerV2.vue | 9 ++++----- .../dashboard-folder/DashboardFolderTreeItem.vue | 3 ++- .../dashboard-main/DashboardLSBTree.vue | 4 ++-- 8 files changed, 27 insertions(+), 32 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 fa0cfb23a4..1abf78284e 100644 --- a/apps/web/src/services/dashboard-shared/dashboard-detail/DashboardDetailBody.vue +++ b/apps/web/src/services/dashboard-shared/dashboard-detail/DashboardDetailBody.vue @@ -58,9 +58,7 @@ const dashboardDetailState = dashboardDetailStore.state; const widgetGenerateStore = useWidgetGenerateStore(); const widgetContainerRef = ref(null); -const { isManageable } = useDashboardManageable({ - dashboardId, -}); +const { getDashboardManageable } = useDashboardManageable(); const { entryPoint, projectGroupOrProjectId, projectContextType, } = useDashboardRouteContext(); @@ -226,7 +224,7 @@ onUnmounted(() => { /> (), { folderItems: () => [], }); -const emit = defineEmits<{(e: 'select-toolset', toolsetId: string|undefined): void; -}>(); +const emit = defineEmits<{(e: 'select-toolset', toolsetId: string|undefined): void;}>(); const { entryPoint } = useDashboardRouteContext(); const { dashboard } = useDashboardGetQuery({ dashboardId: computed(() => props.dashboardId), }); -const { isManageable } = useDashboardManageable({ - dashboardId: computed(() => props.dashboardId), -}); +const { getDashboardManageable } = useDashboardManageable(); +const dashboardManageable = computed(() => getDashboardManageable(dashboard.value)); +const { getControlDashboardMenuItems } = useDashboardControlMenuHelper(); const controlMenuItems = computed(() => { if (!dashboard.value) return []; - return getControlDashboardMenuItems(props.dashboardId, isManageable.value, dashboard.value, entryPoint.value === 'PROJECT'); + return getControlDashboardMenuItems(props.dashboardId, dashboardManageable.value, dashboard.value, entryPoint.value === 'PROJECT'); }); const state = reactive({ @@ -145,7 +144,7 @@ const handleSelectItem = (item: MenuItem) => {