Add enhancements to BVToastMixin

Adds ability to create toasts with multi-lined body content and
options to include a timestamp and application refresh action.

Signed-off-by: Yoshie Muranaka <yoshiemuranaka@gmail.com>
Change-Id: I30b1da04a0e0b5f29a419950462d1ca35e207552
diff --git a/src/components/AppHeader/AppHeader.vue b/src/components/AppHeader/AppHeader.vue
index 5b36cf8..7e1a100 100644
--- a/src/components/AppHeader/AppHeader.vue
+++ b/src/components/AppHeader/AppHeader.vue
@@ -172,10 +172,9 @@
   watch: {
     isAuthorized(value) {
       if (value === false) {
-        this.errorToast(
-          this.$t('global.toast.unAuthDescription'),
-          this.$t('global.toast.unAuthTitle')
-        );
+        this.errorToast(this.$t('global.toast.unAuthDescription'), {
+          title: this.$t('global.toast.unAuthTitle'),
+        });
       }
     },
   },
diff --git a/src/components/Mixins/BVToastMixin.js b/src/components/Mixins/BVToastMixin.js
index 5e8ec00..a04ef43 100644
--- a/src/components/Mixins/BVToastMixin.js
+++ b/src/components/Mixins/BVToastMixin.js
@@ -1,57 +1,113 @@
-import i18n from '@/i18n';
 import StatusIcon from '../Global/StatusIcon';
+
 const BVToastMixin = {
   components: {
     StatusIcon,
   },
   methods: {
-    toastTitle(title, status) {
-      // Create title with icon
+    $_BVToastMixin_createTitle(title, status) {
+      const statusIcon = this.$createElement('StatusIcon', {
+        props: { status },
+      });
       const titleWithIcon = this.$createElement(
         'strong',
         { class: 'toast-icon' },
-        [
-          this.$createElement('StatusIcon', { props: { status: status } }),
-          title,
-        ]
+        [statusIcon, title]
       );
       return titleWithIcon;
     },
-    successToast(message, title = i18n.t('global.status.success')) {
-      this.$root.$bvToast.toast(message, {
-        title: this.toastTitle(title, 'success'),
-        variant: 'success',
+    $_BVToastMixin_createBody(messageBody) {
+      if (Array.isArray(messageBody)) {
+        return messageBody.map((message) =>
+          this.$createElement('p', { class: 'mb-0' }, message)
+        );
+      } else {
+        return [this.$createElement('p', { class: 'mb-0' }, messageBody)];
+      }
+    },
+    $_BVToastMixin_createTimestamp() {
+      const timestamp = this.$options.filters.formatTime(new Date());
+      return this.$createElement('p', { class: 'mt-3 mb-0' }, timestamp);
+    },
+    $_BVToastMixin_createRefreshAction() {
+      return this.$createElement(
+        'BLink',
+        {
+          class: 'd-inline-block mt-3',
+          on: {
+            click: () => {
+              this.$root.$emit('refresh-application');
+            },
+          },
+        },
+        this.$t('global.action.refresh')
+      );
+    },
+    $_BVToastMixin_initToast(body, title, variant) {
+      this.$root.$bvToast.toast(body, {
+        title,
+        variant,
         autoHideDelay: 10000, //auto hide in milliseconds
+        noAutoHide: variant !== 'success',
         isStatus: true,
         solid: true,
       });
     },
-    errorToast(message, title = i18n.t('global.status.error')) {
-      this.$root.$bvToast.toast(message, {
-        title: this.toastTitle(title, 'danger'),
-        variant: 'danger',
-        noAutoHide: true,
-        isStatus: true,
-        solid: true,
-      });
+    successToast(
+      message,
+      {
+        title: t = this.$t('global.status.success'),
+        timestamp,
+        refreshAction,
+      } = {}
+    ) {
+      const body = this.$_BVToastMixin_createBody(message);
+      const title = this.$_BVToastMixin_createTitle(t, 'success');
+      if (refreshAction) body.push(this.$_BVToastMixin_createRefreshAction());
+      if (timestamp) body.push(this.$_BVToastMixin_createTimestamp());
+      this.$_BVToastMixin_initToast(body, title, 'success');
     },
-    warningToast(message, title = i18n.t('global.status.warning')) {
-      this.$root.$bvToast.toast(message, {
-        title: this.toastTitle(title, 'warning'),
-        variant: 'warning',
-        noAutoHide: true,
-        isStatus: true,
-        solid: true,
-      });
+    errorToast(
+      message,
+      {
+        title: t = this.$t('global.status.error'),
+        timestamp,
+        refreshAction,
+      } = {}
+    ) {
+      const body = this.$_BVToastMixin_createBody(message);
+      const title = this.$_BVToastMixin_createTitle(t, 'danger');
+      if (refreshAction) body.push(this.$_BVToastMixin_createRefreshAction());
+      if (timestamp) body.push(this.$_BVToastMixin_createTimestamp());
+      this.$_BVToastMixin_initToast(body, title, 'danger');
     },
-    infoToast(message, title = i18n.t('global.status.informational')) {
-      this.$root.$bvToast.toast(message, {
-        title: this.toastTitle(title, 'info'),
-        variant: 'info',
-        noAutoHide: true,
-        isStatus: true,
-        solid: true,
-      });
+    warningToast(
+      message,
+      {
+        title: t = this.$t('global.status.warning'),
+        timestamp,
+        refreshAction,
+      } = {}
+    ) {
+      const body = this.$_BVToastMixin_createBody(message);
+      const title = this.$_BVToastMixin_createTitle(t, 'warning');
+      if (refreshAction) body.push(this.$_BVToastMixin_createRefreshAction());
+      if (timestamp) body.push(this.$_BVToastMixin_createTimestamp());
+      this.$_BVToastMixin_initToast(body, title, 'warning');
+    },
+    infoToast(
+      message,
+      {
+        title: t = this.$t('global.status.informational'),
+        timestamp,
+        refreshAction,
+      } = {}
+    ) {
+      const body = this.$_BVToastMixin_createBody(message);
+      const title = this.$_BVToastMixin_createTitle(t, 'info');
+      if (refreshAction) body.push(this.$_BVToastMixin_createRefreshAction());
+      if (timestamp) body.push(this.$_BVToastMixin_createTimestamp());
+      this.$_BVToastMixin_initToast(body, title, 'info');
     },
   },
 };
diff --git a/src/env/components/Dumps/DumpsForm.vue b/src/env/components/Dumps/DumpsForm.vue
index 9dc8bcb..02ec186 100644
--- a/src/env/components/Dumps/DumpsForm.vue
+++ b/src/env/components/Dumps/DumpsForm.vue
@@ -68,10 +68,10 @@
         this.$store
           .dispatch('dumps/createBmcDump')
           .then(() =>
-            this.infoToast(
-              this.$t('pageDumps.toast.successStartBmcDump'),
-              this.$t('pageDumps.toast.successStartBmcDumpTitle')
-            )
+            this.infoToast(this.$t('pageDumps.toast.successStartBmcDump'), {
+              title: this.$t('pageDumps.toast.successStartBmcDumpTitle'),
+              timestamp: true,
+            })
           )
           .catch(({ message }) => this.errorToast(message));
       }
@@ -83,10 +83,10 @@
       this.$store
         .dispatch('dumps/createSystemDump')
         .then(() =>
-          this.infoToast(
-            this.$t('pageDumps.toast.successStartSystemDump'),
-            this.$t('pageDumps.toast.successStartSystemDumpTitle')
-          )
+          this.infoToast(this.$t('pageDumps.toast.successStartSystemDump'), {
+            title: this.$t('pageDumps.toast.successStartSystemDumpTitle'),
+            timestamp: true,
+          })
         )
         .catch(({ message }) => this.errorToast(message));
     },
diff --git a/src/env/components/FirmwareSingleImage/FirmwareSingleImage.vue b/src/env/components/FirmwareSingleImage/FirmwareSingleImage.vue
index 517e5fa..1089e7a 100644
--- a/src/env/components/FirmwareSingleImage/FirmwareSingleImage.vue
+++ b/src/env/components/FirmwareSingleImage/FirmwareSingleImage.vue
@@ -195,61 +195,6 @@
       :backup="backupFirmwareVersion"
       @ok="switchToRunning"
     />
-
-    <!-- Toasts -->
-    <b-toast id="switch-images" variant="info" solid :no-auto-hide="true">
-      <template #toast-title>
-        <status-icon status="info" class="toast-icon" />
-        <strong>{{
-          $t('pageFirmware.singleFileUpload.toast.rebootStarted')
-        }}</strong>
-      </template>
-      <p class="m-0">
-        {{ $t('pageFirmware.singleFileUpload.toast.rebootStartedMessage') }}
-      </p>
-    </b-toast>
-
-    <b-toast id="switch-images-2" variant="info" solid :no-auto-hide="true">
-      <template #toast-title>
-        <status-icon status="info" class="toast-icon" />
-        <strong>{{
-          $t('pageFirmware.singleFileUpload.toast.verifySwitch')
-        }}</strong>
-      </template>
-      <p>
-        {{ $t('pageFirmware.singleFileUpload.toast.verifySwitchMessage') }}
-      </p>
-      <b-link @click="onClickRefresh">{{ $t('global.action.refresh') }}</b-link>
-    </b-toast>
-
-    <b-toast id="update-firmware" variant="info" solid :no-auto-hide="true">
-      <template #toast-title>
-        <status-icon status="info" class="toast-icon" />
-        <strong>{{
-          $t('pageFirmware.singleFileUpload.toast.updateStarted')
-        }}</strong>
-      </template>
-      <p>
-        {{ $t('pageFirmware.singleFileUpload.toast.updateStartedMessage') }}
-      </p>
-      <p class="m-0">
-        {{ $t('pageFirmware.singleFileUpload.toast.startTime') }}
-        {{ $options.filters.formatTime(new Date()) }}
-      </p>
-    </b-toast>
-
-    <b-toast id="update-firmware-2" variant="info" solid :no-auto-hide="true">
-      <template #toast-title>
-        <status-icon status="info" class="toast-icon" />
-        <strong>{{
-          $t('pageFirmware.singleFileUpload.toast.verifyUpdate')
-        }}</strong>
-      </template>
-      <p>
-        {{ $t('pageFirmware.singleFileUpload.toast.verifyUpdateMessage') }}
-      </p>
-      <b-link @click="onClickRefresh">{{ $t('global.action.refresh') }}</b-link>
-    </b-toast>
   </b-container>
 </template>
 
@@ -285,7 +230,6 @@
   mixins: [BVToastMixin, LoadingBarMixin, VuelidateMixin],
   beforeRouteLeave(to, from, next) {
     this.hideLoader();
-    this.clearRebootTimeout();
     next();
   },
   data() {
@@ -354,8 +298,22 @@
   },
   methods: {
     updateFirmware() {
-      this.setRebootTimeout(360000, 'update-firmware-2'); //6 minute timeout
-      this.$bvToast.show('update-firmware');
+      this.setRebootTimeout(360000, () => {
+        this.infoToast(
+          this.$t('pageFirmware.singleFileUpload.toast.verifyUpdateMessage'),
+          {
+            title: this.$t('pageFirmware.singleFileUpload.toast.verifyUpdate'),
+            refreshAction: true,
+          }
+        );
+      });
+      this.infoToast(
+        this.$t('pageFirmware.singleFileUpload.toast.updateStartedMessage'),
+        {
+          title: this.$t('pageFirmware.singleFileUpload.toast.updateStarted'),
+          timestamp: true,
+        }
+      );
       if (this.isWorkstationSelected) {
         this.dispatchWorkstationUpload();
       } else {
@@ -382,24 +340,39 @@
         });
     },
     switchToRunning() {
-      this.setRebootTimeout(60000, 'switch-images-2');
+      this.setRebootTimeout(60000, () => {
+        this.infoToast(
+          this.$t('pageFirmware.singleFileUpload.toast.verifySwitchMessage'),
+          {
+            title: this.$t('pageFirmware.singleFileUpload.toast.verifySwitch'),
+            refreshAction: true,
+          }
+        );
+      });
       this.$store
         .dispatch('firmwareSingleImage/switchFirmwareAndReboot')
-        .then(() => this.$bvToast.show('switch-images'))
+        .then(() =>
+          this.infoToast(
+            this.$t('pageFirmware.singleFileUpload.toast.rebootStartedMessage'),
+            {
+              title: this.$t(
+                'pageFirmware.singleFileUpload.toast.rebootStarted'
+              ),
+            }
+          )
+        )
         .catch(({ message }) => {
           this.errorToast(message);
           this.clearRebootTimeout();
         });
     },
-    setRebootTimeout(timeoutMs = 60000, toastId) {
+    setRebootTimeout(timeoutMs = 60000, callback) {
       // Set a timeout to disable page interactions
       // during a BMC reboot
       this.startLoader();
       this.timeoutId = setTimeout(() => {
         this.endLoader();
-        if (toastId) {
-          this.$bvToast.show(toastId);
-        }
+        if (callback) callback();
       }, timeoutMs);
     },
     clearRebootTimeout() {
@@ -417,9 +390,6 @@
       this.file = file;
       this.$v.file.$touch();
     },
-    onClickRefresh() {
-      this.$root.$emit('refresh-application');
-    },
   },
 };
 </script>
diff --git a/src/locales/en-US.json b/src/locales/en-US.json
index d3b6d75..8ca98bf 100644
--- a/src/locales/en-US.json
+++ b/src/locales/en-US.json
@@ -346,7 +346,6 @@
         "errorSwitchImages": "Error switching running and backup images.",
         "rebootStarted": "Reboot started",
         "rebootStartedMessage": "Successfully started reboot from backup image.",
-        "startTime": "Start time:",
         "updateStarted": "Update started",
         "updateStartedMessage": "Wait for the firmware update notification before making any changes.",
         "verifySwitch": "Verify switch",
diff --git a/src/views/Configuration/Firmware/Firmware.vue b/src/views/Configuration/Firmware/Firmware.vue
index 0e16206..5001702 100644
--- a/src/views/Configuration/Firmware/Firmware.vue
+++ b/src/views/Configuration/Firmware/Firmware.vue
@@ -282,7 +282,7 @@
       this.setRebootTimeout(360000); //6 minute timeout
       this.infoToast(
         this.$t('pageFirmware.toast.infoUploadStartTimeMessage', { startTime }),
-        this.$t('pageFirmware.toast.infoUploadStartTimeTitle')
+        { title: this.$t('pageFirmware.toast.infoUploadStartTimeTitle') }
       );
       if (this.isWorkstationSelected) {
         this.dispatchWorkstationUpload();
@@ -294,10 +294,9 @@
       this.$store
         .dispatch('firmware/uploadFirmware', this.file)
         .then((success) =>
-          this.infoToast(
-            success,
-            this.$t('pageFirmware.toast.successUploadTitle')
-          )
+          this.infoToast(success, {
+            title: this.$t('pageFirmware.toast.successUploadTitle'),
+          })
         )
         .catch(({ message }) => {
           this.errorToast(message);
@@ -312,10 +311,9 @@
       this.$store
         .dispatch('firmware/uploadFirmwareTFTP', data)
         .then((success) =>
-          this.infoToast(
-            success,
-            this.$t('pageFirmware.toast.successUploadTitle')
-          )
+          this.infoToast(success, {
+            title: this.$t('pageFirmware.toast.successUploadTitle'),
+          })
         )
         .catch(({ message }) => {
           this.errorToast(message);
@@ -327,7 +325,7 @@
       this.$store
         .dispatch('firmware/switchBmcFirmware')
         .then((success) =>
-          this.infoToast(success, this.$t('global.status.success'))
+          this.infoToast(success, { title: this.$t('global.status.success') })
         )
         .catch(({ message }) => {
           this.errorToast(message);
@@ -342,7 +340,10 @@
         this.endLoader();
         this.infoToast(
           this.$t('pageFirmware.toast.infoRefreshApplicationMessage'),
-          this.$t('pageFirmware.toast.infoRefreshApplicationTitle')
+          {
+            title: this.$t('pageFirmware.toast.infoRefreshApplicationTitle'),
+            refreshAction: true,
+          }
         );
       }, timeoutMs);
     },