@rancher/shell 3.0.5-rc.1 → 3.0.5-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.
Files changed (280) hide show
  1. package/assets/data/aws-regions.json +2 -0
  2. package/assets/images/providers/sks.svg +1 -0
  3. package/assets/styles/base/_helpers.scss +4 -0
  4. package/assets/styles/base/_variables.scss +1 -0
  5. package/assets/styles/global/_layout.scss +0 -1
  6. package/assets/translations/en-us.yaml +92 -34
  7. package/assets/translations/zh-hans.yaml +4 -13
  8. package/chart/monitoring/index.vue +4 -2
  9. package/components/ActionDropdownShell.vue +71 -0
  10. package/components/AppModal.vue +18 -4
  11. package/components/AsyncButton.vue +2 -0
  12. package/components/CodeMirror.vue +3 -3
  13. package/components/CommunityLinks.vue +3 -58
  14. package/components/CruResource.vue +109 -16
  15. package/components/ExplorerProjectsNamespaces.vue +19 -6
  16. package/components/FixedBanner.vue +19 -5
  17. package/components/GlobalRoleBindings.vue +5 -1
  18. package/components/GrowlManager.vue +1 -0
  19. package/components/LandingPagePreference.vue +2 -0
  20. package/components/LocaleSelector.vue +1 -1
  21. package/components/ModalManager.vue +55 -0
  22. package/components/PaginatedResourceTable.vue +7 -0
  23. package/components/PromptModal.vue +47 -8
  24. package/components/ResourceDetail/Masthead.vue +38 -13
  25. package/components/ResourceDetail/__tests__/Masthead.test.ts +5 -1
  26. package/components/ResourceDetail/index.vue +47 -12
  27. package/components/ResourceList/index.vue +2 -1
  28. package/components/ResourceTable.vue +54 -19
  29. package/components/SideNav.vue +5 -1
  30. package/components/SlideInPanelManager.vue +125 -0
  31. package/components/SortableTable/THead.vue +5 -2
  32. package/components/SortableTable/actions.js +1 -1
  33. package/components/SortableTable/index.vue +54 -40
  34. package/components/SortableTable/paging.js +16 -19
  35. package/components/SortableTable/selection.js +1 -12
  36. package/components/Tabbed/index.vue +6 -0
  37. package/components/Wizard.vue +2 -2
  38. package/components/__tests__/AsyncButton.test.ts +39 -0
  39. package/components/__tests__/CruResource.test.ts +63 -0
  40. package/components/__tests__/ModalManager.spec.ts +176 -0
  41. package/components/__tests__/PromptModal.test.ts +146 -0
  42. package/components/__tests__/SlideInPanelManager.spec.ts +166 -0
  43. package/components/auth/AuthBanner.vue +13 -11
  44. package/components/auth/Principal.vue +1 -0
  45. package/components/auth/login/ldap.vue +1 -1
  46. package/components/fleet/FleetResources.vue +21 -6
  47. package/components/form/ArrayList.vue +138 -118
  48. package/components/form/BannerSettings.vue +149 -85
  49. package/components/form/ColorInput.vue +35 -6
  50. package/components/form/EnvVars.vue +1 -0
  51. package/components/form/KeyValue.vue +10 -7
  52. package/components/form/LabeledSelect.vue +25 -23
  53. package/components/form/MatchExpressions.vue +9 -2
  54. package/components/form/NameNsDescription.vue +6 -2
  55. package/components/form/NotificationSettings.vue +15 -1
  56. package/components/form/Password.vue +1 -0
  57. package/components/form/Probe.vue +1 -0
  58. package/components/form/ResourceSelector.vue +26 -23
  59. package/components/form/ResourceTabs/index.vue +2 -1
  60. package/components/form/SSHKnownHosts/__tests__/KnownHostsEditDialog.test.ts +15 -34
  61. package/components/form/SSHKnownHosts/index.vue +14 -11
  62. package/components/form/Select.vue +8 -15
  63. package/components/form/UnitInput.vue +13 -0
  64. package/components/form/ValueFromResource.vue +12 -12
  65. package/components/form/__tests__/ArrayList.test.ts +34 -2
  66. package/components/form/__tests__/ColorInput.test.ts +35 -0
  67. package/components/form/__tests__/KeyValue.test.ts +36 -0
  68. package/components/form/__tests__/LabeledSelect.test.ts +73 -0
  69. package/components/form/__tests__/SSHKnownHosts.test.ts +11 -2
  70. package/components/form/__tests__/Select.test.ts +34 -1
  71. package/components/form/__tests__/UnitInput.test.ts +23 -1
  72. package/components/formatter/ClusterLink.vue +5 -8
  73. package/components/formatter/Description.vue +30 -0
  74. package/components/formatter/__tests__/ClusterLink.test.ts +2 -32
  75. package/components/nav/Group.vue +12 -4
  76. package/components/nav/Header.vue +16 -43
  77. package/components/nav/NamespaceFilter.vue +134 -86
  78. package/components/nav/TopLevelMenu.vue +4 -5
  79. package/components/nav/WindowManager/ContainerLogs.vue +87 -61
  80. package/components/nav/WindowManager/ContainerLogsActions.vue +76 -0
  81. package/components/nav/WindowManager/index.vue +1 -0
  82. package/components/templates/default.vue +6 -3
  83. package/components/templates/home.vue +6 -0
  84. package/components/templates/plain.vue +6 -3
  85. package/composables/focusTrap.ts +12 -4
  86. package/config/product/explorer.js +16 -13
  87. package/config/product/manager.js +1 -28
  88. package/config/settings.ts +11 -13
  89. package/config/store.js +4 -0
  90. package/config/table-headers.js +7 -5
  91. package/config/uiplugins.js +5 -1
  92. package/core/types.ts +7 -6
  93. package/detail/catalog.cattle.io.app.vue +5 -1
  94. package/detail/fleet.cattle.io.bundle.vue +70 -6
  95. package/detail/fleet.cattle.io.gitrepo.vue +1 -1
  96. package/detail/namespace.vue +0 -3
  97. package/detail/node.vue +17 -13
  98. package/detail/provisioning.cattle.io.cluster.vue +85 -9
  99. package/detail/service.vue +0 -1
  100. package/detail/workload/index.vue +21 -34
  101. package/dialog/AddCustomBadgeDialog.vue +0 -1
  102. package/{pages/c/_cluster/uiplugins/AddExtensionRepos.vue → dialog/AddExtensionReposDialog.vue} +72 -42
  103. package/dialog/AssignToDialog.vue +176 -0
  104. package/dialog/ChangePasswordDialog.vue +106 -0
  105. package/{pages/c/_cluster/uiplugins/DeveloperInstallDialog.vue → dialog/DeveloperLoadExtensionDialog.vue} +74 -71
  106. package/dialog/DisableAuthProviderDialog.vue +101 -0
  107. package/dialog/DrainNode.vue +1 -1
  108. package/{pages/c/_cluster/uiplugins/CatalogList/CatalogLoadDialog.vue → dialog/ExtensionCatalogInstallDialog.vue} +100 -88
  109. package/{pages/c/_cluster/uiplugins/CatalogList/CatalogUninstallDialog.vue → dialog/ExtensionCatalogUninstallDialog.vue} +83 -65
  110. package/dialog/FeatureFlagListDialog.vue +288 -0
  111. package/dialog/ForceMachineRemoveDialog.vue +1 -1
  112. package/{components/Import.vue → dialog/ImportDialog.vue} +0 -5
  113. package/{pages/c/_cluster/uiplugins/InstallDialog.vue → dialog/InstallExtensionDialog.vue} +124 -106
  114. package/{components/form/SSHKnownHosts → dialog}/KnownHostsEditDialog.vue +52 -62
  115. package/dialog/MoveNamespaceDialog.vue +157 -0
  116. package/dialog/ScalePoolDownDialog.vue +1 -1
  117. package/{components/nav/Jump.vue → dialog/SearchDialog.vue} +34 -14
  118. package/{pages/c/_cluster/uiplugins/UninstallDialog.vue → dialog/UninstallExtensionDialog.vue} +67 -58
  119. package/dialog/WechatDialog.vue +57 -0
  120. package/edit/__tests__/service.test.ts +2 -1
  121. package/edit/auth/azuread.vue +1 -1
  122. package/edit/auth/github.vue +1 -1
  123. package/edit/auth/googleoauth.vue +1 -1
  124. package/edit/auth/ldap/index.vue +1 -1
  125. package/edit/auth/oidc.vue +1 -1
  126. package/edit/auth/saml.vue +1 -1
  127. package/edit/cloudcredential.vue +24 -10
  128. package/edit/management.cattle.io.user.vue +28 -3
  129. package/edit/namespace.vue +1 -4
  130. package/edit/networking.k8s.io.networkpolicy/PolicyRule.vue +3 -14
  131. package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +57 -62
  132. package/edit/networking.k8s.io.networkpolicy/PolicyRules.vue +3 -14
  133. package/edit/networking.k8s.io.networkpolicy/__tests__/PolicyRuleTarget.test.ts +72 -41
  134. package/edit/networking.k8s.io.networkpolicy/__tests__/utils/mock.json +17 -1
  135. package/edit/networking.k8s.io.networkpolicy/index.vue +18 -30
  136. package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +4 -1
  137. package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +26 -10
  138. package/edit/provisioning.cattle.io.cluster/__tests__/Advanced.test.ts +8 -8
  139. package/edit/provisioning.cattle.io.cluster/__tests__/DirectoryConfig.test.ts +26 -12
  140. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +66 -0
  141. package/edit/provisioning.cattle.io.cluster/__tests__/utils/rke2-test-data.ts +58 -0
  142. package/edit/provisioning.cattle.io.cluster/index.vue +21 -73
  143. package/edit/provisioning.cattle.io.cluster/rke2.vue +24 -7
  144. package/edit/provisioning.cattle.io.cluster/tabs/DirectoryConfig.vue +5 -3
  145. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +4 -1
  146. package/edit/service.vue +13 -28
  147. package/initialize/install-plugins.js +2 -1
  148. package/list/harvesterhci.io.management.cluster.vue +4 -1
  149. package/list/management.cattle.io.feature.vue +4 -288
  150. package/list/workload.vue +6 -1
  151. package/machine-config/azure.vue +16 -4
  152. package/mixins/resource-fetch-api-pagination.js +55 -43
  153. package/mixins/resource-fetch.js +14 -5
  154. package/mixins/vue-select-overrides.js +0 -4
  155. package/models/__tests__/workload.test.ts +1 -0
  156. package/models/cluster/node.js +1 -0
  157. package/models/cluster.js +32 -2
  158. package/models/fleet.cattle.io.cluster.js +8 -2
  159. package/models/fleet.cattle.io.gitrepo.js +8 -34
  160. package/models/management.cattle.io.cluster.js +0 -20
  161. package/models/management.cattle.io.feature.js +7 -1
  162. package/models/management.cattle.io.node.js +7 -22
  163. package/models/management.cattle.io.nodepool.js +12 -0
  164. package/models/namespace.js +12 -1
  165. package/models/provisioning.cattle.io.cluster.js +18 -64
  166. package/models/service.js +24 -9
  167. package/models/workload.js +70 -31
  168. package/package.json +1 -1
  169. package/pages/about.vue +13 -3
  170. package/pages/account/index.vue +12 -5
  171. package/pages/auth/login.vue +7 -4
  172. package/pages/auth/setup.vue +1 -0
  173. package/pages/auth/verify.vue +9 -7
  174. package/pages/c/_cluster/apps/charts/install.vue +25 -26
  175. package/pages/c/_cluster/auth/config/index.vue +10 -12
  176. package/pages/c/_cluster/explorer/EventsTable.vue +38 -33
  177. package/pages/c/_cluster/explorer/index.vue +28 -15
  178. package/pages/c/_cluster/istio/index.vue +2 -2
  179. package/pages/c/_cluster/longhorn/index.vue +3 -3
  180. package/pages/c/_cluster/monitoring/index.vue +1 -1
  181. package/pages/c/_cluster/monitoring/monitor/_namespace/_id.vue +4 -2
  182. package/pages/c/_cluster/monitoring/monitor/create.vue +4 -2
  183. package/pages/c/_cluster/monitoring/route-receiver/_id.vue +4 -2
  184. package/pages/c/_cluster/monitoring/route-receiver/create.vue +5 -2
  185. package/pages/c/_cluster/neuvector/index.vue +1 -1
  186. package/pages/c/_cluster/settings/banners.vue +60 -5
  187. package/pages/c/_cluster/settings/performance.vue +7 -26
  188. package/pages/c/_cluster/uiplugins/CatalogList/index.vue +8 -10
  189. package/pages/c/_cluster/uiplugins/__tests__/AddExtensionRepos.test.ts +4 -7
  190. package/pages/c/_cluster/uiplugins/index.vue +98 -55
  191. package/pages/diagnostic.vue +12 -9
  192. package/pages/fail-whale.vue +8 -5
  193. package/pages/home.vue +11 -52
  194. package/pages/prefs.vue +7 -6
  195. package/plugins/clean-html.js +2 -0
  196. package/plugins/dashboard-store/__tests__/actions.test.ts +4 -1
  197. package/plugins/dashboard-store/actions.js +122 -21
  198. package/plugins/dashboard-store/getters.js +74 -3
  199. package/plugins/dashboard-store/mutations.js +10 -5
  200. package/plugins/dashboard-store/resource-class.js +23 -3
  201. package/plugins/internal-api/index.ts +37 -0
  202. package/plugins/internal-api/shared/base-api.ts +13 -0
  203. package/plugins/internal-api/shell/shell.api.ts +108 -0
  204. package/plugins/steve/__tests__/getters.test.ts +18 -11
  205. package/plugins/steve/__tests__/steve-class.test.ts +1 -0
  206. package/plugins/steve/actions.js +34 -24
  207. package/plugins/steve/getters.js +39 -10
  208. package/plugins/steve/steve-class.js +5 -0
  209. package/plugins/steve/steve-pagination-utils.ts +199 -37
  210. package/plugins/steve/worker/web-worker.advanced.js +3 -1
  211. package/public/index.html +1 -0
  212. package/rancher-components/Banner/Banner.test.ts +51 -3
  213. package/rancher-components/Banner/Banner.vue +28 -6
  214. package/rancher-components/Card/Card.vue +1 -1
  215. package/rancher-components/Form/Checkbox/Checkbox.test.ts +59 -1
  216. package/rancher-components/Form/Checkbox/Checkbox.vue +27 -3
  217. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +51 -0
  218. package/rancher-components/Form/LabeledInput/LabeledInput.vue +20 -2
  219. package/rancher-components/Form/Radio/RadioButton.test.ts +36 -1
  220. package/rancher-components/Form/Radio/RadioButton.vue +20 -4
  221. package/rancher-components/Form/Radio/RadioGroup.test.ts +60 -0
  222. package/rancher-components/Form/Radio/RadioGroup.vue +75 -35
  223. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.test.ts +17 -0
  224. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +26 -1
  225. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +10 -1
  226. package/rancher-components/RcButton/RcButton.vue +2 -1
  227. package/rancher-components/RcButton/types.ts +1 -0
  228. package/rancher-components/RcDropdown/RcDropdown.vue +17 -6
  229. package/rancher-components/RcDropdown/RcDropdownItem.vue +3 -56
  230. package/rancher-components/RcDropdown/RcDropdownItemCheckbox.vue +68 -0
  231. package/rancher-components/RcDropdown/RcDropdownItemSelect.vue +92 -0
  232. package/rancher-components/RcDropdown/index.ts +2 -0
  233. package/rancher-components/RcDropdown/useDropdownItem.ts +63 -0
  234. package/scripts/extension/bundle +20 -0
  235. package/scripts/extension/helm/charts/ui-plugin-server/templates/cr.yaml +2 -1
  236. package/scripts/extension/helm/charts/ui-plugin-server/values.yaml +2 -0
  237. package/scripts/extension/helmpatch +44 -31
  238. package/scripts/extension/publish +12 -13
  239. package/scripts/typegen.sh +2 -4
  240. package/store/action-menu.js +26 -56
  241. package/store/features.js +0 -1
  242. package/store/index.js +5 -0
  243. package/store/modal.ts +71 -0
  244. package/store/slideInPanel.ts +47 -0
  245. package/store/type-map.js +8 -1
  246. package/store/type-map.utils.ts +49 -6
  247. package/types/fleet.d.ts +1 -1
  248. package/types/global-vue.d.ts +5 -0
  249. package/types/internal-api/shell/growl.d.ts +25 -0
  250. package/types/internal-api/shell/modal.d.ts +77 -0
  251. package/types/internal-api/shell/slideIn.d.ts +15 -0
  252. package/types/kube/kube-api.ts +22 -0
  253. package/types/resources/fleet.d.ts +0 -14
  254. package/types/resources/settings.d.ts +0 -4
  255. package/types/shell/index.d.ts +375 -306
  256. package/types/store/dashboard-store.types.ts +24 -1
  257. package/types/store/pagination.types.ts +19 -2
  258. package/types/vue-shim.d.ts +4 -1
  259. package/utils/__mocks__/tabbable.js +13 -0
  260. package/utils/__tests__/object.test.ts +38 -4
  261. package/utils/cluster.js +24 -20
  262. package/utils/fleet.ts +15 -73
  263. package/utils/grafana.js +1 -0
  264. package/utils/object.js +36 -5
  265. package/utils/pagination-utils.ts +6 -2
  266. package/utils/perf-setting.utils.ts +28 -0
  267. package/utils/selector-typed.ts +205 -0
  268. package/utils/selector.js +29 -6
  269. package/utils/uiplugins.ts +10 -6
  270. package/utils/v-sphere.ts +5 -1
  271. package/utils/validators/formRules/__tests__/index.test.ts +10 -1
  272. package/utils/validators/formRules/index.ts +27 -3
  273. package/components/AssignTo.vue +0 -199
  274. package/components/DisableAuthProviderModal.vue +0 -115
  275. package/components/MoveModal.vue +0 -167
  276. package/components/PromptChangePassword.vue +0 -123
  277. package/components/fleet/FleetBundleResources.vue +0 -86
  278. package/components/formatter/RKETemplateName.vue +0 -37
  279. package/dialog/SaveAsRKETemplateDialog.vue +0 -139
  280. package/types/vue-shim.d +0 -20
@@ -1,6 +1,5 @@
1
1
  <script>
2
2
  import SimpleBox from '@shell/components/SimpleBox';
3
- import AppModal from '@shell/components/AppModal.vue';
4
3
  import Closeable from '@shell/mixins/closeable';
5
4
  import { MANAGEMENT } from '@shell/config/types';
6
5
  import { SETTING } from '@shell/config/settings';
@@ -9,11 +8,11 @@ import { isRancherPrime } from '@shell/config/version';
9
8
  import { fetchLinks } from '@shell/config/home-links';
10
9
  import { processLink } from '@shell/plugins/clean-html';
11
10
 
12
- // i18n-ignore footer.wechat.title, footer.wechat.modalText, footer.wechat.modalText2
11
+ // i18n-ignore footer.wechat.title
13
12
  export default {
14
13
  name: 'CommunityLinks',
15
14
 
16
- components: { SimpleBox, AppModal },
15
+ components: { SimpleBox },
17
16
 
18
17
  props: {
19
18
  linkOptions: {
@@ -87,10 +86,7 @@ export default {
87
86
  },
88
87
  methods: {
89
88
  show() {
90
- this.showWeChatModal = true;
91
- },
92
- close() {
93
- this.showWeChatModal = false;
89
+ this.$store.dispatch('management/promptModal', { component: 'WechatDialog' });
94
90
  }
95
91
  },
96
92
  };
@@ -146,32 +142,6 @@ export default {
146
142
  </a>
147
143
  </div>
148
144
  </SimpleBox>
149
- <app-modal
150
- v-if="showWeChatModal"
151
- name="wechat-modal"
152
- height="auto"
153
- :width="640"
154
- :trigger-focus-trap="true"
155
- @close="close"
156
- >
157
- <div class="wechat-modal">
158
- <h1>{{ t('footer.wechat.modalText') }}</h1>
159
- <h1>{{ t('footer.wechat.modalText2') }}</h1>
160
- <div class="qr-img" />
161
- <div>
162
- <button
163
- class="btn role-primary"
164
- tabindex="0"
165
- :aria-label="t('generic.close')"
166
- role="button"
167
- @click="close"
168
- @keydown.enter.stop
169
- >
170
- {{ t('generic.close') }}
171
- </button>
172
- </div>
173
- </div>
174
- </app-modal>
175
145
  </div>
176
146
  </template>
177
147
 
@@ -188,29 +158,4 @@ export default {
188
158
  .support-link:not(:last-child) {
189
159
  margin-bottom: 15px;
190
160
  }
191
-
192
- .wechat-modal {
193
- margin: 60px;
194
- display: flex;
195
- flex-direction: column;
196
- align-items: center;
197
- }
198
-
199
- .link {
200
- cursor: pointer;
201
- }
202
-
203
- .btn {
204
- margin: 20px auto 0;
205
- }
206
-
207
- .qr-img {
208
- background-image: url('../assets/images/wechat-qr-code.jpg');
209
- background-repeat: no-repeat;
210
- background-size: cover;
211
- background-position: center center;
212
- height: 128px;
213
- width: 128px;
214
- margin: 15px auto 10px;
215
- }
216
161
  </style>
@@ -265,7 +265,7 @@ export default {
265
265
  return !this.errors ? {} : this.errorsMap || this.errors.reduce((acc, error) => ({
266
266
  ...acc,
267
267
  [error]: {
268
- message: error,
268
+ message: this.formatError(error),
269
269
  icon: null
270
270
  }
271
271
  }), {});
@@ -371,19 +371,23 @@ export default {
371
371
  },
372
372
 
373
373
  async clickSave(buttonDone) {
374
- try {
375
- await this.createNamespaceIfNeeded();
376
-
377
- // If the attempt to create the new namespace
378
- // is successful, save the resource.
379
- this.$emit('finish', buttonDone);
380
- } catch (err) {
374
+ if (this.createNamespace) {
375
+ try {
376
+ await this.createNamespaceIfNeeded();
377
+ } catch (err) {
381
378
  // After the attempt to create the namespace,
382
379
  // show any applicable errors if the namespace is
383
380
  // invalid.
384
- this.$emit('error', exceptionToErrorsArray(err.message));
385
- buttonDone(false);
381
+ this.$emit('error', exceptionToErrorsArray(err.message));
382
+ buttonDone(false);
383
+
384
+ return;
385
+ }
386
386
  }
387
+
388
+ // If the attempt to create the new namespace
389
+ // was successful or no ns needs to be created, save the resource.
390
+ this.$emit('finish', buttonDone);
387
391
  },
388
392
 
389
393
  save() {
@@ -395,17 +399,13 @@ export default {
395
399
  const newNamespaceName = get(this.resource, this.namespaceKey);
396
400
  let namespaceAlreadyExists = false;
397
401
 
398
- if (!this.createNamespace) {
399
- return;
400
- }
401
-
402
402
  try {
403
403
  // This is in a try-catch block because the call to fetch
404
404
  // a namespace throws an error if the namespace is not found.
405
405
  namespaceAlreadyExists = !!(await this.$store.dispatch(`${ inStore }/find`, { type: NAMESPACE, id: newNamespaceName }));
406
406
  } catch {}
407
407
 
408
- if (this.createNamespace && !namespaceAlreadyExists) {
408
+ if (!namespaceAlreadyExists) {
409
409
  try {
410
410
  const newNamespace = await this.$store.dispatch(`${ inStore }/createNamespace`, { name: newNamespaceName }, { root: true });
411
411
 
@@ -427,6 +427,94 @@ export default {
427
427
 
428
428
  shouldProvideSlot(slot) {
429
429
  return slot !== 'default' && typeof this.$slots[slot] === 'function';
430
+ },
431
+
432
+ formatError(err) {
433
+ if ( typeof err === 'string') {
434
+ return err;
435
+ }
436
+
437
+ if ( err?.code === 'ActionNotAvailable' ) {
438
+ return this.t('errors.actionNotAvailable');
439
+ }
440
+ const msg = !!err?.message ? err.message : '';
441
+ let messageDetail = '';
442
+
443
+ if (!!err?.message && !!err.detail) {
444
+ messageDetail = this.t('errors.messageAndDetail', { message: err.message, detail: err.detail });
445
+ } else if (!!err?.message || !!err.detail) {
446
+ const val = err.message ? err.message : err.detail;
447
+
448
+ messageDetail = this.t('errors.messageOrDetail', { val });
449
+ }
450
+
451
+ if ( err?.status === 422 ) {
452
+ const name = err?.fieldName;
453
+ const code = err?.code;
454
+ let codeExplanation = '';
455
+
456
+ switch ( err?.code ) {
457
+ case 'MissingRequired':
458
+ codeExplanation = this.t('errors.missingRequired'); break;
459
+ case 'NotUnique':
460
+ codeExplanation = this.t('errors.notUnique'); break;
461
+ case 'NotNullable':
462
+ codeExplanation = this.t('errors.notNullable'); break;
463
+ case 'InvalidOption':
464
+ codeExplanation = this.t('errors.invalidOption'); break;
465
+ case 'InvalidCharacters':
466
+ codeExplanation = this.t('errors.invalidCharacters'); break;
467
+ case 'MinLengthExceeded':
468
+ codeExplanation = this.t('errors.minLengthExceeded'); break;
469
+ case 'MaxLengthExceeded':
470
+ codeExplanation = this.t('errors.maxLengthExceeded'); break;
471
+ case 'MinLimitExceeded':
472
+ codeExplanation = this.t('errors.minLimitExceeded'); break;
473
+ case 'MaxLimitExceded':
474
+ codeExplanation = this.t('errors.maxLimitExceded'); break;
475
+ }
476
+
477
+ if (!!name) {
478
+ if (!!codeExplanation) {
479
+ if (!!messageDetail) {
480
+ return this.t('errors.failedInApi.withName.withCodeExplanation.withMessageDetail', {
481
+ name, codeExplanation, messageDetail
482
+ });
483
+ }
484
+
485
+ return this.t('errors.failedInApi.withName.withCodeExplanation.withoutMessageDetail', { name, codeExplanation });
486
+ }
487
+ if (!!messageDetail) {
488
+ return this.t('errors.failedInApi.withName.withMessageDetail', { name, messageDetail });
489
+ }
490
+
491
+ return this.t('errors.failedInApi.withName.withoutAnythingElse', { name });
492
+ } else {
493
+ if (!!messageDetail) {
494
+ if (!!codeExplanation) {
495
+ return this.t('errors.failedInApi.withoutName.withMessageDetail.withCodeExplanation', { codeExplanation, messageDetail });
496
+ }
497
+
498
+ return this.t('errors.failedInApi.withoutName.withMessageDetail.withoutCodeExplanation', { messageDetail });
499
+ } else if (!!code) {
500
+ if (!!codeExplanation) {
501
+ return this.t('errors.failedInApi.withoutName.withCode.withCodeExplanation', { code, codeExplanation });
502
+ }
503
+
504
+ return this.t('errors.failedInApi.withoutName.withCode.withoutCodeExplanation', { code });
505
+ }
506
+
507
+ return this.t('errors.failedInApi.withoutAnything');
508
+ }
509
+ } else if ( err?.status === 404 ) {
510
+ if (!!err?.opt?.url) {
511
+ return this.t('errors.notFound.withUrl', { msg, url: err.opt.url });
512
+ }
513
+
514
+ return this.t('errors.notFound.withoutUrl', { msg });
515
+ }
516
+
517
+ return messageDetail.length > 0 ? messageDetail : err;
430
518
  }
431
519
  },
432
520
 
@@ -544,7 +632,10 @@ export default {
544
632
  class="flex-right"
545
633
  >{{ t('generic.moreInfo') }} <i class="icon icon-external-link" /></a>
546
634
  </div>
547
- <hr v-if="subtype.description">
635
+ <hr
636
+ v-if="subtype.description"
637
+ role="none"
638
+ >
548
639
  <div
549
640
  v-if="subtype.description"
550
641
  class="description"
@@ -906,6 +997,8 @@ form.create-resource-container .cru {
906
997
  position: sticky;
907
998
  bottom: 0;
908
999
  background-color: var(--header-bg);
1000
+ height: $footer-height;
1001
+ box-sizing: border-box;
909
1002
 
910
1003
  // Overrides outlet padding
911
1004
  margin-left: -$space-m;
@@ -1,7 +1,9 @@
1
1
  <script>
2
2
  import { mapGetters, useStore } from 'vuex';
3
3
  import ResourceTable, { defaultTableSortGenerationFn } from '@shell/components/ResourceTable';
4
- import { STATE, AGE, NAME, NS_SNAPSHOT_QUOTA } from '@shell/config/table-headers';
4
+ import {
5
+ STATE, AGE, NAME, NS_SNAPSHOT_QUOTA, DESCRIPTION
6
+ } from '@shell/config/table-headers';
5
7
  import { uniq } from '@shell/utils/array';
6
8
  import { MANAGEMENT, NAMESPACE, VIRTUAL_TYPES, HCI } from '@shell/config/types';
7
9
  import { PROJECT_ID, FLAT_VIEW } from '@shell/config/query-params';
@@ -9,13 +11,13 @@ import { PanelLocation, ExtensionPoint } from '@shell/core/types';
9
11
  import ExtensionPanel from '@shell/components/ExtensionPanel';
10
12
  import Masthead from '@shell/components/ResourceList/Masthead';
11
13
  import { mapPref, GROUP_RESOURCES, ALL_NAMESPACES, DEV } from '@shell/store/prefs';
12
- import MoveModal from '@shell/components/MoveModal';
13
14
  import ButtonMultiAction from '@shell/components/ButtonMultiAction.vue';
14
15
  import { escapeHtml } from '@shell/utils/string';
15
16
  import { NAMESPACE_FILTER_ALL_ORPHANS } from '@shell/utils/namespace-filter';
16
17
  import ResourceFetch from '@shell/mixins/resource-fetch';
17
18
  import DOMPurify from 'dompurify';
18
19
  import { HARVESTER_NAME as HARVESTER } from '@shell/config/features';
20
+ import perfSettingsUtils from '@shell/utils/perf-setting.utils';
19
21
  import ActionMenu from '@shell/components/ActionMenuShell.vue';
20
22
  import { useRuntimeFlag } from '@shell/composables/useRuntimeFlag';
21
23
 
@@ -24,7 +26,6 @@ export default {
24
26
  components: {
25
27
  ExtensionPanel,
26
28
  Masthead,
27
- MoveModal,
28
29
  ResourceTable,
29
30
  ButtonMultiAction,
30
31
  ActionMenu,
@@ -89,9 +90,20 @@ export default {
89
90
  }
90
91
  };
91
92
  },
92
-
93
+ watch: {
94
+ actionCb: {
95
+ handler(neu) {
96
+ if (neu?.moveNamespaceCb) {
97
+ this.clearSelection();
98
+ this.$store.dispatch('action-menu/clearCallbackData');
99
+ }
100
+ },
101
+ immediate: true
102
+ }
103
+ },
93
104
  computed: {
94
105
  ...mapGetters(['currentCluster', 'currentProduct']),
106
+ ...mapGetters({ actionCb: 'action-menu/performCallbackData' }),
95
107
  namespaces() {
96
108
  const inStore = this.$store.getters['currentStore'](NAMESPACE);
97
109
 
@@ -101,7 +113,7 @@ export default {
101
113
  return !this.currentCluster || this.namespaces.length ? false : this.$fetchState.pending;
102
114
  },
103
115
  showIncrementalLoadingIndicator() {
104
- return this.perfConfig?.incrementalLoading?.enabled;
116
+ return perfSettingsUtils.incrementalLoadingUtils.isEnabled(this.calcCanPaginate(), this.perfConfig);
105
117
  },
106
118
  isNamespaceCreatable() {
107
119
  return (this.schema?.collectionMethods || []).includes('POST');
@@ -113,6 +125,7 @@ export default {
113
125
  const headers = [
114
126
  STATE,
115
127
  NAME,
128
+ DESCRIPTION
116
129
  ];
117
130
 
118
131
  if (this.groupPreference === 'none') {
@@ -493,6 +506,7 @@ export default {
493
506
  <ActionMenu
494
507
  v-if="showProjectActionButton(group.group)"
495
508
  :resource="getProjectActions(group.group)"
509
+ data-testid="action-button"
496
510
  :button-aria-label="t('projectNamespaces.tableActionsLabel', { resource: projectResource(group.group) })"
497
511
  />
498
512
  <div
@@ -569,7 +583,6 @@ export default {
569
583
  </tr>
570
584
  </template>
571
585
  </ResourceTable>
572
- <MoveModal @moving="clearSelection" />
573
586
  </div>
574
587
  </template>
575
588
  <style lang="scss" scoped>
@@ -164,7 +164,7 @@ export default {
164
164
  class="banner"
165
165
  data-testid="fixed__banner"
166
166
  :style="bannerStyle"
167
- :class="{'banner-consent': consent}"
167
+ :class="{'banner-consent': consent, 'banner-text': !!banner.text}"
168
168
  >
169
169
  <!-- text as array to support line breaks programmatically rather than just exposing HTML -->
170
170
  <div v-if="isTextAnArray">
@@ -177,11 +177,16 @@ export default {
177
177
  </div>
178
178
  </div>
179
179
  <div
180
- v-else
180
+ v-else-if="banner.text"
181
181
  class="single-row"
182
182
  >
183
183
  {{ banner.text }}
184
184
  </div>
185
+ <div
186
+ v-else-if="banner.html"
187
+ v-clean-html="banner.html"
188
+ class="single-row"
189
+ />
185
190
  </div>
186
191
  <div v-else-if="showDialog">
187
192
  <div class="banner-dialog-glass" />
@@ -205,13 +210,19 @@ export default {
205
210
  </div>
206
211
  </div>
207
212
  <div
208
- v-else
213
+ v-else-if="banner.text"
209
214
  class="single-row"
210
215
  >
211
216
  {{ banner.text }}
212
217
  </div>
218
+ <div
219
+ v-else-if="banner.html"
220
+ v-clean-html="banner.html"
221
+ class="single-row"
222
+ />
213
223
  </div>
214
224
  <button
225
+ data-testid="login-confirmation-accept-button"
215
226
  class="btn role-primary"
216
227
  @click="hideDialog()"
217
228
  >
@@ -225,10 +236,13 @@ export default {
225
236
 
226
237
  <style lang="scss" scoped>
227
238
  .banner {
228
- text-align: center;
229
239
  line-height: 2em;
230
240
  width: 100%;
231
- padding: 0 20px;
241
+
242
+ &.banner-text {
243
+ padding: 0 20px;
244
+ text-align: center;
245
+ }
232
246
 
233
247
  &.banner-consent {
234
248
  height: unset;
@@ -49,6 +49,10 @@ export default {
49
49
  userId: {
50
50
  type: String,
51
51
  default: ''
52
+ },
53
+ watchOverride: {
54
+ type: Boolean,
55
+ default: true,
52
56
  }
53
57
  },
54
58
  async fetch() {
@@ -138,7 +142,7 @@ export default {
138
142
  this.update();
139
143
  },
140
144
  userId(userId, oldUserId) {
141
- if (userId === oldUserId) {
145
+ if (userId === oldUserId || this.watchOverride === true) {
142
146
  return;
143
147
  }
144
148
  this.update();
@@ -107,6 +107,7 @@ export default {
107
107
  @click="close(growl)"
108
108
  />
109
109
  <div
110
+ v-if="growl.title"
110
111
  :id="`growl-title-${ growl.id }`"
111
112
  class="growl-text-title"
112
113
  >
@@ -108,6 +108,7 @@ export default {
108
108
  :value="afterLoginRoute"
109
109
  name="login-route"
110
110
  :options="routeRadioOptions"
111
+ :aria-label="`${t('prefs.landing.label')} - ${ t('landing.landingPrefs.body')}`"
111
112
  @update:value="updateLoginRoute"
112
113
  >
113
114
  <template #label>
@@ -119,6 +120,7 @@ export default {
119
120
  <div class="custom-page">
120
121
  <RadioButton
121
122
  :label="option.label"
123
+ :radio-option-id="option.radioOptionId"
122
124
  :val="false"
123
125
  :value="afterLoginRoute=== 'home' || afterLoginRoute === 'last-visited'"
124
126
  :v-bind="$attrs"
@@ -4,7 +4,7 @@ import Select from '@shell/components/form/Select.vue';
4
4
  import { RcDropdown, RcDropdownTrigger, RcDropdownItem } from '@components/RcDropdown';
5
5
 
6
6
  export default {
7
- name: 'LocalSelector',
7
+ name: 'LocaleSelector',
8
8
 
9
9
  components: {
10
10
  Select,
@@ -0,0 +1,55 @@
1
+ <script lang="ts" setup>
2
+ import { computed, ref } from 'vue';
3
+ import { useStore } from 'vuex';
4
+
5
+ import AppModal from '@shell/components/AppModal.vue';
6
+
7
+ const store = useStore();
8
+
9
+ const isOpen = computed(() => store.getters['modal/isOpen']);
10
+ const component = computed(() => store.getters['modal/component']);
11
+ const componentProps = computed(() => store.getters['modal/componentProps']);
12
+ const resources = computed(() => store.getters['modal/resources']);
13
+ const closeOnClickOutside = computed(() => store.getters['modal/closeOnClickOutside']);
14
+ const modalWidth = computed(() => store.getters['modal/modalWidth']);
15
+ // const modalSticky = computed(() => store.getters['modal/modalSticky']); // TODO: Implement sticky modals
16
+
17
+ const backgroundClosing = ref<Function | null>(null);
18
+
19
+ function close() {
20
+ if (!isOpen.value) return;
21
+
22
+ if (backgroundClosing.value) {
23
+ backgroundClosing.value();
24
+ }
25
+
26
+ store.commit('modal/closeModal');
27
+ }
28
+
29
+ function registerBackgroundClosing(fn: Function) {
30
+ backgroundClosing.value = fn;
31
+ }
32
+ </script>
33
+
34
+ <template>
35
+ <Teleport to="#modals">
36
+ <app-modal
37
+ v-if="isOpen && component"
38
+ :click-to-close="closeOnClickOutside"
39
+ :width="modalWidth"
40
+ :style="{ '--prompt-modal-width': modalWidth }"
41
+ :trigger-focus-trap="true"
42
+ tabindex="0"
43
+ @close="close"
44
+ >
45
+ <component
46
+ :is="component"
47
+ v-bind="componentProps || {}"
48
+ data-testid="modal-manager-component"
49
+ :resources="resources"
50
+ :register-background-closing="registerBackgroundClosing"
51
+ @close="close"
52
+ />
53
+ </app-modal>
54
+ </Teleport>
55
+ </template>
@@ -95,6 +95,12 @@ export default defineComponent({
95
95
 
96
96
  return customHeaders || this.$store.getters['type-map/headersFor'](this.schema, this.canPaginate);
97
97
  }
98
+ },
99
+
100
+ methods: {
101
+ clearSelection() {
102
+ this.$refs.table.clearSelection();
103
+ },
98
104
  }
99
105
  });
100
106
 
@@ -103,6 +109,7 @@ export default defineComponent({
103
109
  <template>
104
110
  <div>
105
111
  <ResourceTable
112
+ ref="table"
106
113
  v-bind="$attrs"
107
114
  :schema="schema"
108
115
  :rows="rows"
@@ -13,12 +13,15 @@ export default {
13
13
  components: { AppModal },
14
14
 
15
15
  data() {
16
- return { opened: false, backgroundClosing: null };
16
+ return {
17
+ opened: false,
18
+ backgroundClosing: null,
19
+ componentRendered: false
20
+ };
17
21
  },
18
22
 
19
23
  computed: {
20
24
  ...mapState('action-menu', ['showModal', 'modalData']),
21
-
22
25
  resources() {
23
26
  let resources = this.modalData?.resources;
24
27
 
@@ -28,11 +31,28 @@ export default {
28
31
 
29
32
  return resources || [];
30
33
  },
31
-
34
+ testId() {
35
+ return this.modalData?.testId || 'prompt-modal-generic-testid';
36
+ },
37
+ returnFocusSelector() {
38
+ return this.modalData?.returnFocusSelector || undefined;
39
+ },
40
+ returnFocusFirstIterableNodeSelector() {
41
+ return this.modalData?.returnFocusFirstIterableNodeSelector || undefined;
42
+ },
32
43
  modalWidth() {
33
44
  // property set from workload.js to overwrite modal default width of 600px, with fallback value as well
34
45
  return this.modalData?.modalWidth || '600px';
35
46
  },
47
+ customClass() {
48
+ return this.modalData?.customClass || undefined;
49
+ },
50
+ styles() {
51
+ return this.modalData?.styles || undefined;
52
+ },
53
+ height() {
54
+ return this.modalData?.height || undefined;
55
+ },
36
56
  component() {
37
57
  // Looks for a dialog component by looking up in plugins and @shell/dialog/${name}.
38
58
  return this.$store.getters['type-map/importDialog'](this.modalData?.component);
@@ -48,27 +68,36 @@ export default {
48
68
  },
49
69
  closeOnClickOutside() {
50
70
  return this.modalData?.closeOnClickOutside;
71
+ },
72
+ modalName() {
73
+ return this.modalData?.modalName;
51
74
  }
52
75
  },
53
76
 
54
77
  watch: {
55
78
  showModal(show) {
56
79
  this.opened = show;
57
- },
80
+ }
58
81
  },
59
82
 
60
83
  methods: {
61
- close() {
84
+ onSlotComponentMounted() {
85
+ // variable for the watcher based focus-trap
86
+ // so that we know when the component is rendered
87
+ this.componentRendered = true;
88
+ },
89
+ close(data) {
62
90
  if (!this.opened) {
63
91
  return;
64
92
  }
65
93
 
66
94
  this.errors = [];
67
- this.$store.commit('action-menu/togglePromptModal');
95
+ this.$store.commit('action-menu/togglePromptModal', data);
68
96
  if (this.backgroundClosing) {
69
97
  this.backgroundClosing();
70
98
  }
71
99
 
100
+ this.componentRendered = false;
72
101
  this.opened = false;
73
102
  },
74
103
 
@@ -83,16 +112,26 @@ export default {
83
112
  <template>
84
113
  <app-modal
85
114
  v-if="opened && component"
115
+ :name="modalName"
86
116
  :click-to-close="closeOnClickOutside"
87
117
  :width="modalWidth"
88
- @close="close()"
118
+ :data-testid="testId"
119
+ :custom-class="customClass"
120
+ :styles="styles"
121
+ :height="height"
122
+ :trigger-focus-trap="true"
123
+ :return-focus-selector="returnFocusSelector"
124
+ :return-focus-first-iterable-node-selector="returnFocusFirstIterableNodeSelector"
125
+ :focus-trap-watcher-based-variable="componentRendered"
126
+ @close="close"
89
127
  >
90
128
  <component
91
129
  v-bind="modalData.componentProps || {}"
92
130
  :is="component"
93
131
  :resources="resources"
94
132
  :register-background-closing="registerBackgroundClosing"
95
- @close="close()"
133
+ @vue:mounted="onSlotComponentMounted"
134
+ @close="close"
96
135
  />
97
136
  </app-modal>
98
137
  </template>