@rancher/shell 3.0.7 → 3.0.8-rc.2
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/vendor/githubapp.svg +13 -0
- package/assets/styles/base/_typography.scss +1 -1
- package/assets/styles/global/_layout.scss +21 -35
- package/assets/styles/themes/_modern.scss +5 -5
- package/assets/translations/en-us.yaml +102 -17
- package/assets/translations/zh-hans.yaml +0 -4
- package/components/EmberPage.vue +1 -1
- package/components/Inactivity.vue +222 -106
- package/components/InstallHelmCharts.vue +2 -2
- package/components/Resource/Detail/CopyToClipboard.vue +1 -1
- package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +0 -2
- package/components/Resource/Detail/TitleBar/index.vue +10 -6
- package/components/ResourceDetail/index.vue +4 -1
- package/components/SortableTable/index.vue +18 -2
- package/components/{nav/WindowManager → Window}/ContainerLogs.vue +1 -1
- package/components/{nav/WindowManager → Window}/ContainerLogsActions.vue +1 -0
- package/components/{nav/WindowManager → Window}/__tests__/ContainerLogs.test.ts +1 -1
- package/components/{nav/WindowManager → Window}/__tests__/ContainerShell.test.ts +2 -2
- 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/__tests__/ResourceLabeledSelect.test.ts +90 -0
- package/components/nav/Header.vue +34 -13
- package/components/{DraggableZone.vue → nav/WindowManager/PinArea.vue} +47 -80
- package/components/nav/WindowManager/composables/useComponentsMount.ts +70 -0
- package/components/nav/WindowManager/composables/useDimensionsHandler.ts +105 -0
- package/components/nav/WindowManager/composables/useDragHandler.ts +99 -0
- package/components/nav/WindowManager/composables/usePanelHandler.ts +72 -0
- package/components/nav/WindowManager/composables/usePanelsHandler.ts +14 -0
- package/components/nav/WindowManager/composables/useResizeHandler.ts +167 -0
- package/components/nav/WindowManager/composables/useTabsHandler.ts +51 -0
- package/components/nav/WindowManager/constants.ts +23 -0
- package/components/nav/WindowManager/index.vue +61 -575
- package/components/nav/WindowManager/panels/HorizontalPanel.vue +265 -0
- package/components/nav/WindowManager/panels/TabBodyContainer.vue +39 -0
- package/components/nav/WindowManager/panels/VerticalPanel.vue +308 -0
- package/components/templates/default.vue +4 -40
- package/components/templates/home.vue +31 -5
- package/config/product/auth.js +1 -0
- package/config/query-params.js +1 -0
- package/config/settings.ts +8 -1
- package/config/store.js +4 -2
- package/config/types.js +2 -0
- package/detail/pod.vue +1 -0
- package/dialog/AddonConfigConfirmationDialog.vue +45 -1
- package/directives/ui-context.ts +97 -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/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/initialize/install-directives.js +2 -0
- package/list/projectsecret.vue +1 -1
- package/machine-config/azure.vue +1 -1
- package/mixins/chart.js +1 -1
- 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/management.cattle.io.authconfig.js +1 -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 +89 -3
- package/pages/c/_cluster/explorer/tools/index.vue +3 -3
- package/pages/c/_cluster/settings/performance.vue +12 -25
- package/pages/home.vue +313 -12
- package/plugins/axios.js +2 -1
- package/plugins/dashboard-store/actions.js +1 -1
- package/plugins/dashboard-store/resource-class.js +17 -2
- package/plugins/steve/steve-pagination-utils.ts +2 -2
- package/rancher-components/RcDropdown/RcDropdownItemSelect.vue +5 -1
- 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 +9 -3
- package/store/prefs.js +6 -0
- package/store/ui-context.ts +86 -0
- package/store/wm.ts +244 -0
- package/types/kube/kube-api.ts +2 -1
- package/types/rancher/index.d.ts +1 -0
- package/types/resources/settings.d.ts +29 -7
- package/types/shell/index.d.ts +59 -0
- package/types/window-manager.ts +22 -0
- 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/dynamic-importer.js +2 -2
- package/utils/inactivity.ts +104 -0
- package/utils/pagination-utils.ts +19 -4
- package/utils/release-notes.ts +1 -1
- package/assets/images/icons/document.svg +0 -3
- package/store/wm.js +0 -95
- /package/components/{nav/WindowManager → Window}/ChartReadme.vue +0 -0
- /package/components/{nav/WindowManager → Window}/ContainerShell.vue +0 -0
- /package/components/{nav/WindowManager → Window}/KubectlShell.vue +0 -0
- /package/components/{nav/WindowManager → Window}/MachineSsh.vue +0 -0
- /package/components/{nav/WindowManager → Window}/Window.vue +0 -0
|
@@ -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
|
|
|
@@ -3,8 +3,6 @@ import TitleBar from '@shell/components/Resource/Detail/TitleBar/index.vue';
|
|
|
3
3
|
import ActionMenu from '@shell/components/ActionMenuShell.vue';
|
|
4
4
|
import { createStore } from 'vuex';
|
|
5
5
|
|
|
6
|
-
jest.mock(`@shell/assets/images/icons/document.svg`, () => `@shell/assets/images/icons/document.svg`);
|
|
7
|
-
|
|
8
6
|
describe('component: TitleBar/index', () => {
|
|
9
7
|
const resourceTypeLabel = 'RESOURCE_TYPE_LABEL';
|
|
10
8
|
const resourceTo = 'RESOURCE_TO';
|
|
@@ -32,8 +32,6 @@ export interface TitleBarProps {
|
|
|
32
32
|
actionMenuResource?: any;
|
|
33
33
|
onShowConfiguration?: (returnFocusSelector: string) => void;
|
|
34
34
|
}
|
|
35
|
-
|
|
36
|
-
const showConfigurationIcon = require(`@shell/assets/images/icons/document.svg`);
|
|
37
35
|
</script>
|
|
38
36
|
|
|
39
37
|
<script setup lang="ts">
|
|
@@ -84,6 +82,7 @@ watch(
|
|
|
84
82
|
</span>
|
|
85
83
|
<BadgeState
|
|
86
84
|
v-if="badge"
|
|
85
|
+
v-ui-context="{ store: store, icon: 'icon-folder', hookable: true, value: resource, tag: '__details-state', description: 'Details' }"
|
|
87
86
|
class="badge-state"
|
|
88
87
|
:color="badge.color"
|
|
89
88
|
:label="badge.label"
|
|
@@ -99,11 +98,10 @@ watch(
|
|
|
99
98
|
:aria-label="i18n.t('component.resource.detail.titleBar.ariaLabel.showConfiguration', { resource: resourceName })"
|
|
100
99
|
@click="() => emit('show-configuration', showConfigurationReturnFocusSelector)"
|
|
101
100
|
>
|
|
102
|
-
<
|
|
103
|
-
|
|
104
|
-
class="mmr-3"
|
|
101
|
+
<i
|
|
102
|
+
class="icon icon-document"
|
|
105
103
|
aria-hidden="true"
|
|
106
|
-
|
|
104
|
+
/>
|
|
107
105
|
{{ i18n.t('component.resource.detail.titleBar.showConfiguration') }}
|
|
108
106
|
</RcButton>
|
|
109
107
|
<ActionMenu
|
|
@@ -139,6 +137,12 @@ watch(
|
|
|
139
137
|
position: relative;
|
|
140
138
|
}
|
|
141
139
|
|
|
140
|
+
.icon-document {
|
|
141
|
+
width: 15px;
|
|
142
|
+
font-size: 16px;
|
|
143
|
+
margin-right: 10px;
|
|
144
|
+
}
|
|
145
|
+
|
|
142
146
|
.show-configuration {
|
|
143
147
|
margin-left: 16px;
|
|
144
148
|
}
|
|
@@ -430,6 +430,7 @@ export default {
|
|
|
430
430
|
:is="showComponent"
|
|
431
431
|
v-else-if="isFullPageOverride"
|
|
432
432
|
v-model:value="value"
|
|
433
|
+
v-ui-context="{ icon: 'icon-folder', value: value.name, tag: value.kind?.toLowerCase(), description: value.kind }"
|
|
433
434
|
v-bind="$data"
|
|
434
435
|
:done-params="doneParams"
|
|
435
436
|
:done-route="doneRoute"
|
|
@@ -446,6 +447,7 @@ export default {
|
|
|
446
447
|
<div v-else>
|
|
447
448
|
<Masthead
|
|
448
449
|
v-if="showMasthead"
|
|
450
|
+
v-ui-context="{ icon: 'icon-folder', value: liveModel.name, tag: liveModel.kind?.toLowerCase(), description: liveModel.kind }"
|
|
449
451
|
:resource="resourceType"
|
|
450
452
|
:value="liveModel"
|
|
451
453
|
:mode="mode"
|
|
@@ -481,7 +483,7 @@ export default {
|
|
|
481
483
|
</div>
|
|
482
484
|
|
|
483
485
|
<ResourceYaml
|
|
484
|
-
v-
|
|
486
|
+
v-if="isYaml"
|
|
485
487
|
ref="resourceyaml"
|
|
486
488
|
:value="value"
|
|
487
489
|
:mode="mode"
|
|
@@ -499,6 +501,7 @@ export default {
|
|
|
499
501
|
v-else
|
|
500
502
|
ref="comp"
|
|
501
503
|
v-model:value="value"
|
|
504
|
+
v-ui-context="{ icon: 'icon-folder', value: value.name, tag: value.kind?.toLowerCase(), description: value.kind }"
|
|
502
505
|
v-bind="$data"
|
|
503
506
|
:done-params="doneParams"
|
|
504
507
|
:done-route="doneRoute"
|
|
@@ -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"
|
|
@@ -1468,6 +1474,7 @@ export default {
|
|
|
1468
1474
|
<td
|
|
1469
1475
|
v-show="!hasAdvancedFiltering || (hasAdvancedFiltering && col.col.isColVisible)"
|
|
1470
1476
|
:key="col.col.name"
|
|
1477
|
+
v-ui-context="col.col.name === 'state' ? { icon: 'icon-folder', hookable: true, value: row.row, tag: '__sortable-table-row', description: 'Row' } : undefined"
|
|
1471
1478
|
:data-title="col.col.label"
|
|
1472
1479
|
:data-testid="`sortable-cell-${ i }-${ j }`"
|
|
1473
1480
|
:align="col.col.align || 'left'"
|
|
@@ -2047,8 +2054,17 @@ export default {
|
|
|
2047
2054
|
grid-template-columns: [bulk] auto [middle] min-content [search] minmax(min-content, 350px);
|
|
2048
2055
|
}
|
|
2049
2056
|
|
|
2057
|
+
$header-padding: 20px;
|
|
2058
|
+
.sub-header-row {
|
|
2059
|
+
padding: 0 0 $header-padding / 2 0;
|
|
2060
|
+
}
|
|
2061
|
+
|
|
2050
2062
|
.fixed-header-actions {
|
|
2051
|
-
padding: 0 0
|
|
2063
|
+
padding: 0 0 $header-padding 0;
|
|
2064
|
+
&.with-sub-header {
|
|
2065
|
+
padding: 0 0 $header-padding / 4 0;
|
|
2066
|
+
}
|
|
2067
|
+
|
|
2052
2068
|
width: 100%;
|
|
2053
2069
|
z-index: z-index('fixedTableHeader');
|
|
2054
2070
|
background: transparent;
|
|
@@ -10,7 +10,7 @@ import AsyncButton from '@shell/components/AsyncButton';
|
|
|
10
10
|
import Select from '@shell/components/form/Select';
|
|
11
11
|
import VirtualList from 'vue3-virtual-scroll-list';
|
|
12
12
|
import LogItem from '@shell/components/LogItem';
|
|
13
|
-
import ContainerLogsActions from '@shell/components/
|
|
13
|
+
import ContainerLogsActions from '@shell/components/Window/ContainerLogsActions.vue';
|
|
14
14
|
import { shallowRef } from 'vue';
|
|
15
15
|
import { useStore } from 'vuex';
|
|
16
16
|
import { debounce } from 'lodash';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { nextTick } from 'vue';
|
|
2
2
|
import { shallowMount } from '@vue/test-utils';
|
|
3
|
-
import ContainerLogs from '@shell/components/
|
|
3
|
+
import ContainerLogs from '@shell/components/Window/ContainerLogs.vue';
|
|
4
4
|
import { base64Encode } from '@shell/utils/crypto';
|
|
5
5
|
import { Buffer } from 'buffer';
|
|
6
6
|
import { addEventListener } from '@shell/utils/socket';
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { flushPromises, mount, Wrapper } from '@vue/test-utils';
|
|
2
|
-
import ContainerShell from '@shell/components/
|
|
2
|
+
import ContainerShell from '@shell/components/Window/ContainerShell.vue';
|
|
3
3
|
import Socket, {
|
|
4
4
|
addEventListener, EVENT_CONNECTED, EVENT_CONNECTING, EVENT_DISCONNECTED, EVENT_MESSAGE, EVENT_CONNECT_ERROR
|
|
5
5
|
} from '@shell/utils/socket';
|
|
6
|
-
import Window from '@shell/components/
|
|
6
|
+
import Window from '@shell/components/Window/Window.vue';
|
|
7
7
|
|
|
8
8
|
jest.mock('@shell/utils/socket');
|
|
9
9
|
jest.mock('@shell/utils/crypto', () => {
|