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 @@ -31,7 +31,7 @@ import WidgetFormDataTableCardSourceForm
import {
DATA_SOURCE_DOMAIN,
DATA_TABLE_OPERATOR,
DATA_TABLE_TYPE,
DATA_TABLE_TYPE, GROUP_BY_INFO_ITEMS_FOR_TAGS,
} from '@/common/modules/widgets/_constants/data-table-constant';
import { useWidgetGenerateStore } from '@/common/modules/widgets/_store/widget-generate-store';
import type { DataTableAlertModalMode } from '@/common/modules/widgets/types/widget-data-table-type';
Expand All @@ -40,6 +40,8 @@ import type {
DataTableQueryFilter, TimeDiff,
} from '@/common/modules/widgets/types/widget-model';

import { GROUP_BY } from '@/services/cost-explorer/constants/cost-explorer-constant';

interface Props {
selected: boolean;
item: PublicDataTableModel|PrivateDataTableModel;
Expand Down Expand Up @@ -73,7 +75,11 @@ const state = reactive({
selectedSourceEndItem: props.item.source_type === DATA_SOURCE_DOMAIN.COST
? props.item.options[DATA_SOURCE_DOMAIN.COST]?.data_key
: props.item.options[DATA_SOURCE_DOMAIN.ASSET]?.metric_id,
selectedGroupByItems: [] as { name: string; label: string; }[],
selectedGroupByItems: [] as { name: string; label: string; tags?: [] }[],
selectedGroupByTagsMap: {
[GROUP_BY.PROJECT]: [],
[GROUP_BY.REGION]: [],
} as Record<string, string[]>,
filter: {} as Record<string, DataTableQueryFilter>,
dataFieldName: '',
dataUnit: '',
Expand Down Expand Up @@ -107,13 +113,15 @@ const state = reactive({
optionsChanged: computed(() => {
const sourceKeyChanged = 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);
const dataTableNameChanged = state.dataFieldName !== originDataState.dataName;
const dataUnitChanged = state.dataUnit !== originDataState.dataUnit;
const timeDiffChanged = advancedOptionsState.selectedTimeDiff !== originDataState.timeDiff;
const timeDiffDateChanged = advancedOptionsState.selectedTimeDiffDate !== originDataState.timeDiffDate;

return sourceKeyChanged || groupByChanged || filterChanged || dataTableNameChanged || dataUnitChanged
return sourceKeyChanged || groupByChanged || groupByTagsChanged || filterChanged
|| dataTableNameChanged || dataUnitChanged
|| timeDiffChanged || timeDiffDateChanged;
}),
failStatus: false,
Expand All @@ -139,7 +147,22 @@ const originDataState = reactive({
groupBy: computed(() => ((props.item.options as DataTableAddOptions).group_by ?? []).map((group) => ({
name: group.key,
label: group.name,
tags: group.tags,
}))),
groupByTagsMap: computed(() => {
const _groupByTagsMap = {
[GROUP_BY.PROJECT]: [],
[GROUP_BY.REGION]: [],
} as Record<string, string[]>;
((props.item.options as DataTableAddOptions).group_by ?? []).forEach((group) => {
const isGroupByTags = GROUP_BY_INFO_ITEMS_FOR_TAGS.some((tag) => tag.key === group.key);
if (isGroupByTags) {
const tagsMenu = group.tags?.map((tag) => ({ name: tag, label: tag }));
_groupByTagsMap[group.key as string] = tagsMenu || [];
}
});
return _groupByTagsMap;
}),
filter: computed<Record<string, DataTableQueryFilter>>(() => {
const _filter = {} as Record<string, DataTableQueryFilter>;
((props.item.options as DataTableAddOptions).filter ?? []).forEach((filter) => {
Expand Down Expand Up @@ -201,10 +224,24 @@ const updateDataTable = async (): Promise<DataTableModel|undefined> => {
const costGroupBy = state.selectedGroupByItems.map((group) => ({
key: group.name,
name: group.label,
tags: group.tags,
}));
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;

GROUP_BY_INFO_ITEMS_FOR_TAGS.forEach((tag) => {
const groupByTags = groupBy.find((group) => group.key === tag.key);
if (groupByTags) {
groupBy.map((group) => {
if (tag.key === group.key) {
group.tags = state.selectedGroupByTagsMap[tag.key]?.map((item) => item.name) || [];
}
return group;
});
}
});

const refinedFilter = Object.values(state.filter as Record<string, DataTableQueryFilter>)
.filter((filter) => {
if (isArray(filter.v)) return filter?.v?.length;
Expand Down Expand Up @@ -311,6 +348,7 @@ const setInitialDataTableForm = () => {
// Initial Form Setting
// Basic Options
state.selectedGroupByItems = [...originDataState.groupBy];
state.selectedGroupByTagsMap = { ...originDataState.groupByTagsMap };
state.filter = originDataState.filter;
state.dataFieldName = originDataState.dataName;
state.dataUnit = originDataState.dataUnit;
Expand Down Expand Up @@ -379,6 +417,7 @@ defineExpose({
:source-type="state.sourceType"
:source-items="state.selectableSourceItems"
:selected-group-by-items.sync="state.selectedGroupByItems"
:selected-group-by-tags-map.sync="state.selectedGroupByTagsMap"
:filter.sync="state.filter"
:data-field-name.sync="state.dataFieldName"
:data-unit.sync="state.dataUnit"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@ import type { TranslateResult } from 'vue-i18n';

import { cloneDeep, range } from 'lodash';

import {
makeDistinctValueHandler,
} from '@cloudforet/core-lib/component-util/query-search';
import {
PFieldGroup, PSelectDropdown, PTextInput,
} from '@cloudforet/mirinae';
import type { MenuItem } from '@cloudforet/mirinae/src/controls/context-menu/type';
import type { SelectDropdownMenuItem } from '@cloudforet/mirinae/src/controls/dropdown/select-dropdown/type';
import type { AutocompleteHandler } from '@cloudforet/mirinae/types/controls/dropdown/select-dropdown/type';

import { i18n } from '@/translations';

Expand All @@ -20,15 +24,23 @@ import type { CostDataSourceReferenceMap } from '@/store/reference/cost-data-sou
import type { MetricReferenceMap } from '@/store/reference/metric-reference-store';

import { showErrorMessage } from '@/lib/helper/notice-alert-helper';
import {
MANAGED_VARIABLE_MODELS, type ManagedVariableModelKey,
} from '@/lib/variable-models/managed-model-config/base-managed-model-config';

import { useCostDataSourceFilterMenuItems } from '@/common/composables/data-source/use-cost-data-source-filter-menu-items';
import ErrorHandler from '@/common/composables/error/errorHandler';
import { useProxyValue } from '@/common/composables/proxy-state';
import WidgetFormDataTableCardFilters from '@/common/modules/widgets/_components/WidgetFormDataTableCardFilters.vue';
import { DATA_SOURCE_DOMAIN } from '@/common/modules/widgets/_constants/data-table-constant';
import {
DATA_SOURCE_DOMAIN,
GROUP_BY_INFO_ITEMS_FOR_TAGS,
} from '@/common/modules/widgets/_constants/data-table-constant';
import type { DataTableQueryFilter } from '@/common/modules/widgets/types/widget-model';

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

const TAGS_DATA_KEY = 'tags';


interface Props {
Expand All @@ -39,6 +51,7 @@ interface Props {
sourceKey: string;
sourceItems: SelectDropdownMenuItem[];
selectedGroupByItems: any[];
selectedGroupByTagsMap: Record<string, string[]>;
filter: Record<string, DataTableQueryFilter>;
dataFieldName: string;
dataUnit: string;
Expand All @@ -55,6 +68,7 @@ const MAX_GROUP_BY_COUNT = 5;
const props = defineProps<Props>();
const emit = defineEmits<{(e: 'update:filter', value: Record<string, string[]>): void;
(e: 'update:selected-group-by-items', value: any[]): void;
(e: 'update:selected-group-by-tags-map', value: Record<string, string[]>): void;
(e: 'update:data-field-name', value: string): void;
(e: 'update:data-unit', value: string): void;
(e: 'update:selected-time-diff', value: string): void;
Expand All @@ -78,6 +92,7 @@ const { allItems: costDataSourceMenuItems } = useCostDataSourceFilterMenuItems({

const state = reactive({
proxySelectedGroupByItems: useProxyValue('selectedGroupByItems', props, emit),
proxySelectedGroupByTagsMap: useProxyValue('selectedGroupByTagsMap', props, emit),
proxyDataFieldName: useProxyValue('dataFieldName', props, emit),
proxyDataUnit: useProxyValue('dataUnit', props, emit),
proxyFilter: useProxyValue<Record<string, DataTableQueryFilter>>('filter', props, emit),
Expand Down Expand Up @@ -144,6 +159,32 @@ const groupByState = reactive({
}),
});

const groupByTagsState = reactive({
tagsAreaVisible: computed(() => state.proxySelectedGroupByItems.some((d) => GROUP_BY_INFO_ITEMS_FOR_TAGS.some((item) => item.key === d.name))),
tagsAreaItems: computed(() => state.proxySelectedGroupByItems.filter((d) => GROUP_BY_INFO_ITEMS_FOR_TAGS.some((item) => item.key === d.name))),
loading: false,
valueHandlerMap: computed(() => {
const handlerMaps = {};
state.proxySelectedGroupByItems.forEach(({ name }) => {
const groupByItemInfo = GROUP_BY_INFO_ITEMS_FOR_TAGS.find((d) => d.key === name);
if (groupByItemInfo) {
handlerMaps[groupByItemInfo.key] = getValueHandlerMap(groupByItemInfo.name);
}
});
return handlerMaps;
}),
selectedMap: computed(() => {
const selectedMap = {};
state.proxySelectedGroupByItems.forEach((item) => {
const groupByItemInfo = GROUP_BY_INFO_ITEMS_FOR_TAGS.find((d) => d.key === item.name);
if (groupByItemInfo) {
selectedMap[item.name] = item.tags || [];
}
});
return selectedMap;
}),
});

const assetFilterState = reactive({
refinedLabelKeys: computed(() => {
const metricLabelsInfo = storeState.metrics[props.sourceId ?? '']?.data?.labels_info;
Expand Down Expand Up @@ -197,6 +238,23 @@ const resetAllFilter = () => {
state.proxyFilter = {};
};

const getValueHandlerMap = (name: ManagedVariableModelKey): AutocompleteHandler => {
const resourceKey = MANAGED_VARIABLE_MODELS[name]?.meta?.resourceType;
const handler = makeDistinctValueHandler(resourceKey, TAGS_DATA_KEY);
return async (...args) => {
try {
groupByTagsState.loading = true;
const results = await handler(...args);
return results;
} catch (e) {
ErrorHandler.handleError(e);
return { results: [] };
} finally {
groupByTagsState.loading = false;
}
};
};

watch([() => props.sourceId, () => props.sourceKey], async () => {
resetAllFilter();
});
Expand Down Expand Up @@ -225,6 +283,26 @@ watch([
block
@select="handleUpdateSelectedGroupBy"
/>
<div v-if="groupByTagsState.tagsAreaVisible"
class="groupb-by-tags-area flex flex-col gap-2"
>
<div v-for="(item) in groupByTagsState.tagsAreaItems"
:key="`tags-dropdown-${item.name}`"
class="tags-dropdown-wrapper"
>
<div class="header-name flex items-center">
{{ item.label }} tags
</div>
<p-select-dropdown block
:selected.sync="state.proxySelectedGroupByTagsMap[item.name]"
:handler="groupByTagsState.valueHandlerMap[item.name]"
is-filterable
multi-selectable
show-select-marker
appearance-type="stack"
/>
</div>
</div>
</p-field-group>
<widget-form-data-table-card-filters :key="props.filterFormKey"
:data-table-id="props.dataTableId"
Expand Down Expand Up @@ -278,6 +356,23 @@ watch([
<style lang="postcss" scoped>
.widget-form-data-table-card-add-form {
padding: 0.75rem;

.groupb-by-tags-area {
@apply bg-gray-100 rounded-lg;
padding: 0.75rem 0.5rem;
margin-top: 0.25rem;

.tags-dropdown-wrapper {
@apply bg-white border border-gray-150 rounded-lg;
width: 100%;
padding: 0.125rem 0.5rem 0.5rem;
.header-name {
@apply text-label-md font-bold text-gray-800;
height: 2rem;
}
}
}

.data-text-input {
@apply w-full;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,14 @@ export const DEFAULT_TRANSFORM_DATA_TABLE_VALUE_MAP = {
],
} as ValueMappingOptions,
};

export const GROUP_BY_INFO_ITEMS_FOR_TAGS = [
{
key: GROUP_BY.PROJECT,
name: 'project',
},
{
key: GROUP_BY.SERVICE_ACCOUNT,
name: 'service_account',
},
] as const;
9 changes: 8 additions & 1 deletion apps/web/src/common/modules/widgets/types/widget-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ export type DataTableSourceType = typeof DATA_SOURCE_DOMAIN[keyof typeof DATA_SO
export type DataTableOperator = typeof DATA_TABLE_OPERATOR[keyof typeof DATA_TABLE_OPERATOR];
export type DataTableDataType = keyof typeof DATA_TABLE_TYPE;
export type AdditionalLabels = Record<string, string>;
export interface DataTableGroupByInfo {
key:string;
name: string;
reference?: object;
search_key?: string;
tags?: string[]; // [tag_key_1, tag_key_2, ...]
}
export interface TimeDiff {
years?: number;
months?: number;
Expand All @@ -26,7 +33,7 @@ export type WidgetState = 'CREATING' | 'INACTIVE' | 'ACTIVE';
export interface DataTableAddOptions {
'ASSET'?: AssetOptions;
'COST'?: CostOptions;
group_by?: {key:string; name: string; reference?: object; search_key?: string }[];
group_by?: DataTableGroupByInfo[];
data_name: string;
data_unit?: string;
timediff?: TimeDiff;
Expand Down
Loading
Loading