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');
     },
   },
 };