diff --git a/apps/web/src/common/modules/widgets/_components/WidgetFormOverlayStep2WidgetForm.vue b/apps/web/src/common/modules/widgets/_components/WidgetFormOverlayStep2WidgetForm.vue index 1f0d0308f3..761041aa00 100644 --- a/apps/web/src/common/modules/widgets/_components/WidgetFormOverlayStep2WidgetForm.vue +++ b/apps/web/src/common/modules/widgets/_components/WidgetFormOverlayStep2WidgetForm.vue @@ -142,10 +142,14 @@ const handleSelectDataTable = async (dataTableId: string) => { await updateWidget({ widget_id: widgetGenerateState.widgetId, data_table_id: dataTableId, + widget_type: widgetType, state: 'INACTIVE', options: sanitizedOptions, }); + const _config = getWidgetConfig(widgetType); + if (!_config) return; + props.fieldManager.updateWidgetConfig(_config); props.fieldManager.updateDataTableAndOriginData(selectedDataTable, sanitizedOptions); }; @@ -189,11 +193,12 @@ const checkDefaultValidation = () => { const changeWidgetType = (widgetName: string) => { const _config = getWidgetConfig(widgetName); if (widgetName === widgetGenerateState.selectedWidgetName || !_config) return; - widgetGenerateStore.setSelectedWidgetName(widgetName); - widgetGenerateStore.setSize(_config.meta.sizes[0]); const sanitizedOptions = sanitizeWidgetOptions(props.fieldManager.data, widgetName, state.selectedDataTable); + props.fieldManager.updateWidgetConfig(_config); props.fieldManager.updateModifiedData(sanitizedOptions); + widgetGenerateStore.setSelectedWidgetName(widgetName); + widgetGenerateStore.setSize(_config.meta.sizes[0]); checkDefaultValidation(); }; diff --git a/apps/web/src/common/modules/widgets/_helpers/widget-helper.ts b/apps/web/src/common/modules/widgets/_helpers/widget-helper.ts index 55209baad7..e943ce2834 100644 --- a/apps/web/src/common/modules/widgets/_helpers/widget-helper.ts +++ b/apps/web/src/common/modules/widgets/_helpers/widget-helper.ts @@ -1,5 +1,5 @@ import bytes from 'bytes'; -import { cloneDeep } from 'lodash'; +import { cloneDeep, isArray } from 'lodash'; import { byteFormatter, customNumberFormatter, numberFormatter } from '@cloudforet/utils'; @@ -10,6 +10,8 @@ import { DATE_FIELD } from '@/common/modules/widgets/_constants/widget-constant' import { NUMBER_FORMAT } from '@/common/modules/widgets/_constants/widget-field-constant'; import { getWidgetConfig } from '@/common/modules/widgets/_helpers/widget-config-helper'; import { integrateFieldsSchema } from '@/common/modules/widgets/_helpers/widget-field-helper'; +// eslint-disable-next-line import/no-cycle +import { widgetFieldDefaultValueSetterRegistry } from '@/common/modules/widgets/_widget-field-value-manager/constant/default-value-registry'; import { WIDGET_OPTIONS_AFFECTED_BY_DATA_TABLE, widgetValidatorRegistry } from '@/common/modules/widgets/_widget-field-value-manager/constant/validator-registry'; import type { NumberFormatInfo } from '@/common/modules/widgets/_widget-fields/number-format/type'; import type { DataTableModel } from '@/common/modules/widgets/types/widget-data-table-type'; @@ -67,51 +69,54 @@ export const sanitizeWidgetOptions = (options: WidgetModel['options'] = {}, widg // Remove keys that are not in the validOptionKeys list currentOptionKeys.forEach((key) => { + const fieldValue = cloneDeep(options[key]).value; if (!validOptionKeys.includes(key)) { delete options[key]; } - const fieldValue = cloneDeep(options[key]).value; const validator = widgetValidatorRegistry[key]; const isFieldAffectedByDataTable = WIDGET_OPTIONS_AFFECTED_BY_DATA_TABLE.includes(key); - if (!dataTable || !fieldValue || !validator || !isFieldAffectedByDataTable) return; + if (!validOptionKeys.includes(key) || !dataTable || !fieldValue || !validator || !isFieldAffectedByDataTable) return; const fieldOptions = _fieldsSchema[key]?.options ?? {}; if (!validator(fieldValue, widgetConfig, dataTable, options)) { const availableFieldKeys = Object.keys(dataTable?.[fieldOptions?.dataTarget || 'data_info'] ?? {}); - console.debug('key', key, dataTable, fieldValue, availableFieldKeys, fieldOptions, validator(fieldValue, widgetConfig, dataTable, options)); - console.debug('availableFieldKeys', key, fieldOptions, availableFieldKeys, fieldValue.data); if (key === 'dataField') { const isMultiSelectable = fieldOptions?.multiSelectable; + const originalData = fieldValue.data; + const filteredData = isArray(originalData) ? originalData.filter((val) => availableFieldKeys.includes(val)) : []; + const getSingleValue = (data) => (availableFieldKeys.includes(data) ? data : availableFieldKeys[0]); const isPivotDataTable = dataTable?.operator === DATA_TABLE_OPERATOR.PIVOT; if (isPivotDataTable) { - const pivotColumnsField = dataTable?.options.PIVOT?.fields?.column; - options[key] = { value: { ...fieldValue, data: isMultiSelectable ? [pivotColumnsField] : pivotColumnsField } }; - } else if (isMultiSelectable) { + const pivotColumnsField = dataTable?.options.PIVOT?.fields?.column; // string; options[key] = { value: { ...fieldValue, - data: fieldValue.data?.filter((val) => availableFieldKeys.includes(val)) || [], + data: isMultiSelectable ? [pivotColumnsField] : pivotColumnsField, }, }; - } else if (!availableFieldKeys.includes(fieldValue.data)) { - options[key] = { value: { ...fieldValue, data: availableFieldKeys[0] } }; + } else { + options[key] = { + ...fieldValue, + value: { data: isMultiSelectable ? filteredData : getSingleValue(originalData) }, + }; } } if (key === 'groupBy') { const isMultiSelectable = fieldOptions?.multiSelectable; - if (isMultiSelectable) { - options[key] = { - value: { - ...fieldValue, - data: fieldValue.data?.filter((val) => availableFieldKeys.includes(val)) || [], - }, - }; - } else if (!availableFieldKeys.includes(fieldValue.data)) { - options[key] = { value: { ...fieldValue, data: availableFieldKeys[0] } }; - } + const originalData = fieldValue.data; + const filteredData = isArray(originalData) ? originalData.filter((val) => availableFieldKeys.includes(val)) : []; + const hideCount = fieldOptions?.hideCount; + const getSingleValue = (data) => (availableFieldKeys.includes(data) ? data : availableFieldKeys[0]); + + options[key] = { + value: { + count: !hideCount ? (fieldValue.count ?? fieldOptions.defaultMaxCount ?? 5) : undefined, + data: isMultiSelectable ? filteredData : getSingleValue(originalData), + }, + }; } if (key === 'categoryBy' || key === 'stackBy' || key === 'xAxis' || key === 'yAxis') { if (!availableFieldKeys.includes(fieldValue.data)) { @@ -155,6 +160,15 @@ export const sanitizeWidgetOptions = (options: WidgetModel['options'] = {}, widg } }); - console.debug('options', options); + validOptionKeys.forEach((key) => { + const fieldValue = cloneDeep(options[key])?.value; + if (!fieldValue) { + const defaultValueSetter = widgetFieldDefaultValueSetterRegistry[key]; + if (defaultValueSetter) { + options[key] = { value: defaultValueSetter(widgetConfig, dataTable) }; + } + } + }); + return options; }; diff --git a/apps/web/src/common/modules/widgets/_widget-field-value-manager/index.ts b/apps/web/src/common/modules/widgets/_widget-field-value-manager/index.ts index 8425d6207d..d24393a68b 100644 --- a/apps/web/src/common/modules/widgets/_widget-field-value-manager/index.ts +++ b/apps/web/src/common/modules/widgets/_widget-field-value-manager/index.ts @@ -106,7 +106,7 @@ export default class WidgetFieldValueManager { this.validationErrors = {}; } - private updateWidgetConfig(widgetConfig: WidgetConfig): void { + updateWidgetConfig(widgetConfig: WidgetConfig): void { this.widgetConfig = widgetConfig; } 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 ff90f85cbe..74d273dde2 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 @@ -307,7 +307,8 @@ watch([() => state.chartData, () => chartContext.value], ([, chartCtx]) => { } }); -watch([() => state.data, () => props.widgetOptions], ([newData]) => { +watch([() => state.data, () => props.widgetOptions, () => state.dataTable], ([newData,, _dataTable]) => { + if (!_dataTable) return; drawChart(newData); }, { immediate: true }); 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 f34d6ee2ba..f136ec423c 100644 --- a/apps/web/src/common/modules/widgets/_widgets/gauge/Gauge.vue +++ b/apps/web/src/common/modules/widgets/_widgets/gauge/Gauge.vue @@ -205,7 +205,8 @@ watch([() => state.chartData, () => chartContext.value, () => props.widgetOption state.chart.setOption(state.chartOptions, true); } }); -watch([() => state.data, () => props.widgetOptions], ([newData]) => { +watch([() => state.data, () => props.widgetOptions, () => state.dataTable], ([newData,, _dataTable]) => { + if (!_dataTable) return; drawChart(newData); }, { immediate: true }); watch(() => props.dataTableId, async (newDataTableId) => { 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 06d5363f78..6e2669e503 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 @@ -206,7 +206,8 @@ watch([() => state.chartData, () => chartContext.value, () => state.mapLoaded], state.chart.setOption(state.chartOptions, true); } }); -watch([() => state.data, () => props.widgetOptions], async ([newData]) => { +watch([() => state.data, () => props.widgetOptions, () => state.dataTable], async ([newData,, _dataTable]) => { + if (!_dataTable) return; await loadMap(); await drawChart(newData); }, { immediate: true }); 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 93070770f4..4eaa591cd2 100644 --- a/apps/web/src/common/modules/widgets/_widgets/heatmap/Heatmap.vue +++ b/apps/web/src/common/modules/widgets/_widgets/heatmap/Heatmap.vue @@ -252,7 +252,8 @@ watch([() => state.chartData, () => chartContext.value], ([, chartCtx]) => { } }); -watch([() => state.data, () => props.widgetOptions], ([newData]) => { +watch([() => state.data, () => props.widgetOptions, () => state.dataTable], ([newData,, _dataTable]) => { + if (!_dataTable) return; drawChart(newData); }, { immediate: true }); 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 6b9487773e..76c27a9bf9 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 @@ -93,7 +93,7 @@ const state = reactive({ }), chartData: [], isAreaChart: computed(() => props.widgetName === 'stackedAreaChart'), - unitMap: computed>(() => widgetFrameProps.value.unitMap || {}), + unitMap: computed>(() => widgetFrameProps?.value?.unitMap || {}), chartOptions: computed(() => ({ color: MASSIVE_CHART_COLORS, grid: { @@ -272,9 +272,17 @@ const drawChart = (rawData: WidgetLoadResponse|null) => { }, }); }); + state.chartData = _seriesData; }; +const { widgetFrameProps, widgetFrameEventHandlers } = useWidgetFrame(props, emit, { + dateRange, + errorMessage, + widgetLoading, + noData: computed(() => (state.data ? !state.data.results?.length : false)), +}); + /* Watcher */ watch([() => state.chartData, () => chartContext.value], ([, chartCtx]) => { if (chartCtx) { @@ -282,16 +290,11 @@ watch([() => state.chartData, () => chartContext.value], ([, chartCtx]) => { state.chart.setOption(state.chartOptions, true); } }); -watch([() => state.data, () => props.widgetOptions], ([newData]) => { +watch([() => state.data, () => props.widgetOptions, () => state.dataTable], ([newData,, _dataTable]) => { + if (!_dataTable) return; drawChart(newData); }, { immediate: true }); -const { widgetFrameProps, widgetFrameEventHandlers } = useWidgetFrame(props, emit, { - dateRange, - errorMessage, - widgetLoading, - noData: computed(() => (state.data ? !state.data.results?.length : false)), -}); useResizeObserver(chartContext, throttle(() => { state.chart?.resize(); 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 63fca9b4c1..94ef55b7d4 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 @@ -286,7 +286,8 @@ watch([() => state.chartData, () => chartContext.value], ([, chartCtx]) => { state.chart.setOption(state.chartOptions, true); } }); -watch([() => state.data, () => props.widgetOptions], ([newData]) => { +watch([() => state.data, () => props.widgetOptions, () => state.dataTable], ([newData,, _dataTable]) => { + if (!_dataTable) return; drawChart(newData); }, { immediate: true }); useResizeObserver(chartContext, throttle(() => { 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 c020dcbced..43b4045794 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 @@ -233,7 +233,8 @@ watch([() => state.chartData, () => chartContext.value], ([, chartCtx]) => { state.chart.setOption(state.chartOptions, true); } }); -watch([() => state.data, () => props.widgetOptions], ([newData]) => { +watch([() => state.data, () => props.widgetOptions, () => state.dataTable], ([newData,, _dataTable]) => { + if (!_dataTable) return; drawChart(newData); }, { immediate: true }); 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 2f1c8bcb48..249f80f640 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 @@ -281,7 +281,8 @@ watch([() => state.chartData, () => chartContext.value], ([, chartCtx]) => { state.chart.setOption(state.chartOptions, true); } }); -watch([() => state.data, () => props.widgetOptions], ([newData]) => { +watch([() => state.data, () => props.widgetOptions, () => state.dataTable], ([newData,, _dataTable]) => { + if (!_dataTable) return; drawChart(newData); }, { immediate: true }); 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 651c5f5a4a..bdcbcb5c7e 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 @@ -274,7 +274,8 @@ watch([() => state.chartData, () => chartContext.value], ([, chartCtx]) => { state.chart.setOption(state.chartOptions, true); } }); -watch([() => state.data, () => props.widgetOptions], ([newData]) => { +watch([() => state.data, () => props.widgetOptions, () => state.dataTable], ([newData,, _dataTable]) => { + if (!_dataTable) return; drawChart(newData); }, { immediate: true }); 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 1a3d76210f..93e3b06ad3 100644 --- a/apps/web/src/common/modules/widgets/_widgets/treemap/Treemap.vue +++ b/apps/web/src/common/modules/widgets/_widgets/treemap/Treemap.vue @@ -235,7 +235,8 @@ watch([() => state.chartData, () => chartContext.value], ([, chartCtx]) => { state.chart.setOption(state.chartOptions, true); } }); -watch([() => state.data, () => props.widgetOptions], ([newData]) => { +watch([() => state.data, () => props.widgetOptions, () => state.dataTable], ([newData,, _dataTable]) => { + if (!_dataTable) return; drawChart(newData); }, { immediate: true });