@rancher/shell 3.0.9-rc.6 → 3.0.10

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 (82) hide show
  1. package/assets/styles/base/_color.scss +4 -0
  2. package/assets/styles/themes/_light.scss +6 -6
  3. package/assets/styles/themes/_modern.scss +14 -6
  4. package/assets/translations/en-us.yaml +2 -5
  5. package/components/CopyToClipboard.vue +28 -0
  6. package/components/CopyToClipboardText.vue +4 -0
  7. package/components/CruResource.vue +1 -0
  8. package/components/GlobalRoleBindings.vue +1 -5
  9. package/components/IconOrSvg.vue +61 -42
  10. package/components/ResourceDetail/index.vue +0 -21
  11. package/components/SortableTable/index.vue +2 -2
  12. package/components/__tests__/CruResource.test.ts +35 -1
  13. package/components/form/BannerSettings.vue +2 -2
  14. package/components/form/NotificationSettings.vue +2 -2
  15. package/composables/useIsNewDetailPageEnabled.test.ts +98 -0
  16. package/composables/useIsNewDetailPageEnabled.ts +12 -0
  17. package/config/product/explorer.js +11 -1
  18. package/config/product/manager.js +0 -1
  19. package/config/table-headers.js +0 -9
  20. package/config/types.js +0 -1
  21. package/detail/fleet.cattle.io.cluster.vue +1 -1
  22. package/dialog/FeatureFlagListDialog.vue +1 -1
  23. package/edit/auth/github-app-steps.vue +2 -0
  24. package/edit/auth/github-steps.vue +2 -0
  25. package/edit/catalog.cattle.io.clusterrepo.vue +1 -1
  26. package/edit/management.cattle.io.user.vue +60 -35
  27. package/edit/monitoring.coreos.com.alertmanagerconfig/__tests__/auth.spec.ts +145 -0
  28. package/edit/monitoring.coreos.com.alertmanagerconfig/__tests__/index.test.ts +202 -0
  29. package/edit/monitoring.coreos.com.alertmanagerconfig/__tests__/tls.spec.ts +226 -0
  30. package/edit/monitoring.coreos.com.alertmanagerconfig/auth.vue +24 -21
  31. package/edit/monitoring.coreos.com.alertmanagerconfig/types/__tests__/opsgenie.spec.ts +157 -0
  32. package/edit/monitoring.coreos.com.alertmanagerconfig/types/__tests__/pagerduty.spec.ts +132 -0
  33. package/edit/monitoring.coreos.com.alertmanagerconfig/types/__tests__/slack.spec.ts +108 -0
  34. package/edit/monitoring.coreos.com.alertmanagerconfig/types/pagerduty.vue +2 -1
  35. package/edit/monitoring.coreos.com.receiver/__tests__/auth.spec.ts +165 -0
  36. package/edit/monitoring.coreos.com.receiver/__tests__/index.test.ts +153 -0
  37. package/edit/monitoring.coreos.com.receiver/__tests__/tls.spec.ts +115 -0
  38. package/edit/monitoring.coreos.com.receiver/types/__tests__/email.spec.ts +86 -0
  39. package/edit/monitoring.coreos.com.receiver/types/__tests__/opsgenie.spec.ts +209 -0
  40. package/edit/monitoring.coreos.com.receiver/types/__tests__/pagerduty.spec.ts +105 -0
  41. package/edit/monitoring.coreos.com.receiver/types/__tests__/slack.spec.ts +92 -0
  42. package/edit/monitoring.coreos.com.receiver/types/__tests__/webhook.spec.ts +131 -0
  43. package/edit/provisioning.cattle.io.cluster/ingress/IngressCards.vue +14 -12
  44. package/edit/provisioning.cattle.io.cluster/rke2.vue +4 -5
  45. package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +18 -3
  46. package/edit/provisioning.cattle.io.cluster/tabs/Ingress.vue +100 -76
  47. package/edit/token.vue +29 -68
  48. package/list/provisioning.cattle.io.cluster.vue +2 -2
  49. package/models/__tests__/chart.test.ts +2 -2
  50. package/models/chart.js +3 -3
  51. package/models/token.js +0 -4
  52. package/package.json +8 -8
  53. package/pages/account/index.vue +67 -96
  54. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +108 -24
  55. package/pages/c/_cluster/apps/charts/index.vue +1 -11
  56. package/pages/c/_cluster/explorer/index.vue +2 -19
  57. package/pages/c/_cluster/explorer/tools/index.vue +1 -1
  58. package/pages/c/_cluster/manager/cloudCredential/index.vue +1 -1
  59. package/pages/c/_cluster/uiplugins/index.vue +1 -1
  60. package/pkg/auto-import.js +41 -0
  61. package/plugins/dashboard-store/resource-class.js +2 -2
  62. package/plugins/steve/__tests__/steve-class.test.ts +1 -1
  63. package/plugins/steve/steve-class.js +3 -3
  64. package/plugins/steve/steve-pagination-utils.ts +2 -4
  65. package/rancher-components/Pill/RcCounterBadge/RcCounterBadge.vue +7 -7
  66. package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.vue +5 -2
  67. package/rancher-components/RcIcon/types.ts +2 -2
  68. package/rancher-components/RcItemCard/RcItemCard.vue +8 -1
  69. package/rancher-components/RcSection/RcSection.test.ts +323 -0
  70. package/rancher-components/RcSection/RcSection.vue +252 -0
  71. package/rancher-components/RcSection/RcSectionActions.test.ts +212 -0
  72. package/rancher-components/RcSection/RcSectionActions.vue +85 -0
  73. package/rancher-components/RcSection/RcSectionBadges.test.ts +149 -0
  74. package/rancher-components/RcSection/RcSectionBadges.vue +29 -0
  75. package/rancher-components/RcSection/index.ts +12 -0
  76. package/rancher-components/RcSection/types.ts +86 -0
  77. package/scripts/test-plugins-build.sh +5 -4
  78. package/types/shell/index.d.ts +93 -108
  79. package/utils/style.ts +17 -0
  80. package/utils/svg-filter.js +4 -3
  81. package/utils/units.js +14 -5
  82. package/models/ext.cattle.io.token.js +0 -48
@@ -19,6 +19,7 @@ interface Props {
19
19
  mode?: string;
20
20
  value: string | string[];
21
21
  nginxSupported: boolean;
22
+ traefikSupported: boolean;
22
23
  nginxChart: string;
23
24
  traefikChart: string;
24
25
  userChartValues: any;
@@ -30,6 +31,7 @@ const {
30
31
  nginxChart,
31
32
  traefikChart,
32
33
  nginxSupported,
34
+ traefikSupported,
33
35
  userChartValues,
34
36
  versionInfo
35
37
  } = defineProps<Props>();
@@ -44,6 +46,9 @@ const showAdvanced = ref<Boolean>(false);
44
46
  const isView = computed(() => mode === _VIEW);
45
47
  const isEdit = computed(() => mode === _EDIT);
46
48
  const showTraefikBanner = ref<Boolean>(false);
49
+ const traefikMerged = ref(initYamlEditor(traefikChart));
50
+ const nginxMerged = ref(initYamlEditor(nginxChart));
51
+ const showConfig = computed(() => !!versionInfo[traefikChart] || !!versionInfo[nginxChart]);
47
52
 
48
53
  const ingressSelection = computed(() => {
49
54
  if (Array.isArray(value) ) {
@@ -55,9 +60,21 @@ const ingressSelection = computed(() => {
55
60
  }
56
61
  });
57
62
  const ingressOptions = computed(() => {
58
- return INGRESS_OPTIONS.filter((option) => !(option.id === INGRESS_DUAL && mode === _CREATE) &&
59
- !((option.id === INGRESS_NGINX || option.id === INGRESS_DUAL) && !nginxSupported)
60
- ).map((option) => {
63
+ return INGRESS_OPTIONS.filter((option) => {
64
+ if (option.id === INGRESS_NGINX) {
65
+ return nginxSupported;
66
+ } else if (option.id === TRAEFIK) {
67
+ return traefikSupported;
68
+ } else if (option.id === INGRESS_DUAL) {
69
+ if (mode === _CREATE) {
70
+ return false;
71
+ } else {
72
+ return traefikSupported && nginxSupported;
73
+ }
74
+ }
75
+
76
+ return true;
77
+ }).map((option) => {
61
78
  return {
62
79
  ...option,
63
80
  selected: option.id === ingressSelection.value
@@ -73,7 +90,13 @@ const ingressEnabled = computed({
73
90
  if (!val) {
74
91
  emit('update:value', INGRESS_NONE);
75
92
  } else {
76
- emit('update:value', TRAEFIK);
93
+ if (traefikSupported) {
94
+ emit('update:value', TRAEFIK);
95
+ } else if (nginxSupported) {
96
+ emit('update:value', INGRESS_NGINX);
97
+ } else {
98
+ emit('update:value', INGRESS_NONE);
99
+ }
77
100
  }
78
101
  }
79
102
  });
@@ -102,9 +125,6 @@ function preconfigureTraefik() {
102
125
  emit('update-values', traefikChart, traefikMerged.value);
103
126
  }
104
127
 
105
- const traefikMerged = ref(initYamlEditor(traefikChart));
106
- const nginxMerged = ref(initYamlEditor(nginxChart));
107
-
108
128
  const nginxHttp = computed({
109
129
  get() {
110
130
  return get(nginxMerged.value, 'controller.hostPort.ports.http');
@@ -193,12 +213,14 @@ function updateYaml(component: any, value: any) {
193
213
  </div>
194
214
  <div v-else>
195
215
  <Banner
216
+ v-if="traefikSupported"
196
217
  color="info"
197
218
  label-key="cluster.ingress.banners.transitioning.label"
198
219
  />
199
220
  <IngressCards
200
221
  :options="ingressOptions"
201
222
  :mode="mode"
223
+ :class="!traefikSupported ? 'mt-10' : ''"
202
224
  @select="selectIngress"
203
225
  />
204
226
  <Banner
@@ -219,79 +241,81 @@ function updateYaml(component: any, value: any) {
219
241
  </template>
220
242
  </RichTranslation>
221
243
  </Banner>
222
- <div class="mt-20">
223
- <IngressConfiguration
224
- v-model:compatibility-mode="compatibilityMode"
225
- v-model:nginxHttp="nginxHttp"
226
- v-model:nginxHttps="nginxHttps"
227
- v-model:traefikHttp="traefikHttp"
228
- v-model:traefikHttps="traefikHttps"
229
- :mode="mode"
230
- :ingress-selection="ingressSelection"
231
- @validation-changed="emit('config-validation-changed', $event)"
232
- />
233
- </div>
234
- <div>
235
- <button
236
- type="button"
237
- class="btn role-link advanced-toggle mb-0"
238
- @click="showAdvanced = !showAdvanced"
239
- >
240
- {{ showAdvanced ? t('cluster.ingress.hideAdvanced') : t('cluster.ingress.showAdvanced') }}
241
- </button>
242
- </div>
243
- <template v-if="showAdvanced">
244
- <div class="row">
245
- <div
246
- v-if="ingressSelection === TRAEFIK || ingressSelection === INGRESS_DUAL"
247
- :class="{ 'col': true, 'span-6': ingressSelection === INGRESS_DUAL, 'span-12': ingressSelection !== INGRESS_DUAL }"
244
+ <div v-if="showConfig">
245
+ <div class="mt-20">
246
+ <IngressConfiguration
247
+ v-model:compatibility-mode="compatibilityMode"
248
+ v-model:nginxHttp="nginxHttp"
249
+ v-model:nginxHttps="nginxHttps"
250
+ v-model:traefikHttp="traefikHttp"
251
+ v-model:traefikHttps="traefikHttps"
252
+ :mode="mode"
253
+ :ingress-selection="ingressSelection"
254
+ @validation-changed="emit('config-validation-changed', $event)"
255
+ />
256
+ </div>
257
+ <div>
258
+ <button
259
+ type="button"
260
+ class="btn role-link advanced-toggle mb-0"
261
+ @click="showAdvanced = !showAdvanced"
248
262
  >
249
- <p
250
- v-if="ingressSelection === INGRESS_DUAL"
251
- class="mb-10"
263
+ {{ showAdvanced ? t('cluster.ingress.hideAdvanced') : t('cluster.ingress.showAdvanced') }}
264
+ </button>
265
+ </div>
266
+ <template v-if="showAdvanced">
267
+ <div class="row">
268
+ <div
269
+ v-if="ingressSelection === TRAEFIK || ingressSelection === INGRESS_DUAL"
270
+ :class="{ 'col': true, 'span-6': ingressSelection === INGRESS_DUAL, 'span-12': ingressSelection !== INGRESS_DUAL }"
252
271
  >
253
- {{ t('cluster.ingress.traefik.header') }}
254
- </p>
255
- <YamlEditor
256
- ref="traefik-yaml"
257
- class="ingress-yaml-editor"
258
- data-testid="traefik-yaml-editor"
259
- :value="traefikMerged"
260
- :mode="mode"
261
- :scrolling="true"
262
- :as-object="true"
263
- :editor-mode="isView ? EDITOR_MODES.VIEW_CODE : EDITOR_MODES.EDIT_CODE"
264
- :hide-preview-buttons="true"
265
- @update:value="emit('update-values', traefikChart, $event)"
266
- @validationChanged="emit('yaml-validation-changed', {name: traefikChart, val: $event})"
267
- />
268
- </div>
269
- <div
270
- v-if="ingressSelection === INGRESS_NGINX || ingressSelection === INGRESS_DUAL"
271
- :class="{ 'col': true, 'span-6': ingressSelection === INGRESS_DUAL, 'span-12': ingressSelection !== INGRESS_DUAL }"
272
- >
273
- <p
274
- v-if="ingressSelection === INGRESS_DUAL"
275
- class="mb-10"
272
+ <p
273
+ v-if="ingressSelection === INGRESS_DUAL"
274
+ class="mb-10"
275
+ >
276
+ {{ t('cluster.ingress.traefik.header') }}
277
+ </p>
278
+ <YamlEditor
279
+ ref="traefik-yaml"
280
+ class="ingress-yaml-editor"
281
+ data-testid="traefik-yaml-editor"
282
+ :value="traefikMerged"
283
+ :mode="mode"
284
+ :scrolling="true"
285
+ :as-object="true"
286
+ :editor-mode="isView ? EDITOR_MODES.VIEW_CODE : EDITOR_MODES.EDIT_CODE"
287
+ :hide-preview-buttons="true"
288
+ @update:value="emit('update-values', traefikChart, $event)"
289
+ @validationChanged="emit('yaml-validation-changed', {name: traefikChart, val: $event})"
290
+ />
291
+ </div>
292
+ <div
293
+ v-if="ingressSelection === INGRESS_NGINX || ingressSelection === INGRESS_DUAL"
294
+ :class="{ 'col': true, 'span-6': ingressSelection === INGRESS_DUAL, 'span-12': ingressSelection !== INGRESS_DUAL }"
276
295
  >
277
- {{ t('cluster.ingress.nginx.header') }}
278
- </p>
279
- <YamlEditor
280
- ref="nginx-yaml"
281
- class="ingress-yaml-editor"
282
- data-testid="ingress-nginx-yaml-editor"
283
- :value="nginxMerged"
284
- :mode="mode"
285
- :scrolling="true"
286
- :as-object="true"
287
- :editor-mode="isView ? EDITOR_MODES.VIEW_CODE : EDITOR_MODES.EDIT_CODE"
288
- :hide-preview-buttons="true"
289
- @update:value="emit('update-values', nginxChart, $event)"
290
- @validationChanged="emit('yaml-validation-changed', {name: nginxChart, val: $event})"
291
- />
296
+ <p
297
+ v-if="ingressSelection === INGRESS_DUAL"
298
+ class="mb-10"
299
+ >
300
+ {{ t('cluster.ingress.nginx.header') }}
301
+ </p>
302
+ <YamlEditor
303
+ ref="nginx-yaml"
304
+ class="ingress-yaml-editor"
305
+ data-testid="ingress-nginx-yaml-editor"
306
+ :value="nginxMerged"
307
+ :mode="mode"
308
+ :scrolling="true"
309
+ :as-object="true"
310
+ :editor-mode="isView ? EDITOR_MODES.VIEW_CODE : EDITOR_MODES.EDIT_CODE"
311
+ :hide-preview-buttons="true"
312
+ @update:value="emit('update-values', nginxChart, $event)"
313
+ @validationChanged="emit('yaml-validation-changed', {name: nginxChart, val: $event})"
314
+ />
315
+ </div>
292
316
  </div>
293
- </div>
294
- </template>
317
+ </template>
318
+ </div>
295
319
  </div>
296
320
  </template>
297
321
 
package/edit/token.vue CHANGED
@@ -2,7 +2,7 @@
2
2
  import { mapGetters } from 'vuex';
3
3
  import day from 'dayjs';
4
4
  import sortBy from 'lodash/sortBy';
5
- import { MANAGEMENT, EXT } from '@shell/config/types';
5
+ import { MANAGEMENT, NORMAN } from '@shell/config/types';
6
6
  import { Banner } from '@components/Banner';
7
7
  import DetailText from '@shell/components/DetailText';
8
8
  import Footer from '@shell/components/form/Footer';
@@ -14,7 +14,6 @@ import CreateEditView from '@shell/mixins/create-edit-view';
14
14
  import { diffFrom } from '@shell/utils/time';
15
15
  import { filterHiddenLocalCluster, filterOnlyKubernetesClusters } from '@shell/utils/cluster';
16
16
  import { SETTING } from '@shell/config/settings';
17
- import Checkbox from '@components/Form/Checkbox/Checkbox.vue';
18
17
 
19
18
  export default {
20
19
  components: {
@@ -25,7 +24,6 @@ export default {
25
24
  LabeledSelect,
26
25
  RadioGroup,
27
26
  Select,
28
- Checkbox,
29
27
  },
30
28
 
31
29
  mixins: [CreateEditView],
@@ -43,11 +41,7 @@ export default {
43
41
 
44
42
  return {
45
43
  errors: null,
46
- user: null,
47
44
  form: {
48
- enabled: true,
49
- description: '',
50
- clusterName: '',
51
45
  expiryType: 'never',
52
46
  customExpiry: 0,
53
47
  customExpiryUnits: 'minute',
@@ -57,13 +51,11 @@ export default {
57
51
  accessKey: '',
58
52
  secretKey: '',
59
53
  maxTTL,
60
- ttl: ''
61
54
  };
62
55
  },
63
56
 
64
57
  computed: {
65
58
  ...mapGetters({ t: 'i18n/t' }),
66
-
67
59
  scopes() {
68
60
  const all = this.$store.getters['management/all'](MANAGEMENT.CLUSTER);
69
61
  const kubeClusters = filterHiddenLocalCluster(filterOnlyKubernetesClusters(all, this.$store), this.$store);
@@ -79,20 +71,16 @@ export default {
79
71
  const options = ['never', 'day', 'month', 'year', 'custom'];
80
72
  let opts = options.map((opt) => ({ value: opt, label: this.t(`accountAndKeys.apiKeys.add.expiry.options.${ opt }`) }));
81
73
 
82
- // When the TTL is greater than 0, present only two options
74
+ // When the TTL is anything other than 0, present only two options
83
75
  // (1) The maximum allowed
84
76
  // (2) Custom
85
- if (this.maxTTL > 0 ) {
77
+ if (this.maxTTL !== 0 ) {
86
78
  const now = day();
87
79
  const expiry = now.add(this.maxTTL, 'minute');
88
80
  const max = diffFrom(expiry, now, this.t);
89
81
 
90
82
  opts = opts.filter((opt) => opt.value === 'custom');
91
83
  opts.unshift({ value: 'max', label: this.t('accountAndKeys.apiKeys.add.expiry.options.maximum', { value: max.string }) });
92
- } else {
93
- // maxTTL <= 0 means there is no maximum, so we can show the 'never' option which results in an infinite TTL
94
- // OR if we set a positive TTL, then it assumes that value
95
- opts = opts.filter((opt) => opt.value === 'never' || opt.value === 'custom');
96
84
  }
97
85
 
98
86
  return opts;
@@ -103,9 +91,6 @@ export default {
103
91
 
104
92
  return filtered.map((opt) => ({ value: opt, label: this.t(`accountAndKeys.apiKeys.add.customExpiry.options.${ opt }`) }));
105
93
  },
106
- hasNeverOption() {
107
- return this.expiryOptions?.filter((opt) => opt.value === 'never')?.length === 1;
108
- }
109
94
  },
110
95
 
111
96
  mounted() {
@@ -130,33 +115,31 @@ export default {
130
115
  });
131
116
  },
132
117
 
133
- async actuallySave() {
134
- // update expiration value before save
118
+ async actuallySave(url) {
135
119
  this.updateExpiry();
136
-
137
120
  if ( this.isCreate ) {
138
- const steveToken = await this.$store.dispatch('management/create', {
139
- type: EXT.TOKEN,
140
- spec: {
141
- description: this.form.description,
142
- kind: '',
143
- userPrincipal: null, // will be set by the backend to the current user
144
- clusterName: this.form.clusterName,
145
- enabled: this.form.enabled,
146
- ttl: this.ttl
147
- // userID: not needed as it will be set by the backend to the current user
148
- }
149
- });
150
-
151
- const steveTokenSaved = await steveToken.save();
152
-
153
- this.created = steveTokenSaved;
154
- this.ttlLimited = this.created?.spec?.ttl !== this.ttl;
155
- const token = this.created?.status?.bearerToken?.split(':');
121
+ // Description is a bit weird, so need to clone and set this
122
+ // rather than use this.value - need to find a way to set this if we ever
123
+ // want to allow edit (which I don't think we do)
124
+ const res = await this.value.save();
125
+
126
+ this.created = res;
127
+ this.ttlLimited = res.ttl !== this.value.ttl;
128
+ const token = this.created.token.split(':');
156
129
 
157
130
  this.accessKey = token[0];
158
131
  this.secretKey = (token.length > 1) ? token[1] : '';
159
- this.token = this.created?.status?.bearerToken;
132
+ this.token = this.created.token;
133
+
134
+ // Force a refresh of the token so we get the expiry date correctly
135
+ await this.$store.dispatch('rancher/find', {
136
+ type: NORMAN.TOKEN,
137
+ id: res.id,
138
+ opt: { force: true }
139
+ }, { root: true });
140
+ } else {
141
+ // Note: update of existing key not supported currently
142
+ await this.value.save();
160
143
  }
161
144
  },
162
145
 
@@ -176,9 +159,7 @@ export default {
176
159
  const units = (v === 'custom') ? this.form.customExpiryUnits : v;
177
160
  let ttl = 0;
178
161
 
179
- if (v === 'never') {
180
- ttl = -1;
181
- } else if (units === 'max') {
162
+ if (units === 'max') {
182
163
  ttl = this.maxTTL * 60 * 1000;
183
164
  } else if ( units !== 'never' ) {
184
165
  const now = day();
@@ -186,8 +167,7 @@ export default {
186
167
 
187
168
  ttl = expiry.diff(now);
188
169
  }
189
-
190
- this.ttl = ttl;
170
+ this.value.ttl = ttl;
191
171
  }
192
172
  }
193
173
  };
@@ -198,7 +178,7 @@ export default {
198
178
  <div class="pl-10 pr-10">
199
179
  <LabeledInput
200
180
  key="description"
201
- v-model:value="form.description"
181
+ v-model:value="value.description"
202
182
  :placeholder="t('accountAndKeys.apiKeys.add.description.placeholder')"
203
183
  label-key="accountAndKeys.apiKeys.add.description.label"
204
184
  mode="edit"
@@ -206,30 +186,13 @@ export default {
206
186
  />
207
187
 
208
188
  <LabeledSelect
209
- v-model:value="form.clusterName"
189
+ v-model:value="value.clusterId"
210
190
  class="mt-20 scope-select"
211
191
  label-key="accountAndKeys.apiKeys.add.scope"
212
192
  :options="scopes"
213
193
  />
214
194
 
215
- <Checkbox
216
- v-model:value="form.enabled"
217
- class="mt-20 mb-20"
218
- :mode="mode"
219
- label-key="accountAndKeys.apiKeys.add.enabled"
220
- />
221
-
222
- <Banner
223
- v-if="hasNeverOption"
224
- color="warning"
225
- class="mt-20"
226
- >
227
- <div>
228
- {{ t('accountAndKeys.apiKeys.info.expiryOptionsWithNever') }}
229
- </div>
230
- </Banner>
231
-
232
- <h5 class="mb-20">
195
+ <h5 class="pt-20">
233
196
  {{ t('accountAndKeys.apiKeys.add.expiry.label') }}
234
197
  </h5>
235
198
 
@@ -241,9 +204,7 @@ export default {
241
204
  class="mr-20"
242
205
  name="expiryGroup"
243
206
  />
244
- <div
245
- class="ml-20 mt-10 expiry"
246
- >
207
+ <div class="ml-20 mt-10 expiry">
247
208
  <input
248
209
  v-model="form.customExpiry"
249
210
  :disabled="form.expiryType !== 'custom'"
@@ -300,7 +300,7 @@ export default {
300
300
  </div>
301
301
  </template>
302
302
 
303
- <stye scoped lang="scss">
303
+ <style scoped lang="scss">
304
304
  .capi-unsupported {
305
305
  &.has-description {
306
306
  border-bottom: none;
@@ -317,4 +317,4 @@ export default {
317
317
  display: flex;
318
318
  }
319
319
  }
320
- </stye>
320
+ </style>
@@ -236,12 +236,12 @@ describe('class Chart', () => {
236
236
 
237
237
  expect(result.footerItems).toHaveLength(3);
238
238
 
239
- const categoryItem = result.footerItems.find((i) => i.icon === 'icon-category-alt');
239
+ const categoryItem = result.footerItems.find((i) => i.icon === 'category-alt');
240
240
 
241
241
  expect(categoryItem).toBeDefined();
242
242
  expect(categoryItem?.labels).toContain('database');
243
243
 
244
- const tagItem = result.footerItems.find((i) => i.icon === 'icon-tag-alt');
244
+ const tagItem = result.footerItems.find((i) => i.icon === 'tag-alt');
245
245
 
246
246
  expect(tagItem).toBeDefined();
247
247
  expect(tagItem?.labels).toStrictEqual(expect.arrayContaining(['linux', 'experimentl']));
package/models/chart.js CHANGED
@@ -164,7 +164,7 @@ export default class Chart extends SteveModel {
164
164
  const footerItems = [
165
165
  {
166
166
  type: REPO,
167
- icon: 'icon-repository-alt',
167
+ icon: 'repository-alt',
168
168
  iconTooltip: { key: 'tableHeaders.repoName' },
169
169
  labels: [this.repoNameDisplay],
170
170
  labelTooltip: this.t('catalog.charts.findSimilar.message', { type: this.t('catalog.charts.findSimilar.types.repo') }, true)
@@ -174,7 +174,7 @@ export default class Chart extends SteveModel {
174
174
  if (this.categories.length) {
175
175
  footerItems.push( {
176
176
  type: CATEGORY,
177
- icon: 'icon-category-alt',
177
+ icon: 'category-alt',
178
178
  iconTooltip: { key: 'generic.category' },
179
179
  labels: this.categories,
180
180
  labelTooltip: this.t('catalog.charts.findSimilar.message', { type: this.t('catalog.charts.findSimilar.types.category') }, true)
@@ -184,7 +184,7 @@ export default class Chart extends SteveModel {
184
184
  if (this.tags.length) {
185
185
  footerItems.push({
186
186
  type: TAG,
187
- icon: 'icon-tag-alt',
187
+ icon: 'tag-alt',
188
188
  iconTooltip: { key: 'generic.tags' },
189
189
  labels: this.tags,
190
190
  labelTooltip: this.t('catalog.charts.findSimilar.message', { type: this.t('catalog.charts.findSimilar.types.tag') }, true)
package/models/token.js CHANGED
@@ -16,8 +16,4 @@ export default class extends NormanModel {
16
16
 
17
17
  return expiry.isBefore(day());
18
18
  }
19
-
20
- get isDeprecated() {
21
- return true;
22
- }
23
19
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rancher/shell",
3
- "version": "3.0.9-rc.6",
3
+ "version": "3.0.10",
4
4
  "description": "Rancher Dashboard Shell",
5
5
  "repository": "https://github.com/rancher/dashboard",
6
6
  "license": "Apache-2.0",
@@ -8,7 +8,7 @@
8
8
  "private": false,
9
9
  "types": "types/shell/index.d.ts",
10
10
  "engines": {
11
- "node": ">=20.0.0"
11
+ "node": ">=24.0.0"
12
12
  },
13
13
  "files": [
14
14
  "**/*"
@@ -41,7 +41,7 @@
41
41
  "@popperjs/core": "2.11.8",
42
42
  "@rancher/icons": "2.0.55",
43
43
  "@types/is-url": "1.2.30",
44
- "@types/node": "20.10.8",
44
+ "@types/node": "25.3.3",
45
45
  "@types/semver": "^7.5.8",
46
46
  "@typescript-eslint/eslint-plugin": "5.62.0",
47
47
  "@typescript-eslint/parser": "5.62.0",
@@ -65,7 +65,7 @@
65
65
  "color": "5.0.3",
66
66
  "cookie-universal": "2.2.2",
67
67
  "cookie": "0.7.0",
68
- "core-js": "3.45.0",
68
+ "core-js": "3.48.0",
69
69
  "cron-validator": "1.4.0",
70
70
  "cronstrue": "3.9.0",
71
71
  "cross-env": "7.0.3",
@@ -119,7 +119,7 @@
119
119
  "papaparse": "5.3.0",
120
120
  "portal-vue": "~3.0.0",
121
121
  "sass-loader": "12.6.0",
122
- "sass": "1.89.2",
122
+ "sass": "1.97.3",
123
123
  "serve-static": "1.14.1",
124
124
  "set-cookie-parser": "2.4.6",
125
125
  "shell-quote": "1.7.3",
@@ -131,10 +131,10 @@
131
131
  "ufo": "0.7.11",
132
132
  "unfetch": "4.2.0",
133
133
  "url-parse": "1.5.10",
134
- "vue-router": "4.5.1",
134
+ "vue-router": "4.6.4",
135
135
  "vue-select": "4.0.0-beta.6",
136
136
  "vue-server-renderer": "2.7.16",
137
- "vue": "3.5.18",
137
+ "vue": "3.5.29",
138
138
  "vue3-resize": "0.2.0",
139
139
  "vue3-virtual-scroll-list": "0.2.1",
140
140
  "vuedraggable": "4.1.0",
@@ -164,7 +164,7 @@
164
164
  "roarr": "7.0.4",
165
165
  "semver": "7.5.4",
166
166
  "@types/lodash": "4.17.5",
167
- "@types/node": "20.10.8",
167
+ "@types/node": "25.3.3",
168
168
  "@vue/cli-service/html-webpack-plugin": "^5.0.0"
169
169
  },
170
170
  "nyc": {