@rancher/shell 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (322) hide show
  1. package/assets/styles/global/_button.scss +5 -1
  2. package/assets/styles/global/_columns.scss +4 -0
  3. package/assets/styles/global/_layout.scss +1 -2
  4. package/assets/styles/global/_select.scss +1 -4
  5. package/assets/styles/themes/_dark.scss +4 -4
  6. package/assets/styles/themes/_light.scss +4 -3
  7. package/assets/styles/themes/_suse.scss +1 -1
  8. package/assets/styles/vendor/vue-select.scss +4 -3
  9. package/assets/translations/en-us.yaml +669 -73
  10. package/assets/translations/zh-hans.yaml +547 -165
  11. package/chart/monitoring/steps/uninstall-v1.vue +2 -2
  12. package/cloud-credential/azure.vue +23 -0
  13. package/cloud-credential/harvester.vue +25 -62
  14. package/cloud-credential/pnap.vue +80 -0
  15. package/components/.DS_Store +0 -0
  16. package/components/AdvancedSection.vue +9 -2
  17. package/components/Alert.vue +2 -2
  18. package/components/ButtonDropdown.vue +0 -2
  19. package/components/ButtonGroup.vue +1 -0
  20. package/components/CollapsibleCard.vue +0 -1
  21. package/components/CruResource.vue +41 -4
  22. package/components/DetailTop.vue +58 -3
  23. package/components/DisableAuthProviderModal.vue +106 -0
  24. package/{rancher-components/components/Utils/DraggableZone → components}/DraggableZone.vue +0 -0
  25. package/components/ExplorerMembers.vue +253 -30
  26. package/components/ExplorerProjectsNamespaces.vue +77 -33
  27. package/components/GrowlManager.vue +3 -3
  28. package/components/IconOrSvg.vue +149 -0
  29. package/components/LogItem.vue +69 -0
  30. package/components/PodSecurityAdmission.vue +302 -0
  31. package/components/PromptModal.vue +1 -0
  32. package/components/ResourceDetail/Masthead.vue +54 -2
  33. package/components/ResourceDetail/index.vue +12 -5
  34. package/components/ResourceList/Masthead.vue +11 -1
  35. package/components/ResourceList/ResourceLoadingIndicator.vue +12 -2
  36. package/components/ResourceList/index.vue +53 -12
  37. package/components/ResourceList/resource-list.config.js +7 -0
  38. package/components/ResourceTable.vue +31 -6
  39. package/components/SimpleBox.vue +1 -1
  40. package/components/SortableTable/THead.vue +15 -5
  41. package/components/SortableTable/index.vue +21 -10
  42. package/components/Tabbed/index.vue +20 -15
  43. package/components/__tests__/.DS_Store +0 -0
  44. package/components/__tests__/AsyncButton.test.ts +140 -0
  45. package/components/__tests__/BackLink.test.ts +33 -0
  46. package/components/__tests__/ButtonGroup.test.ts +124 -0
  47. package/components/__tests__/ClusterBadge.test.ts +32 -0
  48. package/components/__tests__/CollapsibleCard.test.ts +64 -0
  49. package/components/__tests__/ConsumptionGauge.test.ts +88 -0
  50. package/components/__tests__/CruResource.test.ts +3 -2
  51. package/components/__tests__/FixedBanner.test.ts +129 -0
  52. package/components/__tests__/GrowlManager.test.ts +147 -0
  53. package/components/__tests__/NamespaceFilter.test.ts +33 -25
  54. package/components/__tests__/PercentageBar.test.ts +32 -0
  55. package/components/__tests__/PodSecurityAdmission.test.ts +398 -0
  56. package/components/auth/AuthBanner.vue +20 -10
  57. package/components/auth/RoleDetailEdit.vue +26 -17
  58. package/components/auth/SelectPrincipal.vue +36 -5
  59. package/components/form/ArrayList.vue +3 -35
  60. package/components/form/ArrayListGrouped.vue +13 -4
  61. package/components/form/ArrayListSelect.vue +5 -5
  62. package/components/form/Error.vue +8 -0
  63. package/components/form/KeyValue.vue +39 -7
  64. package/components/form/LabeledSelect.vue +5 -2
  65. package/components/form/Labels.vue +46 -16
  66. package/components/form/Members/ClusterPermissionsEditor.vue +17 -17
  67. package/components/form/Members/MembershipEditor.vue +12 -12
  68. package/components/form/NameNsDescription.vue +1 -1
  69. package/components/form/NodeScheduling.vue +1 -1
  70. package/components/form/Probe.vue +3 -3
  71. package/components/form/ResourceQuota/Project.vue +6 -6
  72. package/components/form/ResourceTabs/index.vue +1 -6
  73. package/components/form/Security.vue +7 -6
  74. package/components/form/Select.vue +3 -2
  75. package/components/form/SelectOrCreateAuthSecret.vue +22 -29
  76. package/components/form/ServicePorts.vue +8 -0
  77. package/components/form/WorkloadPorts.vue +7 -1
  78. package/components/form/__tests__/ArrayList.test.ts +74 -0
  79. package/components/form/__tests__/ArrayListGrouped.test.ts +6 -4
  80. package/components/formatter/Checked.vue +1 -1
  81. package/components/formatter/ClusterLink.vue +5 -0
  82. package/components/formatter/IconIsDefault.vue +2 -2
  83. package/components/formatter/InternalExternalIP.vue +11 -8
  84. package/components/formatter/LiveDuration.vue +78 -0
  85. package/components/formatter/WorkloadHealthScale.vue +5 -3
  86. package/components/nav/Header.vue +6 -3
  87. package/components/nav/NamespaceFilter.vue +146 -63
  88. package/components/nav/TopLevelMenu.vue +22 -19
  89. package/components/nav/WindowManager/ContainerLogs.vue +83 -126
  90. package/components/nav/WindowManager/ContainerShell.vue +9 -7
  91. package/components/nav/WindowManager/Window.vue +2 -0
  92. package/components/nav/WindowManager/index.vue +10 -0
  93. package/config/elemental-types.js +9 -0
  94. package/config/features.js +2 -0
  95. package/config/home-links.js +4 -1
  96. package/config/pod-security-admission.ts +82 -0
  97. package/config/product/apps.js +1 -1
  98. package/config/product/auth.js +6 -5
  99. package/config/product/explorer.js +6 -6
  100. package/config/product/fleet.js +1 -1
  101. package/config/product/manager.js +6 -2
  102. package/config/secret.js +0 -1
  103. package/config/settings.ts +26 -9
  104. package/config/table-headers.js +22 -11
  105. package/config/types.js +4 -1
  106. package/content/docs/zh-hans/getting-started.md +113 -137
  107. package/content/docs/zh-hans/whats-new.md +8 -46
  108. package/creators/pkg/package-lock.json +37 -0
  109. package/creators/pkg/package.json +1 -1
  110. package/detail/catalog.cattle.io.app.vue +1 -1
  111. package/detail/pod.vue +1 -1
  112. package/detail/provisioning.cattle.io.cluster.vue +35 -9
  113. package/detail/service.vue +2 -9
  114. package/detail/workload/index.vue +0 -1
  115. package/dialog/AddClusterMemberDialog.vue +22 -28
  116. package/dialog/AddProjectMemberDialog.vue +53 -9
  117. package/dialog/DiagnosticTimingsDialog.vue +8 -7
  118. package/dialog/DrainNode.vue +44 -48
  119. package/dialog/ForceMachineRemoveDialog.vue +5 -7
  120. package/dialog/GenericPrompt.vue +15 -20
  121. package/dialog/RollbackWorkloadDialog.vue +15 -46
  122. package/dialog/RotateCertificatesDialog.vue +5 -7
  123. package/dialog/RotateEncryptionKeyDialog.vue +5 -9
  124. package/dialog/SaveAsRKETemplateDialog.vue +5 -13
  125. package/dialog/ScaleMachineDownDialog.vue +1 -1
  126. package/dialog/ScalePoolDownDialog.vue +121 -0
  127. package/edit/__tests__/management.cattle.io.setting.test.ts +3 -3
  128. package/edit/auth/azuread.vue +16 -16
  129. package/edit/auth/github.vue +8 -0
  130. package/edit/auth/googleoauth.vue +10 -1
  131. package/edit/auth/ldap/index.vue +10 -0
  132. package/edit/auth/oidc.vue +10 -0
  133. package/edit/auth/saml.vue +10 -0
  134. package/edit/autoscaling.horizontalpodautoscaler/index.vue +1 -1
  135. package/edit/cloudcredential.vue +3 -7
  136. package/edit/logging-flow/Match.vue +39 -8
  137. package/edit/logging-flow/index.vue +27 -4
  138. package/edit/management.cattle.io.podsecurityadmissionconfigurationtemplate.vue +107 -0
  139. package/edit/management.cattle.io.project.vue +8 -1
  140. package/edit/management.cattle.io.setting.vue +5 -2
  141. package/edit/management.cattle.io.user.vue +7 -1
  142. package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +23 -7
  143. package/edit/monitoring.coreos.com.alertmanagerconfig/types/email.vue +2 -2
  144. package/edit/monitoring.coreos.com.prometheusrule/GroupRules.vue +14 -6
  145. package/edit/namespace.vue +18 -4
  146. package/edit/networking.k8s.io.ingress/Certificate.vue +1 -0
  147. package/edit/networking.k8s.io.ingress/IngressClass.vue +8 -6
  148. package/edit/networking.k8s.io.ingress/RulePath.vue +12 -6
  149. package/edit/networking.k8s.io.ingress/index.vue +8 -6
  150. package/edit/persistentvolume/index.vue +30 -27
  151. package/edit/persistentvolume/plugins/cephfs.vue +29 -29
  152. package/edit/persistentvolume/plugins/csi.vue +102 -62
  153. package/edit/persistentvolume/plugins/fc.vue +19 -19
  154. package/edit/persistentvolume/plugins/iscsi.vue +45 -45
  155. package/edit/persistentvolume/plugins/rbd.vue +39 -39
  156. package/edit/persistentvolumeclaim.vue +78 -75
  157. package/edit/provisioning.cattle.io.cluster/MachinePool.vue +11 -7
  158. package/edit/provisioning.cattle.io.cluster/RegistryConfigs.vue +10 -1
  159. package/edit/provisioning.cattle.io.cluster/RegistryMirrors.vue +87 -27
  160. package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +3 -6
  161. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +93 -0
  162. package/edit/provisioning.cattle.io.cluster/import.vue +1 -1
  163. package/edit/provisioning.cattle.io.cluster/index.vue +29 -6
  164. package/edit/provisioning.cattle.io.cluster/rke2.vue +440 -152
  165. package/edit/secret/index.vue +3 -7
  166. package/edit/service.vue +3 -1
  167. package/edit/storage.k8s.io.storageclass/index.vue +100 -16
  168. package/edit/storage.k8s.io.storageclass/provisioners/driver.harvesterhci.io.vue +114 -0
  169. package/edit/workload/__tests__/index.test.ts +98 -0
  170. package/edit/workload/index.vue +58 -8
  171. package/edit/workload/mixins/workload.js +107 -70
  172. package/edit/workload/storage/ContainerMountPaths.vue +0 -10
  173. package/edit/workload/storage/emptyDir.vue +88 -0
  174. package/edit/workload/storage/ephemeralVolume/index.vue +1 -1
  175. package/edit/workload/storage/index.vue +8 -0
  176. package/edit/workload/storage/persistentVolumeClaim/index.vue +1 -1
  177. package/layouts/default.vue +57 -44
  178. package/list/__tests__/workload.test.ts +5 -2
  179. package/list/catalog.cattle.io.app.vue +1 -0
  180. package/list/cis.cattle.io.clusterscan.vue +1 -0
  181. package/list/fleet.cattle.io.bundle.vue +5 -6
  182. package/list/fleet.cattle.io.cluster.vue +6 -3
  183. package/list/fleet.cattle.io.clusterregistrationtoken.vue +5 -6
  184. package/list/fleet.cattle.io.gitrepo.vue +4 -9
  185. package/list/helm.cattle.io.projecthelmchart.vue +1 -5
  186. package/list/logging.banzaicloud.io.clusterflow.vue +4 -1
  187. package/list/logging.banzaicloud.io.flow.vue +6 -5
  188. package/list/management.cattle.io.cluster.vue +1 -0
  189. package/list/management.cattle.io.feature.vue +3 -4
  190. package/list/management.cattle.io.podsecurityadmissionconfigurationtemplate.vue +47 -0
  191. package/list/management.cattle.io.setting.vue +2 -2
  192. package/list/management.cattle.io.user.vue +4 -10
  193. package/list/monitoring.coreos.com.alertmanagerconfig.vue +2 -7
  194. package/list/node.vue +8 -5
  195. package/list/persistentvolume.vue +3 -3
  196. package/list/persistentvolumeclaim.vue +3 -4
  197. package/list/provisioning.cattle.io.cluster.vue +18 -19
  198. package/list/service.vue +6 -14
  199. package/list/workload.vue +43 -38
  200. package/machine-config/azure.vue +429 -60
  201. package/machine-config/pnap.vue +288 -0
  202. package/mixins/auth-config.js +1 -3
  203. package/mixins/browser-tab-visibility.js +8 -14
  204. package/mixins/chart.js +1 -1
  205. package/mixins/create-edit-view/impl.js +4 -0
  206. package/mixins/create-edit-view/index.js +4 -2
  207. package/mixins/resource-fetch-namespaced.js +98 -0
  208. package/mixins/resource-fetch.js +79 -45
  209. package/mixins/resource-manager.js +1 -23
  210. package/models/apps.controllerrevision.js +7 -0
  211. package/models/apps.daemonset.js +18 -0
  212. package/models/apps.deployment.js +44 -0
  213. package/models/apps.replicaset.js +7 -0
  214. package/models/apps.statefulset.js +18 -0
  215. package/models/batch.job.js +7 -14
  216. package/models/cluster/node.js +10 -2
  217. package/models/cluster.x-k8s.io.machine.js +26 -4
  218. package/models/cluster.x-k8s.io.machinedeployment.js +12 -2
  219. package/models/event.js +7 -0
  220. package/models/logging.banzaicloud.io.flow.js +4 -0
  221. package/models/management.cattle.io.cluster.js +1 -1
  222. package/models/management.cattle.io.clusterroletemplatebinding.js +1 -1
  223. package/models/management.cattle.io.globalrole.js +2 -2
  224. package/models/management.cattle.io.node.js +37 -2
  225. package/models/management.cattle.io.podsecurityadmissionconfigurationtemplate.ts +4 -0
  226. package/models/management.cattle.io.project.js +30 -11
  227. package/models/management.cattle.io.setting.js +1 -1
  228. package/models/management.cattle.io.user.js +37 -1
  229. package/models/namespace.js +42 -5
  230. package/models/persistentvolume.js +14 -2
  231. package/models/pod.js +15 -0
  232. package/models/projectroletemplatebinding.js +7 -0
  233. package/models/provisioning.cattle.io.cluster.js +61 -10
  234. package/models/rke-machine.cattle.io.pnapmachinetemplate.js +15 -0
  235. package/models/service.js +14 -13
  236. package/models/storage.k8s.io.storageclass.js +33 -18
  237. package/models/workload.js +38 -7
  238. package/nuxt.config.js +27 -17
  239. package/package.json +7 -7
  240. package/pages/about.vue +14 -2
  241. package/pages/c/_cluster/apps/charts/index.vue +4 -3
  242. package/pages/c/_cluster/apps/charts/install.vue +59 -22
  243. package/pages/c/_cluster/auth/config/_id.vue +6 -0
  244. package/pages/c/_cluster/auth/config/index.vue +8 -6
  245. package/pages/c/_cluster/auth/group.principal/assign-edit.vue +1 -1
  246. package/pages/c/_cluster/auth/roles/index.vue +1 -1
  247. package/pages/c/_cluster/explorer/index.vue +12 -6
  248. package/pages/c/_cluster/longhorn/index.vue +1 -1
  249. package/pages/c/_cluster/monitoring/alertmanagerconfig/_alertmanagerconfigid/receiver.vue +15 -4
  250. package/pages/c/_cluster/monitoring/index.vue +1 -1
  251. package/pages/c/_cluster/neuvector/index.vue +1 -1
  252. package/pages/c/_cluster/settings/performance.vue +48 -2
  253. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +34 -1
  254. package/pages/c/_cluster/uiplugins/index.vue +28 -2
  255. package/pages/diagnostic.vue +5 -4
  256. package/pages/home.vue +105 -30
  257. package/pages/prefs.vue +23 -12
  258. package/pages/rio/mesh.vue +1 -1
  259. package/pkg/dynamic-importer.lib.js +8 -0
  260. package/pkg/vue.config.js +4 -0
  261. package/plugins/dashboard-store/__tests__/mutations.spec.js +406 -0
  262. package/plugins/dashboard-store/actions.js +32 -25
  263. package/plugins/dashboard-store/getters.js +50 -33
  264. package/plugins/dashboard-store/mutations.js +134 -28
  265. package/plugins/dashboard-store/resource-class.js +21 -41
  266. package/plugins/steve/actions.js +30 -0
  267. package/plugins/steve/caches/resourceCache.js +60 -0
  268. package/plugins/steve/getters.js +44 -1
  269. package/plugins/steve/mutations.js +97 -36
  270. package/plugins/steve/resourceWatcher.js +277 -0
  271. package/plugins/steve/schema.utils.js +25 -0
  272. package/plugins/steve/subscribe.js +288 -115
  273. package/plugins/steve/worker/index.js +17 -0
  274. package/plugins/steve/worker/web-worker.advanced.js +302 -0
  275. package/plugins/steve/{web-worker.steve-sub-worker.js → worker/web-worker.basic.js} +3 -44
  276. package/rancher-components/Card/Card.vue +3 -3
  277. package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +1 -0
  278. package/rancher-components/StringList/StringList.test.ts +45 -420
  279. package/rancher-components/StringList/StringList.vue +1 -10
  280. package/rancher-components/components/Banner/Banner.test.ts +44 -0
  281. package/rancher-components/components/Banner/Banner.vue +129 -61
  282. package/rancher-components/components/Form/Checkbox/Checkbox.test.ts +13 -22
  283. package/rancher-components/components/Form/Checkbox/Checkbox.vue +8 -6
  284. package/rancher-components/components/Form/ToggleSwitch/ToggleSwitch.test.ts +9 -9
  285. package/rancher-components/components/LabeledTooltip/LabeledTooltip.vue +0 -1
  286. package/rancher-components/components/StringList/StringList.test.ts +7 -7
  287. package/rancher-components/components/StringList/StringList.vue +21 -15
  288. package/scripts/test-plugins-build.sh +8 -0
  289. package/static/loading-indicator.html +1 -1
  290. package/store/index.js +54 -3
  291. package/store/plugins.js +0 -17
  292. package/store/pnap.js +128 -0
  293. package/store/prefs.js +4 -2
  294. package/store/type-map.js +55 -13
  295. package/types/pod-security-admission.ts +36 -0
  296. package/types/shell/index.d.ts +496 -396
  297. package/utils/__tests__/object.test.ts +17 -1
  298. package/utils/__tests__/pod-security-admission.test.ts +61 -0
  299. package/utils/async.ts +36 -0
  300. package/utils/color.js +45 -0
  301. package/utils/crypto/browserHashUtils.js +18 -0
  302. package/utils/dynamic-importer.js +8 -0
  303. package/utils/install-redirect.js +1 -1
  304. package/utils/object.js +24 -0
  305. package/utils/pod-security-admission.ts +39 -0
  306. package/utils/socket.js +61 -24
  307. package/utils/string.js +2 -0
  308. package/utils/svg-filter.js +301 -0
  309. package/utils/time.js +49 -0
  310. package/utils/validators/cidr.js +4 -0
  311. package/utils/validators/formRules/__tests__/index.test.ts +23 -3
  312. package/utils/validators/formRules/index.ts +14 -0
  313. package/config/product/harvester-manager.js +0 -162
  314. package/edit/harvesterhci.io.management.cluster.vue +0 -153
  315. package/list/harvesterhci.io.management.cluster.vue +0 -241
  316. package/machine-config/harvester.vue +0 -693
  317. package/models/harvesterhci.io.management.cluster.js +0 -228
  318. package/pages/c/_cluster/harvesterManager/index.vue +0 -24
  319. package/rancher-components/Card/Card.test.ts +0 -39
  320. package/rancher-components/Utils/DraggableZone/DraggableZone.vue +0 -181
  321. package/rancher-components/Utils/DraggableZone/index.ts +0 -1
  322. package/rancher-components/components/Utils/DraggableZone/index.ts +0 -1
@@ -5,7 +5,12 @@ import Masthead from '@shell/components/ResourceList/Masthead';
5
5
  import { AGE, ROLE, STATE, PRINCIPAL } from '@shell/config/table-headers';
6
6
  import { canViewClusterPermissionsEditor } from '@shell/components/form/Members/ClusterPermissionsEditor.vue';
7
7
  import Banner from '@components/Banner/Banner.vue';
8
+ import Tabbed from '@shell/components/Tabbed/index.vue';
9
+ import Tab from '@shell/components/Tabbed/Tab.vue';
10
+ import SortableTable from '@shell/components/SortableTable';
8
11
  import { mapGetters } from 'vuex';
12
+ import { canViewProjectMembershipEditor } from '@shell/components/form/Members/ProjectMembershipEditor.vue';
13
+ import { allHash } from '@shell/utils/promise';
9
14
 
10
15
  /**
11
16
  * Explorer members page.
@@ -17,7 +22,10 @@ export default {
17
22
  components: {
18
23
  Banner,
19
24
  Masthead,
20
- ResourceTable
25
+ ResourceTable,
26
+ Tabbed,
27
+ Tab,
28
+ SortableTable
21
29
  },
22
30
 
23
31
  props: {
@@ -38,19 +46,39 @@ export default {
38
46
  `rancher/schemaFor`
39
47
  ](NORMAN.CLUSTER_ROLE_TEMPLATE_BINDING);
40
48
 
41
- const hydration = [
42
- clusterRoleTemplateBindingSchema ? this.$store.dispatch(
43
- `rancher/findAll`,
44
- { type: NORMAN.CLUSTER_ROLE_TEMPLATE_BINDING },
45
- { root: true }
46
- ) : [],
47
- clusterRoleTemplateBindingSchema ? this.$store.dispatch(`management/findAll`, { type: MANAGEMENT.CLUSTER_ROLE_TEMPLATE_BINDING }) : [],
48
- this.$store.dispatch('rancher/findAll', { type: NORMAN.PRINCIPAL }),
49
- this.$store.dispatch(`management/findAll`, { type: MANAGEMENT.USER }),
50
- this.$store.dispatch(`management/findAll`, { type: MANAGEMENT.ROLE_TEMPLATE })
51
- ];
52
-
53
- await Promise.all(hydration);
49
+ const projectRoleTemplateBindingSchema = this.$store.getters['rancher/schemaFor'](NORMAN.PROJECT_ROLE_TEMPLATE_BINDING);
50
+
51
+ this.$set(this, 'normanClusterRTBSchema', clusterRoleTemplateBindingSchema);
52
+ this.$set(this, 'normanProjectRTBSchema', projectRoleTemplateBindingSchema);
53
+
54
+ if (clusterRoleTemplateBindingSchema) {
55
+ Promise.all([
56
+ this.$store.dispatch(`rancher/findAll`, { type: NORMAN.CLUSTER_ROLE_TEMPLATE_BINDING }, { root: true }),
57
+ this.$store.dispatch(`management/findAll`, { type: MANAGEMENT.CLUSTER_ROLE_TEMPLATE_BINDING })
58
+ ]).then(([normanBindings]) => {
59
+ this.$set(this, 'normanClusterRoleTemplateBindings', normanBindings);
60
+ this.loadingClusterBindings = false;
61
+ });
62
+ }
63
+
64
+ if (projectRoleTemplateBindingSchema) {
65
+ this.$store.dispatch('rancher/findAll', { type: NORMAN.PROJECT_ROLE_TEMPLATE_BINDING }, { root: true })
66
+ .then((bindings) => {
67
+ this.$set(this, 'projectRoleTemplateBindings', bindings);
68
+ this.loadingProjectBindings = false;
69
+ });
70
+ }
71
+
72
+ this.$store.dispatch('management/findAll', { type: MANAGEMENT.PROJECT })
73
+ .then(projects => this.$set(this, 'projects', projects));
74
+
75
+ const hydration = {
76
+ normanPrincipals: this.$store.dispatch('rancher/findAll', { type: NORMAN.PRINCIPAL }),
77
+ mgmt: this.$store.dispatch(`management/findAll`, { type: MANAGEMENT.USER }),
78
+ mgmtRoleTemplates: this.$store.dispatch(`management/findAll`, { type: MANAGEMENT.ROLE_TEMPLATE }),
79
+ };
80
+
81
+ await allHash(hydration);
54
82
  },
55
83
 
56
84
  data() {
@@ -66,54 +94,249 @@ export default {
66
94
  cluster: this.$store.getters['currentCluster'].id
67
95
  }
68
96
  },
69
- resource: MANAGEMENT.CLUSTER_ROLE_TEMPLATE_BINDING,
70
- VIRTUAL_TYPES
97
+ resource: MANAGEMENT.CLUSTER_ROLE_TEMPLATE_BINDING,
98
+ normanClusterRTBSchema: null,
99
+ normanProjectRTBSchema: null,
100
+ normanClusterRoleTemplateBindings: [],
101
+ projectRoleTemplateBindings: [],
102
+ projects: [],
103
+ VIRTUAL_TYPES,
104
+ projectRoleTemplateColumns: [
105
+ STATE,
106
+ {
107
+ name: 'member',
108
+ labeKey: 'generic.name',
109
+ value: 'principalId',
110
+ formatter: 'Principal'
111
+ },
112
+ {
113
+ name: 'role',
114
+ labelKey: 'tableHeaders.role',
115
+ value: 'roleTemplate.nameDisplay'
116
+ },
117
+ { ...AGE, value: 'createdTS' }
118
+ ],
119
+ loadingProjectBindings: true,
120
+ loadingClusterBindings: true
71
121
  };
72
122
  },
73
123
 
74
124
  computed: {
125
+ ...mapGetters(['currentCluster']),
75
126
  clusterRoleTemplateBindings() {
76
- return this.$store.getters[`rancher/all`](NORMAN.CLUSTER_ROLE_TEMPLATE_BINDING).map(b => b.clusterroletemplatebinding) ;
127
+ return this.normanClusterRoleTemplateBindings.map(b => b.clusterroletemplatebinding) ;
77
128
  },
78
- ...mapGetters(['currentCluster']),
79
129
  filteredClusterRoleTemplateBindings() {
80
130
  return this.clusterRoleTemplateBindings.filter(
81
131
  b => b?.clusterName === this.$store.getters['currentCluster'].id
82
132
  );
83
133
  },
134
+ filteredProjects() {
135
+ return this.projects.reduce((all, p) => {
136
+ if (p?.spec?.clusterName === this.currentCluster.id) {
137
+ all[p.id] = p;
138
+ }
139
+
140
+ return all;
141
+ }, {});
142
+ },
143
+ filteredProjectRoleTemplateBindings() {
144
+ const out = this.projectRoleTemplateBindings.filter((rb) => {
145
+ const projectId = rb.projectId.replace(':', '/');
146
+
147
+ return !!this.filteredProjects[projectId];
148
+ });
149
+
150
+ return out;
151
+ },
152
+ projectsWithoutRoles() {
153
+ const inUse = this.filteredProjectRoleTemplateBindings.reduce((projects, binding) => {
154
+ const thisProjectId = (binding.projectId || '').replace(':', '/');
155
+
156
+ if (!projects.includes(thisProjectId)) {
157
+ projects.push(thisProjectId);
158
+ }
159
+
160
+ return projects;
161
+ }, []);
162
+
163
+ return Object.keys(this.filteredProjects).reduce((all, projectId) => {
164
+ const project = this.filteredProjects[projectId];
165
+
166
+ if ( !inUse.includes(projectId)) {
167
+ all.push(project);
168
+ }
169
+
170
+ return all;
171
+ }, []);
172
+ },
173
+
174
+ // We're using this because we need to show projects as groups even if the project doesn't have any role bindings
175
+ rowsWithFakeProjects() {
176
+ const fakeRows = this.projectsWithoutRoles.map((project) => {
177
+ return {
178
+ groupByLabel: `${ ('resourceTable.groupLabel.notInAProject') }-${ project.id }`,
179
+ isFake: true,
180
+ mainRowKey: project.id,
181
+ nameDisplay: project.spec?.displayName, // Enable filtering by the project name
182
+ project,
183
+ availableActions: [],
184
+ projectId: project.id
185
+ };
186
+ });
187
+
188
+ return [...fakeRows, ...this.filteredProjectRoleTemplateBindings];
189
+ },
84
190
  canManageMembers() {
85
191
  return canViewClusterPermissionsEditor(this.$store);
86
192
  },
193
+ canManageProjectMembers() {
194
+ return canViewProjectMembershipEditor(this.$store);
195
+ },
87
196
  isLocal() {
88
197
  return this.$store.getters['currentCluster'].isLocal;
89
- }
198
+ },
199
+ canEditProjectMembers() {
200
+ return this.normanProjectRTBSchema?.collectionMethods.find(x => x.toLowerCase() === 'post');
201
+ },
202
+ canEditClusterMembers() {
203
+ return this.normanClusterRTBSchema?.collectionMethods.find(x => x.toLowerCase() === 'post');
204
+ },
205
+ },
206
+ methods: {
207
+ getMgmtProjectId(group) {
208
+ return group.group.key.replace(':', '/');
209
+ },
210
+ getMgmtProject(group) {
211
+ return this.$store.getters['management/byId'](MANAGEMENT.PROJECT, this.getMgmtProjectId(group));
212
+ },
213
+ getProjectLabel(group) {
214
+ return this.getMgmtProject(group)?.spec?.displayName;
215
+ },
216
+ addProjectMember(group) {
217
+ this.$store.dispatch('cluster/promptModal', {
218
+ component: 'AddProjectMemberDialog',
219
+ componentProps: {
220
+ projectId: group.group.key.replace('/', ':'),
221
+ saveInModal: true
222
+ },
223
+ modalSticky: true
224
+ });
225
+ },
226
+ slotName(project) {
227
+ return `main-row:${ project.id }`;
228
+ },
90
229
  }
91
230
  };
92
231
  </script>
93
232
 
94
233
  <template>
95
- <div>
234
+ <div class="project-members">
96
235
  <Masthead
97
236
  :schema="schema"
98
237
  :resource="resource"
99
238
  :favorite-resource="VIRTUAL_TYPES.CLUSTER_MEMBERS"
100
239
  :create-location="createLocation"
101
240
  :create-button-label="t('members.createActionLabel')"
241
+ :is-creatable="false"
242
+ :type-display="t('members.clusterAndProject')"
102
243
  />
103
244
  <Banner
104
245
  v-if="isLocal"
105
246
  color="error"
106
247
  :label="t('members.localClusterWarning')"
107
248
  />
108
- <ResourceTable
109
- :schema="schema"
110
- :headers="headers"
111
- :rows="$fetchState.pending ? [] : filteredClusterRoleTemplateBindings"
112
- :groupable="false"
113
- :namespaced="false"
114
- :loading="$fetchState.pending || !currentCluster"
115
- sub-search="subSearch"
116
- :sub-fields="['nameDisplay']"
117
- />
249
+ <Tabbed>
250
+ <Tab
251
+ name="cluster-membership"
252
+ label="Cluster Membership"
253
+ >
254
+ <div
255
+ v-if="canEditClusterMembers"
256
+ class="row mb-10 cluster-add"
257
+ >
258
+ <n-link
259
+ :to="createLocation"
260
+ class="btn role-primary pull-right"
261
+ >
262
+ {{ t('members.createActionLabel') }}
263
+ </n-link>
264
+ </div>
265
+ <ResourceTable
266
+ :schema="schema"
267
+ :headers="headers"
268
+ :rows="filteredClusterRoleTemplateBindings"
269
+ :groupable="false"
270
+ :namespaced="false"
271
+ :loading="$fetchState.pending || !currentCluster || loadingClusterBindings"
272
+ sub-search="subSearch"
273
+ :sub-fields="['nameDisplay']"
274
+ />
275
+ </Tab>
276
+ <Tab
277
+ v-if="canManageProjectMembers"
278
+ name="project-membership"
279
+ label="Project Membership"
280
+ >
281
+ <SortableTable
282
+ group-by="projectId"
283
+ :loading="$fetchState.pending || !currentCluster || loadingProjectBindings"
284
+ :rows="rowsWithFakeProjects"
285
+ :headers="projectRoleTemplateColumns"
286
+ >
287
+ <template #group-by="group">
288
+ <div class="group-bar">
289
+ <div
290
+ v-trim-whitespace
291
+ class="group-tab"
292
+ >
293
+ <div
294
+ class="project-name"
295
+ v-html="getProjectLabel(group)"
296
+ />
297
+ </div>
298
+ <div class="right">
299
+ <button
300
+ v-if="canEditProjectMembers"
301
+ type="button"
302
+ class="create-namespace btn btn-sm role-secondary mr-5 right"
303
+ @click="addProjectMember(group)"
304
+ >
305
+ {{ t('members.createActionLabel') }}
306
+ </button>
307
+ </div>
308
+ </div>
309
+ </template>
310
+ <template
311
+ v-for="project in projectsWithoutRoles"
312
+ v-slot:[slotName(project)]
313
+ >
314
+ <tr
315
+ :key="project.id"
316
+ class="main-row"
317
+ >
318
+ <td
319
+ class="empty text-center"
320
+ colspan="100%"
321
+ >
322
+ {{ t('members.noRolesAssigned') }}
323
+ </td>
324
+ </tr>
325
+ </template>
326
+ </SortableTable>
327
+ </Tab>
328
+ </Tabbed>
118
329
  </div>
119
330
  </template>
331
+
332
+ <style lang='scss' scoped>
333
+ .project-members {
334
+ & ::v-deep .group-bar{
335
+ display: flex;
336
+ justify-content: space-between;
337
+ }
338
+ }
339
+ .cluster-add {
340
+ justify-content: flex-end;
341
+ }
342
+ </style>
@@ -86,7 +86,7 @@ export default {
86
86
  headers() {
87
87
  const project = {
88
88
  name: 'project',
89
- label: 'Project',
89
+ label: this.t('tableHeaders.project'),
90
90
  value: 'project.nameDisplay',
91
91
  sort: ['projectNameSort', 'nameSort'],
92
92
  };
@@ -235,6 +235,19 @@ export default {
235
235
  }
236
236
  },
237
237
  methods: {
238
+ /**
239
+ * Get PSA HTML to be displayed in the tooltips
240
+ */
241
+ getPsaTooltip(row) {
242
+ const dictionary = row.psaTooltipsDescription;
243
+ const list = Object.values(dictionary)
244
+ .sort()
245
+ .map(text => `<li>${ text }</li>`).join('');
246
+ const title = `<p>${ this.t('podSecurityAdmission.name') }: </p>`;
247
+
248
+ return `${ title }<ul class="psa-tooltip">${ list }</ul>`;
249
+ },
250
+
238
251
  userIsFilteringForSpecificNamespaceOrProject() {
239
252
  const activeFilters = this.$store.getters['namespaceFilters'];
240
253
 
@@ -342,21 +355,21 @@ export default {
342
355
  v-on="$listeners"
343
356
  >
344
357
  <template #group-by="group">
345
- <div
346
- class="project-bar"
347
- :class="{'has-description': projectDescription(group.group)}"
358
+ <div
359
+ class="project-bar"
360
+ :class="{'has-description': projectDescription(group.group)}"
348
361
  >
349
- <div
350
- v-trim-whitespace
351
- class="group-tab"
362
+ <div
363
+ v-trim-whitespace
364
+ class="group-tab"
352
365
  >
353
- <div
354
- class="project-name"
355
- v-html="projectLabel(group.group)"
366
+ <div
367
+ class="project-name"
368
+ v-html="projectLabel(group.group)"
356
369
  />
357
- <div
358
- v-if="projectDescription(group.group)"
359
- class="description text-muted text-small"
370
+ <div
371
+ v-if="projectDescription(group.group)"
372
+ class="description text-muted text-small"
360
373
  >
361
374
  {{ projectDescription(group.group) }}
362
375
  </div>
@@ -369,11 +382,11 @@ export default {
369
382
  >
370
383
  {{ t('projectNamespaces.createNamespace') }}
371
384
  </n-link>
372
- <button
373
- type="button"
374
- class="project-action btn btn-sm role-multi-action actions mr-10"
375
- :class="{invisible: !showProjectActionButton(group.group)}"
376
- @click="showProjectAction($event, group.group)"
385
+ <button
386
+ type="button"
387
+ class="project-action btn btn-sm role-multi-action actions mr-10"
388
+ :class="{invisible: !showProjectActionButton(group.group)}"
389
+ @click="showProjectAction($event, group.group)"
377
390
  >
378
391
  <i class="icon icon-actions" />
379
392
  </button>
@@ -382,22 +395,40 @@ export default {
382
395
  </template>
383
396
  <template #cell:project="{row}">
384
397
  <span v-if="row.project">{{ row.project.nameDisplay }}</span>
385
- <span
386
- v-else
387
- class="text-muted"
398
+ <span
399
+ v-else
400
+ class="text-muted"
388
401
  >&ndash;</span>
389
402
  </template>
390
- <template
391
- v-for="project in projectsWithoutNamespaces"
392
- v-slot:[slotName(project)]
403
+ <template #cell:name="{row}">
404
+ <div class="namespace-name">
405
+ <n-link
406
+ v-if="row.detailLocation"
407
+ :to="row.detailLocation"
408
+ >
409
+ {{ row.name }}
410
+ </n-link>
411
+ <span v-else>
412
+ {{ row.name }}
413
+ </span>
414
+ <i
415
+ v-if="row.hasSystemLabels"
416
+ v-tooltip="getPsaTooltip(row)"
417
+ class="icon icon-lock ml-5"
418
+ />
419
+ </div>
420
+ </template>
421
+ <template
422
+ v-for="project in projectsWithoutNamespaces"
423
+ v-slot:[slotName(project)]
393
424
  >
394
- <tr
395
- :key="project.id"
396
- class="main-row"
425
+ <tr
426
+ :key="project.id"
427
+ class="main-row"
397
428
  >
398
- <td
399
- class="empty text-center"
400
- colspan="5"
429
+ <td
430
+ class="empty text-center"
431
+ colspan="5"
401
432
  >
402
433
  {{ t('projectNamespaces.noNamespaces') }}
403
434
  </td>
@@ -405,9 +436,9 @@ export default {
405
436
  </template>
406
437
  <template #main-row:fake-empty>
407
438
  <tr class="main-row">
408
- <td
409
- class="empty text-center"
410
- colspan="5"
439
+ <td
440
+ class="empty text-center"
441
+ colspan="5"
411
442
  >
412
443
  {{ t('projectNamespaces.noProjectNoNamespaces') }}
413
444
  </td>
@@ -448,6 +479,19 @@ export default {
448
479
  }
449
480
  }
450
481
  }
482
+
483
+ .namespace-name {
484
+ display: flex;
485
+ align-items: center;
486
+ }
451
487
  }
452
488
  }
453
489
  </style>
490
+ <style lang="scss">
491
+ .psa-tooltip {
492
+ // These could pop up a lot as the mouse moves around, keep them as small and unintrusive as possible
493
+ // (easier to test with v-tooltip="{ content: getPSA(row), autoHide: false, show: true }")
494
+ margin: 3px 0;
495
+ padding: 0 8px 0 22px;
496
+ }
497
+ </style>
@@ -86,8 +86,9 @@ export default {
86
86
  >
87
87
  <div class="growl-list">
88
88
  <div
89
- v-for="growl in stack"
89
+ v-for="(growl, idx) in stack"
90
90
  :key="growl.id"
91
+ :data-testid="`growl-list-item-${idx}`"
91
92
  :class="{'growl': true, ['bg-'+growl.color]: true}"
92
93
  >
93
94
  <div
@@ -117,7 +118,7 @@ export default {
117
118
  <button
118
119
  type="button"
119
120
  class="btn btn-sm role-primary"
120
- @click="closeAll"
121
+ @click="closeAll()"
121
122
  >
122
123
  {{ t('growl.clearAll') }}
123
124
  </button>
@@ -151,7 +152,6 @@ export default {
151
152
 
152
153
  .close {
153
154
  padding: 5px;
154
- font-size: 24px;
155
155
  }
156
156
 
157
157
  .icon-container {
@@ -0,0 +1,149 @@
1
+ <script>
2
+
3
+ /**
4
+ * This component renders the icon in the top level menu.
5
+ * Icon can either be via a font via the 'icon' property or an svg via the 'src' property
6
+ *
7
+ * The trickiness here is that we want the icon to be the correct color - both normally and when hovered
8
+ * For a font icon, this is easy, since we just set the color css property
9
+ * For an svg icon included with the <img> tag this is harder - there is no way to apply css to
10
+ * the svg brought in this way - the workaround is to apply a css filter - in order to do this we
11
+ * need to generate the css filter for the required color - the code for that is in the 'svg-filter' utility
12
+ *
13
+ * We cache filters and css for given colors, so we only generate them once.
14
+ *
15
+ * This makes the code here look complex - but we are essentially generating the css filters
16
+ * and then injecting custom css into the document so that any icons included via svg will
17
+ * show with the desired colors for the theme.
18
+ */
19
+ import Vue from 'vue';
20
+ import { Solver } from '@shell/utils/svg-filter';
21
+ import { colorToRgb, mapStandardColors } from '@shell/utils/color';
22
+
23
+ const filterCache = {};
24
+ const cssCache = {};
25
+
26
+ const colors = {
27
+ header: {
28
+ color: '--header-btn-text',
29
+ hover: '--header-btn-text-hover'
30
+ },
31
+ primary: {
32
+ color: '--link',
33
+ hover: '--primary-hover-text'
34
+ }
35
+ };
36
+
37
+ export default {
38
+ name: 'IconOrSvg',
39
+ props: {
40
+ src: {
41
+ type: String,
42
+ default: () => undefined,
43
+ },
44
+ icon: {
45
+ type: String,
46
+ default: () => undefined,
47
+ },
48
+ color: {
49
+ type: String,
50
+ default: () => 'primary',
51
+ }
52
+ },
53
+
54
+ data() {
55
+ return { className: '' };
56
+ },
57
+
58
+ created() {
59
+ if (this.src) {
60
+ this.setColor();
61
+ }
62
+ },
63
+
64
+ methods: {
65
+ setColor() {
66
+ const uiColor = mapStandardColors(getComputedStyle(document.body).getPropertyValue(colors[this.color].color).trim());
67
+ const hoverColor = mapStandardColors(getComputedStyle(document.body).getPropertyValue(colors[this.color].hover).trim());
68
+
69
+ const uiColorRGB = colorToRgb(uiColor);
70
+ const hoverColorRGB = colorToRgb(hoverColor);
71
+ const uiColorStr = `${ uiColorRGB.r }-${ uiColorRGB.g }-${ uiColorRGB.b }`;
72
+ const hoverColorStr = `${ hoverColorRGB.r }-${ hoverColorRGB.g }-${ hoverColorRGB.b }`;
73
+
74
+ const className = `svg-icon-${ uiColorStr }-${ hoverColorStr }`;
75
+
76
+ if (!cssCache[className]) {
77
+ let hoverFilter = filterCache[hoverColor];
78
+
79
+ if (!hoverFilter) {
80
+ const solver = new Solver(hoverColorRGB);
81
+ const res = solver.solve();
82
+
83
+ hoverFilter = res?.filter;
84
+ filterCache[hoverColor] = hoverFilter;
85
+ }
86
+
87
+ let mainFilter = filterCache[uiColor];
88
+
89
+ if (!mainFilter) {
90
+ const solver = new Solver(uiColorRGB);
91
+ const res = solver.solve();
92
+
93
+ mainFilter = res?.filter;
94
+ filterCache[uiColor] = mainFilter;
95
+ }
96
+
97
+ // Add stylesheet (added as global styles)
98
+ const styles = `
99
+ img.${ className } {
100
+ ${ mainFilter };
101
+ }
102
+ img.${ className }:hover {
103
+ ${ hoverFilter };
104
+ }
105
+ button:hover > img.${ className } {
106
+ ${ hoverFilter };
107
+ }
108
+ a.option:hover > img.${ className } {
109
+ ${ hoverFilter };
110
+ } `;
111
+
112
+ const styleSheet = document.createElement('style');
113
+
114
+ styleSheet.innerText = styles;
115
+ document.head.appendChild(styleSheet);
116
+
117
+ cssCache[className] = true;
118
+ }
119
+
120
+ Vue.set(this, 'className', className);
121
+ }
122
+ }
123
+ };
124
+ </script>
125
+
126
+ <template>
127
+ <img
128
+ v-if="src"
129
+ :src="src"
130
+ class="svg-icon"
131
+ :class="className"
132
+ >
133
+ <i
134
+ v-else-if="icon"
135
+ class="icon group-icon"
136
+ :class="icon"
137
+ />
138
+ <i
139
+ v-else
140
+ class="icon icon-extension"
141
+ />
142
+ </template>
143
+
144
+ <style lang="scss" scoped>
145
+ .svg-icon {
146
+ height: 24px;
147
+ width: 24px;
148
+ }
149
+ </style>