@rancher/shell 3.0.1-rc.3 → 3.0.1
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/data/aws-regions.json +1 -0
- package/assets/styles/base/_basic.scss +5 -0
- package/assets/styles/base/_mixins.scss +8 -0
- package/assets/styles/global/_button.scss +5 -0
- package/assets/styles/themes/_dark.scss +2 -0
- package/assets/styles/themes/_light.scss +2 -0
- package/assets/translations/en-us.yaml +40 -22
- package/assets/translations/zh-hans.yaml +1 -7
- package/chart/monitoring/StorageClassSelector.vue +1 -1
- package/components/AssignTo.vue +1 -0
- package/components/AsyncButton.vue +1 -0
- package/components/BackLink.vue +8 -2
- package/components/PaginatedResourceTable.vue +135 -0
- package/components/ResourceDetail/Masthead.vue +1 -1
- package/components/ResourceDetail/index.vue +66 -11
- package/components/ResourceList/index.vue +0 -1
- package/components/ResourceTable.vue +6 -1
- package/components/ResourceYaml.vue +0 -53
- package/components/SortableTable/index.vue +8 -6
- package/components/Tabbed/index.vue +35 -2
- package/components/form/ResourceLabeledSelect.vue +2 -2
- package/components/form/ResourceTabs/index.vue +0 -23
- package/components/form/Taints.vue +1 -1
- package/components/form/UnitInput.vue +1 -1
- package/components/form/__tests__/UnitInput.test.ts +1 -1
- package/components/nav/TopLevelMenu.helper.ts +546 -0
- package/components/nav/TopLevelMenu.vue +125 -160
- package/components/nav/WindowManager/ContainerShell.vue +13 -4
- package/components/nav/WindowManager/__tests__/ContainerShell.test.ts +20 -18
- package/components/nav/__tests__/TopLevelMenu.test.ts +338 -326
- package/composables/useLabeledFormElement.ts +6 -2
- package/config/pagination-table-headers.js +4 -4
- package/config/product/explorer.js +2 -0
- package/config/router/navigation-guards/index.js +1 -2
- package/config/router/routes.js +1 -1
- package/config/settings.ts +15 -8
- package/core/plugin.ts +8 -1
- package/core/types-provisioning.ts +5 -0
- package/core/types.ts +26 -1
- package/dialog/DrainNode.vue +6 -6
- package/edit/catalog.cattle.io.clusterrepo.vue +95 -52
- package/edit/provisioning.cattle.io.cluster/index.vue +8 -3
- package/edit/workload/index.vue +1 -1
- package/edit/workload/storage/csi/index.vue +29 -1
- package/edit/workload/storage/index.vue +1 -0
- package/initialize/App.vue +3 -10
- package/initialize/install-plugins.js +1 -2
- package/list/management.cattle.io.podsecurityadmissionconfigurationtemplate.vue +6 -2
- package/list/node.vue +8 -5
- package/mixins/resource-fetch-api-pagination.js +40 -5
- package/mixins/resource-fetch.js +48 -5
- package/models/management.cattle.io.nodepool.js +5 -4
- package/models/nodedriver.js +2 -2
- package/models/provisioning.cattle.io.cluster.js +3 -11
- package/package.json +7 -8
- package/pages/about.vue +22 -0
- package/pages/auth/setup.vue +7 -28
- package/pages/c/_cluster/explorer/__tests__/index.test.ts +36 -24
- package/pages/c/_cluster/explorer/index.vue +100 -59
- package/pages/home.vue +308 -123
- package/plugins/dashboard-store/__tests__/mutations.test.ts +2 -0
- package/plugins/dashboard-store/actions.js +29 -19
- package/plugins/dashboard-store/getters.js +5 -2
- package/plugins/dashboard-store/mutations.js +4 -2
- package/plugins/steve/__tests__/mutations.test.ts +2 -1
- package/plugins/steve/steve-pagination-utils.ts +25 -2
- package/plugins/steve/subscribe.js +22 -8
- package/rancher-components/Banner/Banner.vue +1 -0
- package/rancher-components/Form/Checkbox/Checkbox.vue +2 -0
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +2 -0
- package/rancher-components/Form/Radio/RadioButton.vue +2 -0
- package/rancher-components/Form/Radio/RadioGroup.vue +2 -0
- package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +2 -0
- package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +3 -0
- package/rancher-components/StringList/StringList.test.ts +15 -15
- package/rancher-components/StringList/StringList.vue +3 -0
- package/scripts/extension/helm/charts/ui-plugin-server/Chart.yaml +0 -2
- package/scripts/extension/parse-tag-name +2 -0
- package/scripts/test-plugins-build.sh +4 -2
- package/store/index.js +31 -9
- package/tsconfig.json +7 -1
- package/types/resources/settings.d.ts +1 -1
- package/types/shell/index.d.ts +1107 -1276
- package/types/store/dashboard-store.types.ts +4 -0
- package/types/store/pagination.types.ts +13 -0
- package/types/store/vuex.d.ts +8 -0
- package/types/vue-shim.d.ts +6 -31
- package/utils/cluster.js +92 -1
- package/utils/pagination-utils.ts +17 -8
- package/utils/pagination-wrapper.ts +70 -0
- package/utils/uiplugins.ts +307 -0
- package/components/templates/error.vue +0 -131
- package/config/router/navigation-guards/history.js +0 -13
- package/plugins/back-button.js +0 -3
|
@@ -147,8 +147,16 @@
|
|
|
147
147
|
}
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
+
// -------------------------------------------------------------------------------------------------
|
|
151
|
+
// Focus styles
|
|
152
|
+
|
|
150
153
|
@mixin form-focus {
|
|
151
154
|
// Focus for form like elements (not to be confused with basic :focus style)
|
|
152
155
|
outline: none;
|
|
153
156
|
border-color: var(--outline);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
@mixin focus-outline {
|
|
160
|
+
// Focus for form like elements (not to be confused with basic :focus style)
|
|
161
|
+
outline: 2px solid var(--primary-keyboard-focus);
|
|
154
162
|
}
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
|
|
22
22
|
// dark main text
|
|
23
23
|
$lightest: #ffffff;
|
|
24
|
+
$keyboard-focus: #00ddff;
|
|
24
25
|
|
|
25
26
|
// menu cluster description active+hover
|
|
26
27
|
$desc-light: #EEEFF4;
|
|
@@ -45,6 +46,7 @@
|
|
|
45
46
|
--default-light-bg : #{rgba($dark, 0.05)};
|
|
46
47
|
--slider-light-bg : #{rgba($darker, 1)};
|
|
47
48
|
--slider-light-bg-right : #{rgba($darker, 0)};
|
|
49
|
+
--primary-keyboard-focus : #{$keyboard-focus};
|
|
48
50
|
|
|
49
51
|
--muted : #{$disabled};
|
|
50
52
|
|
|
@@ -26,6 +26,7 @@ $disabled : $medium;
|
|
|
26
26
|
$primary : #3D98D3;
|
|
27
27
|
$secondary : $darker;
|
|
28
28
|
$link : #3D98D3;
|
|
29
|
+
$keyboard-focus : #3300ff;
|
|
29
30
|
|
|
30
31
|
// Status colors
|
|
31
32
|
$success : #5D995D;
|
|
@@ -53,6 +54,7 @@ BODY, .theme-light {
|
|
|
53
54
|
--primary-border : #{$primary};
|
|
54
55
|
--primary-banner-bg : #{rgba($primary, 0.15)};
|
|
55
56
|
--primary-light-bg : #{rgba($primary, 0.05)};
|
|
57
|
+
--primary-keyboard-focus : #{$keyboard-focus};
|
|
56
58
|
|
|
57
59
|
|
|
58
60
|
.text-primary {
|
|
@@ -120,6 +120,7 @@ locale:
|
|
|
120
120
|
none: (None)
|
|
121
121
|
|
|
122
122
|
nav:
|
|
123
|
+
expandCollapseAppBar: Expand/Collapse the Application Bar
|
|
123
124
|
harvesterDashboard: Harvester Dashboard
|
|
124
125
|
backToRancher: Cluster Manager
|
|
125
126
|
tools: Tools
|
|
@@ -273,6 +274,9 @@ suffix:
|
|
|
273
274
|
about:
|
|
274
275
|
title: About
|
|
275
276
|
versions:
|
|
277
|
+
downloadImages: "Download {listName} images list"
|
|
278
|
+
downloadCli: "Download CLI for {os}"
|
|
279
|
+
githubRepo: "Open GitHub repository for the {name} component"
|
|
276
280
|
title: Versions
|
|
277
281
|
component: Component
|
|
278
282
|
version: Version
|
|
@@ -1079,14 +1083,17 @@ catalog:
|
|
|
1079
1083
|
exponentialBackOff:
|
|
1080
1084
|
label: Exponential Back Off
|
|
1081
1085
|
minWait:
|
|
1082
|
-
label: Min Wait Time
|
|
1083
|
-
placeholder: 1
|
|
1086
|
+
label: Min Wait Time
|
|
1087
|
+
placeholder: 'default: 1'
|
|
1084
1088
|
maxWait:
|
|
1085
|
-
label: Max Wait Time
|
|
1086
|
-
placeholder: 5
|
|
1089
|
+
label: Max Wait Time
|
|
1090
|
+
placeholder: 'default: 5'
|
|
1087
1091
|
maxRetries:
|
|
1088
1092
|
label: Max Number of Retries
|
|
1089
|
-
placeholder: 5
|
|
1093
|
+
placeholder: 'default: 5'
|
|
1094
|
+
refreshInterval:
|
|
1095
|
+
label: Refresh Interval
|
|
1096
|
+
placeholder: 'default: {hours}'
|
|
1090
1097
|
tools:
|
|
1091
1098
|
header: Cluster Tools
|
|
1092
1099
|
noTools: "No Cluster Tools found"
|
|
@@ -1480,16 +1487,16 @@ cluster:
|
|
|
1480
1487
|
defaultLabel: Use default data directory configuration
|
|
1481
1488
|
commonLabel: Use a common base directory for data directory configuration (sub-directories will be used for the system-agent, provisioning and distro paths)
|
|
1482
1489
|
customLabel: Use custom data directories
|
|
1483
|
-
common:
|
|
1490
|
+
common:
|
|
1484
1491
|
label: Data directory base path
|
|
1485
1492
|
tooltip: Data directory base path. We will append the sub-directories appropriate for each directory (/agent, /provisioning and either /rke2 or /k3s)
|
|
1486
|
-
systemAgent:
|
|
1493
|
+
systemAgent:
|
|
1487
1494
|
label: System-agent directory path
|
|
1488
1495
|
tooltip: Data directory for the system-agent connection info and plans
|
|
1489
|
-
provisioning:
|
|
1496
|
+
provisioning:
|
|
1490
1497
|
label: Provisioning directory path
|
|
1491
1498
|
tooltip: Data directory for provisioning related files
|
|
1492
|
-
k8sDistro:
|
|
1499
|
+
k8sDistro:
|
|
1493
1500
|
label: K8s Distro directory path
|
|
1494
1501
|
tooltip: Data directory for the k8s distro
|
|
1495
1502
|
machineConfig:
|
|
@@ -1921,7 +1928,7 @@ cluster:
|
|
|
1921
1928
|
header: Registry for Rancher System Container Images
|
|
1922
1929
|
label: Enable cluster scoped container registry for Rancher system container images
|
|
1923
1930
|
description: "If enabled, Rancher will pull container images from this registry during cluster provisioning. By default, Rancher will also use this registry when installing Rancher's official Helm chart apps. If the cluster scoped registry is disabled, system images are pulled from the System Default Registry in the global settings."
|
|
1924
|
-
docsLinkRke2: "For help configuring private registry mirrors, see the RKE2 <a href=\"https://docs.rke2.io/install/
|
|
1931
|
+
docsLinkRke2: "For help configuring private registry mirrors, see the RKE2 <a href=\"https://docs.rke2.io/install/private_registry\" target=\"_blank\">documentation.</a>"
|
|
1925
1932
|
docsLinkK3s: "For help configuring private registry mirrors, see the K3s <a href=\"https://docs.k3s.io/installation/private-registry\" target=\"_blank\">documentation.</a>"
|
|
1926
1933
|
provider:
|
|
1927
1934
|
aliyunecs: Aliyun ECS
|
|
@@ -2198,11 +2205,20 @@ clusterIndexPage:
|
|
|
2198
2205
|
label: Unhealthy Nodes
|
|
2199
2206
|
noRows: There are no unhealthy nodes to show.
|
|
2200
2207
|
componentStatus:
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2208
|
+
component:
|
|
2209
|
+
etcd:
|
|
2210
|
+
label: Etcd
|
|
2211
|
+
scheduler:
|
|
2212
|
+
label: Scheduler
|
|
2213
|
+
controller-manager:
|
|
2214
|
+
label: Controller Manager
|
|
2215
|
+
cattle:
|
|
2216
|
+
label: Cattle
|
|
2217
|
+
fleet:
|
|
2218
|
+
label: Fleet
|
|
2219
|
+
tooltip:
|
|
2220
|
+
disconnected: Agent disconnected
|
|
2221
|
+
unavailableReplicas: Replicas not available
|
|
2206
2222
|
certs:
|
|
2207
2223
|
label: Certificates
|
|
2208
2224
|
|
|
@@ -5300,7 +5316,6 @@ setup:
|
|
|
5300
5316
|
skip: Skip
|
|
5301
5317
|
tip: What URL should be used for this {vendor} installation? All the nodes in your clusters will need to be able to reach this.
|
|
5302
5318
|
setPassword: The first order of business is to set a strong password for the default <code>{username}</code> user. We suggest using this random one generated just for you, but enter your own if you like.
|
|
5303
|
-
telemetry: Allow collection of <a href="{docsBase}/faq/telemetry" target="_blank" rel="noopener noreferrer nofollow">anonymous statistics</a> to help us improve {name}
|
|
5304
5319
|
useManual: Set a specific password to use
|
|
5305
5320
|
useRandom: Use a randomly generated password
|
|
5306
5321
|
welcome: Welcome to {vendor}!
|
|
@@ -6139,6 +6154,14 @@ wm:
|
|
|
6139
6154
|
clear: Clear
|
|
6140
6155
|
containerName: "Container: {label}"
|
|
6141
6156
|
failed: "Unable to open a shell to the container (none of the shell commmands succeeded)\n\r"
|
|
6157
|
+
logLevel:
|
|
6158
|
+
info: INFO
|
|
6159
|
+
error: ERROR
|
|
6160
|
+
warning: WARN
|
|
6161
|
+
debug: DEBUG
|
|
6162
|
+
logMessage:
|
|
6163
|
+
containerError: "{ logLevel }: Container missing shell executable (/bin/sh)"
|
|
6164
|
+
|
|
6142
6165
|
kubectlShell:
|
|
6143
6166
|
title: "Kubectl: {name}"
|
|
6144
6167
|
|
|
@@ -6544,7 +6567,7 @@ workload:
|
|
|
6544
6567
|
managed: Managed
|
|
6545
6568
|
shared: Shared
|
|
6546
6569
|
drivers:
|
|
6547
|
-
driver
|
|
6570
|
+
driver-longhorn-io: Longhorn
|
|
6548
6571
|
fsType: Filesystem Type
|
|
6549
6572
|
shareName: Share Name
|
|
6550
6573
|
secretName: Secret Name
|
|
@@ -7415,7 +7438,6 @@ advancedSettings:
|
|
|
7415
7438
|
'ui-dashboard-index': 'HTML index location for the {appName} UI.'
|
|
7416
7439
|
'ui-offline-preferred': 'Controls whether UI assets are served locally by the server container or from the remote URL defined in the ui-index and ui-dashboard-index settings. The `Dynamic` option will use local assets in production builds of {appName}.'
|
|
7417
7440
|
'ui-pl': 'Private-Label company name.'
|
|
7418
|
-
'telemetry-opt': 'Telemetry reporting opt-in.'
|
|
7419
7441
|
'auth-user-info-max-age-seconds': 'The maximum age of a users auth tokens before an auth provider group membership sync will be performed.'
|
|
7420
7442
|
'auth-user-info-resync-cron': 'Default cron schedule for resyncing auth provider group memberships.'
|
|
7421
7443
|
'cluster-template-enforcement': 'Non-admins will be restricted to launching clusters via preapproved RKE Templates only.'
|
|
@@ -7440,10 +7462,6 @@ advancedSettings:
|
|
|
7440
7462
|
'ui-default-landing':
|
|
7441
7463
|
ember: Cluster Manager
|
|
7442
7464
|
vue: Cluster Explorer
|
|
7443
|
-
'telemetry-opt':
|
|
7444
|
-
prompt: Prompt
|
|
7445
|
-
in: Opt-in to Telemetry
|
|
7446
|
-
out: Opt-out of Telemetry
|
|
7447
7465
|
'ui-offline-preferred':
|
|
7448
7466
|
dynamic: Dynamic
|
|
7449
7467
|
true: Local
|
|
@@ -1751,7 +1751,7 @@ cluster:
|
|
|
1751
1751
|
privateRegistry:
|
|
1752
1752
|
label: 为 Rancher 系统容器镜像启用集群级别的容器镜像仓库
|
|
1753
1753
|
description: "如果启用,Rancher 将在集群配置期间从该镜像仓库中拉取容器镜像。默认情况下,Rancher 在安装 Rancher 的官方 Helm Chart 应用程序时也会使用此镜像仓库。如果集群级别的镜像仓库被禁用,将从全局设置中的系统默认镜像仓库中拉取系统镜像。"
|
|
1754
|
-
docsLinkRke2: "如需配置私有镜像仓库 mirror 的帮助,请参阅 RKE2 <a href=\"https://docs.rke2.io/install/
|
|
1754
|
+
docsLinkRke2: "如需配置私有镜像仓库 mirror 的帮助,请参阅 RKE2 <a href=\"https://docs.rke2.io/install/private_registry\" target=\"_blank\">文档</a>。"
|
|
1755
1755
|
docsLinkK3s: "如需配置私有镜像仓库 mirror 的帮助,请参阅 K3s <a href=\"https://docs.k3s.io/installation/private-registry\" target=\"_blank\">文档</a>。"
|
|
1756
1756
|
provider:
|
|
1757
1757
|
aliyunecs: Aliyun ECS
|
|
@@ -4964,7 +4964,6 @@ setup:
|
|
|
4964
4964
|
skip: 跳过
|
|
4965
4965
|
tip: 此 {vendor} 安装应使用什么 URL?集群中的所有节点都需要能访问该 URL。
|
|
4966
4966
|
setPassword: 请为默认用户 <code>{username}</code>设置强密码。建议使用生成的随机密码。你也可以自行设置。
|
|
4967
|
-
telemetry: 允许收集<a href="{docsBase}/faq/telemetry" target="_blank" rel="noopener noreferrer nofollow">匿名统计数据</a>,以帮我们改进 {name}。
|
|
4968
4967
|
useManual: 设置密码
|
|
4969
4968
|
useRandom: 使用随机生成的密码
|
|
4970
4969
|
welcome: 欢迎使用 {vendor}!
|
|
@@ -7011,7 +7010,6 @@ advancedSettings:
|
|
|
7011
7010
|
'ui-dashboard-index': '{appName} UI 的 HTML 索引位置。'
|
|
7012
7011
|
'ui-offline-preferred': '控制 UI 资产是由服务器容器在本地提供,还是从 ui-index 和 ui-dashboard-index 设置中定义的远程 URL 提供。`动态` 选项将在 {appName} 的生产版本中使用本地资产。'
|
|
7013
7012
|
'ui-pl': '自有品牌公司名称。'
|
|
7014
|
-
'telemetry-opt': '遥测报告加入。'
|
|
7015
7013
|
'auth-user-info-max-age-seconds': '在同步验证提供程序组成员之前,用户验证 Token 的最长存活时间。'
|
|
7016
7014
|
'auth-user-info-resync-cron': '重新同步验证提供程序组成员的默认 cron 调度。'
|
|
7017
7015
|
'cluster-template-enforcement': '非管理员只能通过预先批准的 RKE 模板启动集群。'
|
|
@@ -7032,10 +7030,6 @@ advancedSettings:
|
|
|
7032
7030
|
'ui-default-landing':
|
|
7033
7031
|
ember: Cluster Manager
|
|
7034
7032
|
vue: 集群浏览器
|
|
7035
|
-
'telemetry-opt':
|
|
7036
|
-
prompt: 提示
|
|
7037
|
-
in: 加入遥测
|
|
7038
|
-
out: 退出遥测
|
|
7039
7033
|
'ui-offline-preferred':
|
|
7040
7034
|
dynamic: 动态
|
|
7041
7035
|
true: 本地
|
package/components/AssignTo.vue
CHANGED
package/components/BackLink.vue
CHANGED
|
@@ -13,6 +13,8 @@ export default {
|
|
|
13
13
|
v-if="link && link.name"
|
|
14
14
|
:to="link"
|
|
15
15
|
class="back-link"
|
|
16
|
+
role="link"
|
|
17
|
+
:aria-label="t('generic.back')"
|
|
16
18
|
>
|
|
17
19
|
<i class="icon icon-chevron-left" /> {{ t('generic.back') }}
|
|
18
20
|
</router-link>
|
|
@@ -20,6 +22,7 @@ export default {
|
|
|
20
22
|
v-else
|
|
21
23
|
to="/"
|
|
22
24
|
class="back-link"
|
|
25
|
+
:aria-label="t('nav.home')"
|
|
23
26
|
>
|
|
24
27
|
<i class="icon icon-chevron-left" /> {{ t('nav.home') }}
|
|
25
28
|
</router-link>
|
|
@@ -30,9 +33,12 @@ export default {
|
|
|
30
33
|
align-items: center;
|
|
31
34
|
display: flex;
|
|
32
35
|
font-size: 16px;
|
|
33
|
-
margin
|
|
36
|
+
margin: 10px 0 20px 0;
|
|
34
37
|
outline: 0;
|
|
35
|
-
padding: 10px 0;
|
|
36
38
|
width: fit-content;
|
|
39
|
+
|
|
40
|
+
&:focus-visible {
|
|
41
|
+
@include focus-outline;
|
|
42
|
+
}
|
|
37
43
|
}
|
|
38
44
|
</style>
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { defineComponent } from 'vue';
|
|
3
|
+
import ResourceFetch from '@shell/mixins/resource-fetch';
|
|
4
|
+
import ResourceTable from '@shell/components/ResourceTable.vue';
|
|
5
|
+
import { StorePaginationResult } from '@shell/types/store/pagination.types';
|
|
6
|
+
|
|
7
|
+
export type FetchSecondaryResourcesOpts = { canPaginate: boolean }
|
|
8
|
+
export type FetchSecondaryResources = (opts: FetchSecondaryResourcesOpts) => Promise<any>
|
|
9
|
+
|
|
10
|
+
export type FetchPageSecondaryResourcesOpts = { canPaginate: boolean, force: boolean, page: any[], pagResult: StorePaginationResult }
|
|
11
|
+
export type FetchPageSecondaryResources = (opts: FetchPageSecondaryResourcesOpts) => Promise<any>
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* This is meant to enable ResourceList like capabilities outside of List pages / components
|
|
15
|
+
*
|
|
16
|
+
* Specifically
|
|
17
|
+
* - Resource Fetch features, including server-side pagination
|
|
18
|
+
* - Some plumbing
|
|
19
|
+
*
|
|
20
|
+
* This avoids polluting the owning component with mixins
|
|
21
|
+
*
|
|
22
|
+
*/
|
|
23
|
+
export default defineComponent({
|
|
24
|
+
name: 'PaginatedResourceTable',
|
|
25
|
+
|
|
26
|
+
components: { ResourceTable },
|
|
27
|
+
|
|
28
|
+
mixins: [ResourceFetch],
|
|
29
|
+
|
|
30
|
+
props: {
|
|
31
|
+
schema: {
|
|
32
|
+
type: Object,
|
|
33
|
+
required: true,
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
headers: {
|
|
37
|
+
type: Array,
|
|
38
|
+
default: null,
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
paginationHeaders: {
|
|
42
|
+
type: Array,
|
|
43
|
+
default: null,
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
groupable: {
|
|
47
|
+
type: Boolean,
|
|
48
|
+
default: null, // Null: auto based on namespaced and type custom groupings
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
namespaced: {
|
|
52
|
+
type: Boolean,
|
|
53
|
+
default: null, // Automatic from schema
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Information may be required from resources other than the primary one shown per row
|
|
58
|
+
*
|
|
59
|
+
* This will fetch them ALL and will be run in a non-server-side pagination world
|
|
60
|
+
*/
|
|
61
|
+
fetchSecondaryResources: {
|
|
62
|
+
type: Function,
|
|
63
|
+
default: null,
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Information may be required from resources other than the primary one shown per row
|
|
68
|
+
*
|
|
69
|
+
* This will fetch only those relevant to the current page using server-side pagination based filters
|
|
70
|
+
*
|
|
71
|
+
* called from shell/mixins/resource-fetch-api-pagination.js
|
|
72
|
+
*/
|
|
73
|
+
fetchPageSecondaryResources: {
|
|
74
|
+
type: Function,
|
|
75
|
+
default: null,
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
data() {
|
|
80
|
+
return { resource: this.schema.id };
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
async fetch() {
|
|
84
|
+
const promises = [
|
|
85
|
+
this.$fetchType(this.resource, [], this.inStore),
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
if (this.fetchSecondaryResources) {
|
|
89
|
+
promises.push(this.fetchSecondaryResources({ canPaginate: this.canPaginate }));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
await Promise.all(promises);
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
computed: {
|
|
96
|
+
safeHeaders() {
|
|
97
|
+
const customHeaders = this.canPaginate ? this.paginationHeaders : this.headers;
|
|
98
|
+
|
|
99
|
+
return customHeaders || this.$store.getters['type-map/headersFor'](this.schema, this.canPaginate);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
</script>
|
|
105
|
+
|
|
106
|
+
<template>
|
|
107
|
+
<div>
|
|
108
|
+
<ResourceTable
|
|
109
|
+
v-bind="$attrs"
|
|
110
|
+
:schema="schema"
|
|
111
|
+
:rows="rows"
|
|
112
|
+
:alt-loading="canPaginate"
|
|
113
|
+
:loading="loading"
|
|
114
|
+
:groupable="groupable"
|
|
115
|
+
|
|
116
|
+
:headers="safeHeaders"
|
|
117
|
+
:namespaced="namespaced"
|
|
118
|
+
|
|
119
|
+
:external-pagination-enabled="canPaginate"
|
|
120
|
+
:external-pagination-result="paginationResult"
|
|
121
|
+
@pagination-changed="paginationChanged"
|
|
122
|
+
>
|
|
123
|
+
<!-- Pass down templates provided by the caller -->
|
|
124
|
+
<template
|
|
125
|
+
v-for="(_, slot) of $slots"
|
|
126
|
+
v-slot:[slot]="scope"
|
|
127
|
+
>
|
|
128
|
+
<slot
|
|
129
|
+
:name="slot"
|
|
130
|
+
v-bind="scope"
|
|
131
|
+
/>
|
|
132
|
+
</template>
|
|
133
|
+
</ResourceTable>
|
|
134
|
+
</div>
|
|
135
|
+
</template>
|
|
@@ -447,7 +447,7 @@ export default {
|
|
|
447
447
|
{{ parent.displayName }}:
|
|
448
448
|
</router-link>
|
|
449
449
|
<span v-else>{{ parent.displayName }}:</span>
|
|
450
|
-
<span v-if="value
|
|
450
|
+
<span v-if="value?.detailPageHeaderActionOverride && value?.detailPageHeaderActionOverride(realMode)">{{ value?.detailPageHeaderActionOverride(realMode) }}</span>
|
|
451
451
|
<t
|
|
452
452
|
v-else
|
|
453
453
|
class="masthead-resource-title"
|
|
@@ -14,6 +14,8 @@ import { clone, diff } from '@shell/utils/object';
|
|
|
14
14
|
import IconMessage from '@shell/components/IconMessage';
|
|
15
15
|
import ForceDirectedTreeChart from '@shell/components/fleet/ForceDirectedTreeChart';
|
|
16
16
|
import { checkSchemasForFindAllHash } from '@shell/utils/auth';
|
|
17
|
+
import { stringify } from '@shell/utils/error';
|
|
18
|
+
import { Banner } from '@components/Banner';
|
|
17
19
|
|
|
18
20
|
function modeFor(route) {
|
|
19
21
|
if ( route.query?.mode === _IMPORT ) {
|
|
@@ -48,6 +50,7 @@ export default {
|
|
|
48
50
|
ResourceYaml,
|
|
49
51
|
Masthead,
|
|
50
52
|
IconMessage,
|
|
53
|
+
Banner
|
|
51
54
|
},
|
|
52
55
|
|
|
53
56
|
mixins: [CreateEditView],
|
|
@@ -75,7 +78,11 @@ export default {
|
|
|
75
78
|
componentTestid: {
|
|
76
79
|
type: String,
|
|
77
80
|
default: 'resource-details'
|
|
78
|
-
}
|
|
81
|
+
},
|
|
82
|
+
errorsMap: {
|
|
83
|
+
type: Object,
|
|
84
|
+
default: null
|
|
85
|
+
},
|
|
79
86
|
},
|
|
80
87
|
|
|
81
88
|
async fetch() {
|
|
@@ -202,16 +209,26 @@ export default {
|
|
|
202
209
|
notFound = fqid;
|
|
203
210
|
}
|
|
204
211
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
+
try {
|
|
213
|
+
if (realMode === _VIEW) {
|
|
214
|
+
model = liveModel;
|
|
215
|
+
} else {
|
|
216
|
+
model = await store.dispatch(`${ inStore }/clone`, { resource: liveModel });
|
|
217
|
+
}
|
|
218
|
+
initialModel = await store.dispatch(`${ inStore }/clone`, { resource: liveModel });
|
|
212
219
|
|
|
220
|
+
if ( as === _YAML ) {
|
|
221
|
+
yaml = await getYaml(this.$store, liveModel);
|
|
222
|
+
}
|
|
223
|
+
} catch (e) {
|
|
224
|
+
this.errors.push(e);
|
|
225
|
+
}
|
|
213
226
|
if ( as === _YAML ) {
|
|
214
|
-
|
|
227
|
+
try {
|
|
228
|
+
yaml = await getYaml(this.$store, liveModel);
|
|
229
|
+
} catch (e) {
|
|
230
|
+
this.errors.push(e);
|
|
231
|
+
}
|
|
215
232
|
}
|
|
216
233
|
|
|
217
234
|
if ( as === _GRAPH ) {
|
|
@@ -225,7 +242,11 @@ export default {
|
|
|
225
242
|
}
|
|
226
243
|
|
|
227
244
|
// Ensure common properties exists
|
|
228
|
-
|
|
245
|
+
try {
|
|
246
|
+
model = await store.dispatch(`${ inStore }/cleanForDetail`, model);
|
|
247
|
+
} catch (e) {
|
|
248
|
+
this.errors.push(e);
|
|
249
|
+
}
|
|
229
250
|
|
|
230
251
|
const out = {
|
|
231
252
|
hasGraph,
|
|
@@ -272,6 +293,7 @@ export default {
|
|
|
272
293
|
notFound: null,
|
|
273
294
|
canViewChart: true,
|
|
274
295
|
canViewYaml: null,
|
|
296
|
+
errors: []
|
|
275
297
|
};
|
|
276
298
|
},
|
|
277
299
|
|
|
@@ -311,6 +333,18 @@ export default {
|
|
|
311
333
|
|
|
312
334
|
return null;
|
|
313
335
|
},
|
|
336
|
+
hasErrors() {
|
|
337
|
+
return this.errors?.length && Array.isArray(this.errors);
|
|
338
|
+
},
|
|
339
|
+
mappedErrors() {
|
|
340
|
+
return !this.errors ? {} : this.errorsMap || this.errors.reduce((acc, error) => ({
|
|
341
|
+
...acc,
|
|
342
|
+
[error]: {
|
|
343
|
+
message: error?.data?.message || error,
|
|
344
|
+
icon: null
|
|
345
|
+
}
|
|
346
|
+
}), {});
|
|
347
|
+
},
|
|
314
348
|
},
|
|
315
349
|
|
|
316
350
|
watch: {
|
|
@@ -360,6 +394,7 @@ export default {
|
|
|
360
394
|
},
|
|
361
395
|
|
|
362
396
|
methods: {
|
|
397
|
+
stringify,
|
|
363
398
|
setSubtype(subtype) {
|
|
364
399
|
this.resourceSubtype = subtype;
|
|
365
400
|
},
|
|
@@ -371,6 +406,9 @@ export default {
|
|
|
371
406
|
m[act]();
|
|
372
407
|
}
|
|
373
408
|
},
|
|
409
|
+
closeError(index) {
|
|
410
|
+
this.errors = this.errors.filter((_, i) => i !== index);
|
|
411
|
+
},
|
|
374
412
|
}
|
|
375
413
|
};
|
|
376
414
|
</script>
|
|
@@ -398,6 +436,22 @@ export default {
|
|
|
398
436
|
:value="liveModel"
|
|
399
437
|
/>
|
|
400
438
|
</Masthead>
|
|
439
|
+
<div
|
|
440
|
+
v-if="hasErrors"
|
|
441
|
+
id="cru-errors"
|
|
442
|
+
class="cru__errors"
|
|
443
|
+
>
|
|
444
|
+
<Banner
|
|
445
|
+
v-for="(err, i) in errors"
|
|
446
|
+
:key="i"
|
|
447
|
+
color="error"
|
|
448
|
+
:data-testid="`error-banner${i}`"
|
|
449
|
+
:label="stringify(mappedErrors[err].message)"
|
|
450
|
+
:icon="mappedErrors[err].icon"
|
|
451
|
+
:closable="true"
|
|
452
|
+
@close="closeError(i)"
|
|
453
|
+
/>
|
|
454
|
+
</div>
|
|
401
455
|
|
|
402
456
|
<ForceDirectedTreeChart
|
|
403
457
|
v-if="isGraph && canViewChart"
|
|
@@ -413,8 +467,9 @@ export default {
|
|
|
413
467
|
:yaml="yaml"
|
|
414
468
|
:offer-preview="offerPreview"
|
|
415
469
|
:done-route="doneRoute"
|
|
416
|
-
:done-override="value.doneOverride"
|
|
470
|
+
:done-override="value ? value.doneOverride : null"
|
|
417
471
|
@update:value="$emit('input', $event)"
|
|
472
|
+
@error="e=>errors.push(e)"
|
|
418
473
|
/>
|
|
419
474
|
|
|
420
475
|
<component
|
|
@@ -429,12 +429,17 @@ export default {
|
|
|
429
429
|
},
|
|
430
430
|
|
|
431
431
|
computedGroupBy() {
|
|
432
|
+
// If we're not showing grouping options we shouldn't have a group by property
|
|
433
|
+
if (!this.showGrouping) {
|
|
434
|
+
return null;
|
|
435
|
+
}
|
|
436
|
+
|
|
432
437
|
if ( this.groupBy ) {
|
|
433
438
|
// This probably comes from the type-map config for the resource (see ResourceList)
|
|
434
439
|
return this.groupBy;
|
|
435
440
|
}
|
|
436
441
|
|
|
437
|
-
if ( this.group === 'namespace'
|
|
442
|
+
if ( this.group === 'namespace' ) {
|
|
438
443
|
// This switches to group rows by a key which is the label for the group (??)
|
|
439
444
|
return 'groupByLabel';
|
|
440
445
|
}
|