@rancher/shell 3.0.8-rc.1 → 3.0.8-rc.10

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 (323) hide show
  1. package/assets/brand/suse/banner.svg +1 -0
  2. package/assets/brand/suse/dark/banner.svg +1 -0
  3. package/assets/brand/suse/dark/login-landscape.svg +1 -0
  4. package/assets/brand/suse/dark/rancher-logo.svg +1 -1
  5. package/assets/brand/suse/favicon.png +0 -0
  6. package/assets/brand/suse/login-landscape.svg +1 -0
  7. package/assets/brand/suse/metadata.json +11 -1
  8. package/assets/brand/suse/rancher-logo.svg +1 -1
  9. package/assets/fonts/suse/suse-v2-latin-300.woff +0 -0
  10. package/assets/fonts/suse/suse-v2-latin-300.woff2 +0 -0
  11. package/assets/fonts/suse/suse-v2-latin-600.woff +0 -0
  12. package/assets/fonts/suse/suse-v2-latin-600.woff2 +0 -0
  13. package/assets/fonts/suse/suse-v2-latin-700.woff +0 -0
  14. package/assets/fonts/suse/suse-v2-latin-700.woff2 +0 -0
  15. package/assets/fonts/suse/suse-v2-latin-800.woff +0 -0
  16. package/assets/fonts/suse/suse-v2-latin-800.woff2 +0 -0
  17. package/assets/fonts/suse/suse-v2-latin-regular.woff +0 -0
  18. package/assets/fonts/suse/suse-v2-latin-regular.woff2 +0 -0
  19. package/assets/images/content/README.md +5 -0
  20. package/assets/images/content/cloud-native.svg +84 -0
  21. package/assets/images/content/dark/cloud-native.svg +21 -0
  22. package/assets/images/content/dark/shield.svg +59 -0
  23. package/assets/images/content/dark/suse.svg +10 -0
  24. package/assets/images/content/shield.svg +59 -0
  25. package/assets/images/content/suse.svg +10 -0
  26. package/assets/styles/base/_typography.scss +1 -0
  27. package/assets/styles/fonts/_fontstack.scss +53 -1
  28. package/assets/styles/global/_cards.scss +0 -3
  29. package/assets/styles/global/_layout.scss +21 -35
  30. package/assets/styles/themes/_dark.scss +1 -1
  31. package/assets/styles/themes/_light.scss +1 -1
  32. package/assets/styles/themes/_modern.scss +11 -3
  33. package/assets/styles/themes/_suse.scss +116 -24
  34. package/assets/translations/en-us.yaml +94 -10
  35. package/components/AutoscalerCard.vue +113 -0
  36. package/components/AutoscalerTab.vue +94 -0
  37. package/components/BackLink.vue +8 -0
  38. package/components/BannerGraphic.vue +36 -21
  39. package/components/BrandImage.vue +17 -6
  40. package/components/ClusterIconMenu.vue +1 -1
  41. package/components/ClusterProviderIcon.vue +1 -1
  42. package/components/Cron/CronExpressionEditor.vue +1 -1
  43. package/components/Cron/CronExpressionEditorModal.vue +1 -1
  44. package/components/Drawer/Chrome.vue +2 -6
  45. package/components/Drawer/ResourceDetailDrawer/ConfigTab.vue +4 -9
  46. package/components/Drawer/ResourceDetailDrawer/YamlTab.vue +3 -8
  47. package/components/Drawer/ResourceDetailDrawer/composables.ts +3 -4
  48. package/components/Drawer/ResourceDetailDrawer/index.vue +4 -9
  49. package/components/Drawer/ResourceDetailDrawer/types.ts +17 -0
  50. package/components/Drawer/types.ts +3 -0
  51. package/components/DynamicContent/DynamicContentBanner.vue +102 -0
  52. package/components/DynamicContent/DynamicContentCloseButton.vue +42 -0
  53. package/components/DynamicContent/DynamicContentIcon.vue +132 -0
  54. package/components/DynamicContent/DynamicContentPanel.vue +112 -0
  55. package/components/DynamicContent/content.ts +78 -0
  56. package/components/EmberPage.vue +1 -1
  57. package/components/IconOrSvg.vue +2 -2
  58. package/components/PaginatedResourceTable.vue +2 -6
  59. package/components/PopoverCard.vue +192 -0
  60. package/components/Questions/__tests__/index.test.ts +159 -0
  61. package/components/Resource/Detail/CopyToClipboard.vue +4 -1
  62. package/components/Resource/Detail/FetchLoader/composables.ts +18 -4
  63. package/components/Resource/Detail/Metadata/Annotations/index.vue +2 -2
  64. package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/identifying-fields.test.ts +1 -1
  65. package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +4 -0
  66. package/components/Resource/Detail/Metadata/KeyValueRow.vue +1 -1
  67. package/components/Resource/Detail/Metadata/Labels/index.vue +2 -2
  68. package/components/Resource/Detail/Metadata/composables.ts +9 -9
  69. package/components/Resource/Detail/Metadata/index.vue +3 -3
  70. package/components/Resource/Detail/ResourcePopover/ResourcePopoverCard.vue +2 -19
  71. package/components/Resource/Detail/ResourcePopover/__tests__/ResourcePopoverCard.test.ts +0 -29
  72. package/components/Resource/Detail/ResourcePopover/__tests__/index.test.ts +132 -150
  73. package/components/Resource/Detail/ResourcePopover/index.vue +54 -159
  74. package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +0 -2
  75. package/components/Resource/Detail/TitleBar/composables.ts +2 -1
  76. package/components/Resource/Detail/TitleBar/index.vue +10 -6
  77. package/components/Resource/Detail/composables.ts +12 -0
  78. package/components/ResourceDetail/Masthead/latest.vue +29 -0
  79. package/components/ResourceDetail/index.vue +4 -1
  80. package/components/ResourceList/Masthead.vue +1 -1
  81. package/components/SortableTable/index.vue +1 -0
  82. package/components/Tabbed/__tests__/index.test.ts +86 -0
  83. package/components/{nav/WindowManager → Window}/ContainerLogs.vue +1 -1
  84. package/components/{nav/WindowManager → Window}/ContainerLogsActions.vue +1 -0
  85. package/components/{nav/WindowManager → Window}/__tests__/ContainerLogs.test.ts +1 -1
  86. package/components/{nav/WindowManager → Window}/__tests__/ContainerShell.test.ts +2 -2
  87. package/components/__tests__/AutoscalerCard.test.ts +154 -0
  88. package/components/__tests__/AutoscalerTab.test.ts +125 -0
  89. package/components/__tests__/PopoverCard.test.ts +204 -0
  90. package/components/auth/SelectPrincipal.vue +24 -6
  91. package/components/auth/__tests__/SelectPrincipal.test.ts +119 -0
  92. package/components/formatter/Autoscaler.vue +97 -0
  93. package/components/formatter/InternalExternalIP.vue +198 -24
  94. package/components/formatter/__tests__/Autoscaler.test.ts +156 -0
  95. package/components/formatter/__tests__/InternalExternalIP.test.ts +133 -0
  96. package/components/google/util/__tests__/formatter.test.ts +47 -0
  97. package/components/google/util/formatter.ts +5 -2
  98. package/components/nav/Group.vue +12 -3
  99. package/components/nav/Header.vue +36 -16
  100. package/components/nav/NamespaceFilter.vue +13 -1
  101. package/components/nav/NotificationCenter/index.vue +2 -1
  102. package/components/nav/TopLevelMenu.helper.ts +16 -6
  103. package/components/nav/TopLevelMenu.vue +4 -2
  104. package/components/{DraggableZone.vue → nav/WindowManager/PinArea.vue} +47 -80
  105. package/components/nav/WindowManager/composables/useComponentsMount.ts +70 -0
  106. package/components/nav/WindowManager/composables/useDimensionsHandler.ts +105 -0
  107. package/components/nav/WindowManager/composables/useDragHandler.ts +99 -0
  108. package/components/nav/WindowManager/composables/usePanelHandler.ts +72 -0
  109. package/components/nav/WindowManager/composables/usePanelsHandler.ts +14 -0
  110. package/components/nav/WindowManager/composables/useResizeHandler.ts +167 -0
  111. package/components/nav/WindowManager/composables/useTabsHandler.ts +51 -0
  112. package/components/nav/WindowManager/constants.ts +23 -0
  113. package/components/nav/WindowManager/index.vue +61 -575
  114. package/components/nav/WindowManager/panels/HorizontalPanel.vue +265 -0
  115. package/components/nav/WindowManager/panels/TabBodyContainer.vue +39 -0
  116. package/components/nav/WindowManager/panels/VerticalPanel.vue +308 -0
  117. package/components/templates/default.vue +4 -40
  118. package/components/templates/home.vue +31 -5
  119. package/components/templates/plain.vue +30 -4
  120. package/components/templates/standalone.vue +1 -1
  121. package/composables/useI18n.ts +10 -1
  122. package/composables/useInterval.ts +15 -0
  123. package/config/__test__/uiplugins.test.ts +309 -0
  124. package/config/labels-annotations.js +9 -1
  125. package/config/product/explorer.js +3 -1
  126. package/config/product/manager.js +20 -9
  127. package/config/router/routes.js +10 -2
  128. package/config/settings.ts +2 -1
  129. package/config/store.js +4 -2
  130. package/config/table-headers.js +8 -0
  131. package/config/types.js +9 -0
  132. package/config/uiplugins.js +46 -2
  133. package/config/version.js +1 -1
  134. package/core/__test__/extension-manager-impl.test.js +236 -0
  135. package/core/extension-manager-impl.js +23 -6
  136. package/core/plugin-helpers.ts +2 -0
  137. package/core/types-provisioning.ts +4 -1
  138. package/detail/pod.vue +1 -0
  139. package/detail/provisioning.cattle.io.cluster.vue +13 -1
  140. package/dialog/DeveloperLoadExtensionDialog.vue +12 -3
  141. package/dialog/RollbackWorkloadDialog.vue +2 -5
  142. package/directives/ui-context.ts +103 -0
  143. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +2 -2
  144. package/edit/auth/__tests__/oidc.test.ts +26 -0
  145. package/edit/auth/github.vue +5 -0
  146. package/edit/auth/oidc.vue +5 -1
  147. package/edit/autoscaling.horizontalpodautoscaler/index.vue +1 -0
  148. package/edit/cloudcredential.vue +1 -1
  149. package/edit/configmap.vue +1 -0
  150. package/edit/constraints.gatekeeper.sh.constraint/index.vue +1 -0
  151. package/edit/fleet.cattle.io.gitrepo.vue +0 -10
  152. package/edit/fleet.cattle.io.helmop.vue +6 -6
  153. package/edit/helm.cattle.io.projecthelmchart.vue +1 -0
  154. package/edit/k8s.cni.cncf.io.networkattachmentdefinition.vue +1 -0
  155. package/edit/logging-flow/index.vue +1 -0
  156. package/edit/logging.banzaicloud.io.output/index.vue +1 -0
  157. package/edit/management.cattle.io.fleetworkspace.vue +1 -1
  158. package/edit/management.cattle.io.project.vue +1 -0
  159. package/edit/monitoring.coreos.com.alertmanagerconfig/index.vue +4 -1
  160. package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +2 -1
  161. package/edit/monitoring.coreos.com.prometheusrule/index.vue +1 -0
  162. package/edit/monitoring.coreos.com.receiver/index.vue +2 -1
  163. package/edit/monitoring.coreos.com.route.vue +1 -1
  164. package/edit/namespace.vue +1 -0
  165. package/edit/networking.istio.io.destinationrule/index.vue +1 -0
  166. package/edit/networking.k8s.io.ingress/index.vue +1 -0
  167. package/edit/networking.k8s.io.networkpolicy/PolicyRules.vue +1 -0
  168. package/edit/networking.k8s.io.networkpolicy/index.vue +1 -0
  169. package/edit/node.vue +1 -0
  170. package/edit/persistentvolume/index.vue +27 -22
  171. package/edit/persistentvolume/plugins/awsElasticBlockStore.vue +13 -14
  172. package/edit/persistentvolume/plugins/azureDisk.vue +49 -48
  173. package/edit/persistentvolume/plugins/azureFile.vue +15 -14
  174. package/edit/persistentvolume/plugins/cephfs.vue +15 -14
  175. package/edit/persistentvolume/plugins/cinder.vue +15 -14
  176. package/edit/persistentvolume/plugins/csi.vue +18 -16
  177. package/edit/persistentvolume/plugins/fc.vue +13 -14
  178. package/edit/persistentvolume/plugins/flexVolume.vue +15 -14
  179. package/edit/persistentvolume/plugins/flocker.vue +1 -3
  180. package/edit/persistentvolume/plugins/gcePersistentDisk.vue +13 -14
  181. package/edit/persistentvolume/plugins/glusterfs.vue +15 -14
  182. package/edit/persistentvolume/plugins/hostPath.vue +40 -39
  183. package/edit/persistentvolume/plugins/iscsi.vue +13 -14
  184. package/edit/persistentvolume/plugins/local.vue +1 -3
  185. package/edit/persistentvolume/plugins/longhorn.vue +23 -22
  186. package/edit/persistentvolume/plugins/nfs.vue +15 -14
  187. package/edit/persistentvolume/plugins/photonPersistentDisk.vue +1 -14
  188. package/edit/persistentvolume/plugins/portworxVolume.vue +15 -14
  189. package/edit/persistentvolume/plugins/quobyte.vue +15 -14
  190. package/edit/persistentvolume/plugins/rbd.vue +15 -14
  191. package/edit/persistentvolume/plugins/scaleIO.vue +15 -14
  192. package/edit/persistentvolume/plugins/storageos.vue +15 -14
  193. package/edit/persistentvolume/plugins/vsphereVolume.vue +1 -3
  194. package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +32 -5
  195. package/edit/provisioning.cattle.io.cluster/__tests__/CustomCommand.test.ts +35 -0
  196. package/edit/provisioning.cattle.io.cluster/__tests__/Networking.test.ts +155 -0
  197. package/edit/provisioning.cattle.io.cluster/index.vue +25 -15
  198. package/edit/provisioning.cattle.io.cluster/rke2.vue +42 -8
  199. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +107 -5
  200. package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +92 -4
  201. package/edit/secret/index.vue +1 -1
  202. package/edit/service.vue +9 -4
  203. package/edit/serviceaccount.vue +1 -0
  204. package/edit/storage.k8s.io.storageclass/index.vue +1 -0
  205. package/edit/workload/index.vue +2 -1
  206. package/edit/workload/mixins/workload.js +1 -1
  207. package/initialize/App.vue +4 -4
  208. package/initialize/install-directives.js +2 -0
  209. package/initialize/install-plugins.js +19 -2
  210. package/list/provisioning.cattle.io.cluster.vue +15 -2
  211. package/machine-config/amazonec2.vue +42 -135
  212. package/machine-config/components/EC2Networking.vue +490 -0
  213. package/machine-config/components/__tests__/EC2Networking.test.ts +148 -0
  214. package/machine-config/components/__tests__/utils/vpcSubnetMockData.js +294 -0
  215. package/machine-config/digitalocean.vue +11 -0
  216. package/machine-config/google.vue +1 -1
  217. package/mixins/__tests__/brand.spec.ts +2 -2
  218. package/mixins/__tests__/chart.test.ts +21 -0
  219. package/mixins/brand.js +1 -7
  220. package/mixins/chart.js +7 -1
  221. package/mixins/create-edit-view/index.js +5 -0
  222. package/models/__tests__/chart.test.ts +33 -4
  223. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +112 -5
  224. package/models/chart.js +25 -13
  225. package/models/cluster/node.js +13 -6
  226. package/models/cluster.x-k8s.io.machine.js +10 -20
  227. package/models/cluster.x-k8s.io.machinedeployment.js +5 -1
  228. package/models/management.cattle.io.cluster.js +21 -3
  229. package/models/management.cattle.io.kontainerdriver.js +1 -0
  230. package/models/provisioning.cattle.io.cluster.js +249 -33
  231. package/package.json +6 -5
  232. package/pages/auth/login.vue +38 -2
  233. package/pages/c/_cluster/apps/charts/__tests__/chart.test.ts +135 -0
  234. package/pages/c/_cluster/apps/charts/chart.vue +33 -15
  235. package/pages/c/_cluster/apps/charts/index.vue +11 -13
  236. package/pages/c/_cluster/apps/charts/install.vue +1 -1
  237. package/pages/c/_cluster/explorer/index.vue +8 -6
  238. package/pages/c/_cluster/manager/hostedprovider/index.vue +220 -0
  239. package/pages/c/_cluster/settings/brand.vue +1 -1
  240. package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +7 -0
  241. package/pages/c/_cluster/uiplugins/catalogs.vue +147 -0
  242. package/pages/c/_cluster/uiplugins/index.vue +126 -184
  243. package/pages/home.vue +14 -4
  244. package/pkg/dynamic-importer.lib.js +4 -0
  245. package/plugins/dashboard-client-init.js +3 -0
  246. package/plugins/dashboard-store/getters.js +18 -1
  247. package/plugins/dashboard-store/resource-class.js +4 -4
  248. package/plugins/dynamic-content.js +13 -0
  249. package/plugins/i18n.js +8 -0
  250. package/plugins/steve/__tests__/steve-pagination-utils.test.ts +333 -0
  251. package/plugins/steve/steve-pagination-utils.ts +39 -20
  252. package/plugins/steve/subscribe.js +17 -9
  253. package/plugins/subscribe-events.ts +4 -2
  254. package/rancher-components/Form/Checkbox/Checkbox.vue +1 -1
  255. package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.vue +6 -34
  256. package/rancher-components/Pill/RcStatusBadge/index.ts +0 -1
  257. package/rancher-components/Pill/RcStatusBadge/types.ts +1 -1
  258. package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.vue +5 -28
  259. package/rancher-components/Pill/RcStatusIndicator/types.ts +2 -1
  260. package/rancher-components/Pill/types.ts +0 -1
  261. package/rancher-components/RcDropdown/RcDropdownItem.vue +1 -0
  262. package/rancher-components/RcDropdown/RcDropdownItemSelect.vue +5 -1
  263. package/rancher-components/RcIcon/RcIcon.test.ts +51 -0
  264. package/rancher-components/RcIcon/RcIcon.vue +46 -0
  265. package/rancher-components/RcIcon/index.ts +1 -0
  266. package/rancher-components/RcIcon/types.ts +160 -0
  267. package/rancher-components/utils/status.test.ts +67 -0
  268. package/rancher-components/utils/status.ts +77 -0
  269. package/scripts/typegen.sh +1 -0
  270. package/store/action-menu.js +8 -0
  271. package/store/auth.js +3 -3
  272. package/store/catalog.js +6 -0
  273. package/store/features.js +1 -0
  274. package/store/index.js +36 -17
  275. package/store/notifications.ts +51 -4
  276. package/store/plugins.js +7 -3
  277. package/store/prefs.js +6 -6
  278. package/store/type-map.js +3 -3
  279. package/store/ui-context.ts +86 -0
  280. package/store/wm.ts +244 -0
  281. package/types/notifications/index.ts +27 -3
  282. package/types/shell/index.d.ts +79 -4
  283. package/types/store/__tests__/pagination.types.spec.ts +137 -0
  284. package/types/store/pagination.types.ts +157 -9
  285. package/types/store/subscribe-events.types.ts +8 -1
  286. package/types/store/subscribe.types.ts +1 -0
  287. package/types/window-manager.ts +24 -0
  288. package/utils/__tests__/object.test.ts +19 -0
  289. package/utils/__tests__/provider.test.ts +98 -0
  290. package/utils/__tests__/selector-typed.test.ts +263 -0
  291. package/utils/__tests__/version.test.ts +19 -1
  292. package/utils/autoscaler-utils.ts +7 -0
  293. package/utils/back-off.ts +3 -3
  294. package/utils/brand.ts +29 -0
  295. package/utils/chart.js +18 -0
  296. package/utils/color.js +1 -1
  297. package/utils/dynamic-content/__tests__/announcement.test.ts +498 -0
  298. package/utils/dynamic-content/__tests__/info.test.ts +21 -9
  299. package/utils/dynamic-content/announcement.ts +142 -0
  300. package/utils/dynamic-content/example.json +40 -0
  301. package/utils/dynamic-content/index.ts +6 -2
  302. package/utils/dynamic-content/info.ts +44 -2
  303. package/utils/dynamic-content/new-release.ts +1 -1
  304. package/utils/dynamic-content/notification-handler.ts +48 -0
  305. package/utils/dynamic-content/types.d.ts +53 -1
  306. package/utils/dynamic-importer.js +2 -2
  307. package/utils/favicon.js +4 -4
  308. package/utils/object.js +20 -2
  309. package/utils/pagination-wrapper.ts +12 -8
  310. package/utils/provider.ts +14 -0
  311. package/utils/scroll.js +7 -0
  312. package/utils/selector-typed.ts +6 -2
  313. package/utils/settings.ts +15 -0
  314. package/utils/validators/machine-pool.ts +13 -3
  315. package/utils/version.js +15 -0
  316. package/assets/images/icons/document.svg +0 -3
  317. package/plugins/nuxt-client-init.js +0 -3
  318. package/store/wm.js +0 -95
  319. /package/components/{nav/WindowManager → Window}/ChartReadme.vue +0 -0
  320. /package/components/{nav/WindowManager → Window}/ContainerShell.vue +0 -0
  321. /package/components/{nav/WindowManager → Window}/KubectlShell.vue +0 -0
  322. /package/components/{nav/WindowManager → Window}/MachineSsh.vue +0 -0
  323. /package/components/{nav/WindowManager → Window}/Window.vue +0 -0
package/store/index.js CHANGED
@@ -263,7 +263,7 @@ export const state = () => {
263
263
  $route: markRaw({}),
264
264
  $plugin: markRaw({}),
265
265
  showWorkspaceSwitcher: true,
266
-
266
+ localCluster: null,
267
267
  };
268
268
  };
269
269
 
@@ -272,10 +272,20 @@ export const getters = {
272
272
  return state.clusterReady === true;
273
273
  },
274
274
 
275
+ /**
276
+ * Cache of the mgmt cluster fetched at start up
277
+ *
278
+ * We cannot rely on the store to cache this as the store may contain a page without the local cluster
279
+ */
280
+ localCluster(state) {
281
+ return state.localCluster;
282
+ },
283
+
275
284
  isMultiCluster(state, getters) {
276
- const clusters = getters['management/all'](MANAGEMENT.CLUSTER);
285
+ const clusterCount = getters['management/all'](COUNT)?.[0]?.counts?.[MANAGEMENT.CLUSTER]?.summary?.count || 0;
286
+ const localCluster = getters['localCluster'];
277
287
 
278
- if (clusters.length === 1 && clusters[0].metadata?.name === 'local') {
288
+ if (clusterCount === 1 && !!localCluster) {
279
289
  return false;
280
290
  } else {
281
291
  return true;
@@ -592,10 +602,9 @@ export const getters = {
592
602
  },
593
603
 
594
604
  isStandaloneHarvester(state, getters) {
595
- const clusters = getters['management/all'](MANAGEMENT.CLUSTER);
596
- const cluster = clusters.find((c) => c.id === 'local') || {};
605
+ const localCluster = getters['localCluster'];
597
606
 
598
- return getters['isSingleProduct'] && cluster.isHarvester && !getters['isRancherInHarvester'];
607
+ return getters['isSingleProduct'] && localCluster?.isHarvester && !getters['isRancherInHarvester'];
599
608
  },
600
609
 
601
610
  showTopLevelMenu(getters) {
@@ -640,9 +649,10 @@ export const mutations = {
640
649
  clearPageActionHandler(state) {
641
650
  state.pageActionHandler = null;
642
651
  },
643
- managementChanged(state, { ready, isRancher }) {
652
+ managementChanged(state, { ready, isRancher, localCluster }) {
644
653
  state.managementReady = ready;
645
654
  state.isRancher = isRancher;
655
+ state.localCluster = localCluster;
646
656
  },
647
657
  clusterReady(state, ready) {
648
658
  state.clusterReady = ready;
@@ -846,11 +856,21 @@ export const actions = {
846
856
 
847
857
  res = await allHash(promises);
848
858
 
859
+ let localCluster = null;
860
+
849
861
  if (!res[MANAGEMENT.SETTING] || !paginateClusters({ rootGetters, state })) {
850
862
  // This introduces a synchronous request, however we need settings to determine if SSP is enabled
851
- // Eventually it will be removed when SSP is always on
852
- res[MANAGEMENT.CLUSTER] = await dispatch('management/findAll', { type: MANAGEMENT.CLUSTER, opt: { watch: false } });
863
+ await dispatch('management/findAll', { type: MANAGEMENT.CLUSTER, opt: { watch: false } });
853
864
  toWatch.push(MANAGEMENT.CLUSTER);
865
+
866
+ localCluster = getters['management/byId'](MANAGEMENT.CLUSTER, 'local');
867
+ } else {
868
+ try {
869
+ localCluster = await dispatch('management/find', {
870
+ type: MANAGEMENT.CLUSTER, id: 'local', opt: { watch: false }
871
+ });
872
+ } catch (e) { // we don't care about errors, specifically 404s
873
+ }
854
874
  }
855
875
 
856
876
  // See comment above. Now that we have feature flags we can watch resources
@@ -858,11 +878,7 @@ export const actions = {
858
878
  dispatch('management/watch', { type });
859
879
  });
860
880
 
861
- const isMultiCluster = getters['isMultiCluster'];
862
-
863
881
  // If the local cluster is a Harvester cluster and 'rancher-manager-support' is true, it means that the embedded Rancher is being used.
864
- const localCluster = res[MANAGEMENT.CLUSTER]?.find((c) => c.id === 'local');
865
-
866
882
  if (localCluster?.isHarvester) {
867
883
  const harvesterSetting = await dispatch('cluster/findAll', { type: HCI.SETTING, opt: { url: `/v1/harvester/${ HCI.SETTING }s` } });
868
884
  const rancherManagerSupport = harvesterSetting.find((setting) => setting.id === 'rancher-manager-support');
@@ -904,6 +920,7 @@ export const actions = {
904
920
  commit('managementChanged', {
905
921
  ready: true,
906
922
  isRancher,
923
+ localCluster
907
924
  });
908
925
 
909
926
  if ( res[FLEET.WORKSPACE] ) {
@@ -914,6 +931,8 @@ export const actions = {
914
931
  });
915
932
  }
916
933
 
934
+ const isMultiCluster = getters['isMultiCluster'];
935
+
917
936
  console.log(`Done loading management; isRancher=${ isRancher }; isMultiCluster=${ isMultiCluster }`); // eslint-disable-line no-console
918
937
  },
919
938
 
@@ -1232,10 +1251,10 @@ export const actions = {
1232
1251
  }
1233
1252
  },
1234
1253
 
1235
- nuxtClientInit({ dispatch, commit, rootState }, nuxt) {
1236
- commit('setRouter', nuxt.app.router);
1237
- commit('setRoute', nuxt.route);
1238
- commit('setPlugin', nuxt.app.$plugin);
1254
+ dashboardClientInit({ dispatch, commit, rootState }, context) {
1255
+ commit('setRouter', context.app.router);
1256
+ commit('setRoute', context.route);
1257
+ commit('setPlugin', context.app.$plugin);
1239
1258
 
1240
1259
  dispatch('management/rehydrateSubscribe');
1241
1260
  dispatch('cluster/rehydrateSubscribe');
@@ -1,6 +1,12 @@
1
1
  import { md5 } from '@shell/utils/crypto';
2
2
  import { randomStr } from '@shell/utils/string';
3
- import { EncryptedNotification, Notification, StoredNotification } from '@shell/types/notifications';
3
+ import {
4
+ EncryptedNotification,
5
+ Notification,
6
+ NotificationLevel,
7
+ NotificationHandlerExtensionName,
8
+ StoredNotification
9
+ } from '@shell/types/notifications';
4
10
  import { encrypt, decrypt, deriveKey } from '@shell/utils/crypto/encryption';
5
11
 
6
12
  /**
@@ -65,6 +71,9 @@ async function saveEncryptedNotification(getters: any, notification: Notificatio
65
71
  level: notification.level,
66
72
  primaryAction: notification.primaryAction,
67
73
  secondaryAction: notification.secondaryAction,
74
+ preference: notification.preference,
75
+ handlerName: notification.handlerName,
76
+ data: notification.data,
68
77
  };
69
78
 
70
79
  const localStorageKey = getters['localStorageKey'];
@@ -108,15 +117,23 @@ export const getters = {
108
117
  return state.notifications;
109
118
  },
110
119
 
120
+ visible: (state: NotificationsStore) => {
121
+ return state.notifications.filter((n) => n.level !== NotificationLevel.Hidden);
122
+ },
123
+
124
+ hidden: (state: NotificationsStore) => {
125
+ return state.notifications.filter((n) => n.level === NotificationLevel.Hidden);
126
+ },
127
+
111
128
  item: (state: NotificationsStore) => {
112
129
  return (id: string) => {
113
130
  return state.notifications.find((i) => i.id === id);
114
131
  };
115
132
  },
116
133
 
117
- // Count of unread notifications
134
+ // Count of unread notifications - only considers visible notifications
118
135
  unreadCount: (state: NotificationsStore) => {
119
- return state.notifications.filter((n) => !n.read).length;
136
+ return state.notifications.filter((n) => !n.read && n.level !== NotificationLevel.Hidden).length;
120
137
  },
121
138
 
122
139
  /**
@@ -192,9 +209,10 @@ export const mutations = {
192
209
  syncIndex(state);
193
210
  },
194
211
 
212
+ // Only mark visible notifications as read via mark all
195
213
  markAllRead(state: NotificationsStore) {
196
214
  state.notifications.forEach((notification) => {
197
- if (!notification.read) {
215
+ if (!notification.read && notification.level !== NotificationLevel.Hidden) {
198
216
  notification.read = true;
199
217
  }
200
218
  });
@@ -252,6 +270,20 @@ export const mutations = {
252
270
  },
253
271
  };
254
272
 
273
+ async function callNotifyHandler({ $extension }: any, notification: Notification, read: boolean) {
274
+ if (notification?.handlerName) {
275
+ const handler = $extension.getDynamic(NotificationHandlerExtensionName, notification.handlerName);
276
+
277
+ if (handler) {
278
+ try {
279
+ await handler.onReadUpdated(notification, read);
280
+ } catch (e) {
281
+ console.error('Error invoking notification handler', e); // eslint-disable-line no-console
282
+ }
283
+ }
284
+ }
285
+ }
286
+
255
287
  export const actions = {
256
288
  async add( { commit, dispatch, getters }: any, notification: Notification) {
257
289
  // We encrypt the notification on add - this is the only time we will encrypt it
@@ -295,6 +327,10 @@ export const actions = {
295
327
  if (notification?.preference) {
296
328
  await dispatch('prefs/set', notification.preference, { root: true });
297
329
  }
330
+
331
+ if (notification?.handlerName) {
332
+ await callNotifyHandler({ $extension: (this as any).$extension }, notification, true);
333
+ }
298
334
  },
299
335
 
300
336
  async markUnread({ commit, dispatch, getters }: any, id: string) {
@@ -309,6 +345,10 @@ export const actions = {
309
345
  value: notification.preference.unsetValue || '',
310
346
  }, { root: true });
311
347
  }
348
+
349
+ if (notification?.handlerName) {
350
+ await callNotifyHandler({ $extension: (this as any).$extension }, notification, false);
351
+ }
312
352
  },
313
353
 
314
354
  async markAllRead({ commit, dispatch, getters }: any) {
@@ -321,6 +361,13 @@ export const actions = {
321
361
  for (let i = 0; i < withPreference.length; i++) {
322
362
  await dispatch('prefs/set', withPreference[i].preference, { root: true });
323
363
  }
364
+
365
+ // For all notifications that have a handler, call the handler
366
+ const withHandler = getters.all.filter((n: Notification) => !!n.handlerName);
367
+
368
+ for (let i = 0; i < withHandler.length; i++) {
369
+ await callNotifyHandler({ $extension: (this as any).$extension }, withHandler[i], true);
370
+ }
324
371
  },
325
372
 
326
373
  remove({ commit, getters }: any, id: string) {
package/store/plugins.js CHANGED
@@ -43,7 +43,6 @@ export const rke1Supports = [
43
43
  // Map a credential driver name to a component name
44
44
  // e.g. ec2 and eks both use the 'aws' driver to share the same pool of creds.
45
45
  const driverMap = {
46
- aks: 'azure',
47
46
  amazonec2: 'aws',
48
47
  amazoneks: 'aws',
49
48
  amazonelasticcontainerservice: 'aws',
@@ -62,6 +61,9 @@ const driverToFieldMap = {
62
61
  aws: 'amazonec2',
63
62
  gcp: 'google',
64
63
  oracle: 'oci',
64
+ aks: 'azure',
65
+ eks: 'amazonec2',
66
+ gke: 'google'
65
67
  };
66
68
 
67
69
  // Machine driver fields that are probably a credential field
@@ -107,8 +109,10 @@ const driverToCloudProviderMap = {
107
109
  linode: '', // Show restricted options
108
110
  vmwarevsphere: 'rancher-vsphere',
109
111
  ovhcloudpubliccloud: '',
110
-
111
- custom: undefined // Show all options
112
+ aks: 'azure',
113
+ eks: 'aws',
114
+ gke: 'google',
115
+ custom: undefined // Show all options
112
116
  };
113
117
 
114
118
  // Dynamically loaded drivers can call this eventually to register their options
package/store/prefs.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { SETTING } from '@shell/config/settings';
2
2
  import { MANAGEMENT, STEVE } from '@shell/config/types';
3
3
  import { clone } from '@shell/utils/object';
4
+ import { getBrandMeta } from '@shell/utils/brand';
4
5
 
5
6
  const definitions = {};
6
7
  /**
@@ -119,6 +120,7 @@ export const SCALE_POOL_PROMPT = create('scale-pool-prompt', null, { parseJSON }
119
120
  export const READ_NEW_RELEASE = create('read-new-release', '', { parseJSON });
120
121
  export const READ_SUPPORT_NOTICE = create('read-support-notice', '', { parseJSON });
121
122
  export const READ_UPCOMING_SUPPORT_NOTICE = create('read-upcoming-support-notice', '', { parseJSON });
123
+ export const READ_ANNOUNCEMENTS = create('read-announcements', '', { parseJSON });
122
124
 
123
125
  // --------------------
124
126
 
@@ -521,16 +523,14 @@ export const actions = {
521
523
  setBrandStyle({ rootState, rootGetters }, dark = false) {
522
524
  if (rootState.managementReady) {
523
525
  try {
524
- const brandSetting = rootGetters['management/byId'](MANAGEMENT.SETTING, SETTING.BRAND);
526
+ const brandSetting = rootGetters['management/brand'];
525
527
 
526
- if (brandSetting && brandSetting.value && brandSetting.value !== '') {
527
- const brand = brandSetting.value;
528
-
529
- const brandMeta = require(`~shell/assets/brand/${ brand }/metadata.json`);
528
+ if (brandSetting !== '') {
529
+ const brandMeta = getBrandMeta(brandSetting);
530
530
  const hasStylesheet = brandMeta.hasStylesheet === 'true';
531
531
 
532
532
  if (hasStylesheet) {
533
- document.body.classList.add(brand);
533
+ document.body.classList.add(brandMeta);
534
534
  } else {
535
535
  // TODO option apply color at runtime
536
536
  }
package/store/type-map.js CHANGED
@@ -1895,7 +1895,7 @@ function ifHave(getters, option) {
1895
1895
  case IF_HAVE.NOT_V1_ISTIO: {
1896
1896
  return !isV1Istio(getters);
1897
1897
  }
1898
- case IF_HAVE.MULTI_CLUSTER: {
1898
+ case IF_HAVE.MULTI_CLUSTER: { // Used by harvester extension
1899
1899
  return getters.isMultiCluster;
1900
1900
  }
1901
1901
  case IF_HAVE.NEUVECTOR_NAMESPACE: {
@@ -1904,10 +1904,10 @@ function ifHave(getters, option) {
1904
1904
  case IF_HAVE.ADMIN: {
1905
1905
  return isAdminUser(getters);
1906
1906
  }
1907
- case IF_HAVE.MCM_DISABLED: {
1907
+ case IF_HAVE.MCM_DISABLED: { // There's a general MCM ff, this is conflating it with a harvester concept
1908
1908
  return !getters['isRancherInHarvester'];
1909
1909
  }
1910
- case IF_HAVE.NOT_STANDALONE_HARVESTER: {
1910
+ case IF_HAVE.NOT_STANDALONE_HARVESTER: { // Not used by harvester extension...
1911
1911
  return !getters['isStandaloneHarvester'];
1912
1912
  }
1913
1913
  default:
@@ -0,0 +1,86 @@
1
+ interface Context {
2
+ tag: string;
3
+ value: any;
4
+ hookId?: string;
5
+ description?: string;
6
+ icon?: string;
7
+ }
8
+
9
+ interface Element {
10
+ id: number;
11
+ context: Context
12
+ }
13
+
14
+ interface State {
15
+ idCounter: number;
16
+ elements: Record<string, Element>;
17
+ }
18
+
19
+ export const state = function(): State {
20
+ return {
21
+ idCounter: 0,
22
+ elements: {}
23
+ };
24
+ };
25
+
26
+ export const getters = {
27
+ all: (state: State) => {
28
+ return Object.values(state.elements)
29
+ .map((e) => e.context)
30
+ .sort((a, b) => (a.tag || '').localeCompare(b.tag || '') || 0);
31
+ },
32
+ };
33
+
34
+ export const mutations = {
35
+ add(state: State, element: Element) {
36
+ state.elements[element.id] = element;
37
+ },
38
+
39
+ update(state: State, element: Element) {
40
+ const existingElement = state.elements[element.id];
41
+
42
+ if (existingElement) {
43
+ existingElement.context = element.context;
44
+ }
45
+ },
46
+
47
+ remove(state: State, element: Element) {
48
+ delete state.elements[element.id];
49
+ }
50
+ };
51
+
52
+ let id = null;
53
+
54
+ export const actions = {
55
+ add({ commit, state }: { commit: Function, state: State }, context: Context) {
56
+ if (context?.value === undefined || !context?.tag) {
57
+ throw new Error(`[ui-context] context {{${ JSON.stringify(context) }}} is not valid`);
58
+ }
59
+
60
+ id = `ctx-${ state.idCounter++ }`;
61
+
62
+ commit('add', { id, context });
63
+
64
+ return id;
65
+ },
66
+
67
+ update({ commit, state }: { commit: Function, state: State }, element: Element) {
68
+ const old = state.elements[element.id];
69
+
70
+ if (!old) {
71
+ throw new Error(`[ui-context] element with id {{${ element.id }}} not found`);
72
+ }
73
+
74
+ commit('update', element);
75
+ },
76
+
77
+ remove({ commit, state }: { commit: Function, state: State }, id: number) {
78
+ const element = state.elements[id];
79
+
80
+ if (!element) {
81
+ throw new Error(`[ui-context] element with id {{${ id }}} not found`);
82
+ }
83
+
84
+ commit('remove', element);
85
+ }
86
+ };
package/store/wm.ts ADDED
@@ -0,0 +1,244 @@
1
+ import { STORAGE_KEY } from '@shell/components/nav/WindowManager/constants';
2
+ import { Layout, Position, Tab } from '@shell/types/window-manager';
3
+ import { addObject, removeObject } from '@shell/utils/array';
4
+ import { BOTTOM, LEFT, RIGHT } from '@shell/utils/position';
5
+
6
+ /**
7
+ * This module defines the Vuex store for the window manager, managing tabs, their positions,
8
+ * active states, panel dimensions, and user preferences.
9
+ *
10
+ * The store can be used to add, switch, and close tabs, as well as manage panel dimensions and locked positions.
11
+ * The store can be accessed also by Rancher extensions to integrate to handle the window manager.
12
+ */
13
+ export interface State {
14
+ tabs: Array<Tab>;
15
+ active: Record<Position | string, string>;
16
+ open: Record<Position | string, boolean>;
17
+ panelHeight: Record<Position | string, number | null>;
18
+ panelWidth: Record<Position | string, number | null>;
19
+ userPin: Position | string | null;
20
+ lockedPositions: Position[];
21
+ }
22
+
23
+ function moveTabByReference(tabs: Tab[], fromPosition: Position | undefined, toPosition: Position, tabId: string) {
24
+ const idx = tabs.findIndex((t) => t.id === tabId && t.position === fromPosition);
25
+
26
+ if (idx === -1) return;
27
+ const [tab] = tabs.splice(idx, 1);
28
+
29
+ tab.position = toPosition;
30
+
31
+ tabs.push(tab);
32
+ }
33
+
34
+ export const state = function() {
35
+ return {
36
+ tabs: [],
37
+ active: {},
38
+ open: {},
39
+ panelHeight: { [BOTTOM]: window.localStorage.getItem(STORAGE_KEY[BOTTOM]) },
40
+ panelWidth: {
41
+ [LEFT]: window.localStorage.getItem(STORAGE_KEY[LEFT]),
42
+ [RIGHT]: window.localStorage.getItem(STORAGE_KEY[RIGHT]),
43
+ },
44
+ userPin: null,
45
+ lockedPositions: [],
46
+ };
47
+ };
48
+
49
+ export const getters = {
50
+ byId: (state: State) => (id: string) => state.tabs.find((x) => x.id === id),
51
+ tabs: (state: State) => state.tabs,
52
+ isOpen: (state: State) => (position: string) => state.open[position],
53
+ panelWidth: (state: State) => state.panelWidth,
54
+ panelHeight: (state: State) => state.panelHeight,
55
+ userPin: (state: State) => state.userPin,
56
+ lockedPositions: (state: State) => state.lockedPositions,
57
+ };
58
+
59
+ export const mutations = {
60
+ /**
61
+ * Adds a new tab to the window manager.
62
+ *
63
+ * Usage:
64
+ *
65
+ * store.dispatch('wm/open', {
66
+ * id: PRODUCT_NAME,
67
+ * extensionId: PRODUCT_NAME,
68
+ * label: 'Label',
69
+ * component: 'LabelComponent',
70
+ * position: 'bottom',
71
+ * layouts: [
72
+ * Layout.default,
73
+ * Layout.home
74
+ * ],
75
+ * showHeader: false,
76
+ * }, { root: true });
77
+ *
78
+ * This will add a new tab with the specified properties to the window manager and set it as active.
79
+ */
80
+ addTab(state: State, tab: Tab) {
81
+ const existing = state.tabs.find((x) => x.id === tab.id);
82
+
83
+ if (tab.position === undefined || tab.position as string === 'undefined') {
84
+ tab.position = (window.localStorage.getItem(STORAGE_KEY['pin']) || BOTTOM) as Position;
85
+ }
86
+
87
+ if (!existing) {
88
+ if (state.lockedPositions.includes(BOTTOM)) {
89
+ tab.position = BOTTOM;
90
+ }
91
+
92
+ if (tab.layouts === undefined) {
93
+ tab.layouts = [Layout.default];
94
+ }
95
+
96
+ if (tab.showHeader === undefined) {
97
+ tab.showHeader = true;
98
+ }
99
+
100
+ addObject(state.tabs, tab);
101
+ }
102
+
103
+ state.active[tab.position] = tab.id;
104
+ state.open = { ...state.open, [tab.position]: true };
105
+
106
+ state.userPin = tab.position;
107
+ window.localStorage.setItem(STORAGE_KEY['pin'], tab.position);
108
+ },
109
+
110
+ /**
111
+ * Switches a tab to a different position within the window manager.
112
+ *
113
+ * Usage:
114
+ * store.commit('wm/switchTab', { tabId: 'tab1', targetPosition: LEFT });
115
+ *
116
+ * This will move the tab with ID 'tab1' to the LEFT position and update the active tab accordingly.
117
+ */
118
+ switchTab(state: State, { tabId, targetPosition }: { tabId: string, targetPosition: Position }) {
119
+ const current = { ...(state.tabs.find((x) => x.id === tabId) || {}) };
120
+
121
+ if (current) {
122
+ moveTabByReference(state.tabs, current.position, targetPosition, tabId);
123
+
124
+ state.active[targetPosition] = tabId;
125
+ state.open = { ...state.open, [targetPosition]: true };
126
+
127
+ if (current.position !== targetPosition) {
128
+ const oldPositionTabs = state.tabs.filter((t) => t.position === current.position);
129
+
130
+ if (current.position) {
131
+ state.active[current.position] = oldPositionTabs[0]?.id || '';
132
+ state.open[current.position] = oldPositionTabs.length > 0;
133
+ }
134
+ }
135
+ }
136
+
137
+ state.userPin = targetPosition;
138
+ window.localStorage.setItem(STORAGE_KEY['pin'], targetPosition);
139
+ },
140
+
141
+ closeTab(state: State, { id }: { id: string }) {
142
+ const tab = state.tabs.find((x) => x.id === id);
143
+
144
+ if ( !tab ) {
145
+ return;
146
+ }
147
+
148
+ let idx = state.tabs.indexOf(tab);
149
+
150
+ removeObject(state.tabs, tab);
151
+ if ( idx >= state.tabs.length ) {
152
+ idx = state.tabs.length - 1;
153
+ }
154
+
155
+ if ( idx >= 0 ) {
156
+ state.active[tab.position] = state.tabs[idx].id;
157
+ } else {
158
+ state.open[tab.position] = false;
159
+ }
160
+
161
+ const oldPositionTabs = state.tabs.filter((t) => t.position === tab.position);
162
+
163
+ if (tab.position) {
164
+ state.active[tab.position] = oldPositionTabs[0]?.id || '';
165
+ state.open[tab.position] = oldPositionTabs.length > 0;
166
+ }
167
+ },
168
+
169
+ removeTab(state: State, tab: Tab) {
170
+ removeObject(state.tabs, tab);
171
+ },
172
+
173
+ setOpen(state: State, { position, open }: { position: string, open: boolean }) {
174
+ state.open = { ...state.open, [position]: open };
175
+ },
176
+
177
+ setActive(state: State, { position, id }: { position: string, id: string }) {
178
+ state.active[position] = id;
179
+ },
180
+
181
+ setPanelHeight(state: State, { position, height }: { position: string, height: number | null }) {
182
+ state.panelHeight[position] = height;
183
+ window.localStorage.setItem(STORAGE_KEY[BOTTOM], `${ height }`);
184
+
185
+ for (const tab of state.tabs) {
186
+ if (tab.position === position) {
187
+ tab.containerHeight = height;
188
+ }
189
+ }
190
+ },
191
+
192
+ setPanelWidth(state: State, { position, width }: { position: Position, width: number | null }) {
193
+ state.panelWidth[position] = width;
194
+ window.localStorage.setItem(STORAGE_KEY[position as keyof typeof STORAGE_KEY], `${ width }`);
195
+
196
+ for (const tab of state.tabs) {
197
+ if (tab.position === position) {
198
+ tab.containerWidth = width;
199
+ }
200
+ }
201
+ },
202
+
203
+ /**
204
+ * Sets the user's preferred pin position for tabs in the window manager.
205
+ *
206
+ * Usage:
207
+ * store.commit('wm/setUserPin', LEFT);
208
+ *
209
+ * This will set the user's preferred pin position to LEFT and store it in local storage.
210
+ */
211
+ setUserPin(state: State, pin: string) {
212
+ state.userPin = pin;
213
+ window.localStorage.setItem(STORAGE_KEY['pin'], pin);
214
+ },
215
+
216
+ /**
217
+ * Sets the locked positions for tabs in the window manager.
218
+ *
219
+ * Usage:
220
+ * store.commit('wm/setLockedPositions', [LEFT, RIGHT]);
221
+ *
222
+ * This will lock tabs to the specified positions, preventing them from being moved elsewhere.
223
+ */
224
+ setLockedPositions(state: State, positions: Position[]) {
225
+ state.lockedPositions = positions;
226
+ }
227
+ };
228
+
229
+ export const actions = {
230
+ close({ commit }: { state: State, getters: any, commit: any }, id: string) {
231
+ if ( !id ) {
232
+ throw new Error('[wm] id is not provided');
233
+ }
234
+ commit('closeTab', { id });
235
+ },
236
+
237
+ open({ commit }: { commit: any }, tab: Tab) {
238
+ if ( !tab.id ) {
239
+ throw new Error('[wm] id is not provided');
240
+ }
241
+
242
+ commit('addTab', tab);
243
+ }
244
+ };