@rancher/shell 3.0.9-rc.2 → 3.0.9-rc.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/translations/en-us.yaml +10 -0
- package/assets/translations/zh-hans.yaml +13 -0
- package/components/ActionMenu.vue +7 -8
- package/components/ActionMenuShell.vue +19 -20
- package/components/Resource/Detail/Card/Scaler.vue +10 -2
- package/components/Resource/Detail/Card/StatusCard/index.vue +4 -1
- package/components/ResourceTable.vue +1 -1
- package/components/Tabbed/Tab.vue +4 -0
- package/components/Tabbed/index.vue +11 -3
- package/components/__tests__/ProjectRow.test.ts +102 -15
- package/components/form/ResourceQuota/Project.vue +59 -8
- package/components/form/ResourceQuota/ProjectRow.vue +116 -21
- package/components/form/ResourceQuota/shared.js +42 -18
- package/components/formatter/LinkName.vue +3 -2
- package/config/product/explorer.js +1 -1
- package/config/table-headers.js +9 -7
- package/config/types.js +4 -1
- package/detail/management.cattle.io.oidcclient.vue +15 -4
- package/edit/__tests__/management.cattle.io.project.test.js +137 -0
- package/edit/management.cattle.io.project.vue +36 -6
- package/edit/monitoring.coreos.com.alertmanagerconfig/index.vue +16 -3
- package/initialize/install-plugins.js +0 -2
- package/models/management.cattle.io.cluster.js +22 -30
- package/models/provisioning.cattle.io.cluster.js +2 -2
- package/package.json +1 -1
- package/pages/__tests__/diagnostic.test.ts +71 -0
- package/pages/c/_cluster/explorer/tools/index.vue +23 -5
- package/pages/c/_cluster/monitoring/alertmanagerconfig/_alertmanagerconfigid/receiver.vue +18 -5
- package/pages/c/_cluster/uiplugins/index.vue +40 -8
- package/pages/diagnostic.vue +17 -3
- package/rancher-components/Pill/RcCounterBadge/RcCounterBadge.vue +1 -0
- package/rancher-components/RcItemCard/RcItemCard.test.ts +16 -6
- package/rancher-components/RcItemCard/RcItemCard.vue +13 -23
- package/store/__tests__/auth.test.ts +21 -5
- package/store/auth.js +6 -3
- package/types/shell/index.d.ts +163 -156
|
@@ -3879,6 +3879,8 @@ istio:
|
|
|
3879
3879
|
itemCard:
|
|
3880
3880
|
ariaLabel:
|
|
3881
3881
|
clickable: Click on card for {cardTitle}
|
|
3882
|
+
actionMenu:
|
|
3883
|
+
label: '{cardTitle} actions'
|
|
3882
3884
|
jwt:
|
|
3883
3885
|
title: JWT Authentication
|
|
3884
3886
|
actions:
|
|
@@ -8855,6 +8857,7 @@ notifications:
|
|
|
8855
8857
|
showCheckboxLabel: Show custom login error
|
|
8856
8858
|
messageLabel: Text to display
|
|
8857
8859
|
resourceQuota:
|
|
8860
|
+
banner: Limit resource consumption in a project for both standard (e.g. CPU Limit) and custom resource types. For custom resource types you have to provide the resource identifier yourself. Want to learn more about resource quotas? Read our <a href="https://ranchermanager.docs.rancher.com/how-to-guides/advanced-user-guides/manage-projects/manage-project-resource-quotas" target="_blank" rel="noopener noreferrer nofollow">documentation <i class="icon icon-external-link"></i></a><span class="sr-only">Opens in a new tab</span>
|
|
8858
8861
|
label: Resource Quotas
|
|
8859
8862
|
headers:
|
|
8860
8863
|
limit: Limit
|
|
@@ -8862,9 +8865,14 @@ resourceQuota:
|
|
|
8862
8865
|
projectLimit: Project Limit
|
|
8863
8866
|
projectResourceAvailability: Project Resource Availability
|
|
8864
8867
|
resourceType: Resource Type
|
|
8868
|
+
resourceIdentifier: Resource Identifier
|
|
8865
8869
|
helpText: Configure how much of the resources the namespace as a whole can consume.
|
|
8866
8870
|
helpTextDetail: The amount of resources the namespace as a whole can consume.
|
|
8867
8871
|
helpTextHarvester: VMs need to reserve additional memory overhead.
|
|
8872
|
+
custom: Custom
|
|
8873
|
+
resourceIdentifier:
|
|
8874
|
+
placeholder: e.g. configmaps
|
|
8875
|
+
tooltip: Select 'Custom' from the 'Resource Type' list and enter the resource identifier (e.g. requests.nvidia.com/gpu) to add custom resources quotas.
|
|
8868
8876
|
configMaps: Config Maps
|
|
8869
8877
|
limitsCpu: CPU Limit
|
|
8870
8878
|
limitsMemory: Memory Limit
|
|
@@ -8894,6 +8902,8 @@ resourceQuota:
|
|
|
8894
8902
|
unitlessPlaceholder: e.g. 10
|
|
8895
8903
|
add:
|
|
8896
8904
|
label: Add Resource
|
|
8905
|
+
errors:
|
|
8906
|
+
customTypeRequired: Resource identifier is required
|
|
8897
8907
|
tooltip:
|
|
8898
8908
|
reserved: 'Other Namespaces:'
|
|
8899
8909
|
namespace: 'This Namespace:'
|
|
@@ -651,6 +651,10 @@ asyncButton:
|
|
|
651
651
|
action: 保存
|
|
652
652
|
success: 已保存
|
|
653
653
|
waiting: 正在保存…
|
|
654
|
+
editVersion:
|
|
655
|
+
action: 保存更改
|
|
656
|
+
success: 已保存
|
|
657
|
+
waiting: 正在保存更改…
|
|
654
658
|
enable:
|
|
655
659
|
action: 启用
|
|
656
660
|
success: 启用
|
|
@@ -874,6 +878,13 @@ catalog:
|
|
|
874
878
|
windows: Windows
|
|
875
879
|
search: 筛选
|
|
876
880
|
install:
|
|
881
|
+
warning:
|
|
882
|
+
managed: |-
|
|
883
|
+
警告,{manager} 管理 {name} 应用的部署和升级。不支持升级此应用。<br>
|
|
884
|
+
{version, select,
|
|
885
|
+
null { }
|
|
886
|
+
other { 在大多数情况下,无需用户干预即可确保 {version} 版本与你正在运行的 Rancher 版本兼容。}
|
|
887
|
+
}
|
|
877
888
|
appReadmeMissing: 此 Chart 没有其他 Chart 信息。
|
|
878
889
|
appReadmeTitle: Chart 信息(Helm 自述)
|
|
879
890
|
chart: Chart
|
|
@@ -939,6 +950,8 @@ catalog:
|
|
|
939
950
|
install { 创建 }
|
|
940
951
|
upgrade { 升级 }
|
|
941
952
|
update { 更新 }
|
|
953
|
+
editVersion { 更新 }
|
|
954
|
+
downgrade { 降级 }
|
|
942
955
|
} 这个 {existing, select,
|
|
943
956
|
true { 应用 }
|
|
944
957
|
false { Chart}
|
|
@@ -11,7 +11,7 @@ const SHOW = 'show';
|
|
|
11
11
|
export default {
|
|
12
12
|
name: 'ActionMenu',
|
|
13
13
|
|
|
14
|
-
emits: ['close'],
|
|
14
|
+
emits: ['close', 'action-invoked'],
|
|
15
15
|
|
|
16
16
|
components: { IconOrSvg },
|
|
17
17
|
props: {
|
|
@@ -217,15 +217,14 @@ export default {
|
|
|
217
217
|
// If the state of this component is controlled
|
|
218
218
|
// by props instead of Vuex, we assume you wouldn't want
|
|
219
219
|
// the mutation to have a dependency on Vuex either.
|
|
220
|
-
//
|
|
221
|
-
//
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
action,
|
|
220
|
+
// The parent component should handle the action based on the action property
|
|
221
|
+
// in the 'action-invoked' event payload.
|
|
222
|
+
this.$emit('action-invoked', {
|
|
223
|
+
action: action.action,
|
|
224
|
+
actionData: action,
|
|
226
225
|
event,
|
|
227
226
|
...args,
|
|
228
|
-
route:
|
|
227
|
+
route: this.$route
|
|
229
228
|
});
|
|
230
229
|
} else {
|
|
231
230
|
// If the state of this component is controlled
|
|
@@ -30,7 +30,15 @@ const openChanged = (event: boolean) => {
|
|
|
30
30
|
}
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
export interface ActionMenuSelection {
|
|
34
|
+
action: string;
|
|
35
|
+
actionData: any;
|
|
36
|
+
event: MouseEvent;
|
|
37
|
+
route: ReturnType<typeof useRoute>;
|
|
38
|
+
[key: string]: any;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const emit = defineEmits<{(event: 'action-invoked', payload?: ActionMenuSelection): void;}>();
|
|
34
42
|
const route = useRoute();
|
|
35
43
|
|
|
36
44
|
const execute = (action: any, event: MouseEvent, args?: any) => {
|
|
@@ -38,7 +46,15 @@ const execute = (action: any, event: MouseEvent, args?: any) => {
|
|
|
38
46
|
return;
|
|
39
47
|
}
|
|
40
48
|
|
|
41
|
-
|
|
49
|
+
const payload: ActionMenuSelection = {
|
|
50
|
+
action: action.action,
|
|
51
|
+
actionData: action,
|
|
52
|
+
event,
|
|
53
|
+
...args,
|
|
54
|
+
route,
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
emit('action-invoked', payload);
|
|
42
58
|
|
|
43
59
|
// this will come from extensions...
|
|
44
60
|
if (action.invoke) {
|
|
@@ -56,24 +72,7 @@ const execute = (action: any, event: MouseEvent, args?: any) => {
|
|
|
56
72
|
fn.apply(this, [opts, resources]);
|
|
57
73
|
}
|
|
58
74
|
}
|
|
59
|
-
} else if (props.customActions) {
|
|
60
|
-
// If the state of this component is controlled
|
|
61
|
-
// by props instead of Vuex, we assume you wouldn't want
|
|
62
|
-
// the mutation to have a dependency on Vuex either.
|
|
63
|
-
// So in that case we use events to execute actions instead.
|
|
64
|
-
// If an action list item is clicked, this
|
|
65
|
-
// component emits that event, then we assume the parent
|
|
66
|
-
// component will execute the action.
|
|
67
|
-
emit(
|
|
68
|
-
action.action,
|
|
69
|
-
{
|
|
70
|
-
action,
|
|
71
|
-
event,
|
|
72
|
-
...args,
|
|
73
|
-
route,
|
|
74
|
-
}
|
|
75
|
-
);
|
|
76
|
-
} else {
|
|
75
|
+
} else if (!props.customActions) {
|
|
77
76
|
// If the state of this component is controlled
|
|
78
77
|
// by Vuex, mutate the store when an action is clicked.
|
|
79
78
|
const opts = { alt: isAlternate(event) };
|
|
@@ -19,22 +19,30 @@ const i18n = useI18n(store);
|
|
|
19
19
|
</script>
|
|
20
20
|
|
|
21
21
|
<template>
|
|
22
|
-
<div
|
|
22
|
+
<div
|
|
23
|
+
class="scaler"
|
|
24
|
+
data-testid="scaler"
|
|
25
|
+
>
|
|
23
26
|
<button
|
|
24
27
|
class="decrease"
|
|
25
28
|
:aria-label="i18n.t('component.resource.detail.card.scaler.ariaLabel.decrease', {resourceName: props.ariaResourceName})"
|
|
26
29
|
:disabled="!!props.min && (props.value <= props.min)"
|
|
30
|
+
data-testid="scaler-decrease"
|
|
27
31
|
@click="() => emit('decrease', props.value - 1)"
|
|
28
32
|
>
|
|
29
33
|
<i class="icon icon-sm icon-minus" />
|
|
30
34
|
</button>
|
|
31
|
-
<div
|
|
35
|
+
<div
|
|
36
|
+
class="value"
|
|
37
|
+
data-testid="scaler-value"
|
|
38
|
+
>
|
|
32
39
|
{{ props.value }}
|
|
33
40
|
</div>
|
|
34
41
|
<button
|
|
35
42
|
class="increase"
|
|
36
43
|
:aria-label="i18n.t('component.resource.detail.card.scaler.ariaLabel.increase', {resourceName: props.ariaResourceName})"
|
|
37
44
|
:disabled="!!props.max && (props.value >= props.max)"
|
|
45
|
+
data-testid="scaler-increase"
|
|
38
46
|
@click="() => emit('increase', props.value + 1)"
|
|
39
47
|
>
|
|
40
48
|
<i class="icon icon-sm icon-plus" />
|
|
@@ -427,7 +427,7 @@ export default {
|
|
|
427
427
|
},
|
|
428
428
|
|
|
429
429
|
_applicableExtensionTableHooks() {
|
|
430
|
-
if (this.$store.$
|
|
430
|
+
if (this.$store.$extension?.getUIConfig) {
|
|
431
431
|
const extensionTableHooks = getApplicableExtensionEnhancements(this, ExtensionPoint.TABLE, TableLocation.RESOURCE, this.$route);
|
|
432
432
|
|
|
433
433
|
return extensionTableHooks;
|
|
@@ -207,7 +207,7 @@ export default {
|
|
|
207
207
|
return TabLocation.OTHER;
|
|
208
208
|
}
|
|
209
209
|
},
|
|
210
|
-
|
|
210
|
+
hasErrorIcon(tab) {
|
|
211
211
|
return tab.displayAlertIcon || (tab.error && !tab.active);
|
|
212
212
|
},
|
|
213
213
|
hashChange() {
|
|
@@ -346,6 +346,10 @@ export default {
|
|
|
346
346
|
@click.prevent="select(tab.name, $event)"
|
|
347
347
|
@keyup.enter.space="select(tab.name, $event)"
|
|
348
348
|
>
|
|
349
|
+
<i
|
|
350
|
+
v-if="tab.labelIcon"
|
|
351
|
+
:class="`tab-label-icon icon ${tab.labelIcon}`"
|
|
352
|
+
/>
|
|
349
353
|
<span>
|
|
350
354
|
{{ tab.labelDisplay }}
|
|
351
355
|
</span>
|
|
@@ -354,7 +358,7 @@ export default {
|
|
|
354
358
|
class="tab-badge"
|
|
355
359
|
>{{ tab.badge }}</span>
|
|
356
360
|
<i
|
|
357
|
-
v-if="
|
|
361
|
+
v-if="hasErrorIcon(tab)"
|
|
358
362
|
v-clean-tooltip="t('validation.tab')"
|
|
359
363
|
class="conditions-alert-icon icon-error"
|
|
360
364
|
/>
|
|
@@ -514,11 +518,15 @@ export default {
|
|
|
514
518
|
}
|
|
515
519
|
|
|
516
520
|
&.error {
|
|
517
|
-
& A >
|
|
521
|
+
& A > .icon-error {
|
|
518
522
|
color: var(--error);
|
|
519
523
|
}
|
|
520
524
|
}
|
|
521
525
|
|
|
526
|
+
.tab-label-icon {
|
|
527
|
+
margin-right: 8px;
|
|
528
|
+
}
|
|
529
|
+
|
|
522
530
|
.tab-badge {
|
|
523
531
|
margin-left: 5px;
|
|
524
532
|
background-color: var(--link);
|
|
@@ -1,31 +1,39 @@
|
|
|
1
1
|
import ProjectRow from '@shell/components/form/ResourceQuota/ProjectRow.vue';
|
|
2
|
-
import { RANCHER_TYPES } from '@shell/components/form/ResourceQuota/shared';
|
|
2
|
+
import { RANCHER_TYPES, TYPES } from '@shell/components/form/ResourceQuota/shared';
|
|
3
3
|
import { shallowMount } from '@vue/test-utils';
|
|
4
4
|
|
|
5
|
-
const CONFIGMAP_STRING =
|
|
5
|
+
const CONFIGMAP_STRING = TYPES.CONFIG_MAPS;
|
|
6
6
|
|
|
7
7
|
describe('component: ProjectRow.vue', () => {
|
|
8
|
-
const
|
|
9
|
-
{
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
8
|
+
const defaultMountOptions = {
|
|
9
|
+
props: {
|
|
10
|
+
mode: 'edit',
|
|
11
|
+
types: RANCHER_TYPES,
|
|
12
|
+
type: CONFIGMAP_STRING,
|
|
13
|
+
index: 0,
|
|
14
|
+
value: {
|
|
15
|
+
spec: {
|
|
16
|
+
namespaceDefaultResourceQuota: { limit: {} },
|
|
17
|
+
resourceQuota: { limit: {} }
|
|
19
18
|
}
|
|
20
19
|
}
|
|
21
|
-
}
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
22
|
|
|
23
23
|
it('should render the correct input fields and set the correct computed values, based on the provided data', () => {
|
|
24
|
+
const wrapper = shallowMount(
|
|
25
|
+
ProjectRow,
|
|
26
|
+
{ ...defaultMountOptions }
|
|
27
|
+
);
|
|
28
|
+
|
|
24
29
|
const typeInput = wrapper.find(`[data-testid="projectrow-type-input"]`);
|
|
30
|
+
const customTypeInput = wrapper.find(`[data-testid="projectrow-custom-type-input"]`);
|
|
25
31
|
const projectQuotaInput = wrapper.find(`[data-testid="projectrow-project-quota-input"]`);
|
|
26
32
|
const namespaceQuotaInput = wrapper.find(`[data-testid="projectrow-namespace-quota-input"]`);
|
|
27
33
|
|
|
28
34
|
expect(typeInput.exists()).toBe(true);
|
|
35
|
+
expect(customTypeInput.exists()).toBe(true);
|
|
36
|
+
expect(customTypeInput.attributes().disabled).toBe('true');
|
|
29
37
|
expect(projectQuotaInput.exists()).toBe(true);
|
|
30
38
|
expect(namespaceQuotaInput.exists()).toBe(true);
|
|
31
39
|
expect(wrapper.vm.resourceQuotaLimit).toStrictEqual({});
|
|
@@ -33,6 +41,11 @@ describe('component: ProjectRow.vue', () => {
|
|
|
33
41
|
});
|
|
34
42
|
|
|
35
43
|
it('triggering "updateQuotaLimit" should trigger Vue.set with the correct data', () => {
|
|
44
|
+
const wrapper = shallowMount(
|
|
45
|
+
ProjectRow,
|
|
46
|
+
{ ...defaultMountOptions }
|
|
47
|
+
);
|
|
48
|
+
|
|
36
49
|
wrapper.vm.updateQuotaLimit('resourceQuota', CONFIGMAP_STRING, 10);
|
|
37
50
|
|
|
38
51
|
expect(wrapper.vm.value).toStrictEqual({
|
|
@@ -44,6 +57,11 @@ describe('component: ProjectRow.vue', () => {
|
|
|
44
57
|
});
|
|
45
58
|
|
|
46
59
|
it('triggering "updateType" with the same type that existed should clear limits and trigger emit', () => {
|
|
60
|
+
const wrapper = shallowMount(
|
|
61
|
+
ProjectRow,
|
|
62
|
+
{ ...defaultMountOptions }
|
|
63
|
+
);
|
|
64
|
+
|
|
47
65
|
wrapper.vm.updateType(CONFIGMAP_STRING);
|
|
48
66
|
|
|
49
67
|
expect(wrapper.vm.value).toStrictEqual({
|
|
@@ -54,6 +72,75 @@ describe('component: ProjectRow.vue', () => {
|
|
|
54
72
|
});
|
|
55
73
|
|
|
56
74
|
expect(wrapper.emitted('type-change')).toBeTruthy();
|
|
57
|
-
expect(wrapper.emitted('type-change')[0]).toStrictEqual([CONFIGMAP_STRING]);
|
|
75
|
+
expect(wrapper.emitted('type-change')[0]).toStrictEqual([{ index: 0, type: CONFIGMAP_STRING }]);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should update standard resource types', async() => {
|
|
79
|
+
const wrapper = shallowMount(
|
|
80
|
+
ProjectRow,
|
|
81
|
+
{ ...defaultMountOptions }
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
expect(wrapper.vm.isCustom).toBe(false);
|
|
85
|
+
|
|
86
|
+
await wrapper.vm.updateQuotaLimit('resourceQuota', 'limitsCpu', '100m');
|
|
87
|
+
await wrapper.vm.updateQuotaLimit('namespaceDefaultResourceQuota', 'limitsCpu', '50m');
|
|
88
|
+
|
|
89
|
+
expect(wrapper.vm.value.spec.resourceQuota.limit.limitsCpu).toBe('100m');
|
|
90
|
+
expect(wrapper.vm.value.spec.namespaceDefaultResourceQuota.limit.limitsCpu).toBe('50m');
|
|
91
|
+
expect(wrapper.vm.value.spec.resourceQuota.limit.extended).toBeUndefined();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should switch to a custom resource type', async() => {
|
|
95
|
+
const wrapper = shallowMount(
|
|
96
|
+
ProjectRow,
|
|
97
|
+
{ ...defaultMountOptions }
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
await wrapper.vm.updateType(TYPES.EXTENDED);
|
|
101
|
+
|
|
102
|
+
expect(wrapper.emitted('type-change')).toHaveLength(1);
|
|
103
|
+
expect(wrapper.emitted('type-change')[0][0]).toStrictEqual({ index: 0, type: TYPES.EXTENDED });
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('should update custom resource types', async() => {
|
|
107
|
+
const wrapper = shallowMount(
|
|
108
|
+
ProjectRow,
|
|
109
|
+
{
|
|
110
|
+
...defaultMountOptions,
|
|
111
|
+
props: {
|
|
112
|
+
...defaultMountOptions.props,
|
|
113
|
+
type: TYPES.EXTENDED
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
expect(wrapper.vm.isCustom).toBe(true);
|
|
119
|
+
|
|
120
|
+
const customTypeInput = wrapper.find(`[data-testid="projectrow-custom-type-input"]`);
|
|
121
|
+
|
|
122
|
+
expect(customTypeInput.attributes().disabled).toBe('false');
|
|
123
|
+
await wrapper.vm.updateCustomType('custom.resource/foo');
|
|
124
|
+
|
|
125
|
+
expect(wrapper.vm.customType).toBe('custom.resource/foo');
|
|
126
|
+
|
|
127
|
+
await wrapper.vm.updateQuotaLimit('resourceQuota', 'custom.resource/foo', 1);
|
|
128
|
+
await wrapper.vm.updateQuotaLimit('namespaceDefaultResourceQuota', 'custom.resource/foo', 2);
|
|
129
|
+
|
|
130
|
+
expect(wrapper.vm.value.spec.resourceQuota.limit.extended['custom.resource/foo']).toBe(1);
|
|
131
|
+
expect(wrapper.vm.value.spec.namespaceDefaultResourceQuota.limit.extended['custom.resource/foo']).toBe(2);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('should handle custom resource types with periods', () => {
|
|
135
|
+
const wrapper = shallowMount(ProjectRow, {
|
|
136
|
+
...defaultMountOptions,
|
|
137
|
+
props: {
|
|
138
|
+
...defaultMountOptions.props,
|
|
139
|
+
type: 'extended.requests.nvidia.com/gpu'
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
expect(wrapper.vm.isCustom).toBe(true);
|
|
144
|
+
expect(wrapper.vm.customType).toBe('requests.nvidia.com/gpu');
|
|
58
145
|
});
|
|
59
146
|
});
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import ArrayList from '@shell/components/form/ArrayList';
|
|
3
3
|
import Row from './ProjectRow';
|
|
4
|
-
import { QUOTA_COMPUTED } from './shared';
|
|
4
|
+
import { QUOTA_COMPUTED, TYPES } from './shared';
|
|
5
|
+
import Banner from '@components/Banner/Banner.vue';
|
|
5
6
|
|
|
6
7
|
export default {
|
|
7
8
|
emits: ['remove', 'input'],
|
|
8
9
|
|
|
9
|
-
components: {
|
|
10
|
+
components: {
|
|
11
|
+
ArrayList,
|
|
12
|
+
Row,
|
|
13
|
+
Banner,
|
|
14
|
+
},
|
|
10
15
|
|
|
11
16
|
props: {
|
|
12
17
|
mode: {
|
|
@@ -36,18 +41,35 @@ export default {
|
|
|
36
41
|
this.value.spec['namespaceDefaultResourceQuota'] = this.value.spec.namespaceDefaultResourceQuota || { limit: {} };
|
|
37
42
|
this.value.spec['resourceQuota'] = this.value.spec.resourceQuota || { limit: {} };
|
|
38
43
|
|
|
39
|
-
|
|
44
|
+
const limit = this.value.spec.resourceQuota.limit;
|
|
45
|
+
const extendedKeys = Object.keys(limit.extended || {});
|
|
46
|
+
|
|
47
|
+
this.typeValues = Object.keys(limit).flatMap((k) => {
|
|
48
|
+
if (k !== TYPES.EXTENDED) {
|
|
49
|
+
return k;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return extendedKeys.map((ek) => `extended.${ ek }`);
|
|
53
|
+
});
|
|
40
54
|
},
|
|
41
55
|
|
|
42
56
|
computed: { ...QUOTA_COMPUTED },
|
|
43
57
|
|
|
44
58
|
methods: {
|
|
45
|
-
updateType(
|
|
46
|
-
|
|
59
|
+
updateType(event) {
|
|
60
|
+
const { index, type } = event;
|
|
61
|
+
|
|
62
|
+
this.typeValues[index] = type;
|
|
47
63
|
},
|
|
48
64
|
remainingTypes(currentType) {
|
|
49
65
|
return this.mappedTypes
|
|
50
|
-
.filter((mappedType) =>
|
|
66
|
+
.filter((mappedType) => {
|
|
67
|
+
if (mappedType.value === TYPES.EXTENDED) {
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return !this.typeValues.includes(mappedType.value) || mappedType.value === currentType;
|
|
72
|
+
});
|
|
51
73
|
},
|
|
52
74
|
emitRemove(data) {
|
|
53
75
|
this.$emit('remove', data.row?.value);
|
|
@@ -57,9 +79,33 @@ export default {
|
|
|
57
79
|
</script>
|
|
58
80
|
<template>
|
|
59
81
|
<div>
|
|
82
|
+
<Banner
|
|
83
|
+
color="info"
|
|
84
|
+
label-key="resourceQuota.banner"
|
|
85
|
+
class="mb-20"
|
|
86
|
+
/>
|
|
60
87
|
<div class="headers mb-10">
|
|
61
88
|
<div class="mr-10">
|
|
62
|
-
<label>
|
|
89
|
+
<label>
|
|
90
|
+
{{ t('resourceQuota.headers.resourceType') }}
|
|
91
|
+
<span
|
|
92
|
+
class="required mr-5"
|
|
93
|
+
aria-hidden="true"
|
|
94
|
+
>*</span>
|
|
95
|
+
</label>
|
|
96
|
+
</div>
|
|
97
|
+
<div class="mr-20">
|
|
98
|
+
<label>
|
|
99
|
+
{{ t('resourceQuota.headers.resourceIdentifier') }}
|
|
100
|
+
<span
|
|
101
|
+
class="required mr-5"
|
|
102
|
+
aria-hidden="true"
|
|
103
|
+
>*</span>
|
|
104
|
+
<i
|
|
105
|
+
v-clean-tooltip="t('resourceQuota.resourceIdentifier.tooltip')"
|
|
106
|
+
class="icon icon-info"
|
|
107
|
+
/>
|
|
108
|
+
</label>
|
|
63
109
|
</div>
|
|
64
110
|
<div class="mr-20">
|
|
65
111
|
<label>{{ t('resourceQuota.headers.projectLimit') }}</label>
|
|
@@ -83,8 +129,9 @@ export default {
|
|
|
83
129
|
:mode="mode"
|
|
84
130
|
:types="remainingTypes(typeValues[props.i])"
|
|
85
131
|
:type="typeValues[props.i]"
|
|
132
|
+
:index="props.i"
|
|
86
133
|
@input="$emit('input', $event)"
|
|
87
|
-
@type-change="updateType(
|
|
134
|
+
@type-change="updateType($event)"
|
|
88
135
|
/>
|
|
89
136
|
</template>
|
|
90
137
|
</ArrayList>
|
|
@@ -104,4 +151,8 @@ export default {
|
|
|
104
151
|
width: 100%;
|
|
105
152
|
}
|
|
106
153
|
}
|
|
154
|
+
|
|
155
|
+
.required {
|
|
156
|
+
color: var(--error);
|
|
157
|
+
}
|
|
107
158
|
</style>
|