@rancher/shell 0.1.1 → 0.1.3

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 (294) hide show
  1. package/assets/translations/en-us.yaml +33 -769
  2. package/assets/translations/zh-hans.yaml +153 -781
  3. package/components/ActionMenu.vue +3 -3
  4. package/components/CodeMirror.vue +6 -8
  5. package/components/CommunityLinks.vue +1 -1
  6. package/components/ContainerResourceLimit.vue +14 -0
  7. package/components/ExplorerMembers.vue +123 -0
  8. package/components/ExplorerProjectsNamespaces.vue +405 -0
  9. package/components/GrafanaDashboard.vue +17 -2
  10. package/components/LocaleSelector.vue +81 -0
  11. package/components/PromptModal.vue +2 -3
  12. package/components/ResourceList/index.vue +1 -1
  13. package/components/ResourceTable.vue +3 -6
  14. package/components/SingleClusterInfo.vue +1 -1
  15. package/components/SortableTable/index.vue +23 -20
  16. package/components/SortableTable/selection.js +1 -0
  17. package/components/auth/AzureWarning.vue +5 -1
  18. package/components/auth/Principal.vue +1 -1
  19. package/components/auth/RoleDetailEdit.vue +32 -12
  20. package/components/fleet/FleetRepos.vue +0 -2
  21. package/components/form/NameNsDescription.vue +4 -6
  22. package/components/form/NodeScheduling.vue +1 -1
  23. package/components/form/ResourceTabs/index.vue +27 -18
  24. package/components/form/WorkloadPorts.vue +1 -1
  25. package/components/formatter/ClusterLink.vue +13 -0
  26. package/components/formatter/PodImages.vue +11 -1
  27. package/components/formatter/RKETemplateName.vue +37 -0
  28. package/components/formatter/WorkloadHealthScale.vue +1 -1
  29. package/components/nav/Header.vue +9 -9
  30. package/components/nav/NamespaceFilter.vue +7 -4
  31. package/components/nav/TopLevelMenu.vue +6 -43
  32. package/components/nav/WindowManager/ContainerLogs.vue +1 -1
  33. package/config/product/harvester-manager.js +64 -2
  34. package/config/product/manager.js +9 -0
  35. package/config/settings.js +17 -71
  36. package/config/table-headers.js +0 -1
  37. package/config/types.js +8 -26
  38. package/core/plugin-routes.ts +34 -22
  39. package/core/plugin.ts +15 -3
  40. package/core/plugins-loader.js +2 -0
  41. package/core/plugins.js +79 -36
  42. package/core/types.ts +7 -1
  43. package/creators/app/tsconfig.json +6 -1
  44. package/creators/pkg/init +3 -0
  45. package/creators/pkg/tsconfig.json +7 -2
  46. package/detail/provisioning.cattle.io.cluster.vue +23 -0
  47. package/detail/workload/index.vue +11 -5
  48. package/{components/dialog → dialog}/AddClusterMemberDialog.vue +0 -0
  49. package/{components/dialog → dialog}/AddCustomBadgeDialog.vue +0 -0
  50. package/{components/dialog → dialog}/AddProjectMemberDialog.vue +0 -0
  51. package/{components/dialog → dialog}/AddonConfigConfirmationDialog.vue +0 -0
  52. package/{components/dialog → dialog}/DrainNode.vue +0 -0
  53. package/{components/dialog → dialog}/ForceMachineRemoveDialog.vue +0 -0
  54. package/{components/dialog → dialog}/GenericPrompt.vue +0 -0
  55. package/{components/dialog → dialog}/RollbackWorkloadDialog.vue +0 -0
  56. package/{components/dialog → dialog}/RotateCertificatesDialog.vue +0 -0
  57. package/{components/dialog → dialog}/RotateEncryptionKeyDialog.vue +0 -0
  58. package/{components/dialog → dialog}/SaveAsRKETemplateDialog.vue +0 -0
  59. package/{components/dialog → dialog}/ScaleMachineDownDialog.vue +0 -0
  60. package/edit/auth/azuread.vue +20 -1
  61. package/edit/cloudcredential.vue +7 -1
  62. package/edit/management.cattle.io.project.vue +2 -2
  63. package/edit/namespace.vue +17 -10
  64. package/edit/networking.k8s.io.ingress/index.vue +2 -1
  65. package/edit/persistentvolumeclaim.vue +33 -2
  66. package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +1 -1
  67. package/edit/provisioning.cattle.io.cluster/MachinePool.vue +34 -6
  68. package/edit/provisioning.cattle.io.cluster/index.vue +1 -1
  69. package/edit/provisioning.cattle.io.cluster/rke2.vue +21 -6
  70. package/edit/service.vue +1 -1
  71. package/edit/workload/index.vue +363 -15
  72. package/edit/workload/mixins/workload.js +62 -7
  73. package/edit/workload/storage/persistentVolumeClaim/persistentvolumeclaim.vue +1 -0
  74. package/layouts/default.vue +52 -27
  75. package/layouts/error.vue +5 -1
  76. package/layouts/home.vue +6 -2
  77. package/list/harvesterhci.io.management.cluster.vue +74 -33
  78. package/list/namespace.vue +3 -5
  79. package/list/provisioning.cattle.io.cluster.vue +6 -0
  80. package/machine-config/amazonec2.vue +2 -0
  81. package/machine-config/harvester.vue +96 -49
  82. package/middleware/authenticated.js +56 -52
  83. package/mixins/brand.js +3 -4
  84. package/mixins/create-edit-view/impl.js +0 -8
  85. package/mixins/form-validation.js +1 -1
  86. package/mixins/resource-fetch.js +3 -1
  87. package/models/chart.js +1 -1
  88. package/models/cluster/node.js +12 -1
  89. package/models/fleet.cattle.io.bundle.js +26 -19
  90. package/models/harvesterhci.io.management.cluster.js +194 -5
  91. package/models/management.cattle.io.cluster.js +1 -1
  92. package/models/management.cattle.io.clusterroletemplatebinding.js +9 -0
  93. package/models/management.cattle.io.globalrole.js +0 -19
  94. package/models/management.cattle.io.project.js +23 -2
  95. package/models/management.cattle.io.roletemplate.js +2 -21
  96. package/models/namespace.js +19 -3
  97. package/models/pod.js +19 -2
  98. package/models/provisioning.cattle.io.cluster.js +71 -0
  99. package/models/service.js +5 -1
  100. package/models/workload.js +4 -243
  101. package/models/workload.service.js +314 -0
  102. package/nuxt.config.js +14 -12
  103. package/package.json +3 -3
  104. package/pages/auth/login.vue +11 -2
  105. package/pages/auth/setup.vue +1 -1
  106. package/pages/c/_cluster/_product/members/index.vue +3 -93
  107. package/pages/c/_cluster/_product/projectsnamespaces.vue +6 -403
  108. package/pages/c/_cluster/apps/charts/install.vue +0 -6
  109. package/pages/c/_cluster/settings/performance.vue +19 -16
  110. package/pages/fail-whale.vue +1 -10
  111. package/pages/index.vue +18 -4
  112. package/pages/plugins.vue +2 -2
  113. package/pages/prefs.vue +8 -6
  114. package/pkg/auto-import.js +44 -7
  115. package/pkg/dynamic-plugin-loader.js +28 -0
  116. package/pkg/import.js +2 -2
  117. package/pkg/model-loader-require.lib.js +3 -0
  118. package/pkg/vue.config.js +9 -6
  119. package/plugins/console.js +10 -5
  120. package/plugins/dashboard-store/actions.js +8 -3
  121. package/plugins/dashboard-store/getters.js +7 -2
  122. package/plugins/dashboard-store/model-loader-require.js +12 -0
  123. package/plugins/dashboard-store/model-loader.js +4 -1
  124. package/plugins/dashboard-store/resource-class.js +10 -3
  125. package/plugins/steve/actions.js +1 -1
  126. package/plugins/steve/index.js +6 -4
  127. package/plugins/steve/steve-description-class.js +32 -0
  128. package/plugins/steve/subscribe.js +34 -23
  129. package/rancher-components/Banner/Banner.vue +2 -2
  130. package/rancher-components/Form/Checkbox/Checkbox.test.ts +77 -0
  131. package/rancher-components/Form/Checkbox/Checkbox.vue +12 -2
  132. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +0 -2
  133. package/rancher-components/Form/LabeledInput/LabeledInput.vue +2 -0
  134. package/rancher-components/Form/Radio/RadioButton.vue +14 -1
  135. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.test.ts +107 -0
  136. package/{components/form → rancher-components/Form/ToggleSwitch}/ToggleSwitch.vue +18 -8
  137. package/rancher-components/Form/ToggleSwitch/index.ts +1 -0
  138. package/rancher-components/Form/index.ts +1 -0
  139. package/scripts/build-pkg.sh +48 -2
  140. package/scripts/drone-build-pkg.sh +31 -0
  141. package/scripts/publish-shell.sh +10 -11
  142. package/scripts/serve-pkgs +17 -10
  143. package/scripts/test-plugins-build.sh +18 -1
  144. package/store/catalog.js +3 -1
  145. package/store/i18n.js +16 -11
  146. package/store/index.js +4 -181
  147. package/store/prefs.js +30 -2
  148. package/store/type-map.js +16 -29
  149. package/types/{index.d.ts → rancher/index.d.ts} +0 -0
  150. package/utils/cluster.js +1 -1
  151. package/utils/custom-validators.js +1 -12
  152. package/utils/dynamic-importer.js +1 -1
  153. package/utils/validators/setting.js +0 -35
  154. package/components/FilterLabel.vue +0 -254
  155. package/components/HarvesterUpgradeProgressBarList.vue +0 -109
  156. package/components/VMConsoleBar.vue +0 -87
  157. package/components/dialog/harvester/AddHotplugModal.vue +0 -159
  158. package/components/dialog/harvester/BackupModal.vue +0 -117
  159. package/components/dialog/harvester/CloneTemplate.vue +0 -125
  160. package/components/dialog/harvester/EjectCDROMDialog.vue +0 -157
  161. package/components/dialog/harvester/ExportImageDialog.vue +0 -152
  162. package/components/dialog/harvester/MaintenanceDialog.vue +0 -94
  163. package/components/dialog/harvester/MigrationDialog.vue +0 -154
  164. package/components/dialog/harvester/RestoreDialog.vue +0 -153
  165. package/components/dialog/harvester/SupportBundle.vue +0 -217
  166. package/components/dialog/harvester/UnplugVolume.vue +0 -108
  167. package/components/form/SerialConsole/index.vue +0 -267
  168. package/components/formatter/AttachVMWithName.vue +0 -46
  169. package/components/formatter/CloudInitType.vue +0 -27
  170. package/components/formatter/HarvesterBackupTargetValidation.vue +0 -43
  171. package/components/formatter/HarvesterCPUUsed.vue +0 -122
  172. package/components/formatter/HarvesterDiskState.vue +0 -66
  173. package/components/formatter/HarvesterHostName.vue +0 -66
  174. package/components/formatter/HarvesterIpAddress.vue +0 -90
  175. package/components/formatter/HarvesterMemoryUsed.vue +0 -140
  176. package/components/formatter/HarvesterMigrationState.vue +0 -85
  177. package/components/formatter/HarvesterNodeName.vue +0 -49
  178. package/components/formatter/HarvesterStorageUsed.vue +0 -194
  179. package/components/formatter/HarvesterVmState.vue +0 -123
  180. package/components/nav/HarvesterUpgrade.vue +0 -232
  181. package/components/novnc/NovncConsole.vue +0 -93
  182. package/components/novnc/NovncConsoleItem.vue +0 -89
  183. package/components/novnc/NovncConsoleWrapper.vue +0 -243
  184. package/config/harvester-map.js +0 -44
  185. package/config/harvester-table-headers.js +0 -27
  186. package/config/product/harvester.js +0 -305
  187. package/detail/harvesterhci.io.host/HarvesterHostBasic.vue +0 -364
  188. package/detail/harvesterhci.io.host/HarvesterHostDisk.vue +0 -200
  189. package/detail/harvesterhci.io.host/HarvesterHostNetwork.vue +0 -89
  190. package/detail/harvesterhci.io.host/VirtualMachineInstance.vue +0 -134
  191. package/detail/harvesterhci.io.host/index.vue +0 -243
  192. package/detail/harvesterhci.io.virtualmachinebackup/index.vue +0 -221
  193. package/detail/harvesterhci.io.virtualmachineimage.vue +0 -118
  194. package/detail/kubevirt.io.virtualmachine/VirtualMachineTabs/VirtualMachineBasics.vue +0 -279
  195. package/detail/kubevirt.io.virtualmachine/VirtualMachineTabs/VirtualMachineEvents.vue +0 -75
  196. package/detail/kubevirt.io.virtualmachine/VirtualMachineTabs/VirtualMachineKeypairs.vue +0 -114
  197. package/detail/kubevirt.io.virtualmachine/VirtualMachineTabs/VirtualMachineMigration.vue +0 -79
  198. package/detail/kubevirt.io.virtualmachine/index.vue +0 -213
  199. package/edit/harvesterhci.io.cloudtemplate.vue +0 -123
  200. package/edit/harvesterhci.io.host/HarvesterDisk.vue +0 -262
  201. package/edit/harvesterhci.io.host/index.vue +0 -533
  202. package/edit/harvesterhci.io.keypair.vue +0 -112
  203. package/edit/harvesterhci.io.managedchart/index.vue +0 -25
  204. package/edit/harvesterhci.io.managedchart/rancher-monitoring.vue +0 -172
  205. package/edit/harvesterhci.io.networkattachmentdefinition.vue +0 -210
  206. package/edit/harvesterhci.io.setting/additional-ca.vue +0 -36
  207. package/edit/harvesterhci.io.setting/backup-target.vue +0 -182
  208. package/edit/harvesterhci.io.setting/http-proxy.vue +0 -79
  209. package/edit/harvesterhci.io.setting/index.vue +0 -201
  210. package/edit/harvesterhci.io.setting/overcommit-config.vue +0 -94
  211. package/edit/harvesterhci.io.setting/ssl-certificates.vue +0 -117
  212. package/edit/harvesterhci.io.setting/ssl-parameters.vue +0 -161
  213. package/edit/harvesterhci.io.setting/support-bundle-image.vue +0 -134
  214. package/edit/harvesterhci.io.setting/support-bundle-namespaces.vue +0 -73
  215. package/edit/harvesterhci.io.setting/vip-pools.vue +0 -244
  216. package/edit/harvesterhci.io.setting/vm-force-reset-policy.vue +0 -81
  217. package/edit/harvesterhci.io.virtualmachinebackup.vue +0 -256
  218. package/edit/harvesterhci.io.virtualmachineimage.vue +0 -364
  219. package/edit/harvesterhci.io.virtualmachinetemplateversion.vue +0 -340
  220. package/edit/harvesterhci.io.volume.vue +0 -195
  221. package/edit/kubevirt.io.virtualmachine/VirtualMachineAccessCredentials/AccessCredentialsUsers.vue +0 -190
  222. package/edit/kubevirt.io.virtualmachine/VirtualMachineAccessCredentials/index.vue +0 -212
  223. package/edit/kubevirt.io.virtualmachine/VirtualMachineAccessCredentials/type/basicAuth.vue +0 -94
  224. package/edit/kubevirt.io.virtualmachine/VirtualMachineAccessCredentials/type/sshkey.vue +0 -85
  225. package/edit/kubevirt.io.virtualmachine/VirtualMachineCloudConfig/DataTemplate.vue +0 -153
  226. package/edit/kubevirt.io.virtualmachine/VirtualMachineCloudConfig/index.vue +0 -279
  227. package/edit/kubevirt.io.virtualmachine/VirtualMachineCpuMemory.vue +0 -113
  228. package/edit/kubevirt.io.virtualmachine/VirtualMachineNetwork/__tests__/HarvesterEditNetwork.test.ts +0 -41
  229. package/edit/kubevirt.io.virtualmachine/VirtualMachineNetwork/base.vue +0 -281
  230. package/edit/kubevirt.io.virtualmachine/VirtualMachineNetwork/index.vue +0 -142
  231. package/edit/kubevirt.io.virtualmachine/VirtualMachineReserved.vue +0 -54
  232. package/edit/kubevirt.io.virtualmachine/VirtualMachineSSHKey.vue +0 -256
  233. package/edit/kubevirt.io.virtualmachine/VirtualMachineVolume/index.vue +0 -391
  234. package/edit/kubevirt.io.virtualmachine/VirtualMachineVolume/type/__tests__/HarvesterEditContainer.test.ts +0 -40
  235. package/edit/kubevirt.io.virtualmachine/VirtualMachineVolume/type/__tests__/HarvesterEditExisting.test.ts +0 -102
  236. package/edit/kubevirt.io.virtualmachine/VirtualMachineVolume/type/__tests__/HarvesterEditVMImage.test.ts +0 -117
  237. package/edit/kubevirt.io.virtualmachine/VirtualMachineVolume/type/__tests__/HarvesterEditVolume.test.ts +0 -74
  238. package/edit/kubevirt.io.virtualmachine/VirtualMachineVolume/type/container.vue +0 -132
  239. package/edit/kubevirt.io.virtualmachine/VirtualMachineVolume/type/existing.vue +0 -303
  240. package/edit/kubevirt.io.virtualmachine/VirtualMachineVolume/type/vmImage.vue +0 -285
  241. package/edit/kubevirt.io.virtualmachine/VirtualMachineVolume/type/volume.vue +0 -188
  242. package/edit/kubevirt.io.virtualmachine/index.vue +0 -642
  243. package/edit/network.harvesterhci.io.clusternetwork/index.vue +0 -19
  244. package/edit/network.harvesterhci.io.clusternetwork/vlan.vue +0 -134
  245. package/edit/workload/types/Deployment.vue +0 -377
  246. package/edit/workload/types/Generic.vue +0 -295
  247. package/list/harvesterhci.io.cloudtemplate.vue +0 -78
  248. package/list/harvesterhci.io.dashboard/HarvesterUpgrade.vue +0 -211
  249. package/list/harvesterhci.io.dashboard/UpgradeInfo.vue +0 -40
  250. package/list/harvesterhci.io.dashboard/index.vue +0 -752
  251. package/list/harvesterhci.io.host/index.vue +0 -186
  252. package/list/harvesterhci.io.networkattachmentdefinition.vue +0 -167
  253. package/list/harvesterhci.io.setting.vue +0 -241
  254. package/list/harvesterhci.io.virtualmachinebackup.vue +0 -172
  255. package/list/harvesterhci.io.virtualmachineimage.vue +0 -80
  256. package/list/harvesterhci.io.virtualmachinetemplateversion.vue +0 -173
  257. package/list/harvesterhci.io.volume.vue +0 -122
  258. package/list/kubevirt.io.virtualmachine.vue +0 -193
  259. package/mixins/harvester-vm/impl.js +0 -267
  260. package/mixins/harvester-vm/index.js +0 -1357
  261. package/models/harvester/configmap.js +0 -32
  262. package/models/harvester/harvesterhci.io.blockdevice.js +0 -55
  263. package/models/harvester/harvesterhci.io.keypair.js +0 -12
  264. package/models/harvester/harvesterhci.io.setting.js +0 -127
  265. package/models/harvester/harvesterhci.io.supportbundle.js +0 -35
  266. package/models/harvester/harvesterhci.io.upgrade.js +0 -226
  267. package/models/harvester/harvesterhci.io.virtualmachinebackup.js +0 -116
  268. package/models/harvester/harvesterhci.io.virtualmachineimage.js +0 -255
  269. package/models/harvester/harvesterhci.io.virtualmachinerestore.js +0 -43
  270. package/models/harvester/harvesterhci.io.virtualmachinetemplate.js +0 -69
  271. package/models/harvester/harvesterhci.io.virtualmachinetemplateversion.js +0 -227
  272. package/models/harvester/k8s.cni.cncf.io.networkattachmentdefinition.js +0 -32
  273. package/models/harvester/kubevirt.io.virtualmachine.js +0 -850
  274. package/models/harvester/kubevirt.io.virtualmachineinstance.js +0 -142
  275. package/models/harvester/management.cattle.io.managedchart.js +0 -191
  276. package/models/harvester/management.cattle.io.setting.js +0 -40
  277. package/models/harvester/network.harvesterhci.io.clusternetwork.js +0 -100
  278. package/models/harvester/network.harvesterhci.io.nodenetwork.js +0 -34
  279. package/models/harvester/node.js +0 -255
  280. package/models/harvester/persistentvolumeclaim.js +0 -166
  281. package/models/harvester/pod.js +0 -185
  282. package/pages/c/_cluster/harvester/airgapupgrade/index.vue +0 -309
  283. package/pages/c/_cluster/harvester/console/_uid/serial.vue +0 -51
  284. package/pages/c/_cluster/harvester/console/_uid/vnc.vue +0 -52
  285. package/pages/c/_cluster/harvester/index.vue +0 -24
  286. package/pages/c/_cluster/harvester/support/index.vue +0 -154
  287. package/pkg/model-loader.lib.js +0 -3
  288. package/plugins/lookup.js +0 -50
  289. package/promptRemove/kubevirt.io.virtualmachine.vue +0 -164
  290. package/store/harvester-common.js +0 -126
  291. package/utils/validators/vm-datavolumes.js +0 -38
  292. package/utils/validators/vm-image.js +0 -32
  293. package/utils/validators/vm.js +0 -221
  294. package/yarn-error.log +0 -196
package/pages/index.vue CHANGED
@@ -2,21 +2,35 @@
2
2
  import { SEEN_WHATS_NEW } from '@shell/store/prefs';
3
3
  import { getVersionInfo } from '@shell/utils/version';
4
4
 
5
+ const validRoute = (route, router) => {
6
+ return !!route && !!router.resolve(route)?.resolved?.matched?.length;
7
+ };
8
+
5
9
  export default {
6
- middleware({ redirect, store } ) {
10
+ middleware({ redirect, store, app } ) {
7
11
  const seenWhatsNew = store.getters['prefs/get'](SEEN_WHATS_NEW);
8
12
  const versionInfo = getVersionInfo(store);
9
13
  const isSingleProduct = store.getters['isSingleProduct'];
14
+ const dashboardHome = { name: 'home' };
10
15
 
11
16
  // If this is a new version, then take the user to the home page to view the release notes
12
17
  if (versionInfo.fullVersion !== seenWhatsNew && !isSingleProduct) {
13
- return redirect({ name: 'home' });
18
+ return redirect(dashboardHome);
14
19
  }
15
20
 
16
- // Take the user to the configured login route
17
21
  const afterLoginRouteObject = store.getters['prefs/afterLoginRoute'];
18
22
 
19
- return redirect(afterLoginRouteObject);
23
+ // Confirm this is a valid route (it could have come from an uninstalled plugin)
24
+ if (validRoute(afterLoginRouteObject, app.router)) {
25
+ // Take the user to the configured login route
26
+ return redirect(afterLoginRouteObject);
27
+ }
28
+
29
+ if (validRoute(isSingleProduct?.afterLoginRoute, app.router)) {
30
+ return redirect(isSingleProduct.afterLoginRoute);
31
+ }
32
+
33
+ return redirect(dashboardHome);
20
34
  }
21
35
  };
22
36
  </script>
package/pages/plugins.vue CHANGED
@@ -242,7 +242,7 @@ export default {
242
242
  <LabeledInput v-model="location" label="Plugin URL" @input="updateLocation" />
243
243
  </div>
244
244
  <div class="dialog-buttons">
245
- <button class="btn" @click="closeAddDialog()">
245
+ <button class="btn role-secondary" @click="closeAddDialog()">
246
246
  Cancel
247
247
  </button>
248
248
  <button class="btn role-primary" @click="loadPlugin()">
@@ -268,7 +268,7 @@ export default {
268
268
  <LabeledInput v-model="catalogUrl" label="Catalog URL" />
269
269
  </div>
270
270
  <div class="dialog-buttons">
271
- <button class="btn" @click="closeAddCatalogDialog()">
271
+ <button class="btn role-secondary" @click="closeAddCatalogDialog()">
272
272
  Cancel
273
273
  </button>
274
274
  <button class="btn role-primary" @click="addCatalog()">
package/pages/prefs.vue CHANGED
@@ -159,8 +159,11 @@ export default {
159
159
  <LandingPagePreference />
160
160
  </div>
161
161
  <hr />
162
- <h4 v-t="'prefs.formatting'" />
163
- <div class="row">
162
+ <h4 v-t="'prefs.displaySettings.title'" />
163
+ <p class="set-landing-leadin">
164
+ {{ t('prefs.displaySettings.detail', {}, raw=true) }}
165
+ </p>
166
+ <div class="row mt-20">
164
167
  <div class="col span-4">
165
168
  <LabeledSelect
166
169
  v-model="dateFormat"
@@ -175,7 +178,9 @@ export default {
175
178
  :options="timeOptions"
176
179
  />
177
180
  </div>
181
+ </div>
178
182
 
183
+ <div class="row mt-20">
179
184
  <div class="col span-4">
180
185
  <LabeledSelect
181
186
  v-model.number="perPage"
@@ -186,9 +191,6 @@ export default {
186
191
  placeholder="Select a row count"
187
192
  />
188
193
  </div>
189
- </div>
190
-
191
- <div class="row mt-20">
192
194
  <div class="col span-4">
193
195
  <LabeledSelect
194
196
  v-model.number="menuMaxClusters"
@@ -207,7 +209,7 @@ export default {
207
209
  <h4 v-t="'prefs.advanced'" />
208
210
  <Checkbox v-model="dev" :label="t('prefs.dev.label', {}, true)" />
209
211
  <p class="wrap-text">
210
- {{ t('prefs.advancedTooltip', {}, raw=true) }}
212
+ {{ t('prefs.advancedTooltip') }}
211
213
  </p>
212
214
  <br>
213
215
  <Checkbox v-if="!isSingleProduct" v-model="hideDescriptions" :label="t('prefs.hideDesc.label')" class="mt-10" />
@@ -1,6 +1,6 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
- const contextFolders = ['chart', 'cloud-credential', 'content', 'detail', 'edit', 'list', 'machine-config', 'models', 'promptRemove', 'l10n', 'windowComponents', 'formatters'];
3
+ const contextFolders = ['chart', 'cloud-credential', 'content', 'detail', 'edit', 'list', 'machine-config', 'models', 'promptRemove', 'l10n', 'windowComponents', 'dialog', 'formatters'];
4
4
  const contextMap = contextFolders.reduce((map, obj) => {
5
5
  map[obj] = true;
6
6
 
@@ -11,6 +11,19 @@ function replaceAll(str, find, replace) {
11
11
  return str.split(find).join(replace);
12
12
  }
13
13
 
14
+ function registerFile(file, type, pkg, f) {
15
+ const importType = (f === 'models') ? 'require' : 'import';
16
+ const chunkName = (f === 'l10n') ? '' : `/* webpackChunkName: "${ f }" */`;
17
+
18
+ return ` $plugin.register('${ f }', '${ type }', () => ${ importType }(${ chunkName }'${ pkg }/${ f }/${ file }'));\n`;
19
+ }
20
+
21
+ function register(file, pkg, f) {
22
+ const name = file.replace(/\.[^/.]+$/, '');
23
+
24
+ return registerFile(file, name, pkg, f);
25
+ }
26
+
14
27
  // This function is used to generate the code to register models, edit, detail, list etc for a type
15
28
  // This is used when building as a library - it does not use require.context - it scans the file system and build time.
16
29
  // This ensures that the webpackChunkName is respected (require.context does not support this) - so when build as a library
@@ -20,13 +33,34 @@ function generateTypeImport(pkg, dir) {
20
33
 
21
34
  // Auto-import if the folder exists
22
35
  contextFolders.forEach((f) => {
23
- if (fs.existsSync(path.join(dir, f))) {
36
+ const filePath = path.join(dir, f);
37
+
38
+ if (fs.existsSync(filePath)) {
24
39
  fs.readdirSync(path.join(dir, f)).forEach((file) => {
25
- const name = file.replace(/\.[^/.]+$/, '');
26
- const importType = (f === 'models') ? 'require' : 'import';
27
- const chunkName = (f === 'l10n') ? '' : `/* webpackChunkName: "${ f }" */`;
40
+ const fileStat = fs.lstatSync(path.join(filePath, file));
41
+
42
+ // Directories are special cases
43
+ if (fileStat.isDirectory()) {
44
+ // This might be a <type>/index.vue (aka nested component)
45
+ const indexFilePath = path.join(file, 'index.vue');
46
+ const fullIndexFilePath = path.join(filePath, indexFilePath);
28
47
 
29
- content += ` $plugin.register('${ f }', '${ name }', () => ${ importType }(${ chunkName }'${ pkg }/${ f }/${ file }'));\n`;
48
+ if (fs.existsSync(fullIndexFilePath)) {
49
+ content += registerFile(indexFilePath, file, pkg, f);
50
+
51
+ return;
52
+ }
53
+
54
+ // This might be a <store name>/<model name|type>.js file (aka nested model)
55
+ if (f === 'models') {
56
+ fs.readdirSync(path.join(filePath, file)).forEach((store) => {
57
+ content += register(path.join(file, store), pkg, f);
58
+ });
59
+ }
60
+ } else {
61
+ // This is a simple <resource type>.<file type> file
62
+ content += register(file, pkg, f);
63
+ }
30
64
  });
31
65
  }
32
66
  });
@@ -48,7 +82,10 @@ function generateDynamicTypeImport(pkg, dir) {
48
82
  // Auto-import if the folder exists
49
83
  contextFolders.forEach((f) => {
50
84
  if (fs.existsSync(path.join(dir, f))) {
51
- let genImport = replaceAll(template, 'NAME', f);
85
+ const safeName = f.replace(/\/|-/g, '_');
86
+ let genImport = replaceAll(template, 'NAME', safeName);
87
+
88
+ genImport = replaceAll(genImport, 'DIR', f );
52
89
  const importType = (f === 'models') ? 'require' : 'import';
53
90
  // Ensure i18n chunks are named with the request name (which will be the locale)
54
91
  const chunk = (f === 'l10n') ? '[request]' : f;
@@ -0,0 +1,28 @@
1
+
2
+ /**
3
+ * Some plugins won't be bundled with the dashboard build but loaded on demand at run time.
4
+ * This file allows 'manager' style plugins to defined how to determine if a unknown route
5
+ * belongs to one of their associated plugins and how that plugin can be loaded
6
+ */
7
+ class DynamicPluginLoader {
8
+ dynamicPluginLoaders = [];
9
+
10
+ register(reg) {
11
+ this.dynamicPluginLoaders.push(reg);
12
+ }
13
+
14
+ async check({ route, store }) {
15
+ for (const dpl of this.dynamicPluginLoaders) {
16
+ // Check that the route is valid and then load the plugin associated with it
17
+ const res = await dpl.load({ route, store });
18
+
19
+ if (res) {
20
+ return res;
21
+ }
22
+ }
23
+ }
24
+ }
25
+
26
+ const dynamicPluginLoader = new DynamicPluginLoader();
27
+
28
+ export default dynamicPluginLoader;
package/pkg/import.js CHANGED
@@ -1,4 +1,4 @@
1
- const _NAME = require.context('BASE/NAME', true, /\.(vue|js|yaml)$/).keys();
1
+ const _NAME = require.context('BASE/DIR', true, /\.(vue|js|yaml)$/).keys();
2
2
 
3
3
  _NAME.forEach((f) => {
4
4
  let name = f.substr(2);
@@ -6,5 +6,5 @@ _NAME.forEach((f) => {
6
6
 
7
7
  name = name.substr(0, ext);
8
8
 
9
- $plugin.register('NAME', name, () => REQUIRE(CHUNK`BASE/NAME/${ name }EXT`)); // eslint-disable-line no-undef
9
+ $plugin.register('DIR', name, () => REQUIRE(CHUNK`BASE/DIR/${ name }EXT`)); // eslint-disable-line no-undef
10
10
  });
@@ -0,0 +1,3 @@
1
+ export default function modelLoaderRequire(type) {
2
+ return null;
3
+ }
package/pkg/vue.config.js CHANGED
@@ -46,20 +46,23 @@ module.exports = function(dir) {
46
46
  config.resolve.alias['~pkg'] = dir;
47
47
  delete config.resolve.alias['@'];
48
48
 
49
- // Prevent the dynamic importer and the model-loader from importing anything dynamically - we don't want all of the
49
+ // Prevent the dynamic importer and the model-loader-require from importing anything dynamically - we don't want all of the
50
50
  // models etc when we build as a library
51
- const dynamicImporterOveride = new webpack.NormalModuleReplacementPlugin(/dynamic-importer$/, (resource) => {
51
+ const dynamicImporterOverride = new webpack.NormalModuleReplacementPlugin(/dynamic-importer$/, (resource) => {
52
52
  resource.request = path.join(__dirname, 'dynamic-importer.lib.js');
53
53
  });
54
- const modelLoaderImporterOveride = new webpack.NormalModuleReplacementPlugin(/model-loader$/, (resource) => {
55
- resource.request = path.join(__dirname, 'model-loader.lib.js');
54
+ const modelLoaderImporterOverride = new webpack.NormalModuleReplacementPlugin(/model-loader-require$/, (resource) => {
55
+ const fileName = 'model-loader-require.lib.js';
56
+ const pkgModelLoaderRequire = path.join(dir, fileName);
57
+
58
+ resource.request = fs.existsSync(pkgModelLoaderRequire) ? pkgModelLoaderRequire : path.join(__dirname, fileName);
56
59
  });
57
60
 
58
61
  // Auto-generate module to import the types (model, detail, edit etc)
59
62
  const autoImportPlugin = new VirtualModulesPlugin({ 'node_modules/@rancher/auto-import': generateTypeImport('@pkg', dir) });
60
63
 
61
- config.plugins.unshift(dynamicImporterOveride);
62
- config.plugins.unshift(modelLoaderImporterOveride);
64
+ config.plugins.unshift(dynamicImporterOverride);
65
+ config.plugins.unshift(modelLoaderImporterOverride);
63
66
  config.plugins.unshift(autoImportPlugin);
64
67
  // config.plugins.unshift(debug);
65
68
 
@@ -1,12 +1,17 @@
1
1
  /* eslint-disable no-console */
2
- export default (context) => {
3
- const logTypes = ['log', 'error', 'info', 'warn'];
2
+ export default () => {
3
+ const logTypes = ['warn', 'error'];
4
4
  const MAX_LOGS_STORED = 400;
5
5
 
6
- console.logLog = console.log.bind(console);
7
- console.errorLog = console.error.bind(console);
8
- console.infoLog = console.info.bind(console);
6
+ if (!process.env.dev) {
7
+ console.logLog = console.log.bind(console);
8
+ console.infoLog = console.info.bind(console);
9
+ logTypes.push('log');
10
+ logTypes.push('info');
11
+ }
12
+
9
13
  console.warnLog = console.warn.bind(console);
14
+ console.errorLog = console.error.bind(console);
10
15
  console.logs = [];
11
16
 
12
17
  logTypes.forEach((type) => {
@@ -319,7 +319,12 @@ export default {
319
319
  return all;
320
320
  },
321
321
 
322
- async findMatching(ctx, { type, selector, opt }) {
322
+ async findMatching(ctx, {
323
+ type,
324
+ selector,
325
+ opt,
326
+ namespace
327
+ }) {
323
328
  const {
324
329
  getters, commit, dispatch, rootGetters
325
330
  } = ctx;
@@ -332,7 +337,7 @@ export default {
332
337
  commit('registerType', type);
333
338
  }
334
339
  if ( opt.force !== true && getters['haveSelector'](type, selector) ) {
335
- return getters.matching( type, selector );
340
+ return getters.matching( type, selector, namespace );
336
341
  }
337
342
 
338
343
  const typeOptions = rootGetters['type-map/optionsFor'](type);
@@ -366,7 +371,7 @@ export default {
366
371
  });
367
372
  }
368
373
 
369
- return getters.matching( type, selector );
374
+ return getters.matching( type, selector, namespace );
370
375
  },
371
376
 
372
377
  // opt:
@@ -25,8 +25,13 @@ export default {
25
25
  return state.types[type].list;
26
26
  },
27
27
 
28
- matching: (state, getters) => (type, selector) => {
29
- const all = getters['all'](type);
28
+ matching: (state, getters) => (type, selector, namespace) => {
29
+ let all = getters['all'](type);
30
+
31
+ // Filter first by namespace if one is provided, since this is efficient
32
+ if (namespace) {
33
+ all = all.filter(obj => obj.namespace === namespace);
34
+ }
30
35
 
31
36
  return all.filter((obj) => {
32
37
  return matches(obj, selector);
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Fetch a model from the dashboard's local model's folder
3
+ *
4
+ * Splitting this out into a separate function means packages can
5
+ * override this to ensure dashboard models aren't bundled with it
6
+ *
7
+ * @param {string} type
8
+ * @returns Model for the given type
9
+ */
10
+ export default function modelLoaderRequire(type) {
11
+ return require(`@shell/models/${ type }`);
12
+ }
@@ -1,4 +1,5 @@
1
1
  import { normalizeType } from './normalize';
2
+ import modelLoaderRequire from './model-loader-require';
2
3
 
3
4
  const cache = {};
4
5
 
@@ -16,7 +17,9 @@ function find(cache, type, rootState) {
16
17
  let base;
17
18
 
18
19
  if (!pluginModel) {
19
- base = require(`@shell/models/${ type }`);
20
+ // Model hasn't come from a plugin, fall back on something reasonable
21
+ // by default this is the dashboard's local models folder
22
+ base = modelLoaderRequire(type);
20
23
  } else if (typeof pluginModel === 'function') {
21
24
  // pluginModel could be an object in the case the plugin is built-in
22
25
  base = pluginModel();
@@ -24,7 +24,7 @@ import {
24
24
  validateDnsLikeTypes,
25
25
  validateLength,
26
26
  } from '@shell/utils/validators';
27
- import formRulesGenerator from '@shell/utils/validators/formRules';
27
+ import formRulesGenerator from '@shell/utils/validators/formRules/index';
28
28
  import jsyaml from 'js-yaml';
29
29
  import compact from 'lodash/compact';
30
30
  import forIn from 'lodash/forIn';
@@ -1587,8 +1587,15 @@ export default class Resource {
1587
1587
  if (!isEmpty(validatorName) && validatorExists) {
1588
1588
  CustomValidators[validatorName](pathValue, this.$rootGetters, errors, validatorArgs, displayKey, data);
1589
1589
  } else if (!isEmpty(validatorName) && !validatorExists) {
1590
- // eslint-disable-next-line
1591
- console.warn(this.t('validation.custom.missing', { validatorName }));
1590
+ // Check if validator is imported from plugin
1591
+ const pluginValidator = this.$rootState.$plugin?.getValidator(validatorName);
1592
+
1593
+ if (pluginValidator) {
1594
+ pluginValidator(pathValue, this.$rootGetters, errors, validatorArgs, displayKey, data);
1595
+ } else {
1596
+ // eslint-disable-next-line
1597
+ console.warn(this.t('validation.custom.missing', { validatorName }));
1598
+ }
1592
1599
  }
1593
1600
  });
1594
1601
  });
@@ -178,7 +178,7 @@ export default {
178
178
  const res = err.response;
179
179
 
180
180
  // Go to the logout page for 401s, unless redirectUnauthorized specifically disables (for the login page)
181
- if ( opt.redirectUnauthorized !== false && process.client && res.status === 401 ) {
181
+ if ( opt.redirectUnauthorized !== false && res.status === 401 ) {
182
182
  dispatch('auth/logout', opt.logoutOnError, { root: true });
183
183
  }
184
184
 
@@ -10,7 +10,7 @@ import getters, { STEVE_MODEL_TYPES } from './getters';
10
10
  import mutations from './mutations';
11
11
  import actions from './actions';
12
12
 
13
- function SteveFactory(namespace, baseUrl) {
13
+ export function SteveFactory(namespace, baseUrl) {
14
14
  return {
15
15
  ...coreStoreModule,
16
16
 
@@ -50,6 +50,10 @@ function SteveFactory(namespace, baseUrl) {
50
50
  };
51
51
  }
52
52
 
53
+ export const steveStoreInit = (store, ctx) => {
54
+ createWorker(store, ctx);
55
+ };
56
+
53
57
  export default (config) => {
54
58
  config.namespace = config.namespace || '';
55
59
 
@@ -67,8 +71,6 @@ export default (config) => {
67
71
  return coreStore(
68
72
  SteveFactory(config.namespace, config.baseUrl),
69
73
  config,
70
- (store, ctx) => {
71
- createWorker(store, ctx);
72
- }
74
+ steveStoreInit
73
75
  );
74
76
  };
@@ -0,0 +1,32 @@
1
+ import SteveModel from './steve-class';
2
+
3
+ /**
4
+ * SteveModel that supports the description being in the root 'description' property.
5
+ */
6
+ export default class SteveDescriptionModel extends SteveModel {
7
+ // Preserve description
8
+ constructor(data, ctx, rehydrateNamespace = null, setClone = false) {
9
+ const _description = data.description;
10
+
11
+ super(data, ctx, rehydrateNamespace, setClone);
12
+ this.description = _description;
13
+ }
14
+
15
+ get description() {
16
+ return this._description;
17
+ }
18
+
19
+ set description(value) {
20
+ this._description = value;
21
+ }
22
+
23
+ // Ensure when we clone that we preserve the desription
24
+ toJSON() {
25
+ const data = super.toJSON();
26
+
27
+ data.description = this.description;
28
+ delete data._description;
29
+
30
+ return data;
31
+ }
32
+ }
@@ -1,6 +1,7 @@
1
1
  import { addObject, clear, removeObject } from '@shell/utils/array';
2
2
  import { get } from '@shell/utils/object';
3
- import { COUNT, SCHEMA } from '@shell/config/types';
3
+ import { COUNT, MANAGEMENT, SCHEMA } from '@shell/config/types';
4
+ import { SETTING } from '@shell/config/settings';
4
5
  import Socket, {
5
6
  EVENT_CONNECTED,
6
7
  EVENT_DISCONNECTED,
@@ -456,28 +457,38 @@ export const actions = {
456
457
  clearTimeout(state.queueTimer);
457
458
  state.queueTimer = null;
458
459
  if (e.type === EVENT_DISCONNECT_ERROR) {
459
- // do not send a growl notification unless the socket stays disconnected for more than MINIMUM_TIME_DISCONNECTED
460
- setTimeout(() => {
461
- if (state.socket.isConnected()) {
462
- return;
463
- }
464
- const dateFormat = escapeHtml( rootGetters['prefs/get'](DATE_FORMAT));
465
- const timeFormat = escapeHtml( rootGetters['prefs/get'](TIME_FORMAT));
466
- const time = e?.srcElement?.disconnectedAt || Date.now();
467
-
468
- const timeFormatted = `${ day(time).format(`${ dateFormat } ${ timeFormat }`) }`;
469
- const url = e?.srcElement?.url;
470
-
471
- const t = rootGetters['i18n/t'];
472
-
473
- dispatch('growl/error', {
474
- title: t('growl.disconnected.title'),
475
- message: t('growl.disconnected.message', { url, time: timeFormatted }, { raw: true }),
476
- icon: 'error',
477
- earliestClose: time + MINIMUM_TIME_NOTIFIED + MINIMUM_TIME_DISCONNECTED,
478
- url
479
- }, { root: true });
480
- }, MINIMUM_TIME_DISCONNECTED);
460
+ // determine if websocket notifications are disabled
461
+ const perfSetting = rootGetters['management/byId'](MANAGEMENT.SETTING, SETTING.UI_PERFORMANCE);
462
+ let disableGrowl = false;
463
+
464
+ if ( perfSetting?.value ) {
465
+ disableGrowl = JSON.parse(perfSetting.value).disableWebsocketNotification || false;
466
+ }
467
+
468
+ if ( !disableGrowl ) {
469
+ // do not send a growl notification unless the socket stays disconnected for more than MINIMUM_TIME_DISCONNECTED
470
+ setTimeout(() => {
471
+ if (state.socket.isConnected()) {
472
+ return;
473
+ }
474
+ const dateFormat = escapeHtml( rootGetters['prefs/get'](DATE_FORMAT));
475
+ const timeFormat = escapeHtml( rootGetters['prefs/get'](TIME_FORMAT));
476
+ const time = e?.srcElement?.disconnectedAt || Date.now();
477
+
478
+ const timeFormatted = `${ day(time).format(`${ dateFormat } ${ timeFormat }`) }`;
479
+ const url = e?.srcElement?.url;
480
+
481
+ const t = rootGetters['i18n/t'];
482
+
483
+ dispatch('growl/error', {
484
+ title: t('growl.disconnected.title'),
485
+ message: t('growl.disconnected.message', { url, time: timeFormatted }, { raw: true }),
486
+ icon: 'error',
487
+ earliestClose: time + MINIMUM_TIME_NOTIFIED + MINIMUM_TIME_DISCONNECTED,
488
+ url
489
+ }, { root: true });
490
+ }, MINIMUM_TIME_DISCONNECTED);
491
+ }
481
492
  } else {
482
493
  // if the error is not a disconnect error, the socket never worked: log whether the current browser is safari
483
494
  console.error(`WebSocket Connection Error [${ getters.storeName }]`, e.detail); // eslint-disable-line no-console
@@ -131,8 +131,8 @@ export default Vue.extend({
131
131
  }
132
132
 
133
133
  &.secondary {
134
- background: var(--secondary-banner-bg);
135
- border-left: solid $left-border-size var(--secondary);
134
+ background: var(--default-banner-bg);
135
+ border-left: solid $left-border-size var(--default);
136
136
  color: var(--body-text);
137
137
  }
138
138
 
@@ -0,0 +1,77 @@
1
+ import { shallowMount } from '@vue/test-utils';
2
+ import { Checkbox } from './index';
3
+
4
+ describe('Checkbox.vue', () => {
5
+ it('is unchecked by default', () => {
6
+ const wrapper = shallowMount(Checkbox);
7
+ const cbInput = wrapper.find('input[type="checkbox"]').element as HTMLInputElement;
8
+
9
+ expect(cbInput.checked).toBe(false);
10
+ });
11
+
12
+ it('renders a true value', () => {
13
+ const wrapper = shallowMount(Checkbox, { propsData: { value: true } });
14
+ const cbInput = wrapper.find('input[type="checkbox"]').element as HTMLInputElement;
15
+
16
+ expect(cbInput.checked).toBe(true);
17
+ });
18
+
19
+ it('updates from false to true when props change', async () => {
20
+ const wrapper = shallowMount(Checkbox);
21
+ const cbInput = wrapper.find('input[type="checkbox"]').element as HTMLInputElement;
22
+
23
+ expect(cbInput.checked).toBe(false);
24
+
25
+ await wrapper.setProps({ value: true });
26
+
27
+ expect(cbInput.checked).toBe(true);
28
+ });
29
+
30
+ it('emits an input event with a true value', async () => {
31
+ const wrapper = shallowMount(Checkbox);
32
+ const event = {
33
+ target: { tagName: 'input', href: null },
34
+ stopPropagation: () => { },
35
+ preventDefault: () => { }
36
+ };
37
+
38
+ (wrapper.vm as any).clicked(event);
39
+ await wrapper.vm.$nextTick();
40
+
41
+ expect(wrapper.emitted().input?.length).toBe(1);
42
+ expect(wrapper.emitted().input?.[0][0]).toBe(true);
43
+ });
44
+
45
+ it('emits an input event with a custom valueWhenTrue', async () => {
46
+ const valueWhenTrue = 'BIG IF TRUE';
47
+ const event = {
48
+ target: { tagName: 'input', href: null },
49
+ stopPropagation: () => { },
50
+ preventDefault: () => { }
51
+ };
52
+
53
+ const wrapper = shallowMount(Checkbox, { propsData: { value: false, valueWhenTrue } });
54
+
55
+ (wrapper.vm as any).clicked(event);
56
+ await wrapper.vm.$nextTick();
57
+
58
+ expect(wrapper.emitted().input?.length).toBe(1);
59
+ expect(wrapper.emitted().input?.[0][0]).toBe(valueWhenTrue);
60
+ });
61
+
62
+ it('updates from valueWhenTrue to falsy', async () => {
63
+ const valueWhenTrue = 'REAL HUGE IF FALSE';
64
+ const event = {
65
+ target: { tagName: 'input', href: null },
66
+ stopPropagation: () => { },
67
+ preventDefault: () => { }
68
+ };
69
+
70
+ const wrapper = shallowMount(Checkbox, { propsData: { value: valueWhenTrue, valueWhenTrue } });
71
+
72
+ (wrapper.vm as any).clicked(event);
73
+ await wrapper.vm.$nextTick();
74
+
75
+ expect(wrapper.emitted().input?.[0][0]).toBe(null);
76
+ })
77
+ });