@rancher/shell 3.0.6 → 3.0.8-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. package/assets/images/pl/dark/rancher-logo.svg +131 -44
  2. package/assets/images/pl/rancher-logo.svg +120 -44
  3. package/assets/images/vendor/githubapp.svg +13 -0
  4. package/assets/styles/base/_basic.scss +2 -2
  5. package/assets/styles/base/_color-classic.scss +51 -0
  6. package/assets/styles/base/_color.scss +3 -3
  7. package/assets/styles/base/_mixins.scss +1 -1
  8. package/assets/styles/base/_typography.scss +1 -1
  9. package/assets/styles/base/_variables-classic.scss +47 -0
  10. package/assets/styles/global/_button.scss +49 -17
  11. package/assets/styles/global/_form.scss +1 -1
  12. package/assets/styles/themes/_dark.scss +4 -0
  13. package/assets/styles/themes/_light.scss +3 -69
  14. package/assets/styles/themes/_modern.scss +194 -50
  15. package/assets/styles/vendor/vue-select.scss +1 -2
  16. package/assets/translations/en-us.yaml +124 -32
  17. package/assets/translations/zh-hans.yaml +0 -4
  18. package/components/ClusterIconMenu.vue +1 -1
  19. package/components/ClusterProviderIcon.vue +1 -1
  20. package/components/CodeMirror.vue +1 -1
  21. package/components/IconOrSvg.vue +40 -29
  22. package/components/Inactivity.vue +222 -106
  23. package/components/InstallHelmCharts.vue +2 -2
  24. package/components/ResourceDetail/index.vue +2 -1
  25. package/components/SortableTable/index.vue +17 -2
  26. package/components/SortableTable/sorting.js +3 -1
  27. package/components/Tabbed/index.vue +5 -5
  28. package/components/fleet/FleetConfigMapSelector.vue +117 -0
  29. package/components/fleet/FleetSecretSelector.vue +127 -0
  30. package/components/fleet/__tests__/FleetConfigMapSelector.test.ts +125 -0
  31. package/components/fleet/__tests__/FleetSecretSelector.test.ts +82 -0
  32. package/components/form/FileImageSelector.vue +13 -4
  33. package/components/form/FileSelector.vue +11 -2
  34. package/components/form/ResourceLabeledSelect.vue +1 -0
  35. package/components/form/ResourceTabs/index.vue +37 -18
  36. package/components/form/SecretSelector.vue +6 -2
  37. package/components/form/__tests__/ResourceLabeledSelect.test.ts +90 -0
  38. package/components/nav/Group.vue +29 -9
  39. package/components/nav/Header.vue +7 -8
  40. package/components/nav/NamespaceFilter.vue +1 -1
  41. package/components/nav/TopLevelMenu.helper.ts +47 -20
  42. package/components/nav/TopLevelMenu.vue +44 -14
  43. package/components/nav/Type.vue +0 -5
  44. package/components/nav/__tests__/TopLevelMenu.test.ts +2 -0
  45. package/config/pagination-table-headers.js +10 -2
  46. package/config/product/auth.js +1 -0
  47. package/config/product/explorer.js +4 -3
  48. package/config/query-params.js +1 -0
  49. package/config/settings.ts +8 -1
  50. package/config/table-headers.js +9 -0
  51. package/config/types.js +2 -0
  52. package/core/plugin.ts +18 -6
  53. package/core/types.ts +8 -0
  54. package/detail/provisioning.cattle.io.cluster.vue +1 -0
  55. package/dialog/AddonConfigConfirmationDialog.vue +45 -1
  56. package/dialog/InstallExtensionDialog.vue +71 -45
  57. package/dialog/UninstallExtensionDialog.vue +2 -1
  58. package/dialog/__tests__/InstallExtensionDialog.test.ts +111 -0
  59. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +52 -11
  60. package/edit/auth/AuthProviderWarningBanners.vue +14 -1
  61. package/edit/auth/github-app-steps.vue +97 -0
  62. package/edit/auth/github-steps.vue +75 -0
  63. package/edit/auth/github.vue +94 -65
  64. package/edit/auth/oidc.vue +86 -16
  65. package/edit/fleet.cattle.io.helmop.vue +51 -2
  66. package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +15 -5
  67. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +11 -9
  68. package/edit/provisioning.cattle.io.cluster/rke2.vue +56 -9
  69. package/edit/provisioning.cattle.io.cluster/tabs/AddOnConfig.vue +28 -2
  70. package/list/projectsecret.vue +1 -1
  71. package/machine-config/azure.vue +1 -1
  72. package/mixins/__tests__/chart.test.ts +1 -1
  73. package/mixins/chart.js +2 -2
  74. package/models/__tests__/chart.test.ts +17 -9
  75. package/models/__tests__/compliance.cattle.io.clusterscanprofile.spec.js +30 -0
  76. package/models/catalog.cattle.io.app.js +1 -1
  77. package/models/chart.js +3 -1
  78. package/models/compliance.cattle.io.clusterscanprofile.js +1 -1
  79. package/models/event.js +7 -0
  80. package/models/management.cattle.io.authconfig.js +1 -0
  81. package/models/provisioning.cattle.io.cluster.js +9 -0
  82. package/package.json +2 -2
  83. package/pages/auth/login.vue +5 -2
  84. package/pages/auth/verify.vue +1 -1
  85. package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +3 -2
  86. package/pages/c/_cluster/apps/charts/chart.vue +2 -2
  87. package/pages/c/_cluster/explorer/EventsTable.vue +92 -9
  88. package/pages/c/_cluster/explorer/tools/index.vue +3 -3
  89. package/pages/c/_cluster/settings/performance.vue +13 -26
  90. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +159 -62
  91. package/pages/c/_cluster/uiplugins/__tests__/PluginInfoPanel.test.ts +102 -0
  92. package/pages/c/_cluster/uiplugins/__tests__/{index.spec.ts → index.test.ts} +121 -55
  93. package/pages/c/_cluster/uiplugins/index.vue +110 -94
  94. package/pages/home.vue +313 -12
  95. package/plugins/__tests__/subscribe.events.test.ts +194 -0
  96. package/plugins/axios.js +2 -1
  97. package/plugins/dashboard-store/actions.js +4 -1
  98. package/plugins/dashboard-store/getters.js +1 -1
  99. package/plugins/dashboard-store/resource-class.js +20 -5
  100. package/plugins/steve/__tests__/subscribe.spec.ts +27 -24
  101. package/plugins/steve/index.js +18 -10
  102. package/plugins/steve/mutations.js +2 -2
  103. package/plugins/steve/resourceWatcher.js +2 -2
  104. package/plugins/steve/steve-pagination-utils.ts +12 -9
  105. package/plugins/steve/subscribe.js +113 -85
  106. package/plugins/subscribe-events.ts +211 -0
  107. package/rancher-components/BadgeState/BadgeState.vue +8 -6
  108. package/rancher-components/Banner/Banner.vue +2 -1
  109. package/rancher-components/Form/Checkbox/Checkbox.vue +3 -3
  110. package/rancher-components/Form/Radio/RadioButton.vue +3 -3
  111. package/scripts/extension/publish +1 -1
  112. package/store/auth.js +8 -3
  113. package/store/aws.js +8 -6
  114. package/store/features.js +1 -0
  115. package/store/index.js +21 -25
  116. package/store/prefs.js +6 -0
  117. package/types/extension-manager.ts +8 -1
  118. package/types/kube/kube-api.ts +2 -1
  119. package/types/rancher/index.d.ts +1 -0
  120. package/types/resources/settings.d.ts +52 -23
  121. package/types/shell/index.d.ts +412 -336
  122. package/types/store/subscribe-events.types.ts +70 -0
  123. package/types/store/subscribe.types.ts +6 -22
  124. package/utils/__tests__/cluster.test.ts +379 -1
  125. package/utils/cluster.js +157 -3
  126. package/utils/dynamic-content/__tests__/config.test.ts +187 -0
  127. package/utils/dynamic-content/__tests__/index.test.ts +390 -0
  128. package/utils/dynamic-content/__tests__/info.test.ts +263 -0
  129. package/utils/dynamic-content/__tests__/new-release.test.ts +216 -0
  130. package/utils/dynamic-content/__tests__/support-notice.test.ts +262 -0
  131. package/utils/dynamic-content/__tests__/util.test.ts +235 -0
  132. package/utils/dynamic-content/config.ts +55 -0
  133. package/utils/dynamic-content/index.ts +273 -0
  134. package/utils/dynamic-content/info.ts +219 -0
  135. package/utils/dynamic-content/new-release.ts +126 -0
  136. package/utils/dynamic-content/support-notice.ts +169 -0
  137. package/utils/dynamic-content/types.d.ts +101 -0
  138. package/utils/dynamic-content/util.ts +122 -0
  139. package/utils/inactivity.ts +104 -0
  140. package/utils/pagination-utils.ts +105 -31
  141. package/utils/pagination-wrapper.ts +6 -8
  142. package/utils/release-notes.ts +1 -1
  143. package/utils/sort.js +5 -0
  144. package/utils/unit-tests/pagination-utils.spec.ts +283 -0
  145. package/utils/validators/formRules/__tests__/index.test.ts +7 -0
  146. package/utils/validators/formRules/index.ts +2 -2
@@ -48,6 +48,27 @@ export default {
48
48
  initYamlEditor: {
49
49
  type: Function,
50
50
  required: true,
51
+ },
52
+ /**
53
+ * Indicates if a configuration conflict was detected for this addon.
54
+ */
55
+ hasDiff: {
56
+ type: Boolean,
57
+ default: false
58
+ },
59
+ /**
60
+ * The Kubernetes version the user is upgrading from.
61
+ */
62
+ previousKubeVersion: {
63
+ type: String,
64
+ default: ''
65
+ },
66
+ /**
67
+ * The Kubernetes version the user is upgrading to.
68
+ */
69
+ newKubeVersion: {
70
+ type: String,
71
+ default: ''
51
72
  }
52
73
 
53
74
  },
@@ -73,10 +94,15 @@ export default {
73
94
  <template>
74
95
  <div>
75
96
  <Banner
76
- v-if="isEdit"
97
+ v-if="isEdit && hasDiff"
77
98
  color="warning"
78
99
  >
79
- {{ t('cluster.addOns.dependencyBanner') }}
100
+ <span
101
+ v-clean-html="t('cluster.addOns.dependencyBanner', {
102
+ previousKubeVersion,
103
+ newKubeVersion
104
+ }, true)"
105
+ />
80
106
  </Banner>
81
107
  <div
82
108
  v-if="versionInfo && addonVersion"
@@ -199,7 +199,7 @@ export default {
199
199
  }
200
200
 
201
201
  // Filter in if this cluster
202
- if (metadata.namespace.startsWith(this.currentCluster.id)) {
202
+ if (metadata.namespace?.startsWith(this.currentCluster.id)) {
203
203
  return true;
204
204
  }
205
205
 
@@ -35,7 +35,7 @@ const defaultConfig = {
35
35
  dns: '',
36
36
  environment: 'AzurePublicCloud',
37
37
  faultDomainCount: '3',
38
- image: 'canonical:UbuntuServer:18.04-LTS:latest',
38
+ image: 'canonical:ubuntu-24_04-lts:server-gen1:latest',
39
39
  location: 'westus',
40
40
  managedDisks: false,
41
41
  noPublicIp: false,
@@ -213,7 +213,7 @@ describe('chartMixin', () => {
213
213
  });
214
214
 
215
215
  expect(wrapper.vm.action).toStrictEqual({
216
- name: 'upgradeVersion',
216
+ name: 'upgrade',
217
217
  tKey: 'upgrade',
218
218
  icon: 'icon-upgrade-alt',
219
219
  });
package/mixins/chart.js CHANGED
@@ -220,7 +220,7 @@ export default {
220
220
  },
221
221
 
222
222
  currentVersion() {
223
- return this.existing?.spec.chart.metadata.version;
223
+ return this.existing?.spec?.chart?.metadata?.version;
224
224
  },
225
225
 
226
226
  targetVersion() {
@@ -242,7 +242,7 @@ export default {
242
242
 
243
243
  if (compare(this.currentVersion, this.targetVersion) < 0) {
244
244
  return {
245
- name: 'upgradeVersion', tKey: 'upgrade', icon: 'icon-upgrade-alt'
245
+ name: 'upgrade', tKey: 'upgrade', icon: 'icon-upgrade-alt'
246
246
  };
247
247
  }
248
248
 
@@ -12,9 +12,9 @@ type MockChartContext = {
12
12
  };
13
13
 
14
14
  interface CardContent {
15
- subHeaderItems: { label: string }[];
15
+ subHeaderItems: { label: string, labelTooltip?: string}[];
16
16
  footerItems: { labels: string[]; icon?: string }[];
17
- statuses: { tooltip: { key: string }; color: string }[];
17
+ statuses: { tooltip: { key?: string; text?: string }; color: string }[];
18
18
  }
19
19
 
20
20
  const t = jest.fn((key) => key); // mock translation function
@@ -225,7 +225,7 @@ describe('class Chart', () => {
225
225
 
226
226
  const result = chart.cardContent as CardContent;
227
227
 
228
- const deprecatedStatus = result.statuses.find((s) => s.tooltip.key === 'generic.deprecated');
228
+ const deprecatedStatus = result.statuses.find((s) => s.tooltip?.key === 'generic.deprecated');
229
229
 
230
230
  expect(deprecatedStatus).toBeDefined();
231
231
  expect(deprecatedStatus?.color).toBe('error');
@@ -240,10 +240,11 @@ describe('class Chart', () => {
240
240
 
241
241
  const result = chart.cardContent as CardContent;
242
242
 
243
- const installedStatus = result.statuses.find((s) => s.tooltip.key === 'generic.installed');
243
+ const installedStatus = result.statuses.find((s) => s.tooltip?.text?.startsWith('generic.installed'));
244
244
 
245
245
  expect(installedStatus).toBeDefined();
246
246
  expect(installedStatus?.color).toBe('success');
247
+ expect(installedStatus?.tooltip?.text).toContain(installedApp.spec.chart.metadata.version);
247
248
  });
248
249
 
249
250
  it('includes upgradeable status when upgrade is available', () => {
@@ -255,7 +256,7 @@ describe('class Chart', () => {
255
256
 
256
257
  const result = chart.cardContent as CardContent;
257
258
 
258
- const upgradeableStatus = result.statuses.find((s) => s.tooltip.key === 'generic.upgradeable');
259
+ const upgradeableStatus = result.statuses.find((s) => s.tooltip?.key === 'generic.upgradeable');
259
260
 
260
261
  expect(upgradeableStatus).toBeDefined();
261
262
  expect(upgradeableStatus?.color).toBe('info');
@@ -270,9 +271,16 @@ describe('class Chart', () => {
270
271
 
271
272
  const result = chart.cardContent as CardContent;
272
273
 
273
- const keys = result.statuses.map((s) => s.tooltip.key);
274
+ const statuses = result.statuses.map((s) => {
275
+ if (s.tooltip?.key) {
276
+ return s.tooltip.key;
277
+ }
278
+ if (s.tooltip?.text?.startsWith('generic.installed')) {
279
+ return 'generic.installed';
280
+ }
281
+ });
274
282
 
275
- expect(keys).toStrictEqual(expect.arrayContaining([
283
+ expect(statuses).toStrictEqual(expect.arrayContaining([
276
284
  'generic.deprecated',
277
285
  'generic.upgradeable',
278
286
  'generic.installed'
@@ -290,11 +298,11 @@ describe('class Chart', () => {
290
298
  const chart = new Chart(chartWithZeroTime, {
291
299
  rootGetters: {
292
300
  'cluster/all': () => [],
293
- 'i18n/t': (key) => key
301
+ 'i18n/t': (key: string) => key
294
302
  },
295
303
  });
296
304
 
297
- const result = chart.cardContent;
305
+ const result = chart.cardContent as CardContent;
298
306
  const lastUpdatedItem = result.subHeaderItems[1];
299
307
 
300
308
  expect(lastUpdatedItem.label).toBe('generic.na');
@@ -0,0 +1,30 @@
1
+ import ComplianceProfile from '@shell/models/compliance.cattle.io.clusterscanprofile';
2
+
3
+ describe('class: ComplianceProfile', () => {
4
+ describe('getter: numberTestsSkipped', () => {
5
+ it('should return 0 if skipTests is not present in spec', () => {
6
+ const complianceProfile = new ComplianceProfile({ spec: {} });
7
+
8
+ expect(complianceProfile.numberTestsSkipped).toBe(0);
9
+ });
10
+
11
+ it('should return 0 if skipTests is null', () => {
12
+ const complianceProfile = new ComplianceProfile({ spec: { skipTests: null } });
13
+
14
+ expect(complianceProfile.numberTestsSkipped).toBe(0);
15
+ });
16
+
17
+ it('should return 0 if skipTests is an empty array', () => {
18
+ const complianceProfile = new ComplianceProfile({ spec: { skipTests: [] } });
19
+
20
+ expect(complianceProfile.numberTestsSkipped).toBe(0);
21
+ });
22
+
23
+ it('should return the correct number of skipped tests', () => {
24
+ const tests = ['test-1', 'test-2', 'test-3'];
25
+ const complianceProfile = new ComplianceProfile({ spec: { skipTests: tests } });
26
+
27
+ expect(complianceProfile.numberTestsSkipped).toBe(tests.length);
28
+ });
29
+ });
30
+ });
@@ -323,7 +323,7 @@ export default class CatalogApp extends SteveModel {
323
323
 
324
324
  get relatedResourcesToRemove() {
325
325
  return async() => {
326
- const crd = this.spec.chart.metadata.annotations[CATALOG_ANNOTATIONS.AUTO_INSTALL].replace('=match', '');
326
+ const crd = this.spec?.chart?.metadata?.annotations?.[CATALOG_ANNOTATIONS.AUTO_INSTALL]?.replace('=match', '');
327
327
 
328
328
  return await this.$dispatch('find', {
329
329
  type: CATALOG.APP,
package/models/chart.js CHANGED
@@ -195,8 +195,10 @@ export default class Chart extends SteveModel {
195
195
  }
196
196
 
197
197
  if (this.isInstalled) {
198
+ const installedVersion = this.matchingInstalledApps[0]?.spec?.chart?.metadata?.version;
199
+
198
200
  statuses.push({
199
- icon: 'icon-confirmation-alt', color: 'success', tooltip: { key: 'generic.installed' }
201
+ icon: 'icon-confirmation-alt', color: 'success', tooltip: { text: `${ this.t('generic.installed') } (${ installedVersion })` }
200
202
  });
201
203
  }
202
204
 
@@ -11,7 +11,7 @@ export default class ComplianceProfile extends SteveModel {
11
11
  get numberTestsSkipped() {
12
12
  const { skipTests = [] } = this.spec;
13
13
 
14
- return skipTests.length;
14
+ return skipTests?.length || 0;
15
15
  }
16
16
 
17
17
  get benchmarkVersionLink() {
package/models/event.js CHANGED
@@ -25,6 +25,13 @@ export default class K8sEvent extends SteveModel {
25
25
  return this._type;
26
26
  }
27
27
 
28
+ get firstSeen() {
29
+ const schema = this.$getters['schemaFor'](this.type);
30
+ const rowValueGetter = this.$rootGetters['type-map/rowValueGetter'];
31
+
32
+ return schema && rowValueGetter ? rowValueGetter(schema, 'First Seen')(this) : null;
33
+ }
34
+
28
35
  get lastSeen() {
29
36
  const schema = this.$getters['schemaFor'](this.type);
30
37
  const rowValueGetter = this.$rootGetters['type-map/rowValueGetter'];
@@ -14,6 +14,7 @@ export const configType = {
14
14
  googleoauth: 'oauth',
15
15
  local: '',
16
16
  github: 'oauth',
17
+ githubapp: 'oauth',
17
18
  keycloakoidc: 'oidc',
18
19
  genericoidc: 'oidc',
19
20
  cognito: 'oidc',
@@ -391,6 +391,15 @@ export default class ProvCluster extends SteveModel {
391
391
  const pCluster = this.$rootGetters['management/byId'](CAPI.RANCHER_CLUSTER, this.id);
392
392
  const name = this.status?.clusterName || pCluster?.status?.clusterName;
393
393
 
394
+ try {
395
+ if (name) {
396
+ // Just in case we're not generically watching all mgmt clusters and...
397
+ // thus won't receive new mgmt cluster over socket...
398
+ // fire and forget a request to fetch it (this won't make multiple requests if one is already running)
399
+ this.$dispatch('find', { type: MANAGEMENT.CLUSTER, id: name });
400
+ }
401
+ } catch {}
402
+
394
403
  return name && !!this.$rootGetters['management/byId'](MANAGEMENT.CLUSTER, name);
395
404
  }, this.$rootGetters['i18n/t']('cluster.managementTimeout'), timeout, interval);
396
405
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rancher/shell",
3
- "version": "3.0.6",
3
+ "version": "3.0.8-rc.1",
4
4
  "description": "Rancher Dashboard Shell",
5
5
  "repository": "https://github.com/rancherlabs/dashboard",
6
6
  "license": "Apache-2.0",
@@ -38,7 +38,7 @@
38
38
  "@babel/preset-typescript": "7.16.7",
39
39
  "@novnc/novnc": "1.2.0",
40
40
  "@popperjs/core": "2.11.8",
41
- "@rancher/icons": "2.0.38",
41
+ "@rancher/icons": "2.0.49",
42
42
  "@types/is-url": "1.2.30",
43
43
  "@types/node": "20.10.8",
44
44
  "@types/semver": "^7.5.8",
@@ -10,7 +10,7 @@ import CopyCode from '@shell/components/CopyCode';
10
10
  import { Banner } from '@components/Banner';
11
11
  import {
12
12
  LOCAL, LOGGED_OUT, TIMED_OUT, IS_SSO, _FLAGGED,
13
- IS_SLO
13
+ IS_SLO, IS_SESSION_IDLE
14
14
  } from '@shell/config/query-params';
15
15
  import { Checkbox } from '@components/Form/Checkbox';
16
16
  import Password from '@shell/components/form/Password';
@@ -47,6 +47,7 @@ export default {
47
47
 
48
48
  timedOut: this.$route.query[TIMED_OUT] === _FLAGGED,
49
49
  loggedOut: this.$route.query[LOGGED_OUT] === _FLAGGED,
50
+ isSessionIdle: this.$route.query[IS_SESSION_IDLE] === _FLAGGED,
50
51
  isSsoLogout: this.$route.query[IS_SSO] === _FLAGGED,
51
52
  isSlo: this.$route.query[IS_SLO] === _FLAGGED,
52
53
  err: this.$route.query.err,
@@ -67,7 +68,9 @@ export default {
67
68
  ...mapGetters({ t: 'i18n/t', hasMultipleLocales: 'i18n/hasMultipleLocales' }),
68
69
 
69
70
  loggedOutSuccessMsg() {
70
- if (this.isSlo) {
71
+ if (this.isSessionIdle) {
72
+ return this.t('login.loggedOutSessionIdle');
73
+ } else if (this.isSlo) {
71
74
  return this.t('login.loggedOutFromSlo');
72
75
  } else if (this.isSsoLogout) {
73
76
  return this.t('login.loggedOutFromSso');
@@ -10,7 +10,7 @@ import { AUTH_BROADCAST_CHANNEL_NAME } from '@shell/utils/auth';
10
10
 
11
11
  const samlProviders = ['ping', 'adfs', 'keycloak', 'okta', 'shibboleth'];
12
12
 
13
- const oauthProviders = ['github', 'googleoauth', 'azuread'];
13
+ const oauthProviders = ['github', 'githubapp', 'googleoauth', 'azuread'];
14
14
 
15
15
  function reply(err, code) {
16
16
  try {
@@ -19,7 +19,7 @@ defineProps<{
19
19
  v-for="(subHeaderItem, i) in items"
20
20
  :key="i"
21
21
  class="app-chart-card-sub-header-item"
22
- data-testid="app-chart-card-version"
22
+ data-testid="app-chart-card-sub-header-item"
23
23
  >
24
24
  <i
25
25
  v-clean-tooltip="t(subHeaderItem.iconTooltip.key)"
@@ -35,7 +35,8 @@ defineProps<{
35
35
  <style scoped lang="scss">
36
36
  .app-chart-card-sub-header {
37
37
  display: flex;
38
- gap: var(--gap-md);
38
+ flex-wrap: wrap;
39
+ gap: var(--gap) var(--gap-md);
39
40
  color: var(--link-text-secondary);
40
41
  margin-bottom: 8px;
41
42
 
@@ -259,11 +259,11 @@ export default {
259
259
  class="status"
260
260
  >
261
261
  <i
262
- v-clean-tooltip="t(status.tooltip.key)"
262
+ v-clean-tooltip="status.tooltip.key ? t(status.tooltip.key) : status.tooltip.text"
263
263
  :class="['icon', status.icon, status.color]"
264
264
  :style="{color: status.customColor}"
265
265
  role="img"
266
- :aria-label="t(status.tooltip.key)"
266
+ :aria-label="status.tooltip.key ? t(status.tooltip.key) : status.tooltip.text"
267
267
  />
268
268
  </div>
269
269
  </div>
@@ -3,9 +3,11 @@
3
3
  import { MESSAGE, NAME, OBJECT, REASON } from '@shell/config/table-headers';
4
4
  import { EVENT } from '@shell/config/types';
5
5
  import PaginatedResourceTable from '@shell/components/PaginatedResourceTable';
6
- import { STEVE_EVENT_OBJECT, STEVE_NAME_COL } from '@shell/config/pagination-table-headers';
6
+ import { STEVE_EVENT_FIRST_SEEN, STEVE_EVENT_LAST_SEEN, STEVE_EVENT_OBJECT, STEVE_NAME_COL } from '@shell/config/pagination-table-headers';
7
7
  import { headerFromSchemaColString } from '@shell/store/type-map.utils';
8
8
  import { NAME as EXPLORER } from '@shell/config/product/explorer';
9
+ import { ROWS_PER_PAGE } from '@shell/store/prefs';
10
+ import { RcDropdown, RcDropdownTrigger, RcDropdownItem } from '@components/RcDropdown';
9
11
 
10
12
  const reason = {
11
13
  ...REASON,
@@ -29,15 +31,42 @@ const eventHeaders = [
29
31
  },
30
32
  ];
31
33
 
34
+ // Local storage key for the events table row count preference
35
+ const ROWS_COUNT_PREF = 'events-row-count-pref';
36
+ // Value to use when we want to use the user's preference from the table
37
+ const ROWS_PREF_USE_TABLE = -1;
38
+ // Default number of rows to show in the events table when not set in the preference
39
+ const ROWS_COUNT_DEFAULT = 10;
40
+
32
41
  export default {
33
- components: { PaginatedResourceTable },
42
+ components: {
43
+ PaginatedResourceTable,
44
+ RcDropdown,
45
+ RcDropdownItem,
46
+ RcDropdownTrigger
47
+ },
34
48
 
35
49
  data() {
50
+ const tableRowOptions = this.$store.getters['prefs/options'](ROWS_PER_PAGE);
51
+
52
+ let rowsPerPage = ROWS_COUNT_DEFAULT;
53
+
54
+ // Read the current value from localStorage if it exists
55
+ if (window.localStorage.getItem(ROWS_COUNT_PREF)) {
56
+ try {
57
+ rowsPerPage = parseInt(window.localStorage.getItem(ROWS_COUNT_PREF));
58
+ } catch (e) {
59
+ console.error('Error parsing rows count from localStorage:', e); // eslint-disable-line no-console
60
+ }
61
+ }
62
+
36
63
  return {
64
+ rowsPerPage,
37
65
  schema: null,
38
66
  events: [],
39
67
  eventHeaders,
40
68
  paginationHeaders: null,
69
+ options: tableRowOptions || [],
41
70
  allEventsLink: {
42
71
  name: 'c-cluster-product-resource',
43
72
  params: {
@@ -59,11 +88,8 @@ export default {
59
88
  ...STEVE_NAME_COL,
60
89
  defaultSort: false,
61
90
  },
62
- headerFromSchemaColString('First Seen', schema, this.$store.getters, true),
63
- {
64
- ...headerFromSchemaColString('Last Seen', schema, this.$store.getters, true),
65
- defaultSort: true,
66
- },
91
+ STEVE_EVENT_FIRST_SEEN,
92
+ STEVE_EVENT_LAST_SEEN,
67
93
  headerFromSchemaColString('Count', schema, this.$store.getters, true),
68
94
  ] : [];
69
95
 
@@ -75,7 +101,34 @@ export default {
75
101
  this.dismissRouteHandler = this.$router.beforeEach(this.onRouteChange);
76
102
  },
77
103
 
104
+ computed: {
105
+ userPrefRowsPerPage() {
106
+ return parseInt(this.$store.getters['prefs/get'](ROWS_PER_PAGE), 10) || undefined;
107
+ },
108
+ rowOptions() {
109
+ const rowOptions = [];
110
+
111
+ this.options.forEach((item) => rowOptions.push({
112
+ label: this.t('glance.showXEvents', { count: item }),
113
+ value: item
114
+ }));
115
+
116
+ if (this.userPrefRowsPerPage) {
117
+ rowOptions.push({
118
+ label: this.t('glance.useUserPreference', { count: this.userPrefRowsPerPage }),
119
+ value: ROWS_PREF_USE_TABLE,
120
+ });
121
+ }
122
+
123
+ return rowOptions;
124
+ }
125
+ },
126
+
78
127
  methods: {
128
+ updateRowsCount(val) {
129
+ this.rowsPerPage = val;
130
+ window.localStorage.setItem(ROWS_COUNT_PREF, val);
131
+ },
79
132
  async onRouteChange(to, from, next) {
80
133
  if (this.$route.name !== to.name) {
81
134
  await this.$store.dispatch('cluster/forgetType', EVENT);
@@ -103,7 +156,7 @@ export default {
103
156
  :table-actions="false"
104
157
  :row-actions="false"
105
158
  :groupable="false"
106
- :rows-per-page="10"
159
+ :rows-per-page="rowsPerPage"
107
160
  >
108
161
  <template v-slot:header-right>
109
162
  <router-link
@@ -113,14 +166,44 @@ export default {
113
166
  >
114
167
  <span>{{ t('glance.eventsTable') }}</span>
115
168
  </router-link>
169
+ <rc-dropdown>
170
+ <rc-dropdown-trigger
171
+ data-testid="events-list-row-count-menu-toggle"
172
+ :aria-label="t('glance.changeEventsListRowCount')"
173
+ ghost
174
+ small
175
+ >
176
+ <i class="icon icon-gear" />
177
+ </rc-dropdown-trigger>
178
+ <template #dropdownCollection>
179
+ <rc-dropdown-item
180
+ v-for="(item, i) in rowOptions"
181
+ :key="i"
182
+ :value="item.value"
183
+ @click.stop="updateRowsCount(item.value)"
184
+ >
185
+ <span :class="{ 'selected-pagesize-option': rowsPerPage === item.value }">
186
+ {{ item.label }}
187
+ </span>
188
+ </rc-dropdown-item>
189
+ </template>
190
+ </rc-dropdown>
116
191
  </template>
117
192
  </PaginatedResourceTable>
118
193
  </template>
119
194
 
120
195
  <style lang="scss" scoped>
196
+ .icon.icon-gear {
197
+ color: var(--primary);
198
+ padding: 0 8px;
199
+ }
121
200
  .events-link {
122
201
  align-self: center;
123
- padding-right: 20px;
202
+ margin-right: 10px;
124
203
  white-space: nowrap;
125
204
  }
205
+
206
+ .selected-pagesize-option {
207
+ font-weight: bold;
208
+ }
126
209
  </style>
@@ -96,7 +96,7 @@ export default {
96
96
  title: { text: chart.chartNameDisplay },
97
97
  statuses: chart.cardContent.statuses
98
98
  },
99
- subHeaderItems: chart.cardContent.subHeaderItems.slice(0, 1),
99
+ subHeaderItems: chart.cardContent.subHeaderItems,
100
100
  footerItems: chart.deploysOnWindows ? [{
101
101
  icon: 'icon-tag-alt',
102
102
  iconTooltip: { key: 'generic.tags' },
@@ -151,7 +151,7 @@ export default {
151
151
  action: 'edit',
152
152
  });
153
153
 
154
- const currentVersion = installedApp.spec.chart.metadata.version;
154
+ const currentVersion = installedApp.spec?.chart?.metadata?.version;
155
155
  const versions = rawChart.versions;
156
156
  const currentIndex = versions.findIndex((v) => v.version === currentVersion);
157
157
 
@@ -190,7 +190,7 @@ export default {
190
190
  },
191
191
 
192
192
  downgrade(app, chart) {
193
- const currentVersion = app.spec.chart.metadata.version;
193
+ const currentVersion = app.spec?.chart?.metadata?.version;
194
194
  const versions = chart.versions;
195
195
  const currentIndex = versions.findIndex((v) => v.version === currentVersion);
196
196
 
@@ -76,6 +76,13 @@ export default {
76
76
  resource: MANAGEMENT.FEATURE
77
77
  }
78
78
  }).href,
79
+ settingsPageUrl: this.$router.resolve({
80
+ name: 'c-cluster-product-resource',
81
+ params: {
82
+ product: SETTING_PRODUCT,
83
+ resource: MANAGEMENT.SETTING
84
+ }
85
+ }).href,
79
86
  ssPApplicableTypesOpen: false,
80
87
  };
81
88
  },
@@ -105,7 +112,7 @@ export default {
105
112
  if (settings.resources.enableAll) {
106
113
  resources.push(this.t('performance.serverPagination.resources.all'));
107
114
  } else {
108
- settings.resources.enableSome.enabled.forEach((resource) => {
115
+ settings.resources.enableSome.enabled?.forEach((resource) => {
109
116
  resources.push(!!resource.length ? resource : `${ resource.resource } (${ resource.context })`);
110
117
  });
111
118
  if (settings.resources.enableSome.generic) {
@@ -206,31 +213,11 @@ export default {
206
213
  <!-- Inactivity -->
207
214
  <div class="mt-20">
208
215
  <h2>{{ t('performance.inactivity.title') }}</h2>
209
- <p>{{ t('performance.inactivity.description') }}</p>
210
- <Checkbox
211
- v-model:value="value.inactivity.enabled"
212
- :mode="mode"
213
- :label="t('performance.inactivity.checkboxLabel')"
214
- class="mt-10 mb-20"
215
- :primary="true"
216
- />
217
- <div class="ml-20">
218
- <LabeledInput
219
- v-model:value="value.inactivity.threshold"
220
- data-testid="inactivity-threshold"
221
- :mode="mode"
222
- :label="t('performance.inactivity.inputLabel')"
223
- :disabled="!value.inactivity.enabled"
224
- class="input mb-10"
225
- type="number"
226
- min="0"
227
- :rules="[validateInactivityThreshold]"
228
- />
229
- <span
230
- v-clean-html="t('performance.inactivity.information', {}, true)"
231
- :class="{ 'text-muted': !value.incrementalLoading.enabled }"
232
- />
233
- </div>
216
+ <Banner
217
+ color="warning"
218
+ >
219
+ <span v-clean-html="t(`performance.deprecatedInactivitySetting`, { settingsPageUrl }, true)" />
220
+ </Banner>
234
221
  </div>
235
222
  <!-- Websocket Notifications -->
236
223
  <div class="mt-40">