dashboard-shell-shell 1.0.121 → 1.0.1000000081

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 (372) hide show
  1. package/.DS_Store +0 -0
  2. package/assets/brand/harvester/favicon.png +0 -0
  3. package/assets/brand/suse/favicon.png +0 -0
  4. package/assets/icons/iconfont.css +19 -3
  5. package/assets/icons/iconfont.js +1 -1
  6. package/assets/icons/iconfont.json +28 -0
  7. package/assets/icons/iconfont.ttf +0 -0
  8. package/assets/icons/iconfont.woff +0 -0
  9. package/assets/icons/iconfont.woff2 +0 -0
  10. package/assets/images/pl/half-logo.svg +2 -23
  11. package/assets/styles/base/_functions.scss +0 -0
  12. package/assets/styles/base/_mixins.scss +0 -0
  13. package/assets/styles/base/_variables.scss +1 -1
  14. package/assets/styles/global/_labeled-input.scss +0 -1
  15. package/assets/styles/global/_layout.scss +1 -1
  16. package/assets/styles/global/_select.scss +4 -2
  17. package/assets/styles/global/_table.scss +5 -0
  18. package/assets/styles/vendor/vue-select.scss +2 -1
  19. package/assets/translations/en-us.yaml +1 -1
  20. package/assets/translations/zh-hans.yaml +25 -15
  21. package/chart/monitoring/index.vue +3 -1
  22. package/chart/monitoring/prometheus/index.vue +13 -10
  23. package/cloud-credential/aws.vue +2 -0
  24. package/components/ActionDropdown.vue +1 -1
  25. package/components/ActionDropdownShell.vue +71 -0
  26. package/components/ActionMenu.vue +2 -2
  27. package/components/ActionMenuShell.vue +3 -0
  28. package/components/AppModal.vue +84 -8
  29. package/components/AssignTo.vue +25 -11
  30. package/components/AsyncButton.vue +24 -7
  31. package/components/BannerGraphic.vue +1 -0
  32. package/components/ButtonDropdown.vue +26 -4
  33. package/components/ButtonGroup.vue +4 -0
  34. package/components/ButtonMultiAction.vue +1 -0
  35. package/components/CodeMirror.vue +19 -6
  36. package/components/CommunityLinks.vue +3 -3
  37. package/components/ConsumptionGauge.vue +24 -5
  38. package/components/CopyToClipboardText.vue +2 -1
  39. package/components/CruResource.vue +13 -7
  40. package/components/CruResourceFooter.vue +2 -2
  41. package/components/DashboardOptions.vue +29 -17
  42. package/components/DetailText.vue +5 -0
  43. package/components/DisableAuthProviderModal.vue +1 -0
  44. package/components/DotState.vue +84 -0
  45. package/components/ExplorerMembers.vue +1 -1
  46. package/components/ExplorerProjectsNamespaces.vue +89 -16
  47. package/components/FixedBanner.vue +19 -12
  48. package/components/GlobalRoleBindings.vue +5 -1
  49. package/components/GrafanaDashboard.vue +4 -4
  50. package/components/GrowlManager.vue +4 -1
  51. package/components/HardwareResourceGauge.vue +39 -3
  52. package/components/InfoBox.vue +3 -3
  53. package/components/InputOrDisplay.vue +28 -2
  54. package/components/LabelValue.vue +20 -1
  55. package/components/LandingPagePreference.vue +5 -3
  56. package/components/LocaleSelector.vue +39 -93
  57. package/components/ModalManager.vue +55 -0
  58. package/components/ModalWithCard.vue +13 -3
  59. package/components/MoveModal.vue +1 -0
  60. package/components/PodSecurityAdmission.vue +1 -1
  61. package/components/PromptChangePassword.vue +1 -1
  62. package/components/PromptModal.vue +16 -3
  63. package/components/PromptRemove.vue +29 -9
  64. package/components/PromptRestore.vue +1 -0
  65. package/components/ResourceCancelModal.vue +1 -0
  66. package/components/ResourceDetail/Masthead.vue +52 -17
  67. package/components/ResourceDetail/__tests__/Masthead.test.ts +5 -1
  68. package/components/ResourceDetail/index.vue +54 -16
  69. package/components/ResourceList/Masthead.vue +9 -4
  70. package/components/ResourceList/index.vue +4 -3
  71. package/components/ResourceTable.vue +1 -0
  72. package/components/SideNav.vue +20 -15
  73. package/components/SlideInPanelManager.vue +126 -0
  74. package/components/SortableTable/THead.vue +10 -4
  75. package/components/SortableTable/actions.js +1 -1
  76. package/components/SortableTable/index.vue +540 -554
  77. package/components/SortableTable/selection.js +2 -13
  78. package/components/StatusBadge.vue +77 -0
  79. package/components/Tabbed/Tab.vue +3 -3
  80. package/components/Tabbed/index.vue +47 -29
  81. package/components/Wizard.vue +2 -2
  82. package/components/YamlEditor.vue +1 -1
  83. package/components/__tests__/AsyncButton.test.ts +2 -2
  84. package/components/__tests__/FixedBanner.test.ts +3 -3
  85. package/components/__tests__/ModalManager.spec.ts +176 -0
  86. package/components/__tests__/SlideInPanelManager.spec.ts +166 -0
  87. package/components/auth/Principal.vue +10 -3
  88. package/components/auth/RoleDetailEdit.vue +1 -1
  89. package/components/auth/__tests__/RoleDetailEdit.test.ts +3 -2
  90. package/components/form/ArrayList.vue +123 -85
  91. package/components/form/ArrayListGrouped.vue +10 -2
  92. package/components/form/ArrayListSelect.vue +1 -1
  93. package/components/form/Command.vue +6 -15
  94. package/components/form/EnvVars.vue +16 -8
  95. package/components/form/Footer.vue +10 -7
  96. package/components/form/HealthCheck.vue +3 -3
  97. package/components/form/HookOption.vue +11 -16
  98. package/components/form/InputWithSelect.vue +6 -5
  99. package/components/form/KeyValue.vue +39 -10
  100. package/components/form/LabeledSelect.vue +73 -77
  101. package/components/form/Labels.vue +6 -3
  102. package/components/form/LifecycleHooks.vue +3 -3
  103. package/components/form/MatchExpressions.vue +42 -17
  104. package/components/form/NameNsDescription.vue +163 -116
  105. package/components/form/Networking.vue +20 -12
  106. package/components/form/NodeAffinity.vue +31 -23
  107. package/components/form/NodeScheduling.vue +13 -3
  108. package/components/form/Password.vue +11 -5
  109. package/components/form/PodAffinity.vue +47 -48
  110. package/components/form/Probe.vue +68 -66
  111. package/components/form/ResourceQuota/Namespace.vue +4 -4
  112. package/components/form/ResourceQuota/NamespaceRow.vue +5 -7
  113. package/components/form/ResourceQuota/Project.vue +9 -5
  114. package/components/form/ResourceQuota/ProjectRow.vue +4 -6
  115. package/components/form/ResourceSelector.vue +7 -9
  116. package/components/form/SSHKnownHosts/KnownHostsEditDialog.vue +6 -3
  117. package/components/form/SSHKnownHosts/__tests__/KnownHostsEditDialog.test.ts +12 -1
  118. package/components/form/SSHKnownHosts/index.vue +16 -2
  119. package/components/form/Security.vue +54 -56
  120. package/components/form/Select.vue +44 -7
  121. package/components/form/ShellInput.vue +5 -1
  122. package/components/form/SimpleSecretSelector.vue +29 -9
  123. package/components/form/Tolerations.vue +5 -1
  124. package/components/form/UnitInput.vue +10 -5
  125. package/components/form/ValueFromResource.vue +134 -121
  126. package/components/form/WorkloadPorts.vue +18 -18
  127. package/components/form/__tests__/ArrayList.test.ts +5 -2
  128. package/components/form/__tests__/MatchExpressions.test.ts +12 -12
  129. package/components/form/__tests__/NameNsDescription.test.ts +115 -14
  130. package/components/form/__tests__/Probe.test.ts +12 -8
  131. package/components/form/__tests__/SSHKnownHosts.test.ts +11 -0
  132. package/components/form/__tests__/Select.test.ts +37 -0
  133. package/components/form/__tests__/UnitInput.test.ts +4 -5
  134. package/components/formatter/BadgeStateFormatter.vue +8 -5
  135. package/components/formatter/InternalExternalIP.vue +2 -0
  136. package/components/formatter/LiveDate.vue +3 -3
  137. package/components/formatter/SecretData.vue +20 -7
  138. package/components/nav/Favorite.vue +5 -1
  139. package/components/nav/Group.vue +18 -4
  140. package/components/nav/Header.vue +39 -13
  141. package/components/nav/Jump.vue +7 -0
  142. package/components/nav/NamespaceFilter.vue +21 -11
  143. package/components/nav/Pinned.vue +1 -1
  144. package/components/nav/TopLevelMenu.vue +5 -17
  145. package/components/nav/Type.vue +30 -33
  146. package/components/nav/__tests__/TopLevelMenu.test.ts +0 -40
  147. package/components/rancherResourceDetail/Masthead.vue +769 -0
  148. package/components/rancherResourceDetail/__tests__/Masthead.test.ts +65 -0
  149. package/components/rancherResourceDetail/index.vue +591 -0
  150. package/components/rancherResourceList/Masthead-btn.vue +225 -0
  151. package/components/rancherResourceList/Masthead.vue +375 -0
  152. package/components/rancherResourceList/ResourceLoadingIndicator.vue +140 -0
  153. package/components/rancherResourceList/index.vue +307 -0
  154. package/components/rancherResourceList/resource-list.config.js +7 -0
  155. package/components/rancherResourceTable.vue +783 -0
  156. package/components/rancherSortableTable/THead.vue +561 -0
  157. package/components/rancherSortableTable/actions.js +153 -0
  158. package/components/rancherSortableTable/advanced-filtering.js +272 -0
  159. package/components/rancherSortableTable/debug.js +117 -0
  160. package/components/rancherSortableTable/filtering.js +290 -0
  161. package/components/rancherSortableTable/grouping.js +48 -0
  162. package/components/rancherSortableTable/index.vue +2712 -0
  163. package/components/rancherSortableTable/paging.js +155 -0
  164. package/components/rancherSortableTable/selection.js +629 -0
  165. package/components/rancherSortableTable/sortable-config.ts +4 -0
  166. package/components/rancherSortableTable/sorting.js +129 -0
  167. package/components/templates/blank.vue +4 -1
  168. package/components/templates/default.vue +8 -0
  169. package/components/templates/home.vue +10 -1
  170. package/components/templates/plain.vue +10 -1
  171. package/composables/focusTrap.ts +11 -3
  172. package/composables/useRuntimeFlag.ts +29 -0
  173. package/config/private-label.js +15 -10
  174. package/config/router/routes.js +21 -13
  175. package/config/store.js +4 -0
  176. package/config/table-headers.js +3 -2
  177. package/config/uiplugins.js +5 -1
  178. package/core/plugin-routes.ts +5 -115
  179. package/core/plugins.js +1 -1
  180. package/core/types.ts +23 -2
  181. package/detail/__tests__/autoscaling.horizontalpodautoscaler.test.ts +84 -23
  182. package/detail/autoscaling.horizontalpodautoscaler/index.vue +13 -3
  183. package/detail/provisioning.cattle.io.cluster.vue +72 -6
  184. package/dialog/AddCustomBadgeDialog.vue +1 -0
  185. package/dialog/DeactivateDriverDialog.vue +5 -4
  186. package/dialog/ForceMachineRemoveDialog.vue +6 -3
  187. package/dialog/GitRepoForceUpdateDialog.vue +1 -1
  188. package/dialog/ScalePoolDownDialog.vue +2 -2
  189. package/edit/__tests__/monitoring.coreos.com.prometheusrule.test.ts +16 -3
  190. package/edit/auth/__tests__/oidc.test.ts +162 -88
  191. package/edit/auth/azuread.vue +2 -1
  192. package/edit/auth/github.vue +1 -1
  193. package/edit/auth/googleoauth.vue +5 -1
  194. package/edit/auth/ldap/__tests__/config.test.ts +0 -14
  195. package/edit/auth/ldap/config.vue +0 -24
  196. package/edit/auth/ldap/index.vue +1 -1
  197. package/edit/auth/oidc.vue +39 -6
  198. package/edit/auth/saml.vue +1 -1
  199. package/edit/autoscaling.horizontalpodautoscaler/metric-identifier.vue +5 -2
  200. package/edit/cloudcredential.vue +24 -9
  201. package/edit/fleet.cattle.io.clustergroup.vue +5 -3
  202. package/edit/fleet.cattle.io.gitrepo.vue +2 -0
  203. package/edit/logging-flow/Match.vue +1 -1
  204. package/edit/logging.banzaicloud.io.output/__tests__/logging.banzaicloud.io.output.test.ts +40 -9
  205. package/edit/management.cattle.io.user.vue +28 -3
  206. package/edit/monitoring.coreos.com.alertmanagerconfig/auth.vue +19 -19
  207. package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +31 -31
  208. package/edit/monitoring.coreos.com.alertmanagerconfig/routeConfig.vue +36 -12
  209. package/edit/monitoring.coreos.com.alertmanagerconfig/types/email.vue +6 -6
  210. package/edit/monitoring.coreos.com.alertmanagerconfig/types/opsgenie.vue +10 -10
  211. package/edit/monitoring.coreos.com.alertmanagerconfig/types/pagerduty.vue +4 -4
  212. package/edit/monitoring.coreos.com.alertmanagerconfig/types/slack.vue +4 -4
  213. package/edit/monitoring.coreos.com.alertmanagerconfig/types/webhook.vue +1 -1
  214. package/edit/monitoring.coreos.com.receiver/auth.vue +29 -29
  215. package/edit/monitoring.coreos.com.receiver/types/email.vue +6 -6
  216. package/edit/monitoring.coreos.com.receiver/types/opsgenie.vue +10 -10
  217. package/edit/monitoring.coreos.com.receiver/types/pagerduty.vue +5 -5
  218. package/edit/monitoring.coreos.com.receiver/types/slack.vue +4 -4
  219. package/edit/namespace.vue +1 -2
  220. package/edit/networking.k8s.io.ingress/IngressClass.vue +7 -3
  221. package/edit/networking.k8s.io.ingress/RulePath.vue +1 -1
  222. package/edit/networking.k8s.io.ingress/__tests__/IngressClass.test.ts +58 -0
  223. package/edit/persistentvolume/__tests__/persistentvolume.test.ts +14 -2
  224. package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +4 -1
  225. package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +26 -9
  226. package/edit/provisioning.cattle.io.cluster/__tests__/Advanced.test.ts +8 -10
  227. package/edit/provisioning.cattle.io.cluster/rke2.vue +31 -40
  228. package/edit/provisioning.cattle.io.cluster/tabs/Advanced.vue +5 -2
  229. package/edit/provisioning.cattle.io.cluster/tabs/AgentConfiguration.vue +6 -1
  230. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +33 -2
  231. package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +3 -3
  232. package/edit/service.vue +0 -3
  233. package/edit/token.vue +32 -11
  234. package/edit/workload/Job.vue +6 -6
  235. package/edit/workload/__tests__/Job.test.ts +0 -1
  236. package/edit/workload/index.vue +1 -0
  237. package/edit/workload/mixins/workload.js +3 -3
  238. package/initialize/install-plugins.js +2 -1
  239. package/list/harvesterhci.io.management.cluster.vue +4 -1
  240. package/list/management.cattle.io.feature.vue +1 -0
  241. package/list/namespace.vue +3 -1
  242. package/list/provisioning.cattle.io.cluster.vue +20 -12
  243. package/list/workload.vue +7 -6
  244. package/machine-config/__tests__/vmwarevsphere.test.ts +48 -3
  245. package/machine-config/azure.vue +16 -4
  246. package/machine-config/vmwarevsphere.vue +16 -0
  247. package/mixins/resource-fetch.js +2 -1
  248. package/models/__tests__/logging.banzaicloud.io.flow.test.ts +88 -0
  249. package/models/__tests__/namespace.test.ts +25 -1
  250. package/models/cloudcredential.js +5 -0
  251. package/models/kontainerdriver.js +6 -3
  252. package/models/logging.banzaicloud.io.flow.js +2 -1
  253. package/models/management.cattle.io.node.js +3 -3
  254. package/models/management.cattle.io.setting.js +2 -1
  255. package/models/namespace.js +4 -5
  256. package/models/nodedriver.js +6 -3
  257. package/models/storage.k8s.io.storageclass.js +2 -2
  258. package/models/workload.js +4 -1
  259. package/package.json +1 -1
  260. package/pages/about.vue +16 -8
  261. package/pages/account/index.vue +80 -24
  262. package/pages/account/pri.vue +229 -0
  263. package/pages/auth/login.vue +195 -44
  264. package/pages/auth/logout.vue +4 -1
  265. package/pages/auth/setup.vue +144 -19
  266. package/pages/auth/verify.vue +13 -8
  267. package/pages/auth copy/login.vue +595 -0
  268. package/pages/auth copy/logout.vue +47 -0
  269. package/pages/auth copy/setup.vue +523 -0
  270. package/pages/auth copy/verify.vue +203 -0
  271. package/pages/c/_cluster/_product/namespaces.vue +5 -5
  272. package/pages/c/_cluster/apps/charts/chart.vue +1 -1
  273. package/pages/c/_cluster/apps/charts/install.vue +26 -26
  274. package/pages/c/_cluster/auth/config/index.vue +10 -12
  275. package/pages/c/_cluster/explorer/EventsTable.vue +38 -33
  276. package/pages/c/_cluster/explorer/index.vue +17 -15
  277. package/pages/c/_cluster/istio/index.vue +2 -2
  278. package/pages/c/_cluster/longhorn/index.vue +1 -1
  279. package/pages/c/_cluster/monitoring/index.vue +1 -1
  280. package/pages/c/_cluster/monitoring/monitor/_namespace/_id.vue +4 -2
  281. package/pages/c/_cluster/monitoring/monitor/create.vue +4 -2
  282. package/pages/c/_cluster/monitoring/monitor/index.vue +2 -2
  283. package/pages/c/_cluster/monitoring/route-receiver/_id.vue +4 -2
  284. package/pages/c/_cluster/monitoring/route-receiver/create.vue +5 -2
  285. package/pages/c/_cluster/neuvector/index.vue +1 -1
  286. package/pages/c/_cluster/settings/brand.vue +3 -3
  287. package/pages/c/_cluster/uiplugins/CatalogList/index.vue +8 -10
  288. package/pages/diagnostic.vue +59 -11
  289. package/pages/fail-whale.vue +14 -8
  290. package/pages/home.vue +24 -18
  291. package/pages/prefs.vue +9 -7
  292. package/pages/support/index.vue +4 -1
  293. package/pkg/tsconfig.json +9 -9
  294. package/pkg/vue.config.js +1 -1
  295. package/plugins/dashboard-store/normalize.js +3 -1
  296. package/plugins/dashboard-store/resource-class.js +31 -29
  297. package/plugins/internal-api/index.ts +37 -0
  298. package/plugins/internal-api/shared/base-api.ts +13 -0
  299. package/plugins/internal-api/shell/shell.api.ts +108 -0
  300. package/promptRemove/management.cattle.io.fleetworkspace.vue +1 -1
  301. package/promptRemove/management.cattle.io.globalrole.vue +1 -1
  302. package/promptRemove/management.cattle.io.project.vue +2 -2
  303. package/promptRemove/management.cattle.io.roletemplate.vue +1 -1
  304. package/promptRemove/pod.vue +1 -1
  305. package/public/index.html +2 -1
  306. package/rancher-components/BadgeState/BadgeState.vue +5 -1
  307. package/rancher-components/Banner/Banner.vue +8 -2
  308. package/rancher-components/Card/Card.vue +3 -6
  309. package/rancher-components/Form/Checkbox/Checkbox.vue +4 -0
  310. package/rancher-components/Form/LabeledInput/LabeledInput.vue +5 -2
  311. package/rancher-components/Form/Radio/RadioButton.vue +3 -3
  312. package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +1 -5
  313. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +9 -4
  314. package/rancher-components/RcDropdown/RcDropdownItem.vue +1 -2
  315. package/rancher-components/RcDropdown/RcDropdownMenu.vue +7 -3
  316. package/rancher-components/RcDropdown/types.ts +1 -0
  317. package/scripts/clean +0 -0
  318. package/scripts/extension/bundle +20 -0
  319. package/scripts/extension/helm/charts/ui-plugin-server/templates/_helpers.tpl +2 -2
  320. package/scripts/extension/helm/charts/ui-plugin-server/templates/cr.yaml +2 -1
  321. package/scripts/extension/helm/charts/ui-plugin-server/values.yaml +2 -0
  322. package/scripts/extension/helm/scripts/package +0 -0
  323. package/scripts/extension/helm/scripts/patch +0 -0
  324. package/scripts/extension/helm/scripts/version +0 -0
  325. package/scripts/extension/helmpatch +44 -31
  326. package/scripts/extension/parse-tag-name +0 -0
  327. package/scripts/extension/publish +12 -12
  328. package/scripts/publish-shell.sh +18 -23
  329. package/scripts/serve-pkgs +0 -0
  330. package/scripts/sync-shell-deps +0 -0
  331. package/scripts/test-plugins-build.sh +4 -6
  332. package/scripts/typegen.sh +28 -46
  333. package/server/har-file.js +25 -3
  334. package/static/favicon.ico +0 -0
  335. package/static/favicon.png +0 -0
  336. package/static/loading-indicator.html +2 -2
  337. package/store/aws.js +9 -2
  338. package/store/features.js +2 -1
  339. package/store/i18n.js +3 -3
  340. package/store/modal.ts +71 -0
  341. package/store/slideInPanel.ts +47 -0
  342. package/store/type-map.js +2 -1
  343. package/types/cloud-shell/index.d.ts +11014 -0
  344. package/types/global-vue.d.ts +5 -0
  345. package/types/internal-api/shell/growl.d.ts +25 -0
  346. package/types/internal-api/shell/modal.d.ts +77 -0
  347. package/types/internal-api/shell/slideIn.d.ts +15 -0
  348. package/types/shell/index.d.ts +118 -128
  349. package/types/vue-shim.d.ts +4 -1
  350. package/utils/__tests__/object.test.ts +38 -4
  351. package/utils/__tests__/string.test.ts +2 -2
  352. package/utils/auth.js +1 -0
  353. package/utils/banners.js +0 -45
  354. package/utils/cluster.js +35 -0
  355. package/utils/color.js +9 -8
  356. package/utils/error.js +61 -3
  357. package/utils/errorTranslate.json +450 -30
  358. package/utils/object.js +46 -6
  359. package/utils/router.js +22 -1
  360. package/utils/select.js +26 -3
  361. package/utils/string.js +9 -8
  362. package/utils/title.ts +1 -1
  363. package/utils/validators/machine-pool.ts +20 -0
  364. package/vue.config.js +7 -2
  365. package/components/formatter/ExtensionCache.vue +0 -74
  366. package/components/formatter/Port.vue +0 -24
  367. package/components/formatter/SecretType.vue +0 -41
  368. package/types/resources/fleet.d.ts +0 -57
  369. package/types/resources/pod-security-admission.ts +0 -36
  370. package/types/resources/settings.d.ts +0 -93
  371. package/types/resources/userPreferences.d.ts +0 -13
  372. package/types/vue-shim.d +0 -20
@@ -0,0 +1,65 @@
1
+ import { mount, RouterLinkStub } from '@vue/test-utils';
2
+ import { _VIEW } from '@shell/config/query-params';
3
+ import Masthead from '@shell/components/ResourceDetail/Masthead.vue';
4
+ import { createStore } from 'vuex';
5
+
6
+ const mockedStore = () => {
7
+ return {
8
+ getters: {
9
+ currentStore: () => 'current_store',
10
+ currentProduct: { inStore: 'cluster' },
11
+ isExplorer: false,
12
+ currentCluster: {},
13
+ 'type-map/labelFor': jest.fn(),
14
+ 'type-map/optionsFor': jest.fn(),
15
+ 'current_store/schemaFor': jest.fn(),
16
+ },
17
+ };
18
+ };
19
+
20
+ const requiredSetup = () => {
21
+ const store = createStore({ getters: { 'management/byId': () => jest.fn() } });
22
+
23
+ return {
24
+ stubs: {
25
+ 'router-link': RouterLinkStub,
26
+ LiveDate: true
27
+ },
28
+ provide: { store },
29
+ mocks: { $store: mockedStore() }
30
+ };
31
+ };
32
+
33
+ describe('component: Masthead', () => {
34
+ it.each([
35
+ ['hidden', '', false, { displayName: 'admin', location: { id: 'resource-id' } }, false, false],
36
+ ['plain-text', 'admin', true, { displayName: 'admin', location: null }, false, true],
37
+ ['link', 'foo', true, { displayName: 'foo', location: { id: 'resource-id' } }, true, false],
38
+ ])('"Created By" should be %p, with text: %p', (
39
+ _,
40
+ text,
41
+ showCreatedBy,
42
+ createdBy,
43
+ showLink,
44
+ showPlainText,
45
+ ) => {
46
+ const wrapper = mount(Masthead, {
47
+ props: {
48
+ mode: _VIEW,
49
+ value: {
50
+ showCreatedBy,
51
+ createdBy,
52
+ },
53
+ },
54
+ global: { ...requiredSetup() }
55
+ });
56
+
57
+ const container = wrapper.find('[data-testid="masthead-subheader-createdBy"]');
58
+ const link = wrapper.find('[data-testid="masthead-subheader-createdBy-link"]');
59
+ const plainText = wrapper.find('[data-testid="masthead-subheader-createdBy_plain-text"]');
60
+
61
+ expect(link.exists()).toBe(showLink);
62
+ expect(plainText.exists()).toBe(showPlainText);
63
+ expect(showLink || showPlainText ? container.element.textContent : '').toContain(text);
64
+ });
65
+ });
@@ -0,0 +1,591 @@
1
+ <script>
2
+ import CreateEditView from '@shell/mixins/create-edit-view/impl';
3
+ import Loading from '@shell/components/Loading';
4
+ import ResourceYaml from '@shell/components/ResourceYaml';
5
+ import {
6
+ _VIEW, _EDIT, _CLONE, _IMPORT, _STAGE, _CREATE,
7
+ AS, _YAML, _DETAIL, _CONFIG, _GRAPH, PREVIEW, MODE,
8
+ } from '@shell/config/query-params';
9
+ import { FLEET, SCHEMA } from '@shell/config/types';
10
+ import { createYaml } from '@shell/utils/create-yaml';
11
+ import Masthead from '@shell/components/ResourceDetail/Masthead';
12
+ import DetailTop from '@shell/components/DetailTop';
13
+ import { clone, diff } from '@shell/utils/object';
14
+ import IconMessage from '@shell/components/IconMessage';
15
+ import ForceDirectedTreeChart from '@shell/components/fleet/ForceDirectedTreeChart';
16
+ import { checkSchemasForFindAllHash } from '@shell/utils/auth';
17
+ import { stringify } from '@shell/utils/error';
18
+ import { Banner } from '@components/Banner';
19
+ import { harvesterhci2cloud, cloud2harvesterhci } from '@shell/utils/router'
20
+
21
+ function modeFor(route) {
22
+ if ( route.query?.mode === _IMPORT ) {
23
+ return _IMPORT;
24
+ }
25
+
26
+ if ( route.params?.id ) {
27
+ return route.query.mode || _VIEW;
28
+ } else {
29
+ return _CREATE;
30
+ }
31
+ }
32
+
33
+ async function getYaml(store, model) {
34
+ let yaml;
35
+ const opt = { headers: { accept: 'application/yaml' } };
36
+
37
+ if ( model.hasLink('view') ) {
38
+ yaml = (await model.followLink('view', opt)).data;
39
+ }
40
+
41
+ return model.cleanForDownload(yaml);
42
+ }
43
+
44
+ export default {
45
+ emits: ['input'],
46
+
47
+ components: {
48
+ Loading,
49
+ DetailTop,
50
+ ForceDirectedTreeChart,
51
+ ResourceYaml,
52
+ Masthead,
53
+ IconMessage,
54
+ Banner
55
+ },
56
+
57
+ mixins: [CreateEditView],
58
+
59
+ props: {
60
+ storeOverride: {
61
+ type: String,
62
+ default: null,
63
+ },
64
+
65
+ resourceOverride: {
66
+ type: String,
67
+ default: null,
68
+ },
69
+
70
+ parentRouteOverride: {
71
+ type: String,
72
+ default: null,
73
+ },
74
+
75
+ flexContent: {
76
+ type: Boolean,
77
+ default: false,
78
+ },
79
+
80
+ /**
81
+ * Inherited global identifier prefix for tests
82
+ * Define a term based on the parent component to avoid conflicts on multiple components
83
+ */
84
+ componentTestid: {
85
+ type: String,
86
+ default: 'resource-details'
87
+ },
88
+ errorsMap: {
89
+ type: Object,
90
+ default: null
91
+ },
92
+ },
93
+
94
+ async fetch() {
95
+ const store = this.$store;
96
+ const route = this.$route;
97
+ const params = route.params;
98
+ const cloneParams = clone(params)
99
+ let resourceType = this.resourceOverride || cloud2harvesterhci(cloneParams.resource);
100
+
101
+ const inStore = this.storeOverride || store.getters['currentStore'](resourceType);
102
+ const realMode = this.realMode;
103
+
104
+ // eslint-disable-next-line prefer-const
105
+ let { namespace, id } = params;
106
+
107
+ // There are 6 "real" modes that can be put into the query string
108
+ // These are mapped down to the 3 regular page "mode"s that create-edit-view components
109
+ // know about: view, edit, create (stage, import and clone become "create")
110
+ const mode = ([_CLONE, _IMPORT, _STAGE].includes(realMode) ? _CREATE : realMode);
111
+
112
+ const getGraphConfig = store.getters['type-map/hasGraph'](resourceType);
113
+ const hasGraph = !!getGraphConfig;
114
+ const hasCustomDetail = store.getters['type-map/hasCustomDetail'](resourceType, id);
115
+ const hasCustomEdit = store.getters['type-map/hasCustomEdit'](resourceType, id);
116
+
117
+ const schemas = store.getters[`${ inStore }/all`](SCHEMA);
118
+
119
+ // As determines what component will be rendered
120
+ const requested = route.query[AS];
121
+ let as;
122
+ let notFound = false;
123
+
124
+ if ( mode === _VIEW && hasCustomDetail && (!requested || requested === _DETAIL) ) {
125
+ as = _DETAIL;
126
+ } else if ( mode === _VIEW && hasGraph && requested === _GRAPH) {
127
+ as = _GRAPH;
128
+ } else if ( hasCustomEdit && (!requested || requested === _CONFIG) ) {
129
+ as = _CONFIG;
130
+ } else {
131
+ as = _YAML;
132
+ }
133
+
134
+ this.as = as;
135
+
136
+ const options = store.getters[`type-map/optionsFor`](resourceType);
137
+
138
+ this.showMasthead = [_CREATE, _EDIT].includes(mode) ? options.resourceEditMasthead : true;
139
+ const canViewYaml = options.canYaml;
140
+
141
+ if ( options.resource ) {
142
+ resourceType = options.resource;
143
+ }
144
+
145
+ const schema = store.getters[`${ inStore }/schemaFor`](resourceType);
146
+ let model, initialModel, liveModel, yaml;
147
+
148
+ if ( realMode === _CREATE || realMode === _IMPORT ) {
149
+ if ( !namespace ) {
150
+ namespace = store.getters['defaultNamespace'];
151
+ }
152
+
153
+ const data = { type: resourceType };
154
+
155
+ if ( schema?.attributes?.namespaced ) {
156
+ data.metadata = { namespace };
157
+ }
158
+
159
+ liveModel = await store.dispatch(`${ inStore }/create`, data);
160
+ initialModel = await store.dispatch(`${ inStore }/clone`, { resource: liveModel });
161
+ model = await store.dispatch(`${ inStore }/clone`, { resource: liveModel });
162
+
163
+ if (model.forceYaml === true) {
164
+ as = _YAML;
165
+ this.as = as;
166
+ }
167
+
168
+ if ( as === _YAML ) {
169
+ if (schema?.fetchResourceFields) {
170
+ // fetch resourceFields for createYaml
171
+ await schema.fetchResourceFields();
172
+ }
173
+
174
+ yaml = createYaml(schemas, resourceType, data);
175
+ }
176
+ } else {
177
+ if ( as === _GRAPH ) {
178
+ const graphSchema = await checkSchemasForFindAllHash({
179
+ cluster: {
180
+ inStoreType: 'management',
181
+ type: FLEET.CLUSTER
182
+ },
183
+ bundle: {
184
+ inStoreType: 'management',
185
+ type: FLEET.BUNDLE,
186
+ opt: { excludeFields: ['metadata.managedFields', 'spec.resources'] },
187
+ },
188
+
189
+ bundleDeployment: {
190
+ inStoreType: 'management',
191
+ type: FLEET.BUNDLE_DEPLOYMENT
192
+ }
193
+
194
+ }, this.$store);
195
+
196
+ this.canViewChart = graphSchema.cluster && graphSchema.bundle && graphSchema.bundleDeployment;
197
+ }
198
+
199
+ let fqid = id;
200
+
201
+ if ( schema.attributes?.namespaced && namespace ) {
202
+ fqid = `${ namespace }/${ fqid }`;
203
+ }
204
+
205
+ try {
206
+ liveModel = await store.dispatch(`${ inStore }/find`, {
207
+ type: resourceType,
208
+ id: fqid,
209
+ opt: { watch: true }
210
+ });
211
+ } catch (e) {
212
+ if (e.status === 404 || e.status === 403) {
213
+ store.dispatch('loadingError', new Error(this.t('nav.failWhale.resourceIdNotFound', { resource: resourceType, fqid }, true)));
214
+ }
215
+ liveModel = {};
216
+ notFound = fqid;
217
+ }
218
+
219
+ try {
220
+ if (realMode === _VIEW) {
221
+ model = liveModel;
222
+ } else {
223
+ model = await store.dispatch(`${ inStore }/clone`, { resource: liveModel });
224
+ }
225
+ initialModel = await store.dispatch(`${ inStore }/clone`, { resource: liveModel });
226
+
227
+ if ( as === _YAML ) {
228
+ yaml = await getYaml(this.$store, liveModel);
229
+ }
230
+ } catch (e) {
231
+ this.errors.push(e);
232
+ }
233
+ if ( as === _YAML ) {
234
+ try {
235
+ yaml = await getYaml(this.$store, liveModel);
236
+ } catch (e) {
237
+ this.errors.push(e);
238
+ }
239
+ }
240
+
241
+ if ( as === _GRAPH ) {
242
+ this.chartData = liveModel;
243
+ }
244
+
245
+ if ( [_CLONE, _IMPORT, _STAGE].includes(realMode) ) {
246
+ model.cleanForNew();
247
+ yaml = model.cleanYaml(yaml, realMode);
248
+ }
249
+ }
250
+
251
+ // Ensure common properties exists
252
+ try {
253
+ model = await store.dispatch(`${ inStore }/cleanForDetail`, model);
254
+ } catch (e) {
255
+ this.errors.push(e);
256
+ }
257
+
258
+ const out = {
259
+ hasGraph,
260
+ getGraphConfig,
261
+ hasCustomDetail,
262
+ hasCustomEdit,
263
+ canViewYaml,
264
+ resourceType,
265
+ as,
266
+ yaml,
267
+ initialModel,
268
+ liveModel,
269
+ mode,
270
+ value: model,
271
+ notFound,
272
+ };
273
+
274
+ for ( const key in out ) {
275
+ this[key] = out[key];
276
+ }
277
+
278
+ if ( this.mode === _CREATE ) {
279
+ this.value.applyDefaults(this, realMode);
280
+ }
281
+ },
282
+ data() {
283
+ return {
284
+ chartData: null,
285
+ resourceSubtype: null,
286
+
287
+ // Set by fetch
288
+ hasGraph: null,
289
+ hasCustomDetail: null,
290
+ hasCustomEdit: null,
291
+ resourceType: null,
292
+ asYaml: null,
293
+ yaml: null,
294
+ liveModel: null,
295
+ initialModel: null,
296
+ mode: null,
297
+ as: null,
298
+ value: null,
299
+ model: null,
300
+ notFound: null,
301
+ canViewChart: true,
302
+ canViewYaml: null,
303
+ errors: []
304
+ };
305
+ },
306
+
307
+ computed: {
308
+ realMode() {
309
+ // There are 5 "real" modes that you can start in: view, edit, create, stage, clone
310
+ const realMode = modeFor(this.$route);
311
+
312
+ return realMode;
313
+ },
314
+
315
+ isView() {
316
+ return this.mode === _VIEW;
317
+ },
318
+
319
+ isYaml() {
320
+ return this.as === _YAML;
321
+ },
322
+
323
+ isDetail() {
324
+ return this.as === _DETAIL;
325
+ },
326
+
327
+ isGraph() {
328
+ return this.as === _GRAPH;
329
+ },
330
+
331
+ offerPreview() {
332
+ return this.as === _YAML && [_EDIT, _CLONE, _IMPORT, _STAGE].includes(this.mode);
333
+ },
334
+
335
+ showComponent() {
336
+ switch ( this.as ) {
337
+ case _DETAIL: return this.detailComponent;
338
+ case _CONFIG: return this.editComponent;
339
+ }
340
+
341
+ return null;
342
+ },
343
+ hasErrors() {
344
+ return this.errors?.length && Array.isArray(this.errors);
345
+ },
346
+ mappedErrors() {
347
+ return !this.errors ? {} : this.errorsMap || this.errors.reduce((acc, error) => ({
348
+ ...acc,
349
+ [error]: {
350
+ message: error?.data?.message || error,
351
+ icon: null
352
+ }
353
+ }), {});
354
+ },
355
+ },
356
+
357
+ watch: {
358
+ '$route'(current, prev) {
359
+ if (current.name !== prev.name) {
360
+ return;
361
+ }
362
+ const neu = clone(current.query);
363
+ const old = clone(prev.query);
364
+
365
+ delete neu[PREVIEW];
366
+ delete old[PREVIEW];
367
+
368
+ if ( !this.isView ) {
369
+ delete neu[AS];
370
+ delete old[AS];
371
+ }
372
+
373
+ const queryDiff = Object.keys(diff(neu, old));
374
+
375
+ if (queryDiff.includes(MODE) || queryDiff.includes(AS)) {
376
+ this.$fetch();
377
+ }
378
+ },
379
+
380
+ // Auto refresh YAML when the model changes
381
+ async 'value.metadata.resourceVersion'(a, b) {
382
+ if ( this.mode === _VIEW && this.as === _YAML && a && b && a !== b) {
383
+ this.yaml = await getYaml(this.$store, this.liveModel);
384
+ }
385
+ }
386
+ },
387
+
388
+ created() {
389
+ this.configureResource();
390
+ },
391
+
392
+ methods: {
393
+ stringify,
394
+ setSubtype(subtype) {
395
+ this.resourceSubtype = subtype;
396
+ },
397
+
398
+ keyAction(act) {
399
+ const m = this.liveModel;
400
+
401
+ if ( m?.[act] ) {
402
+ m[act]();
403
+ }
404
+ },
405
+ closeError(index) {
406
+ this.errors = this.errors.filter((_, i) => i !== index);
407
+ },
408
+ /**
409
+ * Initializes the resource components based on the provided user and
410
+ * resource override.
411
+ *
412
+ * Configures the detail and edit components for a resource based on the
413
+ * user's ID and the specified resource.
414
+ *
415
+ * @param {Object} user - The user object containing user-specific
416
+ * information.
417
+ * @param {string|null} resourceOverride - An optional resource override
418
+ * string. If not provided, the method will use the default resource from
419
+ * the route parameters or the instance's resourceOverride property.
420
+ */
421
+ configureResource(userId = '', resourceOverride = null) {
422
+ const id = userId || this.$route.params.id;
423
+ // const paramsResource = this.$route.params.resource
424
+ const routerResource = clone(this.$route.params.resource)
425
+ const resource = resourceOverride || this.resourceOverride || cloud2harvesterhci(routerResource);
426
+ const options = this.$store.getters[`type-map/optionsFor`](resource);
427
+
428
+ const detailResource = options.resourceDetail || options.resource || resource;
429
+ const editResource = options.resourceEdit || options.resource || resource;
430
+
431
+ // FIXME: These aren't right... signature is (rawType, subType).. not (rawType, resourceId)
432
+ // Remove id? How does subtype get in (cluster/node)
433
+ this.detailComponent = this.$store.getters['type-map/importDetail'](detailResource, id);
434
+ this.editComponent = this.$store.getters['type-map/importEdit'](editResource, id);
435
+ },
436
+ /**
437
+ * Sets the mode and initializes the resource components.
438
+ *
439
+ * This method sets the mode of the component and configures the resource
440
+ * components based on the provided user and resource.
441
+ *
442
+ * @param {Object} payload - An object containing the mode, user, and
443
+ * resource properties.
444
+ * @param {string} payload.mode - The mode to set.
445
+ * @param {Object} payload.user - The user object containing user-specific
446
+ * information.
447
+ * @param {string} payload.resource - The resource string to use for
448
+ * initialization.
449
+ */
450
+ setMode({ mode, userId, resource }) {
451
+ this.mode = mode;
452
+ this.value.id = userId;
453
+ this.configureResource(userId, resource);
454
+ }
455
+ }
456
+ };
457
+ </script>
458
+
459
+ <template>
460
+
461
+ <!-- 如果数据还在加载中,或者资源未找到,则显示 Loading 组件 -->
462
+ <Loading v-if="$fetchState.pending || notFound" />
463
+
464
+ <!-- 数据加载完成且资源存在时 -->
465
+ <div style="padding: 20px 0 20px 20px;height: 100%;" v-else>
466
+
467
+ <!-- 顶部 Masthead 区域 -->
468
+ <Masthead
469
+ v-if="showMasthead"
470
+ :resource="resourceType"
471
+ :value="liveModel"
472
+ :mode="mode"
473
+ :real-mode="realMode"
474
+ :as="as"
475
+ :has-graph="hasGraph"
476
+ :has-detail="hasCustomDetail"
477
+ :has-edit="hasCustomEdit"
478
+ :can-view-yaml="canViewYaml"
479
+ :resource-subtype="resourceSubtype"
480
+ :parent-route-override="parentRouteOverride"
481
+ :store-override="storeOverride"
482
+ >
483
+
484
+ <!-- 详情顶部信息,仅在查看模式且是详情页面时显示 -->
485
+ <!-- <DetailTop
486
+ v-if="isView && isDetail"
487
+ :value="liveModel"
488
+ /> -->
489
+ </Masthead>
490
+
491
+
492
+ <!-- 错误信息显示区域 -->
493
+ <div
494
+ v-if="hasErrors"
495
+ id="cru-errors"
496
+ class="cru__errors"
497
+ >
498
+
499
+ <!-- 循环渲染错误 Banner -->
500
+ <Banner
501
+ v-for="(err, i) in errors"
502
+ :key="i"
503
+ color="error"
504
+ :data-testid="`error-banner${i}`"
505
+ :label="stringify(mappedErrors[err].message)"
506
+ :icon="mappedErrors[err].icon"
507
+ :closable="true"
508
+ @close="closeError(i)"
509
+ />
510
+ </div>
511
+
512
+ <!-- 力导向图模式 -->
513
+ <ForceDirectedTreeChart
514
+ v-if="isGraph && canViewChart"
515
+ :data="chartData"
516
+ :fdc-config="getGraphConfig"
517
+ />
518
+
519
+ <!-- YAML 查看/编辑模式 -->
520
+ <ResourceYaml
521
+ v-else-if="isYaml"
522
+ ref="resourceyaml"
523
+ :value="value"
524
+ :mode="mode"
525
+ :yaml="yaml"
526
+ :offer-preview="offerPreview"
527
+ :done-route="doneRoute"
528
+ :done-override="value ? value.doneOverride : null"
529
+ @update:value="$emit('input', $event)"
530
+ @error="e=>errors.push(e)"
531
+ />
532
+
533
+ <!-- 动态组件渲染,根据 showComponent 动态选择组件 -->
534
+ <component
535
+ :is="showComponent"
536
+ v-else
537
+ ref="comp"
538
+ v-model:value="value"
539
+ v-bind="$data"
540
+ :done-params="doneParams"
541
+ :done-route="doneRoute"
542
+ :mode="mode"
543
+ :initial-value="initialModel"
544
+ :live-value="liveModel"
545
+ :real-mode="realMode"
546
+ :class="{'flex-content': flexContent}"
547
+ @update:value="$emit('input', $event)"
548
+ @update:mode="setMode"
549
+ @set-subtype="setSubtype"
550
+ />
551
+
552
+
553
+ <!-- 快捷键按钮(隐藏) -->
554
+ <button
555
+ v-if="isView"
556
+ v-shortkey.once="['shift','d']"
557
+ :data-testid="componentTestid + '-detail'"
558
+ class="hide"
559
+ @shortkey="keyAction('goToDetail')"
560
+ />
561
+ <button
562
+ v-if="isView"
563
+ v-shortkey.once="['shift','c']"
564
+ :data-testid="componentTestid + '-config'"
565
+ class="hide"
566
+ @shortkey="keyAction('goToViewConfig')"
567
+ />
568
+ <button
569
+ v-if="isView"
570
+ v-shortkey.once="['shift','y']"
571
+ :data-testid="componentTestid + '-yaml'"
572
+ class="hide"
573
+ @shortkey="keyAction('goToViewYaml')"
574
+ />
575
+ <button
576
+ v-if="isView"
577
+ v-shortkey.once="['shift','e']"
578
+ :data-testid="componentTestid + '-edit'"
579
+ class="hide"
580
+ @shortkey="keyAction('goToEdit')"
581
+ />
582
+ </div>
583
+ </template>
584
+
585
+ <style lang='scss' scoped>
586
+ .flex-content {
587
+ display: flex;
588
+ flex-direction: column;
589
+ flex-grow: 1;
590
+ }
591
+ </style>