@rancher/shell 3.0.12-rc.3 → 3.0.12-rc.5

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 (315) hide show
  1. package/assets/styles/global/_button.scss +1 -1
  2. package/assets/styles/global/_layout.scss +4 -0
  3. package/assets/translations/en-us.yaml +183 -51
  4. package/assets/translations/zh-hans.yaml +1 -7
  5. package/chart/monitoring/ClusterSelector.vue +0 -21
  6. package/chart/monitoring/prometheus/index.vue +6 -3
  7. package/components/ActionDropdownShell.vue +5 -3
  8. package/components/ButtonGroup.vue +26 -1
  9. package/components/CruResource.vue +212 -16
  10. package/components/ExplorerMembers.vue +8 -4
  11. package/components/ExplorerProjectsNamespaces.vue +10 -6
  12. package/components/GrowlManager.vue +4 -0
  13. package/components/MgmtNodeList.vue +184 -0
  14. package/components/PromptRestore.vue +93 -32
  15. package/components/Questions/index.vue +1 -0
  16. package/components/Resource/Detail/Card/StateCard/__tests__/composables.test.ts +90 -1
  17. package/components/Resource/Detail/Card/StateCard/composables.ts +57 -87
  18. package/components/Resource/Detail/Card/StatusCard/__tests__/StatusCard.test.ts +61 -0
  19. package/components/Resource/Detail/Card/StatusCard/index.vue +61 -15
  20. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +2 -0
  21. package/components/Resource/Detail/Metadata/KeyValue.vue +5 -2
  22. package/components/Resource/Detail/Metadata/KeyValueRow.vue +2 -6
  23. package/components/ResourceDetail/index.vue +1 -1
  24. package/components/ResourceList/Masthead.vue +7 -1
  25. package/components/ResourceList/index.vue +82 -1
  26. package/components/ResourceTable.vue +1 -0
  27. package/components/RichTranslation.vue +5 -2
  28. package/components/Setting.vue +1 -0
  29. package/components/SortableTable/index.vue +4 -3
  30. package/components/SubtleLink.vue +31 -6
  31. package/components/Tabbed/Tab.vue +29 -3
  32. package/components/Tabbed/index.vue +25 -3
  33. package/components/TableOfContents/TableOfContents.vue +109 -0
  34. package/components/TableOfContents/composables.ts +258 -0
  35. package/components/Window/ContainerShell.vue +21 -11
  36. package/components/Window/__tests__/ContainerShell.test.ts +107 -37
  37. package/components/Wizard.vue +23 -5
  38. package/components/__tests__/ButtonGroup.test.ts +56 -0
  39. package/components/__tests__/PromptRestore.test.ts +169 -19
  40. package/components/fleet/AppCoChartGrid.vue +401 -0
  41. package/components/fleet/AppCoEmptyState.vue +127 -0
  42. package/components/fleet/AppCoPageHeader.vue +119 -0
  43. package/components/fleet/AppCoVersionSelect.vue +70 -0
  44. package/components/fleet/FleetClusterTargets/ClusterSelectionFields.vue +217 -0
  45. package/components/fleet/FleetClusterTargets/TargetsList.vue +123 -35
  46. package/components/fleet/FleetClusterTargets/index.vue +189 -146
  47. package/components/fleet/FleetIntro.vue +7 -3
  48. package/components/fleet/FleetNoWorkspaces.vue +7 -3
  49. package/components/fleet/FleetSecretSelector.vue +5 -3
  50. package/components/fleet/FleetValuesFrom.vue +8 -2
  51. package/components/fleet/GitRepoAdvancedTab.vue +1 -0
  52. package/components/fleet/GitRepoMetadataTab.vue +5 -0
  53. package/components/fleet/GitRepoTargetTab.vue +0 -2
  54. package/components/fleet/HelmOpAdvancedTab.vue +19 -53
  55. package/components/fleet/HelmOpAppCoConfigTab.vue +597 -0
  56. package/components/fleet/HelmOpAppCoResourcesSection.vue +162 -0
  57. package/components/fleet/HelmOpMetadataTab.vue +5 -0
  58. package/components/fleet/HelmOpResourcesSection.vue +82 -0
  59. package/components/fleet/HelmOpTargetOptionsSection.vue +89 -0
  60. package/components/fleet/HelmOpTargetTab.vue +64 -60
  61. package/components/fleet/HelmOpValuesTab.vue +129 -105
  62. package/components/fleet/__tests__/AppCoEmptyState.test.ts +71 -0
  63. package/components/fleet/__tests__/AppCoVersionSelect.test.ts +36 -0
  64. package/components/fleet/__tests__/ClusterSelectionFields.test.ts +62 -0
  65. package/components/fleet/__tests__/FleetClusterTargets.test.ts +253 -0
  66. package/components/fleet/__tests__/FleetSecretSelector.test.ts +16 -0
  67. package/components/fleet/__tests__/FleetValuesFrom.test.ts +44 -0
  68. package/components/fleet/__tests__/HelmOpAppCoConfigTab.test.ts +59 -0
  69. package/components/fleet/__tests__/HelmOpAppCoResourcesSection.test.ts +62 -0
  70. package/components/fleet/__tests__/HelmOpResourcesSection.test.ts +43 -0
  71. package/components/fleet/__tests__/HelmOpTargetOptionsSection.test.ts +34 -0
  72. package/components/fleet/__tests__/HelmOpValuesTab.test.ts +39 -0
  73. package/components/fleet/__tests__/__snapshots__/AppCoEmptyState.test.ts.snap +97 -0
  74. package/components/fleet/__tests__/__snapshots__/AppCoVersionSelect.test.ts.snap +30 -0
  75. package/components/fleet/__tests__/__snapshots__/ClusterSelectionFields.test.ts.snap +209 -0
  76. package/components/fleet/__tests__/__snapshots__/HelmOpTargetOptionsSection.test.ts.snap +140 -0
  77. package/components/fleet/dashboard/Empty.vue +8 -4
  78. package/components/fleet/dashboard/ResourceCard.vue +28 -0
  79. package/components/fleet/dashboard/ResourceDetails.vue +28 -0
  80. package/components/fleet/dashboard/__tests__/ResourceCard.test.ts +87 -0
  81. package/components/form/ArrayList.vue +61 -4
  82. package/components/form/FileSelector.vue +39 -1
  83. package/components/form/KeyValue.vue +23 -2
  84. package/components/form/LabeledSelect.vue +39 -1
  85. package/components/form/Labels.vue +22 -3
  86. package/components/form/NameNsDescription.vue +13 -5
  87. package/components/form/PrivateRegistry.constants.ts +7 -0
  88. package/components/form/PrivateRegistry.vue +253 -18
  89. package/components/form/ResourceTabs/index.vue +1 -0
  90. package/components/form/SelectOrCreateAuthSecret.vue +140 -17
  91. package/components/form/__tests__/FileSelector.test.ts +23 -0
  92. package/components/form/__tests__/NameNsDescription.test.ts +75 -0
  93. package/components/form/__tests__/PrivateRegistry.test.ts +463 -73
  94. package/components/form/__tests__/SelectOrCreateAuthSecret.test.ts +122 -0
  95. package/components/formatter/EtcdSnapshotName.vue +73 -0
  96. package/components/formatter/InternalExternalIP.vue +10 -4
  97. package/components/formatter/ServiceTargets.vue +26 -7
  98. package/components/formatter/__tests__/InternalExternalIP.test.ts +132 -0
  99. package/components/formatter/__tests__/ServiceTargets.test.ts +412 -0
  100. package/components/nav/Header.vue +12 -1
  101. package/components/nav/TopLevelMenu.vue +7 -2
  102. package/components/nav/__tests__/Header.test.ts +15 -0
  103. package/components/nav/__tests__/TopLevelMenu.test.ts +120 -2
  104. package/components/templates/default.vue +16 -4
  105. package/components/templates/home.vue +9 -4
  106. package/components/templates/plain.vue +9 -4
  107. package/composables/useHelmOpResources.test.ts +56 -0
  108. package/composables/useHelmOpResources.ts +32 -0
  109. package/composables/useStateColor.test.ts +325 -0
  110. package/composables/useStateColor.ts +128 -0
  111. package/config/features.js +1 -0
  112. package/config/home-links.js +1 -1
  113. package/config/labels-annotations.js +3 -0
  114. package/config/product/explorer.js +17 -4
  115. package/config/product/manager.js +8 -0
  116. package/config/router/index.js +16 -0
  117. package/config/router/navigation-guards/__tests__/authentication.test.ts +130 -0
  118. package/config/router/navigation-guards/authentication.js +10 -4
  119. package/config/router/routes.js +20 -6
  120. package/config/secret.ts +10 -0
  121. package/config/settings.ts +6 -4
  122. package/config/table-headers.js +3 -4
  123. package/config/types.js +16 -0
  124. package/core/plugin-products-base.ts +3 -3
  125. package/core/plugin-types.ts +83 -30
  126. package/core/plugin.ts +3 -0
  127. package/core/types-provisioning.ts +34 -1
  128. package/core/types.ts +15 -2
  129. package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +114 -0
  130. package/detail/__tests__/workload.test.ts +3 -152
  131. package/detail/catalog.cattle.io.clusterrepo.vue +1 -1
  132. package/detail/provisioning.cattle.io.cluster.vue +109 -7
  133. package/detail/workload/index.vue +12 -55
  134. package/dialog/RotateEncryptionKeyDialog.vue +33 -9
  135. package/dialog/__tests__/RotateEncryptionKeyDialog.test.ts +78 -0
  136. package/edit/__tests__/catalog.cattle.io.clusterrepo.test.ts +248 -0
  137. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +92 -0
  138. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +206 -0
  139. package/edit/__tests__/management.cattle.io.setting.test.ts +2 -1
  140. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/General.test.ts.snap +6 -0
  141. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/index.test.ts.snap +1 -0
  142. package/edit/auth/__tests__/azuread.test.ts +34 -9
  143. package/edit/auth/__tests__/github.test.ts +234 -0
  144. package/edit/auth/__tests__/oidc.test.ts +26 -6
  145. package/edit/auth/__tests__/saml.test.ts +196 -0
  146. package/edit/auth/azuread.vue +128 -95
  147. package/edit/auth/github.vue +72 -13
  148. package/edit/auth/ldap/__tests__/index.test.ts +206 -0
  149. package/edit/auth/ldap/config.vue +8 -0
  150. package/edit/auth/ldap/index.vue +75 -1
  151. package/edit/auth/oidc.vue +119 -73
  152. package/edit/auth/saml.vue +76 -12
  153. package/edit/catalog.cattle.io.clusterrepo.vue +140 -32
  154. package/edit/compliance.cattle.io.clusterscanprofile.vue +39 -41
  155. package/edit/fleet.cattle.io.gitrepo.vue +70 -16
  156. package/edit/fleet.cattle.io.helmop.vue +542 -141
  157. package/edit/helm.cattle.io.projecthelmchart.vue +1 -0
  158. package/edit/{management.cattle.io.setting.vue → management.cattle.io.setting/index.vue} +32 -9
  159. package/edit/management.cattle.io.setting/system-default-registry-pull-secrets.vue +81 -0
  160. package/edit/management.cattle.io.user.vue +5 -2
  161. package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +3 -12
  162. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +18 -0
  163. package/edit/provisioning.cattle.io.cluster/rke2.vue +89 -11
  164. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +11 -0
  165. package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +0 -1
  166. package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +14 -55
  167. package/list/group.principal.vue +5 -4
  168. package/list/harvesterhci.io.management.cluster.vue +8 -9
  169. package/list/management.cattle.io.user.vue +12 -9
  170. package/list/provisioning.cattle.io.cluster.vue +16 -10
  171. package/mixins/__tests__/auth-config.test.ts +90 -0
  172. package/mixins/__tests__/chart.test.ts +94 -0
  173. package/mixins/__tests__/resource-fetch-api-pagination.test.ts +48 -0
  174. package/mixins/auth-config.js +7 -0
  175. package/mixins/chart.js +11 -2
  176. package/mixins/child-hook.js +12 -6
  177. package/mixins/create-edit-view/impl.js +5 -3
  178. package/mixins/resource-fetch-api-pagination.js +21 -1
  179. package/models/__tests__/catalog.cattle.io.clusterrepo.test.ts +57 -0
  180. package/models/__tests__/compliance.cattle.io.clusterscan.test.ts +144 -0
  181. package/models/__tests__/fleet-application.test.ts +175 -0
  182. package/models/__tests__/fleet.cattle.io.bundle.test.ts +169 -0
  183. package/models/__tests__/fleet.cattle.io.helmop.test.ts +84 -0
  184. package/models/__tests__/management.cattle.io.node.ts +22 -0
  185. package/models/__tests__/namespace.test.ts +36 -0
  186. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +205 -0
  187. package/models/__tests__/secret.test.ts +68 -1
  188. package/models/__tests__/workload.test.ts +401 -26
  189. package/models/catalog.cattle.io.clusterrepo.js +28 -4
  190. package/models/compliance.cattle.io.clusterscan.js +39 -4
  191. package/models/fleet-application.js +4 -0
  192. package/models/fleet.cattle.io.helmop.js +20 -1
  193. package/models/management.cattle.io.cluster.js +39 -5
  194. package/models/management.cattle.io.node.js +44 -3
  195. package/models/namespace.js +1 -1
  196. package/models/pod.js +46 -3
  197. package/models/provisioning.cattle.io.cluster.js +64 -14
  198. package/models/rke.cattle.io.etcdsnapshot.js +17 -9
  199. package/models/secret.js +19 -0
  200. package/models/workload.js +120 -20
  201. package/models/workload.service.js +5 -0
  202. package/package.json +14 -13
  203. package/pages/about.vue +5 -6
  204. package/pages/auth/login.vue +0 -35
  205. package/pages/auth/setup.vue +11 -0
  206. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +2 -2
  207. package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +10 -1
  208. package/pages/c/_cluster/apps/charts/__tests__/index.test.ts +93 -0
  209. package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +485 -107
  210. package/pages/c/_cluster/apps/charts/chart.vue +2 -1
  211. package/pages/c/_cluster/apps/charts/index.vue +48 -10
  212. package/pages/c/_cluster/apps/charts/install.vue +236 -144
  213. package/pages/c/_cluster/auth/roles/index.vue +5 -4
  214. package/pages/c/_cluster/explorer/workload-dashboard/ByNamespaceSection.vue +31 -0
  215. package/pages/c/_cluster/explorer/workload-dashboard/ByStateSection.vue +138 -0
  216. package/pages/c/_cluster/explorer/workload-dashboard/ByTypeSection.vue +30 -0
  217. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadCard.vue +155 -0
  218. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadNamespaceCard.vue +142 -0
  219. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadTypeCard.vue +159 -0
  220. package/pages/c/_cluster/explorer/workload-dashboard/__tests__/composable.test.ts +561 -0
  221. package/pages/c/_cluster/explorer/workload-dashboard/composable.ts +440 -0
  222. package/pages/c/_cluster/explorer/workload-dashboard/index.vue +187 -0
  223. package/pages/c/_cluster/explorer/workload-dashboard/types.ts +80 -0
  224. package/pages/c/_cluster/fleet/application/create.vue +187 -136
  225. package/pages/c/_cluster/fleet/application/index.vue +5 -3
  226. package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailBody.vue +338 -0
  227. package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailHeader.vue +121 -0
  228. package/pages/c/_cluster/fleet/application/suse-app-collection/chart.vue +369 -0
  229. package/pages/c/_cluster/fleet/application/suse-app-collection/charts.vue +248 -0
  230. package/pages/c/_cluster/fleet/application/suse-app-collection/credentials.vue +310 -0
  231. package/pages/c/_cluster/fleet/index.vue +2 -2
  232. package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +96 -0
  233. package/pages/c/_cluster/uiplugins/index.vue +15 -0
  234. package/pages/fail-whale.vue +16 -11
  235. package/pages/home.vue +16 -46
  236. package/pkg/require-asset.lib.js +25 -0
  237. package/pkg/vue.config.js +7 -0
  238. package/plugins/clean-html.d.ts +9 -0
  239. package/plugins/dashboard-store/__tests__/resource-class.test.ts +177 -0
  240. package/plugins/dashboard-store/getters.js +0 -1
  241. package/plugins/dashboard-store/resource-class.js +114 -19
  242. package/plugins/steve/__tests__/actions.test.ts +212 -0
  243. package/plugins/steve/actions.js +96 -0
  244. package/plugins/steve/steve-pagination-utils.ts +1 -1
  245. package/rancher-components/Accordion/Accordion.vue +53 -9
  246. package/rancher-components/Form/Checkbox/Checkbox.vue +14 -0
  247. package/rancher-components/Form/Radio/RadioButton.vue +17 -1
  248. package/rancher-components/Form/Radio/RadioGroup.vue +10 -0
  249. package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +30 -0
  250. package/rancher-components/Form/TextArea/__tests__/TextAreaAutoGrow.test.ts +95 -0
  251. package/rancher-components/Pill/RcTag/RcTag.vue +3 -2
  252. package/rancher-components/RcButton/RcButton.test.ts +103 -0
  253. package/rancher-components/RcButton/RcButton.vue +94 -15
  254. package/rancher-components/RcButton/index.ts +1 -1
  255. package/rancher-components/RcButton/types.ts +3 -0
  256. package/rancher-components/RcDropdown/RcDropdownTrigger.vue +6 -1
  257. package/rancher-components/RcItemCard/RcItemCard.test.ts +18 -0
  258. package/rancher-components/RcItemCard/RcItemCard.vue +2 -2
  259. package/rancher-components/RcSection/RcSection.vue +28 -3
  260. package/scripts/extension/helm/package/Dockerfile +1 -1
  261. package/scripts/test-plugins-build.sh +2 -1
  262. package/store/__tests__/features.test.ts +131 -0
  263. package/store/__tests__/growl.test.ts +374 -0
  264. package/store/__tests__/modal.test.ts +131 -0
  265. package/store/__tests__/notifications.test.ts +434 -0
  266. package/store/__tests__/slideInPanel.test.ts +88 -0
  267. package/store/__tests__/type-map.utils.test.ts +433 -0
  268. package/store/catalog.js +57 -0
  269. package/store/features.js +4 -0
  270. package/store/plugins.js +7 -4
  271. package/types/components/buttonGroup.ts +5 -0
  272. package/types/shell/index.d.ts +166 -70
  273. package/utils/__tests__/auth.test.ts +273 -0
  274. package/utils/__tests__/computed.test.ts +193 -0
  275. package/utils/__tests__/cspAdaptor.test.ts +163 -0
  276. package/utils/__tests__/dom.test.ts +81 -0
  277. package/utils/__tests__/duration.test.ts +37 -1
  278. package/utils/__tests__/dynamic-importer.test.ts +102 -0
  279. package/utils/__tests__/fleet-appco.test.ts +312 -0
  280. package/utils/__tests__/monitoring.test.ts +130 -0
  281. package/utils/__tests__/object.test.ts +22 -0
  282. package/utils/__tests__/operation-cr.test.ts +34 -0
  283. package/utils/__tests__/platform.test.ts +91 -0
  284. package/utils/__tests__/position.test.ts +237 -0
  285. package/utils/__tests__/provider.test.ts +51 -1
  286. package/utils/__tests__/queue.test.ts +232 -0
  287. package/utils/__tests__/release-notes.test.ts +221 -0
  288. package/utils/__tests__/router.test.js +254 -1
  289. package/utils/__tests__/select.test.ts +208 -0
  290. package/utils/__tests__/time.test.ts +265 -1
  291. package/utils/__tests__/title.test.ts +47 -0
  292. package/utils/__tests__/width.test.ts +53 -0
  293. package/utils/__tests__/window.test.ts +158 -0
  294. package/utils/__tests__/xccdf.test.ts +126 -6
  295. package/utils/crypto/__tests__/browserHashUtils.test.ts +98 -0
  296. package/utils/crypto/__tests__/index.test.ts +144 -0
  297. package/utils/duration.ts +104 -0
  298. package/utils/dynamic-content/__tests__/notification-handler.test.ts +196 -0
  299. package/utils/dynamic-content/info.ts +2 -1
  300. package/utils/error.js +13 -0
  301. package/utils/fleet-appco.ts +323 -0
  302. package/utils/object.js +22 -2
  303. package/utils/operation-cr.js +19 -0
  304. package/utils/provider.ts +12 -0
  305. package/utils/require-asset.ts +7 -0
  306. package/utils/validators/__tests__/container-images.test.ts +104 -0
  307. package/utils/validators/__tests__/flow-output.test.ts +91 -0
  308. package/utils/validators/__tests__/logging-outputs.test.ts +58 -0
  309. package/utils/validators/__tests__/monitoring-route.test.ts +119 -0
  310. package/utils/validators/__tests__/private-registry.test.ts +27 -15
  311. package/utils/validators/private-registry.ts +15 -4
  312. package/utils/xccdf.ts +39 -42
  313. package/vue.config.js +1 -1
  314. package/pages/support/index.vue +0 -264
  315. package/utils/duration.js +0 -43
@@ -8,6 +8,7 @@ import { HARVESTER_NAME as VIRTUAL } from '@shell/config/features';
8
8
  import { CAPI, HCI, MANAGEMENT } from '@shell/config/types';
9
9
  import { isHarvesterCluster } from '@shell/utils/cluster';
10
10
  import { allHash } from '@shell/utils/promise';
11
+ import { RcButton } from '@components/RcButton';
11
12
 
12
13
  export default {
13
14
  components: {
@@ -15,7 +16,8 @@ export default {
15
16
  ResourceTable,
16
17
  Masthead,
17
18
  TypeDescription,
18
- Loading
19
+ Loading,
20
+ RcButton,
19
21
  },
20
22
 
21
23
  props: {
@@ -128,12 +130,12 @@ export default {
128
130
  v-if="canCreateCluster"
129
131
  slot="extraActions"
130
132
  >
131
- <n-link
133
+ <rc-button
132
134
  :to="importLocation"
133
- class="btn role-primary"
135
+ size="large"
134
136
  >
135
137
  {{ t('cluster.importAction') }}
136
- </n-link>
138
+ </rc-button>
137
139
  </template>
138
140
  </Masthead>
139
141
 
@@ -166,12 +168,9 @@ export default {
166
168
  </template>
167
169
 
168
170
  <template #cell:harvester="{row}">
169
- <n-link
170
- class="btn btn-sm role-primary"
171
- :to="row.detailLocation"
172
- >
171
+ <rc-button :to="row.detailLocation">
173
172
  {{ t('harvesterManager.manage') }}
174
- </n-link>
173
+ </rc-button>
175
174
  </template>
176
175
  </ResourceTable>
177
176
  <div v-else>
@@ -7,6 +7,7 @@ import Masthead from '@shell/components/ResourceList/Masthead';
7
7
  import ResourceFetch from '@shell/mixins/resource-fetch';
8
8
  import { isAdminUser } from '@shell/store/type-map';
9
9
  import TableDataUserIcon from '@shell/components/TableDataUserIcon';
10
+ import { RcButton } from '@components/RcButton';
10
11
 
11
12
  export default {
12
13
  components: {
@@ -14,6 +15,7 @@ export default {
14
15
  ResourceTable,
15
16
  Masthead,
16
17
  TableDataUserIcon,
18
+ RcButton
17
19
  },
18
20
  mixins: [ResourceFetch],
19
21
  props: {
@@ -128,14 +130,17 @@ export default {
128
130
  v-if="isAdmin"
129
131
  #subHeader
130
132
  >
131
- <router-link
133
+ <rc-button
134
+ variant="link"
135
+ class="btn-user-retention"
132
136
  :to="{ name: 'c-cluster-auth-user.retention'}"
133
- class="btn role-link btn-sm btn-user-retention"
134
137
  data-testid="router-link-user-retention"
135
138
  >
136
- <i class="icon icon-gear" />
139
+ <template #before>
140
+ <i class="icon icon-gear" />
141
+ </template>
137
142
  {{ t('user.retention.button.label') }}
138
- </router-link>
143
+ </rc-button>
139
144
  </template>
140
145
  </Masthead>
141
146
 
@@ -159,10 +164,8 @@ export default {
159
164
  </div>
160
165
  </template>
161
166
 
162
- <style lang="scss">
163
- .btn-user-retention {
164
- display: flex;
165
- gap: 0.25rem;
166
- padding: 0;
167
+ <style lang="scss" scoped>
168
+ a.rc-button.variant-link.btn-user-retention {
169
+ padding: 0; //retain the padding override for left-alignment with the header
167
170
  }
168
171
  </style>
@@ -17,10 +17,15 @@ import ProvCluster from '@shell/models/provisioning.cattle.io.cluster';
17
17
  import ManagementClusterUtils from '@shell/list/utils/management.cattle.io.cluster.utils';
18
18
  import { STEVE_AUTOSCALER_ENABLED } from '@shell/config/pagination-table-headers';
19
19
  import { filterHiddenLocalCluster, filterOnlyKubernetesClusters } from '@shell/utils/cluster';
20
+ import { RcButton } from '@components/RcButton';
20
21
 
21
22
  export default {
22
23
  components: {
23
- Banner, PaginatedResourceTable, Masthead, MachineSummaryGraph
24
+ Banner,
25
+ PaginatedResourceTable,
26
+ Masthead,
27
+ MachineSummaryGraph,
28
+ RcButton,
24
29
  },
25
30
 
26
31
  data() {
@@ -289,13 +294,14 @@ export default {
289
294
  v-if="canImport"
290
295
  #extraActions
291
296
  >
292
- <router-link
297
+ <rc-button
298
+ size="large"
299
+ class="mr-10"
293
300
  :to="importLocation"
294
- class="btn role-primary mr-10"
295
301
  data-testid="cluster-manager-list-import"
296
302
  >
297
303
  {{ t('cluster.importAction') }}
298
- </router-link>
304
+ </rc-button>
299
305
  </template>
300
306
  </Masthead>
301
307
 
@@ -329,22 +335,22 @@ export default {
329
335
  </template>
330
336
  <template #cell:explorer="{row}">
331
337
  <!-- Align side nav cluster, home page name link and cluster management cluster explor buttons on canExplore -->
332
- <router-link
338
+ <rc-button
333
339
  v-if="row.canExplore"
340
+ variant="secondary"
334
341
  data-testid="cluster-manager-list-explore-management"
335
- class="btn btn-sm role-secondary"
336
342
  :to="{name: 'c-cluster', params: {cluster: row.id}}"
337
343
  >
338
344
  {{ t('cluster.explore') }}
339
- </router-link>
340
- <button
345
+ </rc-button>
346
+ <rc-button
341
347
  v-else
348
+ variant="secondary"
342
349
  data-testid="cluster-manager-list-explore"
343
350
  :disabled="true"
344
- class="btn btn-sm role-secondary"
345
351
  >
346
352
  {{ t('cluster.explore') }}
347
- </button>
353
+ </rc-button>
348
354
  </template>
349
355
  </PaginatedResourceTable>
350
356
  </template>
@@ -69,4 +69,94 @@ describe('mixin: authConfigMixin', () => {
69
69
  expect(instance.model.scope).toStrictEqual(scope);
70
70
  });
71
71
  });
72
+
73
+ describe('accessMode on enable', () => {
74
+ const FakeComponent = {
75
+ render() {},
76
+ mixins: [authConfigMixin, childHook],
77
+ methods: { applyHooks: jest.fn() },
78
+ };
79
+
80
+ const createMock = (model: any, overrides: Record<string, any> = {}) => ({
81
+ data: () => ({
82
+ value: { configType: 'oidc' },
83
+ model,
84
+ ...overrides,
85
+ }),
86
+ computed: { principal: () => ({ me: {} }) },
87
+ global: {
88
+ mocks: {
89
+ $store: { dispatch: () => model },
90
+ $route: {
91
+ params: { id: model.id || '123' },
92
+ query: { mode: 'edit' },
93
+ },
94
+ }
95
+ }
96
+ });
97
+
98
+ it.each([
99
+ 'github',
100
+ 'githubapp',
101
+ ])('should default accessMode to restricted for %s on enable', async(id) => {
102
+ const model = {
103
+ id,
104
+ enabled: false,
105
+ authConfigName: 'whatever',
106
+ doAction: jest.fn(),
107
+ save: jest.fn(),
108
+ };
109
+ const instance = mount(FakeComponent, createMock(model)).vm as any;
110
+
111
+ await instance.save(jest.fn());
112
+
113
+ expect(model.save).toHaveBeenCalled();
114
+ expect(instance.model.accessMode).toStrictEqual('required');
115
+ });
116
+
117
+ it('should default accessMode to unrestricted for non-github oauth on enable', async() => {
118
+ const model = {
119
+ id: 'googleoauth',
120
+ enabled: false,
121
+ authConfigName: 'whatever',
122
+ doAction: jest.fn(),
123
+ save: jest.fn(),
124
+ };
125
+ const instance = mount(FakeComponent, createMock(model)).vm as any;
126
+
127
+ await instance.save(jest.fn());
128
+
129
+ expect(instance.model.accessMode).toStrictEqual('required');
130
+ });
131
+
132
+ it('should set accessMode to required after enabling a provider', async() => {
133
+ const model = {
134
+ enabled: false,
135
+ accessMode: 'unrestricted',
136
+ authConfigName: 'whatever',
137
+ doAction: jest.fn(),
138
+ save: async() => {}
139
+ };
140
+ const instance = mount(FakeComponent, createMock(model)).vm as any;
141
+
142
+ await instance.save(jest.fn());
143
+
144
+ expect(instance.model.accessMode).toStrictEqual('required');
145
+ });
146
+
147
+ it('should not change accessMode when editing an already enabled provider', async() => {
148
+ const model = {
149
+ enabled: true,
150
+ accessMode: 'unrestricted',
151
+ authConfigName: 'whatever',
152
+ doAction: jest.fn(),
153
+ save: async() => {}
154
+ };
155
+ const instance = mount(FakeComponent, createMock(model, { editConfig: true })).vm as any;
156
+
157
+ await instance.save(jest.fn());
158
+
159
+ expect(instance.model.accessMode).toStrictEqual('unrestricted');
160
+ });
161
+ });
72
162
  });
@@ -581,5 +581,99 @@ describe('chartMixin', () => {
581
581
  '0.0.1'
582
582
  ]);
583
583
  });
584
+
585
+ it('should correctly format current version when it is Linux-only', () => {
586
+ const versions = [
587
+ {
588
+ version: '1.0.0',
589
+ created: '2026-01-01',
590
+ annotations: { 'catalog.cattle.io/permits-os': 'linux' }
591
+ }
592
+ ];
593
+
594
+ const mockStore = {
595
+ dispatch: jest.fn(() => Promise.resolve()),
596
+ getters: {
597
+ currentCluster: () => ({ workerOSs: ['linux'] }),
598
+ isRancher: () => true,
599
+ 'catalog/repo': () => () => 'repo',
600
+ 'catalog/chart': () => ({ versions }),
601
+ 'prefs/get': () => () => true,
602
+ 'i18n/t': () => jest.fn()
603
+ }
604
+ };
605
+
606
+ const DummyComponent = {
607
+ mixins: [ChartMixin],
608
+ template: '<div></div>',
609
+ };
610
+
611
+ const wrapper = mount(
612
+ DummyComponent,
613
+ {
614
+ data: () => ({
615
+ chart: { versions },
616
+ existing: { spec: { chart: { metadata: { version: '1.0.0' } } } }
617
+ }),
618
+ global: {
619
+ mocks: {
620
+ $store: mockStore,
621
+ $route: { query: { version: '1.0.0' } },
622
+ t: (key: string, args: any) => `${ args?.ver } (Current, Linux-only)`
623
+ }
624
+ }
625
+ });
626
+
627
+ const result = (wrapper.vm as any).mappedVersions;
628
+
629
+ expect(result[0].label).toBe('1.0.0 (Current, Linux-only)');
630
+ });
631
+
632
+ it('should correctly format current version when it is not OS-restricted', () => {
633
+ const versions = [
634
+ {
635
+ version: '1.0.0',
636
+ created: '2026-01-01',
637
+ annotations: {}
638
+ }
639
+ ];
640
+
641
+ const mockStore = {
642
+ dispatch: jest.fn(() => Promise.resolve()),
643
+ getters: {
644
+ currentCluster: () => ({ workerOSs: ['linux'] }),
645
+ isRancher: () => true,
646
+ 'catalog/repo': () => () => 'repo',
647
+ 'catalog/chart': () => ({ versions }),
648
+ 'prefs/get': () => () => true,
649
+ 'i18n/t': () => jest.fn()
650
+ }
651
+ };
652
+
653
+ const DummyComponent = {
654
+ mixins: [ChartMixin],
655
+ template: '<div></div>',
656
+ };
657
+
658
+ const wrapper = mount(
659
+ DummyComponent,
660
+ {
661
+ data: () => ({
662
+ chart: { versions },
663
+ existing: { spec: { chart: { metadata: { version: '1.0.0' } } } }
664
+ }),
665
+ global: {
666
+ mocks: {
667
+ $store: mockStore,
668
+ $route: { query: { version: '1.0.0' } },
669
+ t: (key: string, args: any) => `${ args?.ver } (Current)`
670
+ }
671
+ }
672
+ });
673
+
674
+ const result = (wrapper.vm as any).mappedVersions;
675
+
676
+ expect(result[0].label).toBe('1.0.0 (Current)');
677
+ });
584
678
  });
585
679
  });
@@ -0,0 +1,48 @@
1
+ import { parseStateFilter } from '@shell/mixins/resource-fetch-api-pagination';
2
+ import { PaginationFilterEquality } from '@shell/types/store/pagination.types';
3
+
4
+ describe('parseStateFilter', () => {
5
+ it('should return null for null input', () => {
6
+ expect(parseStateFilter(null)).toBeNull();
7
+ });
8
+
9
+ it('should return null for undefined input', () => {
10
+ expect(parseStateFilter(undefined)).toBeNull();
11
+ });
12
+
13
+ it('should return null for empty string', () => {
14
+ expect(parseStateFilter('')).toBeNull();
15
+ });
16
+
17
+ it('should parse a single state', () => {
18
+ const result = parseStateFilter('running');
19
+
20
+ expect(result).toHaveLength(1);
21
+ expect(result?.[0].field).toStrictEqual('metadata.state.name');
22
+ expect(result?.[0].value).toStrictEqual('running');
23
+ expect(result?.[0].equality).toStrictEqual(PaginationFilterEquality.IN);
24
+ });
25
+
26
+ it('should parse multiple comma-separated states', () => {
27
+ const result = parseStateFilter('running,active');
28
+
29
+ expect(result).toHaveLength(1);
30
+ expect(result?.[0].field).toStrictEqual('metadata.state.name');
31
+ expect(result?.[0].value).toStrictEqual('running,active');
32
+ expect(result?.[0].equality).toStrictEqual(PaginationFilterEquality.IN);
33
+ });
34
+
35
+ it('should parse three comma-separated states', () => {
36
+ const result = parseStateFilter('running,active,waiting');
37
+
38
+ expect(result).toHaveLength(1);
39
+ expect(result?.[0].value).toStrictEqual('running,active,waiting');
40
+ });
41
+
42
+ it('should ignore empty segments from trailing commas', () => {
43
+ const result = parseStateFilter('running,,active,');
44
+
45
+ expect(result).toHaveLength(1);
46
+ expect(result?.[0].value).toStrictEqual('running,active');
47
+ });
48
+ });
@@ -201,6 +201,9 @@ export default {
201
201
  if (!this.model.accessMode) {
202
202
  this.model.accessMode = 'unrestricted';
203
203
  }
204
+ if (this.model.id === 'github' || this.model.id === 'githubapp') {
205
+ this.model.accessMode = 'restricted';
206
+ }
204
207
  await this.model.doAction('testAndApply', obj, { redirectUnauthorized: false });
205
208
  }
206
209
 
@@ -241,6 +244,10 @@ export default {
241
244
  } else {
242
245
  console.warn(`Unable to find principal marked as 'me'`); // eslint-disable-line no-console
243
246
  }
247
+
248
+ if (!wasEnabled) {
249
+ this.model.accessMode = 'required';
250
+ }
244
251
  }
245
252
  if (wasEnabled && configType === 'oauth') {
246
253
  await this.model.save({ ignoreFields: ['oauthCredential', 'serviceAccountCredential'] } );
package/mixins/chart.js CHANGED
@@ -71,6 +71,8 @@ export default {
71
71
 
72
72
  const selectedVersion = this.targetVersion;
73
73
  const OSs = this.currentCluster?.workerOSs;
74
+ const isRancher = isRancherRepo(this.repo, this.chart);
75
+ const permittedSystemsByVersion = new Map();
74
76
  const out = [];
75
77
 
76
78
  versions.forEach((version) => {
@@ -84,9 +86,10 @@ export default {
84
86
  keywords: version.keywords
85
87
  };
86
88
 
87
- const isRancher = isRancherRepo(this.repo, this.chart);
88
89
  const permittedSystems = getPermittedOSs(version?.annotations, isRancher);
89
90
 
91
+ permittedSystemsByVersion.set(version.version, permittedSystems);
92
+
90
93
  if (permittedSystems.length > 0 && difference(OSs, permittedSystems).length > 0) {
91
94
  nue.disabled = true;
92
95
  }
@@ -118,7 +121,13 @@ export default {
118
121
  const currentVersion = out.find((v) => v.originalVersion === this.currentVersion);
119
122
 
120
123
  if (currentVersion) {
121
- currentVersion.label = this.t('catalog.install.versions.current', { ver: this.currentVersion });
124
+ const permittedSystems = permittedSystemsByVersion.get(currentVersion.originalVersion) || [];
125
+
126
+ if (permittedSystems.length === 1) {
127
+ currentVersion.label = this.t(`catalog.install.versions.current_${ permittedSystems[0] }`, { ver: this.currentVersion });
128
+ } else {
129
+ currentVersion.label = this.t('catalog.install.versions.current', { ver: this.currentVersion });
130
+ }
122
131
  }
123
132
 
124
133
  return out;
@@ -8,20 +8,26 @@ export const AFTER_SAVE_HOOKS = '_afterSaveHooks';
8
8
 
9
9
  export default {
10
10
  methods: {
11
+ registerHook(key, boundFn, name, priority = 99, boundFnContext) {
12
+ this._registerHook(key, boundFn, name, priority, boundFnContext);
13
+ },
14
+
11
15
  registerBeforeHook(boundFn, name, priority = 99, boundFnContext) {
12
- this._registerHook(BEFORE_SAVE_HOOKS, boundFn, name, priority, boundFnContext);
16
+ this.registerHook(BEFORE_SAVE_HOOKS, boundFn, name, priority, boundFnContext);
13
17
  },
14
18
 
15
- unregisterBeforeSaveHook(name) {
16
- this[BEFORE_SAVE_HOOKS] = this[BEFORE_SAVE_HOOKS].filter((hook) => {
17
- // BEFORE_SAVE_HOOKS is an array of objects with keys
18
- // fn, name and priority.
19
+ unregisterHook(key, name) {
20
+ this[key] = (this[key] || []).filter((hook) => {
19
21
  return hook.name !== name;
20
22
  });
21
23
  },
22
24
 
25
+ unregisterBeforeSaveHook(name) {
26
+ this.unregisterHook(BEFORE_SAVE_HOOKS, name);
27
+ },
28
+
23
29
  registerAfterHook(boundFn, name, priority = 99, boundFnContext) {
24
- this._registerHook(AFTER_SAVE_HOOKS, boundFn, name, priority, boundFnContext);
30
+ this.registerHook(AFTER_SAVE_HOOKS, boundFn, name, priority, boundFnContext);
25
31
  },
26
32
 
27
33
  async applyHooks(key, ...args) {
@@ -1,6 +1,6 @@
1
1
  import { _CREATE, _EDIT, _VIEW } from '@shell/config/query-params';
2
2
  import { LAST_NAMESPACE } from '@shell/store/prefs';
3
- import { exceptionToErrorsArray } from '@shell/utils/error';
3
+ import { exceptionToErrorsArray, isDoNotLogError } from '@shell/utils/error';
4
4
  import ChildHook, { BEFORE_SAVE_HOOKS, AFTER_SAVE_HOOKS } from '@shell/mixins/child-hook';
5
5
  import { clear } from '@shell/utils/array';
6
6
  import { DEFAULT_WORKSPACE } from '@shell/config/types';
@@ -184,8 +184,10 @@ export default {
184
184
  } else {
185
185
  this.errors = exceptionToErrorsArray(err);
186
186
  }
187
- // Provide a stack trace for easier debugging of save errors
188
- console.error('CreateEditView mixin failed to save: ', err); // eslint-disable-line no-console
187
+ if (!isDoNotLogError(err)) {
188
+ // Provide a stack trace for easier debugging of save errors
189
+ console.error('CreateEditView mixin failed to save: ', err); // eslint-disable-line no-console
190
+ }
189
191
  buttonDone && buttonDone(false);
190
192
  }
191
193
  },
@@ -5,10 +5,28 @@ import { mapGetters } from 'vuex';
5
5
  import { ResourceListComponentName } from '../components/ResourceList/resource-list.config';
6
6
  import paginationUtils from '@shell/utils/pagination-utils';
7
7
  import debounce from 'lodash/debounce';
8
- import { PaginationParamFilter, PaginationFilterField, PaginationArgs } from '@shell/types/store/pagination.types';
8
+ import { PaginationParamFilter, PaginationFilterField, PaginationFilterEquality, PaginationArgs } from '@shell/types/store/pagination.types';
9
9
  import stevePaginationUtils from '@shell/plugins/steve/steve-pagination-utils';
10
10
  import { STEVE_WATCH_MODE } from '@shell/types/store/subscribe.types';
11
11
 
12
+ export function parseStateFilter(stateFilter) {
13
+ if (!stateFilter) {
14
+ return null;
15
+ }
16
+
17
+ const states = stateFilter.split(',').filter(Boolean);
18
+
19
+ if (!states.length) {
20
+ return null;
21
+ }
22
+
23
+ return [new PaginationFilterField({
24
+ field: 'metadata.state.name',
25
+ value: states.join(','),
26
+ equality: PaginationFilterEquality.IN,
27
+ })];
28
+ }
29
+
12
30
  /**
13
31
  * Companion mixin used with `resource-fetch` for `ResourceList` to determine if the user needs to filter the list by a single namespace
14
32
  */
@@ -75,6 +93,7 @@ export default {
75
93
  const {
76
94
  page, perPage, filter, sort, descending
77
95
  } = event;
96
+ const stateFilters = parseStateFilter(this.$route?.query?.stateFilter) || [];
78
97
  const searchFilters = filter.searchQuery ? filter.searchFields.map((field) => new PaginationFilterField({
79
98
  field,
80
99
  value: filter.searchQuery,
@@ -91,6 +110,7 @@ export default {
91
110
  projectsOrNamespaces: this.requestFilters.projectsOrNamespaces,
92
111
  filters: [
93
112
  new PaginationParamFilter({ fields: searchFilters }),
113
+ new PaginationParamFilter({ fields: stateFilters }),
94
114
  ...this.requestFilters.filters, // Apply the additional filters. these aren't from the user but from ns filtering
95
115
  ]
96
116
  });
@@ -17,6 +17,7 @@ describe('clusterRepo', () => {
17
17
  jest.spyOn(model, 'save').mockImplementation().mockResolvedValue(true);
18
18
  jest.spyOn(model, 'waitForState').mockImplementation().mockResolvedValue(true);
19
19
  jest.spyOn(model, '$dispatch', 'get').mockReturnValue(jest.fn());
20
+ jest.spyOn(model, 't', 'get').mockReturnValue((key: string) => key);
20
21
  });
21
22
 
22
23
  describe('refresh', () => {
@@ -59,6 +60,62 @@ describe('clusterRepo', () => {
59
60
  });
60
61
  });
61
62
 
63
+ describe('defaultRefreshIntervalHours', () => {
64
+ it('returns 24 for OCI repos', () => {
65
+ model.spec.url = 'oci://example.com/chart';
66
+ model.spec.insecurePlainHttp = false;
67
+ expect(model.defaultRefreshIntervalHours).toStrictEqual(24);
68
+ });
69
+
70
+ it('returns 1 for git repos', () => {
71
+ model.spec = { gitRepo: 'https://github.com/example/charts' };
72
+ expect(model.defaultRefreshIntervalHours).toStrictEqual(1);
73
+ });
74
+
75
+ it('returns 1 for helm repos', () => {
76
+ model.spec = { url: 'https://charts.example.com' };
77
+ expect(model.defaultRefreshIntervalHours).toStrictEqual(1);
78
+ });
79
+ });
80
+
81
+ describe('defaultRefreshInterval', () => {
82
+ it('returns seconds for OCI repos (24 hours)', () => {
83
+ model.spec.url = 'oci://example.com/chart';
84
+ model.spec.insecurePlainHttp = false;
85
+ expect(model.defaultRefreshInterval).toStrictEqual(86400);
86
+ });
87
+
88
+ it('returns seconds for non-OCI repos (1 hour)', () => {
89
+ model.spec = { gitRepo: 'https://github.com/example/charts' };
90
+ expect(model.defaultRefreshInterval).toStrictEqual(3600);
91
+ });
92
+ });
93
+
94
+ describe('refreshIntervalDisplay', () => {
95
+ it.each([
96
+ -1,
97
+ -100,
98
+ ])('returns "Disabled" when value is %p', (val) => {
99
+ model.spec.refreshInterval = val;
100
+ expect(model.refreshIntervalDisplay).toStrictEqual('generic.disabled');
101
+ });
102
+
103
+ it('returns formatted duration for positive values', () => {
104
+ model.spec.refreshInterval = 3661;
105
+ expect(model.refreshIntervalDisplay).toStrictEqual('1h 1m 1s');
106
+ });
107
+
108
+ it('returns default formatted duration when not set', () => {
109
+ model.spec = { url: 'https://charts.example.com' };
110
+ expect(model.refreshIntervalDisplay).toStrictEqual('1h');
111
+ });
112
+
113
+ it('returns default formatted duration for OCI when not set', () => {
114
+ model.spec = { url: 'oci://example.com/chart', insecurePlainHttp: false };
115
+ expect(model.refreshIntervalDisplay).toStrictEqual('1d');
116
+ });
117
+ });
118
+
62
119
  describe('refreshBulk', () => {
63
120
  it('calls refresh(false) on all items and then dispatches a single catalog/load with all repoKeys', async() => {
64
121
  const mockItem1 = {