You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by nv...@apache.org on 2021/07/19 11:51:16 UTC

[cloudstack] branch 4.15 updated: UI: Refactor async job polling codebase-wide (#4782)

This is an automated email from the ASF dual-hosted git repository.

nvazquez pushed a commit to branch 4.15
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/4.15 by this push:
     new 535761b  UI: Refactor async job polling codebase-wide (#4782)
535761b is described below

commit 535761b2b9b2571f67d88335386fef1d3ea88085
Author: Hoang Nguyen <ho...@unitech.vn>
AuthorDate: Mon Jul 19 18:50:54 2021 +0700

    UI: Refactor async job polling codebase-wide (#4782)
    
    * refactor async job polling codebase-wide
    
    * fix multiple call fetchData() when async job completed
    
    * remove unnecessary functions
    
    * remove const not use
    
    * move closeaction out of handleResponse
    
    * call closeAction without waiting for all group actions to complete
    
    * refactor polljob network provider
    
    * removed variable not use
    
    * remove await
---
 ui/src/components/header/HeaderNotice.vue          |  70 ++-----------
 ui/src/components/view/ActionButton.vue            |   1 +
 ui/src/components/view/DedicateData.vue            |  44 ++------
 ui/src/components/view/DedicateModal.vue           |  42 ++------
 ui/src/core/bootstrap.js                           |   4 +-
 ui/src/store/getters.js                            |   2 +-
 ui/src/store/modules/user.js                       |  32 ++++--
 ui/src/store/mutation-types.js                     |   2 +-
 ui/src/utils/plugins.js                            |  40 +++++++-
 ui/src/views/AutogenView.vue                       | 111 +++++++++++----------
 ui/src/views/compute/AttachIso.vue                 |  11 +-
 ui/src/views/compute/CreateKubernetesCluster.vue   |  14 +--
 ui/src/views/compute/CreateSnapshotWizard.vue      |   8 +-
 ui/src/views/compute/DeployVM.vue                  |  15 +--
 ui/src/views/compute/DestroyVM.vue                 |  10 +-
 ui/src/views/compute/InstanceTab.vue               |  12 ---
 ui/src/views/compute/MigrateWizard.vue             |   8 +-
 ui/src/views/compute/ScaleKubernetesCluster.vue    |  14 +--
 ui/src/views/compute/StartVirtualMachine.vue       |  12 +--
 ui/src/views/compute/UpgradeKubernetesCluster.vue  |  14 +--
 ui/src/views/iam/DomainActionForm.vue              |  47 ++++-----
 ui/src/views/image/IsoZones.vue                    |  16 +--
 ui/src/views/image/TemplateZones.vue               |  16 +--
 ui/src/views/infra/ClusterAdd.vue                  |   8 +-
 ui/src/views/infra/HostAdd.vue                     |   8 +-
 ui/src/views/infra/MigrateData.vue                 |  41 +++-----
 ui/src/views/infra/PodAdd.vue                      |   8 +-
 ui/src/views/infra/network/DedicatedVLANTab.vue    |   7 +-
 ui/src/views/infra/network/EditTrafficLabel.vue    |  11 +-
 .../views/infra/network/IpRangesTabManagement.vue  |  16 ++-
 ui/src/views/infra/network/IpRangesTabStorage.vue  |  16 ++-
 ui/src/views/infra/network/ServiceProvidersTab.vue |   8 +-
 .../infra/network/providers/AddF5LoadBalancer.vue  |  45 +++------
 .../network/providers/AddNetscalerLoadBalancer.vue |  45 +++------
 .../infra/network/providers/AddNiciraNvpDevice.vue |  45 +++------
 .../network/providers/AddPaloAltoFirewall.vue      |  45 +++------
 .../infra/network/providers/AddSrxFirewall.vue     |  45 +++------
 .../infra/network/providers/ProviderListView.vue   |  18 ++--
 ui/src/views/network/AclListRulesTab.vue           |  35 ++-----
 ui/src/views/network/CreateVpc.vue                 |  11 +-
 ui/src/views/network/CreateVpnCustomerGateway.vue  |  11 +-
 ui/src/views/network/FirewallRules.vue             |   4 -
 .../views/network/IngressEgressRuleConfigure.vue   |  10 --
 ui/src/views/network/InternalLBAssignVmForm.vue    |   7 --
 ui/src/views/network/LoadBalancing.vue             |  15 ---
 ui/src/views/network/PortForwarding.vue            |   6 --
 ui/src/views/network/StaticRoutesTab.vue           |  14 +--
 ui/src/views/network/VpcTab.vue                    |  28 ++----
 ui/src/views/network/VpcTiersTab.vue               |   8 +-
 ui/src/views/network/VpnDetails.vue                |   4 -
 ui/src/views/offering/ImportBackupOffering.vue     |  11 +-
 ui/src/views/project/AccountsTab.vue               |  10 +-
 ui/src/views/project/AddAccountOrUserToProject.vue |   2 -
 ui/src/views/project/InvitationTokenTemplate.vue   |   4 +-
 ui/src/views/project/InvitationsTemplate.vue       |   4 +-
 ui/src/views/storage/AttachVolume.vue              |  12 +--
 .../views/storage/CreateSnapshotFromVMSnapshot.vue |  15 +--
 ui/src/views/storage/CreateVolume.vue              |  15 +--
 ui/src/views/storage/MigrateVolume.vue             |   7 --
 ui/src/views/storage/ResizeVolume.vue              |  11 +-
 ui/src/views/storage/RestoreAttachBackupVolume.vue |  10 +-
 ui/src/views/storage/TakeSnapshot.vue              |  12 +--
 62 files changed, 364 insertions(+), 813 deletions(-)

diff --git a/ui/src/components/header/HeaderNotice.vue b/ui/src/components/header/HeaderNotice.vue
index 03a5acd..d20f606 100644
--- a/ui/src/components/header/HeaderNotice.vue
+++ b/ui/src/components/header/HeaderNotice.vue
@@ -32,16 +32,16 @@
               <a-button size="small" slot="description" @click="clearJobs">{{ $t('label.clear.list') }}</a-button>
             </a-list-item-meta>
           </a-list-item>
-          <a-list-item v-for="(job, index) in jobs" :key="index">
-            <a-list-item-meta :title="job.title" :description="job.description">
-              <a-avatar :style="notificationAvatar[job.status].style" :icon="notificationAvatar[job.status].icon" slot="avatar"/>
+          <a-list-item v-for="(notice, index) in notices" :key="index">
+            <a-list-item-meta :title="notice.title" :description="notice.description">
+              <a-avatar :style="notificationAvatar[notice.status].style" :icon="notificationAvatar[notice.status].icon" slot="avatar"/>
             </a-list-item-meta>
           </a-list-item>
         </a-list>
       </a-spin>
     </template>
     <span @click="showNotifications" class="header-notice-opener">
-      <a-badge :count="jobs.length">
+      <a-badge :count="notices.length">
         <a-icon class="header-notice-icon" type="bell" />
       </a-badge>
     </span>
@@ -49,7 +49,6 @@
 </template>
 
 <script>
-import { api } from '@/api'
 import store from '@/store'
 
 export default {
@@ -58,7 +57,7 @@ export default {
     return {
       loading: false,
       visible: false,
-      jobs: [],
+      notices: [],
       poller: null,
       notificationAvatar: {
         done: { icon: 'check-circle', style: 'backgroundColor:#87d068' },
@@ -72,66 +71,17 @@ export default {
       this.visible = !this.visible
     },
     clearJobs () {
-      this.jobs = this.jobs.filter(x => x.status === 'progress')
-      this.$store.commit('SET_ASYNC_JOB_IDS', this.jobs)
-    },
-    startPolling () {
-      this.poller = setInterval(() => {
-        this.pollJobs()
-      }, 4000)
-    },
-    async pollJobs () {
-      var hasUpdated = false
-      for (var i in this.jobs) {
-        if (this.jobs[i].status === 'progress') {
-          await api('queryAsyncJobResult', { jobid: this.jobs[i].jobid }).then(json => {
-            var result = json.queryasyncjobresultresponse
-            if (result.jobstatus === 1 && this.jobs[i].status !== 'done') {
-              hasUpdated = true
-              const title = this.jobs[i].title
-              const description = this.jobs[i].description
-              this.$message.success({
-                content: title + (description ? ' - ' + description : ''),
-                key: this.jobs[i].jobid,
-                duration: 2
-              })
-              this.jobs[i].status = 'done'
-            } else if (result.jobstatus === 2 && this.jobs[i].status !== 'failed') {
-              hasUpdated = true
-              this.jobs[i].status = 'failed'
-              if (result.jobresult.errortext !== null) {
-                this.jobs[i].description = '(' + this.jobs[i].description + ') ' + result.jobresult.errortext
-              }
-              this.$notification.error({
-                message: this.jobs[i].title,
-                description: this.jobs[i].description,
-                key: this.jobs[i].jobid,
-                duration: 0
-              })
-            }
-          }).catch(function (e) {
-            console.log(this.$t('error.fetching.async.job.result') + e)
-          })
-        }
-      }
-      if (hasUpdated) {
-        this.$store.commit('SET_ASYNC_JOB_IDS', this.jobs.reverse())
-      }
+      this.notices = this.notices.filter(x => x.status === 'progress')
+      this.$store.commit('SET_HEADER_NOTICES', this.notices)
     }
   },
-  beforeDestroy () {
-    clearInterval(this.poller)
-  },
-  created () {
-    this.startPolling()
-  },
   mounted () {
-    this.jobs = (store.getters.asyncJobIds || []).reverse()
+    this.notices = (store.getters.headerNotices || []).reverse()
     this.$store.watch(
-      (state, getters) => getters.asyncJobIds,
+      (state, getters) => getters.headerNotices,
       (newValue, oldValue) => {
         if (oldValue !== newValue && newValue !== undefined) {
-          this.jobs = newValue.reverse()
+          this.notices = newValue.reverse()
         }
       }
     )
diff --git a/ui/src/components/view/ActionButton.vue b/ui/src/components/view/ActionButton.vue
index bf9f63b..7d5497b 100644
--- a/ui/src/components/view/ActionButton.vue
+++ b/ui/src/components/view/ActionButton.vue
@@ -143,6 +143,7 @@ export default {
       this.actionBadge = {}
       const arrAsync = []
       const actionBadge = this.actions.filter(action => action.showBadge === true)
+      if ((actionBadge.dataView ? actionBadge.dataView : false) !== this.dataView) return
 
       if (actionBadge && actionBadge.length > 0) {
         const dataLength = actionBadge.length
diff --git a/ui/src/components/view/DedicateData.vue b/ui/src/components/view/DedicateData.vue
index af50eac..0818c70 100644
--- a/ui/src/components/view/DedicateData.vue
+++ b/ui/src/components/view/DedicateData.vue
@@ -175,20 +175,13 @@ export default {
       }).then(response => {
         this.$pollJob({
           jobId: response.releasededicatedzoneresponse.jobid,
+          title: this.$t('label.release.dedicated.zone'),
+          description: this.resource.id,
           successMessage: this.$t('message.dedicated.zone.released'),
           successMethod: () => {
-            this.parentFetchData()
             this.dedicatedDomainId = null
-            this.$store.dispatch('AddAsyncJob', {
-              title: this.$t('message.dedicated.zone.released'),
-              jobid: response.releasededicatedzoneresponse.jobid,
-              status: 'progress'
-            })
           },
           errorMessage: this.$t('error.release.dedicate.zone'),
-          errorMethod: () => {
-            this.parentFetchData()
-          },
           loadingMessage: this.$t('message.releasing.dedicated.zone'),
           catchMessage: this.$t('error.fetching.async.job.result'),
           catchMethod: () => {
@@ -205,20 +198,13 @@ export default {
       }).then(response => {
         this.$pollJob({
           jobId: response.releasededicatedpodresponse.jobid,
+          title: this.$t('label.release.dedicated.pod'),
+          description: this.resource.id,
           successMessage: this.$t('message.pod.dedication.released'),
           successMethod: () => {
-            this.parentFetchData()
             this.dedicatedDomainId = null
-            this.$store.dispatch('AddAsyncJob', {
-              title: this.$t('message.pod.dedication.released'),
-              jobid: response.releasededicatedpodresponse.jobid,
-              status: 'progress'
-            })
           },
           errorMessage: this.$t('error.release.dedicate.pod'),
-          errorMethod: () => {
-            this.parentFetchData()
-          },
           loadingMessage: this.$t('message.releasing.dedicated.pod'),
           catchMessage: this.$t('error.fetching.async.job.result'),
           catchMethod: () => {
@@ -235,20 +221,13 @@ export default {
       }).then(response => {
         this.$pollJob({
           jobId: response.releasededicatedclusterresponse.jobid,
+          title: this.$t('label.release.dedicated.cluster'),
+          description: this.resource.id,
           successMessage: this.$t('message.cluster.dedication.released'),
           successMethod: () => {
-            this.parentFetchData()
             this.dedicatedDomainId = null
-            this.$store.dispatch('AddAsyncJob', {
-              title: this.$t('message.cluster.dedication.released'),
-              jobid: response.releasededicatedclusterresponse.jobid,
-              status: 'progress'
-            })
           },
           errorMessage: this.$t('error.release.dedicate.cluster'),
-          errorMethod: () => {
-            this.parentFetchData()
-          },
           loadingMessage: this.$t('message.releasing.dedicated.cluster'),
           catchMessage: this.$t('error.fetching.async.job.result'),
           catchMethod: () => {
@@ -265,20 +244,13 @@ export default {
       }).then(response => {
         this.$pollJob({
           jobId: response.releasededicatedhostresponse.jobid,
+          title: this.$t('label.release.dedicated.host'),
+          description: this.resource.id,
           successMessage: this.$t('message.host.dedication.released'),
           successMethod: () => {
-            this.parentFetchData()
             this.dedicatedDomainId = null
-            this.$store.dispatch('AddAsyncJob', {
-              title: this.$t('message.host.dedication.released'),
-              jobid: response.releasededicatedhostresponse.jobid,
-              status: 'progress'
-            })
           },
           errorMessage: this.$t('error.release.dedicate.host'),
-          errorMethod: () => {
-            this.parentFetchData()
-          },
           loadingMessage: this.$t('message.releasing.dedicated.host'),
           catchMessage: this.$t('error.fetching.async.job.result'),
           catchMethod: () => {
diff --git a/ui/src/components/view/DedicateModal.vue b/ui/src/components/view/DedicateModal.vue
index 9443e42..caf564e 100644
--- a/ui/src/components/view/DedicateModal.vue
+++ b/ui/src/components/view/DedicateModal.vue
@@ -93,22 +93,16 @@ export default {
       }).then(response => {
         this.$pollJob({
           jobId: response.dedicatezoneresponse.jobid,
-          successMessage: this.$t('label.zone.dedicated'),
+          title: this.$t('label.dedicate.zone'),
+          description: `${this.$t('label.domain.id')} : ${this.domainId}`,
+          successMessage: `${this.$t('label.zone.dedicated')}`,
           successMethod: () => {
-            this.parentFetchData()
             this.fetchParentData()
             this.dedicatedDomainId = this.domainId
             this.dedicatedDomainModal = false
-            this.$store.dispatch('AddAsyncJob', {
-              title: this.$t('label.zone.dedicated'),
-              jobid: response.dedicatezoneresponse.jobid,
-              description: `${this.$t('label.domain.id')} : ${this.dedicatedDomainId}`,
-              status: 'progress'
-            })
           },
           errorMessage: this.$t('error.dedicate.zone.failed'),
           errorMethod: () => {
-            this.parentFetchData()
             this.fetchParentData()
             this.dedicatedDomainModal = false
           },
@@ -137,22 +131,16 @@ export default {
       }).then(response => {
         this.$pollJob({
           jobId: response.dedicatepodresponse.jobid,
+          title: this.$t('label.dedicate.pod'),
+          description: `${this.$t('label.domain.id')} : ${this.domainId}`,
           successMessage: this.$t('label.pod.dedicated'),
           successMethod: () => {
-            this.parentFetchData()
             this.fetchParentData()
             this.dedicatedDomainId = this.domainId
             this.dedicatedDomainModal = false
-            this.$store.dispatch('AddAsyncJob', {
-              title: this.$t('label.pod.dedicated'),
-              jobid: response.dedicatepodresponse.jobid,
-              description: `${this.$t('label.domainid')}: ${this.dedicatedDomainId}`,
-              status: 'progress'
-            })
           },
           errorMessage: this.$t('error.dedicate.pod.failed'),
           errorMethod: () => {
-            this.parentFetchData()
             this.fetchParentData()
             this.dedicatedDomainModal = false
           },
@@ -181,22 +169,16 @@ export default {
       }).then(response => {
         this.$pollJob({
           jobId: response.dedicateclusterresponse.jobid,
+          title: this.$t('label.dedicate.cluster'),
+          description: `${this.$t('label.domain.id')} : ${this.domainId}`,
           successMessage: this.$t('message.cluster.dedicated'),
           successMethod: () => {
-            this.parentFetchData()
             this.fetchParentData()
             this.dedicatedDomainId = this.domainId
             this.dedicatedDomainModal = false
-            this.$store.dispatch('AddAsyncJob', {
-              title: this.$t('message.cluster.dedicated'),
-              jobid: response.dedicateclusterresponse.jobid,
-              description: `${this.$t('label.domainid')}: ${this.dedicatedDomainId}`,
-              status: 'progress'
-            })
           },
           errorMessage: this.$t('error.dedicate.cluster.failed'),
           errorMethod: () => {
-            this.parentFetchData()
             this.fetchParentData()
             this.dedicatedDomainModal = false
           },
@@ -225,22 +207,16 @@ export default {
       }).then(response => {
         this.$pollJob({
           jobId: response.dedicatehostresponse.jobid,
+          title: this.$t('label.dedicate.host'),
+          description: `${this.$t('label.domain.id')} : ${this.domainId}`,
           successMessage: this.$t('message.host.dedicated'),
           successMethod: () => {
-            this.parentFetchData()
             this.fetchParentData()
             this.dedicatedDomainId = this.domainId
             this.dedicatedDomainModal = false
-            this.$store.dispatch('AddAsyncJob', {
-              title: this.$t('message.host.dedicated'),
-              jobid: response.dedicatehostresponse.jobid,
-              description: `${this.$t('label.domainid')}: ${this.dedicatedDomainId}`,
-              status: 'progress'
-            })
           },
           errorMessage: this.$t('error.dedicate.host.failed'),
           errorMethod: () => {
-            this.parentFetchData()
             this.fetchParentData()
             this.dedicatedDomainModal = false
           },
diff --git a/ui/src/core/bootstrap.js b/ui/src/core/bootstrap.js
index ceefe3f..db49277 100644
--- a/ui/src/core/bootstrap.js
+++ b/ui/src/core/bootstrap.js
@@ -31,7 +31,7 @@ import {
   DEFAULT_FIXED_SIDEMENU,
   DEFAULT_CONTENT_WIDTH_TYPE,
   DEFAULT_MULTI_TAB,
-  ASYNC_JOB_IDS
+  HEADER_NOTICES
 } from '@/store/mutation-types'
 
 export default function Initializer () {
@@ -47,5 +47,5 @@ export default function Initializer () {
   store.commit('TOGGLE_MULTI_TAB', Vue.ls.get(DEFAULT_MULTI_TAB, config.multiTab))
   store.commit('SET_TOKEN', Vue.ls.get(ACCESS_TOKEN))
   store.commit('SET_PROJECT', Vue.ls.get(CURRENT_PROJECT))
-  store.commit('SET_ASYNC_JOB_IDS', Vue.ls.get(ASYNC_JOB_IDS) || [])
+  store.commit('SET_HEADER_NOTICES', Vue.ls.get(HEADER_NOTICES) || [])
 }
diff --git a/ui/src/store/getters.js b/ui/src/store/getters.js
index 359dccc..ed9ab40 100644
--- a/ui/src/store/getters.js
+++ b/ui/src/store/getters.js
@@ -30,7 +30,7 @@ const getters = {
   userInfo: state => state.user.info,
   addRouters: state => state.permission.addRouters,
   multiTab: state => state.app.multiTab,
-  asyncJobIds: state => state.user.asyncJobIds,
+  headerNotices: state => state.user.headerNotices,
   isLdapEnabled: state => state.user.isLdapEnabled,
   cloudian: state => state.user.cloudian,
   zones: state => state.user.zones,
diff --git a/ui/src/store/modules/user.js b/ui/src/store/modules/user.js
index d764ae4..d2a5fd6 100644
--- a/ui/src/store/modules/user.js
+++ b/ui/src/store/modules/user.js
@@ -32,7 +32,7 @@ import {
   ZONES,
   TIMEZONE_OFFSET,
   USE_BROWSER_TIMEZONE,
-  ASYNC_JOB_IDS,
+  HEADER_NOTICES,
   DOMAIN_STORE
 } from '@/store/mutation-types'
 
@@ -46,6 +46,7 @@ const user = {
     features: {},
     project: {},
     asyncJobIds: [],
+    headerNotices: [],
     isLdapEnabled: false,
     cloudian: {},
     zones: {},
@@ -86,9 +87,9 @@ const user = {
     SET_FEATURES: (state, features) => {
       state.features = features
     },
-    SET_ASYNC_JOB_IDS: (state, jobsJsonArray) => {
-      Vue.ls.set(ASYNC_JOB_IDS, jobsJsonArray)
-      state.asyncJobIds = jobsJsonArray
+    SET_HEADER_NOTICES: (state, noticeJsonArray) => {
+      Vue.ls.set(HEADER_NOTICES, noticeJsonArray)
+      state.headerNotices = noticeJsonArray
     },
     SET_LDAP: (state, isLdapEnabled) => {
       state.isLdapEnabled = isLdapEnabled
@@ -137,7 +138,7 @@ const user = {
           commit('SET_AVATAR', '')
           commit('SET_INFO', {})
           commit('SET_PROJECT', {})
-          commit('SET_ASYNC_JOB_IDS', [])
+          commit('SET_HEADER_NOTICES', [])
           commit('SET_FEATURES', {})
           commit('SET_LDAP', {})
           commit('SET_CLOUDIAN', {})
@@ -264,7 +265,7 @@ const user = {
         commit('SET_TOKEN', '')
         commit('SET_APIS', {})
         commit('SET_PROJECT', {})
-        commit('SET_ASYNC_JOB_IDS', [])
+        commit('SET_HEADER_NOTICES', [])
         commit('SET_FEATURES', {})
         commit('SET_LDAP', {})
         commit('SET_CLOUDIAN', {})
@@ -272,7 +273,7 @@ const user = {
         commit('SET_DOMAIN_STORE', {})
         Vue.ls.remove(CURRENT_PROJECT)
         Vue.ls.remove(ACCESS_TOKEN)
-        Vue.ls.remove(ASYNC_JOB_IDS)
+        Vue.ls.remove(HEADER_NOTICES)
 
         logout(state.token).then(() => {
           message.destroy()
@@ -286,10 +287,19 @@ const user = {
         })
       })
     },
-    AddAsyncJob ({ commit }, jobJson) {
-      var jobsArray = Vue.ls.get(ASYNC_JOB_IDS, [])
-      jobsArray.push(jobJson)
-      commit('SET_ASYNC_JOB_IDS', jobsArray)
+    AddHeaderNotice ({ commit }, noticeJson) {
+      if (!noticeJson || !noticeJson.title) {
+        return
+      }
+      const noticeArray = Vue.ls.get(HEADER_NOTICES, [])
+      const noticeIdx = noticeArray.findIndex(notice => notice.key === noticeJson.key)
+      if (noticeIdx === -1) {
+        noticeArray.push(noticeJson)
+      } else {
+        noticeArray[noticeIdx] = noticeJson
+      }
+
+      commit('SET_HEADER_NOTICES', noticeArray)
     },
     ProjectView ({ commit }, projectid) {
       return new Promise((resolve, reject) => {
diff --git a/ui/src/store/mutation-types.js b/ui/src/store/mutation-types.js
index 01a9755..948f938 100644
--- a/ui/src/store/mutation-types.js
+++ b/ui/src/store/mutation-types.js
@@ -29,7 +29,7 @@ export const DEFAULT_CONTENT_WIDTH_TYPE = 'DEFAULT_CONTENT_WIDTH_TYPE'
 export const DEFAULT_MULTI_TAB = 'DEFAULT_MULTI_TAB'
 export const APIS = 'APIS'
 export const ZONES = 'ZONES'
-export const ASYNC_JOB_IDS = 'ASYNC_JOB_IDS'
+export const HEADER_NOTICES = 'HEADER_NOTICES'
 export const TIMEZONE_OFFSET = 'TIMEZONE_OFFSET'
 export const USE_BROWSER_TIMEZONE = 'USE_BROWSER_TIMEZONE'
 export const DOMAIN_STORE = 'DOMAIN_STORE'
diff --git a/ui/src/utils/plugins.js b/ui/src/utils/plugins.js
index 87e4e74..c7a385c 100644
--- a/ui/src/utils/plugins.js
+++ b/ui/src/utils/plugins.js
@@ -20,6 +20,7 @@ import { i18n } from '@/locales'
 import { api } from '@/api'
 import { message, notification } from 'ant-design-vue'
 import eventBus from '@/config/eventBus'
+import store from '@/store'
 
 export const pollJobPlugin = {
   install (Vue) {
@@ -27,6 +28,8 @@ export const pollJobPlugin = {
       /**
        * @param {String} jobId
        * @param {String} [name='']
+       * @param {String} [title='']
+       * @param {String} [description='']
        * @param {String} [successMessage=Success]
        * @param {Function} [successMethod=() => {}]
        * @param {String} [errorMessage=Error]
@@ -40,6 +43,8 @@ export const pollJobPlugin = {
       const {
         jobId,
         name = '',
+        title = '',
+        description = '',
         successMessage = i18n.t('label.success'),
         successMethod = () => {},
         errorMessage = i18n.t('label.error'),
@@ -51,6 +56,13 @@ export const pollJobPlugin = {
         action = null
       } = options
 
+      store.dispatch('AddHeaderNotice', {
+        key: jobId,
+        title: title,
+        description: description,
+        status: 'progress'
+      })
+
       api('queryAsyncJobResult', { jobId }).then(json => {
         const result = json.queryasyncjobresultresponse
         if (result.jobstatus === 1) {
@@ -66,7 +78,16 @@ export const pollJobPlugin = {
             key: jobId,
             duration: 2
           })
-          eventBus.$emit('async-job-complete', action)
+          store.dispatch('AddHeaderNotice', {
+            key: jobId,
+            title: title,
+            description: description,
+            status: 'done',
+            duration: 2
+          })
+          if (!action || !('isFetchData' in action) || (action.isFetchData)) {
+            eventBus.$emit('async-job-complete')
+          }
           successMethod(result)
         } else if (result.jobstatus === 2) {
           message.error({
@@ -74,21 +95,30 @@ export const pollJobPlugin = {
             key: jobId,
             duration: 1
           })
-          var title = errorMessage
+          var errTitle = errorMessage
           if (action && action.label) {
-            title = i18n.t(action.label)
+            errTitle = i18n.t(action.label)
           }
           var desc = result.jobresult.errortext
           if (name) {
             desc = `(${name}) ${desc}`
           }
           notification.error({
-            message: title,
+            message: errTitle,
             description: desc,
             key: jobId,
             duration: 0
           })
-          eventBus.$emit('async-job-complete', action)
+          store.dispatch('AddHeaderNotice', {
+            key: jobId,
+            title: title,
+            description: desc,
+            status: 'failed',
+            duration: 0
+          })
+          if (!action || !('isFetchData' in action) || (action.isFetchData)) {
+            eventBus.$emit('async-job-complete')
+          }
           errorMethod(result)
         } else if (result.jobstatus === 0) {
           if (showLoading) {
diff --git a/ui/src/views/AutogenView.vue b/ui/src/views/AutogenView.vue
index ebeca6e..f274ecf 100644
--- a/ui/src/views/AutogenView.vue
+++ b/ui/src/views/AutogenView.vue
@@ -401,12 +401,18 @@ export default {
       actions: [],
       formModel: {},
       confirmDirty: false,
+      promises: [],
       firstIndex: 0
     }
   },
   beforeCreate () {
     this.form = this.$form.createForm(this)
   },
+  beforeDestroy () {
+    eventBus.$off('vm-refresh-data')
+    eventBus.$off('async-job-complete')
+    eventBus.$off('exec-action')
+  },
   created () {
     eventBus.$on('vm-refresh-data', () => {
       if (this.$route.path === '/vm' || this.$route.path.includes('/vm/')) {
@@ -414,11 +420,6 @@ export default {
       }
     })
     eventBus.$on('async-job-complete', (action) => {
-      if (this.$route.path.includes('/vm/')) {
-        if (action && 'api' in action && ['destroyVirtualMachine'].includes(action.api)) {
-          return
-        }
-      }
       this.fetchData()
     })
     eventBus.$on('exec-action', (action, isGroupAction) => {
@@ -856,30 +857,33 @@ export default {
       })
     },
     pollActionCompletion (jobId, action, resourceName, showLoading = true) {
-      this.$pollJob({
-        jobId,
-        name: resourceName,
-        successMethod: result => {
-          this.fetchData()
-          if (action.response) {
-            const description = action.response(result.jobresult)
-            if (description) {
-              this.$notification.info({
-                message: this.$t(action.label),
-                description: (<span domPropsInnerHTML={description}></span>),
-                duration: 0
-              })
+      return new Promise((resolve) => {
+        this.$pollJob({
+          jobId,
+          title: this.$t(action.label),
+          description: resourceName,
+          name: resourceName,
+          successMethod: result => {
+            if (action.response) {
+              const description = action.response(result.jobresult)
+              if (description) {
+                this.$notification.info({
+                  message: this.$t(action.label),
+                  description: (<span domPropsInnerHTML={description}></span>),
+                  duration: 0
+                })
+              }
             }
-          }
-          if ('successMethod' in action) {
-            action.successMethod(this, result)
-          }
-        },
-        errorMethod: () => this.fetchData(),
-        loadingMessage: `${this.$t(action.label)} - ${resourceName}`,
-        showLoading: showLoading,
-        catchMessage: this.$t('error.fetching.async.job.result'),
-        action
+            resolve(true)
+          },
+          errorMethod: () => {
+            resolve(true)
+          },
+          loadingMessage: `${this.$t(action.label)} - ${resourceName}`,
+          showLoading: showLoading,
+          catchMessage: this.$t('error.fetching.async.job.result'),
+          action
+        })
       })
     },
     fillEditFormFieldValues () {
@@ -900,6 +904,7 @@ export default {
       })
     },
     handleSubmit (e) {
+      this.promises = []
       if (!this.dataView && this.currentAction.groupAction && this.selectedRowKeys.length > 0) {
         this.form.validateFields((err, values) => {
           if (!err) {
@@ -912,18 +917,17 @@ export default {
             for (const params of paramsList) {
               var resourceName = itemsNameMap[params.id]
               // Using a method for this since it's an async call and don't want wrong prarms to be passed
-              this.callGroupApi(params, resourceName)
+              this.promises.push(this.callGroupApi(params, resourceName))
             }
             this.$message.info({
               content: this.$t(this.currentAction.label),
               key: this.currentAction.label,
               duration: 3
             })
-            setTimeout(() => {
+            Promise.all(this.promises).finally(() => {
               this.actionLoading = false
-              this.closeAction()
               this.fetchData()
-            }, 500)
+            })
           }
         })
       } else {
@@ -931,24 +935,27 @@ export default {
       }
     },
     callGroupApi (params, resourceName) {
-      const action = this.currentAction
-      api(action.api, params).then(json => {
-        this.handleResponse(json, resourceName, action, false)
-      }).catch(error => {
-        if ([401].includes(error.response.status)) {
-          return
-        }
-        this.$notifyError(error)
+      return new Promise((resolve, reject) => {
+        const action = this.currentAction
+        api(action.api, params).then(json => {
+          resolve(this.handleResponse(json, resourceName, action, false))
+          this.closeAction()
+        }).catch(error => {
+          if ([401].includes(error.response.status)) {
+            return
+          }
+          this.$notifyError(error)
+        })
       })
     },
     handleResponse (response, resourceName, action, showLoading = true) {
       for (const obj in response) {
         if (obj.includes('response')) {
           if (response[obj].jobid) {
-            const jobid = response[obj].jobid
-            this.$store.dispatch('AddAsyncJob', { title: this.$t(action.label), jobid: jobid, description: resourceName, status: 'progress' })
-            this.pollActionCompletion(jobid, action, resourceName, showLoading)
-            return true
+            return new Promise(resolve => {
+              const jobid = response[obj].jobid
+              resolve(this.pollActionCompletion(jobid, action, resourceName, showLoading))
+            })
           } else {
             var message = action.successMessage ? this.$t(action.successMessage) : this.$t(action.label) +
               (resourceName ? ' - ' + resourceName : '')
@@ -1044,14 +1051,16 @@ export default {
           args = [action.api, params]
         }
         api(...args).then(json => {
-          hasJobId = this.handleResponse(json, resourceName, action)
-          if ((action.icon === 'delete' || ['archiveEvents', 'archiveAlerts', 'unmanageVirtualMachine'].includes(action.api)) && this.dataView) {
-            this.$router.go(-1)
-          } else {
-            if (!hasJobId) {
-              this.fetchData()
+          this.handleResponse(json, resourceName, action).then(jobId => {
+            hasJobId = jobId
+            if ((action.icon === 'delete' || ['archiveEvents', 'archiveAlerts', 'unmanageVirtualMachine'].includes(action.api)) && this.dataView) {
+              this.$router.go(-1)
+            } else {
+              if (!hasJobId) {
+                this.fetchData()
+              }
             }
-          }
+          })
           this.closeAction()
         }).catch(error => {
           if ([401].includes(error.response.status)) {
diff --git a/ui/src/views/compute/AttachIso.vue b/ui/src/views/compute/AttachIso.vue
index bc12c26..655c0bc 100644
--- a/ui/src/views/compute/AttachIso.vue
+++ b/ui/src/views/compute/AttachIso.vue
@@ -56,7 +56,6 @@ export default {
       required: true
     }
   },
-  inject: ['parentFetchData'],
   data () {
     return {
       loading: false,
@@ -131,14 +130,8 @@ export default {
           if (jobId) {
             this.$pollJob({
               jobId,
-              successMethod: result => {
-                this.$store.dispatch('AddAsyncJob', {
-                  title: title,
-                  jobid: jobId,
-                  status: this.$t('progress')
-                })
-                this.parentFetchData()
-              },
+              title: title,
+              description: values.id,
               successMessage: `${this.$t('label.action.attach.iso')} ${this.$t('label.success')}`,
               loadingMessage: `${title} ${this.$t('label.in.progress')}`,
               catchMessage: this.$t('error.fetching.async.job.result')
diff --git a/ui/src/views/compute/CreateKubernetesCluster.vue b/ui/src/views/compute/CreateKubernetesCluster.vue
index c6834da..f30357c 100644
--- a/ui/src/views/compute/CreateKubernetesCluster.vue
+++ b/ui/src/views/compute/CreateKubernetesCluster.vue
@@ -531,23 +531,15 @@ export default {
 
         api('createKubernetesCluster', params).then(json => {
           const jobId = json.createkubernetesclusterresponse.jobid
-          this.$store.dispatch('AddAsyncJob', {
-            title: this.$t('label.kubernetes.cluster.create'),
-            jobid: jobId,
-            description: values.name,
-            status: 'progress'
-          })
           this.$pollJob({
             jobId,
+            title: this.$t('label.kubernetes.cluster.create'),
+            description: values.name,
             loadingMessage: `${this.$t('label.kubernetes.cluster.create')} ${values.name} ${this.$t('label.in.progress')}`,
             catchMessage: this.$t('error.fetching.async.job.result'),
-            successMessage: this.$t('message.success.create.kubernetes.cluter') + ' ' + values.name,
-            successMethod: result => {
-              this.$emit('refresh-data')
-            }
+            successMessage: this.$t('message.success.create.kubernetes.cluter') + ' ' + values.name
           })
           this.closeAction()
-          this.$emit('refresh-data')
         }).catch(error => {
           this.$notifyError(error)
         }).finally(() => {
diff --git a/ui/src/views/compute/CreateSnapshotWizard.vue b/ui/src/views/compute/CreateSnapshotWizard.vue
index 2594b05..a91ff0b 100644
--- a/ui/src/views/compute/CreateSnapshotWizard.vue
+++ b/ui/src/views/compute/CreateSnapshotWizard.vue
@@ -142,6 +142,8 @@ export default {
             if (jobId) {
               this.$pollJob({
                 jobId,
+                title: title,
+                description: description,
                 successMethod: result => {
                   const volumeId = result.jobresult.snapshot.volumeid
                   const snapshotId = result.jobresult.snapshot.id
@@ -154,12 +156,6 @@ export default {
                 loadingMessage: `${title} ${this.$t('label.in.progress')}`,
                 catchMessage: this.$t('error.fetching.async.job.result')
               })
-              this.$store.dispatch('AddAsyncJob', {
-                title: title,
-                jobid: jobId,
-                description: description,
-                status: 'progress'
-              })
             }
           }).catch(error => {
             this.$notifyError(error)
diff --git a/ui/src/views/compute/DeployVM.vue b/ui/src/views/compute/DeployVM.vue
index 7f02e90..7893047 100644
--- a/ui/src/views/compute/DeployVM.vue
+++ b/ui/src/views/compute/DeployVM.vue
@@ -1612,6 +1612,8 @@ export default {
           if (jobId) {
             this.$pollJob({
               jobId,
+              title: title,
+              description: description,
               successMethod: result => {
                 const vm = result.jobresult.virtualmachine
                 const name = vm.displayname || vm.name || vm.id
@@ -1622,23 +1624,16 @@ export default {
                     duration: 0
                   })
                 }
-                eventBus.$emit('vm-refresh-data')
-              },
-              errorMethod: () => {
-                eventBus.$emit('vm-refresh-data')
               },
               loadingMessage: `${title} ${this.$t('label.in.progress')}`,
               catchMessage: this.$t('error.fetching.async.job.result'),
               catchMethod: () => {
                 eventBus.$emit('vm-refresh-data')
+              },
+              action: {
+                isFetchData: false
               }
             })
-            this.$store.dispatch('AddAsyncJob', {
-              title: title,
-              jobid: jobId,
-              description: description,
-              status: 'progress'
-            })
           }
           // Sending a refresh in case it hasn't picked up the new VM
           new Promise(resolve => setTimeout(resolve, 3000)).then(() => {
diff --git a/ui/src/views/compute/DestroyVM.vue b/ui/src/views/compute/DestroyVM.vue
index 56bbb45..e66fd5f 100644
--- a/ui/src/views/compute/DestroyVM.vue
+++ b/ui/src/views/compute/DestroyVM.vue
@@ -122,14 +122,10 @@ export default {
 
         api('destroyVirtualMachine', params).then(json => {
           const jobId = json.destroyvirtualmachineresponse.jobid
-          this.$store.dispatch('AddAsyncJob', {
-            title: this.$t('label.action.destroy.instance'),
-            jobid: jobId,
-            description: this.resource.name,
-            status: 'progress'
-          })
           this.$pollJob({
             jobId,
+            title: this.$t('label.action.destroy.instance'),
+            description: this.resource.name,
             loadingMessage: `${this.$t('message.deleting.vm')} ${this.resource.name}`,
             catchMessage: this.$t('error.fetching.async.job.result'),
             successMessage: `${this.$t('message.success.delete.vm')} ${this.resource.name}`,
@@ -141,7 +137,7 @@ export default {
               }
             },
             action: {
-              api: 'destroyVirtualMachine'
+              isFetchData: false
             }
           })
           this.closeAction()
diff --git a/ui/src/views/compute/InstanceTab.vue b/ui/src/views/compute/InstanceTab.vue
index 1a169d7..dbb1a46 100644
--- a/ui/src/views/compute/InstanceTab.vue
+++ b/ui/src/views/compute/InstanceTab.vue
@@ -477,13 +477,11 @@ export default {
           successMethod: () => {
             this.loadingNic = false
             this.closeModals()
-            this.parentFetchData()
           },
           errorMessage: this.$t('message.add.network.failed'),
           errorMethod: () => {
             this.loadingNic = false
             this.closeModals()
-            this.parentFetchData()
           },
           loadingMessage: this.$t('message.add.network.processing'),
           catchMessage: this.$t('error.fetching.async.job.result'),
@@ -509,12 +507,10 @@ export default {
           successMessage: `${this.$t('label.success.set')} ${item.networkname} ${this.$t('label.as.default')}. ${this.$t('message.set.default.nic.manual')}.`,
           successMethod: () => {
             this.loadingNic = false
-            this.parentFetchData()
           },
           errorMessage: `${this.$t('label.error.setting')} ${item.networkname} ${this.$t('label.as.default')}`,
           errorMethod: () => {
             this.loadingNic = false
-            this.parentFetchData()
           },
           loadingMessage: `${this.$t('label.setting')} ${item.networkname} ${this.$t('label.as.default')}...`,
           catchMessage: this.$t('error.fetching.async.job.result'),
@@ -541,13 +537,11 @@ export default {
           successMethod: () => {
             this.loadingNic = false
             this.closeModals()
-            this.parentFetchData()
           },
           errorMessage: this.$t('label.error'),
           errorMethod: () => {
             this.loadingNic = false
             this.closeModals()
-            this.parentFetchData()
           },
           loadingMessage: this.$t('message.update.ipaddress.processing'),
           catchMessage: this.$t('error.fetching.async.job.result'),
@@ -575,12 +569,10 @@ export default {
           successMessage: this.$t('message.success.remove.nic'),
           successMethod: () => {
             this.loadingNic = false
-            this.parentFetchData()
           },
           errorMessage: this.$t('message.error.remove.nic'),
           errorMethod: () => {
             this.loadingNic = false
-            this.parentFetchData()
           },
           loadingMessage: this.$t('message.remove.nic.processing'),
           catchMessage: this.$t('error.fetching.async.job.result'),
@@ -611,13 +603,11 @@ export default {
           successMethod: () => {
             this.loadingNic = false
             this.fetchSecondaryIPs(this.selectedNicId)
-            this.parentFetchData()
           },
           errorMessage: this.$t('message.error.add.secondary.ipaddress'),
           errorMethod: () => {
             this.loadingNic = false
             this.fetchSecondaryIPs(this.selectedNicId)
-            this.parentFetchData()
           },
           loadingMessage: this.$t('message.add.secondary.ipaddress.processing'),
           catchMessage: this.$t('error.fetching.async.job.result'),
@@ -646,13 +636,11 @@ export default {
             this.loadingNic = false
             this.fetchSecondaryIPs(this.selectedNicId)
             this.fetchPublicIps(this.editNetworkId)
-            this.parentFetchData()
           },
           errorMessage: this.$t('message.error.remove.secondary.ipaddress'),
           errorMethod: () => {
             this.loadingNic = false
             this.fetchSecondaryIPs(this.selectedNicId)
-            this.parentFetchData()
           },
           loadingMessage: this.$t('message.remove.secondary.ipaddress.processing'),
           catchMessage: this.$t('error.fetching.async.job.result'),
diff --git a/ui/src/views/compute/MigrateWizard.vue b/ui/src/views/compute/MigrateWizard.vue
index 93f337f..5a4432d 100644
--- a/ui/src/views/compute/MigrateWizard.vue
+++ b/ui/src/views/compute/MigrateWizard.vue
@@ -162,14 +162,10 @@ export default {
         virtualmachineid: this.resource.id
       }).then(response => {
         const jobid = this.selectedHost.requiresStorageMotion ? response.migratevirtualmachinewithvolumeresponse.jobid : response.migratevirtualmachineresponse.jobid
-        this.$store.dispatch('AddAsyncJob', {
-          title: `${this.$t('label.migrating')} ${this.resource.name}`,
-          jobid: jobid,
-          description: this.resource.name,
-          status: 'progress'
-        })
         this.$pollJob({
           jobId: jobid,
+          title: `${this.$t('label.migrating')} ${this.resource.name}`,
+          description: this.resource.name,
           successMessage: `${this.$t('message.success.migrating')} ${this.resource.name}`,
           successMethod: () => {
             this.$emit('close-action')
diff --git a/ui/src/views/compute/ScaleKubernetesCluster.vue b/ui/src/views/compute/ScaleKubernetesCluster.vue
index 5c6a101..f07424b 100644
--- a/ui/src/views/compute/ScaleKubernetesCluster.vue
+++ b/ui/src/views/compute/ScaleKubernetesCluster.vue
@@ -182,23 +182,15 @@ export default {
         }
         api('scaleKubernetesCluster', params).then(json => {
           const jobId = json.scalekubernetesclusterresponse.jobid
-          this.$store.dispatch('AddAsyncJob', {
-            title: this.$t('label.kubernetes.cluster.scale'),
-            jobid: jobId,
-            description: this.resource.name,
-            status: 'progress'
-          })
           this.$pollJob({
             jobId,
+            title: this.$t('label.kubernetes.cluster.scale'),
+            description: this.resource.name,
             loadingMessage: `${this.$t('label.kubernetes.cluster.scale')} ${this.resource.name} ${this.$t('label.in.progress')}`,
             catchMessage: this.$t('error.fetching.async.job.result'),
-            successMessage: `${this.$t('message.success.scale.kubernetes')} ${this.resource.name}`,
-            successMethod: result => {
-              this.$emit('refresh-data')
-            }
+            successMessage: `${this.$t('message.success.scale.kubernetes')} ${this.resource.name}`
           })
           this.closeAction()
-          this.$emit('refresh-data')
         }).catch(error => {
           this.$notifyError(error)
         }).finally(() => {
diff --git a/ui/src/views/compute/StartVirtualMachine.vue b/ui/src/views/compute/StartVirtualMachine.vue
index 83efee5..256e0dd 100644
--- a/ui/src/views/compute/StartVirtualMachine.vue
+++ b/ui/src/views/compute/StartVirtualMachine.vue
@@ -140,7 +140,6 @@ export default {
       loading: false
     }
   },
-  inject: ['parentFetchData'],
   beforeCreate () {
     this.form = this.$form.createForm(this)
     this.apiParams = this.$getApiParams('startVirtualMachine')
@@ -238,20 +237,13 @@ export default {
         }
         api('startVirtualMachine', params).then(json => {
           const jobId = json.startvirtualmachineresponse.jobid
-          this.$store.dispatch('AddAsyncJob', {
-            title: this.$t('label.action.start.instance'),
-            jobid: jobId,
-            description: this.resource.name,
-            status: 'progress'
-          })
           this.$pollJob({
             jobId,
+            title: this.$t('label.action.start.instance'),
+            description: this.resource.name,
             loadingMessage: `${this.$t('label.action.start.instance')} ${this.resource.name}`,
             catchMessage: this.$t('error.fetching.async.job.result'),
             successMessage: `${this.$t('label.action.start.instance')} ${this.resource.name}`,
-            successMethod: () => {
-              this.parentFetchData()
-            },
             response: (result) => { return result.virtualmachine && result.virtualmachine.password ? `The password of VM <b>${result.virtualmachine.displayname}</b> is <b>${result.virtualmachine.password}</b>` : null }
           })
           this.closeAction()
diff --git a/ui/src/views/compute/UpgradeKubernetesCluster.vue b/ui/src/views/compute/UpgradeKubernetesCluster.vue
index be88fd6..d8a7de0 100644
--- a/ui/src/views/compute/UpgradeKubernetesCluster.vue
+++ b/ui/src/views/compute/UpgradeKubernetesCluster.vue
@@ -154,22 +154,14 @@ export default {
         api('upgradeKubernetesCluster', params).then(json => {
           this.$emit('refresh-data')
           const jobId = json.upgradekubernetesclusterresponse.jobid
-          this.$store.dispatch('AddAsyncJob', {
-            title: this.$t('label.kubernetes.cluster.upgrade'),
-            jobid: jobId,
-            description: this.resource.name,
-            status: 'progress'
-          })
           this.$pollJob({
             jobId,
+            title: this.$t('label.kubernetes.cluster.upgrade'),
+            description: this.resource.name,
             loadingMessage: `${this.$t('label.kubernetes.cluster.upgrade')} ${this.resource.name} ${this.$t('label.in.progress')}`,
             catchMessage: this.$t('error.fetching.async.job.result'),
-            successMessage: `${this.$t('message.success.upgrade.kubernetes')} ${this.resource.name}`,
-            successMethod: result => {
-              this.$emit('refresh-data')
-            }
+            successMessage: `${this.$t('message.success.upgrade.kubernetes')} ${this.resource.name}`
           })
-          this.$emit('refresh-data')
           this.closeAction()
         }).catch(error => {
           this.$notifyError(error)
diff --git a/ui/src/views/iam/DomainActionForm.vue b/ui/src/views/iam/DomainActionForm.vue
index b5152a6..8ffd2ee 100644
--- a/ui/src/views/iam/DomainActionForm.vue
+++ b/ui/src/views/iam/DomainActionForm.vue
@@ -171,28 +171,6 @@ export default {
   },
   inject: ['parentCloseAction', 'parentFetchData'],
   methods: {
-    pollActionCompletion (jobId, action) {
-      this.$pollJob({
-        jobId,
-        successMethod: result => {
-          this.parentFetchData()
-          if (action.response) {
-            const description = action.response(result.jobresult)
-            if (description) {
-              this.$notification.info({
-                message: this.$t(action.label),
-                description: (<span domPropsInnerHTML={description}></span>),
-                duration: 0
-              })
-            }
-          }
-        },
-        errorMethod: () => this.parentFetchData(),
-        loadingMessage: `${this.$t(action.label)} ${this.$t('label.in.progress')} ${this.$t('label.for')} ${this.resource.name}`,
-        catchMessage: this.$t('error.fetching.async.job.result'),
-        action
-      })
-    },
     handleSubmit (e) {
       e.preventDefault()
       this.form.validateFields((err, values) => {
@@ -252,13 +230,30 @@ export default {
             if (obj.includes('response')) {
               for (const res in json[obj]) {
                 if (res === 'jobid') {
-                  this.$store.dispatch('AddAsyncJob', {
+                  this.$pollJob({
+                    jobId: json[obj][res],
                     title: this.$t(this.action.label),
-                    jobid: json[obj][res],
                     description: this.resource.name,
-                    status: 'progress'
+                    successMethod: result => {
+                      if (this.action.api === 'deleteDomain') {
+                        this.$set(this.resource, 'isDel', true)
+                        this.parentUpdActionData(this.resource)
+                      }
+                      if (this.action.response) {
+                        const description = this.action.response(result.jobresult)
+                        if (description) {
+                          this.$notification.info({
+                            message: this.$t(this.action.label),
+                            description: (<span domPropsInnerHTML={description}></span>),
+                            duration: 0
+                          })
+                        }
+                      }
+                    },
+                    loadingMessage: `${this.$t(this.action.label)} ${this.$t('label.in.progress')} ${this.$t('label.for')} ${this.resource.name}`,
+                    catchMessage: this.$t('error.fetching.async.job.result'),
+                    action: this.action
                   })
-                  this.pollActionCompletion(json[obj][res], this.action)
                   hasJobId = true
                   break
                 }
diff --git a/ui/src/views/image/IsoZones.vue b/ui/src/views/image/IsoZones.vue
index a4b089c..4df911d 100644
--- a/ui/src/views/image/IsoZones.vue
+++ b/ui/src/views/image/IsoZones.vue
@@ -246,15 +246,11 @@ export default {
       this.deleteLoading = true
       api('deleteIso', params).then(json => {
         const jobId = json.deleteisoresponse.jobid
-        this.$store.dispatch('AddAsyncJob', {
-          title: this.$t('label.action.delete.iso'),
-          jobid: jobId,
-          description: this.resource.name,
-          status: 'progress'
-        })
         const singleZone = (this.dataSource.length === 1)
         this.$pollJob({
           jobId,
+          title: this.$t('label.action.delete.iso'),
+          description: this.resource.name,
           successMethod: result => {
             if (singleZone) {
               this.$router.go(-1)
@@ -309,14 +305,10 @@ export default {
         this.copyLoading = true
         api('copyIso', params).then(json => {
           const jobId = json.copytemplateresponse.jobid
-          this.$store.dispatch('AddAsyncJob', {
-            title: this.$t('label.action.copy.iso'),
-            jobid: jobId,
-            description: this.resource.name,
-            status: 'progress'
-          })
           this.$pollJob({
             jobId,
+            title: this.$t('label.action.copy.iso'),
+            description: this.resource.name,
             successMethod: result => {
               this.fetchData()
             },
diff --git a/ui/src/views/image/TemplateZones.vue b/ui/src/views/image/TemplateZones.vue
index 89bbdd7..edbedbf 100644
--- a/ui/src/views/image/TemplateZones.vue
+++ b/ui/src/views/image/TemplateZones.vue
@@ -258,15 +258,11 @@ export default {
       this.deleteLoading = true
       api('deleteTemplate', params).then(json => {
         const jobId = json.deletetemplateresponse.jobid
-        this.$store.dispatch('AddAsyncJob', {
-          title: this.$t('label.action.delete.template'),
-          jobid: jobId,
-          description: this.resource.name,
-          status: 'progress'
-        })
         const singleZone = (this.dataSource.length === 1)
         this.$pollJob({
           jobId,
+          title: this.$t('label.action.delete.template'),
+          description: this.resource.name,
           successMethod: result => {
             if (singleZone) {
               const isResourcePage = (this.$route.params && this.$route.params.id)
@@ -331,14 +327,10 @@ export default {
         this.copyLoading = true
         api('copyTemplate', params).then(json => {
           const jobId = json.copytemplateresponse.jobid
-          this.$store.dispatch('AddAsyncJob', {
-            title: this.$t('label.action.copy.template'),
-            jobid: jobId,
-            description: this.resource.name,
-            status: 'progress'
-          })
           this.$pollJob({
             jobId,
+            title: this.$t('label.action.copy.template'),
+            description: this.resource.name,
             successMethod: result => {
               this.fetchData()
             },
diff --git a/ui/src/views/infra/ClusterAdd.vue b/ui/src/views/infra/ClusterAdd.vue
index 249a35f..82d3786 100644
--- a/ui/src/views/infra/ClusterAdd.vue
+++ b/ui/src/views/infra/ClusterAdd.vue
@@ -297,15 +297,11 @@ export default {
       }).then(response => {
         this.$pollJob({
           jobId: response.dedicateclusterresponse.jobid,
+          title: this.$t('message.cluster.dedicated'),
+          description: `${this.$t('label.domainid')} : ${this.dedicatedDomainId}`,
           successMessage: this.$t('message.cluster.dedicated'),
           successMethod: () => {
             this.loading = false
-            this.$store.dispatch('AddAsyncJob', {
-              title: this.$t('message.cluster.dedicated'),
-              jobid: response.dedicateclusterresponse.jobid,
-              description: `${this.$t('label.domainid')} : ${this.dedicatedDomainId}`,
-              status: 'progress'
-            })
           },
           errorMessage: this.$t('error.dedicate.cluster.failed'),
           errorMethod: () => {
diff --git a/ui/src/views/infra/HostAdd.vue b/ui/src/views/infra/HostAdd.vue
index 9e4e3f6..3df457e 100644
--- a/ui/src/views/infra/HostAdd.vue
+++ b/ui/src/views/infra/HostAdd.vue
@@ -315,15 +315,11 @@ export default {
       }).then(response => {
         this.$pollJob({
           jobId: response.dedicatehostresponse.jobid,
+          title: this.$t('message.host.dedicated'),
+          description: `${this.$t('label.domainid')} : ${this.dedicatedDomainId}`,
           successMessage: this.$t('message.host.dedicated'),
           successMethod: () => {
             this.loading = false
-            this.$store.dispatch('AddAsyncJob', {
-              title: this.$t('message.host.dedicated'),
-              jobid: response.dedicatehostresponse.jobid,
-              description: `${this.$t('label.domainid')} : ${this.dedicatedDomainId}`,
-              status: 'progress'
-            })
           },
           errorMessage: this.$t('error.dedicate.host.failed'),
           errorMethod: () => {
diff --git a/ui/src/views/infra/MigrateData.vue b/ui/src/views/infra/MigrateData.vue
index 814369f..813b37a 100644
--- a/ui/src/views/infra/MigrateData.vue
+++ b/ui/src/views/infra/MigrateData.vue
@@ -133,6 +133,7 @@ export default {
 
         const title = this.$t('message.data.migration')
         this.loading = true
+        const loadingJob = this.$message.loading({ content: this.$t('label.migrating.data'), duration: 0 })
 
         const result = this.migrateData(params, title)
         result.then(json => {
@@ -155,12 +156,11 @@ export default {
           this.closeAction()
         }).catch(error => {
           console.log(error)
-        })
-        setTimeout(() => {
-          this.$message.loading({ content: this.$t('label.migrating.data'), duration: 1 })
+        }).finally(() => {
           this.loading = false
+          setTimeout(loadingJob)
           this.closeAction()
-        }, 200)
+        })
       })
     },
     migrateData (args, title) {
@@ -168,39 +168,22 @@ export default {
         api('migrateSecondaryStorageData', args).then(async json => {
           const jobId = json.migratesecondarystoragedataresponse.jobid
           if (jobId) {
-            this.$store.dispatch('AddAsyncJob', {
-              title: title,
-              jobid: jobId,
+            this.$pollJob({
+              jobId,
+              title,
               description: this.$t('message.data.migration.progress'),
-              status: 'progress',
-              silent: true
+              successMethod: (result) => resolve(result),
+              errorMethod: (result) => reject(result.jobresult.errortext),
+              showLoading: false,
+              catchMessage: this.$t('error.fetching.async.job.result'),
+              catchMethod: () => { this.closeAction() }
             })
-            const result = await this.pollJob(jobId, title)
-            if (result.jobstatus === 2) {
-              reject(result.jobresult.errortext)
-              return
-            }
-            resolve(result)
           }
         }).catch(error => {
           reject(error)
         })
       })
     },
-    async pollJob (jobId, title) {
-      return new Promise(resolve => {
-        const asyncJobInterval = setInterval(() => {
-          api('queryAsyncJobResult', { jobId }).then(async json => {
-            const result = json.queryasyncjobresultresponse
-            if (result.jobstatus === 0) {
-              return
-            }
-            clearInterval(asyncJobInterval)
-            resolve(result)
-          })
-        }, 1000)
-      })
-    },
     closeAction () {
       this.$emit('close-action')
     }
diff --git a/ui/src/views/infra/PodAdd.vue b/ui/src/views/infra/PodAdd.vue
index 7850ba6..8bc5ed4 100644
--- a/ui/src/views/infra/PodAdd.vue
+++ b/ui/src/views/infra/PodAdd.vue
@@ -212,15 +212,11 @@ export default {
       }).then(response => {
         this.$pollJob({
           jobId: response.dedicatepodresponse.jobid,
+          title: this.$t('message.pod.dedicated'),
+          description: `${this.$t('label.domainid')} : ${this.dedicatedDomainId}`,
           successMessage: this.$t('message.pod.dedicated'),
           successMethod: () => {
             this.loading = false
-            this.$store.dispatch('AddAsyncJob', {
-              title: this.$t('message.pod.dedicated'),
-              jobid: response.dedicatepodresponse.jobid,
-              description: `${this.$t('label.domainid')} : ${this.dedicatedDomainId}`,
-              status: 'progress'
-            })
           },
           errorMessage: this.$t('error.dedicate.pod.failed'),
           errorMethod: () => {
diff --git a/ui/src/views/infra/network/DedicatedVLANTab.vue b/ui/src/views/infra/network/DedicatedVLANTab.vue
index 2fd7600..80bd33b 100644
--- a/ui/src/views/infra/network/DedicatedVLANTab.vue
+++ b/ui/src/views/infra/network/DedicatedVLANTab.vue
@@ -294,13 +294,10 @@ export default {
       api('releaseDedicatedGuestVlanRange', {
         id: item.id
       }).then(response => {
-        this.$store.dispatch('AddAsyncJob', {
-          title: `${this.$t('label.delete.dedicated.vlan.range')} ${item.guestvlanrange} ${this.$t('label.for')} ${item.account}`,
-          jobid: response.releasededicatedguestvlanrangeresponse.jobid,
-          status: 'progress'
-        })
         this.$pollJob({
           jobId: response.releasededicatedguestvlanrangeresponse.jobid,
+          title: this.$t('label.delete.dedicated.vlan.range'),
+          description: `${this.$t('label.delete.dedicated.vlan.range')} ${item.guestvlanrange} ${this.$t('label.for')} ${item.account}`,
           successMethod: () => {
             this.fetchData()
             this.parentFinishLoading()
diff --git a/ui/src/views/infra/network/EditTrafficLabel.vue b/ui/src/views/infra/network/EditTrafficLabel.vue
index aad05f1..5cd3fb9 100644
--- a/ui/src/views/infra/network/EditTrafficLabel.vue
+++ b/ui/src/views/infra/network/EditTrafficLabel.vue
@@ -184,15 +184,8 @@ export default {
         api('updateTrafficType', params).then(response => {
           this.$pollJob({
             jobId: response.updatetraffictyperesponse.jobid,
-            successMethod: result => {
-              this.$store.dispatch('AddAsyncJob', {
-                title: title,
-                description: description,
-                jobid: response.updatetraffictyperesponse.jobid,
-                status: this.$t('progress')
-              })
-              this.parentFetchData()
-            },
+            title: title,
+            description: description,
             successMessage: `${this.$t('label.update.traffic.label')} ${this.traffictype} ${this.$t('label.success')}`,
             loadingMessage: `${title} ${this.$t('label.in.progress')}`,
             catchMessage: this.$t('error.fetching.async.job.result')
diff --git a/ui/src/views/infra/network/IpRangesTabManagement.vue b/ui/src/views/infra/network/IpRangesTabManagement.vue
index 8865dda..2dc3bea 100644
--- a/ui/src/views/infra/network/IpRangesTabManagement.vue
+++ b/ui/src/views/infra/network/IpRangesTabManagement.vue
@@ -257,13 +257,11 @@ export default {
         endip: record.endip,
         vlan: record.vlanid
       }).then(response => {
-        this.$store.dispatch('AddAsyncJob', {
-          title: this.$t('message.success.remove.iprange'),
-          jobid: response.deletemanagementnetworkiprangeresponse.jobid,
-          status: 'progress'
-        })
         this.$pollJob({
           jobId: response.deletemanagementnetworkiprangeresponse.jobid,
+          title: this.$t('label.remove.ip.range'),
+          description: record.id,
+          successMessage: this.$t('message.success.remove.iprange'),
           successMethod: () => {
             this.componentLoading = false
             this.fetchData()
@@ -301,13 +299,11 @@ export default {
           forsystemvms: values.vms,
           vlan: values.vlan || null
         }).then(response => {
-          this.$store.dispatch('AddAsyncJob', {
-            title: this.$t('message.success.add.iprange'),
-            jobid: response.createmanagementnetworkiprangeresponse.jobid,
-            status: 'progress'
-          })
           this.$pollJob({
             jobId: response.createmanagementnetworkiprangeresponse.jobid,
+            title: this.$t('label.add.ip.range'),
+            description: values.pod,
+            successMessage: this.$t('message.success.add.iprange'),
             successMethod: () => {
               this.componentLoading = false
               this.fetchData()
diff --git a/ui/src/views/infra/network/IpRangesTabStorage.vue b/ui/src/views/infra/network/IpRangesTabStorage.vue
index fdb27f1..9c813e7 100644
--- a/ui/src/views/infra/network/IpRangesTabStorage.vue
+++ b/ui/src/views/infra/network/IpRangesTabStorage.vue
@@ -238,13 +238,11 @@ export default {
     handleDeleteIpRange (id) {
       this.componentLoading = true
       api('deleteStorageNetworkIpRange', { id }).then(response => {
-        this.$store.dispatch('AddAsyncJob', {
-          title: this.$t('message.success.remove.iprange'),
-          jobid: response.deletestoragenetworkiprangeresponse.jobid,
-          status: 'progress'
-        })
         this.$pollJob({
           jobId: response.deletestoragenetworkiprangeresponse.jobid,
+          title: this.$t('label.remove.ip.range'),
+          description: id,
+          successMessage: this.$t('message.success.remove.iprange'),
           successMethod: () => {
             this.componentLoading = false
             this.fetchData()
@@ -282,13 +280,11 @@ export default {
           endip: values.endip,
           vlan: values.vlan || null
         }).then(response => {
-          this.$store.dispatch('AddAsyncJob', {
-            title: this.$t('message.success.add.iprange'),
-            jobid: response.createstoragenetworkiprangeresponse.jobid,
-            status: 'progress'
-          })
           this.$pollJob({
             jobId: response.createstoragenetworkiprangeresponse.jobid,
+            title: this.$t('label.add.ip.range'),
+            description: values.pod,
+            successMessage: this.$t('message.success.add.iprange'),
             successMethod: () => {
               this.componentLoading = false
               this.fetchData()
diff --git a/ui/src/views/infra/network/ServiceProvidersTab.vue b/ui/src/views/infra/network/ServiceProvidersTab.vue
index 6a1740e..e04ddc8 100644
--- a/ui/src/views/infra/network/ServiceProvidersTab.vue
+++ b/ui/src/views/infra/network/ServiceProvidersTab.vue
@@ -1362,13 +1362,7 @@ export default {
             if (obj.includes('response') || obj.includes(apiName)) {
               for (const res in json[obj]) {
                 if (res === 'jobid') {
-                  this.$store.dispatch('AddAsyncJob', {
-                    title: this.$t(this.currentAction.label),
-                    jobid: json[obj][res],
-                    description: this.$t(this.nsp.name),
-                    status: 'progress'
-                  })
-                  this.parentPollActionCompletion(json[obj][res], this.currentAction)
+                  this.parentPollActionCompletion(json[obj][res], this.currentAction, this.$t(this.nsp.name))
                   hasJobId = true
                   break
                 }
diff --git a/ui/src/views/infra/network/providers/AddF5LoadBalancer.vue b/ui/src/views/infra/network/providers/AddF5LoadBalancer.vue
index 23a0c9e..7ba96ad 100644
--- a/ui/src/views/infra/network/providers/AddF5LoadBalancer.vue
+++ b/ui/src/views/infra/network/providers/AddF5LoadBalancer.vue
@@ -242,17 +242,9 @@ export default {
           }
           params.id = this.nsp.id
           const jobId = await this.addF5LoadBalancer(params)
-          if (jobId) {
-            await this.$store.dispatch('AddAsyncJob', {
-              title: this.$t(this.action.label),
-              jobid: jobId,
-              description: this.$t(this.nsp.name),
-              status: 'progress'
-            })
-            await this.parentPollActionCompletion(jobId, this.action)
-          }
+          this.parentPollActionCompletion(jobId, this.action, this.$t(this.nsp.name))
+          this.provideCloseAction()
           this.loading = false
-          await this.provideCloseAction()
         } catch (error) {
           this.loading = false
           this.$notification.error({
@@ -265,15 +257,19 @@ export default {
     addNetworkServiceProvider (args) {
       return new Promise((resolve, reject) => {
         api('addNetworkServiceProvider', args).then(async json => {
-          const jobId = json.addnetworkserviceproviderresponse.jobid
-          if (jobId) {
-            const result = await this.pollJob(jobId)
-            if (result.jobstatus === 2) {
+          this.$pollJob({
+            jobId: json.addnetworkserviceproviderresponse.jobid,
+            successMethod: (result) => {
+              resolve(result.jobresult.networkserviceprovider)
+            },
+            errorMethod: (result) => {
               reject(result.jobresult.errortext)
-              return
+            },
+            catchMessage: this.$t('error.fetching.async.job.result'),
+            action: {
+              isFetchData: false
             }
-            resolve(result.jobresult.networkserviceprovider)
-          }
+          })
         }).catch(error => {
           reject(error)
         })
@@ -288,21 +284,6 @@ export default {
           reject(error)
         })
       })
-    },
-    async pollJob (jobId) {
-      return new Promise(resolve => {
-        const asyncJobInterval = setInterval(() => {
-          api('queryAsyncJobResult', { jobId }).then(async json => {
-            const result = json.queryasyncjobresultresponse
-            if (result.jobstatus === 0) {
-              return
-            }
-
-            clearInterval(asyncJobInterval)
-            resolve(result)
-          })
-        }, 1000)
-      })
     }
   }
 }
diff --git a/ui/src/views/infra/network/providers/AddNetscalerLoadBalancer.vue b/ui/src/views/infra/network/providers/AddNetscalerLoadBalancer.vue
index 3ab1c04..8a9a5e7 100644
--- a/ui/src/views/infra/network/providers/AddNetscalerLoadBalancer.vue
+++ b/ui/src/views/infra/network/providers/AddNetscalerLoadBalancer.vue
@@ -277,17 +277,9 @@ export default {
           }
           params.id = this.nsp.id
           const jobId = await this.addNetscalerLoadBalancer(params)
-          if (jobId) {
-            await this.$store.dispatch('AddAsyncJob', {
-              title: this.$t(this.action.label),
-              jobid: jobId,
-              description: this.$t(this.nsp.name),
-              status: 'progress'
-            })
-            await this.parentPollActionCompletion(jobId, this.action)
-          }
+          this.parentPollActionCompletion(jobId, this.action, this.$t(this.nsp.name))
+          this.provideCloseAction()
           this.loading = false
-          await this.provideCloseAction()
         } catch (error) {
           this.loading = false
           this.$notification.error({
@@ -300,15 +292,19 @@ export default {
     addNetworkServiceProvider (args) {
       return new Promise((resolve, reject) => {
         api('addNetworkServiceProvider', args).then(async json => {
-          const jobId = json.addnetworkserviceproviderresponse.jobid
-          if (jobId) {
-            const result = await this.pollJob(jobId)
-            if (result.jobstatus === 2) {
+          this.$pollJob({
+            jobId: json.addnetworkserviceproviderresponse.jobid,
+            successMethod: (result) => {
+              resolve(result.jobresult.networkserviceprovider)
+            },
+            errorMethod: (result) => {
               reject(result.jobresult.errortext)
-              return
+            },
+            catchMessage: this.$t('error.fetching.async.job.result'),
+            action: {
+              isFetchData: false
             }
-            resolve(result.jobresult.networkserviceprovider)
-          }
+          })
         }).catch(error => {
           reject(error)
         })
@@ -323,21 +319,6 @@ export default {
           reject(error)
         })
       })
-    },
-    async pollJob (jobId) {
-      return new Promise(resolve => {
-        const asyncJobInterval = setInterval(() => {
-          api('queryAsyncJobResult', { jobId }).then(async json => {
-            const result = json.queryasyncjobresultresponse
-            if (result.jobstatus === 0) {
-              return
-            }
-
-            clearInterval(asyncJobInterval)
-            resolve(result)
-          })
-        }, 1000)
-      })
     }
   }
 }
diff --git a/ui/src/views/infra/network/providers/AddNiciraNvpDevice.vue b/ui/src/views/infra/network/providers/AddNiciraNvpDevice.vue
index 2c831da..7c23845 100644
--- a/ui/src/views/infra/network/providers/AddNiciraNvpDevice.vue
+++ b/ui/src/views/infra/network/providers/AddNiciraNvpDevice.vue
@@ -154,17 +154,9 @@ export default {
           }
           params.id = this.nsp.id
           const jobId = await this.addNiciraNvpDevice(params)
-          if (jobId) {
-            await this.$store.dispatch('AddAsyncJob', {
-              title: this.$t(this.action.label),
-              jobid: jobId,
-              description: this.$t(this.nsp.name),
-              status: 'progress'
-            })
-            await this.parentPollActionCompletion(jobId, this.action)
-          }
+          this.parentPollActionCompletion(jobId, this.action, this.$t(this.nsp.name))
+          this.provideCloseAction()
           this.loading = false
-          await this.provideCloseAction()
         } catch (error) {
           this.loading = false
           this.$notification.error({
@@ -177,15 +169,19 @@ export default {
     addNetworkServiceProvider (args) {
       return new Promise((resolve, reject) => {
         api('addNetworkServiceProvider', args).then(async json => {
-          const jobId = json.addnetworkserviceproviderresponse.jobid
-          if (jobId) {
-            const result = await this.pollJob(jobId)
-            if (result.jobstatus === 2) {
+          this.$pollJob({
+            jobId: json.addnetworkserviceproviderresponse.jobid,
+            successMethod: (result) => {
+              resolve(result.jobresult.networkserviceprovider)
+            },
+            errorMethod: (result) => {
               reject(result.jobresult.errortext)
-              return
+            },
+            catchMessage: this.$t('error.fetching.async.job.result'),
+            action: {
+              isFetchData: false
             }
-            resolve(result.jobresult.networkserviceprovider)
-          }
+          })
         }).catch(error => {
           reject(error)
         })
@@ -200,21 +196,6 @@ export default {
           reject(error)
         })
       })
-    },
-    async pollJob (jobId) {
-      return new Promise(resolve => {
-        const asyncJobInterval = setInterval(() => {
-          api('queryAsyncJobResult', { jobId }).then(async json => {
-            const result = json.queryasyncjobresultresponse
-            if (result.jobstatus === 0) {
-              return
-            }
-
-            clearInterval(asyncJobInterval)
-            resolve(result)
-          })
-        }, 1000)
-      })
     }
   }
 }
diff --git a/ui/src/views/infra/network/providers/AddPaloAltoFirewall.vue b/ui/src/views/infra/network/providers/AddPaloAltoFirewall.vue
index 08a2ce7..121a3ca 100644
--- a/ui/src/views/infra/network/providers/AddPaloAltoFirewall.vue
+++ b/ui/src/views/infra/network/providers/AddPaloAltoFirewall.vue
@@ -366,17 +366,9 @@ export default {
           }
           params.id = this.nsp.id
           const jobId = await this.addPaloAltoFirewall(params)
-          if (jobId) {
-            await this.$store.dispatch('AddAsyncJob', {
-              title: this.$t(this.action.label),
-              jobid: jobId,
-              description: this.$t(this.nsp.name),
-              status: 'progress'
-            })
-            await this.parentPollActionCompletion(jobId, this.action)
-          }
+          this.parentPollActionCompletion(jobId, this.action, this.$t(this.nsp.name))
+          this.provideCloseAction()
           this.loading = false
-          await this.provideCloseAction()
         } catch (error) {
           this.loading = false
           this.$notification.error({
@@ -389,15 +381,19 @@ export default {
     addNetworkServiceProvider (args) {
       return new Promise((resolve, reject) => {
         api('addNetworkServiceProvider', args).then(async json => {
-          const jobId = json.addnetworkserviceproviderresponse.jobid
-          if (jobId) {
-            const result = await this.pollJob(jobId)
-            if (result.jobstatus === 2) {
+          this.$pollJob({
+            jobId: json.addnetworkserviceproviderresponse.jobid,
+            successMethod: (result) => {
+              resolve(result.jobresult.networkserviceprovider)
+            },
+            errorMethod: (result) => {
               reject(result.jobresult.errortext)
-              return
+            },
+            catchMessage: this.$t('error.fetching.async.job.result'),
+            action: {
+              isFetchData: false
             }
-            resolve(result.jobresult.networkserviceprovider)
-          }
+          })
         }).catch(error => {
           reject(error)
         })
@@ -412,21 +408,6 @@ export default {
           reject(error)
         })
       })
-    },
-    async pollJob (jobId) {
-      return new Promise(resolve => {
-        const asyncJobInterval = setInterval(() => {
-          api('queryAsyncJobResult', { jobId }).then(async json => {
-            const result = json.queryasyncjobresultresponse
-            if (result.jobstatus === 0) {
-              return
-            }
-
-            clearInterval(asyncJobInterval)
-            resolve(result)
-          })
-        }, 1000)
-      })
     }
   }
 }
diff --git a/ui/src/views/infra/network/providers/AddSrxFirewall.vue b/ui/src/views/infra/network/providers/AddSrxFirewall.vue
index 94c6225..2fb1fe8 100644
--- a/ui/src/views/infra/network/providers/AddSrxFirewall.vue
+++ b/ui/src/views/infra/network/providers/AddSrxFirewall.vue
@@ -311,17 +311,9 @@ export default {
           }
           params.id = this.nsp.id
           const jobId = await this.addSrxFirewall(params)
-          if (jobId) {
-            await this.$store.dispatch('AddAsyncJob', {
-              title: this.$t(this.action.label),
-              jobid: jobId,
-              description: this.$t(this.nsp.name),
-              status: 'progress'
-            })
-            await this.parentPollActionCompletion(jobId, this.action)
-          }
+          this.parentPollActionCompletion(jobId, this.action, this.$t(this.nsp.name))
+          this.provideCloseAction()
           this.loading = false
-          await this.provideCloseAction()
         } catch (error) {
           this.loading = false
           this.$notification.error({
@@ -334,15 +326,19 @@ export default {
     addNetworkServiceProvider (args) {
       return new Promise((resolve, reject) => {
         api('addNetworkServiceProvider', args).then(async json => {
-          const jobId = json.addnetworkserviceproviderresponse.jobid
-          if (jobId) {
-            const result = await this.pollJob(jobId)
-            if (result.jobstatus === 2) {
+          this.$pollJob({
+            jobId: json.addnetworkserviceproviderresponse.jobid,
+            successMethod: (result) => {
+              resolve(result.jobresult.networkserviceprovider)
+            },
+            errorMethod: (result) => {
               reject(result.jobresult.errortext)
-              return
+            },
+            catchMessage: this.$t('error.fetching.async.job.result'),
+            action: {
+              isFetchData: false
             }
-            resolve(result.jobresult.networkserviceprovider)
-          }
+          })
         }).catch(error => {
           reject(error)
         })
@@ -357,21 +353,6 @@ export default {
           reject(error)
         })
       })
-    },
-    async pollJob (jobId) {
-      return new Promise(resolve => {
-        const asyncJobInterval = setInterval(() => {
-          api('queryAsyncJobResult', { jobId }).then(async json => {
-            const result = json.queryasyncjobresultresponse
-            if (result.jobstatus === 0) {
-              return
-            }
-
-            clearInterval(asyncJobInterval)
-            resolve(result)
-          })
-        }, 1000)
-      })
     }
   }
 }
diff --git a/ui/src/views/infra/network/providers/ProviderListView.vue b/ui/src/views/infra/network/providers/ProviderListView.vue
index 0458877..b16957a 100644
--- a/ui/src/views/infra/network/providers/ProviderListView.vue
+++ b/ui/src/views/infra/network/providers/ProviderListView.vue
@@ -188,7 +188,7 @@ export default {
       return columns
     }
   },
-  inject: ['providerChangePage', 'provideReload', 'parentPollActionCompletion'],
+  inject: ['providerChangePage', 'provideReload'],
   methods: {
     changePage (page, pageSize) {
       this.providerChangePage(this.title, page, pageSize)
@@ -286,13 +286,13 @@ export default {
             try {
               const jobId = await this.executeDeleteRecord(apiName, params)
               if (jobId) {
-                this.$store.dispatch('AddAsyncJob', {
+                this.$pollJob({
+                  jobId,
                   title: this.$t(label),
-                  jobid: jobId,
                   description: this.$t(name),
-                  status: 'progress'
+                  loadingMessage: `${this.$t(label)} - ${this.$t(name)}`,
+                  catchMessage: this.$t('error.fetching.async.job.result')
                 })
-                this.parentPollActionCompletion(jobId, this.action)
               } else {
                 this.$success('Success')
                 this.provideReload()
@@ -322,13 +322,13 @@ export default {
           try {
             const jobId = await this.configureOvsElement(params)
             if (jobId) {
-              this.$store.dispatch('AddAsyncJob', {
+              this.$pollJob({
+                jobId,
                 title: this.$t('label.configure.ovs'),
-                jobid: jobId,
                 description: this.$t(record.id),
-                status: 'progress'
+                loadingMessage: `${this.$t('label.configure.ovs')} - ${this.$t(record.id)}`,
+                catchMessage: this.$t('error.fetching.async.job.result')
               })
-              this.parentPollActionCompletion(jobId, this.action)
             } else {
               this.$success('Success')
               this.provideReload()
diff --git a/ui/src/views/network/AclListRulesTab.vue b/ui/src/views/network/AclListRulesTab.vue
index 7cc9520..f59fe1a 100644
--- a/ui/src/views/network/AclListRulesTab.vue
+++ b/ui/src/views/network/AclListRulesTab.vue
@@ -316,13 +316,10 @@ export default {
         resourceIds: this.selectedAcl.id,
         resourceType: 'NetworkACL'
       }).then(response => {
-        this.$store.dispatch('AddAsyncJob', {
-          title: this.$t('message.delete.tag.for.networkacl'),
-          jobid: response.deletetagsresponse.jobid,
-          status: 'progress'
-        })
         this.$pollJob({
           jobId: response.deletetagsresponse.jobid,
+          title: this.$t('message.delete.tag.for.networkacl'),
+          description: `${tag.key} = ${tag.value}`,
           successMessage: this.$t('message.success.delete.tag'),
           successMethod: () => {
             this.fetchTags(this.selectedAcl)
@@ -361,13 +358,10 @@ export default {
           resourceIds: this.selectedAcl.id,
           resourceType: 'NetworkACL'
         }).then(response => {
-          this.$store.dispatch('AddAsyncJob', {
-            title: this.$t('message.add.tag.for.networkacl'),
-            jobid: response.createtagsresponse.jobid,
-            status: 'progress'
-          })
           this.$pollJob({
             jobId: response.createtagsresponse.jobid,
+            title: this.$t('message.add.tag.for.networkacl'),
+            description: `${values.key} = ${values.value}`,
             successMessage: this.$t('message.success.add.tag'),
             successMethod: () => {
               this.fetchTags(this.selectedAcl)
@@ -449,13 +443,10 @@ export default {
         data.partialupgrade = false
 
         api('updateNetworkACLItem', {}, 'POST', data).then(response => {
-          this.$store.dispatch('AddAsyncJob', {
-            title: this.$t('label.edit.acl.rule'),
-            jobid: response.createnetworkaclresponse.jobid,
-            status: 'progress'
-          })
           this.$pollJob({
             jobId: response.createnetworkaclresponse.jobid,
+            title: this.$t('label.edit.acl.rule'),
+            description: this.selectedAcl.id,
             successMessage: this.$t('message.success.edit.acl'),
             successMethod: () => {
               this.fetchData()
@@ -482,13 +473,10 @@ export default {
     handleDeleteRule (id) {
       this.fetchLoading = true
       api('deleteNetworkACL', { id }).then(response => {
-        this.$store.dispatch('AddAsyncJob', {
-          title: this.$t('message.delete.acl.rule'),
-          jobid: response.deletenetworkaclresponse.jobid,
-          status: 'progress'
-        })
         this.$pollJob({
           jobId: response.deletenetworkaclresponse.jobid,
+          title: this.$t('message.delete.acl.rule'),
+          description: id,
           successMessage: this.$t('message.success.delete.acl.rule'),
           successMethod: () => {
             this.fetchData()
@@ -570,13 +558,10 @@ export default {
         previousaclruleid,
         nextaclruleid
       }).then(response => {
-        this.$store.dispatch('AddAsyncJob', {
-          title: this.$t('message.move.acl.order'),
-          jobid: response.moveNetworkAclItemResponse.jobid,
-          status: 'progress'
-        })
         this.$pollJob({
           jobId: response.moveNetworkAclItemResponse.jobid,
+          title: this.$t('message.move.acl.order'),
+          description: id,
           successMessage: this.$t('message.success.move.acl.order'),
           successMethod: () => {
             this.fetchData()
diff --git a/ui/src/views/network/CreateVpc.vue b/ui/src/views/network/CreateVpc.vue
index 91662a0..499dfd2 100644
--- a/ui/src/views/network/CreateVpc.vue
+++ b/ui/src/views/network/CreateVpc.vue
@@ -206,18 +206,11 @@ export default {
           if (jobId) {
             this.$pollJob({
               jobId,
-              successMethod: result => {
-                this.$store.dispatch('AddAsyncJob', {
-                  title: title,
-                  jobid: jobId,
-                  description: description,
-                  status: this.$t('progress')
-                })
-              },
+              title: title,
+              description: description,
               loadingMessage: `${title} ${this.$t('label.in.progress')}`,
               catchMessage: this.$t('error.fetching.async.job.result')
             })
-            this.$emit('refresh-data')
           }
           this.closeAction()
         }).catch(error => {
diff --git a/ui/src/views/network/CreateVpnCustomerGateway.vue b/ui/src/views/network/CreateVpnCustomerGateway.vue
index bc62ba2..ff1662e 100644
--- a/ui/src/views/network/CreateVpnCustomerGateway.vue
+++ b/ui/src/views/network/CreateVpnCustomerGateway.vue
@@ -289,7 +289,6 @@ export default {
       required: true
     }
   },
-  inject: ['parentFetchData', 'parentToggleLoading'],
   data () {
     return {
       encryptionAlgo: [
@@ -358,23 +357,17 @@ export default {
           splitconnections: values.splitconnections,
           ikeversion: values.ikeversion
         }).then(response => {
-          this.$store.dispatch('AddAsyncJob', {
-            title: this.$t('message.add.vpn.customer.gateway'),
-            jobid: response.createvpncustomergatewayresponse.jobid,
-            description: values.name,
-            status: 'progress'
-          })
           this.$pollJob({
             jobId: response.createvpncustomergatewayresponse.jobid,
+            title: this.$t('message.add.vpn.customer.gateway'),
+            description: values.name,
             successMessage: this.$t('message.success.add.vpn.customer.gateway'),
             successMethod: () => {
               this.closeModal()
-              this.parentFetchData()
             },
             errorMessage: `${this.$t('message.create.vpn.customer.gateway.failed')} ` + response,
             errorMethod: () => {
               this.closeModal()
-              this.parentFetchData()
             },
             loadingMessage: this.$t('message.add.vpn.customer.gateway.processing'),
             catchMessage: this.$t('error.fetching.async.job.result'),
diff --git a/ui/src/views/network/FirewallRules.vue b/ui/src/views/network/FirewallRules.vue
index 5996089..0aac4ad 100644
--- a/ui/src/views/network/FirewallRules.vue
+++ b/ui/src/views/network/FirewallRules.vue
@@ -327,13 +327,11 @@ export default {
           jobId: response.createtagsresponse.jobid,
           successMessage: this.$t('message.success.add.tag'),
           successMethod: () => {
-            this.parentFetchData()
             this.parentToggleLoading()
             this.openTagsModal(this.selectedRule)
           },
           errorMessage: this.$t('message.add.tag.failed'),
           errorMethod: () => {
-            this.parentFetchData()
             this.parentToggleLoading()
             this.closeModal()
           },
@@ -362,13 +360,11 @@ export default {
           jobId: response.deletetagsresponse.jobid,
           successMessage: this.$t('message.success.delete.tag'),
           successMethod: () => {
-            this.parentFetchData()
             this.parentToggleLoading()
             this.openTagsModal(this.selectedRule)
           },
           errorMessage: this.$t('message.delete.tag.failed'),
           errorMethod: () => {
-            this.parentFetchData()
             this.parentToggleLoading()
             this.closeModal()
           },
diff --git a/ui/src/views/network/IngressEgressRuleConfigure.vue b/ui/src/views/network/IngressEgressRuleConfigure.vue
index 59a0b24..c513404 100644
--- a/ui/src/views/network/IngressEgressRuleConfigure.vue
+++ b/ui/src/views/network/IngressEgressRuleConfigure.vue
@@ -293,12 +293,10 @@ export default {
             : response.authorizesecuritygroupegressresponse.jobid,
           successMessage: this.$t('message.success.add.rule'),
           successMethod: () => {
-            this.parentFetchData()
             this.parentToggleLoading()
           },
           errorMessage: this.$t('message.add.rule.failed'),
           errorMethod: () => {
-            this.parentFetchData()
             this.parentToggleLoading()
           },
           loadingMessage: this.$t('message.add.rule.processing'),
@@ -325,12 +323,10 @@ export default {
             : response.revokesecuritygroupegressresponse.jobid,
           successMessage: this.$t('message.success.remove.rule'),
           successMethod: () => {
-            this.parentFetchData()
             this.parentToggleLoading()
           },
           errorMessage: this.$t('message.remove.rule.failed'),
           errorMethod: () => {
-            this.parentFetchData()
             this.parentToggleLoading()
           },
           loadingMessage: this.$t('message.remove.securitygroup.rule.processing'),
@@ -369,14 +365,12 @@ export default {
           jobId: response.deletetagsresponse.jobid,
           successMessage: this.$t('message.success.delete.tag'),
           successMethod: () => {
-            this.parentFetchData()
             this.parentToggleLoading()
             this.fetchTags(this.selectedRule)
             this.tagsLoading = false
           },
           errorMessage: this.$t('message.delete.tag.failed'),
           errorMethod: () => {
-            this.parentFetchData()
             this.parentToggleLoading()
             this.fetchTags(this.selectedRule)
             this.tagsLoading = false
@@ -384,7 +378,6 @@ export default {
           loadingMessage: this.$t('message.delete.tag.processing'),
           catchMessage: this.$t('error.fetching.async.job.result'),
           catchMethod: () => {
-            this.parentFetchData()
             this.parentToggleLoading()
             this.fetchTags(this.selectedRule)
             this.tagsLoading = false
@@ -417,14 +410,12 @@ export default {
             jobId: response.createtagsresponse.jobid,
             successMessage: this.$t('message.success.add.tag'),
             successMethod: () => {
-              this.parentFetchData()
               this.parentToggleLoading()
               this.fetchTags(this.selectedRule)
               this.tagsLoading = false
             },
             errorMessage: this.$t('message.add.tag.failed'),
             errorMethod: () => {
-              this.parentFetchData()
               this.parentToggleLoading()
               this.fetchTags(this.selectedRule)
               this.tagsLoading = false
@@ -432,7 +423,6 @@ export default {
             loadingMessage: this.$t('message.add.tag.processing'),
             catchMessage: this.$t('error.fetching.async.job.result'),
             catchMethod: () => {
-              this.parentFetchData()
               this.parentToggleLoading()
               this.fetchTags(this.selectedRule)
               this.tagsLoading = false
diff --git a/ui/src/views/network/InternalLBAssignVmForm.vue b/ui/src/views/network/InternalLBAssignVmForm.vue
index 1969a9c..da534ea 100644
--- a/ui/src/views/network/InternalLBAssignVmForm.vue
+++ b/ui/src/views/network/InternalLBAssignVmForm.vue
@@ -210,17 +210,10 @@ export default {
         this.$pollJob({
           jobId: response.assigntoloadbalancerruleresponse.jobid,
           successMessage: `${this.$t('message.success.assigned.vms')} ${this.$t('label.to')} ${this.resource.name}`,
-          successMethod: () => {
-            this.$emit('refresh-data')
-          },
           errorMessage: `${this.$t('message.failed.to.assign.vms')} ${this.$t('label.to')} ${this.resource.name}`,
-          errorMethod: () => {
-            this.$emit('refresh-data')
-          },
           loadingMessage: `${this.$t('label.assigning.vms')} ${this.$t('label.to')} ${this.resource.name}`,
           catchMessage: this.$t('error.fetching.async.job.result')
         })
-        this.$emit('refresh-data')
         this.closeModal()
       }).catch(error => {
         this.$notification.error({
diff --git a/ui/src/views/network/LoadBalancing.vue b/ui/src/views/network/LoadBalancing.vue
index 45e4d15..66dd0d8 100644
--- a/ui/src/views/network/LoadBalancing.vue
+++ b/ui/src/views/network/LoadBalancing.vue
@@ -677,13 +677,11 @@ export default {
             jobId: response.createtagsresponse.jobid,
             successMessage: this.$t('message.success.add.tag'),
             successMethod: () => {
-              this.parentFetchData()
               this.parentToggleLoading()
               this.openTagsModal(this.selectedRule)
             },
             errorMessage: this.$t('message.add.tag.failed'),
             errorMethod: () => {
-              this.parentFetchData()
               this.parentToggleLoading()
               this.closeModal()
             },
@@ -712,13 +710,11 @@ export default {
           jobId: response.deletetagsresponse.jobid,
           successMessage: this.$t('message.success.delete.tag'),
           successMethod: () => {
-            this.parentFetchData()
             this.parentToggleLoading()
             this.openTagsModal(this.selectedRule)
           },
           errorMessage: this.$t('message.delete.tag.failed'),
           errorMethod: () => {
-            this.parentFetchData()
             this.parentToggleLoading()
             this.closeModal()
           },
@@ -769,14 +765,12 @@ export default {
           jobId: response.createLBStickinessPolicy.jobid,
           successMessage: this.$t('message.success.config.sticky.policy'),
           successMethod: () => {
-            this.parentFetchData()
             this.parentToggleLoading()
             this.fetchData()
             this.closeModal()
           },
           errorMessage: this.$t('message.config.sticky.policy.failed'),
           errorMethod: () => {
-            this.parentFetchData()
             this.parentToggleLoading()
             this.fetchData()
             this.closeModal()
@@ -801,14 +795,12 @@ export default {
           jobId: response.deleteLBstickinessrruleresponse.jobid,
           successMessage: this.$t('message.success.remove.sticky.policy'),
           successMethod: () => {
-            this.parentFetchData()
             this.parentToggleLoading()
             this.fetchData()
             this.closeModal()
           },
           errorMessage: this.$t('message.remove.sticky.policy.failed'),
           errorMethod: () => {
-            this.parentFetchData()
             this.parentToggleLoading()
             this.fetchData()
             this.closeModal()
@@ -913,14 +905,12 @@ export default {
           jobId: response.updateloadbalancerruleresponse.jobid,
           successMessage: this.$t('message.success.edit.rule'),
           successMethod: () => {
-            this.parentFetchData()
             this.parentToggleLoading()
             this.fetchData()
             this.closeModal()
           },
           errorMessage: this.$t('message.edit.rule.failed'),
           errorMethod: () => {
-            this.parentFetchData()
             this.parentToggleLoading()
             this.fetchData()
             this.closeModal()
@@ -948,14 +938,12 @@ export default {
           jobId: response.deleteloadbalancerruleresponse.jobid,
           successMessage: this.$t('message.success.remove.rule'),
           successMethod: () => {
-            this.parentFetchData()
             this.parentToggleLoading()
             this.fetchData()
             this.closeModal()
           },
           errorMessage: this.$t('message.remove.rule.failed'),
           errorMethod: () => {
-            this.parentFetchData()
             this.parentToggleLoading()
             this.fetchData()
             this.closeModal()
@@ -963,7 +951,6 @@ export default {
           loadingMessage: this.$t('message.delete.rule.processing'),
           catchMessage: this.$t('error.fetching.async.job.result'),
           catchMethod: () => {
-            this.parentFetchData()
             this.parentToggleLoading()
             this.fetchData()
             this.closeModal()
@@ -1084,14 +1071,12 @@ export default {
           jobId: response.assigntoloadbalancerruleresponse.jobid,
           successMessage: this.$t('message.success.asign.vm'),
           successMethod: () => {
-            this.parentFetchData()
             this.parentToggleLoading()
             this.fetchData()
             this.closeModal()
           },
           errorMessage: this.$t('message.assign.vm.failed'),
           errorMethod: () => {
-            this.parentFetchData()
             this.parentToggleLoading()
             this.fetchData()
             this.closeModal()
diff --git a/ui/src/views/network/PortForwarding.vue b/ui/src/views/network/PortForwarding.vue
index 8931876..1808309 100644
--- a/ui/src/views/network/PortForwarding.vue
+++ b/ui/src/views/network/PortForwarding.vue
@@ -461,13 +461,11 @@ export default {
           successMessage: this.$t('message.success.add.port.forward'),
           successMethod: () => {
             this.closeModal()
-            this.parentFetchData()
             this.fetchData()
           },
           errorMessage: this.$t('message.add.port.forward.failed'),
           errorMethod: () => {
             this.closeModal()
-            this.parentFetchData()
             this.fetchData()
           },
           loadingMessage: this.$t('message.add.port.forward.processing'),
@@ -536,13 +534,11 @@ export default {
           jobId: response.createtagsresponse.jobid,
           successMessage: this.$t('message.success.add.tag'),
           successMethod: () => {
-            this.parentFetchData()
             this.parentToggleLoading()
             this.openTagsModal(this.selectedRule)
           },
           errorMessage: this.$t('message.add.tag.failed'),
           errorMethod: () => {
-            this.parentFetchData()
             this.parentToggleLoading()
             this.closeModal()
           },
@@ -570,13 +566,11 @@ export default {
           jobId: response.deletetagsresponse.jobid,
           successMessage: this.$t('message.success.delete.tag'),
           successMethod: () => {
-            this.parentFetchData()
             this.parentToggleLoading()
             this.openTagsModal(this.selectedRule)
           },
           errorMessage: this.$t('message.delete.tag.failed'),
           errorMethod: () => {
-            this.parentFetchData()
             this.parentToggleLoading()
             this.closeModal()
           },
diff --git a/ui/src/views/network/StaticRoutesTab.vue b/ui/src/views/network/StaticRoutesTab.vue
index 53a01c0..048025f 100644
--- a/ui/src/views/network/StaticRoutesTab.vue
+++ b/ui/src/views/network/StaticRoutesTab.vue
@@ -136,13 +136,10 @@ export default {
       }).then(response => {
         this.$pollJob({
           jobId: response.createstaticrouteresponse.jobid,
+          title: this.$t('message.success.add.static.route'),
+          description: this.newRoute,
           successMethod: () => {
             this.fetchData()
-            this.$store.dispatch('AddAsyncJob', {
-              title: this.$t('message.success.add.static.route'),
-              jobid: response.createstaticrouteresponse.jobid,
-              status: 'progress'
-            })
             this.componentLoading = false
             this.newRoute = null
           },
@@ -171,13 +168,10 @@ export default {
       }).then(response => {
         this.$pollJob({
           jobId: response.deletestaticrouteresponse.jobid,
+          title: this.$t('message.success.delete.static.route'),
+          description: route.id,
           successMethod: () => {
             this.fetchData()
-            this.$store.dispatch('AddAsyncJob', {
-              title: this.$t('message.success.delete.static.route'),
-              jobid: response.deletestaticrouteresponse.jobid,
-              status: 'progress'
-            })
             this.componentLoading = false
           },
           errorMessage: this.$t('message.delete.static.route.failed'),
diff --git a/ui/src/views/network/VpcTab.vue b/ui/src/views/network/VpcTab.vue
index ae34959..05ef417 100644
--- a/ui/src/views/network/VpcTab.vue
+++ b/ui/src/views/network/VpcTab.vue
@@ -590,13 +590,10 @@ export default {
         }
 
         api('createPrivateGateway', params).then(response => {
-          this.$store.dispatch('AddAsyncJob', {
-            title: this.$t('message.success.add.private.gateway'),
-            jobid: response.createprivategatewayresponse.jobid,
-            status: 'progress'
-          })
           this.$pollJob({
             jobId: response.createprivategatewayresponse.jobid,
+            title: this.$t('message.success.add.private.gateway'),
+            description: this.resource.id,
             successMethod: () => {
               this.modals.gateway = false
               this.handleFetchData()
@@ -637,13 +634,10 @@ export default {
           s2scustomergatewayid: values.vpncustomergateway,
           passive: values.passive ? values.passive : false
         }).then(response => {
-          this.$store.dispatch('AddAsyncJob', {
-            title: this.$t('label.vpn.connection'),
-            jobid: response.createvpnconnectionresponse.jobid,
-            status: 'progress'
-          })
           this.$pollJob({
             jobId: response.createvpnconnectionresponse.jobid,
+            title: this.$t('label.vpn.connection'),
+            description: this.vpnGateways[0].id,
             successMethod: () => {
               this.fetchVpnConnections()
               this.fetchLoading = false
@@ -682,13 +676,10 @@ export default {
           description: values.description,
           vpcid: this.resource.id
         }).then(response => {
-          this.$store.dispatch('AddAsyncJob', {
-            title: this.$t('message.success.add.network.acl'),
-            jobid: response.createnetworkacllistresponse.jobid,
-            status: 'progress'
-          })
           this.$pollJob({
             jobId: response.createnetworkacllistresponse.jobid,
+            title: this.$t('message.success.add.network.acl'),
+            description: values.name || values.description,
             successMethod: () => {
               this.fetchLoading = false
             },
@@ -715,13 +706,10 @@ export default {
       api('createVpnGateway', {
         vpcid: this.resource.id
       }).then(response => {
-        this.$store.dispatch('AddAsyncJob', {
-          title: this.$t('message.success.add.vpn.gateway'),
-          jobid: response.createvpngatewayresponse.jobid,
-          status: 'progress'
-        })
         this.$pollJob({
           jobId: response.createvpngatewayresponse.jobid,
+          title: this.$t('message.success.add.vpn.gateway'),
+          description: this.resource.id,
           successMethod: () => {
             this.fetchLoading = false
           },
diff --git a/ui/src/views/network/VpcTiersTab.vue b/ui/src/views/network/VpcTiersTab.vue
index ffa4477..4770517 100644
--- a/ui/src/views/network/VpcTiersTab.vue
+++ b/ui/src/views/network/VpcTiersTab.vue
@@ -597,14 +597,10 @@ export default {
           sourceipaddressnetworkid: this.networkid,
           scheme: 'Internal'
         }).then(response => {
-          this.$store.dispatch('AddAsyncJob', {
-            title: this.$t('message.create.internallb'),
-            jobid: response.createloadbalancerresponse.jobid,
-            description: values.name,
-            status: 'progress'
-          })
           this.$pollJob({
             jobId: response.createloadbalancerresponse.jobid,
+            title: this.$t('message.create.internallb'),
+            description: values.name,
             successMessage: this.$t('message.success.create.internallb'),
             successMethod: () => {
               this.fetchData()
diff --git a/ui/src/views/network/VpnDetails.vue b/ui/src/views/network/VpnDetails.vue
index a200aa6..10ad92b 100644
--- a/ui/src/views/network/VpnDetails.vue
+++ b/ui/src/views/network/VpnDetails.vue
@@ -130,13 +130,11 @@ export default {
               duration: 0
             })
             this.fetchData()
-            this.parentFetchData()
             this.parentToggleLoading()
           },
           errorMessage: this.$t('message.enable.vpn.failed'),
           errorMethod: () => {
             this.fetchData()
-            this.parentFetchData()
             this.parentToggleLoading()
           },
           loadingMessage: this.$t('message.enable.vpn.processing'),
@@ -166,13 +164,11 @@ export default {
           successMessage: this.$t('message.success.disable.vpn'),
           successMethod: () => {
             this.fetchData()
-            this.parentFetchData()
             this.parentToggleLoading()
           },
           errorMessage: this.$t('message.disable.vpn.failed'),
           errorMethod: () => {
             this.fetchData()
-            this.parentFetchData()
             this.parentToggleLoading()
           },
           loadingMessage: this.$t('message.disable.vpn.processing'),
diff --git a/ui/src/views/offering/ImportBackupOffering.vue b/ui/src/views/offering/ImportBackupOffering.vue
index 3968682..1127c56 100644
--- a/ui/src/views/offering/ImportBackupOffering.vue
+++ b/ui/src/views/offering/ImportBackupOffering.vue
@@ -128,7 +128,6 @@ export default {
   created () {
     this.fetchData()
   },
-  inject: ['parentFetchData'],
   methods: {
     fetchData () {
       this.fetchZone()
@@ -182,15 +181,9 @@ export default {
           if (jobId) {
             this.$pollJob({
               jobId,
+              title: title,
+              description: values.name,
               successMethod: result => {
-                const successDescription = result.jobresult.backupoffering.name
-                this.$store.dispatch('AddAsyncJob', {
-                  title: title,
-                  jobid: jobId,
-                  description: successDescription,
-                  status: 'progress'
-                })
-                this.parentFetchData()
                 this.closeAction()
               },
               loadingMessage: `${title} ${this.$t('label.in.progress')} ${this.$t('label.for')} ${params.name}`,
diff --git a/ui/src/views/project/AccountsTab.vue b/ui/src/views/project/AccountsTab.vue
index 683251a..730f563 100644
--- a/ui/src/views/project/AccountsTab.vue
+++ b/ui/src/views/project/AccountsTab.vue
@@ -348,11 +348,11 @@ export default {
             if (res === 'jobid') {
               hasJobId = true
               const jobId = json[obj][res]
-              this.$store.dispatch('AddAsyncJob', {
-                title: title,
-                jobid: jobId,
-                description: description,
-                status: 'progress'
+              this.$pollJob({
+                jobId,
+                title,
+                description,
+                showLoading: false
               })
             }
           }
diff --git a/ui/src/views/project/AddAccountOrUserToProject.vue b/ui/src/views/project/AddAccountOrUserToProject.vue
index 067b581..2dafe48 100644
--- a/ui/src/views/project/AddAccountOrUserToProject.vue
+++ b/ui/src/views/project/AddAccountOrUserToProject.vue
@@ -266,7 +266,6 @@ export default {
             loadingMessage: `Adding Account: ${params.account} to project...`,
             catchMessage: 'Error encountered while fetching async job result'
           })
-          this.$emit('refresh-data')
           this.closeAction()
         }).catch(error => {
           this.$notifyError(error)
@@ -301,7 +300,6 @@ export default {
             loadingMessage: `Adding User ${params.username} to project...`,
             catchMessage: 'Error encountered while fetching async job result'
           })
-          this.$emit('refresh-data')
           this.closeAction()
         }).catch(error => {
           console.log('catch')
diff --git a/ui/src/views/project/InvitationTokenTemplate.vue b/ui/src/views/project/InvitationTokenTemplate.vue
index 29054da..85f9722 100644
--- a/ui/src/views/project/InvitationTokenTemplate.vue
+++ b/ui/src/views/project/InvitationTokenTemplate.vue
@@ -99,11 +99,11 @@ export default {
             if (res === 'jobid') {
               hasJobId = true
               const jobId = json[obj][res]
-              this.$store.dispatch('AddAsyncJob', {
+              this.$pollJob({
                 title: title,
                 jobid: jobId,
                 description: description,
-                status: 'progress'
+                showLoading: false
               })
             }
           }
diff --git a/ui/src/views/project/InvitationsTemplate.vue b/ui/src/views/project/InvitationsTemplate.vue
index 36d116e..4975a43 100644
--- a/ui/src/views/project/InvitationsTemplate.vue
+++ b/ui/src/views/project/InvitationsTemplate.vue
@@ -295,11 +295,11 @@ export default {
             if (res === 'jobid') {
               hasJobId = true
               const jobId = json[obj][res]
-              this.$store.dispatch('AddAsyncJob', {
+              this.$pollJob({
                 title: title,
                 jobid: jobId,
                 description: description,
-                status: 'progress'
+                showLoading: false
               })
             }
           }
diff --git a/ui/src/views/storage/AttachVolume.vue b/ui/src/views/storage/AttachVolume.vue
index a3e358e..79b8980 100644
--- a/ui/src/views/storage/AttachVolume.vue
+++ b/ui/src/views/storage/AttachVolume.vue
@@ -53,7 +53,6 @@ export default {
       required: true
     }
   },
-  inject: ['parentFetchData'],
   data () {
     return {
       virtualmachines: [],
@@ -110,16 +109,10 @@ export default {
           id: this.resource.id,
           virtualmachineid: values.virtualmachineid
         }).then(response => {
-          this.$store.dispatch('AddAsyncJob', {
-            title: this.$t('label.action.attach.disk'),
-            jobid: response.attachvolumeresponse.jobid,
-            status: 'progress'
-          })
           this.$pollJob({
             jobId: response.attachvolumeresponse.jobid,
-            successMethod: () => {
-              this.parentFetchData()
-            },
+            title: this.$t('label.action.attach.disk'),
+            description: this.resource.id,
             errorMessage: `${this.$t('message.attach.volume.failed')}: ${this.resource.name || this.resource.id}`,
             loadingMessage: `${this.$t('message.attach.volume.progress')}: ${this.resource.name || this.resource.id}`,
             catchMessage: this.$t('error.fetching.async.job.result')
@@ -129,7 +122,6 @@ export default {
           this.$notifyError(error)
         }).finally(() => {
           this.loading = false
-          this.parentFetchData()
         })
       })
     }
diff --git a/ui/src/views/storage/CreateSnapshotFromVMSnapshot.vue b/ui/src/views/storage/CreateSnapshotFromVMSnapshot.vue
index 2e3080b..1259a7d 100644
--- a/ui/src/views/storage/CreateSnapshotFromVMSnapshot.vue
+++ b/ui/src/views/storage/CreateSnapshotFromVMSnapshot.vue
@@ -100,24 +100,13 @@ export default {
         }).then(response => {
           this.$pollJob({
             jobId: response.createsnapshotfromvmsnapshotresponse.jobid,
+            title: this.$t('message.success.create.snapshot.from.vmsnapshot'),
+            description: values.name,
             successMessage: this.$t('message.success.create.snapshot.from.vmsnapshot'),
-            successMethod: () => {
-              this.$store.dispatch('AddAsyncJob', {
-                title: this.$t('message.success.create.snapshot.from.vmsnapshot'),
-                jobid: response.createsnapshotfromvmsnapshotresponse.jobid,
-                description: values.name,
-                status: 'progress'
-              })
-              this.$emit('refresh-data')
-            },
             errorMessage: this.$t('message.create.snapshot.from.vmsnapshot.failed'),
-            errorMethod: () => {
-              this.$emit('refresh-data')
-            },
             loadingMessage: this.$t('message.create.snapshot.from.vmsnapshot.progress'),
             catchMessage: this.$t('error.fetching.async.job.result')
           })
-          this.$emit('refresh-data')
           this.closeModal()
         }).catch(error => {
           this.$notifyError(error)
diff --git a/ui/src/views/storage/CreateVolume.vue b/ui/src/views/storage/CreateVolume.vue
index 385f3b1..bbb7855 100644
--- a/ui/src/views/storage/CreateVolume.vue
+++ b/ui/src/views/storage/CreateVolume.vue
@@ -196,24 +196,13 @@ export default {
         api('createVolume', values).then(response => {
           this.$pollJob({
             jobId: response.createvolumeresponse.jobid,
+            title: this.$t('message.success.create.volume'),
+            description: values.name,
             successMessage: this.$t('message.success.create.volume'),
-            successMethod: () => {
-              this.$store.dispatch('AddAsyncJob', {
-                title: this.$t('message.success.create.volume'),
-                jobid: response.createvolumeresponse.jobid,
-                description: values.name,
-                status: 'progress'
-              })
-              this.$emit('refresh-data')
-            },
             errorMessage: this.$t('message.create.volume.failed'),
-            errorMethod: () => {
-              this.$emit('refresh-data')
-            },
             loadingMessage: this.$t('message.create.volume.processing'),
             catchMessage: this.$t('error.fetching.async.job.result')
           })
-          this.$emit('refresh-data')
           this.closeModal()
         }).catch(error => {
           this.$notifyError(error)
diff --git a/ui/src/views/storage/MigrateVolume.vue b/ui/src/views/storage/MigrateVolume.vue
index 379ec14..ffe1731 100644
--- a/ui/src/views/storage/MigrateVolume.vue
+++ b/ui/src/views/storage/MigrateVolume.vue
@@ -148,13 +148,7 @@ export default {
         this.$pollJob({
           jobId: response.migratevolumeresponse.jobid,
           successMessage: this.$t('message.success.migrate.volume'),
-          successMethod: () => {
-            this.parentFetchData()
-          },
           errorMessage: this.$t('message.migrate.volume.failed'),
-          errorMethod: () => {
-            this.parentFetchData()
-          },
           loadingMessage: this.$t('message.migrate.volume.processing'),
           catchMessage: this.$t('error.fetching.async.job.result'),
           catchMethod: () => {
@@ -162,7 +156,6 @@ export default {
           }
         })
         this.closeModal()
-        this.parentFetchData()
       }).catch(error => {
         this.$notifyError(error)
       })
diff --git a/ui/src/views/storage/ResizeVolume.vue b/ui/src/views/storage/ResizeVolume.vue
index 0da5434..49fa8fc 100644
--- a/ui/src/views/storage/ResizeVolume.vue
+++ b/ui/src/views/storage/ResizeVolume.vue
@@ -103,15 +103,10 @@ export default {
         api('resizeVolume', values).then(response => {
           this.$pollJob({
             jobId: response.resizevolumeresponse.jobid,
+            title: this.$t('message.success.resize.volume'),
+            description: values.name,
             successMessage: this.$t('message.success.resize.volume'),
-            successMethod: () => {
-              this.$store.dispatch('AddAsyncJob', {
-                title: this.$t('message.success.resize.volume'),
-                jobid: response.resizevolumeresponse.jobid,
-                description: values.name,
-                status: 'progress'
-              })
-            },
+            successMethod: () => {},
             errorMessage: this.$t('message.resize.volume.failed'),
             errorMethod: () => {
               this.closeModal()
diff --git a/ui/src/views/storage/RestoreAttachBackupVolume.vue b/ui/src/views/storage/RestoreAttachBackupVolume.vue
index 72af601..1aec318 100644
--- a/ui/src/views/storage/RestoreAttachBackupVolume.vue
+++ b/ui/src/views/storage/RestoreAttachBackupVolume.vue
@@ -144,15 +144,9 @@ export default {
           if (jobId) {
             this.$pollJob({
               jobId,
+              title: title,
+              description: values.volumeid,
               successMethod: result => {
-                const successDescription = result.jobresult.storagebackup.name
-                this.$store.dispatch('AddAsyncJob', {
-                  title: title,
-                  jobid: jobId,
-                  description: successDescription,
-                  status: 'progress'
-                })
-                this.parentFetchData()
                 this.closeAction()
               },
               loadingMessage: `${title} ${this.$t('label.in.progress.for')} ${this.resource.id}`,
diff --git a/ui/src/views/storage/TakeSnapshot.vue b/ui/src/views/storage/TakeSnapshot.vue
index 7ec0053..f8d49b6 100644
--- a/ui/src/views/storage/TakeSnapshot.vue
+++ b/ui/src/views/storage/TakeSnapshot.vue
@@ -165,15 +165,9 @@ export default {
           if (jobId) {
             this.$pollJob({
               jobId,
-              successMethod: result => {
-                const successDescription = result.jobresult.snapshot.name
-                this.$store.dispatch('AddAsyncJob', {
-                  title: title,
-                  jobid: jobId,
-                  description: successDescription,
-                  status: 'progress'
-                })
-              },
+              title: title,
+              description: values.name || this.resource.id,
+              successMethod: result => {},
               loadingMessage: `${title} ${this.$t('label.in.progress.for')} ${description}`,
               catchMessage: this.$t('error.fetching.async.job.result')
             })