@rancher/shell 0.1.4 → 0.2.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 (170) hide show
  1. package/assets/brand/suse/favicon.png +0 -0
  2. package/assets/images/generic-plugin.svg +1 -7
  3. package/assets/styles/global/_button.scss +1 -0
  4. package/assets/translations/en-us.yaml +129 -53
  5. package/assets/translations/zh-hans.yaml +18 -0
  6. package/components/CommunityLinks.vue +40 -49
  7. package/components/ExplorerProjectsNamespaces.vue +20 -3
  8. package/components/HarvesterServiceAddOnConfig.vue +10 -10
  9. package/components/LazyImage.vue +21 -8
  10. package/components/PromptRemove.vue +2 -2
  11. package/components/ResourceList/Masthead.vue +21 -1
  12. package/components/ResourceList/ResourceLoadingIndicator.vue +0 -8
  13. package/components/ResourceList/index.vue +42 -36
  14. package/components/ResourceTable.vue +19 -0
  15. package/components/SortableTable/THead.vue +311 -70
  16. package/components/SortableTable/advanced-filtering.js +272 -0
  17. package/components/SortableTable/filtering.js +90 -29
  18. package/components/SortableTable/index.vue +486 -280
  19. package/components/Tabbed/index.vue +25 -7
  20. package/components/TypeDescription.vue +10 -1
  21. package/components/fleet/FleetClusters.vue +6 -0
  22. package/components/fleet/FleetRepos.vue +7 -1
  23. package/components/form/Command.vue +5 -0
  24. package/components/form/EnvVars.vue +5 -0
  25. package/components/form/NameNsDescription.vue +3 -1
  26. package/components/form/NodeScheduling.vue +6 -1
  27. package/components/form/PodAffinity.vue +5 -0
  28. package/components/form/ServiceNameSelect.vue +5 -0
  29. package/components/form/ValueFromResource.vue +7 -1
  30. package/components/form/WorkloadPorts.vue +2 -2
  31. package/components/nav/TopLevelMenu.vue +2 -1
  32. package/config/home-links.js +155 -0
  33. package/config/private-label.js +1 -1
  34. package/config/product/manager.js +0 -2
  35. package/config/product/settings.js +1 -0
  36. package/config/product/uiplugins.js +2 -1
  37. package/config/settings.js +3 -1
  38. package/config/uiplugins.js +76 -6
  39. package/config/version.js +17 -0
  40. package/core/plugin.ts +12 -0
  41. package/core/plugins.js +29 -5
  42. package/core/types.ts +6 -0
  43. package/creators/app/{.eslintignore → files/.eslintignore} +0 -0
  44. package/creators/app/{.eslintrc.js → files/.eslintrc.js} +0 -0
  45. package/creators/app/{.vscode → files/.vscode}/settings.json +0 -0
  46. package/creators/app/{babel.config.js → files/babel.config.js} +0 -0
  47. package/creators/app/{nuxt.config.js → files/nuxt.config.js} +0 -0
  48. package/creators/app/{tsconfig.json → files/tsconfig.json} +2 -1
  49. package/creators/app/init +16 -17
  50. package/creators/app/package.json +7 -1
  51. package/creators/pkg/{babel.config.js → files/babel.config.js} +0 -0
  52. package/creators/pkg/{index.ts → files/index.ts} +0 -0
  53. package/creators/pkg/{tsconfig.json → files/tsconfig.json} +13 -12
  54. package/creators/pkg/{vue.config.js → files/vue.config.js} +0 -0
  55. package/creators/pkg/init +1 -1
  56. package/creators/pkg/package.json +1 -1
  57. package/creators/update/init +54 -0
  58. package/creators/update/package.json +20 -0
  59. package/creators/update/upgrade +56 -0
  60. package/creators/update/yarn-error.log +54 -0
  61. package/detail/provisioning.cattle.io.cluster.vue +1 -1
  62. package/detail/workload/index.vue +1 -0
  63. package/edit/persistentvolume/index.vue +48 -13
  64. package/edit/persistentvolumeclaim.vue +31 -13
  65. package/edit/provisioning.cattle.io.cluster/ACE.vue +2 -1
  66. package/edit/provisioning.cattle.io.cluster/DrainOptions.vue +0 -1
  67. package/edit/provisioning.cattle.io.cluster/rke2.vue +52 -43
  68. package/edit/service.vue +1 -1
  69. package/edit/workload/index.vue +19 -9
  70. package/edit/workload/mixins/workload.js +109 -114
  71. package/edit/workload/storage/index.vue +11 -17
  72. package/edit/workload/storage/persistentVolumeClaim/index.vue +5 -0
  73. package/edit/workload/storage/secret.vue +6 -1
  74. package/list/catalog.cattle.io.app.vue +10 -9
  75. package/list/catalog.cattle.io.clusterrepo.vue +6 -61
  76. package/list/cis.cattle.io.clusterscan.vue +12 -12
  77. package/list/fleet.cattle.io.bundle.vue +33 -28
  78. package/list/fleet.cattle.io.cluster.vue +26 -22
  79. package/list/fleet.cattle.io.clustergroup.vue +6 -0
  80. package/list/fleet.cattle.io.clusterregistrationtoken.vue +28 -24
  81. package/list/fleet.cattle.io.gitrepo.vue +25 -14
  82. package/list/helm.cattle.io.projecthelmchart.vue +52 -33
  83. package/list/logging.banzaicloud.io.clusterflow.vue +7 -12
  84. package/list/logging.banzaicloud.io.flow.vue +7 -14
  85. package/list/management.cattle.io.cluster.vue +26 -15
  86. package/list/management.cattle.io.feature.vue +13 -8
  87. package/list/management.cattle.io.user.vue +38 -19
  88. package/list/monitoring.coreos.com.alertmanagerconfig.vue +8 -15
  89. package/list/namespace.vue +14 -1
  90. package/list/node.vue +13 -16
  91. package/list/persistentvolume.vue +16 -9
  92. package/list/persistentvolumeclaim.vue +5 -8
  93. package/list/provisioning.cattle.io.cluster.vue +34 -8
  94. package/list/service.vue +24 -12
  95. package/list/ui.cattle.io.navlink.vue +6 -0
  96. package/list/workload.vue +2 -2
  97. package/middleware/authenticated.js +6 -0
  98. package/mixins/resource-fetch.js +12 -18
  99. package/mixins/resource-manager.js +126 -0
  100. package/models/catalog.cattle.io.uiplugin.js +4 -0
  101. package/models/management.cattle.io.cluster.js +9 -1
  102. package/models/pod.js +15 -5
  103. package/models/provisioning.cattle.io.cluster.js +4 -0
  104. package/models/workload.service.js +10 -0
  105. package/nuxt.config.js +2 -1
  106. package/package.json +1 -1
  107. package/pages/auth/login.vue +10 -0
  108. package/pages/auth/verify.vue +9 -0
  109. package/pages/c/_cluster/apps/charts/install.vue +119 -31
  110. package/pages/c/_cluster/settings/DefaultLinksEditor.vue +108 -0
  111. package/pages/c/_cluster/settings/links.vue +53 -101
  112. package/pages/c/_cluster/settings/performance.vue +90 -7
  113. package/pages/c/_cluster/uiplugins/DeveloperInstallDialog.vue +3 -3
  114. package/pages/c/_cluster/uiplugins/InstallDialog.vue +72 -21
  115. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +26 -7
  116. package/pages/c/_cluster/uiplugins/RemoveUIPlugins.vue +2 -7
  117. package/pages/c/_cluster/uiplugins/SetupUIPlugins.vue +23 -15
  118. package/pages/c/_cluster/uiplugins/UninstallDialog.vue +12 -5
  119. package/pages/c/_cluster/uiplugins/index.vue +223 -72
  120. package/pages/support/index.vue +31 -142
  121. package/plugins/dashboard-store/actions.js +19 -0
  122. package/plugins/dashboard-store/getters.js +20 -3
  123. package/plugins/dashboard-store/mutations.js +13 -7
  124. package/plugins/plugin.js +18 -15
  125. package/plugins/steve/getters.js +12 -0
  126. package/plugins/version.js +21 -0
  127. package/promptRemove/mixin/roleDeletionCheck.js +15 -1
  128. package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +6 -7
  129. package/rancher-components/components/BadgeState/BadgeState.spec.ts +12 -0
  130. package/rancher-components/components/BadgeState/BadgeState.vue +107 -0
  131. package/rancher-components/components/BadgeState/index.ts +1 -0
  132. package/rancher-components/components/Banner/Banner.test.ts +13 -0
  133. package/rancher-components/components/Banner/Banner.vue +163 -0
  134. package/rancher-components/components/Banner/index.ts +1 -0
  135. package/rancher-components/components/Card/Card.vue +150 -0
  136. package/rancher-components/components/Card/index.ts +1 -0
  137. package/rancher-components/components/Form/Checkbox/Checkbox.test.ts +77 -0
  138. package/rancher-components/components/Form/Checkbox/Checkbox.vue +395 -0
  139. package/rancher-components/components/Form/Checkbox/index.ts +1 -0
  140. package/rancher-components/components/Form/LabeledInput/LabeledInput.test.ts +29 -0
  141. package/rancher-components/components/Form/LabeledInput/LabeledInput.vue +343 -0
  142. package/rancher-components/components/Form/LabeledInput/index.ts +1 -0
  143. package/rancher-components/components/Form/Radio/RadioButton.vue +270 -0
  144. package/rancher-components/components/Form/Radio/RadioGroup.vue +235 -0
  145. package/rancher-components/components/Form/Radio/index.ts +2 -0
  146. package/rancher-components/components/Form/TextArea/TextAreaAutoGrow.vue +168 -0
  147. package/rancher-components/components/Form/TextArea/index.ts +1 -0
  148. package/rancher-components/components/Form/ToggleSwitch/ToggleSwitch.test.ts +107 -0
  149. package/rancher-components/components/Form/ToggleSwitch/ToggleSwitch.vue +137 -0
  150. package/rancher-components/components/Form/ToggleSwitch/index.ts +1 -0
  151. package/rancher-components/components/Form/index.ts +5 -0
  152. package/rancher-components/components/LabeledTooltip/LabeledTooltip.vue +137 -0
  153. package/rancher-components/components/LabeledTooltip/index.ts +1 -0
  154. package/scripts/publish-shell.sh +39 -6
  155. package/scripts/record-deps.js +37 -0
  156. package/scripts/test-plugins-build.sh +8 -5
  157. package/scripts/typegen.sh +84 -0
  158. package/store/auth.js +3 -0
  159. package/store/index.js +12 -3
  160. package/store/type-map.js +2 -0
  161. package/types/shell/index.d.ts +3046 -0
  162. package/utils/favicon.js +8 -2
  163. package/utils/gc/gc-interval.ts +40 -0
  164. package/utils/gc/gc-root-store.js +76 -0
  165. package/utils/gc/gc-route-changed.ts +44 -0
  166. package/utils/gc/gc-types.ts +21 -0
  167. package/utils/gc/gc.ts +282 -0
  168. package/config/footer.js +0 -18
  169. package/creators/pkg/nuxt.config.js +0 -6
  170. package/yarn-error.log +0 -195
@@ -1,13 +1,12 @@
1
1
  <script>
2
2
  import Vue from 'vue';
3
3
  import { mapGetters } from 'vuex';
4
-
5
4
  import { mapPref, PLUGIN_DEVELOPER } from '@shell/store/prefs';
6
5
  import { sortBy } from '@shell/utils/sort';
7
6
  import { allHash } from '@shell/utils/promise';
8
- import { CATALOG, UI_PLUGIN, SCHEMA } from '@shell/config/types';
7
+ import { CATALOG, UI_PLUGIN, SERVICE } from '@shell/config/types';
9
8
  import { CATALOG as CATALOG_ANNOTATIONS } from '@shell/config/labels-annotations';
10
-
9
+ import { NAME as APP_PRODUCT } from '@shell/config/product/apps';
11
10
  import ActionMenu from '@shell/components/ActionMenu';
12
11
  import Tabbed from '@shell/components/Tabbed/index.vue';
13
12
  import Tab from '@shell/components/Tabbed/Tab.vue';
@@ -19,7 +18,16 @@ import DeveloperInstallDialog from './DeveloperInstallDialog.vue';
19
18
  import PluginInfoPanel from './PluginInfoPanel.vue';
20
19
  import SetupUIPlugins from './SetupUIPlugins';
21
20
  import RemoveUIPlugins from './RemoveUIPlugins';
22
- import { isUIPlugin, uiPluginHasAnnotation, UI_PLUGIN_NAMESPACE } from '@shell/config/uiplugins';
21
+ import {
22
+ isUIPlugin,
23
+ uiPluginAnnotation,
24
+ uiPluginHasAnnotation,
25
+ isSupportedChartVersion,
26
+ UI_PLUGIN_NAMESPACE,
27
+ UI_PLUGIN_CHART_ANNOTATIONS
28
+ } from '@shell/config/uiplugins';
29
+
30
+ const MAX_DESCRIPTION_LENGTH = 200;
23
31
 
24
32
  export default {
25
33
  components: {
@@ -48,7 +56,9 @@ export default {
48
56
  menuTargetElement: null,
49
57
  menuTargetEvent: null,
50
58
  menuOpen: false,
59
+ hasService: false,
51
60
  defaultIcon: require('~shell/assets/images/generic-plugin.svg'),
61
+ reloadRequired: false,
52
62
  };
53
63
  },
54
64
 
@@ -57,8 +67,12 @@ export default {
57
67
  async fetch() {
58
68
  const hash = {};
59
69
 
60
- if (this.hasPluginCRD) {
61
- hash.plugins = this.$store.dispatch('management/findAll', { type: UI_PLUGIN });
70
+ const isSetup = await this.updateInstallStatus();
71
+
72
+ if (isSetup) {
73
+ if (this.$store.getters['management/schemaFor'](UI_PLUGIN)) {
74
+ hash.plugins = this.$store.dispatch('management/findAll', { type: UI_PLUGIN });
75
+ }
62
76
  }
63
77
 
64
78
  hash.load = await this.$store.dispatch('catalog/load');
@@ -76,8 +90,14 @@ export default {
76
90
 
77
91
  this.charts = Object.values(c);
78
92
 
93
+ // If there are no plugins installed, default to the catalog view
94
+ if (this.plugins.length === 0) {
95
+ this.$refs.tabs?.select('available');
96
+ }
97
+
79
98
  this.loading = false;
80
99
  },
100
+
81
101
  computed: {
82
102
  pluginDeveloper: mapPref(PLUGIN_DEVELOPER),
83
103
 
@@ -87,17 +107,25 @@ export default {
87
107
  menuActions() {
88
108
  const menuActions = [];
89
109
 
110
+ // Add link to go to the Repos view of the local cluster
111
+ menuActions.push({
112
+ action: 'manageRepos',
113
+ label: this.t('plugins.manageRepos'),
114
+ enabled: true
115
+ });
116
+
90
117
  // Only show Developer Load action if the user has this enabled in preferences
91
118
  if (this.pluginDeveloper) {
119
+ menuActions.push( { divider: true });
92
120
  menuActions.push({
93
121
  action: 'devLoad',
94
122
  label: this.t('plugins.developer.label'),
95
123
  enabled: true
96
124
  });
97
- menuActions.push( { divider: true });
98
125
  }
99
126
 
100
- if (this.hasPluginCRD) {
127
+ if (this.hasService) {
128
+ menuActions.push( { divider: true });
101
129
  menuActions.push({
102
130
  action: 'removePluginSupport',
103
131
  label: this.t('plugins.setup.remove.label'),
@@ -108,14 +136,6 @@ export default {
108
136
  return menuActions;
109
137
  },
110
138
 
111
- // Is the Plugin CRD available ?
112
- hasPluginCRD() {
113
- const schemas = this.$store.getters[`management/all`](SCHEMA);
114
- const crd = schemas.find(s => s.id === UI_PLUGIN);
115
-
116
- return !!crd;
117
- },
118
-
119
139
  list() {
120
140
  const all = this.available;
121
141
 
@@ -151,8 +171,11 @@ export default {
151
171
  all = all.filter(c => !uiPluginHasAnnotation(c, CATALOG_ANNOTATIONS.HIDDEN, 'true'));
152
172
 
153
173
  all = all.map((chart) => {
174
+ // Label can be overridden by chart annotation
175
+ const label = uiPluginAnnotation(UI_PLUGIN_CHART_ANNOTATIONS.DISPLAY_NAME) || chart.chartNameDisplay;
154
176
  const item = {
155
177
  name: chart.chartNameDisplay,
178
+ label,
156
179
  description: chart.chartDescription,
157
180
  id: chart.id,
158
181
  versions: [],
@@ -167,6 +190,9 @@ export default {
167
190
  item.versions = [...chart.versions];
168
191
  item.chart = chart;
169
192
 
193
+ // Filter the versions, leaving only those that are compatible with this Rancher
194
+ item.versions = item.versions.filter(version => isSupportedChartVersion(version));
195
+
170
196
  if (this.latest) {
171
197
  item.icon = chart.icon || this.latest.annotations['catalog.cattle.io/ui-icon'];
172
198
  }
@@ -178,6 +204,9 @@ export default {
178
204
  return item;
179
205
  });
180
206
 
207
+ // Remove charts with no installable versions
208
+ all = all.filter(c => c.versions.length > 0);
209
+
181
210
  // Check that all of the loaded plugins are represented
182
211
  this.uiplugins.forEach((p) => {
183
212
  const chart = all.find(c => c.name === p.name);
@@ -186,7 +215,9 @@ export default {
186
215
  // A pluign is loaded, but there is no chart, so add an item so that it shows up
187
216
  const item = {
188
217
  name: p.name,
218
+ label: p.name,
189
219
  description: p.metadata?.description,
220
+ icon: p.metadata?.icon,
190
221
  id: p.id,
191
222
  versions: [],
192
223
  displayVersion: p.metadata?.version || '-',
@@ -200,22 +231,36 @@ export default {
200
231
 
201
232
  // Go through the CRs for the plugins and wire them into the catalog
202
233
  this.plugins.forEach((p) => {
203
- if (!p.removed) {
204
- const chart = all.find(c => c.name === p.name);
234
+ const chart = all.find(c => c.name === p.name);
205
235
 
206
- if (chart) {
207
- chart.installed = true;
208
- chart.uiplugin = p;
209
- chart.displayVersion = p.version;
236
+ if (chart) {
237
+ chart.installed = true;
238
+ chart.uiplugin = p;
239
+ chart.displayVersion = p.version;
210
240
 
211
- // Can't do this here
212
- chart.installing = this.installing[chart.name];
241
+ // Can't do this here
242
+ chart.installing = this.installing[chart.name];
213
243
 
214
- // Check for upgrade
215
- if (chart.versions.length && p.version !== chart.versions[0].version) {
216
- chart.upgrade = chart.versions[0].version;
217
- }
244
+ // Check for upgrade
245
+ if (chart.versions.length && p.version !== chart.versions[0].version) {
246
+ chart.upgrade = chart.versions[0].version;
218
247
  }
248
+ } else {
249
+ // No chart, so add a card for the plugin based on its Custom resource being present
250
+ const item = {
251
+ name: p.name,
252
+ label: p.name,
253
+ description: p.description || '-',
254
+ id: `${ p.name }-${ p.version }`,
255
+ versions: [],
256
+ displayVersion: p.version || '-',
257
+ installed: true,
258
+ installing: false,
259
+ builtin: false,
260
+ uiplugin: p,
261
+ };
262
+
263
+ all.push(item);
219
264
  }
220
265
  });
221
266
 
@@ -224,7 +269,13 @@ export default {
224
269
  const chart = all.find(c => c.name === e);
225
270
 
226
271
  if (chart) {
227
- chart.error = !!this.uiErrors[e];
272
+ const error = this.uiErrors[e];
273
+
274
+ if (error && typeof error === 'string') {
275
+ chart.error = this.t(this.uiErrors[e]);
276
+ } else {
277
+ chart.error = false;
278
+ }
228
279
  }
229
280
  });
230
281
 
@@ -237,6 +288,13 @@ export default {
237
288
  }
238
289
  });
239
290
 
291
+ // Clamp the lengths of the descriptions
292
+ all.forEach((plugin) => {
293
+ if (plugin.description && plugin.description.length > MAX_DESCRIPTION_LENGTH) {
294
+ plugin.description = `${ plugin.description.substr(0, MAX_DESCRIPTION_LENGTH) } ...`;
295
+ }
296
+ });
297
+
240
298
  // Sort by name
241
299
  return sortBy(all, 'name', false);
242
300
  },
@@ -262,7 +320,10 @@ export default {
262
320
  Vue.set(this.errors, plugin.name, error);
263
321
 
264
322
  if (active) {
265
- this.updatePluginInstallStatus(plugin.name, op.status.action);
323
+ // Can use the status directly, apart from upgrade, which maps to install
324
+ const status = op.status.action === 'upgrade' ? 'install' : op.status.action;
325
+
326
+ this.updatePluginInstallStatus(plugin.name, status);
266
327
  } else if (op.status.action === 'uninstall') {
267
328
  // Uninstall has finished
268
329
  this.updatePluginInstallStatus(plugin.name, false);
@@ -275,32 +336,63 @@ export default {
275
336
  });
276
337
  },
277
338
 
278
- plugins(neu) {
339
+ plugins(neu, old) {
279
340
  const installed = this.$store.getters['uiplugins/plugins'];
341
+ const shouldHaveLoaded = (installed || []).filter(p => !this.uiErrors[p.name] && !p.builtin);
342
+ let changes = 0;
343
+
344
+ // Did the user remove an extension
345
+ if (neu?.length < shouldHaveLoaded.length) {
346
+ changes++;
347
+ }
280
348
 
281
349
  neu.forEach((plugin) => {
282
- const existing = installed.find(p => !p.removed && p.name === plugin.name);
350
+ const existing = installed.find(p => !p.removed && p.name === plugin.name && p.version === plugin.version);
283
351
 
284
352
  if (!existing && plugin.isCached) {
285
- this.$plugin.loadAsyncByNameAndVersion(plugin.name, plugin.version).catch((e) => {
286
- console.error(`Failed to load plugin ${ plugin.name } (${ plugin.version })`); // eslint-disable-line no-console
287
- });
353
+ if (!this.uiErrors[plugin.name]) {
354
+ changes++;
355
+ }
288
356
 
289
357
  this.updatePluginInstallStatus(plugin.name, false);
290
358
  }
291
359
  });
360
+
361
+ if (changes > 0 ) {
362
+ Vue.set(this, 'reloadRequired', true);
363
+ }
292
364
  },
293
365
  },
294
366
 
295
367
  // Forget the types when we leave the page
296
368
  beforeDestroy() {
297
- this.$store.dispatch('cluster/forgetType', UI_PLUGIN);
298
- this.$store.dispatch('cluster/forgetType', CATALOG.OPERATION);
299
- this.$store.dispatch('cluster/forgetType', CATALOG.APP);
300
- this.$store.dispatch('cluster/forgetType', CATALOG.CLUSTER_REPO);
369
+ this.$store.dispatch('management/forgetType', UI_PLUGIN);
370
+ this.$store.dispatch('management/forgetType', CATALOG.OPERATION);
371
+ this.$store.dispatch('management/forgetType', CATALOG.APP);
372
+ this.$store.dispatch('management/forgetType', CATALOG.CLUSTER_REPO);
301
373
  },
302
374
 
303
375
  methods: {
376
+ async updateInstallStatus() {
377
+ let hasService;
378
+
379
+ try {
380
+ const service = await this.$store.dispatch('management/find', {
381
+ type: SERVICE,
382
+ id: `${ UI_PLUGIN_NAMESPACE }/ui-plugin-operator`,
383
+ opt: { force: true },
384
+ });
385
+
386
+ hasService = !!service;
387
+ } catch (e) {
388
+ hasService = false;
389
+ }
390
+
391
+ Vue.set(this, 'hasService', hasService);
392
+
393
+ return hasService;
394
+ },
395
+
304
396
  filterChanged(f) {
305
397
  this.view = f.selectedName;
306
398
  },
@@ -368,6 +460,21 @@ export default {
368
460
  this.menuTargetElement = undefined;
369
461
  this.menuTargetEvent = undefined;
370
462
  }
463
+ },
464
+
465
+ reload() {
466
+ this.$router.go();
467
+ },
468
+
469
+ manageRepos() {
470
+ this.$router.push({
471
+ name: 'c-cluster-product-resource',
472
+ params: {
473
+ cluster: 'local',
474
+ product: APP_PRODUCT,
475
+ resource: CATALOG.CLUSTER_REPO
476
+ }
477
+ });
371
478
  }
372
479
  }
373
480
  };
@@ -377,18 +484,27 @@ export default {
377
484
  <div class="plugins">
378
485
  <div class="plugin-header">
379
486
  <h2>{{ t('plugins.title') }}</h2>
487
+ <div v-if="reloadRequired" class="plugin-reload-banner mr-20">
488
+ <i class="icon icon-checkmark mr-10" />
489
+ <span>
490
+ {{ t('plugins.reload') }}
491
+ </span>
492
+ <button class="ml-10 btn btn-sm role-primary" @click="reload()">
493
+ {{ t('generic.reload') }}
494
+ </button>
495
+ </div>
380
496
  <button
381
- v-if="hasPluginCRD && hasMenuActions"
497
+ v-if="hasService && hasMenuActions"
382
498
  ref="actions"
383
499
  aria-haspopup="true"
384
500
  type="button"
385
- class="btn actions"
501
+ class="btn actions role-secondary"
386
502
  @click="setMenu"
387
503
  >
388
504
  <i class="icon icon-actions" />
389
505
  </button>
390
506
  <ActionMenu
391
- v-if="hasPluginCRD && hasMenuActions"
507
+ v-if="hasService && hasMenuActions"
392
508
  :custom-actions="menuActions"
393
509
  :open="menuOpen"
394
510
  :use-custom-target-element="true"
@@ -397,17 +513,18 @@ export default {
397
513
  @close="setMenu(false)"
398
514
  @devLoad="showDeveloperLoaddDialog"
399
515
  @removePluginSupport="removePluginSupport"
516
+ @manageRepos="manageRepos"
400
517
  />
401
518
  </div>
402
519
 
403
520
  <PluginInfoPanel ref="infoPanel" />
404
521
 
405
- <div v-if="!hasPluginCRD">
522
+ <div v-if="!hasService">
406
523
  <div v-if="loading" class="data-loading">
407
524
  <i class="icon-spin icon icon-spinner" />
408
525
  <t k="generic.loading" :raw="true" />
409
526
  </div>
410
- <SetupUIPlugins v-else class="setup-message" />
527
+ <SetupUIPlugins v-else class="setup-message" @done="updateInstallStatus" />
411
528
  </div>
412
529
  <div v-else>
413
530
  <Tabbed ref="tabs" :tabs-only="true" @changed="filterChanged">
@@ -425,7 +542,7 @@ export default {
425
542
  v-if="list.length === 0"
426
543
  :vertical="true"
427
544
  :subtle="true"
428
- icon="icon-gear"
545
+ icon="icon-extension"
429
546
  :message="emptyMessage"
430
547
  />
431
548
  <template v-else>
@@ -446,28 +563,24 @@ export default {
446
563
  </div>
447
564
  <div class="plugin-metadata">
448
565
  <div class="plugin-name">
449
- {{ plugin.name }}
566
+ {{ plugin.label }}
450
567
  </div>
451
568
  <div>{{ plugin.description }}</div>
452
- <div v-if="plugin.builtin" class="plugin-builtin">
453
- {{ t('plugins.labels.builtin') }}
454
- </div>
455
569
  <div class="plugin-version">
456
- <div v-if="plugin.installing" class="plugin-installing">
457
- <i class="version-busy icon icon-spin icon-spinner" />
458
- <div v-if="plugin.installing='install'">
459
- {{ t('plugins.labels.installing') }}
460
- </div>
461
- <div v-else>
462
- {{ t('plugins.labels.uninstalling') }}
463
- </div>
464
- </div>
570
+ <span v-if="plugin.installing === 'uninstall'" class="plugin-installing">
571
+ -
572
+ </span>
465
573
  <span v-else>
466
574
  <span>{{ plugin.displayVersion }}</span>
467
575
  <span v-if="plugin.upgrade" v-tooltip="t('plugins.upgradeAvailable')"> -> {{ plugin.upgrade }}</span>
468
576
  </span>
469
577
  </div>
470
- <div class="plugin-badges">
578
+ <div v-if="plugin.builtin" class="plugin-badges">
579
+ <div class="plugin-builtin">
580
+ {{ t('plugins.labels.builtin') }}
581
+ </div>
582
+ </div>
583
+ <div v-else class="plugin-badges">
471
584
  <div v-if="!plugin.certified" v-tooltip="t('plugins.descriptions.third-party')">
472
585
  {{ t('plugins.labels.third-party') }}
473
586
  </div>
@@ -477,17 +590,25 @@ export default {
477
590
  </div>
478
591
  <div class="plugin-spacer" />
479
592
  <div class="plugin-actions">
480
- <div v-if="plugin.error" v-tooltip="t('plugins.loadError')" class="plugin-error">
481
- <i class="icon icon-warning" />
482
- </div>
593
+ <template v-if="plugin.error">
594
+ <div v-tooltip="plugin.error" class="plugin-error">
595
+ <i class="icon icon-warning" />
596
+ </div>
597
+ </template>
483
598
  <div v-if="plugin.helmError" v-tooltip="t('plugins.helmError')" class="plugin-error">
484
599
  <i class="icon icon-warning" />
485
600
  </div>
486
601
 
487
602
  <div class="plugin-spacer" />
488
603
 
489
- <div v-if="plugin.installing">
490
- <!-- Don't show any buttons -->
604
+ <div v-if="plugin.installing" class="plugin-installing">
605
+ <i class="version-busy icon icon-spin icon-spinner" />
606
+ <div v-if="plugin.installing ==='install'">
607
+ {{ t('plugins.labels.installing') }}
608
+ </div>
609
+ <div v-else>
610
+ {{ t('plugins.labels.uninstalling') }}
611
+ </div>
491
612
  </div>
492
613
  <div v-else-if="plugin.installed" class="plugin-buttons">
493
614
  <button v-if="!plugin.builtin" class="btn role-secondary" @click="showUninstallDialog(plugin, $event)">
@@ -515,7 +636,7 @@ export default {
515
636
  <InstallDialog ref="installDialog" @closed="didInstall" @update="updatePluginInstallStatus" />
516
637
  <UninstallDialog ref="uninstallDialog" @closed="didUninstall" @update="updatePluginInstallStatus" />
517
638
  <DeveloperInstallDialog ref="developerInstallDialog" @closed="didInstall" />
518
- <RemoveUIPlugins ref="removeUIPlugins" />
639
+ <RemoveUIPlugins ref="removeUIPlugins" @done="updateInstallStatus" />
519
640
  </div>
520
641
  </template>
521
642
 
@@ -535,6 +656,30 @@ export default {
535
656
  }
536
657
  }
537
658
 
659
+ .plugin-reload-banner {
660
+ align-items: center;
661
+ background-color: var(--success-banner-bg);
662
+ display: flex;
663
+ padding: 0 10px;
664
+ border-radius: 5px;
665
+ min-height: 36px;
666
+
667
+ > i {
668
+ color: var(--success);
669
+ font-size: 20px;
670
+ font-weight: bold;
671
+ }
672
+
673
+ > button {
674
+ line-height: 30px;
675
+ min-height: 30px;
676
+ }
677
+ }
678
+
679
+ .plugin-complete {
680
+ font-size: 18px;
681
+ }
682
+
538
683
  .plugin-list {
539
684
  display: flex;
540
685
  flex-wrap: wrap;
@@ -560,6 +705,12 @@ export default {
560
705
  flex: 1;
561
706
  margin-bottom: 0;
562
707
  }
708
+
709
+ .btn.actions {
710
+ line-height: 36px;
711
+ min-height: 36px;
712
+ padding: 0 10px;
713
+ }
563
714
  }
564
715
 
565
716
  .plugin {
@@ -636,15 +787,15 @@ export default {
636
787
  height: 16px;
637
788
  width: 16px;
638
789
  }
790
+ }
639
791
 
640
- .plugin-installing {
641
- align-items: center;
642
- display: flex;
792
+ .plugin-installing {
793
+ align-items: center;
794
+ display: flex;
643
795
 
644
- > div {
645
- font-size: 14px;
646
- margin-left: 5px;
647
- }
796
+ > div {
797
+ font-size: 14px;
798
+ margin-left: 5px;
648
799
  }
649
800
  }
650
801