@rancher/shell 0.3.5 → 0.3.6

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 (116) hide show
  1. package/assets/images/providers/outscale.svg +19 -0
  2. package/assets/styles/base/_basic.scss +18 -0
  3. package/assets/styles/base/_mixins.scss +0 -11
  4. package/assets/styles/base/_variables.scss +2 -4
  5. package/assets/styles/global/_button.scss +12 -2
  6. package/assets/translations/en-us.yaml +22 -1
  7. package/assets/translations/zh-hans.yaml +30 -10
  8. package/chart/gatekeeper.vue +3 -2
  9. package/chart/istio.vue +29 -3
  10. package/components/BrandImage.vue +1 -4
  11. package/components/Carousel.vue +85 -37
  12. package/components/EtcdInfoBanner.vue +7 -3
  13. package/components/ExplorerMembers.vue +100 -5
  14. package/components/ExplorerProjectsNamespaces.vue +32 -2
  15. package/components/GrafanaDashboard.vue +9 -2
  16. package/components/SortableTable/index.vue +23 -11
  17. package/components/SortableTable/selection.js +58 -50
  18. package/components/Wizard.vue +4 -2
  19. package/components/auth/RoleDetailEdit.vue +2 -2
  20. package/components/form/HookOption.vue +14 -10
  21. package/components/form/Labels.vue +32 -27
  22. package/components/form/MatchExpressions.vue +2 -2
  23. package/components/form/Members/ClusterPermissionsEditor.vue +32 -7
  24. package/components/form/NameNsDescription.vue +1 -1
  25. package/components/form/ProjectMemberEditor.vue +46 -21
  26. package/components/form/Tolerations.vue +4 -1
  27. package/components/form/ValueFromResource.vue +14 -9
  28. package/components/form/WorkloadPorts.vue +2 -2
  29. package/components/form/__tests__/NameNsDescription.ts +27 -0
  30. package/components/formatter/WorkloadHealthScale.vue +8 -2
  31. package/components/nav/NamespaceFilter.vue +8 -0
  32. package/{nuxt/components → components/nuxt}/nuxt.js +1 -1
  33. package/{nuxt → config}/middleware.js +8 -8
  34. package/config/product/explorer.js +24 -3
  35. package/config/query-params.js +1 -0
  36. package/config/router.js +1 -1
  37. package/{nuxt → config}/store.js +82 -79
  38. package/config/table-headers.js +46 -12
  39. package/config/types.js +7 -0
  40. package/core/plugin.ts +4 -2
  41. package/core/types.ts +258 -1
  42. package/creators/app/files/tsconfig.json +0 -1
  43. package/creators/app/files/vue.config.js +0 -1
  44. package/creators/pkg/files/.github/workflows/build-extension.yml +3 -4
  45. package/creators/pkg/files/tsconfig.json +0 -1
  46. package/creators/pkg/pkg.package.json +3 -3
  47. package/detail/constraints.gatekeeper.sh.constraint.vue +14 -7
  48. package/detail/fleet.cattle.io.clustergroup.vue +7 -1
  49. package/edit/fleet.cattle.io.gitrepo.vue +16 -1
  50. package/edit/logging.banzaicloud.io.output/index.vue +18 -5
  51. package/edit/logging.banzaicloud.io.output/providers/loki.vue +1 -0
  52. package/edit/namespace.vue +12 -8
  53. package/edit/provisioning.cattle.io.cluster/MachinePool.vue +11 -4
  54. package/edit/provisioning.cattle.io.cluster/import.vue +23 -25
  55. package/edit/provisioning.cattle.io.cluster/rke2.vue +96 -18
  56. package/edit/workload/mixins/workload.js +6 -7
  57. package/edit/workload/storage/Mount.vue +3 -3
  58. package/initialize/App.js +206 -0
  59. package/{nuxt → initialize}/client.js +406 -360
  60. package/{nuxt → initialize}/index.js +21 -22
  61. package/layouts/standalone.vue +13 -0
  62. package/list/catalog.cattle.io.clusterrepo.vue +1 -0
  63. package/list/rbac.authorization.k8s.io.clusterrolebinding.vue +48 -0
  64. package/list/workload.vue +6 -4
  65. package/mixins/chart.js +29 -1
  66. package/mixins/fetch.client.js +95 -0
  67. package/{nuxt/mixins → mixins}/fetch.server.js +30 -26
  68. package/mixins/labeled-form-element.ts +2 -2
  69. package/models/constraints.gatekeeper.sh.constraint.js +37 -0
  70. package/models/pod.js +4 -0
  71. package/models/rbac.authorization.k8s.io.clusterrolebinding.js +16 -0
  72. package/models/rbac.authorization.k8s.io.rolebinding.js +16 -0
  73. package/package.json +9 -13
  74. package/pages/c/_cluster/apps/charts/install.vue +61 -39
  75. package/pages/diagnostic.vue +32 -25
  76. package/pages/rio/mesh.vue +1 -2
  77. package/pkg/tsconfig.json +0 -1
  78. package/plugins/clean-html-directive.js +3 -0
  79. package/plugins/dashboard-store/index.js +1 -1
  80. package/plugins/plugin.js +0 -14
  81. package/plugins/portal-vue.js +4 -0
  82. package/rancher-components/components/Banner/Banner.test.ts +3 -5
  83. package/rancher-components/components/Banner/Banner.vue +1 -0
  84. package/rancher-components/components/Form/Radio/RadioButton.test.ts +31 -0
  85. package/rancher-components/components/Form/Radio/RadioButton.vue +14 -3
  86. package/scripts/extension/publish +42 -23
  87. package/scripts/serve-pkgs +6 -2
  88. package/store/type-map.js +1 -1
  89. package/tsconfig.json +0 -1
  90. package/types/rancher/index.d.ts +2 -0
  91. package/types/shell/index.d.ts +353 -284
  92. package/utils/__tests__/grafana.test.ts +44 -0
  93. package/utils/axios.js +190 -0
  94. package/{nuxt → utils}/cookie-universal-nuxt.js +7 -6
  95. package/utils/dom.js +15 -0
  96. package/utils/grafana.js +35 -16
  97. package/{nuxt/utils.js → utils/nuxt.js} +265 -236
  98. package/utils/router.scrollBehavior.js +1 -1
  99. package/vue.config.js +30 -19
  100. package/nuxt/App.js +0 -210
  101. package/nuxt/axios.js +0 -186
  102. package/nuxt/empty.js +0 -1
  103. package/nuxt/jsonp.js +0 -82
  104. package/nuxt/loading.html +0 -39
  105. package/nuxt/mixins/fetch.client.js +0 -90
  106. package/nuxt/portal-vue.js +0 -4
  107. package/nuxt/server.js +0 -312
  108. package/nuxt/views/app.template.html +0 -9
  109. package/nuxt/views/error.html +0 -23
  110. package/plugins/dashboard-store/extensions.js +0 -22
  111. /package/{nuxt/components → components/nuxt}/nuxt-build-indicator.vue +0 -0
  112. /package/{nuxt/components → components/nuxt}/nuxt-child.js +0 -0
  113. /package/{nuxt/components → components/nuxt}/nuxt-error.vue +0 -0
  114. /package/{nuxt/components → components/nuxt}/nuxt-link.client.js +0 -0
  115. /package/{nuxt/components → components/nuxt}/nuxt-link.server.js +0 -0
  116. /package/{nuxt/components → components/nuxt}/nuxt-loading.vue +0 -0
@@ -47,11 +47,19 @@ export default {
47
47
  computed: {
48
48
  ...mapGetters(['clusterId']),
49
49
  trackStyle() {
50
- const sliderItem = this.activeItemId * 100 / this.slider.length;
51
- const width = 60 * this.slider.length;
50
+ let sliderItem = ( this.activeItemId + 1) * 100 / (this.slider.length + 2);
51
+ const width = 60 * (this.slider.length + 2);
52
+
53
+ if (this.slider.length === 1) {
54
+ sliderItem = 0;
55
+ }
52
56
 
53
57
  return `transform: translateX(-${ sliderItem }%); width: ${ width }%`;
54
58
  },
59
+
60
+ test() {
61
+ return 'test';
62
+ }
55
63
  },
56
64
 
57
65
  methods: {
@@ -64,26 +72,35 @@ export default {
64
72
  scrollSlide(i) {
65
73
  this.autoScroll = false;
66
74
  this.activeItemId = i;
67
- setTimeout(() => {
68
- this.slidePosition();
69
- }, 400);
70
75
  },
71
76
 
72
- nextPrev(item) {
77
+ nextPrev(direction) {
73
78
  this.autoScroll = false;
74
- if (item === 'next' && this.activeItemId < this.slider.length - 1) {
75
- this.activeItemId++;
76
- }
79
+ const slideTrack = document.getElementById('slide-track');
77
80
 
78
- if (item === 'prev' && this.activeItemId > 0) {
79
- this.activeItemId--;
80
- }
81
+ slideTrack.style.transition = `transform 450ms ease-in-out`;
82
+
83
+ direction !== 'prev' ? (this.activeItemId++) : (this.activeItemId--);
81
84
 
82
- this.slidePosition();
85
+ slideTrack.addEventListener('transitionend', this.slideTransition);
86
+ },
87
+
88
+ slideTransition() {
89
+ const slideTrack = document.getElementById('slide-track');
90
+ const slidesArray = this.slider.length + 2;
91
+
92
+ if (this.activeItemId === -1) {
93
+ slideTrack.style.transition = 'none';
94
+ this.activeItemId = this.slider.length - 1;
95
+ }
96
+ if (this.activeItemId === slidesArray - 2) {
97
+ slideTrack.style.transition = 'none';
98
+ this.activeItemId = 0;
99
+ }
83
100
  },
84
101
 
85
102
  autoScrollSlide() {
86
- if (this.activeItemId < this.slider.length && this.autoScroll ) {
103
+ if (this.activeItemId < (this.slider.length + 1) && this.autoScroll ) {
87
104
  this.activeItemId++;
88
105
  }
89
106
 
@@ -91,18 +108,7 @@ export default {
91
108
  this.autoScroll = false;
92
109
  this.activeItemId = 0;
93
110
  }
94
- this.slidePosition();
95
111
  },
96
-
97
- slidePosition() {
98
- if (this.activeItemId <= 1) {
99
- this.$refs.slide[this.slider.length - 1].style.left = '-93%';
100
- this.$refs.slide[0].style.left = '7%';
101
- } else {
102
- this.$refs.slide[this.slider.length - 1].style.left = '7%';
103
- this.$refs.slide[0].style.left = '107%';
104
- }
105
- }
106
112
  },
107
113
 
108
114
  beforeDestroy() {
@@ -112,14 +118,30 @@ export default {
112
118
  },
113
119
 
114
120
  mounted() {
121
+ const slideTrack = document.getElementById('slide-track');
122
+
123
+ if (this.slider.length === 1) {
124
+ // singleSlide.style = 'width: 100%; max-width: 100%';
125
+ slideTrack.style = 'transform:translateX(0%); width:100%; left:0';
126
+ } else {
127
+ const node = document.getElementById('slide0');
128
+ const clone = node.cloneNode(true);
129
+
130
+ const nodeLast = document.getElementById(`slide${ this.slider.length - 1 }`);
131
+ const cloneLast = nodeLast.cloneNode(true);
132
+
133
+ slideTrack.appendChild(clone);
134
+ slideTrack.insertBefore(cloneLast, slideTrack.children[0]);
135
+ }
136
+
115
137
  const lastSeenCluster = sessionStorage.getItem(carouselSeenStorageKey);
116
138
 
117
139
  if (lastSeenCluster !== this.clusterId) {
118
140
  // Session storage lasts until tab/window closed (retained on refresh)
119
141
  sessionStorage.setItem(carouselSeenStorageKey, this.clusterId);
120
-
121
- this.autoScrollSlideInterval = setInterval(this.autoScrollSlide, 5000);
122
142
  }
143
+
144
+ this.autoScrollSlideInterval = setInterval(this.autoScrollSlide, 5000);
123
145
  },
124
146
 
125
147
  };
@@ -127,7 +149,10 @@ export default {
127
149
  </script>
128
150
 
129
151
  <template>
130
- <div class="slider">
152
+ <div
153
+ class="slider"
154
+ :class="{'disable': sliders.length === 1}"
155
+ >
131
156
  <div
132
157
  id="slide-track"
133
158
  ref="slider"
@@ -141,6 +166,7 @@ export default {
141
166
  ref="slide"
142
167
  :key="get(slide, keyField)"
143
168
  class="slide"
169
+ :class="{'singleSlide': sliders.length === 1}"
144
170
  :href="asLink ? get(slide, linkField) : null"
145
171
  :target="get(slide, targetField)"
146
172
  :rel="rel"
@@ -155,13 +181,16 @@ export default {
155
181
  :label="slide.repoName"
156
182
  color="slider-badge mb-20"
157
183
  />
158
- <h1>{{ slide.chartNameDisplay }}</h1>
184
+ <h1>{{ slide.chartNameDisplay }} {{ i + 1 }}</h1>
159
185
  <p>{{ slide.chartDescription }}</p>
160
186
  </div>
161
187
  </div>
162
188
  </div>
163
189
  </div>
164
- <div class="controls">
190
+ <div
191
+ class="controls"
192
+ :class="{'disable': sliders.length === 1}"
193
+ >
165
194
  <div
166
195
  v-for="(slide, i) in slider"
167
196
  :key="i"
@@ -173,7 +202,7 @@ export default {
173
202
  <div
174
203
  ref="prev"
175
204
  class="prev"
176
- :class="[activeItemId === 0 ? 'disabled' : 'prev']"
205
+ :class="{'disable': sliders.length === 1}"
177
206
  @click="nextPrev('prev')"
178
207
  >
179
208
  <i class="icon icon-chevron-left icon-4x" />
@@ -181,7 +210,7 @@ export default {
181
210
  <div
182
211
  ref="next"
183
212
  class="next"
184
- :class="[activeItemId === slider.length - 1 ? 'disabled' : 'next']"
213
+ :class="{'disable': sliders.length === 1}"
185
214
  @click="nextPrev('next')"
186
215
  >
187
216
  <i class="icon icon-chevron-right icon-4x" />
@@ -197,7 +226,19 @@ export default {
197
226
  place-items: center;
198
227
  overflow: hidden;
199
228
  margin-bottom: 30px;
200
- min-width: 700px;
229
+ // min-width: 700px;
230
+
231
+ &.disable::before,
232
+ &.disable::after {
233
+ display: none;
234
+ }
235
+
236
+ &.disable:hover {
237
+ .prev,
238
+ .next {
239
+ display: none;
240
+ }
241
+ }
201
242
 
202
243
  &:hover {
203
244
  .prev,
@@ -212,6 +253,7 @@ export default {
212
253
  animation: scrolls 10s ;
213
254
  position: relative;
214
255
  transition: 1s ease-in-out;
256
+ left: 21%;
215
257
  }
216
258
 
217
259
  .slider-badge {
@@ -226,13 +268,12 @@ export default {
226
268
  position: relative;
227
269
  border: 1px solid var(--tabbed-border);
228
270
  border-radius: var(--border-radius);
229
- left: 7%;
230
271
  cursor: pointer;
231
272
 
232
- &:last-child {
233
- left: -93%;
273
+ &.singleSlide {
274
+ width: 100%;
275
+ max-width: 100%;
234
276
  }
235
-
236
277
  .slide-header {
237
278
  background: var(--default);
238
279
  width: 100%;
@@ -278,6 +319,9 @@ export default {
278
319
  .slider::before {
279
320
  left: 0;
280
321
  top: 0;
322
+ &.disable {
323
+ display: none;
324
+ }
281
325
  }
282
326
  .slider::after{
283
327
  right: -1px;
@@ -291,6 +335,10 @@ export default {
291
335
  justify-content: center;
292
336
  margin-top: 10px;
293
337
 
338
+ &.disable {
339
+ display: none;
340
+ }
341
+
294
342
  .control-item {
295
343
  width: 10px;
296
344
  height: 10px;
@@ -3,15 +3,19 @@ import { Banner } from '@components/Banner';
3
3
  import Loading from '@shell/components/Loading';
4
4
  import { mapGetters } from 'vuex';
5
5
  import { hasLeader, leaderChanges, failedProposals } from '@shell/utils/grafana';
6
+ import { CATALOG } from '@shell/config/types';
6
7
 
7
8
  export default {
8
9
  components: { Banner, Loading },
9
10
  async fetch() {
10
- const leader = await hasLeader(this.$store.dispatch, this.currentCluster.id);
11
+ const inStore = this.$store.getters['currentProduct'].inStore;
12
+ const res = await this.$store.dispatch(`${ inStore }/find`, { type: CATALOG.APP, id: 'cattle-monitoring-system/rancher-monitoring' });
13
+ const monitoringVersion = res?.currentVersion;
14
+ const leader = await hasLeader(monitoringVersion, this.$store.dispatch, this.currentCluster.id);
11
15
 
12
16
  this.hasLeader = leader ? this.t('generic.yes') : this.t('generic.no');
13
- this.leaderChanges = await leaderChanges(this.$store.dispatch, this.currentCluster.id);
14
- this.failedProposals = await failedProposals(this.$store.dispatch, this.currentCluster.id);
17
+ this.leaderChanges = await leaderChanges(monitoringVersion, this.$store.dispatch, this.currentCluster.id);
18
+ this.failedProposals = await failedProposals(monitoringVersion, this.$store.dispatch, this.currentCluster.id);
15
19
  },
16
20
  data() {
17
21
  return {
@@ -114,7 +114,6 @@ export default {
114
114
  labelKey: 'tableHeaders.role',
115
115
  value: 'roleTemplate.nameDisplay'
116
116
  },
117
- { ...AGE, value: 'createdTS' }
118
117
  ],
119
118
  loadingProjectBindings: true,
120
119
  loadingClusterBindings: true
@@ -185,7 +184,25 @@ export default {
185
184
  };
186
185
  });
187
186
 
188
- return [...fakeRows, ...this.filteredProjectRoleTemplateBindings];
187
+ // We need to group each of the TemplateRoleBindings by the user + project
188
+ const userRoles = [...fakeRows, ...this.filteredProjectRoleTemplateBindings].reduce((rows, curr) => {
189
+ const { userId, roleTemplate, projectId } = curr;
190
+
191
+ const userKey = userId + projectId;
192
+
193
+ if (!rows[userKey] && userId ) {
194
+ rows[userKey] = curr;
195
+ rows[userKey].allRoles = [];
196
+ }
197
+
198
+ if (roleTemplate && userId) {
199
+ rows[userKey].allRoles.push(curr.roleTemplate);
200
+ }
201
+
202
+ return rows;
203
+ }, {});
204
+
205
+ return Object.values(userRoles);
189
206
  },
190
207
  canManageMembers() {
191
208
  return canViewClusterPermissionsEditor(this.$store);
@@ -223,6 +240,26 @@ export default {
223
240
  modalSticky: true
224
241
  });
225
242
  },
243
+
244
+ getProjectRoleBinding(row, role) {
245
+ // Each row is a combination of user and project
246
+ // So find the specfic roleBindingTemplate corresponding to the specific role + project
247
+ return this.projectRoleTemplateBindings.find(r => r.roleTemplateId === role.id && r.userId === row.userId);
248
+ },
249
+
250
+ async removeRole(row, role, event) {
251
+ const resource = this.getProjectRoleBinding(row, role);
252
+
253
+ await resource.promptRemove();
254
+ },
255
+
256
+ viewRoleInAPI(row, role) {
257
+ const resource = this.getProjectRoleBinding(row, role);
258
+
259
+ if (resource?.canViewInApi) {
260
+ resource.viewInApi();
261
+ }
262
+ },
226
263
  slotName(project) {
227
264
  return `main-row:${ project.id }`;
228
265
  },
@@ -249,7 +286,7 @@ export default {
249
286
  <Tabbed>
250
287
  <Tab
251
288
  name="cluster-membership"
252
- label="Cluster Membership"
289
+ :label="t('members.clusterMemebership')"
253
290
  >
254
291
  <div
255
292
  v-if="canEditClusterMembers"
@@ -266,7 +303,8 @@ export default {
266
303
  :schema="schema"
267
304
  :headers="headers"
268
305
  :rows="filteredClusterRoleTemplateBindings"
269
- :groupable="false"
306
+ :groupable="true"
307
+ :show-grouping="true"
270
308
  :namespaced="false"
271
309
  :loading="$fetchState.pending || !currentCluster || loadingClusterBindings"
272
310
  sub-search="subSearch"
@@ -276,13 +314,15 @@ export default {
276
314
  <Tab
277
315
  v-if="canManageProjectMembers"
278
316
  name="project-membership"
279
- label="Project Membership"
317
+ :label="t('members.projectMembership')"
280
318
  >
281
319
  <SortableTable
282
320
  group-by="projectId"
283
321
  :loading="$fetchState.pending || !currentCluster || loadingProjectBindings"
284
322
  :rows="rowsWithFakeProjects"
285
323
  :headers="projectRoleTemplateColumns"
324
+ :table-actions="false"
325
+ :row-actions="false"
286
326
  >
287
327
  <template #group-by="group">
288
328
  <div class="group-bar">
@@ -307,6 +347,31 @@ export default {
307
347
  </div>
308
348
  </div>
309
349
  </template>
350
+ <template
351
+ #cell:role="{row}"
352
+ >
353
+ <span
354
+ v-for="(role, j) in row.allRoles"
355
+ :key="j"
356
+
357
+ ref="value"
358
+ :data-testid="`role-value-${j}`"
359
+ class="role"
360
+ >
361
+ <span
362
+ class="role-value"
363
+ :class="{'text-link-enabled' : row.canViewInApi}"
364
+ @click="viewRoleInAPI(row, role)"
365
+ >
366
+ {{ role.nameDisplay }}
367
+ </span>
368
+ <i
369
+ class="icon icon-close"
370
+ :data-testid="`role-values-close-${j}`"
371
+ @click="removeRole(row, role, $event)"
372
+ />
373
+ </span>
374
+ </template>
310
375
  <template
311
376
  v-for="project in projectsWithoutRoles"
312
377
  v-slot:[slotName(project)]
@@ -330,6 +395,36 @@ export default {
330
395
  </template>
331
396
 
332
397
  <style lang='scss' scoped>
398
+
399
+ .role {
400
+ align-items: center;
401
+ background-color: rgba(0, 0, 0, 0.05);
402
+ border: 1px solid var(--header-border);
403
+ border-radius: 5px;
404
+ color: var(--tag-text);
405
+ line-height: 20px;
406
+ padding: 2px 5px;
407
+ white-space: nowrap;
408
+ display: inline-flex;
409
+ margin-right: 3px;
410
+ }
411
+
412
+ .role-value {
413
+ &.text-link-enabled {
414
+ cursor: pointer;
415
+ &:hover {
416
+ color: var(--primary);
417
+ }
418
+ }
419
+ + .icon-close {
420
+ margin-left: 3px;
421
+ cursor: pointer;
422
+ &:hover {
423
+ color: var(--primary);
424
+ }
425
+ }
426
+ }
427
+
333
428
  .project-members {
334
429
  & ::v-deep .group-bar{
335
430
  display: flex;
@@ -4,7 +4,7 @@ import ResourceTable from '@shell/components/ResourceTable';
4
4
  import { STATE, AGE, NAME } from '@shell/config/table-headers';
5
5
  import { uniq } from '@shell/utils/array';
6
6
  import { MANAGEMENT, NAMESPACE, VIRTUAL_TYPES } from '@shell/config/types';
7
- import { PROJECT_ID } from '@shell/config/query-params';
7
+ import { PROJECT_ID, FLAT_VIEW } from '@shell/config/query-params';
8
8
  import Masthead from '@shell/components/ResourceList/Masthead';
9
9
  import { mapPref, GROUP_RESOURCES, ALL_NAMESPACES } from '@shell/store/prefs';
10
10
  import MoveModal from '@shell/components/MoveModal';
@@ -236,6 +236,9 @@ export default {
236
236
 
237
237
  notInProjectKey() {
238
238
  return this.$store.getters['i18n/t']('resourceTable.groupLabel.notInAProject');
239
+ },
240
+ showCreateNsButton() {
241
+ return this.groupPreference !== 'namespace';
239
242
  }
240
243
  },
241
244
  methods: {
@@ -284,6 +287,21 @@ export default {
284
287
 
285
288
  return location;
286
289
  },
290
+
291
+ createNamespaceLocationFlatList() {
292
+ const location = this.createNamespaceLocationOverride ? { ...this.createNamespaceLocationOverride } : {
293
+ name: 'c-cluster-product-resource-create',
294
+ params: {
295
+ product: this.$store.getters['currentProduct']?.name,
296
+ resource: NAMESPACE
297
+ },
298
+ };
299
+
300
+ location.query = { [FLAT_VIEW]: true };
301
+
302
+ return location;
303
+ },
304
+
287
305
  showProjectAction(event, group) {
288
306
  const project = group.rows[0].project;
289
307
 
@@ -343,7 +361,19 @@ export default {
343
361
  :show-incremental-loading-indicator="showIncrementalLoadingIndicator"
344
362
  :load-resources="loadResources"
345
363
  :load-indeterminate="loadIndeterminate"
346
- />
364
+ >
365
+ <template
366
+ v-if="showCreateNsButton"
367
+ slot="extraActions"
368
+ >
369
+ <n-link
370
+ :to="createNamespaceLocationFlatList()"
371
+ class="btn role-primary mr-10"
372
+ >
373
+ {{ t('projectNamespaces.createNamespace') }}
374
+ </n-link>
375
+ </template>
376
+ </Masthead>
347
377
  <ResourceTable
348
378
  ref="table"
349
379
  class="table"
@@ -2,6 +2,7 @@
2
2
  import Loading from '@shell/components/Loading';
3
3
  import { Banner } from '@components/Banner';
4
4
  import { computeDashboardUrl } from '@shell/utils/grafana';
5
+ import { CATALOG } from '@shell/config/types';
5
6
 
6
7
  export default {
7
8
  components: { Banner, Loading },
@@ -31,9 +32,15 @@ export default {
31
32
  default: 'dark'
32
33
  }
33
34
  },
35
+ async fetch() {
36
+ const inStore = this.$store.getters['currentProduct'].inStore;
37
+ const res = await this.$store.dispatch(`${ inStore }/find`, { type: CATALOG.APP, id: 'cattle-monitoring-system/rancher-monitoring' });
38
+
39
+ this.monitoringVersion = res?.currentVersion;
40
+ },
34
41
  data() {
35
42
  return {
36
- loading: false, error: false, interval: null, initialUrl: this.computeUrl(), errorTimer: null
43
+ loading: false, error: false, interval: null, initialUrl: this.computeUrl(), errorTimer: null, monitoringVersion: null
37
44
  };
38
45
  },
39
46
  computed: {
@@ -131,7 +138,7 @@ export default {
131
138
  const clusterId = this.$store.getters['currentCluster'].id;
132
139
  const params = this.computeParams();
133
140
 
134
- return computeDashboardUrl(embedUrl, clusterId, params);
141
+ return computeDashboardUrl(this.monitoringVersion, embedUrl, clusterId, params);
135
142
  },
136
143
  computeParams() {
137
144
  const params = {};
@@ -7,7 +7,6 @@ import { removeObject } from '@shell/utils/array';
7
7
  import { Checkbox } from '@components/Form/Checkbox';
8
8
  import AsyncButton, { ASYNC_BUTTON_STATES } from '@shell/components/AsyncButton';
9
9
  import ActionDropdown from '@shell/components/ActionDropdown';
10
- import $ from 'jquery';
11
10
  import throttle from 'lodash/throttle';
12
11
  import debounce from 'lodash/debounce';
13
12
  import THead from './THead';
@@ -19,6 +18,8 @@ import grouping from './grouping';
19
18
  import actions from './actions';
20
19
  import AdvancedFiltering from './advanced-filtering';
21
20
  import LabeledSelect from '@shell/components/form/LabeledSelect';
21
+ import { getParent } from '@shell/utils/dom';
22
+
22
23
  // Uncomment for table performance debugging
23
24
  // import tableDebug from './debug';
24
25
 
@@ -340,10 +341,10 @@ export default {
340
341
  }, 200);
341
342
 
342
343
  // Add scroll listener to the main element
343
- const $main = $('main');
344
+ const $main = document.querySelector('main');
344
345
 
345
346
  this._onScroll = this.onScroll.bind(this);
346
- $main.on('scroll', this._onScroll);
347
+ $main?.addEventListener('scroll', this._onScroll);
347
348
  },
348
349
 
349
350
  beforeDestroy() {
@@ -354,9 +355,9 @@ export default {
354
355
  clearTimeout(this._delayedColumnsTimer);
355
356
  clearTimeout(this.manualRefreshTimer);
356
357
 
357
- const $main = $('main');
358
+ const $main = document.querySelector('main');
358
359
 
359
- $main.off('scroll', this._onScroll);
360
+ $main?.removeEventListener('scroll', this._onScroll);
360
361
  },
361
362
 
362
363
  watch: {
@@ -817,13 +818,12 @@ export default {
817
818
  },
818
819
 
819
820
  nearestCheckbox() {
820
- const $cur = $(document.activeElement).closest('tr.main-row').find('.checkbox-custom');
821
-
822
- return $cur[0];
821
+ return document.activeElement.closest('tr.main-row')?.querySelector('.checkbox-custom');
823
822
  },
824
823
 
825
824
  focusAdjacent(next = true) {
826
- const all = $('.checkbox-custom', this.$el).toArray();
825
+ const all = Array.from(this.$el.querySelectorAll('.checkbox-custom'));
826
+
827
827
  const cur = this.nearestCheckbox();
828
828
  let idx = -1;
829
829
 
@@ -837,10 +837,14 @@ export default {
837
837
 
838
838
  if ( idx < 1 ) { // Don't go up to the check all button
839
839
  idx = 1;
840
+
841
+ return null;
840
842
  }
841
843
 
842
844
  if ( idx >= all.length ) {
843
845
  idx = all.length - 1;
846
+
847
+ return null;
844
848
  }
845
849
 
846
850
  if ( all[idx] ) {
@@ -852,14 +856,22 @@ export default {
852
856
 
853
857
  focusNext: throttle(function(event, more = false) {
854
858
  const elem = this.focusAdjacent(true);
855
- const row = $(elem).parents('tr');
859
+ const row = getParent(elem, 'tr');
860
+
861
+ if (row?.classList.contains('row-selected')) {
862
+ return;
863
+ }
856
864
 
857
865
  this.keySelectRow(row, more);
858
866
  }, 50),
859
867
 
860
868
  focusPrevious: throttle(function(event, more = false) {
861
869
  const elem = this.focusAdjacent(false);
862
- const row = $(elem).parents('tr');
870
+ const row = getParent(elem, 'tr');
871
+
872
+ if (row?.classList.contains('row-selected')) {
873
+ return;
874
+ }
863
875
 
864
876
  this.keySelectRow(row, more);
865
877
  }, 50),