@rancher/shell 0.3.15 → 0.3.17

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 (155) hide show
  1. package/assets/images/wechat-qr-code.jpg +0 -0
  2. package/assets/translations/en-us.yaml +70 -15
  3. package/assets/translations/zh-hans.yaml +155 -33
  4. package/chart/__tests__/S3.test.ts +50 -0
  5. package/chart/rancher-backup/S3.vue +21 -0
  6. package/chart/rancher-backup/index.vue +4 -0
  7. package/cloud-credential/generic.vue +1 -1
  8. package/components/BannerGraphic.vue +1 -0
  9. package/components/CommunityLinks.vue +1 -0
  10. package/components/CruResource.vue +1 -1
  11. package/components/EmberPage.vue +1 -0
  12. package/components/FileDiff.vue +92 -85
  13. package/components/GrafanaDashboard.vue +7 -1
  14. package/components/ResourceDetail/index.vue +4 -12
  15. package/components/ResourceList/index.vue +1 -1
  16. package/components/ResourceTable.vue +50 -2
  17. package/components/SimpleBox.vue +1 -0
  18. package/components/SortableTable/index.vue +5 -1
  19. package/components/YamlEditor.vue +1 -0
  20. package/components/auth/RoleDetailEdit.vue +1 -0
  21. package/components/form/GitPicker.vue +1 -1
  22. package/components/form/NameNsDescription.vue +28 -12
  23. package/components/form/NodeAffinity.vue +2 -2
  24. package/components/form/PodAffinity.vue +8 -3
  25. package/components/form/ResourceTabs/index.vue +8 -2
  26. package/components/form/Select.vue +16 -0
  27. package/components/form/__tests__/NodeAffinity.test.ts +38 -0
  28. package/components/form/__tests__/PodAffinity.test.ts +46 -0
  29. package/components/formatter/ClusterLink.vue +8 -4
  30. package/components/formatter/ImageName.vue +23 -0
  31. package/components/formatter/PodImages.vue +7 -1
  32. package/components/formatter/__tests__/ClusterLink.test.ts +101 -0
  33. package/components/nav/Header.vue +2 -2
  34. package/config/__test__/home-links.test.ts +62 -0
  35. package/config/home-links.js +15 -3
  36. package/config/labels-annotations.js +5 -1
  37. package/config/product/auth.js +1 -1
  38. package/config/router.js +0 -9
  39. package/config/settings.ts +4 -0
  40. package/config/table-headers.js +6 -5
  41. package/config/uiplugins.js +50 -5
  42. package/core/plugin-helpers.js +20 -12
  43. package/core/plugin.ts +9 -0
  44. package/core/plugins.js +1 -1
  45. package/core/types-provisioning.ts +253 -0
  46. package/core/types.ts +17 -3
  47. package/detail/autoscaling.horizontalpodautoscaler/index.vue +50 -1
  48. package/detail/catalog.cattle.io.clusterrepo.vue +8 -1
  49. package/detail/node.vue +6 -6
  50. package/detail/pod.vue +2 -6
  51. package/detail/provisioning.cattle.io.cluster.vue +46 -7
  52. package/detail/workload/index.vue +9 -9
  53. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +62 -0
  54. package/edit/__tests__/monitoring.coreos.com.prometheusrule.test.ts +56 -0
  55. package/edit/auth/github.vue +1 -0
  56. package/edit/autoscaling.horizontalpodautoscaler/hpa-scaling-rule.vue +130 -0
  57. package/edit/autoscaling.horizontalpodautoscaler/index.vue +79 -0
  58. package/edit/fleet.cattle.io.gitrepo.vue +18 -1
  59. package/edit/monitoring.coreos.com.prometheusrule/index.vue +8 -3
  60. package/edit/namespace.vue +9 -1
  61. package/edit/networking.k8s.io.ingress/RulePath.vue +0 -2
  62. package/edit/provisioning.cattle.io.cluster/AgentConfiguration.vue +1 -30
  63. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +79 -1
  64. package/edit/provisioning.cattle.io.cluster/index.vue +52 -0
  65. package/edit/provisioning.cattle.io.cluster/rke2.vue +330 -150
  66. package/edit/ui.cattle.io.navlink.vue +2 -1
  67. package/initialize/App.js +3 -13
  68. package/initialize/layouts.ts +26 -0
  69. package/list/provisioning.cattle.io.cluster.vue +8 -1
  70. package/middleware/authenticated.js +93 -5
  71. package/mixins/brand.js +39 -3
  72. package/mixins/child-hook.js +2 -2
  73. package/mixins/create-edit-view/impl.js +2 -2
  74. package/models/fleet.cattle.io.gitrepo.js +1 -0
  75. package/models/provisioning.cattle.io.cluster.js +9 -1
  76. package/package.json +3 -3
  77. package/pages/about.vue +8 -2
  78. package/pages/auth/login.vue +10 -0
  79. package/pages/auth/logout.vue +11 -3
  80. package/pages/auth/setup.vue +4 -0
  81. package/pages/c/_cluster/apps/charts/index.vue +5 -2
  82. package/pages/c/_cluster/apps/charts/install.vue +5 -0
  83. package/pages/c/_cluster/auth/roles/index.vue +1 -1
  84. package/pages/c/_cluster/explorer/index.vue +1 -10
  85. package/pages/c/_cluster/uiplugins/AddExtensionRepos.vue +177 -0
  86. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +19 -3
  87. package/pages/c/_cluster/uiplugins/RemoveUIPlugins.vue +90 -21
  88. package/pages/c/_cluster/uiplugins/SetupUIPlugins.vue +107 -37
  89. package/pages/c/_cluster/uiplugins/index.vue +155 -44
  90. package/pages/docs/_doc.vue +9 -3
  91. package/pages/home.vue +10 -5
  92. package/pages/support/index.vue +10 -4
  93. package/pkg/auto-import.js +1 -1
  94. package/plugins/clean-tooltip-directive.js +1 -1
  95. package/plugins/dashboard-store/resource-class.js +35 -2
  96. package/plugins/plugin.js +9 -1
  97. package/plugins/steve/actions.js +22 -0
  98. package/rancher-components/BadgeState/BadgeState.vue +5 -1
  99. package/rancher-components/Banner/Banner.test.ts +51 -1
  100. package/rancher-components/Banner/Banner.vue +134 -53
  101. package/rancher-components/Card/Card.vue +24 -7
  102. package/rancher-components/Form/Checkbox/Checkbox.test.ts +20 -29
  103. package/rancher-components/Form/Checkbox/Checkbox.vue +45 -20
  104. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +2 -8
  105. package/rancher-components/Form/LabeledInput/LabeledInput.vue +22 -10
  106. package/rancher-components/Form/Radio/RadioButton.vue +30 -13
  107. package/rancher-components/Form/Radio/RadioGroup.vue +26 -7
  108. package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +7 -6
  109. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.test.ts +25 -38
  110. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +23 -11
  111. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +19 -5
  112. package/rancher-components/StringList/StringList.test.ts +453 -49
  113. package/rancher-components/StringList/StringList.vue +92 -58
  114. package/scripts/extension/publish +2 -2
  115. package/scripts/typegen.sh +1 -0
  116. package/server/server-middleware.js +4 -12
  117. package/store/index.js +13 -0
  118. package/store/prefs.js +0 -3
  119. package/store/type-map.js +17 -29
  120. package/types/shell/index.d.ts +243 -90
  121. package/utils/kube.js +9 -0
  122. package/utils/object.js +27 -0
  123. package/utils/settings.ts +2 -2
  124. package/vue.config.js +3 -2
  125. package/pages/safeMode.vue +0 -17
  126. package/rancher-components/components/BadgeState/BadgeState.spec.ts +0 -12
  127. package/rancher-components/components/BadgeState/BadgeState.vue +0 -111
  128. package/rancher-components/components/BadgeState/index.ts +0 -1
  129. package/rancher-components/components/Banner/Banner.test.ts +0 -63
  130. package/rancher-components/components/Banner/Banner.vue +0 -244
  131. package/rancher-components/components/Banner/index.ts +0 -1
  132. package/rancher-components/components/Card/Card.vue +0 -167
  133. package/rancher-components/components/Card/index.ts +0 -1
  134. package/rancher-components/components/Form/Checkbox/Checkbox.test.ts +0 -68
  135. package/rancher-components/components/Form/Checkbox/Checkbox.vue +0 -420
  136. package/rancher-components/components/Form/Checkbox/index.ts +0 -1
  137. package/rancher-components/components/Form/LabeledInput/LabeledInput.test.ts +0 -23
  138. package/rancher-components/components/Form/LabeledInput/LabeledInput.vue +0 -355
  139. package/rancher-components/components/Form/LabeledInput/index.ts +0 -1
  140. package/rancher-components/components/Form/Radio/RadioButton.vue +0 -287
  141. package/rancher-components/components/Form/Radio/RadioGroup.vue +0 -254
  142. package/rancher-components/components/Form/Radio/index.ts +0 -2
  143. package/rancher-components/components/Form/TextArea/TextAreaAutoGrow.vue +0 -170
  144. package/rancher-components/components/Form/TextArea/index.ts +0 -1
  145. package/rancher-components/components/Form/ToggleSwitch/ToggleSwitch.test.ts +0 -94
  146. package/rancher-components/components/Form/ToggleSwitch/ToggleSwitch.vue +0 -149
  147. package/rancher-components/components/Form/ToggleSwitch/index.ts +0 -1
  148. package/rancher-components/components/Form/index.ts +0 -5
  149. package/rancher-components/components/LabeledTooltip/LabeledTooltip.vue +0 -151
  150. package/rancher-components/components/LabeledTooltip/index.ts +0 -1
  151. package/rancher-components/components/StringList/StringList.test.ts +0 -484
  152. package/rancher-components/components/StringList/StringList.vue +0 -611
  153. package/rancher-components/components/StringList/index.ts +0 -1
  154. /package/rancher-components/{components/Card → Card}/Card.test.ts +0 -0
  155. /package/rancher-components/{components/Form → Form}/Radio/RadioButton.test.ts +0 -0
@@ -4,7 +4,9 @@ import { mapGetters } from 'vuex';
4
4
  import { mapPref, PLUGIN_DEVELOPER } from '@shell/store/prefs';
5
5
  import { sortBy } from '@shell/utils/sort';
6
6
  import { allHash } from '@shell/utils/promise';
7
- import { CATALOG, UI_PLUGIN, SERVICE } from '@shell/config/types';
7
+ import { CATALOG, UI_PLUGIN, SERVICE, MANAGEMENT } from '@shell/config/types';
8
+ import { SETTING } from '@shell/config/settings';
9
+ import { fetchOrCreateSetting } from '@shell/utils/settings';
8
10
  import { getVersionData } from '@shell/config/version';
9
11
  import { CATALOG as CATALOG_ANNOTATIONS } from '@shell/config/labels-annotations';
10
12
  import { NAME as APP_PRODUCT } from '@shell/config/product/apps';
@@ -21,7 +23,9 @@ import DeveloperInstallDialog from './DeveloperInstallDialog.vue';
21
23
  import PluginInfoPanel from './PluginInfoPanel.vue';
22
24
  import SetupUIPlugins from './SetupUIPlugins';
23
25
  import RemoveUIPlugins from './RemoveUIPlugins';
26
+ import AddExtensionRepos from './AddExtensionRepos';
24
27
  import CatalogList from './CatalogList/index.vue';
28
+ import Banner from '@components/Banner/Banner.vue';
25
29
  import {
26
30
  isUIPlugin,
27
31
  uiPluginAnnotation,
@@ -31,11 +35,20 @@ import {
31
35
  isChartVersionHigher,
32
36
  UI_PLUGIN_NAMESPACE,
33
37
  UI_PLUGIN_CHART_ANNOTATIONS,
34
- UI_PLUGIN_LABELS
38
+ UI_PLUGIN_LABELS,
39
+ UI_PLUGINS_REPO_URL,
40
+ UI_PLUGINS_PARTNERS_REPO_URL
35
41
  } from '@shell/config/uiplugins';
36
42
 
37
43
  const MAX_DESCRIPTION_LENGTH = 200;
38
44
 
45
+ const TABS_VALUES = {
46
+ INSTALLED: 'installed',
47
+ UPDATES: 'updates',
48
+ AVAILABLE: 'available',
49
+ ALL: 'all'
50
+ };
51
+
39
52
  export default {
40
53
  components: {
41
54
  ActionMenu,
@@ -43,6 +56,7 @@ export default {
43
56
  DeveloperInstallDialog,
44
57
  IconMessage,
45
58
  CatalogList,
59
+ Banner,
46
60
  CatalogLoadDialog,
47
61
  InstallDialog,
48
62
  LazyImage,
@@ -52,25 +66,29 @@ export default {
52
66
  UninstallDialog,
53
67
  SetupUIPlugins,
54
68
  RemoveUIPlugins,
69
+ AddExtensionRepos,
55
70
  },
56
71
 
57
72
  data() {
58
73
  return {
59
- view: '',
60
- charts: [],
61
- installing: {},
62
- errors: {},
63
- plugins: [], // The installed plugins
64
- helmOps: [], // Helm operations
65
- loading: true,
66
- menuTargetElement: null,
67
- menuTargetEvent: null,
68
- menuOpen: false,
69
- hasService: false,
70
- defaultIcon: require('~shell/assets/images/generic-plugin.svg'),
71
- reloadRequired: false,
72
- rancherVersion: getVersionData()?.Version,
73
- showCatalogList: false
74
+ TABS_VALUES,
75
+ kubeVersion: null,
76
+ view: '',
77
+ charts: [],
78
+ installing: {},
79
+ errors: {},
80
+ plugins: [], // The installed plugins
81
+ helmOps: [], // Helm operations
82
+ addExtensionReposBannerSetting: undefined,
83
+ loading: true,
84
+ menuTargetElement: null,
85
+ menuTargetEvent: null,
86
+ menuOpen: false,
87
+ hasService: false,
88
+ defaultIcon: require('~shell/assets/images/generic-plugin.svg'),
89
+ reloadRequired: false,
90
+ rancherVersion: getVersionData()?.Version,
91
+ showCatalogList: false
74
92
  };
75
93
  },
76
94
 
@@ -79,7 +97,7 @@ export default {
79
97
  async fetch() {
80
98
  const hash = {};
81
99
 
82
- const isSetup = await this.updateInstallStatus();
100
+ const isSetup = await this.updateInstallStatus(true);
83
101
 
84
102
  if (isSetup) {
85
103
  if (this.$store.getters['management/schemaFor'](UI_PLUGIN)) {
@@ -89,6 +107,10 @@ export default {
89
107
 
90
108
  hash.load = await this.$store.dispatch('catalog/load', { reset: true });
91
109
 
110
+ if (this.$store.getters['management/schemaFor'](MANAGEMENT.CLUSTER)) {
111
+ hash.localCluster = await this.$store.dispatch('management/find', { type: MANAGEMENT.CLUSTER, id: 'local' });
112
+ }
113
+
92
114
  if (this.$store.getters['management/schemaFor'](CATALOG.OPERATION)) {
93
115
  hash.helmOps = await this.$store.dispatch('management/findAll', { type: CATALOG.OPERATION });
94
116
  }
@@ -100,7 +122,11 @@ export default {
100
122
  const res = await allHash(hash);
101
123
 
102
124
  this.plugins = res.plugins || [];
125
+ this.repos = res.repos || [];
103
126
  this.helmOps = res.helmOps || [];
127
+ this.kubeVersion = res.localCluster?.kubernetesVersionBase || [];
128
+
129
+ this.addExtensionReposBannerSetting = await fetchOrCreateSetting(this.$store, SETTING.ADD_EXTENSION_REPOS_BANNER_DISPLAY, 'true', true) || {};
104
130
 
105
131
  const c = this.$store.getters['catalog/rawCharts'];
106
132
 
@@ -108,7 +134,7 @@ export default {
108
134
 
109
135
  // If there are no plugins installed, default to the catalog view
110
136
  if (this.plugins.length === 0) {
111
- this.$refs.tabs?.select('available');
137
+ this.$refs.tabs?.select(TABS_VALUES.AVAILABLE);
112
138
  }
113
139
 
114
140
  this.loading = false;
@@ -121,6 +147,10 @@ export default {
121
147
  ...mapGetters({ uiErrors: 'uiplugins/errors' }),
122
148
  ...mapGetters({ theme: 'prefs/theme' }),
123
149
 
150
+ showAddReposBanner() {
151
+ return this.addExtensionReposBannerSetting?.value === 'true' && (!this.repos.find((r) => r.urlDisplay === UI_PLUGINS_REPO_URL) || !this.repos.find((r) => r.urlDisplay === UI_PLUGINS_PARTNERS_REPO_URL));
152
+ },
153
+
124
154
  applyDarkModeBg() {
125
155
  if (this.theme === 'dark') {
126
156
  return { 'dark-mode': true };
@@ -139,6 +169,13 @@ export default {
139
169
  enabled: true
140
170
  });
141
171
 
172
+ // Add link to add extensions repositories (Official, Partners, etc)
173
+ menuActions.push({
174
+ action: 'addRancherRepos',
175
+ label: this.t('plugins.addRancherRepos'),
176
+ enabled: true
177
+ });
178
+
142
179
  // Only show Manage Extension Catalogs when on main charts view
143
180
  if (!this.showCatalogList) {
144
181
  menuActions.push({
@@ -174,11 +211,11 @@ export default {
174
211
  const all = this.available;
175
212
 
176
213
  switch (this.view) {
177
- case 'installed':
214
+ case TABS_VALUES.INSTALLED:
178
215
  return all.filter((p) => !!p.installed || !!p.installing);
179
- case 'updates':
216
+ case TABS_VALUES.UPDATES:
180
217
  return this.updates;
181
- case 'available':
218
+ case TABS_VALUES.AVAILABLE:
182
219
  return all.filter((p) => !p.installed);
183
220
  default:
184
221
  return all;
@@ -223,24 +260,32 @@ export default {
223
260
  item.chart = chart;
224
261
 
225
262
  // Filter the versions available to install (plugins-api version and current dashboard version)
226
- item.installableVersions = item.versions.filter((version) => isSupportedChartVersion(version) && isChartVersionAvailableForInstall(version, this.rancherVersion));
263
+ item.installableVersions = item.versions.filter((version) => isSupportedChartVersion({ version, kubeVersion: this.kubeVersion }) && isChartVersionAvailableForInstall({
264
+ version, rancherVersion: this.rancherVersion, kubeVersion: this.kubeVersion
265
+ }));
227
266
 
228
267
  // add prop to version object if version is compatible with the current dashboard version
229
- item.versions = item.versions.map((version) => isChartVersionAvailableForInstall(version, this.rancherVersion, true));
268
+ item.versions = item.versions.map((version) => isChartVersionAvailableForInstall({
269
+ version, rancherVersion: this.rancherVersion, kubeVersion: this.kubeVersion
270
+ }, true));
230
271
 
231
272
  const latestCompatible = item.installableVersions?.[0];
232
- const latestNotCompatible = item.versions.find((version) => !version.isCompatibleWithUi);
273
+ const latestNotCompatible = item.versions.find((version) => !version.isCompatibleWithUi || !version.isCompatibleWithKubeVersion);
233
274
 
234
275
  if (latestCompatible) {
235
276
  item.displayVersion = latestCompatible.version;
236
277
  item.icon = latestCompatible.icon;
237
278
  } else {
238
279
  item.displayVersion = item.versions?.[0]?.version;
239
- item.icon = chart.icon || latestCompatible.annotations['catalog.cattle.io/ui-icon'];
280
+ item.icon = chart.icon || latestCompatible?.annotations?.['catalog.cattle.io/ui-icon'];
240
281
  }
241
282
 
242
283
  if (latestNotCompatible && item.installableVersions.length && isChartVersionHigher(latestNotCompatible.version, item.installableVersions?.[0].version)) {
243
- item.incompatibleDisclaimer = this.t('plugins.incompatibleDisclaimer', { version: latestNotCompatible.version, rancherVersion: latestNotCompatible.requiredUiVersion }, true);
284
+ if (!item.isCompatibleWithUi) {
285
+ item.incompatibleRancherVersion = this.t('plugins.incompatibleRancherVersion', { version: latestNotCompatible.version, rancherVersion: latestNotCompatible.requiredUiVersion }, true);
286
+ } else if (!item.isCompatibleWithKubeVersion) {
287
+ item.incompatibleKubeVersion = this.t('plugins.incompatibleKubeVersion', { version: latestNotCompatible.version, kubeVersion: latestNotCompatible.requiredKubeVersion }, true);
288
+ }
244
289
  }
245
290
 
246
291
  if (this.installing[item.name]) {
@@ -341,11 +386,25 @@ export default {
341
386
  }
342
387
  });
343
388
 
344
- // Clamp the lengths of the descriptions
345
389
  all.forEach((plugin) => {
390
+ // Clamp the lengths of the descriptions
346
391
  if (plugin.description && plugin.description.length > MAX_DESCRIPTION_LENGTH) {
347
392
  plugin.description = `${ plugin.description.substr(0, MAX_DESCRIPTION_LENGTH) } ...`;
348
393
  }
394
+
395
+ // check if kube version compatibility is met for installed extension
396
+ if (plugin.uiplugin) {
397
+ const versionInstalled = plugin.uiplugin.spec?.plugin?.version;
398
+ const versionInstalledData = plugin.versions.find((v) => v.version === versionInstalled);
399
+
400
+ if (versionInstalledData) {
401
+ const kubeVersionToCheck = versionInstalledData.annotations?.[UI_PLUGIN_CHART_ANNOTATIONS.KUBE_VERSION];
402
+
403
+ if (this.kubeVersion && !isSupportedChartVersion({ version: versionInstalledData, kubeVersion: this.kubeVersion })) {
404
+ plugin.installedError = this.t('plugins.currentInstalledVersionBlockedByKubeVersion', { kubeVersion: this.kubeVersion, kubeVersionToCheck }, true);
405
+ }
406
+ }
407
+ }
349
408
  });
350
409
 
351
410
  // Sort by name
@@ -412,7 +471,7 @@ export default {
412
471
  changes++;
413
472
  }
414
473
  if (isCustomImage) {
415
- this.refreshCharts();
474
+ this.refreshCharts(true);
416
475
  }
417
476
 
418
477
  this.updatePluginInstallStatus(plugin.name, false);
@@ -434,14 +493,16 @@ export default {
434
493
  },
435
494
 
436
495
  methods: {
437
- async refreshCharts() {
438
- await this.$store.dispatch('catalog/load', { reset: true });
496
+ async refreshCharts(forceChartsUpdate = false) {
497
+ // we might need to force the request, so that we know at all times if what's the status of the offical and partners repos (installed or not)
498
+ // tied to the SetupUIPlugins, AddExtensionRepos and RemoveUIPlugins checkboxes
499
+ await this.$store.dispatch('catalog/load', { reset: true, force: forceChartsUpdate });
439
500
  const c = this.$store.getters['catalog/rawCharts'];
440
501
 
441
502
  this.charts = Object.values(c);
442
503
  },
443
504
 
444
- async updateInstallStatus() {
505
+ async updateInstallStatus(forceChartsUpdate = false) {
445
506
  let hasService;
446
507
 
447
508
  try {
@@ -456,8 +517,8 @@ export default {
456
517
  hasService = false;
457
518
  }
458
519
 
459
- if (hasService) {
460
- this.refreshCharts();
520
+ if (hasService || forceChartsUpdate) {
521
+ this.refreshCharts(forceChartsUpdate);
461
522
  }
462
523
 
463
524
  Vue.set(this, 'hasService', hasService);
@@ -470,6 +531,7 @@ export default {
470
531
  },
471
532
 
472
533
  removePluginSupport() {
534
+ this.refreshCharts(true);
473
535
  this.$refs.removeUIPlugins.showDialog();
474
536
  },
475
537
 
@@ -478,6 +540,11 @@ export default {
478
540
  this.$refs.developerInstallDialog.showDialog();
479
541
  },
480
542
 
543
+ showAddExtensionReposDialog() {
544
+ this.refreshCharts(true);
545
+ this.$refs.addExtensionReposDialog.showDialog();
546
+ },
547
+
481
548
  showCatalogLoadDialog() {
482
549
  this.$refs.catalogLoadDialog.showDialog();
483
550
  },
@@ -514,7 +581,7 @@ export default {
514
581
  didInstall(plugin) {
515
582
  if (plugin) {
516
583
  // Change the view to installed if we started installing a plugin
517
- this.$refs.tabs?.select('installed');
584
+ this.$refs.tabs?.select(TABS_VALUES.INSTALLED);
518
585
 
519
586
  // Clear the load error, if there was one previously
520
587
  this.$store.dispatch('uiplugins/setError', { name: plugin.name, error: false });
@@ -526,7 +593,6 @@ export default {
526
593
  },
527
594
 
528
595
  updatePluginInstallStatus(name, status) {
529
- // console.log(`UPDATING PLUGIN STATUS: ${ name } ${ status }`);
530
596
  Vue.set(this.installing, name, status);
531
597
  },
532
598
 
@@ -559,6 +625,13 @@ export default {
559
625
 
560
626
  manageExtensionView() {
561
627
  this.showCatalogList = !this.showCatalogList;
628
+ },
629
+
630
+ updateAddReposSetting() {
631
+ if (this.addExtensionReposBannerSetting?.value === 'true') {
632
+ this.addExtensionReposBannerSetting.value = 'false';
633
+ this.addExtensionReposBannerSetting.save();
634
+ }
562
635
  }
563
636
  }
564
637
  };
@@ -634,6 +707,7 @@ export default {
634
707
  @devLoad="showDeveloperLoadDialog"
635
708
  @removePluginSupport="removePluginSupport"
636
709
  @manageRepos="manageRepos"
710
+ @addRancherRepos="showAddExtensionReposDialog"
637
711
  @manageExtensionView="manageExtensionView"
638
712
  />
639
713
  </div>
@@ -655,7 +729,8 @@ export default {
655
729
  <SetupUIPlugins
656
730
  v-else
657
731
  class="setup-message"
658
- @done="updateInstallStatus"
732
+ @done="updateInstallStatus(true)"
733
+ @refreshCharts="refreshCharts(true)"
659
734
  />
660
735
  </div>
661
736
  <div v-else>
@@ -667,6 +742,23 @@ export default {
667
742
  />
668
743
  </template>
669
744
  <template v-else>
745
+ <Banner
746
+ v-if="showAddReposBanner"
747
+ color="warning"
748
+ class="mb-20"
749
+ :closable="true"
750
+ data-testid="extensions-new-repos-banner"
751
+ @close="updateAddReposSetting"
752
+ >
753
+ <span>{{ t('plugins.addRepos.banner', {}, true) }}</span>
754
+ <button
755
+ class="ml-10 btn btn-sm role-primary"
756
+ @click="showAddExtensionReposDialog()"
757
+ >
758
+ {{ t('plugins.addRepos.bannerBtn') }}
759
+ </button>
760
+ </Banner>
761
+
670
762
  <Tabbed
671
763
  ref="tabs"
672
764
  :tabs-only="true"
@@ -674,25 +766,25 @@ export default {
674
766
  @changed="filterChanged"
675
767
  >
676
768
  <Tab
677
- name="installed"
769
+ :name="TABS_VALUES.INSTALLED"
678
770
  data-testid="extension-tab-installed"
679
771
  label-key="plugins.tabs.installed"
680
772
  :weight="20"
681
773
  />
682
774
  <Tab
683
- name="available"
775
+ :name="TABS_VALUES.AVAILABLE"
684
776
  data-testid="extension-tab-available"
685
777
  label-key="plugins.tabs.available"
686
778
  :weight="19"
687
779
  />
688
780
  <Tab
689
- name="updates"
781
+ :name="TABS_VALUES.UPDATES"
690
782
  label-key="plugins.tabs.updates"
691
783
  :weight="18"
692
784
  :badge="updates.length"
693
785
  />
694
786
  <Tab
695
- name="all"
787
+ :name="TABS_VALUES.ALL"
696
788
  label-key="plugins.tabs.all"
697
789
  :weight="17"
698
790
  />
@@ -767,9 +859,20 @@ export default {
767
859
  v-clean-tooltip="t('plugins.upgradeAvailable')"
768
860
  > -> {{ plugin.upgrade }}</span>
769
861
  <p
770
- v-if="plugin.incompatibleDisclaimer"
862
+ v-if="plugin.installedError"
771
863
  class="incompatible"
772
- >{{ plugin.incompatibleDisclaimer }}</p>
864
+ >
865
+ <i class="icon icon-warning icon-lg text-warning" />
866
+ <span>{{ plugin.installedError }}</span>
867
+ </p>
868
+ <p
869
+ v-else-if="plugin.incompatibleRancherVersion"
870
+ class="incompatible"
871
+ >{{ plugin.incompatibleRancherVersion }}</p>
872
+ <p
873
+ v-else-if="plugin.incompatibleKubeVersion"
874
+ class="incompatible"
875
+ >{{ plugin.incompatibleKubeVersion }}</p>
773
876
  </span>
774
877
  </div>
775
878
  <!-- plugin badges -->
@@ -863,7 +966,7 @@ export default {
863
966
  </button>
864
967
  </div>
865
968
  <div
866
- v-else
969
+ v-else-if="plugin.installableVersions && plugin.installableVersions.length"
867
970
  class="plugin-buttons"
868
971
  >
869
972
  <button
@@ -905,6 +1008,10 @@ export default {
905
1008
  ref="removeUIPlugins"
906
1009
  @done="updateInstallStatus"
907
1010
  />
1011
+ <AddExtensionRepos
1012
+ ref="addExtensionReposDialog"
1013
+ @done="updateInstallStatus(true)"
1014
+ />
908
1015
  </div>
909
1016
  </template>
910
1017
 
@@ -1131,6 +1238,10 @@ export default {
1131
1238
  }
1132
1239
  }
1133
1240
 
1241
+ ::v-deep .checkbox-label {
1242
+ font-weight: normal !important;
1243
+ }
1244
+
1134
1245
  @media screen and (max-width: 1200px) {
1135
1246
  .plugin-list {
1136
1247
  .plugin {
@@ -121,8 +121,14 @@ export default {
121
121
  const y = event.srcElement.scrollTop - top - 20;
122
122
  let found = false;
123
123
 
124
+ if (!this.doc?.toc || this.doc.toc.length === 0) {
125
+ this.selected = null;
126
+
127
+ return;
128
+ }
129
+
124
130
  // Debounce scroll events
125
- this.doc.toc.forEach((item) => {
131
+ this.doc.toc?.forEach((item) => {
126
132
  const elm = document.getElementById(item.id);
127
133
  const tocElm = document.getElementById(`toc-link-${ item.id }`);
128
134
 
@@ -146,8 +152,8 @@ export default {
146
152
  }
147
153
  });
148
154
 
149
- if (!found) {
150
- const last = this.doc.toc[this.doc.toc.length - 1].id;
155
+ if (!found & this.doc.toc) {
156
+ const last = this.doc.toc[this.doc.toc.length - 1]?.id;
151
157
  const tocElm = document.getElementById(`toc-link-${ last }`);
152
158
 
153
159
  if ( tocElm ) {
package/pages/home.vue CHANGED
@@ -86,7 +86,7 @@ export default {
86
86
 
87
87
  computed: {
88
88
  ...mapState(['managementReady']),
89
- ...mapGetters(['currentCluster']),
89
+ ...mapGetters(['currentCluster', 'defaultClusterId', 'releaseNotesUrl']),
90
90
  mcm: mapFeature(MULTI_CLUSTER),
91
91
 
92
92
  provClusters() {
@@ -203,8 +203,6 @@ export default {
203
203
  ];
204
204
  },
205
205
 
206
- ...mapGetters(['currentCluster', 'defaultClusterId']),
207
-
208
206
  kubeClusters() {
209
207
  return filterHiddenLocalCluster(filterOnlyKubernetesClusters(this.provClusters || [], this.$store), this.$store);
210
208
  }
@@ -266,7 +264,6 @@ export default {
266
264
  showWhatsNew() {
267
265
  // Update the value, so that the message goes away
268
266
  markReadReleaseNotes(this.$store);
269
- this.$router.push({ name: 'docs-doc', params: { doc: 'whats-new' } });
270
267
  },
271
268
 
272
269
  showUserPrefs() {
@@ -306,6 +303,7 @@ export default {
306
303
  :title="t('landing.welcomeToRancher', {vendor})"
307
304
  :pref="HIDE_HOME_PAGE_CARDS"
308
305
  pref-key="welcomeBanner"
306
+ data-testid="home-banner-graphic"
309
307
  />
310
308
  <IndentedPanel class="mt-20 mb-20">
311
309
  <div
@@ -322,7 +320,10 @@ export default {
322
320
  </div>
323
321
  <a
324
322
  class="hand"
325
- @click.prevent.stop="showWhatsNew"
323
+ :href="releaseNotesUrl"
324
+ target="_blank"
325
+ rel="noopener noreferrer nofollow"
326
+ @click.stop="showWhatsNew"
326
327
  ><span v-clean-html="t('landing.whatsNewLink')" /></a>
327
328
  </Banner>
328
329
  </div>
@@ -337,6 +338,7 @@ export default {
337
338
  <div class="col span-12">
338
339
  <Banner
339
340
  color="set-login-page mt-0"
341
+ data-testid="set-login-page-banner"
340
342
  :closable="true"
341
343
  @close="closeSetLoginBanner()"
342
344
  >
@@ -384,6 +386,7 @@ export default {
384
386
  v-if="canManageClusters"
385
387
  :to="manageLocation"
386
388
  class="btn btn-sm role-secondary"
389
+ data-testid="cluster-management-manage-button"
387
390
  >
388
391
  {{ t('cluster.manageAction') }}
389
392
  </n-link>
@@ -391,6 +394,7 @@ export default {
391
394
  v-if="canCreateCluster"
392
395
  :to="importLocation"
393
396
  class="btn btn-sm role-primary"
397
+ data-testid="cluster-create-import-button"
394
398
  >
395
399
  {{ t('cluster.importAction') }}
396
400
  </n-link>
@@ -398,6 +402,7 @@ export default {
398
402
  v-if="canCreateCluster"
399
403
  :to="createLocation"
400
404
  class="btn btn-sm role-primary"
405
+ data-testid="cluster-create-button"
401
406
  >
402
407
  {{ t('generic.create') }}
403
408
  </n-link>
@@ -5,9 +5,9 @@ import CommunityLinks from '@shell/components/CommunityLinks';
5
5
  import { CATALOG, MANAGEMENT } from '@shell/config/types';
6
6
  import { getVendor } from '@shell/config/private-label';
7
7
  import { SETTING } from '@shell/config/settings';
8
- import { findBy } from '@shell/utils/array';
9
8
  import { addParam } from '@shell/utils/url';
10
9
  import { isRancherPrime } from '@shell/config/version';
10
+ import { hasCspAdapter } from 'mixins/brand';
11
11
  import { generateSupportLink } from '@shell/utils/version';
12
12
 
13
13
  export default {
@@ -71,7 +71,7 @@ export default {
71
71
 
72
72
  computed: {
73
73
  cspAdapter() {
74
- return findBy(this.apps, 'metadata.name', 'rancher-csp-adapter' );
74
+ return hasCspAdapter(this.apps);
75
75
  },
76
76
 
77
77
  hasSupport() {
@@ -93,11 +93,17 @@ export default {
93
93
  },
94
94
 
95
95
  supportConfigLink() {
96
- if (!this.cspAdapter) {
96
+ const adapter = this.cspAdapter;
97
+
98
+ if (!adapter) {
97
99
  return false;
98
100
  }
99
101
 
100
- return `${ this.serverUrl }/v1/generateSUSERancherSupportConfig`;
102
+ if (adapter.metadata.name === 'rancher-csp-billing-adapter') {
103
+ return `${ this.serverUrl }/v1/generateSUSERancherSupportConfig?usePAYG=true`;
104
+ } else {
105
+ return `${ this.serverUrl }/v1/generateSUSERancherSupportConfig`;
106
+ }
101
107
  },
102
108
 
103
109
  title() {
@@ -1,6 +1,6 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
- const contextFolders = ['chart', 'cloud-credential', 'content', 'detail', 'edit', 'list', 'machine-config', 'models', 'promptRemove', 'l10n', 'windowComponents', 'dialog', 'formatters', 'login'];
3
+ const contextFolders = ['chart', 'cloud-credential', 'content', 'detail', 'edit', 'layouts', 'list', 'machine-config', 'models', 'promptRemove', 'l10n', 'windowComponents', 'dialog', 'formatters', 'login'];
4
4
  const contextMap = contextFolders.reduce((map, obj) => {
5
5
  map[obj] = true;
6
6
 
@@ -24,7 +24,7 @@ function bind(el, { value, oldValue, modifiers }) {
24
24
  });
25
25
  }
26
26
 
27
- const VCleanTooltip = {
27
+ export const VCleanTooltip = {
28
28
  ...VTooltip,
29
29
  bind,
30
30
  update: bind,
@@ -518,6 +518,14 @@ export function stateSort(color, display) {
518
518
  return `${ SORT_ORDER[color] || SORT_ORDER['other'] } ${ display }`;
519
519
  }
520
520
 
521
+ export function isConditionReadyAndWaiting(condition) {
522
+ if (!condition) {
523
+ return false;
524
+ }
525
+
526
+ return condition?.type?.toLowerCase() === 'ready' && condition?.reason?.toLowerCase() === 'waiting';
527
+ }
528
+
521
529
  function maybeFn(val) {
522
530
  if ( isFunction(val) ) {
523
531
  return val(this);
@@ -858,7 +866,7 @@ export default class Resource {
858
866
  const currentRoute = this.currentRouter().app._route;
859
867
  const extensionMenuActions = getApplicableExtensionEnhancements(this.$rootState, ExtensionPoint.ACTION, ActionLocation.TABLE, currentRoute, this);
860
868
 
861
- let all = [
869
+ const all = [
862
870
  { divider: true },
863
871
  {
864
872
  action: this.canUpdate ? 'goToEdit' : 'goToViewConfig',
@@ -910,7 +918,32 @@ export default class Resource {
910
918
  if (extensionMenuActions.length) {
911
919
  // Add a divider first
912
920
  all.push({ divider: true });
913
- all = all.concat(extensionMenuActions);
921
+
922
+ extensionMenuActions.forEach((action) => {
923
+ const newActionInstance = { ...action };
924
+
925
+ const enabledFn = newActionInstance.enabled;
926
+ const typeofEnabled = typeof enabledFn;
927
+
928
+ switch (typeofEnabled) {
929
+ case 'undefined':
930
+ newActionInstance.enabled = true;
931
+ break;
932
+ case 'function':
933
+ Object.defineProperty(newActionInstance, 'enabled', { get: () => enabledFn(this) });
934
+ break;
935
+ case 'boolean':
936
+ // no op, just use it directly
937
+ break;
938
+ default:
939
+ // unsupported value
940
+ console.warn(`Unsupported 'enabled' property type for action: ${ action.label || action.labelKey }` ); // eslint-disable-line no-console
941
+ delete newActionInstance.enabled;
942
+ break;
943
+ }
944
+
945
+ all.push(newActionInstance);
946
+ });
914
947
  }
915
948
 
916
949
  return all;