@rancher/shell 3.0.5-rc.8 → 3.0.5

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 (199) hide show
  1. package/assets/styles/base/_color.scss +4 -1
  2. package/assets/styles/global/_tooltip.scss +7 -4
  3. package/assets/styles/themes/_dark.scss +11 -0
  4. package/assets/styles/themes/_light.scss +13 -1
  5. package/assets/styles/themes/_modern.scss +22 -0
  6. package/assets/translations/en-us.yaml +147 -19
  7. package/assets/translations/zh-hans.yaml +0 -1
  8. package/chart/monitoring/grafana/index.vue +8 -2
  9. package/components/ActionMenuShell.vue +3 -1
  10. package/components/Cron/CronExpressionEditor.vue +299 -0
  11. package/components/Cron/CronExpressionEditorModal.vue +247 -0
  12. package/components/Cron/CronTooltip.vue +87 -0
  13. package/components/Cron/types.ts +13 -0
  14. package/components/ForceDirectedTreeChart/composable.ts +11 -0
  15. package/components/PodSecurityAdmission.vue +2 -0
  16. package/components/PromptModal.vue +1 -1
  17. package/components/Resource/Detail/Card/__tests__/StateCard.test.ts +1 -0
  18. package/components/Resource/Detail/CopyToClipboard.vue +78 -0
  19. package/components/Resource/Detail/FetchLoader/__tests__/composables.test.ts +69 -0
  20. package/components/Resource/Detail/FetchLoader/composables.ts +27 -0
  21. package/components/Resource/Detail/Metadata/Annotations/__tests__/index.test.ts +1 -1
  22. package/components/Resource/Detail/Metadata/Annotations/index.vue +1 -1
  23. package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/identifying-fields.test.ts +13 -61
  24. package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/index.test.ts +33 -6
  25. package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +24 -38
  26. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +25 -5
  27. package/components/Resource/Detail/Metadata/KeyValue.vue +12 -23
  28. package/components/Resource/Detail/Metadata/KeyValueRow.vue +144 -0
  29. package/components/Resource/Detail/Metadata/Labels/__tests__/index.test.ts +1 -0
  30. package/components/Resource/Detail/Metadata/Labels/index.vue +1 -0
  31. package/components/Resource/Detail/Metadata/__tests__/KeyValue.test.ts +30 -32
  32. package/components/Resource/Detail/Metadata/__tests__/KeyValueRow.test.ts +108 -0
  33. package/components/Resource/Detail/Metadata/__tests__/composables.test.ts +0 -3
  34. package/components/Resource/Detail/Metadata/__tests__/index.test.ts +12 -5
  35. package/components/Resource/Detail/Metadata/composables.ts +1 -4
  36. package/components/Resource/Detail/Metadata/index.vue +1 -0
  37. package/components/Resource/Detail/Preview/Content.vue +63 -0
  38. package/components/Resource/Detail/Preview/Preview.vue +128 -0
  39. package/components/Resource/Detail/Preview/__tests__/Content.spec.ts +71 -0
  40. package/components/Resource/Detail/Preview/__tests__/Preview.spec.ts +121 -0
  41. package/components/Resource/Detail/ResourcePopover/ResourcePopoverCard.vue +141 -0
  42. package/components/Resource/Detail/ResourcePopover/__tests__/ResourcePopoverCard.test.ts +136 -0
  43. package/components/Resource/Detail/ResourcePopover/__tests__/index.test.ts +245 -0
  44. package/components/Resource/Detail/ResourcePopover/index.vue +226 -0
  45. package/components/Resource/Detail/SpacedRow.vue +1 -0
  46. package/components/Resource/Detail/TitleBar/__tests__/composables.test.ts +0 -5
  47. package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +1 -1
  48. package/components/Resource/Detail/TitleBar/composables.ts +1 -3
  49. package/components/Resource/Detail/TitleBar/index.vue +2 -29
  50. package/components/Resource/Detail/ViewOptions/composable.ts +9 -0
  51. package/components/Resource/Detail/ViewOptions/index.vue +41 -0
  52. package/components/Resource/Detail/__tests__/CopyToClipboard.spec.ts +82 -0
  53. package/components/ResourceDetail/Masthead/legacy.vue +0 -19
  54. package/components/ResourceDetail/index.vue +1 -26
  55. package/components/ResourceTable.vue +24 -0
  56. package/components/SortableTable/index.vue +7 -1
  57. package/components/SortableTable/paging.js +3 -0
  58. package/components/Tabbed/Tab.vue +43 -1
  59. package/components/Tabbed/index.vue +3 -1
  60. package/components/__tests__/Cron/CronExpressionEditor.test.ts +151 -0
  61. package/components/__tests__/Cron/CronExpressionEditorModal.test.ts +81 -0
  62. package/components/auth/login/saml.vue +86 -0
  63. package/components/form/LabeledSelect.vue +8 -8
  64. package/components/form/ProjectMemberEditor.vue +2 -0
  65. package/components/form/ResourceTabs/composable.ts +54 -0
  66. package/components/form/ResourceTabs/index.vue +10 -7
  67. package/components/form/Select.vue +13 -10
  68. package/components/form/__tests__/LabeledSelect.test.ts +133 -0
  69. package/components/form/__tests__/Select.test.ts +134 -0
  70. package/components/nav/Header.vue +6 -5
  71. package/composables/useExtensionManager.ts +17 -0
  72. package/config/home-links.js +12 -0
  73. package/config/labels-annotations.js +0 -1
  74. package/config/page-actions.js +0 -1
  75. package/config/product/explorer.js +3 -1
  76. package/config/product/fleet.js +2 -7
  77. package/config/product/manager.js +0 -5
  78. package/config/query-params.js +1 -0
  79. package/config/router/navigation-guards/clusters.js +2 -1
  80. package/config/router/navigation-guards/products.js +1 -1
  81. package/config/store.js +2 -0
  82. package/core/extension-manager-impl.js +518 -0
  83. package/core/plugins.js +35 -468
  84. package/core/types.ts +8 -2
  85. package/detail/__tests__/autoscaling.horizontalpodautoscaler.test.ts +1 -0
  86. package/detail/catalog.cattle.io.app.vue +7 -4
  87. package/detail/fleet.cattle.io.bundle.vue +1 -5
  88. package/detail/fleet.cattle.io.cluster.vue +3 -2
  89. package/detail/fleet.cattle.io.gitrepo.vue +76 -49
  90. package/detail/fleet.cattle.io.helmop.vue +78 -49
  91. package/dialog/AddonConfigConfirmationDialog.vue +1 -1
  92. package/dialog/GenericPrompt.vue +1 -1
  93. package/dialog/ImportDialog.vue +9 -2
  94. package/dialog/InstallExtensionDialog.vue +18 -10
  95. package/dialog/SloDialog.vue +1 -1
  96. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +2 -1
  97. package/edit/__tests__/resources.cattle.io.restore.test.ts +106 -0
  98. package/edit/auth/oidc.vue +106 -6
  99. package/edit/auth/saml.vue +5 -5
  100. package/edit/cloudcredential.vue +31 -17
  101. package/edit/constraints.gatekeeper.sh.constraint/index.vue +10 -2
  102. package/edit/fleet.cattle.io.cluster.vue +19 -0
  103. package/edit/fleet.cattle.io.gitrepo.vue +23 -16
  104. package/edit/monitoring.coreos.com.alertmanagerconfig/index.vue +12 -11
  105. package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +11 -1
  106. package/edit/provisioning.cattle.io.cluster/index.vue +14 -19
  107. package/edit/provisioning.cattle.io.cluster/rke2.vue +11 -3
  108. package/edit/provisioning.cattle.io.cluster/tabs/AddOnAdditionalManifest.vue +1 -0
  109. package/edit/provisioning.cattle.io.cluster/tabs/AddOnConfig.vue +1 -0
  110. package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +1 -0
  111. package/edit/provisioning.cattle.io.cluster/tabs/etcd/S3Config.vue +1 -0
  112. package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +2 -0
  113. package/edit/provisioning.cattle.io.cluster/tabs/upgrade/DrainOptions.vue +6 -0
  114. package/edit/resources.cattle.io.restore.vue +5 -8
  115. package/initialize/install-plugins.js +1 -3
  116. package/list/__tests__/workload.test.ts +1 -0
  117. package/list/workload.vue +8 -1
  118. package/machine-config/components/GCEImage.vue +6 -5
  119. package/machine-config/google.vue +11 -6
  120. package/mixins/__tests__/auth-config.test.ts +4 -6
  121. package/mixins/__tests__/chart.test.ts +139 -1
  122. package/mixins/auth-config.js +33 -10
  123. package/mixins/chart.js +58 -18
  124. package/models/__tests__/namespace.test.ts +69 -0
  125. package/models/apps.statefulset.js +8 -10
  126. package/models/chart.js +5 -1
  127. package/models/fleet-application.js +16 -46
  128. package/models/fleet.cattle.io.bundle.js +1 -38
  129. package/models/fleet.cattle.io.gitrepo.js +4 -0
  130. package/models/fleet.cattle.io.helmop.js +4 -0
  131. package/models/management.cattle.io.cluster.js +1 -1
  132. package/models/management.cattle.io.project.js +12 -0
  133. package/models/namespace.js +30 -0
  134. package/models/workload.js +4 -1
  135. package/package.json +10 -10
  136. package/pages/auth/login.vue +8 -3
  137. package/pages/auth/logout.vue +6 -5
  138. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +26 -11
  139. package/pages/c/_cluster/apps/charts/chart.vue +29 -20
  140. package/pages/c/_cluster/apps/charts/index.vue +1 -0
  141. package/pages/c/_cluster/apps/charts/install.vue +6 -5
  142. package/pages/c/_cluster/explorer/tools/__tests__/index.test.ts +102 -12
  143. package/pages/c/_cluster/explorer/tools/index.vue +145 -254
  144. package/pages/c/_cluster/manager/cloudCredential/index.vue +18 -1
  145. package/pages/c/_cluster/manager/drivers/kontainerDriver/index.vue +12 -2
  146. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +1 -1
  147. package/pages/c/_cluster/uiplugins/__tests__/index.spec.ts +318 -0
  148. package/pages/c/_cluster/uiplugins/index.vue +221 -363
  149. package/pages/home.vue +1 -9
  150. package/plugins/axios.js +3 -2
  151. package/plugins/dashboard-store/resource-class.js +49 -0
  152. package/plugins/ember-cookie.js +7 -3
  153. package/plugins/steve/subscribe.js +4 -2
  154. package/public/index.html +2 -1
  155. package/rancher-components/Card/Card.vue +1 -1
  156. package/rancher-components/Form/Checkbox/Checkbox.vue +1 -1
  157. package/rancher-components/Form/Radio/RadioButton.vue +1 -1
  158. package/rancher-components/Form/Radio/RadioGroup.vue +1 -1
  159. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +1 -11
  160. package/rancher-components/Pill/RcCounterBadge/RcCounterBadge.test.ts +53 -0
  161. package/rancher-components/Pill/RcCounterBadge/RcCounterBadge.vue +65 -0
  162. package/rancher-components/Pill/RcCounterBadge/index.ts +1 -0
  163. package/rancher-components/Pill/RcCounterBadge/types.ts +7 -0
  164. package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.vue +1 -1
  165. package/rancher-components/Pill/RcStatusBadge/index.ts +1 -1
  166. package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.vue +3 -3
  167. package/rancher-components/Pill/RcStatusIndicator/types.ts +1 -1
  168. package/rancher-components/Pill/RcTag/RcTag.test.ts +64 -0
  169. package/rancher-components/Pill/RcTag/RcTag.vue +94 -0
  170. package/rancher-components/Pill/RcTag/index.ts +1 -0
  171. package/rancher-components/Pill/RcTag/types.ts +9 -0
  172. package/rancher-components/Pill/types.ts +1 -0
  173. package/rancher-components/RcItemCard/RcItemCard.vue +1 -0
  174. package/rancher-components/RcItemCard/RcItemCardAction.vue +12 -0
  175. package/scripts/test-plugins-build.sh +0 -1
  176. package/store/__tests__/catalog.test.ts +63 -0
  177. package/store/__tests__/cookies.test.ts +72 -0
  178. package/store/auth.js +33 -10
  179. package/store/catalog.js +2 -2
  180. package/store/cookies.ts +30 -0
  181. package/store/prefs.js +10 -5
  182. package/store/type-map.js +3 -15
  183. package/types/extension-manager.ts +26 -0
  184. package/types/shell/index.d.ts +123 -27
  185. package/utils/__tests__/product.test.ts +129 -0
  186. package/utils/__tests__/resource.test.ts +87 -0
  187. package/utils/alertmanagerconfig.js +2 -2
  188. package/utils/auth.js +4 -77
  189. package/utils/product.ts +39 -0
  190. package/utils/resource.ts +35 -0
  191. package/utils/select.js +0 -24
  192. package/utils/validators/formRules/__tests__/index.test.ts +3 -0
  193. package/utils/validators/formRules/index.ts +2 -1
  194. package/vue.config.js +1 -1
  195. package/components/Resource/Detail/Metadata/Rectangle.vue +0 -34
  196. package/components/Resource/Detail/Metadata/__tests__/Rectangle.test.ts +0 -24
  197. package/components/ResourceDetail/Masthead/__tests__/legacy.test.ts +0 -65
  198. package/utils/cookie-universal.js +0 -10
  199. /package/components/{ForceDirectedTreeChart.vue → ForceDirectedTreeChart/index.vue} +0 -0
@@ -13,7 +13,6 @@ import ActionMenu from '@shell/components/ActionMenuShell';
13
13
  import Tabbed from '@shell/components/Tabbed/index.vue';
14
14
  import Tab from '@shell/components/Tabbed/Tab.vue';
15
15
  import IconMessage from '@shell/components/IconMessage.vue';
16
- import LazyImage from '@shell/components/LazyImage';
17
16
  import { BadgeState } from '@components/BadgeState';
18
17
  import PluginInfoPanel from './PluginInfoPanel.vue';
19
18
  import SetupUIPlugins from './SetupUIPlugins.vue';
@@ -31,6 +30,9 @@ import {
31
30
  EXTENSIONS_INCOMPATIBILITY_TYPES
32
31
  } from '@shell/config/uiplugins';
33
32
  import TabTitle from '@shell/components/TabTitle';
33
+ import { RcItemCard } from '@components/RcItemCard';
34
+ import AppChartCardSubHeader from '@shell/pages/c/_cluster/apps/charts/AppChartCardSubHeader';
35
+ import AppChartCardFooter from '@shell/pages/c/_cluster/apps/charts/AppChartCardFooter.vue';
34
36
  import versions from '@shell/utils/versions';
35
37
  import { getPluginChartVersionLabel } from '@shell/utils/uiplugins';
36
38
 
@@ -51,12 +53,14 @@ export default {
51
53
  IconMessage,
52
54
  CatalogList,
53
55
  Banner,
54
- LazyImage,
55
56
  PluginInfoPanel,
56
57
  Tab,
57
58
  Tabbed,
58
59
  SetupUIPlugins,
59
- TabTitle
60
+ TabTitle,
61
+ RcItemCard,
62
+ AppChartCardSubHeader,
63
+ AppChartCardFooter
60
64
  },
61
65
 
62
66
  data() {
@@ -232,6 +236,25 @@ export default {
232
236
  return this.available.filter((plugin) => !!plugin.upgrade);
233
237
  },
234
238
 
239
+ pluginCards() {
240
+ return this.list.map((plugin) => ({
241
+ id: plugin.id,
242
+ header: {
243
+ title: { text: plugin.label },
244
+ statuses: this.getStatuses(plugin),
245
+ },
246
+ image: {
247
+ src: plugin.icon || this.defaultIcon,
248
+ alt: { text: this.t('plugins.altIcon', { extension: plugin.name }) },
249
+ },
250
+ content: { text: plugin.description },
251
+ actions: this.getPluginActions(plugin),
252
+ subHeaderItems: this.getSubHeaderItems(plugin),
253
+ footerItems: this.getFooterItems(plugin),
254
+ plugin
255
+ }));
256
+ },
257
+
235
258
  available() {
236
259
  let all = this.charts.filter((c) => isUIPlugin(c));
237
260
 
@@ -450,10 +473,14 @@ export default {
450
473
  this.errors[plugin.name] = error;
451
474
 
452
475
  if (active) {
453
- // Can use the status directly, apart from upgrade, which maps to install
454
- const status = op.status.action === 'upgrade' ? 'install' : op.status.action;
455
-
456
- this.updatePluginInstallStatus(plugin.name, status);
476
+ // Can use the status directly, apart from upgrade, which maps to update
477
+ const status = op.status.action === 'upgrade' ? 'update' : op.status.action;
478
+
479
+ if (status === 'update' && this.installing[plugin.name] === 'rollback') {
480
+ // Helm op is an upgrade, but we initiated a rollback, so keep the 'rollback' status
481
+ } else {
482
+ this.updatePluginInstallStatus(plugin.name, status);
483
+ }
457
484
  } else if (op.status.action === 'uninstall') {
458
485
  // Uninstall has finished
459
486
  this.updatePluginInstallStatus(plugin.name, false);
@@ -596,7 +623,7 @@ export default {
596
623
  });
597
624
  },
598
625
 
599
- showInstallDialog(plugin, mode, ev) {
626
+ showInstallDialog(plugin, action, ev) {
600
627
  ev.target?.blur();
601
628
  ev.preventDefault();
602
629
  ev.stopPropagation();
@@ -604,11 +631,11 @@ export default {
604
631
  this.$store.dispatch('management/promptModal', {
605
632
  component: 'InstallExtensionDialog',
606
633
  testId: 'install-extension-modal',
607
- returnFocusSelector: `[data-testid="extension-card-${ mode }-btn-${ plugin?.name }"]`,
634
+ returnFocusSelector: `[data-testid="extension-card-${ action }-btn-${ plugin?.name }"]`,
608
635
  returnFocusFirstIterableNodeSelector: '#extensions-main-page',
609
636
  componentProps: {
610
637
  plugin,
611
- mode,
638
+ action,
612
639
  updateStatus: (pluginName, type) => {
613
640
  this.updatePluginInstallStatus(pluginName, type);
614
641
  },
@@ -709,16 +736,156 @@ export default {
709
736
  this.addExtensionReposBannerSetting.value = 'false';
710
737
  this.addExtensionReposBannerSetting.save();
711
738
  }
739
+ },
740
+
741
+ getPluginActions(plugin) {
742
+ const actions = [];
743
+
744
+ const canInstall = !plugin.installed && plugin.installableVersions?.length;
745
+ const canUninstall = plugin.installed && !plugin.builtin;
746
+ const canUpdate = plugin.installed && plugin.upgrade;
747
+ const canRollback = plugin.installed && !plugin.upgrade && plugin.installableVersions?.length > 1;
748
+
749
+ if (canUninstall) {
750
+ actions.push({
751
+ label: this.t('plugins.uninstall.label'),
752
+ action: 'uninstall',
753
+ icon: 'icon-delete',
754
+ });
755
+ }
756
+
757
+ if (canUpdate) {
758
+ actions.push({
759
+ label: this.t('plugins.update.label'),
760
+ action: 'update',
761
+ icon: 'icon-edit',
762
+ });
763
+ }
764
+
765
+ if (canRollback) {
766
+ actions.push({
767
+ label: this.t('plugins.rollback.label'),
768
+ action: 'rollback',
769
+ icon: 'icon-history',
770
+ });
771
+ }
772
+
773
+ if (canInstall) {
774
+ actions.push({
775
+ label: this.t('plugins.install.label'),
776
+ action: 'install',
777
+ icon: 'icon-plus',
778
+ enabled: true,
779
+ });
780
+ }
781
+
782
+ return actions;
783
+ },
784
+
785
+ getSubHeaderItems(plugin) {
786
+ const items = [{
787
+ icon: 'icon-version-alt',
788
+ iconTooltip: { key: 'tableHeaders.version' },
789
+ label: plugin.displayVersionLabel,
790
+ labelTooltip: plugin.upgrade ? this.t('plugins.upgradeAvailableTooltip', { version: plugin.upgrade }) : ''
791
+ }];
792
+
793
+ if (plugin.installing) {
794
+ let label;
795
+
796
+ switch (plugin.installing) {
797
+ case 'install':
798
+ label = this.t('plugins.labels.installing');
799
+ break;
800
+ case 'uninstall':
801
+ label = this.t('plugins.labels.uninstalling');
802
+ break;
803
+ case 'update':
804
+ label = this.t('plugins.labels.updating');
805
+ break;
806
+ case 'rollback':
807
+ label = this.t('plugins.labels.rollingBack');
808
+ break;
809
+ default:
810
+ label = this.t('plugins.labels.installing');
811
+ break;
812
+ }
813
+ items.push({
814
+ icon: 'icon-spinner icon-spin',
815
+ iconTooltip: { key: 'plugins.tooltips.installing' },
816
+ label,
817
+ });
818
+ }
819
+
820
+ return items;
821
+ },
822
+
823
+ getFooterItems(plugin) {
824
+ const labels = [];
825
+
826
+ if (plugin.builtin) {
827
+ labels.push(this.t('plugins.labels.builtin'));
828
+ }
829
+
830
+ if (!plugin.builtin && !plugin.certified) {
831
+ labels.push(this.t('plugins.labels.third-party'));
832
+ }
833
+
834
+ if (!plugin.builtin && plugin.experimental) {
835
+ labels.push(this.t('plugins.labels.experimental'));
836
+ }
837
+
838
+ return labels.length ? [{
839
+ icon: 'icon-tag-alt',
840
+ iconTooltip: { key: 'generic.tags' },
841
+ labels,
842
+ }] : [];
843
+ },
844
+
845
+ getStatuses(plugin) {
846
+ const statuses = [];
847
+
848
+ const errorTooltip = plugin.installedError || plugin.incompatibilityMessage || (plugin.helmError ? this.t('plugins.helmError') : null);
849
+ const isDeprecated = plugin?.chart?.deprecated;
850
+
851
+ if (isDeprecated || errorTooltip) {
852
+ let tooltip;
853
+
854
+ if (isDeprecated && errorTooltip) {
855
+ tooltip = { text: `${ this.t('generic.deprecated') }. ${ this.t('generic.error') }: ${ errorTooltip }` };
856
+ } else if (isDeprecated) {
857
+ tooltip = { key: 'generic.deprecated' };
858
+ } else { // errorTooltip is present
859
+ tooltip = { text: `${ this.t('generic.error') }: ${ errorTooltip }` };
860
+ }
861
+
862
+ statuses.push({
863
+ icon: 'icon-alert-alt',
864
+ color: 'error',
865
+ tooltip
866
+ });
867
+ }
868
+
869
+ if (plugin.upgrade) {
870
+ statuses.push({
871
+ icon: 'icon-upgrade-alt', color: 'info', tooltip: { key: 'generic.upgradeable' }
872
+ });
873
+ }
874
+
875
+ if (plugin.installed && !plugin.builtin && !plugin.installing) {
876
+ statuses.push({
877
+ icon: 'icon-confirmation-alt', color: 'success', tooltip: { key: 'generic.installed' }
878
+ });
879
+ }
880
+
881
+ return statuses;
712
882
  }
713
883
  }
714
884
  };
715
885
  </script>
716
886
 
717
887
  <template>
718
- <div
719
- id="extensions-main-page"
720
- class="plugins"
721
- >
888
+ <div id="extensions-main-page">
722
889
  <div class="plugin-header">
723
890
  <!-- catalog view header -->
724
891
  <template v-if="showCatalogList">
@@ -757,7 +924,7 @@ export default {
757
924
  <!-- extensions reload toast/notification -->
758
925
  <div
759
926
  v-if="reloadRequired"
760
- class="plugin-reload-banner mr-20"
927
+ class="plugin-reload-banner mmr-6"
761
928
  data-testid="extension-reload-banner"
762
929
  >
763
930
  <i class="icon icon-checkmark mr-10" />
@@ -823,7 +990,7 @@ export default {
823
990
  </template>
824
991
  <template v-else>
825
992
  <Banner
826
- v-if="showAddReposBanner"
993
+ v-if="!loading && showAddReposBanner"
827
994
  color="warning"
828
995
  class="add-repos-banner mb-20"
829
996
  data-testid="extensions-new-repos-banner"
@@ -888,7 +1055,7 @@ export default {
888
1055
  </div>
889
1056
  <div
890
1057
  v-else
891
- class="plugin-list"
1058
+ class="plugin-cards"
892
1059
  :class="{'v-margin': !list.length}"
893
1060
  >
894
1061
  <IconMessage
@@ -896,185 +1063,35 @@ export default {
896
1063
  :vertical="true"
897
1064
  :subtle="true"
898
1065
  icon="icon-extension"
1066
+ class="mmt-9"
899
1067
  :message="emptyMessage"
900
1068
  />
901
1069
  <template v-else>
902
- <!-- extension card! -->
903
- <div
904
- v-for="(plugin, i) in list"
905
- :id="`list-item-${i}`"
906
- :key="i"
907
- class="plugin"
908
- :data-testid="`extension-card-${plugin.name}`"
909
- role="button"
910
- tabindex="0"
911
- :aria-label="plugin.name || ''"
912
- @click="showPluginDetail(plugin)"
913
- @keyup.enter.space="showPluginDetail(plugin)"
1070
+ <rc-item-card
1071
+ v-for="card in pluginCards"
1072
+ :id="card.id"
1073
+ :key="card.id"
1074
+ :class="{ 'single-card': pluginCards.length === 1 }"
1075
+ :header="card.header"
1076
+ :image="card.image"
1077
+ :content="card.content"
1078
+ :actions="card.actions"
1079
+ :clickable="true"
1080
+ @card-click="showPluginDetail(card.plugin)"
1081
+ @uninstall="({event}) => showUninstallDialog(card.plugin, event)"
1082
+ @update="({event}) => showInstallDialog(card.plugin, 'update', event)"
1083
+ @rollback="({event}) => showInstallDialog(card.plugin, 'rollback', event)"
1084
+ @install="({event}) => showInstallDialog(card.plugin, 'install', event)"
914
1085
  >
915
- <!-- extension icon -->
916
- <div
917
- class="plugin-icon"
918
- :class="applyDarkModeBg"
919
- >
920
- <LazyImage
921
- v-if="plugin.icon"
922
- :initial-src="defaultIcon"
923
- :error-src="defaultIcon"
924
- :src="plugin.icon"
925
- class="icon plugin-icon-img"
926
- :alt="t('plugins.altIcon', { extension: plugin.name })"
1086
+ <template #item-card-sub-header>
1087
+ <AppChartCardSubHeader
1088
+ :items="card.subHeaderItems"
927
1089
  />
928
- <img
929
- v-else
930
- :src="defaultIcon"
931
- class="icon plugin-icon-img"
932
- :alt="t('plugins.altIcon', { extension: plugin.name })"
933
- >
934
- </div>
935
- <!-- extension card -->
936
- <div class="plugin-metadata">
937
- <!-- extension basic info -->
938
- <div class="plugin-name">
939
- {{ plugin.label }}
940
- </div>
941
- <div>{{ plugin.description }}</div>
942
- <!-- extension version info and error display -->
943
- <div class="plugin-version">
944
- <span
945
- v-if="plugin.installing"
946
- class="plugin-installing"
947
- >
948
- -
949
- </span>
950
- <span v-else>
951
- <span>{{ plugin.displayVersionLabel }}</span>
952
- <span
953
- v-if="plugin.upgrade"
954
- v-clean-tooltip="t('plugins.upgradeAvailable')"
955
- > -> {{ plugin.upgrade }}</span>
956
- <p
957
- v-if="plugin.installedError"
958
- class="install-error"
959
- >
960
- <i class="icon icon-warning icon-lg" />
961
- <span>{{ plugin.installedError }}</span>
962
- </p>
963
- <p
964
- v-else-if="plugin.incompatibilityMessage"
965
- class="incompatible"
966
- >{{ plugin.incompatibilityMessage }}</p>
967
- </span>
968
- </div>
969
- <!-- extension badges -->
970
- <div
971
- v-if="plugin.builtin"
972
- class="plugin-badges"
973
- >
974
- <div class="plugin-builtin">
975
- {{ t('plugins.labels.builtin') }}
976
- </div>
977
- </div>
978
- <div
979
- v-else
980
- class="plugin-badges"
981
- >
982
- <div
983
- v-if="!plugin.certified"
984
- v-clean-tooltip="t('plugins.descriptions.third-party')"
985
- >
986
- {{ t('plugins.labels.third-party') }}
987
- </div>
988
- <div
989
- v-if="plugin.experimental"
990
- v-clean-tooltip="t('plugins.descriptions.experimental')"
991
- >
992
- {{ t('plugins.labels.experimental') }}
993
- </div>
994
- </div>
995
- <div class="plugin-spacer" />
996
- <!-- extension actions -->
997
- <div class="plugin-actions">
998
- <!-- extension status -->
999
- <div
1000
- v-if="plugin.helmError"
1001
- v-clean-tooltip="t('plugins.helmError')"
1002
- class="plugin-error"
1003
- >
1004
- <i class="icon icon-warning" />
1005
- </div>
1006
-
1007
- <div class="plugin-spacer" />
1008
-
1009
- <div
1010
- v-if="plugin.installing"
1011
- class="plugin-installing"
1012
- >
1013
- <i class="version-busy icon icon-spin icon-spinner" />
1014
- <div v-if="plugin.installing ==='install'">
1015
- {{ t('plugins.labels.installing') }}
1016
- </div>
1017
- <div v-else>
1018
- {{ t('plugins.labels.uninstalling') }}
1019
- </div>
1020
- </div>
1021
- <!-- extension buttons -->
1022
- <div
1023
- v-else-if="plugin.installed"
1024
- class="plugin-buttons"
1025
- >
1026
- <button
1027
- v-if="!plugin.builtin"
1028
- class="btn role-secondary"
1029
- :data-testid="`extension-card-uninstall-btn-${plugin.name}`"
1030
- role="button"
1031
- :aria-label="t('plugins.uninstall.label')"
1032
- @click.stop="showUninstallDialog(plugin, $event)"
1033
- @keyup.space.stop="showUninstallDialog(plugin, $event)"
1034
- >
1035
- {{ t('plugins.uninstall.label') }}
1036
- </button>
1037
- <button
1038
- v-if="plugin.upgrade"
1039
- class="btn role-secondary"
1040
- :data-testid="`extension-card-update-btn-${plugin.name}`"
1041
- role="button"
1042
- :aria-label="t('plugins.update.label')"
1043
- @click.stop="showInstallDialog(plugin, 'update', $event)"
1044
- @keyup.space.stop="showInstallDialog(plugin, 'update', $event)"
1045
- >
1046
- {{ t('plugins.update.label') }}
1047
- </button>
1048
- <button
1049
- v-if="!plugin.upgrade && plugin.installableVersions && plugin.installableVersions.length > 1"
1050
- class="btn role-secondary"
1051
- :data-testid="`extension-card-rollback-btn-${plugin.name}`"
1052
- role="button"
1053
- :aria-label="t('plugins.rollback.label')"
1054
- @click.stop="showInstallDialog(plugin, 'rollback', $event)"
1055
- @keyup.space.stop="showInstallDialog(plugin, 'rollback', $event)"
1056
- >
1057
- {{ t('plugins.rollback.label') }}
1058
- </button>
1059
- </div>
1060
- <div
1061
- v-else-if="plugin.installableVersions && plugin.installableVersions.length"
1062
- class="plugin-buttons"
1063
- >
1064
- <button
1065
- class="btn role-secondary"
1066
- :data-testid="`extension-card-install-btn-${plugin.name}`"
1067
- role="button"
1068
- :aria-label="t('plugins.install.label')"
1069
- @click.stop="showInstallDialog(plugin, 'install', $event)"
1070
- @keyup.space.stop="showInstallDialog(plugin, 'install', $event)"
1071
- >
1072
- {{ t('plugins.install.label') }}
1073
- </button>
1074
- </div>
1075
- </div>
1076
- </div>
1077
- </div>
1090
+ </template>
1091
+ <template #item-card-footer>
1092
+ <AppChartCardFooter :items="card.footerItems" />
1093
+ </template>
1094
+ </rc-item-card>
1078
1095
  </template>
1079
1096
  </div>
1080
1097
  </template>
@@ -1083,7 +1100,6 @@ export default {
1083
1100
  </template>
1084
1101
 
1085
1102
  <style lang="scss" scoped>
1086
-
1087
1103
  .setup-message {
1088
1104
  margin-top: 100px;
1089
1105
  }
@@ -1102,7 +1118,7 @@ export default {
1102
1118
  align-items: center;
1103
1119
  background-color: var(--success-banner-bg);
1104
1120
  display: flex;
1105
- padding: 0 10px;
1121
+ padding: 4px 4px 4px 12px;
1106
1122
  border-radius: 5px;
1107
1123
  min-height: 36px;
1108
1124
 
@@ -1122,22 +1138,6 @@ export default {
1122
1138
  font-size: 18px;
1123
1139
  }
1124
1140
 
1125
- .plugin-list {
1126
- display: flex;
1127
- flex-wrap: wrap;
1128
-
1129
- > .plugin:not(:last-child) {
1130
- margin-right: 20px;
1131
- }
1132
-
1133
- &.v-margin {
1134
- margin-top: 40px;
1135
- }
1136
- }
1137
- .plugins {
1138
- display: inherit;
1139
- }
1140
-
1141
1141
  .plugin-header {
1142
1142
  display: flex;
1143
1143
  align-items: center;
@@ -1162,6 +1162,7 @@ export default {
1162
1162
  .actions-container {
1163
1163
  display: flex;
1164
1164
  flex-direction: row;
1165
+ align-items: center;
1165
1166
  justify-content: flex-end;
1166
1167
  }
1167
1168
 
@@ -1172,146 +1173,6 @@ export default {
1172
1173
  }
1173
1174
  }
1174
1175
 
1175
- .plugin {
1176
- display: flex;
1177
- border: 1px solid var(--border);
1178
- padding: 10px;
1179
- width: calc(33% - 20px);
1180
- max-width: 540px;
1181
- margin-bottom: 20px;
1182
- cursor: pointer;
1183
-
1184
- .plugin-icon {
1185
- font-size: 40px;
1186
- margin-right:10px;
1187
- color: #888;
1188
- width: 44px;
1189
- height: 44px;
1190
- display: flex;
1191
- justify-content: center;
1192
- align-items: center;
1193
-
1194
- &.dark-mode {
1195
- border-radius: calc(2 * var(--border-radius));
1196
- overflow: hidden;
1197
- background-color: white;
1198
- }
1199
-
1200
- .plugin-icon-img {
1201
- height: 40px;
1202
- width: 40px;
1203
- -o-object-fit: contain;
1204
- object-fit: contain;
1205
- top: 2px;
1206
- left: 2px;
1207
- }
1208
- }
1209
-
1210
- .plugin-spacer {
1211
- flex: 1;
1212
- }
1213
-
1214
- .plugin-metadata {
1215
- display: flex;
1216
- flex: 1;
1217
- flex-direction: column;
1218
-
1219
- .plugin-buttons {
1220
- > button:not(:first-child) {
1221
- margin-left: 5px;
1222
- }
1223
- }
1224
- }
1225
-
1226
- .plugin-builtin {
1227
- color: var(--primary);
1228
- display: block;
1229
- padding: 2px 0;
1230
- text-transform: uppercase;
1231
- }
1232
-
1233
- .plugin-name {
1234
- font-size: 16px;
1235
- font-weight: bold;
1236
- margin-bottom: 5px;
1237
- text-transform: capitalize;
1238
- }
1239
-
1240
- .plugin-badges {
1241
- display: flex;
1242
-
1243
- > div {
1244
- border: 1px solid var(--border);
1245
- border-radius: 4px;
1246
- padding: 2px 8px;
1247
- margin-right: 10px;
1248
- font-size: 12px;
1249
- }
1250
- }
1251
-
1252
- .plugin-version {
1253
- align-items: center;
1254
- display: inline-flex;
1255
- font-size: 12px;
1256
- border-radius: 4px;
1257
- margin: 5px 0;
1258
-
1259
- i.icon-spinner {
1260
- padding-right: 5px;
1261
- font-size: 16px;
1262
- height: 16px;
1263
- width: 16px;
1264
- }
1265
-
1266
- .install-error {
1267
- margin: 10px 10px 5px 0;
1268
- font-weight: bold;
1269
- $error-icon-size: 22px;
1270
-
1271
- > i {
1272
- color: var(--error);
1273
- height: $error-icon-size;
1274
- font-size: $error-icon-size;
1275
- width: $error-icon-size;
1276
- }
1277
- }
1278
- }
1279
-
1280
- .plugin-installing {
1281
- align-items: center;
1282
- display: flex;
1283
-
1284
- > div {
1285
- font-size: 14px;
1286
- margin-left: 5px;
1287
- }
1288
- }
1289
-
1290
- .plugin-actions {
1291
- align-items:center;
1292
- display: flex;
1293
-
1294
- $error-icon-size: 22px;
1295
-
1296
- .plugin-error {
1297
- display: inline;
1298
- cursor: help;
1299
-
1300
- > i {
1301
- color: var(--error);
1302
- height: $error-icon-size;
1303
- font-size: $error-icon-size;
1304
- width: $error-icon-size;
1305
- }
1306
- }
1307
-
1308
- .btn {
1309
- line-height: 20px;
1310
- min-height: 20px;
1311
- padding: 0 5px;
1312
- }
1313
- }
1314
- }
1315
1176
  :deep() .checkbox-label {
1316
1177
  font-weight: normal !important;
1317
1178
  }
@@ -1322,20 +1183,17 @@ export default {
1322
1183
  align-items: center;
1323
1184
  }
1324
1185
 
1325
- @media screen and (max-width: 1200px) {
1326
- .plugin-list {
1327
- .plugin {
1328
- width: calc(50% - 20px);
1329
- }
1330
- }
1331
- }
1332
-
1333
- @media screen and (max-width: 960px) {
1334
- .plugin-list {
1335
- .plugin {
1336
- margin-right: 0 !important;
1337
- width: 100%;
1338
- }
1186
+ .plugin-cards {
1187
+ display: grid;
1188
+ grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
1189
+ grid-gap: var(--gap-md);
1190
+ width: 100%;
1191
+ height: max-content;
1192
+ overflow: hidden;
1193
+ padding-bottom: 48px;
1194
+
1195
+ .single-card {
1196
+ max-width: 500px;
1339
1197
  }
1340
1198
  }
1341
1199