@rancher/shell 0.3.23 → 0.3.25

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 (149) hide show
  1. package/assets/styles/base/_variables.scss +1 -0
  2. package/assets/styles/themes/_dark.scss +1 -0
  3. package/assets/styles/themes/_light.scss +6 -5
  4. package/assets/translations/en-us.yaml +44 -17
  5. package/assets/translations/zh-hans.yaml +2 -2
  6. package/components/ClusterIconMenu.vue +143 -0
  7. package/components/CruResource.vue +7 -1
  8. package/components/ExplorerProjectsNamespaces.vue +11 -1
  9. package/components/FixedBanner.vue +17 -1
  10. package/components/Loading.vue +1 -1
  11. package/components/Markdown.vue +1 -1
  12. package/components/Questions/__tests__/Yaml.test.ts +3 -2
  13. package/components/SideNav.vue +1 -1
  14. package/components/SortableTable/index.vue +3 -2
  15. package/components/auth/RoleDetailEdit.vue +15 -2
  16. package/components/auth/login/saml.vue +12 -1
  17. package/components/form/LabeledSelect.vue +12 -5
  18. package/components/form/Members/ClusterPermissionsEditor.vue +1 -1
  19. package/components/form/Members/MembershipEditor.vue +6 -1
  20. package/components/form/SelectOrCreateAuthSecret.vue +7 -0
  21. package/components/form/__tests__/KeyValue.test.ts +6 -3
  22. package/components/form/__tests__/LabeledSelect.test.ts +18 -0
  23. package/components/formatter/PodsUsage.vue +11 -36
  24. package/components/formatter/PrincipalGroupBindings.vue +8 -5
  25. package/components/formatter/__tests__/PodsUsage.test.ts +36 -19
  26. package/components/nav/Group.vue +62 -34
  27. package/components/nav/Header.vue +13 -6
  28. package/components/nav/Pinned.vue +47 -0
  29. package/components/nav/TopLevelMenu.vue +673 -325
  30. package/components/nav/Type.vue +88 -8
  31. package/config/home-links.js +1 -1
  32. package/config/product/istio.js +15 -5
  33. package/config/router.js +3 -9
  34. package/config/table-headers.js +5 -6
  35. package/config/uiplugins.js +1 -0
  36. package/core/plugin-helpers.js +3 -0
  37. package/core/types.ts +6 -1
  38. package/creators/app/files/.vscode/settings.json +0 -1
  39. package/creators/pkg/init +2 -2
  40. package/detail/__tests__/autoscaling.horizontalpodautoscaler.test.ts +118 -0
  41. package/detail/autoscaling.horizontalpodautoscaler/index.vue +4 -4
  42. package/detail/provisioning.cattle.io.cluster.vue +7 -5
  43. package/edit/__tests__/management.cattle.io.clusterroletemplatebinding.test.ts +58 -0
  44. package/edit/__tests__/namespace.test.ts +5 -3
  45. package/edit/fleet.cattle.io.gitrepo.vue +43 -15
  46. package/edit/logging.banzaicloud.io.output/index.vue +7 -0
  47. package/edit/management.cattle.io.clusterroletemplatebinding.vue +3 -11
  48. package/edit/namespace.vue +8 -4
  49. package/edit/provisioning.cattle.io.cluster/Basics.vue +662 -0
  50. package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +9 -8
  51. package/edit/provisioning.cattle.io.cluster/DrainOptions.vue +13 -8
  52. package/edit/provisioning.cattle.io.cluster/MachinePool.vue +11 -2
  53. package/edit/provisioning.cattle.io.cluster/MemberRoles.vue +40 -0
  54. package/edit/provisioning.cattle.io.cluster/__tests__/Basics.tests.ts +237 -0
  55. package/edit/provisioning.cattle.io.cluster/__tests__/CustomCommand.tests.ts +71 -23
  56. package/edit/provisioning.cattle.io.cluster/__tests__/DrainOptions.test.ts +52 -0
  57. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +65 -142
  58. package/edit/provisioning.cattle.io.cluster/rke2.vue +253 -582
  59. package/edit/workload/storage/ContainerMountPaths.vue +7 -5
  60. package/edit/workload/storage/__tests__/Storage.test.ts +2 -2
  61. package/edit/workload/storage/persistentVolumeClaim/__tests__/persistentvolumeclaim.test.ts +36 -0
  62. package/edit/workload/storage/persistentVolumeClaim/persistentvolumeclaim.vue +15 -7
  63. package/initialize/App.js +2 -0
  64. package/initialize/client.js +63 -51
  65. package/initialize/index.js +7 -5
  66. package/layouts/default.vue +10 -2
  67. package/layouts/home.vue +6 -2
  68. package/layouts/plain.vue +9 -2
  69. package/list/fleet.cattle.io.cluster.vue +2 -2
  70. package/list/management.cattle.io.feature.vue +1 -1
  71. package/machine-config/amazonec2.vue +1 -0
  72. package/machine-config/vmwarevsphere.vue +48 -7
  73. package/mixins/brand.js +0 -8
  74. package/mixins/child-hook.js +2 -2
  75. package/mixins/create-edit-view/impl.js +3 -3
  76. package/mixins/fetch.client.js +3 -3
  77. package/models/__tests__/management.cattle.io.node.ts +96 -0
  78. package/models/__tests__/node.ts +74 -0
  79. package/models/cluster/node.js +6 -5
  80. package/models/cluster.x-k8s.io.machinedeployment.js +2 -2
  81. package/models/management.cattle.io.cluster.js +22 -1
  82. package/models/management.cattle.io.clusterroletemplatebinding.js +3 -3
  83. package/models/management.cattle.io.globalrole.js +17 -2
  84. package/models/management.cattle.io.node.js +6 -4
  85. package/models/management.cattle.io.projectroletemplatebinding.js +3 -3
  86. package/models/management.cattle.io.roletemplate.js +17 -2
  87. package/package.json +2 -6
  88. package/pages/__tests__/prefs.test.ts +1 -1
  89. package/pages/about.vue +2 -0
  90. package/pages/auth/setup.vue +5 -4
  91. package/pages/c/_cluster/explorer/ConfigBadge.vue +1 -0
  92. package/pages/c/_cluster/monitoring/index.vue +8 -3
  93. package/pages/c/_cluster/uiplugins/CatalogList/CatalogLoadDialog.vue +9 -66
  94. package/pages/c/_cluster/uiplugins/CatalogList/CatalogUninstallDialog.vue +182 -0
  95. package/pages/c/_cluster/uiplugins/CatalogList/index.vue +15 -32
  96. package/pages/c/_cluster/uiplugins/UninstallDialog.vue +8 -46
  97. package/pages/c/_cluster/uiplugins/index.vue +64 -64
  98. package/pages/diagnostic.vue +0 -39
  99. package/pages/home.vue +1 -1
  100. package/pages/prefs.vue +3 -13
  101. package/plugins/dashboard-store/normalize.js +4 -4
  102. package/plugins/dashboard-store/resource-class.js +1 -1
  103. package/plugins/int-number.js +5 -2
  104. package/plugins/positive-int-number.js +19 -0
  105. package/plugins/steve/__tests__/getters.spec.ts +15 -0
  106. package/plugins/steve/getters.js +22 -10
  107. package/public/index.html +4 -2
  108. package/rancher-components/BadgeState/BadgeState.vue +5 -1
  109. package/rancher-components/Banner/Banner.test.ts +51 -1
  110. package/rancher-components/Banner/Banner.vue +134 -53
  111. package/rancher-components/Card/Card.test.ts +37 -0
  112. package/rancher-components/Card/Card.vue +24 -7
  113. package/rancher-components/Form/Checkbox/Checkbox.test.ts +20 -29
  114. package/rancher-components/Form/Checkbox/Checkbox.vue +45 -20
  115. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +2 -8
  116. package/rancher-components/Form/LabeledInput/LabeledInput.vue +22 -10
  117. package/rancher-components/Form/Radio/RadioButton.test.ts +31 -0
  118. package/rancher-components/Form/Radio/RadioButton.vue +30 -13
  119. package/rancher-components/Form/Radio/RadioGroup.vue +26 -7
  120. package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +7 -6
  121. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.test.ts +25 -38
  122. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +23 -11
  123. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +19 -5
  124. package/rancher-components/StringList/StringList.test.ts +453 -49
  125. package/rancher-components/StringList/StringList.vue +92 -58
  126. package/scripts/extension/parse-tag-name +0 -0
  127. package/store/index.js +4 -0
  128. package/store/prefs.js +4 -4
  129. package/store/type-map.js +2 -16
  130. package/types/shell/index.d.ts +26 -14
  131. package/utils/__tests__/cluster.test.ts +55 -0
  132. package/utils/__tests__/object.test.ts +21 -2
  133. package/utils/__tests__/sort.test.ts +61 -0
  134. package/utils/cluster.js +47 -1
  135. package/utils/object.js +12 -5
  136. package/utils/string.js +12 -0
  137. package/utils/validators/formRules/__tests__/index.test.ts +13 -1
  138. package/utils/validators/formRules/index.ts +4 -0
  139. package/utils/validators/role-template.js +9 -1
  140. package/utils/version.js +1 -1
  141. package/vue.config.js +1 -4
  142. package/yarn-error.log +200 -0
  143. package/content/docs/en-us/getting-started.md +0 -224
  144. package/content/docs/en-us/whats-new.md +0 -29
  145. package/content/docs/zh-hans/getting-started.md +0 -224
  146. package/content/docs/zh-hans/whats-new.md +0 -28
  147. package/pages/docs/_doc.vue +0 -345
  148. package/pages/docs/toc.js +0 -27
  149. package/plugins/console.js +0 -34
@@ -1,10 +1,12 @@
1
1
  <script>
2
- import ButtonDropdown from '@shell/components/ButtonDropdown';
3
- import Mount from '@shell/edit/workload/storage/Mount';
2
+ import { clone } from '@shell/utils/object';
4
3
  import { _VIEW } from '@shell/config/query-params';
5
- import ArrayListGrouped from '@shell/components/form/ArrayListGrouped';
6
4
  import { randomStr } from '@shell/utils/string';
7
5
 
6
+ import Mount from '@shell/edit/workload/storage/Mount';
7
+ import ButtonDropdown from '@shell/components/ButtonDropdown';
8
+ import ArrayListGrouped from '@shell/components/form/ArrayListGrouped';
9
+
8
10
  export default {
9
11
  name: 'ContainerMountPaths',
10
12
  components: {
@@ -109,7 +111,7 @@ export default {
109
111
  const names = volumeMounts.map(({ name }) => name);
110
112
 
111
113
  // Extract storage volumes to allow mutation, if matches mount map
112
- return this.value.volumes.filter((volume) => names.includes(volume.name));
114
+ return clone(this.value.volumes.filter((volume) => names.includes(volume.name)));
113
115
  },
114
116
 
115
117
  getSelectedContainerVolumes() {
@@ -118,7 +120,7 @@ export default {
118
120
  const names = volumeMounts.map(({ name }) => name);
119
121
 
120
122
  // Extract storage volumes to allow mutation, if matches mount map
121
- return this.value.volumes.filter((volume) => names.includes(volume.name));
123
+ return clone(this.value.volumes.filter((volume) => names.includes(volume.name)));
122
124
  },
123
125
 
124
126
  /**
@@ -33,7 +33,7 @@ describe('component: Storage', () => {
33
33
  t: (text: string) => text, // Mock i18n global function used as alternative to the getter
34
34
  $store: {
35
35
  getters: {
36
- 'i18n/t': jest.fn(),
36
+ 'i18n/t': jest.fn().mockImplementation((key: string) => key),
37
37
  'i18n/exists': jest.fn()
38
38
  }
39
39
  }
@@ -63,7 +63,7 @@ describe('component: Storage', () => {
63
63
  t: (text: string) => text, // Mock i18n global function used as alternative to the getter
64
64
  $store: {
65
65
  getters: {
66
- 'i18n/t': jest.fn(),
66
+ 'i18n/t': jest.fn().mockImplementation((key: string) => key),
67
67
  'i18n/exists': jest.fn()
68
68
  }
69
69
  }
@@ -0,0 +1,36 @@
1
+ import { createLocalVue, mount } from '@vue/test-utils';
2
+ import PVC from '@shell/edit/workload/storage/persistentVolumeClaim/persistentvolumeclaim.vue';
3
+
4
+ describe('component: PVC', () => {
5
+ // TODO: Enable test after allowing to test async data with either #9711 or #9322
6
+ // eslint-disable-next-line jest/no-disabled-tests
7
+ it.skip('should initialize storage class on create mode', async() => {
8
+ const localVue = createLocalVue();
9
+ const name = 'test';
10
+ const wrapper = mount(PVC, {
11
+ localVue,
12
+ propsData: {
13
+ savePvcHookName: '',
14
+ value: { spec: { resources: { requests: {} } } }
15
+ },
16
+ mocks: {
17
+ $store: {
18
+ getters: {
19
+ 'cluster/findAll': [{
20
+ metadata: {
21
+ name,
22
+ annotations: { 'storageclass.beta.kubernetes.io/is-default-class': true }
23
+ }
24
+ }],
25
+ 'i18n/t': jest.fn()
26
+ }
27
+ }
28
+ },
29
+ stubs: { LabeledSelect: { template: '<input />' } }
30
+ });
31
+
32
+ const inputElement = wrapper.find('[data-testid="storage-class-name"]').element as HTMLInputElement;
33
+
34
+ expect(inputElement.value).toBe(name);
35
+ });
36
+ });
@@ -48,6 +48,7 @@ export default {
48
48
 
49
49
  this.storageClasses = hash.storageClasses;
50
50
  this.persistentVolumes = hash.persistentVolumes;
51
+ this.$set(this.spec, 'storageClassName', (this.spec.storageClassName || this.defaultStorageClassName));
51
52
  },
52
53
 
53
54
  data() {
@@ -62,7 +63,7 @@ export default {
62
63
  return {
63
64
  storageClasses: [],
64
65
  persistentVolumes: [],
65
- createPV: true,
66
+ isCreatePV: true,
66
67
  spec,
67
68
  uniqueId: new Date().getTime() // Allows form state to be individually deleted
68
69
  };
@@ -73,6 +74,13 @@ export default {
73
74
  return this.storageClasses.map((sc) => sc.metadata.name);
74
75
  },
75
76
 
77
+ /**
78
+ * Required to initialize with default SC on creation
79
+ */
80
+ defaultStorageClassName() {
81
+ return this.storageClasses.find((sc) => sc.metadata?.annotations?.['storageclass.beta.kubernetes.io/is-default-class'] || sc.metadata?.annotations?.['storageclass.kubernetes.io/is-default-class'])?.metadata.name;
82
+ },
83
+
76
84
  availablePVs() {
77
85
  return this.persistentVolumes.reduce((total, each) => {
78
86
  if (each?.status?.phase === 'Available') {
@@ -91,12 +99,11 @@ export default {
91
99
  },
92
100
 
93
101
  watch: {
94
- createPV(neu, old) {
102
+ isCreatePV(neu) {
95
103
  if (neu) {
96
104
  delete this.spec.volumeName;
97
105
  this.spec.resources.requests.storage = null;
98
106
  } else {
99
- this.spec.storageClassName = '';
100
107
  this.spec.resources.requests.storage = null;
101
108
  }
102
109
  },
@@ -161,8 +168,8 @@ export default {
161
168
  <div class="row mb-10">
162
169
  <div class="col span-6">
163
170
  <RadioGroup
164
- v-model="createPV"
165
- name="createPV"
171
+ v-model="isCreatePV"
172
+ name="isCreatePV"
166
173
  :options="[true, false]"
167
174
  :labels="[t('persistentVolumeClaim.source.options.new'), t('persistentVolumeClaim.source.options.existing')]"
168
175
  :mode="mode"
@@ -170,8 +177,9 @@ export default {
170
177
  </div>
171
178
  <div class="col span-6">
172
179
  <LabeledSelect
173
- v-if="createPV"
180
+ v-if="isCreatePV"
174
181
  v-model="spec.storageClassName"
182
+ data-testid="storage-class-name"
175
183
  :mode="mode"
176
184
  :required="true"
177
185
  :label="t('persistentVolumeClaim.storageClass')"
@@ -221,7 +229,7 @@ export default {
221
229
  </div>
222
230
  </div>
223
231
  <div
224
- v-if="createPV"
232
+ v-if="isCreatePV"
225
233
  class="col span-6"
226
234
  >
227
235
  <UnitInput
package/initialize/App.js CHANGED
@@ -1,3 +1,5 @@
1
+ // Taken from @nuxt/vue-app/template/App.js
2
+
1
3
  import Vue from 'vue';
2
4
 
3
5
  import { getMatchedComponentsInstances, getChildrenComponentInstancesUsingFetch, promisify, globalHandleError } from '../utils/nuxt';
@@ -1,3 +1,5 @@
1
+ // Taken from @nuxt/vue-app/template/client.js
2
+
1
3
  import Vue from 'vue';
2
4
  import fetch from 'unfetch';
3
5
  import middleware from '../config/middleware.js';
@@ -21,6 +23,10 @@ import { createApp, NuxtError } from './index.js';
21
23
  import fetchMixin from '../mixins/fetch.client';
22
24
  import NuxtLink from '../components/nuxt/nuxt-link.client.js'; // should be included after ./index.js
23
25
 
26
+ // Mimic old @nuxt/vue-app/template/client.js
27
+ const isDev = process.env.dev;
28
+ const debug = isDev;
29
+
24
30
  // Fetch mixin
25
31
  if (!Vue.__nuxt__fetch__mixin__) {
26
32
  Vue.mixin(fetchMixin);
@@ -51,69 +57,71 @@ if ($config._app) {
51
57
 
52
58
  Object.assign(Vue.config, { silent: false, performance: true });
53
59
 
54
- const logs = NUXT.logs || [];
60
+ if (debug) {
61
+ const logs = NUXT.logs || [];
55
62
 
56
- if (logs.length > 0) {
57
- const ssrLogStyle = 'background: #2E495E;border-radius: 0.5em;color: white;font-weight: bold;padding: 2px 0.5em;';
63
+ if (logs.length > 0) {
64
+ const ssrLogStyle = 'background: #2E495E;border-radius: 0.5em;color: white;font-weight: bold;padding: 2px 0.5em;';
58
65
 
59
- console.group && console.group('%cNuxt SSR', ssrLogStyle); // eslint-disable-line no-console
60
- logs.forEach((logObj) => (console[logObj.type] || console.log)(...logObj.args)); // eslint-disable-line no-console
61
- delete NUXT.logs;
62
- console.groupEnd && console.groupEnd(); // eslint-disable-line no-console
63
- }
66
+ console.group && console.group('%cNuxt SSR', ssrLogStyle); // eslint-disable-line no-console
67
+ logs.forEach((logObj) => (console[logObj.type] || console.log)(...logObj.args)); // eslint-disable-line no-console
68
+ delete NUXT.logs;
69
+ console.groupEnd && console.groupEnd(); // eslint-disable-line no-console
70
+ }
64
71
 
65
- // Setup global Vue error handler
66
- if (!Vue.config.$nuxt) {
67
- const defaultErrorHandler = Vue.config.errorHandler;
72
+ // Setup global Vue error handler
73
+ if (!Vue.config.$nuxt) {
74
+ const defaultErrorHandler = Vue.config.errorHandler;
68
75
 
69
- Vue.config.errorHandler = async(err, vm, info, ...rest) => {
76
+ Vue.config.errorHandler = async(err, vm, info, ...rest) => {
70
77
  // Call other handler if exist
71
- let handled = null;
78
+ let handled = null;
72
79
 
73
- if (typeof defaultErrorHandler === 'function') {
74
- handled = defaultErrorHandler(err, vm, info, ...rest);
75
- }
76
- if (handled === true) {
77
- return handled;
78
- }
80
+ if (typeof defaultErrorHandler === 'function') {
81
+ handled = defaultErrorHandler(err, vm, info, ...rest);
82
+ }
83
+ if (handled === true) {
84
+ return handled;
85
+ }
79
86
 
80
- if (vm && vm.$root) {
81
- const nuxtApp = Object.keys(Vue.config.$nuxt)
82
- .find((nuxtInstance) => vm.$root[nuxtInstance]);
87
+ if (vm && vm.$root) {
88
+ const nuxtApp = Object.keys(Vue.config.$nuxt)
89
+ .find((nuxtInstance) => vm.$root[nuxtInstance]);
83
90
 
84
- // Show Nuxt Error Page
85
- if (nuxtApp && vm.$root[nuxtApp].error && info !== 'render function') {
86
- const currentApp = vm.$root[nuxtApp];
91
+ // Show Nuxt Error Page
92
+ if (nuxtApp && vm.$root[nuxtApp].error && info !== 'render function') {
93
+ const currentApp = vm.$root[nuxtApp];
87
94
 
88
- // Load error layout
89
- let layout = (NuxtError.options || NuxtError).layout;
95
+ // Load error layout
96
+ let layout = (NuxtError.options || NuxtError).layout;
90
97
 
91
- if (typeof layout === 'function') {
92
- layout = layout(currentApp.context);
93
- }
94
- if (layout) {
95
- await currentApp.loadLayout(layout).catch(() => {});
96
- }
97
- currentApp.setLayout(layout);
98
+ if (typeof layout === 'function') {
99
+ layout = layout(currentApp.context);
100
+ }
101
+ if (layout) {
102
+ await currentApp.loadLayout(layout).catch(() => {});
103
+ }
104
+ currentApp.setLayout(layout);
98
105
 
99
- currentApp.error(err);
106
+ currentApp.error(err);
107
+ }
100
108
  }
101
- }
102
109
 
103
- if (typeof defaultErrorHandler === 'function') {
104
- return handled;
105
- }
110
+ if (typeof defaultErrorHandler === 'function') {
111
+ return handled;
112
+ }
106
113
 
107
- // Log to console
108
- if (process.env.NODE_ENV !== 'production') {
109
- console.error(err); // eslint-disable-line no-console
110
- } else {
111
- console.error(err.message || err); // eslint-disable-line no-console
112
- }
113
- };
114
- Vue.config.$nuxt = {};
114
+ // Log to console
115
+ if (process.env.NODE_ENV !== 'production') {
116
+ console.error(err); // eslint-disable-line no-console
117
+ } else {
118
+ console.error(err.message || err); // eslint-disable-line no-console
119
+ }
120
+ };
121
+ Vue.config.$nuxt = {};
122
+ }
123
+ Vue.config.$nuxt.$nuxt = true;
115
124
  }
116
- Vue.config.$nuxt.$nuxt = true;
117
125
 
118
126
  const errorHandler = Vue.config.errorHandler || console.error; // eslint-disable-line no-console
119
127
 
@@ -623,7 +631,9 @@ function fixPrepatch(to, ___) {
623
631
  checkForErrors(this);
624
632
 
625
633
  // Hot reloading
626
- setTimeout(() => hotReloadAPI(this), 100);
634
+ if (isDev) {
635
+ setTimeout(() => hotReloadAPI(this), 100);
636
+ }
627
637
  });
628
638
  }
629
639
 
@@ -794,8 +804,10 @@ async function mountApp(__app) {
794
804
  // Call window.{{globals.readyCallback}} callbacks
795
805
  nuxtReady(_app);
796
806
 
797
- // Enable hot reloading
798
- hotReloadAPI(_app);
807
+ if (isDev) {
808
+ // Enable hot reloading
809
+ hotReloadAPI(_app);
810
+ }
799
811
  });
800
812
  };
801
813
 
@@ -1,3 +1,5 @@
1
+ // Taken from @nuxt/vue-app/template/index.js
2
+
1
3
  import Vue from 'vue';
2
4
  import Meta from 'vue-meta';
3
5
  import ClientOnly from 'vue-client-only';
@@ -34,8 +36,8 @@ import '../plugins/global-formatters';
34
36
  import '../plugins/trim-whitespace';
35
37
  import '../plugins/extend-router';
36
38
 
37
- import consolePlugin from '../plugins/console';
38
39
  import intNumber from '../plugins/int-number';
40
+ import positiveIntNumber from '../plugins/positive-int-number.js';
39
41
  import nuxtClientInit from '../plugins/nuxt-client-init';
40
42
  import replaceAll from '../plugins/replaceall';
41
43
  import backButton from '../plugins/back-button';
@@ -272,14 +274,14 @@ async function createApp(ssrContext, config = {}) {
272
274
  await axiosShell(app.context, inject);
273
275
  }
274
276
 
275
- if (process.client && typeof consolePlugin === 'function') {
276
- await consolePlugin(app.context, inject);
277
- }
278
-
279
277
  if (process.client && typeof intNumber === 'function') {
280
278
  await intNumber(app.context, inject);
281
279
  }
282
280
 
281
+ if (process.client && typeof positiveIntNumber === 'function') {
282
+ await positiveIntNumber(app.context, inject);
283
+ }
284
+
283
285
  if (process.client && typeof nuxtClientInit === 'function') {
284
286
  await nuxtClientInit(app.context, inject);
285
287
  }
@@ -70,7 +70,7 @@ export default {
70
70
 
71
71
  computed: {
72
72
  ...mapState(['managementReady', 'clusterReady']),
73
- ...mapGetters(['clusterId', 'currentProduct', 'isRancherInHarvester']),
73
+ ...mapGetters(['clusterId', 'currentProduct', 'isRancherInHarvester', 'showTopLevelMenu']),
74
74
 
75
75
  afterLoginRoute: mapPref(AFTER_LOGIN_ROUTE),
76
76
 
@@ -237,7 +237,7 @@ export default {
237
237
  <div
238
238
  v-if="managementReady"
239
239
  class="dashboard-content"
240
- :class="{[pinClass]: true}"
240
+ :class="{[pinClass]: true, 'dashboard-padding-left': showTopLevelMenu}"
241
241
  >
242
242
  <Header />
243
243
  <SideNav
@@ -318,6 +318,14 @@ export default {
318
318
  overflow-y: auto;
319
319
  min-height: 0px;
320
320
 
321
+ &.dashboard-padding-left {
322
+ padding-left: $app-bar-collapsed-width;
323
+
324
+ .overlay-content-mode {
325
+ left: calc(var(--nav-width) + $app-bar-collapsed-width);
326
+ }
327
+ }
328
+
321
329
  &.pin-right {
322
330
  grid-template-areas:
323
331
  "header header header"
package/layouts/home.vue CHANGED
@@ -8,7 +8,7 @@ import AwsComplianceBanner from '@shell/components/AwsComplianceBanner';
8
8
  import AzureWarning from '@shell/components/auth/AzureWarning';
9
9
  import BrowserTabVisibility from '@shell/mixins/browser-tab-visibility';
10
10
  import Inactivity from '@shell/components/Inactivity';
11
- import { mapState } from 'vuex';
11
+ import { mapState, mapGetters } from 'vuex';
12
12
 
13
13
  export default {
14
14
 
@@ -36,6 +36,7 @@ export default {
36
36
  computed: {
37
37
  themeShortcut: mapPref(THEME_SHORTCUT),
38
38
  ...mapState(['managementReady']),
39
+ ...mapGetters(['showTopLevelMenu']),
39
40
  },
40
41
 
41
42
  methods: {
@@ -57,7 +58,10 @@ export default {
57
58
  <AwsComplianceBanner />
58
59
  <AzureWarning />
59
60
 
60
- <div class="dashboard-content">
61
+ <div
62
+ class="dashboard-content"
63
+ :class="{'dashboard-padding-left': showTopLevelMenu}"
64
+ >
61
65
  <Header
62
66
  v-if="managementReady"
63
67
  :simple="true"
package/layouts/plain.vue CHANGED
@@ -12,6 +12,7 @@ import AwsComplianceBanner from '@shell/components/AwsComplianceBanner';
12
12
  import AzureWarning from '@shell/components/auth/AzureWarning';
13
13
  import BrowserTabVisibility from '@shell/mixins/browser-tab-visibility';
14
14
  import Inactivity from '@shell/components/Inactivity';
15
+ import { mapGetters } from 'vuex';
15
16
 
16
17
  export default {
17
18
 
@@ -40,7 +41,10 @@ export default {
40
41
  };
41
42
  },
42
43
 
43
- computed: { themeShortcut: mapPref(THEME_SHORTCUT) },
44
+ computed: {
45
+ themeShortcut: mapPref(THEME_SHORTCUT),
46
+ ...mapGetters(['showTopLevelMenu']),
47
+ },
44
48
 
45
49
  methods: {
46
50
  toggleTheme() {
@@ -59,7 +63,10 @@ export default {
59
63
  <AwsComplianceBanner />
60
64
  <AzureWarning />
61
65
 
62
- <div class="dashboard-content">
66
+ <div
67
+ class="dashboard-content"
68
+ :class="{'dashboard-padding-left': showTopLevelMenu}"
69
+ >
63
70
  <Header :simple="true" />
64
71
  <main class="main-layout">
65
72
  <IndentedPanel class="pt-20">
@@ -1,7 +1,7 @@
1
1
  <script>
2
2
  import FleetClusters from '@shell/components/fleet/FleetClusters';
3
3
  import { FLEET, MANAGEMENT } from '@shell/config/types';
4
- import { isHarvesterCluster } from '@shell/utils/cluster';
4
+ import { filterOnlyKubernetesClusters } from '@shell/utils/cluster';
5
5
  import { Banner } from '@components/Banner';
6
6
  import ResourceFetch from '@shell/mixins/resource-fetch';
7
7
 
@@ -56,7 +56,7 @@ export default {
56
56
  },
57
57
 
58
58
  filteredRows() {
59
- return this.fleetClusters.filter((c) => !isHarvesterCluster(c));
59
+ return filterOnlyKubernetesClusters(this.fleetClusters, this.$store);
60
60
  },
61
61
 
62
62
  fleetClusters() {
@@ -82,7 +82,7 @@ export default {
82
82
  },
83
83
 
84
84
  enableRowActions() {
85
- const schema = this.$store.getters[`management/schemaFor`](MANAGEMENT.SETTING);
85
+ const schema = this.$store.getters[`management/schemaFor`](MANAGEMENT.FEATURE);
86
86
 
87
87
  return schema?.resourceMethods?.includes('PUT');
88
88
  },
@@ -303,6 +303,7 @@ export default {
303
303
  },
304
304
 
305
305
  'value.region'() {
306
+ this.updateNetwork();
306
307
  this.$fetch();
307
308
  },
308
309
 
@@ -14,7 +14,7 @@ import ArrayListSelect from '@shell/components/form/ArrayListSelect';
14
14
  import YamlEditor from '@shell/components/YamlEditor';
15
15
  import { get, set } from '@shell/utils/object';
16
16
  import { integerString, keyValueStrings } from '@shell/utils/computed';
17
- import { _CREATE } from '@shell/config/query-params';
17
+ import { _CREATE, _EDIT, _VIEW } from '@shell/config/query-params';
18
18
 
19
19
  const SENTINEL = '__SENTINEL__';
20
20
  const VAPP_MODE = {
@@ -136,6 +136,11 @@ function createOptionHelpers(name) {
136
136
  };
137
137
  }
138
138
 
139
+ const errorActions = Object.freeze({
140
+ CREATE: 'create',
141
+ DELETE: 'delete',
142
+ });
143
+
139
144
  export default {
140
145
  components: {
141
146
  ArrayListSelect, Card, KeyValue, Loading, LabeledInput, LabeledSelect, Banner, UnitInput, RadioGroup, YamlEditor
@@ -144,6 +149,10 @@ export default {
144
149
  mixins: [CreateEditView],
145
150
 
146
151
  props: {
152
+ poolId: {
153
+ type: String,
154
+ default: '',
155
+ },
147
156
  credentialId: {
148
157
  type: String,
149
158
  required: true,
@@ -246,6 +255,7 @@ export default {
246
255
  vAppOptions,
247
256
  vappMode: getInitialVappMode(this.value),
248
257
  osOptions: OS_OPTIONS,
258
+ validationErrors: {},
249
259
  };
250
260
  },
251
261
 
@@ -347,6 +357,9 @@ export default {
347
357
  }
348
358
 
349
359
  this.updateVappOptions(INITIAL_VAPP_OPTIONS);
360
+ },
361
+ validationErrors(value) {
362
+ this.$emit('error', value);
350
363
  }
351
364
  },
352
365
 
@@ -384,9 +397,17 @@ export default {
384
397
  const valueInContent = content.find((c) => c.value === this.value.datacenter );
385
398
 
386
399
  if (!valueInContent) {
387
- set(this.value, 'datacenter', options[0]);
388
- set(this.value, 'cloneFrom', undefined);
389
- set(this.value, 'useDataStoreCluster', false);
400
+ if (this.mode === _CREATE) {
401
+ set(this.value, 'datacenter', options[0]);
402
+ set(this.value, 'cloneFrom', undefined);
403
+ set(this.value, 'useDataStoreCluster', false);
404
+ }
405
+
406
+ if ([_EDIT, _VIEW].includes(this.mode)) {
407
+ this.manageErrors(errorActions.CREATE, 'datacenter');
408
+ }
409
+ } else {
410
+ this.manageErrors(errorActions.DELETE, 'datacenter');
390
411
  }
391
412
 
392
413
  set(this, 'dataCentersResults', content);
@@ -582,11 +603,19 @@ export default {
582
603
  };
583
604
 
584
605
  if (!isValueInContent()) {
585
- const value = isArray ? [] : content[0]?.value;
606
+ if (this.mode === _CREATE) {
607
+ const value = isArray ? [] : content[0]?.value;
608
+
609
+ if (value !== SENTINEL) {
610
+ set(this.value, key, value);
611
+ }
612
+ }
586
613
 
587
- if (value !== SENTINEL) {
588
- set(this.value, key, value);
614
+ if ([_EDIT, _VIEW].includes(this.mode)) {
615
+ this.manageErrors(errorActions.CREATE, key);
589
616
  }
617
+ } else {
618
+ this.manageErrors(errorActions.DELETE, key);
590
619
  }
591
620
  },
592
621
 
@@ -654,6 +683,18 @@ export default {
654
683
  set(this.value, 'vappProperty', opts.vappProperty);
655
684
  this.initKeyValueParams('value.vappProperty', 'initVappArray');
656
685
  },
686
+
687
+ manageErrors(action = errorActions.CREATE, key) {
688
+ if (action === errorActions.CREATE) {
689
+ const keys = [key, ...(this.validationErrors[this.poolId] || [])];
690
+
691
+ this.validationErrors = Object.assign({}, this.validationErrors, { [this.poolId]: keys });
692
+ }
693
+
694
+ if (action === errorActions.DELETE && this.validationErrors[this.poolId]) {
695
+ this.validationErrors = Object.assign({}, this.validationErrors, { [this.poolId]: this.validationErrors[this.poolId].filter((x) => x === key) }) ;
696
+ }
697
+ },
657
698
  }
658
699
  };
659
700
  </script>
package/mixins/brand.js CHANGED
@@ -141,14 +141,6 @@ export default {
141
141
  }).then((setting) => setting.save());
142
142
  }
143
143
  }
144
- } else if (!neu) {
145
- const brandSetting = findBy(this.globalSettings, 'id', SETTING.BRAND);
146
-
147
- if (brandSetting && brandSetting.value !== '') {
148
- // 2) There should not be a brand... but there is a brand setting
149
- brandSetting.value = '';
150
- brandSetting.save();
151
- }
152
144
  }
153
145
  }
154
146
  },
@@ -25,14 +25,14 @@ export default {
25
25
  },
26
26
 
27
27
  async applyHooks(key, ...args) {
28
- if ( !key ) {
28
+ if (!key) {
29
29
  throw new Error('Must specify key');
30
30
  }
31
31
 
32
32
  const hooks = sortBy(this[key] || [], ['priority', 'name']);
33
33
  const out = {};
34
34
 
35
- for ( const x of hooks ) {
35
+ for (const x of hooks) {
36
36
  console.debug('Applying hook', x.name); // eslint-disable-line no-console
37
37
  out[x.name] = await x.fn.apply(x.fnContext || this, args);
38
38
  }
@@ -114,8 +114,8 @@ export default {
114
114
  // Detect and resolve conflicts from a 409 response.
115
115
  // If they are resolved, return a false-y value
116
116
  // Else they can't be resolved, return an array of errors to show to the user.
117
- conflict() {
118
- return handleConflict(this.initialValue.toJSON(), this.value, this.liveValue, this.$store.getters, this.$store);
117
+ async conflict() {
118
+ return await handleConflict(this.initialValue.toJSON(), this.value, this.liveValue, this.$store.getters, this.$store, this.storeOverride || this.$store.getters['currentStore'](this.value.type));
119
119
  },
120
120
 
121
121
  async save(buttonDone, url, depth = 0) {
@@ -159,7 +159,7 @@ export default {
159
159
  } catch (err) {
160
160
  // Conflict, the resource being edited has changed since starting editing
161
161
  if ( err.status === 409 && depth === 0 && this.isEdit) {
162
- const errors = this.conflict();
162
+ const errors = await this.conflict();
163
163
 
164
164
  if ( errors === false ) {
165
165
  // It was automatically figured out, save again