@rancher/shell 0.1.3 → 0.1.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 (131) hide show
  1. package/assets/brand/suse/dark/rancher-logo.svg +1 -148
  2. package/assets/brand/suse/rancher-logo.svg +1 -130
  3. package/assets/images/featured/img1.jpg +0 -0
  4. package/assets/images/featured.jpg +0 -0
  5. package/assets/images/generic-plugin.svg +7 -0
  6. package/assets/styles/themes/_dark.scss +3 -0
  7. package/assets/styles/themes/_light.scss +3 -0
  8. package/assets/styles/themes/_suse.scss +1 -1
  9. package/assets/translations/en-us.yaml +183 -45
  10. package/assets/translations/zh-hans.yaml +21 -24
  11. package/components/AsyncButton.vue +17 -2
  12. package/components/ButtonDropdown.vue +4 -0
  13. package/components/Carousel.vue +291 -0
  14. package/components/CommunityLinks.vue +69 -18
  15. package/components/CruResource.vue +11 -3
  16. package/components/Dialog.vue +102 -0
  17. package/components/ExplorerMembers.vue +2 -4
  18. package/components/ExplorerProjectsNamespaces.vue +6 -7
  19. package/components/IconMessage.vue +9 -1
  20. package/components/LocaleSelector.vue +62 -29
  21. package/components/ResourceTable.vue +7 -2
  22. package/components/SimpleBox.vue +6 -4
  23. package/components/SortableTable/index.vue +11 -21
  24. package/components/Tabbed/Tab.vue +5 -0
  25. package/components/Tabbed/index.vue +29 -2
  26. package/components/auth/Principal.vue +1 -0
  27. package/components/fleet/FleetBundles.vue +8 -3
  28. package/components/fleet/FleetSummary.vue +6 -0
  29. package/components/form/KeyValue.vue +80 -58
  30. package/components/form/NameNsDescription.vue +10 -4
  31. package/components/form/ResourceTabs/index.vue +5 -1
  32. package/components/formatter/ClusterLink.vue +3 -7
  33. package/components/nav/NamespaceFilter.vue +3 -3
  34. package/components/nav/TopLevelMenu.vue +10 -28
  35. package/config/footer.js +13 -14
  36. package/config/labels-annotations.js +2 -1
  37. package/config/product/explorer.js +5 -4
  38. package/config/product/legacy.js +0 -47
  39. package/config/product/multi-cluster-apps.js +0 -12
  40. package/config/product/settings.js +12 -1
  41. package/config/product/uiplugins.js +17 -0
  42. package/config/settings.js +21 -2
  43. package/config/types.js +5 -1
  44. package/config/uiplugins.js +60 -0
  45. package/content/docs/en-us/getting-started.md +1 -26
  46. package/core/plugins.js +12 -0
  47. package/detail/provisioning.cattle.io.cluster.vue +3 -3
  48. package/detail/workload/index.vue +2 -2
  49. package/dialog/DiagnosticTimingsDialog.vue +116 -0
  50. package/dialog/RotateCertificatesDialog.vue +9 -3
  51. package/edit/auth/azuread.vue +28 -9
  52. package/edit/networking.k8s.io.ingress/index.vue +2 -2
  53. package/edit/persistentvolume/index.vue +3 -0
  54. package/edit/pod.vue +27 -0
  55. package/edit/provisioning.cattle.io.cluster/rke2.vue +76 -5
  56. package/edit/service.vue +7 -5
  57. package/edit/workload/__tests__/Upgrading.test.ts +1 -0
  58. package/edit/workload/index.vue +13 -1
  59. package/edit/workload/mixins/workload.js +13 -13
  60. package/edit/workload/storage/ContainerMountPaths.vue +240 -0
  61. package/edit/workload/storage/Mount.vue +1 -0
  62. package/edit/workload/storage/awsElasticBlockStore.vue +20 -1
  63. package/edit/workload/storage/azureDisk.vue +22 -2
  64. package/edit/workload/storage/azureFile.vue +20 -2
  65. package/edit/workload/storage/csi/index.vue +23 -1
  66. package/edit/workload/storage/gcePersistentDisk.vue +20 -2
  67. package/edit/workload/storage/index.vue +23 -49
  68. package/edit/workload/storage/vsphereVolume.vue +11 -1
  69. package/layouts/default.vue +14 -8
  70. package/layouts/home.vue +9 -4
  71. package/layouts/plain.vue +10 -5
  72. package/list/management.cattle.io.setting.vue +3 -3
  73. package/list/provisioning.cattle.io.cluster.vue +1 -1
  74. package/machine-config/harvester.vue +5 -3
  75. package/models/catalog.cattle.io.uiplugin.js +34 -0
  76. package/models/cluster/node.js +25 -2
  77. package/models/fleet.cattle.io.bundle.js +1 -1
  78. package/models/harvesterhci.io.management.cluster.js +11 -5
  79. package/models/provisioning.cattle.io.cluster.js +12 -6
  80. package/models/workload.js +5 -3
  81. package/nuxt.config.js +69 -25
  82. package/package.json +108 -109
  83. package/pages/auth/login.vue +1 -1
  84. package/pages/c/_cluster/apps/charts/index.vue +46 -1
  85. package/pages/c/_cluster/apps/charts/install.vue +10 -9
  86. package/pages/c/_cluster/explorer/index.vue +72 -9
  87. package/pages/c/_cluster/explorer/tools/index.vue +12 -5
  88. package/pages/c/_cluster/mcapps/index.vue +1 -1
  89. package/pages/c/_cluster/settings/brand.vue +0 -40
  90. package/pages/c/_cluster/settings/links.vue +200 -0
  91. package/pages/c/_cluster/uiplugins/DeveloperInstallDialog.vue +232 -0
  92. package/pages/c/_cluster/uiplugins/InstallDialog.vue +242 -0
  93. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +284 -0
  94. package/pages/c/_cluster/uiplugins/RemoveUIPlugins.vue +130 -0
  95. package/pages/c/_cluster/uiplugins/SetupUIPlugins.vue +253 -0
  96. package/pages/c/_cluster/uiplugins/UninstallDialog.vue +115 -0
  97. package/pages/c/_cluster/uiplugins/index.vue +694 -0
  98. package/pages/diagnostic.vue +185 -101
  99. package/pages/docs/_doc.vue +3 -1
  100. package/pages/home.vue +21 -56
  101. package/pages/prefs.vue +108 -88
  102. package/pages/safeMode.vue +17 -0
  103. package/pages/support/index.vue +23 -15
  104. package/pkg/dynamic-importer.lib.js +4 -0
  105. package/plugins/dashboard-store/resource-class.js +2 -2
  106. package/plugins/formatters.js +15 -0
  107. package/plugins/plugin.js +56 -4
  108. package/plugins/steve/mutations.js +1 -1
  109. package/plugins/steve/subscribe.js +94 -72
  110. package/plugins/steve/web-worker.steve-sub-worker.js +24 -15
  111. package/promptRemove/management.cattle.io.globalrole.vue +47 -0
  112. package/promptRemove/management.cattle.io.roletemplate.vue +47 -0
  113. package/promptRemove/mixin/roleDeletionCheck.js +97 -0
  114. package/scripts/publish-shell.sh +1 -1
  115. package/scripts/sync-shell-deps +37 -0
  116. package/store/catalog.js +9 -8
  117. package/store/i18n.js +10 -1
  118. package/store/prefs.js +16 -0
  119. package/store/type-map.js +32 -5
  120. package/store/uiplugins.ts +15 -61
  121. package/utils/__tests__/object.test.ts +0 -24
  122. package/utils/__tests__/selector.test.ts +1 -1
  123. package/utils/dynamic-importer.js +4 -0
  124. package/utils/grafana.js +2 -6
  125. package/utils/socket.js +41 -20
  126. package/utils/string.js +1 -7
  127. package/utils/validators/formRules/__tests__/index.test.ts +108 -0
  128. package/utils/validators/formRules/index.ts +9 -1
  129. package/yarn-error.log +195 -0
  130. package/pages/plugins.vue +0 -387
  131. package/server/verdaccio-middleware.js +0 -56
@@ -1,12 +1,35 @@
1
1
  <script>
2
2
  import { mapGetters } from 'vuex';
3
+ import Select from '@shell/components/form/Select.vue';
3
4
 
4
5
  export default {
5
6
  name: 'LocalSelector',
6
7
 
8
+ components: { Select },
9
+
10
+ props: {
11
+ mode: {
12
+ type: String,
13
+ default: ''
14
+ },
15
+ },
16
+
7
17
  computed: {
8
18
  ...mapGetters('i18n', ['selectedLocaleLabel', 'availableLocales']),
9
19
 
20
+ localesOptions() {
21
+ return Object.keys(this.availableLocales).map((value) => {
22
+ return {
23
+ label: this.t(`locale.${ value }`),
24
+ value
25
+ };
26
+ });
27
+ },
28
+
29
+ selectedOption() {
30
+ return Object.keys(this.availableLocales)[Object.values(this.availableLocales).indexOf(this.selectedLocaleLabel)];
31
+ },
32
+
10
33
  showLocale() {
11
34
  return (this.availableLocales && Object.keys(this.availableLocales).length > 1) || this.dev;
12
35
  },
@@ -17,42 +40,52 @@ export default {
17
40
  },
18
41
 
19
42
  methods: {
20
- switchLocale(locale) {
21
- this.$store.dispatch('i18n/switchTo', locale);
43
+ switchLocale($event) {
44
+ this.$store.dispatch('i18n/switchTo', $event);
22
45
  },
23
46
  }
24
47
  };
25
48
  </script>
26
49
 
27
50
  <template>
28
- <div v-if="showLocale">
29
- <v-popover
30
- popover-class="localeSelector"
31
- placement="top"
32
- trigger="click"
33
- >
34
- <a
35
- data-testid="locale-selector"
36
- class="locale-chooser"
37
- >
38
- {{ selectedLocaleLabel }}
39
- <i class="icon icon-fw icon-sort-down" />
40
- </a>
41
-
42
- <template slot="popover">
43
- <ul class="list-unstyled dropdown" style="margin: -1px;">
44
- <li v-if="showNone" v-t="'locale.none'" class="hand" @click="switchLocale('none')" />
45
- <li
46
- v-for="(label, name) in availableLocales"
47
- :key="name"
48
- class="hand"
49
- @click="switchLocale(name)"
51
+ <div>
52
+ <div v-if="mode === 'login'">
53
+ <div v-if="showLocale">
54
+ <v-popover
55
+ popover-class="localeSelector"
56
+ placement="top"
57
+ trigger="click"
58
+ >
59
+ <a
60
+ data-testid="locale-selector"
61
+ class="locale-chooser"
50
62
  >
51
- {{ label }}
52
- </li>
53
- </ul>
54
- </template>
55
- </v-popover>
63
+ {{ selectedLocaleLabel }}
64
+ <i class="icon icon-fw icon-sort-down" />
65
+ </a>
66
+ <template slot="popover">
67
+ <ul class="list-unstyled dropdown" style="margin: -1px;">
68
+ <li v-if="showNone" v-t="'locale.none'" class="hand" @click="switchLocale('none')" />
69
+ <li
70
+ v-for="(label, name) in availableLocales"
71
+ :key="name"
72
+ class="hand"
73
+ @click="switchLocale(name)"
74
+ >
75
+ {{ label }}
76
+ </li>
77
+ </ul>
78
+ </template>
79
+ </v-popover>
80
+ </div>
81
+ </div>
82
+ <div v-else>
83
+ <Select
84
+ :value="selectedOption"
85
+ :options="localesOptions"
86
+ @input="switchLocale($event)"
87
+ />
88
+ </div>
56
89
  </div>
57
90
  </template>
58
91
 
@@ -116,7 +116,12 @@ export default {
116
116
  getCustomDetailLink: {
117
117
  type: Function,
118
118
  default: null
119
- }
119
+ },
120
+
121
+ ignoreFilter: {
122
+ type: Boolean,
123
+ default: false
124
+ },
120
125
  },
121
126
 
122
127
  data() {
@@ -197,7 +202,7 @@ export default {
197
202
  const isAll = this.$store.getters['isAllNamespaces'];
198
203
 
199
204
  // If the resources isn't namespaced or we want ALL of them, there's nothing to do.
200
- if ( !this.isNamespaced || (isAll && !this.currentProduct?.hideSystemResources)) {
205
+ if ( !this.isNamespaced || (isAll && !this.currentProduct?.hideSystemResources) || this.ignoreFilter) {
201
206
  return this.rows || [];
202
207
  }
203
208
 
@@ -29,10 +29,12 @@ export default {
29
29
 
30
30
  <template>
31
31
  <div v-if="shown" class="simple-box" v-on="$listeners">
32
- <div v-if="title || canClose" class="top">
33
- <h2 v-if="title">
34
- {{ title }}
35
- </h2>
32
+ <div v-if="title || canClose || $slots.title" class="top">
33
+ <slot name="title">
34
+ <h2 v-if="title">
35
+ {{ title }}
36
+ </h2>
37
+ </slot>
36
38
  <div v-if="canClose || pref" class="close-button" @click="closeBox($event)">
37
39
  <i class="icon icon-close" />
38
40
  </div>
@@ -20,6 +20,11 @@ import actions from './actions';
20
20
  // Uncomment for table performance debugging
21
21
  // import tableDebug from './debug';
22
22
 
23
+ // Its quicker to render if we directly supply the components for the formatters
24
+ // rather than just the name of a global component - so create a map of the formatter comoponents
25
+ // NOTE: This is populated by a plugin (formatters.js) to avoid issues with plugins
26
+ export const FORMATTERS = {};
27
+
23
28
  export const COLUMN_BREAKPOINTS = {
24
29
  /**
25
30
  * Only show column if at tablet width or wider
@@ -35,19 +40,6 @@ export const COLUMN_BREAKPOINTS = {
35
40
  DESKTOP: 'desktop'
36
41
  };
37
42
 
38
- // Its quicker to render if we directly supply the components for the formatters
39
- // rather than just the name of a global component - so create a map of the formatter comoponents
40
- const FORMATTERS = {};
41
-
42
- const components = require.context('@shell/components/formatter', false, /[A-Z]\w+\.(vue)$/);
43
-
44
- components.keys().forEach((fileName) => {
45
- const componentConfig = components(fileName);
46
- const componentName = fileName.split('/').pop().split('.')[0];
47
-
48
- FORMATTERS[componentName] = componentConfig.default || componentConfig;
49
- });
50
-
51
43
  // @TODO:
52
44
  // Fixed header/scrolling
53
45
 
@@ -334,6 +326,7 @@ export default {
334
326
  clearTimeout(this._loadingDelayTimer);
335
327
  clearTimeout(this._liveColumnsTimer);
336
328
  clearTimeout(this._delayedColumnsTimer);
329
+ clearTimeout(this.manualRefreshTimer);
337
330
 
338
331
  const $main = $('main');
339
332
 
@@ -348,23 +341,18 @@ export default {
348
341
  descending(neu, old) {
349
342
  this.watcherUpdateLiveAndDelayed(neu, old);
350
343
  },
351
-
352
344
  searchQuery(neu, old) {
353
345
  this.watcherUpdateLiveAndDelayed(neu, old);
354
346
  },
355
-
356
347
  sortFields(neu, old) {
357
348
  this.watcherUpdateLiveAndDelayed(neu, old);
358
349
  },
359
-
360
350
  groupBy(neu, old) {
361
351
  this.watcherUpdateLiveAndDelayed(neu, old);
362
352
  },
363
-
364
353
  namespaces(neu, old) {
365
354
  this.watcherUpdateLiveAndDelayed(neu, old);
366
355
  },
367
-
368
356
  page(neu, old) {
369
357
  this.watcherUpdateLiveAndDelayed(neu, old);
370
358
  },
@@ -385,9 +373,11 @@ export default {
385
373
 
386
374
  // setTimeout is needed so that this is pushed further back on the JS computing queue
387
375
  // because nextTick isn't enough to capture the DOM update for the manual refresh only scenario
388
- setTimeout(() => {
389
- this.watcherUpdateLiveAndDelayed(neu, old);
390
- }, 500);
376
+ if (old && !neu) {
377
+ this.manualRefreshTimer = setTimeout(() => {
378
+ this.watcherUpdateLiveAndDelayed(neu, old);
379
+ }, 1000);
380
+ }
391
381
  }
392
382
  },
393
383
 
@@ -36,6 +36,11 @@ export default {
36
36
  type: Boolean,
37
37
  default: false
38
38
  },
39
+ badge: {
40
+ default: 0,
41
+ required: false,
42
+ type: Number
43
+ },
39
44
  },
40
45
 
41
46
  data() {
@@ -38,6 +38,11 @@ export default {
38
38
  noContent: {
39
39
  type: Boolean,
40
40
  default: false,
41
+ },
42
+
43
+ tabsOnly: {
44
+ type: Boolean,
45
+ default: false,
41
46
  }
42
47
  },
43
48
 
@@ -207,7 +212,7 @@ export default {
207
212
  </script>
208
213
 
209
214
  <template>
210
- <div :class="{'side-tabs': !!sideTabs }">
215
+ <div :class="{'side-tabs': !!sideTabs, 'tabs-only': tabsOnly }">
211
216
  <ul
212
217
  ref="tablist"
213
218
  role="tablist"
@@ -233,6 +238,7 @@ export default {
233
238
  @click.prevent="select(tab.name, $event)"
234
239
  >
235
240
  <span>{{ tab.labelDisplay }}</span>
241
+ <span v-if="tab.badge" class="tab-badge">{{ tab.badge }}</span>
236
242
  <i v-if="hasIcon(tab)" v-tooltip="t('validation.tab')" class="conditions-alert-icon icon-error icon-lg" />
237
243
  </a>
238
244
  </li>
@@ -326,6 +332,15 @@ export default {
326
332
  color: var(--error);
327
333
  }
328
334
  }
335
+
336
+ .tab-badge {
337
+ margin-left: 5px;
338
+ background-color: var(--link);
339
+ color: #fff;
340
+ border-radius: 6px;
341
+ padding: 1px 7px;
342
+ font-size: 11px;
343
+ }
329
344
  }
330
345
  }
331
346
 
@@ -337,6 +352,19 @@ export default {
337
352
  }
338
353
  }
339
354
 
355
+ .tabs-only {
356
+ margin-bottom: 20px;
357
+
358
+ .tab-container {
359
+ display: none;
360
+ }
361
+
362
+ .tabs {
363
+ border: 0;
364
+ border-bottom: 2px solid var(--border);
365
+ }
366
+ }
367
+
340
368
  .side-tabs {
341
369
  display: flex;
342
370
  box-shadow: 0 0 20px var(--shadow);
@@ -349,7 +377,6 @@ export default {
349
377
 
350
378
  // Tabbed component within a tabbed component
351
379
  .tab-container & {
352
- margin: -20px;
353
380
  box-shadow: unset;
354
381
  }
355
382
 
@@ -159,6 +159,7 @@ export default {
159
159
  .name {
160
160
  grid-area: name;
161
161
  line-height: math.div($size, 2);
162
+ overflow-wrap: anywhere;
162
163
  }
163
164
 
164
165
  .description {
@@ -103,9 +103,14 @@ export default {
103
103
  return out;
104
104
  },
105
105
  },
106
+
107
+ methods: {
108
+ displayWarning(row) {
109
+ return !!row.status?.summary && (row.status.summary.desiredReady !== row.status.summary.ready);
110
+ }
111
+ }
106
112
  };
107
113
  </script>
108
-
109
114
  <template>
110
115
  <div>
111
116
  <Loading v-if="$fetchState.pending" />
@@ -117,9 +122,9 @@ export default {
117
122
  :rows="bundles"
118
123
  >
119
124
  <template #cell:deploymentsReady="{row}">
120
- <span v-if="row.status.summary.desiredReady != row.status.summary.ready" class="text-warning">
125
+ <span v-if="displayWarning(row)" class="text-warning">
121
126
  {{ row.status.summary.ready }}/{{ row.status.summary.desiredReady }}</span>
122
- <span v-else>{{ row.status.summary.desiredReady }}</span>
127
+ <span v-else-if="row.status">{{ row.status.summary.desiredReady }}</span>
123
128
  </template>
124
129
  </ResourceTable>
125
130
  </div>
@@ -84,6 +84,12 @@ export default {
84
84
  const out = { ...getResourceDefaultState(this.$store.getters['i18n/withFallback'], this.stateKey) };
85
85
 
86
86
  resources.forEach(({ status, metadata }) => {
87
+ if (!status) {
88
+ out[STATES_ENUM.UNKNOWN].count += 1;
89
+
90
+ return;
91
+ }
92
+
87
93
  const k = status?.summary.ready > 0 && status?.summary.desiredReady === status.summary.ready;
88
94
 
89
95
  if (k) {
@@ -12,6 +12,8 @@ import { _EDIT, _VIEW } from '@shell/config/query-params';
12
12
  import { asciiLike } from '@shell/utils/string';
13
13
 
14
14
  export default {
15
+ name: 'KeyValue',
16
+
15
17
  components: {
16
18
  Select,
17
19
  TextAreaAutoGrow,
@@ -22,6 +24,10 @@ export default {
22
24
  type: [Array, Object],
23
25
  default: null,
24
26
  },
27
+ defaultValue: {
28
+ type: [Array, Object],
29
+ default: null,
30
+ },
25
31
  // If the user supplies this array, then it indicates which keys should be shown as binary
26
32
  binaryValueKeys: {
27
33
  type: [Array, Object],
@@ -223,68 +229,13 @@ export default {
223
229
  }
224
230
  },
225
231
  data() {
226
- const rows = [];
227
-
228
- if ( this.asMap ) {
229
- const input = this.value || {};
230
-
231
- Object.keys(input).forEach((key) => {
232
- let value = input[key];
233
- const decodedValue = base64Decode(input[key]);
234
- const asciiValue = asciiLike(decodedValue);
235
-
236
- if ( this.handleBase64 && asciiValue) {
237
- value = base64Decode(value);
238
- }
239
-
240
- rows.push({
241
- key,
242
- value,
243
- binary: this.displayValuesAsBinary || (this.handleBase64 && !asciiValue),
244
- canEncode: this.handleBase64 && asciiValue,
245
- supported: true,
246
- });
247
- });
248
- } else {
249
- const input = this.value || [];
250
-
251
- for ( const row of input ) {
252
- let value = row[this.valueName] || '';
253
- const decodedValue = base64Decode(row[this.valueName]);
254
- const asciiValue = asciiLike(decodedValue);
255
-
256
- if ( this.handleBase64 && asciiValue) {
257
- value = base64Decode(value);
258
- }
259
- const entry = {
260
- [this.keyName]: row[this.keyName] || '',
261
- [this.valueName]: value,
262
- binary: this.displayValuesAsBinary || (this.handleBase64 && !asciiValue),
263
- canEncode: this.handleBase64 && asciiValue,
264
- supported: this.supported(row),
265
- };
266
-
267
- this.preserveKeys?.map((k) => {
268
- if ( typeof row[k] !== 'undefined' ) {
269
- entry[k] = row[k];
270
- }
271
- });
272
- rows.push(entry);
273
- }
274
- }
275
- if ( !rows.length && this.initialEmptyRow ) {
276
- rows.push({
277
- [this.keyName]: '',
278
- [this.valueName]: '',
279
- binary: false,
280
- canEncode: this.handleBase64,
281
- supported: true
282
- });
283
- }
232
+ const rows = this.getRows(this.value);
284
233
 
285
234
  return { rows };
286
235
  },
236
+
287
237
  computed: {
238
+
288
239
  isView() {
289
240
  return this.mode === _VIEW;
290
241
  },
@@ -315,7 +266,78 @@ export default {
315
266
  created() {
316
267
  this.queueUpdate = debounce(this.update, 500);
317
268
  },
269
+ watch: {
270
+ defaultValue(neu) {
271
+ if (Array.isArray(neu)) {
272
+ this.rows = this.getRows(neu);
273
+ this.$emit('input', neu);
274
+ }
275
+ }
276
+ },
318
277
  methods: {
278
+ getRows(value) {
279
+ const rows = [];
280
+
281
+ if ( this.asMap ) {
282
+ const input = value || {};
283
+
284
+ Object.keys(input).forEach((key) => {
285
+ let value = input[key];
286
+ const decodedValue = base64Decode(input[key]);
287
+ const asciiValue = asciiLike(decodedValue);
288
+
289
+ if ( this.handleBase64 && asciiValue) {
290
+ value = base64Decode(value);
291
+ }
292
+
293
+ rows.push({
294
+ key,
295
+ value,
296
+ binary: this.displayValuesAsBinary || (this.handleBase64 && !asciiValue),
297
+ canEncode: this.handleBase64 && asciiValue,
298
+ supported: true,
299
+ });
300
+ });
301
+ } else {
302
+ const input = value || [];
303
+
304
+ for ( const row of input ) {
305
+ let value = row[this.valueName] || '';
306
+ const decodedValue = base64Decode(row[this.valueName]);
307
+ const asciiValue = asciiLike(decodedValue);
308
+
309
+ if ( this.handleBase64 && asciiValue) {
310
+ value = base64Decode(value);
311
+ }
312
+ const entry = {
313
+ [this.keyName]: row[this.keyName] || '',
314
+ [this.valueName]: value,
315
+ binary: this.displayValuesAsBinary || (this.handleBase64 && !asciiValue),
316
+ canEncode: this.handleBase64 && asciiValue,
317
+ supported: this.supported(row),
318
+ };
319
+
320
+ this.preserveKeys?.map((k) => {
321
+ if ( typeof row[k] !== 'undefined' ) {
322
+ entry[k] = row[k];
323
+ }
324
+ });
325
+ rows.push(entry);
326
+ }
327
+ }
328
+ if ( !rows.length && this.initialEmptyRow ) {
329
+ rows.push({
330
+ [this.keyName]: '',
331
+ [this.valueName]: '',
332
+ binary: false,
333
+ canEncode: this.handleBase64,
334
+ supported: true
335
+ });
336
+ }
337
+
338
+ return rows;
339
+ },
340
+
319
341
  add(key = '', value = '') {
320
342
  const obj = {
321
343
  ...this.defaultAddData,
@@ -5,7 +5,7 @@ import { get, set } from '@shell/utils/object';
5
5
  import { sortBy } from '@shell/utils/sort';
6
6
  import { NAMESPACE } from '@shell/config/types';
7
7
  import { DESCRIPTION } from '@shell/config/labels-annotations';
8
- import { _VIEW, _EDIT } from '@shell/config/query-params';
8
+ import { _VIEW, _EDIT, _CREATE } from '@shell/config/query-params';
9
9
  import { LabeledInput } from '@components/Form/LabeledInput';
10
10
  import LabeledSelect from '@shell/components/form/LabeledSelect';
11
11
 
@@ -224,15 +224,21 @@ export default {
224
224
  const namespaces = this.namespacesOverride || this.$store.getters[`${ currentStore }/all`](this.namespaceType);
225
225
 
226
226
  const filtered = namespaces.filter( this.namespaceFilter || ((namespace) => {
227
+ // By default, include the namespace in the dropdown.
228
+ let out = true;
229
+
227
230
  if (this.currentProduct?.hideSystemResources) {
228
231
  // Filter out the namespace
229
232
  // if it is a system namespace or if it is managed by
230
233
  // Fleet.
231
- return !namespace.isSystem && !namespace.isFleetManaged;
234
+ out = !namespace.isSystem && !namespace.isFleetManaged;
232
235
  }
233
236
 
234
- // By default, include the namespace in the dropdown.
235
- return true;
237
+ if (this.mode === _CREATE) {
238
+ out = out && !!namespace.links.update;
239
+ }
240
+
241
+ return out;
236
242
  }));
237
243
 
238
244
  const withLabels = filtered.map(this.namespaceMapper || ((obj) => {
@@ -85,7 +85,11 @@ export default {
85
85
  showConditions() {
86
86
  const inStore = this.$store.getters['currentStore'](this.value.type);
87
87
 
88
- return this.isView && this.needConditions && this.value?.type && this.$store.getters[`${ inStore }/pathExistsInSchema`](this.value.type, 'status.conditions');
88
+ if ( this.$store.getters[`${ inStore }/schemaFor`](this.value.type) ) {
89
+ return this.isView && this.needConditions && this.value?.type && this.$store.getters[`${ inStore }/pathExistsInSchema`](this.value.type, 'status.conditions');
90
+ }
91
+
92
+ return false;
89
93
  },
90
94
  showEvents() {
91
95
  return this.isView && this.needEvents && this.hasEvents && (this.events.length || this.alwaysShowEvents);
@@ -25,12 +25,8 @@ export default {
25
25
  return this.row?.detailLocation;
26
26
  },
27
27
 
28
- clusterHasIssues() {
29
- return this.row.status?.conditions?.some(condition => condition.error === true);
30
- },
31
-
32
28
  statusErrorConditions() {
33
- if (this.clusterHasIssues) {
29
+ if (this.row.hasError) {
34
30
  return this.row?.status.conditions.filter(condition => condition.error === true);
35
31
  }
36
32
 
@@ -38,7 +34,7 @@ export default {
38
34
  },
39
35
 
40
36
  formattedConditions() {
41
- if (this.clusterHasIssues) {
37
+ if (this.row.hasError) {
42
38
  const filteredConditions = this.statusErrorConditions;
43
39
  const formattedTooltip = [];
44
40
 
@@ -69,7 +65,7 @@ export default {
69
65
  class="template-upgrade-icon icon-alert icon"
70
66
  />
71
67
  <i
72
- v-if="clusterHasIssues"
68
+ v-if="row.hasError"
73
69
  v-tooltip="{ content: `<div>${formattedConditions}</div>`, html: true }"
74
70
  class="conditions-alert-icon icon-error icon-lg"
75
71
  />
@@ -1,6 +1,6 @@
1
1
  <script>
2
2
  import { mapGetters } from 'vuex';
3
- import { NAMESPACE_FILTERS, DEV } from '@shell/store/prefs';
3
+ import { NAMESPACE_FILTERS, ALL_NAMESPACES } from '@shell/store/prefs';
4
4
  import { NAMESPACE, MANAGEMENT } from '@shell/config/types';
5
5
  import { sortBy } from '@shell/utils/sort';
6
6
  import { isArray, addObjects, findBy, filterBy } from '@shell/utils/array';
@@ -322,8 +322,8 @@ export default {
322
322
 
323
323
  methods: {
324
324
  filterNamespaces(namespaces) {
325
- if (this.$store.getters['prefs/get'](DEV)) {
326
- // If developer tools are turned on in the user preferences,
325
+ if (this.$store.getters['prefs/get'](ALL_NAMESPACES)) {
326
+ // If all namespaces options are turned on in the user preferences,
327
327
  // return all namespaces including system namespaces and RBAC
328
328
  // management namespaces.
329
329
  return namespaces;