@rancher/shell 3.0.12-rc.3 → 3.0.12-rc.4

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 (258) hide show
  1. package/assets/styles/global/_layout.scss +4 -0
  2. package/assets/translations/en-us.yaml +144 -41
  3. package/assets/translations/zh-hans.yaml +1 -7
  4. package/chart/monitoring/ClusterSelector.vue +0 -21
  5. package/chart/monitoring/prometheus/index.vue +6 -3
  6. package/components/CruResource.vue +161 -14
  7. package/components/ExplorerMembers.vue +8 -4
  8. package/components/ExplorerProjectsNamespaces.vue +10 -6
  9. package/components/GrowlManager.vue +4 -0
  10. package/components/MgmtNodeList.vue +184 -0
  11. package/components/Resource/Detail/Card/StateCard/__tests__/composables.test.ts +90 -1
  12. package/components/Resource/Detail/Card/StateCard/composables.ts +57 -87
  13. package/components/Resource/Detail/Card/StatusCard/__tests__/StatusCard.test.ts +61 -0
  14. package/components/Resource/Detail/Card/StatusCard/index.vue +61 -15
  15. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +2 -0
  16. package/components/Resource/Detail/Metadata/KeyValue.vue +5 -2
  17. package/components/Resource/Detail/Metadata/KeyValueRow.vue +2 -6
  18. package/components/ResourceDetail/index.vue +1 -1
  19. package/components/ResourceList/Masthead.vue +7 -1
  20. package/components/ResourceList/index.vue +82 -1
  21. package/components/RichTranslation.vue +5 -2
  22. package/components/Setting.vue +1 -0
  23. package/components/SubtleLink.vue +31 -6
  24. package/components/Tabbed/Tab.vue +29 -3
  25. package/components/Tabbed/index.vue +25 -3
  26. package/components/TableOfContents/TableOfContents.vue +109 -0
  27. package/components/TableOfContents/composables.ts +258 -0
  28. package/components/Window/ContainerShell.vue +21 -11
  29. package/components/Window/__tests__/ContainerShell.test.ts +107 -37
  30. package/components/Wizard.vue +9 -4
  31. package/components/fleet/AppCoChartGrid.vue +401 -0
  32. package/components/fleet/AppCoEmptyState.vue +127 -0
  33. package/components/fleet/AppCoPageHeader.vue +119 -0
  34. package/components/fleet/AppCoVersionSelect.vue +70 -0
  35. package/components/fleet/FleetClusterTargets/ClusterSelectionFields.vue +217 -0
  36. package/components/fleet/FleetClusterTargets/TargetsList.vue +123 -35
  37. package/components/fleet/FleetClusterTargets/index.vue +189 -146
  38. package/components/fleet/FleetIntro.vue +7 -3
  39. package/components/fleet/FleetNoWorkspaces.vue +7 -3
  40. package/components/fleet/FleetSecretSelector.vue +5 -3
  41. package/components/fleet/FleetValuesFrom.vue +8 -2
  42. package/components/fleet/GitRepoTargetTab.vue +0 -2
  43. package/components/fleet/HelmOpAdvancedTab.vue +19 -53
  44. package/components/fleet/HelmOpAppCoConfigTab.vue +593 -0
  45. package/components/fleet/HelmOpAppCoResourcesSection.vue +162 -0
  46. package/components/fleet/HelmOpResourcesSection.vue +82 -0
  47. package/components/fleet/HelmOpTargetOptionsSection.vue +89 -0
  48. package/components/fleet/HelmOpTargetTab.vue +64 -60
  49. package/components/fleet/HelmOpValuesTab.vue +129 -105
  50. package/components/fleet/__tests__/AppCoEmptyState.test.ts +71 -0
  51. package/components/fleet/__tests__/AppCoVersionSelect.test.ts +36 -0
  52. package/components/fleet/__tests__/ClusterSelectionFields.test.ts +62 -0
  53. package/components/fleet/__tests__/FleetClusterTargets.test.ts +253 -0
  54. package/components/fleet/__tests__/FleetSecretSelector.test.ts +16 -0
  55. package/components/fleet/__tests__/FleetValuesFrom.test.ts +44 -0
  56. package/components/fleet/__tests__/HelmOpAppCoConfigTab.test.ts +59 -0
  57. package/components/fleet/__tests__/HelmOpAppCoResourcesSection.test.ts +62 -0
  58. package/components/fleet/__tests__/HelmOpResourcesSection.test.ts +43 -0
  59. package/components/fleet/__tests__/HelmOpTargetOptionsSection.test.ts +34 -0
  60. package/components/fleet/__tests__/HelmOpValuesTab.test.ts +39 -0
  61. package/components/fleet/__tests__/__snapshots__/AppCoEmptyState.test.ts.snap +97 -0
  62. package/components/fleet/__tests__/__snapshots__/AppCoVersionSelect.test.ts.snap +30 -0
  63. package/components/fleet/__tests__/__snapshots__/ClusterSelectionFields.test.ts.snap +209 -0
  64. package/components/fleet/__tests__/__snapshots__/HelmOpTargetOptionsSection.test.ts.snap +140 -0
  65. package/components/fleet/dashboard/Empty.vue +8 -4
  66. package/components/fleet/dashboard/ResourceCard.vue +28 -0
  67. package/components/fleet/dashboard/ResourceDetails.vue +28 -0
  68. package/components/fleet/dashboard/__tests__/ResourceCard.test.ts +87 -0
  69. package/components/form/ArrayList.vue +61 -4
  70. package/components/form/KeyValue.vue +23 -2
  71. package/components/form/LabeledSelect.vue +39 -1
  72. package/components/form/Labels.vue +22 -3
  73. package/components/form/NameNsDescription.vue +13 -5
  74. package/components/form/ResourceTabs/index.vue +1 -0
  75. package/components/form/__tests__/NameNsDescription.test.ts +75 -0
  76. package/components/formatter/InternalExternalIP.vue +10 -4
  77. package/components/formatter/ServiceTargets.vue +26 -7
  78. package/components/formatter/__tests__/InternalExternalIP.test.ts +132 -0
  79. package/components/formatter/__tests__/ServiceTargets.test.ts +412 -0
  80. package/components/nav/Header.vue +4 -0
  81. package/components/nav/TopLevelMenu.vue +7 -2
  82. package/components/nav/__tests__/Header.test.ts +15 -0
  83. package/components/nav/__tests__/TopLevelMenu.test.ts +120 -2
  84. package/components/templates/default.vue +9 -4
  85. package/components/templates/home.vue +9 -4
  86. package/components/templates/plain.vue +9 -4
  87. package/composables/useHelmOpResources.test.ts +56 -0
  88. package/composables/useHelmOpResources.ts +32 -0
  89. package/composables/useStateColor.test.ts +325 -0
  90. package/composables/useStateColor.ts +128 -0
  91. package/config/home-links.js +1 -1
  92. package/config/labels-annotations.js +1 -0
  93. package/config/product/explorer.js +17 -4
  94. package/config/product/manager.js +2 -0
  95. package/config/router/index.js +16 -0
  96. package/config/router/navigation-guards/__tests__/authentication.test.ts +130 -0
  97. package/config/router/navigation-guards/authentication.js +10 -4
  98. package/config/router/routes.js +20 -6
  99. package/config/settings.ts +0 -2
  100. package/config/table-headers.js +3 -4
  101. package/config/types.js +9 -0
  102. package/core/plugin-products-base.ts +3 -3
  103. package/core/plugin-types.ts +83 -30
  104. package/core/plugin.ts +3 -0
  105. package/core/types-provisioning.ts +34 -1
  106. package/core/types.ts +15 -2
  107. package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +114 -0
  108. package/detail/__tests__/workload.test.ts +3 -152
  109. package/detail/catalog.cattle.io.clusterrepo.vue +1 -1
  110. package/detail/provisioning.cattle.io.cluster.vue +30 -4
  111. package/detail/workload/index.vue +12 -55
  112. package/edit/__tests__/catalog.cattle.io.clusterrepo.test.ts +248 -0
  113. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +105 -0
  114. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/General.test.ts.snap +6 -0
  115. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/index.test.ts.snap +1 -0
  116. package/edit/auth/__tests__/azuread.test.ts +34 -9
  117. package/edit/auth/__tests__/github.test.ts +234 -0
  118. package/edit/auth/__tests__/oidc.test.ts +26 -6
  119. package/edit/auth/__tests__/saml.test.ts +196 -0
  120. package/edit/auth/azuread.vue +128 -95
  121. package/edit/auth/github.vue +72 -13
  122. package/edit/auth/ldap/__tests__/index.test.ts +206 -0
  123. package/edit/auth/ldap/config.vue +8 -0
  124. package/edit/auth/ldap/index.vue +75 -1
  125. package/edit/auth/oidc.vue +119 -73
  126. package/edit/auth/saml.vue +76 -12
  127. package/edit/catalog.cattle.io.clusterrepo.vue +140 -32
  128. package/edit/fleet.cattle.io.helmop.vue +491 -136
  129. package/edit/management.cattle.io.user.vue +5 -2
  130. package/edit/provisioning.cattle.io.cluster/rke2.vue +84 -10
  131. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +11 -0
  132. package/list/group.principal.vue +5 -4
  133. package/list/harvesterhci.io.management.cluster.vue +8 -9
  134. package/list/management.cattle.io.user.vue +12 -9
  135. package/list/provisioning.cattle.io.cluster.vue +16 -10
  136. package/mixins/__tests__/auth-config.test.ts +90 -0
  137. package/mixins/__tests__/chart.test.ts +94 -0
  138. package/mixins/__tests__/resource-fetch-api-pagination.test.ts +48 -0
  139. package/mixins/auth-config.js +7 -0
  140. package/mixins/chart.js +11 -2
  141. package/mixins/child-hook.js +12 -6
  142. package/mixins/create-edit-view/impl.js +5 -3
  143. package/mixins/resource-fetch-api-pagination.js +21 -1
  144. package/models/__tests__/catalog.cattle.io.clusterrepo.test.ts +57 -0
  145. package/models/__tests__/compliance.cattle.io.clusterscan.test.ts +144 -0
  146. package/models/__tests__/fleet-application.test.ts +175 -0
  147. package/models/__tests__/fleet.cattle.io.bundle.test.ts +169 -0
  148. package/models/__tests__/fleet.cattle.io.helmop.test.ts +84 -0
  149. package/models/__tests__/management.cattle.io.node.ts +22 -0
  150. package/models/__tests__/namespace.test.ts +36 -0
  151. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +49 -0
  152. package/models/__tests__/workload.test.ts +401 -26
  153. package/models/catalog.cattle.io.clusterrepo.js +28 -4
  154. package/models/compliance.cattle.io.clusterscan.js +39 -4
  155. package/models/fleet-application.js +4 -0
  156. package/models/fleet.cattle.io.helmop.js +20 -1
  157. package/models/management.cattle.io.cluster.js +18 -2
  158. package/models/management.cattle.io.node.js +44 -3
  159. package/models/namespace.js +1 -1
  160. package/models/pod.js +33 -1
  161. package/models/provisioning.cattle.io.cluster.js +5 -5
  162. package/models/workload.js +108 -13
  163. package/models/workload.service.js +5 -0
  164. package/package.json +14 -13
  165. package/pages/about.vue +5 -6
  166. package/pages/auth/login.vue +0 -35
  167. package/pages/auth/setup.vue +11 -0
  168. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +2 -2
  169. package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +10 -1
  170. package/pages/c/_cluster/apps/charts/__tests__/index.test.ts +93 -0
  171. package/pages/c/_cluster/apps/charts/chart.vue +2 -1
  172. package/pages/c/_cluster/apps/charts/index.vue +48 -10
  173. package/pages/c/_cluster/apps/charts/install.vue +122 -116
  174. package/pages/c/_cluster/auth/roles/index.vue +5 -4
  175. package/pages/c/_cluster/explorer/workload-dashboard/ByNamespaceSection.vue +31 -0
  176. package/pages/c/_cluster/explorer/workload-dashboard/ByStateSection.vue +138 -0
  177. package/pages/c/_cluster/explorer/workload-dashboard/ByTypeSection.vue +30 -0
  178. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadCard.vue +155 -0
  179. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadNamespaceCard.vue +142 -0
  180. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadTypeCard.vue +159 -0
  181. package/pages/c/_cluster/explorer/workload-dashboard/__tests__/composable.test.ts +561 -0
  182. package/pages/c/_cluster/explorer/workload-dashboard/composable.ts +440 -0
  183. package/pages/c/_cluster/explorer/workload-dashboard/index.vue +187 -0
  184. package/pages/c/_cluster/explorer/workload-dashboard/types.ts +80 -0
  185. package/pages/c/_cluster/fleet/application/create.vue +187 -136
  186. package/pages/c/_cluster/fleet/application/index.vue +5 -3
  187. package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailBody.vue +338 -0
  188. package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailHeader.vue +121 -0
  189. package/pages/c/_cluster/fleet/application/suse-app-collection/chart.vue +369 -0
  190. package/pages/c/_cluster/fleet/application/suse-app-collection/charts.vue +248 -0
  191. package/pages/c/_cluster/fleet/application/suse-app-collection/credentials.vue +310 -0
  192. package/pages/c/_cluster/fleet/index.vue +2 -2
  193. package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +96 -0
  194. package/pages/c/_cluster/uiplugins/index.vue +15 -0
  195. package/pages/fail-whale.vue +16 -11
  196. package/pages/home.vue +16 -46
  197. package/plugins/clean-html.d.ts +9 -0
  198. package/plugins/dashboard-store/__tests__/resource-class.test.ts +93 -0
  199. package/plugins/dashboard-store/resource-class.js +62 -7
  200. package/plugins/steve/__tests__/actions.test.ts +212 -0
  201. package/plugins/steve/actions.js +96 -0
  202. package/plugins/steve/steve-pagination-utils.ts +1 -1
  203. package/rancher-components/Accordion/Accordion.vue +53 -9
  204. package/rancher-components/Form/Checkbox/Checkbox.vue +14 -0
  205. package/rancher-components/Form/Radio/RadioButton.vue +17 -1
  206. package/rancher-components/Form/Radio/RadioGroup.vue +10 -0
  207. package/rancher-components/Pill/RcTag/RcTag.vue +3 -2
  208. package/rancher-components/RcButton/RcButton.test.ts +103 -0
  209. package/rancher-components/RcButton/RcButton.vue +94 -15
  210. package/rancher-components/RcButton/types.ts +3 -0
  211. package/rancher-components/RcItemCard/RcItemCard.test.ts +18 -0
  212. package/rancher-components/RcItemCard/RcItemCard.vue +2 -2
  213. package/rancher-components/RcSection/RcSection.vue +28 -3
  214. package/scripts/extension/helm/package/Dockerfile +1 -1
  215. package/scripts/test-plugins-build.sh +2 -1
  216. package/store/__tests__/notifications.test.ts +434 -0
  217. package/store/catalog.js +57 -0
  218. package/store/plugins.js +7 -4
  219. package/types/components/buttonGroup.ts +5 -0
  220. package/types/shell/index.d.ts +104 -70
  221. package/utils/__tests__/auth.test.ts +273 -0
  222. package/utils/__tests__/computed.test.ts +193 -0
  223. package/utils/__tests__/cspAdaptor.test.ts +163 -0
  224. package/utils/__tests__/dom.test.ts +81 -0
  225. package/utils/__tests__/duration.test.ts +37 -1
  226. package/utils/__tests__/dynamic-importer.test.ts +102 -0
  227. package/utils/__tests__/fleet-appco.test.ts +312 -0
  228. package/utils/__tests__/monitoring.test.ts +130 -0
  229. package/utils/__tests__/object.test.ts +22 -0
  230. package/utils/__tests__/platform.test.ts +91 -0
  231. package/utils/__tests__/position.test.ts +237 -0
  232. package/utils/__tests__/provider.test.ts +51 -1
  233. package/utils/__tests__/queue.test.ts +232 -0
  234. package/utils/__tests__/release-notes.test.ts +221 -0
  235. package/utils/__tests__/router.test.js +254 -1
  236. package/utils/__tests__/select.test.ts +208 -0
  237. package/utils/__tests__/time.test.ts +265 -1
  238. package/utils/__tests__/title.test.ts +47 -0
  239. package/utils/__tests__/width.test.ts +53 -0
  240. package/utils/__tests__/window.test.ts +158 -0
  241. package/utils/__tests__/xccdf.test.ts +126 -6
  242. package/utils/crypto/__tests__/browserHashUtils.test.ts +98 -0
  243. package/utils/crypto/__tests__/index.test.ts +144 -0
  244. package/utils/duration.ts +104 -0
  245. package/utils/dynamic-content/__tests__/notification-handler.test.ts +196 -0
  246. package/utils/dynamic-content/info.ts +2 -1
  247. package/utils/error.js +13 -0
  248. package/utils/fleet-appco.ts +323 -0
  249. package/utils/object.js +22 -2
  250. package/utils/provider.ts +12 -0
  251. package/utils/validators/__tests__/container-images.test.ts +104 -0
  252. package/utils/validators/__tests__/flow-output.test.ts +91 -0
  253. package/utils/validators/__tests__/logging-outputs.test.ts +58 -0
  254. package/utils/validators/__tests__/monitoring-route.test.ts +119 -0
  255. package/utils/xccdf.ts +39 -42
  256. package/vue.config.js +1 -1
  257. package/pages/support/index.vue +0 -264
  258. package/utils/duration.js +0 -43
@@ -1,10 +1,13 @@
1
1
  <script lang="ts">
2
2
  import { PropType } from 'vue';
3
3
  import { Cluster } from '@shell/components/fleet/FleetClusterTargets/index.vue';
4
+ import { RcTag, RcCounterBadge } from '@components/Pill';
4
5
 
5
6
  export default {
6
7
  name: 'FleetTargetsList',
7
8
 
9
+ components: { RcTag, RcCounterBadge },
10
+
8
11
  props: {
9
12
  clusters: {
10
13
  type: Array as PropType<Cluster[]>,
@@ -13,47 +16,93 @@ export default {
13
16
 
14
17
  emptyLabel: {
15
18
  type: String,
16
- default: ''
17
- }
19
+ default: '',
20
+ },
21
+
22
+ compact: {
23
+ type: Boolean,
24
+ default: false
25
+ },
18
26
  },
19
27
 
20
28
  computed: {
21
29
  clustersRenderList() {
22
- const clustersRenderList = this.clusters.map(({ nameDisplay, name, detailLocation }) => ({
30
+ return this.clusters.map(({ nameDisplay, name, detailLocation }) => ({
23
31
  name: nameDisplay || name,
24
32
  detailLocation,
25
33
  }));
26
-
27
- return clustersRenderList;
28
- }
29
- }
34
+ },
35
+ },
30
36
  };
31
37
  </script>
32
38
 
33
39
  <template>
34
- <div class="targets-list-main">
35
- <h3>{{ t('fleet.clusterTargets.rules.matching.title', { n: clustersRenderList.length }) }}</h3>
36
- <div class="targets-list-list">
37
- <span
38
- v-for="(cluster, i) in clustersRenderList"
39
- :key="i"
40
- class="row mt-5"
41
- >
42
- <router-link
43
- :to="cluster.detailLocation"
44
- target="_blank"
45
- class="link-main"
46
- >
47
- {{ cluster.name }}&nbsp;<i class="link-icon icon icon-external-link" />
48
- </router-link>
49
- </span>
50
- <span
51
- v-if="!clustersRenderList.length"
52
- class="text-label"
53
- >
54
- {{ emptyLabel || t('fleet.clusterTargets.rules.matching.empty') }}
55
- </span>
40
+ <div
41
+ class="targets-list-main"
42
+ :class="{ 'compact': compact }"
43
+ >
44
+ <h3
45
+ v-if="!compact"
46
+ class="m-0"
47
+ >
48
+ {{ t('fleet.clusterTargets.rules.matching.title', { n: clustersRenderList.length }) }}
49
+ </h3>
50
+ <div
51
+ v-else
52
+ class="compact-title"
53
+ >
54
+ <h3>{{ t('fleet.clusterTargets.rules.matching.selectedClusters') }}</h3>
55
+ <RcCounterBadge
56
+ :count="clustersRenderList.length"
57
+ type="inactive"
58
+ />
56
59
  </div>
60
+
61
+ <template v-if="compact">
62
+ <div class="targets-list-chips">
63
+ <RcTag
64
+ v-for="cluster in clustersRenderList"
65
+ :key="cluster.name"
66
+ type="inactive"
67
+ class="do-not-shrink"
68
+ >
69
+ {{ cluster.name }}
70
+ </RcTag>
71
+ <span
72
+ v-if="!clustersRenderList.length"
73
+ class="text-label"
74
+ >
75
+ {{ emptyLabel || t('fleet.clusterTargets.rules.matching.empty') }}
76
+ </span>
77
+ </div>
78
+ </template>
79
+
80
+ <template v-else>
81
+ <div class="targets-list-list">
82
+ <span
83
+ v-for="cluster in clustersRenderList"
84
+ :key="cluster.name"
85
+ class="row"
86
+ >
87
+ <router-link
88
+ :to="cluster.detailLocation"
89
+ target="_blank"
90
+ class="link-main"
91
+ >
92
+ {{ cluster.name }}&nbsp;<i
93
+ class="link-icon icon icon-external-link"
94
+ aria-hidden="true"
95
+ />
96
+ </router-link>
97
+ </span>
98
+ <span
99
+ v-if="!clustersRenderList.length"
100
+ class="text-label"
101
+ >
102
+ {{ emptyLabel || t('fleet.clusterTargets.rules.matching.empty') }}
103
+ </span>
104
+ </div>
105
+ </template>
57
106
  </div>
58
107
  </template>
59
108
 
@@ -65,20 +114,59 @@ export default {
65
114
  background-color: var(--tabbed-sidebar-bg);
66
115
  display: flex;
67
116
  flex-direction: column;
117
+ gap: var(--gap-md);
118
+
119
+ &.compact {
120
+ background-color: var(--body-bg);
121
+ max-height: none;
122
+
123
+ .targets-list-list {
124
+ display: flex;
125
+ flex-direction: column;
126
+ gap: 6px;
127
+
128
+ .row {
129
+ line-height: 24px;
130
+ }
131
+ }
132
+ }
133
+
134
+ .compact-title {
135
+ display: flex;
136
+ align-items: center;
137
+ gap: var(--gap);
138
+
139
+ h3 {
140
+ margin: 0;
141
+ }
142
+ }
68
143
  }
69
144
  .targets-list-list {
70
145
  overflow-y: auto;
71
146
  }
72
147
  .link-main{
73
- word-spacing: 22px;
74
- line-height: 17px; // To fit the icon size and make sure it doesnt resize
148
+ word-spacing: 15px;
149
+ line-height: 17px;
75
150
  }
76
151
  .link-icon {
77
- margin-left: -14px; // Remove the space of the icon to make it float to accomodate the underline
78
- display: none; // Make the icon disappear by default
152
+ margin-left: -14px;
153
+ display: none;
79
154
  }
80
-
81
155
  .link-main:hover .link-icon {
82
- display: inline; // Only appear when hovered
156
+ display: inline;
157
+ }
158
+ .targets-list-chips {
159
+ display: flex;
160
+ flex-direction: column;
161
+ gap: var(--gap);
162
+ align-items: flex-start;
163
+ flex: 1;
164
+ min-height: 0;
165
+ overflow-y: auto;
166
+
167
+ .do-not-shrink {
168
+ flex-shrink: 0;
169
+ }
83
170
  }
171
+
84
172
  </style>
@@ -8,12 +8,11 @@ import { FLEET } from '@shell/config/types';
8
8
  import FleetUtils from '@shell/utils/fleet';
9
9
  import { Expression, Selector, Target, TargetMode } from '@shell/types/fleet';
10
10
  import { _CREATE, _EDIT, _VIEW } from '@shell/config/query-params';
11
- import LabeledSelect from '@shell/components/form/LabeledSelect.vue';
12
- import MatchExpressions from '@shell/components/form/MatchExpressions.vue';
13
11
  import { Banner } from '@components/Banner';
14
- import { RcButton } from '@components/RcButton';
12
+ import { RcSection } from '@components/RcSection';
15
13
  import RadioGroup from '@components/Form/Radio/RadioGroup.vue';
16
14
  import TargetsList from '@shell/components/fleet/FleetClusterTargets/TargetsList.vue';
15
+ import ClusterSelectionFields from '@shell/components/fleet/FleetClusterTargets/ClusterSelectionFields.vue';
17
16
 
18
17
  export interface Cluster {
19
18
  name: string,
@@ -21,14 +20,24 @@ export interface Cluster {
21
20
  detailLocation: object,
22
21
  }
23
22
 
23
+ interface FleetResource {
24
+ name: string,
25
+ nameDisplay: string,
26
+ metadata: {
27
+ name: string,
28
+ namespace: string,
29
+ },
30
+ }
31
+
24
32
  interface DataType {
25
33
  targetMode: TargetMode,
26
- allClusters: any[],
27
- allClusterGroups: any[],
34
+ allClusters: FleetResource[],
35
+ allClusterGroups: FleetResource[],
28
36
  selectedClusters: string[],
29
37
  selectedClusterGroups: string[],
30
38
  clusterSelectors: Selector[],
31
39
  key: number,
40
+ clustersExpanded: boolean,
32
41
  areHarvesterHostsVisible: boolean,
33
42
  }
34
43
 
@@ -43,10 +52,9 @@ export default {
43
52
 
44
53
  components: {
45
54
  Banner,
46
- LabeledSelect,
47
- MatchExpressions,
55
+ ClusterSelectionFields,
48
56
  RadioGroup,
49
- RcButton,
57
+ RcSection,
50
58
  TargetsList,
51
59
  },
52
60
 
@@ -75,6 +83,12 @@ export default {
75
83
  type: String as PropType<TargetMode>,
76
84
  default: '',
77
85
  },
86
+
87
+ compact: {
88
+ type: Boolean,
89
+ default: false,
90
+ },
91
+
78
92
  },
79
93
 
80
94
  async fetch() {
@@ -87,7 +101,7 @@ export default {
87
101
  inStoreType: 'management',
88
102
  type: FLEET.CLUSTER_GROUP
89
103
  },
90
- }, this.$store) as { allClusters: any[], allClusterGroups: any[] };
104
+ }, this.$store) as { allClusters: FleetResource[], allClusterGroups: FleetResource[] };
91
105
 
92
106
  this.allClusters = hash.allClusters || [];
93
107
  this.allClusterGroups = hash.allClusterGroups || [];
@@ -102,6 +116,7 @@ export default {
102
116
  selectedClusterGroups: [],
103
117
  clusterSelectors: [],
104
118
  key: 0, // Generates a unique key to handle Targets
119
+ clustersExpanded: true,
105
120
  /**
106
121
  * Are host harvesters treated as normal clusters... or are they hidden
107
122
  */
@@ -138,7 +153,7 @@ export default {
138
153
  }
139
154
  },
140
155
 
141
- allClusters(clusters: any[]) {
156
+ allClusters(clusters: FleetResource[]) {
142
157
  if (clusters.length) {
143
158
  // Resolve metadata.name values to nameDisplay for UI display
144
159
  this.selectedClusters = this.selectedClusters.map(
@@ -152,25 +167,27 @@ export default {
152
167
  targetModeOptions(): { label: string, value: TargetMode }[] {
153
168
  if (this.namespace === 'fleet-local') {
154
169
  return [{
155
- label: 'local cluster',
170
+ label: this.t('fleet.clusterTargets.targetMode.local'),
156
171
  value: 'local'
157
172
  }];
158
173
  }
159
174
 
175
+ const allLabel = this.compact ? this.t('fleet.clusterTargets.targetMode.allCompact', { namespace: this.namespace, count: this.clustersOptions.length }, { raw: true }) : this.t('fleet.clusterTargets.targetMode.all');
176
+
160
177
  const out: { label: string, value: TargetMode }[] = [
161
178
  {
162
- label: 'All Clusters in the workspace',
179
+ label: allLabel,
163
180
  value: 'all',
164
181
  },
165
182
  {
166
- label: 'No clusters',
183
+ label: this.t('fleet.clusterTargets.targetMode.none'),
167
184
  value: 'none'
168
185
  },
169
186
  ];
170
187
 
171
188
  if (this.clustersOptions.length) {
172
189
  out.push({
173
- label: 'Manually selected clusters',
190
+ label: this.t('fleet.clusterTargets.targetMode.clusters'),
174
191
  value: 'clusters'
175
192
  });
176
193
  }
@@ -232,12 +249,7 @@ export default {
232
249
 
233
250
  this.clusterSelectors.push(neu);
234
251
 
235
- // Focus first element in MatchExpression
236
- this.$nextTick(() => {
237
- const matchExpression = (this.$refs[`match-expression-${ neu.key }`] as HTMLElement[])?.[0];
238
-
239
- matchExpression?.focus();
240
- });
252
+ (this.$refs.selectionFields as any)?.focusMatchExpression(neu.key);
241
253
 
242
254
  this.update();
243
255
  },
@@ -385,7 +397,7 @@ export default {
385
397
 
386
398
  resolveClusterDisplayName(name: string): string {
387
399
  const cluster = this.allClusters.find(
388
- (c: any) => c.metadata.namespace === this.namespace && c.metadata.name === name
400
+ (c: FleetResource) => c.metadata.namespace === this.namespace && c.metadata.name === name
389
401
  );
390
402
 
391
403
  return cluster ? cluster.nameDisplay : name;
@@ -402,151 +414,182 @@ export default {
402
414
  </script>
403
415
 
404
416
  <template>
405
- <div
406
- v-if="targetMode !== 'advanced'"
407
- class="row"
408
- >
409
- <RadioGroup
410
- name="targetMode"
411
- data-testid="fleet-target-cluster-radio-button"
412
- :value="isLocal ? 'local' : targetMode"
413
- :mode="mode"
414
- :options="targetModeOptions"
415
- :disabled="isView"
416
- @update:value="selectTargetMode"
417
- />
418
- </div>
419
-
420
- <Banner
421
- v-if="targetMode === 'advanced'"
422
- class="row"
423
- color="warning"
424
- :label="t('fleet.clusterTargets.advancedConfigs')"
425
- />
426
-
427
- <div
428
- v-if="targetMode === 'clusters'"
429
- class="row mt-20"
430
- >
431
- <div class="col span-9">
432
- <h3 class="m-0">
433
- {{ t('fleet.clusterTargets.clusters.title') }}
434
- </h3>
435
- <LabeledSelect
436
- data-testid="fleet-target-cluster-name-selector"
437
- class="mmt-4"
438
- :value="selectedClusters"
439
- :label="t('fleet.clusterTargets.clusters.byName.label')"
440
- :options="clustersOptions"
441
- :taggable="true"
442
- :close-on-select="false"
417
+ <div :class="compact ? 'gap-md' : 'gap-20'">
418
+ <div
419
+ v-if="targetMode !== 'advanced'"
420
+ class="row"
421
+ >
422
+ <RadioGroup
423
+ name="targetMode"
424
+ data-testid="fleet-target-cluster-radio-button"
425
+ :value="isLocal ? 'local' : targetMode"
443
426
  :mode="mode"
444
- :multiple="true"
445
- :placeholder="t('fleet.clusterTargets.clusters.byName.placeholder')"
446
- @update:value="selectClusters"
427
+ :options="targetModeOptions"
428
+ :disabled="isView"
429
+ :use-body-text-color="compact"
430
+ @update:value="selectTargetMode"
447
431
  />
448
- <div
449
- v-if="!isView || (clusterSelectors && clusterSelectors.length > 0)"
450
- class="mmt-6"
451
- >
452
- <h4 class="m-0">
453
- {{ t('fleet.clusterTargets.clusters.byLabel.title') }}
454
- </h4>
455
- <div
456
- v-for="(selector, i) in clusterSelectors"
457
- :key="selector.key"
458
- class="match-expressions-container mmt-4"
432
+ </div>
433
+
434
+ <Banner
435
+ v-if="targetMode === 'advanced'"
436
+ class="row"
437
+ color="warning"
438
+ :label="t('fleet.clusterTargets.advancedConfigs')"
439
+ />
440
+
441
+ <!-- AppCo: RcSection layout -->
442
+ <div
443
+ v-if="targetMode === 'clusters' && compact && !isView"
444
+ class="row"
445
+ >
446
+ <div class="col span-12 content-group">
447
+ <RcSection
448
+ v-model:expanded="clustersExpanded"
449
+ :title="t('fleet.clusterTargets.clusters.title')"
450
+ mode="with-header"
451
+ type="secondary"
452
+ expandable
453
+ data-testid="fleet-target-clusters-section"
459
454
  >
460
- <MatchExpressions
461
- :ref="`match-expression-${ selector.key }`"
462
- class="body"
463
- :value="selector"
464
- :mode="mode"
465
- :initial-empty-row="true"
466
- :label-key="t('fleet.clusterTargets.clusters.byLabel.labelKey')"
467
- :add-icon="'icon-plus'"
468
- :add-class="'btn-sm'"
469
- @update:value="updateMatchExpressions(i, $event, selector.key)"
470
- />
471
- <RcButton
472
- v-if="!isView"
473
- size="small"
474
- variant="link"
475
- @click="removeMatchExpressions(selector.key)"
455
+ <template
456
+ v-if="!clustersExpanded"
457
+ #badges
476
458
  >
477
- <i class="icon icon-x" />
478
- </RcButton>
479
- </div>
480
- <RcButton
481
- v-if="!isView"
482
- size="small"
483
- variant="secondary"
484
- class="mmt-4"
485
- @click="addMatchExpressions"
486
- >
487
- <i class="icon icon-plus" />
488
- <span>{{ t('fleet.clusterTargets.clusters.byLabel.addSelector') }}</span>
489
- </RcButton>
459
+ <span
460
+ class="cluster-count-badge"
461
+ :aria-label="t('fleet.clusterTargets.rules.matching.title', { n: matching.length })"
462
+ >
463
+ {{ t('fleet.clusterTargets.rules.matching.title', { n: matching.length }) }}
464
+ </span>
465
+ </template>
466
+ <div class="row">
467
+ <div class="col span-8">
468
+ <ClusterSelectionFields
469
+ ref="selectionFields"
470
+ variant="appco"
471
+ :selected-clusters="selectedClusters"
472
+ :selected-cluster-groups="selectedClusterGroups"
473
+ :cluster-selectors="clusterSelectors"
474
+ :clusters-options="clustersOptions"
475
+ :cluster-groups-options="clusterGroupsOptions"
476
+ :mode="mode"
477
+ :is-view="isView"
478
+ :compact="compact"
479
+ @select-clusters="selectClusters"
480
+ @select-cluster-groups="selectClusterGroups"
481
+ @add-match-expressions="addMatchExpressions"
482
+ @update-match-expressions="updateMatchExpressions"
483
+ @remove-match-expressions="removeMatchExpressions"
484
+ />
485
+ </div>
486
+ <div class="col span-4 targets-col">
487
+ <TargetsList
488
+ class="target-list"
489
+ :clusters="matching"
490
+ :compact="compact"
491
+ :empty-label="t('fleet.clusterTargets.rules.matching.placeholder')"
492
+ />
493
+ </div>
494
+ </div>
495
+ </RcSection>
490
496
  </div>
491
- <div class="mmt-8">
492
- <h3 class="m-0">
493
- {{ t('fleet.clusterTargets.clusterGroups.title') }}
497
+ </div>
498
+
499
+ <!-- Default: original layout -->
500
+ <div
501
+ v-if="targetMode === 'clusters' && (!compact || isView)"
502
+ class="row"
503
+ >
504
+ <div class="col span-8">
505
+ <h3
506
+ v-if="!compact"
507
+ class="m-0"
508
+ >
509
+ {{ t('fleet.clusterTargets.clusters.title') }}
494
510
  </h3>
495
- <LabeledSelect
496
- data-testid="fleet-target-cluster-group-selector"
497
- class="mmt-4"
498
- :value="selectedClusterGroups"
499
- :label="t('fleet.clusterTargets.clusterGroups.byName.label')"
500
- :options="clusterGroupsOptions"
501
- :taggable="true"
502
- :close-on-select="false"
511
+ <ClusterSelectionFields
512
+ ref="selectionFields"
513
+ variant="default"
514
+ :selected-clusters="selectedClusters"
515
+ :selected-cluster-groups="selectedClusterGroups"
516
+ :cluster-selectors="clusterSelectors"
517
+ :clusters-options="clustersOptions"
518
+ :cluster-groups-options="clusterGroupsOptions"
503
519
  :mode="mode"
504
- :multiple="true"
505
- :placeholder="t('fleet.clusterTargets.clusterGroups.byName.placeholder')"
506
- @update:value="selectClusterGroups"
520
+ :is-view="isView"
521
+ :compact="compact"
522
+ @select-clusters="selectClusters"
523
+ @select-cluster-groups="selectClusterGroups"
524
+ @add-match-expressions="addMatchExpressions"
525
+ @update-match-expressions="updateMatchExpressions"
526
+ @remove-match-expressions="removeMatchExpressions"
527
+ />
528
+ </div>
529
+ <div class="col span-4">
530
+ <TargetsList
531
+ class="target-list"
532
+ :clusters="matching"
533
+ :empty-label="t('fleet.clusterTargets.rules.matching.placeholder')"
507
534
  />
508
535
  </div>
509
536
  </div>
510
- <div class="col span-3">
511
- <TargetsList
512
- class="target-list"
513
- :clusters="matching"
514
- :empty-label="t('fleet.clusterTargets.rules.matching.placeholder')"
515
- />
516
- </div>
517
- </div>
518
537
 
519
- <div
520
- v-if="targetMode === 'all' && !isLocal"
521
- class="row"
522
- >
523
- <div class="col span-6">
524
- <TargetsList
525
- class="target-list mt-20"
526
- :clusters="matching"
527
- />
538
+ <!-- All mode: compact intentionally omits the target list since the parent handles cluster visibility -->
539
+ <div
540
+ v-if="targetMode === 'all' && !isLocal && !compact"
541
+ class="row"
542
+ >
543
+ <div class="col span-6">
544
+ <TargetsList
545
+ class="target-list"
546
+ :clusters="matching"
547
+ :compact="compact"
548
+ />
549
+ </div>
528
550
  </div>
529
551
  </div>
530
552
  </template>
531
553
 
532
554
  <style lang="scss" scoped>
533
- .match-expressions-container {
555
+ .content-group {
534
556
  display: flex;
535
- align-items: start;
536
- border: 1px solid var(--border);
537
- border-radius: 5px;
557
+ flex-direction: column;
558
+ gap: var(--gap-md);
559
+ }
538
560
 
539
- .body {
540
- padding: 15px;
541
- width: 100%;
542
- }
561
+ .gap-md {
562
+ display: flex;
563
+ flex-direction: column;
564
+ gap: var(--gap-md);
565
+ }
566
+ .gap-20 {
567
+ display: flex;
568
+ flex-direction: column;
569
+ gap: 20px;
570
+ }
543
571
 
544
- .btn {
545
- margin: 5px;
546
- }
572
+ .targets-col {
573
+ position: relative;
574
+ }
575
+
576
+ .targets-col .target-list {
577
+ position: absolute;
578
+ top: 0;
579
+ right: 0;
580
+ bottom: 0;
581
+ left: 0;
547
582
  }
548
583
 
549
- .target-list {
550
- max-height: 320px;
584
+ .cluster-count-badge {
585
+ display: inline-flex;
586
+ padding: 2px 8px;
587
+ align-items: center;
588
+ border-radius: 30px;
589
+ border: 1px solid var(--rc-inactive-border);
590
+ background: var(--body-bg);
591
+ font-size: 12px;
592
+ line-height: 17px;
593
+ color: var(--body-text);
551
594
  }
552
595
  </style>
@@ -1,10 +1,13 @@
1
1
  <script>
2
+ import { RcButton } from '@components/RcButton';
2
3
  import { NAME } from '@shell/config/product/fleet';
3
4
 
4
5
  export default {
5
6
 
6
7
  name: 'FleetIntro',
7
8
 
9
+ components: { RcButton },
10
+
8
11
  props: {
9
12
  schema: {
10
13
  type: Object,
@@ -63,12 +66,13 @@ export default {
63
66
  v-if="canCreate"
64
67
  class="actions"
65
68
  >
66
- <router-link
69
+ <rc-button
70
+ size="large"
71
+ variant="secondary"
67
72
  :to="to"
68
- class="btn role-secondary"
69
73
  >
70
74
  {{ t(`fleet.${ labelKey }.intro.add`) }}
71
- </router-link>
75
+ </rc-button>
72
76
  </div>
73
77
  </div>
74
78
  </template>