@rancher/shell 0.1.4 → 0.1.21

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 (152) hide show
  1. package/assets/brand/suse/favicon.png +0 -0
  2. package/assets/images/generic-plugin.svg +1 -7
  3. package/assets/translations/en-us.yaml +81 -47
  4. package/components/CommunityLinks.vue +40 -49
  5. package/components/ExplorerProjectsNamespaces.vue +20 -3
  6. package/components/LazyImage.vue +21 -8
  7. package/components/PromptRemove.vue +2 -2
  8. package/components/ResourceList/Masthead.vue +21 -1
  9. package/components/ResourceList/ResourceLoadingIndicator.vue +0 -8
  10. package/components/ResourceList/index.vue +9 -23
  11. package/components/SortableTable/index.vue +13 -10
  12. package/components/Tabbed/index.vue +25 -7
  13. package/components/TypeDescription.vue +10 -1
  14. package/components/fleet/FleetClusters.vue +6 -0
  15. package/components/fleet/FleetRepos.vue +7 -1
  16. package/components/form/Command.vue +5 -0
  17. package/components/form/EnvVars.vue +5 -0
  18. package/components/form/NameNsDescription.vue +3 -1
  19. package/components/form/NodeScheduling.vue +6 -1
  20. package/components/form/PodAffinity.vue +5 -0
  21. package/components/form/ServiceNameSelect.vue +5 -0
  22. package/components/form/ValueFromResource.vue +7 -1
  23. package/components/nav/TopLevelMenu.vue +2 -1
  24. package/config/home-links.js +155 -0
  25. package/config/private-label.js +1 -1
  26. package/config/product/manager.js +0 -2
  27. package/config/product/uiplugins.js +1 -1
  28. package/config/settings.js +3 -1
  29. package/config/uiplugins.js +63 -6
  30. package/config/version.js +17 -0
  31. package/core/plugin.ts +12 -0
  32. package/core/plugins.js +29 -5
  33. package/core/types.ts +6 -0
  34. package/creators/app/{.eslintignore → files/.eslintignore} +0 -0
  35. package/creators/app/{.eslintrc.js → files/.eslintrc.js} +0 -0
  36. package/creators/app/{.vscode → files/.vscode}/settings.json +0 -0
  37. package/creators/app/{babel.config.js → files/babel.config.js} +0 -0
  38. package/creators/app/{nuxt.config.js → files/nuxt.config.js} +0 -0
  39. package/creators/app/{tsconfig.json → files/tsconfig.json} +2 -1
  40. package/creators/app/init +16 -17
  41. package/creators/app/package.json +6 -0
  42. package/creators/pkg/{babel.config.js → files/babel.config.js} +0 -0
  43. package/creators/pkg/{index.ts → files/index.ts} +0 -0
  44. package/creators/pkg/{tsconfig.json → files/tsconfig.json} +13 -12
  45. package/creators/pkg/{vue.config.js → files/vue.config.js} +0 -0
  46. package/creators/pkg/init +1 -1
  47. package/creators/update/init +54 -0
  48. package/creators/update/package.json +20 -0
  49. package/creators/update/upgrade +56 -0
  50. package/creators/update/yarn-error.log +54 -0
  51. package/detail/workload/index.vue +1 -0
  52. package/edit/persistentvolume/index.vue +48 -13
  53. package/edit/persistentvolumeclaim.vue +31 -13
  54. package/edit/provisioning.cattle.io.cluster/rke2.vue +27 -19
  55. package/edit/workload/index.vue +19 -9
  56. package/edit/workload/mixins/workload.js +109 -114
  57. package/edit/workload/storage/index.vue +11 -17
  58. package/edit/workload/storage/persistentVolumeClaim/index.vue +5 -0
  59. package/edit/workload/storage/secret.vue +6 -1
  60. package/list/catalog.cattle.io.app.vue +10 -9
  61. package/list/catalog.cattle.io.clusterrepo.vue +6 -61
  62. package/list/cis.cattle.io.clusterscan.vue +12 -12
  63. package/list/fleet.cattle.io.bundle.vue +33 -28
  64. package/list/fleet.cattle.io.cluster.vue +26 -22
  65. package/list/fleet.cattle.io.clustergroup.vue +6 -0
  66. package/list/fleet.cattle.io.clusterregistrationtoken.vue +28 -24
  67. package/list/fleet.cattle.io.gitrepo.vue +25 -14
  68. package/list/helm.cattle.io.projecthelmchart.vue +52 -33
  69. package/list/logging.banzaicloud.io.clusterflow.vue +7 -12
  70. package/list/logging.banzaicloud.io.flow.vue +7 -14
  71. package/list/management.cattle.io.cluster.vue +26 -15
  72. package/list/management.cattle.io.feature.vue +13 -8
  73. package/list/management.cattle.io.user.vue +38 -19
  74. package/list/monitoring.coreos.com.alertmanagerconfig.vue +8 -15
  75. package/list/namespace.vue +14 -1
  76. package/list/node.vue +13 -16
  77. package/list/persistentvolume.vue +16 -9
  78. package/list/persistentvolumeclaim.vue +5 -8
  79. package/list/provisioning.cattle.io.cluster.vue +34 -8
  80. package/list/service.vue +24 -12
  81. package/list/ui.cattle.io.navlink.vue +6 -0
  82. package/list/workload.vue +2 -2
  83. package/middleware/authenticated.js +6 -0
  84. package/mixins/resource-fetch.js +12 -18
  85. package/mixins/resource-manager.js +126 -0
  86. package/models/catalog.cattle.io.uiplugin.js +4 -0
  87. package/models/pod.js +15 -5
  88. package/models/provisioning.cattle.io.cluster.js +4 -0
  89. package/models/workload.service.js +10 -0
  90. package/nuxt.config.js +2 -1
  91. package/package.json +1 -1
  92. package/pages/auth/login.vue +10 -0
  93. package/pages/auth/verify.vue +9 -0
  94. package/pages/c/_cluster/settings/DefaultLinksEditor.vue +108 -0
  95. package/pages/c/_cluster/settings/links.vue +53 -101
  96. package/pages/c/_cluster/settings/performance.vue +90 -7
  97. package/pages/c/_cluster/uiplugins/DeveloperInstallDialog.vue +3 -3
  98. package/pages/c/_cluster/uiplugins/InstallDialog.vue +71 -20
  99. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +21 -5
  100. package/pages/c/_cluster/uiplugins/RemoveUIPlugins.vue +2 -7
  101. package/pages/c/_cluster/uiplugins/SetupUIPlugins.vue +23 -15
  102. package/pages/c/_cluster/uiplugins/UninstallDialog.vue +11 -4
  103. package/pages/c/_cluster/uiplugins/index.vue +179 -65
  104. package/pages/support/index.vue +31 -142
  105. package/plugins/dashboard-store/actions.js +19 -0
  106. package/plugins/dashboard-store/getters.js +20 -3
  107. package/plugins/dashboard-store/mutations.js +13 -7
  108. package/plugins/plugin.js +18 -15
  109. package/plugins/steve/getters.js +12 -0
  110. package/plugins/version.js +21 -0
  111. package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +6 -7
  112. package/rancher-components/components/BadgeState/BadgeState.spec.ts +12 -0
  113. package/rancher-components/components/BadgeState/BadgeState.vue +107 -0
  114. package/rancher-components/components/BadgeState/index.ts +1 -0
  115. package/rancher-components/components/Banner/Banner.test.ts +13 -0
  116. package/rancher-components/components/Banner/Banner.vue +163 -0
  117. package/rancher-components/components/Banner/index.ts +1 -0
  118. package/rancher-components/components/Card/Card.vue +150 -0
  119. package/rancher-components/components/Card/index.ts +1 -0
  120. package/rancher-components/components/Form/Checkbox/Checkbox.test.ts +77 -0
  121. package/rancher-components/components/Form/Checkbox/Checkbox.vue +395 -0
  122. package/rancher-components/components/Form/Checkbox/index.ts +1 -0
  123. package/rancher-components/components/Form/LabeledInput/LabeledInput.test.ts +29 -0
  124. package/rancher-components/components/Form/LabeledInput/LabeledInput.vue +343 -0
  125. package/rancher-components/components/Form/LabeledInput/index.ts +1 -0
  126. package/rancher-components/components/Form/Radio/RadioButton.vue +270 -0
  127. package/rancher-components/components/Form/Radio/RadioGroup.vue +235 -0
  128. package/rancher-components/components/Form/Radio/index.ts +2 -0
  129. package/rancher-components/components/Form/TextArea/TextAreaAutoGrow.vue +168 -0
  130. package/rancher-components/components/Form/TextArea/index.ts +1 -0
  131. package/rancher-components/components/Form/ToggleSwitch/ToggleSwitch.test.ts +107 -0
  132. package/rancher-components/components/Form/ToggleSwitch/ToggleSwitch.vue +137 -0
  133. package/rancher-components/components/Form/ToggleSwitch/index.ts +1 -0
  134. package/rancher-components/components/Form/index.ts +5 -0
  135. package/rancher-components/components/LabeledTooltip/LabeledTooltip.vue +137 -0
  136. package/rancher-components/components/LabeledTooltip/index.ts +1 -0
  137. package/scripts/publish-shell.sh +39 -6
  138. package/scripts/record-deps.js +37 -0
  139. package/scripts/test-plugins-build.sh +8 -5
  140. package/scripts/typegen.sh +84 -0
  141. package/store/auth.js +3 -0
  142. package/store/index.js +12 -3
  143. package/types/shell/index.d.ts +3046 -0
  144. package/utils/favicon.js +8 -2
  145. package/utils/gc/gc-interval.ts +40 -0
  146. package/utils/gc/gc-root-store.js +76 -0
  147. package/utils/gc/gc-route-changed.ts +44 -0
  148. package/utils/gc/gc-types.ts +21 -0
  149. package/utils/gc/gc.ts +282 -0
  150. package/config/footer.js +0 -18
  151. package/creators/pkg/nuxt.config.js +0 -6
  152. package/yarn-error.log +0 -195
@@ -26,6 +26,8 @@ export default {
26
26
 
27
27
  const plugin = this.plugin;
28
28
 
29
+ this.$emit('update', plugin.name, 'uninstall');
30
+
29
31
  // Delete the CR if this is a developer plugin (there is no Helm App, so need to remove the CRD ourselves)
30
32
  if (plugin.uiplugin?.isDeveloper) {
31
33
  // Delete the custom resource
@@ -40,14 +42,19 @@ export default {
40
42
  });
41
43
 
42
44
  if (pluginApp) {
43
- await pluginApp.remove();
45
+ try {
46
+ await pluginApp.remove();
47
+ } catch (e) {
48
+ this.$store.dispatch('growl/error', {
49
+ title: this.t('plugins.error.generic'),
50
+ message: e.message ? e.message : e,
51
+ timeout: 10000
52
+ }, { root: true });
53
+ }
44
54
 
45
55
  await this.$store.dispatch('management/findAll', { type: CATALOG.OPERATION });
46
56
  }
47
57
 
48
- // Unload the plugin code
49
- this.$plugin.removePlugin(plugin.name);
50
-
51
58
  this.closeDialog(plugin);
52
59
  }
53
60
  }
@@ -1,11 +1,10 @@
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
 
11
10
  import ActionMenu from '@shell/components/ActionMenu';
@@ -19,7 +18,9 @@ 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 { isUIPlugin, uiPluginHasAnnotation, isSupportedChartVersion, UI_PLUGIN_NAMESPACE } from '@shell/config/uiplugins';
22
+
23
+ const MAX_DESCRIPTION_LENGTH = 200;
23
24
 
24
25
  export default {
25
26
  components: {
@@ -48,7 +49,9 @@ export default {
48
49
  menuTargetElement: null,
49
50
  menuTargetEvent: null,
50
51
  menuOpen: false,
52
+ hasService: false,
51
53
  defaultIcon: require('~shell/assets/images/generic-plugin.svg'),
54
+ reloadRequired: false,
52
55
  };
53
56
  },
54
57
 
@@ -57,8 +60,12 @@ export default {
57
60
  async fetch() {
58
61
  const hash = {};
59
62
 
60
- if (this.hasPluginCRD) {
61
- hash.plugins = this.$store.dispatch('management/findAll', { type: UI_PLUGIN });
63
+ const isSetup = await this.updateInstallStatus();
64
+
65
+ if (isSetup) {
66
+ if (this.$store.getters['management/schemaFor'](UI_PLUGIN)) {
67
+ hash.plugins = this.$store.dispatch('management/findAll', { type: UI_PLUGIN });
68
+ }
62
69
  }
63
70
 
64
71
  hash.load = await this.$store.dispatch('catalog/load');
@@ -76,8 +83,14 @@ export default {
76
83
 
77
84
  this.charts = Object.values(c);
78
85
 
86
+ // If there are no plugins installed, default to the catalog view
87
+ if (this.plugins.length === 0) {
88
+ this.$refs.tabs?.select('available');
89
+ }
90
+
79
91
  this.loading = false;
80
92
  },
93
+
81
94
  computed: {
82
95
  pluginDeveloper: mapPref(PLUGIN_DEVELOPER),
83
96
 
@@ -97,7 +110,7 @@ export default {
97
110
  menuActions.push( { divider: true });
98
111
  }
99
112
 
100
- if (this.hasPluginCRD) {
113
+ if (this.hasService) {
101
114
  menuActions.push({
102
115
  action: 'removePluginSupport',
103
116
  label: this.t('plugins.setup.remove.label'),
@@ -108,14 +121,6 @@ export default {
108
121
  return menuActions;
109
122
  },
110
123
 
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
124
  list() {
120
125
  const all = this.available;
121
126
 
@@ -167,6 +172,9 @@ export default {
167
172
  item.versions = [...chart.versions];
168
173
  item.chart = chart;
169
174
 
175
+ // Filter the versions, leaving only those that are compatible with this Rancher
176
+ item.versions = item.versions.filter(version => isSupportedChartVersion(version));
177
+
170
178
  if (this.latest) {
171
179
  item.icon = chart.icon || this.latest.annotations['catalog.cattle.io/ui-icon'];
172
180
  }
@@ -178,6 +186,9 @@ export default {
178
186
  return item;
179
187
  });
180
188
 
189
+ // Remove charts with no installable versions
190
+ all = all.filter(c => c.versions.length > 0);
191
+
181
192
  // Check that all of the loaded plugins are represented
182
193
  this.uiplugins.forEach((p) => {
183
194
  const chart = all.find(c => c.name === p.name);
@@ -200,22 +211,35 @@ export default {
200
211
 
201
212
  // Go through the CRs for the plugins and wire them into the catalog
202
213
  this.plugins.forEach((p) => {
203
- if (!p.removed) {
204
- const chart = all.find(c => c.name === p.name);
214
+ const chart = all.find(c => c.name === p.name);
205
215
 
206
- if (chart) {
207
- chart.installed = true;
208
- chart.uiplugin = p;
209
- chart.displayVersion = p.version;
216
+ if (chart) {
217
+ chart.installed = true;
218
+ chart.uiplugin = p;
219
+ chart.displayVersion = p.version;
210
220
 
211
- // Can't do this here
212
- chart.installing = this.installing[chart.name];
221
+ // Can't do this here
222
+ chart.installing = this.installing[chart.name];
213
223
 
214
- // Check for upgrade
215
- if (chart.versions.length && p.version !== chart.versions[0].version) {
216
- chart.upgrade = chart.versions[0].version;
217
- }
224
+ // Check for upgrade
225
+ if (chart.versions.length && p.version !== chart.versions[0].version) {
226
+ chart.upgrade = chart.versions[0].version;
218
227
  }
228
+ } else {
229
+ // No chart, so add a card for the plugin based on its Custom resource being present
230
+ const item = {
231
+ name: p.name,
232
+ description: p.description || '-',
233
+ id: `${ p.name }-${ p.version }`,
234
+ versions: [],
235
+ displayVersion: p.version || '-',
236
+ installed: true,
237
+ installing: false,
238
+ builtin: false,
239
+ uiplugin: p,
240
+ };
241
+
242
+ all.push(item);
219
243
  }
220
244
  });
221
245
 
@@ -224,7 +248,13 @@ export default {
224
248
  const chart = all.find(c => c.name === e);
225
249
 
226
250
  if (chart) {
227
- chart.error = !!this.uiErrors[e];
251
+ const error = this.uiErrors[e];
252
+
253
+ if (error && typeof error === 'string') {
254
+ chart.error = this.t(this.uiErrors[e]);
255
+ } else {
256
+ chart.error = false;
257
+ }
228
258
  }
229
259
  });
230
260
 
@@ -237,6 +267,13 @@ export default {
237
267
  }
238
268
  });
239
269
 
270
+ // Clamp the lengths of the descriptions
271
+ all.forEach((plugin) => {
272
+ if (plugin.description && plugin.description.length > MAX_DESCRIPTION_LENGTH) {
273
+ plugin.description = `${ plugin.description.substr(0, MAX_DESCRIPTION_LENGTH) } ...`;
274
+ }
275
+ });
276
+
240
277
  // Sort by name
241
278
  return sortBy(all, 'name', false);
242
279
  },
@@ -262,7 +299,10 @@ export default {
262
299
  Vue.set(this.errors, plugin.name, error);
263
300
 
264
301
  if (active) {
265
- this.updatePluginInstallStatus(plugin.name, op.status.action);
302
+ // Can use the status directly, apart from upgrade, which maps to install
303
+ const status = op.status.action === 'upgrade' ? 'install' : op.status.action;
304
+
305
+ this.updatePluginInstallStatus(plugin.name, status);
266
306
  } else if (op.status.action === 'uninstall') {
267
307
  // Uninstall has finished
268
308
  this.updatePluginInstallStatus(plugin.name, false);
@@ -275,32 +315,61 @@ export default {
275
315
  });
276
316
  },
277
317
 
278
- plugins(neu) {
318
+ plugins(neu, old) {
279
319
  const installed = this.$store.getters['uiplugins/plugins'];
280
320
 
321
+ let changes = 0;
322
+
323
+ // Did the user remove an extension
324
+ if (neu?.length < installed.length) {
325
+ changes++;
326
+ }
327
+
281
328
  neu.forEach((plugin) => {
282
- const existing = installed.find(p => !p.removed && p.name === plugin.name);
329
+ const existing = installed.find(p => !p.removed && p.name === plugin.name && p.version === plugin.version);
283
330
 
284
331
  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
- });
332
+ changes++;
288
333
 
289
334
  this.updatePluginInstallStatus(plugin.name, false);
290
335
  }
291
336
  });
337
+
338
+ if (changes > 0 ) {
339
+ Vue.set(this, 'reloadRequired', true);
340
+ }
292
341
  },
293
342
  },
294
343
 
295
344
  // Forget the types when we leave the page
296
345
  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);
346
+ this.$store.dispatch('management/forgetType', UI_PLUGIN);
347
+ this.$store.dispatch('management/forgetType', CATALOG.OPERATION);
348
+ this.$store.dispatch('management/forgetType', CATALOG.APP);
349
+ this.$store.dispatch('management/forgetType', CATALOG.CLUSTER_REPO);
301
350
  },
302
351
 
303
352
  methods: {
353
+ async updateInstallStatus() {
354
+ let hasService;
355
+
356
+ try {
357
+ const service = await this.$store.dispatch('management/find', {
358
+ type: SERVICE,
359
+ id: `${ UI_PLUGIN_NAMESPACE }/ui-plugin-operator`,
360
+ opt: { force: true },
361
+ });
362
+
363
+ hasService = !!service;
364
+ } catch (e) {
365
+ hasService = false;
366
+ }
367
+
368
+ Vue.set(this, 'hasService', hasService);
369
+
370
+ return hasService;
371
+ },
372
+
304
373
  filterChanged(f) {
305
374
  this.view = f.selectedName;
306
375
  },
@@ -368,6 +437,10 @@ export default {
368
437
  this.menuTargetElement = undefined;
369
438
  this.menuTargetEvent = undefined;
370
439
  }
440
+ },
441
+
442
+ reload() {
443
+ this.$router.go();
371
444
  }
372
445
  }
373
446
  };
@@ -377,18 +450,27 @@ export default {
377
450
  <div class="plugins">
378
451
  <div class="plugin-header">
379
452
  <h2>{{ t('plugins.title') }}</h2>
453
+ <div v-if="reloadRequired" class="plugin-reload-banner mr-20">
454
+ <i class="icon icon-checkmark mr-10" />
455
+ <span>
456
+ {{ t('plugins.reload') }}
457
+ </span>
458
+ <button class="ml-10 btn btn-sm role-primary" @click="reload()">
459
+ {{ t('generic.reload') }}
460
+ </button>
461
+ </div>
380
462
  <button
381
- v-if="hasPluginCRD && hasMenuActions"
463
+ v-if="hasService && hasMenuActions"
382
464
  ref="actions"
383
465
  aria-haspopup="true"
384
466
  type="button"
385
- class="btn actions"
467
+ class="btn actions role-secondary"
386
468
  @click="setMenu"
387
469
  >
388
470
  <i class="icon icon-actions" />
389
471
  </button>
390
472
  <ActionMenu
391
- v-if="hasPluginCRD && hasMenuActions"
473
+ v-if="hasService && hasMenuActions"
392
474
  :custom-actions="menuActions"
393
475
  :open="menuOpen"
394
476
  :use-custom-target-element="true"
@@ -402,12 +484,12 @@ export default {
402
484
 
403
485
  <PluginInfoPanel ref="infoPanel" />
404
486
 
405
- <div v-if="!hasPluginCRD">
487
+ <div v-if="!hasService">
406
488
  <div v-if="loading" class="data-loading">
407
489
  <i class="icon-spin icon icon-spinner" />
408
490
  <t k="generic.loading" :raw="true" />
409
491
  </div>
410
- <SetupUIPlugins v-else class="setup-message" />
492
+ <SetupUIPlugins v-else class="setup-message" @done="updateInstallStatus" />
411
493
  </div>
412
494
  <div v-else>
413
495
  <Tabbed ref="tabs" :tabs-only="true" @changed="filterChanged">
@@ -425,7 +507,7 @@ export default {
425
507
  v-if="list.length === 0"
426
508
  :vertical="true"
427
509
  :subtle="true"
428
- icon="icon-gear"
510
+ icon="icon-extension"
429
511
  :message="emptyMessage"
430
512
  />
431
513
  <template v-else>
@@ -453,15 +535,9 @@ export default {
453
535
  {{ t('plugins.labels.builtin') }}
454
536
  </div>
455
537
  <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>
538
+ <span v-if="plugin.installing === 'uninstall'" class="plugin-installing">
539
+ -
540
+ </span>
465
541
  <span v-else>
466
542
  <span>{{ plugin.displayVersion }}</span>
467
543
  <span v-if="plugin.upgrade" v-tooltip="t('plugins.upgradeAvailable')"> -> {{ plugin.upgrade }}</span>
@@ -477,17 +553,25 @@ export default {
477
553
  </div>
478
554
  <div class="plugin-spacer" />
479
555
  <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>
556
+ <template v-if="plugin.error">
557
+ <div v-tooltip="plugin.error" class="plugin-error">
558
+ <i class="icon icon-warning" />
559
+ </div>
560
+ </template>
483
561
  <div v-if="plugin.helmError" v-tooltip="t('plugins.helmError')" class="plugin-error">
484
562
  <i class="icon icon-warning" />
485
563
  </div>
486
564
 
487
565
  <div class="plugin-spacer" />
488
566
 
489
- <div v-if="plugin.installing">
490
- <!-- Don't show any buttons -->
567
+ <div v-if="plugin.installing" class="plugin-installing">
568
+ <i class="version-busy icon icon-spin icon-spinner" />
569
+ <div v-if="plugin.installing ==='install'">
570
+ {{ t('plugins.labels.installing') }}
571
+ </div>
572
+ <div v-else>
573
+ {{ t('plugins.labels.uninstalling') }}
574
+ </div>
491
575
  </div>
492
576
  <div v-else-if="plugin.installed" class="plugin-buttons">
493
577
  <button v-if="!plugin.builtin" class="btn role-secondary" @click="showUninstallDialog(plugin, $event)">
@@ -515,7 +599,7 @@ export default {
515
599
  <InstallDialog ref="installDialog" @closed="didInstall" @update="updatePluginInstallStatus" />
516
600
  <UninstallDialog ref="uninstallDialog" @closed="didUninstall" @update="updatePluginInstallStatus" />
517
601
  <DeveloperInstallDialog ref="developerInstallDialog" @closed="didInstall" />
518
- <RemoveUIPlugins ref="removeUIPlugins" />
602
+ <RemoveUIPlugins ref="removeUIPlugins" @done="updateInstallStatus" />
519
603
  </div>
520
604
  </template>
521
605
 
@@ -535,6 +619,30 @@ export default {
535
619
  }
536
620
  }
537
621
 
622
+ .plugin-reload-banner {
623
+ align-items: center;
624
+ background-color: var(--success-banner-bg);
625
+ display: flex;
626
+ padding: 0 10px;
627
+ border-radius: 5px;
628
+ min-height: 36px;
629
+
630
+ > i {
631
+ color: var(--success);
632
+ font-size: 20px;
633
+ font-weight: bold;
634
+ }
635
+
636
+ > button {
637
+ line-height: 30px;
638
+ min-height: 30px;
639
+ }
640
+ }
641
+
642
+ .plugin-complete {
643
+ font-size: 18px;
644
+ }
645
+
538
646
  .plugin-list {
539
647
  display: flex;
540
648
  flex-wrap: wrap;
@@ -560,6 +668,12 @@ export default {
560
668
  flex: 1;
561
669
  margin-bottom: 0;
562
670
  }
671
+
672
+ .btn.actions {
673
+ line-height: 36px;
674
+ min-height: 36px;
675
+ padding: 0 10px;
676
+ }
563
677
  }
564
678
 
565
679
  .plugin {
@@ -636,15 +750,15 @@ export default {
636
750
  height: 16px;
637
751
  width: 16px;
638
752
  }
753
+ }
639
754
 
640
- .plugin-installing {
641
- align-items: center;
642
- display: flex;
755
+ .plugin-installing {
756
+ align-items: center;
757
+ display: flex;
643
758
 
644
- > div {
645
- font-size: 14px;
646
- margin-left: 5px;
647
- }
759
+ > div {
760
+ font-size: 14px;
761
+ margin-left: 5px;
648
762
  }
649
763
  }
650
764