@rancher/shell 3.0.6 → 3.0.7
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.
- package/assets/images/pl/dark/rancher-logo.svg +131 -44
- package/assets/images/pl/rancher-logo.svg +120 -44
- package/assets/styles/base/_basic.scss +2 -2
- package/assets/styles/base/_color-classic.scss +51 -0
- package/assets/styles/base/_color.scss +3 -3
- package/assets/styles/base/_mixins.scss +1 -1
- package/assets/styles/base/_variables-classic.scss +47 -0
- package/assets/styles/global/_button.scss +49 -17
- package/assets/styles/global/_form.scss +1 -1
- package/assets/styles/themes/_dark.scss +4 -0
- package/assets/styles/themes/_light.scss +3 -69
- package/assets/styles/themes/_modern.scss +194 -50
- package/assets/styles/vendor/vue-select.scss +1 -2
- package/assets/translations/en-us.yaml +33 -21
- package/components/ClusterIconMenu.vue +1 -1
- package/components/ClusterProviderIcon.vue +1 -1
- package/components/CodeMirror.vue +1 -1
- package/components/IconOrSvg.vue +40 -29
- package/components/ResourceDetail/index.vue +1 -0
- package/components/SortableTable/sorting.js +3 -1
- package/components/Tabbed/index.vue +5 -5
- package/components/form/ResourceTabs/index.vue +37 -18
- package/components/form/SecretSelector.vue +6 -2
- package/components/nav/Group.vue +29 -9
- package/components/nav/Header.vue +6 -8
- package/components/nav/NamespaceFilter.vue +1 -1
- package/components/nav/TopLevelMenu.helper.ts +47 -20
- package/components/nav/TopLevelMenu.vue +44 -14
- package/components/nav/Type.vue +0 -5
- package/components/nav/__tests__/TopLevelMenu.test.ts +2 -0
- package/config/pagination-table-headers.js +10 -2
- package/config/product/explorer.js +4 -3
- package/config/table-headers.js +9 -0
- package/core/plugin.ts +18 -6
- package/core/types.ts +8 -0
- package/detail/provisioning.cattle.io.cluster.vue +1 -0
- package/dialog/InstallExtensionDialog.vue +71 -45
- package/dialog/UninstallExtensionDialog.vue +2 -1
- package/dialog/__tests__/InstallExtensionDialog.test.ts +111 -0
- package/edit/auth/oidc.vue +86 -16
- package/mixins/__tests__/chart.test.ts +1 -1
- package/mixins/chart.js +1 -1
- package/models/event.js +7 -0
- package/models/provisioning.cattle.io.cluster.js +9 -0
- package/package.json +1 -1
- package/pages/c/_cluster/explorer/EventsTable.vue +3 -6
- package/pages/c/_cluster/settings/performance.vue +1 -1
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +159 -62
- package/pages/c/_cluster/uiplugins/__tests__/PluginInfoPanel.test.ts +102 -0
- package/pages/c/_cluster/uiplugins/__tests__/{index.spec.ts → index.test.ts} +121 -55
- package/pages/c/_cluster/uiplugins/index.vue +110 -94
- package/plugins/__tests__/subscribe.events.test.ts +194 -0
- package/plugins/dashboard-store/actions.js +3 -0
- package/plugins/dashboard-store/getters.js +1 -1
- package/plugins/dashboard-store/resource-class.js +3 -3
- package/plugins/steve/__tests__/subscribe.spec.ts +27 -24
- package/plugins/steve/index.js +18 -10
- package/plugins/steve/mutations.js +2 -2
- package/plugins/steve/resourceWatcher.js +2 -2
- package/plugins/steve/steve-pagination-utils.ts +12 -9
- package/plugins/steve/subscribe.js +113 -85
- package/plugins/subscribe-events.ts +211 -0
- package/rancher-components/BadgeState/BadgeState.vue +8 -6
- package/rancher-components/Banner/Banner.vue +2 -1
- package/rancher-components/Form/Checkbox/Checkbox.vue +3 -3
- package/rancher-components/Form/Radio/RadioButton.vue +3 -3
- package/store/index.js +12 -22
- package/types/extension-manager.ts +8 -1
- package/types/resources/settings.d.ts +24 -17
- package/types/shell/index.d.ts +352 -335
- package/types/store/subscribe-events.types.ts +70 -0
- package/types/store/subscribe.types.ts +6 -22
- package/utils/pagination-utils.ts +87 -28
- package/utils/pagination-wrapper.ts +6 -8
- package/utils/sort.js +5 -0
- package/utils/unit-tests/pagination-utils.spec.ts +283 -0
- package/utils/validators/formRules/__tests__/index.test.ts +7 -0
- package/utils/validators/formRules/index.ts +2 -2
|
@@ -139,12 +139,6 @@ export abstract class BaseTopLevelMenuHelper {
|
|
|
139
139
|
this.$store = $store;
|
|
140
140
|
|
|
141
141
|
this.hasProvCluster = this.$store.getters[`management/schemaFor`](CAPI.RANCHER_CLUSTER);
|
|
142
|
-
|
|
143
|
-
// Reduce flicker when component is recreated on a different layout
|
|
144
|
-
const { clustersPinned = [], clustersOthers = [] } = this.$store.getters['sideNavCache'] || {};
|
|
145
|
-
|
|
146
|
-
this.clustersPinned.push(...clustersPinned);
|
|
147
|
-
this.clustersOthers.push(...clustersOthers);
|
|
148
142
|
}
|
|
149
143
|
|
|
150
144
|
protected convertToCluster(mgmtCluster: MgmtCluster, provCluster: ProvCluster): TopLevelMenuCluster {
|
|
@@ -163,10 +157,6 @@ export abstract class BaseTopLevelMenuHelper {
|
|
|
163
157
|
clusterRoute: { name: 'c-cluster-explorer', params: { cluster: mgmtCluster.id } }
|
|
164
158
|
};
|
|
165
159
|
}
|
|
166
|
-
|
|
167
|
-
protected cacheClusters() {
|
|
168
|
-
this.$store.dispatch('setSideNavCache', { clustersPinned: this.clustersPinned, clustersOthers: this.clustersOthers });
|
|
169
|
-
}
|
|
170
160
|
}
|
|
171
161
|
|
|
172
162
|
/**
|
|
@@ -202,9 +192,9 @@ export class TopLevelMenuHelperPagination extends BaseTopLevelMenuHelper impleme
|
|
|
202
192
|
this.clustersOthersWrapper = new PaginationWrapper({
|
|
203
193
|
$store,
|
|
204
194
|
id: 'tlm-unpinned-clusters',
|
|
205
|
-
onChange: () => {
|
|
195
|
+
onChange: async() => {
|
|
206
196
|
if (this.args) {
|
|
207
|
-
this.update(this.args);
|
|
197
|
+
await this.update(this.args);
|
|
208
198
|
}
|
|
209
199
|
},
|
|
210
200
|
enabledFor: {
|
|
@@ -220,9 +210,9 @@ export class TopLevelMenuHelperPagination extends BaseTopLevelMenuHelper impleme
|
|
|
220
210
|
this.provClusterWrapper = new PaginationWrapper({
|
|
221
211
|
$store,
|
|
222
212
|
id: 'tlm-prov-clusters',
|
|
223
|
-
onChange: () => {
|
|
213
|
+
onChange: async() => {
|
|
224
214
|
if (this.args) {
|
|
225
|
-
this.update(this.args);
|
|
215
|
+
await this.update(this.args);
|
|
226
216
|
}
|
|
227
217
|
},
|
|
228
218
|
enabledFor: {
|
|
@@ -276,8 +266,6 @@ export class TopLevelMenuHelperPagination extends BaseTopLevelMenuHelper impleme
|
|
|
276
266
|
|
|
277
267
|
this.clustersPinned.push(..._clustersPinned);
|
|
278
268
|
this.clustersOthers.push(..._clustersNotPinned);
|
|
279
|
-
|
|
280
|
-
this.cacheClusters();
|
|
281
269
|
}
|
|
282
270
|
|
|
283
271
|
async destroy() {
|
|
@@ -390,7 +378,6 @@ export class TopLevelMenuHelperPagination extends BaseTopLevelMenuHelper impleme
|
|
|
390
378
|
private async updateProvCluster(notPinned: MgmtCluster[], pinned: MgmtCluster[]): Promise<ProvCluster[]> {
|
|
391
379
|
return this.provClusterWrapper.request({
|
|
392
380
|
pagination: {
|
|
393
|
-
|
|
394
381
|
filters: [
|
|
395
382
|
PaginationParamFilter.createMultipleFields(
|
|
396
383
|
[...notPinned, ...pinned]
|
|
@@ -399,7 +386,6 @@ export class TopLevelMenuHelperPagination extends BaseTopLevelMenuHelper impleme
|
|
|
399
386
|
}))
|
|
400
387
|
)
|
|
401
388
|
],
|
|
402
|
-
|
|
403
389
|
page: 1,
|
|
404
390
|
sort: [],
|
|
405
391
|
projectsOrNamespaces: []
|
|
@@ -432,8 +418,6 @@ export class TopLevelMenuHelperLegacy extends BaseTopLevelMenuHelper implements
|
|
|
432
418
|
|
|
433
419
|
this.clustersPinned.push(..._clustersPinned);
|
|
434
420
|
this.clustersOthers.push(..._clustersNotPinned);
|
|
435
|
-
|
|
436
|
-
this.cacheClusters();
|
|
437
421
|
}
|
|
438
422
|
|
|
439
423
|
async destroy() {
|
|
@@ -581,3 +565,46 @@ export class TopLevelMenuHelperLegacy extends BaseTopLevelMenuHelper implements
|
|
|
581
565
|
return sorted;
|
|
582
566
|
}
|
|
583
567
|
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* Retain state of the side nav, no matter when the TopLevelMenu component is created/deleted (on layout change)
|
|
571
|
+
*
|
|
572
|
+
* This means there's no flickering when the user changes pages and the side nav component re-renders
|
|
573
|
+
*
|
|
574
|
+
* Also it means we're not unwatching then watching the clusters
|
|
575
|
+
*/
|
|
576
|
+
class TopLevelMenuHelperService {
|
|
577
|
+
private _helper?: TopLevelMenuHelper;
|
|
578
|
+
public init($store: VuexStore) {
|
|
579
|
+
if (this._helper) {
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
const canPagination = $store.getters[`management/paginationEnabled`]({
|
|
584
|
+
id: MANAGEMENT.CLUSTER,
|
|
585
|
+
context: 'side-bar',
|
|
586
|
+
}) && $store.getters[`management/paginationEnabled`]({
|
|
587
|
+
id: CAPI.RANCHER_CLUSTER,
|
|
588
|
+
context: 'side-bar',
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
this._helper = canPagination ? new TopLevelMenuHelperPagination({ $store }) : new TopLevelMenuHelperLegacy({ $store });
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
public async reset() {
|
|
595
|
+
await this._helper?.destroy();
|
|
596
|
+
delete this._helper;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
get helper(): TopLevelMenuHelper {
|
|
600
|
+
if (!this._helper) {
|
|
601
|
+
throw new Error('Unable to use the side nav cluster helper (not initialised)');
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
return this._helper;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
const instance = new TopLevelMenuHelperService();
|
|
609
|
+
|
|
610
|
+
export default instance;
|
|
@@ -14,7 +14,7 @@ import { SETTING } from '@shell/config/settings';
|
|
|
14
14
|
import { getProductFromRoute } from '@shell/utils/router';
|
|
15
15
|
import { isRancherPrime } from '@shell/config/version';
|
|
16
16
|
import Pinned from '@shell/components/nav/Pinned';
|
|
17
|
-
import
|
|
17
|
+
import sideNavService from '@shell/components/nav/TopLevelMenu.helper';
|
|
18
18
|
import { debounce } from 'lodash';
|
|
19
19
|
import { sameContents } from '@shell/utils/array';
|
|
20
20
|
|
|
@@ -27,6 +27,8 @@ export default {
|
|
|
27
27
|
},
|
|
28
28
|
|
|
29
29
|
data() {
|
|
30
|
+
sideNavService.init(this.$store);
|
|
31
|
+
|
|
30
32
|
const { displayVersion, fullVersion } = getVersionInfo(this.$store);
|
|
31
33
|
const hasProvCluster = this.$store.getters[`management/schemaFor`](CAPI.RANCHER_CLUSTER);
|
|
32
34
|
|
|
@@ -37,7 +39,7 @@ export default {
|
|
|
37
39
|
id: CAPI.RANCHER_CLUSTER,
|
|
38
40
|
context: 'side-bar',
|
|
39
41
|
});
|
|
40
|
-
const helper =
|
|
42
|
+
const helper = sideNavService.helper;
|
|
41
43
|
const provClusters = !canPagination && hasProvCluster ? this.$store.getters[`management/all`](CAPI.RANCHER_CLUSTER) : [];
|
|
42
44
|
const mgmtClusters = !canPagination ? this.$store.getters[`management/all`](MANAGEMENT.CLUSTER) : [];
|
|
43
45
|
|
|
@@ -327,7 +329,6 @@ export default {
|
|
|
327
329
|
|
|
328
330
|
beforeUnmount() {
|
|
329
331
|
document.removeEventListener('keyup', this.handler);
|
|
330
|
-
this.helper?.destroy();
|
|
331
332
|
},
|
|
332
333
|
|
|
333
334
|
methods: {
|
|
@@ -1073,6 +1074,14 @@ export default {
|
|
|
1073
1074
|
width: 300px;
|
|
1074
1075
|
overflow: auto;
|
|
1075
1076
|
|
|
1077
|
+
& .category {
|
|
1078
|
+
& a.router-link-active {
|
|
1079
|
+
&:hover {
|
|
1080
|
+
color: var(--on-active, var(--default));
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1076
1085
|
.option {
|
|
1077
1086
|
align-items: center;
|
|
1078
1087
|
cursor: pointer;
|
|
@@ -1119,6 +1128,15 @@ export default {
|
|
|
1119
1128
|
}
|
|
1120
1129
|
}
|
|
1121
1130
|
|
|
1131
|
+
&:not(.active-menu-link) {
|
|
1132
|
+
&:hover {
|
|
1133
|
+
.pin {
|
|
1134
|
+
display: block;
|
|
1135
|
+
color: var(--body-text-hover);
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1122
1140
|
&:hover {
|
|
1123
1141
|
text-decoration: none;
|
|
1124
1142
|
|
|
@@ -1178,19 +1196,31 @@ export default {
|
|
|
1178
1196
|
outline-offset: -4px;
|
|
1179
1197
|
}
|
|
1180
1198
|
|
|
1181
|
-
background: var(--primary-hover-bg);
|
|
1182
|
-
color: var(--primary-hover-text);
|
|
1199
|
+
background: var(--active-nav, var(--primary-hover-bg));
|
|
1200
|
+
color: var(--on-active, var(--primary-hover-text));
|
|
1183
1201
|
|
|
1184
1202
|
svg {
|
|
1185
|
-
fill: var(--primary-hover-text);
|
|
1203
|
+
fill: var(--on-active, var(--primary-hover-text));
|
|
1186
1204
|
}
|
|
1187
1205
|
|
|
1188
1206
|
i {
|
|
1189
|
-
color: var(--primary-hover-text);
|
|
1207
|
+
color: var(--on-active, var(--primary-hover-text));
|
|
1190
1208
|
}
|
|
1191
1209
|
|
|
1192
1210
|
div .description {
|
|
1193
|
-
color: var(--default);
|
|
1211
|
+
color: var(--on-active, var(--default));
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
&:hover {
|
|
1215
|
+
background: var(--active-hover, var(--primary-hover-bg));
|
|
1216
|
+
|
|
1217
|
+
div {
|
|
1218
|
+
color: var(--on-active, var(--default));
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
svg {
|
|
1222
|
+
fill: var(--on-active, var(--primary-hover-text));
|
|
1223
|
+
}
|
|
1194
1224
|
}
|
|
1195
1225
|
}
|
|
1196
1226
|
|
|
@@ -1201,8 +1231,8 @@ export default {
|
|
|
1201
1231
|
}
|
|
1202
1232
|
|
|
1203
1233
|
&:hover {
|
|
1204
|
-
color: var(--primary-hover-text);
|
|
1205
|
-
background: var(--primary-hover-bg);
|
|
1234
|
+
color: var(--tertiary-hover-app-bar, var(--primary-hover-text));
|
|
1235
|
+
background: var(--nav-hover-top-level, var(--primary-hover-bg));
|
|
1206
1236
|
> div {
|
|
1207
1237
|
color: var(--primary-hover-text);
|
|
1208
1238
|
|
|
@@ -1211,10 +1241,10 @@ export default {
|
|
|
1211
1241
|
}
|
|
1212
1242
|
}
|
|
1213
1243
|
svg {
|
|
1214
|
-
fill: var(--primary-hover-text);
|
|
1244
|
+
fill: var(--tertiary-hover-app-bar, var(--primary-hover-text));
|
|
1215
1245
|
}
|
|
1216
1246
|
div {
|
|
1217
|
-
color: var(--primary-hover-text);
|
|
1247
|
+
color: var(--tertiary-hover-app-bar, var(--primary-hover-text));
|
|
1218
1248
|
}
|
|
1219
1249
|
&.disabled {
|
|
1220
1250
|
background: transparent;
|
|
@@ -1549,8 +1579,8 @@ export default {
|
|
|
1549
1579
|
overflow: hidden;
|
|
1550
1580
|
& IMG {
|
|
1551
1581
|
object-fit: contain;
|
|
1552
|
-
height: 21px;
|
|
1553
1582
|
max-width: 200px;
|
|
1583
|
+
height: 36px;
|
|
1554
1584
|
}
|
|
1555
1585
|
}
|
|
1556
1586
|
|
|
@@ -1592,7 +1622,7 @@ export default {
|
|
|
1592
1622
|
padding: 8px 20px;
|
|
1593
1623
|
|
|
1594
1624
|
&:hover {
|
|
1595
|
-
background-color: var(--primary-hover-bg);
|
|
1625
|
+
background-color: var(--active-hover, var(--primary-hover-bg));
|
|
1596
1626
|
color: var(--primary-hover-text);
|
|
1597
1627
|
text-decoration: none;
|
|
1598
1628
|
}
|
package/components/nav/Type.vue
CHANGED
|
@@ -3,6 +3,7 @@ import { mount, Wrapper } from '@vue/test-utils';
|
|
|
3
3
|
import { CAPI, COUNT, MANAGEMENT } from '@shell/config/types';
|
|
4
4
|
import { PINNED_CLUSTERS } from '@shell/store/prefs';
|
|
5
5
|
import { nextTick } from 'vue';
|
|
6
|
+
import sideNavService from '@shell/components/nav/TopLevelMenu.helper';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* `clusters` doubles up as both mgmt and prov clusters (don't shoot the messenger)
|
|
@@ -53,6 +54,7 @@ const waitForIt = async() => {
|
|
|
53
54
|
describe('topLevelMenu', () => {
|
|
54
55
|
beforeEach(() => {
|
|
55
56
|
jest.useFakeTimers();
|
|
57
|
+
sideNavService.reset();
|
|
56
58
|
});
|
|
57
59
|
|
|
58
60
|
afterEach(() => {
|
|
@@ -3,7 +3,8 @@ import {
|
|
|
3
3
|
STATE, NAME as NAME_COL, NAMESPACE as NAMESPACE_COL, AGE, OBJECT,
|
|
4
4
|
EVENT_LAST_SEEN_TIME,
|
|
5
5
|
EVENT_TYPE,
|
|
6
|
-
SECRET_CLONE
|
|
6
|
+
SECRET_CLONE,
|
|
7
|
+
EVENT_FIRST_SEEN_TIME
|
|
7
8
|
} from '@shell/config/table-headers';
|
|
8
9
|
|
|
9
10
|
// This file contains table headers
|
|
@@ -56,10 +57,17 @@ export const STEVE_EVENT_OBJECT = {
|
|
|
56
57
|
search: 'involvedObject.kind',
|
|
57
58
|
};
|
|
58
59
|
|
|
60
|
+
export const STEVE_EVENT_FIRST_SEEN = {
|
|
61
|
+
...EVENT_FIRST_SEEN_TIME,
|
|
62
|
+
|
|
63
|
+
value: 'metadata.fields.7',
|
|
64
|
+
sort: 'metadata.fields.7:desc',
|
|
65
|
+
};
|
|
66
|
+
|
|
59
67
|
export const STEVE_EVENT_LAST_SEEN = {
|
|
60
68
|
...EVENT_LAST_SEEN_TIME,
|
|
61
69
|
value: 'metadata.fields.0',
|
|
62
|
-
sort: 'metadata.fields.0',
|
|
70
|
+
sort: 'metadata.fields.0:desc',
|
|
63
71
|
};
|
|
64
72
|
|
|
65
73
|
export const STEVE_EVENT_TYPE = {
|
|
@@ -22,11 +22,12 @@ import {
|
|
|
22
22
|
ACCESS_KEY, DESCRIPTION, EXPIRES, EXPIRY_STATE, LAST_USED, SUB_TYPE, AGE_NORMAN, SCOPE_NORMAN, PERSISTENT_VOLUME_CLAIM, RECLAIM_POLICY, PV_REASON, WORKLOAD_HEALTH_SCALE, POD_RESTARTS,
|
|
23
23
|
DURATION, MESSAGE, REASON, EVENT_TYPE, OBJECT, ROLE, ROLES, VERSION, INTERNAL_EXTERNAL_IP, KUBE_NODE_OS, CPU, RAM, SECRET_DATA,
|
|
24
24
|
EVENT_LAST_SEEN_TIME,
|
|
25
|
+
EVENT_FIRST_SEEN_TIME,
|
|
25
26
|
} from '@shell/config/table-headers';
|
|
26
27
|
|
|
27
28
|
import { DSL } from '@shell/store/type-map';
|
|
28
29
|
import {
|
|
29
|
-
STEVE_AGE_COL, STEVE_EVENT_LAST_SEEN, STEVE_EVENT_OBJECT, STEVE_EVENT_TYPE, STEVE_LIST_GROUPS, STEVE_NAMESPACE_COL, STEVE_NAME_COL, STEVE_STATE_COL
|
|
30
|
+
STEVE_AGE_COL, STEVE_EVENT_FIRST_SEEN, STEVE_EVENT_LAST_SEEN, STEVE_EVENT_OBJECT, STEVE_EVENT_TYPE, STEVE_LIST_GROUPS, STEVE_NAMESPACE_COL, STEVE_NAME_COL, STEVE_STATE_COL
|
|
30
31
|
} from '@shell/config/pagination-table-headers';
|
|
31
32
|
|
|
32
33
|
import { COLUMN_BREAKPOINTS } from '@shell/types/store/type-map';
|
|
@@ -337,7 +338,7 @@ export function init(store) {
|
|
|
337
338
|
);
|
|
338
339
|
|
|
339
340
|
headers(EVENT,
|
|
340
|
-
[STATE, EVENT_LAST_SEEN_TIME, EVENT_TYPE, REASON, OBJECT, 'Subobject', 'Source', MESSAGE,
|
|
341
|
+
[STATE, EVENT_LAST_SEEN_TIME, EVENT_TYPE, REASON, OBJECT, 'Subobject', 'Source', MESSAGE, EVENT_FIRST_SEEN_TIME, 'Count', NAME_COL, NAMESPACE_COL],
|
|
341
342
|
[
|
|
342
343
|
STEVE_STATE_COL,
|
|
343
344
|
STEVE_EVENT_LAST_SEEN,
|
|
@@ -347,7 +348,7 @@ export function init(store) {
|
|
|
347
348
|
'Subobject',
|
|
348
349
|
'Source',
|
|
349
350
|
MESSAGE,
|
|
350
|
-
|
|
351
|
+
STEVE_EVENT_FIRST_SEEN,
|
|
351
352
|
'Count',
|
|
352
353
|
STEVE_NAME_COL,
|
|
353
354
|
STEVE_NAMESPACE_COL,
|
package/config/table-headers.js
CHANGED
|
@@ -541,6 +541,15 @@ export const LAST_SEEN_TIME = {
|
|
|
541
541
|
tooltip: 'tableHeaders.lastSeenTooltip'
|
|
542
542
|
};
|
|
543
543
|
|
|
544
|
+
export const EVENT_FIRST_SEEN_TIME = {
|
|
545
|
+
name: 'firstSeen',
|
|
546
|
+
labelKey: 'tableHeaders.firstSeen',
|
|
547
|
+
tooltip: 'tableHeaders.firstSeenTooltip',
|
|
548
|
+
|
|
549
|
+
value: 'firstSeen',
|
|
550
|
+
sort: 'firstSeen:desc',
|
|
551
|
+
};
|
|
552
|
+
|
|
544
553
|
export const EVENT_LAST_SEEN_TIME = {
|
|
545
554
|
...LAST_SEEN_TIME,
|
|
546
555
|
defaultSort: true,
|
package/core/plugin.ts
CHANGED
|
@@ -17,24 +17,31 @@ import {
|
|
|
17
17
|
PluginRouteRecordRaw, RegisterStore, UnregisterStore, CoreStoreSpecifics, CoreStoreConfig,
|
|
18
18
|
NavHooks, OnNavToPackage, OnNavAwayFromPackage, OnLogIn, OnLogOut,
|
|
19
19
|
PaginationTableColumn,
|
|
20
|
-
ExtensionEnvironment
|
|
20
|
+
ExtensionEnvironment,
|
|
21
|
+
ServerSidePaginationExtensionConfig
|
|
21
22
|
} from './types';
|
|
22
23
|
import coreStore, { coreStoreModule, coreStoreState } from '@shell/plugins/dashboard-store';
|
|
23
24
|
import { defineAsyncComponent, markRaw, Component } from 'vue';
|
|
24
25
|
import { getVersionData, CURRENT_RANCHER_VERSION } from '@shell/config/version';
|
|
26
|
+
import { ExtensionManagerTypes } from '@shell/types/extension-manager';
|
|
25
27
|
|
|
26
|
-
|
|
28
|
+
/** Registration IDs used for different extension points in the extensions catalog */
|
|
27
29
|
export const EXT_IDS = {
|
|
28
|
-
MODELS:
|
|
29
|
-
MODEL_EXTENSION:
|
|
30
|
-
|
|
30
|
+
MODELS: 'models',
|
|
31
|
+
MODEL_EXTENSION: 'model-extension',
|
|
32
|
+
/**
|
|
33
|
+
* Extension can provide resources that use server-side-pagination
|
|
34
|
+
*/
|
|
35
|
+
SERVER_SIDE_PAGINATION_RESOURCES: 'server-side-pagination',
|
|
36
|
+
} as const;
|
|
37
|
+
export type EXT_IDS_VALUES = (typeof EXT_IDS)[keyof typeof EXT_IDS];
|
|
31
38
|
|
|
32
39
|
export type ProductFunction = (plugin: IPlugin, store: any) => void;
|
|
33
40
|
|
|
34
41
|
export class Plugin implements IPlugin {
|
|
35
42
|
public id: string;
|
|
36
43
|
public name: string;
|
|
37
|
-
public types:
|
|
44
|
+
public types: ExtensionManagerTypes = {};
|
|
38
45
|
public l10n: { [key: string]: Function[] } = {};
|
|
39
46
|
public modelExtensions: { [key: string]: Function[] } = {};
|
|
40
47
|
public locales: { locale: string, label: string}[] = [];
|
|
@@ -360,6 +367,11 @@ export class Plugin implements IPlugin {
|
|
|
360
367
|
}
|
|
361
368
|
}
|
|
362
369
|
|
|
370
|
+
public enableServerSidePagination(config: ServerSidePaginationExtensionConfig) {
|
|
371
|
+
console.info(`Extension "${ this.name || this.id }" is enabling server-side pagination for some resources`, config); // eslint-disable-line no-console
|
|
372
|
+
this.register(EXT_IDS.SERVER_SIDE_PAGINATION_RESOURCES, this.id, () => config);
|
|
373
|
+
}
|
|
374
|
+
|
|
363
375
|
public async onLogOut(store: any) {
|
|
364
376
|
await Promise.all(this.stores.map((s: any) => store.dispatch(`${ s.storeName }/onLogout`)));
|
|
365
377
|
|
package/core/types.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ProductFunction } from './plugin';
|
|
2
2
|
import { RouteRecordRaw } from 'vue-router';
|
|
3
3
|
import type { ExtensionManager } from '@shell/types/extension-manager';
|
|
4
|
+
import { PaginationSettingsStores } from '@shell/types/resources/settings';
|
|
4
5
|
|
|
5
6
|
// Cluster Provisioning types
|
|
6
7
|
export * from './types-provisioning';
|
|
@@ -351,6 +352,11 @@ export type TableColumn = HeaderOptions;
|
|
|
351
352
|
*/
|
|
352
353
|
export type PaginationTableColumn = PaginationHeaderOptions;
|
|
353
354
|
|
|
355
|
+
/**
|
|
356
|
+
* External extension configuration for @PaginationSettingsStores
|
|
357
|
+
*/
|
|
358
|
+
export type ServerSidePaginationExtensionConfig = PaginationSettingsStores;
|
|
359
|
+
|
|
354
360
|
export interface ConfigureTypeOptions {
|
|
355
361
|
/**
|
|
356
362
|
* Override for the create button string on a list view
|
|
@@ -693,6 +699,8 @@ export interface IPlugin {
|
|
|
693
699
|
): void;
|
|
694
700
|
addNavHooks(hooks: NavHooks): void;
|
|
695
701
|
|
|
702
|
+
enableServerSidePagination(config: ServerSidePaginationExtensionConfig): void;
|
|
703
|
+
|
|
696
704
|
/**
|
|
697
705
|
* Adds a model extension
|
|
698
706
|
* @experimental May change or be removed in the future
|
|
@@ -3,12 +3,12 @@ import AsyncButton from '@shell/components/AsyncButton';
|
|
|
3
3
|
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
|
4
4
|
import { CATALOG, MANAGEMENT } from '@shell/config/types';
|
|
5
5
|
import { CATALOG as CATALOG_ANNOTATIONS } from '@shell/config/labels-annotations';
|
|
6
|
-
import { UI_PLUGIN_NAMESPACE } from '@shell/config/uiplugins';
|
|
6
|
+
import { UI_PLUGIN_NAMESPACE, isChartVersionHigher } from '@shell/config/uiplugins';
|
|
7
7
|
import Banner from '@components/Banner/Banner.vue';
|
|
8
8
|
import { SETTING } from '@shell/config/settings';
|
|
9
|
-
import {
|
|
9
|
+
import { getPluginChartVersionLabel } from '@shell/utils/uiplugins';
|
|
10
10
|
|
|
11
|
-
// Note: This dialog handles installation and
|
|
11
|
+
// Note: This dialog handles installation, upgrade and downgrade of a plugin
|
|
12
12
|
|
|
13
13
|
export default {
|
|
14
14
|
emits: ['close'],
|
|
@@ -29,7 +29,14 @@ export default {
|
|
|
29
29
|
required: true
|
|
30
30
|
},
|
|
31
31
|
/**
|
|
32
|
-
* The
|
|
32
|
+
* The pre-selected version in the dropdown
|
|
33
|
+
*/
|
|
34
|
+
initialVersion: {
|
|
35
|
+
type: String,
|
|
36
|
+
default: null
|
|
37
|
+
},
|
|
38
|
+
/**
|
|
39
|
+
* The action to perform (install, upgrade, downgrade)
|
|
33
40
|
*/
|
|
34
41
|
action: {
|
|
35
42
|
type: String,
|
|
@@ -63,33 +70,32 @@ export default {
|
|
|
63
70
|
},
|
|
64
71
|
|
|
65
72
|
async fetch() {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if (this.action === 'update') {
|
|
72
|
-
this.currentVersion = chartVersion;
|
|
73
|
-
|
|
74
|
-
// Update to latest version, so take the first version
|
|
75
|
-
if (this.plugin?.installableVersions?.length > 0) {
|
|
76
|
-
this.version = this.plugin?.installableVersions?.[0]?.version;
|
|
77
|
-
}
|
|
78
|
-
} else if (this.action === 'rollback') {
|
|
79
|
-
// Find the newest version once we remove the current version
|
|
80
|
-
const versionNames = this.plugin.installableVersions.filter((v) => v.version !== chartVersion);
|
|
73
|
+
// Determine the currently installed version, if any
|
|
74
|
+
if (this.plugin.installed) {
|
|
75
|
+
this.currentVersion = this.plugin.installedVersion;
|
|
76
|
+
}
|
|
81
77
|
|
|
82
|
-
|
|
78
|
+
// Determine the initial version to select in the dropdown
|
|
79
|
+
if (this.initialVersion) {
|
|
80
|
+
this.version = this.initialVersion;
|
|
81
|
+
} else if (this.action === 'upgrade') {
|
|
82
|
+
// Upgrade to the latest version, so take the first version
|
|
83
|
+
this.version = this.plugin?.installableVersions?.[0]?.version;
|
|
84
|
+
} else if (this.action === 'downgrade') {
|
|
85
|
+
const versions = this.plugin.installableVersions;
|
|
86
|
+
const currentIndex = versions.findIndex((v) => v.version === this.currentVersion);
|
|
83
87
|
|
|
84
|
-
if (
|
|
85
|
-
|
|
88
|
+
if (currentIndex !== -1 && currentIndex < versions.length - 1) {
|
|
89
|
+
// Select the version just below the current version
|
|
90
|
+
this.version = versions[currentIndex + 1].version;
|
|
86
91
|
}
|
|
92
|
+
} else {
|
|
93
|
+
// Default to the latest installable version for new installs
|
|
94
|
+
this.version = this.plugin?.installableVersions?.[0]?.version;
|
|
87
95
|
}
|
|
88
96
|
|
|
89
|
-
//
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (!versionChart) {
|
|
97
|
+
// Fallback if no version could be determined
|
|
98
|
+
if (!this.version) {
|
|
93
99
|
this.version = this.plugin?.installableVersions?.[0]?.version;
|
|
94
100
|
}
|
|
95
101
|
|
|
@@ -119,37 +125,39 @@ export default {
|
|
|
119
125
|
},
|
|
120
126
|
|
|
121
127
|
versionOptions() {
|
|
122
|
-
if (!this.plugin) {
|
|
128
|
+
if (!this.plugin?.installableVersions) {
|
|
123
129
|
return [];
|
|
124
130
|
}
|
|
125
131
|
|
|
126
|
-
// Don't allow
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
return v.version !== this.currentVersion;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
return true;
|
|
133
|
-
});
|
|
132
|
+
// Don't allow upgrade/downgrade to current version by disabling the option
|
|
133
|
+
return this.plugin.installableVersions.map((v) => {
|
|
134
|
+
const isCurrent = v.version === this.currentVersion;
|
|
134
135
|
|
|
135
|
-
return versions.map((version) => {
|
|
136
136
|
return {
|
|
137
|
-
label:
|
|
138
|
-
value:
|
|
137
|
+
label: getPluginChartVersionLabel(v) + (isCurrent ? ` (${ this.t('plugins.labels.current') })` : ''),
|
|
138
|
+
value: v.version,
|
|
139
|
+
disabled: isCurrent,
|
|
139
140
|
};
|
|
140
141
|
});
|
|
141
142
|
},
|
|
142
143
|
|
|
143
144
|
buttonMode() {
|
|
144
|
-
if (this.action === '
|
|
145
|
-
return '
|
|
145
|
+
if (this.action === 'install') {
|
|
146
|
+
return 'install';
|
|
146
147
|
}
|
|
147
148
|
|
|
148
|
-
if (this.
|
|
149
|
-
|
|
149
|
+
if (this.currentVersion && this.version) {
|
|
150
|
+
if (isChartVersionHigher(this.version, this.currentVersion)) {
|
|
151
|
+
return 'upgrade';
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (isChartVersionHigher(this.currentVersion, this.version)) {
|
|
155
|
+
return 'downgrade';
|
|
156
|
+
}
|
|
150
157
|
}
|
|
151
158
|
|
|
152
|
-
|
|
159
|
+
// Fallback for safety, though should not be reached if version is selected
|
|
160
|
+
return this.action;
|
|
153
161
|
},
|
|
154
162
|
|
|
155
163
|
chartVersionLoadsWithoutAuth() {
|
|
@@ -158,6 +166,23 @@ export default {
|
|
|
158
166
|
|
|
159
167
|
returnFocusSelector() {
|
|
160
168
|
return `[data-testid="extension-card-${ this.action }-btn-${ this.plugin?.name }"]`;
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
buttonIcon() {
|
|
172
|
+
if (this.busy) {
|
|
173
|
+
return '';
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
switch (this.buttonMode) {
|
|
177
|
+
case 'install':
|
|
178
|
+
return 'icon-plus';
|
|
179
|
+
case 'upgrade':
|
|
180
|
+
return 'icon-upgrade-alt';
|
|
181
|
+
case 'downgrade':
|
|
182
|
+
return 'icon-downgrade-alt';
|
|
183
|
+
default:
|
|
184
|
+
return '';
|
|
185
|
+
}
|
|
161
186
|
}
|
|
162
187
|
},
|
|
163
188
|
|
|
@@ -295,12 +320,12 @@ export default {
|
|
|
295
320
|
<template>
|
|
296
321
|
<div class="plugin-install-dialog">
|
|
297
322
|
<h4 class="mt-10">
|
|
298
|
-
{{ t(`plugins.${
|
|
323
|
+
{{ t(`plugins.${ buttonMode }.title`, { name: `"${plugin?.label}"` }, true) }}
|
|
299
324
|
</h4>
|
|
300
325
|
<div class="custom mt-10">
|
|
301
326
|
<div class="dialog-panel">
|
|
302
327
|
<p>
|
|
303
|
-
{{ t(`plugins.${
|
|
328
|
+
{{ t(`plugins.${ buttonMode }.prompt`) }}
|
|
304
329
|
</p>
|
|
305
330
|
<Banner
|
|
306
331
|
v-if="chartVersionLoadsWithoutAuth"
|
|
@@ -335,6 +360,7 @@ export default {
|
|
|
335
360
|
</button>
|
|
336
361
|
<AsyncButton
|
|
337
362
|
:mode="buttonMode"
|
|
363
|
+
:icon="buttonIcon"
|
|
338
364
|
data-testid="install-ext-modal-install-btn"
|
|
339
365
|
@click="install"
|
|
340
366
|
/>
|
|
@@ -103,7 +103,7 @@ export default {
|
|
|
103
103
|
<template>
|
|
104
104
|
<div class="plugin-install-dialog">
|
|
105
105
|
<h4 class="mt-10">
|
|
106
|
-
{{ t('plugins.uninstall.title', { name: plugin?.label }) }}
|
|
106
|
+
{{ t('plugins.uninstall.title', { name: `"${plugin?.label}"` }, true) }}
|
|
107
107
|
</h4>
|
|
108
108
|
<div class="mt-10 dialog-panel">
|
|
109
109
|
<div class="dialog-info">
|
|
@@ -122,6 +122,7 @@ export default {
|
|
|
122
122
|
</button>
|
|
123
123
|
<AsyncButton
|
|
124
124
|
mode="uninstall"
|
|
125
|
+
:icon="busy ? '' : 'icon-delete'"
|
|
125
126
|
data-testid="uninstall-ext-modal-uninstall-btn"
|
|
126
127
|
@click="uninstall()"
|
|
127
128
|
/>
|