Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
32934e4
chore: merge master into develop after dev2.0.dev307 version tagging
kkdy21 Feb 5, 2025
83ce7a9
chore: merge master into develop after 2.0.dev308 version tagging
admin-cloudforet Feb 5, 2025
963728b
chore(vercel): remove specific app option from vercel.json (#5619)
sulmoJ Feb 5, 2025
ecfbe17
build: set unfied-storybook build config (#5620)
sulmoJ Feb 5, 2025
c2345a0
Merge remote-tracking branch 'cloudforet-io/master' into develop
piggggggggy Feb 6, 2025
631253b
chore: merge master into develop after 2.0.dev309 version tagging
admin-cloudforet Feb 6, 2025
76c3c03
chore(build): add a GitHub Actions workflow to deploy unified-storybo…
sulmoJ Feb 6, 2025
f5f3da4
chore: merge master into develop after 2.0.dev310 version tagging
admin-cloudforet Feb 7, 2025
964182a
feat: add header slot at overlay layout
skdud4659 Feb 7, 2025
298d7e3
Merge pull request #5625 from skdud4659/feature/overlay
skdud4659 Feb 7, 2025
e7c4a70
chore: changed style section
skdud4659 Feb 7, 2025
3610ac5
Merge pull request #5626 from skdud4659/feature/overlay
skdud4659 Feb 7, 2025
cead913
ci: fix pr review labler workflow to use PAT
WANZARGEN Feb 7, 2025
5f89dbc
ci: update PR review labeler workflow token name
WANZARGEN Feb 7, 2025
339695f
Merge pull request #5627 from WANZARGEN/develop
WANZARGEN Feb 7, 2025
a9681e9
chore: merge master into develop after 2.0.dev311 version tagging
admin-cloudforet Feb 10, 2025
5d138de
chore: merge master into develop after 2.0.dev312 version tagging
admin-cloudforet Feb 10, 2025
9815e35
fix(metric-explorer): solve widget metric link bug & refactor metric …
piggggggggy Feb 11, 2025
0d82d52
fix(cost-analysis): apply project/service-account link to cost data t…
piggggggggy Feb 12, 2025
07029b7
chore: merge master into develop after 2.0.dev313 version tagging
admin-cloudforet Feb 12, 2025
addd79a
feat(group-by-tags): create group by tags form
piggggggggy Feb 12, 2025
311eabd
Merge pull request #5638 from cloudforet-io/feature-dashboard-indoor-…
piggggggggy Feb 12, 2025
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
31 changes: 31 additions & 0 deletions .github/workflows/dispatch_deploy_unfied_storybook.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: "[Dispatch] Deploy unified storybook"

on:
workflow_dispatch:

env:
TEAM_NAME: ${{ vars.VERCEL_TEAM_NAME }}
PROJECT_NAME: "unified-storybook"
VERCEL_TOKEN: ${{ secrets.VERCEL_CLOUDFORET_TOKEN }}

jobs:
Deploy-Production:
runs-on: ubuntu-latest
steps:
- name: Checkout code and submodules
uses: actions/checkout@v2
with:
submodules: true
fetch-depth: 0

- name: Set up environment
run: sudo apt-get install jq

- name: Install Vercel CLI
run: npm install --global vercel@latest

- name: Checkout Vercel Target Project
run: vercel link --yes --scope=${{ env.TEAM_NAME }} --project=${{ env.PROJECT_NAME }} --token=${{ env.VERCEL_TOKEN }}

- name: Deploy Project Artifacts to Vercel
run: vercel deploy --prod --token=${{ env.VERCEL_TOKEN }}
6 changes: 3 additions & 3 deletions .github/workflows/pull_request_review_state_labeler.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
id: check_self_approved
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
github-token: ${{ secrets.CONSOLE_PR_PAT_TOKEN }}
script: |
const prNumber = context.payload.pull_request.number;
const owner = context.repo.owner;
Expand All @@ -36,7 +36,7 @@ jobs:
if: github.event_name == 'pull_request_target' && steps.check_self_approved.outputs.isSelfApprovedPR == 'false'
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
github-token: ${{ secrets.CONSOLE_PR_PAT_TOKEN }}
script: |
const prNumber = context.payload.pull_request.number;
const owner = context.repo.owner;
Expand All @@ -56,7 +56,7 @@ jobs:
if: github.event_name == 'pull_request_review' && steps.check_self_approved.outputs.isSelfApprovedPR == 'false'
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
github-token: ${{ secrets.CONSOLE_PR_PAT_TOKEN }}
script: |
const prNumber = context.payload.pull_request.number;
const owner = context.repo.owner;
Expand Down
4 changes: 2 additions & 2 deletions apps/unified-storybook/.storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ const config: StorybookConfig = {
return {
mirinae: {
title: 'Mirinae Component',
url: 'https://your-production-react-storybook-url',
url: 'https://storybook.developer.spaceone.dev',
},
web: {
title: 'Web Component',
url: 'https://your-production-angular-storybook-url',
url: 'https://spaceone-web-storybook.vercel.app',
},
};
},
Expand Down
1 change: 1 addition & 0 deletions apps/web-storybook/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# Transpiled code
.out
storybook-static
.vercel
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 @@ -25,8 +25,6 @@ import type { DateRange } from '@/common/modules/widgets/types/widget-data-type'
import type {
WidgetEmit, WidgetProps, WidgetSize,
} from '@/common/modules/widgets/types/widget-display-type';
import type { WidgetFieldName } from '@/common/modules/widgets/types/widget-field-type';
import type { WidgetFieldValues } from '@/common/modules/widgets/types/widget-field-value-type';
import type { FullDataLink, WidgetFrameProps } from '@/common/modules/widgets/types/widget-frame-type';

import { ASSET_INVENTORY_ROUTE_V1 } from '@/services/asset-inventory-v1/routes/route-constant';
Expand Down Expand Up @@ -86,12 +84,16 @@ const getRecursiveDataTableIds = (prevValue: string[] = [], dataTable: DataTable
_results = _results.concat(..._dataTables.map((d) => getRecursiveDataTableIds(prevValue, d, dataTables)));
return _results;
}
return prevValue.concat(dataTable.data_table_id);

const _dataTableId = dataTable?.options?.[dataTable.operator]?.data_table_id;
const _dataTable = dataTables.find((d) => d.data_table_id === _dataTableId);
_results = _results.concat(...getRecursiveDataTableIds(prevValue, _dataTable, dataTables));
return _results;
}
return prevValue.concat(dataTable.data_table_id);
};
const getFullDataLocation = (dataTable: DataTableModel, widgetOptions?: Record<WidgetFieldName, WidgetFieldValues>, dateRange?: DateRange, dashboardVars?: DashboardVars): Location|undefined => {
const _granularity = (widgetOptions?.granularity as GranularityValue).granularity || 'MONTHLY';
const getFullDataLocation = (dataTable: DataTableModel, widgetOptions?: WidgetProps['widgetOptions'], dateRange?: DateRange, dashboardVars?: DashboardVars): Location|undefined => {
const _granularity = (widgetOptions?.granularity?.value as GranularityValue)?.granularity || 'MONTHLY';
const _groupBy: string[] = dataTable?.options?.group_by?.map((d) => d.key);
const _costFilters = [
...(dataTable?.options?.filter ?? []),
Expand Down
Loading
Loading