summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/locales/en-US.json4
-rw-r--r--src/locales/ru-RU.json3
-rw-r--r--src/store/api.js25
-rw-r--r--src/store/modules/Authentication/AuthenticanStore.js53
-rw-r--r--src/store/modules/Logs/EventLogStore.js16
-rw-r--r--src/store/modules/Operations/FirmwareStore.js45
-rw-r--r--src/views/HardwareStatus/Inventory/InventoryTableFans.vue41
-rw-r--r--src/views/HardwareStatus/Inventory/InventoryTablePowerSupplies.vue39
-rw-r--r--src/views/Login/Login.vue5
-rw-r--r--src/views/Logs/Dumps/Dumps.vue2
-rw-r--r--src/views/Logs/EventLogs/EventLogs.vue20
-rw-r--r--src/views/Operations/Firmware/FirmwareFormUpdate.vue78
-rw-r--r--src/views/SecurityAndAccess/UserManagement/UserManagement.vue43
13 files changed, 242 insertions, 132 deletions
diff --git a/src/locales/en-US.json b/src/locales/en-US.json
index 44a63de3..bd820666 100644
--- a/src/locales/en-US.json
+++ b/src/locales/en-US.json
@@ -282,6 +282,7 @@
"errorLogStatusUpdate": "Error updating log status.",
"errorResolveLogs": "Error resolving %{count} log. | Error resolving %{count} logs.",
"errorUnresolveLogs": "Error unresolving %{count} log. | Error unresolving %{count} logs.",
+ "errorDownloadEventEntry": "Error download event log entry.",
"successDelete": "Successfully deleted %{count} log. | Successfully deleted %{count} logs.",
"successResolveLogs": "Successfully resolved %{count} log. | Successfully resolved %{count} logs.",
"successUnresolveLogs": "Successfully unresolved %{count} log. | Successfully unresolved %{count} logs."
@@ -347,7 +348,6 @@
"fileSource": "File source",
"imageFile": "Image file",
"startUpdate": "Start update",
- "tftpServer": "TFTP server",
"workstation": "Workstation"
}
},
@@ -554,6 +554,7 @@
"accountPolicySettings": "Account policy settings",
"addUser": "Add user",
"deleteUser": "Delete user | Delete users",
+ "disableUser": "Disable user | Disable users",
"editUser": "Edit user",
"viewPrivilegeRoleDescriptions": "View privilege role descriptions",
"modal": {
@@ -561,6 +562,7 @@
"accountStatus": "Account status",
"automaticAfterTimeout": "Automatic after timeout",
"batchDeleteConfirmMessage": "Are you sure you want to delete %{count} user? This action cannot be undone. | Are you sure you want to delete %{count} users? This action cannot be undone.",
+ "batchDisableConfirmMessage": "Are you sure you want to disable %{count} user? | Are you sure you want to disable %{count} users?",
"cannotStartWithANumber": "Cannot start with a number",
"clickSaveToUnlockAccount": "Click \"Save\" to unlock account",
"confirmUserPassword": "Confirm user password",
diff --git a/src/locales/ru-RU.json b/src/locales/ru-RU.json
index dcf7c597..4a6106de 100644
--- a/src/locales/ru-RU.json
+++ b/src/locales/ru-RU.json
@@ -347,7 +347,6 @@
"fileSource": "Источник файла",
"imageFile": "Файл образа",
"startUpdate": "Начать обновление",
- "tftpServer": "TFTP сервер",
"workstation": "Рабочая станция"
}
},
@@ -552,6 +551,7 @@
"accountPolicySettings": "Настройки политики учётной записи",
"addUser": "Добавить пользователя",
"deleteUser": "Удалить пользователя | Удалить пользователей",
+ "disableUser": "Отключить пользователя | Отключить пользователей",
"editUser": "Редактировать пользователя",
"viewPrivilegeRoleDescriptions": "Просмотр описаний привилегий ролей",
"modal": {
@@ -559,6 +559,7 @@
"accountStatus": "Статус учётной записи",
"automaticAfterTimeout": "Автоматически после истечения таймаута",
"batchDeleteConfirmMessage": "Вы уверены, что хотите удалить %{count} пользователя? Это действие нельзя отменить. | Вы уверены, что хотите удалить %{count} пользователей? Это действие нельзя отменить.",
+ "batchDisableConfirmMessage": "Вы уверены, что хотите отключить пользователя %{count}? | Вы уверены, что хотите отключить пользователей %{count}?",
"cannotStartWithANumber": "Не может начинаться с цифры",
"clickSaveToUnlockAccount": "Нажмите \"Сохранить\" для разблокировки учётной записи",
"confirmUserPassword": "Подтвердите пароль пользователя",
diff --git a/src/store/api.js b/src/store/api.js
index 0bd84e62..664e2b76 100644
--- a/src/store/api.js
+++ b/src/store/api.js
@@ -1,4 +1,5 @@
import Axios from 'axios';
+import router from '../router';
import { setupCache, buildWebStorage } from 'axios-cache-interceptor';
//Do not change store import.
@@ -36,11 +37,14 @@ api.interceptors.response.use(undefined, (error) => {
}
}
+ // Check if action is unauthorized.
if (response.status == 403) {
- // Check if action is unauthorized.
- // Toast error message will appear on screen
- // when the action is unauthorized.
- store.commit('global/setUnauthorized');
+ if (isPasswordExpired(response)) {
+ router.push('/change-password');
+ } else {
+ // Toast error message will appear on screen.
+ store.commit('global/setUnauthorized');
+ }
}
return Promise.reject(error);
@@ -68,6 +72,9 @@ export default {
spread(callback) {
return Axios.spread(callback);
},
+ set_auth_token(token) {
+ axiosInstance.defaults.headers.common['X-Auth-Token'] = token;
+ },
};
export const getResponseCount = (responses) => {
@@ -84,3 +91,13 @@ export const getResponseCount = (responses) => {
errorCount,
};
};
+
+export const isPasswordExpired = (response) => {
+ let extInfoMsgs = response?.data?.['@Message.ExtendedInfo'];
+ return (
+ extInfoMsgs &&
+ extInfoMsgs.find(
+ (i) => i.MessageId.split('.')[4] === 'PasswordChangeRequired',
+ )
+ );
+};
diff --git a/src/store/modules/Authentication/AuthenticanStore.js b/src/store/modules/Authentication/AuthenticanStore.js
index 2006661b..3122ab2f 100644
--- a/src/store/modules/Authentication/AuthenticanStore.js
+++ b/src/store/modules/Authentication/AuthenticanStore.js
@@ -1,4 +1,4 @@
-import api from '@/store/api';
+import api, { isPasswordExpired } from '@/store/api';
import Cookies from 'js-cookie';
import router from '@/router';
import { roles } from '@/router/routes';
@@ -10,21 +10,39 @@ const AuthenticationStore = {
authError: false,
xsrfCookie: Cookies.get('XSRF-TOKEN'),
isAuthenticatedCookie: Cookies.get('IsAuthenticated'),
+ sessionURI: localStorage.getItem('sessionURI'),
+ xAuthToken: null,
},
getters: {
consoleWindow: (state) => state.consoleWindow,
authError: (state) => state.authError,
isLoggedIn: (state) => {
+ // We might have gotten XSRF-TOKEN (and HttpOnly SESSION cookie) by Mutual TLS authentication,
+ // without going through explicit Session creation
return (
- state.xsrfCookie !== undefined || state.isAuthenticatedCookie == 'true'
+ state.xsrfCookie !== undefined ||
+ state.isAuthenticatedCookie == 'true' ||
+ state.xAuthToken !== null
);
},
+ // Used to authenticate WebSocket connections via subprotocol value
token: (state) => state.xsrfCookie,
},
mutations: {
- authSuccess(state) {
+ authSuccess(state, { session, token }) {
state.authError = false;
state.xsrfCookie = Cookies.get('XSRF-TOKEN');
+ // Preserve session data across page reloads and browser restarts
+ localStorage.setItem('sessionURI', session);
+ state.sessionURI = session;
+ // If we didn't get the XSRF cookie it means we are talking to a
+ // Redfish implementation that is not bmcweb. In this case get the token
+ // from headers and send it with the future requests, do not permanently
+ // save anywhere.
+ if (state.xsrfCookie === undefined) {
+ api.set_auth_token(token);
+ state.xAuthToken = token;
+ }
},
authError(state, authError = true) {
state.authError = authError;
@@ -32,33 +50,40 @@ const AuthenticationStore = {
logout(state) {
Cookies.remove('XSRF-TOKEN');
Cookies.remove('IsAuthenticated');
+ api.set_auth_token(undefined);
localStorage.removeItem('storedUsername');
state.xsrfCookie = undefined;
state.isAuthenticatedCookie = undefined;
+ localStorage.removeItem('sessionURI');
+ state.sessionURI = null;
+ state.xAuthToken = null;
+ state.consoleWindow = false;
},
- setConsoleWindow: (state, window) => (state.consoleWindow = window),
},
actions: {
login({ commit }, { username, password }) {
commit('authError', false);
return api
- .post('/login', {
- username: username,
- password: password,
+ .post('/redfish/v1/SessionService/Sessions', {
+ UserName: username,
+ Password: password,
+ })
+ .then((response) => {
+ commit('authSuccess', {
+ session: response.headers['location'],
+ token: response.headers['x-auth-token'],
+ });
+ return isPasswordExpired(response);
})
- .then(() => commit('authSuccess'))
.catch((error) => {
commit('authError');
throw new Error(error);
});
},
- logout({ commit }) {
+ logout({ commit, state }) {
api
- .post('/logout', { data: [] })
- .then(() => {
- commit('setConsoleWindow', false);
- commit('logout');
- })
+ .delete(state.sessionURI)
+ .then(() => commit('logout'))
.then(() => router.push('/login'))
.catch((error) => console.log(error));
},
diff --git a/src/store/modules/Logs/EventLogStore.js b/src/store/modules/Logs/EventLogStore.js
index e67da39b..f302dffb 100644
--- a/src/store/modules/Logs/EventLogStore.js
+++ b/src/store/modules/Logs/EventLogStore.js
@@ -220,6 +220,22 @@ const EventLogStore = {
throw new Error(i18n.t('pageEventLogs.toast.errorLogStatusUpdate'));
});
},
+ async downloadEntry(_, uri) {
+ return await api
+ .get(uri)
+ .then((response) => {
+ const blob = new Blob([response.data], {
+ type: response.headers['content-type'],
+ });
+ return blob;
+ })
+ .catch((error) => {
+ console.log(error);
+ throw new Error(
+ i18n.t('pageEventLogs.toast.errorDownloadEventEntry'),
+ );
+ });
+ },
},
};
diff --git a/src/store/modules/Operations/FirmwareStore.js b/src/store/modules/Operations/FirmwareStore.js
index f6f965f9..6c216da8 100644
--- a/src/store/modules/Operations/FirmwareStore.js
+++ b/src/store/modules/Operations/FirmwareStore.js
@@ -9,11 +9,10 @@ const FirmwareStore = {
bmcActiveFirmwareId: null,
hostActiveFirmwareId: null,
applyTime: null,
+ multipartHttpPushUri: null,
httpPushUri: null,
- tftpAvailable: false,
},
getters: {
- isTftpUploadAvailable: (state) => state.tftpAvailable,
isSingleFileUploadEnabled: (state) => state.hostFirmware.length === 0,
activeBmcFirmware: (state) => {
return state.bmcFirmware.find(
@@ -43,8 +42,8 @@ const FirmwareStore = {
setHostFirmware: (state, firmware) => (state.hostFirmware = firmware),
setApplyTime: (state, applyTime) => (state.applyTime = applyTime),
setHttpPushUri: (state, httpPushUri) => (state.httpPushUri = httpPushUri),
- setTftpUploadAvailable: (state, tftpAvailable) =>
- (state.tftpAvailable = tftpAvailable),
+ setMultipartHttpPushUri: (state, multipartHttpPushUri) =>
+ (state.multipartHttpPushUri = multipartHttpPushUri),
},
actions: {
async getFirmwareInformation({ dispatch }) {
@@ -111,20 +110,24 @@ const FirmwareStore = {
.then(({ data }) => {
const applyTime =
data.HttpPushUriOptions.HttpPushUriApplyTime.ApplyTime;
- const allowableActions =
- data?.Actions?.['#UpdateService.SimpleUpdate']?.[
- 'TransferProtocol@Redfish.AllowableValues'
- ];
commit('setApplyTime', applyTime);
const httpPushUri = data.HttpPushUri;
commit('setHttpPushUri', httpPushUri);
- if (allowableActions?.includes('TFTP')) {
- commit('setTftpUploadAvailable', true);
- }
+ const multipartHttpPushUri = data.MultipartHttpPushUri;
+ commit('setMultipartHttpPushUri', multipartHttpPushUri);
})
.catch((error) => console.log(error));
},
- async uploadFirmware({ state }, image) {
+ async uploadFirmware({ state, dispatch }, params) {
+ if (state.multipartHttpPushUri != null) {
+ return dispatch('uploadFirmwareMultipartHttpPush', params);
+ } else if (state.httpPushUri != null) {
+ return dispatch('uploadFirmwareHttpPush', params);
+ } else {
+ console.log('Do not support firmware push update');
+ }
+ },
+ async uploadFirmwareHttpPush({ state }, { image }) {
return await api
.post(state.httpPushUri, image, {
headers: { 'Content-Type': 'application/octet-stream' },
@@ -134,16 +137,16 @@ const FirmwareStore = {
throw new Error(i18n.t('pageFirmware.toast.errorUpdateFirmware'));
});
},
- async uploadFirmwareTFTP(fileAddress) {
- const data = {
- TransferProtocol: 'TFTP',
- ImageURI: fileAddress,
- };
+ async uploadFirmwareMultipartHttpPush({ state }, { image, targets }) {
+ const formData = new FormData();
+ formData.append('UpdateFile', image);
+ let params = {};
+ if (targets != null && targets.length > 0) params.Targets = targets;
+ formData.append('UpdateParameters', JSON.stringify(params));
return await api
- .post(
- '/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate',
- data,
- )
+ .post(state.multipartHttpPushUri, formData, {
+ headers: { 'Content-Type': 'multipart/form-data' },
+ })
.catch((error) => {
console.log(error);
throw new Error(i18n.t('pageFirmware.toast.errorUpdateFirmware'));
diff --git a/src/views/HardwareStatus/Inventory/InventoryTableFans.vue b/src/views/HardwareStatus/Inventory/InventoryTableFans.vue
index 62f0b76b..af4b461e 100644
--- a/src/views/HardwareStatus/Inventory/InventoryTableFans.vue
+++ b/src/views/HardwareStatus/Inventory/InventoryTableFans.vue
@@ -51,6 +51,12 @@
{{ value }}
</template>
+ <!-- StatusState -->
+ <template #cell(statusState)="{ value }">
+ <status-icon :status="statusStateIcon(value)" />
+ {{ value }}
+ </template>
+
<template #row-details="{ item }">
<b-container fluid>
<b-row>
@@ -146,6 +152,12 @@ export default {
tdClass: 'text-nowrap',
},
{
+ key: 'statusState',
+ label: this.$t('pageInventory.table.state'),
+ formatter: this.dataFormatter,
+ tdClass: 'text-nowrap',
+ },
+ {
key: 'partNumber',
label: this.$t('pageInventory.table.partNumber'),
formatter: this.dataFormatter,
@@ -183,11 +195,40 @@ export default {
sortCompare(a, b, key) {
if (key === 'health') {
return this.sortStatus(a, b, key);
+ } else if (key === 'statusState') {
+ return this.sortStatusState(a, b, key);
}
},
onFiltered(filteredItems) {
this.searchTotalFilteredRows = filteredItems.length;
},
+ /**
+ * Returns the appropriate icon based on the given status.
+ *
+ * @param {string} status - The status to determine the icon for.
+ * @return {string} The icon corresponding to the given status.
+ */
+ statusStateIcon(status) {
+ switch (status) {
+ case 'Enabled':
+ return 'success';
+ case 'Absent':
+ return 'warning';
+ default:
+ return '';
+ }
+ },
+ /**
+ * Sorts the status state of two objects based on the provided key.
+ *
+ * @param {Object} a - The first object to compare.
+ * @param {Object} b - The second object to compare.
+ * @param {string} key - The key to use for comparison.
+ */
+ sortStatusState(a, b, key) {
+ const statusState = ['Enabled', 'Absent'];
+ return statusState.indexOf(a[key]) - statusState.indexOf(b[key]);
+ },
},
};
</script>
diff --git a/src/views/HardwareStatus/Inventory/InventoryTablePowerSupplies.vue b/src/views/HardwareStatus/Inventory/InventoryTablePowerSupplies.vue
index df03fdf2..0ce8c823 100644
--- a/src/views/HardwareStatus/Inventory/InventoryTablePowerSupplies.vue
+++ b/src/views/HardwareStatus/Inventory/InventoryTablePowerSupplies.vue
@@ -51,6 +51,12 @@
{{ value }}
</template>
+ <!-- StatusState -->
+ <template #cell(statusState)="{ value }">
+ <status-icon :status="statusStateIcon(value)" />
+ {{ value }}
+ </template>
+
<template #row-details="{ item }">
<b-container fluid>
<b-row>
@@ -167,6 +173,12 @@ export default {
tdClass: 'text-nowrap',
},
{
+ key: 'statusState',
+ label: this.$t('pageInventory.table.state'),
+ formatter: this.dataFormatter,
+ tdClass: 'text-nowrap',
+ },
+ {
key: 'locationNumber',
label: this.$t('pageInventory.table.locationNumber'),
formatter: this.dataFormatter,
@@ -204,11 +216,38 @@ export default {
sortCompare(a, b, key) {
if (key === 'health') {
return this.sortStatus(a, b, key);
+ } else if (key === 'statusState') {
+ return this.sortStatusState(a, b, key);
}
},
onFiltered(filteredItems) {
this.searchTotalFilteredRows = filteredItems.length;
},
+ /**
+ * Returns the icon to use for status state based on the given status.
+ * @param {string} status The status to determine the icon for.
+ * @return {string} The icon for the given status.
+ */
+ statusStateIcon(status) {
+ switch (status) {
+ case 'Enabled':
+ return 'success';
+ case 'Absent':
+ return 'warning';
+ default:
+ return '';
+ }
+ },
+ /**
+ * Sorts the status state of two objects based on the provided key.
+ * @param {Object} a The first object to compare.
+ * @param {Object} b The second object to compare.
+ * @param {string} key The key to use for comparison.
+ */
+ sortStatusState(a, b, key) {
+ const statusState = ['Enabled', 'Absent'];
+ return statusState.indexOf(a[key]) - statusState.indexOf(b[key]);
+ },
},
};
</script>
diff --git a/src/views/Login/Login.vue b/src/views/Login/Login.vue
index db475c56..5e1e6ddd 100644
--- a/src/views/Login/Login.vue
+++ b/src/views/Login/Login.vue
@@ -117,14 +117,11 @@ export default {
const password = this.userInfo.password;
this.$store
.dispatch('authentication/login', { username, password })
- .then(() => {
+ .then((PasswordChangeRequired) => {
localStorage.setItem('storedLanguage', i18n.locale);
localStorage.setItem('storedUsername', username);
this.$store.commit('global/setUsername', username);
this.$store.commit('global/setLanguagePreference', i18n.locale);
- return this.$store.dispatch('authentication/getUserInfo', username);
- })
- .then(({ PasswordChangeRequired }) => {
if (PasswordChangeRequired) {
this.$router.push('/change-password');
} else {
diff --git a/src/views/Logs/Dumps/Dumps.vue b/src/views/Logs/Dumps/Dumps.vue
index 75873a8c..e89acd93 100644
--- a/src/views/Logs/Dumps/Dumps.vue
+++ b/src/views/Logs/Dumps/Dumps.vue
@@ -306,7 +306,7 @@ export default {
},
created() {
this.startLoader();
- this.$store.dispatch('dumps/getBmcDumpEntries').finally(() => {
+ this.$store.dispatch('dumps/getAllDumps').finally(() => {
this.endLoader();
this.isBusy = false;
});
diff --git a/src/views/Logs/EventLogs/EventLogs.vue b/src/views/Logs/EventLogs/EventLogs.vue
index 0e7c494e..b48bd441 100644
--- a/src/views/Logs/EventLogs/EventLogs.vue
+++ b/src/views/Logs/EventLogs/EventLogs.vue
@@ -151,11 +151,7 @@
</dl>
</b-col>
<b-col class="text-nowrap">
- <b-button
- class="btn btn-secondary float-right"
- :href="item.additionalDataUri"
- target="_blank"
- >
+ <b-button @click="downloadEntry(item.additionalDataUri)">
<icon-download />{{ $t('pageEventLogs.additionalDataUri') }}
</b-button>
</b-col>
@@ -471,6 +467,20 @@ export default {
});
},
methods: {
+ downloadEntry(uri) {
+ let filename = uri?.split('LogServices/')?.[1];
+ filename.replace(RegExp('/', 'g'), '_');
+ this.$store
+ .dispatch('eventLog/downloadEntry', uri)
+ .then((blob) => {
+ const link = document.createElement('a');
+ link.href = URL.createObjectURL(blob);
+ link.download = filename;
+ link.click();
+ URL.revokeObjectURL(link.href);
+ })
+ .catch(({ message }) => this.errorToast(message));
+ },
changelogStatus(row) {
this.$store
.dispatch('eventLog/updateEventLogStatus', {
diff --git a/src/views/Operations/Firmware/FirmwareFormUpdate.vue b/src/views/Operations/Firmware/FirmwareFormUpdate.vue
index ac4b23fc..28d1104d 100644
--- a/src/views/Operations/Firmware/FirmwareFormUpdate.vue
+++ b/src/views/Operations/Firmware/FirmwareFormUpdate.vue
@@ -2,21 +2,8 @@
<div>
<div class="form-background p-3">
<b-form @submit.prevent="onSubmitUpload">
- <b-form-group
- v-if="isTftpUploadAvailable"
- :label="$t('pageFirmware.form.updateFirmware.fileSource')"
- :disabled="isPageDisabled"
- >
- <b-form-radio v-model="isWorkstationSelected" :value="true">
- {{ $t('pageFirmware.form.updateFirmware.workstation') }}
- </b-form-radio>
- <b-form-radio v-model="isWorkstationSelected" :value="false">
- {{ $t('pageFirmware.form.updateFirmware.tftpServer') }}
- </b-form-radio>
- </b-form-group>
-
<!-- Workstation Upload -->
- <template v-if="isWorkstationSelected">
+ <template>
<b-form-group
:label="$t('pageFirmware.form.updateFirmware.imageFile')"
label-for="image-file"
@@ -37,25 +24,6 @@
</b-form-group>
</template>
- <!-- TFTP Server Upload -->
- <template v-else>
- <b-form-group
- :label="$t('pageFirmware.form.updateFirmware.fileAddress')"
- label-for="tftp-address"
- >
- <b-form-input
- id="tftp-address"
- v-model="tftpFileAddress"
- type="text"
- :state="getValidationState($v.tftpFileAddress)"
- :disabled="isPageDisabled"
- @input="$v.tftpFileAddress.$touch()"
- />
- <b-form-invalid-feedback role="alert">
- {{ $t('global.form.fieldRequired') }}
- </b-form-invalid-feedback>
- </b-form-group>
- </template>
<b-btn
data-test-id="firmware-button-startUpdate"
type="submit"
@@ -73,7 +41,7 @@
</template>
<script>
-import { requiredIf } from 'vuelidate/lib/validators';
+import { required } from 'vuelidate/lib/validators';
import BVToastMixin from '@/components/Mixins/BVToastMixin';
import LoadingBarMixin, { loading } from '@/components/Mixins/LoadingBarMixin';
@@ -99,36 +67,15 @@ export default {
data() {
return {
loading,
- isWorkstationSelected: true,
file: null,
- tftpFileAddress: null,
isServerPowerOffRequired:
process.env.VUE_APP_SERVER_OFF_REQUIRED === 'true',
};
},
- computed: {
- isTftpUploadAvailable() {
- return this.$store.getters['firmware/isTftpUploadAvailable'];
- },
- },
- watch: {
- isWorkstationSelected: function () {
- this.$v.$reset();
- this.file = null;
- this.tftpFileAddress = null;
- },
- },
validations() {
return {
file: {
- required: requiredIf(function () {
- return this.isWorkstationSelected;
- }),
- },
- tftpFileAddress: {
- required: requiredIf(function () {
- return !this.isWorkstationSelected;
- }),
+ required,
},
};
},
@@ -149,24 +96,13 @@ export default {
title: this.$t('pageFirmware.toast.updateStarted'),
timestamp: true,
});
- if (this.isWorkstationSelected) {
- this.dispatchWorkstationUpload(timerId);
- } else {
- this.dispatchTftpUpload(timerId);
- }
+ this.dispatchWorkstationUpload(timerId);
},
dispatchWorkstationUpload(timerId) {
this.$store
- .dispatch('firmware/uploadFirmware', this.file)
- .catch(({ message }) => {
- this.endLoader();
- this.errorToast(message);
- clearTimeout(timerId);
- });
- },
- dispatchTftpUpload(timerId) {
- this.$store
- .dispatch('firmware/uploadFirmwareTFTP', this.tftpFileAddress)
+ .dispatch('firmware/uploadFirmware', {
+ image: this.file,
+ })
.catch(({ message }) => {
this.endLoader();
this.errorToast(message);
diff --git a/src/views/SecurityAndAccess/UserManagement/UserManagement.vue b/src/views/SecurityAndAccess/UserManagement/UserManagement.vue
index ab316688..944ea258 100644
--- a/src/views/SecurityAndAccess/UserManagement/UserManagement.vue
+++ b/src/views/SecurityAndAccess/UserManagement/UserManagement.vue
@@ -361,16 +361,39 @@ export default {
.finally(() => this.endLoader());
break;
case 'disable':
- this.startLoader();
- this.$store
- .dispatch('userManagement/disableUsers', this.selectedRows)
- .then((messages) => {
- messages.forEach(({ type, message }) => {
- if (type === 'success') this.successToast(message);
- if (type === 'error') this.errorToast(message);
- });
- })
- .finally(() => this.endLoader());
+ this.$bvModal
+ .msgBoxConfirm(
+ this.$tc(
+ 'pageUserManagement.modal.batchDisableConfirmMessage',
+ this.selectedRows.length,
+ ),
+ {
+ title: this.$tc(
+ 'pageUserManagement.disableUser',
+ this.selectedRows.length,
+ ),
+ okTitle: this.$tc(
+ 'pageUserManagement.disableUser',
+ this.selectedRows.length,
+ ),
+ cancelTitle: this.$t('global.action.cancel'),
+ autoFocusButton: 'ok',
+ },
+ )
+ .then((disableConfirmed) => {
+ if (disableConfirmed) {
+ this.startLoader();
+ this.$store
+ .dispatch('userManagement/disableUsers', this.selectedRows)
+ .then((messages) => {
+ messages.forEach(({ type, message }) => {
+ if (type === 'success') this.successToast(message);
+ if (type === 'error') this.errorToast(message);
+ });
+ })
+ .finally(() => this.endLoader());
+ }
+ });
break;
}
},