Add event log resolve and unresolve log functionality

Displays resolved or unresolved status, adds ability to filter
by resolved or unresolved, and adds ability to resolve or
unresolve  one or more logs.

Move event type table field to expanded row.

Signed-off-by: Dixsie Wolmers <dixsie@ibm.com>
Change-Id: Ie5761753a7660a714f98c238d8d89aa018719dcf
diff --git a/src/components/Global/TableToolbar.vue b/src/components/Global/TableToolbar.vue
index a928174..5235fea 100644
--- a/src/components/Global/TableToolbar.vue
+++ b/src/components/Global/TableToolbar.vue
@@ -6,6 +6,7 @@
           {{ selectedItemsCount }} {{ $t('global.action.selected') }}
         </p>
         <div class="toolbar-actions d-flex">
+          <slot name="toolbar-buttons"></slot>
           <b-button
             v-for="(action, index) in actions"
             :key="index"
@@ -16,7 +17,6 @@
           >
             {{ action.label }}
           </b-button>
-          <slot name="export"></slot>
           <b-button
             variant="secondary"
             class="d-block"
diff --git a/src/locales/en-US.json b/src/locales/en-US.json
index c6a8745..34efa62 100644
--- a/src/locales/en-US.json
+++ b/src/locales/en-US.json
@@ -246,6 +246,10 @@
   },
   "pageEventLogs": {
     "exportFilePrefix": "event_logs_",
+    "resolve": "Resolve",
+    "resolved": "Resolved",
+    "unresolve": "Unresolve",
+    "unresolved": "Unresolved",
     "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."
@@ -258,11 +262,19 @@
       "name": "Name",
       "searchLogs": "Search logs",
       "severity": "Severity",
+      "status": "Status",
       "type": "Type"
     },
     "toast": {
       "errorDelete": "Error deleting %{count} log. | Error deleting %{count} logs.",
-      "successDelete": "Successfully deleted %{count} log. | Successfully deleted %{count} logs."
+      "successDelete": "Successfully deleted %{count} log. | Successfully deleted %{count} logs.",
+      "errorLogStatusUpdate": "Error updating log status.",
+      "errorResolveLogs": "Error resolving %{count} log. | Error resolving %{count} logs.",
+      "errorUnresolveLogs": "Error unresolving %{count} log. | Error unresolving %{count} logs.",
+      "successResolveLog": "Successfully resolved log.",
+      "successResolveLogs": "Successfully resolved %{count} log. | Successfully resolved %{count} logs.",
+      "successUnresolveLog": "Successfully unresolved log.",
+      "successUnresolveLogs": "Successfully unresolved %{count} log. | Successfully unresolved %{count} logs."
     }
   },
   "pageFactoryReset": {
diff --git a/src/store/modules/Health/EventLogStore.js b/src/store/modules/Health/EventLogStore.js
index eaec749..2b6d5f9 100644
--- a/src/store/modules/Health/EventLogStore.js
+++ b/src/store/modules/Health/EventLogStore.js
@@ -51,6 +51,7 @@
               Message,
               Name,
               Modified,
+              Resolved,
             } = log;
             return {
               id: Id,
@@ -61,6 +62,8 @@
               name: Name,
               modifiedDate: new Date(Modified),
               uri: log['@odata.id'],
+              filterByStatus: Resolved ? 'Resolved' : 'Unresolved',
+              status: Resolved, //true or false
             };
           });
           commit('setAllEvents', eventLogs);
@@ -119,6 +122,95 @@
           })
         );
     },
+    async resolveEventLogs({ dispatch }, logs) {
+      const promises = logs.map((log) =>
+        api.patch(log.uri, { Resolved: true }).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.successResolveLogs',
+                successCount
+              );
+              toastMessages.push({ type: 'success', message });
+            }
+            if (errorCount) {
+              const message = i18n.tc(
+                'pageEventLogs.toast.errorResolveLogs',
+                errorCount
+              );
+              toastMessages.push({ type: 'error', message });
+            }
+            return toastMessages;
+          })
+        );
+    },
+    async unresolveEventLogs({ dispatch }, logs) {
+      const promises = logs.map((log) =>
+        api.patch(log.uri, { Resolved: false }).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.successUnresolveLogs',
+                successCount
+              );
+              toastMessages.push({ type: 'success', message });
+            }
+            if (errorCount) {
+              const message = i18n.tc(
+                'pageEventLogs.toast.errorUnresolveLogs',
+                errorCount
+              );
+              toastMessages.push({ type: 'error', message });
+            }
+            return toastMessages;
+          })
+        );
+    },
+    async updateEventLogStatus({ dispatch }, log) {
+      const updatedEventLogStatus = log.status;
+      return await api
+        .patch(log.uri, { Resolved: updatedEventLogStatus })
+        .then(() => {
+          dispatch('getEventLogData');
+        })
+        .then(() => {
+          if (log.status) {
+            return i18n.t('pageEventLogs.toast.successResolveLog');
+          } else {
+            return i18n.t('pageEventLogs.toast.successUnresolveLog');
+          }
+        })
+        .catch((error) => {
+          console.log(error);
+          throw new Error(i18n.t('pageEventLogs.toast.errorLogStatusUpdate'));
+        });
+    },
   },
 };
 
diff --git a/src/views/Health/EventLogs/EventLogs.vue b/src/views/Health/EventLogs/EventLogs.vue
index 69545a5..64e2adb 100644
--- a/src/views/Health/EventLogs/EventLogs.vue
+++ b/src/views/Health/EventLogs/EventLogs.vue
@@ -34,7 +34,13 @@
           @clear-selected="clearSelectedRows($refs.table)"
           @batch-action="onBatchAction"
         >
-          <template #export>
+          <template #toolbar-buttons>
+            <b-button variant="primary" @click="resolveLogs">
+              {{ $t('pageEventLogs.resolve') }}
+            </b-button>
+            <b-button variant="primary" @click="unresolveLogs">
+              {{ $t('pageEventLogs.unresolve') }}
+            </b-button>
             <table-toolbar-export
               :data="batchExportData"
               :file-name="exportFileNameByDate()"
@@ -107,6 +113,11 @@
                     <dt>{{ $t('pageEventLogs.table.name') }}:</dt>
                     <dd>{{ tableFormatter(item.name) }}</dd>
                   </dl>
+                  <dl>
+                    <!-- Type -->
+                    <dt>{{ $t('pageEventLogs.table.type') }}:</dt>
+                    <dd>{{ tableFormatter(item.type) }}</dd>
+                  </dl>
                 </b-col>
                 <b-col sm="6" xl="4">
                   <dl>
@@ -128,13 +139,30 @@
             <status-icon v-if="value" :status="statusIcon(value)" />
             {{ value }}
           </template>
-
           <!-- Date column -->
           <template #cell(date)="{ value }">
             <p class="mb-0">{{ value | formatDate }}</p>
             <p class="mb-0">{{ value | formatTime }}</p>
           </template>
 
+          <!-- Status column -->
+          <template #cell(status)="row">
+            <b-form-checkbox
+              v-model="row.item.status"
+              name="switch"
+              switch
+              @change="changelogStatus(row.item)"
+            >
+              <span v-if="row.item.status">
+                {{ $t('pageEventLogs.resolved') }}
+              </span>
+              <span v-else> {{ $t('pageEventLogs.unresolved') }} </span>
+            </b-form-checkbox>
+          </template>
+          <template #cell(filterByStatus)="{ value }">
+            {{ value }}
+          </template>
+
           <!-- Actions column -->
           <template #cell(actions)="row">
             <table-row-action
@@ -280,18 +308,19 @@
           tdClass: 'text-nowrap',
         },
         {
-          key: 'type',
-          label: this.$t('pageEventLogs.table.type'),
-          sortable: true,
-        },
-        {
           key: 'date',
           label: this.$t('pageEventLogs.table.date'),
           sortable: true,
+          tdClass: 'text-nowrap',
         },
         {
           key: 'description',
           label: this.$t('pageEventLogs.table.description'),
+          tdClass: 'text-break',
+        },
+        {
+          key: 'status',
+          label: this.$t('pageEventLogs.table.status'),
         },
         {
           key: 'actions',
@@ -306,6 +335,11 @@
           label: this.$t('pageEventLogs.table.severity'),
           values: ['OK', 'Warning', 'Critical'],
         },
+        {
+          key: 'filterByStatus',
+          label: this.$t('pageEventLogs.table.status'),
+          values: ['Resolved', 'Unresolved'],
+        },
       ],
       expandRowLabel,
       activeFilters: [],
@@ -374,6 +408,17 @@
       .finally(() => this.endLoader());
   },
   methods: {
+    changelogStatus(row) {
+      this.$store
+        .dispatch('eventLog/updateEventLogStatus', {
+          uri: row.uri,
+          status: row.status,
+        })
+        .then((success) => {
+          this.successToast(success);
+        })
+        .catch(({ message }) => this.errorToast(message));
+    },
     deleteLogs(uris) {
       this.$store
         .dispatch('eventLog/deleteEventLogs', uris)
@@ -459,6 +504,32 @@
         date.toString().split(':').join('-').split(' ')[4];
       return this.$t('pageEventLogs.exportFilePrefix') + date;
     },
+    resolveLogs() {
+      this.$store
+        .dispatch('eventLog/resolveEventLogs', this.selectedRows)
+        .then((messages) => {
+          messages.forEach(({ type, message }) => {
+            if (type === 'success') {
+              this.successToast(message);
+            } else if (type === 'error') {
+              this.errorToast(message);
+            }
+          });
+        });
+    },
+    unresolveLogs() {
+      this.$store
+        .dispatch('eventLog/unresolveEventLogs', this.selectedRows)
+        .then((messages) => {
+          messages.forEach(({ type, message }) => {
+            if (type === 'success') {
+              this.successToast(message);
+            } else if (type === 'error') {
+              this.errorToast(message);
+            }
+          });
+        });
+    },
   },
 };
 </script>
diff --git a/src/views/Health/Sensors/Sensors.vue b/src/views/Health/Sensors/Sensors.vue
index 8bc4e22..c69532a 100644
--- a/src/views/Health/Sensors/Sensors.vue
+++ b/src/views/Health/Sensors/Sensors.vue
@@ -27,7 +27,7 @@
           :selected-items-count="selectedRows.length"
           @clear-selected="clearSelectedRows($refs.table)"
         >
-          <template #export>
+          <template #toolbar-buttons>
             <table-toolbar-export
               :data="selectedRows"
               :file-name="exportFileNameByDate()"