@rancher/shell 3.0.5-rc.6 → 3.0.5-rc.8

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 (243) hide show
  1. package/assets/brand/classic/metadata.json +3 -0
  2. package/assets/styles/app.scss +1 -0
  3. package/assets/styles/base/_color.scss +16 -0
  4. package/assets/styles/base/_helpers.scss +10 -0
  5. package/assets/styles/base/_variables.scss +18 -12
  6. package/assets/styles/fonts/_icons.scss +1 -32
  7. package/assets/styles/global/_layout.scss +1 -1
  8. package/assets/styles/themes/_dark.scss +262 -258
  9. package/assets/styles/themes/_light.scss +538 -509
  10. package/assets/styles/themes/_modern.scss +914 -0
  11. package/assets/translations/en-us.yaml +110 -29
  12. package/chart/__tests__/S3.test.ts +2 -1
  13. package/cloud-credential/generic.vue +18 -10
  14. package/cloud-credential/harvester.vue +1 -9
  15. package/components/AdvancedSection.vue +8 -0
  16. package/components/ChartReadme.vue +17 -7
  17. package/components/CodeMirror.vue +1 -1
  18. package/components/Drawer/Chrome.vue +0 -1
  19. package/components/Drawer/ResourceDetailDrawer/__tests__/composables.test.ts +27 -28
  20. package/components/Drawer/ResourceDetailDrawer/composables.ts +4 -24
  21. package/components/Drawer/ResourceDetailDrawer/index.vue +18 -4
  22. package/components/InstallHelmCharts.vue +656 -0
  23. package/components/LazyImage.vue +60 -4
  24. package/components/Loading.vue +1 -1
  25. package/components/LocaleSelector.vue +7 -2
  26. package/components/Markdown.vue +4 -0
  27. package/components/PaginatedResourceTable.vue +46 -1
  28. package/components/PromptRestore.vue +22 -44
  29. package/components/Resource/Detail/Masthead/composable.ts +16 -0
  30. package/components/Resource/Detail/Masthead/index.vue +37 -0
  31. package/components/Resource/Detail/Metadata/IdentifyingInformation/composable.ts +10 -2
  32. package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +26 -7
  33. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +8 -1
  34. package/components/Resource/Detail/Metadata/KeyValue.vue +12 -10
  35. package/components/Resource/Detail/Metadata/Rectangle.vue +3 -1
  36. package/components/Resource/Detail/Metadata/__tests__/composables.test.ts +10 -17
  37. package/components/Resource/Detail/Metadata/composables.ts +9 -7
  38. package/components/Resource/Detail/Metadata/index.vue +17 -2
  39. package/components/Resource/Detail/Page.vue +35 -21
  40. package/components/Resource/Detail/SpacedRow.vue +1 -1
  41. package/components/Resource/Detail/TitleBar/__tests__/composables.test.ts +8 -9
  42. package/components/Resource/Detail/TitleBar/composables.ts +5 -5
  43. package/components/Resource/Detail/TitleBar/index.vue +12 -3
  44. package/components/ResourceDetail/Masthead/legacy.vue +1 -1
  45. package/components/ResourceDetail/index.vue +569 -72
  46. package/components/ResourceList/index.vue +1 -0
  47. package/components/ResourceTable.vue +6 -1
  48. package/components/ResourceYaml.vue +1 -1
  49. package/components/RichTranslation.vue +106 -0
  50. package/components/SlideInPanelManager.vue +13 -10
  51. package/components/SortableTable/index.vue +5 -5
  52. package/components/SortableTable/selection.js +0 -1
  53. package/components/Tabbed/index.vue +35 -4
  54. package/components/__tests__/LazyImage.spec.ts +121 -0
  55. package/components/__tests__/PromptRestore.test.ts +1 -65
  56. package/components/__tests__/RichTranslation.test.ts +115 -0
  57. package/components/fleet/FleetStatus.vue +4 -0
  58. package/components/fleet/dashboard/ResourcePanel.vue +2 -1
  59. package/components/form/ClusterAppearance.vue +5 -0
  60. package/components/form/FileImageSelector.vue +1 -1
  61. package/components/form/Members/ClusterPermissionsEditor.vue +1 -1
  62. package/components/form/NameNsDescription.vue +1 -0
  63. package/components/form/Networking.vue +24 -19
  64. package/components/form/ProjectMemberEditor.vue +1 -1
  65. package/components/form/ResourceLabeledSelect.vue +22 -8
  66. package/components/form/ResourceTabs/index.vue +20 -0
  67. package/components/form/SecretSelector.vue +9 -0
  68. package/components/form/SelectOrCreateAuthSecret.vue +6 -3
  69. package/components/form/__tests__/Networking.test.ts +116 -0
  70. package/components/form/labeled-select-utils/labeled-select-pagination.ts +3 -38
  71. package/components/formatter/FleetApplicationSource.vue +25 -17
  72. package/components/formatter/PodImages.vue +1 -1
  73. package/components/formatter/__tests__/LiveDate.test.ts +10 -2
  74. package/components/google/AccountAccess.vue +44 -46
  75. package/components/nav/Favorite.vue +4 -0
  76. package/components/nav/Group.vue +4 -1
  77. package/components/nav/NotificationCenter/Notification.vue +1 -27
  78. package/components/nav/WindowManager/index.vue +3 -3
  79. package/composables/resources.ts +2 -2
  80. package/config/labels-annotations.js +3 -2
  81. package/config/pagination-table-headers.js +8 -1
  82. package/config/product/explorer.js +27 -2
  83. package/config/product/manager.js +0 -1
  84. package/config/query-params.js +10 -0
  85. package/config/router/routes.js +21 -1
  86. package/config/system-namespaces.js +1 -1
  87. package/config/table-headers.js +30 -1
  88. package/config/types.js +1 -1
  89. package/config/version.js +1 -1
  90. package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +11 -0
  91. package/detail/__tests__/workload.test.ts +164 -0
  92. package/detail/configmap.vue +33 -75
  93. package/detail/projectsecret.vue +11 -0
  94. package/detail/provisioning.cattle.io.cluster.vue +351 -369
  95. package/detail/secret.vue +49 -308
  96. package/detail/workload/index.vue +38 -21
  97. package/dialog/InstallExtensionDialog.vue +8 -5
  98. package/dialog/RotateEncryptionKeyDialog.vue +10 -30
  99. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +224 -0
  100. package/edit/auth/ldap/__tests__/config.test.ts +14 -0
  101. package/edit/auth/ldap/config.vue +24 -0
  102. package/edit/compliance.cattle.io.clusterscan.vue +1 -1
  103. package/edit/configmap.vue +4 -1
  104. package/edit/fleet.cattle.io.gitrepo.vue +5 -6
  105. package/edit/fleet.cattle.io.helmop.vue +78 -56
  106. package/edit/logging.banzaicloud.io.output/index.vue +1 -1
  107. package/edit/logging.banzaicloud.io.output/providers/awsElasticsearch.vue +5 -6
  108. package/edit/networking.k8s.io.ingress/Certificate.vue +20 -22
  109. package/edit/networking.k8s.io.ingress/DefaultBackend.vue +8 -3
  110. package/edit/networking.k8s.io.ingress/Rule.vue +2 -5
  111. package/edit/networking.k8s.io.ingress/RulePath.vue +17 -11
  112. package/edit/networking.k8s.io.ingress/__tests__/Certificate.test.ts +165 -0
  113. package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +11 -10
  114. package/edit/networking.k8s.io.networkpolicy/PolicyRules.vue +1 -3
  115. package/edit/networking.k8s.io.networkpolicy/index.vue +17 -17
  116. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +3 -2
  117. package/edit/provisioning.cattle.io.cluster/rke2.vue +123 -61
  118. package/edit/provisioning.cattle.io.cluster/tabs/AgentConfiguration.vue +9 -7
  119. package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +22 -13
  120. package/edit/provisioning.cattle.io.cluster/tabs/DirectoryConfig.vue +10 -12
  121. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +39 -38
  122. package/edit/provisioning.cattle.io.cluster/tabs/etcd/S3Config.vue +41 -19
  123. package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +16 -3
  124. package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +32 -33
  125. package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryMirrors.vue +9 -10
  126. package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +1 -3
  127. package/edit/provisioning.cattle.io.cluster/tabs/upgrade/DrainOptions.vue +16 -9
  128. package/edit/secret/basic.vue +1 -0
  129. package/edit/secret/index.vue +126 -15
  130. package/edit/workload/index.vue +5 -14
  131. package/list/projectsecret.vue +345 -0
  132. package/list/provisioning.cattle.io.cluster.vue +1 -69
  133. package/list/secret.vue +109 -0
  134. package/machine-config/__tests__/vmwarevsphere.test.ts +5 -7
  135. package/machine-config/google.vue +9 -1
  136. package/machine-config/vmwarevsphere.vue +7 -17
  137. package/mixins/__tests__/brand.spec.ts +2 -2
  138. package/mixins/chart.js +0 -2
  139. package/mixins/create-edit-view/impl.js +10 -1
  140. package/mixins/resource-fetch-api-pagination.js +11 -12
  141. package/mixins/resource-fetch.js +3 -1
  142. package/models/__tests__/chart.test.ts +111 -80
  143. package/models/__tests__/fleet.cattle.io.helmop.test.ts +224 -0
  144. package/models/__tests__/node.test.ts +7 -63
  145. package/models/catalog.cattle.io.app.js +1 -1
  146. package/models/catalog.cattle.io.operation.js +1 -1
  147. package/models/chart.js +36 -20
  148. package/models/cloudcredential.js +2 -163
  149. package/models/cluster/node.js +7 -7
  150. package/models/cluster.x-k8s.io.machine.js +3 -3
  151. package/models/cluster.x-k8s.io.machinedeployment.js +11 -2
  152. package/models/compliance.cattle.io.clusterscan.js +2 -2
  153. package/models/configmap.js +4 -0
  154. package/models/constraints.gatekeeper.sh.constraint.js +1 -1
  155. package/models/fleet-application.js +0 -17
  156. package/models/fleet.cattle.io.cluster.js +2 -2
  157. package/models/fleet.cattle.io.gitrepo.js +15 -1
  158. package/models/fleet.cattle.io.helmop.js +26 -22
  159. package/models/management.cattle.io.setting.js +4 -0
  160. package/models/persistentvolumeclaim.js +1 -1
  161. package/models/pod.js +2 -2
  162. package/models/provisioning.cattle.io.cluster.js +39 -67
  163. package/models/rke.cattle.io.etcdsnapshot.js +1 -1
  164. package/models/secret.js +161 -2
  165. package/models/storage.k8s.io.storageclass.js +2 -2
  166. package/models/workload.js +3 -3
  167. package/package.json +11 -10
  168. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +1 -0
  169. package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +4 -1
  170. package/pages/c/_cluster/apps/charts/__tests__/AppChartCardFooter.spec.js +41 -0
  171. package/pages/c/_cluster/apps/charts/chart.vue +422 -174
  172. package/pages/c/_cluster/apps/charts/index.vue +46 -35
  173. package/pages/c/_cluster/apps/charts/install.vue +1 -1
  174. package/pages/c/_cluster/explorer/projectsecret.vue +24 -0
  175. package/pages/c/_cluster/fleet/__tests__/index.test.ts +608 -314
  176. package/pages/c/_cluster/fleet/index.vue +103 -45
  177. package/pages/c/_cluster/manager/cloudCredential/index.vue +2 -59
  178. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +10 -3
  179. package/pages/c/_cluster/uiplugins/index.vue +36 -25
  180. package/plugins/dashboard-store/__tests__/normalize.test.ts +223 -0
  181. package/plugins/dashboard-store/__tests__/resource-class.test.ts +191 -0
  182. package/plugins/dashboard-store/__tests__/utils/normalize-usecases.ts +1526 -0
  183. package/plugins/dashboard-store/actions.js +42 -22
  184. package/plugins/dashboard-store/normalize.js +29 -17
  185. package/plugins/dashboard-store/resource-class.js +83 -17
  186. package/plugins/steve/__tests__/getters.test.ts +1 -1
  187. package/plugins/steve/__tests__/subscribe.spec.ts +259 -1
  188. package/plugins/steve/getters.js +8 -2
  189. package/plugins/steve/resourceWatcher.js +10 -3
  190. package/plugins/steve/steve-pagination-utils.ts +14 -3
  191. package/plugins/steve/subscribe.js +192 -19
  192. package/plugins/steve/worker/web-worker.advanced.js +2 -0
  193. package/rancher-components/Card/Card.vue +0 -18
  194. package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.test.ts +15 -0
  195. package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.vue +65 -0
  196. package/rancher-components/Pill/RcStatusBadge/index.ts +2 -0
  197. package/rancher-components/Pill/RcStatusBadge/types.ts +5 -0
  198. package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.test.ts +33 -0
  199. package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.vue +75 -0
  200. package/rancher-components/Pill/RcStatusIndicator/index.ts +2 -0
  201. package/rancher-components/Pill/RcStatusIndicator/types.ts +7 -0
  202. package/rancher-components/Pill/types.ts +2 -0
  203. package/rancher-components/RcButton/RcButton.vue +1 -1
  204. package/rancher-components/RcDropdown/RcDropdown.test.ts +98 -0
  205. package/rancher-components/RcDropdown/RcDropdown.vue +5 -0
  206. package/rancher-components/RcDropdown/RcDropdownItem.vue +7 -1
  207. package/rancher-components/RcDropdown/RcDropdownItemCheckbox.vue +2 -1
  208. package/rancher-components/RcDropdown/RcDropdownItemSelect.vue +2 -1
  209. package/rancher-components/RcDropdown/useDropdownContext.ts +21 -0
  210. package/rancher-components/RcDropdown/useDropdownItem.ts +30 -1
  211. package/rancher-components/RcItemCard/RcItemCard.test.ts +20 -0
  212. package/rancher-components/RcItemCard/RcItemCard.vue +40 -6
  213. package/store/__tests__/catalog.test.ts +93 -1
  214. package/store/aws.js +19 -8
  215. package/store/catalog.js +8 -3
  216. package/types/kube/kube-api.ts +12 -0
  217. package/types/resources/settings.d.ts +1 -1
  218. package/types/shell/index.d.ts +643 -585
  219. package/types/store/pagination.types.ts +16 -6
  220. package/types/uiplugins.ts +73 -0
  221. package/utils/__tests__/back-off.test.ts +354 -0
  222. package/utils/__tests__/create-yaml.test.ts +235 -0
  223. package/utils/__tests__/kontainer.test.ts +19 -0
  224. package/utils/__tests__/uiplugins.test.ts +84 -0
  225. package/utils/back-off.ts +176 -0
  226. package/utils/create-yaml.js +103 -9
  227. package/utils/dynamic-importer.js +8 -0
  228. package/utils/kontainer.ts +3 -5
  229. package/utils/pagination-utils.ts +18 -0
  230. package/utils/style.ts +3 -0
  231. package/utils/uiplugins.ts +29 -2
  232. package/utils/validators/__tests__/setting.test.js +92 -0
  233. package/utils/validators/formRules/__tests__/index.test.ts +88 -7
  234. package/utils/validators/formRules/index.ts +83 -8
  235. package/utils/validators/setting.js +17 -0
  236. package/cloud-credential/__tests__/harvester.test.ts +0 -18
  237. package/components/ResourceDetail/__tests__/index.test.ts +0 -135
  238. package/components/ResourceDetail/legacy.vue +0 -562
  239. package/components/formatter/CloudCredExpired.vue +0 -69
  240. package/models/etcdbackup.js +0 -45
  241. package/pages/explorer/resource/detail/configmap.vue +0 -42
  242. package/pages/explorer/resource/detail/secret.vue +0 -50
  243. package/utils/aws.js +0 -0
@@ -1,9 +1,11 @@
1
1
  <script>
2
+ import { BLANK_IMAGE } from '@shell/utils/style';
3
+
2
4
  export default {
3
5
  props: {
4
6
  initialSrc: {
5
7
  type: String,
6
- default: require('@shell/assets/images/generic-catalog.svg'),
8
+ default: BLANK_IMAGE,
7
9
  },
8
10
 
9
11
  errorSrc: {
@@ -20,29 +22,83 @@ export default {
20
22
  watch: {
21
23
  src(neu, old) {
22
24
  if (neu !== old) {
23
- this.loadImage();
25
+ // Show error image if src is falsy
26
+ if (!neu) {
27
+ return this.onError();
28
+ }
29
+
30
+ if (this.intersected) {
31
+ // The component is in the viewport, load the new image right away
32
+ this.loadImage();
33
+ } else if (!this.observer) {
34
+ // The component is not in the viewport and not being observed,
35
+ // which can happen if `src` was null during mount.
36
+ // Start observing.
37
+ this.startObserver();
38
+ }
24
39
  }
25
40
  }
26
41
  },
27
42
 
43
+ created() {
44
+ // initialize non-reactive data
45
+ this.observer = null;
46
+ this.intersected = false;
47
+ this.boundError = null;
48
+ },
49
+
28
50
  mounted() {
29
- this.loadImage();
51
+ // Show error image if src is falsy
52
+ if (!this.src) {
53
+ this.onError();
54
+ } else {
55
+ this.startObserver();
56
+ }
30
57
  },
31
58
 
32
59
  beforeUnmount() {
33
60
  const img = this.$refs.img;
34
61
 
35
- if (img) {
62
+ if (img && this.boundError) {
36
63
  img.removeEventListener('error', this.boundError);
37
64
  }
65
+
66
+ if (this.observer) {
67
+ this.observer.disconnect();
68
+ this.observer = null;
69
+ }
38
70
  },
39
71
 
40
72
  methods: {
73
+ startObserver() {
74
+ if (this.src && !this.observer && this.$refs.img) {
75
+ this.observer = new IntersectionObserver((entries) => {
76
+ const image = entries[0];
77
+
78
+ if (image.isIntersecting) {
79
+ this.intersected = true;
80
+ this.loadImage();
81
+ // Once the image is loaded, we don't need the observer anymore
82
+ if (this.observer) {
83
+ this.observer.disconnect();
84
+ this.observer = null;
85
+ }
86
+ }
87
+ });
88
+ this.observer.observe(this.$refs.img);
89
+ }
90
+ },
91
+
41
92
  // Ensure we load the image when the source changes
42
93
  loadImage() {
43
94
  const img = this.$refs.img;
44
95
 
45
96
  if (this.src) {
97
+ // Remove previous error listener if any
98
+ if (img && this.boundError) {
99
+ img.removeEventListener('error', this.boundError);
100
+ }
101
+
46
102
  img.setAttribute('src', this.src);
47
103
  this.boundError = this.onError.bind(this);
48
104
 
@@ -59,7 +59,7 @@ export default {
59
59
  <style lang="scss" scoped>
60
60
  .overlay {
61
61
  align-items: center;
62
- background-color: var(--overlay-bg);
62
+ background-color: var(--subtle-overlay-bg);
63
63
  display: flex;
64
64
  justify-content: center;
65
65
  position: absolute;
@@ -80,7 +80,7 @@ export default {
80
80
  <rc-dropdown-trigger
81
81
  data-testid="locale-selector"
82
82
  link
83
- class="baseline"
83
+ class="baseline locale-selector-btn"
84
84
  :aria-label="t('locale.menu')"
85
85
  >
86
86
  {{ selectedLocaleLabel }}
@@ -88,7 +88,7 @@ export default {
88
88
  v-if="showIcon"
89
89
  #after
90
90
  >
91
- <i class="icon icon-fw icon-sort-down" />
91
+ <i class="ml-5 icon icon-chevron-down" />
92
92
  </template>
93
93
  </rc-dropdown-trigger>
94
94
  <template #dropdownCollection>
@@ -126,4 +126,9 @@ export default {
126
126
  .baseline {
127
127
  align-items: baseline;
128
128
  }
129
+
130
+ .locale-selector-btn {
131
+ align-items: center;
132
+ display: flex;
133
+ }
129
134
  </style>
@@ -133,6 +133,10 @@ export default {
133
133
  table tr th :last-child, table tr td :last-child {
134
134
  margin-bottom: 0;
135
135
  }
136
+
137
+ img {
138
+ max-width: 100%;
139
+ }
136
140
  }
137
141
 
138
142
  </style>
@@ -36,6 +36,35 @@ export default defineComponent({
36
36
  default: null,
37
37
  },
38
38
 
39
+ groupTooltip: {
40
+ type: String,
41
+ default: 'resourceTable.groupBy.namespace',
42
+ },
43
+
44
+ /**
45
+ * Field to group rows by, row[groupBy] must be something that can be a map key (or also use groupSort)
46
+ */
47
+ groupBy: {
48
+ type: String,
49
+ default: null,
50
+ },
51
+
52
+ /**
53
+ * Field to order groups by, defaults to groupBy
54
+ */
55
+ groupSort: {
56
+ type: String,
57
+ default: null
58
+ },
59
+
60
+ /**
61
+ * Override any product based group options
62
+ */
63
+ groupOptions: {
64
+ type: Array,
65
+ default: null
66
+ },
67
+
39
68
  groupable: {
40
69
  type: Boolean,
41
70
  default: null, // Null: auto based on namespaced and type custom groupings
@@ -58,6 +87,14 @@ export default defineComponent({
58
87
  default: null,
59
88
  },
60
89
 
90
+ /**
91
+ * Use this store instead of the store `inStore` getters
92
+ */
93
+ overrideInStore: {
94
+ type: String,
95
+ default: undefined,
96
+ },
97
+
61
98
  /**
62
99
  * Information may be required from resources other than the primary one shown per row
63
100
  *
@@ -79,7 +116,7 @@ export default defineComponent({
79
116
 
80
117
  async fetch() {
81
118
  const promises = [
82
- this.$fetchType(this.resource, [], this.inStore),
119
+ this.$fetchType(this.resource, [], this.overrideInStore || this.inStore),
83
120
  ];
84
121
 
85
122
  if (this.fetchSecondaryResources) {
@@ -115,13 +152,21 @@ export default defineComponent({
115
152
  :rows="rows"
116
153
  :alt-loading="canPaginate && !isFirstLoad"
117
154
  :loading="loading"
155
+
156
+ :group-by="groupBy"
157
+ :group-sort="groupSort"
118
158
  :groupable="groupable"
159
+ :groupTooltip="groupTooltip"
160
+ :groupOptions="groupOptions"
161
+
162
+ :override-in-store="overrideInStore"
119
163
 
120
164
  :headers="safeHeaders"
121
165
  :namespaced="namespaced"
122
166
 
123
167
  :external-pagination-enabled="canPaginate"
124
168
  :external-pagination-result="paginationResult"
169
+
125
170
  @pagination-changed="paginationChanged"
126
171
  >
127
172
  <!-- Pass down templates provided by the caller -->
@@ -7,7 +7,7 @@ import Date from '@shell/components/formatter/Date.vue';
7
7
  import RadioGroup from '@components/Form/Radio/RadioGroup.vue';
8
8
  import LabeledSelect from '@shell/components/form/LabeledSelect.vue';
9
9
  import { exceptionToErrorsArray } from '@shell/utils/error';
10
- import { CAPI, NORMAN, SNAPSHOT } from '@shell/config/types';
10
+ import { CAPI, SNAPSHOT } from '@shell/config/types';
11
11
  import { set } from '@shell/utils/object';
12
12
  import ChildHook, { BEFORE_SAVE_HOOKS } from '@shell/mixins/child-hook';
13
13
  import { DATE_FORMAT, TIME_FORMAT } from '@shell/store/prefs';
@@ -47,14 +47,13 @@ export default {
47
47
  },
48
48
 
49
49
  computed: {
50
- // toRestore can be a provisioning.cattle.io.cluster or a rke.cattle.io.etcdsnapshot or an etcdBackup resource
50
+ // toRestore can be a provisioning.cattle.io.cluster or a rke.cattle.io.etcdsnapshot resource
51
51
  ...mapState('action-menu', ['showPromptRestore', 'toRestore']),
52
52
  ...mapGetters({ t: 'i18n/t' }),
53
53
 
54
54
  // Was the dialog opened to restore a specific snapshot, or opened on a cluster to choose
55
55
  isCluster() {
56
- const isSnapshot = this.toRestore[0]?.type.toLowerCase() === NORMAN.ETCD_BACKUP ||
57
- this.toRestore[0]?.type.toLowerCase() === SNAPSHOT;
56
+ const isSnapshot = this.toRestore[0]?.type.toLowerCase() === SNAPSHOT;
58
57
 
59
58
  return !isSnapshot;
60
59
  },
@@ -79,9 +78,7 @@ export default {
79
78
  }
80
79
  },
81
80
  restoreModeOptions() {
82
- const etcdOption = this.isRke2 ? 'none' : 'etcd';
83
-
84
- return [etcdOption, 'kubernetesVersion', 'all'];
81
+ return ['none', 'kubernetesVersion', 'all'];
85
82
  }
86
83
  },
87
84
 
@@ -112,20 +109,12 @@ export default {
112
109
  }
113
110
 
114
111
  const cluster = this.toRestore?.[0];
115
- let promise;
116
-
117
- if (!cluster?.isRke2) {
118
- promise = this.$store.dispatch('rancher/findAll', { type: NORMAN.ETCD_BACKUP }).then((snapshots) => {
119
- return snapshots.filter((s) => s.state === STATES_ENUM.ACTIVE && s.clusterId === cluster.metadata.name);
120
- });
121
- } else {
122
- promise = this.$store.dispatch('management/findAll', { type: SNAPSHOT }).then((snapshots) => {
123
- const toRestoreClusterName = cluster?.clusterName || cluster?.metadata?.name;
112
+ const promise = this.$store.dispatch('management/findAll', { type: SNAPSHOT }).then((snapshots) => {
113
+ const toRestoreClusterName = cluster?.clusterName || cluster?.metadata?.name;
124
114
 
125
- return snapshots.filter((s) => s?.snapshotFile?.status === STATES_ENUM.SUCCESSFUL && s.clusterName === toRestoreClusterName
126
- );
127
- });
128
- }
115
+ return snapshots.filter((s) => s?.snapshotFile?.status === STATES_ENUM.SUCCESSFUL && s.clusterName === toRestoreClusterName
116
+ );
117
+ });
129
118
 
130
119
  // Map of snapshots by name
131
120
  const allSnapshots = await promise.then((snapshots) => {
@@ -154,30 +143,19 @@ export default {
154
143
 
155
144
  async apply(buttonDone) {
156
145
  try {
157
- if ( this.isRke2 ) {
158
- const cluster = this.$store.getters['management/byId'](CAPI.RANCHER_CLUSTER, this.snapshot.clusterId);
159
-
160
- await this.applyHooks(BEFORE_SAVE_HOOKS);
161
-
162
- const now = cluster.spec?.rkeConfig?.etcdSnapshotRestore?.generation || 0;
163
-
164
- set(cluster, 'spec.rkeConfig.etcdSnapshotRestore', {
165
- generation: now + 1,
166
- name: this.snapshot.name,
167
- restoreRKEConfig: this.restoreMode,
168
- });
169
-
170
- await cluster.save();
171
- } else {
172
- await this.$store.dispatch('rancher/request', {
173
- url: `/v3/clusters/${ escape(this.snapshot.clusterId) }?action=restoreFromEtcdBackup`,
174
- method: 'post',
175
- data: {
176
- etcdBackupId: this.snapshot.id,
177
- restoreRkeConfig: this.restoreMode,
178
- },
179
- });
180
- }
146
+ const cluster = this.$store.getters['management/byId'](CAPI.RANCHER_CLUSTER, this.snapshot.clusterId);
147
+
148
+ await this.applyHooks(BEFORE_SAVE_HOOKS);
149
+
150
+ const now = cluster.spec?.rkeConfig?.etcdSnapshotRestore?.generation || 0;
151
+
152
+ set(cluster, 'spec.rkeConfig.etcdSnapshotRestore', {
153
+ generation: now + 1,
154
+ name: this.snapshot.name,
155
+ restoreRKEConfig: this.restoreMode,
156
+ });
157
+
158
+ await cluster.save();
181
159
 
182
160
  this.$store.dispatch('growl/success', {
183
161
  title: this.t('promptRestore.notification.title'),
@@ -0,0 +1,16 @@
1
+ import { useDefaultMetadataForLegacyPagesProps } from '@shell/components/Resource/Detail/Metadata/composables';
2
+ import { useDefaultTitleBarProps } from '@shell/components/Resource/Detail/TitleBar/composables';
3
+ import { MastheadProps } from '@shell/components/Resource/Detail/Masthead/index.vue';
4
+ import { computed, Ref } from 'vue';
5
+
6
+ export const useDefaultMastheadProps = (resource: any): Ref<MastheadProps> => {
7
+ const titleBarProps = useDefaultTitleBarProps(resource);
8
+ const metadataProps = useDefaultMetadataForLegacyPagesProps(resource);
9
+
10
+ return computed(() => {
11
+ return {
12
+ titleBarProps: titleBarProps.value,
13
+ metadataProps: metadataProps.value,
14
+ };
15
+ });
16
+ };
@@ -0,0 +1,37 @@
1
+ <script lang="ts">
2
+ import TitleBar, { TitleBarProps } from '@shell/components/Resource/Detail/TitleBar/index.vue';
3
+ import Metadata, { MetadataProps } from '@shell/components/Resource/Detail/Metadata/index.vue';
4
+
5
+ export interface MastheadProps {
6
+ titleBarProps: TitleBarProps;
7
+ metadataProps: MetadataProps;
8
+ }
9
+ </script>
10
+
11
+ <script setup lang="ts">
12
+ const props = defineProps<MastheadProps>();
13
+
14
+ </script>
15
+ <template>
16
+ <div class="masthead">
17
+ <TitleBar v-bind="props.titleBarProps">
18
+ <template
19
+ v-if="$slots['additional-actions']"
20
+ #additional-actions
21
+ >
22
+ <slot name="additional-actions" />
23
+ </template>
24
+ </TitleBar>
25
+ <Metadata
26
+ v-bind="props.metadataProps"
27
+ />
28
+ </div>
29
+ </template>
30
+
31
+ <style lang='scss' scoped>
32
+ .masthead {
33
+ :deep().metadata {
34
+ margin-top: 24px;
35
+ }
36
+ }
37
+ </style>
@@ -3,7 +3,7 @@ import { Row } from '@shell/components/Resource/Detail/Metadata/IdentifyingInfor
3
3
  import {
4
4
  useCertificate,
5
5
  useExpires,
6
- useImage, useIssuer, useLiveDate, useNamespace, useReady, useSecretType,
6
+ useImage, useIssuer, useLiveDate, useNamespace, useProject, useReady, useSecretCluster, useSecretType,
7
7
  useServiceAccount
8
8
  } from '@shell/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields';
9
9
  import { useStore } from 'vuex';
@@ -21,7 +21,11 @@ export const useDefaultIdentifyingInformation = (resource: any): ComputedRef<Row
21
21
  });
22
22
  };
23
23
 
24
- export const useSecretIdentifyingInformation = (resource: any): ComputedRef<Row[]> => {
24
+ export const useSecretIdentifyingInformation = (resource: any, isProjectSecret: boolean): ComputedRef<Row[]> => {
25
+ const namespace = isProjectSecret ? undefined : useNamespace(resource);
26
+ const project = isProjectSecret ? useProject(resource) : undefined;
27
+ const cluster = isProjectSecret ? useSecretCluster(resource) : undefined;
28
+ const age = useLiveDate(resource);
25
29
  const secretType = useSecretType(resource);
26
30
  const serviceAccount = useServiceAccount(resource);
27
31
  const certificate = useCertificate(resource);
@@ -30,6 +34,10 @@ export const useSecretIdentifyingInformation = (resource: any): ComputedRef<Row[
30
34
 
31
35
  return computed(() => {
32
36
  const rows = [
37
+ age?.value,
38
+ namespace?.value,
39
+ project?.value,
40
+ cluster?.value,
33
41
  secretType?.value,
34
42
  serviceAccount?.value,
35
43
  certificate?.value,
@@ -2,7 +2,9 @@ import { useI18n } from '@shell/composables/useI18n';
2
2
  import { computed, ComputedRef, markRaw, toValue } from 'vue';
3
3
  import Additional from '@shell/components/Resource/Detail/Additional.vue';
4
4
  import { useStore } from 'vuex';
5
- import { NAMESPACE, FLEET, SERVICE_ACCOUNT } from '@shell/config/types';
5
+ import {
6
+ NAMESPACE, FLEET, SERVICE_ACCOUNT, SECRET, CAPI
7
+ } from '@shell/config/types';
6
8
  import { Row } from '@shell/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue';
7
9
  import { NAME as FLEET_NAME } from '@shell/config/product/fleet';
8
10
  import { useRoute } from 'vue-router';
@@ -14,7 +16,7 @@ export const useNamespace = (resource: any): ComputedRef<Row> | undefined => {
14
16
  const i18n = useI18n(store);
15
17
  const resourceValue = toValue(resource);
16
18
 
17
- if (!resourceValue.namespace || resourceValue.namespaces) {
19
+ if (!resourceValue.namespace || resourceValue.namespaces || resourceValue.isProjectScoped) {
18
20
  return;
19
21
  }
20
22
 
@@ -113,7 +115,12 @@ export const useProject = (resource: any): ComputedRef<Row> | undefined => {
113
115
  const i18n = useI18n(store);
114
116
  const resourceValue = toValue(resource);
115
117
 
116
- if (resource.type !== NAMESPACE || !resourceValue.project) {
118
+ // Only show project if one of these types
119
+ if (resource.type !== NAMESPACE && resource.type !== SECRET) {
120
+ return;
121
+ }
122
+
123
+ if (!resourceValue.project) {
117
124
  return;
118
125
  }
119
126
 
@@ -126,10 +133,22 @@ export const useProject = (resource: any): ComputedRef<Row> | undefined => {
126
133
  });
127
134
  };
128
135
 
136
+ export const useSecretCluster = (resource: any): ComputedRef<Row> | undefined => {
137
+ const store = useStore();
138
+ const resourceValue = toValue(resource);
139
+
140
+ return computed(() => {
141
+ return {
142
+ label: store.getters['type-map/labelFor']({ id: CAPI.RANCHER_CLUSTER }),
143
+ value: resourceValue.projectCluster?.nameDisplay,
144
+ };
145
+ });
146
+ };
147
+
129
148
  export const useResourceDetails = (resource: any): undefined | ComputedRef<Row[]> => {
130
- const details = resource.details;
149
+ const details = computed(() => resource.details);
131
150
 
132
- if (!details) {
151
+ if (!details.value) {
133
152
  return;
134
153
  }
135
154
 
@@ -148,8 +167,8 @@ export const useResourceDetails = (resource: any): undefined | ComputedRef<Row[]
148
167
  };
149
168
 
150
169
  return computed(() => {
151
- return details
152
- .filter((detail: any) => !detail.separator)
170
+ return details.value
171
+ .filter((detail: any) => !detail.separator && detail.content !== undefined && detail.content !== null)
153
172
  .map((detail: any) => {
154
173
  return {
155
174
  label: detail.label,
@@ -34,7 +34,7 @@ const getRowValueId = (row:Row): string => `value-${ row.label }:${ row.value }`
34
34
  :data-testid="row.dataTestid"
35
35
  >
36
36
  <label
37
- class="label text-muted"
37
+ class="label text-deemphasized"
38
38
  :for="getRowValueId(row)"
39
39
  >
40
40
  {{ row.label }}
@@ -95,6 +95,13 @@ const getRowValueId = (row:Row): string => `value-${ row.label }:${ row.value }`
95
95
  display: flex;
96
96
  flex-direction: row;
97
97
  align-items: center;
98
+
99
+ &, & * {
100
+ max-width: 100%;
101
+ overflow: hidden;
102
+ text-overflow: ellipsis;
103
+ white-space: nowrap;
104
+ }
98
105
  }
99
106
 
100
107
  .label {
@@ -54,12 +54,12 @@ const showConfigurationMoreFocusSelector = computed(() => `[data-testid="${ show
54
54
  <template>
55
55
  <div class="key-value">
56
56
  <div class="heading">
57
- <span class="title text-muted">{{ propertyName }}</span>
57
+ <span class="title text-deemphasized">{{ propertyName }}</span>
58
58
  <span class="count">{{ rows.length }}</span>
59
59
  </div>
60
60
  <div
61
61
  v-if="visibleRows.length === 0"
62
- class="empty mmt-2 text-muted"
62
+ class="empty mmt-2 text-deemphasized"
63
63
  >
64
64
  <div class="no-rows">
65
65
  {{ i18n.t('component.resource.detail.metadata.keyValue.noRows', {propertyName: lowercasePropertyName}) }}
@@ -67,7 +67,7 @@ const showConfigurationMoreFocusSelector = computed(() => `[data-testid="${ show
67
67
  <div class="show-configuration mmt-1">
68
68
  <a
69
69
  :data-testid="showConfigurationEmptyDataTestId"
70
- class="secondary text-muted"
70
+ class="secondary text-deemphasized"
71
71
  href="#"
72
72
  @click="(ev: MouseEvent) => {ev.preventDefault(); emit('show-configuration', showConfigurationEmptyFocusSelector);}"
73
73
  >
@@ -91,7 +91,7 @@ const showConfigurationMoreFocusSelector = computed(() => `[data-testid="${ show
91
91
  v-if="showShowAllButton"
92
92
  :data-testid="showConfigurationMoreDataTestId"
93
93
  href="#"
94
- class="show-all secondary"
94
+ class="show-all"
95
95
  @click="(ev: MouseEvent) => {ev.preventDefault(); emit('show-configuration', showConfigurationMoreFocusSelector);}"
96
96
  >
97
97
  {{ showAllLabel }}
@@ -110,29 +110,31 @@ const showConfigurationMoreFocusSelector = computed(() => `[data-testid="${ show
110
110
  }
111
111
 
112
112
  .heading {
113
- margin-bottom: 4px;
113
+ margin-bottom: 8px;
114
114
  }
115
115
 
116
116
  .row {
117
+ display: block;
117
118
  width: 100%;
118
119
 
119
- &:not(:first-of-type) {
120
+ &:not(:nth-child(2)) {
120
121
  margin-top: 4px;
121
122
  }
122
-
123
- & {
124
- margin-top: 8px;
125
- }
126
123
  }
127
124
  .show-all {
128
125
  margin-top: 8px;
129
126
  }
130
127
 
131
128
  .rectangle {
129
+ display: inline-block;
132
130
  max-width: 100%;
133
131
  overflow: hidden;
134
132
  text-overflow: ellipsis;
135
133
  white-space: nowrap;
136
134
  }
135
+
136
+ .no-rows {
137
+ line-height: 21px;
138
+ }
137
139
  }
138
140
  </style>
@@ -23,7 +23,9 @@ const props = withDefaults(
23
23
  .rectangle {
24
24
  border: 1px solid var(--tag-bg);
25
25
  border-radius: 4px;
26
- padding: 4px;
26
+ padding: 0 8px;
27
+ height: 23px;
28
+ line-height: 23px;
27
29
 
28
30
  &:not(.outline) {
29
31
  background-color: var(--tag-bg);
@@ -2,16 +2,7 @@ import { useDefaultMetadataForLegacyPagesProps } from '@shell/components/Resourc
2
2
  import * as IdentifyingFields from '@shell/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields';
3
3
  import { computed } from 'vue';
4
4
 
5
- const mockDrawer = { openResourceDetailDrawer: jest.fn() };
6
-
7
5
  jest.mock('@shell/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields');
8
- jest.mock('@shell/components/Drawer/ResourceDetailDrawer/composables', () => {
9
- return {
10
- useResourceDetailDrawer() {
11
- return mockDrawer;
12
- }
13
- };
14
- });
15
6
 
16
7
  describe('composables: Metadata/composables', () => {
17
8
  beforeEach(() => {
@@ -28,9 +19,10 @@ describe('composables: Metadata/composables', () => {
28
19
 
29
20
  it('should filter out undefined identifyingInformation', () => {
30
21
  const resource = {
31
- type: 'RESOURCE',
32
- annotations: { annotation: 'ANNOTATION' },
33
- labels: { label: 'LABEL' }
22
+ type: 'RESOURCE',
23
+ annotations: { annotation: 'ANNOTATION' },
24
+ labels: { label: 'LABEL' },
25
+ showConfiguration: jest.fn()
34
26
  };
35
27
  const result = useDefaultMetadataForLegacyPagesProps(resource);
36
28
 
@@ -39,7 +31,7 @@ describe('composables: Metadata/composables', () => {
39
31
  expect(result.value.annotations).toStrictEqual([{ key: 'annotation', value: resource.annotations.annotation }]);
40
32
  expect(result.value.labels).toStrictEqual([{ key: 'label', value: resource.labels.label }]);
41
33
  expect(result.value.identifyingInformation).toHaveLength(0);
42
- expect(mockDrawer.openResourceDetailDrawer).toHaveBeenCalledTimes(1);
34
+ expect(resource.showConfiguration).toHaveBeenCalledTimes(1);
43
35
  });
44
36
 
45
37
  it('should fill identifyingInformation', () => {
@@ -51,9 +43,10 @@ describe('composables: Metadata/composables', () => {
51
43
  useResourceDetailsSpy.mockReturnValue(computed(() => [{ label: 'RESOURCE_DETAILS' }]));
52
44
 
53
45
  const resource = {
54
- type: 'RESOURCE',
55
- annotations: { annotation: 'ANNOTATION' },
56
- labels: { label: 'LABEL' }
46
+ type: 'RESOURCE',
47
+ annotations: { annotation: 'ANNOTATION' },
48
+ labels: { label: 'LABEL' },
49
+ showConfiguration: jest.fn()
57
50
  };
58
51
  const result = useDefaultMetadataForLegacyPagesProps(resource);
59
52
 
@@ -69,7 +62,7 @@ describe('composables: Metadata/composables', () => {
69
62
  { label: 'CREATED_BY' },
70
63
  { label: 'RESOURCE_DETAILS' }
71
64
  ]);
72
- expect(mockDrawer.openResourceDetailDrawer).toHaveBeenCalledTimes(1);
65
+ expect(resource.showConfiguration).toHaveBeenCalledTimes(1);
73
66
  });
74
67
  });
75
68
  });