Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
90f5e4e
chore: merge master into develop after 2.0.dev366 version tagging
admin-cloudforet Jun 4, 2025
4d01919
chore: merge master into develop after 2.0.dev367 version tagging
admin-cloudforet Jun 4, 2025
2f83dd9
chore: merge master into develop after 2.0.dev368 version tagging
admin-cloudforet Jun 4, 2025
682d08e
chore: merge master into develop after 2.0.dev369 version tagging
admin-cloudforet Jun 4, 2025
6c68433
chore: merge master into develop after 2.0.dev369-hf1 version tagging
admin-cloudforet Jun 5, 2025
51f1610
chore: merge master into develop after 2.0.dev369-hf1.1 version tagging
admin-cloudforet Jun 5, 2025
4f62e12
Merge branch 'develop' into feature-service-pagination
seungyeoneeee Jun 9, 2025
30c9d07
feat(alert-service): enhance pagination logic in service page (#5934)
seungyeoneeee Jun 10, 2025
5cea1dd
Merge pull request #5933 from yuda110/feature/cost-analysis-page
yuda110 Jun 10, 2025
bed49b4
Merge branch 'develop' into feature-service-pagination
seungyeoneeee Jun 10, 2025
80b5fbf
Merge pull request #5935 from cloudforet-io/feature-service-pagination
seungyeoneeee Jun 10, 2025
d5400ee
chore: merge master into develop after 2.0.dev369-hf2.0 version tagging
admin-cloudforet Jun 11, 2025
9206fcc
Merge pull request #5940 from yuda110/feature/cost-report-workspace-f…
yuda110 Jun 17, 2025
b028aff
chore: merge master into develop after 2.0.dev370 version tagging
admin-cloudforet Jun 17, 2025
e2fa3c4
chore: merge master into develop after 2.0.dev371 version tagging
admin-cloudforet Jun 23, 2025
8feaabd
Merge pull request #5957 from cloudforet-io/hotfix-cost-anomaly
skdud4659 Jun 23, 2025
46b43dc
chore: merge master into develop after 2.0.dev372 version tagging
admin-cloudforet Jun 23, 2025
45fa75c
chore: merge master into develop after 2.0.dev373 version tagging
admin-cloudforet Jun 23, 2025
c55a332
fix: prevent query filter cleared on change
seungyeoneeee Jun 24, 2025
a8fa69b
Merge pull request #5961 from seungyeoneeee/feature/cloud-service-fee…
seungyeoneeee Jun 25, 2025
2c8f07e
chore: merge master into develop after 2.0.dev374 version tagging
admin-cloudforet Jun 27, 2025
28cdf2c
chore: merge master into develop after 2.0.dev375 version tagging
admin-cloudforet Jun 27, 2025
8b1caec
chore(cost-report): add status column & update design (#5966)
yuda110 Jun 30, 2025
0798fe8
feat: changed menuList name to activeModeMenuList
skdud4659 Jul 2, 2025
70d5406
feat: separate configuration of current mode menu and mode-specific m…
skdud4659 Jul 2, 2025
cb30635
feat: handle missing service config with default
skdud4659 Jul 2, 2025
850bf2e
feat: apply role-based config to menu
skdud4659 Jul 2, 2025
b809557
fix: apply pr review
skdud4659 Jul 2, 2025
ace1f9b
Merge pull request #5974 from skdud4659/feature/global-config-default
skdud4659 Jul 2, 2025
e2f73f9
feat(info-tooltip): create info-tooltip console common UI component
piggggggggy Jul 7, 2025
ebeb175
Merge pull request #5986 from piggggggggy/feat/info-tooltip
piggggggggy Jul 7, 2025
60102b5
fix: change job history to display overall progress as a percentage
skdud4659 Jul 8, 2025
fda44d8
fix(cloud-service-monitoring): solve wrong usage of data-loader in me…
piggggggggy Jul 9, 2025
d57631a
fix: change job history to display overall progress as a percentage (…
skdud4659 Jul 9, 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { SpaceConnector } from '@cloudforet/core-lib/space-connector';

import type { ListResponse } from '@/api-clients/_common/schema/api-verbs/list';
import type { CostQuerySetCreateParameters } from '@/api-clients/cost-analysis/cost-query-set/schema/api-verbs/create';
import type { CostQuerySetDeleteParameters } from '@/api-clients/cost-analysis/cost-query-set/schema/api-verbs/delete';
import type { CostQuerySetListParameters } from '@/api-clients/cost-analysis/cost-query-set/schema/api-verbs/list';
import type { CostQuerySetUpdateParameters } from '@/api-clients/cost-analysis/cost-query-set/schema/api-verbs/update';
import type { CostQuerySetModel } from '@/api-clients/cost-analysis/cost-query-set/schema/model';

export const useCostQuerySetApi = () => {
const actions = {
create: SpaceConnector.clientV2.costAnalysis.costQuerySet.create<CostQuerySetCreateParameters, CostQuerySetModel>,
list: SpaceConnector.clientV2.costAnalysis.costQuerySet.list<CostQuerySetListParameters, ListResponse<CostQuerySetModel>>,
update: SpaceConnector.clientV2.costAnalysis.costQuerySet.update<CostQuerySetUpdateParameters, CostQuerySetModel>,
delete: SpaceConnector.clientV2.costAnalysis.costQuerySet.delete<CostQuerySetDeleteParameters, void>,
};

return {
costQuerySetAPI: actions,
};
};
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export type CostReportStatus = 'IN_PROGRESS' | 'ADJUSTING' | 'DONE';
export type CostReportStatus = 'IN_PROGRESS' | 'ADJUSTING' | 'DONE' | 'EXPIRED';
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { SpaceConnector } from '@cloudforet/core-lib/space-connector';

import type { AnalyzeResponse } from '@/api-clients/_common/schema/api-verbs/analyze';
import type { CostAnalyzeParameters } from '@/api-clients/cost-analysis/cost/schema/api-verbs/analyze';
import type { CostStatParameters } from '@/api-clients/cost-analysis/cost/schema/api-verbs/stat';

import type { ListResponse } from '@/lib/variable-models/_base/types';

import type { CostAnalyzeRawData } from '@/services/cost-explorer/types/cost-analyze-type';

import type { CostModel } from '../schema/model';

interface UseCostApiReturn {
costAPI: {
analyze: (params: CostAnalyzeParameters) => Promise<AnalyzeResponse<CostAnalyzeRawData>>;
stat: (params: CostStatParameters) => Promise<ListResponse<CostModel>>;
}
}
export const useCostApi = (): UseCostApiReturn => {
const actions = {
analyze: SpaceConnector.clientV2.costAnalysis.cost.analyze<CostAnalyzeParameters, AnalyzeResponse<CostAnalyzeRawData>>,
stat: SpaceConnector.clientV2.costAnalysis.cost.stat<CostStatParameters, ListResponse<CostModel>>,
};

return {
costAPI: actions,
};
};
35 changes: 35 additions & 0 deletions apps/web/src/common/components/info-tooltip/InfoTooltip.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<script setup lang="ts">
import { PTooltip, PI } from '@cloudforet/mirinae';
import type { TooltipPosition } from '@cloudforet/mirinae/types/data-display/tooltips/type';

interface Props {
tooltipContents: string;
tooltipPosition?: TooltipPosition;
width?: string;
height?: string;
color?: string;
iconName?: string;
}

const props = withDefaults(defineProps<Props>(), {
tooltipPosition: 'bottom',
width: '0.75rem',
height: '0.75rem',
color: 'inherit',
iconName: 'ic_info-circle',
});
</script>

<template>
<p-tooltip class="info-tooltip"
:contents="props.tooltipContents"
:position="props.tooltipPosition"
>
<p-i :name="props.iconName"
:height="props.height"
:width="props.width"
:color="props.color"
class="info-tooltip-icon"
/>
</p-tooltip>
</template>
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const useContentsAccessibility = (menuId: MenuId): UseContentsAccessibili
const menuStore = useMenuStore();

const state = reactive({
visibleContents: computed<boolean>(() => menuStore.getters.menuList.findIndex((menu) => menu.id === menuId) !== -1),
visibleContents: computed<boolean>(() => menuStore.getters.activeModeMenuList.findIndex((menu) => menu.id === menuId) !== -1),
});

return {
Expand Down
3 changes: 2 additions & 1 deletion apps/web/src/common/modules/monitoring/MetricChart.vue
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ const state = reactive({
},
series: state.chartData,
})),
chartDataForDataLoader: computed<ChartData[]|undefined>(() => state.chartData?.filter((item) => !isEmpty(item?.data)) ?? undefined),
});

const drawChart = (rawData) => {
Expand Down Expand Up @@ -125,7 +126,7 @@ useResizeObserver(chartContext, throttle(() => {
<p-spinner v-if="props.loading && state.chart" />
</div>
<p-data-loader :loading="loading && !state.chart"
:data="state.chartData"
:data="state.chartDataForDataLoader"
class="chart-wrapper"
show-data-from-scratch
>
Expand Down
28 changes: 26 additions & 2 deletions apps/web/src/common/pages/CostReportDetailPage.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<script setup lang="ts">
import { computed, reactive, ref } from 'vue';
import type { TranslateResult } from 'vue-i18n';
import { useRouter } from 'vue-router/composables';

import dayjs from 'dayjs';
Expand All @@ -12,7 +13,7 @@ import {

import { SpaceConnector } from '@cloudforet/core-lib/space-connector';
import {
PLink, PDataTable, PIconButton, PI, PButton,
PLink, PDataTable, PIconButton, PI, PButton, PScopedNotification,
} from '@cloudforet/mirinae';
import type { DataTableFieldType, DataTableField } from '@cloudforet/mirinae/types/data-display/tables/data-table/type';
import { numberFormatter } from '@cloudforet/utils';
Expand All @@ -21,7 +22,8 @@ import type { AnalyzeResponse } from '@/api-clients/_common/schema/api-verbs/ana
import type { CostReportDataAnalyzeParameters } from '@/api-clients/cost-analysis/cost-report-data/schema/api-verbs/analyze';
import type { CostReportGetParameters } from '@/api-clients/cost-analysis/cost-report/schema/api-verbs/get';
import type { CostReportModel } from '@/api-clients/cost-analysis/cost-report/schema/model';
import { setI18nLocale } from '@/translations';
import type { CostReportStatus } from '@/api-clients/cost-analysis/cost-report/schema/type';
import { i18n, setI18nLocale } from '@/translations';

import { ERROR_ROUTE } from '@/router/constant';

Expand Down Expand Up @@ -228,6 +230,16 @@ const getFormattedProductTotalValue = (provider: string, serviceAccount: string)
const productData = state.productRawData.find((d) => d.provider === provider && d.service_account_name === serviceAccount);
return currencyMoneyFormatter(productData?._total_value_sum ?? 0, numberFormatterOption.value) ?? '';
};
const getWarningTitle = (status: CostReportStatus): TranslateResult => {
if (status === 'EXPIRED') return i18n.t('COMMON.COST_REPORT.EXPIRED_REPORT_TITLE');
if (status === 'ADJUSTING' || status === 'IN_PROGRESS') return i18n.t('COMMON.COST_REPORT.ADJUSTING_REPORT_TITLE');
return '';
};
const getWarningDescription = (status: CostReportStatus): TranslateResult => {
if (status === 'EXPIRED') return i18n.t('COMMON.COST_REPORT.EXPIRED_REPORT_DESCRIPTION');
if (status === 'ADJUSTING' || status === 'IN_PROGRESS') return i18n.t('COMMON.COST_REPORT.ADJUSTING_REPORT_DESCRIPTION');
return '';
};


const drawChart = () => {
Expand Down Expand Up @@ -405,6 +417,14 @@ const handleCollapseAll = () => {
>
<label>{{ $t('COMMON.COST_REPORT.ISSUE_DATE') }}:</label>{{ state.baseInfo?.issue_date }} <span class="real-date-range">({{ reportDateRange }})</span>
</p>
<p-scoped-notification v-if="state.baseInfo?.status !== 'DONE'"
:title="getWarningTitle(state.baseInfo?.status)"
icon="ic_warning-filled"
type="warning"
class="expired-report-notification"
>
{{ getWarningDescription(state.baseInfo?.status) }}
</p-scoped-notification>
</div>
<div class="total">
<p class="title">
Expand Down Expand Up @@ -672,6 +692,10 @@ const handleCollapseAll = () => {
}
}
}

.expired-report-notification {
margin-top: 1rem;
}
}

.total {
Expand Down
39 changes: 1 addition & 38 deletions apps/web/src/lib/access-control/page-access-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,10 @@ import {
WORKSPACE_OWNER_DEFAULT_PERMISSIONS,
WORKSPACE_USER_MINIMAL_PERMISSIONS,
} from '@/lib/access-control/config';
import config from '@/lib/config';
import type { GlobalServiceConfig } from '@/lib/config/global-config/types/type';
import type { Menu, MenuId } from '@/lib/menu/config';

import type { LSBItem, LSBMenu } from '@/common/modules/navigations/lsb/type';

import {
ALERT_V1_WORKSPACE_PAGE_ACCESS_MENU_LIST,
ALERT_V2_WORKSPACE_PAGE_ACCESS_MENU_LIST, DEFAULT_WORKSPACE_PAGE_ACCESS_MENU_LIST,
} from '@/services/iam/constants/role-constant';
import type { PageAccessMenuByConfig } from '@/services/iam/types/role-type';

export const getDefaultPageAccessPermissionList = (roleType?: RoleType): MenuId[] => {
if (roleType === 'SYSTEM_ADMIN') return SYSTEM_USER_DEFAULT_PERMISSIONS;
if (roleType === 'DOMAIN_ADMIN') return DOMAIN_ADMIN_DEFAULT_PERMISSIONS;
Expand All @@ -46,16 +38,13 @@ export const flattenMenu = (menuList: Menu[]): Menu[] => menuList.flatMap((menu)

export const getPageAccessMapFromRawData = ({
pageAccessPermissions,
isRolePage,
menuList,
}: {
pageAccessPermissions?: string[],
isRolePage?: boolean,
menuList?: Menu[],
}): PageAccessMap => {
const globalConfig = config.get('SERVICES') || {};
const result: PageAccessMap = {};
const menuListByVersion = !isRolePage ? (menuList || []) : getEnabledMenus(globalConfig);
const menuListByVersion = menuList || [];
const flattenedMenuList = flattenMenu(menuListByVersion);
const setPermissions = (id: string, read = true, write = true, access = true) => {
result[id] = { read, write, access };
Expand Down Expand Up @@ -127,29 +116,3 @@ export const checkAllMenuReadonly = (permissions: string[]) => permissions.every
const accessType = item.split('.*')[0].split(':')[1];
return accessType === PAGE_ACCESS.READONLY;
});

export const getEnabledMenus = (globalServiceConfig: GlobalServiceConfig): Menu[] => {
const versionMenuMap = {
V1: ALERT_V1_WORKSPACE_PAGE_ACCESS_MENU_LIST,
V2: ALERT_V2_WORKSPACE_PAGE_ACCESS_MENU_LIST,
};

const getAllMenus = (version: string): PageAccessMenuByConfig[] => [
...DEFAULT_WORKSPACE_PAGE_ACCESS_MENU_LIST,
...versionMenuMap[version],
];

return Object.entries(globalServiceConfig)
// eslint-disable-next-line @typescript-eslint/no-unused-vars
.filter(([_, cfg]) => cfg.ENABLED)
.flatMap(([configKey, cfg]) => {
const targetMenus = getAllMenus(cfg.VERSION);
const matchedMenu = targetMenus
.filter((menuItem) => menuItem.key === configKey);

return matchedMenu.map((i) => ({
id: i.id,
subMenuList: (i.subMenuList || [{ id: i.id }]) as Menu[],
}));
});
};
3 changes: 2 additions & 1 deletion apps/web/src/lib/config/global-config/api-client-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import kebabCase from 'lodash/kebabCase';

import { SpaceConnector } from '@cloudforet/core-lib/space-connector';

import { DEFAULT_VERSION } from '@/lib/config/global-config/constants/constants';
import { ApiClientEndpoint } from '@/lib/config/global-config/schema/api-client-schema';
import type { ApiClientsSchemaType, GlobalServiceConfig } from '@/lib/config/global-config/types/type';

Expand Down Expand Up @@ -44,7 +45,7 @@ class APIClientManager {

Object.entries(this.config).forEach(([serviceName, config]) => {
if (this.apiClientsSchema?.[serviceName]) {
const version = config.VERSION;
const version = config.VERSION || DEFAULT_VERSION;
const endpoint = this.apiClientsSchema[serviceName][version];
if (endpoint) {
const formattedEndpoint = APIClientManager.formatEndpoint(endpoint);
Expand Down
29 changes: 29 additions & 0 deletions apps/web/src/lib/config/global-config/constants/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,32 @@ export const SERVICE_FEATURES = {
OPS_FLOW: 'OPS_FLOW',
IAM: 'IAM',
} as const;

export const DEFAULT_VERSION = 'V1';

export const DEFAULT_CONFIG = {
DASHBOARDS: {
ENABLED: true,
VERSION: DEFAULT_VERSION,
},
PROJECT: {
ENABLED: true,
VERSION: DEFAULT_VERSION,
},
SERVICE_ACCOUNT: {
ENABLED: true,
VERSION: DEFAULT_VERSION,
},
ASSET_INVENTORY: {
ENABLED: true,
VERSION: DEFAULT_VERSION,
},
COST_ANALYSIS: {
ENABLED: true,
VERSION: DEFAULT_VERSION,
},
ALERT_MANAGER: {
ENABLED: true,
VERSION: DEFAULT_VERSION,
},
};
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { DEFAULT_VERSION } from '@/lib/config/global-config/constants/constants';
import dynamicSchemaManager from '@/lib/config/global-config/dynamic-schema-manager';
import { getFeatureConfigurator } from '@/lib/config/global-config/helpers/get-feature-configurator';
import type {
Expand Down Expand Up @@ -46,7 +47,7 @@ export class FeatureSchemaManager {
if (this.config[feature]?.ENABLED) {
const configurator = getFeatureConfigurator(feature);
if (configurator) {
const currentVersion = this.config[feature]?.VERSION || 'V1';
const currentVersion = this.config[feature]?.VERSION || DEFAULT_VERSION;
configurator.initialize(currentVersion);
results.push(callback(feature, configurator, currentVersion));
}
Expand Down Expand Up @@ -109,7 +110,7 @@ export class FeatureSchemaManager {
const targetFeature = uiAffect.feature;
const targetVersion = targetFeature === feature
? currentVersion
: (this.config[targetFeature]?.VERSION || 'V1');
: (this.config[targetFeature]?.VERSION || DEFAULT_VERSION);

if (!featureMethodMap[targetFeature]) {
featureMethodMap[targetFeature] = {};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ import type { PublicConfigGetParameters } from '@/api-clients/config/public-conf
import { PUBLIC_CONFIG_NAMES } from '@/api-clients/config/public-config/schema/constant';
import type { PublicConfigModel } from '@/api-clients/config/public-config/schema/model';

import { DEFAULT_CONFIG } from '@/lib/config/global-config/constants/constants';
import type { GlobalServiceConfig } from '@/lib/config/global-config/types/type';

import ErrorHandler from '@/common/composables/error/errorHandler';

export const mergeConfig = async (config, domainId: string): Promise<GlobalServiceConfig> => {
const baseConfig = config.get('SERVICES') || {};
const baseConfig = config.get('SERVICES') || DEFAULT_CONFIG;

let overrideConfig = {};
try {
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/lib/menu/use-all-menu-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const useAllMenuList = () => {
const isMyPage = route?.path.startsWith('/my-page');
let _allGnbMenuList: DisplayMenu[];

_allGnbMenuList = _getDisplayMenuList(menuStore.getters.menuList, _isAdminMode.value);
_allGnbMenuList = _getDisplayMenuList(menuStore.getters.activeModeMenuList, _isAdminMode.value);
_allGnbMenuList = _filterMenuByRoute(_allGnbMenuList, router, _currentWorkspaceId.value);
if (!_isAdminMode.value) {
_allGnbMenuList = _filterMenuByAccessPermission(_allGnbMenuList, authorizationStore.getters.pageAccessPermissionList);
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/lib/site-initializer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ const initI18n = () => {
};

const initOpsFlowTaskManagementTemplate = async (mergedConfig: GlobalServiceConfig) => {
if (!mergedConfig.OPS_FLOW.ENABLED) return;
if (!mergedConfig?.OPS_FLOW?.ENABLED) return;
const taskManagementTemplateStore = useTaskManagementTemplateStore(pinia);
await Promise.allSettled([
taskManagementTemplateStore.setInitialTemplateId(),
Expand Down
Loading
Loading