Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import ErrorHandler from '@/common/composables/error/errorHandler';
import WidgetFormAssetSecurityDataSourcePopper
from '@/common/modules/widgets/_components/WidgetFormAssetSecurityDataSourcePopper.vue';
import WidgetFormCostDataSourcePopper from '@/common/modules/widgets/_components/WidgetFormCostDataSourcePopper.vue';
import WidgetFormUnifiedCostDataSourcePopper
from '@/common/modules/widgets/_components/WidgetFormUnifiedCostDataSourcePopper.vue';
import { useWidgetFormQuery } from '@/common/modules/widgets/_composables/use-widget-form-query';
import {
DATA_SOURCE_DOMAIN,
Expand Down Expand Up @@ -104,6 +106,9 @@ const state = reactive({
if (state.selectedDataSourceDomain === DATA_SOURCE_DOMAIN.COST) {
return !state.selectedCostDataSourceId || !state.selectedCostDataType;
}
if (state.selectedDataSourceDomain === DATA_SOURCE_DOMAIN.UNIFIED_COST) {
return !state.selectedUnifiedCostDataType;
}
if ([DATA_SOURCE_DOMAIN.ASSET, DATA_SOURCE_DOMAIN.SECURITY].includes(state.selectedDataSourceDomain)) {
return !state.selectedMetricId;
}
Expand All @@ -112,6 +117,7 @@ const state = reactive({
// cost
selectedCostDataSourceId: undefined as undefined|string,
selectedCostDataType: undefined as undefined|string,
selectedUnifiedCostDataType: undefined as undefined|string,
// asset & security
selectedMetricId: undefined as undefined|string,
selectedNamespace: computed(() => {
Expand Down Expand Up @@ -236,6 +242,7 @@ const { mutateAsync: addDataTable, isPending: dataTableAddLoading } = useMutatio
const resetSelectedDataSource = () => {
state.selectedCostDataSourceId = undefined;
state.selectedCostDataType = undefined;
state.selectedUnifiedCostDataType = undefined;
state.selectedMetricId = undefined;
};

Expand Down Expand Up @@ -287,9 +294,11 @@ const handleConfirmDataSource = async () => {
}

if (state.selectedPopperCondition === DATA_TABLE_TYPE.ADDED) {
const dataTableBaseName = state.selectedDataSourceDomain === DATA_SOURCE_DOMAIN.COST
? `${storeState.costDataSources[state.selectedCostDataSourceId].name} - ${state.selectedCostDataTypeLabel}`
: `${state.selectedNamespace.name} - ${storeState.metrics[state.selectedMetricId]?.label}`;
let dataTableBaseName: string;
if (state.selectedDataSourceDomain === DATA_SOURCE_DOMAIN.COST) dataTableBaseName = `${storeState.costDataSources[state.selectedCostDataSourceId].name} - ${state.selectedCostDataTypeLabel}`;
else if (state.selectedDataSourceDomain === DATA_SOURCE_DOMAIN.UNIFIED_COST) dataTableBaseName = 'Unified Cost';
else dataTableBaseName = `${state.selectedNamespace.name} - ${storeState.metrics[state.selectedMetricId]?.label}`;

const addParameters = {
widget_id: widgetGenerateState.widgetId as string,
source_type: state.selectedDataSourceDomain,
Expand All @@ -313,7 +322,13 @@ const handleConfirmDataSource = async () => {
data_key: state.selectedCostDataType,
},
};
const assetOptions = {
const unifiedCostOptions: DataTableAddOptions = {
data_name: dataTableBaseName,
UNIFIED_COST: {
data_key: state.selectedUnifiedCostDataType,
},
};
const assetOptions: DataTableAddOptions = {
data_name: storeState.metrics[state.selectedMetricId]?.label,
data_unit: storeState.metrics[state.selectedMetricId]?.data.unit,
ASSET: {
Expand All @@ -325,13 +340,19 @@ const handleConfirmDataSource = async () => {
widgetGenerateStore.setDataTableCreateLoading(true);
state.showPopover = false;

await addDataTable({
const mergedParams = {
...addParameters,
vars: dashboard.value?.vars || {},
options: {
...state.selectedDataSourceDomain === DATA_SOURCE_DOMAIN.COST ? costOptions : assetOptions,
},
});
};
if (state.selectedDataSourceDomain === DATA_SOURCE_DOMAIN.COST) {
mergedParams.options = costOptions;
} else if (state.selectedDataSourceDomain === DATA_SOURCE_DOMAIN.UNIFIED_COST) {
mergedParams.options = unifiedCostOptions;
} else {
mergedParams.options = assetOptions;
}

await addDataTable(mergedParams);
emit('scroll');
}
widgetGenerateStore.setDataTableCreateLoading(false);
Expand Down Expand Up @@ -413,6 +434,29 @@ watch(() => state.showPopover, (val) => {
</p>
</div>
</p-select-card>
<p-select-card :class="{'custom-select-card': true, 'selected': state.selectedDataSourceDomain === DATA_SOURCE_DOMAIN.UNIFIED_COST }"
:value="DATA_SOURCE_DOMAIN.UNIFIED_COST"
@click="handleClickDataSourceDomain(DATA_SOURCE_DOMAIN.UNIFIED_COST)"
>
<div class="domain-contents">
<p-i v-if="state.selectedDataSourceDomain === DATA_SOURCE_DOMAIN.UNIFIED_COST"
class="selected-marker"
name="ic_checkbox-circle-selected"
width="1.25rem"
height="1.25rem"
/>
<div class="icon-wrapper">
<p-i name="ic_data-domain-cost"
width="1.25rem"
height="1.25rem"
/>
</div>
<p class="name">
{{ i18n.t('DASHBOARDS.WIDGET.OVERLAY.STEP_1.UNIFIED_COST') }}
</p>
</div>
</p-select-card>

<p class="data-source-domain-title mt-2">
{{ i18n.t('DASHBOARDS.WIDGET.OVERLAY.STEP_1.INVENTORY') }}
</p>
Expand Down Expand Up @@ -445,6 +489,9 @@ watch(() => state.showPopover, (val) => {
:selected-cost-data-source-id.sync="state.selectedCostDataSourceId"
:selected-cost-data-type.sync="state.selectedCostDataType"
/>
<widget-form-unified-cost-data-source-popper v-if="state.selectedDataSourceDomain === DATA_SOURCE_DOMAIN.UNIFIED_COST"
:selected-cost-data-type.sync="state.selectedUnifiedCostDataType"
/>
<widget-form-asset-security-data-source-popper
v-if="[DATA_SOURCE_DOMAIN.ASSET, DATA_SOURCE_DOMAIN.SECURITY].includes(state.selectedDataSourceDomain)"
:data-source-domain="state.selectedDataSourceDomain"
Expand Down Expand Up @@ -647,7 +694,7 @@ watch(() => state.showPopover, (val) => {
.data-source-popover-content {
display: flex;
flex-direction: column;
min-width: 43.5rem;
min-width: 44rem;
height: 26rem;
.top-part {
display: flex;
Expand All @@ -657,7 +704,7 @@ watch(() => state.showPopover, (val) => {
@apply border-r border-gray-200;
display: flex;
flex-direction: column;
min-width: 11.5rem;
min-width: 12rem;
height: 100%;
padding: 1rem 0.75rem;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ const state = reactive({
}),
filterFormKey: getRandomId(),
optionsChanged: computed(() => {
const sourceKeyChanged = state.selectedSourceEndItem !== originDataState.sourceKey;
const sourceKeyChanged = state.sourceType !== DATA_SOURCE_DOMAIN.UNIFIED_COST && state.selectedSourceEndItem !== originDataState.sourceKey;
const groupByChanged = !isEqual(state.selectedGroupByItems, originDataState.groupBy);
const groupByTagsChanged = !isEqual(state.selectedGroupByTagsMap, originDataState.groupByTagsMap);
const filterChanged = !isEqual(state.filter, originDataState.filter);
Expand Down Expand Up @@ -175,7 +175,11 @@ const validationState = reactive({
});

const originDataState = reactive({
sourceKey: computed(() => (state.sourceType === DATA_SOURCE_DOMAIN.COST ? props.item.options[DATA_SOURCE_DOMAIN.COST]?.data_key : props.item.options[DATA_SOURCE_DOMAIN.ASSET]?.metric_id)),
sourceKey: computed(() => {
if (state.sourceType === DATA_SOURCE_DOMAIN.COST) return props.item.options[DATA_SOURCE_DOMAIN.COST]?.data_key;
if (state.sourceType === DATA_SOURCE_DOMAIN.UNIFIED_COST) return 'cost';
return props.item.options[DATA_SOURCE_DOMAIN.ASSET]?.metric_id;
}),
groupBy: computed(() => ((props.item.options as DataTableAddOptions).group_by ?? []).map((group) => ({
name: group.key,
label: group.name,
Expand Down Expand Up @@ -302,9 +306,10 @@ const updateDataTable = async (): Promise<DataTableModel|undefined> => {
return undefined;
}

const domainOptions = state.sourceType === DATA_SOURCE_DOMAIN.COST
? { data_source_id: state.dataSourceId, data_key: state.selectedSourceEndItem }
: { metric_id: state.selectedSourceEndItem };
let domainOptions;
if (state.sourceType === DATA_SOURCE_DOMAIN.COST) domainOptions = { data_source_id: state.dataSourceId, data_key: state.selectedSourceEndItem };
if (state.sourceType === DATA_SOURCE_DOMAIN.UNIFIED_COST) domainOptions = { data_key: 'cost' };
if (state.sourceType === DATA_SOURCE_DOMAIN.ASSET) domainOptions = { metric_id: state.selectedSourceEndItem };

const costGroupBy = state.selectedGroupByItems.map((group) => ({
key: group.name,
Expand All @@ -313,7 +318,8 @@ const updateDataTable = async (): Promise<DataTableModel|undefined> => {
}));
const metricLabelsInfo = storeState.metrics[state.metricId ?? '']?.data?.labels_info;
const assetGroupBy = (metricLabelsInfo ?? []).filter((label) => state.selectedGroupByItems.map((group) => group.name).includes(label.key));
const groupBy = state.sourceType === DATA_SOURCE_DOMAIN.COST ? costGroupBy : assetGroupBy;

const groupBy = (state.sourceType === DATA_SOURCE_DOMAIN.COST || state.sourceType === DATA_SOURCE_DOMAIN.UNIFIED_COST) ? costGroupBy : assetGroupBy;

GROUP_BY_INFO_ITEMS_FOR_TAGS.forEach((tag) => {
const groupByTags = groupBy.find((group) => group.key === tag.key);
Expand Down Expand Up @@ -532,7 +538,8 @@ defineExpose({
:selected="props.selected"
:data-table-name.sync="dataTableNameState.dataTableName"
/>
<widget-form-data-table-card-source-form :source-type="state.sourceType"
<widget-form-data-table-card-source-form v-if="state.sourceType !== DATA_SOURCE_DOMAIN.UNIFIED_COST"
:source-type="state.sourceType"
:parent-source-id="state.sourceType === DATA_SOURCE_DOMAIN.COST ? state.dataSourceId : state.namespaceId"
:menu="state.selectableSourceItems"
:selected="state.selectedSourceEndItem"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
import type { DataTableQueryFilter } from '@/common/modules/widgets/types/widget-model';

import { PROJECT_GROUP_LABEL_INFO } from '@/services/asset-inventory-v1/constants/asset-analysis-constant';
import { GROUP_BY_ITEM_MAP } from '@/services/cost-explorer/constants/cost-explorer-constant';

const TAGS_DATA_KEY = 'tags';

Expand All @@ -47,8 +48,8 @@ interface Props {
filterFormKey: string;
dataTableId: string;
sourceType?: string;
sourceId: string;
sourceKey: string;
sourceId?: string;
sourceKey?: string;
sourceItems: SelectDropdownMenuItem[];
selectedGroupByItems: any[];
selectedGroupByTagsMap: Record<string, string[]>;
Expand Down Expand Up @@ -87,7 +88,7 @@ const storeState = reactive({
});
const { allItems: costDataSourceMenuItems } = useCostDataSourceFilterMenuItems({
isAdminMode: computed(() => storeState.isAdminMode),
costDataSource: computed(() => storeState.costDataSources[props.sourceId]),
costDataSource: computed(() => storeState.costDataSources[props.sourceId ?? '']),
});

const state = reactive({
Expand Down Expand Up @@ -149,12 +150,20 @@ const groupByState = reactive({
if (props.sourceType === DATA_SOURCE_DOMAIN.COST) {
return costDataSourceMenuItems.value.filter((d) => d.name !== 'project_group_id');
}
if (props.sourceType === DATA_SOURCE_DOMAIN.UNIFIED_COST) {
const groupByItemValueList = Object.values(GROUP_BY_ITEM_MAP);
if (!storeState.isAdminMode) return groupByItemValueList.filter((d) => d.name !== 'workspace_id').map((d) => ({ name: d.name, label: d.label }));
return groupByItemValueList.map((d) => ({ name: d.name, label: d.label }));
}
return [...assetFilterState.metricItems];
}),
filterItems: computed(() => {
filterItems: computed<MenuItem[]>(() => {
if (props.sourceType === DATA_SOURCE_DOMAIN.COST) {
return costDataSourceMenuItems.value;
}
if (props.sourceType === DATA_SOURCE_DOMAIN.UNIFIED_COST) {
return groupByState.items;
}
return [...assetFilterState.metricFilterItems];
}),
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,12 @@ const state = reactive({
proxyFilter: useProxyValue<Record<string, DataTableQueryFilter>>('filter', props, emit),
filterItems: computed(() => props.filterItems),
selectedItems: [] as any[],
primaryCostOptions: computed<Record<string, any>>(() => ({
...((props.sourceType === DATA_SOURCE_DOMAIN.COST && props.sourceId) ? { data_source_id: props.sourceId } : {}),
})),
handlerMap: computed(() => {
const handlerMaps = {};
if (props.sourceType === DATA_SOURCE_DOMAIN.COST) {
if (props.sourceType === DATA_SOURCE_DOMAIN.COST || props.sourceType === DATA_SOURCE_DOMAIN.UNIFIED_COST) {
state.selectedItems.forEach(({ name, presetKeys }) => {
handlerMaps[name] = getCostMenuHandler(name, { presetKeys });
});
Expand All @@ -118,9 +121,6 @@ const state = reactive({
}
return handlerMaps;
}),
primaryCostOptions: computed<Record<string, any>>(() => ({
data_source_id: props.sourceId,
})),
primaryMetricStatOptions: computed<Record<string, any>>(() => ({
metric_id: props.sourceId,
})),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,10 +179,12 @@ const updateWidget = async () => {
if (_layouts.length) {
const _targetLayout = _layouts[0];
if (_targetLayout.widgets) {
_targetLayout.widgets.push(widgetGenerateState.widgetId);
const newLayoutWidgets = [..._targetLayout.widgets as string[], widgetGenerateState.widgetId];
_targetLayout.widgets = sanitizeAndSortWidgets(newLayoutWidgets, widgetList.value.map((w) => w.widget_id));
} else {
_targetLayout.widgets = [widgetGenerateState.widgetId];
}

_layouts[0] = _targetLayout;
} else {
_layouts.push({
Expand All @@ -208,6 +210,28 @@ const { mutate: updateDashboard } = useMutation(
);

/* Util */
const sanitizeAndSortWidgets = (_layoutWidgets: string[] = [], _widgetList: string[] = []): string[] => {
const uniqueWidgets = [...new Set(_layoutWidgets)];

if (uniqueWidgets.length === _widgetList.length && uniqueWidgets.every((item) => _widgetList.includes(item))) {
return [...uniqueWidgets];
}

const widgetsSet = new Set(uniqueWidgets);
const widgetListSet = new Set(_widgetList);

const missingInWidgets = [...widgetListSet].filter((item) => !widgetsSet.has(item));

const sanitizedWidgets = uniqueWidgets.filter((item) => widgetListSet.has(item)).concat(missingInWidgets);

const indexMap = new Map(_widgetList.map((item, index) => [item, index]));
sanitizedWidgets.sort((a, b) => (indexMap.get(a) ?? Infinity) - (indexMap.get(b) ?? Infinity));


return sanitizedWidgets;
};


const initSnapshot = () => {
state.varsSnapshot = cloneDeep(dashboard.value?.vars || {});
state.dashboardOptionsSnapshot = cloneDeep(dashboardDetailState.options);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<script lang="ts" setup>
import {
computed, reactive, watch,
} from 'vue';

import type { MenuItem } from '@cloudforet/mirinae/types/controls/context-menu/type';

import { i18n } from '@/translations';

import DataSelector from '@/common/components/select/DataSelector.vue';
import { useProxyValue } from '@/common/composables/proxy-state';


interface Props {
selectedCostDataType?: string;
}
const props = defineProps<Props>();
const emit = defineEmits<{(e: 'update:selected-cost-data-type', costDataType: string): void; }>();
const state = reactive({
proxySelectedCostDataType: useProxyValue('selectedCostDataType', props, emit),
// data type
dataTypeMenuItems: computed<MenuItem[]>(() => [
{ type: 'item', name: 'cost', label: 'Cost' },
]),
selectedDataType: [] as MenuItem[],
});

/* Watcher */
watch(() => state.selectedDataType, (val) => {
state.proxySelectedCostDataType = val[0]?.name;
});
</script>

<template>
<div class="widget-form-cost-data-source-popper">
<div class="data-source-select-col">
<data-selector :label="i18n.t('Data Type')"
:menu="state.dataTypeMenuItems"
@update:selected="state.selectedDataType = $event"
/>
</div>
</div>
</template>

<style lang="scss" scoped>
.widget-form-cost-data-source-popper {
display: flex;
width: 16rem;
flex: 1;
.data-source-select-col {
@apply border-r border-gray-200;
display: flex;
flex-direction: column;
gap: 0.5rem;
width: 16rem;
padding: 0.75rem 0;
&:last-child {
@apply border-r-0;
}
}
}
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export const DATA_TABLE_TYPE = {

export const DATA_SOURCE_DOMAIN = {
COST: 'COST',
UNIFIED_COST: 'UNIFIED_COST',
ASSET: 'ASSET',
SECURITY: 'SECURITY',
};
Expand Down
Loading
Loading