@rancher/shell 3.0.6 → 3.0.8-rc.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.
- package/assets/images/pl/dark/rancher-logo.svg +131 -44
- package/assets/images/pl/rancher-logo.svg +120 -44
- package/assets/images/vendor/githubapp.svg +13 -0
- 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/_typography.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 +124 -32
- package/assets/translations/zh-hans.yaml +0 -4
- 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/Inactivity.vue +222 -106
- package/components/InstallHelmCharts.vue +2 -2
- package/components/ResourceDetail/index.vue +2 -1
- package/components/SortableTable/index.vue +17 -2
- package/components/SortableTable/sorting.js +3 -1
- package/components/Tabbed/index.vue +5 -5
- package/components/fleet/FleetConfigMapSelector.vue +117 -0
- package/components/fleet/FleetSecretSelector.vue +127 -0
- package/components/fleet/__tests__/FleetConfigMapSelector.test.ts +125 -0
- package/components/fleet/__tests__/FleetSecretSelector.test.ts +82 -0
- package/components/form/FileImageSelector.vue +13 -4
- package/components/form/FileSelector.vue +11 -2
- package/components/form/ResourceLabeledSelect.vue +1 -0
- package/components/form/ResourceTabs/index.vue +37 -18
- package/components/form/SecretSelector.vue +6 -2
- package/components/form/__tests__/ResourceLabeledSelect.test.ts +90 -0
- package/components/nav/Group.vue +29 -9
- package/components/nav/Header.vue +7 -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/auth.js +1 -0
- package/config/product/explorer.js +4 -3
- package/config/query-params.js +1 -0
- package/config/settings.ts +8 -1
- package/config/table-headers.js +9 -0
- package/config/types.js +2 -0
- package/core/plugin.ts +18 -6
- package/core/types.ts +8 -0
- package/detail/provisioning.cattle.io.cluster.vue +1 -0
- package/dialog/AddonConfigConfirmationDialog.vue +45 -1
- package/dialog/InstallExtensionDialog.vue +71 -45
- package/dialog/UninstallExtensionDialog.vue +2 -1
- package/dialog/__tests__/InstallExtensionDialog.test.ts +111 -0
- package/edit/__tests__/fleet.cattle.io.helmop.test.ts +52 -11
- package/edit/auth/AuthProviderWarningBanners.vue +14 -1
- package/edit/auth/github-app-steps.vue +97 -0
- package/edit/auth/github-steps.vue +75 -0
- package/edit/auth/github.vue +94 -65
- package/edit/auth/oidc.vue +86 -16
- package/edit/fleet.cattle.io.helmop.vue +51 -2
- package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +15 -5
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +11 -9
- package/edit/provisioning.cattle.io.cluster/rke2.vue +56 -9
- package/edit/provisioning.cattle.io.cluster/tabs/AddOnConfig.vue +28 -2
- package/list/projectsecret.vue +1 -1
- package/machine-config/azure.vue +1 -1
- package/mixins/__tests__/chart.test.ts +1 -1
- package/mixins/chart.js +2 -2
- package/models/__tests__/chart.test.ts +17 -9
- package/models/__tests__/compliance.cattle.io.clusterscanprofile.spec.js +30 -0
- package/models/catalog.cattle.io.app.js +1 -1
- package/models/chart.js +3 -1
- package/models/compliance.cattle.io.clusterscanprofile.js +1 -1
- package/models/event.js +7 -0
- package/models/management.cattle.io.authconfig.js +1 -0
- package/models/provisioning.cattle.io.cluster.js +9 -0
- package/package.json +2 -2
- package/pages/auth/login.vue +5 -2
- package/pages/auth/verify.vue +1 -1
- package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +3 -2
- package/pages/c/_cluster/apps/charts/chart.vue +2 -2
- package/pages/c/_cluster/explorer/EventsTable.vue +92 -9
- package/pages/c/_cluster/explorer/tools/index.vue +3 -3
- package/pages/c/_cluster/settings/performance.vue +13 -26
- 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/pages/home.vue +313 -12
- package/plugins/__tests__/subscribe.events.test.ts +194 -0
- package/plugins/axios.js +2 -1
- package/plugins/dashboard-store/actions.js +4 -1
- package/plugins/dashboard-store/getters.js +1 -1
- package/plugins/dashboard-store/resource-class.js +20 -5
- 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/scripts/extension/publish +1 -1
- package/store/auth.js +8 -3
- package/store/aws.js +8 -6
- package/store/features.js +1 -0
- package/store/index.js +21 -25
- package/store/prefs.js +6 -0
- package/types/extension-manager.ts +8 -1
- package/types/kube/kube-api.ts +2 -1
- package/types/rancher/index.d.ts +1 -0
- package/types/resources/settings.d.ts +52 -23
- package/types/shell/index.d.ts +412 -336
- package/types/store/subscribe-events.types.ts +70 -0
- package/types/store/subscribe.types.ts +6 -22
- package/utils/__tests__/cluster.test.ts +379 -1
- package/utils/cluster.js +157 -3
- package/utils/dynamic-content/__tests__/config.test.ts +187 -0
- package/utils/dynamic-content/__tests__/index.test.ts +390 -0
- package/utils/dynamic-content/__tests__/info.test.ts +263 -0
- package/utils/dynamic-content/__tests__/new-release.test.ts +216 -0
- package/utils/dynamic-content/__tests__/support-notice.test.ts +262 -0
- package/utils/dynamic-content/__tests__/util.test.ts +235 -0
- package/utils/dynamic-content/config.ts +55 -0
- package/utils/dynamic-content/index.ts +273 -0
- package/utils/dynamic-content/info.ts +219 -0
- package/utils/dynamic-content/new-release.ts +126 -0
- package/utils/dynamic-content/support-notice.ts +169 -0
- package/utils/dynamic-content/types.d.ts +101 -0
- package/utils/dynamic-content/util.ts +122 -0
- package/utils/inactivity.ts +104 -0
- package/utils/pagination-utils.ts +105 -31
- package/utils/pagination-wrapper.ts +6 -8
- package/utils/release-notes.ts +1 -1
- 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
|
@@ -3,11 +3,14 @@ import ModalWithCard from '@shell/components/ModalWithCard';
|
|
|
3
3
|
import { Banner } from '@components/Banner';
|
|
4
4
|
import PercentageBar from '@shell/components/PercentageBar.vue';
|
|
5
5
|
import throttle from 'lodash/throttle';
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
6
|
+
import Inactivity from '@shell/utils/inactivity';
|
|
7
|
+
import { MANAGEMENT, EXT, NORMAN } from '@shell/config/types';
|
|
8
|
+
import { SETTING } from '@shell/config/settings';
|
|
8
9
|
|
|
9
10
|
let globalId;
|
|
10
11
|
|
|
12
|
+
const MODAL_VISIBILITY_CHECK_DELAY_SECONDS = 10;
|
|
13
|
+
|
|
11
14
|
export default {
|
|
12
15
|
name: 'Inactivity',
|
|
13
16
|
components: {
|
|
@@ -15,64 +18,151 @@ export default {
|
|
|
15
18
|
},
|
|
16
19
|
data() {
|
|
17
20
|
return {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
sessionTokenName: null,
|
|
22
|
+
tokens: [],
|
|
23
|
+
isUserActive: false,
|
|
24
|
+
userActivityIsoDate: '',
|
|
25
|
+
modalVisibilityCheckRan: false,
|
|
26
|
+
isOpen: false,
|
|
27
|
+
showModalAfter: null,
|
|
28
|
+
expiresAt: null,
|
|
29
|
+
inactivityTimeoutId: null,
|
|
30
|
+
courtesyTimer: null,
|
|
31
|
+
courtesyTimerId: null,
|
|
32
|
+
courtesyCountdown: null,
|
|
33
|
+
trackInactivity: throttle(this._trackInactivity, 1000),
|
|
34
|
+
id: null
|
|
28
35
|
};
|
|
29
36
|
},
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
37
|
+
beforeUnmount() {
|
|
38
|
+
this.removeEventListeners();
|
|
39
|
+
this.clearAllTimeouts();
|
|
40
|
+
},
|
|
41
|
+
computed: {
|
|
42
|
+
timerPercentageLeft() {
|
|
43
|
+
return Math.floor((this.courtesyCountdown / this.courtesyTimer ) * 100);
|
|
44
|
+
},
|
|
45
|
+
colorStops() {
|
|
46
|
+
return {
|
|
47
|
+
0: '--info', 30: '--info', 70: '--info'
|
|
48
|
+
};
|
|
49
|
+
},
|
|
50
|
+
userSessionTtlIdleSetting() {
|
|
51
|
+
return this.$store.getters['management/byId'](MANAGEMENT.SETTING, SETTING.AUTH_USER_SESSION_IDLE_TTL_MINUTES);
|
|
52
|
+
},
|
|
53
|
+
userSessionTtlSetting() {
|
|
54
|
+
return this.$store.getters['management/byId'](MANAGEMENT.SETTING, SETTING.AUTH_USER_SESSION_TTL_MINUTES);
|
|
55
|
+
},
|
|
56
|
+
userActivityResource() {
|
|
57
|
+
return this.$store.getters['management/byId'](EXT.USER_ACTIVITY, this.sessionTokenName);
|
|
58
|
+
},
|
|
59
|
+
ttlIdleValue() {
|
|
60
|
+
return parseInt(this.userSessionTtlIdleSetting?.value || 0);
|
|
61
|
+
},
|
|
62
|
+
ttlValue() {
|
|
63
|
+
return parseInt(this.userSessionTtlSetting?.value || 0);
|
|
64
|
+
},
|
|
65
|
+
isFeatureEnabled() {
|
|
66
|
+
return this.ttlIdleValue < this.ttlValue;
|
|
67
|
+
},
|
|
68
|
+
userActivityExpiresAt() {
|
|
69
|
+
return this.userActivityResource?.status?.expiresAt || '';
|
|
70
|
+
},
|
|
71
|
+
watcherData() {
|
|
72
|
+
return {
|
|
73
|
+
userActivityExpiresAt: this.userActivityExpiresAt,
|
|
74
|
+
sessionTokenName: this.sessionTokenName,
|
|
75
|
+
isFeatureEnabled: this.isFeatureEnabled
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
},
|
|
36
79
|
|
|
37
|
-
|
|
38
|
-
|
|
80
|
+
watch: {
|
|
81
|
+
// every time the Idle setting changes, we need to fetch the updated userActivity
|
|
82
|
+
async ttlIdleValue() {
|
|
83
|
+
await Inactivity.getUserActivity(this.$store, this.sessionTokenName);
|
|
84
|
+
},
|
|
85
|
+
watcherData: {
|
|
86
|
+
async handler(neu, old) {
|
|
87
|
+
if (!old?.isFeatureEnabled && neu?.isFeatureEnabled) {
|
|
88
|
+
const tokenName = Inactivity.getSessionTokenName();
|
|
39
89
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
90
|
+
if (tokenName) {
|
|
91
|
+
this.sessionTokenName = tokenName;
|
|
92
|
+
}
|
|
43
93
|
|
|
44
|
-
|
|
94
|
+
await this.initializeInactivityData();
|
|
95
|
+
}
|
|
45
96
|
|
|
46
|
-
|
|
47
|
-
|
|
97
|
+
const currDate = Date.now();
|
|
98
|
+
const endDate = new Date(neu.userActivityExpiresAt || '0001-01-01 00:00:00 +0000 UTC').getTime();
|
|
48
99
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
// Note - time before warning is shown + time warning is shown = settings threshold (total amount of time)
|
|
54
|
-
this.showModalAfter = thresholdToSeconds - this.courtesyTimer;
|
|
100
|
+
if (endDate > currDate && neu?.sessionTokenName && neu?.isFeatureEnabled) {
|
|
101
|
+
// feature is considered as enabled
|
|
102
|
+
// make sure we always clean up first so that we don't get duplicate timers running
|
|
103
|
+
this.stopInactivity();
|
|
55
104
|
|
|
56
|
-
|
|
105
|
+
// resets inactivity data and timers, starting the inactivity again with the proper data
|
|
106
|
+
this.resetInactivityDataAndTimers(this.userActivityResource);
|
|
57
107
|
|
|
58
|
-
|
|
108
|
+
// add event listeners for UI interaction
|
|
109
|
+
this.addIdleListeners();
|
|
110
|
+
}
|
|
59
111
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
112
|
+
if (!neu?.isFeatureEnabled) {
|
|
113
|
+
this.stopInactivity();
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
immediate: true,
|
|
117
|
+
deep: true
|
|
63
118
|
}
|
|
64
119
|
},
|
|
65
|
-
|
|
66
|
-
this.removeEventListener();
|
|
67
|
-
this.clearAllTimeouts();
|
|
68
|
-
},
|
|
120
|
+
|
|
69
121
|
methods: {
|
|
122
|
+
async initializeInactivityData() {
|
|
123
|
+
const canListUserAct = this.$store.getters[`management/canList`](EXT.USER_ACTIVITY);
|
|
124
|
+
const canListTokens = this.$store.getters[`rancher/canList`](NORMAN.TOKEN);
|
|
125
|
+
|
|
126
|
+
if (canListUserAct && canListTokens) {
|
|
127
|
+
const tokens = await this.$store.dispatch('rancher/findAll', { type: NORMAN.TOKEN, opt: { watch: false } });
|
|
128
|
+
|
|
129
|
+
this.tokens = tokens;
|
|
130
|
+
|
|
131
|
+
// handle the fetching/storage of session token name
|
|
132
|
+
if (!this.sessionTokenName) {
|
|
133
|
+
const sessionToken = this.tokens.find((token) => {
|
|
134
|
+
return token.description === 'UI session' && token.current;
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
if (sessionToken?.name) {
|
|
138
|
+
this.sessionTokenName = sessionToken.name;
|
|
139
|
+
Inactivity.setSessionTokenName(sessionToken.name);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// get the latest userActivity data so that get reactivity on all this logic
|
|
144
|
+
const userActivityData = await Inactivity.getUserActivity(this.$store, this.sessionTokenName, false);
|
|
145
|
+
|
|
146
|
+
const expiresAt = userActivityData?.status?.expiresAt;
|
|
147
|
+
const currDate = Date.now();
|
|
148
|
+
const endDate = new Date(expiresAt).getTime();
|
|
149
|
+
|
|
150
|
+
// If expiresAt isn't initialised yet '0001-01-01 00:00:00 +0000 UTC' || '', or just passed the 'now' date
|
|
151
|
+
// We need to update/initialise the UserActivity resource
|
|
152
|
+
if ((currDate > endDate) || !expiresAt) {
|
|
153
|
+
const updatedData = await Inactivity.updateUserActivity(this.userActivityResource, this.sessionTokenName, new Date().toISOString());
|
|
154
|
+
|
|
155
|
+
this.expiresAt = updatedData?.status?.expiresAt;
|
|
156
|
+
} else if (expiresAt) {
|
|
157
|
+
this.expiresAt = expiresAt;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
},
|
|
70
161
|
_trackInactivity() {
|
|
71
|
-
if (this.
|
|
162
|
+
if (this.isOpen || !this.showModalAfter) {
|
|
72
163
|
return;
|
|
73
164
|
}
|
|
74
165
|
|
|
75
|
-
this.clearAllTimeouts();
|
|
76
166
|
const endTime = Date.now() + this.showModalAfter * 1000;
|
|
77
167
|
|
|
78
168
|
this.id = endTime;
|
|
@@ -89,6 +179,18 @@ export default {
|
|
|
89
179
|
this.isOpen = true;
|
|
90
180
|
this.startCountdown();
|
|
91
181
|
} else {
|
|
182
|
+
// When we have X seconds to go until we display the modal, check for activity on the backend flag
|
|
183
|
+
// it may have come from another tab in the same browser
|
|
184
|
+
if (now >= endTime - (MODAL_VISIBILITY_CHECK_DELAY_SECONDS * 1000) && !this.modalVisibilityCheckRan) {
|
|
185
|
+
this.modalVisibilityCheckRan = true;
|
|
186
|
+
|
|
187
|
+
if (this.isUserActive) {
|
|
188
|
+
this.resetUserActivity();
|
|
189
|
+
} else {
|
|
190
|
+
this.checkBackendInactivity(this.expiresAt);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
92
194
|
this.inactivityTimeoutId = setTimeout(checkInactivityTimer, 1000);
|
|
93
195
|
}
|
|
94
196
|
};
|
|
@@ -98,13 +200,16 @@ export default {
|
|
|
98
200
|
startCountdown() {
|
|
99
201
|
const endTime = Date.now() + (this.courtesyCountdown * 1000);
|
|
100
202
|
|
|
101
|
-
const checkCountdown = () => {
|
|
203
|
+
const checkCountdown = async() => {
|
|
102
204
|
const now = Date.now();
|
|
103
205
|
|
|
104
206
|
if (now >= endTime) {
|
|
105
|
-
this.isInactive = true;
|
|
106
|
-
this.unsubscribe();
|
|
107
207
|
this.clearAllTimeouts();
|
|
208
|
+
const isUserActive = await this.checkBackendInactivity(this.expiresAt);
|
|
209
|
+
|
|
210
|
+
if (!isUserActive) {
|
|
211
|
+
return this.$store.dispatch('auth/logout', { sessionIdle: true });
|
|
212
|
+
}
|
|
108
213
|
} else {
|
|
109
214
|
this.courtesyCountdown = Math.floor((endTime - now) / 1000);
|
|
110
215
|
this.courtesyTimerId = setTimeout(checkCountdown, 1000);
|
|
@@ -113,63 +218,85 @@ export default {
|
|
|
113
218
|
|
|
114
219
|
checkCountdown();
|
|
115
220
|
},
|
|
221
|
+
async checkBackendInactivity(currExpiresAt) {
|
|
222
|
+
let isUserActive = false;
|
|
223
|
+
const userActivityData = await Inactivity.getUserActivity(this.$store, this.sessionTokenName);
|
|
224
|
+
|
|
225
|
+
// this means that something updated the backend expiresAt, which means we must now reset the timers and adjust for new data
|
|
226
|
+
if (userActivityData?.status?.expiresAt && (userActivityData?.status?.expiresAt !== currExpiresAt)) {
|
|
227
|
+
isUserActive = true;
|
|
228
|
+
this.resetInactivityDataAndTimers(userActivityData);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return isUserActive;
|
|
232
|
+
},
|
|
233
|
+
setUserAsActive() {
|
|
234
|
+
this.isUserActive = true;
|
|
235
|
+
this.userActivityIsoDate = new Date().toISOString();
|
|
236
|
+
},
|
|
116
237
|
addIdleListeners() {
|
|
117
|
-
document.addEventListener('mousemove', this.
|
|
118
|
-
document.addEventListener('mousedown', this.
|
|
119
|
-
document.addEventListener('keypress', this.
|
|
120
|
-
document.addEventListener('touchmove', this.
|
|
121
|
-
document.addEventListener('visibilitychange', this.
|
|
122
|
-
},
|
|
123
|
-
|
|
124
|
-
document.removeEventListener('mousemove', this.
|
|
125
|
-
document.removeEventListener('mousedown', this.
|
|
126
|
-
document.removeEventListener('keypress', this.
|
|
127
|
-
document.removeEventListener('touchmove', this.
|
|
128
|
-
document.removeEventListener('visibilitychange', this.
|
|
129
|
-
},
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
238
|
+
document.addEventListener('mousemove', this.setUserAsActive);
|
|
239
|
+
document.addEventListener('mousedown', this.setUserAsActive);
|
|
240
|
+
document.addEventListener('keypress', this.setUserAsActive);
|
|
241
|
+
document.addEventListener('touchmove', this.setUserAsActive);
|
|
242
|
+
document.addEventListener('visibilitychange', this.setUserAsActive);
|
|
243
|
+
},
|
|
244
|
+
removeEventListeners() {
|
|
245
|
+
document.removeEventListener('mousemove', this.setUserAsActive);
|
|
246
|
+
document.removeEventListener('mousedown', this.setUserAsActive);
|
|
247
|
+
document.removeEventListener('keypress', this.setUserAsActive);
|
|
248
|
+
document.removeEventListener('touchmove', this.setUserAsActive);
|
|
249
|
+
document.removeEventListener('visibilitychange', this.setUserAsActive);
|
|
250
|
+
},
|
|
251
|
+
async resetUserActivity(useCurrDate = false) {
|
|
252
|
+
let seenAt;
|
|
253
|
+
|
|
254
|
+
if (useCurrDate) {
|
|
255
|
+
seenAt = new Date().toISOString();
|
|
256
|
+
} else {
|
|
257
|
+
seenAt = this.userActivityIsoDate;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const userActivityData = await Inactivity.updateUserActivity(this.userActivityResource, this.sessionTokenName, seenAt);
|
|
261
|
+
|
|
262
|
+
this.resetInactivityDataAndTimers(userActivityData);
|
|
263
|
+
},
|
|
264
|
+
stopInactivity() {
|
|
265
|
+
this.modalVisibilityCheckRan = false;
|
|
133
266
|
this.isOpen = false;
|
|
134
|
-
this.
|
|
267
|
+
this.isUserActive = false;
|
|
268
|
+
this.userActivityIsoDate = '';
|
|
269
|
+
|
|
270
|
+
this.removeEventListeners();
|
|
135
271
|
this.clearAllTimeouts();
|
|
136
272
|
},
|
|
273
|
+
resetInactivityDataAndTimers(userActivityData) {
|
|
274
|
+
const data = Inactivity.parseTTLData(userActivityData);
|
|
137
275
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
276
|
+
this.modalVisibilityCheckRan = false;
|
|
277
|
+
this.isOpen = false;
|
|
278
|
+
this.isUserActive = false;
|
|
279
|
+
this.userActivityIsoDate = '';
|
|
141
280
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
this
|
|
281
|
+
this.courtesyTimer = data.courtesyTimer;
|
|
282
|
+
this.courtesyCountdown = data.courtesyCountdown;
|
|
283
|
+
this.showModalAfter = data.showModalAfter;
|
|
284
|
+
this.sessionTokenName = data.sessionTokenName;
|
|
285
|
+
this.expiresAt = data.expiresAt;
|
|
286
|
+
|
|
287
|
+
const shownAfter = Math.round(((data.showModalAfter || 0) / 60) * 100) / 100;
|
|
288
|
+
const shownFor = Math.round(((data.courtesyTimer || 0) / 60) * 100) / 100;
|
|
289
|
+
|
|
290
|
+
console.debug(`UI inactivity modal (backend-based) will show after ${ shownAfter }(m) and be shown for ${ shownFor }(m)`); // eslint-disable-line no-console
|
|
291
|
+
|
|
292
|
+
this.clearAllTimeouts();
|
|
293
|
+
this.trackInactivity();
|
|
145
294
|
},
|
|
146
295
|
clearAllTimeouts() {
|
|
147
296
|
clearTimeout(this.inactivityTimeoutId);
|
|
148
297
|
clearTimeout(this.courtesyTimerId);
|
|
149
298
|
}
|
|
150
|
-
|
|
151
299
|
},
|
|
152
|
-
computed: {
|
|
153
|
-
isInactiveTexts() {
|
|
154
|
-
return this.isInactive ? {
|
|
155
|
-
title: this.t('inactivity.titleExpired'),
|
|
156
|
-
banner: this.t('inactivity.bannerExpired'),
|
|
157
|
-
content: this.t('inactivity.contentExpired'),
|
|
158
|
-
} : {
|
|
159
|
-
title: this.t('inactivity.title'),
|
|
160
|
-
banner: this.t('inactivity.banner'),
|
|
161
|
-
content: this.t('inactivity.content'),
|
|
162
|
-
};
|
|
163
|
-
},
|
|
164
|
-
timerPercentageLeft() {
|
|
165
|
-
return Math.floor((this.courtesyCountdown / this.courtesyTimer ) * 100);
|
|
166
|
-
},
|
|
167
|
-
colorStops() {
|
|
168
|
-
return {
|
|
169
|
-
0: '--info', 30: '--info', 70: '--info'
|
|
170
|
-
};
|
|
171
|
-
},
|
|
172
|
-
}
|
|
173
300
|
};
|
|
174
301
|
</script>
|
|
175
302
|
|
|
@@ -179,24 +306,22 @@ export default {
|
|
|
179
306
|
ref="inactivityModal"
|
|
180
307
|
name="inactivityModal"
|
|
181
308
|
save-text="Continue"
|
|
182
|
-
@finish="resume"
|
|
183
309
|
>
|
|
184
310
|
<template #title>
|
|
185
|
-
{{
|
|
311
|
+
{{ t('inactivity.title') }}
|
|
186
312
|
</template>
|
|
187
313
|
<span>{{ courtesyCountdown }}</span>
|
|
188
314
|
|
|
189
315
|
<template #content>
|
|
190
316
|
<Banner color="info">
|
|
191
|
-
{{
|
|
317
|
+
{{ t('inactivity.banner') }}
|
|
192
318
|
</Banner>
|
|
193
319
|
|
|
194
320
|
<p>
|
|
195
|
-
{{
|
|
321
|
+
{{ t('inactivity.content') }}
|
|
196
322
|
</p>
|
|
197
323
|
|
|
198
324
|
<PercentageBar
|
|
199
|
-
v-if="!isInactive"
|
|
200
325
|
class="mt-20"
|
|
201
326
|
:modelValue="timerPercentageLeft"
|
|
202
327
|
:color-stops="colorStops"
|
|
@@ -208,20 +333,11 @@ export default {
|
|
|
208
333
|
>
|
|
209
334
|
<div class="card-actions">
|
|
210
335
|
<button
|
|
211
|
-
v-if="!isInactive"
|
|
212
336
|
class="btn role-tertiary bg-primary"
|
|
213
|
-
@click.prevent="
|
|
337
|
+
@click.prevent="resetUserActivity(true)"
|
|
214
338
|
>
|
|
215
339
|
<t k="inactivity.cta" />
|
|
216
340
|
</button>
|
|
217
|
-
|
|
218
|
-
<button
|
|
219
|
-
v-if="isInactive"
|
|
220
|
-
class="btn role-tertiary bg-primary"
|
|
221
|
-
@click.prevent="refresh"
|
|
222
|
-
>
|
|
223
|
-
<t k="inactivity.ctaExpired" />
|
|
224
|
-
</button>
|
|
225
341
|
</div>
|
|
226
342
|
</template>
|
|
227
343
|
</ModalWithCard>
|
|
@@ -117,7 +117,7 @@ export default {
|
|
|
117
117
|
|
|
118
118
|
// server url and project ids are used in global values
|
|
119
119
|
try {
|
|
120
|
-
this.serverUrlSetting = await this.$store.dispatch(
|
|
120
|
+
this.serverUrlSetting = await this.$store.dispatch(`management/find`, {
|
|
121
121
|
type: MANAGEMENT.SETTING,
|
|
122
122
|
id: SETTING.SERVER_URL,
|
|
123
123
|
});
|
|
@@ -125,7 +125,7 @@ export default {
|
|
|
125
125
|
this.$store.dispatch('growl/fromError', { err: e });
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
await this.$store.dispatch(
|
|
128
|
+
await this.$store.dispatch(`management/findAll`, { type: MANAGEMENT.PROJECT });
|
|
129
129
|
}
|
|
130
130
|
},
|
|
131
131
|
|
|
@@ -181,6 +181,7 @@ export default {
|
|
|
181
181
|
if (e.status === 404 || e.status === 403) {
|
|
182
182
|
store.dispatch('loadingError', new Error(this.t('nav.failWhale.resourceIdNotFound', { resource: resourceType, fqid }, true)));
|
|
183
183
|
}
|
|
184
|
+
console.debug(`Could not find '${ resourceType }' with id '${ id }''`, e); // eslint-disable-line no-console
|
|
184
185
|
liveModel = {};
|
|
185
186
|
notFound = fqid;
|
|
186
187
|
}
|
|
@@ -480,7 +481,7 @@ export default {
|
|
|
480
481
|
</div>
|
|
481
482
|
|
|
482
483
|
<ResourceYaml
|
|
483
|
-
v-
|
|
484
|
+
v-if="isYaml"
|
|
484
485
|
ref="resourceyaml"
|
|
485
486
|
:value="value"
|
|
486
487
|
:mode="mode"
|
|
@@ -1090,7 +1090,7 @@ export default {
|
|
|
1090
1090
|
<div
|
|
1091
1091
|
v-if="showHeaderRow"
|
|
1092
1092
|
class="fixed-header-actions"
|
|
1093
|
-
:class="{button: !!$slots['header-button'], 'advanced-filtering': hasAdvancedFiltering}"
|
|
1093
|
+
:class="{button: !!$slots['header-button'], 'with-sub-header': !!$slots['sub-header-row'], 'advanced-filtering': hasAdvancedFiltering}"
|
|
1094
1094
|
>
|
|
1095
1095
|
<div
|
|
1096
1096
|
:class="bulkActionsClass"
|
|
@@ -1296,6 +1296,12 @@ export default {
|
|
|
1296
1296
|
<slot name="header-button" />
|
|
1297
1297
|
</div>
|
|
1298
1298
|
</div>
|
|
1299
|
+
<div
|
|
1300
|
+
v-if="!!$slots['sub-header-row']"
|
|
1301
|
+
class="sub-header-row"
|
|
1302
|
+
>
|
|
1303
|
+
<slot name="sub-header-row" />
|
|
1304
|
+
</div>
|
|
1299
1305
|
</div>
|
|
1300
1306
|
<table
|
|
1301
1307
|
ref="table"
|
|
@@ -2047,8 +2053,17 @@ export default {
|
|
|
2047
2053
|
grid-template-columns: [bulk] auto [middle] min-content [search] minmax(min-content, 350px);
|
|
2048
2054
|
}
|
|
2049
2055
|
|
|
2056
|
+
$header-padding: 20px;
|
|
2057
|
+
.sub-header-row {
|
|
2058
|
+
padding: 0 0 $header-padding / 2 0;
|
|
2059
|
+
}
|
|
2060
|
+
|
|
2050
2061
|
.fixed-header-actions {
|
|
2051
|
-
padding: 0 0
|
|
2062
|
+
padding: 0 0 $header-padding 0;
|
|
2063
|
+
&.with-sub-header {
|
|
2064
|
+
padding: 0 0 $header-padding / 4 0;
|
|
2065
|
+
}
|
|
2066
|
+
|
|
2052
2067
|
width: 100%;
|
|
2053
2068
|
z-index: z-index('fixedTableHeader');
|
|
2054
2069
|
background: transparent;
|
|
@@ -68,6 +68,7 @@ export default {
|
|
|
68
68
|
|
|
69
69
|
data() {
|
|
70
70
|
let sortBy = null;
|
|
71
|
+
let descending = false;
|
|
71
72
|
|
|
72
73
|
this._defaultSortBy = this.defaultSortBy;
|
|
73
74
|
|
|
@@ -78,6 +79,7 @@ export default {
|
|
|
78
79
|
|
|
79
80
|
if ( markedColumn ) {
|
|
80
81
|
this._defaultSortBy = markedColumn.name;
|
|
82
|
+
descending = markedColumn.defaultSortDescending;
|
|
81
83
|
} else if ( nameColumn ) {
|
|
82
84
|
// Use the name column if there is one
|
|
83
85
|
this._defaultSortBy = nameColumn.name;
|
|
@@ -101,7 +103,7 @@ export default {
|
|
|
101
103
|
|
|
102
104
|
return {
|
|
103
105
|
sortBy,
|
|
104
|
-
descending
|
|
106
|
+
descending,
|
|
105
107
|
cachedRows: null,
|
|
106
108
|
cacheKey: null,
|
|
107
109
|
};
|
|
@@ -412,7 +412,7 @@ export default {
|
|
|
412
412
|
}
|
|
413
413
|
|
|
414
414
|
.tab.active {
|
|
415
|
-
border-bottom: solid 2px var(--primary);
|
|
415
|
+
border-bottom: solid 2px var(--active, var(--primary));
|
|
416
416
|
}
|
|
417
417
|
}
|
|
418
418
|
|
|
@@ -450,7 +450,7 @@ export default {
|
|
|
450
450
|
|
|
451
451
|
&.active {
|
|
452
452
|
> A {
|
|
453
|
-
color: var(--primary);
|
|
453
|
+
color: var(--active, var(--primary));
|
|
454
454
|
text-decoration: none;
|
|
455
455
|
}
|
|
456
456
|
}
|
|
@@ -530,16 +530,16 @@ export default {
|
|
|
530
530
|
border-left: solid 5px transparent;
|
|
531
531
|
|
|
532
532
|
&.toggle A {
|
|
533
|
-
color: var(--primary);
|
|
533
|
+
color: var(--active, var(--primary));
|
|
534
534
|
}
|
|
535
535
|
|
|
536
536
|
A {
|
|
537
|
-
color: var(--primary);
|
|
537
|
+
color: var(--link, var(--primary));
|
|
538
538
|
}
|
|
539
539
|
|
|
540
540
|
&.active {
|
|
541
541
|
background-color: var(--body-bg);
|
|
542
|
-
border-left: solid 5px var(--primary);
|
|
542
|
+
border-left: solid 5px var(--active, var(--primary));
|
|
543
543
|
|
|
544
544
|
& A {
|
|
545
545
|
color: var(--input-label);
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { ref } from 'vue';
|
|
3
|
+
import { _EDIT } from '@shell/config/query-params';
|
|
4
|
+
import { CONFIG_MAP } from '@shell/config/types';
|
|
5
|
+
import { PaginationParamFilter } from '@shell/types/store/pagination.types';
|
|
6
|
+
import ResourceLabeledSelect from '@shell/components/form/ResourceLabeledSelect.vue';
|
|
7
|
+
|
|
8
|
+
interface ConfigMap {
|
|
9
|
+
id?: string;
|
|
10
|
+
name: string;
|
|
11
|
+
namespace: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const props = defineProps({
|
|
15
|
+
value: {
|
|
16
|
+
type: Object,
|
|
17
|
+
required: true,
|
|
18
|
+
},
|
|
19
|
+
namespace: {
|
|
20
|
+
type: String,
|
|
21
|
+
required: true,
|
|
22
|
+
},
|
|
23
|
+
inStore: {
|
|
24
|
+
type: String,
|
|
25
|
+
default: 'management',
|
|
26
|
+
},
|
|
27
|
+
mode: {
|
|
28
|
+
type: String,
|
|
29
|
+
default: _EDIT
|
|
30
|
+
},
|
|
31
|
+
label: {
|
|
32
|
+
type: String,
|
|
33
|
+
default: '',
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const emit = defineEmits(['update:value']);
|
|
38
|
+
|
|
39
|
+
const configMaps = ref<ConfigMap[]>([]);
|
|
40
|
+
|
|
41
|
+
const allConfigMapsSettings = {
|
|
42
|
+
updateResources: (configMapsList: ConfigMap[]) => {
|
|
43
|
+
const allConfigMapsInNamespace = configMapsList.filter((configMap) => configMap.namespace === props.namespace);
|
|
44
|
+
const mappedConfigMaps = mapConfigMaps(allConfigMapsInNamespace.sort((a, b) => a.name.localeCompare(b.name)));
|
|
45
|
+
|
|
46
|
+
configMaps.value = allConfigMapsInNamespace;
|
|
47
|
+
|
|
48
|
+
return mappedConfigMaps;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const paginateConfigMapsSetting = {
|
|
53
|
+
requestSettings: paginatePageOptions,
|
|
54
|
+
updateResources: (configMapsList: ConfigMap[]) => {
|
|
55
|
+
const mappedConfigMaps = mapConfigMaps(configMapsList);
|
|
56
|
+
|
|
57
|
+
configMaps.value = configMapsList;
|
|
58
|
+
|
|
59
|
+
return mappedConfigMaps;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
function mapConfigMaps(configMapsList: ConfigMap[]) {
|
|
64
|
+
return configMapsList.reduce<{ label: string; value: string }[]>((res, c) => {
|
|
65
|
+
if (c.id) {
|
|
66
|
+
res.push({ label: c.name, value: c.name });
|
|
67
|
+
} else {
|
|
68
|
+
res.push(c as any);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return res;
|
|
72
|
+
}, []);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function paginatePageOptions(opts: any) {
|
|
76
|
+
const { opts: { filter } } = opts;
|
|
77
|
+
|
|
78
|
+
const filters = !!filter ? [PaginationParamFilter.createSingleField({
|
|
79
|
+
field: 'metadata.name', value: filter, exact: false, equals: true
|
|
80
|
+
})] : [];
|
|
81
|
+
|
|
82
|
+
filters.push(
|
|
83
|
+
PaginationParamFilter.createSingleField({ field: 'metadata.namespace', value: props.namespace }),
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
...opts,
|
|
88
|
+
filters,
|
|
89
|
+
groupByNamespace: false,
|
|
90
|
+
classify: true,
|
|
91
|
+
sort: [{ asc: true, field: 'metadata.name' }],
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function update(value: any) {
|
|
96
|
+
emit('update:value', value);
|
|
97
|
+
}
|
|
98
|
+
</script>
|
|
99
|
+
|
|
100
|
+
<template>
|
|
101
|
+
<ResourceLabeledSelect
|
|
102
|
+
:key="namespace"
|
|
103
|
+
:value="value"
|
|
104
|
+
:label="label || t('fleet.configMaps.label')"
|
|
105
|
+
:mode="mode"
|
|
106
|
+
:resource-type="CONFIG_MAP"
|
|
107
|
+
:loading="$fetchState.pending"
|
|
108
|
+
:in-store="inStore"
|
|
109
|
+
:paginated-resource-settings="paginateConfigMapsSetting"
|
|
110
|
+
:all-resources-settings="allConfigMapsSettings"
|
|
111
|
+
:multiple="true"
|
|
112
|
+
@update:value="update"
|
|
113
|
+
/>
|
|
114
|
+
</template>
|
|
115
|
+
|
|
116
|
+
<style lang="scss" scoped>
|
|
117
|
+
</style>
|