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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. package/assets/styles/global/_layout.scss +4 -0
  2. package/assets/translations/en-us.yaml +144 -41
  3. package/assets/translations/zh-hans.yaml +1 -7
  4. package/chart/monitoring/ClusterSelector.vue +0 -21
  5. package/chart/monitoring/prometheus/index.vue +6 -3
  6. package/components/CruResource.vue +161 -14
  7. package/components/ExplorerMembers.vue +8 -4
  8. package/components/ExplorerProjectsNamespaces.vue +10 -6
  9. package/components/GrowlManager.vue +4 -0
  10. package/components/MgmtNodeList.vue +184 -0
  11. package/components/Resource/Detail/Card/StateCard/__tests__/composables.test.ts +90 -1
  12. package/components/Resource/Detail/Card/StateCard/composables.ts +57 -87
  13. package/components/Resource/Detail/Card/StatusCard/__tests__/StatusCard.test.ts +61 -0
  14. package/components/Resource/Detail/Card/StatusCard/index.vue +61 -15
  15. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +2 -0
  16. package/components/Resource/Detail/Metadata/KeyValue.vue +5 -2
  17. package/components/Resource/Detail/Metadata/KeyValueRow.vue +2 -6
  18. package/components/ResourceDetail/index.vue +1 -1
  19. package/components/ResourceList/Masthead.vue +7 -1
  20. package/components/ResourceList/index.vue +82 -1
  21. package/components/RichTranslation.vue +5 -2
  22. package/components/Setting.vue +1 -0
  23. package/components/SubtleLink.vue +31 -6
  24. package/components/Tabbed/Tab.vue +29 -3
  25. package/components/Tabbed/index.vue +25 -3
  26. package/components/TableOfContents/TableOfContents.vue +109 -0
  27. package/components/TableOfContents/composables.ts +258 -0
  28. package/components/Window/ContainerShell.vue +21 -11
  29. package/components/Window/__tests__/ContainerShell.test.ts +107 -37
  30. package/components/Wizard.vue +9 -4
  31. package/components/fleet/AppCoChartGrid.vue +401 -0
  32. package/components/fleet/AppCoEmptyState.vue +127 -0
  33. package/components/fleet/AppCoPageHeader.vue +119 -0
  34. package/components/fleet/AppCoVersionSelect.vue +70 -0
  35. package/components/fleet/FleetClusterTargets/ClusterSelectionFields.vue +217 -0
  36. package/components/fleet/FleetClusterTargets/TargetsList.vue +123 -35
  37. package/components/fleet/FleetClusterTargets/index.vue +189 -146
  38. package/components/fleet/FleetIntro.vue +7 -3
  39. package/components/fleet/FleetNoWorkspaces.vue +7 -3
  40. package/components/fleet/FleetSecretSelector.vue +5 -3
  41. package/components/fleet/FleetValuesFrom.vue +8 -2
  42. package/components/fleet/GitRepoTargetTab.vue +0 -2
  43. package/components/fleet/HelmOpAdvancedTab.vue +19 -53
  44. package/components/fleet/HelmOpAppCoConfigTab.vue +593 -0
  45. package/components/fleet/HelmOpAppCoResourcesSection.vue +162 -0
  46. package/components/fleet/HelmOpResourcesSection.vue +82 -0
  47. package/components/fleet/HelmOpTargetOptionsSection.vue +89 -0
  48. package/components/fleet/HelmOpTargetTab.vue +64 -60
  49. package/components/fleet/HelmOpValuesTab.vue +129 -105
  50. package/components/fleet/__tests__/AppCoEmptyState.test.ts +71 -0
  51. package/components/fleet/__tests__/AppCoVersionSelect.test.ts +36 -0
  52. package/components/fleet/__tests__/ClusterSelectionFields.test.ts +62 -0
  53. package/components/fleet/__tests__/FleetClusterTargets.test.ts +253 -0
  54. package/components/fleet/__tests__/FleetSecretSelector.test.ts +16 -0
  55. package/components/fleet/__tests__/FleetValuesFrom.test.ts +44 -0
  56. package/components/fleet/__tests__/HelmOpAppCoConfigTab.test.ts +59 -0
  57. package/components/fleet/__tests__/HelmOpAppCoResourcesSection.test.ts +62 -0
  58. package/components/fleet/__tests__/HelmOpResourcesSection.test.ts +43 -0
  59. package/components/fleet/__tests__/HelmOpTargetOptionsSection.test.ts +34 -0
  60. package/components/fleet/__tests__/HelmOpValuesTab.test.ts +39 -0
  61. package/components/fleet/__tests__/__snapshots__/AppCoEmptyState.test.ts.snap +97 -0
  62. package/components/fleet/__tests__/__snapshots__/AppCoVersionSelect.test.ts.snap +30 -0
  63. package/components/fleet/__tests__/__snapshots__/ClusterSelectionFields.test.ts.snap +209 -0
  64. package/components/fleet/__tests__/__snapshots__/HelmOpTargetOptionsSection.test.ts.snap +140 -0
  65. package/components/fleet/dashboard/Empty.vue +8 -4
  66. package/components/fleet/dashboard/ResourceCard.vue +28 -0
  67. package/components/fleet/dashboard/ResourceDetails.vue +28 -0
  68. package/components/fleet/dashboard/__tests__/ResourceCard.test.ts +87 -0
  69. package/components/form/ArrayList.vue +61 -4
  70. package/components/form/KeyValue.vue +23 -2
  71. package/components/form/LabeledSelect.vue +39 -1
  72. package/components/form/Labels.vue +22 -3
  73. package/components/form/NameNsDescription.vue +13 -5
  74. package/components/form/ResourceTabs/index.vue +1 -0
  75. package/components/form/__tests__/NameNsDescription.test.ts +75 -0
  76. package/components/formatter/InternalExternalIP.vue +10 -4
  77. package/components/formatter/ServiceTargets.vue +26 -7
  78. package/components/formatter/__tests__/InternalExternalIP.test.ts +132 -0
  79. package/components/formatter/__tests__/ServiceTargets.test.ts +412 -0
  80. package/components/nav/Header.vue +4 -0
  81. package/components/nav/TopLevelMenu.vue +7 -2
  82. package/components/nav/__tests__/Header.test.ts +15 -0
  83. package/components/nav/__tests__/TopLevelMenu.test.ts +120 -2
  84. package/components/templates/default.vue +9 -4
  85. package/components/templates/home.vue +9 -4
  86. package/components/templates/plain.vue +9 -4
  87. package/composables/useHelmOpResources.test.ts +56 -0
  88. package/composables/useHelmOpResources.ts +32 -0
  89. package/composables/useStateColor.test.ts +325 -0
  90. package/composables/useStateColor.ts +128 -0
  91. package/config/home-links.js +1 -1
  92. package/config/labels-annotations.js +1 -0
  93. package/config/product/explorer.js +17 -4
  94. package/config/product/manager.js +2 -0
  95. package/config/router/index.js +16 -0
  96. package/config/router/navigation-guards/__tests__/authentication.test.ts +130 -0
  97. package/config/router/navigation-guards/authentication.js +10 -4
  98. package/config/router/routes.js +20 -6
  99. package/config/settings.ts +0 -2
  100. package/config/table-headers.js +3 -4
  101. package/config/types.js +9 -0
  102. package/core/plugin-products-base.ts +3 -3
  103. package/core/plugin-types.ts +83 -30
  104. package/core/plugin.ts +3 -0
  105. package/core/types-provisioning.ts +34 -1
  106. package/core/types.ts +15 -2
  107. package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +114 -0
  108. package/detail/__tests__/workload.test.ts +3 -152
  109. package/detail/catalog.cattle.io.clusterrepo.vue +1 -1
  110. package/detail/provisioning.cattle.io.cluster.vue +30 -4
  111. package/detail/workload/index.vue +12 -55
  112. package/edit/__tests__/catalog.cattle.io.clusterrepo.test.ts +248 -0
  113. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +105 -0
  114. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/General.test.ts.snap +6 -0
  115. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/index.test.ts.snap +1 -0
  116. package/edit/auth/__tests__/azuread.test.ts +34 -9
  117. package/edit/auth/__tests__/github.test.ts +234 -0
  118. package/edit/auth/__tests__/oidc.test.ts +26 -6
  119. package/edit/auth/__tests__/saml.test.ts +196 -0
  120. package/edit/auth/azuread.vue +128 -95
  121. package/edit/auth/github.vue +72 -13
  122. package/edit/auth/ldap/__tests__/index.test.ts +206 -0
  123. package/edit/auth/ldap/config.vue +8 -0
  124. package/edit/auth/ldap/index.vue +75 -1
  125. package/edit/auth/oidc.vue +119 -73
  126. package/edit/auth/saml.vue +76 -12
  127. package/edit/catalog.cattle.io.clusterrepo.vue +140 -32
  128. package/edit/fleet.cattle.io.helmop.vue +491 -136
  129. package/edit/management.cattle.io.user.vue +5 -2
  130. package/edit/provisioning.cattle.io.cluster/rke2.vue +84 -10
  131. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +11 -0
  132. package/list/group.principal.vue +5 -4
  133. package/list/harvesterhci.io.management.cluster.vue +8 -9
  134. package/list/management.cattle.io.user.vue +12 -9
  135. package/list/provisioning.cattle.io.cluster.vue +16 -10
  136. package/mixins/__tests__/auth-config.test.ts +90 -0
  137. package/mixins/__tests__/chart.test.ts +94 -0
  138. package/mixins/__tests__/resource-fetch-api-pagination.test.ts +48 -0
  139. package/mixins/auth-config.js +7 -0
  140. package/mixins/chart.js +11 -2
  141. package/mixins/child-hook.js +12 -6
  142. package/mixins/create-edit-view/impl.js +5 -3
  143. package/mixins/resource-fetch-api-pagination.js +21 -1
  144. package/models/__tests__/catalog.cattle.io.clusterrepo.test.ts +57 -0
  145. package/models/__tests__/compliance.cattle.io.clusterscan.test.ts +144 -0
  146. package/models/__tests__/fleet-application.test.ts +175 -0
  147. package/models/__tests__/fleet.cattle.io.bundle.test.ts +169 -0
  148. package/models/__tests__/fleet.cattle.io.helmop.test.ts +84 -0
  149. package/models/__tests__/management.cattle.io.node.ts +22 -0
  150. package/models/__tests__/namespace.test.ts +36 -0
  151. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +49 -0
  152. package/models/__tests__/workload.test.ts +401 -26
  153. package/models/catalog.cattle.io.clusterrepo.js +28 -4
  154. package/models/compliance.cattle.io.clusterscan.js +39 -4
  155. package/models/fleet-application.js +4 -0
  156. package/models/fleet.cattle.io.helmop.js +20 -1
  157. package/models/management.cattle.io.cluster.js +18 -2
  158. package/models/management.cattle.io.node.js +44 -3
  159. package/models/namespace.js +1 -1
  160. package/models/pod.js +33 -1
  161. package/models/provisioning.cattle.io.cluster.js +5 -5
  162. package/models/workload.js +108 -13
  163. package/models/workload.service.js +5 -0
  164. package/package.json +14 -13
  165. package/pages/about.vue +5 -6
  166. package/pages/auth/login.vue +0 -35
  167. package/pages/auth/setup.vue +11 -0
  168. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +2 -2
  169. package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +10 -1
  170. package/pages/c/_cluster/apps/charts/__tests__/index.test.ts +93 -0
  171. package/pages/c/_cluster/apps/charts/chart.vue +2 -1
  172. package/pages/c/_cluster/apps/charts/index.vue +48 -10
  173. package/pages/c/_cluster/apps/charts/install.vue +122 -116
  174. package/pages/c/_cluster/auth/roles/index.vue +5 -4
  175. package/pages/c/_cluster/explorer/workload-dashboard/ByNamespaceSection.vue +31 -0
  176. package/pages/c/_cluster/explorer/workload-dashboard/ByStateSection.vue +138 -0
  177. package/pages/c/_cluster/explorer/workload-dashboard/ByTypeSection.vue +30 -0
  178. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadCard.vue +155 -0
  179. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadNamespaceCard.vue +142 -0
  180. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadTypeCard.vue +159 -0
  181. package/pages/c/_cluster/explorer/workload-dashboard/__tests__/composable.test.ts +561 -0
  182. package/pages/c/_cluster/explorer/workload-dashboard/composable.ts +440 -0
  183. package/pages/c/_cluster/explorer/workload-dashboard/index.vue +187 -0
  184. package/pages/c/_cluster/explorer/workload-dashboard/types.ts +80 -0
  185. package/pages/c/_cluster/fleet/application/create.vue +187 -136
  186. package/pages/c/_cluster/fleet/application/index.vue +5 -3
  187. package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailBody.vue +338 -0
  188. package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailHeader.vue +121 -0
  189. package/pages/c/_cluster/fleet/application/suse-app-collection/chart.vue +369 -0
  190. package/pages/c/_cluster/fleet/application/suse-app-collection/charts.vue +248 -0
  191. package/pages/c/_cluster/fleet/application/suse-app-collection/credentials.vue +310 -0
  192. package/pages/c/_cluster/fleet/index.vue +2 -2
  193. package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +96 -0
  194. package/pages/c/_cluster/uiplugins/index.vue +15 -0
  195. package/pages/fail-whale.vue +16 -11
  196. package/pages/home.vue +16 -46
  197. package/plugins/clean-html.d.ts +9 -0
  198. package/plugins/dashboard-store/__tests__/resource-class.test.ts +93 -0
  199. package/plugins/dashboard-store/resource-class.js +62 -7
  200. package/plugins/steve/__tests__/actions.test.ts +212 -0
  201. package/plugins/steve/actions.js +96 -0
  202. package/plugins/steve/steve-pagination-utils.ts +1 -1
  203. package/rancher-components/Accordion/Accordion.vue +53 -9
  204. package/rancher-components/Form/Checkbox/Checkbox.vue +14 -0
  205. package/rancher-components/Form/Radio/RadioButton.vue +17 -1
  206. package/rancher-components/Form/Radio/RadioGroup.vue +10 -0
  207. package/rancher-components/Pill/RcTag/RcTag.vue +3 -2
  208. package/rancher-components/RcButton/RcButton.test.ts +103 -0
  209. package/rancher-components/RcButton/RcButton.vue +94 -15
  210. package/rancher-components/RcButton/types.ts +3 -0
  211. package/rancher-components/RcItemCard/RcItemCard.test.ts +18 -0
  212. package/rancher-components/RcItemCard/RcItemCard.vue +2 -2
  213. package/rancher-components/RcSection/RcSection.vue +28 -3
  214. package/scripts/extension/helm/package/Dockerfile +1 -1
  215. package/scripts/test-plugins-build.sh +2 -1
  216. package/store/__tests__/notifications.test.ts +434 -0
  217. package/store/catalog.js +57 -0
  218. package/store/plugins.js +7 -4
  219. package/types/components/buttonGroup.ts +5 -0
  220. package/types/shell/index.d.ts +104 -70
  221. package/utils/__tests__/auth.test.ts +273 -0
  222. package/utils/__tests__/computed.test.ts +193 -0
  223. package/utils/__tests__/cspAdaptor.test.ts +163 -0
  224. package/utils/__tests__/dom.test.ts +81 -0
  225. package/utils/__tests__/duration.test.ts +37 -1
  226. package/utils/__tests__/dynamic-importer.test.ts +102 -0
  227. package/utils/__tests__/fleet-appco.test.ts +312 -0
  228. package/utils/__tests__/monitoring.test.ts +130 -0
  229. package/utils/__tests__/object.test.ts +22 -0
  230. package/utils/__tests__/platform.test.ts +91 -0
  231. package/utils/__tests__/position.test.ts +237 -0
  232. package/utils/__tests__/provider.test.ts +51 -1
  233. package/utils/__tests__/queue.test.ts +232 -0
  234. package/utils/__tests__/release-notes.test.ts +221 -0
  235. package/utils/__tests__/router.test.js +254 -1
  236. package/utils/__tests__/select.test.ts +208 -0
  237. package/utils/__tests__/time.test.ts +265 -1
  238. package/utils/__tests__/title.test.ts +47 -0
  239. package/utils/__tests__/width.test.ts +53 -0
  240. package/utils/__tests__/window.test.ts +158 -0
  241. package/utils/__tests__/xccdf.test.ts +126 -6
  242. package/utils/crypto/__tests__/browserHashUtils.test.ts +98 -0
  243. package/utils/crypto/__tests__/index.test.ts +144 -0
  244. package/utils/duration.ts +104 -0
  245. package/utils/dynamic-content/__tests__/notification-handler.test.ts +196 -0
  246. package/utils/dynamic-content/info.ts +2 -1
  247. package/utils/error.js +13 -0
  248. package/utils/fleet-appco.ts +323 -0
  249. package/utils/object.js +22 -2
  250. package/utils/provider.ts +12 -0
  251. package/utils/validators/__tests__/container-images.test.ts +104 -0
  252. package/utils/validators/__tests__/flow-output.test.ts +91 -0
  253. package/utils/validators/__tests__/logging-outputs.test.ts +58 -0
  254. package/utils/validators/__tests__/monitoring-route.test.ts +119 -0
  255. package/utils/xccdf.ts +39 -42
  256. package/vue.config.js +1 -1
  257. package/pages/support/index.vue +0 -264
  258. package/utils/duration.js +0 -43
@@ -1,4 +1,9 @@
1
1
  <script>
2
+ import { ref, computed, provide } from 'vue';
3
+ import { useStore } from 'vuex';
4
+ import { useForm } from 'vee-validate';
5
+ import { toTypedSchema } from '@vee-validate/zod';
6
+ import * as z from 'zod';
2
7
  import Loading from '@shell/components/Loading';
3
8
  import CreateEditView from '@shell/mixins/create-edit-view';
4
9
  import AuthConfig, { SLO_OPTION_VALUES } from '@shell/mixins/auth-config';
@@ -12,6 +17,8 @@ import AuthBanner from '@shell/components/auth/AuthBanner';
12
17
  import config, { OKTA, SHIBBOLETH } from '@shell/edit/auth/ldap/config';
13
18
  import AuthProviderWarningBanners from '@shell/edit/auth/AuthProviderWarningBanners';
14
19
  import RadioGroup from '@components/Form/Radio/RadioGroup.vue';
20
+ import { RcButton } from '@components/RcButton';
21
+ import { useI18n } from '@shell/composables/useI18n';
15
22
 
16
23
  // Standard LDAP defaults
17
24
  const LDAP_DEFAULTS = {
@@ -47,10 +54,48 @@ export default {
47
54
  FileSelector,
48
55
  config,
49
56
  AuthBanner,
50
- AuthProviderWarningBanners
57
+ AuthProviderWarningBanners,
58
+ RcButton,
51
59
  },
52
60
 
53
61
  mixins: [CreateEditView, AuthConfig],
62
+
63
+ setup() {
64
+ const store = useStore();
65
+ const { t } = useI18n(store);
66
+
67
+ const coerce = (schema) => z.preprocess((v) => v ?? '', schema);
68
+ const requiredField = (key) => coerce(z.string().min(1, t('validation.required', { key: t(key) })));
69
+ const requiredUrlField = (key) => coerce(z.string().min(1, t('validation.required', { key: t(key) })).url(t('validation.genericUrl')));
70
+
71
+ const validationSchema = computed(() => toTypedSchema(
72
+ z.object({
73
+ displayNameField: requiredField('authConfig.saml.displayName'),
74
+ userNameField: requiredField('authConfig.saml.userName'),
75
+ uidField: requiredField('authConfig.saml.UID'),
76
+ groupsField: requiredField('authConfig.saml.groups'),
77
+ rancherApiHost: requiredUrlField('authConfig.saml.api'),
78
+ spKey: requiredField('authConfig.saml.key.label'),
79
+ spCert: requiredField('authConfig.saml.cert.label'),
80
+ idpMetadataContent: requiredField('authConfig.saml.metadata.label'),
81
+ })
82
+ ));
83
+
84
+ const showAllErrors = ref(false);
85
+
86
+ provide('vee-show-all-errors', showAllErrors);
87
+
88
+ const { errors, validate } = useForm({ validationSchema });
89
+ const isFormValid = computed(() => Object.keys(errors.value).length === 0);
90
+
91
+ const validateAllFields = async() => {
92
+ await validate();
93
+ showAllErrors.value = true;
94
+ };
95
+
96
+ return { isFormValid, validateAllFields };
97
+ },
98
+
54
99
  data() {
55
100
  return {
56
101
  showLdap: false,
@@ -58,7 +103,19 @@ export default {
58
103
  };
59
104
  },
60
105
 
106
+ created() {
107
+ this.registerBeforeHook(this.validateAllFields, 'willSave');
108
+ },
109
+
61
110
  computed: {
111
+ validationPassed() {
112
+ if (this.model?.enabled && !this.editConfig) {
113
+ return true;
114
+ }
115
+
116
+ return this.isFormValid;
117
+ },
118
+
62
119
  tArgs() {
63
120
  return {
64
121
  baseUrl: this.serverSetting,
@@ -152,7 +209,7 @@ export default {
152
209
  :mode="mode"
153
210
  :resource="model"
154
211
  :subtypes="[]"
155
- :validation-passed="true"
212
+ :validation-passed="validationPassed"
156
213
  :finish-button-mode="model.enabled ? 'edit' : 'enable'"
157
214
  :can-yaml="false"
158
215
  :errors="errors"
@@ -193,13 +250,17 @@ export default {
193
250
  >
194
251
  <div>{{ t('authConfig.saml.search.on') }}</div>
195
252
  <div>
196
- <a
197
- class="toggle-btn"
253
+ <rc-button
254
+ variant="link"
198
255
  @click="showLdapDetails = !showLdapDetails"
199
256
  >
200
- <template v-if="showLdapDetails">{{ t('authConfig.saml.search.hide') }}</template>
201
- <template v-else>{{ t('authConfig.saml.search.show') }}</template>
202
- </a>
257
+ <template v-if="showLdapDetails">
258
+ {{ t('authConfig.saml.search.hide') }}
259
+ </template>
260
+ <template v-else>
261
+ {{ t('authConfig.saml.search.show') }}
262
+ </template>
263
+ </rc-button>
203
264
  </div>
204
265
  </div>
205
266
  </Banner>
@@ -244,6 +305,7 @@ export default {
244
305
  <div class="col span-6">
245
306
  <LabeledInput
246
307
  v-model:value="model.displayNameField"
308
+ name="displayNameField"
247
309
  :label="t(`authConfig.saml.displayName`)"
248
310
  :mode="mode"
249
311
  required
@@ -252,6 +314,7 @@ export default {
252
314
  <div class="col span-6">
253
315
  <LabeledInput
254
316
  v-model:value="model.userNameField"
317
+ name="userNameField"
255
318
  :label="t(`authConfig.saml.userName`)"
256
319
  :mode="mode"
257
320
  required
@@ -263,6 +326,7 @@ export default {
263
326
  <div class="col span-6">
264
327
  <LabeledInput
265
328
  v-model:value="model.uidField"
329
+ name="uidField"
266
330
  :label="t(`authConfig.saml.UID`)"
267
331
  :mode="mode"
268
332
  required
@@ -271,6 +335,7 @@ export default {
271
335
  <div class="col span-6">
272
336
  <LabeledInput
273
337
  v-model:value="model.groupsField"
338
+ name="groupsField"
274
339
  :label="t(`authConfig.saml.groups`)"
275
340
  :mode="mode"
276
341
  required
@@ -292,6 +357,7 @@ export default {
292
357
  <div class="col span-6">
293
358
  <LabeledInput
294
359
  v-model:value="model.rancherApiHost"
360
+ name="rancherApiHost"
295
361
  :label="t(`authConfig.saml.api`)"
296
362
  :mode="mode"
297
363
  required
@@ -303,6 +369,7 @@ export default {
303
369
  <div class="col span-4">
304
370
  <LabeledInput
305
371
  v-model:value="model.spKey"
372
+ name="spKey"
306
373
  :label="t(`authConfig.saml.key.label`)"
307
374
  :placeholder="t(`authConfig.saml.key.placeholder`)"
308
375
  :mode="mode"
@@ -319,6 +386,7 @@ export default {
319
386
  <div class="col span-4">
320
387
  <LabeledInput
321
388
  v-model:value="model.spCert"
389
+ name="spCert"
322
390
  :label="t(`authConfig.saml.cert.label`)"
323
391
  :placeholder="t(`authConfig.saml.cert.placeholder`)"
324
392
  :mode="mode"
@@ -335,6 +403,7 @@ export default {
335
403
  <div class="col span-4">
336
404
  <LabeledInput
337
405
  v-model:value="model.idpMetadataContent"
406
+ name="idpMetadataContent"
338
407
  :label="t(`authConfig.saml.metadata.label`)"
339
408
  :placeholder="t(`authConfig.saml.metadata.placeholder`)"
340
409
  :mode="mode"
@@ -423,10 +492,5 @@ export default {
423
492
  > :first-child {
424
493
  flex: 1;
425
494
  }
426
-
427
- .toggle-btn {
428
- cursor: pointer;
429
- user-select: none;
430
- }
431
495
  }
432
496
  </style>
@@ -1,11 +1,11 @@
1
1
  <script lang="ts">
2
2
  import CreateEditView from '@shell/mixins/create-edit-view';
3
3
  import AsyncButton from '@shell/components/AsyncButton.vue';
4
- import Footer from '@shell/components/form/Footer';
4
+ import Footer from '@shell/components/form/Footer.vue';
5
5
  import { LabeledInput } from '@components/Form/LabeledInput';
6
- import NameNsDescription from '@shell/components/form/NameNsDescription';
7
- import Labels from '@shell/components/form/Labels';
8
- import SelectOrCreateAuthSecret from '@shell/components/form/SelectOrCreateAuthSecret';
6
+ import NameNsDescription from '@shell/components/form/NameNsDescription.vue';
7
+ import Labels from '@shell/components/form/Labels.vue';
8
+ import SelectOrCreateAuthSecret from '@shell/components/form/SelectOrCreateAuthSecret.vue';
9
9
  import Banner from '@components/Banner/Banner.vue';
10
10
  import { Checkbox } from '@components/Form/Checkbox';
11
11
  import {
@@ -13,11 +13,14 @@ import {
13
13
  CLUSTER_REPO_APPCO_AUTH_GENERATE_NAME, CLUSTER_REPO_AUTH_GENERATE_NAME
14
14
  } from '@shell/config/types';
15
15
  import UnitInput from '@shell/components/form/UnitInput.vue';
16
+ import LabeledSelect from '@shell/components/form/LabeledSelect.vue';
16
17
  import { getVersionData } from '@shell/config/version';
18
+ import { SECONDS_PER, secondsToLargestUnit } from '@shell/utils/duration';
17
19
  import { RcItemCard } from '@components/RcItemCard';
18
- import { _CREATE, _EDIT, TARGET, _VIEW } from '@shell/config/query-params';
20
+ import { _CREATE, _EDIT, TARGET, _VIEW } from '@shell/config/query-params.js';
19
21
  import { RcIconType } from '@components/RcIcon/types';
20
22
  import { requireAsset } from '@shell/utils/require-asset';
23
+ import formRulesGenerator from '@shell/utils/validators/formRules';
21
24
 
22
25
  export default {
23
26
  name: 'CruCatalogRepo',
@@ -34,6 +37,7 @@ export default {
34
37
  Banner,
35
38
  Checkbox,
36
39
  UnitInput,
40
+ LabeledSelect,
37
41
  RcItemCard,
38
42
  },
39
43
 
@@ -82,6 +86,19 @@ export default {
82
86
  });
83
87
  }
84
88
 
89
+ const storedInterval = this.value.spec.refreshInterval;
90
+ const refreshEnabled = storedInterval === undefined || storedInterval >= 0;
91
+
92
+ let refreshDisplayValue: number | null = null;
93
+ let refreshUnit: number = SECONDS_PER.h;
94
+
95
+ if (storedInterval && storedInterval > 0) {
96
+ const parsed = secondsToLargestUnit(storedInterval);
97
+
98
+ refreshDisplayValue = parsed.value;
99
+ refreshUnit = parsed.unit;
100
+ }
101
+
85
102
  return {
86
103
  CLUSTER_REPO_TYPES,
87
104
  AUTH_TYPE,
@@ -97,6 +114,9 @@ export default {
97
114
  clusterRepoTargets,
98
115
  previousName: '',
99
116
  previousDescription: '',
117
+ refreshEnabled,
118
+ refreshDisplayValue,
119
+ refreshUnit,
100
120
  };
101
121
  },
102
122
 
@@ -114,6 +134,27 @@ export default {
114
134
  inStore() {
115
135
  return this.$store.getters['currentProduct']?.inStore || MANAGEMENT;
116
136
  },
137
+ unitOptions() {
138
+ return [
139
+ { label: this.t('unit.sec'), value: SECONDS_PER.s },
140
+ { label: this.t('unit.min'), value: SECONDS_PER.m },
141
+ { label: this.t('unit.hour', { count: 2 }), value: SECONDS_PER.h },
142
+ { label: this.t('unit.day', { count: 2 }), value: SECONDS_PER.d },
143
+ ];
144
+ },
145
+ refreshIntervalRules() {
146
+ const { isPositive } = formRulesGenerator(this.$store.getters['i18n/t'], {});
147
+
148
+ return [isPositive];
149
+ },
150
+ refreshIntervalPlaceholder() {
151
+ const defaultHours = (this.clusterRepoType === CLUSTER_REPO_TYPES.OCI_URL || this.clusterRepoType === CLUSTER_REPO_TYPES.SUSE_APP_COLLECTION) ? 24 : 1;
152
+
153
+ return this.t('catalog.repo.refreshInterval.placeholder', {
154
+ value: defaultHours,
155
+ unit: this.t('unit.hour', { count: defaultHours }),
156
+ });
157
+ },
117
158
  secretNamespace() {
118
159
  const tryNames = ['cattle-system', 'default'];
119
160
 
@@ -174,34 +215,58 @@ export default {
174
215
 
175
216
  this.value.spec.exponentialBackOffValues[key] = Number(newVal);
176
217
  },
177
- updateRefreshInterval(newVal) {
178
- // when user removes the value we don't send refreshInterval along with the payload
179
- if (newVal === null) {
218
+ syncRefreshIntervalToSpec() {
219
+ if (!this.refreshEnabled) {
220
+ this.value.spec.refreshInterval = -1;
221
+
222
+ return;
223
+ }
224
+
225
+ if (this.refreshDisplayValue === null || this.refreshDisplayValue === 0) {
180
226
  delete this.value.spec.refreshInterval;
181
227
 
182
228
  return;
183
229
  }
184
230
 
185
- this.value.spec.refreshInterval = newVal;
231
+ this.value.spec.refreshInterval = this.refreshDisplayValue * this.refreshUnit;
232
+ },
233
+ onRefreshEnabledChange(val: boolean) {
234
+ this.refreshEnabled = val;
235
+ this.syncRefreshIntervalToSpec();
236
+ },
237
+ onRefreshValueChange(val: string | null) {
238
+ this.refreshDisplayValue = val === '' || val === null ? null : Number(val);
239
+ this.syncRefreshIntervalToSpec();
240
+ },
241
+ onRefreshUnitChange(val: number) {
242
+ this.refreshUnit = val;
243
+ this.syncRefreshIntervalToSpec();
244
+ },
245
+ resetRefreshIntervalValues() {
246
+ delete this.value.spec['refreshInterval'];
247
+ this.refreshEnabled = true;
248
+ this.refreshDisplayValue = null;
249
+ this.refreshUnit = SECONDS_PER.h;
186
250
  },
187
251
  resetGitRepoValues() {
188
252
  delete this.value.spec['gitRepo'];
189
253
  delete this.value.spec['gitBranch'];
190
- delete this.value.spec['refreshInterval'];
254
+ this.resetRefreshIntervalValues();
191
255
  },
192
256
  resetOciValues() {
193
257
  delete this.value.spec['url'];
194
258
  delete this.value.spec['insecurePlainHttp'];
195
259
  delete this.value.spec['insecureSkipTLSVerify'];
196
260
  delete this.value.spec['caBundle'];
197
- delete this.value.spec['refreshInterval'];
198
261
  delete this.value.spec['exponentialBackOffValues'];
199
262
  this.ociMinWait = undefined;
200
263
  this.ociMaxWait = undefined;
201
264
  this.ociMaxRetries = undefined;
265
+ this.resetRefreshIntervalValues();
202
266
  },
203
267
  resetHelmValues() {
204
268
  delete this.value.spec['url'];
269
+ this.resetRefreshIntervalValues();
205
270
  },
206
271
  resetClientSecret() {
207
272
  this.value.spec['clientSecret'] = null;
@@ -255,6 +320,7 @@ export default {
255
320
  :value="value"
256
321
  :mode="mode"
257
322
  :namespaced="isNamespaced"
323
+ :name-col-span="6"
258
324
  @update:value="$emit('input', $event)"
259
325
  />
260
326
 
@@ -264,7 +330,7 @@ export default {
264
330
  color="warning"
265
331
  />
266
332
  <Banner
267
- :label="t('catalog.repo.oci.info', null, true)"
333
+ :label="t('catalog.repo.oci.info', {}, true)"
268
334
  color="info"
269
335
  />
270
336
  </template>
@@ -276,7 +342,7 @@ export default {
276
342
  v-model:value.trim="value.spec.gitRepo"
277
343
  :required="true"
278
344
  :label="t('catalog.repo.gitRepo.label')"
279
- :placeholder="t('catalog.repo.gitRepo.placeholder', null, true)"
345
+ :placeholder="t('catalog.repo.gitRepo.placeholder', {}, true)"
280
346
  :mode="mode"
281
347
  data-testid="clusterrepo-git-repo-input"
282
348
  />
@@ -284,9 +350,9 @@ export default {
284
350
  <div class="col span-3">
285
351
  <LabeledInput
286
352
  v-model:value.trim="value.spec.gitBranch"
287
- :sub-label="!value.spec.gitBranch ? t('catalog.repo.gitBranch.defaultMessage', null, true) : undefined"
353
+ :sub-label="!value.spec.gitBranch ? t('catalog.repo.gitBranch.defaultMessage', {}, true) : undefined"
288
354
  :label="t('catalog.repo.gitBranch.label')"
289
- :placeholder="t('catalog.repo.gitBranch.placeholder', null, true)"
355
+ :placeholder="t('catalog.repo.gitBranch.placeholder', {}, true)"
290
356
  :mode="mode"
291
357
  data-testid="clusterrepo-git-branch-input"
292
358
  />
@@ -299,7 +365,7 @@ export default {
299
365
  v-model:value.trim="value.spec.url"
300
366
  :required="true"
301
367
  :label="t('catalog.repo.oci.urlLabel')"
302
- :placeholder="t('catalog.repo.oci.placeholder', null, true)"
368
+ :placeholder="t('catalog.repo.oci.placeholder', {}, true)"
303
369
  :mode="mode"
304
370
  data-testid="clusterrepo-oci-url-input"
305
371
  />
@@ -312,7 +378,7 @@ export default {
312
378
  v-model:value.trim="value.spec.url"
313
379
  :required="true"
314
380
  :label="t('catalog.repo.oci.urlLabel')"
315
- :placeholder="t('catalog.repo.oci.placeholder', null, true)"
381
+ :placeholder="t('catalog.repo.oci.placeholder', {}, true)"
316
382
  :mode="mode"
317
383
  data-testid="clusterrepo-oci-url-input"
318
384
  :disabled="true"
@@ -328,25 +394,52 @@ export default {
328
394
  v-model:value.trim="value.spec.url"
329
395
  :required="true"
330
396
  :label="t('catalog.repo.url.label')"
331
- :placeholder="t('catalog.repo.url.placeholder', null, true)"
397
+ :placeholder="t('catalog.repo.url.placeholder', {}, true)"
332
398
  :mode="mode"
333
399
  data-testid="clusterrepo-helm-url-input"
334
400
  />
335
401
  </div>
402
+ </div>
336
403
 
337
- <div
338
- class="col span-3"
339
- data-testid="clusterrepo-refresh-interval"
340
- >
341
- <UnitInput
342
- v-model:value.trim="value.spec.refreshInterval"
343
- :label="t('catalog.repo.refreshInterval.label')"
344
- :mode="mode"
345
- min="0"
346
- :suffix="t('unit.hour', { count: value.spec.refreshInterval })"
347
- :placeholder="t('catalog.repo.refreshInterval.placeholder', { hours: clusterRepoType === CLUSTER_REPO_TYPES.OCI_URL ? 24 : 6 })"
348
- @update:value="updateRefreshInterval($event)"
349
- />
404
+ <div
405
+ class="mt-20"
406
+ data-testid="clusterrepo-refresh-interval"
407
+ >
408
+ <Checkbox
409
+ :value="refreshEnabled"
410
+ class="mb-10"
411
+ :mode="mode"
412
+ :label="t('catalog.repo.refreshInterval.enableLabel')"
413
+ @update:value="onRefreshEnabledChange"
414
+ />
415
+ <div class="row mb-10">
416
+ <div class="col span-6">
417
+ <div class="refresh-interval-controls">
418
+ <LabeledInput
419
+ class="refresh-interval-value"
420
+ :value="refreshDisplayValue"
421
+ :label="t('catalog.repo.refreshInterval.label')"
422
+ :rules="refreshIntervalRules"
423
+ type="number"
424
+ min="0"
425
+ :disabled="!refreshEnabled || isView"
426
+ :placeholder="refreshIntervalPlaceholder"
427
+ data-testid="clusterrepo-refresh-interval-input"
428
+ @update:value="onRefreshValueChange"
429
+ />
430
+ <LabeledSelect
431
+ class="refresh-interval-unit"
432
+ :value="refreshUnit"
433
+ :label="t('catalog.repo.refreshInterval.unitLabel')"
434
+ :mode="mode"
435
+ :options="unitOptions"
436
+ :disabled="!refreshEnabled || isView"
437
+ :clearable="false"
438
+ :searchable="false"
439
+ @update:value="onRefreshUnitChange"
440
+ />
441
+ </div>
442
+ </div>
350
443
  </div>
351
444
  </div>
352
445
 
@@ -468,7 +561,7 @@ export default {
468
561
  </form>
469
562
  </template>
470
563
 
471
- <style lang="css" scoped>
564
+ <style lang="scss" scoped>
472
565
  .target-groups {
473
566
  display: grid;
474
567
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
@@ -477,4 +570,19 @@ export default {
477
570
  height: max-content;
478
571
  overflow: hidden;
479
572
  }
573
+
574
+ .refresh-interval-controls {
575
+ display: flex;
576
+ align-items: flex-end;
577
+ gap: var(--gap-md);
578
+ }
579
+
580
+ .refresh-interval-value {
581
+ flex: 2;
582
+ }
583
+
584
+ .refresh-interval-unit {
585
+ flex: 1;
586
+ }
587
+
480
588
  </style>