@rancher/shell 3.0.12-rc.2 → 3.0.12-rc.4

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 (473) hide show
  1. package/apis/impl/apis.ts +6 -0
  2. package/apis/index.ts +26 -0
  3. package/apis/intf/resources-api/cluster-api.ts +18 -0
  4. package/apis/intf/resources-api/mgmt-api.ts +15 -0
  5. package/apis/intf/resources-api/resource-base.ts +107 -0
  6. package/apis/intf/resources-api/resource-constants.ts +147 -0
  7. package/apis/intf/resources-api/resources-api.ts +143 -0
  8. package/apis/intf/resources.ts +49 -0
  9. package/apis/intf/{modal.ts → shell-api/modal.ts} +21 -26
  10. package/apis/intf/shell-api/proxy.ts +216 -0
  11. package/apis/intf/{slide-in.ts → shell-api/slide-in.ts} +4 -3
  12. package/apis/intf/{system.ts → shell-api/system.ts} +4 -1
  13. package/apis/intf/shell.ts +12 -6
  14. package/apis/resources/__tests__/resources-api-class.test.ts +550 -0
  15. package/apis/resources/index.ts +22 -0
  16. package/apis/resources/resources-api-class.ts +187 -0
  17. package/apis/shell/__tests__/proxy.test.ts +369 -0
  18. package/apis/shell/index.ts +8 -1
  19. package/apis/shell/modal.ts +4 -1
  20. package/apis/shell/notifications.ts +9 -6
  21. package/apis/shell/proxy.ts +256 -0
  22. package/apis/shell/slide-in.ts +4 -1
  23. package/apis/vue-shim.d.ts +2 -1
  24. package/assets/data/aws-regions.json +4 -0
  25. package/assets/fonts/lato/LatoLatin-Black.woff +0 -0
  26. package/assets/fonts/lato/LatoLatin-Black.woff2 +0 -0
  27. package/assets/fonts/lato/LatoLatin-BlackItalic.woff +0 -0
  28. package/assets/fonts/lato/LatoLatin-BlackItalic.woff2 +0 -0
  29. package/assets/fonts/lato/LatoLatin-Bold.woff +0 -0
  30. package/assets/fonts/lato/LatoLatin-Bold.woff2 +0 -0
  31. package/assets/fonts/lato/LatoLatin-BoldItalic.woff +0 -0
  32. package/assets/fonts/lato/LatoLatin-BoldItalic.woff2 +0 -0
  33. package/assets/fonts/lato/LatoLatin-Heavy.woff +0 -0
  34. package/assets/fonts/lato/LatoLatin-Heavy.woff2 +0 -0
  35. package/assets/fonts/lato/LatoLatin-HeavyItalic.woff +0 -0
  36. package/assets/fonts/lato/LatoLatin-HeavyItalic.woff2 +0 -0
  37. package/assets/fonts/lato/LatoLatin-Italic.woff +0 -0
  38. package/assets/fonts/lato/LatoLatin-Italic.woff2 +0 -0
  39. package/assets/fonts/lato/LatoLatin-Light.woff +0 -0
  40. package/assets/fonts/lato/LatoLatin-Light.woff2 +0 -0
  41. package/assets/fonts/lato/LatoLatin-LightItalic.woff +0 -0
  42. package/assets/fonts/lato/LatoLatin-LightItalic.woff2 +0 -0
  43. package/assets/fonts/lato/LatoLatin-Medium.woff +0 -0
  44. package/assets/fonts/lato/LatoLatin-Medium.woff2 +0 -0
  45. package/assets/fonts/lato/LatoLatin-MediumItalic.woff +0 -0
  46. package/assets/fonts/lato/LatoLatin-MediumItalic.woff2 +0 -0
  47. package/assets/fonts/lato/LatoLatin-Regular.woff +0 -0
  48. package/assets/fonts/lato/LatoLatin-Regular.woff2 +0 -0
  49. package/assets/fonts/lato/LatoLatin-Semibold.woff +0 -0
  50. package/assets/fonts/lato/LatoLatin-Semibold.woff2 +0 -0
  51. package/assets/fonts/lato/LatoLatin-SemiboldItalic.woff +0 -0
  52. package/assets/fonts/lato/LatoLatin-SemiboldItalic.woff2 +0 -0
  53. package/assets/styles/base/_variables.scss +2 -0
  54. package/assets/styles/fonts/_fontstack.scss +132 -8
  55. package/assets/styles/global/_layout.scss +4 -0
  56. package/assets/translations/en-us.yaml +165 -45
  57. package/assets/translations/zh-hans.yaml +1 -7
  58. package/chart/monitoring/ClusterSelector.vue +0 -21
  59. package/chart/monitoring/index.vue +10 -1
  60. package/chart/monitoring/prometheus/index.vue +6 -3
  61. package/components/ActionDropdownShell.vue +2 -1
  62. package/components/CruResource.vue +161 -14
  63. package/components/CruResourceFooter.vue +9 -5
  64. package/components/ExplorerMembers.vue +8 -4
  65. package/components/ExplorerProjectsNamespaces.vue +11 -7
  66. package/components/GrowlManager.vue +4 -0
  67. package/components/InstallHelmCharts.vue +2 -2
  68. package/components/LandingPagePreference.vue +14 -5
  69. package/components/MgmtNodeList.vue +184 -0
  70. package/components/Resource/Detail/Card/StateCard/__tests__/composables.test.ts +90 -1
  71. package/components/Resource/Detail/Card/StateCard/composables.ts +57 -87
  72. package/components/Resource/Detail/Card/StatusCard/__tests__/StatusCard.test.ts +61 -0
  73. package/components/Resource/Detail/Card/StatusCard/index.vue +61 -15
  74. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +17 -1
  75. package/components/Resource/Detail/Metadata/KeyValue.vue +5 -2
  76. package/components/Resource/Detail/Metadata/KeyValueRow.vue +2 -6
  77. package/components/Resource/Detail/Metadata/index.vue +6 -0
  78. package/components/Resource/Detail/ResourcePopover/index.vue +12 -1
  79. package/components/Resource/Detail/SpacedRow.vue +3 -1
  80. package/components/Resource/Detail/TitleBar/index.vue +10 -11
  81. package/components/ResourceDetail/index.vue +1 -1
  82. package/components/ResourceList/Masthead.vue +19 -9
  83. package/components/ResourceList/index.vue +82 -1
  84. package/components/RichTranslation.vue +5 -2
  85. package/components/SelectIconGrid.vue +0 -10
  86. package/components/Setting.vue +1 -0
  87. package/components/SingleClusterInfo.vue +1 -0
  88. package/components/SortableTable/__tests__/sorting.test.ts +126 -0
  89. package/components/SortableTable/index.vue +6 -9
  90. package/components/SortableTable/selection.js +23 -5
  91. package/components/SortableTable/sorting.js +6 -3
  92. package/components/SubtleLink.vue +31 -6
  93. package/components/Tabbed/Tab.vue +29 -3
  94. package/components/Tabbed/index.vue +25 -3
  95. package/components/TableOfContents/TableOfContents.vue +109 -0
  96. package/components/TableOfContents/composables.ts +258 -0
  97. package/components/Window/ContainerShell.vue +21 -11
  98. package/components/Window/__tests__/ContainerShell.test.ts +107 -37
  99. package/components/Wizard.vue +23 -17
  100. package/components/fleet/AppCoChartGrid.vue +401 -0
  101. package/components/fleet/AppCoEmptyState.vue +127 -0
  102. package/components/fleet/AppCoPageHeader.vue +119 -0
  103. package/components/fleet/AppCoVersionSelect.vue +70 -0
  104. package/components/fleet/FleetBundles.vue +100 -12
  105. package/components/fleet/FleetClusterTargets/ClusterSelectionFields.vue +217 -0
  106. package/components/fleet/FleetClusterTargets/TargetsList.vue +123 -35
  107. package/components/fleet/FleetClusterTargets/index.vue +226 -161
  108. package/components/fleet/FleetIntro.vue +7 -3
  109. package/components/fleet/FleetNoWorkspaces.vue +7 -3
  110. package/components/fleet/FleetSecretSelector.vue +5 -3
  111. package/components/fleet/FleetValuesFrom.vue +8 -2
  112. package/components/fleet/GitRepoTargetTab.vue +0 -2
  113. package/components/fleet/HelmOpAdvancedTab.vue +19 -53
  114. package/components/fleet/HelmOpAppCoConfigTab.vue +593 -0
  115. package/components/fleet/HelmOpAppCoResourcesSection.vue +162 -0
  116. package/components/fleet/HelmOpResourcesSection.vue +82 -0
  117. package/components/fleet/HelmOpTargetOptionsSection.vue +89 -0
  118. package/components/fleet/HelmOpTargetTab.vue +64 -60
  119. package/components/fleet/HelmOpValuesTab.vue +129 -105
  120. package/components/fleet/__tests__/AppCoEmptyState.test.ts +71 -0
  121. package/components/fleet/__tests__/AppCoVersionSelect.test.ts +36 -0
  122. package/components/fleet/__tests__/ClusterSelectionFields.test.ts +62 -0
  123. package/components/fleet/__tests__/FleetClusterTargets.test.ts +402 -115
  124. package/components/fleet/__tests__/FleetClusters.test.ts +12 -12
  125. package/components/fleet/__tests__/FleetSecretSelector.test.ts +16 -0
  126. package/components/fleet/__tests__/FleetValuesFrom.test.ts +44 -0
  127. package/components/fleet/__tests__/HelmOpAppCoConfigTab.test.ts +59 -0
  128. package/components/fleet/__tests__/HelmOpAppCoResourcesSection.test.ts +62 -0
  129. package/components/fleet/__tests__/HelmOpResourcesSection.test.ts +43 -0
  130. package/components/fleet/__tests__/HelmOpTargetOptionsSection.test.ts +34 -0
  131. package/components/fleet/__tests__/HelmOpValuesTab.test.ts +39 -0
  132. package/components/fleet/__tests__/__snapshots__/AppCoEmptyState.test.ts.snap +97 -0
  133. package/components/fleet/__tests__/__snapshots__/AppCoVersionSelect.test.ts.snap +30 -0
  134. package/components/fleet/__tests__/__snapshots__/ClusterSelectionFields.test.ts.snap +209 -0
  135. package/components/fleet/__tests__/__snapshots__/HelmOpTargetOptionsSection.test.ts.snap +140 -0
  136. package/components/fleet/dashboard/Empty.vue +8 -4
  137. package/components/fleet/dashboard/ResourceCard.vue +28 -0
  138. package/components/fleet/dashboard/ResourceDetails.vue +28 -0
  139. package/components/fleet/dashboard/__tests__/ResourceCard.test.ts +87 -0
  140. package/components/form/ArrayList.vue +61 -4
  141. package/components/form/KeyValue.vue +23 -2
  142. package/components/form/LabeledSelect.vue +59 -4
  143. package/components/form/Labels.vue +22 -3
  144. package/components/form/NameNsDescription.vue +24 -5
  145. package/components/form/ResourceTabs/index.vue +1 -0
  146. package/components/form/Security.vue +6 -2
  147. package/components/form/WorkloadPorts.vue +2 -7
  148. package/components/form/__tests__/NameNsDescription.test.ts +75 -0
  149. package/components/form/__tests__/Security.test.ts +76 -0
  150. package/components/formatter/Autoscaler.vue +4 -4
  151. package/components/formatter/ClusterKubeVersion.vue +27 -0
  152. package/components/formatter/ClusterLink.vue +1 -7
  153. package/components/formatter/ClusterProvider.vue +6 -10
  154. package/components/formatter/FleetSummaryGraph.vue +0 -3
  155. package/components/formatter/InternalExternalIP.vue +10 -4
  156. package/components/formatter/MachineSummaryGraph.vue +1 -1
  157. package/components/formatter/PodsUsage.vue +2 -2
  158. package/components/formatter/ServiceTargets.vue +26 -7
  159. package/components/formatter/__tests__/Autoscaler.test.ts +19 -22
  160. package/components/formatter/__tests__/FleetSummaryGraph.test.ts +216 -0
  161. package/components/formatter/__tests__/InternalExternalIP.test.ts +132 -0
  162. package/components/formatter/__tests__/PodsUsage.test.ts +6 -10
  163. package/components/formatter/__tests__/ServiceTargets.test.ts +412 -0
  164. package/components/nav/Header.vue +4 -0
  165. package/components/nav/NamespaceFilter.vue +2 -2
  166. package/components/nav/TopLevelMenu.helper.ts +15 -3
  167. package/components/nav/TopLevelMenu.vue +22 -6
  168. package/components/nav/__tests__/Header.test.ts +15 -0
  169. package/components/nav/__tests__/TopLevelMenu.test.ts +263 -21
  170. package/components/templates/default.vue +9 -4
  171. package/components/templates/home.vue +23 -0
  172. package/components/templates/plain.vue +23 -0
  173. package/components/templates/standalone.vue +17 -0
  174. package/composables/useFormValidation.ts +93 -0
  175. package/composables/useHelmOpResources.test.ts +56 -0
  176. package/composables/useHelmOpResources.ts +32 -0
  177. package/composables/useStateColor.test.ts +325 -0
  178. package/composables/useStateColor.ts +128 -0
  179. package/composables/useVeeValidateField.test.ts +159 -0
  180. package/composables/useVeeValidateField.ts +67 -0
  181. package/config/home-links.js +1 -1
  182. package/config/labels-annotations.js +1 -0
  183. package/config/pagination-table-headers.js +18 -1
  184. package/config/product/explorer.js +17 -4
  185. package/config/product/manager.js +84 -21
  186. package/config/router/index.js +16 -0
  187. package/config/router/navigation-guards/__tests__/authentication.test.ts +130 -0
  188. package/config/router/navigation-guards/authentication.js +10 -4
  189. package/config/router/routes.js +26 -6
  190. package/config/settings.ts +0 -2
  191. package/config/table-headers.js +23 -5
  192. package/config/types.js +11 -1
  193. package/core/__tests__/plugin-products.test.ts +904 -20
  194. package/core/plugin-products-base.ts +110 -10
  195. package/core/plugin-products.ts +4 -0
  196. package/core/plugin-types.ts +194 -31
  197. package/core/plugin.ts +18 -7
  198. package/core/productDebugger.js +9 -4
  199. package/core/types-provisioning.ts +77 -31
  200. package/core/types.ts +72 -22
  201. package/detail/__tests__/pod.test.ts +41 -0
  202. package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +114 -0
  203. package/detail/__tests__/workload.test.ts +3 -152
  204. package/detail/catalog.cattle.io.clusterrepo.vue +1 -1
  205. package/detail/harvesterhci.io.management.cluster.vue +6 -2
  206. package/detail/pod.vue +1 -1
  207. package/detail/provisioning.cattle.io.cluster.vue +34 -14
  208. package/detail/workload/index.vue +12 -55
  209. package/edit/__tests__/catalog.cattle.io.clusterrepo.test.ts +248 -0
  210. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +105 -0
  211. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/General.test.ts.snap +6 -0
  212. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/index.test.ts.snap +1 -0
  213. package/edit/auth/__tests__/azuread.test.ts +247 -39
  214. package/edit/auth/__tests__/github.test.ts +234 -0
  215. package/edit/auth/__tests__/oidc.test.ts +26 -6
  216. package/edit/auth/__tests__/saml.test.ts +196 -0
  217. package/edit/auth/azuread.vue +197 -56
  218. package/edit/auth/github.vue +72 -13
  219. package/edit/auth/ldap/__tests__/index.test.ts +206 -0
  220. package/edit/auth/ldap/config.vue +8 -0
  221. package/edit/auth/ldap/index.vue +75 -1
  222. package/edit/auth/oidc.vue +119 -73
  223. package/edit/auth/saml.vue +76 -12
  224. package/edit/catalog.cattle.io.clusterrepo.vue +140 -32
  225. package/edit/fleet.cattle.io.helmop.vue +491 -136
  226. package/edit/management.cattle.io.user.vue +5 -2
  227. package/edit/networking.k8s.io.ingress/DefaultBackend.vue +13 -4
  228. package/edit/networking.k8s.io.ingress/RulePath.vue +8 -4
  229. package/edit/networking.k8s.io.ingress/index.vue +75 -20
  230. package/edit/provisioning.cattle.io.cluster/__tests__/MachinePool.test.ts +104 -0
  231. package/edit/provisioning.cattle.io.cluster/index.vue +11 -7
  232. package/edit/provisioning.cattle.io.cluster/rke2.vue +92 -14
  233. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +22 -0
  234. package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +37 -4
  235. package/edit/provisioning.cattle.io.cluster/tabs/registries/__tests__/RegistryConfigs.test.ts +132 -7
  236. package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +2 -1
  237. package/edit/secret/__tests__/ssh.test.ts +5 -6
  238. package/edit/secret/basic.vue +31 -0
  239. package/edit/secret/index.vue +68 -17
  240. package/edit/secret/registry.vue +38 -0
  241. package/edit/secret/ssh.vue +29 -0
  242. package/edit/secret/tls.vue +30 -0
  243. package/edit/service.vue +4 -4
  244. package/edit/workload/Upgrading.vue +3 -3
  245. package/edit/workload/__tests__/Upgrading.test.ts +6 -9
  246. package/edit/workload/mixins/workload.js +2 -1
  247. package/list/fleet.cattle.io.bundle.vue +7 -104
  248. package/list/fleet.cattle.io.clusterregistrationtoken.vue +20 -0
  249. package/list/group.principal.vue +5 -4
  250. package/list/harvesterhci.io.management.cluster.vue +8 -9
  251. package/list/management.cattle.io.user.vue +12 -9
  252. package/list/provisioning.cattle.io.cluster.vue +268 -180
  253. package/list/utils/management.cattle.io.cluster.utils.ts +128 -0
  254. package/mixins/__tests__/auth-config.test.ts +90 -0
  255. package/mixins/__tests__/chart.test.ts +206 -0
  256. package/mixins/__tests__/resource-fetch-api-pagination.test.ts +48 -0
  257. package/mixins/auth-config.js +7 -0
  258. package/mixins/brand.js +2 -1
  259. package/mixins/chart.js +22 -9
  260. package/mixins/child-hook.js +12 -6
  261. package/mixins/create-edit-view/impl.js +5 -3
  262. package/mixins/resource-fetch-api-pagination.js +62 -6
  263. package/models/__tests__/catalog.cattle.io.clusterrepo.test.ts +57 -0
  264. package/models/__tests__/compliance.cattle.io.clusterscan.test.ts +144 -0
  265. package/models/__tests__/ext.cattle.io.kubeconfig.test.ts +67 -67
  266. package/models/__tests__/fleet-application.test.ts +175 -0
  267. package/models/__tests__/fleet.cattle.io.bundle.test.ts +169 -0
  268. package/models/__tests__/fleet.cattle.io.helmop.test.ts +84 -0
  269. package/models/__tests__/management.cattle.io.cluster.test.ts +1 -1
  270. package/models/__tests__/management.cattle.io.node.ts +28 -5
  271. package/models/__tests__/management.cattle.io.nodepool.ts +5 -4
  272. package/models/__tests__/namespace.test.ts +36 -0
  273. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +81 -11
  274. package/models/__tests__/workload.test.ts +401 -26
  275. package/models/base-cluster.x-k8s.io.js +26 -0
  276. package/models/catalog.cattle.io.clusterrepo.js +28 -4
  277. package/models/cluster.js +1 -1
  278. package/models/cluster.x-k8s.io.machine.js +4 -22
  279. package/models/cluster.x-k8s.io.machinedeployment.js +2 -20
  280. package/models/cluster.x-k8s.io.machineset.js +2 -20
  281. package/models/compliance.cattle.io.clusterscan.js +165 -2
  282. package/models/ext.cattle.io.kubeconfig.ts +4 -7
  283. package/models/fleet-application.js +7 -1
  284. package/models/fleet.cattle.io.helmop.js +20 -1
  285. package/models/management.cattle.io.cluster.js +434 -41
  286. package/models/management.cattle.io.node.js +50 -7
  287. package/models/management.cattle.io.nodepool.js +1 -1
  288. package/models/namespace.js +1 -1
  289. package/models/networking.k8s.io.ingress.js +12 -4
  290. package/models/pod.js +33 -1
  291. package/models/provisioning.cattle.io.cluster.js +51 -334
  292. package/models/rke.cattle.io.etcdsnapshot.js +1 -2
  293. package/models/workload.js +108 -13
  294. package/models/workload.service.js +5 -0
  295. package/package.json +22 -39
  296. package/pages/__tests__/readme.test.ts +49 -0
  297. package/pages/about.vue +5 -6
  298. package/pages/auth/login.vue +0 -35
  299. package/pages/auth/setup.vue +13 -3
  300. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +2 -2
  301. package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +10 -1
  302. package/pages/c/_cluster/apps/charts/__tests__/chart.test.ts +76 -0
  303. package/pages/c/_cluster/apps/charts/__tests__/index.test.ts +93 -0
  304. package/pages/c/_cluster/apps/charts/chart.vue +62 -9
  305. package/pages/c/_cluster/apps/charts/index.vue +48 -10
  306. package/pages/c/_cluster/apps/charts/install.vue +122 -113
  307. package/pages/c/_cluster/auth/roles/index.vue +5 -4
  308. package/pages/c/_cluster/explorer/__tests__/index.test.ts +23 -25
  309. package/pages/c/_cluster/explorer/index.vue +5 -49
  310. package/pages/c/_cluster/explorer/workload-dashboard/ByNamespaceSection.vue +31 -0
  311. package/pages/c/_cluster/explorer/workload-dashboard/ByStateSection.vue +138 -0
  312. package/pages/c/_cluster/explorer/workload-dashboard/ByTypeSection.vue +30 -0
  313. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadCard.vue +155 -0
  314. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadNamespaceCard.vue +142 -0
  315. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadTypeCard.vue +159 -0
  316. package/pages/c/_cluster/explorer/workload-dashboard/__tests__/composable.test.ts +561 -0
  317. package/pages/c/_cluster/explorer/workload-dashboard/composable.ts +440 -0
  318. package/pages/c/_cluster/explorer/workload-dashboard/index.vue +187 -0
  319. package/pages/c/_cluster/explorer/workload-dashboard/types.ts +80 -0
  320. package/pages/c/_cluster/fleet/application/create.vue +187 -136
  321. package/pages/c/_cluster/fleet/application/index.vue +5 -3
  322. package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailBody.vue +338 -0
  323. package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailHeader.vue +121 -0
  324. package/pages/c/_cluster/fleet/application/suse-app-collection/chart.vue +369 -0
  325. package/pages/c/_cluster/fleet/application/suse-app-collection/charts.vue +248 -0
  326. package/pages/c/_cluster/fleet/application/suse-app-collection/credentials.vue +310 -0
  327. package/pages/c/_cluster/fleet/index.vue +2 -2
  328. package/pages/c/_cluster/istio/__tests__/istio.index.test.ts +194 -0
  329. package/pages/c/_cluster/istio/index.vue +21 -6
  330. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +1 -0
  331. package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +815 -2
  332. package/pages/c/_cluster/uiplugins/index.vue +218 -197
  333. package/pages/diagnostic.vue +13 -17
  334. package/pages/fail-whale.vue +30 -7
  335. package/pages/home.vue +93 -306
  336. package/pages/readme.vue +88 -0
  337. package/plugins/clean-html.d.ts +9 -0
  338. package/plugins/dashboard-store/__tests__/resource-class.test.ts +181 -0
  339. package/plugins/dashboard-store/actions.js +40 -18
  340. package/plugins/dashboard-store/resource-class.js +67 -9
  341. package/plugins/steve/__tests__/actions.test.ts +212 -0
  342. package/plugins/steve/__tests__/subscribe.spec.ts +6 -3
  343. package/plugins/steve/actions.js +96 -0
  344. package/plugins/steve/steve-pagination-utils.ts +12 -4
  345. package/plugins/steve/subscribe.js +35 -5
  346. package/rancher-components/Accordion/Accordion.vue +53 -9
  347. package/rancher-components/Form/Checkbox/Checkbox.vue +14 -0
  348. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +10 -4
  349. package/rancher-components/Form/LabeledInput/LabeledInput.vue +7 -52
  350. package/rancher-components/Form/Radio/RadioButton.vue +17 -1
  351. package/rancher-components/Form/Radio/RadioGroup.vue +10 -0
  352. package/rancher-components/Pill/RcTag/RcTag.vue +3 -2
  353. package/rancher-components/RcButton/RcButton.test.ts +140 -1
  354. package/rancher-components/RcButton/RcButton.vue +126 -17
  355. package/rancher-components/RcButton/types.ts +3 -0
  356. package/rancher-components/RcDropdown/RcDropdownTrigger.vue +10 -8
  357. package/rancher-components/RcItemCard/RcItemCard.test.ts +18 -0
  358. package/rancher-components/RcItemCard/RcItemCard.vue +2 -2
  359. package/rancher-components/RcSection/RcSection.vue +28 -3
  360. package/scripts/extension/helm/package/Dockerfile +1 -1
  361. package/scripts/test-plugins-build.sh +2 -1
  362. package/store/__tests__/catalog.test.ts +115 -1
  363. package/store/__tests__/notifications.test.ts +434 -0
  364. package/store/__tests__/type-map.test.ts +556 -1
  365. package/store/action-menu.js +8 -3
  366. package/store/auth.js +1 -1
  367. package/store/aws.js +27 -16
  368. package/store/catalog.js +84 -3
  369. package/store/digitalocean.js +20 -38
  370. package/store/index.js +2 -0
  371. package/store/linode.js +25 -40
  372. package/store/plugins.js +7 -4
  373. package/store/pnap.js +1 -0
  374. package/store/type-map.js +111 -29
  375. package/tsconfig.paths.json +8 -8
  376. package/types/components/buttonGroup.ts +5 -0
  377. package/types/kube/kube-api.ts +14 -1
  378. package/types/rancher/steve.api.ts +12 -12
  379. package/types/resources/settings.d.ts +2 -1
  380. package/types/shell/index.d.ts +206 -72
  381. package/types/store/dashboard-store.types.ts +108 -11
  382. package/types/store/pagination.types.ts +6 -3
  383. package/utils/__tests__/alertmanagerconfig.test.ts +117 -0
  384. package/utils/__tests__/async.test.ts +87 -0
  385. package/utils/__tests__/auth.test.ts +273 -0
  386. package/utils/__tests__/aws.test.ts +140 -0
  387. package/utils/__tests__/banners.test.ts +176 -0
  388. package/utils/__tests__/chart.test.ts +64 -1
  389. package/utils/__tests__/color.test.ts +226 -0
  390. package/utils/__tests__/computed.test.ts +193 -0
  391. package/utils/__tests__/cspAdaptor.test.ts +163 -0
  392. package/utils/__tests__/dom.test.ts +81 -0
  393. package/utils/__tests__/duration.test.ts +176 -0
  394. package/utils/__tests__/dynamic-importer.test.ts +102 -0
  395. package/utils/__tests__/fleet-appco.test.ts +312 -0
  396. package/utils/__tests__/fleet.test.ts +340 -0
  397. package/utils/__tests__/ingress.test.ts +553 -0
  398. package/utils/__tests__/kube.test.ts +68 -0
  399. package/utils/__tests__/monitoring.test.ts +130 -0
  400. package/utils/__tests__/namespace-filter.test.ts +109 -0
  401. package/utils/__tests__/object.test.ts +22 -0
  402. package/utils/__tests__/pagination-utils.test.ts +361 -0
  403. package/utils/__tests__/parse-externalid.test.ts +137 -0
  404. package/utils/__tests__/perf-setting.utils.test.ts +98 -0
  405. package/utils/__tests__/platform.test.ts +91 -0
  406. package/utils/__tests__/poller-sequential.test.ts +177 -0
  407. package/utils/__tests__/poller.test.ts +170 -0
  408. package/utils/__tests__/position.test.ts +237 -0
  409. package/utils/__tests__/promise.test.ts +346 -0
  410. package/utils/__tests__/provider.test.ts +51 -1
  411. package/utils/__tests__/queue.test.ts +232 -0
  412. package/utils/__tests__/release-notes.test.ts +221 -0
  413. package/utils/__tests__/router.test.js +254 -1
  414. package/utils/__tests__/select.test.ts +208 -0
  415. package/utils/__tests__/settings.test.ts +140 -0
  416. package/utils/__tests__/sort-utils.test.ts +301 -0
  417. package/utils/__tests__/string-utils.test.ts +798 -0
  418. package/utils/__tests__/string.test.ts +23 -1
  419. package/utils/__tests__/style.test.ts +154 -0
  420. package/utils/__tests__/svg-filter.test.ts +184 -0
  421. package/utils/__tests__/time.test.ts +265 -1
  422. package/utils/__tests__/title.test.ts +47 -0
  423. package/utils/__tests__/units.test.ts +417 -0
  424. package/utils/__tests__/versions.test.ts +128 -0
  425. package/utils/__tests__/width.test.ts +53 -0
  426. package/utils/__tests__/window.test.ts +158 -0
  427. package/utils/__tests__/xccdf.test.ts +511 -0
  428. package/utils/chart.js +36 -0
  429. package/utils/crypto/__tests__/browserHashUtils.test.ts +98 -0
  430. package/utils/crypto/__tests__/index.test.ts +144 -0
  431. package/utils/duration.ts +104 -0
  432. package/utils/dynamic-content/__tests__/notification-handler.test.ts +196 -0
  433. package/utils/dynamic-content/info.ts +2 -1
  434. package/utils/error.js +13 -0
  435. package/utils/fleet-appco.ts +323 -0
  436. package/utils/fleet.ts +13 -3
  437. package/utils/gatekeeper/__tests__/util.test.ts +174 -0
  438. package/utils/gc/__tests__/gc-interval.test.ts +119 -0
  439. package/utils/gc/__tests__/gc-root-store.test.ts +225 -0
  440. package/utils/gc/__tests__/gc-route-changed.test.ts +96 -0
  441. package/utils/gc/__tests__/gc.test.ts +487 -0
  442. package/utils/ingress.ts +9 -1
  443. package/utils/object.js +22 -2
  444. package/utils/pagination-utils.ts +2 -1
  445. package/utils/provider.ts +12 -0
  446. package/utils/string.js +25 -2
  447. package/utils/uiplugins.ts +5 -5
  448. package/utils/validators/__tests__/cluster-name.test.ts +110 -0
  449. package/utils/validators/__tests__/container-images.test.ts +104 -0
  450. package/utils/validators/__tests__/cron-schedule.test.ts +79 -0
  451. package/utils/validators/__tests__/flow-output.test.ts +91 -0
  452. package/utils/validators/__tests__/index.test.ts +481 -0
  453. package/utils/validators/__tests__/kubernetes-name.test.ts +163 -0
  454. package/utils/validators/__tests__/logging-outputs.test.ts +58 -0
  455. package/utils/validators/__tests__/misc-validators.test.ts +246 -0
  456. package/utils/validators/__tests__/monitoring-route.test.ts +119 -0
  457. package/utils/validators/__tests__/pod-affinity.test.ts +382 -0
  458. package/utils/validators/__tests__/prometheusrule.test.ts +211 -0
  459. package/utils/validators/__tests__/role-template.test.ts +149 -0
  460. package/utils/validators/__tests__/service.test.ts +283 -0
  461. package/utils/validators/__tests__/setting.test.js +32 -0
  462. package/utils/validators/formRules/__tests__/index.test.ts +50 -0
  463. package/utils/validators/formRules/index.ts +5 -5
  464. package/utils/validators/machine-pool.ts +1 -1
  465. package/utils/validators/setting.js +18 -3
  466. package/utils/xccdf.ts +415 -0
  467. package/vue.config.js +1 -1
  468. package/assets/fonts/lato/lato-v17-latin-700.woff +0 -0
  469. package/assets/fonts/lato/lato-v17-latin-700.woff2 +0 -0
  470. package/assets/fonts/lato/lato-v17-latin-regular.woff +0 -0
  471. package/assets/fonts/lato/lato-v17-latin-regular.woff2 +0 -0
  472. package/pages/support/index.vue +0 -264
  473. package/utils/duration.js +0 -43
@@ -0,0 +1,184 @@
1
+ <script>
2
+ import { MANAGEMENT } from '@shell/config/types';
3
+ import ResourceTable from '@shell/components/ResourceTable';
4
+ import {
5
+ INTERNAL_EXTERNAL_IP,
6
+ STATE, NAME as NAME_COL, AGE, MANAGEMENT_NODE_OS
7
+ } from '@shell/config/table-headers';
8
+
9
+ // exclude roles column - it is not necessarily used (or used the same way) outside of Rancher provisioning
10
+ export const DEFAULT_HEADERS = [STATE, {
11
+ ...NAME_COL,
12
+ value: 'status.nodeName',
13
+ formatterOpts: { reference: 'kubeNodeDetailLocation' }
14
+ }, INTERNAL_EXTERNAL_IP, MANAGEMENT_NODE_OS, AGE];
15
+
16
+ export default {
17
+ name: 'ClusterScopedManagementNodeList',
18
+
19
+ components: { ResourceTable },
20
+
21
+ props: {
22
+ resource: {
23
+ type: Object,
24
+ default: () => {
25
+ return {};
26
+ }
27
+ },
28
+
29
+ // override default management node schema headers
30
+ headers: {
31
+ type: Array,
32
+ default: () => DEFAULT_HEADERS
33
+ },
34
+
35
+ // function to get node group for a given node, used for grouping nodes by pool in the table
36
+ // result should be an object with name and (optionally) description properties
37
+ getNodeGroup: {
38
+ type: Function,
39
+ default: null
40
+ }
41
+
42
+ },
43
+
44
+ /**
45
+ * avoid fetching ALL nodes to find this cluster's nodes
46
+ * mgmt nodes do not have labels that can be used with a labelSelector action
47
+ * neither the prov cluster nor mgmt cluster list mgmt nodes in their metadata.relationships nor are the node names listed in either cluster's status
48
+ * BUT mgmt nodes are scoped to a cluster's namespace, so we can fetch only mgmt nodes in the cluster's namespace
49
+ */
50
+ async fetch() {
51
+ const canList = this.$store.getters['management/canList'](MANAGEMENT.NODE);
52
+
53
+ if ( canList ) {
54
+ const hasAllMgmtNodes = this.$store.getters['management/haveAll'](MANAGEMENT.NODE);
55
+
56
+ if (hasAllMgmtNodes) {
57
+ this.mgmtNodes = this.$store.getters['management/all'](MANAGEMENT.NODE);
58
+ } else {
59
+ const res = await this.$store.dispatch('management/findLabelSelector', {
60
+ type: MANAGEMENT.NODE,
61
+ matching: {
62
+ namespace: this.resource.mgmtClusterId,
63
+ labelSelector: {
64
+ matchExpressions: [
65
+ {
66
+ key: 'management.cattle.io/nodename',
67
+ operator: 'Exists',
68
+ }
69
+ ]
70
+ }
71
+ }
72
+ } );
73
+
74
+ this.mgmtNodes = res;
75
+ }
76
+ }
77
+ },
78
+
79
+ data() {
80
+ return {
81
+ mgmtNodes: [],
82
+
83
+ mgmtNodeSchema: this.$store.getters[`management/schemaFor`](MANAGEMENT.NODE),
84
+
85
+ noneGroupOption: {
86
+ tooltipKey: 'resourceTable.groupBy.none',
87
+ icon: 'icon-list-flat',
88
+ value: 'none',
89
+ },
90
+
91
+ poolGroupOption: {
92
+ tooltipKey: 'resourceTable.groupBy.pool',
93
+ icon: 'icon-cluster',
94
+ value: 'poolRef',
95
+ field: 'poolRef.name'
96
+ }
97
+
98
+ };
99
+ },
100
+
101
+ computed: {
102
+ nodes() {
103
+ return this.mgmtNodes.filter((x) => x.mgmtClusterId === this.resource.mgmtClusterId).map((node) => {
104
+ const poolRef = typeof this.getNodeGroup === 'function' ? this.getNodeGroup(node) : null;
105
+
106
+ node.poolRef = poolRef;
107
+
108
+ return node;
109
+ });
110
+ },
111
+ },
112
+ };
113
+ </script>
114
+
115
+ <template>
116
+ <ResourceTable
117
+ v-bind="$attrs"
118
+ :schema="mgmtNodeSchema"
119
+ :headers="headers"
120
+ :rows="nodes"
121
+ :ignore-filter="true"
122
+ group-ref="poolRef"
123
+ data-testid="mgmt-node-table"
124
+ :group-options="[noneGroupOption, poolGroupOption]"
125
+ >
126
+ <template #main-row:isFake="{fullColspan}">
127
+ <tr class="main-row">
128
+ <td
129
+ :colspan="fullColspan"
130
+ class="no-entries"
131
+ >
132
+ {{ t('node.list.noNodes') }}
133
+ </td>
134
+ </tr>
135
+ </template>
136
+
137
+ <template #group-by="{group}">
138
+ <div
139
+ class="pool-row"
140
+ :class="{'has-description':group.ref}"
141
+ >
142
+ <div
143
+ v-trim-whitespace
144
+ class="group-tab"
145
+ >
146
+ {{ group.ref?.name || t('resourceTable.groupLabel.notInANodePool') }}
147
+ <div
148
+ v-if="group.ref && group.ref.description"
149
+ v-clean-html="group.ref.description"
150
+ class="description text-muted text-small"
151
+ />
152
+ </div>
153
+ </div>
154
+ </template>
155
+ </ResourceTable>
156
+ </template>
157
+
158
+ <style scoped lang="scss">
159
+ .pool-row {
160
+ display: flex;
161
+ align-items: center;
162
+ justify-content: space-between;
163
+
164
+ &.has-description {
165
+ .group-tab {
166
+ &, &::after {
167
+ height: 50px;
168
+ }
169
+
170
+ &::after {
171
+ right: -20px;
172
+ }
173
+
174
+ .description {
175
+ margin-top: -20px;
176
+ }
177
+ }
178
+ }
179
+ .group-header-buttons {
180
+ align-items: center;
181
+ display: flex;
182
+ }
183
+ }
184
+ </style>
@@ -1,4 +1,4 @@
1
- import { useResourceCardRow } from '@shell/components/Resource/Detail/Card/StateCard/composables';
1
+ import { useResourceCardRow, useResourceCardRowFromRelationships } from '@shell/components/Resource/Detail/Card/StateCard/composables';
2
2
 
3
3
  describe('useResourceCardRow', () => {
4
4
  describe('with default keys', () => {
@@ -140,3 +140,92 @@ describe('useResourceCardRow', () => {
140
140
  });
141
141
  });
142
142
  });
143
+
144
+ describe('useResourceCardRowFromRelationships', () => {
145
+ it('should return empty props for empty relationships', () => {
146
+ const result = useResourceCardRowFromRelationships('Refers to', []);
147
+
148
+ expect(result.label).toBe('Refers to');
149
+ expect(result.color).toBeUndefined();
150
+ expect(result.counts).toBeUndefined();
151
+ });
152
+
153
+ it('should aggregate relationship states', () => {
154
+ const rels = [
155
+ { toType: 'configmap', state: 'active' },
156
+ { toType: 'secret', state: 'active' },
157
+ { toType: 'serviceaccount', state: 'error' }
158
+ ];
159
+
160
+ const result = useResourceCardRowFromRelationships('Refers to', rels);
161
+
162
+ expect(result.counts).toHaveLength(2);
163
+ expect(result.counts).toContainEqual(expect.objectContaining({ label: 'active', count: 2 }));
164
+ expect(result.counts).toContainEqual(expect.objectContaining({ label: 'error', count: 1 }));
165
+ });
166
+
167
+ it('should default missing state to "missing"', () => {
168
+ const rels = [
169
+ { toType: 'configmap' },
170
+ { toType: 'secret', state: 'active' }
171
+ ];
172
+
173
+ const result = useResourceCardRowFromRelationships('Refers to', rels);
174
+
175
+ expect(result.counts).toContainEqual(expect.objectContaining({
176
+ label: 'missing', count: 1, color: 'warning'
177
+ }));
178
+ expect(result.counts).toContainEqual(expect.objectContaining({
179
+ label: 'active', count: 1, color: 'success'
180
+ }));
181
+ });
182
+
183
+ it('should set the highest alert color as main color', () => {
184
+ const rels = [
185
+ { toType: 'configmap', state: 'active' },
186
+ { toType: 'secret', state: 'error' }
187
+ ];
188
+
189
+ const result = useResourceCardRowFromRelationships('Refers to', rels);
190
+
191
+ expect(result.color).toBe('error');
192
+ });
193
+
194
+ it('should sort by alert level then by count', () => {
195
+ const rels = [
196
+ { toType: 'a', state: 'active' },
197
+ { toType: 'b', state: 'active' },
198
+ { toType: 'c', state: 'active' },
199
+ { toType: 'd', state: 'error' },
200
+ { toType: 'e', state: 'warning' },
201
+ { toType: 'f', state: 'warning' }
202
+ ];
203
+
204
+ const result = useResourceCardRowFromRelationships('Refers to', rels);
205
+
206
+ expect(result.counts![0].color).toBe('error');
207
+ expect(result.counts![1].color).toBe('warning');
208
+ expect(result.counts![2].color).toBe('success');
209
+ });
210
+
211
+ it('should pass the to parameter through', () => {
212
+ const to = { hash: '#related' };
213
+ const result = useResourceCardRowFromRelationships('Refers to', [], to);
214
+
215
+ expect(result.to).toStrictEqual(to);
216
+ });
217
+
218
+ it('should handle all relationships having no state', () => {
219
+ const rels = [
220
+ { toType: 'configmap' },
221
+ { toType: 'secret' }
222
+ ];
223
+
224
+ const result = useResourceCardRowFromRelationships('Refers to', rels);
225
+
226
+ expect(result.counts).toHaveLength(1);
227
+ expect(result.counts![0]).toStrictEqual(expect.objectContaining({
228
+ label: 'missing', count: 2, color: 'warning'
229
+ }));
230
+ });
231
+ });
@@ -1,11 +1,26 @@
1
1
  import { Count, Props as ResourceRowProps } from '@shell/components/Resource/Detail/ResourceRow.types';
2
- import { useI18n } from '@shell/composables/useI18n';
3
- import { INGRESS, SERVICE } from '@shell/config/types';
4
2
  import { isHigherAlert, StateColor } from '@shell/utils/style';
5
- import { computed, Ref, toValue } from 'vue';
6
- import { useStore } from 'vuex';
7
- import { Props as StateCardProps } from '@shell/components/Resource/Detail/Card/StateCard/types';
8
3
  import { RouteLocationRaw } from 'vue-router';
4
+ import { simpleColorForState, stateDisplay as stateDisplayFn } from '@shell/plugins/dashboard-store/resource-class';
5
+
6
+ interface Tuple extends Count {
7
+ color: StateColor;
8
+ }
9
+
10
+ export interface SummaryCountValue {
11
+ total: number;
12
+ namespace?: Record<string, number>;
13
+ }
14
+
15
+ export interface SummaryEntry {
16
+ property: string;
17
+ counts: Record<string, SummaryCountValue>;
18
+ }
19
+
20
+ export interface SummaryResult {
21
+ count: number;
22
+ summary: SummaryEntry[] | null;
23
+ }
9
24
 
10
25
  export function useResourceCardRow(label: string, resources: any[], stateColorKey = 'stateSimpleColor', stateDisplayKey = 'stateDisplay', to?: RouteLocationRaw): ResourceRowProps {
11
26
  const agg: any = {};
@@ -20,12 +35,9 @@ export function useResourceCardRow(label: string, resources: any[], stateColorKe
20
35
  agg[state].count++;
21
36
  });
22
37
 
23
- interface Tuple extends Count {
24
- color: StateColor;
25
- }
26
38
  const tuples: Tuple[] = Object.values(agg);
27
39
 
28
- tuples.sort((left: any, right: any) => {
40
+ tuples.sort((left: Tuple, right: Tuple) => {
29
41
  if (isHigherAlert(left.color, right.color)) {
30
42
  return -1;
31
43
  }
@@ -49,94 +61,52 @@ export function useResourceCardRow(label: string, resources: any[], stateColorKe
49
61
  };
50
62
  }
51
63
 
52
- export interface Pairs {
53
- label: string;
54
- to?: RouteLocationRaw;
55
- resources: any[];
56
- }
57
-
58
- export function useDefaultResources(pairs: Ref<Pairs[]>) {
59
- const pairsValue = toValue(pairs);
60
- const rows = computed(() => pairsValue.map(({ label, resources, to }) => useResourceCardRow(label, resources, undefined, undefined, to)));
61
-
62
- return rows;
63
- }
64
-
65
- export function useResourcePair(label: string, type: string, resources?: any[]) {
66
- const store = useStore();
64
+ /**
65
+ * Builds a ResourceRowProps from relationship state data.
66
+ * Each relationship entry includes a `state` field (e.g. "active", "error").
67
+ */
68
+ export function useResourceCardRowFromRelationships(label: string, relationships: any[], to?: RouteLocationRaw): ResourceRowProps {
69
+ if (!relationships.length) {
70
+ return {
71
+ label, color: undefined, counts: undefined, to
72
+ };
73
+ }
67
74
 
68
- return computed(() => {
69
- if (!resources) {
70
- return undefined;
71
- }
75
+ const agg: Record<string, { color: StateColor; label: string; count: number }> = {};
72
76
 
73
- const resourcesValue = toValue(resources);
77
+ relationships.forEach((r: any) => {
78
+ const state = (r.state || 'missing').toLowerCase();
79
+ const color = simpleColorForState(state) as StateColor;
80
+ const display = (stateDisplayFn(state) as string)?.toLowerCase() || state;
74
81
 
75
- return {
76
- label,
77
- resources: resourcesValue,
78
- to: (resourcesValue?.length > 0) ? {
79
- name: 'c-cluster-product-resource',
80
- params: {
81
- cluster: store.getters.currentCluster.id,
82
- product: store.getters.currentProduct.name,
83
- resource: type
84
- }
85
- } : undefined,
82
+ agg[state] = agg[state] || {
83
+ color, label: display, count: 0
86
84
  };
85
+ agg[state].count++;
87
86
  });
88
- }
89
87
 
90
- export function useDefaultWorkloadResources(services?: any[], ingresses?: any[], referredToBy?: any[], refersTo?: any[]): StateCardProps {
91
- const store = useStore();
92
- const i18n = useI18n(store);
93
-
94
- const pairs: Ref<(Pairs | undefined)[]> = computed(() => [
95
- useResourcePair(i18n.t('component.resource.detail.card.resourcesCard.rows.services'), SERVICE, services).value,
96
- useResourcePair(i18n.t('component.resource.detail.card.resourcesCard.rows.ingresses'), INGRESS, ingresses).value,
97
- referredToBy ? {
98
- label: i18n.t('component.resource.detail.card.resourcesCard.rows.referredToBy'),
99
- resources: toValue(referredToBy || []),
100
- to: { hash: '#related' }
101
- } : undefined,
102
- refersTo ? {
103
- label: i18n.t('component.resource.detail.card.resourcesCard.rows.refersTo'),
104
- resources: toValue(refersTo || []),
105
- to: { hash: '#related' }
106
- } : undefined
107
- ]);
108
-
109
- const remainingPairs = computed(() => pairs.value.filter((p: (Pairs | undefined)): p is Pairs => p !== undefined));
110
-
111
- const rows = useDefaultResources(remainingPairs);
88
+ const tuples: Tuple[] = Object.values(agg);
112
89
 
113
- return {
114
- title: 'Resources',
115
- rows: rows.value
116
- };
117
- }
90
+ tuples.sort((left: Tuple, right: Tuple) => {
91
+ if (isHigherAlert(left.color, right.color)) {
92
+ return -1;
93
+ }
118
94
 
119
- export function useDefaultWorkloadInsightsCardProps(): StateCardProps {
120
- const store = useStore();
121
- const i18n = useI18n(store);
122
-
123
- const rows: ResourceRowProps[] = [
124
- {
125
- label: i18n.t('component.resource.detail.card.insightsCard.rows.conditions'),
126
- to: '#conditions',
127
- color: 'disabled',
128
- counts: [{ label: 'Available', count: 1 }, { label: 'Progressing', count: 1 }]
129
- },
130
- {
131
- label: i18n.t('component.resource.detail.card.insightsCard.rows.events'),
132
- to: '#events',
133
- color: 'disabled',
134
- counts: [{ label: 'Normal', count: 2 }]
95
+ if (left.color !== right.color) {
96
+ return 1;
135
97
  }
136
- ];
98
+
99
+ if (left.count === right.count) {
100
+ return 0;
101
+ }
102
+
103
+ return left.count > right.count ? -1 : 1;
104
+ });
137
105
 
138
106
  return {
139
- title: i18n.t('component.resource.detail.card.insightsCard.title'),
140
- rows
107
+ label,
108
+ color: tuples.length ? tuples[0].color : undefined,
109
+ counts: tuples.length ? tuples : undefined,
110
+ to
141
111
  };
142
112
  }
@@ -3,6 +3,7 @@ import StatusCard from '@shell/components/Resource/Detail/Card/StatusCard/index.
3
3
  import StatusBar from '@shell/components/Resource/Detail/StatusBar.vue';
4
4
  import StatusRow from '@shell/components/Resource/Detail/StatusRow.vue';
5
5
  import Scaler from '@shell/components/Resource/Detail/Card/Scaler.vue';
6
+ import type { SummaryResult } from '@shell/components/Resource/Detail/Card/StateCard/composables';
6
7
 
7
8
  describe('component: StatusCard', () => {
8
9
  const mockResource = (stateDisplay: string, stateSimpleColor: string) => ({
@@ -106,4 +107,64 @@ describe('component: StatusCard', () => {
106
107
  expect(wrapper.findComponent(Scaler).exists()).toBe(false);
107
108
  });
108
109
  });
110
+
111
+ describe('with summaryData', () => {
112
+ it('should render StatusBar and StatusRows from summary counts', () => {
113
+ const summaryData: SummaryResult = {
114
+ count: 5,
115
+ summary: [{ property: 'metadata.state.name', counts: { running: { total: 3 }, error: { total: 2 } } }]
116
+ };
117
+
118
+ const wrapper = mountCard({ summaryData });
119
+
120
+ expect(wrapper.findComponent(StatusBar).exists()).toBe(true);
121
+ expect(wrapper.findAllComponents(StatusRow)).toHaveLength(2);
122
+ });
123
+
124
+ it('should use summaryData over resources when both are provided', () => {
125
+ const summaryData: SummaryResult = {
126
+ count: 4,
127
+ summary: [{ property: 'metadata.state.name', counts: { running: { total: 3 }, completed: { total: 1 } } }]
128
+ };
129
+ const resources = [
130
+ mockResource('Running', 'text-success'),
131
+ ];
132
+
133
+ const wrapper = mountCard({ summaryData, resources });
134
+
135
+ expect(wrapper.findAllComponents(StatusRow)).toHaveLength(2);
136
+ });
137
+
138
+ it('should fall back to resources when summaryData has no summary', () => {
139
+ const summaryData: SummaryResult = { count: 0, summary: null };
140
+ const resources = [
141
+ mockResource('Running', 'text-success'),
142
+ mockResource('Error', 'text-error'),
143
+ ];
144
+
145
+ const wrapper = mountCard({ summaryData, resources });
146
+
147
+ expect(wrapper.findAllComponents(StatusRow)).toHaveLength(2);
148
+ });
149
+
150
+ it('should show noResourcesMessage when summaryData has no counts', () => {
151
+ const summaryData: SummaryResult = { count: 0, summary: [] };
152
+
153
+ const wrapper = mountCard({ summaryData, noResourcesMessage: 'No pods' });
154
+
155
+ expect(wrapper.findComponent(StatusBar).exists()).toBe(false);
156
+ expect(wrapper.find('.text-deemphasized').text()).toBe('No pods');
157
+ });
158
+
159
+ it('should render a single state from summary', () => {
160
+ const summaryData: SummaryResult = {
161
+ count: 2,
162
+ summary: [{ property: 'metadata.state.name', counts: { active: { total: 2 } } }]
163
+ };
164
+
165
+ const wrapper = mountCard({ summaryData });
166
+
167
+ expect(wrapper.findAllComponents(StatusRow)).toHaveLength(1);
168
+ });
169
+ });
109
170
  });
@@ -8,10 +8,13 @@ import { useI18n } from '@shell/composables/useI18n';
8
8
  import { StateColor } from '@shell/utils/style';
9
9
  import { computed } from 'vue';
10
10
  import { useStore } from 'vuex';
11
+ import { colorForState as colorForStateFn, stateDisplay as stateDisplayFn } from '@shell/plugins/dashboard-store/resource-class';
12
+ import type { SummaryResult } from '@shell/components/Resource/Detail/Card/StateCard/composables';
11
13
 
12
14
  export interface Props {
13
15
  title: string;
14
16
  resources?: any[];
17
+ summaryData?: SummaryResult | null;
15
18
  showScaling?: boolean;
16
19
  noResourcesMessage?: string;
17
20
  }
@@ -23,23 +26,47 @@ const i18n = useI18n(store);
23
26
 
24
27
  const props = withDefaults(defineProps<Props>(), {
25
28
  resources: undefined,
29
+ summaryData: undefined,
26
30
  showScaling: false,
27
31
  noResourcesMessage: undefined
28
32
  });
29
33
  const emit = defineEmits(['decrease', 'increase']);
30
34
 
35
+ const summaryStateCounts = computed(() => {
36
+ const summary = props.summaryData?.summary;
37
+
38
+ if (!summary?.length) {
39
+ return null;
40
+ }
41
+
42
+ const entry = summary.find((s) => s.property === 'metadata.state.name');
43
+
44
+ return entry?.counts || null;
45
+ });
46
+
31
47
  const segmentAccumulator = computed(() => {
32
48
  interface Value {
33
49
  count: number;
34
50
  }
35
51
  const accumulator: {[key in StateColor]?: Value} = {};
36
-
37
- props.resources?.forEach((resource: any) => {
38
- const color: StateColor = resource.stateSimpleColor;
39
-
40
- accumulator[color] = accumulator[color] || { count: 0 };
41
- accumulator[color].count++;
42
- });
52
+ const stateCounts = summaryStateCounts.value;
53
+
54
+ if (stateCounts) {
55
+ for (const [state, stateCount] of Object.entries(stateCounts)) {
56
+ const colorRaw = colorForStateFn(state) as string;
57
+ const color = (colorRaw?.replace('text-', '') || 'disabled') as StateColor;
58
+
59
+ accumulator[color] = accumulator[color] || { count: 0 };
60
+ accumulator[color].count += stateCount.total;
61
+ }
62
+ } else {
63
+ props.resources?.forEach((resource: any) => {
64
+ const color: StateColor = resource.stateSimpleColor || 'disabled';
65
+
66
+ accumulator[color] = accumulator[color] || { count: 0 };
67
+ accumulator[color].count++;
68
+ });
69
+ }
43
70
 
44
71
  return accumulator;
45
72
  });
@@ -50,21 +77,40 @@ const rowAccumulator = computed(() => {
50
77
  color: StateColor;
51
78
  }
52
79
  const accumulator: {[key in string]: Value} = {};
53
-
54
- props.resources?.forEach((resource: any) => {
55
- accumulator[resource.stateDisplay] = accumulator[resource.stateDisplay] || { count: 0 };
56
- accumulator[resource.stateDisplay].count++;
57
- accumulator[resource.stateDisplay].color = resource.stateSimpleColor.replace('text-', '') as StateColor;
58
- });
80
+ const stateCounts = summaryStateCounts.value;
81
+
82
+ if (stateCounts) {
83
+ for (const [state, stateCount] of Object.entries(stateCounts)) {
84
+ const display = stateDisplayFn(state) as string;
85
+ const colorRaw = colorForStateFn(state) as string;
86
+ const color = (colorRaw?.replace('text-', '') || 'disabled') as StateColor;
87
+
88
+ accumulator[display] = accumulator[display] || { count: 0, color };
89
+ accumulator[display].count += stateCount.total;
90
+ }
91
+ } else {
92
+ props.resources?.forEach((resource: any) => {
93
+ const color = (resource.stateSimpleColor?.replace('text-', '') || 'disabled') as StateColor;
94
+
95
+ accumulator[resource.stateDisplay] = accumulator[resource.stateDisplay] || { count: 0, color };
96
+ accumulator[resource.stateDisplay].count++;
97
+ });
98
+ }
59
99
 
60
100
  return accumulator;
61
101
  });
62
102
 
63
103
  const percent = (count: number, total: number) => {
64
- return count / total * 100;
104
+ return total > 0 ? count / total * 100 : 0;
65
105
  };
66
106
 
67
- const count = computed(() => props.resources?.length || 0);
107
+ const count = computed(() => {
108
+ if (summaryStateCounts.value) {
109
+ return props.summaryData?.count ?? 0;
110
+ }
111
+
112
+ return props.resources?.length ?? 0;
113
+ });
68
114
 
69
115
  const segmentColors = computed(() => Object.keys(segmentAccumulator.value) as StateColor[]);
70
116
  const segments = computed(() => segmentColors.value.map((color: StateColor) => ({
@@ -103,12 +103,28 @@ const getRowValueId = (row:Row): string => `value-${ row.label }:${ row.value }`
103
103
  flex-direction: column;
104
104
 
105
105
  .row {
106
+ display: flex;
107
+ align-items: center;
108
+ gap: 8px;
109
+
110
+ // Hide clearfix pseudo-elements inherited from the global .row class
111
+ &::before, &::after {
112
+ display: none;
113
+ }
114
+
106
115
  &:not(:last-of-type) {
107
116
  margin-bottom: 8px;
108
117
  }
109
118
 
110
119
  .full-custom-value {
111
120
  flex: 1;
121
+ min-width: 0;
122
+
123
+ // Override inline-block on .popover-card-target so it respects the
124
+ // parent's width constraint instead of sizing to its content
125
+ :deep(.popover-card-target) {
126
+ width: 100%;
127
+ }
112
128
  }
113
129
 
114
130
  .value {
@@ -128,7 +144,7 @@ const getRowValueId = (row:Row): string => `value-${ row.label }:${ row.value }`
128
144
 
129
145
  .label {
130
146
  width: 30%;
131
- min-width: 120px;
147
+ min-width: min-content;
132
148
  }
133
149
 
134
150
  .status {