@rancher/shell 3.0.1-rc.4 → 3.0.1

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 (64) hide show
  1. package/assets/data/aws-regions.json +1 -0
  2. package/assets/styles/base/_basic.scss +5 -0
  3. package/assets/styles/base/_mixins.scss +8 -0
  4. package/assets/styles/global/_button.scss +5 -0
  5. package/assets/styles/themes/_dark.scss +2 -0
  6. package/assets/styles/themes/_light.scss +2 -0
  7. package/assets/translations/en-us.yaml +27 -11
  8. package/assets/translations/zh-hans.yaml +1 -1
  9. package/chart/monitoring/StorageClassSelector.vue +1 -1
  10. package/components/AssignTo.vue +1 -0
  11. package/components/AsyncButton.vue +1 -0
  12. package/components/BackLink.vue +8 -2
  13. package/components/PaginatedResourceTable.vue +135 -0
  14. package/components/ResourceList/index.vue +0 -1
  15. package/components/ResourceTable.vue +6 -1
  16. package/components/SortableTable/index.vue +8 -6
  17. package/components/Tabbed/index.vue +35 -2
  18. package/components/form/ResourceLabeledSelect.vue +2 -2
  19. package/components/form/ResourceTabs/index.vue +0 -23
  20. package/components/form/Taints.vue +1 -1
  21. package/components/nav/TopLevelMenu.helper.ts +546 -0
  22. package/components/nav/TopLevelMenu.vue +124 -159
  23. package/components/nav/__tests__/TopLevelMenu.test.ts +338 -326
  24. package/config/pagination-table-headers.js +4 -4
  25. package/config/product/explorer.js +2 -0
  26. package/config/router/routes.js +1 -1
  27. package/config/settings.ts +13 -1
  28. package/core/plugin.ts +8 -1
  29. package/core/types-provisioning.ts +5 -0
  30. package/core/types.ts +26 -1
  31. package/dialog/DrainNode.vue +6 -6
  32. package/edit/catalog.cattle.io.clusterrepo.vue +95 -52
  33. package/edit/provisioning.cattle.io.cluster/index.vue +8 -3
  34. package/list/node.vue +8 -5
  35. package/mixins/resource-fetch-api-pagination.js +40 -5
  36. package/mixins/resource-fetch.js +48 -5
  37. package/models/management.cattle.io.nodepool.js +5 -4
  38. package/models/provisioning.cattle.io.cluster.js +2 -10
  39. package/package.json +6 -6
  40. package/pages/about.vue +22 -0
  41. package/pages/c/_cluster/explorer/__tests__/index.test.ts +36 -24
  42. package/pages/c/_cluster/explorer/index.vue +100 -59
  43. package/pages/home.vue +308 -123
  44. package/plugins/dashboard-store/__tests__/mutations.test.ts +2 -0
  45. package/plugins/dashboard-store/actions.js +29 -19
  46. package/plugins/dashboard-store/getters.js +5 -2
  47. package/plugins/dashboard-store/mutations.js +4 -2
  48. package/plugins/steve/__tests__/mutations.test.ts +2 -1
  49. package/plugins/steve/steve-pagination-utils.ts +25 -2
  50. package/plugins/steve/subscribe.js +22 -8
  51. package/scripts/extension/parse-tag-name +2 -0
  52. package/scripts/test-plugins-build.sh +1 -0
  53. package/store/index.js +31 -9
  54. package/tsconfig.json +7 -1
  55. package/types/resources/settings.d.ts +1 -1
  56. package/types/shell/index.d.ts +1107 -1276
  57. package/types/store/dashboard-store.types.ts +4 -0
  58. package/types/store/pagination.types.ts +13 -0
  59. package/types/store/vuex.d.ts +8 -0
  60. package/types/vue-shim.d.ts +6 -31
  61. package/utils/cluster.js +92 -1
  62. package/utils/pagination-utils.ts +17 -8
  63. package/utils/pagination-wrapper.ts +70 -0
  64. package/utils/uiplugins.ts +18 -4
package/pages/home.vue CHANGED
@@ -1,12 +1,13 @@
1
- <script>
1
+ <script lang="ts">
2
+ import { defineComponent } from 'vue';
2
3
  import { mapPref, AFTER_LOGIN_ROUTE, READ_WHATS_NEW, HIDE_HOME_PAGE_CARDS } from '@shell/store/prefs';
3
4
  import { Banner } from '@components/Banner';
4
- import BannerGraphic from '@shell/components/BannerGraphic';
5
- import IndentedPanel from '@shell/components/IndentedPanel';
6
- import SortableTable from '@shell/components/SortableTable';
5
+ import BannerGraphic from '@shell/components/BannerGraphic.vue';
6
+ import IndentedPanel from '@shell/components/IndentedPanel.vue';
7
+ import PaginatedResourceTable, { FetchPageSecondaryResourcesOpts, FetchSecondaryResourcesOpts } from '@shell/components/PaginatedResourceTable.vue';
7
8
  import { BadgeState } from '@components/BadgeState';
8
- import CommunityLinks from '@shell/components/CommunityLinks';
9
- import SingleClusterInfo from '@shell/components/SingleClusterInfo';
9
+ import CommunityLinks from '@shell/components/CommunityLinks.vue';
10
+ import SingleClusterInfo from '@shell/components/SingleClusterInfo.vue';
10
11
  import { mapGetters, mapState } from 'vuex';
11
12
  import { MANAGEMENT, CAPI } from '@shell/config/types';
12
13
  import { NAME as MANAGER } from '@shell/config/product/manager';
@@ -18,19 +19,24 @@ import PageHeaderActions from '@shell/mixins/page-actions';
18
19
  import { getVendor } from '@shell/config/private-label';
19
20
  import { mapFeature, MULTI_CLUSTER } from '@shell/store/features';
20
21
  import { BLANK_CLUSTER } from '@shell/store/store-types.js';
21
- import { filterOnlyKubernetesClusters, filterHiddenLocalCluster } from '@shell/utils/cluster';
22
- import TabTitle from '@shell/components/TabTitle';
22
+ import { filterHiddenLocalCluster, filterOnlyKubernetesClusters, paginationFilterClusters } from '@shell/utils/cluster';
23
+ import TabTitle from '@shell/components/TabTitle.vue';
24
+ import { ActionFindPageArgs } from '@shell/types/store/dashboard-store.types';
23
25
 
24
26
  import { RESET_CARDS_ACTION, SET_LOGIN_ACTION } from '@shell/config/page-actions';
27
+ import { STEVE_NAME_COL, STEVE_STATE_COL } from '@shell/config/pagination-table-headers';
28
+ import { PaginationParamFilter, FilterArgs, PaginationFilterField, PaginationArgs } from '@shell/types/store/pagination.types';
29
+ import ProvCluster from '@shell/models/provisioning.cattle.io.cluster';
30
+ import { sameContents } from 'utils/array';
25
31
 
26
- export default {
32
+ export default defineComponent({
27
33
  name: 'Home',
28
34
  layout: 'home',
29
35
  components: {
30
36
  Banner,
31
37
  BannerGraphic,
32
38
  IndentedPanel,
33
- SortableTable,
39
+ PaginatedResourceTable,
34
40
  BadgeState,
35
41
  CommunityLinks,
36
42
  SingleClusterInfo,
@@ -39,102 +45,51 @@ export default {
39
45
 
40
46
  mixins: [PageHeaderActions],
41
47
 
42
- fetch() {
43
- if ( this.$store.getters['management/schemaFor'](CAPI.RANCHER_CLUSTER) ) {
44
- this.$store.dispatch('management/findAll', { type: CAPI.RANCHER_CLUSTER });
45
- }
46
-
47
- if ( this.$store.getters['management/schemaFor'](MANAGEMENT.CLUSTER) ) {
48
- this.$store.dispatch('management/findAll', { type: MANAGEMENT.CLUSTER });
49
- }
50
-
51
- if ( this.$store.getters['management/canList'](CAPI.MACHINE) ) {
52
- this.$store.dispatch('management/findAll', { type: CAPI.MACHINE });
53
- }
54
-
55
- if ( this.$store.getters['management/canList'](MANAGEMENT.NODE) ) {
56
- this.$store.dispatch('management/findAll', { type: MANAGEMENT.NODE });
57
- }
58
-
59
- // We need to fetch node pools and node templates in order to correctly show the provider for RKE1 clusters
60
- if ( this.$store.getters['management/canList'](MANAGEMENT.NODE_POOL) ) {
61
- this.$store.dispatch('management/findAll', { type: MANAGEMENT.NODE_POOL });
62
- }
63
-
64
- if ( this.$store.getters['management/canList'](MANAGEMENT.NODE_TEMPLATE) ) {
65
- this.$store.dispatch('management/findAll', { type: MANAGEMENT.NODE_TEMPLATE });
66
- }
67
- },
68
-
69
48
  data() {
70
- const fullVersion = getVersionInfo(this.$store).fullVersion;
71
- // Page actions don't change on the Home Page
72
- const pageActions = [
73
- {
74
- labelKey: 'nav.header.setLoginPage',
75
- action: SET_LOGIN_ACTION
76
- },
77
- { separator: true },
78
- {
79
- labelKey: 'nav.header.restoreCards',
80
- action: RESET_CARDS_ACTION
81
- },
82
- ];
83
-
84
49
  return {
85
50
  HIDE_HOME_PAGE_CARDS,
86
- fullVersion,
87
- pageActions,
51
+ fullVersion: getVersionInfo(this.$store).fullVersion,
52
+ // Page actions don't change on the Home Page
53
+ pageActions: [
54
+ {
55
+ labelKey: 'nav.header.setLoginPage',
56
+ action: SET_LOGIN_ACTION
57
+ },
58
+ { separator: true },
59
+ {
60
+ labelKey: 'nav.header.restoreCards',
61
+ action: RESET_CARDS_ACTION
62
+ },
63
+ ],
88
64
  vendor: getVendor(),
89
- };
90
- },
91
-
92
- computed: {
93
- ...mapState(['managementReady']),
94
- ...mapGetters(['currentCluster', 'defaultClusterId', 'releaseNotesUrl']),
95
- mcm: mapFeature(MULTI_CLUSTER),
96
-
97
- provClusters() {
98
- return this.$store.getters['management/all'](CAPI.RANCHER_CLUSTER);
99
- },
100
65
 
101
- // User can go to Cluster Management if they can see the cluster schema
102
- canManageClusters() {
103
- const schema = this.$store.getters['management/schemaFor'](CAPI.RANCHER_CLUSTER);
66
+ provClusterSchema: this.$store.getters['management/schemaFor'](CAPI.RANCHER_CLUSTER),
104
67
 
105
- return !!schema;
106
- },
68
+ canViewMgmtClusters: !!this.$store.getters['management/schemaFor'](MANAGEMENT.CLUSTER),
69
+ canViewMachine: !!this.$store.getters['management/canList'](CAPI.MACHINE),
70
+ canViewMgmtNodes: !!this.$store.getters['management/canList'](MANAGEMENT.NODE),
71
+ canViewMgmtPools: !!this.$store.getters['management/canList'](MANAGEMENT.NODE_POOL),
72
+ canViewMgmtTemplates: !!this.$store.getters['management/canList'](MANAGEMENT.NODE_TEMPLATE),
107
73
 
108
- canCreateCluster() {
109
- const schema = this.$store.getters['management/schemaFor'](CAPI.RANCHER_CLUSTER);
110
-
111
- return !!schema?.collectionMethods.find((x) => x.toLowerCase() === 'post');
112
- },
113
-
114
- manageLocation() {
115
- return {
74
+ manageLocation: {
116
75
  name: 'c-cluster-product-resource',
117
76
  params: {
118
77
  product: MANAGER,
119
78
  cluster: BLANK_CLUSTER,
120
79
  resource: CAPI.RANCHER_CLUSTER
121
80
  },
122
- };
123
- },
81
+ },
124
82
 
125
- createLocation() {
126
- return {
83
+ createLocation: {
127
84
  name: 'c-cluster-product-resource-create',
128
85
  params: {
129
86
  product: MANAGER,
130
87
  cluster: BLANK_CLUSTER,
131
88
  resource: CAPI.RANCHER_CLUSTER
132
89
  },
133
- };
134
- },
90
+ },
135
91
 
136
- importLocation() {
137
- return {
92
+ importLocation: {
138
93
  name: 'c-cluster-product-resource-create',
139
94
  params: {
140
95
  product: MANAGER,
@@ -142,22 +97,9 @@ export default {
142
97
  resource: CAPI.RANCHER_CLUSTER
143
98
  },
144
99
  query: { [MODE]: _IMPORT }
145
- };
146
- },
147
-
148
- afterLoginRoute: mapPref(AFTER_LOGIN_ROUTE),
149
- homePageCards: mapPref(HIDE_HOME_PAGE_CARDS),
150
-
151
- readWhatsNewAlready() {
152
- return readReleaseNotes(this.$store);
153
- },
154
-
155
- showSetLoginBanner() {
156
- return this.homePageCards?.setLoginPage;
157
- },
100
+ },
158
101
 
159
- clusterHeaders() {
160
- return [
102
+ headers: [
161
103
  STATE,
162
104
  {
163
105
  name: 'name',
@@ -165,7 +107,7 @@ export default {
165
107
  value: 'nameDisplay',
166
108
  sort: ['nameSort'],
167
109
  canBeVariable: true,
168
- getValue: (row) => row.mgmt?.nameDisplay
110
+ getValue: (row: ProvCluster) => row.mgmt?.nameDisplay
169
111
  },
170
112
  {
171
113
  label: this.t('landing.clusters.provider'),
@@ -185,14 +127,12 @@ export default {
185
127
  value: '',
186
128
  name: 'cpu',
187
129
  sort: ['status.allocatable.cpu', 'status.available.cpu']
188
-
189
130
  },
190
131
  {
191
132
  label: this.t('tableHeaders.memory'),
192
133
  value: '',
193
134
  name: 'memory',
194
135
  sort: ['status.allocatable.memory', 'status.available.memory']
195
-
196
136
  },
197
137
  {
198
138
  label: this.t('tableHeaders.pods'),
@@ -206,12 +146,80 @@ export default {
206
146
  // name: 'explorer',
207
147
  // label: this.t('landing.clusters.explorer')
208
148
  // }
209
- ];
149
+ ],
150
+
151
+ paginationHeaders: [
152
+ STEVE_STATE_COL,
153
+ // https://github.com/rancher/dashboard/issues/12890 BUG - rke1 cluster's prov cluster metadata.name is the mgmt cluster id rather than true name
154
+ {
155
+ ...STEVE_NAME_COL,
156
+ canBeVariable: true,
157
+ getValue: (row: ProvCluster) => row.metadata?.name
158
+ },
159
+ {
160
+ label: this.t('landing.clusters.provider'),
161
+ subLabel: this.t('landing.clusters.distro'),
162
+ value: 'mgmt.status.provider',
163
+ name: 'Provider',
164
+ sort: false,
165
+ search: false,
166
+ formatter: 'ClusterProvider'
167
+ },
168
+ {
169
+ label: this.t('landing.clusters.kubernetesVersion'),
170
+ subLabel: this.t('landing.clusters.architecture'),
171
+ name: 'kubernetesVersion',
172
+ sort: false,
173
+ search: false,
174
+ },
175
+ {
176
+ label: this.t('tableHeaders.cpu'),
177
+ value: '',
178
+ name: 'cpu',
179
+ sort: false,
180
+ search: false,
181
+ },
182
+ {
183
+ label: this.t('tableHeaders.memory'),
184
+ value: '',
185
+ name: 'memory',
186
+ sort: false,
187
+ search: false,
188
+ },
189
+ {
190
+ label: this.t('tableHeaders.pods'),
191
+ name: 'pods',
192
+ value: '',
193
+ sort: false,
194
+ search: false,
195
+ formatter: 'PodsUsage',
196
+ delayLoading: true
197
+ },
198
+ ],
199
+
200
+ clusterCount: 0,
201
+ };
202
+ },
203
+
204
+ computed: {
205
+ ...mapState(['managementReady']),
206
+ ...mapGetters(['currentCluster', 'defaultClusterId', 'releaseNotesUrl']),
207
+ mcm: mapFeature(MULTI_CLUSTER),
208
+
209
+ canCreateCluster() {
210
+ return !!this.provClusterSchema?.collectionMethods.find((x: string) => x.toLowerCase() === 'post');
210
211
  },
211
212
 
212
- kubeClusters() {
213
- return filterHiddenLocalCluster(filterOnlyKubernetesClusters(this.provClusters || [], this.$store), this.$store);
214
- }
213
+ afterLoginRoute: mapPref(AFTER_LOGIN_ROUTE),
214
+ homePageCards: mapPref(HIDE_HOME_PAGE_CARDS),
215
+
216
+ readWhatsNewAlready() {
217
+ return readReleaseNotes(this.$store);
218
+ },
219
+
220
+ showSetLoginBanner() {
221
+ return this.homePageCards?.setLoginPage;
222
+ },
215
223
  },
216
224
 
217
225
  async created() {
@@ -229,11 +237,131 @@ export default {
229
237
  },
230
238
 
231
239
  methods: {
240
+ /**
241
+ * Of type FetchSecondaryResources
242
+ */
243
+ fetchSecondaryResources(opts: FetchSecondaryResourcesOpts): Promise<any> {
244
+ if (opts.canPaginate) {
245
+ return Promise.resolve({});
246
+ }
247
+
248
+ if ( this.canViewMgmtClusters ) {
249
+ this.$store.dispatch('management/findAll', { type: MANAGEMENT.CLUSTER });
250
+ }
251
+
252
+ if ( this.canViewMachine ) {
253
+ this.$store.dispatch('management/findAll', { type: CAPI.MACHINE });
254
+ }
255
+
256
+ if ( this.canViewMgmtNodes ) {
257
+ this.$store.dispatch('management/findAll', { type: MANAGEMENT.NODE });
258
+ }
259
+
260
+ // We need to fetch node pools and node templates in order to correctly show the provider for RKE1 clusters
261
+ if ( this.canViewMgmtPools ) {
262
+ this.$store.dispatch('management/findAll', { type: MANAGEMENT.NODE_POOL });
263
+ }
264
+
265
+ if ( this.canViewMgmtTemplates ) {
266
+ this.$store.dispatch('management/findAll', { type: MANAGEMENT.NODE_TEMPLATE });
267
+ }
268
+
269
+ return Promise.resolve({});
270
+ },
271
+
272
+ async fetchPageSecondaryResources({
273
+ canPaginate, force, page, pagResult
274
+ }: FetchPageSecondaryResourcesOpts) {
275
+ if (!canPaginate || !page?.length) {
276
+ this.clusterCount = 0;
277
+
278
+ return;
279
+ }
280
+
281
+ this.clusterCount = pagResult.count;
282
+
283
+ if ( this.canViewMgmtClusters ) {
284
+ const opt: ActionFindPageArgs = {
285
+ force,
286
+ pagination: new FilterArgs({
287
+ filters: PaginationParamFilter.createMultipleFields(page.map((r: any) => new PaginationFilterField({
288
+ field: 'id',
289
+ value: r.mgmtClusterId
290
+ }))),
291
+ })
292
+ };
293
+
294
+ this.$store.dispatch(`management/findPage`, { type: MANAGEMENT.CLUSTER, opt });
295
+ }
296
+
297
+ if ( this.canViewMachine ) {
298
+ const opt: ActionFindPageArgs = {
299
+ force,
300
+ pagination: new FilterArgs({
301
+ filters: PaginationParamFilter.createMultipleFields(page.map((r: any) => new PaginationFilterField({
302
+ field: 'spec.clusterName',
303
+ value: r.metadata.name
304
+ }))),
305
+ })
306
+ };
307
+
308
+ await this.$store.dispatch(`management/findPage`, { type: CAPI.MACHINE, opt });
309
+ }
310
+
311
+ if ( this.canViewMgmtNodes ) {
312
+ const opt: ActionFindPageArgs = {
313
+ force,
314
+ pagination: new FilterArgs({
315
+ filters: PaginationParamFilter.createMultipleFields(page.map((r: any) => new PaginationFilterField({
316
+ field: 'id',
317
+ value: r.mgmtClusterId,
318
+ exact: false,
319
+ }))),
320
+ })
321
+ };
322
+
323
+ this.$store.dispatch(`management/findPage`, { type: MANAGEMENT.NODE, opt });
324
+ }
325
+
326
+ // We need to fetch node pools and node templates in order to correctly show the provider for RKE1 clusters
327
+ if ( this.canViewMgmtPools && this.canViewMgmtTemplates ) {
328
+ const nodePoolFilters = PaginationParamFilter.createMultipleFields(page
329
+ .filter((p: any) => p.status?.clusterName)
330
+ .map((r: any) => new PaginationFilterField({
331
+ field: 'spec.clusterName',
332
+ value: r.status?.clusterName
333
+ })));
334
+ const nodePools = await this.$store.dispatch(`management/findPage`, {
335
+ type: MANAGEMENT.NODE_POOL,
336
+ opt: {
337
+ force,
338
+ pagination: new FilterArgs({ filters: nodePoolFilters })
339
+ }
340
+ });
341
+
342
+ const templateOpt = PaginationParamFilter.createMultipleFields(nodePools
343
+ .filter((np: any) => !!np.nodeTemplateId)
344
+ .map((np: any) => new PaginationFilterField({
345
+ field: 'id',
346
+ value: np.nodeTemplateId,
347
+ exact: true,
348
+ })));
349
+
350
+ this.$store.dispatch(`management/findPage`, {
351
+ type: MANAGEMENT.NODE_TEMPLATE,
352
+ opt: {
353
+ force,
354
+ pagination: new FilterArgs({ filters: templateOpt })
355
+ }
356
+ });
357
+ }
358
+ },
359
+
232
360
  /**
233
361
  * Define actions for each navigation link
234
362
  * @param {*} action
235
363
  */
236
- handlePageAction(action) {
364
+ handlePageAction(action: any) {
237
365
  switch (action.action) {
238
366
  case RESET_CARDS_ACTION:
239
367
  this.resetCards();
@@ -247,21 +375,22 @@ export default {
247
375
  }
248
376
  },
249
377
 
250
- cpuUsed(cluster) {
378
+ cpuUsed(cluster: any) {
251
379
  return parseSi(cluster.status.requested?.cpu);
252
380
  },
253
381
 
254
- cpuAllocatable(cluster) {
382
+ cpuAllocatable(cluster: any) {
255
383
  return parseSi(cluster.status.allocatable?.cpu);
256
384
  },
257
- memoryAllocatable(cluster) {
385
+
386
+ memoryAllocatable(cluster: any) {
258
387
  const parsedAllocatable = (parseSi(cluster.status.allocatable?.memory) || 0).toString();
259
388
  const format = createMemoryFormat(parsedAllocatable);
260
389
 
261
390
  return formatSi(parsedAllocatable, format);
262
391
  },
263
392
 
264
- memoryReserved(cluster) {
393
+ memoryReserved(cluster: any) {
265
394
  const memValues = createMemoryValues(cluster?.status?.allocatable?.memory, cluster?.status?.requested?.memory);
266
395
 
267
396
  return `${ memValues.useful }/${ memValues.total } ${ memValues.units }`;
@@ -294,11 +423,55 @@ export default {
294
423
  if (retry === 0 && res?.type === 'error' && res?.status === 500) {
295
424
  await this.closeSetLoginBanner(retry + 1);
296
425
  }
426
+ },
427
+
428
+ /**
429
+ * Filter out hidden clusters from list of all clusters
430
+ */
431
+ filterRowsLocal(rows: any[]) {
432
+ return filterHiddenLocalCluster(filterOnlyKubernetesClusters(rows || [], this.$store), this.$store);
433
+ },
434
+
435
+ /**
436
+ * Filter out hidden clusters via api
437
+ */
438
+ filterRowsApi(pagination: PaginationArgs): PaginationArgs {
439
+ if (!pagination.filters) {
440
+ pagination.filters = [];
441
+ }
442
+
443
+ const existingFilters = pagination.filters;
444
+ const requiredFilters = paginationFilterClusters(this.$store, false);
445
+
446
+ for (let i = 0; i < requiredFilters.length; i++) {
447
+ let found = false;
448
+ const required = requiredFilters[i];
449
+
450
+ for (let j = 0; j < existingFilters.length; j++) {
451
+ const existing = existingFilters[j];
452
+
453
+ if (
454
+ required.fields.length === existing.fields.length &&
455
+ sameContents(required.fields.map((e) => e.field), existing.fields.map((e) => e.field))
456
+ ) {
457
+ Object.assign(existing, required);
458
+ found = true;
459
+ break;
460
+ }
461
+ }
462
+
463
+ if (!found) {
464
+ pagination.filters.push(required);
465
+ }
466
+ }
467
+
468
+ return pagination;
297
469
  }
298
470
  }
299
- };
471
+ });
300
472
 
301
473
  </script>
474
+
302
475
  <template>
303
476
  <div
304
477
  v-if="managementReady"
@@ -369,14 +542,24 @@ export default {
369
542
  v-if="mcm"
370
543
  class="col span-12"
371
544
  >
372
- <SortableTable
545
+ <PaginatedResourceTable
546
+ v-if="provClusterSchema"
547
+ :schema="provClusterSchema"
373
548
  :table-actions="false"
374
549
  :row-actions="false"
375
550
  key-field="id"
376
- :rows="kubeClusters"
377
- :headers="clusterHeaders"
378
- :loading="!kubeClusters"
379
- :paging="true"
551
+ :headers="headers"
552
+ :pagination-headers="paginationHeaders"
553
+ context="home"
554
+
555
+ :local-filter="filterRowsLocal"
556
+ :api-filter="filterRowsApi"
557
+
558
+ :namespaced="false"
559
+ :groupable="false"
560
+ manualRefreshButtonSize="sm"
561
+ :fetchSecondaryResources="fetchSecondaryResources"
562
+ :fetchPageSecondaryResources="fetchPageSecondaryResources"
380
563
  >
381
564
  <template #header-left>
382
565
  <div class="row table-heading">
@@ -384,19 +567,19 @@ export default {
384
567
  {{ t('landing.clusters.title') }}
385
568
  </h2>
386
569
  <BadgeState
387
- v-if="kubeClusters"
388
- :label="kubeClusters.length.toString()"
570
+ v-if="clusterCount"
571
+ :label="clusterCount.toString()"
389
572
  color="role-tertiary ml-20 mr-20"
390
573
  />
391
574
  </div>
392
575
  </template>
393
576
  <template
394
- v-if="canCreateCluster || canManageClusters"
577
+ v-if="canCreateCluster || !!provClusterSchema"
395
578
  #header-middle
396
579
  >
397
580
  <div class="table-heading">
398
581
  <router-link
399
- v-if="canManageClusters"
582
+ v-if="!!provClusterSchema"
400
583
  :to="manageLocation"
401
584
  class="btn btn-sm role-secondary"
402
585
  data-testid="cluster-management-manage-button"
@@ -487,7 +670,7 @@ export default {
487
670
  {{ t('landing.clusters.explore') }}
488
671
  </button>
489
672
  </template> -->
490
- </SortableTable>
673
+ </PaginatedResourceTable>
491
674
  </div>
492
675
  <div
493
676
  v-else
@@ -502,6 +685,7 @@ export default {
502
685
  </IndentedPanel>
503
686
  </div>
504
687
  </template>
688
+
505
689
  <style lang='scss' scoped>
506
690
  .home-panels {
507
691
  display: flex;
@@ -591,6 +775,7 @@ export default {
591
775
  }
592
776
  }
593
777
  </style>
778
+
594
779
  <style lang="scss">
595
780
  .home-page {
596
781
  .search {
@@ -375,6 +375,8 @@ describe('dashboard-store: mutations', () => {
375
375
  it.each([
376
376
  ['Add a new pod', mutationHelpers.loadAll.createNewEntry()],
377
377
  ])('%s', (_, run) => { // eslint-disable-line jest/no-identical-title
378
+ run.expected.types[POD].havePage = false;
379
+
378
380
  loadAdd(...run.params);
379
381
  const { map: cacheMap, ...cacheState } = run.params[0].types?.[POD] || {};
380
382
  const { map: expectedMap, ...expected } = run.expected.types?.[POD];
@@ -146,7 +146,7 @@ export default {
146
146
  /**
147
147
  *
148
148
  * @param {*} ctx
149
- * @param { {type: string, opt: ActionFindPageArgs} } opt
149
+ * @param { {type: string, opt: ActionFindAllArgs} } opt
150
150
  */
151
151
  async findAll(ctx, { type, opt }) {
152
152
  const {
@@ -364,7 +364,7 @@ export default {
364
364
  /**
365
365
  *
366
366
  * @param {*} ctx
367
- * @param { {type: string, opt: FindPageOpt} } opt
367
+ * @param { {type: string, opt: ActionFindPageArgs} } opt
368
368
  */
369
369
  async findPage(ctx, { type, opt }) {
370
370
  const { getters, commit, dispatch } = ctx;
@@ -384,7 +384,7 @@ export default {
384
384
  }
385
385
 
386
386
  // No need to request the resources if we have them already
387
- if (!opt.force && getters['havePaginatedPage'](type, opt)) {
387
+ if (!opt.transient && !opt.force && getters['havePaginatedPage'](type, opt)) {
388
388
  return findAllGetter(getters, type, opt);
389
389
  }
390
390
 
@@ -408,24 +408,31 @@ export default {
408
408
  return Promise.reject(e);
409
409
  }
410
410
 
411
- commit('loadPage', {
412
- ctx,
411
+ await dispatch('unwatch', {
413
412
  type,
414
- data: out.data,
415
- pagination: opt.pagination ? {
416
- request: {
417
- namespace: opt.namespaced,
418
- pagination: opt.pagination
419
- },
420
- result: {
421
- count: out.count,
422
- pages: out.pages || Math.ceil(out.count / (opt.pagination.pageSize || Number.MAX_SAFE_INTEGER)),
423
- timestamp: new Date().getTime()
424
- }
425
- } : undefined,
413
+ all: true,
426
414
  });
427
415
 
428
- const all = findAllGetter(getters, type, opt);
416
+ const pagination = opt.pagination ? {
417
+ request: {
418
+ namespace: opt.namespaced,
419
+ pagination: opt.pagination
420
+ },
421
+ result: {
422
+ count: out.count,
423
+ pages: out.pages || Math.ceil(out.count / (opt.pagination.pageSize || Number.MAX_SAFE_INTEGER)),
424
+ timestamp: new Date().getTime()
425
+ }
426
+ } : undefined;
427
+
428
+ if (!opt.transient) {
429
+ commit('loadPage', {
430
+ ctx,
431
+ type,
432
+ data: out.data,
433
+ pagination,
434
+ });
435
+ }
429
436
 
430
437
  if (opt.hasManualRefresh) {
431
438
  dispatch('resource-fetch/updateManualRefreshIsLoading', false, { root: true });
@@ -433,7 +440,10 @@ export default {
433
440
 
434
441
  garbageCollect.gcUpdateLastAccessed(ctx, type);
435
442
 
436
- return all;
443
+ return opt.transient ? {
444
+ data: out.data,
445
+ pagination
446
+ } : findAllGetter(getters, type, opt);
437
447
  },
438
448
 
439
449
  async findMatching(ctx, {