@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.
Files changed (94) hide show
  1. package/assets/data/aws-regions.json +1 -0
  2. package/assets/styles/base/_basic.scss +5 -0
  3. package/assets/styles/base/_mixins.scss +8 -0
  4. package/assets/styles/global/_button.scss +5 -0
  5. package/assets/styles/themes/_dark.scss +2 -0
  6. package/assets/styles/themes/_light.scss +2 -0
  7. package/assets/translations/en-us.yaml +40 -22
  8. package/assets/translations/zh-hans.yaml +1 -7
  9. package/chart/monitoring/StorageClassSelector.vue +1 -1
  10. package/components/AssignTo.vue +1 -0
  11. package/components/AsyncButton.vue +1 -0
  12. package/components/BackLink.vue +8 -2
  13. package/components/PaginatedResourceTable.vue +135 -0
  14. package/components/ResourceDetail/Masthead.vue +1 -1
  15. package/components/ResourceDetail/index.vue +66 -11
  16. package/components/ResourceList/index.vue +0 -1
  17. package/components/ResourceTable.vue +6 -1
  18. package/components/ResourceYaml.vue +0 -53
  19. package/components/SortableTable/index.vue +8 -6
  20. package/components/Tabbed/index.vue +35 -2
  21. package/components/form/ResourceLabeledSelect.vue +2 -2
  22. package/components/form/ResourceTabs/index.vue +0 -23
  23. package/components/form/Taints.vue +1 -1
  24. package/components/form/UnitInput.vue +1 -1
  25. package/components/form/__tests__/UnitInput.test.ts +1 -1
  26. package/components/nav/TopLevelMenu.helper.ts +546 -0
  27. package/components/nav/TopLevelMenu.vue +125 -160
  28. package/components/nav/WindowManager/ContainerShell.vue +13 -4
  29. package/components/nav/WindowManager/__tests__/ContainerShell.test.ts +20 -18
  30. package/components/nav/__tests__/TopLevelMenu.test.ts +338 -326
  31. package/composables/useLabeledFormElement.ts +6 -2
  32. package/config/pagination-table-headers.js +4 -4
  33. package/config/product/explorer.js +2 -0
  34. package/config/router/navigation-guards/index.js +1 -2
  35. package/config/router/routes.js +1 -1
  36. package/config/settings.ts +15 -8
  37. package/core/plugin.ts +8 -1
  38. package/core/types-provisioning.ts +5 -0
  39. package/core/types.ts +26 -1
  40. package/dialog/DrainNode.vue +6 -6
  41. package/edit/catalog.cattle.io.clusterrepo.vue +95 -52
  42. package/edit/provisioning.cattle.io.cluster/index.vue +8 -3
  43. package/edit/workload/index.vue +1 -1
  44. package/edit/workload/storage/csi/index.vue +29 -1
  45. package/edit/workload/storage/index.vue +1 -0
  46. package/initialize/App.vue +3 -10
  47. package/initialize/install-plugins.js +1 -2
  48. package/list/management.cattle.io.podsecurityadmissionconfigurationtemplate.vue +6 -2
  49. package/list/node.vue +8 -5
  50. package/mixins/resource-fetch-api-pagination.js +40 -5
  51. package/mixins/resource-fetch.js +48 -5
  52. package/models/management.cattle.io.nodepool.js +5 -4
  53. package/models/nodedriver.js +2 -2
  54. package/models/provisioning.cattle.io.cluster.js +3 -11
  55. package/package.json +7 -8
  56. package/pages/about.vue +22 -0
  57. package/pages/auth/setup.vue +7 -28
  58. package/pages/c/_cluster/explorer/__tests__/index.test.ts +36 -24
  59. package/pages/c/_cluster/explorer/index.vue +100 -59
  60. package/pages/home.vue +308 -123
  61. package/plugins/dashboard-store/__tests__/mutations.test.ts +2 -0
  62. package/plugins/dashboard-store/actions.js +29 -19
  63. package/plugins/dashboard-store/getters.js +5 -2
  64. package/plugins/dashboard-store/mutations.js +4 -2
  65. package/plugins/steve/__tests__/mutations.test.ts +2 -1
  66. package/plugins/steve/steve-pagination-utils.ts +25 -2
  67. package/plugins/steve/subscribe.js +22 -8
  68. package/rancher-components/Banner/Banner.vue +1 -0
  69. package/rancher-components/Form/Checkbox/Checkbox.vue +2 -0
  70. package/rancher-components/Form/LabeledInput/LabeledInput.vue +2 -0
  71. package/rancher-components/Form/Radio/RadioButton.vue +2 -0
  72. package/rancher-components/Form/Radio/RadioGroup.vue +2 -0
  73. package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +2 -0
  74. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +3 -0
  75. package/rancher-components/StringList/StringList.test.ts +15 -15
  76. package/rancher-components/StringList/StringList.vue +3 -0
  77. package/scripts/extension/helm/charts/ui-plugin-server/Chart.yaml +0 -2
  78. package/scripts/extension/parse-tag-name +2 -0
  79. package/scripts/test-plugins-build.sh +4 -2
  80. package/store/index.js +31 -9
  81. package/tsconfig.json +7 -1
  82. package/types/resources/settings.d.ts +1 -1
  83. package/types/shell/index.d.ts +1107 -1276
  84. package/types/store/dashboard-store.types.ts +4 -0
  85. package/types/store/pagination.types.ts +13 -0
  86. package/types/store/vuex.d.ts +8 -0
  87. package/types/vue-shim.d.ts +6 -31
  88. package/utils/cluster.js +92 -1
  89. package/utils/pagination-utils.ts +17 -8
  90. package/utils/pagination-wrapper.ts +70 -0
  91. package/utils/uiplugins.ts +307 -0
  92. package/components/templates/error.vue +0 -131
  93. package/config/router/navigation-guards/history.js +0 -13
  94. package/plugins/back-button.js +0 -3
@@ -1,4 +1,6 @@
1
- import { ref, computed, ComputedRef, Ref } from 'vue';
1
+ import {
2
+ ref, computed, ComputedRef, Ref, defineEmits
3
+ } from 'vue';
2
4
  import { _VIEW, _EDIT } from '@shell/config/query-params';
3
5
 
4
6
  interface LabeledFormElementProps {
@@ -70,7 +72,9 @@ export const labeledFormElementProps = {
70
72
  }
71
73
  };
72
74
 
73
- export const useLabeledFormElement = (props: LabeledFormElementProps, emit: (event: string, ...args: any[]) => void): UseLabeledFormElement => {
75
+ const labeledFormElementEmits = defineEmits(['update:validation']);
76
+
77
+ export const useLabeledFormElement = (props: LabeledFormElementProps, emit: typeof labeledFormElementEmits): UseLabeledFormElement => {
74
78
  const raised = ref(props.mode === _VIEW || !!`${ props.value }`);
75
79
  const focused = ref(false);
76
80
  const blurred = ref<number | null>(null);
@@ -17,8 +17,8 @@ export const STEVE_ID_COL = {
17
17
  name: 'steve-id',
18
18
  labelKey: 'tableHeaders.id',
19
19
  value: 'id',
20
- sort: false, // sort: ['id'], // Pending API support
21
- search: false, // search: 'id', // Pending API support
20
+ sort: ['id'],
21
+ search: 'id',
22
22
  };
23
23
 
24
24
  export const STEVE_STATE_COL = {
@@ -26,8 +26,8 @@ export const STEVE_STATE_COL = {
26
26
  // Note, we're show the 'state' as per model, not the 'metadata.state.name' that's available in the model to remotely sort/filter
27
27
  // Need to investigate whether we should 'dumb down' the state we show to the native one (tracked via https://github.com/rancher/dashboard/issues/8527)
28
28
  // This means we'll show something different to what we sort and filter on.
29
- sort: false, // ['metadata.state.name'], // Pending API support
30
- search: false, // 'metadata.state.name', // Pending API support
29
+ sort: ['metadata.state.name'],
30
+ search: 'metadata.state.name',
31
31
  };
32
32
 
33
33
  export const STEVE_AGE_COL = {
@@ -9,6 +9,7 @@ import {
9
9
  NORMAN,
10
10
  SNAPSHOT,
11
11
  VIRTUAL_TYPES,
12
+ CAPI,
12
13
  } from '@shell/config/types';
13
14
 
14
15
  import {
@@ -60,6 +61,7 @@ export function init(store) {
60
61
  [MANAGEMENT.PROJECT_ROLE_TEMPLATE_BINDING]: 'management',
61
62
  [NORMAN.CLUSTER_ROLE_TEMPLATE_BINDING]: 'rancher',
62
63
  [NORMAN.PROJECT_ROLE_TEMPLATE_BINDING]: 'rancher',
64
+ [CAPI.RANCHER_CLUSTER]: 'management',
63
65
  }
64
66
  });
65
67
 
@@ -4,7 +4,6 @@ import { install as installAuthentication } from '@shell/config/router/navigatio
4
4
  import { install as installRuntimeExtensionRoute } from '@shell/config/router/navigation-guards/runtime-extension-route';
5
5
  import { install as installI18N } from '@shell/config/router/navigation-guards/i18n';
6
6
  import { install as installProducts } from '@shell/config/router/navigation-guards/products';
7
- import { install as installHistory } from '@shell/config/router/navigation-guards/history';
8
7
  import { install as installClusters } from '@shell/config/router/navigation-guards/clusters';
9
8
  import { install as installHandleInstallRedirect } from '@shell/config/router/navigation-guards/install-redirect';
10
9
  import { install as installPageTitle } from '@shell/config/router/navigation-guards/page-title';
@@ -17,7 +16,7 @@ export function installNavigationGuards(router, context) {
17
16
  // NOTE: the order of the installation matters.
18
17
  // Be intentional when adding, removing or modifying the guards that are installed.
19
18
 
20
- const navigationGuardInstallers = [installLoadInitialSettings, installAttemptFirstLogin, installAuthentication, installProducts, installHistory, installClusters, installRuntimeExtensionRoute, installI18N, installHandleInstallRedirect, installPageTitle, installServerUpgradeGrowl];
19
+ const navigationGuardInstallers = [installLoadInitialSettings, installAttemptFirstLogin, installAuthentication, installProducts, installClusters, installRuntimeExtensionRoute, installI18N, installHandleInstallRedirect, installPageTitle, installServerUpgradeGrowl];
21
20
 
22
21
  navigationGuardInstallers.forEach((installer) => installer(router, context));
23
22
  }
@@ -5,7 +5,7 @@ import { CAPI, MANAGEMENT, BACKUP_RESTORE, CIS } from '@shell/config/types';
5
5
  import { NAME as AUTH } from '@shell/config/product/auth';
6
6
 
7
7
  // All these imports are related to the install-redirect.js navigation guard.
8
- import { installRedirectRouteMeta } from 'config/router/navigation-guards/install-redirect';
8
+ import { installRedirectRouteMeta } from '@shell/config/router/navigation-guards/install-redirect';
9
9
  import { NAME as BACKUP_NAME, CHART_NAME as BACKUP_CHART_NAME } from '@shell/config/product/backup';
10
10
  import { NAME as CIS_NAME, CHART_NAME as CIS_CHART_NAME } from '@shell/config/product/cis';
11
11
  import { NAME as GATEKEEPER_NAME, CHART_NAME as GATEKEEPER_CHART_NAME } from '@shell/config/product/gatekeeper';
@@ -1,6 +1,7 @@
1
1
  // Settings
2
2
  import { GC_DEFAULTS, GC_PREFERENCES } from '@shell/utils/gc/gc-types';
3
3
  import { PaginationSettings } from '@shell/types/resources/settings';
4
+ import { CAPI, MANAGEMENT } from '@shell/config/types';
4
5
 
5
6
  interface GlobalSettingRuleset {
6
7
  name: string,
@@ -51,7 +52,6 @@ export const SETTING = {
51
52
  INGRESS_IP_DOMAIN: 'ingress-ip-domain',
52
53
  SERVER_URL: 'server-url',
53
54
  RKE_METADATA_CONFIG: 'rke-metadata-config',
54
- TELEMETRY: 'telemetry-opt',
55
55
  EULA_AGREED: 'eula-agreed',
56
56
  AUTH_USER_INFO_MAX_AGE_SECONDS: 'auth-user-info-max-age-seconds',
57
57
  AUTH_USER_SESSION_TTL_MINUTES: 'auth-user-session-ttl-minutes',
@@ -156,12 +156,8 @@ export const ALLOWED_SETTINGS: GlobalSetting = {
156
156
  },
157
157
  [SETTING.BRAND]: { canReset: true },
158
158
  [SETTING.CLUSTER_TEMPLATE_ENFORCEMENT]: { kind: 'boolean' },
159
- [SETTING.TELEMETRY]: {
160
- kind: 'enum',
161
- options: ['prompt', 'in', 'out']
162
- },
163
- [SETTING.HIDE_LOCAL_CLUSTER]: { kind: 'boolean' },
164
- [SETTING.AGENT_TLS_MODE]: {
159
+ [SETTING.HIDE_LOCAL_CLUSTER]: { kind: 'boolean' },
160
+ [SETTING.AGENT_TLS_MODE]: {
165
161
  kind: 'enum',
166
162
  options: ['strict', 'system-store'],
167
163
  warning: 'agent-tls-mode'
@@ -258,8 +254,19 @@ export const DEFAULT_PERF_SETTING: PerfSettings = {
258
254
  generic: true,
259
255
  }
260
256
  }
257
+ },
258
+ management: {
259
+ resources: {
260
+ enableAll: false,
261
+ enableSome: {
262
+ enabled: [
263
+ { resource: CAPI.RANCHER_CLUSTER, context: ['home', 'side-bar'] },
264
+ { resource: MANAGEMENT.CLUSTER, context: ['side-bar'] },
265
+ ],
266
+ generic: false,
267
+ }
268
+ }
261
269
  }
262
270
  }
263
271
  }
264
-
265
272
  };
package/core/plugin.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
2
2
  import { RouteRecordRaw } from 'vue-router';
3
3
  import { DSL as STORE_DSL } from '@shell/store/type-map';
4
+ import { _DETAIL } from '@shell/config/query-params';
4
5
  import {
5
6
  CoreStoreInit,
6
7
  Action,
@@ -11,7 +12,7 @@ import {
11
12
  IPlugin,
12
13
  LocationConfig,
13
14
  ExtensionPoint,
14
-
15
+ TabLocation,
15
16
  PluginRouteRecordRaw, RegisterStore, UnregisterStore, CoreStoreSpecifics, CoreStoreConfig, OnNavToPackage, OnNavAwayFromPackage, OnLogOut
16
17
  } from './types';
17
18
  import coreStore, { coreStoreModule, coreStoreState } from '@shell/plugins/dashboard-store';
@@ -162,6 +163,12 @@ export class Plugin implements IPlugin {
162
163
  * Adds a tab to the UI
163
164
  */
164
165
  addTab(where: string, when: LocationConfig | string, tab: Tab): void {
166
+ // tackling https://github.com/rancher/dashboard/issues/11122, we don't want the tab to added in _EDIT view, unless overriden
167
+ // on extensions side we won't document the mode param for this extension point
168
+ if (where === TabLocation.RESOURCE_DETAIL && (typeof when === 'object' && !when.mode)) {
169
+ when.mode = [_DETAIL];
170
+ }
171
+
165
172
  this._addUIConfig(ExtensionPoint.TAB, where, when, this._createAsyncComponent(tab));
166
173
  }
167
174
 
@@ -106,6 +106,11 @@ export interface IClusterProvisioner {
106
106
  */
107
107
  disabled?: boolean;
108
108
 
109
+ /**
110
+ * Hide the cluster provider card
111
+ */
112
+ hidden?: boolean;
113
+
109
114
  /**
110
115
  * Custom Dashboard route to navigate to when the cluster provider card is clicked
111
116
  */
package/core/types.ts CHANGED
@@ -237,6 +237,16 @@ export interface ProductOptions {
237
237
  */
238
238
  to?: PluginRouteRecordRaw;
239
239
 
240
+ /**
241
+ * Alternative to the icon property. Uses require
242
+ */
243
+ svg?: Function;
244
+
245
+ /**
246
+ * Product name
247
+ */
248
+ name?: string;
249
+
240
250
  /**
241
251
  * Leaving these here for completeness but I don't think these should be advertised as useable to plugin creators.
242
252
  */
@@ -373,10 +383,25 @@ export interface ConfigureTypeOptions {
373
383
  }
374
384
 
375
385
  export interface ConfigureVirtualTypeOptions extends ConfigureTypeOptions {
386
+ /**
387
+ * Only load the product if the type is present
388
+ */
389
+ ifHave?: string;
390
+
391
+ /**
392
+ * Only load the product if the type is present
393
+ */
394
+ ifHaveType?: string | RegExp | Object;
395
+
396
+ /**
397
+ * The label that this type should display
398
+ */
399
+ label?: string;
400
+
376
401
  /**
377
402
  * The translation key displayed anywhere this type is referenced
378
403
  */
379
- labelKey: string;
404
+ labelKey?: string;
380
405
 
381
406
  /**
382
407
  * An identifier that should be unique across all types
@@ -146,7 +146,7 @@ export default {
146
146
  <div class="pl-10 pr-10">
147
147
  <div>
148
148
  <RadioGroup
149
- v-model="body.deleteLocalData"
149
+ v-model:value="body.deleteLocalData"
150
150
  name="deleteLocalData"
151
151
  :options="radioOptions"
152
152
  :row="true"
@@ -157,7 +157,7 @@ export default {
157
157
  </template>
158
158
  </RadioGroup>
159
159
  <RadioGroup
160
- v-model="body.force"
160
+ v-model:value="body.force"
161
161
  name="force"
162
162
  :options="radioOptions"
163
163
  :row="true"
@@ -168,7 +168,7 @@ export default {
168
168
  </template>
169
169
  </RadioGroup>
170
170
  <RadioGroup
171
- v-model="gracePeriod"
171
+ v-model:value="gracePeriod"
172
172
  name="gracePeriod"
173
173
  :options="gracePeriodOptions"
174
174
  class="mb-15"
@@ -178,7 +178,7 @@ export default {
178
178
  </template>
179
179
  </RadioGroup>
180
180
  <UnitInput
181
- v-model="body.gracePeriod"
181
+ v-model:value="body.gracePeriod"
182
182
  :mode="gracePeriod ? EDIT : VIEW"
183
183
  type="number"
184
184
  min="1"
@@ -187,7 +187,7 @@ export default {
187
187
  class="mb-10"
188
188
  />
189
189
  <RadioGroup
190
- v-model="timeout"
190
+ v-model:value="timeout"
191
191
  name="timeout"
192
192
  :options="timeoutOptions"
193
193
  class="mb-15"
@@ -197,7 +197,7 @@ export default {
197
197
  </template>
198
198
  </RadioGroup>
199
199
  <UnitInput
200
- v-model="body.timeout"
200
+ v-model:value="body.timeout"
201
201
  :mode="timeout ? EDIT : VIEW"
202
202
  type="number"
203
203
  min="1"
@@ -9,6 +9,7 @@ import SelectOrCreateAuthSecret from '@shell/components/form/SelectOrCreateAuthS
9
9
  import InfoBox from '@shell/components/InfoBox';
10
10
  import { Checkbox } from '@components/Form/Checkbox';
11
11
  import { MANAGEMENT, NAMESPACE, CLUSTER_REPO_TYPES } from '@shell/config/types';
12
+ import UnitInput from '@shell/components/form/UnitInput.vue';
12
13
 
13
14
  export default {
14
15
  name: 'CruCatalogRepo',
@@ -23,7 +24,8 @@ export default {
23
24
  Labels,
24
25
  SelectOrCreateAuthSecret,
25
26
  InfoBox,
26
- Checkbox
27
+ Checkbox,
28
+ UnitInput
27
29
  },
28
30
 
29
31
  mixins: [CreateEditView],
@@ -83,7 +85,7 @@ export default {
83
85
  this.value.spec['exponentialBackOffValues'] = {};
84
86
  }
85
87
  // when user removes the value we remove the key too, backend will set the default value
86
- if (newVal === '') {
88
+ if (newVal === '' || newVal === null) {
87
89
  delete this.value.spec.exponentialBackOffValues[key];
88
90
 
89
91
  return;
@@ -91,15 +93,27 @@ export default {
91
93
 
92
94
  this.value.spec.exponentialBackOffValues[key] = Number(newVal);
93
95
  },
96
+ updateRefreshInterval(newVal) {
97
+ // when user removes the value we don't send refreshInterval along with the payload
98
+ if (newVal === null) {
99
+ delete this.value.spec.refreshInterval;
100
+
101
+ return;
102
+ }
103
+
104
+ this.value.spec.refreshInterval = newVal;
105
+ },
94
106
  resetGitRepoValues() {
95
107
  delete this.value.spec['gitRepo'];
96
108
  delete this.value.spec['gitBranch'];
109
+ delete this.value.spec['refreshInterval'];
97
110
  },
98
111
  resetOciValues() {
99
112
  delete this.value.spec['url'];
100
113
  delete this.value.spec['insecurePlainHttp'];
101
114
  delete this.value.spec['insecureSkipTLSVerify'];
102
115
  delete this.value.spec['caBundle'];
116
+ delete this.value.spec['refreshInterval'];
103
117
  delete this.value.spec['exponentialBackOffValues'];
104
118
  this.ociMinWait = undefined;
105
119
  this.ociMaxWait = undefined;
@@ -139,55 +153,80 @@ export default {
139
153
  </div>
140
154
  </div>
141
155
 
142
- <div
143
- v-if="clusterRepoType === CLUSTER_REPO_TYPES.GIT_REPO"
144
- class="row mb-10"
156
+ <InfoBox
157
+ v-if="clusterRepoType === CLUSTER_REPO_TYPES.OCI_URL"
158
+ class="p-10"
145
159
  >
146
- <div class="col span-6">
160
+ {{ t('catalog.repo.oci.info', null, true) }}
161
+ </InfoBox>
162
+
163
+ <div class="row mb-10">
164
+ <template v-if="clusterRepoType === CLUSTER_REPO_TYPES.GIT_REPO">
165
+ <div class="col span-6">
166
+ <LabeledInput
167
+ v-model:value.trim="value.spec.gitRepo"
168
+ :required="true"
169
+ :label="t('catalog.repo.gitRepo.label')"
170
+ :placeholder="t('catalog.repo.gitRepo.placeholder', null, true)"
171
+ :mode="mode"
172
+ data-testid="clusterrepo-git-repo-input"
173
+ />
174
+ </div>
175
+ <div class="col span-3">
176
+ <LabeledInput
177
+ v-model:value.trim="value.spec.gitBranch"
178
+ :sub-label="!value.spec.gitBranch ? t('catalog.repo.gitBranch.defaultMessage', null, true) : undefined"
179
+ :label="t('catalog.repo.gitBranch.label')"
180
+ :placeholder="t('catalog.repo.gitBranch.placeholder', null, true)"
181
+ :mode="mode"
182
+ data-testid="clusterrepo-git-branch-input"
183
+ />
184
+ </div>
185
+ </template>
186
+
187
+ <template v-else-if="clusterRepoType === CLUSTER_REPO_TYPES.OCI_URL">
188
+ <div class="col span-6">
189
+ <LabeledInput
190
+ v-model:value.trim="value.spec.url"
191
+ :required="true"
192
+ :label="t('catalog.repo.oci.urlLabel')"
193
+ :placeholder="t('catalog.repo.oci.placeholder', null, true)"
194
+ :mode="mode"
195
+ data-testid="clusterrepo-oci-url-input"
196
+ />
197
+ </div>
198
+ </template>
199
+
200
+ <div
201
+ v-else
202
+ class="col span-6"
203
+ >
147
204
  <LabeledInput
148
- v-model:value.trim="value.spec.gitRepo"
205
+ v-model:value.trim="value.spec.url"
149
206
  :required="true"
150
- :label="t('catalog.repo.gitRepo.label')"
151
- :placeholder="t('catalog.repo.gitRepo.placeholder', null, true)"
207
+ :label="t('catalog.repo.url.label')"
208
+ :placeholder="t('catalog.repo.url.placeholder', null, true)"
152
209
  :mode="mode"
153
- data-testid="clusterrepo-git-repo-input"
210
+ data-testid="clusterrepo-helm-url-input"
154
211
  />
155
212
  </div>
156
- <div class="col span-6">
157
- <LabeledInput
158
- v-model:value.trim="value.spec.gitBranch"
159
- :sub-label="!value.spec.gitBranch ? t('catalog.repo.gitBranch.defaultMessage', null, true) : undefined"
160
- :label="t('catalog.repo.gitBranch.label')"
161
- :placeholder="t('catalog.repo.gitBranch.placeholder', null, true)"
213
+
214
+ <div
215
+ class="col span-3"
216
+ data-testid="clusterrepo-refresh-interval"
217
+ >
218
+ <UnitInput
219
+ v-model:value.trim="value.spec.refreshInterval"
220
+ :label="t('catalog.repo.refreshInterval.label')"
162
221
  :mode="mode"
163
- data-testid="clusterrepo-git-branch-input"
222
+ min="0"
223
+ :suffix="t('unit.hour', { count: value.spec.refreshInterval })"
224
+ :placeholder="t('catalog.repo.refreshInterval.placeholder', { hours: clusterRepoType === CLUSTER_REPO_TYPES.OCI_URL ? 24 : 6 })"
225
+ @update:value="updateRefreshInterval($event)"
164
226
  />
165
227
  </div>
166
228
  </div>
167
229
 
168
- <div v-else-if="clusterRepoType === CLUSTER_REPO_TYPES.OCI_URL">
169
- <InfoBox class="p-10">
170
- {{ t('catalog.repo.oci.info', null, true) }}
171
- </InfoBox>
172
- <LabeledInput
173
- v-model:value.trim="value.spec.url"
174
- :required="true"
175
- :label="t('catalog.repo.oci.urlLabel')"
176
- :placeholder="t('catalog.repo.oci.placeholder', null, true)"
177
- :mode="mode"
178
- data-testid="clusterrepo-oci-url-input"
179
- />
180
- </div>
181
-
182
- <LabeledInput
183
- v-else
184
- v-model:value.trim="value.spec.url"
185
- :required="true"
186
- :label="t('catalog.repo.url.label')"
187
- :placeholder="t('catalog.repo.url.placeholder', null, true)"
188
- :mode="mode"
189
- data-testid="clusterrepo-helm-url-input"
190
- />
191
230
  <SelectOrCreateAuthSecret
192
231
  v-model:value="value.spec.clientSecret"
193
232
  :mode="mode"
@@ -231,28 +270,32 @@ export default {
231
270
  <h4 class="mb-10 mt-20">
232
271
  {{ t('catalog.repo.oci.exponentialBackOff.label') }}
233
272
  </h4>
234
- <div class="row mb-10 mt-10">
235
- <div class="col span-4">
236
- <LabeledInput
273
+ <div class="row mb-40 mt-10">
274
+ <div
275
+ class="col span-4"
276
+ data-testid="clusterrepo-oci-min-wait-input"
277
+ >
278
+ <UnitInput
237
279
  v-model:value.trim="ociMinWait"
238
280
  :label="t('catalog.repo.oci.exponentialBackOff.minWait.label')"
239
- :placeholder="!ociMinWait ? t('catalog.repo.oci.exponentialBackOff.minWait.placeholder') : undefined"
281
+ :placeholder="t('catalog.repo.oci.exponentialBackOff.minWait.placeholder')"
240
282
  :mode="mode"
241
- type="number"
242
283
  min="1"
243
- data-testid="clusterrepo-oci-min-wait-input"
284
+ :suffix="t('suffix.seconds', { count: ociMinWait })"
244
285
  @update:value="updateExponentialBackOffValues('minWait', $event)"
245
286
  />
246
287
  </div>
247
- <div class="col span-4">
248
- <LabeledInput
288
+ <div
289
+ class="col span-4"
290
+ data-testid="clusterrepo-oci-max-wait-input"
291
+ >
292
+ <UnitInput
249
293
  v-model:value.trim="ociMaxWait"
250
294
  :label="t('catalog.repo.oci.exponentialBackOff.maxWait.label')"
251
- :placeholder="!ociMaxWait ? t('catalog.repo.oci.exponentialBackOff.maxWait.placeholder') : undefined"
295
+ :placeholder="t('catalog.repo.oci.exponentialBackOff.maxWait.placeholder')"
252
296
  :mode="mode"
253
- type="number"
254
297
  min="1"
255
- data-testid="clusterrepo-oci-max-wait-input"
298
+ :suffix="t('suffix.seconds', { count: ociMaxWait })"
256
299
  @update:value="updateExponentialBackOffValues('maxWait', $event)"
257
300
  />
258
301
  </div>
@@ -260,7 +303,7 @@ export default {
260
303
  <LabeledInput
261
304
  v-model:value.trim="ociMaxRetries"
262
305
  :label="t('catalog.repo.oci.exponentialBackOff.maxRetries.label')"
263
- :placeholder="!ociMaxRetries ? t('catalog.repo.oci.exponentialBackOff.maxRetries.placeholder') : undefined"
306
+ :placeholder="t('catalog.repo.oci.exponentialBackOff.maxRetries.placeholder')"
264
307
  :mode="mode"
265
308
  type="number"
266
309
  min="0"
@@ -404,7 +404,8 @@ export default {
404
404
  disabled: ext.disabled || false,
405
405
  link: ext.link,
406
406
  tag: ext.tag,
407
- component: ext.component
407
+ component: ext.component,
408
+ hidden: ext.hidden,
408
409
  };
409
410
 
410
411
  out.push(subtype);
@@ -449,10 +450,14 @@ export default {
449
450
  }
450
451
  },
451
452
 
453
+ filteredSubTypes() {
454
+ return this.subTypes.filter((subtype) => !subtype.hidden);
455
+ },
456
+
452
457
  groupedSubTypes() {
453
458
  const out = {};
454
459
 
455
- for ( const row of this.subTypes ) {
460
+ for ( const row of this.filteredSubTypes ) {
456
461
  const name = row.group;
457
462
  let entry = out[name];
458
463
 
@@ -623,7 +628,7 @@ export default {
623
628
  <div
624
629
  v-for="(obj, i) in groupedSubTypes"
625
630
  :key="i"
626
- class="mb-20"
631
+ :class="{'mt-5': i === 0, 'mt-20': i !== 0 }"
627
632
  style="width: 100%;"
628
633
  >
629
634
  <h4>
@@ -415,7 +415,7 @@ export default {
415
415
  >
416
416
  <Tab
417
417
  :label="t('workload.storage.title')"
418
- name="storage"
418
+ name="storage-pod"
419
419
  :weight="tabWeightMap['storage']"
420
420
  @active="$refs.storage.refresh()"
421
421
  >
@@ -44,6 +44,33 @@ export default {
44
44
 
45
45
  ...mapGetters({ t: 'i18n/t' })
46
46
  },
47
+
48
+ methods: {
49
+ /**
50
+ * Retrieves the label for a given option
51
+ * @param option The option for which to retrieve the label. option can be
52
+ * either a string or an object. If it is an object, is should have a `label`
53
+ * property associated with it.
54
+ */
55
+ getOptionLabel(option) {
56
+ if (typeof option === 'string') {
57
+ return this.getOptionLabelString(option);
58
+ }
59
+
60
+ const { label } = option;
61
+
62
+ return this.getOptionLabelString(label);
63
+ },
64
+ /**
65
+ * Translates a given key into a localized string.
66
+ * @param key The key to be translated.
67
+ */
68
+ getOptionLabelString(key) {
69
+ // Periods are replaced with `-` to prevent conflict with the default key
70
+ // separator.
71
+ return this.t(`workload.storage.csi.drivers.'${ key.replaceAll('.', '-') }'`);
72
+ }
73
+ }
47
74
  };
48
75
  </script>
49
76
 
@@ -71,10 +98,11 @@ export default {
71
98
  <div class="col span-6">
72
99
  <LabeledSelect
73
100
  v-model:value="value.csi.driver"
101
+ data-testid="workload-storage-driver"
74
102
  :mode="mode"
75
103
  :label="t('workload.storage.driver')"
76
104
  :options="driverOpts"
77
- :get-option-label="opt=>t(`workload.storage.csi.drivers.'${opt}'`)"
105
+ :get-option-label="getOptionLabel"
78
106
  :required="true"
79
107
  />
80
108
  </div>
@@ -301,6 +301,7 @@ export default {
301
301
  :button-label="t('workload.storage.addVolume')"
302
302
  :dropdown-options="volumeTypeOptions"
303
303
  size="sm"
304
+ data-testid="dropdown-button-storage-volume"
304
305
  @click-action="e=>addVolume(e.value)"
305
306
  />
306
307
  </template>
@@ -1,15 +1,10 @@
1
1
  <script>
2
- import NuxtError from '@shell/components/templates/error.vue';
3
2
  import NuxtLoading from '@shell/components/nav/GlobalLoading.vue';
4
3
 
5
4
  import '@shell/assets/styles/app.scss';
6
5
 
7
6
  export default {
8
- data: () => ({
9
- isOnline: true,
10
-
11
- showErrorPage: false,
12
- }),
7
+ data: () => ({ isOnline: true }),
13
8
 
14
9
  created() {
15
10
  // add to window so we can listen when ready
@@ -60,7 +55,7 @@ export default {
60
55
  },
61
56
  },
62
57
 
63
- components: { NuxtLoading, NuxtError }
58
+ components: { NuxtLoading }
64
59
  };
65
60
  </script>
66
61
  <template>
@@ -68,10 +63,8 @@ export default {
68
63
  <NuxtLoading ref="loading" />
69
64
  <div
70
65
  id="__layout"
71
- :key="showErrorPage"
72
66
  >
73
- <NuxtError v-if="showErrorPage" />
74
- <router-view v-else />
67
+ <router-view />
75
68
  </div>
76
69
  </div>
77
70
  </template>
@@ -15,7 +15,6 @@ import axios from '@shell/utils/axios';
15
15
  import cookieUniversal from '@shell/utils/cookie-universal';
16
16
  import config from '@shell/utils/config';
17
17
  import axiosShell from '@shell/plugins/axios';
18
- import backButton from '@shell/plugins/back-button';
19
18
  import codeMirror from '@shell/plugins/codemirror-loader';
20
19
  import { InstallCodeMirror } from 'codemirror-editor-vue3';
21
20
  import * as intNumber from '@shell/directives/int-number';
@@ -42,7 +41,7 @@ export async function installPlugins(vueApp) {
42
41
  }
43
42
 
44
43
  export async function installInjectedPlugins(app, vueApp) {
45
- const pluginDefinitions = [config, cookieUniversal, axios, plugins, pluginsLoader, axiosShell, intNumber, codeMirror, nuxtClientInit, replaceAll, backButton, plugin, steveCreateWorker, emberCookie];
44
+ const pluginDefinitions = [config, cookieUniversal, axios, plugins, pluginsLoader, axiosShell, intNumber, codeMirror, nuxtClientInit, replaceAll, plugin, steveCreateWorker, emberCookie];
46
45
 
47
46
  const installations = pluginDefinitions.map(async(pluginDefinition) => {
48
47
  if (typeof pluginDefinition === 'function') {