summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/components/Global/TableRowAction.vue66
-rw-r--r--src/locales/en-US.json8
-rw-r--r--src/store/api.js15
-rw-r--r--src/store/modules/Health/EventLogStore.js63
-rw-r--r--src/views/Health/EventLogs/EventLogs.vue164
5 files changed, 280 insertions, 36 deletions
diff --git a/src/components/Global/TableRowAction.vue b/src/components/Global/TableRowAction.vue
index d41fd50c..f86bce22 100644
--- a/src/components/Global/TableRowAction.vue
+++ b/src/components/Global/TableRowAction.vue
@@ -1,18 +1,36 @@
<template>
- <b-button
- :aria-label="title"
- :title="title"
- variant="link"
- :disabled="!enabled"
- @click="$emit('click:tableAction', value)"
- >
- <slot name="icon">
- {{ title }}
- </slot>
- </b-button>
+ <span>
+ <b-link
+ v-if="value === 'export'"
+ class="align-bottom btn-link py-0"
+ :download="download"
+ :href="href"
+ :title="title"
+ :aria-label="title"
+ >
+ <slot name="icon">
+ {{ $t('global.action.export') }}
+ </slot>
+ </b-link>
+ <b-button
+ v-else
+ variant="link"
+ class="py-0"
+ :aria-label="title"
+ :title="title"
+ :disabled="!enabled"
+ @click="$emit('click:tableAction', value)"
+ >
+ <slot name="icon">
+ {{ title }}
+ </slot>
+ </b-button>
+ </span>
</template>
<script>
+import { omit } from 'lodash';
+
export default {
name: 'TableRowAction',
props: {
@@ -27,14 +45,26 @@ export default {
title: {
type: String,
default: null
+ },
+ rowData: {
+ type: Object,
+ default: () => {}
+ },
+ exportName: {
+ type: String,
+ default: 'export'
+ }
+ },
+ computed: {
+ dataForExport() {
+ return JSON.stringify(omit(this.rowData, 'actions'));
+ },
+ download() {
+ return `${this.exportName}.json`;
+ },
+ href() {
+ return `data:text/json;charset=utf-8,${this.dataForExport}`;
}
}
};
</script>
-
-<style lang="scss" scoped>
-.btn.btn-link {
- padding-top: 0;
- padding-bottom: 0;
-}
-</style>
diff --git a/src/locales/en-US.json b/src/locales/en-US.json
index a09047e9..4636503b 100644
--- a/src/locales/en-US.json
+++ b/src/locales/en-US.json
@@ -98,6 +98,10 @@
"unauthorized": "Unauthorized"
},
"pageEventLogs": {
+ "modal": {
+ "deleteTitle": "Delete log | Delete logs",
+ "deleteMessage": "Are you sure you want to delete %{count} log? This action cannot be undone. | Are you sure you want to delete %{count} logs? This action cannot be undone."
+ },
"table": {
"date": "Date",
"description": "Description",
@@ -105,6 +109,10 @@
"id": "ID",
"severity": "Severity",
"type": "Type"
+ },
+ "toast": {
+ "errorDelete": "Error deleting %{count} log. | Error deleting %{count} logs.",
+ "successDelete": "Successfully deleted %{count} log. | Successfully deleted %{count} logs."
}
},
"pageLdap": {
diff --git a/src/store/api.js b/src/store/api.js
index 4a8b8e80..63fd75cb 100644
--- a/src/store/api.js
+++ b/src/store/api.js
@@ -55,3 +55,18 @@ export default {
return Axios.spread(callback);
}
};
+
+export const getResponseCount = responses => {
+ let successCount = 0;
+ let errorCount = 0;
+
+ responses.forEach(response => {
+ if (response instanceof Error) errorCount++;
+ else successCount++;
+ });
+
+ return {
+ successCount,
+ errorCount
+ };
+};
diff --git a/src/store/modules/Health/EventLogStore.js b/src/store/modules/Health/EventLogStore.js
index 2f0b800f..2b93ffa7 100644
--- a/src/store/modules/Health/EventLogStore.js
+++ b/src/store/modules/Health/EventLogStore.js
@@ -1,4 +1,5 @@
-import api from '../../api';
+import api, { getResponseCount } from '@/store/api';
+import i18n from '@/i18n';
const getHealthStatus = events => {
let status = 'OK';
@@ -37,22 +38,60 @@ const EventLogStore = {
return await api
.get('/redfish/v1/Systems/system/LogServices/EventLog/Entries')
.then(({ data: { Members = [] } = {} }) => {
- const eventLogs = Members.map(
- ({ Id, Severity, Created, EntryType, Message }) => {
- return {
- id: Id,
- severity: Severity,
- date: new Date(Created),
- type: EntryType,
- description: Message
- };
- }
- );
+ const eventLogs = Members.map(log => {
+ const { Id, Severity, Created, EntryType, Message } = log;
+ return {
+ id: Id,
+ severity: Severity,
+ date: new Date(Created),
+ type: EntryType,
+ description: Message,
+ uri: log['@odata.id']
+ };
+ });
commit('setAllEvents', eventLogs);
})
.catch(error => {
console.log('Event Log Data:', error);
});
+ },
+ async deleteEventLogs({ dispatch }, uris = []) {
+ const promises = uris.map(uri =>
+ api.delete(uri).catch(error => {
+ console.log(error);
+ return error;
+ })
+ );
+ return await api
+ .all(promises)
+ .then(response => {
+ dispatch('getEventLogData');
+ return response;
+ })
+ .then(
+ api.spread((...responses) => {
+ const { successCount, errorCount } = getResponseCount(responses);
+ const toastMessages = [];
+
+ if (successCount) {
+ const message = i18n.tc(
+ 'pageEventLogs.toast.successDelete',
+ successCount
+ );
+ toastMessages.push({ type: 'success', message });
+ }
+
+ if (errorCount) {
+ const message = i18n.tc(
+ 'pageEventLogs.toast.errorDelete',
+ errorCount
+ );
+ toastMessages.push({ type: 'error', message });
+ }
+
+ return toastMessages;
+ })
+ );
}
}
};
diff --git a/src/views/Health/EventLogs/EventLogs.vue b/src/views/Health/EventLogs/EventLogs.vue
index d7a64c90..a5ef3757 100644
--- a/src/views/Health/EventLogs/EventLogs.vue
+++ b/src/views/Health/EventLogs/EventLogs.vue
@@ -8,27 +8,81 @@
</b-row>
<b-row>
<b-col>
+ <table-toolbar
+ ref="toolbar"
+ :selected-items-count="selectedRows.length"
+ :actions="batchActions"
+ @clearSelected="clearSelectedRows($refs.table)"
+ @batchAction="onBatchAction"
+ >
+ <template v-slot:export>
+ <table-toolbar-export
+ :data="batchExportData"
+ :file-name="$t('appPageTitle.eventLogs')"
+ />
+ </template>
+ </table-toolbar>
<b-table
id="table-event-logs"
- :fields="fields"
- :items="filteredLogs"
+ ref="table"
+ selectable
+ no-select-on-click
sort-icon-left
no-sort-reset
sort-desc
show-empty
sort-by="date"
+ :fields="fields"
+ :items="filteredLogs"
:sort-compare="onSortCompare"
:empty-text="$t('pageEventLogs.table.emptyMessage')"
:per-page="perPage"
:current-page="currentPage"
+ @row-selected="onRowSelected($event, filteredLogs.length)"
>
+ <!-- Checkbox column -->
+ <template v-slot:head(checkbox)>
+ <b-form-checkbox
+ v-model="tableHeaderCheckboxModel"
+ :indeterminate="tableHeaderCheckboxIndeterminate"
+ @change="onChangeHeaderCheckbox($refs.table)"
+ />
+ </template>
+ <template v-slot:cell(checkbox)="row">
+ <b-form-checkbox
+ v-model="row.rowSelected"
+ @change="toggleSelectRow($refs.table, row.index)"
+ />
+ </template>
+
+ <!-- Severity column -->
<template v-slot:cell(severity)="{ value }">
<status-icon :status="getStatus(value)" />
{{ value }}
</template>
+
+ <!-- Date column -->
<template v-slot:cell(date)="{ value }">
{{ value | formatDate }} {{ value | formatTime }}
</template>
+
+ <!-- Actions column -->
+ <template v-slot:cell(actions)="{ item }">
+ <table-row-action
+ v-for="(action, index) in item.actions"
+ :key="index"
+ :value="action.value"
+ :title="action.title"
+ :row-data="item"
+ :export-name="item.id"
+ @click:tableAction="onTableRowAction($event, item)"
+ >
+ <template v-slot:icon>
+ <icon-export v-if="action.value === 'export'" />
+ <icon-trashcan v-if="action.value === 'delete'" />
+ </template>
+ </table-row-action>
+ </template>
</b-table>
</b-col>
</b-row>
@@ -61,27 +115,51 @@
</template>
<script>
+import IconTrashcan from '@carbon/icons-vue/es/trash-can/20';
+import IconExport from '@carbon/icons-vue/es/export/20';
+import { omit } from 'lodash';
+
import PageTitle from '@/components/Global/PageTitle';
import StatusIcon from '@/components/Global/StatusIcon';
import TableFilter from '@/components/Global/TableFilter';
+import TableRowAction from '@/components/Global/TableRowAction';
+import TableToolbar from '@/components/Global/TableToolbar';
+import TableToolbarExport from '@/components/Global/TableToolbarExport';
import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin';
import TableFilterMixin from '@/components/Mixins/TableFilterMixin';
import BVPaginationMixin from '@/components/Mixins/BVPaginationMixin';
+import BVTableSelectableMixin from '@/components/Mixins/BVTableSelectableMixin';
+import BVToastMixin from '@/components/Mixins/BVToastMixin';
const SEVERITY = ['OK', 'Warning', 'Critical'];
export default {
components: {
+ IconExport,
+ IconTrashcan,
PageTitle,
StatusIcon,
- TableFilter
+ TableFilter,
+ TableRowAction,
+ TableToolbar,
+ TableToolbarExport
},
- mixins: [LoadingBarMixin, TableFilterMixin, BVPaginationMixin],
+ mixins: [
+ BVPaginationMixin,
+ BVTableSelectableMixin,
+ BVToastMixin,
+ LoadingBarMixin,
+ TableFilterMixin
+ ],
data() {
return {
fields: [
{
+ key: 'checkbox',
+ sortable: false
+ },
+ {
key: 'id',
label: this.$t('pageEventLogs.table.id'),
sortable: true
@@ -104,6 +182,12 @@ export default {
{
key: 'description',
label: this.$t('pageEventLogs.table.description')
+ },
+ {
+ key: 'actions',
+ sortable: false,
+ label: '',
+ tdClass: 'text-right'
}
],
tableFilters: [
@@ -112,12 +196,32 @@ export default {
values: SEVERITY
}
],
- activeFilters: []
+ activeFilters: [],
+ batchActions: [
+ {
+ value: 'delete',
+ label: this.$t('global.action.delete')
+ }
+ ]
};
},
computed: {
allLogs() {
- return this.$store.getters['eventLog/allEvents'];
+ return this.$store.getters['eventLog/allEvents'].map(event => {
+ return {
+ ...event,
+ actions: [
+ {
+ value: 'export',
+ title: this.$t('global.action.export')
+ },
+ {
+ value: 'delete',
+ title: this.$t('global.action.delete')
+ }
+ ]
+ };
+ });
},
filteredLogs: {
get: function() {
@@ -126,6 +230,9 @@ export default {
set: function(newVal) {
return newVal;
}
+ },
+ batchExportData() {
+ return this.selectedRows.map(row => omit(row, 'actions'));
}
},
created() {
@@ -153,6 +260,17 @@ export default {
return '';
}
},
+ deleteLogs(uris) {
+ this.$store.dispatch('eventLog/deleteEventLogs', uris).then(messages => {
+ messages.forEach(({ type, message }) => {
+ if (type === 'success') {
+ this.successToast(message);
+ } else if (type === 'error') {
+ this.errorToast(message);
+ }
+ });
+ });
+ },
onFilterChange({ activeFilters }) {
this.activeFilters = activeFilters;
this.filteredLogs = this.getFilteredTableData(
@@ -164,6 +282,40 @@ export default {
if (key === 'severity') {
return SEVERITY.indexOf(a.status) - SEVERITY.indexOf(b.status);
}
+ },
+ onTableRowAction(action, { uri }) {
+ if (action === 'delete') {
+ this.$bvModal
+ .msgBoxConfirm(this.$tc('pageEventLogs.modal.deleteMessage'), {
+ title: this.$tc('pageEventLogs.modal.deleteTitle'),
+ okTitle: this.$t('global.action.delete')
+ })
+ .then(deleteConfirmed => {
+ if (deleteConfirmed) this.deleteLogs([uri]);
+ });
+ }
+ },
+ onBatchAction(action) {
+ if (action === 'delete') {
+ const uris = this.selectedRows.map(row => row.uri);
+ this.$bvModal
+ .msgBoxConfirm(
+ this.$tc(
+ 'pageEventLogs.modal.deleteMessage',
+ this.selectedRows.length
+ ),
+ {
+ title: this.$tc(
+ 'pageEventLogs.modal.deleteTitle',
+ this.selectedRows.length
+ ),
+ okTitle: this.$t('global.action.delete')
+ }
+ )
+ .then(deleteConfirmed => {
+ if (deleteConfirmed) this.deleteLogs(uris);
+ });
+ }
}
}
};