@rancher/shell 3.0.12-rc.3 → 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 (258) hide show
  1. package/assets/styles/global/_layout.scss +4 -0
  2. package/assets/translations/en-us.yaml +144 -41
  3. package/assets/translations/zh-hans.yaml +1 -7
  4. package/chart/monitoring/ClusterSelector.vue +0 -21
  5. package/chart/monitoring/prometheus/index.vue +6 -3
  6. package/components/CruResource.vue +161 -14
  7. package/components/ExplorerMembers.vue +8 -4
  8. package/components/ExplorerProjectsNamespaces.vue +10 -6
  9. package/components/GrowlManager.vue +4 -0
  10. package/components/MgmtNodeList.vue +184 -0
  11. package/components/Resource/Detail/Card/StateCard/__tests__/composables.test.ts +90 -1
  12. package/components/Resource/Detail/Card/StateCard/composables.ts +57 -87
  13. package/components/Resource/Detail/Card/StatusCard/__tests__/StatusCard.test.ts +61 -0
  14. package/components/Resource/Detail/Card/StatusCard/index.vue +61 -15
  15. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +2 -0
  16. package/components/Resource/Detail/Metadata/KeyValue.vue +5 -2
  17. package/components/Resource/Detail/Metadata/KeyValueRow.vue +2 -6
  18. package/components/ResourceDetail/index.vue +1 -1
  19. package/components/ResourceList/Masthead.vue +7 -1
  20. package/components/ResourceList/index.vue +82 -1
  21. package/components/RichTranslation.vue +5 -2
  22. package/components/Setting.vue +1 -0
  23. package/components/SubtleLink.vue +31 -6
  24. package/components/Tabbed/Tab.vue +29 -3
  25. package/components/Tabbed/index.vue +25 -3
  26. package/components/TableOfContents/TableOfContents.vue +109 -0
  27. package/components/TableOfContents/composables.ts +258 -0
  28. package/components/Window/ContainerShell.vue +21 -11
  29. package/components/Window/__tests__/ContainerShell.test.ts +107 -37
  30. package/components/Wizard.vue +9 -4
  31. package/components/fleet/AppCoChartGrid.vue +401 -0
  32. package/components/fleet/AppCoEmptyState.vue +127 -0
  33. package/components/fleet/AppCoPageHeader.vue +119 -0
  34. package/components/fleet/AppCoVersionSelect.vue +70 -0
  35. package/components/fleet/FleetClusterTargets/ClusterSelectionFields.vue +217 -0
  36. package/components/fleet/FleetClusterTargets/TargetsList.vue +123 -35
  37. package/components/fleet/FleetClusterTargets/index.vue +189 -146
  38. package/components/fleet/FleetIntro.vue +7 -3
  39. package/components/fleet/FleetNoWorkspaces.vue +7 -3
  40. package/components/fleet/FleetSecretSelector.vue +5 -3
  41. package/components/fleet/FleetValuesFrom.vue +8 -2
  42. package/components/fleet/GitRepoTargetTab.vue +0 -2
  43. package/components/fleet/HelmOpAdvancedTab.vue +19 -53
  44. package/components/fleet/HelmOpAppCoConfigTab.vue +593 -0
  45. package/components/fleet/HelmOpAppCoResourcesSection.vue +162 -0
  46. package/components/fleet/HelmOpResourcesSection.vue +82 -0
  47. package/components/fleet/HelmOpTargetOptionsSection.vue +89 -0
  48. package/components/fleet/HelmOpTargetTab.vue +64 -60
  49. package/components/fleet/HelmOpValuesTab.vue +129 -105
  50. package/components/fleet/__tests__/AppCoEmptyState.test.ts +71 -0
  51. package/components/fleet/__tests__/AppCoVersionSelect.test.ts +36 -0
  52. package/components/fleet/__tests__/ClusterSelectionFields.test.ts +62 -0
  53. package/components/fleet/__tests__/FleetClusterTargets.test.ts +253 -0
  54. package/components/fleet/__tests__/FleetSecretSelector.test.ts +16 -0
  55. package/components/fleet/__tests__/FleetValuesFrom.test.ts +44 -0
  56. package/components/fleet/__tests__/HelmOpAppCoConfigTab.test.ts +59 -0
  57. package/components/fleet/__tests__/HelmOpAppCoResourcesSection.test.ts +62 -0
  58. package/components/fleet/__tests__/HelmOpResourcesSection.test.ts +43 -0
  59. package/components/fleet/__tests__/HelmOpTargetOptionsSection.test.ts +34 -0
  60. package/components/fleet/__tests__/HelmOpValuesTab.test.ts +39 -0
  61. package/components/fleet/__tests__/__snapshots__/AppCoEmptyState.test.ts.snap +97 -0
  62. package/components/fleet/__tests__/__snapshots__/AppCoVersionSelect.test.ts.snap +30 -0
  63. package/components/fleet/__tests__/__snapshots__/ClusterSelectionFields.test.ts.snap +209 -0
  64. package/components/fleet/__tests__/__snapshots__/HelmOpTargetOptionsSection.test.ts.snap +140 -0
  65. package/components/fleet/dashboard/Empty.vue +8 -4
  66. package/components/fleet/dashboard/ResourceCard.vue +28 -0
  67. package/components/fleet/dashboard/ResourceDetails.vue +28 -0
  68. package/components/fleet/dashboard/__tests__/ResourceCard.test.ts +87 -0
  69. package/components/form/ArrayList.vue +61 -4
  70. package/components/form/KeyValue.vue +23 -2
  71. package/components/form/LabeledSelect.vue +39 -1
  72. package/components/form/Labels.vue +22 -3
  73. package/components/form/NameNsDescription.vue +13 -5
  74. package/components/form/ResourceTabs/index.vue +1 -0
  75. package/components/form/__tests__/NameNsDescription.test.ts +75 -0
  76. package/components/formatter/InternalExternalIP.vue +10 -4
  77. package/components/formatter/ServiceTargets.vue +26 -7
  78. package/components/formatter/__tests__/InternalExternalIP.test.ts +132 -0
  79. package/components/formatter/__tests__/ServiceTargets.test.ts +412 -0
  80. package/components/nav/Header.vue +4 -0
  81. package/components/nav/TopLevelMenu.vue +7 -2
  82. package/components/nav/__tests__/Header.test.ts +15 -0
  83. package/components/nav/__tests__/TopLevelMenu.test.ts +120 -2
  84. package/components/templates/default.vue +9 -4
  85. package/components/templates/home.vue +9 -4
  86. package/components/templates/plain.vue +9 -4
  87. package/composables/useHelmOpResources.test.ts +56 -0
  88. package/composables/useHelmOpResources.ts +32 -0
  89. package/composables/useStateColor.test.ts +325 -0
  90. package/composables/useStateColor.ts +128 -0
  91. package/config/home-links.js +1 -1
  92. package/config/labels-annotations.js +1 -0
  93. package/config/product/explorer.js +17 -4
  94. package/config/product/manager.js +2 -0
  95. package/config/router/index.js +16 -0
  96. package/config/router/navigation-guards/__tests__/authentication.test.ts +130 -0
  97. package/config/router/navigation-guards/authentication.js +10 -4
  98. package/config/router/routes.js +20 -6
  99. package/config/settings.ts +0 -2
  100. package/config/table-headers.js +3 -4
  101. package/config/types.js +9 -0
  102. package/core/plugin-products-base.ts +3 -3
  103. package/core/plugin-types.ts +83 -30
  104. package/core/plugin.ts +3 -0
  105. package/core/types-provisioning.ts +34 -1
  106. package/core/types.ts +15 -2
  107. package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +114 -0
  108. package/detail/__tests__/workload.test.ts +3 -152
  109. package/detail/catalog.cattle.io.clusterrepo.vue +1 -1
  110. package/detail/provisioning.cattle.io.cluster.vue +30 -4
  111. package/detail/workload/index.vue +12 -55
  112. package/edit/__tests__/catalog.cattle.io.clusterrepo.test.ts +248 -0
  113. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +105 -0
  114. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/General.test.ts.snap +6 -0
  115. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/index.test.ts.snap +1 -0
  116. package/edit/auth/__tests__/azuread.test.ts +34 -9
  117. package/edit/auth/__tests__/github.test.ts +234 -0
  118. package/edit/auth/__tests__/oidc.test.ts +26 -6
  119. package/edit/auth/__tests__/saml.test.ts +196 -0
  120. package/edit/auth/azuread.vue +128 -95
  121. package/edit/auth/github.vue +72 -13
  122. package/edit/auth/ldap/__tests__/index.test.ts +206 -0
  123. package/edit/auth/ldap/config.vue +8 -0
  124. package/edit/auth/ldap/index.vue +75 -1
  125. package/edit/auth/oidc.vue +119 -73
  126. package/edit/auth/saml.vue +76 -12
  127. package/edit/catalog.cattle.io.clusterrepo.vue +140 -32
  128. package/edit/fleet.cattle.io.helmop.vue +491 -136
  129. package/edit/management.cattle.io.user.vue +5 -2
  130. package/edit/provisioning.cattle.io.cluster/rke2.vue +84 -10
  131. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +11 -0
  132. package/list/group.principal.vue +5 -4
  133. package/list/harvesterhci.io.management.cluster.vue +8 -9
  134. package/list/management.cattle.io.user.vue +12 -9
  135. package/list/provisioning.cattle.io.cluster.vue +16 -10
  136. package/mixins/__tests__/auth-config.test.ts +90 -0
  137. package/mixins/__tests__/chart.test.ts +94 -0
  138. package/mixins/__tests__/resource-fetch-api-pagination.test.ts +48 -0
  139. package/mixins/auth-config.js +7 -0
  140. package/mixins/chart.js +11 -2
  141. package/mixins/child-hook.js +12 -6
  142. package/mixins/create-edit-view/impl.js +5 -3
  143. package/mixins/resource-fetch-api-pagination.js +21 -1
  144. package/models/__tests__/catalog.cattle.io.clusterrepo.test.ts +57 -0
  145. package/models/__tests__/compliance.cattle.io.clusterscan.test.ts +144 -0
  146. package/models/__tests__/fleet-application.test.ts +175 -0
  147. package/models/__tests__/fleet.cattle.io.bundle.test.ts +169 -0
  148. package/models/__tests__/fleet.cattle.io.helmop.test.ts +84 -0
  149. package/models/__tests__/management.cattle.io.node.ts +22 -0
  150. package/models/__tests__/namespace.test.ts +36 -0
  151. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +49 -0
  152. package/models/__tests__/workload.test.ts +401 -26
  153. package/models/catalog.cattle.io.clusterrepo.js +28 -4
  154. package/models/compliance.cattle.io.clusterscan.js +39 -4
  155. package/models/fleet-application.js +4 -0
  156. package/models/fleet.cattle.io.helmop.js +20 -1
  157. package/models/management.cattle.io.cluster.js +18 -2
  158. package/models/management.cattle.io.node.js +44 -3
  159. package/models/namespace.js +1 -1
  160. package/models/pod.js +33 -1
  161. package/models/provisioning.cattle.io.cluster.js +5 -5
  162. package/models/workload.js +108 -13
  163. package/models/workload.service.js +5 -0
  164. package/package.json +14 -13
  165. package/pages/about.vue +5 -6
  166. package/pages/auth/login.vue +0 -35
  167. package/pages/auth/setup.vue +11 -0
  168. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +2 -2
  169. package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +10 -1
  170. package/pages/c/_cluster/apps/charts/__tests__/index.test.ts +93 -0
  171. package/pages/c/_cluster/apps/charts/chart.vue +2 -1
  172. package/pages/c/_cluster/apps/charts/index.vue +48 -10
  173. package/pages/c/_cluster/apps/charts/install.vue +122 -116
  174. package/pages/c/_cluster/auth/roles/index.vue +5 -4
  175. package/pages/c/_cluster/explorer/workload-dashboard/ByNamespaceSection.vue +31 -0
  176. package/pages/c/_cluster/explorer/workload-dashboard/ByStateSection.vue +138 -0
  177. package/pages/c/_cluster/explorer/workload-dashboard/ByTypeSection.vue +30 -0
  178. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadCard.vue +155 -0
  179. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadNamespaceCard.vue +142 -0
  180. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadTypeCard.vue +159 -0
  181. package/pages/c/_cluster/explorer/workload-dashboard/__tests__/composable.test.ts +561 -0
  182. package/pages/c/_cluster/explorer/workload-dashboard/composable.ts +440 -0
  183. package/pages/c/_cluster/explorer/workload-dashboard/index.vue +187 -0
  184. package/pages/c/_cluster/explorer/workload-dashboard/types.ts +80 -0
  185. package/pages/c/_cluster/fleet/application/create.vue +187 -136
  186. package/pages/c/_cluster/fleet/application/index.vue +5 -3
  187. package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailBody.vue +338 -0
  188. package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailHeader.vue +121 -0
  189. package/pages/c/_cluster/fleet/application/suse-app-collection/chart.vue +369 -0
  190. package/pages/c/_cluster/fleet/application/suse-app-collection/charts.vue +248 -0
  191. package/pages/c/_cluster/fleet/application/suse-app-collection/credentials.vue +310 -0
  192. package/pages/c/_cluster/fleet/index.vue +2 -2
  193. package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +96 -0
  194. package/pages/c/_cluster/uiplugins/index.vue +15 -0
  195. package/pages/fail-whale.vue +16 -11
  196. package/pages/home.vue +16 -46
  197. package/plugins/clean-html.d.ts +9 -0
  198. package/plugins/dashboard-store/__tests__/resource-class.test.ts +93 -0
  199. package/plugins/dashboard-store/resource-class.js +62 -7
  200. package/plugins/steve/__tests__/actions.test.ts +212 -0
  201. package/plugins/steve/actions.js +96 -0
  202. package/plugins/steve/steve-pagination-utils.ts +1 -1
  203. package/rancher-components/Accordion/Accordion.vue +53 -9
  204. package/rancher-components/Form/Checkbox/Checkbox.vue +14 -0
  205. package/rancher-components/Form/Radio/RadioButton.vue +17 -1
  206. package/rancher-components/Form/Radio/RadioGroup.vue +10 -0
  207. package/rancher-components/Pill/RcTag/RcTag.vue +3 -2
  208. package/rancher-components/RcButton/RcButton.test.ts +103 -0
  209. package/rancher-components/RcButton/RcButton.vue +94 -15
  210. package/rancher-components/RcButton/types.ts +3 -0
  211. package/rancher-components/RcItemCard/RcItemCard.test.ts +18 -0
  212. package/rancher-components/RcItemCard/RcItemCard.vue +2 -2
  213. package/rancher-components/RcSection/RcSection.vue +28 -3
  214. package/scripts/extension/helm/package/Dockerfile +1 -1
  215. package/scripts/test-plugins-build.sh +2 -1
  216. package/store/__tests__/notifications.test.ts +434 -0
  217. package/store/catalog.js +57 -0
  218. package/store/plugins.js +7 -4
  219. package/types/components/buttonGroup.ts +5 -0
  220. package/types/shell/index.d.ts +104 -70
  221. package/utils/__tests__/auth.test.ts +273 -0
  222. package/utils/__tests__/computed.test.ts +193 -0
  223. package/utils/__tests__/cspAdaptor.test.ts +163 -0
  224. package/utils/__tests__/dom.test.ts +81 -0
  225. package/utils/__tests__/duration.test.ts +37 -1
  226. package/utils/__tests__/dynamic-importer.test.ts +102 -0
  227. package/utils/__tests__/fleet-appco.test.ts +312 -0
  228. package/utils/__tests__/monitoring.test.ts +130 -0
  229. package/utils/__tests__/object.test.ts +22 -0
  230. package/utils/__tests__/platform.test.ts +91 -0
  231. package/utils/__tests__/position.test.ts +237 -0
  232. package/utils/__tests__/provider.test.ts +51 -1
  233. package/utils/__tests__/queue.test.ts +232 -0
  234. package/utils/__tests__/release-notes.test.ts +221 -0
  235. package/utils/__tests__/router.test.js +254 -1
  236. package/utils/__tests__/select.test.ts +208 -0
  237. package/utils/__tests__/time.test.ts +265 -1
  238. package/utils/__tests__/title.test.ts +47 -0
  239. package/utils/__tests__/width.test.ts +53 -0
  240. package/utils/__tests__/window.test.ts +158 -0
  241. package/utils/__tests__/xccdf.test.ts +126 -6
  242. package/utils/crypto/__tests__/browserHashUtils.test.ts +98 -0
  243. package/utils/crypto/__tests__/index.test.ts +144 -0
  244. package/utils/duration.ts +104 -0
  245. package/utils/dynamic-content/__tests__/notification-handler.test.ts +196 -0
  246. package/utils/dynamic-content/info.ts +2 -1
  247. package/utils/error.js +13 -0
  248. package/utils/fleet-appco.ts +323 -0
  249. package/utils/object.js +22 -2
  250. package/utils/provider.ts +12 -0
  251. package/utils/validators/__tests__/container-images.test.ts +104 -0
  252. package/utils/validators/__tests__/flow-output.test.ts +91 -0
  253. package/utils/validators/__tests__/logging-outputs.test.ts +58 -0
  254. package/utils/validators/__tests__/monitoring-route.test.ts +119 -0
  255. package/utils/xccdf.ts +39 -42
  256. package/vue.config.js +1 -1
  257. package/pages/support/index.vue +0 -264
  258. package/utils/duration.js +0 -43
@@ -52,4 +52,97 @@ describe('page: Charts Index', () => {
52
52
  expect(hasExtensionCategory).toBe(false);
53
53
  });
54
54
  });
55
+
56
+ describe('method: loadMore', () => {
57
+ const createContext = (overrides: Record<string, any> = {}) => ({
58
+ isLoadingMore: false,
59
+ visibleChartsCount: 30,
60
+ initialVisibleChartsCount: 30,
61
+ filteredCharts: new Array(100),
62
+ _loadMoreTimer: null,
63
+ $nextTick: (cb: () => void) => cb(),
64
+ ...overrides,
65
+ });
66
+
67
+ beforeEach(() => {
68
+ jest.useFakeTimers();
69
+ });
70
+
71
+ afterEach(() => {
72
+ jest.useRealTimers();
73
+ });
74
+
75
+ it('should set isLoadingMore, then increment visibleChartsCount and clear the flag after the delay', () => {
76
+ const ctx = createContext();
77
+
78
+ (Charts.methods!.loadMore as () => void).call(ctx);
79
+
80
+ expect(ctx.isLoadingMore).toBe(true);
81
+ expect(ctx.visibleChartsCount).toBe(30);
82
+ expect(ctx._loadMoreTimer).not.toBeNull();
83
+
84
+ jest.runAllTimers();
85
+
86
+ expect(ctx.visibleChartsCount).toBe(60);
87
+ expect(ctx.isLoadingMore).toBe(false);
88
+ expect(ctx._loadMoreTimer).toBeNull();
89
+ });
90
+
91
+ it('should do nothing when already loading', () => {
92
+ const ctx = createContext({ isLoadingMore: true });
93
+
94
+ (Charts.methods!.loadMore as () => void).call(ctx);
95
+
96
+ expect(ctx._loadMoreTimer).toBeNull();
97
+ expect(ctx.visibleChartsCount).toBe(30);
98
+ });
99
+
100
+ it('should do nothing when all charts are already visible', () => {
101
+ const ctx = createContext({ visibleChartsCount: 100 });
102
+
103
+ (Charts.methods!.loadMore as () => void).call(ctx);
104
+
105
+ expect(ctx.isLoadingMore).toBe(false);
106
+ expect(ctx._loadMoreTimer).toBeNull();
107
+ });
108
+ });
109
+
110
+ describe('method: resetLazyLoadState', () => {
111
+ beforeEach(() => {
112
+ jest.useFakeTimers();
113
+ });
114
+
115
+ afterEach(() => {
116
+ jest.useRealTimers();
117
+ });
118
+
119
+ it('should reset state and cancel a pending loadMore so visibleChartsCount is not incremented after reset', () => {
120
+ const ctx: Record<string, any> = {
121
+ isLoadingMore: false,
122
+ visibleChartsCount: 30,
123
+ initialVisibleChartsCount: 30,
124
+ observerInitialized: true,
125
+ hasOverflow: true,
126
+ filteredCharts: new Array(100),
127
+ _loadMoreTimer: null,
128
+ $nextTick: (cb: () => void) => cb(),
129
+ };
130
+
131
+ (Charts.methods!.loadMore as () => void).call(ctx);
132
+ expect(ctx._loadMoreTimer).not.toBeNull();
133
+
134
+ (Charts.methods!.resetLazyLoadState as () => void).call(ctx);
135
+
136
+ expect(ctx.visibleChartsCount).toBe(30);
137
+ expect(ctx.observerInitialized).toBe(false);
138
+ expect(ctx.hasOverflow).toBe(false);
139
+ expect(ctx.isLoadingMore).toBe(false);
140
+ expect(ctx._loadMoreTimer).toBeNull();
141
+
142
+ jest.runAllTimers();
143
+
144
+ // The cancelled timer must not have fired — count stays at the reset value.
145
+ expect(ctx.visibleChartsCount).toBe(30);
146
+ });
147
+ });
55
148
  });
@@ -859,6 +859,7 @@ export default {
859
859
  .readme-wrapper {
860
860
  position: relative;
861
861
  flex: 1;
862
+ min-width: 0;
862
863
 
863
864
  .open-readme-button {
864
865
  display: flex;
@@ -881,7 +882,7 @@ export default {
881
882
 
882
883
  &__readme {
883
884
  flex: 1;
884
- min-width: 400px;
885
+ min-width: 0;
885
886
  padding: 12px 24px;
886
887
  }
887
888
  &__info {
@@ -25,6 +25,7 @@ import AppChartCardFooter from '@shell/pages/c/_cluster/apps/charts/AppChartCard
25
25
  import AddRepoLink from '@shell/pages/c/_cluster/apps/charts/AddRepoLink';
26
26
  import StatusLabel from '@shell/pages/c/_cluster/apps/charts/StatusLabel';
27
27
  import RichTranslation from '@shell/components/RichTranslation.vue';
28
+ import SubtleLink from '@shell/components/SubtleLink.vue';
28
29
  import { getLatestCompatibleVersion } from '@shell/utils/chart';
29
30
  import Select from '@shell/components/form/Select';
30
31
  import { getVersionData } from '@shell/config/version';
@@ -36,6 +37,8 @@ const createInitialFilters = () => ({
36
37
  tags: []
37
38
  });
38
39
 
40
+ const LOAD_MORE_DELAY_MS = 500;
41
+
39
42
  export default {
40
43
  name: 'Charts',
41
44
  components: {
@@ -47,7 +50,8 @@ export default {
47
50
  AppChartCardSubHeader,
48
51
  AppChartCardFooter,
49
52
  Select,
50
- RichTranslation
53
+ RichTranslation,
54
+ SubtleLink,
51
55
  },
52
56
 
53
57
  async fetch() {
@@ -84,6 +88,10 @@ export default {
84
88
  if (this.observer) {
85
89
  this.observer.disconnect();
86
90
  }
91
+ if (this._loadMoreTimer) {
92
+ clearTimeout(this._loadMoreTimer);
93
+ this._loadMoreTimer = null;
94
+ }
87
95
  },
88
96
 
89
97
  data() {
@@ -145,6 +153,7 @@ export default {
145
153
  canCreateRepos: false,
146
154
  showAppCollectionBanner: true,
147
155
  isPrime: getVersionData().RancherPrime === 'true',
156
+ isLoadingMore: false,
148
157
  };
149
158
  },
150
159
 
@@ -482,6 +491,11 @@ export default {
482
491
  this.visibleChartsCount = this.initialVisibleChartsCount;
483
492
  this.observerInitialized = false;
484
493
  this.hasOverflow = false;
494
+ this.isLoadingMore = false;
495
+ if (this._loadMoreTimer) {
496
+ clearTimeout(this._loadMoreTimer);
497
+ this._loadMoreTimer = null;
498
+ }
485
499
  },
486
500
 
487
501
  // The lazy loading implementation has two parts
@@ -529,10 +543,17 @@ export default {
529
543
  },
530
544
 
531
545
  loadMore() {
532
- if (this.visibleChartsCount >= this.filteredCharts.length) {
546
+ if (this.isLoadingMore || this.visibleChartsCount >= this.filteredCharts.length) {
533
547
  return;
534
548
  }
535
- this.visibleChartsCount += this.initialVisibleChartsCount;
549
+ this.isLoadingMore = true;
550
+ this._loadMoreTimer = setTimeout(() => {
551
+ this._loadMoreTimer = null;
552
+ this.visibleChartsCount += this.initialVisibleChartsCount;
553
+ this.$nextTick(() => {
554
+ this.isLoadingMore = false;
555
+ });
556
+ }, LOAD_MORE_DELAY_MS);
536
557
  },
537
558
 
538
559
  initIntersectionObserver() {
@@ -683,16 +704,13 @@ export default {
683
704
  tag="span"
684
705
  >
685
706
  <template #docsUrl="{ content }">
686
- <a
707
+ <SubtleLink
687
708
  :href="`${DOCS_BASE}/how-to-guides/new-user-guides/helm-charts-in-rancher`"
688
- class="secondary-text-link"
689
- tabindex="0"
690
709
  target="_blank"
691
- rel="noopener noreferrer nofollow"
710
+ :open-in-new-tab-label="t('generic.opensInNewTab')"
692
711
  >
693
- <span class="sr-only">{{ t('generic.opensInNewTab') }}</span>
694
- {{ content }} <i class="icon icon-external-link" />
695
- </a>
712
+ {{ content }}
713
+ </SubtleLink>
696
714
  </template>
697
715
  </RichTranslation>
698
716
  </div>
@@ -785,6 +803,16 @@ export default {
785
803
  </template>
786
804
  </rc-item-card>
787
805
  </div>
806
+ <div
807
+ v-if="isLoadingMore"
808
+ class="loading-more"
809
+ role="status"
810
+ aria-live="polite"
811
+ data-testid="charts-loading-more"
812
+ >
813
+ <i class="icon icon-spinner icon-spin" />
814
+ {{ t('catalog.charts.loadingMore') }}
815
+ </div>
788
816
  <div
789
817
  ref="sentinel"
790
818
  class="sentinel-charts"
@@ -838,6 +866,16 @@ export default {
838
866
  .sentinel-charts {
839
867
  height: 1px;
840
868
  }
869
+
870
+ .loading-more {
871
+ display: flex;
872
+ align-items: center;
873
+ justify-content: center;
874
+ gap: var(--gap);
875
+ padding: 16px;
876
+ font-size: 14px;
877
+ color: var(--muted);
878
+ }
841
879
  }
842
880
 
843
881
  .total-and-sort {
@@ -22,7 +22,6 @@ import Tabbed from '@shell/components/Tabbed';
22
22
  import UnitInput from '@shell/components/form/UnitInput';
23
23
  import YamlEditor, { EDITOR_MODES } from '@shell/components/YamlEditor';
24
24
  import Wizard from '@shell/components/Wizard';
25
- import TypeDescription from '@shell/components/TypeDescription';
26
25
  import ChartMixin from '@shell/mixins/chart';
27
26
  import ChildHook, { BEFORE_SAVE_HOOKS, AFTER_SAVE_HOOKS } from '@shell/mixins/child-hook';
28
27
  import {
@@ -41,7 +40,7 @@ import {
41
40
  import { ignoreVariables } from './install.helpers';
42
41
  import { findBy, insertAt } from '@shell/utils/array';
43
42
  import { saferDump } from '@shell/utils/create-yaml';
44
- import { WINDOWS, isRancherRepo, getPermittedOSs } from '@shell/store/catalog';
43
+ import { WINDOWS } from '@shell/store/catalog';
45
44
  import { SETTING } from '@shell/config/settings';
46
45
  import SelectOrCreateAuthSecret from '@shell/components/form/SelectOrCreateAuthSecret.vue';
47
46
  import { generateRandomAlphaString } from '@shell/utils/string';
@@ -96,7 +95,6 @@ export default {
96
95
  UnitInput,
97
96
  YamlEditor,
98
97
  Wizard,
99
- TypeDescription,
100
98
  SelectOrCreateAuthSecret
101
99
  },
102
100
 
@@ -601,6 +599,10 @@ export default {
601
599
  return out;
602
600
  },
603
601
 
602
+ selectedVersionOption() {
603
+ return this.filteredVersions?.find((v) => v.id === this.query.versionName) || this.query.versionName;
604
+ },
605
+
604
606
  showSelectVersionOrChart() {
605
607
  // Allow the user to choose a version if:
606
608
  // - the app exists (editing/upgrading)
@@ -680,7 +682,7 @@ export default {
680
682
  },
681
683
 
682
684
  stepperSubtext() {
683
- return this.existing && this.currentVersion !== this.targetVersion ? `${ this.currentVersion } > ${ this.targetVersion }` : this.targetVersion;
685
+ return this.mappedVersions?.find((v) => v.id === this.targetVersion)?.label || this.targetVersion;
684
686
  },
685
687
 
686
688
  readmeWindowName() {
@@ -765,24 +767,6 @@ export default {
765
767
  return { name: 'c-cluster-legacy-project' };
766
768
  },
767
769
 
768
- windowsIncompatible() {
769
- if (this.versionInfo) {
770
- const isRancher = isRancherRepo(this.repo, this.chart);
771
- const permittedSystems = getPermittedOSs(this.versionInfo?.chart?.annotations, isRancher);
772
- const incompatibleVersion = permittedSystems.length > 0 && !permittedSystems.includes('windows');
773
-
774
- if (incompatibleVersion) {
775
- if (!this.chart?.windowsIncompatible) {
776
- return this.t('catalog.charts.versionWindowsIncompatible');
777
- }
778
-
779
- return this.t('catalog.charts.windowsIncompatible');
780
- }
781
- }
782
-
783
- return null;
784
- },
785
-
786
770
  /**
787
771
  * Check if the chart contains `systemDefaultRegistry` properties.
788
772
  * If not we shouldn't apply the setting, because if the option
@@ -1554,10 +1538,9 @@ export default {
1554
1538
  <Loading v-if="$fetchState.pending" />
1555
1539
  <div
1556
1540
  v-else-if="!legacyApp && !mcapp"
1557
- class="install-steps pt-20"
1541
+ class="install-steps"
1558
1542
  :class="{ 'isPlainLayout': isPlainLayout}"
1559
1543
  >
1560
- <TypeDescription resource="chart" />
1561
1544
  <Wizard
1562
1545
  v-if="value"
1563
1546
  :steps="steps"
@@ -1567,25 +1550,45 @@ export default {
1567
1550
  :banner-title-subtext="stepperSubtext"
1568
1551
  :finish-mode="action.name"
1569
1552
  :header-mode="action.tKey"
1553
+ :show-step-header="false"
1570
1554
  class="wizard"
1571
- :class="{'windowsIncompatible': windowsIncompatible}"
1572
1555
  @cancel="cancel"
1573
1556
  @finish="finish"
1574
1557
  >
1575
- <template #bannerTitleImage>
1576
- <div>
1577
- <div class="logo-bg">
1578
- <LazyImage
1579
- :src="chart ? chart.icon : ''"
1580
- class="logo"
1581
- />
1558
+ <template #bannerTitle>
1559
+ <div class="chart-title-container">
1560
+ <div class="logo-container">
1561
+ <div class="logo-box">
1562
+ <LazyImage
1563
+ :src="chart ? chart.icon : ''"
1564
+ class="logo"
1565
+ />
1566
+ </div>
1567
+ </div>
1568
+ <div class="chart-title">
1569
+ <h1>
1570
+ <router-link
1571
+ v-if="chart"
1572
+ :to="chartLocation()"
1573
+ data-testid="chart-install-name-link"
1574
+ >
1575
+ {{ stepperName }}
1576
+ </router-link>
1577
+ <span v-else>
1578
+ {{ stepperName }}
1579
+ </span>: {{ t(`wizard.${action.tKey}`) }}
1580
+ </h1>
1581
+ <span
1582
+ v-if="stepperSubtext"
1583
+ class="subtext"
1584
+ >
1585
+ <i
1586
+ v-clean-tooltip="t('tableHeaders.version')"
1587
+ class="icon icon-version-alt"
1588
+ />
1589
+ {{ stepperSubtext }}
1590
+ </span>
1582
1591
  </div>
1583
- <label
1584
- v-if="windowsIncompatible"
1585
- class="os-label"
1586
- >
1587
- {{ windowsIncompatible }}
1588
- </label>
1589
1592
  </div>
1590
1593
  </template>
1591
1594
  <template #basics>
@@ -1629,20 +1632,26 @@ export default {
1629
1632
  v-if="showSelectVersionOrChart"
1630
1633
  class="row mb-20"
1631
1634
  >
1632
- <div class="col span-4">
1633
- <!-- We have a chart for the app, let the user select a new version -->
1635
+ <!-- We have a chart for the app, let the user select a new version -->
1636
+ <div
1637
+ v-if="chart"
1638
+ class="col span-4"
1639
+ >
1634
1640
  <LabeledSelect
1635
- v-if="chart"
1636
1641
  data-testid="chart-version-selector"
1637
1642
  :label="t('catalog.install.version')"
1638
- :value="query.versionName"
1643
+ :value="selectedVersionOption"
1639
1644
  :options="filteredVersions"
1640
1645
  :selectable="version => !version.disabled"
1641
1646
  @update:value="selectVersion"
1642
1647
  />
1643
- <!-- Can't find the chart for the app, let the user try to select one -->
1648
+ </div>
1649
+ <!-- Can't find the chart for the app, let the user try to select one -->
1650
+ <div
1651
+ v-else
1652
+ class="col span-4"
1653
+ >
1644
1654
  <LabeledSelect
1645
- v-else
1646
1655
  :label="t('catalog.install.chart')"
1647
1656
  :value="chart"
1648
1657
  :options="charts"
@@ -1791,7 +1800,7 @@ export default {
1791
1800
  <LabeledSelect
1792
1801
  v-if="chart"
1793
1802
  :label="t('catalog.install.version')"
1794
- :value="query.versionName"
1803
+ :value="selectedVersionOption"
1795
1804
  :options="filteredVersions"
1796
1805
  :selectable="version => !version.disabled"
1797
1806
  @update:value="selectVersion"
@@ -2046,30 +2055,37 @@ export default {
2046
2055
  :class="{ 'isPlainLayout': isPlainLayout}"
2047
2056
  >
2048
2057
  <div class="outer-container">
2049
- <div class="header mb-20">
2050
- <div class="title">
2051
- <div class="top choice-banner">
2052
- <div class="title">
2053
- <!-- Logo -->
2054
- <slot name="bannerTitleImage">
2055
- <div class="round-image">
2056
- <LazyImage
2057
- :src="chart ? chart.icon : ''"
2058
- class="logo"
2059
- />
2060
- </div>
2061
- </slot>
2062
- <!-- Title with subtext -->
2063
- <div class="subtitle">
2064
- <h2 v-if="stepperName">
2065
- {{ stepperName }}
2066
- </h2>
2067
- <span
2068
- v-if="stepperSubtext"
2069
- class="subtext"
2070
- >{{ stepperSubtext }}</span>
2058
+ <div class="header mmt-6 mmb-6">
2059
+ <div class="top choice-banner">
2060
+ <div class="chart-title-container mmb-6">
2061
+ <div class="logo-container">
2062
+ <div class="logo-box">
2063
+ <LazyImage
2064
+ :src="chart ? chart.icon : ''"
2065
+ class="logo"
2066
+ />
2071
2067
  </div>
2072
2068
  </div>
2069
+ <div class="chart-title">
2070
+ <h2 v-if="stepperName">
2071
+ <router-link
2072
+ :to="chartLocation()"
2073
+ data-testid="chart-install-name-link"
2074
+ >
2075
+ {{ stepperName }}
2076
+ </router-link>
2077
+ </h2>
2078
+ <span
2079
+ v-if="stepperSubtext"
2080
+ class="subtext"
2081
+ >
2082
+ <i
2083
+ v-clean-tooltip="t('tableHeaders.version')"
2084
+ class="icon icon-version-alt"
2085
+ />
2086
+ {{ stepperSubtext }}
2087
+ </span>
2088
+ </div>
2073
2089
  </div>
2074
2090
  </div>
2075
2091
  </div>
@@ -2100,12 +2116,16 @@ export default {
2100
2116
  </template>
2101
2117
 
2102
2118
  <style lang="scss" scoped>
2119
+ .chart-version-footnote {
2120
+ margin-top: 8px;
2121
+ color: var(--input-label);
2122
+ }
2123
+
2103
2124
  $title-height: 50px;
2104
2125
  $padding: 5px;
2105
2126
  $slideout-width: 35%;
2106
2127
 
2107
2128
  .install-steps {
2108
- padding-top: 0;
2109
2129
  height: 0;
2110
2130
  position: relative;
2111
2131
  overflow: hidden;
@@ -2121,37 +2141,29 @@ export default {
2121
2141
  }
2122
2142
  }
2123
2143
 
2124
- .wizard {
2125
- .logo-bg {
2126
- margin-right: 10px;
2127
- height: $title-height;
2128
- width: $title-height;
2129
- background-color: white;
2130
- border: $padding solid white;
2131
- border-radius: calc( 3 * var(--border-radius));
2132
- position: relative;
2133
- }
2144
+ .logo-container {
2145
+ display: flex;
2146
+ flex-direction: column;
2147
+ align-items: center;
2134
2148
 
2135
- .logo {
2136
- max-height: $title-height - 2 * $padding;
2137
- max-width: $title-height - 2 * $padding;
2138
- position: absolute;
2139
- width: auto;
2140
- height: auto;
2141
- top: 0;
2142
- right: 0;
2143
- bottom: 0;
2144
- left: 0;
2145
- margin: auto;
2146
- }
2149
+ .logo-box {
2150
+ width: 60px;
2151
+ height: 60px;
2152
+ display: flex;
2153
+ justify-content: center;
2154
+ align-items: center;
2155
+ background: #fff;
2156
+ border-radius: var(--border-radius);
2147
2157
 
2148
- // Hack - We're adding an absolute tag under the logo that we want to consume space without breaking vertical alignment of row.
2149
- // W ith the slots available this isn't possible without adding tag specific styles to the root wizard classes
2150
- &.windowsIncompatible {
2151
- :deep() .header {
2152
- padding-bottom: 15px;
2158
+ .logo {
2159
+ width: 48px;
2160
+ height: 48px;
2161
+ object-fit: contain;
2153
2162
  }
2154
2163
  }
2164
+ }
2165
+
2166
+ .wizard {
2155
2167
 
2156
2168
  .os-label {
2157
2169
  position: absolute;
@@ -2300,38 +2312,43 @@ export default {
2300
2312
 
2301
2313
  border-bottom: var(--header-border-size) solid var(--header-border);
2302
2314
 
2303
- & > .title {
2315
+ & > .chart-title-container {
2304
2316
  flex: 1;
2305
2317
  min-height: 75px;
2306
2318
  }
2307
2319
 
2308
2320
  .choice-banner {
2309
2321
 
2310
- flex-basis: 40%;
2322
+ flex-basis: 100%;
2311
2323
  display: flex;
2312
2324
  align-items: center;
2313
2325
 
2314
2326
  &.top {
2315
2327
 
2316
- H2 {
2328
+ H1, H2 {
2317
2329
  margin: 0px;
2318
2330
  }
2319
2331
 
2320
- .title{
2332
+ .chart-title-container {
2321
2333
  display: flex;
2322
- align-items: center;
2334
+ align-items: flex-start;
2323
2335
  justify-content: space-evenly;
2324
-
2325
- & > .subtitle {
2326
- margin: 0 20px;
2327
- }
2336
+ gap: 24px;
2328
2337
  }
2329
2338
 
2330
- .subtitle{
2339
+ .chart-title {
2331
2340
  display: flex;
2332
2341
  flex-direction: column;
2333
2342
  & .subtext {
2343
+ display: flex;
2344
+ align-items: center;
2345
+ gap: 8px;
2334
2346
  color: var(--input-label);
2347
+ margin-top: 8px;
2348
+
2349
+ .icon-version-alt {
2350
+ font-size: 19px;
2351
+ }
2335
2352
  }
2336
2353
  }
2337
2354
 
@@ -2348,17 +2365,6 @@ export default {
2348
2365
  }
2349
2366
  }
2350
2367
 
2351
- & .round-image {
2352
- min-width: 50px;
2353
- height: 50px;
2354
- margin: 10px 10px 10px 0;
2355
- border-radius: 50%;
2356
- overflow: hidden;
2357
- .logo {
2358
- min-width: 50px;
2359
- height: 50px;
2360
- }
2361
- }
2362
2368
  }
2363
2369
  }
2364
2370
 
@@ -7,6 +7,7 @@ import Loading from '@shell/components/Loading';
7
7
  import { SUBTYPE_MAPPING, CREATE_VERBS } from '@shell/models/management.cattle.io.roletemplate';
8
8
  import { NAME } from '@shell/config/product/auth';
9
9
  import { BLANK_CLUSTER } from '@shell/store/store-types.js';
10
+ import { RcButton } from '@components/RcButton';
10
11
 
11
12
  const GLOBAL = SUBTYPE_MAPPING.GLOBAL.key;
12
13
  const CLUSTER = SUBTYPE_MAPPING.CLUSTER.key;
@@ -31,7 +32,7 @@ export default {
31
32
  name: 'Roles',
32
33
 
33
34
  components: {
34
- Tab, Tabbed, ResourceTable, Loading
35
+ Tab, Tabbed, ResourceTable, Loading, RcButton
35
36
  },
36
37
 
37
38
  async fetch() {
@@ -162,13 +163,13 @@ export default {
162
163
  </div>
163
164
  <div class="actions-container">
164
165
  <div class="actions">
165
- <router-link
166
+ <rc-button
166
167
  v-if="canCreate"
168
+ size="large"
167
169
  :to="createLocation"
168
- class="btn role-primary"
169
170
  >
170
171
  {{ createLabel }}
171
- </router-link>
172
+ </rc-button>
172
173
  </div>
173
174
  </div>
174
175
  </header>
@@ -0,0 +1,31 @@
1
+ <script setup lang="ts">
2
+ import WorkloadNamespaceCard from './WorkloadNamespaceCard.vue';
3
+ import type { WorkloadDashboardByNamespaceCard, WorkloadDashboardNamespaceNavigateFn, WorkloadDashboardFilterByNamespaceFn } from './types';
4
+
5
+ defineProps<{
6
+ cards: WorkloadDashboardByNamespaceCard[];
7
+ navigateToNamespace: WorkloadDashboardNamespaceNavigateFn;
8
+ filterByNamespace: WorkloadDashboardFilterByNamespaceFn;
9
+ }>();
10
+ </script>
11
+
12
+ <template>
13
+ <div class="card-grid">
14
+ <WorkloadNamespaceCard
15
+ v-for="card in cards"
16
+ :key="card.title"
17
+ :title="card.title"
18
+ :rows="card.rows"
19
+ :navigate-to-namespace="navigateToNamespace"
20
+ :filter-by-namespace="filterByNamespace"
21
+ />
22
+ </div>
23
+ </template>
24
+
25
+ <style lang="scss" scoped>
26
+ .card-grid {
27
+ display: grid;
28
+ grid-template-columns: repeat(3, 1fr);
29
+ gap: 15px;
30
+ }
31
+ </style>