Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
2b6fa31
chore: merge master into develop after 2.0.dev359 version tagging
admin-cloudforet May 9, 2025
e4db647
feat: update languages
seungyeoneeee May 9, 2025
207a85b
feat: update user group name duplicated validation check
seungyeoneeee May 9, 2025
cc8ccc5
feat: update notification channel schedule displaying method
seungyeoneeee May 9, 2025
f6f90ed
fix: fix incorrect v1/v2 branching logic using globalConfig
seungyeoneeee May 9, 2025
669c20a
feat: update languages about days
seungyeoneeee May 9, 2025
3b1b0e0
Merge pull request #5841 from seungyeoneeee/feature/user-notichannel-…
seungyeoneeee May 9, 2025
057cbf8
chore(budget-qa): update about minor additional QA (#5845)
seungyeoneeee May 13, 2025
4a4d9d4
feat: fix major bug about service list pagination improvement
seungyeoneeee May 13, 2025
3b38a0a
feat: update css style as design
seungyeoneeee May 13, 2025
d2b5245
Merge pull request #5848 from seungyeoneeee/feature/service-list-update
seungyeoneeee May 13, 2025
11eb632
chore: update translations
skdud4659 May 13, 2025
df1b5d9
Merge pull request #5851 from cloudforet-io/feature-budget-update
seungyeoneeee May 15, 2025
da94599
Merge remote-tracking branch 'cloudforet-io/develop' into feature/lang
skdud4659 May 15, 2025
66ebf56
chore: update translations
skdud4659 May 15, 2025
dec61ef
Merge pull request #5852 from skdud4659/feature/lang
skdud4659 May 16, 2025
72a4978
feat: update service main page pagination
seungyeoneeee May 16, 2025
e6ebecd
Merge pull request #5853 from seungyeoneeee/feature/service-list-update
seungyeoneeee May 16, 2025
7f31946
chore: apply jp at ops-flow
skdud4659 May 16, 2025
b94b81c
Merge pull request #5854 from skdud4659/feature/lang
skdud4659 May 16, 2025
a66a695
feat: changed excel export API
skdud4659 May 16, 2025
16240f0
Merge pull request #5855 from skdud4659/feature/excel
skdud4659 May 16, 2025
3cde595
feat: update service main page pagination
seungyeoneeee May 19, 2025
83c1fc9
Merge pull request #5857 from seungyeoneeee/feature/service-pg-ud
seungyeoneeee May 19, 2025
844ee9c
Merge branch 'develop' into feature/service-pg-ud
seungyeoneeee May 19, 2025
bc8de96
fix: remove unnecessary console log
seungyeoneeee May 19, 2025
aa8b255
Merge pull request #5858 from seungyeoneeee/feature/service-pg-ud
seungyeoneeee May 19, 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
Expand Up @@ -19,6 +19,8 @@ import type { ValueHandlerMap } from '@cloudforet/mirinae/types/controls/search/
import type { DataTableFieldType } from '@cloudforet/mirinae/types/data-display/tables/data-table/type';
import { iso8601Formatter } from '@cloudforet/utils';

import type { ExportParameter } from '@/api-clients/_common/schema/api-verbs/export';
import { QueryType } from '@/api-clients/_common/schema/api-verbs/export';
import { ALERT_STATUS, ALERT_URGENCY } from '@/schema/alert-manager/alert/constants';
import type { AlertModel } from '@/schema/alert-manager/alert/model';
import type { CloudServiceGetParameters } from '@/schema/inventory/cloud-service/api-verbs/get';
Expand All @@ -32,8 +34,7 @@ import type { ServiceReferenceMap } from '@/store/reference/service-reference-st
import type { WebhookReferenceMap } from '@/store/reference/webhook-reference-store';
import { useUserStore } from '@/store/user/user-store';

import { FILE_NAME_PREFIX } from '@/lib/excel-export/constant';
import { downloadExcel } from '@/lib/helper/file-download-helper';
import { downloadExcelByExportFetcher } from '@/lib/helper/file-download-helper';
import { replaceUrlQuery } from '@/lib/router-query-string';

import CustomDateModal from '@/common/components/custom-date-modal/CustomDateModal.vue';
Expand Down Expand Up @@ -295,15 +296,27 @@ const handleCustomFieldUpdate = (fields: DataTableFieldType[]) => {
state.fields = fields;
};
const handleExportToExcel = async () => {
await downloadExcel({
url: '/alert-manager/alert/list',
param: {
query: { ...alertListApiQueryHelper.data, only: ALERT_EXCEL_FIELDS.map((d) => d.key) },
},
fields: ALERT_EXCEL_FIELDS,
file_name_prefix: FILE_NAME_PREFIX.alert,
timezone: storeState.timezone,
});
const excelQuery = new ApiQueryHelper()
.setMultiSortV2([{ key: 'created_at', desc: true }])
.setFilters([...filterQueryHelper.filters]);
const excelExportFetcher = () => {
const alertExcelExportParams: ExportParameter = {
file_name: 'alert_export',
options: [
{
name: 'Main Table',
query_type: QueryType.SEARCH,
search_query: {
...excelQuery.data,
fields: ALERT_EXCEL_FIELDS,
},
},
],
timezone: state.timezone,
};
return SpaceConnector.clientV2.alertManager.alert.export(alertExcelExportParams);
};
await downloadExcelByExportFetcher(excelExportFetcher);
};
const handleCustomRangeModalConfirm = (start: string, end: string) => {
alertPageStore.setSelectedPeriodRange(ALERT_PERIOD_DROPDOWN_MENU.CUSTOM);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ const handleGoBackButton = () => {
const validHealthyPage = (!Number.isNaN(serviceListPageStore.healthyThisPage) && serviceListPageStore.healthyThisPage > 0)
? serviceListPageStore.healthyThisPage
: 1;

router.push({
name: ALERT_MANAGER_ROUTE.SERVICE._NAME,
query: {
Expand Down
141 changes: 85 additions & 56 deletions apps/web/src/services/alert-manager/v2/components/ServiceList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,29 @@ const queryTagHelper = useQueryTags({ keyItemSets: SERVICE_SEARCH_HANDLER.keyIte
const { queryTags } = queryTagHelper;

const handleToolbox = async (options: ToolboxOptions) => {
if (options.queryTags !== undefined) queryTagHelper.setQueryTags(options.queryTags);
if (options.queryTags !== undefined) {
queryTagHelper.setQueryTags(options.queryTags);

const nameTags = options.queryTags.filter((tag) => tag.key?.name === 'name');
const nameValues = nameTags.map((tag) => tag.value.name).filter(Boolean);

const newQuery: Record<string, any> = {
unhealthyPage: String(serviceListPageStore.unhealthyThisPage),
healthyPage: String(serviceListPageStore.healthyThisPage),
};

if (nameValues.length > 0) {
newQuery.serviceName = nameValues.join(',');
} else {
newQuery.serviceName = undefined;
}

replaceUrlQuery(newQuery);

serviceListPageStore.setUnhealthyPage(1);
serviceListPageStore.setHealthyPage(1);
}

await fetchBothLists();
};

Expand Down Expand Up @@ -166,33 +188,35 @@ onMounted(async () => {
serviceListPageStore.setUnhealthyPage(parsedUnhealthy);
serviceListPageStore.setHealthyPage(parsedHealthy);

const { serviceName } = route.query;
if (serviceName && typeof serviceName === 'string') {
const nameValues = serviceName.split(',').map((name) => ({
key: { name: 'name' },
value: { label: name, name },
}));
queryTagHelper.setQueryTags(nameValues);
}

await fetchBothLists();
});

watch(() => route.query.serviceName, async (serviceName) => {
if (!serviceName) return;
queryTagHelper.setQueryTags([
{
key: { name: 'name', label: 'Name' },
operator: '=',
value: { label: serviceName as string, name: serviceName },
},
]);
await fetchBothLists();
}, { immediate: true });
watch(() => serviceListPageStore.unhealthyThisPage, (val) => {
replaceUrlQuery({
unhealthyPage: String(val),
healthyPage: String(serviceListPageStore.healthyThisPage),
});
if (!queryTags.value.some((tag) => tag.key?.name === 'name')) {
replaceUrlQuery({
unhealthyPage: String(val),
healthyPage: String(serviceListPageStore.healthyThisPage),
});
}
handleUnhealthyPageChange();
});

watch(() => serviceListPageStore.healthyThisPage, (val) => {
replaceUrlQuery({
unhealthyPage: String(serviceListPageStore.unhealthyThisPage),
healthyPage: String(val),
});
if (!queryTags.value.some((tag) => tag.key?.name === 'name')) {
replaceUrlQuery({
unhealthyPage: String(serviceListPageStore.unhealthyThisPage),
healthyPage: String(val),
});
}
handleHealthyPageChange();
});
</script>
Expand All @@ -212,10 +236,12 @@ watch(() => serviceListPageStore.healthyThisPage, (val) => {
/>

<section>
<p-data-loader :loading="state.loading"
:data="state.alertServiceList"
loader-backdrop-color="transparent"
class="loader-wrapper"
<p-data-loader
:loading="state.loading"
:data="state.alertServiceList"
loader-backdrop-color="transparent"
disable-empty-case
class="loader-wrapper"
>
<div>
<service-list-content v-if="state.alertServiceList.length > 0"
Expand All @@ -225,60 +251,63 @@ watch(() => serviceListPageStore.healthyThisPage, (val) => {
/>
<div class="flex justify-center mt-4">
<p-pagination
v-if="state.totalCount > 0"
:total-count="state.totalCount"
:this-page.sync="serviceListPageStore.unhealthyThisPage"
:page-size.sync="serviceListPageStore.unhealthyPageSize"
@change="handleUnhealthyPageChange"
/>
</div>
</div>
</p-data-loader>

<p-data-loader
:loading="state.healthyLoading"
:data="state.healthyServiceList"
loader-backdrop-color="transparent"
disable-empty-case
class="loader-wrapper"
>
<div>
<service-list-content v-if="state.healthyServiceList.length > 0"
:list="state.healthyServiceList"
type="healthy"
@navigate-to-detail="handleNavigateToDetail"
/>
<div class="flex justify-center mt-4">
<p-pagination v-if="state.healthyTotalCount > serviceListPageStore.healthyPageSize"
<p-pagination v-if="state.healthyTotalCount > 0"
:total-count="state.healthyTotalCount"
:this-page.sync="serviceListPageStore.healthyThisPage"
:page-size.sync="serviceListPageStore.healthyPageSize"
@change="handleHealthyPageChange"
/>
</div>
</div>
<template #no-data>
<div class="pt-12">
<p-empty show-image
:show-button="hasReadWriteAccess"
>
<template #image>
<img src="../../../../assets/images/img_jellyocto-with-a-telescope.png"
alt="empty-image"
>
</template>
<template v-if="hasReadWriteAccess"
#button
>
<p-button icon-left="ic_plus_bold"
@click="handleClickCreateButton"
>
{{ $t('ALERT_MANAGER.SERVICE.NO_DATA') }}
</p-button>
</template>
{{ $t('ALERT_MANAGER.SERVICE.NO_DATA_DESC') }}
</p-empty>
</div>
</template>
</p-data-loader>

<div v-if="!state.loading && !state.healthyLoading && state.alertServiceList.length === 0 && state.healthyServiceList.length === 0"
class="mt-4"
>
<p-empty show-image
:show-button="hasReadWriteAccess"
>
<template #image>
<img src="../../../../assets/images/img_jellyocto-with-a-telescope.png"
alt="empty-image"
>
</template>
<template v-if="hasReadWriteAccess"
#button
>
<p-button icon-left="ic_plus_bold"
@click="handleClickCreateButton"
>
{{ $t('ALERT_MANAGER.SERVICE.NO_DATA') }}
</p-button>
</template>
{{ $t('ALERT_MANAGER.SERVICE.NO_DATA_DESC') }}
</p-empty>
</div>
</section>
</div>
</template>

<style scoped lang="postcss">
.service-list {
.loader-wrapper {
min-height: 13.75rem;
}
}
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ watch(() => budgetCreatePageState.thresholds, () => {
const values = budgetCreatePageState.thresholds.map((t) => Number(t.value));
invalidThreshold.value = budgetCreatePageState.thresholds.map((threshold, idx) => {
const val = values[idx];
const isValidNumber = val > 0 && val < 101;
const isValidNumber = val > 0;
const isDuplicate = values.indexOf(val) !== idx;
return !(isValidNumber && !isDuplicate);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,11 +159,7 @@ watch(() => budgetCreatePageState, async () => {
<template>
<p-pane-layout class="chart-wrapper p-4">
<p class="font-bold text-sm">
{{ $t('BILLING.COST_MANAGEMENT.BUDGET.FORM.CREATE.LAST_COST_TREND', {
count: budgetCreatePageState.startMonth.length > 0 && budgetCreatePageState.endMonth.length > 0
? Number(dayjs(budgetCreatePageState.endMonth[0], 'YYYY-MM').diff(dayjs(budgetCreatePageState.startMonth[0], 'YYYY-MM'), 'month') + 1)
: 3,
}) }}
{{ $t('BILLING.COST_MANAGEMENT.BUDGET.FORM.CREATE.PREVIOUS_PERIOD_SPEND') }}
</p>
<div ref="chartContext"
class="chart"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,37 @@
<script lang="ts" setup>
import { reactive, watchEffect } from 'vue';
import { computed, reactive, watchEffect } from 'vue';

import { PFieldGroup, PTextInput } from '@cloudforet/mirinae';

import { useUserGroupReferenceStore } from '@/store/reference/user-group-reference-store';

import { USER_GROUP_MODAL_TYPE } from '@/services/iam/constants/user-group-constant';
import { useUserGroupPageStore } from '@/services/iam/store/user-group-page-store';

const userGroupPageStore = useUserGroupPageStore();
const userGroupPageState = userGroupPageStore.state;
const userGroupPageGetters = userGroupPageStore.getters;
const userGroupReferenceStore = useUserGroupReferenceStore();
const userGroupReferenceState = userGroupReferenceStore.state;

const emit = defineEmits(['update:values']);

const state = reactive({
groupName: userGroupPageState.modal.type === USER_GROUP_MODAL_TYPE.CREATE ? '' : userGroupPageGetters.selectedUserGroups[0].name,
description: userGroupPageState.modal.type === USER_GROUP_MODAL_TYPE.CREATE ? '' : userGroupPageGetters.selectedUserGroups[0].description,
isGroupNameDuplicated: computed<boolean>(() => {
if (userGroupReferenceState.items && state.groupName && Object.values(userGroupReferenceState.items).map((d) => d.name).includes(state.groupName)) {
return true;
}
return false;
}),
});

watchEffect(() => {
emit('update:values', {
groupName: state.groupName,
description: state.description,
isGroupNameDuplicated: state.isGroupNameDuplicated,
});
});

Expand All @@ -37,12 +48,15 @@ const handleChangeDescription = (value: string) => {
<template>
<div class="user-group-info-wrapper">
<p-field-group :label="$t('IAM.USER_GROUP.MODAL.CREATE_USER_GROUP.GROUP_NAME')"
:invalid-text="$t('IAM.USER_GROUP.MODAL.CREATE_USER_GROUP.DUPLICATED_INVALID_TEXT')"
:invalid="state.isGroupNameDuplicated"
required
>
<template #default>
<p-text-input
:value="state.groupName"
:placeholder="$t('IAM.USER_GROUP.MODAL.CREATE_USER_GROUP.GROUP_NAME')"
:invalid="state.isGroupNameDuplicated"
@update:value="handleChangeGroupName"
/>
</template>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,13 @@ const state = reactive({
groupName: '',
description: '',
disabled: false,
isGroupNameDuplicated: false,
});

/* Watcher */
watch(() => state.groupName, (groupName) => {
state.disabled = groupName.length > 0;
}, { immediate: true });
watch(() => state, () => {
state.disabled = state.groupName.length > 0 && !state.isGroupNameDuplicated;
}, { deep: true, immediate: true });

/* Component */
const handleConfirm = async () => {
Expand Down Expand Up @@ -94,6 +95,7 @@ const handleClose = () => {
const handleUpdateValues = (data) => {
state.groupName = data.groupName;
state.description = data.description;
state.isGroupNameDuplicated = data.isGroupNameDuplicated;
};

/* API */
Expand Down
Loading
Loading