@rancher/shell 3.0.5-rc.2 → 3.0.5-rc.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 (289) hide show
  1. package/assets/data/aws-regions.json +2 -0
  2. package/assets/images/icons/document.svg +3 -0
  3. package/assets/images/vendor/cognito.svg +1 -0
  4. package/assets/styles/app.scss +1 -0
  5. package/assets/styles/base/_basic.scss +10 -0
  6. package/assets/styles/base/_spacing.scss +29 -0
  7. package/assets/styles/global/_layout.scss +1 -2
  8. package/assets/styles/themes/_dark.scss +25 -0
  9. package/assets/styles/themes/_light.scss +65 -0
  10. package/assets/translations/en-us.yaml +377 -37
  11. package/assets/translations/zh-hans.yaml +8 -15
  12. package/chart/monitoring/index.vue +1 -1
  13. package/components/AsyncButton.vue +2 -0
  14. package/components/Certificates.vue +5 -0
  15. package/components/CodeMirror.vue +3 -3
  16. package/components/CruResource.vue +103 -15
  17. package/components/ExplorerProjectsNamespaces.vue +7 -2
  18. package/components/FilterPanel.vue +156 -0
  19. package/components/FixedBanner.vue +19 -5
  20. package/components/{fleet/ForceDirectedTreeChart/index.vue → ForceDirectedTreeChart.vue} +47 -41
  21. package/components/IconOrSvg.vue +14 -35
  22. package/components/PaginatedResourceTable.vue +7 -0
  23. package/components/PromptRemove.vue +5 -1
  24. package/components/Resource/Detail/Card/PodsCard/Bubble.vue +13 -0
  25. package/components/Resource/Detail/Card/PodsCard/composable.ts +30 -0
  26. package/components/Resource/Detail/Card/PodsCard/index.vue +118 -0
  27. package/components/Resource/Detail/Card/ResourceUsageCard/composable.ts +51 -0
  28. package/components/Resource/Detail/Card/ResourceUsageCard/index.vue +79 -0
  29. package/components/Resource/Detail/Card/Scaler.vue +89 -0
  30. package/components/Resource/Detail/Card/StateCard/composables.ts +112 -0
  31. package/components/Resource/Detail/Card/StateCard/index.vue +39 -0
  32. package/components/Resource/Detail/Card/VerticalGap.vue +11 -0
  33. package/components/Resource/Detail/Card/__tests__/Card.test.ts +36 -0
  34. package/components/Resource/Detail/Card/__tests__/PodsCard.test.ts +84 -0
  35. package/components/Resource/Detail/Card/__tests__/ResourceUsageCard.test.ts +72 -0
  36. package/components/Resource/Detail/Card/__tests__/Scaler.test.ts +87 -0
  37. package/components/Resource/Detail/Card/__tests__/StateCard.test.ts +53 -0
  38. package/components/Resource/Detail/Card/__tests__/VerticalGap.test.ts +14 -0
  39. package/components/Resource/Detail/Card/__tests__/index.test.ts +36 -0
  40. package/components/Resource/Detail/Card/index.vue +56 -0
  41. package/components/Resource/Detail/Metadata/Annotations/__tests__/index.test.ts +19 -0
  42. package/components/Resource/Detail/Metadata/Annotations/composable.ts +12 -0
  43. package/components/Resource/Detail/Metadata/Annotations/index.vue +26 -0
  44. package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/index.test.ts +103 -0
  45. package/components/Resource/Detail/Metadata/IdentifyingInformation/composable.ts +281 -0
  46. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +111 -0
  47. package/components/Resource/Detail/Metadata/KeyValue.vue +130 -0
  48. package/components/Resource/Detail/Metadata/Labels/__tests__/index.test.ts +18 -0
  49. package/components/Resource/Detail/Metadata/Labels/composable.ts +12 -0
  50. package/components/Resource/Detail/Metadata/Labels/index.vue +27 -0
  51. package/components/Resource/Detail/Metadata/Rectangle.vue +32 -0
  52. package/components/Resource/Detail/Metadata/__tests__/KeyValue.test.ts +107 -0
  53. package/components/Resource/Detail/Metadata/__tests__/Rectangle.test.ts +24 -0
  54. package/components/Resource/Detail/Metadata/__tests__/index.test.ts +91 -0
  55. package/components/Resource/Detail/Metadata/composables.ts +29 -0
  56. package/components/Resource/Detail/Metadata/index.vue +66 -0
  57. package/components/Resource/Detail/Page.vue +22 -0
  58. package/components/Resource/Detail/PercentageBar.vue +40 -0
  59. package/components/Resource/Detail/ResourceRow.vue +119 -0
  60. package/components/Resource/Detail/SpacedRow.vue +14 -0
  61. package/components/Resource/Detail/StatusBar.vue +59 -0
  62. package/components/Resource/Detail/StatusRow.vue +61 -0
  63. package/components/Resource/Detail/TitleBar/Title.vue +13 -0
  64. package/components/Resource/Detail/TitleBar/Top.vue +14 -0
  65. package/components/Resource/Detail/TitleBar/__tests__/Title.test.ts +17 -0
  66. package/components/Resource/Detail/TitleBar/__tests__/Top.test.ts +17 -0
  67. package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +142 -0
  68. package/components/Resource/Detail/TitleBar/composable.ts +31 -0
  69. package/components/Resource/Detail/TitleBar/index.vue +124 -0
  70. package/components/Resource/Detail/Top/index.vue +34 -0
  71. package/components/Resource/Detail/__tests__/Page.test.ts +32 -0
  72. package/components/ResourceDetail/Masthead.vue +0 -1
  73. package/components/ResourceDetail/__tests__/index.test.ts +114 -0
  74. package/components/ResourceDetail/index.vue +64 -562
  75. package/components/ResourceDetail/legacy.vue +545 -0
  76. package/components/ResourceList/index.vue +2 -1
  77. package/components/ResourceTable.vue +41 -7
  78. package/components/SlideInPanelManager.vue +77 -10
  79. package/components/SortableTable/index.vue +13 -2
  80. package/components/SortableTable/selection.js +22 -9
  81. package/components/StatusBadge.vue +6 -4
  82. package/components/SubtleLink.vue +25 -0
  83. package/components/Tabbed/index.vue +6 -0
  84. package/components/Wizard.vue +12 -1
  85. package/components/YamlEditor.vue +1 -1
  86. package/components/__tests__/AsyncButton.test.ts +39 -0
  87. package/components/__tests__/CruResource.test.ts +63 -0
  88. package/components/__tests__/FilterPanel.test.ts +81 -0
  89. package/components/__tests__/PromptModal.test.ts +0 -2
  90. package/components/auth/AuthBanner.vue +2 -3
  91. package/components/auth/RoleDetailEdit.vue +45 -3
  92. package/components/auth/login/oidc.vue +6 -1
  93. package/components/fleet/FleetApplications.vue +181 -0
  94. package/components/fleet/FleetHelmOps.vue +115 -0
  95. package/components/fleet/FleetIntro.vue +58 -28
  96. package/components/fleet/FleetNoWorkspaces.vue +5 -1
  97. package/components/fleet/FleetOCIStorageSecret.vue +171 -0
  98. package/components/fleet/FleetRepos.vue +38 -76
  99. package/components/fleet/FleetResources.vue +50 -22
  100. package/components/fleet/FleetSummary.vue +26 -51
  101. package/components/fleet/__tests__/FleetOCIStorageSecret.test.ts +213 -0
  102. package/components/fleet/__tests__/FleetSummary.test.ts +39 -39
  103. package/components/fleet/dashboard/Empty.vue +73 -0
  104. package/components/fleet/dashboard/ResourceCard.vue +183 -0
  105. package/components/fleet/dashboard/ResourceCardSummary.vue +199 -0
  106. package/components/fleet/dashboard/ResourceDetails.vue +196 -0
  107. package/components/fleet/dashboard/ResourcePanel.vue +376 -0
  108. package/components/form/ArrayList.vue +139 -117
  109. package/components/form/BannerSettings.vue +145 -96
  110. package/components/form/KeyValue.vue +10 -7
  111. package/components/form/LabeledSelect.vue +9 -2
  112. package/components/form/MatchExpressions.vue +5 -1
  113. package/components/form/NameNsDescription.vue +1 -1
  114. package/components/form/ResourceSelector.vue +26 -23
  115. package/components/form/ResourceTabs/index.vue +2 -1
  116. package/components/form/Select.vue +9 -2
  117. package/components/form/SimpleSecretSelector.vue +8 -2
  118. package/components/form/UnitInput.vue +13 -0
  119. package/components/form/ValueFromResource.vue +31 -19
  120. package/components/form/__tests__/ArrayList.test.ts +32 -0
  121. package/components/form/__tests__/KeyValue.test.ts +36 -0
  122. package/components/form/__tests__/LabeledSelect.test.ts +33 -0
  123. package/components/form/__tests__/Select.test.ts +34 -1
  124. package/components/form/__tests__/UnitInput.test.ts +23 -1
  125. package/components/formatter/ClusterLink.vue +5 -8
  126. package/components/formatter/Description.vue +30 -0
  127. package/components/formatter/FleetApplicationClustersReady.vue +77 -0
  128. package/components/formatter/FleetApplicationSource.vue +71 -0
  129. package/components/formatter/FleetSummaryGraph.vue +7 -0
  130. package/components/formatter/__tests__/ClusterLink.test.ts +2 -32
  131. package/components/nav/Header.vue +8 -7
  132. package/components/nav/NamespaceFilter.vue +1 -1
  133. package/components/nav/TopLevelMenu.helper.ts +55 -34
  134. package/components/nav/TopLevelMenu.vue +11 -0
  135. package/components/nav/Type.vue +4 -1
  136. package/components/nav/WindowManager/index.vue +1 -0
  137. package/composables/useI18n.ts +12 -11
  138. package/config/labels-annotations.js +14 -11
  139. package/config/product/auth.js +1 -0
  140. package/config/product/explorer.js +16 -13
  141. package/config/product/fleet.js +70 -17
  142. package/config/product/manager.js +1 -28
  143. package/config/query-params.js +3 -1
  144. package/config/roles.ts +1 -0
  145. package/config/router/routes.js +20 -2
  146. package/config/secret.ts +15 -0
  147. package/config/settings.ts +14 -15
  148. package/config/table-headers.js +59 -27
  149. package/config/types.js +2 -0
  150. package/core/plugin-helpers.ts +3 -2
  151. package/detail/catalog.cattle.io.app.vue +0 -1
  152. package/detail/fleet.cattle.io.cluster.vue +28 -15
  153. package/detail/fleet.cattle.io.gitrepo.vue +10 -1
  154. package/detail/fleet.cattle.io.helmop.vue +157 -0
  155. package/detail/provisioning.cattle.io.cluster.vue +13 -3
  156. package/detail/service.vue +0 -1
  157. package/detail/workload/index.vue +21 -34
  158. package/dialog/ExtensionCatalogUninstallDialog.vue +14 -8
  159. package/dialog/HelmOpForceUpdateDialog.vue +132 -0
  160. package/dialog/RedeployWorkloadDialog.vue +164 -0
  161. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +56 -67
  162. package/edit/__tests__/service.test.ts +2 -1
  163. package/edit/auth/oidc.vue +159 -93
  164. package/edit/fleet.cattle.io.gitrepo.vue +26 -33
  165. package/edit/fleet.cattle.io.helmop.vue +997 -0
  166. package/edit/management.cattle.io.fleetworkspace.vue +43 -10
  167. package/edit/networking.k8s.io.networkpolicy/PolicyRule.vue +3 -14
  168. package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +57 -62
  169. package/edit/networking.k8s.io.networkpolicy/PolicyRules.vue +3 -14
  170. package/edit/networking.k8s.io.networkpolicy/__tests__/PolicyRuleTarget.test.ts +72 -41
  171. package/edit/networking.k8s.io.networkpolicy/__tests__/utils/mock.json +17 -1
  172. package/edit/networking.k8s.io.networkpolicy/index.vue +18 -30
  173. package/edit/provisioning.cattle.io.cluster/index.vue +21 -73
  174. package/edit/service.vue +13 -28
  175. package/list/fleet.cattle.io.gitrepo.vue +1 -1
  176. package/list/fleet.cattle.io.helmop.vue +108 -0
  177. package/list/namespace.vue +5 -2
  178. package/list/workload.vue +6 -1
  179. package/mixins/auth-config.js +8 -1
  180. package/mixins/preset.js +100 -0
  181. package/mixins/resource-fetch-api-pagination.js +57 -43
  182. package/mixins/resource-fetch.js +15 -6
  183. package/mixins/resource-table-watch.js +45 -0
  184. package/models/__tests__/chart.test.ts +273 -0
  185. package/models/__tests__/fleet.cattle.io.gitrepo.test.ts +1 -1
  186. package/models/__tests__/workload.test.ts +1 -0
  187. package/models/chart.js +144 -2
  188. package/models/cluster/node.js +1 -0
  189. package/models/cluster.js +32 -2
  190. package/models/fleet-application.js +385 -0
  191. package/models/fleet.cattle.io.bundle.js +9 -8
  192. package/models/fleet.cattle.io.gitrepo.js +41 -365
  193. package/models/fleet.cattle.io.helmop.js +228 -0
  194. package/models/management.cattle.io.authconfig.js +1 -0
  195. package/models/management.cattle.io.cluster.js +0 -20
  196. package/models/management.cattle.io.fleetworkspace.js +12 -0
  197. package/models/management.cattle.io.node.js +7 -22
  198. package/models/management.cattle.io.nodepool.js +12 -0
  199. package/models/namespace.js +5 -0
  200. package/models/provisioning.cattle.io.cluster.js +18 -64
  201. package/models/service.js +24 -9
  202. package/models/workload.js +84 -49
  203. package/package.json +2 -1
  204. package/pages/auth/verify.vue +13 -1
  205. package/pages/c/_cluster/apps/charts/AddRepoLink.vue +37 -0
  206. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +80 -0
  207. package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +54 -0
  208. package/pages/c/_cluster/apps/charts/StatusLabel.vue +33 -0
  209. package/pages/c/_cluster/apps/charts/index.vue +302 -484
  210. package/pages/c/_cluster/apps/charts/install.vue +0 -1
  211. package/pages/c/_cluster/explorer/EventsTable.vue +1 -1
  212. package/pages/c/_cluster/explorer/index.vue +11 -0
  213. package/pages/c/_cluster/fleet/__tests__/index.test.ts +426 -0
  214. package/pages/c/_cluster/fleet/application/_resource/_id.vue +14 -0
  215. package/pages/c/_cluster/fleet/application/_resource/create.vue +14 -0
  216. package/pages/c/_cluster/fleet/application/create.vue +340 -0
  217. package/pages/c/_cluster/fleet/application/index.vue +139 -0
  218. package/pages/c/_cluster/fleet/graph/config.js +277 -0
  219. package/pages/c/_cluster/fleet/index.vue +772 -330
  220. package/pages/c/_cluster/longhorn/index.vue +2 -2
  221. package/pages/c/_cluster/settings/banners.vue +56 -2
  222. package/pages/c/_cluster/settings/performance.vue +7 -26
  223. package/pages/explorer/resource/detail/configmap.vue +19 -0
  224. package/pages/home.vue +11 -52
  225. package/plugins/clean-html.js +2 -0
  226. package/plugins/dashboard-store/__tests__/actions.test.ts +4 -1
  227. package/plugins/dashboard-store/actions.js +153 -30
  228. package/plugins/dashboard-store/getters.js +108 -24
  229. package/plugins/dashboard-store/mutations.js +61 -12
  230. package/plugins/dashboard-store/resource-class.js +36 -4
  231. package/plugins/steve/__tests__/getters.test.ts +18 -11
  232. package/plugins/steve/__tests__/steve-class.test.ts +1 -0
  233. package/plugins/steve/__tests__/subscribe.spec.ts +66 -1
  234. package/plugins/steve/actions.js +37 -12
  235. package/plugins/steve/getters.js +39 -10
  236. package/plugins/steve/steve-class.js +5 -0
  237. package/plugins/steve/steve-pagination-utils.ts +213 -50
  238. package/plugins/steve/subscribe.js +229 -42
  239. package/plugins/steve/worker/web-worker.advanced.js +3 -1
  240. package/rancher-components/BadgeState/BadgeState.vue +3 -1
  241. package/rancher-components/Banner/Banner.test.ts +51 -3
  242. package/rancher-components/Banner/Banner.vue +28 -6
  243. package/rancher-components/Form/Checkbox/Checkbox.vue +2 -2
  244. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +5 -1
  245. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +21 -1
  246. package/rancher-components/RcItemCard/RcItemCard.test.ts +189 -0
  247. package/rancher-components/RcItemCard/RcItemCard.vue +425 -0
  248. package/rancher-components/RcItemCard/RcItemCardAction.vue +24 -0
  249. package/rancher-components/RcItemCard/index.ts +2 -0
  250. package/store/auth.js +1 -0
  251. package/store/catalog.js +62 -24
  252. package/store/features.js +0 -1
  253. package/store/index.js +33 -14
  254. package/store/slideInPanel.ts +6 -0
  255. package/store/type-map.js +1 -0
  256. package/store/type-map.utils.ts +45 -2
  257. package/types/fleet.d.ts +36 -1
  258. package/types/kube/kube-api.ts +22 -0
  259. package/types/resources/settings.d.ts +19 -5
  260. package/types/shell/index.d.ts +595 -471
  261. package/types/store/dashboard-store.types.ts +41 -4
  262. package/types/store/pagination.types.ts +25 -3
  263. package/types/store/subscribe.types.ts +50 -0
  264. package/utils/auth.js +32 -3
  265. package/utils/cluster.js +24 -20
  266. package/utils/fleet-types.ts +0 -0
  267. package/utils/fleet.ts +200 -1
  268. package/utils/grafana.js +1 -0
  269. package/utils/object.js +0 -12
  270. package/utils/pagination-utils.ts +32 -3
  271. package/utils/pagination-wrapper.ts +132 -50
  272. package/utils/perf-setting.utils.ts +28 -0
  273. package/utils/selector-typed.ts +205 -0
  274. package/utils/selector.js +29 -6
  275. package/utils/settings.ts +4 -1
  276. package/utils/style.ts +39 -0
  277. package/utils/uiplugins.ts +10 -6
  278. package/utils/v-sphere.ts +5 -1
  279. package/utils/validators/formRules/__tests__/index.test.ts +36 -3
  280. package/utils/validators/formRules/index.ts +10 -3
  281. package/utils/window.js +11 -7
  282. package/components/__tests__/ApplicationCard.test.ts +0 -27
  283. package/components/cards/ApplicationCard.vue +0 -145
  284. package/components/fleet/ForceDirectedTreeChart/chartIcons.js +0 -17
  285. package/components/formatter/RKETemplateName.vue +0 -37
  286. package/config/secret.js +0 -14
  287. package/dialog/SaveAsRKETemplateDialog.vue +0 -139
  288. package/pages/c/_cluster/fleet/GitRepoGraphConfig.js +0 -249
  289. /package/{components/form/SSHKnownHosts → dialog}/__tests__/KnownHostsEditDialog.test.ts +0 -0
@@ -0,0 +1,196 @@
1
+ <script lang="ts">
2
+ import { PropType } from 'vue';
3
+ import { FLEET } from '@shell/config/types';
4
+ import LabeledSelect from '@shell/components/form/LabeledSelect.vue';
5
+ import FleetResources from '@shell/components/fleet/FleetResources.vue';
6
+ import { RcButton } from '@components/RcButton';
7
+ import { FleetDashboardState } from '@shell/types/fleet';
8
+ import FleetApplicationSource from '@shell/components/formatter/FleetApplicationSource.vue';
9
+
10
+ export default {
11
+ name: 'FleetDashboardResourceDetails',
12
+
13
+ components: {
14
+ LabeledSelect,
15
+ FleetResources,
16
+ FleetApplicationSource,
17
+ RcButton,
18
+ },
19
+
20
+ props: {
21
+ value: {
22
+ type: Object,
23
+ required: true
24
+ },
25
+
26
+ statePanel: {
27
+ type: Object as PropType<FleetDashboardState>,
28
+ required: true
29
+ },
30
+
31
+ workspace: {
32
+ type: Object,
33
+ required: true
34
+ }
35
+ },
36
+
37
+ data() {
38
+ return {
39
+ FLEET,
40
+ clusterId: '',
41
+ detailLocation: {
42
+ ...this.value._detailLocation,
43
+ name: 'c-cluster-fleet-application-resource-namespace-id'
44
+ }
45
+ };
46
+ },
47
+
48
+ mounted() {
49
+ this.clusterId = this.clusters[0]?.value || '';
50
+ },
51
+
52
+ computed: {
53
+ clusters() {
54
+ return this.value.targetClusters.map((cluster: { id: string, nameDisplay: string }) => ({
55
+ label: cluster.nameDisplay,
56
+ value: cluster.id
57
+ }));
58
+ },
59
+
60
+ noResources() {
61
+ return !this.value.resourcesStatuses?.length;
62
+ },
63
+ },
64
+
65
+ methods: {
66
+ closePanel() {
67
+ this.$store.commit('slideInPanel/close');
68
+ }
69
+ }
70
+ };
71
+ </script>
72
+
73
+ <template>
74
+ <div class="details-panel">
75
+ <div
76
+ class="header"
77
+ :data-testid="'fleet-dashboard-resource-details-header'"
78
+ >
79
+ <div class="title">
80
+ <i :class="value.dashboardIcon" />
81
+ <router-link
82
+ class="label"
83
+ :to="detailLocation"
84
+ >
85
+ {{ value.id }}
86
+ </router-link>
87
+ <i
88
+ v-if="statePanel.id !== 'success'"
89
+ class="ml-5 state-icon"
90
+ :class="statePanel.icon"
91
+ :style="{ color: statePanel.color }"
92
+ />
93
+ </div>
94
+ <RcButton
95
+ small
96
+ ghost
97
+ data-testid="slide-in-close"
98
+ :aria-label="'slide-in-close'"
99
+ tabindex="0"
100
+ @click="closePanel"
101
+ @keydown.space.enter.stop.prevent="closePanel"
102
+ >
103
+ <i class="icon icon-close" />
104
+ </RcButton>
105
+ </div>
106
+
107
+ <h3>
108
+ {{ t('fleet.dashboard.source') }}
109
+ </h3>
110
+ <div class="mb-15">
111
+ <FleetApplicationSource
112
+ v-if="value.source.value"
113
+ :row="value"
114
+ />
115
+ <div
116
+ v-else
117
+ class="text-muted"
118
+ >
119
+ &mdash;
120
+ </div>
121
+ </div>
122
+
123
+ <h3>
124
+ {{ t('fleet.dashboard.resources') }}
125
+ </h3>
126
+ <FleetResources
127
+ :rows="value.resourcesStatuses"
128
+ :cluster-id="clusterId"
129
+ :search="!noResources"
130
+ >
131
+ <template
132
+ v-if="!noResources"
133
+ #header-left
134
+ >
135
+ <div class="row">
136
+ <div class="col span-10">
137
+ <LabeledSelect
138
+ v-model:value="clusterId"
139
+ :label="'Cluster'"
140
+ :options="clusters"
141
+ :mode="'edit'"
142
+ :disabled="workspace.id === 'fleet-local'"
143
+ />
144
+ </div>
145
+ </div>
146
+ </template>
147
+ </FleetResources>
148
+ </div>
149
+ </template>
150
+
151
+ <style lang="scss">
152
+ .details-panel {
153
+ padding: 10px;
154
+
155
+ .sortable-table-header {
156
+ .fixed-header-actions {
157
+ align-items: center;
158
+ }
159
+ }
160
+
161
+ .header {
162
+ display: flex;
163
+ align-items: center;
164
+ padding: 0;
165
+ margin: 0 0 20px 0;
166
+
167
+ .title {
168
+ display: flex;
169
+ align-items: center;
170
+ flex: 1;
171
+ font-style: normal;
172
+ font-weight: 600;
173
+ font-size: 18px;
174
+
175
+ .icon {
176
+ font-size: 2em;
177
+ margin-right: 16px;
178
+ }
179
+
180
+ .label {
181
+ margin-right: 8px;
182
+ }
183
+
184
+ .state-icon {
185
+ font-size: 1.5em;
186
+ }
187
+ }
188
+ }
189
+ }
190
+
191
+ .col {
192
+ .labeled-select {
193
+ min-width: 250px;
194
+ }
195
+ }
196
+ </style>
@@ -0,0 +1,376 @@
1
+ <script lang="ts">
2
+ import { markRaw, PropType } from 'vue';
3
+ import { mapGetters } from 'vuex';
4
+ import { lcFirst } from '@shell/utils/string';
5
+ import {
6
+ Chart, ChartOptions, ChartEvent, TooltipItem, registerables
7
+ } from 'chart.js';
8
+ import { BadgeState } from '@components/BadgeState';
9
+ import FleetUtils from '@shell/utils/fleet';
10
+ import { FleetDashboardResourceStates, FleetDashboardState } from '@shell/types/fleet';
11
+
12
+ type ChartOptionsType = ChartOptions<'doughnut'>;
13
+ type ChartType = Chart<'doughnut', any[], ChartOptionsType>;
14
+
15
+ interface DataType {
16
+ chartId: string,
17
+ chart: ChartType | null
18
+ }
19
+
20
+ type StatePanel = FleetDashboardState & { count: number }
21
+
22
+ Chart.register(...registerables);
23
+
24
+ export default {
25
+
26
+ name: 'FleetDashboardResourcePanel',
27
+
28
+ emits: ['click:state'],
29
+
30
+ components: { BadgeState },
31
+
32
+ props: {
33
+ states: {
34
+ type: Array as PropType<FleetDashboardResourceStates[]>,
35
+ default: () => []
36
+ },
37
+
38
+ workspace: {
39
+ type: String,
40
+ default: ''
41
+ },
42
+
43
+ type: {
44
+ type: String,
45
+ default: ''
46
+ },
47
+
48
+ showChart: {
49
+ type: Boolean,
50
+ default: true
51
+ },
52
+
53
+ selectable: {
54
+ type: Boolean,
55
+ default: true
56
+ },
57
+
58
+ selectedStates: {
59
+ type: Object as PropType<Record<string, boolean>>,
60
+ default: () => ({})
61
+ },
62
+ },
63
+
64
+ data(): DataType {
65
+ return {
66
+ chartId: `${ this.workspace }-${ this.type }`,
67
+ chart: null,
68
+ };
69
+ },
70
+
71
+ mounted() {
72
+ if (this.showChart) {
73
+ const container = document.getElementById(`${ this.chartId }-container`);
74
+
75
+ if (!container) {
76
+ return;
77
+ }
78
+
79
+ const canvas = document.createElement('canvas');
80
+
81
+ canvas.id = this.chartId;
82
+ canvas.className = 'chart';
83
+
84
+ container.append(canvas);
85
+
86
+ const data = this.buildChartData();
87
+ const options: ChartOptionsType = {
88
+ responsive: true,
89
+ elements: {
90
+ arc: {
91
+ borderWidth: 4,
92
+ borderColor: this.bgColor,
93
+ hoverBorderColor: this.bgColor,
94
+ }
95
+ },
96
+ plugins: {
97
+ legend: { display: false },
98
+ title: { display: false },
99
+ tooltip: {
100
+ yAlign: 'bottom',
101
+ callbacks: {
102
+ title: (ctx: TooltipItem<'doughnut'>[]) => {
103
+ const v = ctx[0];
104
+
105
+ return `${ v?.formattedValue } ${ lcFirst(v.label) }`;
106
+ },
107
+ label: () => '',
108
+ }
109
+ }
110
+ },
111
+ cutout: 13,
112
+ onHover: (event: any) => {
113
+ if (this.selectable && event?.native?.target?.style) {
114
+ event.native.target.style.cursor = 'pointer';
115
+ }
116
+ },
117
+ onClick: (_: ChartEvent, element?: { index: number }[]) => {
118
+ const idx = element?.[0]?.index;
119
+
120
+ if (idx === undefined) {
121
+ return;
122
+ }
123
+
124
+ const state = this.statePanels.find(({ index }) => idx === index);
125
+
126
+ this.selectState(state);
127
+ },
128
+ animations: { borderColor: { duration: 0 } }
129
+ };
130
+
131
+ const chart = new Chart(canvas, {
132
+ type: 'doughnut',
133
+ data,
134
+ options,
135
+ }) as ChartType;
136
+
137
+ this.chart = markRaw(chart);
138
+ }
139
+ },
140
+
141
+ computed: {
142
+ ...mapGetters({ theme: 'prefs/theme' }),
143
+
144
+ statePanels() {
145
+ const out: StatePanel[] = [];
146
+
147
+ this.states.forEach((state) => {
148
+ const {
149
+ statePanel,
150
+ resources
151
+ } = state;
152
+
153
+ const exists = out.find((s) => s.id === statePanel.id);
154
+
155
+ if (exists) {
156
+ exists.count += resources.length;
157
+ } else {
158
+ out.push({
159
+ ...statePanel,
160
+ count: resources.length
161
+ });
162
+ }
163
+ });
164
+
165
+ return out;
166
+ },
167
+
168
+ count() {
169
+ return this.statePanels.reduce((acc, state) => acc + state.count, 0);
170
+ },
171
+
172
+ typeLabel() {
173
+ return this.t(`typeLabel."${ this.type }"`, { count: this.count });
174
+ },
175
+ },
176
+
177
+ watch: {
178
+ statePanels: {
179
+ handler: 'updateStates',
180
+ deep: true,
181
+ },
182
+ theme() {
183
+ if (this.chart) {
184
+ this.chart.update();
185
+ }
186
+ },
187
+ },
188
+
189
+ methods: {
190
+ buildChartData() {
191
+ const chartStates = FleetUtils.dashboardStates;
192
+
193
+ const labels = chartStates.map(({ label }) => label);
194
+ const backgroundColor = chartStates.map(({ color }) => color);
195
+
196
+ const data = this.getChartStates(this.statePanels);
197
+
198
+ return {
199
+ labels,
200
+ datasets: [
201
+ {
202
+ data,
203
+ backgroundColor,
204
+ },
205
+ ],
206
+ };
207
+ },
208
+
209
+ updateStates(value: StatePanel[]) {
210
+ if (this.chart) {
211
+ this.chart.data.datasets.forEach((dataset) => {
212
+ dataset.data = this.getChartStates(value);
213
+ });
214
+
215
+ this.chart.update();
216
+ }
217
+ },
218
+
219
+ getChartStates(states: StatePanel[]) {
220
+ return FleetUtils.dashboardStates.map(({ id }) => states.find((s) => s.id === id)?.count || 0);
221
+ },
222
+
223
+ selectState(state?: StatePanel) {
224
+ if (!this.selectable) {
225
+ return;
226
+ }
227
+
228
+ this.$emit('click:state', state?.id);
229
+ },
230
+
231
+ onClickBadge(state: StatePanel) {
232
+ this.selectState(state);
233
+ },
234
+
235
+ bgColor() {
236
+ return getComputedStyle(document.body).getPropertyValue('--body-bg');
237
+ }
238
+ }
239
+ };
240
+ </script>
241
+
242
+ <template>
243
+ <div class="panel">
244
+ <div
245
+ v-if="showChart"
246
+ :id="chartId + '-container'"
247
+ :data-testid="'chart-container'"
248
+ class="panel-chart-container"
249
+ />
250
+ <div class="details">
251
+ <div
252
+ class="description"
253
+ :data-testid="'description'"
254
+ >
255
+ <span class="count">{{ count }}</span>
256
+ <span class="label">{{ typeLabel }}</span>
257
+ </div>
258
+ <div class="states">
259
+ <BadgeState
260
+ v-for="(state, i) in statePanels"
261
+ :key="i"
262
+ class="badge"
263
+ :tabindex="selectable ? 0 : undefined"
264
+ :role="selectable ? 'button' : undefined"
265
+ :aria-label="selectable ? t('fleet.dashboard.state') + '-' + state.id : undefined"
266
+ :class="{
267
+ ['selectable']: selectable,
268
+ ['selected']: selectable && selectedStates?.[state.id]
269
+ }"
270
+ :color="state.stateBackground"
271
+ :label="` ${ state.count } `"
272
+ :icon="state.icon"
273
+ @click="onClickBadge(state)"
274
+ @keydown.space.enter.stop.prevent="onClickBadge(state)"
275
+ >
276
+ <template
277
+ v-if="selectable && selectedStates?.[state.id]"
278
+ #content-right
279
+ >
280
+ <i class="icon icon-close ml-5" />
281
+ </template>
282
+ </BadgeState>
283
+ </div>
284
+ </div>
285
+ </div>
286
+ </template>
287
+
288
+ <style lang="scss" scoped>
289
+ .panel {
290
+ display: flex;
291
+ align-items: center;
292
+ gap: 5px;
293
+
294
+ .panel-chart-container {
295
+ padding: 3px;
296
+ min-width: 80px;
297
+ width: 80px;
298
+ }
299
+
300
+ .details {
301
+ padding: 5px;
302
+
303
+ .description {
304
+ .count {
305
+ font-size: 25px;
306
+ font-weight: bold;
307
+ margin-right: 4px;
308
+ }
309
+
310
+ .label {
311
+ font-size: 20px;
312
+ }
313
+ }
314
+
315
+ .states {
316
+ display: flex;
317
+ align-items: center;
318
+ margin-top: 4px;
319
+
320
+ .badge {
321
+ margin: 2px 5px 2px 2px;
322
+ border: 0;
323
+
324
+ &:focus-visible {
325
+ @include focus-outline;
326
+ }
327
+
328
+ &.selectable {
329
+ cursor: pointer;
330
+ }
331
+
332
+ &.bg-error {
333
+ background: var(--click-badge-error-bg);
334
+ color: var(--click-badge-error);
335
+ }
336
+
337
+ &.bg-warning {
338
+ background: var(--click-badge-warning-bg);
339
+ color: var(--click-badge-warning);
340
+ }
341
+
342
+ &.bg-success {
343
+ background: var(--click-badge-success-bg);
344
+ color: var(--click-badge-success);
345
+ }
346
+
347
+ &.bg-info {
348
+ background: var(--click-badge-info-bg);
349
+ color: var(--click-badge-info);
350
+ }
351
+
352
+ &.selected {
353
+ margin: 0 5px 0 0;
354
+
355
+ &.bg-error {
356
+ border: 2px solid var(--click-badge-error-border);
357
+ }
358
+
359
+ &.bg-warning {
360
+ border: 2px solid var(--click-badge-warning-border);
361
+ }
362
+
363
+ &.bg-success {
364
+ border: 2px solid var(--click-badge-success-border);
365
+ }
366
+
367
+ &.bg-info {
368
+ border: 2px solid var(--click-badge-info-border);
369
+ }
370
+ }
371
+ }
372
+ }
373
+ }
374
+ }
375
+
376
+ </style>