From 6d35d14778b9250c4454832d3996da1601d224e5 Mon Sep 17 00:00:00 2001
From: Rubankumar Moorthy <r.moorthy@fz-juelich.de>
Date: Tue, 8 Aug 2023 16:57:35 +0200
Subject: [PATCH] Resolve two loading spinners when loading platforms

---
 components/TermsOfUseAcceptanceDialog.vue     | 27 ++++----
 .../ConfigurationsTimelineActionCard.vue      | 24 ++++---
 components/configurations/MountWizard.vue     | 20 +++---
 .../MountWizardEntitySelect.vue               | 24 +++----
 .../configurations/StaticLocationView.vue     | 20 +++---
 .../dynamicLocation/DynamicLocationView.vue   | 20 +++---
 .../tsmLinking/TsmLinkingDeviceSelect.vue     |  6 +-
 .../tsmLinking/TsmLinkingForm.vue             |  8 +--
 .../tsmLinking/TsmLinkingOverviewTable.vue    | 16 ++---
 components/devices/AggregationTypeDialog.vue  | 18 +++--
 components/devices/CompartmentDialog.vue      | 18 +++--
 components/devices/CvPropertyDialog.vue       | 18 +++--
 components/devices/DeviceTypeDialog.vue       | 18 +++--
 components/devices/LicenseDialog.vue          | 18 +++--
 .../devices/MeasuredQuantityUnitDialog.vue    | 18 +++--
 components/devices/SamplingMediaDialog.vue    | 18 +++--
 components/devices/UnitDialog.vue             | 18 +++--
 components/platforms/PlatformSearch.vue       | 19 +++---
 components/platforms/PlatformTypeDialog.vue   | 18 +++--
 components/shared/ActionTypeDialog.vue        | 18 +++--
 components/shared/ContactRoleDialog.vue       | 18 +++--
 components/shared/ManufacturerDialog.vue      | 18 +++--
 components/shared/StatusDialog.vue            | 18 +++--
 components/sites/SiteTypeDialog.vue           | 18 +++--
 components/sites/SiteUsageDialog.vue          | 18 +++--
 layouts/default.vue                           | 17 +++--
 pages/configurations/_configurationId.vue     | 16 ++---
 .../_configurationId/actions.vue              | 22 +++----
 .../_actionId/edit.vue                        | 33 +++++-----
 .../_configurationId/actions/new.vue          | 16 ++---
 .../new/generic-configuration-actions.vue     | 19 +++---
 .../actions/new/parameter-change-actions.vue  | 19 +++---
 .../_actionId/edit.vue                        | 22 +++----
 .../_configurationId/attachments.vue          | 18 +++--
 .../attachments/_attachmentId/edit.vue        | 35 +++++-----
 .../_configurationId/attachments/index.vue    | 26 ++++----
 .../_configurationId/attachments/new.vue      | 17 ++---
 .../_configurationId/basic/edit.vue           | 18 ++---
 .../_configurationId/basic/index.vue          | 48 +++++++-------
 .../_configurationId/contacts.vue             | 16 ++---
 .../_configurationId/contacts/index.vue       | 19 +++---
 .../_configurationId/contacts/new.vue         | 31 ++++-----
 .../_configurationId/customfields.vue         | 18 +++--
 .../customfields/_customfieldId/edit.vue      | 34 +++++-----
 .../_configurationId/customfields/index.vue   | 26 ++++----
 .../_configurationId/customfields/new.vue     | 19 +++---
 .../_configurationId/locations.vue            | 26 +++-----
 .../_actionId/edit.vue                        | 30 ++++-----
 .../_actionId/index.vue                       | 17 ++---
 .../index/dynamic-location-actions/new.vue    | 17 ++---
 .../_actionId/edit.vue                        | 32 ++++-----
 .../_actionId/index.vue                       | 20 ++----
 .../index/static-location-actions/new.vue     | 19 +++---
 .../_configurationId/parameters.vue           | 17 ++---
 .../parameters/_parameterId/copy.vue          | 16 ++---
 .../parameters/_parameterId/edit.vue          | 16 ++---
 .../_configurationId/parameters/index.vue     | 37 +++++------
 .../_configurationId/parameters/new.vue       | 16 ++---
 .../device-mount-actions/_actionId/edit.vue   | 22 +++----
 .../platforms-and-devices/index.vue           | 42 ++++++------
 .../platforms-and-devices/mount.vue           | 15 ++---
 .../platform-mount-actions/_actionId/edit.vue | 20 +++---
 .../platforms-and-devices/unmount.vue         | 32 ++++-----
 .../_configurationId/tsm-linking.vue          | 20 +++---
 .../tsm-linking/_linkingId.vue                | 16 ++---
 .../tsm-linking/_linkingId/edit.vue           | 25 ++++---
 .../_configurationId/tsm-linking/new.vue      | 18 ++---
 .../tsm-linking/new/index.vue                 | 23 ++++---
 pages/configurations/index.vue                | 45 ++++++-------
 pages/configurations/new.vue                  | 18 ++---
 pages/contacts/_contactId.vue                 | 16 ++---
 pages/contacts/_contactId/edit.vue            | 17 ++---
 pages/contacts/_contactId/index.vue           | 27 ++++----
 pages/contacts/index.vue                      | 42 +++++-------
 pages/contacts/new.vue                        | 19 +++---
 pages/devices/_deviceId.vue                   | 17 ++---
 pages/devices/_deviceId/actions.vue           | 18 +++--
 .../_actionId/edit.vue                        | 33 +++++-----
 .../generic-device-actions/_actionId/edit.vue | 33 +++++-----
 pages/devices/_deviceId/actions/index.vue     | 50 +++++++-------
 pages/devices/_deviceId/actions/new.vue       | 17 ++---
 .../new/device-calibration-actions.vue        | 18 +++--
 .../actions/new/generic-device-actions.vue    | 19 +++---
 .../actions/new/parameter-change-actions.vue  | 18 +++--
 .../actions/new/software-update-actions.vue   | 20 +++---
 .../_actionId/edit.vue                        | 22 +++----
 .../_actionId/edit.vue                        | 34 +++++-----
 pages/devices/_deviceId/attachments.vue       | 18 +++--
 .../attachments/_attachmentId/edit.vue        | 35 +++++-----
 pages/devices/_deviceId/attachments/index.vue | 27 ++++----
 pages/devices/_deviceId/attachments/new.vue   | 18 ++---
 pages/devices/_deviceId/basic/edit.vue        | 20 +++---
 pages/devices/_deviceId/basic/index.vue       | 35 +++++-----
 pages/devices/_deviceId/contacts.vue          | 16 ++---
 pages/devices/_deviceId/contacts/index.vue    | 19 +++---
 pages/devices/_deviceId/contacts/new.vue      | 31 ++++-----
 pages/devices/_deviceId/customfields.vue      | 18 +++--
 .../customfields/_customfieldId/edit.vue      | 34 +++++-----
 .../devices/_deviceId/customfields/index.vue  | 26 ++++----
 pages/devices/_deviceId/customfields/new.vue  | 19 +++---
 .../devices/_deviceId/measuredquantities.vue  | 20 +++---
 .../_measuredquantityId/copy.vue              | 33 ++++------
 .../_measuredquantityId/edit.vue              | 29 ++++-----
 .../_deviceId/measuredquantities/index.vue    | 31 ++++-----
 .../_deviceId/measuredquantities/new.vue      | 17 ++---
 pages/devices/_deviceId/parameters.vue        | 16 ++---
 .../parameters/_parameterId/copy.vue          | 16 ++---
 .../parameters/_parameterId/edit.vue          | 16 ++---
 pages/devices/_deviceId/parameters/index.vue  | 37 +++++------
 pages/devices/_deviceId/parameters/new.vue    | 16 ++---
 pages/devices/copy/_deviceId.vue              | 25 +++----
 pages/devices/index.vue                       | 57 ++++++----------
 pages/devices/new.vue                         | 17 ++---
 pages/platforms/_platformId.vue               | 16 ++---
 pages/platforms/_platformId/actions.vue       | 18 +++--
 .../_actionId/edit.vue                        | 33 +++++-----
 pages/platforms/_platformId/actions/index.vue | 38 +++++------
 pages/platforms/_platformId/actions/new.vue   | 17 ++---
 .../actions/new/generic-platform-actions.vue  | 19 +++---
 .../actions/new/parameter-change-actions.vue  | 18 +++--
 .../actions/new/software-update-actions.vue   | 18 +++--
 .../_actionId/edit.vue                        | 24 ++++---
 .../_actionId/edit.vue                        | 33 +++++-----
 pages/platforms/_platformId/attachments.vue   | 16 ++---
 .../attachments/_attachmentId/edit.vue        | 35 +++++-----
 .../_platformId/attachments/index.vue         | 27 ++++----
 .../platforms/_platformId/attachments/new.vue | 19 ++----
 pages/platforms/_platformId/basic/edit.vue    | 20 +++---
 pages/platforms/_platformId/basic/index.vue   | 32 ++++-----
 pages/platforms/_platformId/contacts.vue      | 18 ++---
 .../platforms/_platformId/contacts/index.vue  | 19 +++---
 pages/platforms/_platformId/contacts/new.vue  | 31 ++++-----
 pages/platforms/_platformId/parameters.vue    | 17 ++---
 .../parameters/_parameterId/copy.vue          | 16 ++---
 .../parameters/_parameterId/edit.vue          | 16 ++---
 .../_platformId/parameters/index.vue          | 37 +++++------
 .../platforms/_platformId/parameters/new.vue  | 16 ++---
 pages/platforms/copy/_platformId.vue          | 29 ++++-----
 pages/platforms/index.vue                     | 49 +++++---------
 pages/platforms/new.vue                       | 17 ++---
 pages/sites/_siteId.vue                       | 16 ++---
 pages/sites/_siteId/attachments.vue           | 18 +++--
 .../attachments/_attachmentId/edit.vue        | 35 +++++-----
 pages/sites/_siteId/attachments/index.vue     | 19 +++---
 pages/sites/_siteId/attachments/new.vue       | 17 ++---
 pages/sites/_siteId/basic.vue                 | 11 ++--
 pages/sites/_siteId/basic/edit.vue            | 16 ++---
 pages/sites/_siteId/basic/index.vue           | 42 ++++++------
 pages/sites/_siteId/contacts.vue              | 16 ++---
 pages/sites/_siteId/contacts/index.vue        | 19 +++---
 pages/sites/_siteId/contacts/new.vue          | 31 ++++-----
 pages/sites/copy/_siteId.vue                  | 31 ++++-----
 pages/sites/index.vue                         | 54 ++++++---------
 pages/sites/new.vue                           | 17 ++---
 store/progressindicator.ts                    | 65 +++++++++++++++++++
 store/sites.ts                                |  2 +-
 156 files changed, 1603 insertions(+), 2022 deletions(-)
 create mode 100644 store/progressindicator.ts

diff --git a/components/TermsOfUseAcceptanceDialog.vue b/components/TermsOfUseAcceptanceDialog.vue
index 4e9cfdfa9..2a4c275f6 100644
--- a/components/TermsOfUseAcceptanceDialog.vue
+++ b/components/TermsOfUseAcceptanceDialog.vue
@@ -33,14 +33,11 @@ permissions and limitations under the Licence.
 -->
 <template>
   <v-dialog
+    v-if="!isLoading"
     v-model="showDialog"
     max-width="600px"
     persistent
   >
-    <ProgressIndicator
-      v-model="saving"
-      dark
-    />
     <v-card>
       <v-card-title class="headline">
         User agreement
@@ -83,17 +80,20 @@ permissions and limitations under the Licence.
 </template>
 <script lang="ts">
 import { Component, Prop, Vue } from 'nuxt-property-decorator'
-import { mapActions } from 'vuex'
+import { mapActions, mapState } from 'vuex'
 
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 
 import { AcceptTermsOfUseAction } from '@/store/permissions'
 
 @Component({
-  components: {
-    ProgressIndicator
+  computed: {
+    ...mapState('progressindicator', ['isLoading'])
   },
-  methods: mapActions('permissions', ['acceptTermsOfUse'])
+  methods: {
+    ...mapActions('permissions', ['acceptTermsOfUse']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class TermsOfUseAcceptanceDialog extends Vue {
   @Prop({
@@ -102,7 +102,6 @@ export default class TermsOfUseAcceptanceDialog extends Vue {
   })
   readonly value!: boolean
 
-  private saving = false
   private willAcceptTermsOfUse = false
   private willAcceptPrivacyPolicy = false
 
@@ -111,6 +110,7 @@ export default class TermsOfUseAcceptanceDialog extends Vue {
   }
 
   acceptTermsOfUse!: AcceptTermsOfUseAction
+  setLoading!: SetLoadingAction
 
   get showDialog (): boolean {
     return this.value
@@ -121,14 +121,15 @@ export default class TermsOfUseAcceptanceDialog extends Vue {
   }
 
   async sendAccept () {
-    this.saving = true
+    this.setLoading(true)
+    this.showDialog = false
     try {
       await this.acceptTermsOfUse()
+      this.$store.commit('snackbar/setSuccess', 'Accepted the terms of use.')
     } catch {
       this.$store.commit('snackbar/setError', 'Accepting the terms of use failed.')
     } finally {
-      this.saving = false
-      this.showDialog = false
+      this.setLoading(false)
     }
   }
 
diff --git a/components/configurations/ConfigurationsTimelineActionCard.vue b/components/configurations/ConfigurationsTimelineActionCard.vue
index 3c948f48a..1a6ff05b2 100644
--- a/components/configurations/ConfigurationsTimelineActionCard.vue
+++ b/components/configurations/ConfigurationsTimelineActionCard.vue
@@ -267,7 +267,7 @@ permissions and limitations under the Licence.
       v-if="actionToDelete"
       v-model="showDeleteDialog"
       title="Delete Action"
-      :disabled="isSaving"
+      :disabled="isLoading"
       @cancel="closeDialog"
       @delete="deleteAndCloseDialog"
     >
@@ -278,7 +278,7 @@ permissions and limitations under the Licence.
 
 <script lang="ts">
 import { Component, Prop, Vue, InjectReactive } from 'nuxt-property-decorator'
-import { mapActions } from 'vuex'
+import { mapActions, mapState } from 'vuex'
 
 import {
   DeleteConfigurationGenericAction,
@@ -290,7 +290,7 @@ import BaseExpandableListItem from '@/components/shared/BaseExpandableListItem.v
 import DotMenu from '@/components/DotMenu.vue'
 import DotMenuActionDelete from '@/components/DotMenuActionDelete.vue'
 import DeleteDialog from '@/components/shared/DeleteDialog.vue'
-
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 import { ITimelineAction } from '@/utils/configurationInterfaces'
 import { dateToDateTimeString } from '@/utils/dateHelper'
 import { GenericAction } from '@/models/GenericAction'
@@ -307,7 +307,11 @@ import { Attachment } from '@/models/Attachment'
     DotMenuActionDelete,
     DeleteDialog
   },
-  methods: mapActions('configurations', ['deleteConfigurationGenericAction', 'deleteConfigurationParameterChangeAction', 'loadAllConfigurationActions'])
+  computed: mapState('progressindicator', ['isLoading']),
+  methods: {
+    ...mapActions('configurations', ['deleteConfigurationGenericAction', 'deleteConfigurationParameterChangeAction', 'loadAllConfigurationActions']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class ConfigurationsTimelineActionCard extends Vue {
   @InjectReactive()
@@ -325,7 +329,6 @@ export default class ConfigurationsTimelineActionCard extends Vue {
   })
   readonly isPublic!: boolean
 
-  private isSaving: boolean = false
   private showDeleteDialog: boolean = false
   private genericActionToDelete: GenericAction | null = null
   private parameterChangeActionToDelete: ParameterChangeAction | null = null
@@ -334,6 +337,8 @@ export default class ConfigurationsTimelineActionCard extends Vue {
   deleteConfigurationGenericAction!: DeleteConfigurationGenericAction
   deleteConfigurationParameterChangeAction!: DeleteConfigurationParameterChangeActionAction
   loadAllConfigurationActions!: LoadAllConfigurationActionsAction
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
 
   get configurationId (): string {
     return this.$route.params.configurationId
@@ -395,13 +400,14 @@ export default class ConfigurationsTimelineActionCard extends Vue {
       return
     }
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.deleteConfigurationGenericAction(this.genericActionToDelete.id)
       this.$store.commit('snackbar/setSuccess', 'Generic action deleted')
     } catch (_error) {
       this.$store.commit('snackbar/setError', 'Generic action could not be deleted')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
+      this.closeDialog()
     }
   }
 
@@ -410,13 +416,13 @@ export default class ConfigurationsTimelineActionCard extends Vue {
       return
     }
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.deleteConfigurationParameterChangeAction(this.parameterChangeActionToDelete.id)
       this.$store.commit('snackbar/setSuccess', 'Parameter change action deleted')
     } catch (_error) {
       this.$store.commit('snackbar/setError', 'Parameter change action could not be deleted')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 
diff --git a/components/configurations/MountWizard.vue b/components/configurations/MountWizard.vue
index f008e3d1e..5e6bf9dce 100644
--- a/components/configurations/MountWizard.vue
+++ b/components/configurations/MountWizard.vue
@@ -33,10 +33,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      :dark="true"
-    />
     <v-stepper
       v-model="step"
       vertical
@@ -256,7 +252,7 @@ import MountWizardNodeSelect from '@/components/configurations/MountWizardNodeSe
 import MountWizardEntitySelect from '@/components/configurations/MountWizardEntitySelect.vue'
 import MountWizardMountForm from '@/components/configurations/MountWizardMountForm.vue'
 import MountWizardSubmitOverview from '@/components/configurations/MountWizardSubmitOverview.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import MountActionDetailsForm from '@/components/configurations/MountActionDetailsForm.vue'
 
 @Component({
@@ -266,8 +262,7 @@ import MountActionDetailsForm from '@/components/configurations/MountActionDetai
     MountWizardNodeSelect,
     MountWizardEntitySelect,
     MountWizardMountForm,
-    MountWizardSubmitOverview,
-    ProgressIndicator
+    MountWizardSubmitOverview
   },
   computed: {
     ...mapState('configurations', ['configuration', 'configurationMountingActionsForDate']),
@@ -276,6 +271,7 @@ import MountActionDetailsForm from '@/components/configurations/MountActionDetai
   methods: {
     ...mapActions('devices', ['clearDeviceAvailabilities']),
     ...mapActions('platforms', ['clearPlatformAvailabilities']),
+    ...mapActions('progressindicator', ['setLoading']),
     ...mapActions('configurations', ['addDeviceMountAction', 'addPlatformMountAction', 'loadConfiguration', 'loadMountingConfigurationForDate'])
   }
 })
@@ -286,7 +282,6 @@ export default class MountWizard extends Vue {
   })
     syncedHasSaved!: boolean
 
-  private isSaving = false
   private step = 1
 
   private tree: ConfigurationsTree = new ConfigurationsTree()
@@ -316,6 +311,7 @@ export default class MountWizard extends Vue {
   configurationMountingActionsForDate!: ConfigurationsTree
   clearDeviceAvailabilities!: ClearDeviceAvailabilitiesAction
   clearPlatformAvailabilities!: ClearPlatformAvailabilitiesAction
+  setLoading!: SetLoadingAction
 
   async created () {
     this.syncedHasSaved = false
@@ -427,7 +423,7 @@ export default class MountWizard extends Vue {
         return
       }
 
-      this.isSaving = true
+      this.setLoading(true)
 
       let parentPlatform = null
       if (this.selectedNode && this.selectedNode.canHaveChildren() && !this.selectedNode.isConfiguration()) {
@@ -457,7 +453,7 @@ export default class MountWizard extends Vue {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to add device mount action')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 
@@ -470,7 +466,7 @@ export default class MountWizard extends Vue {
 
       let parentPlatform = null
 
-      this.isSaving = true
+      this.setLoading(true)
 
       if (this.selectedNode && this.selectedNode.canHaveChildren() && !this.selectedNode.isConfiguration()) {
         parentPlatform = (this.selectedNode as PlatformNode).unpack().platform
@@ -499,7 +495,7 @@ export default class MountWizard extends Vue {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to add platform mount action')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 
diff --git a/components/configurations/MountWizardEntitySelect.vue b/components/configurations/MountWizardEntitySelect.vue
index 7684c8f95..bf5f46747 100644
--- a/components/configurations/MountWizardEntitySelect.vue
+++ b/components/configurations/MountWizardEntitySelect.vue
@@ -87,9 +87,6 @@ permissions and limitations under the Licence.
                           />
                         </v-subheader>
                       </v-col>
-                      <ProgressIndicator
-                        v-model="isLoading"
-                      />
                     </v-row>
                     <div v-if="devicesTotalCount > 0 && deviceAvailabilities.length>0">
                       <v-subheader>
@@ -173,9 +170,6 @@ permissions and limitations under the Licence.
                           />
                         </v-subheader>
                       </v-col>
-                      <ProgressIndicator
-                        v-model="isLoading"
-                      />
                     </v-row>
                     <div v-if="platformsTotalCount > 0 && platformAvailabilities.length>0">
                       <v-subheader>
@@ -263,11 +257,10 @@ import PlatformsListItem from '@/components/platforms/PlatformsListItem.vue'
 import DevicesListItem from '@/components/devices/DevicesListItem.vue'
 import PageSizeSelect from '@/components/shared/PageSizeSelect.vue'
 
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 
 @Component({
   components: {
-    ProgressIndicator,
     PlatformsListItem,
     DevicesListItem,
     BaseList,
@@ -288,6 +281,7 @@ import ProgressIndicator from '@/components/ProgressIndicator.vue'
       platformsTotalCount: 'totalCount',
       platformsTotalPages: 'totalPages'
     }),
+    ...mapState('progressindicator', ['isLoading']),
     ...mapGetters('devices', {
       devicePageSizeItems: 'pageSizes'
     }),
@@ -297,7 +291,8 @@ import ProgressIndicator from '@/components/ProgressIndicator.vue'
   },
   methods: {
     ...mapActions('devices', ['searchDevicesPaginated', 'loadDeviceAvailabilities']),
-    ...mapActions('platforms', ['searchPlatformsPaginated', 'loadPlatformAvailabilities'])
+    ...mapActions('platforms', ['searchPlatformsPaginated', 'loadPlatformAvailabilities']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class MountWizardEntitySelect extends Vue {
@@ -340,9 +335,10 @@ export default class MountWizardEntitySelect extends Vue {
   loadDeviceAvailabilities!: LoadDeviceAvailabilitiesAction
   searchPlatformsPaginated!: SearchPlatformsPaginatedAction
   loadPlatformAvailabilities!: LoadPlatformAvailabilitiesAction
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
 
   private tab = null
-  private isLoading = false
 
   private searchTextPlatforms: string = ''
   private searchTextDevices: string = ''
@@ -376,7 +372,7 @@ export default class MountWizardEntitySelect extends Vue {
       this.resetDeviceSearchToFirstPage = false
     }
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.searchDevicesPaginated({
         searchText: this.searchTextDevices,
         manufacturer: [],
@@ -394,7 +390,7 @@ export default class MountWizardEntitySelect extends Vue {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Loading of devices failed')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
       this.hasSearchedDevice = true
     }
   }
@@ -405,7 +401,7 @@ export default class MountWizardEntitySelect extends Vue {
       this.resetPlatformSearchToFirstPage = false
     }
     try {
-      this.isLoading = true
+      this.setLoading(true)
 
       // Not bound as methods, as there can be name conflicts with the devices.
       this.$store.dispatch('platforms/setSearchText', this.searchTextPlatforms)
@@ -425,7 +421,7 @@ export default class MountWizardEntitySelect extends Vue {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Loading of platforms failed')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
       this.hasSearchedPlatform = true
     }
   }
diff --git a/components/configurations/StaticLocationView.vue b/components/configurations/StaticLocationView.vue
index d8de8a35e..6779df648 100644
--- a/components/configurations/StaticLocationView.vue
+++ b/components/configurations/StaticLocationView.vue
@@ -34,9 +34,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-    />
     <StaticLocationActionData
       v-if="action"
       :value="action"
@@ -74,9 +71,9 @@ permissions and limitations under the Licence.
 
 <script lang="ts">
 import { Component, Prop, Vue } from 'nuxt-property-decorator'
-import { mapActions } from 'vuex'
+import { mapActions, mapState } from 'vuex'
 import { IStaticLocationAction } from '@/models/StaticLocationAction'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 import DotMenu from '@/components/DotMenu.vue'
 import DotMenuActionDelete from '@/components/DotMenuActionDelete.vue'
 import DotMenuActionEdit from '@/components/DotMenuActionEdit.vue'
@@ -88,10 +85,12 @@ import {
 import StaticLocationActionData from '@/components/configurations/StaticLocationActionData.vue'
 import DeleteDialog from '@/components/shared/DeleteDialog.vue'
 @Component({
-  components: { DeleteDialog, StaticLocationActionData, DotMenuActionEdit, DotMenuActionDelete, DotMenu, ProgressIndicator },
+  components: { DeleteDialog, StaticLocationActionData, DotMenuActionEdit, DotMenuActionDelete, DotMenu },
+  computed: mapState('progressindicator', ['isLoading']),
   middleware: ['auth'],
   methods: {
-    ...mapActions('configurations', ['loadStaticLocationAction', 'updateStaticLocationAction', 'deleteStaticLocationAction', 'loadLocationActionTimepoints'])
+    ...mapActions('configurations', ['loadStaticLocationAction', 'updateStaticLocationAction', 'deleteStaticLocationAction', 'loadLocationActionTimepoints']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class StaticLocationView extends Vue {
@@ -112,13 +111,14 @@ export default class StaticLocationView extends Vue {
   })
   private editable!: boolean
 
-  private isLoading = false
   private showBeginDeleteDialog = false
 
   // vuex definition for typescript check
   loadStaticLocationAction!: LoadStaticLocationActionAction
   deleteStaticLocationAction!: DeleteStaticLocationActionAction
   loadLocationActionTimepoints!: LoadLocationActionTimepointsAction
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
 
   openEditStaticLocationForm () {
     this.$router.push('/configurations/' + this.configurationId + '/locations/static-location-actions/' + this.action.id + '/edit')
@@ -134,7 +134,7 @@ export default class StaticLocationView extends Vue {
 
   async deleteAndCloseBeginDeleteDialog () {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.deleteStaticLocationAction(this.action.id)
       await this.loadLocationActionTimepoints(this.configurationId)
       this.$store.commit('snackbar/setSuccess', 'Deletion successful')
@@ -142,7 +142,7 @@ export default class StaticLocationView extends Vue {
     } catch (_e) {
       this.$store.commit('snackbar/setError', 'Deletion failed')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
       this.closeBeginDeleteDialog()
     }
   }
diff --git a/components/configurations/dynamicLocation/DynamicLocationView.vue b/components/configurations/dynamicLocation/DynamicLocationView.vue
index af8ff25d5..612d2c8c3 100644
--- a/components/configurations/dynamicLocation/DynamicLocationView.vue
+++ b/components/configurations/dynamicLocation/DynamicLocationView.vue
@@ -34,9 +34,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-    />
     <dynamic-location-action-data
       v-if="action"
       :value="action"
@@ -75,8 +72,8 @@ permissions and limitations under the Licence.
 
 <script lang="ts">
 import { Component, Prop, Vue } from 'nuxt-property-decorator'
-import { mapActions, mapGetters } from 'vuex'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { mapActions, mapState, mapGetters } from 'vuex'
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 import { IDynamicLocationAction } from '@/models/DynamicLocationAction'
 import DotMenu from '@/components/DotMenu.vue'
 import DotMenuActionDelete from '@/components/DotMenuActionDelete.vue'
@@ -89,16 +86,17 @@ import {
 import DynamicLocationActionData from '@/components/configurations/dynamicLocation/DynamicLocationActionData.vue'
 import DeleteDialog from '@/components/shared/DeleteDialog.vue'
 @Component({
-  components: { DeleteDialog, DynamicLocationActionData, DotMenuActionEdit, DotMenuActionDelete, DotMenu, ProgressIndicator },
+  components: { DeleteDialog, DynamicLocationActionData, DotMenuActionEdit, DotMenuActionDelete, DotMenu },
   computed: {
+    ...mapState('progressindicator', ['isLoading']),
     ...mapGetters('configurations', ['devicesForDynamicLocation'])
   },
   methods: {
-    ...mapActions('configurations', ['updateDynamicLocationAction', 'deleteDynamicLocationAction', 'loadDynamicLocationAction', 'loadLocationActionTimepoints'])
+    ...mapActions('configurations', ['updateDynamicLocationAction', 'deleteDynamicLocationAction', 'loadDynamicLocationAction', 'loadLocationActionTimepoints']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class DynamicLocationView extends Vue {
-  private isLoading = false
   private showDeleteDialog = false
 
   // vuex definition for typescript check
@@ -107,6 +105,8 @@ export default class DynamicLocationView extends Vue {
   deleteDynamicLocationAction!: DeleteDynamicLocationActionAction
   loadDynamicLocationAction!: LoadDynamicLocationActionAction
   loadLocationActionTimepoints!: LoadLocationActionTimepointsAction
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
 
   @Prop({
     type: String
@@ -139,7 +139,7 @@ export default class DynamicLocationView extends Vue {
 
   async deleteAndCloseDialog () {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.deleteDynamicLocationAction(this.action.id)
       await this.loadLocationActionTimepoints(this.configurationId)
       this.$store.commit('snackbar/setSuccess', 'Deletion successful')
@@ -147,7 +147,7 @@ export default class DynamicLocationView extends Vue {
     } catch (_e) {
       this.$store.commit('snackbar/setError', 'Deletion failed')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
       this.closeDeleteDialog()
     }
   }
diff --git a/components/configurations/tsmLinking/TsmLinkingDeviceSelect.vue b/components/configurations/tsmLinking/TsmLinkingDeviceSelect.vue
index 097197e6c..987b6011a 100644
--- a/components/configurations/tsmLinking/TsmLinkingDeviceSelect.vue
+++ b/components/configurations/tsmLinking/TsmLinkingDeviceSelect.vue
@@ -85,16 +85,13 @@
 
 <script lang="ts">
 import { Vue, Component, Prop, Watch } from 'nuxt-property-decorator'
-
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
 import { Device, IDevice } from '@/models/Device'
 import { TsmLinking } from '@/models/TsmLinking'
 import DevicesListItem from '@/components/devices/DevicesListItem.vue'
 
 @Component({
   components: {
-    DevicesListItem,
-    ProgressIndicator
+    DevicesListItem
   }
 })
 export default class TsmLinkingDeviceSelect extends Vue {
@@ -116,7 +113,6 @@ export default class TsmLinkingDeviceSelect extends Vue {
   })
     devices!: Device[]
 
-  private isLoading: boolean = false
   private menu = false
   private search = ''
   private selectedItems: Device[] = []
diff --git a/components/configurations/tsmLinking/TsmLinkingForm.vue b/components/configurations/tsmLinking/TsmLinkingForm.vue
index ce70281ad..a10a63cf1 100644
--- a/components/configurations/tsmLinking/TsmLinkingForm.vue
+++ b/components/configurations/tsmLinking/TsmLinkingForm.vue
@@ -86,7 +86,7 @@
               <v-autocomplete
                 :items="datasources"
                 :value="value.datasource"
-                :loading="isLaodingDatasource"
+                :loading="isLoadingDatasource"
                 label="Select datasource"
                 item-text="id"
                 return-object
@@ -364,7 +364,7 @@ export default class TsmLinkingForm extends mixins(Rules) {
   })
   readonly selectedDeviceActionPropertyCombination!: TsmDeviceMountPropertyCombination
 
-  private isLaodingDatasource = false
+  private isLoadingDatasource = false
   private isLoadingThing = false
   private isLoadingDatastream = false
 
@@ -530,12 +530,12 @@ export default class TsmLinkingForm extends mixins(Rules) {
         newObj.datastream = null
 
         try {
-          this.isLaodingDatasource = true
+          this.isLoadingDatasource = true
           await this.loadDatasources({ endpoint: result })
         } catch (e) {
           this.$store.commit('snackbar/setError', 'Failed to load datasources')
         } finally {
-          this.isLaodingDatasource = false
+          this.isLoadingDatasource = false
         }
         break
       case 'datasource':
diff --git a/components/configurations/tsmLinking/TsmLinkingOverviewTable.vue b/components/configurations/tsmLinking/TsmLinkingOverviewTable.vue
index c7f8652dd..8b24c0ce0 100644
--- a/components/configurations/tsmLinking/TsmLinkingOverviewTable.vue
+++ b/components/configurations/tsmLinking/TsmLinkingOverviewTable.vue
@@ -30,10 +30,6 @@
  -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-      dark
-    />
     <v-data-table
       :headers="headers"
       :items="dataTableLinkings"
@@ -95,13 +91,12 @@ import {
   ITsmLinkingState,
   LoadConfigurationTsmLinkingsAction
 } from '@/store/tsmLinking'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
 import { ConfigurationsState } from '@/store/configurations'
 import { CanModifyEntityGetter } from '@/store/permissions'
+import { SetLoadingAction } from '@/store/progressindicator'
 
 @Component({
   components: {
-    ProgressIndicator,
     DeleteDialog,
     ExtendedItemName,
     TsmLinkingBasicDataTable
@@ -112,7 +107,8 @@ import { CanModifyEntityGetter } from '@/store/permissions'
     ...mapGetters('permissions', ['canModifyEntity'])
   },
   methods: {
-    ...mapActions('tsmLinking', ['deleteConfigurationTsmLinking', 'loadConfigurationTsmLinkings'])
+    ...mapActions('tsmLinking', ['deleteConfigurationTsmLinking', 'loadConfigurationTsmLinkings']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class TsmLinkingOverviewTable extends Vue {
@@ -146,7 +142,6 @@ export default class TsmLinkingOverviewTable extends Vue {
   ]
 
   private itemToDelete: TsmLinking | null = null
-  private isLoading: boolean = false
   private showDeleteDialog: boolean = false
   // vuex definition for typescript check
   configuration!: ConfigurationsState['configuration']
@@ -154,6 +149,7 @@ export default class TsmLinkingOverviewTable extends Vue {
   deleteConfigurationTsmLinking!: DeleteConfigurationTsmLinkingAction
   loadConfigurationTsmLinkings!: LoadConfigurationTsmLinkingsAction
   canModifyEntity!: CanModifyEntityGetter
+  setLoading!: SetLoadingAction
 
   get configurationId (): string {
     return this.$route.params.configurationId
@@ -200,7 +196,7 @@ export default class TsmLinkingOverviewTable extends Vue {
       this.closeDialog()
       return
     }
-    this.isLoading = true
+    this.setLoading(true)
     try {
       this.showDeleteDialog = false
       await this.deleteConfigurationTsmLinking(this.itemToDelete)
@@ -210,7 +206,7 @@ export default class TsmLinkingOverviewTable extends Vue {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Linking could not be deleted')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/components/devices/AggregationTypeDialog.vue b/components/devices/AggregationTypeDialog.vue
index d9162f874..693f341ac 100644
--- a/components/devices/AggregationTypeDialog.vue
+++ b/components/devices/AggregationTypeDialog.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      :dark="true"
-    />
     <v-dialog
       v-model="showDialog"
       max-width="600"
@@ -145,7 +141,7 @@ permissions and limitations under the Licence.
 <script lang="ts">
 import { Component, Prop, Vue, Watch, mixins } from 'nuxt-property-decorator'
 import { mapActions, mapState } from 'vuex'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import ProvenanceHint from '@/components/shared/ProvenanceHint.vue'
 import { Rules } from '@/mixins/Rules'
 import { AggregationType } from '@/models/AggregationType'
@@ -156,21 +152,23 @@ import { AddAggregationTypeAction, LoadGlobalProvenancesAction, VocabularyState
     ...mapState('vocabulary', ['aggregationtypes', 'globalProvenances'])
   },
   methods: {
-    ...mapActions('vocabulary', ['addAggregationType', 'loadGlobalProvenances'])
+    ...mapActions('vocabulary', ['addAggregationType', 'loadGlobalProvenances']),
+    ...mapActions('progressindicator', ['setLoading'])
   },
   components: {
-    ProgressIndicator,
     ProvenanceHint
   }
 })
 export default class AggregationTypeDialog extends mixins(Rules) {
   private suggestion = new AggregationType()
-  private isSaving: boolean = false
   private addAggregationType!: AddAggregationTypeAction
   private loadGlobalProvenances!: LoadGlobalProvenancesAction
   private aggregationtypes!: VocabularyState['aggregationtypes']
   private globalProvenances!: VocabularyState['globalProvenances']
 
+  // vuex definition for typescript check
+  setLoading!: SetLoadingAction
+
   @Prop({
     required: true,
     type: Boolean
@@ -223,7 +221,7 @@ export default class AggregationTypeDialog extends mixins(Rules) {
       this.$store.commit('snackbar/setError', 'Please correct the errors before submitting')
       return
     }
-    this.isSaving = true
+    this.setLoading(true)
     // Copy before closing the dialog
     const aggregationType = AggregationType.createFromObject(this.suggestion)
     this.showDialog = false
@@ -234,7 +232,7 @@ export default class AggregationTypeDialog extends mixins(Rules) {
     } catch (err) {
       this.$store.commit('snackbar/setError', 'Error on submitting the aggregation type')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
       this.resetInputs()
     }
   }
diff --git a/components/devices/CompartmentDialog.vue b/components/devices/CompartmentDialog.vue
index e70f727a3..58560bc8f 100644
--- a/components/devices/CompartmentDialog.vue
+++ b/components/devices/CompartmentDialog.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      :dark="true"
-    />
     <v-dialog
       v-model="showDialog"
       max-width="600"
@@ -145,7 +141,7 @@ permissions and limitations under the Licence.
 <script lang="ts">
 import { Component, Prop, Vue, Watch, mixins } from 'nuxt-property-decorator'
 import { mapActions, mapState } from 'vuex'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import ProvenanceHint from '@/components/shared/ProvenanceHint.vue'
 import { Rules } from '@/mixins/Rules'
 import { Compartment } from '@/models/Compartment'
@@ -156,21 +152,23 @@ import { AddCompartmentAction, LoadGlobalProvenancesAction, VocabularyState } fr
     ...mapState('vocabulary', ['compartments', 'globalProvenances'])
   },
   methods: {
-    ...mapActions('vocabulary', ['addCompartment', 'loadGlobalProvenances'])
+    ...mapActions('vocabulary', ['addCompartment', 'loadGlobalProvenances']),
+    ...mapActions('progressindicator', ['setLoading'])
   },
   components: {
-    ProgressIndicator,
     ProvenanceHint
   }
 })
 export default class CompartmentDialog extends mixins(Rules) {
   private suggestion = new Compartment()
-  private isSaving: boolean = false
   private addCompartment!: AddCompartmentAction
   private loadGlobalProvenances!: LoadGlobalProvenancesAction
   private compartments!: VocabularyState['compartments']
   private globalProvenances!: VocabularyState['globalProvenances']
 
+  // vuex definition for typescript check
+  setLoading!: SetLoadingAction
+
   @Prop({
     required: true,
     type: Boolean
@@ -223,7 +221,7 @@ export default class CompartmentDialog extends mixins(Rules) {
       this.$store.commit('snackbar/setError', 'Please correct the errors before submitting')
       return
     }
-    this.isSaving = true
+    this.setLoading(true)
     // Copy before closing the dialog
     const compartment = Compartment.createFromObject(this.suggestion)
     this.showDialog = false
@@ -234,7 +232,7 @@ export default class CompartmentDialog extends mixins(Rules) {
     } catch (err) {
       this.$store.commit('snackbar/setError', 'Error on submitting the compartment')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
       this.resetInputs()
     }
   }
diff --git a/components/devices/CvPropertyDialog.vue b/components/devices/CvPropertyDialog.vue
index 11d38a607..3842128ad 100644
--- a/components/devices/CvPropertyDialog.vue
+++ b/components/devices/CvPropertyDialog.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      :dark="true"
-    />
     <v-dialog
       v-model="showDialog"
       max-width="600"
@@ -173,7 +169,7 @@ permissions and limitations under the Licence.
 <script lang="ts">
 import { Component, Prop, Vue, Watch, mixins } from 'nuxt-property-decorator'
 import { mapActions, mapState } from 'vuex'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import ProvenanceHint from '@/components/shared/ProvenanceHint.vue'
 import { Rules } from '@/mixins/Rules'
 import { Property } from '@/models/Property'
@@ -184,16 +180,15 @@ import { AddPropertyAction, LoadAggregationtypesAction, LoadGlobalProvenancesAct
     ...mapState('vocabulary', ['globalProvenances', 'samplingMedia', 'properties', 'aggregationtypes'])
   },
   methods: {
-    ...mapActions('vocabulary', ['loadGlobalProvenances', 'loadAggregationtypes', 'addProperty'])
+    ...mapActions('vocabulary', ['loadGlobalProvenances', 'loadAggregationtypes', 'addProperty']),
+    ...mapActions('progressindicator', ['setLoading'])
   },
   components: {
-    ProgressIndicator,
     ProvenanceHint
   }
 })
 export default class CvPropertyDialog extends mixins(Rules) {
   private suggestion = new Property()
-  private isSaving: boolean = false
   private addProperty!: AddPropertyAction
   private loadGlobalProvenances!: LoadGlobalProvenancesAction
   private loadAggregationtypes!: LoadAggregationtypesAction
@@ -202,6 +197,9 @@ export default class CvPropertyDialog extends mixins(Rules) {
   private globalProvenances!: VocabularyState['globalProvenances']
   private aggregationtypes!: VocabularyState['aggregationtypes']
 
+  // vuex definition for typescript check
+  setLoading!: SetLoadingAction
+
   @Prop({
     required: true,
     type: Boolean
@@ -261,7 +259,7 @@ export default class CvPropertyDialog extends mixins(Rules) {
       this.$store.commit('snackbar/setError', 'Please correct the errors before submitting')
       return
     }
-    this.isSaving = true
+    this.setLoading(true)
     const property = Property.createFromObject(this.suggestion)
     this.showDialog = false
     try {
@@ -271,7 +269,7 @@ export default class CvPropertyDialog extends mixins(Rules) {
     } catch (err) {
       this.$store.commit('snackbar/setError', 'Error on submitting the property')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
       this.resetInputs()
     }
   }
diff --git a/components/devices/DeviceTypeDialog.vue b/components/devices/DeviceTypeDialog.vue
index 68b04357c..afd8fb786 100644
--- a/components/devices/DeviceTypeDialog.vue
+++ b/components/devices/DeviceTypeDialog.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      :dark="true"
-    />
     <v-dialog
       v-model="showDialog"
       max-width="600"
@@ -145,7 +141,7 @@ permissions and limitations under the Licence.
 <script lang="ts">
 import { Component, Prop, Vue, Watch, mixins } from 'nuxt-property-decorator'
 import { mapActions, mapState } from 'vuex'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import ProvenanceHint from '@/components/shared/ProvenanceHint.vue'
 import { Rules } from '@/mixins/Rules'
 import { DeviceType } from '@/models/DeviceType'
@@ -156,21 +152,23 @@ import { AddDeviceTypeAction, LoadGlobalProvenancesAction, VocabularyState } fro
     ...mapState('vocabulary', ['devicetypes', 'globalProvenances'])
   },
   methods: {
-    ...mapActions('vocabulary', ['addDevicetype', 'loadGlobalProvenances'])
+    ...mapActions('vocabulary', ['addDevicetype', 'loadGlobalProvenances']),
+    ...mapActions('progressindicator', ['setLoading'])
   },
   components: {
-    ProgressIndicator,
     ProvenanceHint
   }
 })
 export default class DeviceTypeDialog extends mixins(Rules) {
   private newDeviceType = new DeviceType()
-  private isSaving: boolean = false
   private addDevicetype!: AddDeviceTypeAction
   private loadGlobalProvenances!: LoadGlobalProvenancesAction
   private devicetypes!: VocabularyState['devicetypes']
   private globalProvenances!: VocabularyState['globalProvenances']
 
+  // vuex definition for typescript check
+  setLoading!: SetLoadingAction
+
   @Prop({
     required: true,
     type: Boolean
@@ -223,7 +221,7 @@ export default class DeviceTypeDialog extends mixins(Rules) {
       this.$store.commit('snackbar/setError', 'Please correct the errors before submitting')
       return
     }
-    this.isSaving = true
+    this.setLoading(true)
     const devicetype = DeviceType.createFromObject(this.newDeviceType)
     this.showDialog = false
     try {
@@ -233,7 +231,7 @@ export default class DeviceTypeDialog extends mixins(Rules) {
     } catch (err) {
       this.$store.commit('snackbar/setError', 'Error on submitting the device type')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
       this.resetInputs()
     }
   }
diff --git a/components/devices/LicenseDialog.vue b/components/devices/LicenseDialog.vue
index 5bc3a69aa..86010123d 100644
--- a/components/devices/LicenseDialog.vue
+++ b/components/devices/LicenseDialog.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      :dark="true"
-    />
     <v-dialog
       v-model="showDialog"
       max-width="600"
@@ -145,32 +141,34 @@ permissions and limitations under the Licence.
 <script lang="ts">
 import { Component, Prop, Vue, Watch, mixins } from 'nuxt-property-decorator'
 import { mapActions, mapState } from 'vuex'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
 import ProvenanceHint from '@/components/shared/ProvenanceHint.vue'
 import { Rules } from '@/mixins/Rules'
 import { License } from '@/models/License'
 import { AddLicenseAction, LoadGlobalProvenancesAction, VocabularyState } from '@/store/vocabulary'
+import { SetLoadingAction } from '@/store/progressindicator'
 
 @Component({
   computed: {
     ...mapState('vocabulary', ['licenses', 'globalProvenances'])
   },
   methods: {
-    ...mapActions('vocabulary', ['addLicense', 'loadGlobalProvenances'])
+    ...mapActions('vocabulary', ['addLicense', 'loadGlobalProvenances']),
+    ...mapActions('progressindicator', ['setLoading'])
   },
   components: {
-    ProgressIndicator,
     ProvenanceHint
   }
 })
 export default class LicenseDialog extends mixins(Rules) {
   private newLicense = new License()
-  private isSaving: boolean = false
   private addLicense!: AddLicenseAction
   private loadGlobalProvenances!: LoadGlobalProvenancesAction
   private licenses!: VocabularyState['licenses']
   private globalProvenances!: VocabularyState['globalProvenances']
 
+  // vuex definition for typescript check
+  setLoading!: SetLoadingAction
+
   @Prop({
     required: true,
     type: Boolean
@@ -223,7 +221,7 @@ export default class LicenseDialog extends mixins(Rules) {
       this.$store.commit('snackbar/setError', 'Please correct the errors before submitting')
       return
     }
-    this.isSaving = true
+    this.setLoading(true)
     const license = License.createFromObject(this.newLicense)
     this.showDialog = false
     try {
@@ -233,7 +231,7 @@ export default class LicenseDialog extends mixins(Rules) {
     } catch (err) {
       this.$store.commit('snackbar/setError', 'Error on submitting the license')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
       this.resetInputs()
     }
   }
diff --git a/components/devices/MeasuredQuantityUnitDialog.vue b/components/devices/MeasuredQuantityUnitDialog.vue
index 1ad44752a..1ebcd64e4 100644
--- a/components/devices/MeasuredQuantityUnitDialog.vue
+++ b/components/devices/MeasuredQuantityUnitDialog.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      :dark="true"
-    />
     <v-dialog
       v-model="showDialog"
       max-width="600"
@@ -204,7 +200,7 @@ permissions and limitations under the Licence.
 <script lang="ts">
 import { Component, Prop, Vue, Watch, mixins } from 'nuxt-property-decorator'
 import { mapActions, mapState } from 'vuex'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import ProvenanceHint from '@/components/shared/ProvenanceHint.vue'
 import { Rules } from '@/mixins/Rules'
 import { MeasuredQuantityUnit } from '@/models/MeasuredQuantityUnit'
@@ -216,17 +212,16 @@ import { AddUnitAction, AddMeasuredQuantityUnitAction, LoadGlobalProvenancesActi
     ...mapState('vocabulary', ['units', 'globalProvenances', 'measuredQuantityUnits'])
   },
   methods: {
-    ...mapActions('vocabulary', ['addUnit', 'addMeasuredQuantityUnit', 'loadGlobalProvenances'])
+    ...mapActions('vocabulary', ['addUnit', 'addMeasuredQuantityUnit', 'loadGlobalProvenances']),
+    ...mapActions('progressindicator', ['setLoading'])
   },
   components: {
-    ProgressIndicator,
     ProvenanceHint
   }
 })
 export default class MeasuredQuantityUnitDialog extends mixins(Rules) {
   private unit = new Unit()
   private measuredQuantityUnit = new MeasuredQuantityUnit()
-  private isSaving: boolean = false
   private addUnit!: AddUnitAction
   private addMeasuredQuantityUnit!: AddMeasuredQuantityUnitAction
   private loadGlobalProvenances!: LoadGlobalProvenancesAction
@@ -239,6 +234,9 @@ export default class MeasuredQuantityUnitDialog extends mixins(Rules) {
     { text: 'New unit', value: false }
   ]
 
+  // vuex definition for typescript check
+  setLoading!: SetLoadingAction
+
   @Prop({
     required: true,
     type: Boolean
@@ -317,7 +315,7 @@ export default class MeasuredQuantityUnitDialog extends mixins(Rules) {
       this.$store.commit('snackbar/setError', 'Please correct the errors before submitting')
       return
     }
-    this.isSaving = true
+    this.setLoading(true)
     const unit = Unit.createFromObject(this.unit)
     const measuredQuantityUnit = MeasuredQuantityUnit.createFromObject(this.measuredQuantityUnit)
     this.showDialog = false
@@ -341,7 +339,7 @@ export default class MeasuredQuantityUnitDialog extends mixins(Rules) {
     } catch (err) {
       this.$store.commit('snackbar/setError', 'Error on submitting the unit')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
       this.resetInputs()
     }
   }
diff --git a/components/devices/SamplingMediaDialog.vue b/components/devices/SamplingMediaDialog.vue
index 7f9d93939..dbafd01b8 100644
--- a/components/devices/SamplingMediaDialog.vue
+++ b/components/devices/SamplingMediaDialog.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      :dark="true"
-    />
     <v-dialog
       v-model="showDialog"
       max-width="600"
@@ -159,7 +155,7 @@ permissions and limitations under the Licence.
 <script lang="ts">
 import { Component, Prop, Vue, Watch, mixins } from 'nuxt-property-decorator'
 import { mapActions, mapState } from 'vuex'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import ProvenanceHint from '@/components/shared/ProvenanceHint.vue'
 import { Rules } from '@/mixins/Rules'
 import { SamplingMedia } from '@/models/SamplingMedia'
@@ -170,22 +166,24 @@ import { AddSamplingMediaAction, LoadGlobalProvenancesAction, VocabularyState }
     ...mapState('vocabulary', ['compartments', 'globalProvenances', 'samplingMedia'])
   },
   methods: {
-    ...mapActions('vocabulary', ['loadGlobalProvenances', 'addSamplingMedia'])
+    ...mapActions('vocabulary', ['loadGlobalProvenances', 'addSamplingMedia']),
+    ...mapActions('progressindicator', ['setLoading'])
   },
   components: {
-    ProgressIndicator,
     ProvenanceHint
   }
 })
 export default class SamplingMediaDialog extends mixins(Rules) {
   private suggestion = new SamplingMedia()
-  private isSaving: boolean = false
   private addSamplingMedia!: AddSamplingMediaAction
   private loadGlobalProvenances!: LoadGlobalProvenancesAction
   private compartments!: VocabularyState['compartments']
   private samplingMedia!: VocabularyState['samplingMedia']
   private globalProvenances!: VocabularyState['globalProvenances']
 
+  // vuex definition for typescript check
+  setLoading!: SetLoadingAction
+
   @Prop({
     required: true,
     type: Boolean
@@ -245,7 +243,7 @@ export default class SamplingMediaDialog extends mixins(Rules) {
       this.$store.commit('snackbar/setError', 'Please correct the errors before submitting')
       return
     }
-    this.isSaving = true
+    this.setLoading(true)
     const samplingMedium = SamplingMedia.createFromObject(this.suggestion)
     this.showDialog = false
     try {
@@ -255,7 +253,7 @@ export default class SamplingMediaDialog extends mixins(Rules) {
     } catch (err) {
       this.$store.commit('snackbar/setError', 'Error on submitting the sampling medium')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
       this.resetInputs()
     }
   }
diff --git a/components/devices/UnitDialog.vue b/components/devices/UnitDialog.vue
index 28788c3e4..888270eb5 100644
--- a/components/devices/UnitDialog.vue
+++ b/components/devices/UnitDialog.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      :dark="true"
-    />
     <v-dialog
       v-model="showDialog"
       max-width="600"
@@ -145,7 +141,7 @@ permissions and limitations under the Licence.
 <script lang="ts">
 import { Component, Prop, Vue, Watch, mixins } from 'nuxt-property-decorator'
 import { mapActions, mapState } from 'vuex'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import ProvenanceHint from '@/components/shared/ProvenanceHint.vue'
 import { Rules } from '@/mixins/Rules'
 import { Unit } from '@/models/Unit'
@@ -156,21 +152,23 @@ import { AddUnitAction, LoadGlobalProvenancesAction, VocabularyState } from '@/s
     ...mapState('vocabulary', ['units', 'globalProvenances'])
   },
   methods: {
-    ...mapActions('vocabulary', ['addUnit', 'loadGlobalProvenances'])
+    ...mapActions('vocabulary', ['addUnit', 'loadGlobalProvenances']),
+    ...mapActions('progressindicator', ['setLoading'])
   },
   components: {
-    ProgressIndicator,
     ProvenanceHint
   }
 })
 export default class UnitDialog extends mixins(Rules) {
   private unit = new Unit()
-  private isSaving: boolean = false
   private addUnit!: AddUnitAction
   private loadGlobalProvenances!: LoadGlobalProvenancesAction
   private units!: VocabularyState['units']
   private globalProvenances!: VocabularyState['globalProvenances']
 
+  // vuex definition for typescript check
+  setLoading!: SetLoadingAction
+
   @Prop({
     required: true,
     type: Boolean
@@ -216,7 +214,7 @@ export default class UnitDialog extends mixins(Rules) {
       this.$store.commit('snackbar/setError', 'Please correct the errors before submitting')
       return
     }
-    this.isSaving = true
+    this.setLoading(true)
     const unit = Unit.createFromObject(this.unit)
     this.showDialog = false
     try {
@@ -226,7 +224,7 @@ export default class UnitDialog extends mixins(Rules) {
     } catch (err) {
       this.$store.commit('snackbar/setError', 'Error on submitting the unit')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
       this.resetInputs()
     }
   }
diff --git a/components/platforms/PlatformSearch.vue b/components/platforms/PlatformSearch.vue
index 288a406b7..72baafd1c 100644
--- a/components/platforms/PlatformSearch.vue
+++ b/components/platforms/PlatformSearch.vue
@@ -34,9 +34,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-    />
     <v-tabs-items
       v-model="activeTab"
     >
@@ -197,12 +194,12 @@ import ManufacturerSelect from '@/components/ManufacturerSelect.vue'
 import StatusSelect from '@/components/StatusSelect.vue'
 import PlatformTypeSelect from '@/components/PlatformTypeSelect.vue'
 import PermissionGroupSearchSelect from '@/components/PermissionGroupSearchSelect.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import { PermissionsState, LoadPermissionGroupsAction } from '@/store/permissions'
 import { SetActiveTabAction } from '@/store/appbar'
 
 @Component({
-  components: { ProgressIndicator, PlatformTypeSelect, StatusSelect, ManufacturerSelect, PermissionGroupSearchSelect },
+  components: { PlatformTypeSelect, StatusSelect, ManufacturerSelect, PermissionGroupSearchSelect },
   computed: {
     ...mapState('vocabulary', ['platformtypes', 'manufacturers', 'equipmentstatus']),
     ...mapState('platforms', [
@@ -218,6 +215,7 @@ import { SetActiveTabAction } from '@/store/appbar'
     ...mapGetters('permissions', ['permissionGroups'])
   },
   methods: {
+    ...mapActions('progressindicator', ['setLoading']),
     ...mapActions('vocabulary', ['loadEquipmentstatus', 'loadPlatformtypes', 'loadManufacturers']),
     ...mapActions('platforms', [
       'searchPlatformsPaginated',
@@ -236,8 +234,6 @@ import { SetActiveTabAction } from '@/store/appbar'
   }
 })
 export default class PlatformSearch extends Vue {
-  private isLoading = false
-
   // vuex definition for typescript check
   selectedSearchManufacturers!: PlatformsState['selectedSearchManufacturers']
   selectedSearchStates!: PlatformsState['selectedSearchStates']
@@ -266,6 +262,7 @@ export default class PlatformSearch extends Vue {
   manufacturers!: VocabularyState['manufacturers']
   permissionGroups!: PermissionsState['permissionGroups']
   setActiveTab!: SetActiveTabAction
+  setLoading!: SetLoadingAction
 
   get activeTab (): number | null {
     return this.$store.state.appbar.activeTab
@@ -339,7 +336,7 @@ export default class PlatformSearch extends Vue {
 
   async fetch (): Promise<void> {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await Promise.all([
         this.loadEquipmentstatus(),
         this.loadPlatformtypes(),
@@ -351,7 +348,7 @@ export default class PlatformSearch extends Vue {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Loading of platforms failed')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
@@ -402,14 +399,14 @@ export default class PlatformSearch extends Vue {
 
   async runSearch () {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       this.setPageNumber(1) // important for query
       this.initUrlQueryParams()
       await this.searchPlatformsPaginated()
     } catch (_error) {
       this.$store.commit('snackbar/setError', 'Loading of platforms failed')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/components/platforms/PlatformTypeDialog.vue b/components/platforms/PlatformTypeDialog.vue
index 836bcdcbc..1540dd633 100644
--- a/components/platforms/PlatformTypeDialog.vue
+++ b/components/platforms/PlatformTypeDialog.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      :dark="true"
-    />
     <v-dialog
       v-model="showDialog"
       max-width="600"
@@ -145,7 +141,7 @@ permissions and limitations under the Licence.
 <script lang="ts">
 import { Component, Prop, Vue, Watch, mixins } from 'nuxt-property-decorator'
 import { mapActions, mapState } from 'vuex'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import ProvenanceHint from '@/components/shared/ProvenanceHint.vue'
 import { Rules } from '@/mixins/Rules'
 import { PlatformType } from '@/models/PlatformType'
@@ -156,21 +152,23 @@ import { AddPlatformTypeAction, LoadGlobalProvenancesAction, VocabularyState } f
     ...mapState('vocabulary', ['platformtypes', 'globalProvenances'])
   },
   methods: {
-    ...mapActions('vocabulary', ['addPlatformtype', 'loadGlobalProvenances'])
+    ...mapActions('vocabulary', ['addPlatformtype', 'loadGlobalProvenances']),
+    ...mapActions('progressindicator', ['setLoading'])
   },
   components: {
-    ProgressIndicator,
     ProvenanceHint
   }
 })
 export default class PlatformTypeDialog extends mixins(Rules) {
   private newPlatformType = new PlatformType()
-  private isSaving: boolean = false
   private addPlatformtype!: AddPlatformTypeAction
   private loadGlobalProvenances!: LoadGlobalProvenancesAction
   private platformtypes!: VocabularyState['platformtypes']
   private globalProvenances!: VocabularyState['globalProvenances']
 
+  // vuex definition for typescript check
+  setLoading!: SetLoadingAction
+
   @Prop({
     required: true,
     type: Boolean
@@ -223,7 +221,7 @@ export default class PlatformTypeDialog extends mixins(Rules) {
       this.$store.commit('snackbar/setError', 'Please correct the errors before submitting')
       return
     }
-    this.isSaving = true
+    this.setLoading(true)
     const platformtype = PlatformType.createFromObject(this.newPlatformType)
     this.showDialog = false
     try {
@@ -233,7 +231,7 @@ export default class PlatformTypeDialog extends mixins(Rules) {
     } catch (err) {
       this.$store.commit('snackbar/setError', 'Error on submitting the platform type')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
       this.resetInputs()
     }
   }
diff --git a/components/shared/ActionTypeDialog.vue b/components/shared/ActionTypeDialog.vue
index ea98cefdb..c86cde6f3 100644
--- a/components/shared/ActionTypeDialog.vue
+++ b/components/shared/ActionTypeDialog.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      :dark="true"
-    />
     <v-dialog
       v-model="showDialog"
       max-width="600"
@@ -160,7 +156,7 @@ permissions and limitations under the Licence.
 <script lang="ts">
 import { Component, Prop, Vue, Watch, mixins } from 'nuxt-property-decorator'
 import { mapActions, mapState } from 'vuex'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import ProvenanceHint from '@/components/shared/ProvenanceHint.vue'
 import { Rules } from '@/mixins/Rules'
 import { ActionType } from '@/models/ActionType'
@@ -172,16 +168,15 @@ import { ActionTypeApiFilterType, ACTION_TYPE_API_FILTER_DEVICE, ACTION_TYPE_API
     ...mapState('vocabulary', ['actionCategories', 'globalProvenances', 'platformGenericActionTypes', 'deviceGenericActionTypes', 'configurationGenericActionTypes'])
   },
   methods: {
-    ...mapActions('vocabulary', ['loadGlobalProvenances', 'loadActionCategories', 'addActiontype'])
+    ...mapActions('vocabulary', ['loadGlobalProvenances', 'loadActionCategories', 'addActiontype']),
+    ...mapActions('progressindicator', ['setLoading'])
   },
   components: {
-    ProgressIndicator,
     ProvenanceHint
   }
 })
 export default class ActionTypeDialog extends mixins(Rules) {
   private suggestion = new ActionType()
-  private isSaving: boolean = false
   private addActiontype!: AddActiontypeAction
   private loadGlobalProvenances!: LoadGlobalProvenancesAction
   private loadActionCategories!: LoadActionCategoriesAction
@@ -191,6 +186,9 @@ export default class ActionTypeDialog extends mixins(Rules) {
   private configurationGenericActionTypes!: VocabularyState['configurationGenericActionTypes']
   private globalProvenances!: VocabularyState['globalProvenances']
 
+  // vuex definition for typescript check
+  setLoading!: SetLoadingAction
+
   @Prop({
     required: true,
     type: Boolean
@@ -259,7 +257,7 @@ export default class ActionTypeDialog extends mixins(Rules) {
       this.$store.commit('snackbar/setError', 'Please correct the errors before submitting')
       return
     }
-    this.isSaving = true
+    this.setLoading(true)
     // We copy the values as we reset the data when we close the dialog
     const suggestion = ActionType.createFromObject(this.suggestion)
     const actionCategoryTerm = this.initialActionTypeApiFilterType
@@ -271,7 +269,7 @@ export default class ActionTypeDialog extends mixins(Rules) {
     } catch (err) {
       this.$store.commit('snackbar/setError', 'Error on submitting the action type')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
       this.resetInputs()
     }
   }
diff --git a/components/shared/ContactRoleDialog.vue b/components/shared/ContactRoleDialog.vue
index 58f768f2f..cddfcc3b8 100644
--- a/components/shared/ContactRoleDialog.vue
+++ b/components/shared/ContactRoleDialog.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      :dark="true"
-    />
     <v-dialog
       v-model="showDialog"
       max-width="600"
@@ -145,7 +141,7 @@ permissions and limitations under the Licence.
 <script lang="ts">
 import { Component, Prop, Vue, Watch, mixins } from 'nuxt-property-decorator'
 import { mapActions, mapState } from 'vuex'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import ProvenanceHint from '@/components/shared/ProvenanceHint.vue'
 import { Rules } from '@/mixins/Rules'
 import { CvContactRole } from '@/models/CvContactRole'
@@ -156,21 +152,23 @@ import { AddCvContactRoleAction, LoadGlobalProvenancesAction, VocabularyState }
     ...mapState('vocabulary', ['cvContactRoles', 'globalProvenances'])
   },
   methods: {
-    ...mapActions('vocabulary', ['addCvContactRole', 'loadGlobalProvenances'])
+    ...mapActions('vocabulary', ['addCvContactRole', 'loadGlobalProvenances']),
+    ...mapActions('progressindicator', ['setLoading'])
   },
   components: {
-    ProgressIndicator,
     ProvenanceHint
   }
 })
 export default class ContactRoleDialog extends mixins(Rules) {
   private suggestion = new CvContactRole()
-  private isSaving: boolean = false
   private addCvContactRole!: AddCvContactRoleAction
   private loadGlobalProvenances!: LoadGlobalProvenancesAction
   private cvContactRoles!: VocabularyState['cvContactRoles']
   private globalProvenances!: VocabularyState['globalProvenances']
 
+  // vuex definition for typescript check
+  setLoading!: SetLoadingAction
+
   @Prop({
     required: true,
     type: Boolean
@@ -223,7 +221,7 @@ export default class ContactRoleDialog extends mixins(Rules) {
       this.$store.commit('snackbar/setError', 'Please correct the errors before submitting')
       return
     }
-    this.isSaving = true
+    this.setLoading(true)
     const suggestion = CvContactRole.createFromObject(this.suggestion)
     this.showDialog = false
     try {
@@ -233,7 +231,7 @@ export default class ContactRoleDialog extends mixins(Rules) {
     } catch (err) {
       this.$store.commit('snackbar/setError', 'Error on submitting the contact role')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
       this.resetInputs()
     }
   }
diff --git a/components/shared/ManufacturerDialog.vue b/components/shared/ManufacturerDialog.vue
index 10863d7cc..3efa87e73 100644
--- a/components/shared/ManufacturerDialog.vue
+++ b/components/shared/ManufacturerDialog.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      :dark="true"
-    />
     <v-dialog
       v-model="showDialog"
       max-width="600"
@@ -145,7 +141,7 @@ permissions and limitations under the Licence.
 <script lang="ts">
 import { Component, Prop, Vue, Watch, mixins } from 'nuxt-property-decorator'
 import { mapActions, mapState } from 'vuex'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import ProvenanceHint from '@/components/shared/ProvenanceHint.vue'
 import { Rules } from '@/mixins/Rules'
 import { Manufacturer } from '@/models/Manufacturer'
@@ -156,21 +152,23 @@ import { AddManufacturerAction, LoadGlobalProvenancesAction, VocabularyState } f
     ...mapState('vocabulary', ['manufacturers', 'globalProvenances'])
   },
   methods: {
-    ...mapActions('vocabulary', ['addManufacturer', 'loadGlobalProvenances'])
+    ...mapActions('vocabulary', ['addManufacturer', 'loadGlobalProvenances']),
+    ...mapActions('progressindicator', ['setLoading'])
   },
   components: {
-    ProgressIndicator,
     ProvenanceHint
   }
 })
 export default class ManufacturerDialog extends mixins(Rules) {
   private suggestion = new Manufacturer()
-  private isSaving: boolean = false
   private addManufacturer!: AddManufacturerAction
   private loadGlobalProvenances!: LoadGlobalProvenancesAction
   private manufacturers!: VocabularyState['manufacturers']
   private globalProvenances!: VocabularyState['globalProvenances']
 
+  // vuex definition for typescript check
+  setLoading!: SetLoadingAction
+
   @Prop({
     required: true,
     type: Boolean
@@ -223,7 +221,7 @@ export default class ManufacturerDialog extends mixins(Rules) {
       this.$store.commit('snackbar/setError', 'Please correct the errors before submitting')
       return
     }
-    this.isSaving = true
+    this.setLoading(true)
     const suggestion = Manufacturer.createFromObject(this.suggestion)
     this.showDialog = false
     try {
@@ -233,7 +231,7 @@ export default class ManufacturerDialog extends mixins(Rules) {
     } catch (err) {
       this.$store.commit('snackbar/setError', 'Error on submitting the manufacturer')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
       this.resetInputs()
     }
   }
diff --git a/components/shared/StatusDialog.vue b/components/shared/StatusDialog.vue
index 57d43abe4..69e633dcc 100644
--- a/components/shared/StatusDialog.vue
+++ b/components/shared/StatusDialog.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      :dark="true"
-    />
     <v-dialog
       v-model="showDialog"
       max-width="600"
@@ -145,7 +141,7 @@ permissions and limitations under the Licence.
 <script lang="ts">
 import { Component, Prop, Vue, Watch, mixins } from 'nuxt-property-decorator'
 import { mapActions, mapState } from 'vuex'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import ProvenanceHint from '@/components/shared/ProvenanceHint.vue'
 import { Rules } from '@/mixins/Rules'
 import { Status } from '@/models/Status'
@@ -156,21 +152,23 @@ import { AddEquipmentstatusAction, LoadGlobalProvenancesAction, VocabularyState
     ...mapState('vocabulary', ['equipmentstatus', 'globalProvenances'])
   },
   methods: {
-    ...mapActions('vocabulary', ['addEquipmentstatus', 'loadGlobalProvenances'])
+    ...mapActions('vocabulary', ['addEquipmentstatus', 'loadGlobalProvenances']),
+    ...mapActions('progressindicator', ['setLoading'])
   },
   components: {
-    ProgressIndicator,
     ProvenanceHint
   }
 })
 export default class StatusDialog extends mixins(Rules) {
   private suggestion = new Status()
-  private isSaving: boolean = false
   private addEquipmentstatus!: AddEquipmentstatusAction
   private loadGlobalProvenances!: LoadGlobalProvenancesAction
   private equipmentstatus!: VocabularyState['equipmentstatus']
   private globalProvenances!: VocabularyState['globalProvenances']
 
+  // vuex definition for typescript check
+  setLoading!: SetLoadingAction
+
   @Prop({
     required: true,
     type: Boolean
@@ -223,7 +221,7 @@ export default class StatusDialog extends mixins(Rules) {
       this.$store.commit('snackbar/setError', 'Please correct the errors before submitting')
       return
     }
-    this.isSaving = true
+    this.setLoading(true)
     const suggestion = Status.createFromObject(this.suggestion)
     this.showDialog = false
     try {
@@ -233,7 +231,7 @@ export default class StatusDialog extends mixins(Rules) {
     } catch (err) {
       this.$store.commit('snackbar/setError', 'Error on submitting the status')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
       this.resetInputs()
     }
   }
diff --git a/components/sites/SiteTypeDialog.vue b/components/sites/SiteTypeDialog.vue
index 1de12f702..03ce59bab 100644
--- a/components/sites/SiteTypeDialog.vue
+++ b/components/sites/SiteTypeDialog.vue
@@ -33,10 +33,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      :dark="true"
-    />
     <v-dialog
       v-model="showDialog"
       max-width="600"
@@ -162,7 +158,7 @@ permissions and limitations under the Licence.
 <script lang="ts">
 import { Component, Prop, Vue, Watch, mixins } from 'nuxt-property-decorator'
 import { mapActions, mapState } from 'vuex'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import ProvenanceHint from '@/components/shared/ProvenanceHint.vue'
 import { Rules } from '@/mixins/Rules'
 import { SiteType } from '@/models/SiteType'
@@ -173,22 +169,24 @@ import { AddSiteTypeAction, LoadGlobalProvenancesAction, VocabularyState } from
     ...mapState('vocabulary', ['siteTypes', 'siteUsages', 'globalProvenances'])
   },
   methods: {
-    ...mapActions('vocabulary', ['addSiteType', 'loadGlobalProvenances'])
+    ...mapActions('vocabulary', ['addSiteType', 'loadGlobalProvenances']),
+    ...mapActions('progressindicator', ['setLoading'])
   },
   components: {
-    ProgressIndicator,
     ProvenanceHint
   }
 })
 export default class SiteTypeDialog extends mixins(Rules) {
   private suggestion = new SiteType()
-  private isSaving: boolean = false
   private addSiteType!: AddSiteTypeAction
   private loadGlobalProvenances!: LoadGlobalProvenancesAction
   private siteUsages!: VocabularyState['siteUsages']
   private siteTypes!: VocabularyState['siteTypes']
   private globalProvenances!: VocabularyState['globalProvenances']
 
+  // vuex definition for typescript check
+  setLoading!: SetLoadingAction
+
   @Prop({
     required: true,
     type: Boolean
@@ -248,7 +246,7 @@ export default class SiteTypeDialog extends mixins(Rules) {
       this.$store.commit('snackbar/setError', 'Please correct the errors before submitting')
       return
     }
-    this.isSaving = true
+    this.setLoading(true)
     // Copy before closing the dialog
     const siteType = SiteType.createFromObject(this.suggestion)
     this.showDialog = false
@@ -259,7 +257,7 @@ export default class SiteTypeDialog extends mixins(Rules) {
     } catch (err) {
       this.$store.commit('snackbar/setError', 'Error on submitting the site / lab type')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
       this.resetInputs()
     }
   }
diff --git a/components/sites/SiteUsageDialog.vue b/components/sites/SiteUsageDialog.vue
index d2843b060..4aa38a866 100644
--- a/components/sites/SiteUsageDialog.vue
+++ b/components/sites/SiteUsageDialog.vue
@@ -32,10 +32,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      :dark="true"
-    />
     <v-dialog
       v-model="showDialog"
       max-width="600"
@@ -147,7 +143,7 @@ permissions and limitations under the Licence.
 <script lang="ts">
 import { Component, Prop, Vue, Watch, mixins } from 'nuxt-property-decorator'
 import { mapActions, mapState } from 'vuex'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import ProvenanceHint from '@/components/shared/ProvenanceHint.vue'
 import { Rules } from '@/mixins/Rules'
 import { SiteUsage } from '@/models/SiteUsage'
@@ -158,21 +154,23 @@ import { AddSiteUsageAction, LoadGlobalProvenancesAction, VocabularyState } from
     ...mapState('vocabulary', ['siteUsages', 'globalProvenances'])
   },
   methods: {
-    ...mapActions('vocabulary', ['addSiteUsage', 'loadGlobalProvenances'])
+    ...mapActions('vocabulary', ['addSiteUsage', 'loadGlobalProvenances']),
+    ...mapActions('progressindicator', ['setLoading'])
   },
   components: {
-    ProgressIndicator,
     ProvenanceHint
   }
 })
 export default class SiteUsageDialog extends mixins(Rules) {
   private suggestion = new SiteUsage()
-  private isSaving: boolean = false
   private addSiteUsage!: AddSiteUsageAction
   private loadGlobalProvenances!: LoadGlobalProvenancesAction
   private siteUsages!: VocabularyState['siteUsages']
   private globalProvenances!: VocabularyState['globalProvenances']
 
+  // vuex definition for typescript check
+  setLoading!: SetLoadingAction
+
   @Prop({
     required: true,
     type: Boolean
@@ -225,7 +223,7 @@ export default class SiteUsageDialog extends mixins(Rules) {
       this.$store.commit('snackbar/setError', 'Please correct the errors before submitting')
       return
     }
-    this.isSaving = true
+    this.setLoading(true)
     // Copy before closing the dialog
     const siteUsage = SiteUsage.createFromObject(this.suggestion)
     this.showDialog = false
@@ -236,7 +234,7 @@ export default class SiteUsageDialog extends mixins(Rules) {
     } catch (err) {
       this.$store.commit('snackbar/setError', 'Error on submitting the site / lab usage')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
       this.resetInputs()
     }
   }
diff --git a/layouts/default.vue b/layouts/default.vue
index 40c899576..c6850d828 100644
--- a/layouts/default.vue
+++ b/layouts/default.vue
@@ -34,6 +34,11 @@ permissions and limitations under the Licence.
 -->
 <template>
   <v-app>
+    <div>
+      <ProgressIndicator
+        :value="isLoading"
+      />
+    </div>
     <terms-of-use-acceptance-dialog
       :value="(!infoPage) && needToAcceptTermsOfUse"
       @logout="logout"
@@ -352,19 +357,22 @@ permissions and limitations under the Licence.
 
 import CookieLaw from 'vue-cookie-law'
 import { mapActions, mapState, mapGetters } from 'vuex'
-import AppBarTabsExtension from '@/components/AppBarTabsExtension'
+
 import AppBarEditModeContent from '@/components/AppBarEditModeContent'
+import AppBarTabsExtension from '@/components/AppBarTabsExtension'
 import LogoFooter from '@/components/LogoFooter'
+import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import TermsOfUseAcceptanceDialog from '@/components/TermsOfUseAcceptanceDialog.vue'
 
 import { saveCurrentRoute } from '@/utils/loginHelpers'
-import TermsOfUseAcceptanceDialog from '@/components/TermsOfUseAcceptanceDialog.vue'
 
 export default {
   components: {
-    AppBarTabsExtension,
     AppBarEditModeContent,
+    AppBarTabsExtension,
     CookieLaw,
     LogoFooter,
+    ProgressIndicator,
     TermsOfUseAcceptanceDialog
   },
   data () {
@@ -385,6 +393,7 @@ export default {
   },
   computed: {
     ...mapState('defaultlayout', ['fullWidth']),
+    ...mapState('progressindicator', ['isLoading']),
     ...mapGetters('permissions', ['needToAcceptTermsOfUse']),
     browserTitle () {
       if (this.title === this.appBarTitle) {
@@ -566,4 +575,4 @@ export default {
     margin-bottom: 0 !important;
   }
 
-</style>>
+</style>
diff --git a/pages/configurations/_configurationId.vue b/pages/configurations/_configurationId.vue
index 8e470a79e..c991c6650 100644
--- a/pages/configurations/_configurationId.vue
+++ b/pages/configurations/_configurationId.vue
@@ -31,9 +31,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-    />
     <v-card flat>
       <center>
         <v-alert
@@ -70,13 +67,12 @@ import { CanAccessEntityGetter, CanModifyEntityGetter, CanDeleteEntityGetter, Ca
 
 import { Configuration } from '@/models/Configuration'
 
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import ModificationInfo from '@/components/ModificationInfo.vue'
 import { ConfigurationsState } from '@/store/configurations'
 
 @Component({
   components: {
-    ProgressIndicator,
     ModificationInfo
   },
   computed: {
@@ -85,13 +81,12 @@ import { ConfigurationsState } from '@/store/configurations'
   },
   methods: {
     ...mapActions('configurations', ['loadConfiguration']),
-    ...mapActions('appbar', ['setTitle', 'setTabs'])
+    ...mapActions('appbar', ['setTitle', 'setTabs']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 // @ts-ignore
 export default class ConfigurationsIdPage extends Vue {
-  private isLoading: boolean = false
-
   @ProvideReactive()
     editable: boolean = false
 
@@ -114,10 +109,11 @@ export default class ConfigurationsIdPage extends Vue {
   canRestoreEntity!: CanRestoreEntityGetter
   setTabs!: SetTabsAction
   setTitle!: SetTitleAction
+  setLoading!: SetLoadingAction
 
   async created () {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       this.initializeAppBar()
       await this.loadConfiguration(this.configurationId)
 
@@ -139,7 +135,7 @@ export default class ConfigurationsIdPage extends Vue {
     } catch (_e) {
       this.$store.commit('snackbar/setError', 'Loading configuration failed')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/configurations/_configurationId/actions.vue b/pages/configurations/_configurationId/actions.vue
index 2b7fd564d..e7f5b073a 100644
--- a/pages/configurations/_configurationId/actions.vue
+++ b/pages/configurations/_configurationId/actions.vue
@@ -35,9 +35,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-    />
     <NuxtChild />
   </div>
 </template>
@@ -50,20 +47,17 @@ import {
   LoadAllConfigurationActionsAction
 } from '@/store/configurations'
 
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 
 @Component({
-  components: {
-    ProgressIndicator
-  },
-  methods: mapActions('configurations', [
-    'loadAllConfigurationActions'
-  ])
+  methods: {
+    ...mapActions('configurations', ['loadAllConfigurationActions']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class ConfigurationActions extends Vue {
-  private isLoading = false
-
   loadAllConfigurationActions!: LoadAllConfigurationActionsAction
+  setLoading!: SetLoadingAction
 
   head () {
     return {
@@ -77,12 +71,12 @@ export default class ConfigurationActions extends Vue {
 
   async fetch () {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.loadAllConfigurationActions(this.configurationId)
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to fetch actions')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/configurations/_configurationId/actions/generic-configuration-actions/_actionId/edit.vue b/pages/configurations/_configurationId/actions/generic-configuration-actions/_actionId/edit.vue
index ea49ed08a..a55c2462f 100644
--- a/pages/configurations/_configurationId/actions/generic-configuration-actions/_actionId/edit.vue
+++ b/pages/configurations/_configurationId/actions/generic-configuration-actions/_actionId/edit.vue
@@ -31,10 +31,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isInProgress"
-      :dark="isSaving"
-    />
     <!-- just to be consistent with the new mask, we show the selected action type as an disabled v-select here -->
     <v-select
       :value="action.actionTypeName"
@@ -89,24 +85,27 @@ import {
 import { GenericAction } from '@/models/GenericAction'
 
 import GenericActionForm from '@/components/actions/GenericActionForm.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
 
 @Component({
   components: {
     SaveAndCancelButtons,
-    ProgressIndicator,
     GenericActionForm
   },
   scrollToTop: true,
   middleware: ['auth'],
-  computed: mapState('configurations', ['configurationGenericAction', 'configurationAttachments']),
-  methods: mapActions('configurations', ['loadConfigurationGenericAction', 'loadAllConfigurationActions', 'loadConfigurationAttachments', 'updateConfigurationGenericAction'])
+  computed: {
+    ...mapState('configurations', ['configurationGenericAction', 'configurationAttachments']),
+    ...mapState('progressindicator', ['isLoading'])
+  },
+  methods: {
+    ...mapActions('configurations', ['loadConfigurationGenericAction', 'loadAllConfigurationActions', 'loadConfigurationAttachments', 'updateConfigurationGenericAction']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class GenericConfigurationActionEditPage extends mixins(CheckEditAccess) {
   private action: GenericAction = new GenericAction()
-  private isSaving = false
-  private isLoading = false
 
   // vuex definition for typescript check
   configurationGenericAction!: ConfigurationsState['configurationGenericAction']
@@ -115,6 +114,8 @@ export default class GenericConfigurationActionEditPage extends mixins(CheckEdit
   loadConfigurationAttachments!: LoadConfigurationAttachmentsAction
   updateConfigurationGenericAction!: UpdateConfigurationGenericActionAction
   loadAllConfigurationActions!: LoadAllConfigurationActionsAction
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -140,7 +141,7 @@ export default class GenericConfigurationActionEditPage extends mixins(CheckEdit
 
   async fetch (): Promise<void> {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await Promise.all([
         this.loadConfigurationGenericAction(this.actionId),
         this.loadConfigurationAttachments(this.configurationId)
@@ -151,7 +152,7 @@ export default class GenericConfigurationActionEditPage extends mixins(CheckEdit
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to fetch action')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
@@ -163,17 +164,13 @@ export default class GenericConfigurationActionEditPage extends mixins(CheckEdit
     return this.$route.params.actionId
   }
 
-  get isInProgress (): boolean {
-    return this.isLoading || this.isSaving
-  }
-
   async save () {
     if (!(this.$refs.genericConfigurationActionForm as Vue & { isValid: () => boolean }).isValid()) {
       this.$store.commit('snackbar/setError', 'Please correct the errors')
       return
     }
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.updateConfigurationGenericAction({
         configurationId: this.configurationId,
         genericAction: this.action
@@ -184,7 +181,7 @@ export default class GenericConfigurationActionEditPage extends mixins(CheckEdit
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to save the action')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/configurations/_configurationId/actions/new.vue b/pages/configurations/_configurationId/actions/new.vue
index dcce70596..e8fb7023c 100644
--- a/pages/configurations/_configurationId/actions/new.vue
+++ b/pages/configurations/_configurationId/actions/new.vue
@@ -35,9 +35,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-    />
     <v-card
       flat
     >
@@ -94,11 +91,11 @@ import {
   LoadConfigurationParametersAction
 } from '@/store/configurations'
 
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import { ACTION_TYPE_API_FILTER_CONFIGURATION } from '@/services/cv/ActionTypeApi'
 
 @Component({
-  components: { ActionTypeDialog, ProgressIndicator },
+  components: { ActionTypeDialog },
   middleware: ['auth'],
   computed: {
     ...mapGetters('vocabulary', ['configurationActionTypeItems']),
@@ -106,11 +103,11 @@ import { ACTION_TYPE_API_FILTER_CONFIGURATION } from '@/services/cv/ActionTypeAp
   },
   methods: {
     ...mapActions('vocabulary', ['loadConfigurationGenericActionTypes']),
-    ...mapActions('configurations', ['setChosenKindOfConfigurationAction', 'loadConfigurationAttachments', 'loadConfigurationParameters'])
+    ...mapActions('configurations', ['setChosenKindOfConfigurationAction', 'loadConfigurationAttachments', 'loadConfigurationParameters']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class ActionAddPage extends mixins(CheckEditAccess) {
-  private isLoading: boolean = false
   private showNewActionTypeDialog = false
 
   // vuex definition for typescript check
@@ -121,6 +118,7 @@ export default class ActionAddPage extends mixins(CheckEditAccess) {
   loadConfigurationAttachments!: LoadConfigurationAttachmentsAction
   loadConfigurationParameters!: LoadConfigurationParametersAction
   setChosenKindOfConfigurationAction!: SetChosenKindOfConfigurationActionAction
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -150,7 +148,7 @@ export default class ActionAddPage extends mixins(CheckEditAccess) {
 
   async fetch (): Promise<void> {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       this.chosenKindOfAction = null
       await Promise.all([
         this.loadConfigurationGenericActionTypes(),
@@ -160,7 +158,7 @@ export default class ActionAddPage extends mixins(CheckEditAccess) {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to fetch action types')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/configurations/_configurationId/actions/new/generic-configuration-actions.vue b/pages/configurations/_configurationId/actions/new/generic-configuration-actions.vue
index 801badea1..cbf176759 100644
--- a/pages/configurations/_configurationId/actions/new/generic-configuration-actions.vue
+++ b/pages/configurations/_configurationId/actions/new/generic-configuration-actions.vue
@@ -32,10 +32,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card-actions>
       <v-spacer />
       <SaveAndCancelButtons
@@ -79,23 +75,26 @@ import { GenericAction } from '@/models/GenericAction'
 
 import GenericActionForm from '@/components/actions/GenericActionForm.vue'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 
 @Component({
   middleware: ['auth'],
-  components: { ProgressIndicator, SaveAndCancelButtons, GenericActionForm },
+  components: { SaveAndCancelButtons, GenericActionForm },
   computed: mapState('configurations', ['configurationAttachments', 'chosenKindOfConfigurationAction']),
-  methods: mapActions('configurations', ['addConfigurationGenericAction', 'loadAllConfigurationActions'])
+  methods: {
+    ...mapActions('configurations', ['addConfigurationGenericAction', 'loadAllConfigurationActions']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class NewGenericConfigurationAction extends mixins(CheckEditAccess) {
   private genericConfigurationAction: GenericAction = new GenericAction()
-  private isSaving: boolean = false
 
   // vuex definition for typescript check
   configurationAttachments!: ConfigurationsState['configurationAttachments']
   chosenKindOfConfigurationAction!: ConfigurationsState['chosenKindOfConfigurationAction']
   addConfigurationGenericAction!: AddConfigurationGenericAction
   loadAllConfigurationActions!: LoadAllConfigurationActionsAction
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -139,7 +138,7 @@ export default class NewGenericConfigurationAction extends mixins(CheckEditAcces
     this.genericConfigurationAction.actionTypeUrl = this.chosenKindOfConfigurationAction?.uri || ''
 
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.addConfigurationGenericAction({
         configurationId: this.configurationId,
         genericAction: this.genericConfigurationAction
@@ -151,7 +150,7 @@ export default class NewGenericConfigurationAction extends mixins(CheckEditAcces
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to save the action')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/configurations/_configurationId/actions/new/parameter-change-actions.vue b/pages/configurations/_configurationId/actions/new/parameter-change-actions.vue
index 11ab381a2..b80b8825e 100644
--- a/pages/configurations/_configurationId/actions/new/parameter-change-actions.vue
+++ b/pages/configurations/_configurationId/actions/new/parameter-change-actions.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card-actions>
       <v-spacer />
       <SaveAndCancelButtons
@@ -74,30 +70,31 @@ import {
 } from '@/store/configurations'
 
 import { ParameterChangeAction } from '@/models/ParameterChangeAction'
-
+import { SetLoadingAction } from '@/store/progressindicator'
 import ParameterChangeActionForm from '@/components/actions/ParameterChangeActionForm.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
 
 @Component({
   middleware: ['auth'],
   components: {
     SaveAndCancelButtons,
-    ProgressIndicator,
     ParameterChangeActionForm
   },
   computed: mapState('configurations', ['chosenKindOfConfigurationAction', 'configurationParameters']),
-  methods: mapActions('configurations', ['addConfigurationParameterChangeAction', 'loadAllConfigurationActions'])
+  methods: {
+    ...mapActions('configurations', ['addConfigurationParameterChangeAction', 'loadAllConfigurationActions']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class NewParameterChangeAction extends mixins(CheckEditAccess) {
   private parameterChangeAction: ParameterChangeAction = new ParameterChangeAction()
-  private isSaving: boolean = false
 
   // vuex definition for typescript check
   configurationParameters!: ConfigurationsState['configurationParameters']
   chosenKindOfConfigurationAction!: ConfigurationsState['chosenKindOfConfigurationAction']
   addConfigurationParameterChangeAction!: AddConfigurationParameterChangeActionAction
   loadAllConfigurationActions!: LoadAllConfigurationActionsAction
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -145,7 +142,7 @@ export default class NewParameterChangeAction extends mixins(CheckEditAccess) {
     }
 
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.addConfigurationParameterChangeAction({
         parameterId: this.parameterChangeAction.parameter.id,
         action: this.parameterChangeAction
@@ -156,7 +153,7 @@ export default class NewParameterChangeAction extends mixins(CheckEditAccess) {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to save the action')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/configurations/_configurationId/actions/parameter-change-actions/_actionId/edit.vue b/pages/configurations/_configurationId/actions/parameter-change-actions/_actionId/edit.vue
index fae3cd09f..9fead4316 100644
--- a/pages/configurations/_configurationId/actions/parameter-change-actions/_actionId/edit.vue
+++ b/pages/configurations/_configurationId/actions/parameter-change-actions/_actionId/edit.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-      dark
-    />
     <v-select
       value="Parameter Value Change"
       :items="['Parameter Value Change']"
@@ -87,23 +83,24 @@ import {
 import { ParameterChangeAction } from '@/models/ParameterChangeAction'
 
 import ParameterChangeActionForm from '@/components/actions/ParameterChangeActionForm.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
 
 @Component({
   components: {
     ParameterChangeActionForm,
-    ProgressIndicator,
     SaveAndCancelButtons
   },
   scrollToTop: true,
   middleware: ['auth'],
   computed: mapState('configurations', ['configurationParameterChangeAction', 'configurationParameters']),
-  methods: mapActions('configurations', ['loadConfigurationParameterChangeAction', 'loadAllConfigurationActions', 'loadConfigurationParameters', 'updateConfigurationParameterChangeAction'])
+  methods: {
+    ...mapActions('configurations', ['loadConfigurationParameterChangeAction', 'loadAllConfigurationActions', 'loadConfigurationParameters', 'updateConfigurationParameterChangeAction']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class ConfigurationParameterChangeActionEditPage extends mixins(CheckEditAccess) {
   private action: ParameterChangeAction = new ParameterChangeAction()
-  private isLoading = false
 
   // vuex definition for typescript check
   configurationParameterChangeAction!: ConfigurationsState['configurationParameterChangeAction']
@@ -112,6 +109,7 @@ export default class ConfigurationParameterChangeActionEditPage extends mixins(C
   loadConfigurationParameters!: LoadConfigurationParametersAction
   updateConfigurationParameterChangeAction!: UpdateConfigurationParameterChangeActionAction
   loadAllConfigurationActions!: LoadAllConfigurationActionsAction
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -137,7 +135,7 @@ export default class ConfigurationParameterChangeActionEditPage extends mixins(C
 
   async fetch (): Promise<void> {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await Promise.all([
         this.loadConfigurationParameterChangeAction(this.actionId),
         this.loadConfigurationParameters(this.configurationId)
@@ -148,7 +146,7 @@ export default class ConfigurationParameterChangeActionEditPage extends mixins(C
     } catch {
       this.$store.commit('snackbar/setError', 'Failed to fetch action')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
@@ -171,7 +169,7 @@ export default class ConfigurationParameterChangeActionEditPage extends mixins(C
     }
 
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.updateConfigurationParameterChangeAction({
         parameterId: this.action.parameter.id,
         action: this.action
@@ -181,7 +179,7 @@ export default class ConfigurationParameterChangeActionEditPage extends mixins(C
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to save the action')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/configurations/_configurationId/attachments.vue b/pages/configurations/_configurationId/attachments.vue
index a217e403e..9c6cdd71c 100644
--- a/pages/configurations/_configurationId/attachments.vue
+++ b/pages/configurations/_configurationId/attachments.vue
@@ -31,9 +31,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-    />
     <NuxtChild />
   </div>
 </template>
@@ -44,26 +41,27 @@ import { mapActions } from 'vuex'
 
 import { LoadConfigurationAttachmentsAction } from '@/store/configurations'
 
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 
 @Component({
-  components: { ProgressIndicator },
-  methods: mapActions('configurations', ['loadConfigurationAttachments'])
+  methods: {
+    ...mapActions('configurations', ['loadConfigurationAttachments']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class ConfigurationAttachmentsPage extends Vue {
-  private isLoading = false
-
   // vuex definition for typescript check
   loadConfigurationAttachments!: LoadConfigurationAttachmentsAction
+  setLoading!: SetLoadingAction
 
   async created () {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.loadConfigurationAttachments(this.configurationId)
     } catch (e) {
       this.$store.commit('snackbar/setError', 'failed to fetch attachments')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/configurations/_configurationId/attachments/_attachmentId/edit.vue b/pages/configurations/_configurationId/attachments/_attachmentId/edit.vue
index 9f7ca1176..2ab416815 100644
--- a/pages/configurations/_configurationId/attachments/_attachmentId/edit.vue
+++ b/pages/configurations/_configurationId/attachments/_attachmentId/edit.vue
@@ -32,10 +32,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isInProgress"
-      :dark="isSaving"
-    />
     <v-card-actions>
       <v-spacer />
       <SaveAndCancelButtons
@@ -104,7 +100,7 @@ import { Attachment } from '@/models/Attachment'
 
 import { Rules } from '@/mixins/Rules'
 
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
 
 import { AttachmentsMixin } from '@/mixins/AttachmentsMixin'
@@ -115,17 +111,20 @@ import { AttachmentsMixin } from '@/mixins/AttachmentsMixin'
  */
 @Component({
   components: {
-    SaveAndCancelButtons,
-    ProgressIndicator
+    SaveAndCancelButtons
   },
   middleware: ['auth'],
-  computed: mapState('configurations', ['configurationAttachment']),
-  methods: mapActions('configurations', ['loadConfigurationAttachment', 'loadConfigurationAttachments', 'updateConfigurationAttachment'])
+  computed: {
+    ...mapState('configurations', ['configurationAttachment']),
+    ...mapState('progressindicator', ['isLoading'])
+  },
+  methods: {
+    ...mapActions('configurations', ['loadConfigurationAttachment', 'loadConfigurationAttachments', 'updateConfigurationAttachment']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 // @ts-ignore
 export default class AttachmentEditPage extends mixins(Rules, AttachmentsMixin, CheckEditAccess) {
-  private isSaving = false
-  private isLoading = false
   private valueCopy: Attachment = new Attachment()
 
   // vuex definition for typescript check
@@ -133,6 +132,8 @@ export default class AttachmentEditPage extends mixins(Rules, AttachmentsMixin,
   loadConfigurationAttachment!: LoadConfigurationAttachmentAction
   loadConfigurationAttachments!: LoadConfigurationAttachmentsAction
   updateConfigurationAttachment!: UpdateConfigurationAttachmentAction
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -158,7 +159,7 @@ export default class AttachmentEditPage extends mixins(Rules, AttachmentsMixin,
 
   async fetch (): Promise<void> {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.loadConfigurationAttachment(this.attachmentId)
       if (this.configurationAttachment) {
         this.valueCopy = Attachment.createFromObject(this.configurationAttachment)
@@ -166,7 +167,7 @@ export default class AttachmentEditPage extends mixins(Rules, AttachmentsMixin,
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to load attachment')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
@@ -178,17 +179,13 @@ export default class AttachmentEditPage extends mixins(Rules, AttachmentsMixin,
     return this.$route.params.attachmentId
   }
 
-  get isInProgress (): boolean {
-    return this.isLoading || this.isSaving
-  }
-
   async save () {
     if (!(this.$refs.attachmentsEditForm as Vue & { validate: () => boolean }).validate()) {
       this.$store.commit('snackbar/setError', 'Please correct your input')
       return
     }
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.updateConfigurationAttachment({
         configurationId: this.configurationId,
         attachment: this.valueCopy
@@ -199,7 +196,7 @@ export default class AttachmentEditPage extends mixins(Rules, AttachmentsMixin,
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to save attachments')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/configurations/_configurationId/attachments/index.vue b/pages/configurations/_configurationId/attachments/index.vue
index b36ec1200..56456c983 100644
--- a/pages/configurations/_configurationId/attachments/index.vue
+++ b/pages/configurations/_configurationId/attachments/index.vue
@@ -32,10 +32,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card-actions
       v-if="editable"
     >
@@ -99,7 +95,7 @@ permissions and limitations under the Licence.
       v-if="attachmentToDelete"
       v-model="showDeleteDialog"
       title="Delete Attachment"
-      :disabled="isSaving"
+      :disabled="isLoading"
       @cancel="closeDialog"
       @delete="deleteAndCloseDialog"
     >
@@ -129,19 +125,24 @@ import DeleteDialog from '@/components/shared/DeleteDialog.vue'
 import DownloadDialog from '@/components/shared/DownloadDialog.vue'
 import HintCard from '@/components/HintCard.vue'
 import DotMenuActionDelete from '@/components/DotMenuActionDelete.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import { getLastPathElement } from '@/utils/urlHelpers'
 
 @Component({
-  components: { ProgressIndicator, DotMenuActionDelete, HintCard, DeleteDialog, AttachmentListItem, BaseList, DownloadDialog },
-  computed: mapState('configurations', ['configurationAttachments', 'configuration']),
-  methods: mapActions('configurations', ['loadConfigurationAttachments', 'deleteConfigurationAttachment', 'downloadAttachment'])
+  components: { DotMenuActionDelete, HintCard, DeleteDialog, AttachmentListItem, BaseList, DownloadDialog },
+  computed: {
+    ...mapState('configurations', ['configurationAttachments', 'configuration']),
+    ...mapState('progressindicator', ['isLoading'])
+  },
+  methods: {
+    ...mapActions('configurations', ['loadConfigurationAttachments', 'deleteConfigurationAttachment', 'downloadAttachment']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class ConfigurationAttachmentShowPage extends Vue {
   @InjectReactive()
     editable!: boolean
 
-  private isSaving = false
   private showDeleteDialog = false
   private attachmentToDelete: Attachment | null = null
 
@@ -154,6 +155,7 @@ export default class ConfigurationAttachmentShowPage extends Vue {
   deleteConfigurationAttachment!: DeleteConfigurationAttachmentAction
   loadConfigurationAttachments!: LoadConfigurationAttachmentsAction
   downloadAttachment!: DownloadAttachmentAction
+  setLoading!: SetLoadingAction
 
   get configurationId (): string {
     return this.$route.params.configurationId
@@ -174,7 +176,7 @@ export default class ConfigurationAttachmentShowPage extends Vue {
       return
     }
     try {
-      this.isSaving = true
+      this.setLoading(true)
       const attachmendId = this.attachmentToDelete.id
       this.closeDialog()
       await this.deleteConfigurationAttachment(attachmendId)
@@ -183,7 +185,7 @@ export default class ConfigurationAttachmentShowPage extends Vue {
     } catch (_error) {
       this.$store.commit('snackbar/setError', 'Failed to delete attachment')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/configurations/_configurationId/attachments/new.vue b/pages/configurations/_configurationId/attachments/new.vue
index 9005389bb..7513ca8d7 100644
--- a/pages/configurations/_configurationId/attachments/new.vue
+++ b/pages/configurations/_configurationId/attachments/new.vue
@@ -32,10 +32,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-form ref="attachmentsForm" class="pb-2" @submit.prevent>
       <v-card-actions>
         <v-spacer />
@@ -135,29 +131,30 @@ import { IUploadResult } from '@/services/sms/UploadApi'
 import { Attachment } from '@/models/Attachment'
 
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 
 import { Rules } from '@/mixins/Rules'
 import { UploadRules } from '@/mixins/UploadRules'
 
 @Component({
-  components: { ProgressIndicator, SaveAndCancelButtons },
+  components: { SaveAndCancelButtons },
   middleware: ['auth'],
   methods: {
     ...mapActions('configurations', ['addConfigurationAttachment', 'loadConfigurationAttachments']),
-    ...mapActions('files', ['uploadFile'])
+    ...mapActions('files', ['uploadFile']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class ConfigurationAttachmentAddPage extends mixins(Rules, UploadRules, CheckEditAccess) {
   private attachment: Attachment = new Attachment()
   private attachmentType: string = 'file'
   private file: File | null = null
-  private isSaving: boolean = false
 
   // vuex definition for typescript check
   uploadFile!: (file: File) => Promise<IUploadResult>
   addConfigurationAttachment!: AddConfigurationAttachmentAction
   loadConfigurationAttachments!: LoadConfigurationAttachmentsAction
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -203,7 +200,7 @@ export default class ConfigurationAttachmentAddPage extends mixins(Rules, Upload
 
     let theFailureCanBeFromUpload = true
     try {
-      this.isSaving = true
+      this.setLoading(true)
 
       if (this.attachmentType !== 'url') {
         // Due to the validation we can be sure that the file is not null
@@ -222,7 +219,7 @@ export default class ConfigurationAttachmentAddPage extends mixins(Rules, Upload
     } catch (error: any) {
       this.handleError(error, theFailureCanBeFromUpload)
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/configurations/_configurationId/basic/edit.vue b/pages/configurations/_configurationId/basic/edit.vue
index 721478e27..1d91cb301 100644
--- a/pages/configurations/_configurationId/basic/edit.vue
+++ b/pages/configurations/_configurationId/basic/edit.vue
@@ -34,10 +34,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-      dark
-    />
     <v-card
       flat
     >
@@ -91,7 +87,7 @@ import { Configuration, IConfiguration } from '@/models/Configuration'
 
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
 import ConfigurationsBasicDataForm from '@/components/configurations/ConfigurationsBasicDataForm.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import NavigationGuardDialog from '@/components/shared/NavigationGuardDialog.vue'
 import NonModelOptionsForm, { NonModelOptions } from '@/components/shared/NonModelOptionsForm.vue'
 import { CreatePidAction, LoadConfigurationAction, SaveConfigurationAction } from '@/store/configurations'
@@ -101,19 +97,19 @@ import { CreatePidAction, LoadConfigurationAction, SaveConfigurationAction } fro
     ConfigurationsBasicDataForm,
     NavigationGuardDialog,
     NonModelOptionsForm,
-    ProgressIndicator,
     SaveAndCancelButtons
   },
   middleware: ['auth'],
   computed: mapState('configurations', ['configuration']),
   methods: {
     ...mapActions('configurations', ['saveConfiguration', 'loadConfiguration', 'createPid']),
-    ...mapActions('appbar', ['setTitle'])
+    ...mapActions('appbar', ['setTitle']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class ConfigurationEditBasicPage extends mixins(CheckEditAccess) {
   private configurationCopy: Configuration = new Configuration()
-  private isLoading: boolean = false
+
   private hasSaved: boolean = false
   private showNavigationWarning: boolean = false
   private to: RawLocation | null = null
@@ -127,7 +123,7 @@ export default class ConfigurationEditBasicPage extends mixins(CheckEditAccess)
   loadConfiguration!: LoadConfigurationAction
   createPid!: CreatePidAction
   setTitle!: SetTitleAction
-
+  setLoading!: SetLoadingAction
   /**
    * route to which the user is redirected when he is not allowed to access the page
    *
@@ -171,7 +167,7 @@ export default class ConfigurationEditBasicPage extends mixins(CheckEditAccess)
     }
 
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.saveConfiguration(this.configurationCopy)
       if (this.editOptions.persistentIdentifierShouldBeCreated) {
         await this.createPid(this.configurationId)
@@ -184,7 +180,7 @@ export default class ConfigurationEditBasicPage extends mixins(CheckEditAccess)
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Save failed')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/configurations/_configurationId/basic/index.vue b/pages/configurations/_configurationId/basic/index.vue
index 77dc708ea..ccd6ce075 100644
--- a/pages/configurations/_configurationId/basic/index.vue
+++ b/pages/configurations/_configurationId/basic/index.vue
@@ -34,10 +34,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card flat>
       <v-card-actions>
         <v-spacer />
@@ -113,7 +109,7 @@ permissions and limitations under the Licence.
       <DeleteDialog
         v-model="showDeleteDialog"
         title="Delete Configuration"
-        :disabled="isSaving"
+        :disabled="isLoading"
         @cancel="closeDialog"
         @delete="deleteAndCloseDialog"
       >
@@ -146,7 +142,7 @@ import DotMenuActionDelete from '@/components/DotMenuActionDelete.vue'
 import ConfigurationArchiveDialog from '@/components/configurations/ConfigurationArchiveDialog.vue'
 import DotMenuActionSensorML from '@/components/DotMenuActionSensorML.vue'
 import DeleteDialog from '@/components/shared/DeleteDialog.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import DotMenuActionArchive from '@/components/DotMenuActionArchive.vue'
 import DotMenuActionRestore from '@/components/DotMenuActionRestore.vue'
 import DownloadDialog from '@/components/shared/DownloadDialog.vue'
@@ -156,7 +152,6 @@ import { ArchiveConfigurationAction, LoadConfigurationAction, RestoreConfigurati
 
 @Component({
   components: {
-    ProgressIndicator,
     DeleteDialog,
     DotMenuActionDelete,
     DotMenuActionSensorML,
@@ -167,15 +162,21 @@ import { ArchiveConfigurationAction, LoadConfigurationAction, RestoreConfigurati
     DotMenuActionRestore,
     DownloadDialog
   },
-  computed: mapState('configurations', ['configuration']),
-  methods: mapActions('configurations', [
-    'deleteConfiguration',
-    'loadConfiguration',
-    'archiveConfiguration',
-    'restoreConfiguration',
-    'exportAsSensorML',
-    'getSensorMLUrl'
-  ])
+  computed: {
+    ...mapState('configurations', ['configuration']),
+    ...mapState('progressindicator', ['isLoading'])
+  },
+  methods: {
+    ...mapActions('configurations', [
+      'deleteConfiguration',
+      'loadConfiguration',
+      'archiveConfiguration',
+      'restoreConfiguration',
+      'exportAsSensorML',
+      'getSensorMLUrl'
+    ]),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class ConfigurationShowBasicPage extends Vue {
   @InjectReactive()
@@ -190,8 +191,6 @@ export default class ConfigurationShowBasicPage extends Vue {
   @InjectReactive()
     restoreable!: boolean
 
-  private isSaving = false
-
   private showDeleteDialog: boolean = false
   private showArchiveDialog: boolean = false
   private showDownloadDialog: boolean = false
@@ -204,6 +203,7 @@ export default class ConfigurationShowBasicPage extends Vue {
   restoreConfiguration!: RestoreConfigurationAction
   exportAsSensorML!: ExportAsSensorMLAction
   getSensorMLUrl!: GetSensorMLUrlAction
+  setLoading!: SetLoadingAction
 
   get configurationId () {
     return this.$route.params.configurationId
@@ -255,14 +255,14 @@ export default class ConfigurationShowBasicPage extends Vue {
       return
     }
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.deleteConfiguration(this.configuration.id)
       this.$store.commit('snackbar/setSuccess', 'Configuration deleted')
       this.$router.push('/configurations')
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Configuration could not be deleted')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 
@@ -280,14 +280,14 @@ export default class ConfigurationShowBasicPage extends Vue {
       return
     }
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.archiveConfiguration(this.configuration.id)
       await this.loadConfiguration(this.configurationId)
       this.$store.commit('snackbar/setSuccess', 'Configuration archived')
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Configuration could not be archived')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
       this.showArchiveDialog = false
     }
   }
@@ -296,7 +296,7 @@ export default class ConfigurationShowBasicPage extends Vue {
     if (this.configuration === null || this.configuration.id === null) {
       return
     }
-    this.isSaving = true
+    this.setLoading(true)
     try {
       await this.restoreConfiguration(this.configuration.id)
       await this.loadConfiguration(this.configurationId)
@@ -304,7 +304,7 @@ export default class ConfigurationShowBasicPage extends Vue {
     } catch (error) {
       this.$store.commit('snackbar/setError', 'Configuration could not be restored')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/configurations/_configurationId/contacts.vue b/pages/configurations/_configurationId/contacts.vue
index 059f72de9..ea26bb0a8 100644
--- a/pages/configurations/_configurationId/contacts.vue
+++ b/pages/configurations/_configurationId/contacts.vue
@@ -34,9 +34,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-    />
     <NuxtChild />
   </div>
 </template>
@@ -45,27 +42,26 @@ permissions and limitations under the Licence.
 import { Component, Vue } from 'nuxt-property-decorator'
 
 import { mapActions } from 'vuex'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import { LoadCvContactRolesAction } from '@/store/vocabulary'
 import { LoadConfigurationContactRolesAction } from '@/store/configurations'
 
 @Component({
-  components: { ProgressIndicator },
   methods: {
     ...mapActions('configurations', ['loadConfigurationContactRoles']),
-    ...mapActions('vocabulary', ['loadCvContactRoles'])
+    ...mapActions('vocabulary', ['loadCvContactRoles']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class ContactTab extends Vue {
-  private isLoading = false
-
   // vuex definition for typescript check
   loadConfigurationContactRoles!: LoadConfigurationContactRolesAction
   loadCvContactRoles!: LoadCvContactRolesAction
+  setLoading!: SetLoadingAction
 
   async fetch () {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await Promise.all([
         this.loadConfigurationContactRoles(this.configurationId),
         this.loadCvContactRoles()
@@ -73,7 +69,7 @@ export default class ContactTab extends Vue {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to fetch contacts')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/configurations/_configurationId/contacts/index.vue b/pages/configurations/_configurationId/contacts/index.vue
index ce5a25934..ca6d40f9c 100644
--- a/pages/configurations/_configurationId/contacts/index.vue
+++ b/pages/configurations/_configurationId/contacts/index.vue
@@ -31,10 +31,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card-actions>
       <v-spacer />
       <v-btn
@@ -93,13 +89,12 @@ import { LoadConfigurationContactRolesAction, RemoveConfigurationContactRoleActi
 import BaseList from '@/components/shared/BaseList.vue'
 import ContactRoleListItem from '@/components/contacts/ContactRoleListItem.vue'
 import DotMenuActionDelete from '@/components/DotMenuActionDelete.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import HintCard from '@/components/HintCard.vue'
 
 @Component({
   components: {
     HintCard,
-    ProgressIndicator,
     DotMenuActionDelete,
     ContactRoleListItem,
     BaseList
@@ -108,17 +103,19 @@ import HintCard from '@/components/HintCard.vue'
     ...mapState('configurations', ['configurationContactRoles']),
     ...mapState('vocabulary', ['cvContactRoles'])
   },
-  methods: mapActions('configurations', ['loadConfigurationContactRoles', 'removeConfigurationContactRole'])
+  methods: {
+    ...mapActions('configurations', ['loadConfigurationContactRoles', 'removeConfigurationContactRole']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class ConfigurationShowContactPage extends Vue {
   @InjectReactive()
     editable!: boolean
 
-  private isSaving = false
-
   // vuex definition for typescript check
   loadConfigurationContactRoles!: LoadConfigurationContactRolesAction
   removeConfigurationContactRole!: RemoveConfigurationContactRoleAction
+  setLoading!: SetLoadingAction
 
   get configurationId (): string {
     return this.$route.params.configurationId
@@ -126,7 +123,7 @@ export default class ConfigurationShowContactPage extends Vue {
 
   async removeContactRole (contactRoleId: string) {
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.removeConfigurationContactRole({
         configurationContactRoleId: contactRoleId
       })
@@ -135,7 +132,7 @@ export default class ConfigurationShowContactPage extends Vue {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Removing contact failed')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/configurations/_configurationId/contacts/new.vue b/pages/configurations/_configurationId/contacts/new.vue
index e5a760ffa..7a1ad2cd2 100644
--- a/pages/configurations/_configurationId/contacts/new.vue
+++ b/pages/configurations/_configurationId/contacts/new.vue
@@ -2,7 +2,7 @@
 Web client of the Sensor Management System software developed within the
 Helmholtz DataHub Initiative by GFZ and UFZ.
 
-Copyright (C) 2020 - 2022
+Copyright (C) 2020 - 2023
 - Nils Brinckmann (GFZ, nils.brinckmann@gfz-potsdam.de)
 - Marc Hanisch (GFZ, marc.hanisch@gfz-potsdam.de)
 - Helmholtz Centre Potsdam - GFZ German Research Centre for
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isInProgress"
-      :dark="isSaving"
-    />
     <contact-role-assignment-form
       :contacts="contacts"
       :contact="selectedContact"
@@ -58,30 +54,29 @@ import { LoadCvContactRolesAction } from '@/store/vocabulary'
 import { Contact } from '@/models/Contact'
 import { ContactRole } from '@/models/ContactRole'
 
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 import ContactRoleAssignmentForm from '@/components/shared/ContactRoleAssignmentForm.vue'
 
 @Component({
   components: {
-    ProgressIndicator,
     ContactRoleAssignmentForm
   },
   middleware: ['auth'],
   computed: {
     ...mapState('contacts', ['contacts']),
     ...mapState('configurations', ['configurationContactRoles']),
-    ...mapState('vocabulary', ['cvContactRoles'])
+    ...mapState('vocabulary', ['cvContactRoles']),
+    ...mapState('progressindicator', ['isLoading'])
   },
   methods: {
     ...mapActions('contacts', ['loadAllContacts']),
     ...mapActions('configurations', ['loadConfigurationContactRoles', 'addConfigurationContactRole']),
-    ...mapActions('vocabulary', ['loadCvContactRoles'])
+    ...mapActions('vocabulary', ['loadCvContactRoles']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class ConfigurationAssignContactPage extends mixins(CheckEditAccess) {
   private selectedContact: Contact | null = null
-  private isLoading: boolean = false
-  private isSaving: boolean = false
 
   // vuex definition for typescript check
   configurationContactRoles!: ConfigurationsState['configurationContactRoles']
@@ -90,6 +85,8 @@ export default class ConfigurationAssignContactPage extends mixins(CheckEditAcce
   loadAllContacts!: LoadAllContactsAction
   loadCvContactRoles!: LoadCvContactRolesAction
   addConfigurationContactRole!: AddConfigurationContactRoleAction
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -115,7 +112,7 @@ export default class ConfigurationAssignContactPage extends mixins(CheckEditAcce
 
   async fetch (): Promise<void> {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.loadAllContacts()
 
       const redirectContactId = this.$route.query.contact
@@ -125,7 +122,7 @@ export default class ConfigurationAssignContactPage extends mixins(CheckEditAcce
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to fetch related contacts')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
@@ -133,14 +130,10 @@ export default class ConfigurationAssignContactPage extends mixins(CheckEditAcce
     return this.$route.params.configurationId
   }
 
-  get isInProgress (): boolean {
-    return this.isLoading || this.isSaving
-  }
-
   async assignContact (contactRole: ContactRole | null) {
     if (this.editable && contactRole) {
       try {
-        this.isSaving = true
+        this.setLoading(true)
         await this.addConfigurationContactRole({
           configurationId: this.configurationId,
           contactRole
@@ -151,7 +144,7 @@ export default class ConfigurationAssignContactPage extends mixins(CheckEditAcce
       } catch (e) {
         this.$store.commit('snackbar/setError', 'Failed to add a contact')
       } finally {
-        this.isSaving = false
+        this.setLoading(false)
       }
     }
   }
diff --git a/pages/configurations/_configurationId/customfields.vue b/pages/configurations/_configurationId/customfields.vue
index 95e6a58ef..93b450990 100644
--- a/pages/configurations/_configurationId/customfields.vue
+++ b/pages/configurations/_configurationId/customfields.vue
@@ -30,9 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-    />
     <NuxtChild />
   </div>
 </template>
@@ -43,26 +40,27 @@ import { mapActions } from 'vuex'
 
 import { LoadConfigurationCustomFieldsAction } from '@/store/configurations'
 
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 
 @Component({
-  components: { ProgressIndicator },
-  methods: mapActions('configurations', ['loadConfigurationCustomFields'])
+  methods: {
+    ...mapActions('configurations', ['loadConfigurationCustomFields']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class ConfigurationCustomFieldsPage extends Vue {
-  private isLoading = false
-
   // vuex definition for typescript check
   loadConfigurationCustomFields!: LoadConfigurationCustomFieldsAction
+  setLoading!: SetLoadingAction
 
   async fetch () {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.loadConfigurationCustomFields(this.configurationId)
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to fetch custom fields')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/configurations/_configurationId/customfields/_customfieldId/edit.vue b/pages/configurations/_configurationId/customfields/_customfieldId/edit.vue
index ac0feb55f..05f49be3d 100644
--- a/pages/configurations/_configurationId/customfields/_customfieldId/edit.vue
+++ b/pages/configurations/_configurationId/customfields/_customfieldId/edit.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isInProgress"
-      :dark="isSaving"
-    />
     <v-card
       flat
     >
@@ -89,7 +85,7 @@ import { CustomTextField } from '@/models/CustomTextField'
 import BaseList from '@/components/shared/BaseList.vue'
 import CustomFieldForm from '@/components/shared/CustomFieldForm.vue'
 import CustomFieldListItem from '@/components/shared/CustomFieldListItem.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
 
 @Component({
@@ -97,17 +93,19 @@ import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
     BaseList,
     CustomFieldForm,
     CustomFieldListItem,
-    ProgressIndicator,
     SaveAndCancelButtons
   },
   middleware: ['auth'],
-  computed: mapState('configurations', ['configurationCustomField', 'configurationCustomFields']),
-  methods: mapActions('configurations', ['loadConfigurationCustomField', 'loadConfigurationCustomFields', 'updateConfigurationCustomField'])
+  computed: {
+    ...mapState('configurations', ['configurationCustomField', 'configurationCustomFields']),
+    ...mapState('progressindicator', ['isLoading'])
+  },
+  methods: {
+    ...mapActions('configurations', ['loadConfigurationCustomField', 'loadConfigurationCustomFields', 'updateConfigurationCustomField']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class ConfigurationCustomFieldsEditPage extends mixins(CheckEditAccess) {
-  private isSaving = false
-  private isLoading = false
-
   private valueCopy: CustomTextField = new CustomTextField()
 
   // vuex definition for typescript check
@@ -116,6 +114,8 @@ export default class ConfigurationCustomFieldsEditPage extends mixins(CheckEditA
   loadConfigurationCustomField!: LoadConfigurationCustomFieldAction
   updateConfigurationCustomField!: UpdateConfigurationCustomFieldAction
   loadConfigurationCustomFields!: LoadConfigurationCustomFieldsAction
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -141,7 +141,7 @@ export default class ConfigurationCustomFieldsEditPage extends mixins(CheckEditA
 
   async fetch (): Promise<void> {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.loadConfigurationCustomField(this.customFieldId)
       if (this.configurationCustomField) {
         this.valueCopy = CustomTextField.createFromObject(this.configurationCustomField)
@@ -149,7 +149,7 @@ export default class ConfigurationCustomFieldsEditPage extends mixins(CheckEditA
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to load custom field')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
@@ -161,17 +161,13 @@ export default class ConfigurationCustomFieldsEditPage extends mixins(CheckEditA
     return this.$route.params.customfieldId
   }
 
-  get isInProgress (): boolean {
-    return this.isLoading || this.isSaving
-  }
-
   get configurationCustomFieldsExceptCurrent (): CustomTextField[] {
     return this.configurationCustomFields.filter(i => i.id !== this.customFieldId)
   }
 
   async save () {
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.updateConfigurationCustomField({
         configurationId: this.configurationId,
         configurationCustomField: this.valueCopy
@@ -182,7 +178,7 @@ export default class ConfigurationCustomFieldsEditPage extends mixins(CheckEditA
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to save custom field')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/configurations/_configurationId/customfields/index.vue b/pages/configurations/_configurationId/customfields/index.vue
index dbb2b5098..82184ef60 100644
--- a/pages/configurations/_configurationId/customfields/index.vue
+++ b/pages/configurations/_configurationId/customfields/index.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card-actions
       v-if="editable"
     >
@@ -84,7 +80,7 @@ permissions and limitations under the Licence.
       v-if="customFieldToDelete"
       v-model="showDeleteDialog"
       title="Delete Custom Field"
-      :disabled="isSaving"
+      :disabled="isLoading"
       @cancel="closeDialog"
       @delete="deleteAndCloseDialog"
     >
@@ -106,18 +102,23 @@ import CustomFieldListItem from '@/components/shared/CustomFieldListItem.vue'
 import DotMenuActionDelete from '@/components/DotMenuActionDelete.vue'
 import DeleteDialog from '@/components/shared/DeleteDialog.vue'
 import HintCard from '@/components/HintCard.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 
 @Component({
-  components: { ProgressIndicator, HintCard, DeleteDialog, DotMenuActionDelete, CustomFieldListItem, BaseList },
-  computed: mapState('configurations', ['configurationCustomFields']),
-  methods: mapActions('configurations', ['deleteConfigurationCustomField', 'loadConfigurationCustomFields'])
+  components: { HintCard, DeleteDialog, DotMenuActionDelete, CustomFieldListItem, BaseList },
+  computed: {
+    ...mapState('configurations', ['configurationCustomFields']),
+    ...mapState('progressindicator', ['isLoading'])
+  },
+  methods: {
+    ...mapActions('configurations', ['deleteConfigurationCustomField', 'loadConfigurationCustomFields']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class ConfigurationCustomFieldsShowPage extends Vue {
   @InjectReactive()
     editable!: boolean
 
-  private isSaving = false
   private showDeleteDialog = false
   private customFieldToDelete: CustomTextField | null = null
 
@@ -125,6 +126,7 @@ export default class ConfigurationCustomFieldsShowPage extends Vue {
   configurationCustomFields!: ConfigurationsState['configurationCustomFields']
   loadConfigurationCustomFields!: LoadConfigurationCustomFieldsAction
   deleteConfigurationCustomField!: DeleteConfigurationCustomFieldAction
+  setLoading!: SetLoadingAction
 
   get configurationId (): string {
     return this.$route.params.configurationId
@@ -145,14 +147,14 @@ export default class ConfigurationCustomFieldsShowPage extends Vue {
       return
     }
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.deleteConfigurationCustomField(this.customFieldToDelete.id)
       this.loadConfigurationCustomFields(this.configurationId)
       this.$store.commit('snackbar/setSuccess', 'Custom field deleted')
     } catch (_error) {
       this.$store.commit('snackbar/setError', 'Failed to delete custom field')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
       this.closeDialog()
     }
   }
diff --git a/pages/configurations/_configurationId/customfields/new.vue b/pages/configurations/_configurationId/customfields/new.vue
index 2eb86ad9b..10537b3fa 100644
--- a/pages/configurations/_configurationId/customfields/new.vue
+++ b/pages/configurations/_configurationId/customfields/new.vue
@@ -31,10 +31,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card
       flat
     >
@@ -92,7 +88,7 @@ import { CustomTextField } from '@/models/CustomTextField'
 import BaseList from '@/components/shared/BaseList.vue'
 import CustomFieldForm from '@/components/shared/CustomFieldForm.vue'
 import CustomFieldListItem from '@/components/shared/CustomFieldListItem.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
 
 @Component({
@@ -101,21 +97,22 @@ import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
     BaseList,
     CustomFieldForm,
     CustomFieldListItem,
-    ProgressIndicator,
     SaveAndCancelButtons
   },
   computed: mapState('configurations', ['configurationCustomField', 'configurationCustomFields']),
-  methods: mapActions('configurations', ['addConfigurationCustomField', 'loadConfigurationCustomFields'])
+  methods: {
+    ...mapActions('configurations', ['addConfigurationCustomField', 'loadConfigurationCustomFields']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class ConfigurationCustomFieldAddPage extends mixins(CheckEditAccess) {
-  private isSaving = false
-
   private customField: CustomTextField = new CustomTextField()
 
   // vuex definition for typescript check
   configurationCustomFields!: ConfigurationsState['configurationCustomFields']
   loadConfigurationCustomFields!: LoadConfigurationCustomFieldsAction
   addConfigurationCustomField!: AddConfigurationCustomFieldAction
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -149,7 +146,7 @@ export default class ConfigurationCustomFieldAddPage extends mixins(CheckEditAcc
       return
     }
     try {
-      this.isSaving = true
+      this.setLoading(true)
 
       await this.addConfigurationCustomField({
         configurationId: this.configurationId,
@@ -161,7 +158,7 @@ export default class ConfigurationCustomFieldAddPage extends mixins(CheckEditAcc
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to save custom field')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/configurations/_configurationId/locations.vue b/pages/configurations/_configurationId/locations.vue
index f3329ae50..031d2ccdf 100644
--- a/pages/configurations/_configurationId/locations.vue
+++ b/pages/configurations/_configurationId/locations.vue
@@ -34,10 +34,7 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-    />
-    <NuxtChild v-if="!isLoading" />
+    <NuxtChild />
   </div>
 </template>
 
@@ -46,41 +43,34 @@ import { Component, Vue } from 'nuxt-property-decorator'
 
 import { mapActions } from 'vuex'
 
-import {
-  LoadEpsgCodesAction,
-  LoadElevationDataAction
-} from '@/store/vocabulary'
+import { LoadEpsgCodesAction, LoadElevationDataAction } from '@/store/vocabulary'
 import {
   LoadDeviceMountActionsIncludingDeviceInformationAction,
   LoadLocationActionTimepointsAction
 } from '@/store/configurations'
 import { LoadAllContactsAction } from '@/store/contacts'
-
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 
 @Component({
-  components: {
-    ProgressIndicator
-  },
   methods: {
     ...mapActions('vocabulary', ['loadEpsgCodes', 'loadElevationData']),
     ...mapActions('contacts', ['loadAllContacts']),
-    ...mapActions('configurations', ['loadLocationActionTimepoints', 'loadDeviceMountActionsIncludingDeviceInformation'])
+    ...mapActions('configurations', ['loadLocationActionTimepoints', 'loadDeviceMountActionsIncludingDeviceInformation']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class ConfigurationLocations extends Vue {
-  private isLoading = false
-
   // vuex definition for typescript check
   loadEpsgCodes!: LoadEpsgCodesAction
   loadElevationData!: LoadElevationDataAction
   loadLocationActionTimepoints!: LoadLocationActionTimepointsAction
   loadDeviceMountActionsIncludingDeviceInformation!: LoadDeviceMountActionsIncludingDeviceInformationAction
   loadAllContacts!: LoadAllContactsAction
+  setLoading!: SetLoadingAction
 
   async fetch () {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await Promise.all([
         this.loadEpsgCodes(),
         this.loadElevationData(),
@@ -91,7 +81,7 @@ export default class ConfigurationLocations extends Vue {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to fetch locations')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/configurations/_configurationId/locations/index/dynamic-location-actions/_actionId/edit.vue b/pages/configurations/_configurationId/locations/index/dynamic-location-actions/_actionId/edit.vue
index 8c81a3820..38531f200 100644
--- a/pages/configurations/_configurationId/locations/index/dynamic-location-actions/_actionId/edit.vue
+++ b/pages/configurations/_configurationId/locations/index/dynamic-location-actions/_actionId/edit.vue
@@ -32,10 +32,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isInProgress"
-      :dark="isSaving"
-    />
     <v-card-actions>
       <v-card-title class="pl-0">
         Edit Dynamic Location
@@ -73,7 +69,7 @@ import { Component, Vue, mixins } from 'nuxt-property-decorator'
 import { mapActions, mapGetters, mapState } from 'vuex'
 import { DateTime } from 'luxon'
 import CheckEditAccess from '@/mixins/CheckEditAccess'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 import { DynamicLocationAction } from '@/models/DynamicLocationAction'
 import {
   ActiveDevicesWithPropertiesForDateGetter,
@@ -89,22 +85,21 @@ import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
 @Component({
   components: {
     SaveAndCancelButtons,
-    DynamicLocationWizard,
-    ProgressIndicator
+    DynamicLocationWizard
   },
   middleware: ['auth'],
   computed: {
     ...mapState('configurations', ['dynamicLocationAction', 'selectedLocationDate']),
+    ...mapState('progressindicator', ['isLoading']),
     ...mapGetters('configurations', ['activeDevicesWithPropertiesForDate'])
   },
   methods: {
-    ...mapActions('configurations', ['loadDynamicLocationAction', 'updateDynamicLocationAction', 'loadDeviceMountActionsIncludingDeviceInformation', 'loadLocationActionTimepoints'])
+    ...mapActions('configurations', ['loadDynamicLocationAction', 'updateDynamicLocationAction', 'loadDeviceMountActionsIncludingDeviceInformation', 'loadLocationActionTimepoints']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class DynamicLocationActionEdit extends mixins(CheckEditAccess) {
   private valueCopy: DynamicLocationAction|null = null
-  private isSaving: boolean = false
-  private isLoading: boolean = false
 
   // vuex definition for typescript check
   dynamicLocationAction!: ConfigurationsState['dynamicLocationAction']
@@ -114,7 +109,8 @@ export default class DynamicLocationActionEdit extends mixins(CheckEditAccess) {
   updateDynamicLocationAction!: UpdateDynamicLocationActionAction
   activeDevicesWithPropertiesForDate!: ActiveDevicesWithPropertiesForDateGetter
   loadLocationActionTimepoints!: LoadLocationActionTimepointsAction
-
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
   /**
    * route to which the user is redirected when he is not allowed to access the page
    *
@@ -139,7 +135,7 @@ export default class DynamicLocationActionEdit extends mixins(CheckEditAccess) {
 
   async created () {
     try {
-      this.isLoading = true
+      this.setLoading(true)
 
       await this.loadDynamicLocationAction(this.actionId)
       await this.loadDeviceMountActionsIncludingDeviceInformation(this.configurationId)
@@ -149,7 +145,7 @@ export default class DynamicLocationActionEdit extends mixins(CheckEditAccess) {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to load action')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
@@ -157,10 +153,6 @@ export default class DynamicLocationActionEdit extends mixins(CheckEditAccess) {
     return this.selectedLocationDate !== null ? this.selectedLocationDate : currentAsUtcDateSecondsAsZeros()
   }
 
-  get isInProgress (): boolean {
-    return this.isLoading || this.isSaving
-  }
-
   get actionId (): string {
     return this.$route.params.actionId
   }
@@ -191,7 +183,7 @@ export default class DynamicLocationActionEdit extends mixins(CheckEditAccess) {
     }
 
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.updateDynamicLocationAction({
         configurationId: this.configurationId,
         dynamicLocationAction: this.valueCopy
@@ -203,7 +195,7 @@ export default class DynamicLocationActionEdit extends mixins(CheckEditAccess) {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Save failed')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/configurations/_configurationId/locations/index/dynamic-location-actions/_actionId/index.vue b/pages/configurations/_configurationId/locations/index/dynamic-location-actions/_actionId/index.vue
index efb5f9dd0..193a68e80 100644
--- a/pages/configurations/_configurationId/locations/index/dynamic-location-actions/_actionId/index.vue
+++ b/pages/configurations/_configurationId/locations/index/dynamic-location-actions/_actionId/index.vue
@@ -32,9 +32,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-    />
     <DynamicLocationView
       v-if="dynamicLocationAction"
       :action="dynamicLocationAction"
@@ -48,6 +45,7 @@ permissions and limitations under the Licence.
 import { Component, Vue, Watch, InjectReactive } from 'nuxt-property-decorator'
 import * as VueRouter from 'vue-router'
 import { mapActions, mapState } from 'vuex'
+import { SetLoadingAction } from '@/store/progressindicator'
 import {
   ConfigurationsState,
   LoadDynamicLocationActionAction,
@@ -56,11 +54,10 @@ import {
   LoadDeviceMountActionsIncludingDeviceInformationAction
 } from '@/store/configurations'
 
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
 import DynamicLocationView from '@/components/configurations/dynamicLocation/DynamicLocationView.vue'
 
 @Component({
-  components: { DynamicLocationView, ProgressIndicator },
+  components: { DynamicLocationView },
   middleware: ['auth'],
   computed: {
     ...mapState('configurations',
@@ -80,15 +77,14 @@ import DynamicLocationView from '@/components/configurations/dynamicLocation/Dyn
         'setSelectedTimepointItem',
         'setSelectedLocationDate'
       ]
-    )
+    ),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class DynamicLocationActionView extends Vue {
   @InjectReactive()
     editable!: boolean
 
-  private isLoading = false
-
   // vuex definition for typescript check
   dynamicLocationAction!: ConfigurationsState['dynamicLocationAction']
   loadDynamicLocationAction!: LoadDynamicLocationActionAction
@@ -98,6 +94,7 @@ export default class DynamicLocationActionView extends Vue {
   setSelectedLocationDate!: SetSelectedLocationDateAction
   configurationLocationActionTimepoints!: ConfigurationsState['configurationLocationActionTimepoints']
   loadDeviceMountActionsIncludingDeviceInformation!: LoadDeviceMountActionsIncludingDeviceInformationAction
+  setLoading!: SetLoadingAction
 
   async fetch () {
     await this.loadLocationAction()
@@ -113,7 +110,7 @@ export default class DynamicLocationActionView extends Vue {
 
   async loadLocationAction () {
     try {
-      this.isLoading = true
+      this.setLoading(true)
 
       await Promise.all([
         this.loadDynamicLocationAction(this.actionId),
@@ -148,7 +145,7 @@ export default class DynamicLocationActionView extends Vue {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Loading failed')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/configurations/_configurationId/locations/index/dynamic-location-actions/new.vue b/pages/configurations/_configurationId/locations/index/dynamic-location-actions/new.vue
index f153bbc52..32c09a394 100644
--- a/pages/configurations/_configurationId/locations/index/dynamic-location-actions/new.vue
+++ b/pages/configurations/_configurationId/locations/index/dynamic-location-actions/new.vue
@@ -32,10 +32,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card-actions>
       <v-card-title class="pl-0">
         New Dynamic Location
@@ -72,7 +68,7 @@ import { Component, Vue, mixins } from 'nuxt-property-decorator'
 import { DateTime } from 'luxon'
 import { mapActions, mapGetters, mapState } from 'vuex'
 import CheckEditAccess from '@/mixins/CheckEditAccess'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import { DynamicLocationAction } from '@/models/DynamicLocationAction'
 import { VocabularyState } from '@/store/vocabulary'
 import { ContactsState } from '@/store/contacts'
@@ -85,7 +81,7 @@ import {
 import { currentAsUtcDateSecondsAsZeros } from '@/utils/dateHelper'
 import DynamicLocationWizard from '@/components/configurations/dynamicLocation/DynamicLocationWizard.vue'
 @Component({
-  components: { DynamicLocationWizard, ProgressIndicator },
+  components: { DynamicLocationWizard },
   middleware: ['auth'],
   computed: {
     ...mapState('vocabulary', ['epsgCodes', 'elevationData']),
@@ -94,12 +90,12 @@ import DynamicLocationWizard from '@/components/configurations/dynamicLocation/D
     ...mapState('configurations', ['selectedLocationDate'])
   },
   methods: {
-    ...mapActions('configurations', ['addDynamicLocationBeginAction', 'loadLocationActionTimepoints', 'loadDeviceMountActionsIncludingDeviceInformation'])
+    ...mapActions('configurations', ['addDynamicLocationBeginAction', 'loadLocationActionTimepoints', 'loadDeviceMountActionsIncludingDeviceInformation']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class DynamicLocationActionNew extends mixins(CheckEditAccess) {
   private beginAction: DynamicLocationAction = new DynamicLocationAction()
-  private isSaving: boolean = false
 
   // vuex definition for typescript check
   epsgCodes!: VocabularyState['epsgCodes']
@@ -109,6 +105,7 @@ export default class DynamicLocationActionNew extends mixins(CheckEditAccess) {
   loadLocationActionTimepoints!: LoadLocationActionTimepointsAction
   loadDeviceMountActionsIncludingDeviceInformation!: LoadDeviceMountActionsIncludingDeviceInformationAction
   addDynamicLocationBeginAction!: AddDynamicLocationBeginActionAction
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -155,7 +152,7 @@ export default class DynamicLocationActionNew extends mixins(CheckEditAccess) {
     }
 
     try {
-      this.isSaving = true
+      this.setLoading(true)
       const newId = await this.addDynamicLocationBeginAction({
         configurationId: this.configurationId,
         dynamicLocationAction: this.beginAction
@@ -166,7 +163,7 @@ export default class DynamicLocationActionNew extends mixins(CheckEditAccess) {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Save failed')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/configurations/_configurationId/locations/index/static-location-actions/_actionId/edit.vue b/pages/configurations/_configurationId/locations/index/static-location-actions/_actionId/edit.vue
index 47110f4ea..c9791a454 100644
--- a/pages/configurations/_configurationId/locations/index/static-location-actions/_actionId/edit.vue
+++ b/pages/configurations/_configurationId/locations/index/static-location-actions/_actionId/edit.vue
@@ -2,7 +2,7 @@
 Web client of the Sensor Management System software developed within the
 Helmholtz DataHub Initiative by GFZ and UFZ.
 
-Copyright (C) 2020 - 2022
+Copyright (C) 2020 - 2023
 - Nils Brinckmann (GFZ, nils.brinckmann@gfz-potsdam.de)
 - Marc Hanisch (GFZ, marc.hanisch@gfz-potsdam.de)
 - Tobias Kuhnert (UFZ, tobias.kuhnert@ufz.de)
@@ -32,10 +32,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isInProgress"
-      :dark="isSaving"
-    />
     <StaticLocationActionDataForm
       ref="editStaticLocationForm"
       v-model="valueCopy"
@@ -59,7 +55,7 @@ permissions and limitations under the Licence.
 import { Component, Vue, mixins } from 'nuxt-property-decorator'
 import { mapActions, mapState } from 'vuex'
 import CheckEditAccess from '@/mixins/CheckEditAccess'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 import { StaticLocationAction } from '@/models/StaticLocationAction'
 import {
   ConfigurationsState, LoadLocationActionTimepointsAction,
@@ -68,25 +64,27 @@ import {
 } from '@/store/configurations'
 import StaticLocationActionDataForm from '@/components/configurations/StaticLocationActionDataForm.vue'
 @Component({
-  components: { StaticLocationActionDataForm, ProgressIndicator },
+  components: { StaticLocationActionDataForm },
   middleware: ['auth'],
   computed: {
-    ...mapState('configurations', ['staticLocationAction'])
+    ...mapState('configurations', ['staticLocationAction']),
+    ...mapState('progressindicator', ['isLoading'])
   },
   methods: {
-    ...mapActions('configurations', ['loadStaticLocationAction', 'updateStaticLocationAction', 'loadLocationActionTimepoints'])
+    ...mapActions('configurations', ['loadStaticLocationAction', 'updateStaticLocationAction', 'loadLocationActionTimepoints']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class StaticLocationActionEdit extends mixins(CheckEditAccess) {
   private valueCopy: StaticLocationAction = new StaticLocationAction()
-  private isSaving: boolean = false
-  private isLoading: boolean = false
 
   // vuex definition for typescript check
   staticLocationAction!: ConfigurationsState['staticLocationAction']
   loadStaticLocationAction!: LoadStaticLocationActionAction
   updateStaticLocationAction!: UpdateStaticLocationActionAction
   loadLocationActionTimepoints!: LoadLocationActionTimepointsAction
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -112,7 +110,7 @@ export default class StaticLocationActionEdit extends mixins(CheckEditAccess) {
 
   async created () {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.loadStaticLocationAction(this.actionId)
       if (this.staticLocationAction) {
         this.valueCopy = StaticLocationAction.createFromObject(this.staticLocationAction)
@@ -120,7 +118,7 @@ export default class StaticLocationActionEdit extends mixins(CheckEditAccess) {
     } catch (_e) {
       this.$store.commit('snackbar/setError', 'Failed to load action')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
@@ -132,10 +130,6 @@ export default class StaticLocationActionEdit extends mixins(CheckEditAccess) {
     return this.$route.params.configurationId
   }
 
-  get isInProgress (): boolean {
-    return this.isLoading || this.isSaving
-  }
-
   closeEditStaticLocationForm (): void {
     this.$router.push('/configurations/' + this.configurationId + '/locations/static-location-actions/' + this.actionId)
   }
@@ -148,7 +142,7 @@ export default class StaticLocationActionEdit extends mixins(CheckEditAccess) {
       this.$store.commit('snackbar/setError', 'Please correct the errors')
       return
     }
-    this.isSaving = true
+    this.setLoading(true)
     try {
       await this.updateStaticLocationAction({
         configurationId: this.configurationId,
@@ -161,7 +155,7 @@ export default class StaticLocationActionEdit extends mixins(CheckEditAccess) {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Save failed')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/configurations/_configurationId/locations/index/static-location-actions/_actionId/index.vue b/pages/configurations/_configurationId/locations/index/static-location-actions/_actionId/index.vue
index 6cb26b1b3..b4a35589d 100644
--- a/pages/configurations/_configurationId/locations/index/static-location-actions/_actionId/index.vue
+++ b/pages/configurations/_configurationId/locations/index/static-location-actions/_actionId/index.vue
@@ -32,9 +32,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-    />
     <StaticLocationView
       v-if="staticLocationAction"
       :action="staticLocationAction"
@@ -56,14 +53,11 @@ import {
   SetSelectedTimepointItemAction
 } from '@/store/configurations'
 
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import StaticLocationView from '@/components/configurations/StaticLocationView.vue'
 
 @Component({
-  components: {
-    StaticLocationView,
-    ProgressIndicator
-  },
+  components: { StaticLocationView },
   middleware: ['auth'],
   computed: {
     ...mapState('configurations',
@@ -82,15 +76,14 @@ import StaticLocationView from '@/components/configurations/StaticLocationView.v
         'setSelectedTimepointItem',
         'setSelectedLocationDate'
       ]
-    )
+    ),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class StaticLocationActionView extends Vue {
   @InjectReactive()
     editable!: boolean
 
-  private isLoading = false
-
   // vuex definition for typescript check
   staticLocationAction!: ConfigurationsState['staticLocationAction']
   loadStaticLocationAction!: LoadStaticLocationActionAction
@@ -99,6 +92,7 @@ export default class StaticLocationActionView extends Vue {
   setSelectedTimepointItem!: SetSelectedTimepointItemAction
   setSelectedLocationDate!: SetSelectedLocationDateAction
   configurationLocationActionTimepoints!: ConfigurationsState['configurationLocationActionTimepoints']
+  setLoading!: SetLoadingAction
 
   async fetch () {
     await this.loadLocationAction()
@@ -114,7 +108,7 @@ export default class StaticLocationActionView extends Vue {
 
   async loadLocationAction () {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.loadStaticLocationAction(this.actionId)
 
       // set the date field and the date select to the correct values
@@ -146,7 +140,7 @@ export default class StaticLocationActionView extends Vue {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Loading failed')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/configurations/_configurationId/locations/index/static-location-actions/new.vue b/pages/configurations/_configurationId/locations/index/static-location-actions/new.vue
index 6bd8c8abe..5ab86fe7b 100644
--- a/pages/configurations/_configurationId/locations/index/static-location-actions/new.vue
+++ b/pages/configurations/_configurationId/locations/index/static-location-actions/new.vue
@@ -32,10 +32,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <StaticLocationActionDataForm
       ref="newStaticLocationForm"
       v-model="newAction"
@@ -68,19 +64,21 @@ import {
   LoadLocationActionTimepointsAction
 } from '@/store/configurations'
 import { currentAsUtcDateSecondsAsZeros } from '@/utils/dateHelper'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import StaticLocationActionDataForm from '@/components/configurations/StaticLocationActionDataForm.vue'
 @Component({
-  components: { StaticLocationActionDataForm, ProgressIndicator },
+  components: { StaticLocationActionDataForm },
   middleware: ['auth'],
-  methods: mapActions('configurations', ['addStaticLocationBeginAction', 'loadLocationActionTimepoints']),
+  methods: {
+    ...mapActions('configurations', ['addStaticLocationBeginAction', 'loadLocationActionTimepoints']),
+    ...mapActions('progressindicator', ['setLoading'])
+  },
   computed: {
     ...mapState('configurations', ['selectedLocationDate'])
   }
 })
 export default class StaticLocationActionNew extends mixins(CheckEditAccess) {
   private newAction: StaticLocationAction = new StaticLocationAction()
-  private isSaving: boolean = false
 
   // vuex definition for typescript check
   epsgCodes!: VocabularyState['epsgCodes']
@@ -89,6 +87,7 @@ export default class StaticLocationActionNew extends mixins(CheckEditAccess) {
   selectedLocationDate!: ConfigurationsState['selectedLocationDate']
   loadLocationActionTimepoints!: LoadLocationActionTimepointsAction
   addStaticLocationBeginAction!: AddStaticLocationBeginActionAction
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -134,7 +133,7 @@ export default class StaticLocationActionNew extends mixins(CheckEditAccess) {
       return
     }
     try {
-      this.isSaving = true
+      this.setLoading(true)
       const newId = await this.addStaticLocationBeginAction({
         configurationId: this.configurationId,
         staticLocationAction: this.newAction
@@ -145,7 +144,7 @@ export default class StaticLocationActionNew extends mixins(CheckEditAccess) {
     } catch (_e) {
       this.$store.commit('snackbar/setError', 'Save failed')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/configurations/_configurationId/parameters.vue b/pages/configurations/_configurationId/parameters.vue
index 6d526001b..c337fffa3 100644
--- a/pages/configurations/_configurationId/parameters.vue
+++ b/pages/configurations/_configurationId/parameters.vue
@@ -30,9 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-    />
     <NuxtChild />
   </div>
 </template>
@@ -46,27 +43,25 @@ import {
   LoadConfigurationParameterChangeActionsAction
 } from '@/store/configurations'
 import { LoadUnitsAction } from '@/store/vocabulary'
-
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 
 @Component({
-  components: { ProgressIndicator },
   methods: {
     ...mapActions('configurations', ['loadConfigurationParameters', 'loadConfigurationParameterChangeActions']),
-    ...mapActions('vocabulary', ['loadUnits'])
+    ...mapActions('vocabulary', ['loadUnits']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class ConfigurationParametersPage extends Vue {
-  private isLoading = false
-
   // vuex definition for typescript check
   loadConfigurationParameters!: LoadConfigurationParametersAction
   loadConfigurationParameterChangeActions!: LoadConfigurationParameterChangeActionsAction
   loadUnits!: LoadUnitsAction
+  setLoading!: SetLoadingAction
 
   async fetch (): Promise<void> {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await Promise.all([
         this.loadConfigurationParameters(this.configurationId),
         this.loadConfigurationParameterChangeActions(this.configurationId),
@@ -75,7 +70,7 @@ export default class ConfigurationParametersPage extends Vue {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to fetch parameters')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/configurations/_configurationId/parameters/_parameterId/copy.vue b/pages/configurations/_configurationId/parameters/_parameterId/copy.vue
index 51a3fe739..0c5e6a774 100644
--- a/pages/configurations/_configurationId/parameters/_parameterId/copy.vue
+++ b/pages/configurations/_configurationId/parameters/_parameterId/copy.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card
       flat
     >
@@ -99,8 +95,8 @@ import { Parameter } from '@/models/Parameter'
 import BaseList from '@/components/shared/BaseList.vue'
 import ParameterForm from '@/components/shared/ParameterForm.vue'
 import ParameterListItem from '@/components/shared/ParameterListItem.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 
 @Component({
   middleware: ['auth'],
@@ -108,7 +104,6 @@ import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
     BaseList,
     ParameterForm,
     ParameterListItem,
-    ProgressIndicator,
     SaveAndCancelButtons
   },
   computed: {
@@ -116,12 +111,12 @@ import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
     ...mapState('configurations', ['configurationParameter', 'configurationParameters'])
   },
   methods: {
-    ...mapActions('configurations', ['addConfigurationParameter', 'loadConfigurationParameters', 'loadConfigurationParameter'])
+    ...mapActions('configurations', ['addConfigurationParameter', 'loadConfigurationParameters', 'loadConfigurationParameter']),
+    ...mapActions('progressindicator', ['setLoading'])
   },
   scrollToTop: true
 })
 export default class ParametersCopyPage extends mixins(CheckEditAccess) {
-  private isSaving = false
   private valueCopy: Parameter = new Parameter()
 
   // vuex definition for typescript check
@@ -131,6 +126,7 @@ export default class ParametersCopyPage extends mixins(CheckEditAccess) {
   loadConfigurationParameters!: LoadConfigurationParametersAction
   addConfigurationParameter!: AddConfigurationParameterAction
   units!: VocabularyState['units']
+  setLoading!: SetLoadingAction
 
   mounted () {
     (this.$refs.parameterForm as ParameterForm).focus()
@@ -187,7 +183,7 @@ export default class ParametersCopyPage extends mixins(CheckEditAccess) {
     }
 
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.addConfigurationParameter({
         configurationId: this.configurationId,
         parameter: this.valueCopy
@@ -198,7 +194,7 @@ export default class ParametersCopyPage extends mixins(CheckEditAccess) {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to copy parameter')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/configurations/_configurationId/parameters/_parameterId/edit.vue b/pages/configurations/_configurationId/parameters/_parameterId/edit.vue
index 792c4e58b..6588d1b97 100644
--- a/pages/configurations/_configurationId/parameters/_parameterId/edit.vue
+++ b/pages/configurations/_configurationId/parameters/_parameterId/edit.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card
       flat
     >
@@ -100,8 +96,8 @@ import { Parameter } from '@/models/Parameter'
 import BaseList from '@/components/shared/BaseList.vue'
 import ParameterForm from '@/components/shared/ParameterForm.vue'
 import ParameterListItem from '@/components/shared/ParameterListItem.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 
 @Component({
   middleware: ['auth'],
@@ -109,7 +105,6 @@ import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
     BaseList,
     ParameterForm,
     ParameterListItem,
-    ProgressIndicator,
     SaveAndCancelButtons
   },
   computed: {
@@ -117,12 +112,12 @@ import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
     ...mapState('configurations', ['configurationParameter', 'configurationParameters'])
   },
   methods: {
-    ...mapActions('configurations', ['updateConfigurationParameter', 'loadConfigurationParameters', 'loadConfigurationParameter'])
+    ...mapActions('configurations', ['updateConfigurationParameter', 'loadConfigurationParameters', 'loadConfigurationParameter']),
+    ...mapActions('progressindicator', ['setLoading'])
   },
   scrollToTop: true
 })
 export default class ParametersEditPage extends mixins(CheckEditAccess) {
-  private isSaving = false
   private valueCopy: Parameter = new Parameter()
 
   // vuex definition for typescript check
@@ -132,6 +127,7 @@ export default class ParametersEditPage extends mixins(CheckEditAccess) {
   loadConfigurationParameters!: LoadConfigurationParametersAction
   updateConfigurationParameter!: UpdateConfigurationParameterAction
   units!: VocabularyState['units']
+  setLoading!: SetLoadingAction
 
   mounted () {
     (this.$refs.parameterForm as ParameterForm).focus()
@@ -185,7 +181,7 @@ export default class ParametersEditPage extends mixins(CheckEditAccess) {
       return
     }
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.updateConfigurationParameter({
         configurationId: this.configurationId,
         parameter: this.valueCopy
@@ -196,7 +192,7 @@ export default class ParametersEditPage extends mixins(CheckEditAccess) {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to save parameter')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/configurations/_configurationId/parameters/index.vue b/pages/configurations/_configurationId/parameters/index.vue
index a10dad27a..c61497473 100644
--- a/pages/configurations/_configurationId/parameters/index.vue
+++ b/pages/configurations/_configurationId/parameters/index.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isInProgress"
-      dark
-    />
     <v-card-actions
       v-if="editable"
     >
@@ -41,7 +37,7 @@ permissions and limitations under the Licence.
       <v-btn
         color="primary"
         small
-        :disabled="isFetching"
+        :disabled="isLoading"
         :to="'/configurations/' + configurationId + '/parameters/new'"
       >
         Add Parameter
@@ -112,7 +108,7 @@ permissions and limitations under the Licence.
       v-if="editable && parameterToDelete"
       v-model="showDeleteDialog"
       title="Delete Parameter"
-      :disabled="isInProgress"
+      :disabled="isLoading"
       @cancel="closeDialog"
       @delete="deleteAndCloseDialog"
     >
@@ -122,7 +118,7 @@ permissions and limitations under the Licence.
 </template>
 
 <script lang="ts">
-import { Component, Vue, InjectReactive, Prop } from 'nuxt-property-decorator'
+import { Component, Vue, InjectReactive } from 'nuxt-property-decorator'
 import { mapActions, mapState } from 'vuex'
 
 import {
@@ -144,7 +140,7 @@ import ExpandableText from '@/components/shared/ExpandableText.vue'
 import HintCard from '@/components/HintCard.vue'
 import ParameterListItem from '@/components/shared/ParameterListItem.vue'
 import ParameterValueTable from '@/components/shared/ParameterValueTable.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 
 @Component({
   components: {
@@ -155,30 +151,25 @@ import ProgressIndicator from '@/components/ProgressIndicator.vue'
     ExpandableText,
     HintCard,
     ParameterListItem,
-    ParameterValueTable,
-    ProgressIndicator
+    ParameterValueTable
   },
   computed: {
     ...mapState('vocabulary', ['units']),
-    ...mapState('configurations', ['configurationParameters', 'configurationParameterChangeActions'])
+    ...mapState('configurations', ['configurationParameters', 'configurationParameterChangeActions']),
+    ...mapState('progressindicator', ['isLoading'])
   },
   methods: {
-    ...mapActions('configurations', ['deleteConfigurationParameter', 'loadConfigurationParameters'])
+    ...mapActions('configurations', ['deleteConfigurationParameter', 'loadConfigurationParameters']),
+    ...mapActions('progressindicator', ['setLoading'])
   },
   scrollToTop: true
 })
-export default class ConfigurationPropertyShowPage extends Vue {
+export default class ConfigurationParameterShowPage extends Vue {
   @InjectReactive()
   private editable!: boolean
 
-  @Prop({
-    type: Boolean
-  })
-  private isFetching!: boolean
-
-  private isInProgress = false
-
   private showDeleteDialog = false
+
   private parameterToDelete: Parameter | null = null
 
   // vuex definition for typescript check
@@ -187,6 +178,8 @@ export default class ConfigurationPropertyShowPage extends Vue {
   configurationParameterChangeActions!: ConfigurationsState['configurationParameterChangeActions']
   loadConfigurationParameters!: LoadConfigurationParametersAction
   deleteConfigurationParameter!: DeleteConfigurationParameterAction
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
 
   get configurationId (): string {
     return this.$route.params.configurationId
@@ -207,7 +200,7 @@ export default class ConfigurationPropertyShowPage extends Vue {
       return
     }
     try {
-      this.isInProgress = true
+      this.setLoading(true)
 
       await this.deleteConfigurationParameter(this.parameterToDelete.id)
       this.loadConfigurationParameters(this.configurationId)
@@ -219,7 +212,7 @@ export default class ConfigurationPropertyShowPage extends Vue {
         this.$store.commit('snackbar/setError', 'Failed to delete parameter')
       }
     } finally {
-      this.isInProgress = false
+      this.setLoading(false)
       this.closeDialog()
     }
   }
diff --git a/pages/configurations/_configurationId/parameters/new.vue b/pages/configurations/_configurationId/parameters/new.vue
index 9131ab493..2b7105e8d 100644
--- a/pages/configurations/_configurationId/parameters/new.vue
+++ b/pages/configurations/_configurationId/parameters/new.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card
       flat
     >
@@ -99,7 +95,7 @@ import { Parameter } from '@/models/Parameter'
 import BaseList from '@/components/shared/BaseList.vue'
 import ParameterForm from '@/components/shared/ParameterForm.vue'
 import ParameterListItem from '@/components/shared/ParameterListItem.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
 
 @Component({
@@ -108,7 +104,6 @@ import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
     BaseList,
     ParameterForm,
     ParameterListItem,
-    ProgressIndicator,
     SaveAndCancelButtons
   },
   computed: {
@@ -116,12 +111,12 @@ import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
     ...mapState('configurations', ['configurationParameters', 'configurationParameterChangeActions'])
   },
   methods: {
-    ...mapActions('configurations', ['addConfigurationParameter', 'loadConfigurationParameters'])
+    ...mapActions('configurations', ['addConfigurationParameter', 'loadConfigurationParameters']),
+    ...mapActions('progressindicator', ['setLoading'])
   },
   scrollToTop: true
 })
 export default class ParametersAddPage extends mixins(CheckEditAccess) {
-  private isSaving = false
   private value: Parameter = new Parameter()
 
   // vuex definition for typescript check
@@ -130,6 +125,7 @@ export default class ParametersAddPage extends mixins(CheckEditAccess) {
   configurationParameterChangeActions!: ConfigurationsState['configurationParameterChangeActions']
   addConfigurationParameter!: AddConfigurationParameterAction
   loadConfigurationParameters!: LoadConfigurationParametersAction
+  setLoading!: SetLoadingAction
 
   mounted () {
     (this.$refs.parameterForm as ParameterForm).focus()
@@ -168,7 +164,7 @@ export default class ParametersAddPage extends mixins(CheckEditAccess) {
     }
 
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.addConfigurationParameter({
         configurationId: this.configurationId,
         parameter: this.value
@@ -179,7 +175,7 @@ export default class ParametersAddPage extends mixins(CheckEditAccess) {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to save parameter')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/configurations/_configurationId/platforms-and-devices/device-mount-actions/_actionId/edit.vue b/pages/configurations/_configurationId/platforms-and-devices/device-mount-actions/_actionId/edit.vue
index 6032dfb8c..1bd1c1f4f 100644
--- a/pages/configurations/_configurationId/platforms-and-devices/device-mount-actions/_actionId/edit.vue
+++ b/pages/configurations/_configurationId/platforms-and-devices/device-mount-actions/_actionId/edit.vue
@@ -34,9 +34,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-    />
     <v-card
       v-if="mountInfoAvailable"
       flat
@@ -104,13 +101,12 @@ import { ConfigurationsTreeNode } from '@/viewmodels/ConfigurationsTreeNode'
 import CheckEditAccess from '@/mixins/CheckEditAccess'
 
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import NavigationGuardDialog from '@/components/shared/NavigationGuardDialog.vue'
 import MountActionEditForm from '@/components/configurations/MountActionEditForm.vue'
 
 @Component({
   components: {
-    ProgressIndicator,
     NavigationGuardDialog,
     SaveAndCancelButtons,
     MountActionEditForm
@@ -125,7 +121,8 @@ import MountActionEditForm from '@/components/configurations/MountActionEditForm
   },
   methods: {
     ...mapActions('configurations', ['loadMountingConfigurationForDate', 'loadDeviceMountAction', 'updateDeviceMountAction']),
-    ...mapActions('contacts', ['loadAllContacts'])
+    ...mapActions('contacts', ['loadAllContacts']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class ConfigurationEditDeviceMountActionsPage extends mixins(CheckEditAccess) {
@@ -144,13 +141,16 @@ export default class ConfigurationEditDeviceMountActionsPage extends mixins(Chec
   private showNavigationWarning: boolean = false
   private to: VueRouter.RawLocation | null = null
   private mountActionHasChanged = false
-  private isLoading = false
+
   private tree: ConfigurationsTree = new ConfigurationsTree()
   private formIsValid: boolean = true
   private beginDateErrorMessage: string = ''
   private endDateErrorMessage: string = ''
   private availabilities: Availability[] = []
 
+  // vuex definition for typescript check
+  setLoading!: SetLoadingAction
+
   /**
    * route to which the user is redirected when he is not allowed to access the page
    *
@@ -178,7 +178,7 @@ export default class ConfigurationEditDeviceMountActionsPage extends mixins(Chec
   }
 
   async loadActionAndTree (): Promise<void> {
-    this.isLoading = true
+    this.setLoading(true)
     try {
       await Promise.all([
         this.loadDeviceMountAction(this.mountActionId),
@@ -193,7 +193,7 @@ export default class ConfigurationEditDeviceMountActionsPage extends mixins(Chec
     } catch (error) {
       this.$store.commit('snackbar/setError', 'Loading of mount action failed')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
@@ -246,7 +246,7 @@ export default class ConfigurationEditDeviceMountActionsPage extends mixins(Chec
     if (!this.formIsValid) {
       return
     }
-    this.isLoading = true
+    this.setLoading(true)
     try {
       await this.updateDeviceMountAction({ configurationId: this.configurationId, deviceMountAction: this.deviceMountAction })
       this.mountActionHasChanged = false
@@ -254,7 +254,7 @@ export default class ConfigurationEditDeviceMountActionsPage extends mixins(Chec
     } catch (_e) {
       this.$store.commit('snackbar/setError', 'Saving of mount action failed')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/configurations/_configurationId/platforms-and-devices/index.vue b/pages/configurations/_configurationId/platforms-and-devices/index.vue
index 44c7fcc13..cb76fefa7 100644
--- a/pages/configurations/_configurationId/platforms-and-devices/index.vue
+++ b/pages/configurations/_configurationId/platforms-and-devices/index.vue
@@ -33,9 +33,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-    />
     <v-card-actions>
       <v-spacer />
       <v-btn
@@ -155,7 +152,7 @@ import DateTimePicker from '@/components/DateTimePicker.vue'
 import ConfigurationsTreeView from '@/components/ConfigurationsTreeView.vue'
 import ConfigurationsTreeNodeDetail from '@/components/configurations/ConfigurationsTreeNodeDetail.vue'
 import ConfigurationsTreeTitle from '@/components/configurations/ConfigurationsTreeTitle.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import DeleteDialog from '@/components/shared/DeleteDialog.vue'
 
 @Component({
@@ -164,7 +161,6 @@ import DeleteDialog from '@/components/shared/DeleteDialog.vue'
     ConfigurationsTreeTitle,
     ConfigurationsTreeView,
     DateTimePicker,
-    ProgressIndicator,
     DeleteDialog
   },
   computed: {
@@ -175,16 +171,20 @@ import DeleteDialog from '@/components/shared/DeleteDialog.vue'
       'configurationMountingActionsForDate',
       'configurationDynamicLocationActions'
     ]),
-    ...mapGetters('configurations', ['mountActionDateItems'])
+    ...mapGetters('configurations', ['mountActionDateItems']),
+    ...mapState('progressindicator', ['isLoading'])
   },
-  methods: mapActions('configurations', [
-    'setSelectedDate',
-    'loadMountingActions',
-    'loadMountingConfigurationForDate',
-    'deleteDeviceMountAction',
-    'deletePlatformMountAction',
-    'loadConfigurationDynamicLocationActions'
-  ])
+  methods: {
+    ...mapActions('configurations', [
+      'setSelectedDate',
+      'loadMountingActions',
+      'loadMountingConfigurationForDate',
+      'deleteDeviceMountAction',
+      'deletePlatformMountAction',
+      'loadConfigurationDynamicLocationActions'
+    ]),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class ConfigurationShowPlatformsAndDevicesPage extends Vue {
   @InjectReactive()
@@ -193,7 +193,6 @@ export default class ConfigurationShowPlatformsAndDevicesPage extends Vue {
   private selectedNode: ConfigurationsTreeNode | null = null
   private tree: ConfigurationsTree = new ConfigurationsTree()
 
-  private isLoading: boolean = false
   private isDeleteDialogShown: boolean = false
 
   @ProvideReactive()
@@ -211,6 +210,7 @@ export default class ConfigurationShowPlatformsAndDevicesPage extends Vue {
   private deletePlatformMountAction!: DeletePlatformMountActionAction
   private configurationDynamicLocationActions!: ConfigurationsState['configurationDynamicLocationActions']
   private loadConfigurationDynamicLocationActions!: LoadConfigurationDynamicLocationActionsAction
+  setLoading!: SetLoadingAction
 
   created () {
     if (this.$route.query.date) {
@@ -220,7 +220,7 @@ export default class ConfigurationShowPlatformsAndDevicesPage extends Vue {
 
   async fetch (): Promise<void> {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await Promise.all([
         this.loadMountingActions(this.configurationId),
         this.loadMountingConfigurationForDate({ id: this.configurationId, timepoint: this.selectedDate }),
@@ -231,7 +231,7 @@ export default class ConfigurationShowPlatformsAndDevicesPage extends Vue {
     } catch (error) {
       this.$store.commit('snackbar/setError', 'Loading of configuration tree failed')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
@@ -285,7 +285,7 @@ export default class ConfigurationShowPlatformsAndDevicesPage extends Vue {
       return
     }
     const action = this.selectedNode.unpack()
-    this.isLoading = true
+    this.setLoading(true)
     try {
       if ('isDeviceMountAction' in action && action.isDeviceMountAction()) {
         await this.deleteDeviceMountAction(action.id)
@@ -297,7 +297,7 @@ export default class ConfigurationShowPlatformsAndDevicesPage extends Vue {
     } catch (err) {
       this.$store.commit('snackbar/setError', 'Mount Action could not be deleted.')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
       this.selectedNode = null
       this.isDeleteDialogShown = false
       this.$router.replace({
@@ -336,13 +336,13 @@ export default class ConfigurationShowPlatformsAndDevicesPage extends Vue {
   @Watch('selectedDate')
   async onPropertyChanged (_value: DateTime, _oldValue: DateTime) {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.loadMountingConfigurationForDate({ id: this.configurationId, timepoint: this.selectedDate })
       this.createTreeWithConfigAsRootNode()
     } catch (error) {
       this.$store.commit('snackbar/setError', 'Loading of configuration tree failed')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/configurations/_configurationId/platforms-and-devices/mount.vue b/pages/configurations/_configurationId/platforms-and-devices/mount.vue
index cb8d837d3..6fab0ead0 100644
--- a/pages/configurations/_configurationId/platforms-and-devices/mount.vue
+++ b/pages/configurations/_configurationId/platforms-and-devices/mount.vue
@@ -32,9 +32,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-    />
     <v-card-actions>
       <v-card-title class="pl-0">
         Mount devices or platforms
@@ -74,12 +71,11 @@ import { ContactsState, LoadAllContactsAction } from '@/store/contacts'
 
 import MountWizard from '@/components/configurations/MountWizard.vue'
 
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import NavigationGuardDialog from '@/components/shared/NavigationGuardDialog.vue'
 
 @Component({
   components: {
-    ProgressIndicator,
     MountWizard,
     NavigationGuardDialog
   },
@@ -89,7 +85,8 @@ import NavigationGuardDialog from '@/components/shared/NavigationGuardDialog.vue
   },
   methods: {
     ...mapActions('configurations', ['loadConfiguration']),
-    ...mapActions('contacts', ['loadAllContacts'])
+    ...mapActions('contacts', ['loadAllContacts']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class ConfigurationMountPlatformsAndDevicesPage extends mixins(CheckEditAccess) {
@@ -97,11 +94,11 @@ export default class ConfigurationMountPlatformsAndDevicesPage extends mixins(Ch
   loadConfiguration!: LoadConfigurationAction
   contacts!: ContactsState['contacts']
   loadAllContacts!: LoadAllContactsAction
+  setLoading!: SetLoadingAction
 
   private showNavigationWarning: boolean = false
   private to: RawLocation | null = null
   private hasSaved = false
-  private isLoading = false
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -128,14 +125,14 @@ export default class ConfigurationMountPlatformsAndDevicesPage extends mixins(Ch
   async created () {
     if (!this.configuration) {
       try {
-        this.isLoading = true
+        this.setLoading(true)
         await this.loadAllContacts()
         await this.loadConfiguration(this.configurationId)
         // })
       } catch (_e) {
         this.$store.commit('snackbar/setError', 'Failed to fetch configuration')
       } finally {
-        this.isLoading = false
+        this.setLoading(false)
       }
     }
   }
diff --git a/pages/configurations/_configurationId/platforms-and-devices/platform-mount-actions/_actionId/edit.vue b/pages/configurations/_configurationId/platforms-and-devices/platform-mount-actions/_actionId/edit.vue
index 1edc9aff6..9bd0198a4 100644
--- a/pages/configurations/_configurationId/platforms-and-devices/platform-mount-actions/_actionId/edit.vue
+++ b/pages/configurations/_configurationId/platforms-and-devices/platform-mount-actions/_actionId/edit.vue
@@ -34,9 +34,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-    />
     <v-card
       v-if="mountInfoAvailable"
       flat
@@ -101,13 +98,12 @@ import { PlatformNode } from '@/viewmodels/PlatformNode'
 import { Availability } from '@/models/Availability'
 
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import NavigationGuardDialog from '@/components/shared/NavigationGuardDialog.vue'
 import MountActionEditForm from '@/components/configurations/MountActionEditForm.vue'
 
 @Component({
   components: {
-    ProgressIndicator,
     NavigationGuardDialog,
     SaveAndCancelButtons,
     MountActionEditForm
@@ -119,7 +115,8 @@ import MountActionEditForm from '@/components/configurations/MountActionEditForm
   },
   methods: {
     ...mapActions('configurations', ['loadMountingConfigurationForDate', 'loadPlatformMountAction', 'updatePlatformMountAction']),
-    ...mapActions('contacts', ['loadAllContacts'])
+    ...mapActions('contacts', ['loadAllContacts']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class ConfigurationEditPlatformMountActionsPage extends mixins(CheckEditAccess) {
@@ -129,6 +126,7 @@ export default class ConfigurationEditPlatformMountActionsPage extends mixins(Ch
   loadMountingConfigurationForDate!: LoadMountingConfigurationForDateAction
   loadPlatformMountAction!: LoadPlatformMountActionAction
   updatePlatformMountAction!: UpdatePlatformMountActionAction
+  setLoading!: SetLoadingAction
 
   contacts!: ContactsState['contacts']
   loadAllContacts!: LoadAllContactsAction
@@ -137,7 +135,7 @@ export default class ConfigurationEditPlatformMountActionsPage extends mixins(Ch
   private showNavigationWarning: boolean = false
   private to: RawLocation | null = null
   private mountActionHasChanged = false
-  private isLoading = false
+
   private tree: ConfigurationsTree = new ConfigurationsTree()
   private formIsValid: boolean = false
   private beginDateErrorMessage: string = ''
@@ -167,7 +165,7 @@ export default class ConfigurationEditPlatformMountActionsPage extends mixins(Ch
   }
 
   async fetch () {
-    this.isLoading = true
+    this.setLoading(true)
     try {
       await Promise.all([
         this.loadPlatformMountAction(this.mountActionId),
@@ -183,7 +181,7 @@ export default class ConfigurationEditPlatformMountActionsPage extends mixins(Ch
     } catch (error) {
       this.$store.commit('snackbar/setError', 'Loading of mount action failed')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
@@ -231,7 +229,7 @@ export default class ConfigurationEditPlatformMountActionsPage extends mixins(Ch
     if (!this.formIsValid) {
       return
     }
-    this.isLoading = true
+    this.setLoading(true)
     try {
       await this.updatePlatformMountAction({ configurationId: this.configurationId, platformMountAction: this.platformMountAction })
       this.mountActionHasChanged = false
@@ -239,7 +237,7 @@ export default class ConfigurationEditPlatformMountActionsPage extends mixins(Ch
     } catch (_e) {
       this.$store.commit('snackbar/setError', 'Saving of mount action failed')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/configurations/_configurationId/platforms-and-devices/unmount.vue b/pages/configurations/_configurationId/platforms-and-devices/unmount.vue
index 1e210b0a1..cf2db07f7 100644
--- a/pages/configurations/_configurationId/platforms-and-devices/unmount.vue
+++ b/pages/configurations/_configurationId/platforms-and-devices/unmount.vue
@@ -33,10 +33,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isInProgress"
-      :dark="isSaving"
-    />
     <v-card-actions>
       <v-card-title class="pl-0">
         Unmount devices or platforms
@@ -153,17 +149,18 @@ import { ConfigurationMountAction } from '@/viewmodels/ConfigurationMountAction'
 
 import { MountActionValidator } from '@/utils/MountActionValidator'
 
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 import DateTimePicker from '@/components/DateTimePicker.vue'
 import ConfigurationsTreeView from '@/components/ConfigurationsTreeView.vue'
 import ConfigurationsSelectedItemUnmountForm from '@/components/ConfigurationsSelectedItemUnmountForm.vue'
 
 @Component({
-  components: { ProgressIndicator, ConfigurationsSelectedItemUnmountForm, ConfigurationsTreeView, DateTimePicker },
+  components: { ConfigurationsSelectedItemUnmountForm, ConfigurationsTreeView, DateTimePicker },
   middleware: ['auth'],
   computed: {
     ...mapState('configurations', ['configuration', 'configurationMountingActionsForDate', 'selectedDate', 'configurationDynamicLocationActions', 'deviceMountAction']),
     ...mapState('contacts', ['contacts']),
+    ...mapState('progressindicator', ['isLoading']),
     ...mapGetters('configurations', ['mountActionDateItems'])
   },
   methods: {
@@ -175,15 +172,14 @@ import ConfigurationsSelectedItemUnmountForm from '@/components/ConfigurationsSe
       'loadConfigurationDynamicLocationActions',
       'loadDeviceMountAction',
       'loadMountingActions'
-    ])
+    ]),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class ConfigurationUnMountPlatformsAndDevicesPage extends mixins(CheckEditAccess) {
   private selectedNode: ConfigurationsTreeNode | null = null
   private tree: ConfigurationsTree = ConfigurationsTree.fromArray([])
 
-  private isSaving: boolean = false
-  private isLoading: boolean = false
   private errorMessage: string = ''
   private nodeCanBeUnmounted: boolean = false
 
@@ -200,6 +196,8 @@ export default class ConfigurationUnMountPlatformsAndDevicesPage extends mixins(
   private deviceMountAction!: ConfigurationsState['deviceMountAction']
   private loadDeviceMountAction!: LoadDeviceMountActionAction
   private loadMountingActions!: LoadMountingActionsAction
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -225,7 +223,7 @@ export default class ConfigurationUnMountPlatformsAndDevicesPage extends mixins(
 
   async fetch () {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await Promise.all([
         this.loadConfigurationDynamicLocationActions(this.configurationId),
         this.loadMountingActions(this.configurationId),
@@ -234,7 +232,7 @@ export default class ConfigurationUnMountPlatformsAndDevicesPage extends mixins(
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to fetch resources')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
@@ -318,10 +316,6 @@ export default class ConfigurationUnMountPlatformsAndDevicesPage extends mixins(
     return this.$route.params.configurationId
   }
 
-  get isInProgress (): boolean {
-    return this.isLoading || this.isSaving
-  }
-
   get currentUserMail (): string | null {
     if (this.$auth.user && this.$auth.user.email) {
       return this.$auth.user.email as string
@@ -334,7 +328,7 @@ export default class ConfigurationUnMountPlatformsAndDevicesPage extends mixins(
       return
     }
     try {
-      this.isSaving = true
+      this.setLoading(true)
       if (this.selectedNode.isDevice()) {
         await this.unmountDevice((this.selectedNode as DeviceNode).unpack(), contact, description)
         this.$store.commit('snackbar/setSuccess', 'Save successful')
@@ -352,7 +346,7 @@ export default class ConfigurationUnMountPlatformsAndDevicesPage extends mixins(
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to unmount node')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 
@@ -396,13 +390,13 @@ export default class ConfigurationUnMountPlatformsAndDevicesPage extends mixins(
   @Watch('selectedDate')
   async onPropertyChanged (_value: DateTime, _oldValue: DateTime) {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.loadTree()
       this.onNodeSelect(this.selectedNode)
     } catch (error) {
       this.$store.commit('snackbar/setError', 'Loading of configuration tree failed')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/configurations/_configurationId/tsm-linking.vue b/pages/configurations/_configurationId/tsm-linking.vue
index b9bfe2436..8a2b4e64c 100644
--- a/pages/configurations/_configurationId/tsm-linking.vue
+++ b/pages/configurations/_configurationId/tsm-linking.vue
@@ -2,7 +2,7 @@
 Web client of the Sensor Management System software developed within the
 Helmholtz DataHub Initiative by GFZ and UFZ.
 
-Copyright (C) 2020 - 2022
+Copyright (C) 2020 - 2023
 - Nils Brinckmann (GFZ, nils.brinckmann@gfz-potsdam.de)
 - Marc Hanisch (GFZ, marc.hanisch@gfz-potsdam.de)
 - Tobias Kuhnert (UFZ, tobias.kuhnert@ufz.de)
@@ -35,39 +35,35 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-    />
-    <NuxtChild v-if="!isLoading" />
+    <NuxtChild />
   </div>
 </template>
 
 <script lang="ts">
 import { Component, Vue } from 'nuxt-property-decorator'
 import { mapActions } from 'vuex'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
 import { LoadConfigurationTsmLinkingsAction, LoadTsmEndpointsAction } from '@/store/tsmLinking'
 import { LoadLicensesAction } from '@/store/vocabulary'
+import { SetLoadingAction } from '@/store/progressindicator'
 
 @Component({
-  components: { ProgressIndicator },
   middleware: ['auth'],
   methods: {
     ...mapActions('tsmLinking', ['loadConfigurationTsmLinkings', 'loadTsmEndpoints']),
-    ...mapActions('vocabulary', ['loadLicenses'])
+    ...mapActions('vocabulary', ['loadLicenses']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class ConfigurationTsmLinking extends Vue {
-  private isLoading = false
-
   // vuex definition for typescript check
   loadConfigurationTsmLinkings!: LoadConfigurationTsmLinkingsAction
   loadTsmEndpoints!: LoadTsmEndpointsAction
   loadLicenses!: LoadLicensesAction
+  setLoading!: SetLoadingAction
 
   async created () {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await Promise.all([
         this.loadConfigurationTsmLinkings(this.configurationId),
         this.loadTsmEndpoints(),
@@ -76,7 +72,7 @@ export default class ConfigurationTsmLinking extends Vue {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to fetch linkings')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/configurations/_configurationId/tsm-linking/_linkingId.vue b/pages/configurations/_configurationId/tsm-linking/_linkingId.vue
index 3736a95e7..d6c29ca2b 100644
--- a/pages/configurations/_configurationId/tsm-linking/_linkingId.vue
+++ b/pages/configurations/_configurationId/tsm-linking/_linkingId.vue
@@ -30,9 +30,6 @@
  -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-    />
     <NuxtChild v-if="linking" />
   </div>
 </template>
@@ -44,35 +41,34 @@ import {
   ITsmLinkingState,
   LoadConfigurationTsmLinkingAction
 } from '@/store/tsmLinking'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 
 @Component({
-  components: { ProgressIndicator },
   computed: {
     ...mapState('tsmLinking', ['linking'])
   },
   methods: {
     ...mapActions('tsmLinking', [
       'loadConfigurationTsmLinking'
-    ])
+    ]),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class LinkingIdPage extends Vue {
-  private isLoading = false
-
   // vuex definition for typescript check
   linking!: ITsmLinkingState['linking']
   loadConfigurationTsmLinking!: LoadConfigurationTsmLinkingAction
+  setLoading!: SetLoadingAction
 
   async created () {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       this.$store.commit('tsmLinking/setLinking', null)
       await this.loadConfigurationTsmLinking(this.linkingId)
     } catch (_e) {
       this.$store.commit('snackbar/setError', 'Loading of linking failed')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/configurations/_configurationId/tsm-linking/_linkingId/edit.vue b/pages/configurations/_configurationId/tsm-linking/_linkingId/edit.vue
index 15fcaad3c..dbe35dd84 100644
--- a/pages/configurations/_configurationId/tsm-linking/_linkingId/edit.vue
+++ b/pages/configurations/_configurationId/tsm-linking/_linkingId/edit.vue
@@ -30,9 +30,6 @@
  -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-    />
     <v-card-actions>
       <v-card-title class="pl-0">
         Edit TSM-Linking
@@ -42,7 +39,7 @@
         v-if="$auth.loggedIn"
         save-btn-text="Apply"
         :to="redirectRoute"
-        :disabled="isSaving"
+        :disabled="isLoading"
         @save="save"
       />
     </v-card-actions>
@@ -71,7 +68,7 @@
         v-if="$auth.loggedIn"
         save-btn-text="Apply"
         :to="redirectRoute"
-        :disabled="isSaving"
+        :disabled="isLoading"
         @save="save"
       />
     </v-card-actions>
@@ -89,7 +86,7 @@ import { Component, Vue } from 'nuxt-property-decorator'
 import { mapActions, mapState } from 'vuex'
 import { RawLocation } from 'vue-router'
 
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 import TsmLinkingForm from '@/components/configurations/tsmLinking/TsmLinkingForm.vue'
 import { TsmDeviceMountPropertyCombination } from '@/utils/configurationInterfaces'
 import { TsmLinking } from '@/models/TsmLinking'
@@ -112,24 +109,24 @@ import { LoadLicensesAction } from '@/store/vocabulary'
     NavigationGuardDialog,
     TsmLinkingFormItemHeader,
     SaveAndCancelButtons,
-    TsmLinkingForm,
-    ProgressIndicator
+    TsmLinkingForm
   },
   middleware: ['auth'],
   computed: {
-    ...mapState('tsmLinking', ['linking'])
+    ...mapState('tsmLinking', ['linking']),
+    ...mapState('progressindicator', ['isLoading'])
   },
   methods: {
     ...mapActions('tsmLinking', [
       'loadConfigurationTsmLinkings', 'updateConfigurationTsmLinking', 'loadThingsForDatasource', 'loadDatastreamsForDatasourceAndThing', 'loadDatasources'
     ]),
-    ...mapActions('vocabulary', ['loadLicenses'])
+    ...mapActions('vocabulary', ['loadLicenses']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 
 })
 export default class TsmLinkingEditPage extends Vue {
   private editLinking: TsmLinking | null = null
-  private isSaving = false
   private hasSaved = false
   private to: RawLocation | null = null
   private showNavigationWarning = false
@@ -143,6 +140,8 @@ export default class TsmLinkingEditPage extends Vue {
   loadThingsForDatasource!: LoadThingsForDatasourceAction
   loadDatastreamsForDatasourceAndThing!: LoadDatastreamsForDatasourceAndThingAction
   loadLicenses!: LoadLicensesAction
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
 
   async created () {
     this.editLinking = TsmLinking.createFromObject(this.linking!)
@@ -184,7 +183,7 @@ export default class TsmLinkingEditPage extends Vue {
     }
 
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.updateConfigurationTsmLinking(this.editLinking)
       await this.loadConfigurationTsmLinkings(this.configurationId)
       this.$store.commit('tsmLinking/setLinking', null)
@@ -194,7 +193,7 @@ export default class TsmLinkingEditPage extends Vue {
     } catch (_e) {
       this.$store.commit('snackbar/setError', 'Save failed')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
       this.hasSaved = true
     }
   }
diff --git a/pages/configurations/_configurationId/tsm-linking/new.vue b/pages/configurations/_configurationId/tsm-linking/new.vue
index 4fbd9c4e3..c6ce2ac07 100644
--- a/pages/configurations/_configurationId/tsm-linking/new.vue
+++ b/pages/configurations/_configurationId/tsm-linking/new.vue
@@ -30,9 +30,6 @@
  -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-    />
     <NuxtChild
       v-if="configuration"
     />
@@ -45,36 +42,33 @@ import { mapState, mapActions } from 'vuex'
 
 import CheckEditAccess from '@/mixins/CheckEditAccess'
 
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import { LoadDeviceMountActionsIncludingDeviceInformationAction } from '@/store/configurations'
 
 @Component({
-  components: {
-    ProgressIndicator
-  },
   computed: {
     ...mapState('configurations', ['configuration'])
   },
   methods: {
-    ...mapActions('configurations', ['loadDeviceMountActionsIncludingDeviceInformation'])
+    ...mapActions('configurations', ['loadDeviceMountActionsIncludingDeviceInformation']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class ConfigurationNewTsmLinkingPageParent extends mixins(CheckEditAccess) {
-  private isLoading = false
-
   // vuex definition for typescript check
   loadDeviceMountActionsIncludingDeviceInformation!: LoadDeviceMountActionsIncludingDeviceInformationAction
+  setLoading!: SetLoadingAction
 
   async created () {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       // reset new linkings to remove cached data
       this.$store.commit('tsmLinking/setNewLinkings', [])
       await this.loadDeviceMountActionsIncludingDeviceInformation(this.configurationId)
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Loading device mount actions failed')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/configurations/_configurationId/tsm-linking/new/index.vue b/pages/configurations/_configurationId/tsm-linking/new/index.vue
index 8cfd93980..e03457fa2 100644
--- a/pages/configurations/_configurationId/tsm-linking/new/index.vue
+++ b/pages/configurations/_configurationId/tsm-linking/new/index.vue
@@ -30,9 +30,6 @@
  -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-    />
     <v-card-actions>
       <v-card-title class="pl-0">
         New TSM-Linking
@@ -40,7 +37,7 @@
       <v-spacer />
       <v-btn
         v-if="$auth.loggedIn"
-        :disabled="isSaving"
+        :disabled="isLoading"
         small
         text
         nuxt
@@ -57,7 +54,7 @@
         <v-btn
           block
           color="primary"
-          :disabled="isSaving"
+          :disabled="isLoading"
           @click="save()"
         >
           Submit
@@ -79,7 +76,7 @@ import { mapActions, mapState } from 'vuex'
 import { RawLocation } from 'vue-router'
 
 import TsmLinkingWizard from '@/components/configurations/tsmLinking/TsmLinkingWizard.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 import { TsmLinking } from '@/models/TsmLinking'
 import {
   AddConfigurationTsmLinkingAction,
@@ -91,19 +88,19 @@ import NavigationGuardDialog from '@/components/shared/NavigationGuardDialog.vue
 @Component({
   components: {
     NavigationGuardDialog,
-    ProgressIndicator,
     TsmLinkingWizard
   },
   middleware: ['auth'],
   computed: {
-    ...mapState('tsmLinking', ['newLinkings'])
+    ...mapState('tsmLinking', ['newLinkings']),
+    ...mapState('progressindicator', ['isLoading'])
   },
   methods: {
-    ...mapActions('tsmLinking', ['addConfigurationTsmLinking', 'loadConfigurationTsmLinkings'])
+    ...mapActions('tsmLinking', ['addConfigurationTsmLinking', 'loadConfigurationTsmLinkings']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class ConfigurationNewTsmLinkingPageChild extends Vue {
-  private isSaving = false
   private hasSaved = false
   private errorLinkings: TsmLinking[] = []
   private to: RawLocation | null = null
@@ -113,6 +110,8 @@ export default class ConfigurationNewTsmLinkingPageChild extends Vue {
   newLinkings!: ITsmLinkingState['newLinkings']
   addConfigurationTsmLinking!: AddConfigurationTsmLinkingAction
   loadConfigurationTsmLinkings!: LoadConfigurationTsmLinkingsAction
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
 
   get configurationId () {
     return this.$route.params.configurationId
@@ -132,7 +131,7 @@ export default class ConfigurationNewTsmLinkingPageChild extends Vue {
       return
     }
 
-    this.isSaving = true
+    this.setLoading(true)
 
     for (const newLinking of this.newLinkings) {
       try {
@@ -142,7 +141,7 @@ export default class ConfigurationNewTsmLinkingPageChild extends Vue {
       }
     }
 
-    this.isSaving = false
+    this.setLoading(false)
     this.hasSaved = true
 
     if (this.errorLinkings.length > 0) {
diff --git a/pages/configurations/index.vue b/pages/configurations/index.vue
index 8035e1175..d313b4b00 100644
--- a/pages/configurations/index.vue
+++ b/pages/configurations/index.vue
@@ -181,13 +181,7 @@ permissions and limitations under the Licence.
         </v-row>
       </v-tab-item>
     </v-tabs-items>
-    <v-progress-circular
-      v-if="loading"
-      class="progress-spinner"
-      color="primary"
-      indeterminate
-    />
-    <div v-if="configurations.length <=0 && !loading">
+    <div v-if="configurations.length <=0 && !isLoading">
       <p class="text-center">
         There are no configurations that match your search criteria.
       </p>
@@ -215,7 +209,7 @@ permissions and limitations under the Licence.
         >
           <v-pagination
             v-model="page"
-            :disabled="loading"
+            :disabled="isLoading"
             :length="totalPages"
             :total-visible="7"
             @input="runSearch"
@@ -267,7 +261,7 @@ permissions and limitations under the Licence.
       </BaseList>
       <v-pagination
         v-model="page"
-        :disabled="loading"
+        :disabled="isLoading"
         :length="totalPages"
         :total-visible="7"
         @input="runSearch"
@@ -277,7 +271,7 @@ permissions and limitations under the Licence.
       v-if="configurationToDelete"
       v-model="showDeleteDialog"
       title="Delete Configuration"
-      :disabled="loading"
+      :disabled="isLoading"
       @cancel="closeDialog"
       @delete="deleteAndCloseDialog"
     >
@@ -320,6 +314,7 @@ import PageSizeSelect from '@/components/shared/PageSizeSelect.vue'
 import PermissionGroupSearchSelect from '@/components/PermissionGroupSearchSelect.vue'
 import DotMenuActionArchive from '@/components/DotMenuActionArchive.vue'
 import DotMenuActionRestore from '@/components/DotMenuActionRestore.vue'
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 
 import { Configuration } from '@/models/Configuration'
 import { PermissionGroup } from '@/models/PermissionGroup'
@@ -350,19 +345,19 @@ import FoundEntries from '@/components/shared/FoundEntries.vue'
     ...mapState('appbar', ['activeTab']),
     ...mapGetters('configurations', ['pageSizes']),
     ...mapGetters('permissions', ['canDeleteEntity', 'canAccessEntity', 'permissionGroups', 'canArchiveEntity', 'canRestoreEntity']),
-    ...mapState('sites', ['sites'])
+    ...mapState('sites', ['sites']),
+    ...mapState('progressindicator', ['isLoading'])
   },
   methods: {
     ...mapActions('configurations', ['searchConfigurationsPaginated', 'loadConfiguration', 'setPageNumber', 'setPageSize', 'loadConfigurationsStates', 'deleteConfiguration', 'archiveConfiguration', 'restoreConfiguration', 'exportAsSensorML', 'replaceConfigurationInConfigurations', 'getSensorMLUrl', 'loadProjects']),
     ...mapActions('appbar', ['setTitle', 'setTabs', 'setActiveTab']),
     ...mapActions('permissions', ['loadPermissionGroups']),
-    ...mapActions('sites', ['searchSites'])
+    ...mapActions('sites', ['searchSites']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 // @ts-ignore
 export default class SearchConfigurationsPage extends Vue {
-  private loading = false
-
   private searchText: string | null = null
   private selectedConfigurationStates: string[] = []
   private selectedPermissionGroups: PermissionGroup[] = []
@@ -413,10 +408,12 @@ export default class SearchConfigurationsPage extends Vue {
   searchSites!: SearchSitesAction
   sites!: SitesState['sites']
   projects!: ConfigurationsState['projects']
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
 
   async created () {
     try {
-      this.loading = true
+      this.setLoading(true)
       this.initializeAppBar()
       await Promise.all([
         this.loadConfigurationsStates(),
@@ -429,7 +426,7 @@ export default class SearchConfigurationsPage extends Vue {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Loading of configurations failed')
     } finally {
-      this.loading = false
+      this.setLoading(false)
     }
   }
 
@@ -524,14 +521,14 @@ export default class SearchConfigurationsPage extends Vue {
 
   async runSearch () {
     try {
-      this.loading = true
+      this.setLoading(true)
       this.initUrlQueryParams()
       await this.searchConfigurationsPaginated(this.searchParams)
       this.setPageAndSizeInUrl()
     } catch (_error) {
       this.$store.commit('snackbar/setError', 'Loading of configurations failed')
     } finally {
-      this.loading = false
+      this.setLoading(false)
     }
   }
 
@@ -551,14 +548,14 @@ export default class SearchConfigurationsPage extends Vue {
       return
     }
     try {
-      this.loading = true
+      this.setLoading(true)
       await this.deleteConfiguration(this.configurationToDelete.id)
       this.runSearch()
       this.$store.commit('snackbar/setSuccess', 'Configuration deleted')
     } catch {
       this.$store.commit('snackbar/setError', 'Configuration could not be deleted')
     } finally {
-      this.loading = false
+      this.setLoading(false)
       this.closeDialog()
     }
   }
@@ -578,7 +575,7 @@ export default class SearchConfigurationsPage extends Vue {
       return
     }
     try {
-      this.loading = true
+      this.setLoading(true)
       await this.archiveConfiguration(this.configurationToArchive.id)
       await this.loadConfiguration(this.configurationToArchive.id)
       await this.replaceConfigurationInConfigurations(this.configuration!)
@@ -586,14 +583,14 @@ export default class SearchConfigurationsPage extends Vue {
     } catch (_error) {
       this.$store.commit('snackbar/setError', 'Configuration could not be archived')
     } finally {
-      this.loading = false
+      this.setLoading(false)
       this.closeArchiveDialog()
     }
   }
 
   async runRestoreConfiguration (configuration: Configuration) {
     if (configuration.id) {
-      this.loading = true
+      this.setLoading(true)
       try {
         await this.restoreConfiguration(configuration.id)
         await this.loadConfiguration(configuration.id)
@@ -602,7 +599,7 @@ export default class SearchConfigurationsPage extends Vue {
       } catch (error) {
         this.$store.commit('snackbar/setError', 'Configuration could not be restored')
       } finally {
-        this.loading = false
+        this.setLoading(false)
       }
     }
   }
diff --git a/pages/configurations/new.vue b/pages/configurations/new.vue
index fa1472c97..3a57e66d5 100644
--- a/pages/configurations/new.vue
+++ b/pages/configurations/new.vue
@@ -34,10 +34,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-      dark
-    />
     <v-card
       flat
     >
@@ -81,7 +77,7 @@ import { Configuration } from '@/models/Configuration'
 
 import { CreatePidAction, SaveConfigurationAction } from '@/store/configurations'
 
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
 import ConfigurationsBasicDataForm from '@/components/configurations/ConfigurationsBasicDataForm.vue'
 import NonModelOptionsForm, { NonModelOptions } from '@/components/shared/NonModelOptionsForm.vue'
@@ -90,18 +86,17 @@ import NonModelOptionsForm, { NonModelOptions } from '@/components/shared/NonMod
   components: {
     ConfigurationsBasicDataForm,
     NonModelOptionsForm,
-    SaveAndCancelButtons,
-    ProgressIndicator
+    SaveAndCancelButtons
   },
   middleware: ['auth'],
   methods: {
     ...mapActions('configurations', ['saveConfiguration', 'createPid']),
-    ...mapActions('appbar', ['setTitle', 'setTabs'])
+    ...mapActions('appbar', ['setTitle', 'setTabs']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class ConfigurationNewPage extends Vue {
   private configuration: Configuration = new Configuration()
-  private isLoading: boolean = false
   private createOptions: NonModelOptions = {
     persistentIdentifierShouldBeCreated: false
   }
@@ -112,6 +107,7 @@ export default class ConfigurationNewPage extends Vue {
   createPid!: CreatePidAction
   setTabs!: SetTabsAction
   setTitle!: SetTitleAction
+  setLoading!: SetLoadingAction
 
   created () {
     this.initializeAppBar()
@@ -124,7 +120,7 @@ export default class ConfigurationNewPage extends Vue {
     }
 
     try {
-      this.isLoading = true
+      this.setLoading(true)
       const savedConfiguration = await this.saveConfiguration(this.configuration)
       if (this.createOptions.persistentIdentifierShouldBeCreated) {
         savedConfiguration.persistentIdentifier = await this.createPid(savedConfiguration.id)
@@ -134,7 +130,7 @@ export default class ConfigurationNewPage extends Vue {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Save failed')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/contacts/_contactId.vue b/pages/contacts/_contactId.vue
index 4ed41c96e..283b3bad7 100644
--- a/pages/contacts/_contactId.vue
+++ b/pages/contacts/_contactId.vue
@@ -30,9 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-    />
     <NuxtChild
       v-if="contact"
     />
@@ -46,18 +43,18 @@ import { mapActions, mapGetters, mapState } from 'vuex'
 import { SetTitleAction } from '@/store/appbar'
 import { ContactsState, LoadContactAction } from '@/store/contacts'
 
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import { CanDeleteContactGetter, CanModifyContactGetter } from '@/store/permissions'
 
 @Component({
-  components: { ProgressIndicator },
   computed: {
     ...mapState('contacts', ['contact']),
     ...mapGetters('permissions', ['canModifyContact', 'canDeleteContact'])
   },
   methods: {
     ...mapActions('contacts', ['loadContact']),
-    ...mapActions('appbar', ['setTitle'])
+    ...mapActions('appbar', ['setTitle']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class ContactShowPage extends Vue {
@@ -67,14 +64,13 @@ export default class ContactShowPage extends Vue {
   @ProvideReactive()
     deletable: boolean = false
 
-  private isLoading: boolean = false
-
   // vuex definition for typescript check
   contact!: ContactsState['contact']
   loadContact!: LoadContactAction
   setTitle!: SetTitleAction
   canModifyContact!: CanModifyContactGetter
   canDeleteContact!: CanDeleteContactGetter
+  setLoading!: SetLoadingAction
 
   async created () {
     if (!this.$auth.loggedIn) {
@@ -85,14 +81,14 @@ export default class ContactShowPage extends Vue {
     }
 
     try {
-      this.isLoading = true
+      this.setLoading(true)
       this.initializeAppBar()
       await this.loadContact(this.contactId)
       this.updatePermissions(this.contact)
     } catch (_error) {
       this.$store.commit('snackbar/setError', 'Loading contact failed')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/contacts/_contactId/edit.vue b/pages/contacts/_contactId/edit.vue
index dfab0d7e6..5c5e1f4fd 100644
--- a/pages/contacts/_contactId/edit.vue
+++ b/pages/contacts/_contactId/edit.vue
@@ -30,9 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-    />
     <v-card flat>
       <v-card-actions>
         <v-spacer />
@@ -72,7 +69,7 @@ import { ContactsState, LoadContactAction, SaveContactAction } from '@/store/con
 import { Contact } from '@/models/Contact'
 
 import ContactBasicDataForm from '@/components/ContactBasicDataForm.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
 import CheckEditAccess from '@/mixins/CheckEditAccess'
 import { ErrorMessageDispatcher, sourceLowerCaseIncludes } from '@/utils/errorHelpers'
@@ -80,18 +77,17 @@ import { ErrorMessageDispatcher, sourceLowerCaseIncludes } from '@/utils/errorHe
 @Component({
   components: {
     SaveAndCancelButtons,
-    ContactBasicDataForm,
-    ProgressIndicator
+    ContactBasicDataForm
   },
   middleware: ['auth'],
   computed: mapState('contacts', ['contact']),
   methods: {
     ...mapActions('contacts', ['saveContact', 'loadContact']),
-    ...mapActions('appbar', ['setTitle'])
+    ...mapActions('appbar', ['setTitle']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class ContactEditPage extends mixins(CheckEditAccess) {
-  private isLoading: boolean = false
   private contactCopy: Contact = new Contact()
 
   // vuex definition for typescript check
@@ -99,6 +95,7 @@ export default class ContactEditPage extends mixins(CheckEditAccess) {
   saveContact!: SaveContactAction
   loadContact!: LoadContactAction
   setTitle!: SetTitleAction
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -138,7 +135,7 @@ export default class ContactEditPage extends mixins(CheckEditAccess) {
       return
     }
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.saveContact(this.contactCopy)
       this.loadContact(this.contactId)
       this.$router.push('/contacts/' + this.contactId)
@@ -157,7 +154,7 @@ export default class ContactEditPage extends mixins(CheckEditAccess) {
 
       this.$store.commit('snackbar/setError', msg)
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/contacts/_contactId/index.vue b/pages/contacts/_contactId/index.vue
index 2e8b7bfbf..89a58eb85 100644
--- a/pages/contacts/_contactId/index.vue
+++ b/pages/contacts/_contactId/index.vue
@@ -31,10 +31,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-      dark
-    />
     <v-card flat>
       <center>
         <v-alert
@@ -118,19 +114,22 @@ import DotMenu from '@/components/DotMenu.vue'
 import DotMenuActionDelete from '@/components/DotMenuActionDelete.vue'
 import ContactBasicData from '@/components/ContactBasicData.vue'
 import DeleteDialog from '@/components/shared/DeleteDialog.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 
 @Component({
   components: {
     DeleteDialog,
     ContactBasicData,
     DotMenuActionDelete,
-    DotMenu,
-    ProgressIndicator
+    DotMenu
+  },
+  computed: {
+    ...mapState('contacts', ['contact']),
+    ...mapState('progressindicator', ['isLoading'])
   },
-  computed: mapState('contacts', ['contact']),
   methods: {
-    ...mapActions('contacts', ['deleteContact', 'loadContact'])
+    ...mapActions('contacts', ['deleteContact', 'loadContact']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class ContactIndexPage extends Vue {
@@ -141,21 +140,21 @@ export default class ContactIndexPage extends Vue {
     deletable!: boolean
 
   showDeleteDialog = false
-  isLoading = false
 
   // vuex definition for typescript check
   contact!: ContactsState['contact']
   deleteContact!: DeleteContactAction
   loadContact!: LoadContactAction
+  setLoading!: SetLoadingAction
 
   async created () {
     // We load the contact in the upper context (that component that
     // include this page here is a child)
     // However, we can't be sure that this was already loaded
     // so we need to load it here in order to show the right name
-    this.isLoading = true
+    this.setLoading(true)
     await this.loadContact(this.contactId)
-    this.isLoading = false
+    this.setLoading(false)
   }
 
   get contactId () {
@@ -175,14 +174,14 @@ export default class ContactIndexPage extends Vue {
       return
     }
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.deleteContact(this.contact.id)
       this.$router.push('/contacts')
       this.$store.commit('snackbar/setSuccess', 'Contact deleted')
     } catch (_error) {
       this.$store.commit('snackbar/setError', 'Contact could not be deleted')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
       this.closeDialog()
     }
   }
diff --git a/pages/contacts/index.vue b/pages/contacts/index.vue
index 1fbbfff48..2ed3ef896 100644
--- a/pages/contacts/index.vue
+++ b/pages/contacts/index.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-      dark
-    />
     <v-row
       dense
     >
@@ -80,13 +76,7 @@ permissions and limitations under the Licence.
       </v-col>
     </v-row>
 
-    <v-progress-circular
-      v-if="loading"
-      class="progress-spinner"
-      color="primary"
-      indeterminate
-    />
-    <div v-if="contacts.length<=0 && !loading">
+    <div v-if="contacts.length<=0 && !isLoading">
       <p class="text-center">
         There are no contacts that match your search criteria.
       </p>
@@ -114,7 +104,7 @@ permissions and limitations under the Licence.
         >
           <v-pagination
             v-model="page"
-            :disabled="loading"
+            :disabled="isLoading"
             :length="totalPages"
             :total-visible="7"
             @input="runSearch"
@@ -167,7 +157,7 @@ permissions and limitations under the Licence.
       </BaseList>
       <v-pagination
         v-model="page"
-        :disabled="loading"
+        :disabled="isLoading"
         :length="totalPages"
         :total-visible="7"
         @input="runSearch"
@@ -177,7 +167,7 @@ permissions and limitations under the Licence.
       v-if="contactToDelete"
       v-model="showDeleteDialog"
       title="Delete Contact"
-      :disabled="loading"
+      :disabled="isLoading"
       @cancel="closeDialog"
       @delete="deleteAndCloseDialog"
     >
@@ -207,7 +197,7 @@ import DeleteDialog from '@/components/shared/DeleteDialog.vue'
 import PageSizeSelect from '@/components/shared/PageSizeSelect.vue'
 import BaseList from '@/components/shared/BaseList.vue'
 import ContactsListItem from '@/components/contacts/ContactsListItem.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 
 import { QueryParams } from '@/modelUtils/QueryParams'
 import { CanDeleteContactGetter } from '@/store/permissions'
@@ -220,21 +210,21 @@ import FoundEntries from '@/components/shared/FoundEntries.vue'
     BaseList,
     DeleteDialog,
     DotMenuActionDelete,
-    PageSizeSelect,
-    ProgressIndicator
+    PageSizeSelect
   },
   computed: {
     ...mapState('contacts', ['pageNumber', 'pageSize', 'totalPages', 'totalCount', 'contacts']),
+    ...mapState('progressindicator', ['isLoading']),
     ...mapGetters('contacts', ['pageSizes']),
     ...mapGetters('permissions', ['canDeleteContact'])
   },
   methods: {
     ...mapActions('contacts', ['searchContactsPaginated', 'setPageNumber', 'setPageSize', 'deleteContact']),
-    ...mapActions('appbar', ['setTitle', 'setTabs'])
+    ...mapActions('appbar', ['setTitle', 'setTabs']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class SearchContactsPage extends Vue {
-  private loading: boolean = false
   private searchText: string = ''
 
   private showDeleteDialog: boolean = false
@@ -252,6 +242,8 @@ export default class SearchContactsPage extends Vue {
   setTabs!: SetTabsAction
   setTitle!: SetTitleAction
   canDeleteContact!: CanDeleteContactGetter
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
 
   async created () {
     if (!this.$auth.loggedIn) {
@@ -262,14 +254,14 @@ export default class SearchContactsPage extends Vue {
     }
 
     try {
-      this.loading = true
+      this.setLoading(true)
       this.initializeAppBar()
       this.initSearchQueryParams()
       await this.runInitialSearch()
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Loading of contacts failed')
     } finally {
-      this.loading = false
+      this.setLoading(false)
     }
   }
 
@@ -325,14 +317,14 @@ export default class SearchContactsPage extends Vue {
 
   async runSearch () {
     try {
-      this.loading = true
+      this.setLoading(true)
       this.initUrlQueryParams()
       await this.searchContactsPaginated(this.searchText)
       this.setPageAndSizeInUrl()
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Loading of contacts failed')
     } finally {
-      this.loading = false
+      this.setLoading(false)
     }
   }
 
@@ -352,14 +344,14 @@ export default class SearchContactsPage extends Vue {
     }
 
     try {
-      this.loading = true
+      this.setLoading(true)
       await this.deleteContact(this.contactToDelete.id)
       this.runSearch()// to update the list
       this.$store.commit('snackbar/setSuccess', 'Contact deleted')
     } catch (_error) {
       this.$store.commit('snackbar/setError', 'Contact could not be deleted')
     } finally {
-      this.loading = false
+      this.setLoading(false)
       this.closeDialog()
     }
   }
diff --git a/pages/contacts/new.vue b/pages/contacts/new.vue
index 76d562c56..16df955ba 100644
--- a/pages/contacts/new.vue
+++ b/pages/contacts/new.vue
@@ -34,10 +34,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-      dark
-    />
     <v-card
       flat
     >
@@ -78,25 +74,25 @@ import { Rules } from '@/mixins/Rules'
 import { Contact } from '@/models/Contact'
 
 import ContactBasicDataForm from '@/components/ContactBasicDataForm.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
 import { ErrorMessageDispatcher, sourceLowerCaseIncludes } from '@/utils/errorHelpers'
 
 @Component({
   components: {
     SaveAndCancelButtons,
-    ContactBasicDataForm,
-    ProgressIndicator
+    ContactBasicDataForm
   },
   middleware: ['auth'],
   methods: {
     ...mapActions('contacts', ['saveContact']),
-    ...mapActions('appbar', ['setDefaults', 'setTitle'])
+    ...mapActions('appbar', ['setDefaults', 'setTitle']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class ContactNewPage extends mixins(Rules) {
   private contact: Contact = new Contact()
-  private isLoading: boolean = false
+
   private redirect: string = ''
 
   // vuex definition for typescript check
@@ -104,6 +100,7 @@ export default class ContactNewPage extends mixins(Rules) {
   saveContact!: SaveContactAction
   setDefaults!: SetDefaultsAction
   setTitle!: SetTitleAction
+  setLoading!: SetLoadingAction
 
   created () {
     /** this page accepts the query parameter 'redirect' as a html encoded URI
@@ -131,7 +128,7 @@ export default class ContactNewPage extends mixins(Rules) {
     const redirect: string | (string | null)[] = this.$route.query.redirect
 
     try {
-      this.isLoading = true
+      this.setLoading(true)
       const savedContact = await this.saveContact(this.contact)
       this.$store.commit('snackbar/setSuccess', 'Contact created')
       // sends the user back to the previous contact creation page
@@ -154,7 +151,7 @@ export default class ContactNewPage extends mixins(Rules) {
         .dispatch(e)
       this.$store.commit('snackbar/setError', msg)
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/devices/_deviceId.vue b/pages/devices/_deviceId.vue
index c4e7e6377..1651baee9 100644
--- a/pages/devices/_deviceId.vue
+++ b/pages/devices/_deviceId.vue
@@ -34,9 +34,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-    />
     <v-card flat>
       <center>
         <v-alert
@@ -72,12 +69,11 @@ import { SetTitleAction, SetTabsAction } from '@/store/appbar'
 import { DevicesState, LoadDeviceAction } from '@/store/devices'
 import { CanAccessEntityGetter, CanModifyEntityGetter, CanDeleteEntityGetter, CanArchiveEntityGetter, CanRestoreEntityGetter } from '@/store/permissions'
 
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import ModificationInfo from '@/components/ModificationInfo.vue'
 
 @Component({
   components: {
-    ProgressIndicator,
     ModificationInfo
   },
   computed: {
@@ -86,8 +82,8 @@ import ModificationInfo from '@/components/ModificationInfo.vue'
   },
   methods: {
     ...mapActions('devices', ['loadDevice']),
-    ...mapActions('appbar', ['setTitle', 'setTabs'])
-
+    ...mapActions('appbar', ['setTitle', 'setTabs']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class DevicePage extends Vue {
@@ -103,8 +99,6 @@ export default class DevicePage extends Vue {
   @ProvideReactive()
     restoreable: boolean = false
 
-  private isLoading: boolean = false
-
   // vuex definition for typescript check
   device!: DevicesState['device']
   loadDevice!: LoadDeviceAction
@@ -115,6 +109,7 @@ export default class DevicePage extends Vue {
   canRestoreEntity!: CanRestoreEntityGetter
   setTabs!: SetTabsAction
   setTitle!: SetTitleAction
+  setLoading!: SetLoadingAction
 
   mounted () {
     this.initializeAppBar()
@@ -122,7 +117,7 @@ export default class DevicePage extends Vue {
 
   async fetch (): Promise<void> {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.loadDevice({
         deviceId: this.deviceId,
         includeContacts: false,
@@ -147,7 +142,7 @@ export default class DevicePage extends Vue {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Loading of device failed')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/devices/_deviceId/actions.vue b/pages/devices/_deviceId/actions.vue
index 9602494e4..5c3791299 100644
--- a/pages/devices/_deviceId/actions.vue
+++ b/pages/devices/_deviceId/actions.vue
@@ -30,9 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-    />
     <NuxtChild />
   </div>
 </template>
@@ -43,26 +40,27 @@ import { mapActions } from 'vuex'
 
 import { LoadAllDeviceActionsAction } from '@/store/devices'
 
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 
 @Component({
-  components: { ProgressIndicator },
-  methods: mapActions('devices', ['loadAllDeviceActions'])
+  methods: {
+    ...mapActions('devices', ['loadAllDeviceActions']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class DeviceActionsPage extends Vue {
-  private isLoading = false
-
   // vuex definition for typescript check
   loadAllDeviceActions!: LoadAllDeviceActionsAction
+  setLoading!: SetLoadingAction
 
   async created () {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.loadAllDeviceActions(this.deviceId)
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to fetch actions')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/devices/_deviceId/actions/device-calibration-actions/_actionId/edit.vue b/pages/devices/_deviceId/actions/device-calibration-actions/_actionId/edit.vue
index 1709e8d83..2cd39c697 100644
--- a/pages/devices/_deviceId/actions/device-calibration-actions/_actionId/edit.vue
+++ b/pages/devices/_deviceId/actions/device-calibration-actions/_actionId/edit.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isInProgress"
-      :dark="isSaving"
-    />
     <v-select
       value="Device Calibration"
       :items="['Device Calibration']"
@@ -90,23 +86,26 @@ import { DeviceCalibrationAction } from '@/models/DeviceCalibrationAction'
 
 import DeviceCalibrationActionForm from '@/components/actions/DeviceCalibrationActionForm.vue'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 
 @Component({
   components: {
-    ProgressIndicator,
     SaveAndCancelButtons,
     DeviceCalibrationActionForm
   },
   scrollToTop: true,
   middleware: ['auth'],
-  computed: mapState('devices', ['deviceCalibrationAction', 'deviceAttachments', 'deviceMeasuredQuantities']),
-  methods: mapActions('devices', ['loadDeviceCalibrationAction', 'loadAllDeviceActions', 'loadDeviceAttachments', 'loadDeviceMeasuredQuantities', 'updateDeviceCalibrationAction'])
+  computed: {
+    ...mapState('devices', ['deviceCalibrationAction', 'deviceAttachments', 'deviceMeasuredQuantities']),
+    ...mapState('progressindicator', ['isLoading'])
+  },
+  methods: {
+    ...mapActions('devices', ['loadDeviceCalibrationAction', 'loadAllDeviceActions', 'loadDeviceAttachments', 'loadDeviceMeasuredQuantities', 'updateDeviceCalibrationAction']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class DeviceCalibrationActionEditPage extends mixins(CheckEditAccess) {
   private action: DeviceCalibrationAction = new DeviceCalibrationAction()
-  private isSaving = false
-  private isLoading = false
 
   // vuex definition for typescript check
   deviceCalibrationAction!: DevicesState['deviceCalibrationAction']
@@ -117,6 +116,8 @@ export default class DeviceCalibrationActionEditPage extends mixins(CheckEditAcc
   loadDeviceMeasuredQuantities!: LoadDeviceMeasuredQuantitiesAction
   updateDeviceCalibrationAction!: UpdateDeviceCalibrationAction
   loadAllDeviceActions!: LoadAllDeviceActionsAction
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -142,7 +143,7 @@ export default class DeviceCalibrationActionEditPage extends mixins(CheckEditAcc
 
   async fetch (): Promise<void> {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await Promise.all([
         this.loadDeviceCalibrationAction(this.actionId),
         this.loadDeviceAttachments(this.deviceId),
@@ -154,7 +155,7 @@ export default class DeviceCalibrationActionEditPage extends mixins(CheckEditAcc
     } catch {
       this.$store.commit('snackbar/setError', 'Failed to fetch action')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
@@ -166,10 +167,6 @@ export default class DeviceCalibrationActionEditPage extends mixins(CheckEditAcc
     return this.$route.params.actionId
   }
 
-  get isInProgress (): boolean {
-    return this.isLoading || this.isSaving
-  }
-
   async save () {
     if (!(this.$refs.deviceCalibrationActionForm as Vue & { isValid: () => boolean }).isValid()) {
       this.$store.commit('snackbar/setError', 'Please correct the errors')
@@ -177,7 +174,7 @@ export default class DeviceCalibrationActionEditPage extends mixins(CheckEditAcc
     }
 
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.updateDeviceCalibrationAction({
         deviceId: this.deviceId,
         calibrationAction: this.action
@@ -187,7 +184,7 @@ export default class DeviceCalibrationActionEditPage extends mixins(CheckEditAcc
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to save the action')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/devices/_deviceId/actions/generic-device-actions/_actionId/edit.vue b/pages/devices/_deviceId/actions/generic-device-actions/_actionId/edit.vue
index 435f5c0a4..8c9e4680c 100644
--- a/pages/devices/_deviceId/actions/generic-device-actions/_actionId/edit.vue
+++ b/pages/devices/_deviceId/actions/generic-device-actions/_actionId/edit.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isInProgress"
-      :dark="isSaving"
-    />
     <!-- just to be consistent with the new mask, we show the selected action type as an disabled v-select here -->
     <v-select
       :value="action.actionTypeName"
@@ -88,24 +84,27 @@ import {
 import { GenericAction } from '@/models/GenericAction'
 
 import GenericActionForm from '@/components/actions/GenericActionForm.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
 
 @Component({
   components: {
     SaveAndCancelButtons,
-    ProgressIndicator,
     GenericActionForm
   },
   scrollToTop: true,
   middleware: ['auth'],
-  computed: mapState('devices', ['deviceGenericAction', 'deviceAttachments']),
-  methods: mapActions('devices', ['loadDeviceGenericAction', 'loadAllDeviceActions', 'loadDeviceAttachments', 'updateDeviceGenericAction'])
+  computed: {
+    ...mapState('devices', ['deviceGenericAction', 'deviceAttachments']),
+    ...mapState('progressindicator', ['isLoading'])
+  },
+  methods: {
+    ...mapActions('devices', ['loadDeviceGenericAction', 'loadAllDeviceActions', 'loadDeviceAttachments', 'updateDeviceGenericAction']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class GenericDeviceActionEditPage extends mixins(CheckEditAccess) {
   private action: GenericAction = new GenericAction()
-  private isSaving = false
-  private isLoading = false
 
   // vuex definition for typescript check
   deviceGenericAction!: DevicesState['deviceGenericAction']
@@ -114,6 +113,8 @@ export default class GenericDeviceActionEditPage extends mixins(CheckEditAccess)
   loadDeviceAttachments!: LoadDeviceAttachmentsAction
   updateDeviceGenericAction!: UpdateDeviceGenericAction
   loadAllDeviceActions!: LoadAllDeviceActionsAction
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -139,7 +140,7 @@ export default class GenericDeviceActionEditPage extends mixins(CheckEditAccess)
 
   async fetch (): Promise<void> {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await Promise.all([
         this.loadDeviceGenericAction(this.actionId),
         this.loadDeviceAttachments(this.deviceId)
@@ -150,7 +151,7 @@ export default class GenericDeviceActionEditPage extends mixins(CheckEditAccess)
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to fetch action')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
@@ -162,17 +163,13 @@ export default class GenericDeviceActionEditPage extends mixins(CheckEditAccess)
     return this.$route.params.actionId
   }
 
-  get isInProgress (): boolean {
-    return this.isLoading || this.isSaving
-  }
-
   async save () {
     if (!(this.$refs.genericDeviceActionForm as Vue & { isValid: () => boolean }).isValid()) {
       this.$store.commit('snackbar/setError', 'Please correct the errors')
       return
     }
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.updateDeviceGenericAction({
         deviceId: this.deviceId,
         genericAction: this.action
@@ -183,7 +180,7 @@ export default class GenericDeviceActionEditPage extends mixins(CheckEditAccess)
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to save the action')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/devices/_deviceId/actions/index.vue b/pages/devices/_deviceId/actions/index.vue
index 2b73ff18a..d6ed8c5d3 100644
--- a/pages/devices/_deviceId/actions/index.vue
+++ b/pages/devices/_deviceId/actions/index.vue
@@ -31,10 +31,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card-actions>
       <v-spacer />
       <v-btn
@@ -167,7 +163,7 @@ permissions and limitations under the Licence.
       v-if="actionToDelete"
       v-model="showDeleteDialog"
       title="Delete Action"
-      :disabled="isSaving"
+      :disabled="isLoading"
       @cancel="closeDialog"
       @delete="deleteAndCloseDialog"
     >
@@ -216,7 +212,7 @@ import DownloadDialog from '@/components/shared/DownloadDialog.vue'
 import GenericActionCard from '@/components/actions/GenericActionCard.vue'
 import HintCard from '@/components/HintCard.vue'
 import ParameterChangeActionCard from '@/components/actions/ParameterChangeActionCard.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 import SoftwareUpdateActionCard from '@/components/actions/SoftwareUpdateActionCard.vue'
 
 @Component({
@@ -231,27 +227,29 @@ import SoftwareUpdateActionCard from '@/components/actions/SoftwareUpdateActionC
     GenericActionCard,
     HintCard,
     ParameterChangeActionCard,
-    ProgressIndicator,
     SoftwareUpdateActionCard
   },
   computed: {
     ...mapGetters('devices', ['actions']),
-    ...mapState('devices', ['device'])
+    ...mapState('devices', ['device']),
+    ...mapState('progressindicator', ['isLoading'])
   },
-  methods: mapActions('devices', [
-    'deleteDeviceSoftwareUpdateAction',
-    'deleteDeviceGenericAction',
-    'deleteDeviceCalibrationAction',
-    'deleteDeviceParameterChangeAction',
-    'loadAllDeviceActions',
-    'downloadAttachment'
-  ])
+  methods: {
+    ...mapActions('devices', [
+      'deleteDeviceSoftwareUpdateAction',
+      'deleteDeviceGenericAction',
+      'deleteDeviceCalibrationAction',
+      'deleteDeviceParameterChangeAction',
+      'loadAllDeviceActions',
+      'downloadAttachment'
+    ]),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class DeviceActionsShowPage extends Vue {
   @InjectReactive()
     editable!: boolean
 
-  private isSaving: boolean = false
   private genericActionToDelete: GenericAction | null = null
   private softwareUpdateActionToDelete: SoftwareUpdateAction | null = null
   private calibrationActionToDelete: DeviceCalibrationAction | null = null
@@ -270,6 +268,8 @@ export default class DeviceActionsShowPage extends Vue {
   deleteDeviceCalibrationAction!: DeleteDeviceCalibrationAction
   deleteDeviceParameterChangeAction!: DeleteDeviceParameterChangeActionAction
   downloadAttachment!: DownloadAttachmentAction
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
 
   get deviceId (): string {
     return this.$route.params.deviceId
@@ -366,14 +366,14 @@ export default class DeviceActionsShowPage extends Vue {
     }
 
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.deleteDeviceGenericAction(this.genericActionToDelete.id)
       this.loadAllDeviceActions(this.deviceId)
       this.$store.commit('snackbar/setSuccess', 'Generic action deleted')
     } catch (_error) {
       this.$store.commit('snackbar/setError', 'Generic action could not be deleted')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 
@@ -382,14 +382,14 @@ export default class DeviceActionsShowPage extends Vue {
       return
     }
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.deleteDeviceSoftwareUpdateAction(this.softwareUpdateActionToDelete.id)
       this.loadAllDeviceActions(this.deviceId)
       this.$store.commit('snackbar/setSuccess', 'Software update action deleted')
     } catch (_error) {
       this.$store.commit('snackbar/setError', 'Software update action could not be deleted')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 
@@ -399,14 +399,14 @@ export default class DeviceActionsShowPage extends Vue {
     }
 
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.deleteDeviceCalibrationAction(this.calibrationActionToDelete.id)
       this.loadAllDeviceActions(this.deviceId)
       this.$store.commit('snackbar/setSuccess', 'Calibration action deleted')
     } catch (_error) {
       this.$store.commit('snackbar/setError', 'Calibration action could not be deleted')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 
@@ -416,14 +416,14 @@ export default class DeviceActionsShowPage extends Vue {
     }
 
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.deleteDeviceParameterChangeAction(this.parameterChangeActionToDelete.id)
       this.loadAllDeviceActions(this.deviceId)
       this.$store.commit('snackbar/setSuccess', 'Parameter value change action deleted')
     } catch (_error) {
       this.$store.commit('snackbar/setError', 'Parameter value change action could not be deleted')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/devices/_deviceId/actions/new.vue b/pages/devices/_deviceId/actions/new.vue
index ff1fca9ab..cc9f8bc5d 100644
--- a/pages/devices/_deviceId/actions/new.vue
+++ b/pages/devices/_deviceId/actions/new.vue
@@ -30,9 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-    />
     <v-card
       flat
     >
@@ -72,8 +69,7 @@ import { Component, mixins } from 'nuxt-property-decorator'
 import { mapActions, mapGetters, mapState } from 'vuex'
 
 import ActionTypeDialog from '@/components/shared/ActionTypeDialog.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
-
+import { SetLoadingAction } from '@/store/progressindicator'
 import CheckEditAccess from '@/mixins/CheckEditAccess'
 import { ActionType } from '@/models/ActionType'
 
@@ -98,7 +94,7 @@ const KIND_OF_ACTION_TYPE_GENERIC_DEVICE_ACTION = 'generic_device_action'
 const KIND_OF_ACTION_TYPE_PARAMETER_CHANGE_ACTION = 'parameter_change_action'
 
 @Component({
-  components: { ActionTypeDialog, ProgressIndicator },
+  components: { ActionTypeDialog },
   middleware: ['auth'],
   computed: {
     ...mapGetters('vocabulary', ['deviceActionTypeItems']),
@@ -106,11 +102,11 @@ const KIND_OF_ACTION_TYPE_PARAMETER_CHANGE_ACTION = 'parameter_change_action'
   },
   methods: {
     ...mapActions('vocabulary', ['loadDeviceGenericActionTypes']),
-    ...mapActions('devices', ['loadDeviceAttachments', 'setChosenKindOfDeviceAction', 'loadDeviceMeasuredQuantities', 'loadDeviceParameters'])
+    ...mapActions('devices', ['loadDeviceAttachments', 'setChosenKindOfDeviceAction', 'loadDeviceMeasuredQuantities', 'loadDeviceParameters']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class ActionAddPage extends mixins(CheckEditAccess) {
-  private isLoading: boolean = false
   private showNewActionTypeDialog = false
 
   // vuex definition for typescript check
@@ -121,6 +117,7 @@ export default class ActionAddPage extends mixins(CheckEditAccess) {
   loadDeviceMeasuredQuantities!: LoadDeviceMeasuredQuantitiesAction
   loadDeviceParameters!: LoadDeviceParametersAction
   setChosenKindOfDeviceAction!: SetChosenKindOfDeviceActionAction
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -146,7 +143,7 @@ export default class ActionAddPage extends mixins(CheckEditAccess) {
 
   async fetch (): Promise<void> {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       this.chosenKindOfAction = null
       await Promise.all([
         this.loadDeviceGenericActionTypes(),
@@ -157,7 +154,7 @@ export default class ActionAddPage extends mixins(CheckEditAccess) {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to fetch action resources')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/devices/_deviceId/actions/new/device-calibration-actions.vue b/pages/devices/_deviceId/actions/new/device-calibration-actions.vue
index a12f7e90c..e41424f8c 100644
--- a/pages/devices/_deviceId/actions/new/device-calibration-actions.vue
+++ b/pages/devices/_deviceId/actions/new/device-calibration-actions.vue
@@ -31,10 +31,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card-actions>
       <v-spacer />
       <SaveAndCancelButtons
@@ -78,22 +74,23 @@ import {
 import { DeviceCalibrationAction } from '@/models/DeviceCalibrationAction'
 
 import DeviceCalibrationActionForm from '@/components/actions/DeviceCalibrationActionForm.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
 
 @Component({
   middleware: ['auth'],
   components: {
     SaveAndCancelButtons,
-    ProgressIndicator,
     DeviceCalibrationActionForm
   },
   computed: mapState('devices', ['deviceAttachments', 'chosenKindOfDeviceAction', 'deviceMeasuredQuantities']),
-  methods: mapActions('devices', ['addDeviceCalibrationAction', 'loadAllDeviceActions'])
+  methods: {
+    ...mapActions('devices', ['addDeviceCalibrationAction', 'loadAllDeviceActions']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class NewDeviceCalibrationAction extends mixins(CheckEditAccess) {
   private deviceCalibrationAction: DeviceCalibrationAction = new DeviceCalibrationAction()
-  private isSaving: boolean = false
 
   // vuex definition for typescript check
   deviceAttachments!: DevicesState['deviceAttachments']
@@ -101,6 +98,7 @@ export default class NewDeviceCalibrationAction extends mixins(CheckEditAccess)
   chosenKindOfDeviceAction!: DevicesState['chosenKindOfDeviceAction']
   addDeviceCalibrationAction!: AddDeviceCalibrationAction
   loadAllDeviceActions!: LoadAllDeviceActionsAction
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -144,7 +142,7 @@ export default class NewDeviceCalibrationAction extends mixins(CheckEditAccess)
     }
 
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.addDeviceCalibrationAction({
         deviceId: this.deviceId,
         calibrationAction: this.deviceCalibrationAction
@@ -155,7 +153,7 @@ export default class NewDeviceCalibrationAction extends mixins(CheckEditAccess)
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to save the action')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/devices/_deviceId/actions/new/generic-device-actions.vue b/pages/devices/_deviceId/actions/new/generic-device-actions.vue
index 717068791..caefe9521 100644
--- a/pages/devices/_deviceId/actions/new/generic-device-actions.vue
+++ b/pages/devices/_deviceId/actions/new/generic-device-actions.vue
@@ -31,10 +31,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card-actions>
       <v-spacer />
       <SaveAndCancelButtons
@@ -78,23 +74,26 @@ import { GenericAction } from '@/models/GenericAction'
 
 import GenericActionForm from '@/components/actions/GenericActionForm.vue'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 
 @Component({
   middleware: ['auth'],
-  components: { ProgressIndicator, SaveAndCancelButtons, GenericActionForm },
+  components: { SaveAndCancelButtons, GenericActionForm },
   computed: mapState('devices', ['deviceAttachments', 'chosenKindOfDeviceAction']),
-  methods: mapActions('devices', ['addDeviceGenericAction', 'loadAllDeviceActions'])
+  methods: {
+    ...mapActions('devices', ['addDeviceGenericAction', 'loadAllDeviceActions']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class NewGenericDeviceAction extends mixins(CheckEditAccess) {
   private genericDeviceAction: GenericAction = new GenericAction()
-  private isSaving: boolean = false
 
   // vuex definition for typescript check
   deviceAttachments!: DevicesState['deviceAttachments']
   chosenKindOfDeviceAction!: DevicesState['chosenKindOfDeviceAction']
   addDeviceGenericAction!: AddDeviceGenericAction
   loadAllDeviceActions!: LoadAllDeviceActionsAction
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -138,7 +137,7 @@ export default class NewGenericDeviceAction extends mixins(CheckEditAccess) {
     this.genericDeviceAction.actionTypeUrl = this.chosenKindOfDeviceAction?.uri || ''
 
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.addDeviceGenericAction({
         deviceId: this.deviceId,
         genericAction: this.genericDeviceAction
@@ -150,7 +149,7 @@ export default class NewGenericDeviceAction extends mixins(CheckEditAccess) {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to save the action')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/devices/_deviceId/actions/new/parameter-change-actions.vue b/pages/devices/_deviceId/actions/new/parameter-change-actions.vue
index c2898cbaf..b811334bb 100644
--- a/pages/devices/_deviceId/actions/new/parameter-change-actions.vue
+++ b/pages/devices/_deviceId/actions/new/parameter-change-actions.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card-actions>
       <v-spacer />
       <SaveAndCancelButtons
@@ -76,28 +72,30 @@ import {
 import { ParameterChangeAction } from '@/models/ParameterChangeAction'
 
 import ParameterChangeActionForm from '@/components/actions/ParameterChangeActionForm.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 
 @Component({
   middleware: ['auth'],
   components: {
     SaveAndCancelButtons,
-    ProgressIndicator,
     ParameterChangeActionForm
   },
   computed: mapState('devices', ['chosenKindOfDeviceAction', 'deviceParameters']),
-  methods: mapActions('devices', ['addDeviceParameterChangeAction', 'loadAllDeviceActions'])
+  methods: {
+    ...mapActions('devices', ['addDeviceParameterChangeAction', 'loadAllDeviceActions']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class NewParameterChangeAction extends mixins(CheckEditAccess) {
   private parameterChangeAction: ParameterChangeAction = new ParameterChangeAction()
-  private isSaving: boolean = false
 
   // vuex definition for typescript check
   deviceParameters!: DevicesState['deviceParameters']
   chosenKindOfDeviceAction!: DevicesState['chosenKindOfDeviceAction']
   addDeviceParameterChangeAction!: AddDeviceParameterChangeActionAction
   loadAllDeviceActions!: LoadAllDeviceActionsAction
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -145,7 +143,7 @@ export default class NewParameterChangeAction extends mixins(CheckEditAccess) {
     }
 
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.addDeviceParameterChangeAction({
         parameterId: this.parameterChangeAction.parameter.id,
         action: this.parameterChangeAction
@@ -156,7 +154,7 @@ export default class NewParameterChangeAction extends mixins(CheckEditAccess) {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to save the action')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/devices/_deviceId/actions/new/software-update-actions.vue b/pages/devices/_deviceId/actions/new/software-update-actions.vue
index c675b3568..75a69b645 100644
--- a/pages/devices/_deviceId/actions/new/software-update-actions.vue
+++ b/pages/devices/_deviceId/actions/new/software-update-actions.vue
@@ -31,10 +31,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card-actions>
       <v-spacer />
       <SaveAndCancelButtons
@@ -78,24 +74,26 @@ import { SoftwareUpdateAction } from '@/models/SoftwareUpdateAction'
 
 import SoftwareUpdateActionForm from '@/components/actions/SoftwareUpdateActionForm.vue'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 
 @Component({
   middleware: ['auth'],
-  components: { ProgressIndicator, SaveAndCancelButtons, SoftwareUpdateActionForm },
+  components: { SaveAndCancelButtons, SoftwareUpdateActionForm },
   computed: mapState('devices', ['deviceAttachments', 'chosenKindOfDeviceAction']),
-  methods: mapActions('devices', ['addDeviceSoftwareUpdateAction', 'loadAllDeviceActions'])
+  methods: {
+    ...mapActions('devices', ['addDeviceSoftwareUpdateAction', 'loadAllDeviceActions']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class NewDeviceSoftwareUpdateActions extends mixins(CheckEditAccess) {
   private softwareUpdateAction: SoftwareUpdateAction = new SoftwareUpdateAction()
-  private isSaving: boolean = false
 
   // vuex definition for typescript check
   chosenKindOfDeviceAction!: DevicesState['chosenKindOfDeviceAction']
   deviceAttachments!: DevicesState['deviceAttachments']
   addDeviceSoftwareUpdateAction!: AddDeviceSoftwareUpdateAction
   loadAllDeviceActions!: LoadAllDeviceActionsAction
-
+  setLoading!: SetLoadingAction
   /**
    * route to which the user is redirected when he is not allowed to access the page
    *
@@ -135,7 +133,7 @@ export default class NewDeviceSoftwareUpdateActions extends mixins(CheckEditAcce
     }
 
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.addDeviceSoftwareUpdateAction({
         deviceId: this.deviceId,
         softwareUpdateAction: this.softwareUpdateAction
@@ -146,7 +144,7 @@ export default class NewDeviceSoftwareUpdateActions extends mixins(CheckEditAcce
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to save the action')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/devices/_deviceId/actions/parameter-change-actions/_actionId/edit.vue b/pages/devices/_deviceId/actions/parameter-change-actions/_actionId/edit.vue
index 085dba2f5..53b84a48a 100644
--- a/pages/devices/_deviceId/actions/parameter-change-actions/_actionId/edit.vue
+++ b/pages/devices/_deviceId/actions/parameter-change-actions/_actionId/edit.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-      dark
-    />
     <v-select
       value="Parameter Value Change"
       :items="['Parameter Value Change']"
@@ -87,23 +83,24 @@ import {
 import { ParameterChangeAction } from '@/models/ParameterChangeAction'
 
 import ParameterChangeActionForm from '@/components/actions/ParameterChangeActionForm.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
 
 @Component({
   components: {
     ParameterChangeActionForm,
-    ProgressIndicator,
     SaveAndCancelButtons
   },
   scrollToTop: true,
   middleware: ['auth'],
   computed: mapState('devices', ['deviceParameterChangeAction', 'deviceParameters']),
-  methods: mapActions('devices', ['loadDeviceParameterChangeAction', 'loadAllDeviceActions', 'loadDeviceParameters', 'updateDeviceParameterChangeAction'])
+  methods: {
+    ...mapActions('devices', ['loadDeviceParameterChangeAction', 'loadAllDeviceActions', 'loadDeviceParameters', 'updateDeviceParameterChangeAction']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class DeviceParameterChangeActionEditPage extends mixins(CheckEditAccess) {
   private action: ParameterChangeAction = new ParameterChangeAction()
-  private isLoading = false
 
   // vuex definition for typescript check
   deviceParameterChangeAction!: DevicesState['deviceParameterChangeAction']
@@ -112,6 +109,7 @@ export default class DeviceParameterChangeActionEditPage extends mixins(CheckEdi
   loadDeviceParameters!: LoadDeviceParametersAction
   updateDeviceParameterChangeAction!: UpdateDeviceParameterChangeActionAction
   loadAllDeviceActions!: LoadAllDeviceActionsAction
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -137,7 +135,7 @@ export default class DeviceParameterChangeActionEditPage extends mixins(CheckEdi
 
   async fetch (): Promise<void> {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await Promise.all([
         this.loadDeviceParameterChangeAction(this.actionId),
         this.loadDeviceParameters(this.deviceId)
@@ -148,7 +146,7 @@ export default class DeviceParameterChangeActionEditPage extends mixins(CheckEdi
     } catch {
       this.$store.commit('snackbar/setError', 'Failed to fetch action')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
@@ -171,7 +169,7 @@ export default class DeviceParameterChangeActionEditPage extends mixins(CheckEdi
     }
 
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.updateDeviceParameterChangeAction({
         parameterId: this.action.parameter.id,
         action: this.action
@@ -181,7 +179,7 @@ export default class DeviceParameterChangeActionEditPage extends mixins(CheckEdi
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to save the action')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/devices/_deviceId/actions/software-update-actions/_actionId/edit.vue b/pages/devices/_deviceId/actions/software-update-actions/_actionId/edit.vue
index f3fdd642b..71e64bb05 100644
--- a/pages/devices/_deviceId/actions/software-update-actions/_actionId/edit.vue
+++ b/pages/devices/_deviceId/actions/software-update-actions/_actionId/edit.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isInProgress"
-      :dark="isSaving"
-    />
     <v-select
       value="Software Update"
       :items="['Software Update']"
@@ -87,23 +83,27 @@ import { SoftwareUpdateAction } from '@/models/SoftwareUpdateAction'
 
 import SoftwareUpdateActionForm from '@/components/actions/SoftwareUpdateActionForm.vue'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 
 @Component({
   components: {
-    ProgressIndicator,
     SaveAndCancelButtons,
     SoftwareUpdateActionForm
   },
   scrollToTop: true,
   middleware: ['auth'],
-  computed: mapState('devices', ['deviceSoftwareUpdateAction', 'deviceAttachments']),
-  methods: mapActions('devices', ['loadDeviceSoftwareUpdateAction', 'loadAllDeviceActions', 'loadDeviceAttachments', 'updateDeviceSoftwareUpdateAction'])
+  computed: {
+    ...mapState('devices', ['deviceSoftwareUpdateAction', 'deviceAttachments']),
+    ...mapState('progressindicator', ['isLoading'])
+  },
+  methods: {
+    ...mapActions('devices', ['loadDeviceSoftwareUpdateAction', 'loadAllDeviceActions', 'loadDeviceAttachments', 'updateDeviceSoftwareUpdateAction']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class DeviceSoftwareUpdateActionEditPage extends mixins(CheckEditAccess) {
   private action: SoftwareUpdateAction = new SoftwareUpdateAction()
-  private isSaving = false
-  private isLoading = false
 
   // vuex definition for typescript check
   deviceSoftwareUpdateAction!: DevicesState['deviceSoftwareUpdateAction']
@@ -112,6 +112,8 @@ export default class DeviceSoftwareUpdateActionEditPage extends mixins(CheckEdit
   loadDeviceAttachments!: LoadDeviceAttachmentsAction
   updateDeviceSoftwareUpdateAction!: UpdateDeviceSoftwareUpdateAction
   loadAllDeviceActions!: LoadAllDeviceActionsAction
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -137,7 +139,7 @@ export default class DeviceSoftwareUpdateActionEditPage extends mixins(CheckEdit
 
   async fetch (): Promise<void> {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await Promise.all([
         this.loadDeviceSoftwareUpdateAction(this.actionId),
         this.loadDeviceAttachments(this.deviceId)
@@ -148,7 +150,7 @@ export default class DeviceSoftwareUpdateActionEditPage extends mixins(CheckEdit
     } catch {
       this.$store.commit('snackbar/setError', 'Failed to fetch action')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
@@ -160,10 +162,6 @@ export default class DeviceSoftwareUpdateActionEditPage extends mixins(CheckEdit
     return this.$route.params.actionId
   }
 
-  get isInProgress (): boolean {
-    return this.isLoading || this.isSaving
-  }
-
   async save () {
     if (!(this.$refs.softwareUpdateActionForm as Vue & { isValid: () => boolean }).isValid()) {
       this.$store.commit('snackbar/setError', 'Please correct the errors')
@@ -171,7 +169,7 @@ export default class DeviceSoftwareUpdateActionEditPage extends mixins(CheckEdit
     }
 
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.updateDeviceSoftwareUpdateAction({
         deviceId: this.deviceId,
         softwareUpdateAction: this.action
@@ -182,7 +180,7 @@ export default class DeviceSoftwareUpdateActionEditPage extends mixins(CheckEdit
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to save the action')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/devices/_deviceId/attachments.vue b/pages/devices/_deviceId/attachments.vue
index 5195514b6..424fb002e 100644
--- a/pages/devices/_deviceId/attachments.vue
+++ b/pages/devices/_deviceId/attachments.vue
@@ -30,9 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-    />
     <NuxtChild />
   </div>
 </template>
@@ -43,26 +40,27 @@ import { mapActions } from 'vuex'
 
 import { LoadDeviceAttachmentsAction } from '@/store/devices'
 
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 
 @Component({
-  components: { ProgressIndicator },
-  methods: mapActions('devices', ['loadDeviceAttachments'])
+  methods: {
+    ...mapActions('devices', ['loadDeviceAttachments']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class DeviceAttachmentsPage extends Vue {
-  private isLoading = false
-
   // vuex definition for typescript check
   loadDeviceAttachments!: LoadDeviceAttachmentsAction
+  setLoading!: SetLoadingAction
 
   async created () {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.loadDeviceAttachments(this.deviceId)
     } catch (e) {
       this.$store.commit('snackbar/setError', 'failed to fetch attachments')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/devices/_deviceId/attachments/_attachmentId/edit.vue b/pages/devices/_deviceId/attachments/_attachmentId/edit.vue
index 5b2d6717c..8f588a473 100644
--- a/pages/devices/_deviceId/attachments/_attachmentId/edit.vue
+++ b/pages/devices/_deviceId/attachments/_attachmentId/edit.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isInProgress"
-      :dark="isSaving"
-    />
     <v-card-actions>
       <v-spacer />
       <SaveAndCancelButtons
@@ -103,7 +99,7 @@ import { Attachment } from '@/models/Attachment'
 
 import { Rules } from '@/mixins/Rules'
 
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
 
 import { AttachmentsMixin } from '@/mixins/AttachmentsMixin'
@@ -114,17 +110,20 @@ import { AttachmentsMixin } from '@/mixins/AttachmentsMixin'
  */
 @Component({
   components: {
-    SaveAndCancelButtons,
-    ProgressIndicator
+    SaveAndCancelButtons
   },
   middleware: ['auth'],
-  computed: mapState('devices', ['deviceAttachment']),
-  methods: mapActions('devices', ['loadDeviceAttachment', 'loadDeviceAttachments', 'updateDeviceAttachment'])
+  computed: {
+    ...mapState('devices', ['deviceAttachment']),
+    ...mapState('progressindicator', ['isLoading'])
+  },
+  methods: {
+    ...mapActions('devices', ['loadDeviceAttachment', 'loadDeviceAttachments', 'updateDeviceAttachment']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 // @ts-ignore
 export default class AttachmentEditPage extends mixins(Rules, AttachmentsMixin, CheckEditAccess) {
-  private isSaving = false
-  private isLoading = false
   private valueCopy: Attachment = new Attachment()
 
   // vuex definition for typescript check
@@ -132,6 +131,8 @@ export default class AttachmentEditPage extends mixins(Rules, AttachmentsMixin,
   loadDeviceAttachment!: LoadDeviceAttachmentAction
   loadDeviceAttachments!: LoadDeviceAttachmentsAction
   updateDeviceAttachment!: UpdateDeviceAttachmentAction
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -157,7 +158,7 @@ export default class AttachmentEditPage extends mixins(Rules, AttachmentsMixin,
 
   async fetch (): Promise<void> {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.loadDeviceAttachment(this.attachmentId)
       if (this.deviceAttachment) {
         this.valueCopy = Attachment.createFromObject(this.deviceAttachment)
@@ -165,7 +166,7 @@ export default class AttachmentEditPage extends mixins(Rules, AttachmentsMixin,
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to load attachment')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
@@ -177,17 +178,13 @@ export default class AttachmentEditPage extends mixins(Rules, AttachmentsMixin,
     return this.$route.params.attachmentId
   }
 
-  get isInProgress (): boolean {
-    return this.isLoading || this.isSaving
-  }
-
   async save () {
     if (!(this.$refs.attachmentsEditForm as Vue & { validate: () => boolean }).validate()) {
       this.$store.commit('snackbar/setError', 'Please correct your input')
       return
     }
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.updateDeviceAttachment({
         deviceId: this.deviceId,
         attachment: this.valueCopy
@@ -198,7 +195,7 @@ export default class AttachmentEditPage extends mixins(Rules, AttachmentsMixin,
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to save attachments')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/devices/_deviceId/attachments/index.vue b/pages/devices/_deviceId/attachments/index.vue
index d0c9bbd34..41ad206cc 100644
--- a/pages/devices/_deviceId/attachments/index.vue
+++ b/pages/devices/_deviceId/attachments/index.vue
@@ -32,10 +32,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card-actions
       v-if="editable"
     >
@@ -99,7 +95,7 @@ permissions and limitations under the Licence.
       v-if="attachmentToDelete"
       v-model="showDeleteDialog"
       title="Delete Attachment"
-      :disabled="isSaving"
+      :disabled="isLoading"
       @cancel="closeDialog"
       @delete="deleteAndCloseDialog"
     >
@@ -128,20 +124,25 @@ import DeleteDialog from '@/components/shared/DeleteDialog.vue'
 import DownloadDialog from '@/components/shared/DownloadDialog.vue'
 import HintCard from '@/components/HintCard.vue'
 import DotMenuActionDelete from '@/components/DotMenuActionDelete.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 import { Visibility } from '@/models/Visibility'
 import { getLastPathElement } from '@/utils/urlHelpers'
 
 @Component({
-  components: { ProgressIndicator, DotMenuActionDelete, HintCard, DeleteDialog, AttachmentListItem, BaseList, DownloadDialog },
-  computed: mapState('devices', ['deviceAttachments', 'device']),
-  methods: mapActions('devices', ['loadDeviceAttachments', 'deleteDeviceAttachment', 'downloadAttachment'])
+  components: { DotMenuActionDelete, HintCard, DeleteDialog, AttachmentListItem, BaseList, DownloadDialog },
+  computed: {
+    ...mapState('devices', ['deviceAttachments', 'device']),
+    ...mapState('progressindicator', ['isLoading'])
+  },
+  methods: {
+    ...mapActions('devices', ['loadDeviceAttachments', 'deleteDeviceAttachment', 'downloadAttachment']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class DeviceAttachmentShowPage extends Vue {
   @InjectReactive()
     editable!: boolean
 
-  private isSaving = false
   private showDeleteDialog = false
   private attachmentToDelete: Attachment | null = null
 
@@ -154,6 +155,8 @@ export default class DeviceAttachmentShowPage extends Vue {
   deleteDeviceAttachment!: DeleteDeviceAttachmentAction
   loadDeviceAttachments!: LoadDeviceAttachmentsAction
   downloadAttachment!: DownloadAttachmentAction
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
 
   get deviceId (): string {
     return this.$route.params.deviceId
@@ -184,7 +187,7 @@ export default class DeviceAttachmentShowPage extends Vue {
       return
     }
     try {
-      this.isSaving = true
+      this.setLoading(true)
       const attachmentId = this.attachmentToDelete.id
       await this.deleteDeviceAttachment(attachmentId)
       await this.loadDeviceAttachments(this.deviceId)
@@ -192,7 +195,7 @@ export default class DeviceAttachmentShowPage extends Vue {
     } catch (_error) {
       this.$store.commit('snackbar/setError', 'Failed to delete attachment')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
       this.closeDialog()
     }
   }
diff --git a/pages/devices/_deviceId/attachments/new.vue b/pages/devices/_deviceId/attachments/new.vue
index 6484f76b6..806cfc5a4 100644
--- a/pages/devices/_deviceId/attachments/new.vue
+++ b/pages/devices/_deviceId/attachments/new.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-form ref="attachmentsForm" class="pb-2" @submit.prevent>
       <v-card-actions>
         <v-spacer />
@@ -134,29 +130,29 @@ import { IUploadResult } from '@/services/sms/UploadApi'
 import { Attachment } from '@/models/Attachment'
 
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
-
+import { SetLoadingAction } from '@/store/progressindicator'
 import { Rules } from '@/mixins/Rules'
 import { UploadRules } from '@/mixins/UploadRules'
 
 @Component({
-  components: { ProgressIndicator, SaveAndCancelButtons },
+  components: { SaveAndCancelButtons },
   middleware: ['auth'],
   methods: {
     ...mapActions('devices', ['addDeviceAttachment', 'loadDeviceAttachments']),
-    ...mapActions('files', ['uploadFile'])
+    ...mapActions('files', ['uploadFile']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class DeviceAttachmentAddPage extends mixins(Rules, UploadRules, CheckEditAccess) {
   private attachment: Attachment = new Attachment()
   private attachmentType: string = 'file'
   private file: File | null = null
-  private isSaving: boolean = false
 
   // vuex definition for typescript check
   uploadFile!: (file: File) => Promise<IUploadResult>
   addDeviceAttachment!: AddDeviceAttachmentAction
   loadDeviceAttachments!: LoadDeviceAttachmentsAction
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -202,7 +198,7 @@ export default class DeviceAttachmentAddPage extends mixins(Rules, UploadRules,
 
     let theFailureCanBeFromUpload = true
     try {
-      this.isSaving = true
+      this.setLoading(true)
 
       if (this.attachmentType !== 'url') {
         // Due to the validation we can be sure that the file is not null
@@ -221,7 +217,7 @@ export default class DeviceAttachmentAddPage extends mixins(Rules, UploadRules,
     } catch (error: any) {
       this.handelError(error, theFailureCanBeFromUpload)
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/devices/_deviceId/basic/edit.vue b/pages/devices/_deviceId/basic/edit.vue
index 72a9aa9b6..13ac5b997 100644
--- a/pages/devices/_deviceId/basic/edit.vue
+++ b/pages/devices/_deviceId/basic/edit.vue
@@ -33,10 +33,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card-actions>
       <v-spacer />
       <SaveAndCancelButtons
@@ -87,7 +83,7 @@ import { CreatePidAction, DevicesState, LoadDeviceAction, SaveDeviceAction } fro
 import { Device } from '@/models/Device'
 
 import DeviceBasicDataForm from '@/components/DeviceBasicDataForm.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
 import NavigationGuardDialog from '@/components/shared/NavigationGuardDialog.vue'
 import NonModelOptionsForm, { NonModelOptions } from '@/components/shared/NonModelOptionsForm.vue'
@@ -96,17 +92,19 @@ import NonModelOptionsForm, { NonModelOptions } from '@/components/shared/NonMod
   components: {
     SaveAndCancelButtons,
     DeviceBasicDataForm,
-    ProgressIndicator,
     NavigationGuardDialog,
     NonModelOptionsForm
   },
   middleware: ['auth'],
   computed: mapState('devices', ['device']),
-  methods: mapActions('devices', ['saveDevice', 'loadDevice', 'createPid'])
+  methods: {
+    ...mapActions('devices', ['saveDevice', 'loadDevice', 'createPid']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class DeviceEditBasicPage extends mixins(CheckEditAccess) {
   private deviceCopy: Device | null = null
-  private isSaving: boolean = false
+
   private hasSaved: boolean = false
   private showNavigationWarning: boolean = false
   private to: RawLocation | null = null
@@ -119,7 +117,7 @@ export default class DeviceEditBasicPage extends mixins(CheckEditAccess) {
   saveDevice!: SaveDeviceAction
   loadDevice!: LoadDeviceAction
   createPid!: CreatePidAction
-
+  setLoading!: SetLoadingAction
   /**
    * route to which the user is redirected when he is not allowed to access the page
    *
@@ -169,7 +167,7 @@ export default class DeviceEditBasicPage extends mixins(CheckEditAccess) {
     }
 
     try {
-      this.isSaving = true
+      this.setLoading(true)
       const savedDevice = await this.saveDevice(this.deviceCopy)
       if (this.editOptions.persistentIdentifierShouldBeCreated) {
         savedDevice.persistentIdentifier = await this.createPid(savedDevice.id)
@@ -187,7 +185,7 @@ export default class DeviceEditBasicPage extends mixins(CheckEditAccess) {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Save failed')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/devices/_deviceId/basic/index.vue b/pages/devices/_deviceId/basic/index.vue
index e8075a04c..916e161bc 100644
--- a/pages/devices/_deviceId/basic/index.vue
+++ b/pages/devices/_deviceId/basic/index.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card-actions>
       <v-spacer />
       <v-btn
@@ -114,7 +110,7 @@ permissions and limitations under the Licence.
       v-if="device"
       v-model="showDeleteDialog"
       title="Delete Device"
-      :disabled="isSaving"
+      :disabled="isLoading"
       @cancel="closeDialog"
       @delete="deleteAndCloseDialog"
     >
@@ -152,12 +148,11 @@ import DotMenuActionArchive from '@/components/DotMenuActionArchive.vue'
 import DotMenuActionRestore from '@/components/DotMenuActionRestore.vue'
 import DotMenuActionSensorML from '@/components/DotMenuActionSensorML.vue'
 import DownloadDialog from '@/components/shared/DownloadDialog.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 import { Visibility } from '@/models/Visibility'
 
 @Component({
   components: {
-    ProgressIndicator,
     DotMenuActionDelete,
     DotMenuActionCopy,
     DotMenuActionSensorML,
@@ -169,8 +164,14 @@ import { Visibility } from '@/models/Visibility'
     DeviceArchiveDialog,
     DownloadDialog
   },
-  computed: mapState('devices', ['device']),
-  methods: mapActions('devices', ['loadDevice', 'deleteDevice', 'archiveDevice', 'restoreDevice', 'exportAsSensorML', 'getSensorMLUrl'])
+  computed: {
+    ...mapState('devices', ['device']),
+    ...mapState('progressindicator', ['isLoading'])
+  },
+  methods: {
+    ...mapActions('devices', ['loadDevice', 'deleteDevice', 'archiveDevice', 'restoreDevice', 'exportAsSensorML', 'getSensorMLUrl']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class DeviceShowBasicPage extends Vue {
   @InjectReactive()
@@ -185,8 +186,6 @@ export default class DeviceShowBasicPage extends Vue {
   @InjectReactive()
     restoreable!: boolean
 
-  private isSaving = false
-
   private showDeleteDialog: boolean = false
   private showArchiveDialog: boolean = false
   private showDownloadDialog: boolean = false
@@ -199,6 +198,8 @@ export default class DeviceShowBasicPage extends Vue {
   restoreDevice!: RestoreDeviceAction
   exportAsSensorML!: ExportAsSensorMLAction
   getSensorMLUrl!: GetSensorMLUrlAction
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
 
   get deviceId () {
     return this.$route.params.deviceId
@@ -249,14 +250,14 @@ export default class DeviceShowBasicPage extends Vue {
       return
     }
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.deleteDevice(this.device.id)
       this.$store.commit('snackbar/setSuccess', 'Device deleted')
       this.$router.push('/devices')
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Device could not be deleted')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
       this.showDeleteDialog = false
     }
   }
@@ -275,7 +276,7 @@ export default class DeviceShowBasicPage extends Vue {
       return
     }
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.archiveDevice(this.device.id)
       await this.loadDevice({
         deviceId: this.deviceId,
@@ -290,7 +291,7 @@ export default class DeviceShowBasicPage extends Vue {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Device could not be archived')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
       this.showArchiveDialog = false
     }
   }
@@ -299,7 +300,7 @@ export default class DeviceShowBasicPage extends Vue {
     if (this.device === null || this.device.id === null) {
       return
     }
-    this.isSaving = true
+    this.setLoading(true)
     try {
       await this.restoreDevice(this.device.id)
       await this.loadDevice({
@@ -315,7 +316,7 @@ export default class DeviceShowBasicPage extends Vue {
     } catch (error) {
       this.$store.commit('snackbar/setError', 'Device could not be restored')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/devices/_deviceId/contacts.vue b/pages/devices/_deviceId/contacts.vue
index ca1e4dd73..2e3604807 100644
--- a/pages/devices/_deviceId/contacts.vue
+++ b/pages/devices/_deviceId/contacts.vue
@@ -30,9 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-    />
     <NuxtChild />
   </div>
 </template>
@@ -44,25 +41,24 @@ import { mapActions } from 'vuex'
 import { LoadDeviceContactRolesAction } from '@/store/devices'
 import { LoadCvContactRolesAction } from '@/store/vocabulary'
 
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 
 @Component({
-  components: { ProgressIndicator },
   methods: {
     ...mapActions('devices', ['loadDeviceContactRoles']),
-    ...mapActions('vocabulary', ['loadCvContactRoles'])
+    ...mapActions('vocabulary', ['loadCvContactRoles']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class DeviceContactsPage extends Vue {
-  private isLoading = false
-
   // vuex definition for typescript check
   loadDeviceContactRoles!: LoadDeviceContactRolesAction
   loadCvContactRoles!: LoadCvContactRolesAction
+  setLoading!: SetLoadingAction
 
   async fetch () {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await Promise.all([
         this.loadDeviceContactRoles(this.deviceId),
         this.loadCvContactRoles()
@@ -70,7 +66,7 @@ export default class DeviceContactsPage extends Vue {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to fetch contacts')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/devices/_deviceId/contacts/index.vue b/pages/devices/_deviceId/contacts/index.vue
index 6b0f8469d..128693562 100644
--- a/pages/devices/_deviceId/contacts/index.vue
+++ b/pages/devices/_deviceId/contacts/index.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card-actions>
       <v-spacer />
       <v-btn
@@ -90,7 +86,7 @@ import { mapActions, mapState } from 'vuex'
 import { LoadDeviceContactRolesAction, RemoveDeviceContactRoleAction } from '@/store/devices'
 
 import HintCard from '@/components/HintCard.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import BaseList from '@/components/shared/BaseList.vue'
 import ContactRoleListItem from '@/components/contacts/ContactRoleListItem.vue'
 import DotMenuActionDelete from '@/components/DotMenuActionDelete.vue'
@@ -100,24 +96,25 @@ import DotMenuActionDelete from '@/components/DotMenuActionDelete.vue'
     DotMenuActionDelete,
     ContactRoleListItem,
     BaseList,
-    ProgressIndicator,
     HintCard
   },
   computed: {
     ...mapState('devices', ['deviceContactRoles']),
     ...mapState('vocabulary', ['cvContactRoles'])
   },
-  methods: mapActions('devices', ['loadDeviceContactRoles', 'removeDeviceContactRole'])
+  methods: {
+    ...mapActions('devices', ['loadDeviceContactRoles', 'removeDeviceContactRole']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class DeviceShowContactPage extends Vue {
   @InjectReactive()
     editable!: boolean
 
-  private isSaving = false
-
   // vuex definition for typescript check
   removeDeviceContactRole!: RemoveDeviceContactRoleAction
   loadDeviceContactRoles!: LoadDeviceContactRolesAction
+  setLoading!: SetLoadingAction
 
   get deviceId (): string {
     return this.$route.params.deviceId
@@ -125,7 +122,7 @@ export default class DeviceShowContactPage extends Vue {
 
   async removeContactRole (contactRoleId: string) {
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.removeDeviceContactRole({
         deviceContactRoleId: contactRoleId
       })
@@ -134,7 +131,7 @@ export default class DeviceShowContactPage extends Vue {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Removing contact failed')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/devices/_deviceId/contacts/new.vue b/pages/devices/_deviceId/contacts/new.vue
index b289ab19f..70d157593 100644
--- a/pages/devices/_deviceId/contacts/new.vue
+++ b/pages/devices/_deviceId/contacts/new.vue
@@ -2,7 +2,7 @@
 Web client of the Sensor Management System software developed within the
 Helmholtz DataHub Initiative by GFZ and UFZ.
 
-Copyright (C) 2020 - 2022
+Copyright (C) 2020 - 2023
 - Nils Brinckmann (GFZ, nils.brinckmann@gfz-potsdam.de)
 - Marc Hanisch (GFZ, marc.hanisch@gfz-potsdam.de)
 - Helmholtz Centre Potsdam - GFZ German Research Centre for
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isInProgress"
-      :dark="isSaving"
-    />
     <contact-role-assignment-form
       :contacts="contacts"
       :contact="selectedContact"
@@ -58,30 +54,29 @@ import { LoadCvContactRolesAction } from '@/store/vocabulary'
 import { Contact } from '@/models/Contact'
 import { ContactRole } from '@/models/ContactRole'
 
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 import ContactRoleAssignmentForm from '@/components/shared/ContactRoleAssignmentForm.vue'
 
 @Component({
   components: {
-    ProgressIndicator,
     ContactRoleAssignmentForm
   },
   middleware: ['auth'],
   computed: {
     ...mapState('devices', ['deviceContactRoles']),
     ...mapState('contacts', ['contacts']),
-    ...mapState('vocabulary', ['cvContactRoles'])
+    ...mapState('vocabulary', ['cvContactRoles']),
+    ...mapState('progressindicator', ['isLoading'])
   },
   methods: {
     ...mapActions('contacts', ['loadAllContacts']),
     ...mapActions('devices', ['loadDeviceContactRoles', 'addDeviceContactRole']),
-    ...mapActions('vocabulary', ['loadCvContactRoles'])
+    ...mapActions('vocabulary', ['loadCvContactRoles']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class DeviceAssignContactPage extends mixins(CheckEditAccess) {
   private selectedContact: Contact | null = null
-  private isLoading: boolean = false
-  private isSaving: boolean = false
 
   // vuex definition for typescript check
   deviceContactRoles!: DevicesState['deviceContactRoles']
@@ -90,6 +85,8 @@ export default class DeviceAssignContactPage extends mixins(CheckEditAccess) {
   addDeviceContactRole!: AddDeviceContactRoleAction
   loadAllContacts!: LoadAllContactsAction
   loadCvContactRoles!: LoadCvContactRolesAction
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -115,7 +112,7 @@ export default class DeviceAssignContactPage extends mixins(CheckEditAccess) {
 
   async fetch (): Promise<void> {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.loadAllContacts()
 
       const redirectContactId = this.$route.query.contact
@@ -125,7 +122,7 @@ export default class DeviceAssignContactPage extends mixins(CheckEditAccess) {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to fetch related contacts')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
@@ -133,14 +130,10 @@ export default class DeviceAssignContactPage extends mixins(CheckEditAccess) {
     return this.$route.params.deviceId
   }
 
-  get isInProgress (): boolean {
-    return this.isLoading || this.isSaving
-  }
-
   async assignContact (contactRole: ContactRole | null) {
     if (this.editable && contactRole) {
       try {
-        this.isSaving = true
+        this.setLoading(true)
         await this.addDeviceContactRole({
           deviceId: this.deviceId,
           contactRole
@@ -151,7 +144,7 @@ export default class DeviceAssignContactPage extends mixins(CheckEditAccess) {
       } catch (e) {
         this.$store.commit('snackbar/setError', 'Failed to add a contact')
       } finally {
-        this.isSaving = false
+        this.setLoading(false)
       }
     }
   }
diff --git a/pages/devices/_deviceId/customfields.vue b/pages/devices/_deviceId/customfields.vue
index 12786ad19..c1eea2c73 100644
--- a/pages/devices/_deviceId/customfields.vue
+++ b/pages/devices/_deviceId/customfields.vue
@@ -30,9 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-    />
     <NuxtChild />
   </div>
 </template>
@@ -43,26 +40,27 @@ import { mapActions } from 'vuex'
 
 import { LoadDeviceCustomFieldsAction } from '@/store/devices'
 
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 
 @Component({
-  components: { ProgressIndicator },
-  methods: mapActions('devices', ['loadDeviceCustomFields'])
+  methods: {
+    ...mapActions('devices', ['loadDeviceCustomFields']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class DeviceCustomFieldsPage extends Vue {
-  private isLoading = false
-
   // vuex definition for typescript check
   loadDeviceCustomFields!: LoadDeviceCustomFieldsAction
+  setLoading!: SetLoadingAction
 
   async created () {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.loadDeviceCustomFields(this.deviceId)
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to fetch custom fields')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/devices/_deviceId/customfields/_customfieldId/edit.vue b/pages/devices/_deviceId/customfields/_customfieldId/edit.vue
index 51a232018..a5d78734e 100644
--- a/pages/devices/_deviceId/customfields/_customfieldId/edit.vue
+++ b/pages/devices/_deviceId/customfields/_customfieldId/edit.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isInProgress"
-      :dark="isSaving"
-    />
     <v-card
       flat
     >
@@ -89,7 +85,7 @@ import { CustomTextField } from '@/models/CustomTextField'
 import BaseList from '@/components/shared/BaseList.vue'
 import CustomFieldForm from '@/components/shared/CustomFieldForm.vue'
 import CustomFieldListItem from '@/components/shared/CustomFieldListItem.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
 
 @Component({
@@ -97,17 +93,19 @@ import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
     BaseList,
     CustomFieldForm,
     CustomFieldListItem,
-    ProgressIndicator,
     SaveAndCancelButtons
   },
   middleware: ['auth'],
-  computed: mapState('devices', ['deviceCustomField', 'deviceCustomFields']),
-  methods: mapActions('devices', ['loadDeviceCustomField', 'loadDeviceCustomFields', 'updateDeviceCustomField'])
+  computed: {
+    ...mapState('devices', ['deviceCustomField', 'deviceCustomFields']),
+    ...mapState('progressindicator', ['isLoading'])
+  },
+  methods: {
+    ...mapActions('devices', ['loadDeviceCustomField', 'loadDeviceCustomFields', 'updateDeviceCustomField']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class DeviceCustomFieldsEditPage extends mixins(CheckEditAccess) {
-  private isSaving = false
-  private isLoading = false
-
   private valueCopy: CustomTextField = new CustomTextField()
 
   // vuex definition for typescript check
@@ -116,6 +114,8 @@ export default class DeviceCustomFieldsEditPage extends mixins(CheckEditAccess)
   loadDeviceCustomField!: LoadDeviceCustomFieldAction
   updateDeviceCustomField!: UpdateDeviceCustomFieldAction
   loadDeviceCustomFields!: LoadDeviceCustomFieldsAction
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -141,7 +141,7 @@ export default class DeviceCustomFieldsEditPage extends mixins(CheckEditAccess)
 
   async fetch (): Promise<void> {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.loadDeviceCustomField(this.customFieldId)
       if (this.deviceCustomField) {
         this.valueCopy = CustomTextField.createFromObject(this.deviceCustomField)
@@ -149,7 +149,7 @@ export default class DeviceCustomFieldsEditPage extends mixins(CheckEditAccess)
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to load custom field')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
@@ -161,17 +161,13 @@ export default class DeviceCustomFieldsEditPage extends mixins(CheckEditAccess)
     return this.$route.params.customfieldId
   }
 
-  get isInProgress (): boolean {
-    return this.isLoading || this.isSaving
-  }
-
   get deviceCustomFieldsExceptCurrent (): CustomTextField[] {
     return this.deviceCustomFields.filter(i => i.id !== this.customFieldId)
   }
 
   async save () {
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.updateDeviceCustomField({
         deviceId: this.deviceId,
         deviceCustomField: this.valueCopy
@@ -182,7 +178,7 @@ export default class DeviceCustomFieldsEditPage extends mixins(CheckEditAccess)
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to save custom field')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/devices/_deviceId/customfields/index.vue b/pages/devices/_deviceId/customfields/index.vue
index ff571520f..d8271f3d1 100644
--- a/pages/devices/_deviceId/customfields/index.vue
+++ b/pages/devices/_deviceId/customfields/index.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card-actions
       v-if="editable"
     >
@@ -84,7 +80,7 @@ permissions and limitations under the Licence.
       v-if="customFieldToDelete"
       v-model="showDeleteDialog"
       title="Delete Custom Field"
-      :disabled="isSaving"
+      :disabled="isLoading"
       @cancel="closeDialog"
       @delete="deleteAndCloseDialog"
     >
@@ -106,18 +102,23 @@ import CustomFieldListItem from '@/components/shared/CustomFieldListItem.vue'
 import DotMenuActionDelete from '@/components/DotMenuActionDelete.vue'
 import DeleteDialog from '@/components/shared/DeleteDialog.vue'
 import HintCard from '@/components/HintCard.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 
 @Component({
-  components: { ProgressIndicator, HintCard, DeleteDialog, DotMenuActionDelete, CustomFieldListItem, BaseList },
-  computed: mapState('devices', ['deviceCustomFields']),
-  methods: mapActions('devices', ['deleteDeviceCustomField', 'loadDeviceCustomFields'])
+  components: { HintCard, DeleteDialog, DotMenuActionDelete, CustomFieldListItem, BaseList },
+  computed: {
+    ...mapState('devices', ['deviceCustomFields']),
+    ...mapState('progressindicator', ['isLoading'])
+  },
+  methods: {
+    ...mapActions('devices', ['deleteDeviceCustomField', 'loadDeviceCustomFields']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class DeviceCustomFieldsShowPage extends Vue {
   @InjectReactive()
     editable!: boolean
 
-  private isSaving = false
   private showDeleteDialog = false
   private customFieldToDelete: CustomTextField | null = null
 
@@ -125,6 +126,7 @@ export default class DeviceCustomFieldsShowPage extends Vue {
   deviceCustomFields!: DevicesState['deviceCustomFields']
   loadDeviceCustomFields!: LoadDeviceCustomFieldsAction
   deleteDeviceCustomField!: DeleteDeviceCustomFieldAction
+  setLoading!: SetLoadingAction
 
   get deviceId (): string {
     return this.$route.params.deviceId
@@ -145,14 +147,14 @@ export default class DeviceCustomFieldsShowPage extends Vue {
       return
     }
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.deleteDeviceCustomField(this.customFieldToDelete.id)
       this.loadDeviceCustomFields(this.deviceId)
       this.$store.commit('snackbar/setSuccess', 'Custom field deleted')
     } catch (_error) {
       this.$store.commit('snackbar/setError', 'Failed to delete custom field')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
       this.closeDialog()
     }
   }
diff --git a/pages/devices/_deviceId/customfields/new.vue b/pages/devices/_deviceId/customfields/new.vue
index 2f805f4f5..1088cfe01 100644
--- a/pages/devices/_deviceId/customfields/new.vue
+++ b/pages/devices/_deviceId/customfields/new.vue
@@ -31,10 +31,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card
       flat
     >
@@ -92,7 +88,7 @@ import { CustomTextField } from '@/models/CustomTextField'
 import BaseList from '@/components/shared/BaseList.vue'
 import CustomFieldForm from '@/components/shared/CustomFieldForm.vue'
 import CustomFieldListItem from '@/components/shared/CustomFieldListItem.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
 
 @Component({
@@ -101,21 +97,22 @@ import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
     BaseList,
     CustomFieldForm,
     CustomFieldListItem,
-    ProgressIndicator,
     SaveAndCancelButtons
   },
   computed: mapState('devices', ['deviceCustomField', 'deviceCustomFields']),
-  methods: mapActions('devices', ['addDeviceCustomField', 'loadDeviceCustomFields'])
+  methods: {
+    ...mapActions('devices', ['addDeviceCustomField', 'loadDeviceCustomFields']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class DeviceCustomFieldAddPage extends mixins(CheckEditAccess) {
-  private isSaving = false
-
   private customField: CustomTextField = new CustomTextField()
 
   // vuex definition for typescript check
   deviceCustomFields!: DevicesState['deviceCustomFields']
   loadDeviceCustomFields!: LoadDeviceCustomFieldsAction
   addDeviceCustomField!: AddDeviceCustomFieldAction
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -149,7 +146,7 @@ export default class DeviceCustomFieldAddPage extends mixins(CheckEditAccess) {
       return
     }
     try {
-      this.isSaving = true
+      this.setLoading(true)
 
       await this.addDeviceCustomField({
         deviceId: this.deviceId,
@@ -161,7 +158,7 @@ export default class DeviceCustomFieldAddPage extends mixins(CheckEditAccess) {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to save custom field')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/devices/_deviceId/measuredquantities.vue b/pages/devices/_deviceId/measuredquantities.vue
index 2aeb88e0d..ec1c76138 100644
--- a/pages/devices/_deviceId/measuredquantities.vue
+++ b/pages/devices/_deviceId/measuredquantities.vue
@@ -30,11 +30,7 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-      color="primary"
-    />
-    <NuxtChild :is-fetching="isLoading" />
+    <NuxtChild />
   </div>
 </template>
 
@@ -52,18 +48,17 @@ import {
   LoadAggregationtypesAction
 } from '@/store/vocabulary'
 
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 
 @Component({
-  components: { ProgressIndicator },
   methods: {
     ...mapActions('devices', ['loadDeviceMeasuredQuantities']),
-    ...mapActions('vocabulary', ['loadCompartments', 'loadSamplingMedia', 'loadProperties', 'loadUnits', 'loadMeasuredQuantityUnits', 'loadAggregationtypes'])
+    ...mapActions('vocabulary', ['loadCompartments', 'loadSamplingMedia', 'loadProperties', 'loadUnits', 'loadMeasuredQuantityUnits', 'loadAggregationtypes']),
+    ...mapActions('progressindicator', ['setLoading'])
+
   }
 })
 export default class DevicePropertiesPage extends Vue {
-  private isLoading = false
-
   // vuex definition for typescript check
   loadDeviceMeasuredQuantities!: LoadDeviceMeasuredQuantitiesAction
   loadCompartments!: LoadCompartmentsAction
@@ -72,10 +67,11 @@ export default class DevicePropertiesPage extends Vue {
   loadUnits!: LoadUnitsAction
   loadMeasuredQuantityUnits!: LoadMeasuredQuantityUnitsAction
   loadAggregationtypes!: LoadAggregationtypesAction
+  setLoading!: SetLoadingAction
 
   async fetch (): Promise<void> {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await Promise.all([
         this.loadDeviceMeasuredQuantities(this.deviceId),
         this.loadCompartments(),
@@ -88,7 +84,7 @@ export default class DevicePropertiesPage extends Vue {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to fetch measured quantities')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/devices/_deviceId/measuredquantities/_measuredquantityId/copy.vue b/pages/devices/_deviceId/measuredquantities/_measuredquantityId/copy.vue
index a05cd4516..4587b454f 100644
--- a/pages/devices/_deviceId/measuredquantities/_measuredquantityId/copy.vue
+++ b/pages/devices/_deviceId/measuredquantities/_measuredquantityId/copy.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isInProgress"
-      :dark="isSaving"
-    />
     <v-card
       flat
     >
@@ -56,6 +52,7 @@ permissions and limitations under the Licence.
           :properties="properties"
           :units="units"
           :measured-quantity-units="measuredQuantityUnits"
+          :aggregation-types="aggregationtypes"
         />
       </v-card-text>
       <v-card-actions>
@@ -83,6 +80,7 @@ permissions and limitations under the Licence.
           :properties="properties"
           :units="units"
           :measured-quantity-units="measuredQuantityUnits"
+          :aggregation-types="aggregationtypes"
         />
       </template>
     </BaseList>
@@ -107,26 +105,26 @@ import { VocabularyState } from '@/store/vocabulary'
 import { DeviceProperty } from '@/models/DeviceProperty'
 
 import DevicePropertyForm from '@/components/DevicePropertyForm.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
 import DevicesMeasuredQuantitiesListItem from '@/components/devices/DevicesMeasuredQuantitiesListItem.vue'
 import DotMenuActionDelete from '@/components/DotMenuActionDelete.vue'
 import BaseList from '@/components/shared/BaseList.vue'
 
 @Component({
-  components: { BaseList, DotMenuActionDelete, DevicesMeasuredQuantitiesListItem, SaveAndCancelButtons, ProgressIndicator, DevicePropertyForm },
+  components: { BaseList, DotMenuActionDelete, DevicesMeasuredQuantitiesListItem, SaveAndCancelButtons, DevicePropertyForm },
   middleware: ['auth'],
   computed: {
-    ...mapState('vocabulary', ['compartments', 'samplingMedia', 'properties', 'units', 'measuredQuantityUnits']),
+    ...mapState('vocabulary', ['compartments', 'samplingMedia', 'properties', 'units', 'measuredQuantityUnits', 'aggregationtypes']),
     ...mapState('devices', ['deviceMeasuredQuantity', 'deviceMeasuredQuantities'])
   },
-  methods: mapActions('devices', ['loadDeviceMeasuredQuantity', 'loadDeviceMeasuredQuantities', 'addDeviceMeasuredQuantity']),
+  methods: {
+    ...mapActions('devices', ['loadDeviceMeasuredQuantity', 'loadDeviceMeasuredQuantities', 'addDeviceMeasuredQuantity']),
+    ...mapActions('progressindicator', ['setLoading'])
+  },
   scrollToTop: true
 })
 export default class DevicePropertyCopyPage extends mixins(CheckEditAccess) {
-  private isSaving = false
-  private isLoading = false
-
   private valueCopy: DeviceProperty = new DeviceProperty()
 
   // vuex definition for typescript check
@@ -140,6 +138,7 @@ export default class DevicePropertyCopyPage extends mixins(CheckEditAccess) {
   loadDeviceMeasuredQuantity!: LoadDeviceMeasuredQuantityAction
   addDeviceMeasuredQuantity!: AddDeviceMeasuredQuantityAction
   loadDeviceMeasuredQuantities!: LoadDeviceMeasuredQuantitiesAction
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -165,7 +164,7 @@ export default class DevicePropertyCopyPage extends mixins(CheckEditAccess) {
 
   async created () {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.loadDeviceMeasuredQuantity(this.measuredquantityId)
       if (this.deviceMeasuredQuantity) {
         this.valueCopy = DeviceProperty.createFromObject(this.deviceMeasuredQuantity)
@@ -177,7 +176,7 @@ export default class DevicePropertyCopyPage extends mixins(CheckEditAccess) {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to load measured quantity')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
@@ -189,13 +188,9 @@ export default class DevicePropertyCopyPage extends mixins(CheckEditAccess) {
     return this.$route.params.measuredquantityId
   }
 
-  get isInProgress (): boolean {
-    return this.isLoading || this.isSaving
-  }
-
   async save () {
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.addDeviceMeasuredQuantity({
         deviceId: this.deviceId,
         deviceMeasuredQuantity: this.valueCopy
@@ -206,7 +201,7 @@ export default class DevicePropertyCopyPage extends mixins(CheckEditAccess) {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to save measured quantity')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/devices/_deviceId/measuredquantities/_measuredquantityId/edit.vue b/pages/devices/_deviceId/measuredquantities/_measuredquantityId/edit.vue
index 1289569e1..c6e1fba64 100644
--- a/pages/devices/_deviceId/measuredquantities/_measuredquantityId/edit.vue
+++ b/pages/devices/_deviceId/measuredquantities/_measuredquantityId/edit.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isInProgress"
-      :dark="isSaving"
-    />
     <v-card
       flat
     >
@@ -114,26 +110,26 @@ import { VocabularyState } from '@/store/vocabulary'
 import { DeviceProperty } from '@/models/DeviceProperty'
 
 import DevicePropertyForm from '@/components/DevicePropertyForm.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
 import DevicesMeasuredQuantitiesListItem from '@/components/devices/DevicesMeasuredQuantitiesListItem.vue'
 import DotMenuActionDelete from '@/components/DotMenuActionDelete.vue'
 import BaseList from '@/components/shared/BaseList.vue'
 
 @Component({
-  components: { BaseList, DotMenuActionDelete, DevicesMeasuredQuantitiesListItem, SaveAndCancelButtons, ProgressIndicator, DevicePropertyForm },
+  components: { BaseList, DotMenuActionDelete, DevicesMeasuredQuantitiesListItem, SaveAndCancelButtons, DevicePropertyForm },
   middleware: ['auth'],
   computed: {
     ...mapState('vocabulary', ['compartments', 'samplingMedia', 'properties', 'units', 'measuredQuantityUnits', 'aggregationtypes']),
     ...mapState('devices', ['deviceMeasuredQuantity', 'deviceMeasuredQuantities'])
   },
-  methods: mapActions('devices', ['loadDeviceMeasuredQuantity', 'loadDeviceMeasuredQuantities', 'updateDeviceMeasuredQuantity']),
+  methods: {
+    ...mapActions('devices', ['loadDeviceMeasuredQuantity', 'loadDeviceMeasuredQuantities', 'updateDeviceMeasuredQuantity']),
+    ...mapActions('progressindicator', ['setLoading'])
+  },
   scrollToTop: true
 })
 export default class DevicePropertyEditPage extends mixins(CheckEditAccess) {
-  private isSaving = false
-  private isLoading = false
-
   private valueCopy: DeviceProperty = new DeviceProperty()
 
   // vuex definition for typescript check
@@ -148,6 +144,7 @@ export default class DevicePropertyEditPage extends mixins(CheckEditAccess) {
   loadDeviceMeasuredQuantity!: LoadDeviceMeasuredQuantityAction
   updateDeviceMeasuredQuantity!: UpdateDeviceMeasuredQuantityAction
   loadDeviceMeasuredQuantities!: LoadDeviceMeasuredQuantitiesAction
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -173,7 +170,7 @@ export default class DevicePropertyEditPage extends mixins(CheckEditAccess) {
 
   async created () {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.loadDeviceMeasuredQuantity(this.measuredquantityId)
       if (this.deviceMeasuredQuantity) {
         this.valueCopy = DeviceProperty.createFromObject(this.deviceMeasuredQuantity)
@@ -181,7 +178,7 @@ export default class DevicePropertyEditPage extends mixins(CheckEditAccess) {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to load measured quantity')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
@@ -193,13 +190,9 @@ export default class DevicePropertyEditPage extends mixins(CheckEditAccess) {
     return this.$route.params.measuredquantityId
   }
 
-  get isInProgress (): boolean {
-    return this.isLoading || this.isSaving
-  }
-
   async save () {
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.updateDeviceMeasuredQuantity({
         deviceId: this.deviceId,
         deviceMeasuredQuantity: this.valueCopy
@@ -210,7 +203,7 @@ export default class DevicePropertyEditPage extends mixins(CheckEditAccess) {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to save measured quantity')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/devices/_deviceId/measuredquantities/index.vue b/pages/devices/_deviceId/measuredquantities/index.vue
index a6098d22a..661f60b37 100644
--- a/pages/devices/_deviceId/measuredquantities/index.vue
+++ b/pages/devices/_deviceId/measuredquantities/index.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card-actions
       v-if="editable"
     >
@@ -41,7 +37,7 @@ permissions and limitations under the Licence.
       <v-btn
         color="primary"
         small
-        :disabled="isFetching"
+        :disabled="isLoading"
         :to="'/devices/' + deviceId + '/measuredquantities/new'"
       >
         Add Measured Quantity
@@ -112,7 +108,7 @@ permissions and limitations under the Licence.
       v-if="editable && measuredQuantityToDelete"
       v-model="showDeleteDialog"
       title="Delete Measured Quantity"
-      :disabled="isSaving"
+      :disabled="isLoading"
       @cancel="closeDialog"
       @delete="deleteAndCloseDialog"
     >
@@ -122,7 +118,7 @@ permissions and limitations under the Licence.
 </template>
 
 <script lang="ts">
-import { Component, Vue, InjectReactive, Prop } from 'nuxt-property-decorator'
+import { Component, Vue, InjectReactive } from 'nuxt-property-decorator'
 import { mapActions, mapState } from 'vuex'
 
 import { DeleteDeviceMeasuredQuantityAction, DevicesState, LoadDeviceMeasuredQuantitiesAction } from '@/store/devices'
@@ -134,7 +130,7 @@ import DevicesMeasuredQuantitiesListItem from '@/components/devices/DevicesMeasu
 import DeleteDialog from '@/components/shared/DeleteDialog.vue'
 import HintCard from '@/components/HintCard.vue'
 import BaseList from '@/components/shared/BaseList.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import DotMenuActionDelete from '@/components/DotMenuActionDelete.vue'
 import DotMenuActionCopy from '@/components/DotMenuActionCopy.vue'
 
@@ -142,7 +138,6 @@ import DotMenuActionCopy from '@/components/DotMenuActionCopy.vue'
   components: {
     DotMenuActionDelete,
     DotMenuActionCopy,
-    ProgressIndicator,
     BaseList,
     HintCard,
     DeleteDialog,
@@ -150,10 +145,12 @@ import DotMenuActionCopy from '@/components/DotMenuActionCopy.vue'
   },
   computed: {
     ...mapState('vocabulary', ['compartments', 'samplingMedia', 'properties', 'units', 'measuredQuantityUnits', 'aggregationtypes']),
-    ...mapState('devices', ['deviceMeasuredQuantities'])
+    ...mapState('devices', ['deviceMeasuredQuantities']),
+    ...mapState('progressindicator', ['isLoading'])
   },
   methods: {
-    ...mapActions('devices', ['deleteDeviceMeasuredQuantity', 'loadDeviceMeasuredQuantities'])
+    ...mapActions('devices', ['deleteDeviceMeasuredQuantity', 'loadDeviceMeasuredQuantities']),
+    ...mapActions('progressindicator', ['setLoading'])
   },
   scrollToTop: true
 })
@@ -161,13 +158,6 @@ export default class DevicePropertyShowPage extends Vue {
   @InjectReactive()
     editable!: boolean
 
-  @Prop({
-    type: Boolean
-  })
-    isFetching!: boolean
-
-  private isSaving = false
-
   private showDeleteDialog = false
   private measuredQuantityToDelete: DeviceProperty | null = null
 
@@ -181,6 +171,7 @@ export default class DevicePropertyShowPage extends Vue {
   deviceMeasuredQuantities!: DevicesState['deviceMeasuredQuantities']
   loadDeviceMeasuredQuantities!: LoadDeviceMeasuredQuantitiesAction
   deleteDeviceMeasuredQuantity!: DeleteDeviceMeasuredQuantityAction
+  setLoading!: SetLoadingAction
 
   get deviceId (): string {
     return this.$route.params.deviceId
@@ -201,7 +192,7 @@ export default class DevicePropertyShowPage extends Vue {
       return
     }
     try {
-      this.isSaving = true
+      this.setLoading(true)
 
       await this.deleteDeviceMeasuredQuantity(this.measuredQuantityToDelete.id)
       this.loadDeviceMeasuredQuantities(this.deviceId)
@@ -209,7 +200,7 @@ export default class DevicePropertyShowPage extends Vue {
     } catch (_error) {
       this.$store.commit('snackbar/setError', 'Failed to delete measured quantity')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
       this.closeDialog()
     }
   }
diff --git a/pages/devices/_deviceId/measuredquantities/new.vue b/pages/devices/_deviceId/measuredquantities/new.vue
index 254a08c9f..d3081b738 100644
--- a/pages/devices/_deviceId/measuredquantities/new.vue
+++ b/pages/devices/_deviceId/measuredquantities/new.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card
       flat
     >
@@ -112,25 +108,25 @@ import { DeviceProperty } from '@/models/DeviceProperty'
 
 import DevicePropertyForm from '@/components/DevicePropertyForm.vue'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import BaseList from '@/components/shared/BaseList.vue'
 import DevicesMeasuredQuantitiesListItem from '@/components/devices/DevicesMeasuredQuantitiesListItem.vue'
 
 @Component({
   middleware: ['auth'],
-  components: { DevicesMeasuredQuantitiesListItem, BaseList, ProgressIndicator, SaveAndCancelButtons, DevicePropertyForm },
+  components: { DevicesMeasuredQuantitiesListItem, BaseList, SaveAndCancelButtons, DevicePropertyForm },
   computed: {
     ...mapState('vocabulary', ['compartments', 'samplingMedia', 'properties', 'units', 'measuredQuantityUnits', 'aggregationtypes']),
     ...mapState('devices', ['deviceMeasuredQuantities'])
   },
   methods: {
     ...mapActions('devices', ['addDeviceMeasuredQuantity', 'loadDeviceMeasuredQuantities']),
-    ...mapActions('vocabulary', ['loadCompartments', 'loadSamplingMedia', 'loadProperties', 'loadUnits', 'loadMeasuredQuantityUnits'])
+    ...mapActions('vocabulary', ['loadCompartments', 'loadSamplingMedia', 'loadProperties', 'loadUnits', 'loadMeasuredQuantityUnits']),
+    ...mapActions('progressindicator', ['setLoading'])
   },
   scrollToTop: true
 })
 export default class DevicePropertyAddPage extends mixins(CheckEditAccess) {
-  private isSaving = false
   private valueCopy: DeviceProperty = new DeviceProperty()
 
   // vuex definition for typescript check
@@ -148,6 +144,7 @@ export default class DevicePropertyAddPage extends mixins(CheckEditAccess) {
   loadMeasuredQuantityUnits!: LoadMeasuredQuantityUnitsAction
   addDeviceMeasuredQuantity!: AddDeviceMeasuredQuantityAction
   loadDeviceMeasuredQuantities!: LoadDeviceMeasuredQuantitiesAction
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -182,7 +179,7 @@ export default class DevicePropertyAddPage extends mixins(CheckEditAccess) {
     }
 
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.addDeviceMeasuredQuantity({
         deviceId: this.deviceId,
         deviceMeasuredQuantity: this.valueCopy
@@ -193,7 +190,7 @@ export default class DevicePropertyAddPage extends mixins(CheckEditAccess) {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to save measured quantity')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/devices/_deviceId/parameters.vue b/pages/devices/_deviceId/parameters.vue
index 07d72078e..8478e2db6 100644
--- a/pages/devices/_deviceId/parameters.vue
+++ b/pages/devices/_deviceId/parameters.vue
@@ -30,9 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-    />
     <NuxtChild />
   </div>
 </template>
@@ -47,26 +44,25 @@ import {
 } from '@/store/devices'
 import { LoadUnitsAction } from '@/store/vocabulary'
 
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 
 @Component({
-  components: { ProgressIndicator },
   methods: {
     ...mapActions('devices', ['loadDeviceParameters', 'loadDeviceParameterChangeActions']),
-    ...mapActions('vocabulary', ['loadUnits'])
+    ...mapActions('vocabulary', ['loadUnits']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class DeviceParametersPage extends Vue {
-  private isLoading = false
-
   // vuex definition for typescript check
   loadDeviceParameters!: LoadDeviceParametersAction
   loadDeviceParameterChangeActions!: LoadDeviceParameterChangeActionsAction
   loadUnits!: LoadUnitsAction
+  setLoading!: SetLoadingAction
 
   async fetch (): Promise<void> {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await Promise.all([
         this.loadDeviceParameters(this.deviceId),
         this.loadDeviceParameterChangeActions(this.deviceId),
@@ -75,7 +71,7 @@ export default class DeviceParametersPage extends Vue {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to fetch parameters')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/devices/_deviceId/parameters/_parameterId/copy.vue b/pages/devices/_deviceId/parameters/_parameterId/copy.vue
index bdb4f0e33..a5acbf2b5 100644
--- a/pages/devices/_deviceId/parameters/_parameterId/copy.vue
+++ b/pages/devices/_deviceId/parameters/_parameterId/copy.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card
       flat
     >
@@ -99,7 +95,7 @@ import { Parameter } from '@/models/Parameter'
 import BaseList from '@/components/shared/BaseList.vue'
 import ParameterForm from '@/components/shared/ParameterForm.vue'
 import ParameterListItem from '@/components/shared/ParameterListItem.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
 
 @Component({
@@ -108,7 +104,6 @@ import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
     BaseList,
     ParameterForm,
     ParameterListItem,
-    ProgressIndicator,
     SaveAndCancelButtons
   },
   computed: {
@@ -116,12 +111,12 @@ import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
     ...mapState('devices', ['deviceParameter', 'deviceParameters'])
   },
   methods: {
-    ...mapActions('devices', ['addDeviceParameter', 'loadDeviceParameters', 'loadDeviceParameter'])
+    ...mapActions('devices', ['addDeviceParameter', 'loadDeviceParameters', 'loadDeviceParameter']),
+    ...mapActions('progressindicator', ['setLoading'])
   },
   scrollToTop: true
 })
 export default class ParametersCopyPage extends mixins(CheckEditAccess) {
-  private isSaving = false
   private valueCopy: Parameter = new Parameter()
 
   // vuex definition for typescript check
@@ -131,6 +126,7 @@ export default class ParametersCopyPage extends mixins(CheckEditAccess) {
   loadDeviceParameters!: LoadDeviceParametersAction
   addDeviceParameter!: AddDeviceParameterAction
   units!: VocabularyState['units']
+  setLoading!: SetLoadingAction
 
   mounted () {
     (this.$refs.parameterForm as ParameterForm).focus()
@@ -187,7 +183,7 @@ export default class ParametersCopyPage extends mixins(CheckEditAccess) {
     }
 
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.addDeviceParameter({
         deviceId: this.deviceId,
         parameter: this.valueCopy
@@ -198,7 +194,7 @@ export default class ParametersCopyPage extends mixins(CheckEditAccess) {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to copy parameter')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/devices/_deviceId/parameters/_parameterId/edit.vue b/pages/devices/_deviceId/parameters/_parameterId/edit.vue
index 3f421113e..ddcff1b6f 100644
--- a/pages/devices/_deviceId/parameters/_parameterId/edit.vue
+++ b/pages/devices/_deviceId/parameters/_parameterId/edit.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card
       flat
     >
@@ -100,7 +96,7 @@ import { Parameter } from '@/models/Parameter'
 import BaseList from '@/components/shared/BaseList.vue'
 import ParameterForm from '@/components/shared/ParameterForm.vue'
 import ParameterListItem from '@/components/shared/ParameterListItem.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
 
 @Component({
@@ -109,7 +105,6 @@ import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
     BaseList,
     ParameterForm,
     ParameterListItem,
-    ProgressIndicator,
     SaveAndCancelButtons
   },
   computed: {
@@ -117,12 +112,12 @@ import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
     ...mapState('devices', ['deviceParameter', 'deviceParameters'])
   },
   methods: {
-    ...mapActions('devices', ['updateDeviceParameter', 'loadDeviceParameters', 'loadDeviceParameter'])
+    ...mapActions('devices', ['updateDeviceParameter', 'loadDeviceParameters', 'loadDeviceParameter']),
+    ...mapActions('progressindicator', ['setLoading'])
   },
   scrollToTop: true
 })
 export default class ParametersEditPage extends mixins(CheckEditAccess) {
-  private isSaving = false
   private valueCopy: Parameter = new Parameter()
 
   // vuex definition for typescript check
@@ -132,6 +127,7 @@ export default class ParametersEditPage extends mixins(CheckEditAccess) {
   loadDeviceParameters!: LoadDeviceParametersAction
   updateDeviceParameter!: UpdateDeviceParameterAction
   units!: VocabularyState['units']
+  setLoading!: SetLoadingAction
 
   mounted () {
     (this.$refs.parameterForm as ParameterForm).focus()
@@ -185,7 +181,7 @@ export default class ParametersEditPage extends mixins(CheckEditAccess) {
       return
     }
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.updateDeviceParameter({
         deviceId: this.deviceId,
         parameter: this.valueCopy
@@ -196,7 +192,7 @@ export default class ParametersEditPage extends mixins(CheckEditAccess) {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to save parameter')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/devices/_deviceId/parameters/index.vue b/pages/devices/_deviceId/parameters/index.vue
index 454d1837b..db8062272 100644
--- a/pages/devices/_deviceId/parameters/index.vue
+++ b/pages/devices/_deviceId/parameters/index.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isInProgress"
-      dark
-    />
     <v-card-actions
       v-if="editable"
     >
@@ -41,7 +37,7 @@ permissions and limitations under the Licence.
       <v-btn
         color="primary"
         small
-        :disabled="isFetching"
+        :disabled="isLoading"
         :to="'/devices/' + deviceId + '/parameters/new'"
       >
         Add Parameter
@@ -112,7 +108,7 @@ permissions and limitations under the Licence.
       v-if="editable && parameterToDelete"
       v-model="showDeleteDialog"
       title="Delete Parameter"
-      :disabled="isInProgress"
+      :disabled="isLoading"
       @cancel="closeDialog"
       @delete="deleteAndCloseDialog"
     >
@@ -122,7 +118,7 @@ permissions and limitations under the Licence.
 </template>
 
 <script lang="ts">
-import { Component, Vue, InjectReactive, Prop } from 'nuxt-property-decorator'
+import { Component, Vue, InjectReactive } from 'nuxt-property-decorator'
 import { mapActions, mapState } from 'vuex'
 
 import {
@@ -144,7 +140,7 @@ import ExpandableText from '@/components/shared/ExpandableText.vue'
 import HintCard from '@/components/HintCard.vue'
 import ParameterListItem from '@/components/shared/ParameterListItem.vue'
 import ParameterValueTable from '@/components/shared/ParameterValueTable.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 
 @Component({
   components: {
@@ -155,30 +151,25 @@ import ProgressIndicator from '@/components/ProgressIndicator.vue'
     ExpandableText,
     HintCard,
     ParameterListItem,
-    ParameterValueTable,
-    ProgressIndicator
+    ParameterValueTable
   },
   computed: {
     ...mapState('vocabulary', ['units']),
-    ...mapState('devices', ['deviceParameters', 'deviceParameterChangeActions'])
+    ...mapState('devices', ['deviceParameters', 'deviceParameterChangeActions']),
+    ...mapState('progressindicator', ['isLoading'])
   },
   methods: {
-    ...mapActions('devices', ['deleteDeviceParameter', 'loadDeviceParameters'])
+    ...mapActions('devices', ['deleteDeviceParameter', 'loadDeviceParameters']),
+    ...mapActions('progressindicator', ['setLoading'])
   },
   scrollToTop: true
 })
-export default class DevicePropertyShowPage extends Vue {
+export default class DeviceParameterShowPage extends Vue {
   @InjectReactive()
   private editable!: boolean
 
-  @Prop({
-    type: Boolean
-  })
-  private isFetching!: boolean
-
-  private isInProgress = false
-
   private showDeleteDialog = false
+
   private parameterToDelete: Parameter | null = null
 
   // vuex definition for typescript check
@@ -187,6 +178,8 @@ export default class DevicePropertyShowPage extends Vue {
   deviceParameterChangeActions!: DevicesState['deviceParameterChangeActions']
   loadDeviceParameters!: LoadDeviceParametersAction
   deleteDeviceParameter!: DeleteDeviceParameterAction
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
 
   get deviceId (): string {
     return this.$route.params.deviceId
@@ -207,7 +200,7 @@ export default class DevicePropertyShowPage extends Vue {
       return
     }
     try {
-      this.isInProgress = true
+      this.setLoading(true)
 
       await this.deleteDeviceParameter(this.parameterToDelete.id)
       this.loadDeviceParameters(this.deviceId)
@@ -219,7 +212,7 @@ export default class DevicePropertyShowPage extends Vue {
         this.$store.commit('snackbar/setError', 'Failed to delete parameter')
       }
     } finally {
-      this.isInProgress = false
+      this.setLoading(false)
       this.closeDialog()
     }
   }
diff --git a/pages/devices/_deviceId/parameters/new.vue b/pages/devices/_deviceId/parameters/new.vue
index bfbe5297f..53234caff 100644
--- a/pages/devices/_deviceId/parameters/new.vue
+++ b/pages/devices/_deviceId/parameters/new.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card
       flat
     >
@@ -99,8 +95,8 @@ import { Parameter } from '@/models/Parameter'
 import BaseList from '@/components/shared/BaseList.vue'
 import ParameterForm from '@/components/shared/ParameterForm.vue'
 import ParameterListItem from '@/components/shared/ParameterListItem.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 
 @Component({
   middleware: ['auth'],
@@ -108,7 +104,6 @@ import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
     BaseList,
     ParameterForm,
     ParameterListItem,
-    ProgressIndicator,
     SaveAndCancelButtons
   },
   computed: {
@@ -116,12 +111,12 @@ import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
     ...mapState('devices', ['deviceParameters', 'deviceParameterChangeActions'])
   },
   methods: {
-    ...mapActions('devices', ['addDeviceParameter', 'loadDeviceParameters'])
+    ...mapActions('devices', ['addDeviceParameter', 'loadDeviceParameters']),
+    ...mapActions('progressindicator', ['setLoading'])
   },
   scrollToTop: true
 })
 export default class ParametersAddPage extends mixins(CheckEditAccess) {
-  private isSaving = false
   private value: Parameter = new Parameter()
 
   // vuex definition for typescript check
@@ -130,6 +125,7 @@ export default class ParametersAddPage extends mixins(CheckEditAccess) {
   deviceParameterChangeActions!: DevicesState['deviceParameterChangeActions']
   addDeviceParameter!: AddDeviceParameterAction
   loadDeviceParameters!: LoadDeviceParametersAction
+  setLoading!: SetLoadingAction
 
   mounted () {
     (this.$refs.parameterForm as ParameterForm).focus()
@@ -168,7 +164,7 @@ export default class ParametersAddPage extends mixins(CheckEditAccess) {
     }
 
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.addDeviceParameter({
         deviceId: this.deviceId,
         parameter: this.value
@@ -179,7 +175,7 @@ export default class ParametersAddPage extends mixins(CheckEditAccess) {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to save parameter')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/devices/copy/_deviceId.vue b/pages/devices/copy/_deviceId.vue
index e780e2726..eed936d8f 100644
--- a/pages/devices/copy/_deviceId.vue
+++ b/pages/devices/copy/_deviceId.vue
@@ -35,10 +35,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isInProgress"
-      :dark="isSaving"
-    />
     <v-card
       flat
     >
@@ -156,14 +152,13 @@ import { Device } from '@/models/Device'
 
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
 import DeviceBasicDataForm from '@/components/DeviceBasicDataForm.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import NonModelOptionsForm, { NonModelOptions } from '@/components/shared/NonModelOptionsForm.vue'
 
 @Component({
   components: {
     SaveAndCancelButtons,
     DeviceBasicDataForm,
-    ProgressIndicator,
     NonModelOptionsForm
   },
   middleware: ['auth'],
@@ -173,14 +168,13 @@ import NonModelOptionsForm, { NonModelOptions } from '@/components/shared/NonMod
   },
   methods: {
     ...mapActions('devices', ['copyDevice', 'loadDevice', 'createPid']),
-    ...mapActions('appbar', ['setTitle', 'setTabs'])
+    ...mapActions('appbar', ['setTitle', 'setTabs']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 // @ts-ignore
 export default class DeviceCopyPage extends Vue {
   private deviceToCopy: Device = new Device()
-  private isSaving = false
-  private isLoading = false
   private copyOptions: NonModelOptions = {
     persistentIdentifierShouldBeCreated: false
   }
@@ -205,11 +199,12 @@ export default class DeviceCopyPage extends Vue {
   setTabs!: SetTabsAction
   setTitle!: SetTitleAction
   createPid!: CreatePidAction
+  setLoading!: SetLoadingAction
 
   async created () {
     this.initializeAppBar()
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.loadDevice({
         deviceId: this.deviceId,
         includeContacts: true,
@@ -232,7 +227,7 @@ export default class DeviceCopyPage extends Vue {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Loading device failed')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
@@ -240,10 +235,6 @@ export default class DeviceCopyPage extends Vue {
     return this.$route.params.deviceId
   }
 
-  get isInProgress (): boolean {
-    return this.isLoading || this.isSaving
-  }
-
   getPreparedDeviceForCopy (): Device | null {
     if (!this.device) {
       return null
@@ -273,7 +264,7 @@ export default class DeviceCopyPage extends Vue {
     }
 
     try {
-      this.isSaving = true
+      this.setLoading(true)
       const savedDeviceId = await this.copyDevice({
         device: this.deviceToCopy,
         copyContacts: this.copyContacts,
@@ -291,7 +282,7 @@ export default class DeviceCopyPage extends Vue {
     } catch (_error) {
       this.$store.commit('snackbar/setError', 'Copy failed')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/devices/index.vue b/pages/devices/index.vue
index d1d5f1854..f71d04003 100644
--- a/pages/devices/index.vue
+++ b/pages/devices/index.vue
@@ -176,14 +176,7 @@ permissions and limitations under the Licence.
       </v-tab-item>
     </v-tabs-items>
 
-    <v-progress-circular
-      v-if="loading"
-      class="progress-spinner"
-      color="primary"
-      indeterminate
-    />
-
-    <div v-if="devices.length <=0 && !loading">
+    <div v-if="devices.length <=0 && !isLoading">
       <p class="text-center">
         There are no devices that match your search criteria.
       </p>
@@ -201,16 +194,6 @@ permissions and limitations under the Licence.
           <v-subheader>
             <FoundEntries v-model="totalCount" entity-name="device" />
             <template v-if="devices.length>0">
-              <v-dialog v-model="processing" max-width="100">
-                <v-card>
-                  <v-card-text>
-                    <div class="text-center pt-2">
-                      <v-progress-circular indeterminate />
-                    </div>
-                  </v-card-text>
-                </v-card>
-              </v-dialog>
-
               <v-menu
                 close-on-click
                 close-on-content-click
@@ -263,7 +246,7 @@ permissions and limitations under the Licence.
         >
           <v-pagination
             v-model="page"
-            :disabled="loading"
+            :disabled="isLoading"
             :length="totalPages"
             :total-visible="7"
             @input="runSearch"
@@ -320,7 +303,7 @@ permissions and limitations under the Licence.
       </BaseList>
       <v-pagination
         v-model="page"
-        :disabled="loading"
+        :disabled="isLoading"
         :length="totalPages"
         :total-visible="7"
         @input="runSearch"
@@ -422,6 +405,7 @@ import PageSizeSelect from '@/components/shared/PageSizeSelect.vue'
 import PermissionGroupSearchSelect from '@/components/PermissionGroupSearchSelect.vue'
 import { Visibility } from '@/models/Visibility'
 import FoundEntries from '@/components/shared/FoundEntries.vue'
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 
 @Component({
   components: {
@@ -444,6 +428,7 @@ import FoundEntries from '@/components/shared/FoundEntries.vue'
   },
   computed: {
     ...mapGetters('permissions', ['canDeleteEntity', 'canArchiveEntity', 'canRestoreEntity', 'canAccessEntity', 'permissionGroups']),
+    ...mapState('progressindicator', ['isLoading']),
     ...mapState('appbar', ['activeTab']),
     ...mapState('vocabulary', ['devicetypes', 'manufacturers', 'equipmentstatus']),
     ...mapState('devices', ['devices', 'pageNumber', 'pageSize', 'totalPages', 'totalCount', 'device']),
@@ -453,13 +438,11 @@ import FoundEntries from '@/components/shared/FoundEntries.vue'
     ...mapActions('vocabulary', ['loadEquipmentstatus', 'loadDevicetypes', 'loadManufacturers']),
     ...mapActions('devices', ['searchDevicesPaginated', 'setPageNumber', 'setPageSize', 'exportAsCsv', 'deleteDevice', 'archiveDevice', 'restoreDevice', 'exportAsSensorML', 'loadDevice', 'replaceDeviceInDevices', 'getSensorMLUrl']),
     ...mapActions('appbar', ['setTitle', 'setTabs', 'setActiveTab']),
-    ...mapActions('permissions', ['loadPermissionGroups'])
+    ...mapActions('permissions', ['loadPermissionGroups']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class SearchDevicesPage extends Vue {
-  private loading: boolean = false
-  private processing: boolean = false
-
   private selectedSearchManufacturers: Manufacturer[] = []
   private selectedSearchStates: Status[] = []
   private selectedSearchDeviceTypes: DeviceType[] = []
@@ -514,11 +497,13 @@ export default class SearchDevicesPage extends Vue {
   replaceDeviceInDevices!: ReplaceDeviceInDevicesAction
   device!: DevicesState['device']
   getSensorMLUrl!: GetSensorMLUrlAction
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
 
   async created () {
     this.initializeAppBar()
     try {
-      this.loading = true
+      this.setLoading(true)
       await Promise.all([
         this.loadEquipmentstatus(),
         this.loadDevicetypes(),
@@ -530,7 +515,7 @@ export default class SearchDevicesPage extends Vue {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Loading of devices failed')
     } finally {
-      this.loading = false
+      this.setLoading(false)
     }
   }
 
@@ -631,21 +616,21 @@ export default class SearchDevicesPage extends Vue {
 
   async runSearch (): Promise<void> {
     try {
-      this.loading = true
+      this.setLoading(true)
       this.initUrlQueryParams()
       await this.searchDevicesPaginated(this.searchParams)
       this.setPageAndSizeInUrl()
     } catch {
       this.$store.commit('snackbar/setError', 'Loading of devices failed')
     } finally {
-      this.loading = false
+      this.setLoading(false)
     }
   }
 
   async exportCsvUrl (): Promise<string> {
-    this.processing = true
+    this.setLoading(true)
     const blob = await this.exportAsCsv(this.searchParams)
-    this.processing = false
+    this.setLoading(false)
     return window.URL.createObjectURL(blob)
   }
 
@@ -664,14 +649,14 @@ export default class SearchDevicesPage extends Vue {
       return
     }
     try {
-      this.loading = true
+      this.setLoading(true)
       await this.deleteDevice(this.deviceToDelete.id)
       this.runSearch()
       this.$store.commit('snackbar/setSuccess', 'Device deleted')
     } catch (_error) {
       this.$store.commit('snackbar/setError', 'Device could not be deleted')
     } finally {
-      this.loading = false
+      this.setLoading(false)
       this.closeDialog()
     }
   }
@@ -691,7 +676,7 @@ export default class SearchDevicesPage extends Vue {
       return
     }
     try {
-      this.loading = true
+      this.setLoading(true)
       await this.archiveDevice(this.deviceToArchive.id)
       await this.loadDevice({
         deviceId: this.deviceToArchive.id,
@@ -703,14 +688,14 @@ export default class SearchDevicesPage extends Vue {
     } catch (_error) {
       this.$store.commit('snackbar/setError', 'Device could not be archived')
     } finally {
-      this.loading = false
+      this.setLoading(false)
       this.closeArchiveDialog()
     }
   }
 
   async runRestoreDevice (device: Device) {
     if (device.id) {
-      this.loading = true
+      this.setLoading(true)
       try {
         await this.restoreDevice(device.id)
         await this.loadDevice({
@@ -723,7 +708,7 @@ export default class SearchDevicesPage extends Vue {
       } catch (error) {
         this.$store.commit('snackbar/setError', 'Device could not be restored')
       } finally {
-        this.loading = false
+        this.setLoading(false)
       }
     }
   }
diff --git a/pages/devices/new.vue b/pages/devices/new.vue
index 5d0780cc4..7a3d12bf6 100644
--- a/pages/devices/new.vue
+++ b/pages/devices/new.vue
@@ -33,10 +33,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-      dark
-    />
     <v-card
       flat
     >
@@ -77,7 +73,7 @@ import { SetTitleAction, SetTabsAction } from '@/store/appbar'
 import { CreatePidAction, SaveDeviceAction } from '@/store/devices'
 
 import DeviceBasicDataForm from '@/components/DeviceBasicDataForm.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import NonModelOptionsForm, { NonModelOptions } from '@/components/shared/NonModelOptionsForm.vue'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
 import SerialNumberWarningDialog from '@/components/shared/SerialNumberWarningDialog.vue'
@@ -88,20 +84,20 @@ import { Device } from '@/models/Device'
   components: {
     SaveAndCancelButtons,
     DeviceBasicDataForm,
-    ProgressIndicator,
     NonModelOptionsForm,
     SerialNumberWarningDialog
   },
   middleware: ['auth'],
   methods: {
     ...mapActions('devices', ['saveDevice', 'createPid']),
-    ...mapActions('appbar', ['setTitle', 'setTabs'])
+    ...mapActions('appbar', ['setTitle', 'setTabs']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 // @ts-ignore
 export default class DeviceNewPage extends Vue {
   private device: Device = new Device()
-  private isLoading: boolean = false
+
   private showSerialNumberWarning = false
   private wantsToSaveWithoutSerialNumber = false
   private createOptions: NonModelOptions = {
@@ -113,6 +109,7 @@ export default class DeviceNewPage extends Vue {
   setTabs!: SetTabsAction
   setTitle!: SetTitleAction
   createPid!: CreatePidAction
+  setLoading!: SetLoadingAction
 
   created () {
     this.initializeAppBar()
@@ -137,7 +134,7 @@ export default class DeviceNewPage extends Vue {
     }
 
     try {
-      this.isLoading = true
+      this.setLoading(true)
       const savedDevice = await this.saveDevice(this.device)
       if (this.createOptions.persistentIdentifierShouldBeCreated) {
         savedDevice.persistentIdentifier = await this.createPid(savedDevice.id)
@@ -147,7 +144,7 @@ export default class DeviceNewPage extends Vue {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Save failed')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/platforms/_platformId.vue b/pages/platforms/_platformId.vue
index 661e40dbe..eff61b903 100644
--- a/pages/platforms/_platformId.vue
+++ b/pages/platforms/_platformId.vue
@@ -30,9 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-    />
     <v-card flat>
       <center>
         <v-alert
@@ -68,12 +65,11 @@ import { SetTitleAction, SetTabsAction } from '@/store/appbar'
 import { PlatformsState, LoadPlatformAction } from '@/store/platforms'
 import { CanAccessEntityGetter, CanModifyEntityGetter, CanDeleteEntityGetter, CanArchiveEntityGetter, CanRestoreEntityGetter } from '@/store/permissions'
 
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import ModificationInfo from '@/components/ModificationInfo.vue'
 
 @Component({
   components: {
-    ProgressIndicator,
     ModificationInfo
   },
   computed: {
@@ -82,12 +78,11 @@ import ModificationInfo from '@/components/ModificationInfo.vue'
   },
   methods: {
     ...mapActions('platforms', ['loadPlatform']),
-    ...mapActions('appbar', ['setTitle', 'setTabs'])
+    ...mapActions('appbar', ['setTitle', 'setTabs']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class PlatformPage extends Vue {
-  private isLoading: boolean = false
-
   @ProvideReactive()
     editable: boolean = false
 
@@ -111,6 +106,7 @@ export default class PlatformPage extends Vue {
   canRestoreEntity!: CanRestoreEntityGetter
   setTabs!: SetTabsAction
   setTitle!: SetTitleAction
+  setLoading!: SetLoadingAction
 
   created () {
     this.initializeAppBar()
@@ -118,7 +114,7 @@ export default class PlatformPage extends Vue {
 
   async fetch () {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.loadPlatform({
         platformId: this.platformId,
         includeContacts: false,
@@ -142,7 +138,7 @@ export default class PlatformPage extends Vue {
       this.$store.commit('snackbar/setError', 'Loading platform failed')
       this.$router.replace('/platforms/')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/platforms/_platformId/actions.vue b/pages/platforms/_platformId/actions.vue
index 28980fa98..5f65b0c28 100644
--- a/pages/platforms/_platformId/actions.vue
+++ b/pages/platforms/_platformId/actions.vue
@@ -34,9 +34,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-    />
     <NuxtChild />
   </div>
 </template>
@@ -47,26 +44,27 @@ import { mapActions } from 'vuex'
 
 import { LoadAllPlatformActionsAction } from '@/store/platforms'
 
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 
 @Component({
-  components: { ProgressIndicator },
-  methods: mapActions('platforms', ['loadAllPlatformActions'])
+  methods: {
+    ...mapActions('platforms', ['loadAllPlatformActions']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class PlatformActionsPage extends Vue {
-  private isLoading = false
-
   // vuex definition for typescript check
   loadAllPlatformActions!: LoadAllPlatformActionsAction
+  setLoading!: SetLoadingAction
 
   async fetch () {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.loadAllPlatformActions(this.platformId)
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to fetch actions')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/platforms/_platformId/actions/generic-platform-actions/_actionId/edit.vue b/pages/platforms/_platformId/actions/generic-platform-actions/_actionId/edit.vue
index 8ed2731f2..cdaa25a18 100644
--- a/pages/platforms/_platformId/actions/generic-platform-actions/_actionId/edit.vue
+++ b/pages/platforms/_platformId/actions/generic-platform-actions/_actionId/edit.vue
@@ -34,10 +34,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isInProgress"
-      :dark="isSaving"
-    />
     <!-- just to be consistent with the new mask, we show the selected action type as an disabled v-select here -->
     <v-select
       :value="action.actionTypeName"
@@ -89,24 +85,27 @@ import {
 import { GenericAction } from '@/models/GenericAction'
 
 import GenericActionForm from '@/components/actions/GenericActionForm.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
 
 @Component({
   components: {
     SaveAndCancelButtons,
-    ProgressIndicator,
     GenericActionForm
   },
   scrollToTop: true,
   middleware: ['auth'],
-  computed: mapState('platforms', ['platformGenericAction', 'platformAttachments']),
-  methods: mapActions('platforms', ['loadPlatformGenericAction', 'loadAllPlatformActions', 'loadPlatformAttachments', 'updatePlatformGenericAction'])
+  computed: {
+    ...mapState('platforms', ['platformGenericAction', 'platformAttachments']),
+    ...mapState('progressindicator', ['isLoading'])
+  },
+  methods: {
+    ...mapActions('platforms', ['loadPlatformGenericAction', 'loadAllPlatformActions', 'loadPlatformAttachments', 'updatePlatformGenericAction']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class EditPlatformAction extends mixins(CheckEditAccess) {
   private action: GenericAction = new GenericAction()
-  private isSaving = false
-  private isLoading = false
 
   // vuex definition for typescript check
   platforms!: PlatformsState['platforms']
@@ -115,6 +114,8 @@ export default class EditPlatformAction extends mixins(CheckEditAccess) {
   loadPlatformGenericAction!: LoadPlatformGenericActionAction
   loadPlatformAttachments!: LoadPlatformAttachmentsAction
   updatePlatformGenericAction!: UpdatePlatformGenericActionAction
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -140,7 +141,7 @@ export default class EditPlatformAction extends mixins(CheckEditAccess) {
 
   async fetch (): Promise<void> {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await Promise.all([
         this.loadPlatformGenericAction(this.actionId),
         this.loadPlatformAttachments(this.platformId)
@@ -151,7 +152,7 @@ export default class EditPlatformAction extends mixins(CheckEditAccess) {
     } catch (error) {
       this.$store.commit('snackbar/setError', 'Failed to fetch action')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
@@ -163,10 +164,6 @@ export default class EditPlatformAction extends mixins(CheckEditAccess) {
     return this.$route.params.actionId
   }
 
-  get isInProgress (): boolean {
-    return this.isLoading || this.isSaving
-  }
-
   async save () {
     if (!(this.$refs.genericPlatformActionForm as Vue & { isValid: () => boolean }).isValid()) {
       this.$store.commit('snackbar/setError', 'Please correct the errors')
@@ -174,7 +171,7 @@ export default class EditPlatformAction extends mixins(CheckEditAccess) {
     }
 
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.updatePlatformGenericAction({ platformId: this.platformId, genericPlatformAction: this.action })
       this.loadAllPlatformActions(this.platformId)
       this.$store.commit('snackbar/setSuccess', `${this.action.actionTypeName} updated`)
@@ -182,7 +179,7 @@ export default class EditPlatformAction extends mixins(CheckEditAccess) {
     } catch (err) {
       this.$store.commit('snackbar/setError', 'Failed to save the action')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/platforms/_platformId/actions/index.vue b/pages/platforms/_platformId/actions/index.vue
index d7636fac6..9f1e76d55 100644
--- a/pages/platforms/_platformId/actions/index.vue
+++ b/pages/platforms/_platformId/actions/index.vue
@@ -31,10 +31,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card-actions>
       <v-spacer />
       <v-btn
@@ -190,7 +186,7 @@ import ParameterChangeActionCard from '@/components/actions/ParameterChangeActio
 import PlatformActionTimeline from '@/components/actions/PlatformActionTimeline.vue'
 import PlatformMountActionCard from '@/components/actions/PlatformMountActionCard.vue'
 import PlatformUnmountActionCard from '@/components/actions/PlatformUnmountActionCard.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import SoftwareUpdateActionCard from '@/components/actions/SoftwareUpdateActionCard.vue'
 
 @Component({
@@ -204,26 +200,27 @@ import SoftwareUpdateActionCard from '@/components/actions/SoftwareUpdateActionC
     PlatformActionTimeline,
     PlatformMountActionCard,
     PlatformUnmountActionCard,
-    ProgressIndicator,
     SoftwareUpdateActionCard
   },
   computed: {
     ...mapGetters('platforms', ['actions']),
     ...mapState('platforms', ['platform'])
   },
-  methods: mapActions('platforms', [
-    'loadAllPlatformActions',
-    'deletePlatformSoftwareUpdateAction',
-    'deletePlatformGenericAction',
-    'deletePlatformParameterChangeAction',
-    'downloadAttachment'
-  ])
+  methods: {
+    ...mapActions('platforms', [
+      'loadAllPlatformActions',
+      'deletePlatformSoftwareUpdateAction',
+      'deletePlatformGenericAction',
+      'deletePlatformParameterChangeAction',
+      'downloadAttachment'
+    ]),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class PlatformActionsShowPage extends Vue {
   @InjectReactive()
     editable!: boolean
 
-  private isSaving: boolean = false
   private genericActionToDelete: GenericAction | null = null
   private softwareUpdateActionToDelete: SoftwareUpdateAction | null = null
   private parameterChangeActionToDelete: ParameterChangeAction | null = null
@@ -240,6 +237,7 @@ export default class PlatformActionsShowPage extends Vue {
   deletePlatformSoftwareUpdateAction!: DeletePlatformSoftwareUpdateActionAction
   deletePlatformParameterChangeAction!: DeletePlatformParameterChangeActionAction
   downloadAttachment!: DownloadAttachmentAction
+  setLoading!: SetLoadingAction
 
   get platformId (): string {
     return this.$route.params.platformId
@@ -313,14 +311,14 @@ export default class PlatformActionsShowPage extends Vue {
     }
 
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.deletePlatformGenericAction(this.genericActionToDelete.id)
       this.loadAllPlatformActions(this.platformId)
       this.$store.commit('snackbar/setSuccess', 'Generic action deleted')
     } catch (_error) {
       this.$store.commit('snackbar/setError', 'Generic action could not be deleted')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 
@@ -330,14 +328,14 @@ export default class PlatformActionsShowPage extends Vue {
     }
 
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.deletePlatformSoftwareUpdateAction(this.softwareUpdateActionToDelete.id)
       this.loadAllPlatformActions(this.platformId)
       this.$store.commit('snackbar/setSuccess', 'Software update action deleted')
     } catch (_error) {
       this.$store.commit('snackbar/setError', 'Software update action could not be deleted')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 
@@ -347,14 +345,14 @@ export default class PlatformActionsShowPage extends Vue {
     }
 
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.deletePlatformParameterChangeAction(this.parameterChangeActionToDelete.id)
       this.loadAllPlatformActions(this.platformId)
       this.$store.commit('snackbar/setSuccess', 'Parameter value change action deleted')
     } catch (_error) {
       this.$store.commit('snackbar/setError', 'Parameter value change action could not be deleted')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/platforms/_platformId/actions/new.vue b/pages/platforms/_platformId/actions/new.vue
index 19179c85c..880adae5e 100644
--- a/pages/platforms/_platformId/actions/new.vue
+++ b/pages/platforms/_platformId/actions/new.vue
@@ -34,9 +34,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-    />
     <v-card
       flat
     >
@@ -76,8 +73,7 @@ import { Component, mixins } from 'nuxt-property-decorator'
 import { mapActions, mapGetters, mapState } from 'vuex'
 
 import ActionTypeDialog from '@/components/shared/ActionTypeDialog.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
-
+import { SetLoadingAction } from '@/store/progressindicator'
 import CheckEditAccess from '@/mixins/CheckEditAccess'
 import { ActionType } from '@/models/ActionType'
 
@@ -96,7 +92,7 @@ const KIND_OF_ACTION_TYPE_GENERIC_PLATFORM_ACTION = 'generic_platform_action'
 const KIND_OF_ACTION_TYPE_PARAMETER_CHANGE_ACTION = 'parameter_change_action'
 
 @Component({
-  components: { ActionTypeDialog, ProgressIndicator },
+  components: { ActionTypeDialog },
   middleware: ['auth'],
   computed: {
     ...mapGetters('vocabulary', ['platformActionTypeItems']),
@@ -104,11 +100,11 @@ const KIND_OF_ACTION_TYPE_PARAMETER_CHANGE_ACTION = 'parameter_change_action'
   },
   methods: {
     ...mapActions('vocabulary', ['loadPlatformGenericActionTypes']),
-    ...mapActions('platforms', ['loadPlatformAttachments', 'setChosenKindOfPlatformAction', 'loadPlatformParameters'])
+    ...mapActions('platforms', ['loadPlatformAttachments', 'setChosenKindOfPlatformAction', 'loadPlatformParameters']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class NewPlatformAction extends mixins(CheckEditAccess) {
-  private isLoading: boolean = false
   private showNewActionTypeDialog = false
 
   // vuex definition for typescript check
@@ -118,6 +114,7 @@ export default class NewPlatformAction extends mixins(CheckEditAccess) {
   chosenKindOfPlatformAction!: PlatformsState['chosenKindOfPlatformAction']
   loadPlatformParameters!: LoadPlatformParametersAction
   setChosenKindOfPlatformAction!: SetChosenKindOfPlatformActionAction
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -143,7 +140,7 @@ export default class NewPlatformAction extends mixins(CheckEditAccess) {
 
   async fetch (): Promise<void> {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       this.chosenKindOfAction = null
       await Promise.all([
         this.loadPlatformGenericActionTypes(),
@@ -153,7 +150,7 @@ export default class NewPlatformAction extends mixins(CheckEditAccess) {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to fetch action types')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/platforms/_platformId/actions/new/generic-platform-actions.vue b/pages/platforms/_platformId/actions/new/generic-platform-actions.vue
index a8271e43d..e93e1138c 100644
--- a/pages/platforms/_platformId/actions/new/generic-platform-actions.vue
+++ b/pages/platforms/_platformId/actions/new/generic-platform-actions.vue
@@ -31,10 +31,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card-actions>
       <v-spacer />
       <SaveAndCancelButtons
@@ -79,23 +75,26 @@ import { GenericAction } from '@/models/GenericAction'
 
 import GenericActionForm from '@/components/actions/GenericActionForm.vue'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 
 @Component({
   middleware: ['auth'],
-  components: { ProgressIndicator, SaveAndCancelButtons, GenericActionForm },
+  components: { SaveAndCancelButtons, GenericActionForm },
   computed: mapState('platforms', ['platformAttachments', 'chosenKindOfPlatformAction']),
-  methods: mapActions('platforms', ['addPlatformGenericAction', 'loadAllPlatformActions'])
+  methods: {
+    ...mapActions('platforms', ['addPlatformGenericAction', 'loadAllPlatformActions']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class NewGenericPlatformAction extends mixins(CheckEditAccess) {
   private genericPlatformAction: GenericAction = new GenericAction()
-  private isSaving: boolean = false
 
   // vuex definition for typescript check
   platformAttachements!: PlatformsState['platformAttachments']
   chosenKindOfPlatformAction!: PlatformsState['chosenKindOfPlatformAction']
   addPlatformGenericAction!: AddPlatformGenericActionAction
   loadAllPlatformActions!: LoadAllPlatformActionsAction
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -139,7 +138,7 @@ export default class NewGenericPlatformAction extends mixins(CheckEditAccess) {
     this.genericPlatformAction.actionTypeUrl = this.chosenKindOfPlatformAction?.uri || ''
 
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.addPlatformGenericAction({ platformId: this.platformId, genericPlatformAction: this.genericPlatformAction })
       this.loadAllPlatformActions(this.platformId)
       const successMessage = this.genericPlatformAction.actionTypeName ?? 'Action'
@@ -148,7 +147,7 @@ export default class NewGenericPlatformAction extends mixins(CheckEditAccess) {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to save the action')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/platforms/_platformId/actions/new/parameter-change-actions.vue b/pages/platforms/_platformId/actions/new/parameter-change-actions.vue
index 6550871ba..da0168f38 100644
--- a/pages/platforms/_platformId/actions/new/parameter-change-actions.vue
+++ b/pages/platforms/_platformId/actions/new/parameter-change-actions.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card-actions>
       <v-spacer />
       <SaveAndCancelButtons
@@ -76,28 +72,30 @@ import {
 import { ParameterChangeAction } from '@/models/ParameterChangeAction'
 
 import ParameterChangeActionForm from '@/components/actions/ParameterChangeActionForm.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
 
 @Component({
   middleware: ['auth'],
   components: {
     SaveAndCancelButtons,
-    ProgressIndicator,
     ParameterChangeActionForm
   },
   computed: mapState('platforms', ['chosenKindOfPlatformAction', 'platformParameters']),
-  methods: mapActions('platforms', ['addPlatformParameterChangeAction', 'loadAllPlatformActions'])
+  methods: {
+    ...mapActions('platforms', ['addPlatformParameterChangeAction', 'loadAllPlatformActions']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class NewParameterChangeAction extends mixins(CheckEditAccess) {
   private parameterChangeAction: ParameterChangeAction = new ParameterChangeAction()
-  private isSaving: boolean = false
 
   // vuex definition for typescript check
   platformParameters!: PlatformsState['platformParameters']
   chosenKindOfPlatformAction!: PlatformsState['chosenKindOfPlatformAction']
   addPlatformParameterChangeAction!: AddPlatformParameterChangeActionAction
   loadAllPlatformActions!: LoadAllPlatformActionsAction
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -145,7 +143,7 @@ export default class NewParameterChangeAction extends mixins(CheckEditAccess) {
     }
 
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.addPlatformParameterChangeAction({
         parameterId: this.parameterChangeAction.parameter.id,
         action: this.parameterChangeAction
@@ -156,7 +154,7 @@ export default class NewParameterChangeAction extends mixins(CheckEditAccess) {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to save the action')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/platforms/_platformId/actions/new/software-update-actions.vue b/pages/platforms/_platformId/actions/new/software-update-actions.vue
index 435f7af44..5ab5cab21 100644
--- a/pages/platforms/_platformId/actions/new/software-update-actions.vue
+++ b/pages/platforms/_platformId/actions/new/software-update-actions.vue
@@ -31,10 +31,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card-actions>
       <v-spacer />
       <SaveAndCancelButtons
@@ -77,28 +73,30 @@ import {
 import { SoftwareUpdateAction } from '@/models/SoftwareUpdateAction'
 
 import SoftwareUpdateActionForm from '@/components/actions/SoftwareUpdateActionForm.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
 
 @Component({
   middleware: ['auth'],
   components: {
     SaveAndCancelButtons,
-    ProgressIndicator,
     SoftwareUpdateActionForm
   },
   computed: mapState('platforms', ['platformAttachments', 'chosenKindOfPlatformAction']),
-  methods: mapActions('platforms', ['addPlatformSoftwareUpdateAction', 'loadAllPlatformActions'])
+  methods: {
+    ...mapActions('platforms', ['addPlatformSoftwareUpdateAction', 'loadAllPlatformActions']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class NewPlatformSoftwareUpdateActions extends mixins(CheckEditAccess) {
   private softwareUpdateAction: SoftwareUpdateAction = new SoftwareUpdateAction()
-  private isSaving: boolean = false
 
   // vuex definition for typescript check
   platformAttachments!: PlatformsState['platformAttachments']
   chosenKindOfPlatformAction!: PlatformsState['chosenKindOfPlatformAction']
   addPlatformSoftwareUpdateAction!: AddPlatformSoftwareUpdateActionAction
   loadAllPlatformActions!: LoadAllPlatformActionsAction
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -141,7 +139,7 @@ export default class NewPlatformSoftwareUpdateActions extends mixins(CheckEditAc
       return
     }
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.addPlatformSoftwareUpdateAction(
         {
           platformId: this.platformId,
@@ -153,7 +151,7 @@ export default class NewPlatformSoftwareUpdateActions extends mixins(CheckEditAc
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to save the action')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/platforms/_platformId/actions/parameter-change-actions/_actionId/edit.vue b/pages/platforms/_platformId/actions/parameter-change-actions/_actionId/edit.vue
index 60a597784..c0d89663b 100644
--- a/pages/platforms/_platformId/actions/parameter-change-actions/_actionId/edit.vue
+++ b/pages/platforms/_platformId/actions/parameter-change-actions/_actionId/edit.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-      dark
-    />
     <v-select
       value="Parameter Value Change"
       :items="['Parameter Value Change']"
@@ -85,25 +81,26 @@ import {
 } from '@/store/platforms'
 
 import { ParameterChangeAction } from '@/models/ParameterChangeAction'
-
+import { SetLoadingAction } from '@/store/progressindicator'
 import ParameterChangeActionForm from '@/components/actions/ParameterChangeActionForm.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
 
 @Component({
   components: {
     ParameterChangeActionForm,
-    ProgressIndicator,
     SaveAndCancelButtons
   },
   scrollToTop: true,
   middleware: ['auth'],
   computed: mapState('platforms', ['platformParameterChangeAction', 'platformParameters']),
-  methods: mapActions('platforms', ['loadPlatformParameterChangeAction', 'loadAllPlatformActions', 'loadPlatformParameters', 'updatePlatformParameterChangeAction'])
+  methods: {
+    ...mapActions('platforms', ['loadPlatformParameterChangeAction', 'loadAllPlatformActions', 'loadPlatformParameters', 'updatePlatformParameterChangeAction']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
+
 export default class PlatformParameterChangeActionEditPage extends mixins(CheckEditAccess) {
   private action: ParameterChangeAction = new ParameterChangeAction()
-  private isLoading = false
 
   // vuex definition for typescript check
   platformParameterChangeAction!: PlatformsState['platformParameterChangeAction']
@@ -112,6 +109,7 @@ export default class PlatformParameterChangeActionEditPage extends mixins(CheckE
   loadPlatformParameters!: LoadPlatformParametersAction
   updatePlatformParameterChangeAction!: UpdatePlatformParameterChangeActionAction
   loadAllPlatformActions!: LoadAllPlatformActionsAction
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -137,7 +135,7 @@ export default class PlatformParameterChangeActionEditPage extends mixins(CheckE
 
   async fetch (): Promise<void> {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await Promise.all([
         this.loadPlatformParameterChangeAction(this.actionId),
         this.loadPlatformParameters(this.platformId)
@@ -148,7 +146,7 @@ export default class PlatformParameterChangeActionEditPage extends mixins(CheckE
     } catch {
       this.$store.commit('snackbar/setError', 'Failed to fetch action')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
@@ -171,7 +169,7 @@ export default class PlatformParameterChangeActionEditPage extends mixins(CheckE
     }
 
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.updatePlatformParameterChangeAction({
         parameterId: this.action.parameter.id,
         action: this.action
@@ -181,7 +179,7 @@ export default class PlatformParameterChangeActionEditPage extends mixins(CheckE
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to save the action')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/platforms/_platformId/actions/software-update-actions/_actionId/edit.vue b/pages/platforms/_platformId/actions/software-update-actions/_actionId/edit.vue
index ff45d1832..48a3e1417 100644
--- a/pages/platforms/_platformId/actions/software-update-actions/_actionId/edit.vue
+++ b/pages/platforms/_platformId/actions/software-update-actions/_actionId/edit.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isInProgress"
-      :dark="isSaving"
-    />
     <v-select
       value="Software Update"
       :items="['Software Update']"
@@ -86,23 +82,26 @@ import { SoftwareUpdateAction } from '@/models/SoftwareUpdateAction'
 
 import SoftwareUpdateActionForm from '@/components/actions/SoftwareUpdateActionForm.vue'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 
 @Component({
   components: {
-    ProgressIndicator,
     SaveAndCancelButtons,
     SoftwareUpdateActionForm
   },
   scrollToTop: true,
   middleware: ['auth'],
-  computed: mapState('platforms', ['platformSoftwareUpdateAction', 'platformAttachments']),
-  methods: mapActions('platforms', ['loadPlatformSoftwareUpdateAction', 'loadAllPlatformActions', 'loadPlatformAttachments', 'updatePlatformSoftwareUpdateAction'])
+  computed: {
+    ...mapState('platforms', ['platformSoftwareUpdateAction', 'platformAttachments']),
+    ...mapState('progressindicator', ['isLoading'])
+  },
+  methods: {
+    ...mapActions('platforms', ['loadPlatformSoftwareUpdateAction', 'loadAllPlatformActions', 'loadPlatformAttachments', 'updatePlatformSoftwareUpdateAction']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class PlatformSoftwareUpdateActionEditPage extends mixins(CheckEditAccess) {
   private action: SoftwareUpdateAction = new SoftwareUpdateAction()
-  private isSaving = false
-  private isLoading = false
 
   // vuex definition for typescript check
   platformSoftwareUpdateAction!: PlatformsState['platformSoftwareUpdateAction']
@@ -111,6 +110,8 @@ export default class PlatformSoftwareUpdateActionEditPage extends mixins(CheckEd
   loadPlatformAttachments!: LoadPlatformAttachmentsAction
   updatePlatformSoftwareUpdateAction!: UpdatePlatformSoftwareUpdateActionAction
   loadAllPlatformActions!: LoadAllPlatformActionsAction
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -136,7 +137,7 @@ export default class PlatformSoftwareUpdateActionEditPage extends mixins(CheckEd
 
   async fetch (): Promise<void> {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await Promise.all([
         this.loadPlatformSoftwareUpdateAction(this.actionId),
         this.loadPlatformAttachments(this.platformId)
@@ -147,7 +148,7 @@ export default class PlatformSoftwareUpdateActionEditPage extends mixins(CheckEd
     } catch (error) {
       this.$store.commit('snackbar/setError', 'Failed to fetch action')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
@@ -159,10 +160,6 @@ export default class PlatformSoftwareUpdateActionEditPage extends mixins(CheckEd
     return this.$route.params.actionId
   }
 
-  get isInProgress (): boolean {
-    return this.isLoading || this.isSaving
-  }
-
   async save () {
     if (!(this.$refs.softwareUpdateActionForm as Vue & { isValid: () => boolean }).isValid()) {
       this.$store.commit('snackbar/setError', 'Please correct the errors')
@@ -170,7 +167,7 @@ export default class PlatformSoftwareUpdateActionEditPage extends mixins(CheckEd
     }
 
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.updatePlatformSoftwareUpdateAction({
         platformId: this.platformId,
         softwareUpdateAction: this.action
@@ -181,7 +178,7 @@ export default class PlatformSoftwareUpdateActionEditPage extends mixins(CheckEd
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to save the action')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/platforms/_platformId/attachments.vue b/pages/platforms/_platformId/attachments.vue
index 9d6d87a58..ed2510bb4 100644
--- a/pages/platforms/_platformId/attachments.vue
+++ b/pages/platforms/_platformId/attachments.vue
@@ -33,7 +33,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator v-model="isLoading" />
     <NuxtChild />
   </div>
 </template>
@@ -44,26 +43,27 @@ import { mapActions } from 'vuex'
 
 import { LoadPlatformAttachmentsAction } from '@/store/platforms'
 
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 
 @Component({
-  components: { ProgressIndicator },
-  methods: mapActions('platforms', ['loadPlatformAttachments'])
+  methods: {
+    ...mapActions('platforms', ['loadPlatformAttachments']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class PlatformAttachmentsPage extends Vue {
-  private isLoading = false
-
   // vuex definition for typescript check
   loadPlatformAttachments!: LoadPlatformAttachmentsAction
+  setLoading!: SetLoadingAction
 
   async fetch () {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.loadPlatformAttachments(this.platformId)
     } catch (e) {
       this.$store.commit('snackbar/setError', 'failed to fetch attachments')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/platforms/_platformId/attachments/_attachmentId/edit.vue b/pages/platforms/_platformId/attachments/_attachmentId/edit.vue
index b05e86b02..802736fdc 100644
--- a/pages/platforms/_platformId/attachments/_attachmentId/edit.vue
+++ b/pages/platforms/_platformId/attachments/_attachmentId/edit.vue
@@ -33,10 +33,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isInProgress"
-      :dark="isSaving"
-    />
     <v-card-actions>
       <v-spacer />
       <SaveAndCancelButtons
@@ -106,8 +102,7 @@ import {
 import { Attachment } from '@/models/Attachment'
 
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
-
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 import { AttachmentsMixin } from '@/mixins/AttachmentsMixin'
 import { Rules } from '@/mixins/Rules'
 
@@ -116,15 +111,19 @@ import { Rules } from '@/mixins/Rules'
  * @extends Vue
  */
 @Component({
-  components: { ProgressIndicator, SaveAndCancelButtons },
+  components: { SaveAndCancelButtons },
   middleware: ['auth'],
-  computed: mapState('platforms', ['platformAttachment']),
-  methods: mapActions('platforms', ['loadPlatformAttachment', 'loadPlatformAttachments', 'updatePlatformAttachment'])
+  computed: {
+    ...mapState('platforms', ['platformAttachment']),
+    ...mapState('progressindicator', ['isLoading'])
+  },
+  methods: {
+    ...mapActions('platforms', ['loadPlatformAttachment', 'loadPlatformAttachments', 'updatePlatformAttachment']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 // @ts-ignore
 export default class AttachmentEditPage extends mixins(Rules, AttachmentsMixin, CheckEditAccess) {
-  private isSaving = false
-  private isLoading = false
   private valueCopy: Attachment = new Attachment()
 
   // vuex definition for typescript check
@@ -132,6 +131,8 @@ export default class AttachmentEditPage extends mixins(Rules, AttachmentsMixin,
   loadPlatformAttachment!: LoadPlatformAttachmentAction
   updatePlatformAttachment!: UpdatePlatformAttachmentAction
   loadPlatformAttachments!: LoadPlatformAttachmentsAction
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -157,7 +158,7 @@ export default class AttachmentEditPage extends mixins(Rules, AttachmentsMixin,
 
   async fetch () {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.loadPlatformAttachment(this.attachmentId)
       if (this.platformAttachment) {
         this.valueCopy = Attachment.createFromObject(this.platformAttachment)
@@ -165,7 +166,7 @@ export default class AttachmentEditPage extends mixins(Rules, AttachmentsMixin,
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to load attachment')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
@@ -177,17 +178,13 @@ export default class AttachmentEditPage extends mixins(Rules, AttachmentsMixin,
     return this.$route.params.attachmentId
   }
 
-  get isInProgress (): boolean {
-    return this.isLoading || this.isSaving
-  }
-
   async save () {
     if (!(this.$refs.attachmentsEditForm as Vue & { validate: () => boolean }).validate()) {
       this.$store.commit('snackbar/setError', 'Please correct your input')
       return
     }
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.updatePlatformAttachment({
         platformId: this.platformId,
         attachment: this.valueCopy
@@ -198,7 +195,7 @@ export default class AttachmentEditPage extends mixins(Rules, AttachmentsMixin,
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to save attachment')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/platforms/_platformId/attachments/index.vue b/pages/platforms/_platformId/attachments/index.vue
index 479815012..c7b229bbf 100644
--- a/pages/platforms/_platformId/attachments/index.vue
+++ b/pages/platforms/_platformId/attachments/index.vue
@@ -34,10 +34,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card-actions
       v-if="$auth.loggedIn"
     >
@@ -101,7 +97,7 @@ permissions and limitations under the Licence.
       v-if="attachmentToDelete"
       v-model="showDeleteDialog"
       title="Delete Attachment"
-      :disabled="isSaving"
+      :disabled="isLoading"
       @cancel="closeDialog"
       @delete="deleteAndCloseDialog"
     >
@@ -125,7 +121,7 @@ import { PlatformsState, LoadPlatformAttachmentsAction, DeletePlatformAttachment
 import { Attachment } from '@/models/Attachment'
 import { Visibility } from '@/models/Visibility'
 
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 import HintCard from '@/components/HintCard.vue'
 import BaseList from '@/components/shared/BaseList.vue'
 import AttachmentListItem from '@/components/shared/AttachmentListItem.vue'
@@ -136,15 +132,20 @@ import DownloadDialog from '@/components/shared/DownloadDialog.vue'
 import { getLastPathElement } from '@/utils/urlHelpers'
 
 @Component({
-  components: { DeleteDialog, DotMenuActionDelete, AttachmentListItem, BaseList, HintCard, ProgressIndicator, DownloadDialog },
-  computed: mapState('platforms', ['platformAttachments', 'platform']),
-  methods: mapActions('platforms', ['loadPlatformAttachments', 'deletePlatformAttachment', 'downloadAttachment'])
+  components: { DeleteDialog, DotMenuActionDelete, AttachmentListItem, BaseList, HintCard, DownloadDialog },
+  computed: {
+    ...mapState('platforms', ['platformAttachments', 'platform']),
+    ...mapState('progressindicator', ['isLoading'])
+  },
+  methods: {
+    ...mapActions('platforms', ['loadPlatformAttachments', 'deletePlatformAttachment', 'downloadAttachment']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class PlatformAttachmentShowPage extends Vue {
   @InjectReactive()
     editable!: boolean
 
-  private isSaving = false
   private showDeleteDialog = false
   private attachmentToDelete: Attachment|null = null
 
@@ -157,6 +158,8 @@ export default class PlatformAttachmentShowPage extends Vue {
   loadPlatformAttachments!: LoadPlatformAttachmentsAction
   deletePlatformAttachment!: DeletePlatformAttachmentAction
   downloadAttachment!: DownloadAttachmentAction
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
 
   get platformId (): string {
     return this.$route.params.platformId
@@ -187,7 +190,7 @@ export default class PlatformAttachmentShowPage extends Vue {
       return
     }
     try {
-      this.isSaving = true
+      this.setLoading(true)
       const attachmentId = this.attachmentToDelete.id
       await this.deletePlatformAttachment(attachmentId)
       this.loadPlatformAttachments(this.platformId)
@@ -195,7 +198,7 @@ export default class PlatformAttachmentShowPage extends Vue {
     } catch (_error) {
       this.$store.commit('snackbar/setError', 'Failed to delete attachment')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
       this.closeDialog()
     }
   }
diff --git a/pages/platforms/_platformId/attachments/new.vue b/pages/platforms/_platformId/attachments/new.vue
index bac1decc7..b6f0c6c2c 100644
--- a/pages/platforms/_platformId/attachments/new.vue
+++ b/pages/platforms/_platformId/attachments/new.vue
@@ -33,10 +33,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-form ref="attachmentsForm" class="pb-2" @submit.prevent>
       <v-card-actions>
         <v-spacer />
@@ -124,30 +120,29 @@ import { IUploadResult } from '@/services/sms/UploadApi'
 import { Attachment } from '@/models/Attachment'
 
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
-
+import { SetLoadingAction } from '@/store/progressindicator'
 import { Rules } from '@/mixins/Rules'
 import { UploadRules } from '@/mixins/UploadRules'
 
 @Component({
-  components: { ProgressIndicator, SaveAndCancelButtons },
+  components: { SaveAndCancelButtons },
   middleware: ['auth'],
   methods: {
     ...mapActions('platforms', ['addPlatformAttachment', 'loadPlatformAttachments']),
-    ...mapActions('files', ['uploadFile'])
+    ...mapActions('files', ['uploadFile']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class PlatformAttachmentAddPage extends mixins(Rules, UploadRules, CheckEditAccess) {
   private attachment: Attachment = new Attachment()
   private attachmentType: string = 'file'
   private file: File | null = null
-  private isSaving: boolean = false
 
   // vuex definition for typescript check
   uploadFile!: (file: File) => Promise<IUploadResult>
   addPlatformAttachment!: AddPlatformAttachmentAction
   loadPlatformAttachments!: LoadPlatformAttachmentsAction
-
+  setLoading!: SetLoadingAction
   /**
    * route to which the user is redirected when he is not allowed to access the page
    *
@@ -193,7 +188,7 @@ export default class PlatformAttachmentAddPage extends mixins(Rules, UploadRules
     let theFailureCanBeFromUpload = true
 
     try {
-      this.isSaving = true
+      this.setLoading(true)
 
       if (this.attachmentType !== 'url' && this.file !== null) {
         // Due to the validation we can be sure that the file is not null
@@ -208,7 +203,7 @@ export default class PlatformAttachmentAddPage extends mixins(Rules, UploadRules
     } catch (error: any) {
       this.handleError(theFailureCanBeFromUpload, error)
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/platforms/_platformId/basic/edit.vue b/pages/platforms/_platformId/basic/edit.vue
index 813bd861c..f8cdaebfd 100644
--- a/pages/platforms/_platformId/basic/edit.vue
+++ b/pages/platforms/_platformId/basic/edit.vue
@@ -35,10 +35,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card-actions>
       <v-spacer />
       <SaveAndCancelButtons
@@ -88,7 +84,7 @@ import { PlatformsState, LoadPlatformAction, SavePlatformAction, CreatePidAction
 import { Platform } from '@/models/Platform'
 
 import PlatformBasicDataForm from '@/components/PlatformBasicDataForm.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
 import NavigationGuardDialog from '@/components/shared/NavigationGuardDialog.vue'
 import NonModelOptionsForm, { NonModelOptions } from '@/components/shared/NonModelOptionsForm.vue'
@@ -97,17 +93,19 @@ import NonModelOptionsForm, { NonModelOptions } from '@/components/shared/NonMod
   components: {
     SaveAndCancelButtons,
     PlatformBasicDataForm,
-    ProgressIndicator,
     NavigationGuardDialog,
     NonModelOptionsForm
   },
   middleware: ['auth'],
   computed: mapState('platforms', ['platform']),
-  methods: mapActions('platforms', ['loadPlatform', 'savePlatform', 'createPid'])
+  methods: {
+    ...mapActions('platforms', ['loadPlatform', 'savePlatform', 'createPid']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class PlatformEditBasicPage extends mixins(CheckEditAccess) {
   private platformCopy: Platform | null = null
-  private isSaving: boolean = false
+
   private hasSaved: boolean = false
   private showNavigationWarning: boolean = false
   private to: RawLocation | null = null
@@ -120,7 +118,7 @@ export default class PlatformEditBasicPage extends mixins(CheckEditAccess) {
   savePlatform!: SavePlatformAction
   loadPlatform!: LoadPlatformAction
   createPid!: CreatePidAction
-
+  setLoading!: SetLoadingAction
   /**
    * route to which the user is redirected when he is not allowed to access the page
    *
@@ -170,7 +168,7 @@ export default class PlatformEditBasicPage extends mixins(CheckEditAccess) {
     }
 
     try {
-      this.isSaving = true
+      this.setLoading(true)
       const savedPlatform = await this.savePlatform(this.platformCopy)
       if (this.editOptions.persistentIdentifierShouldBeCreated) {
         savedPlatform.persistentIdentifier = await this.createPid(savedPlatform.id)
@@ -188,7 +186,7 @@ export default class PlatformEditBasicPage extends mixins(CheckEditAccess) {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Save failed')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/platforms/_platformId/basic/index.vue b/pages/platforms/_platformId/basic/index.vue
index 94cc946f4..54119f94c 100644
--- a/pages/platforms/_platformId/basic/index.vue
+++ b/pages/platforms/_platformId/basic/index.vue
@@ -33,10 +33,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card-actions>
       <v-spacer />
       <v-btn
@@ -117,7 +113,7 @@ permissions and limitations under the Licence.
       v-if="platform"
       v-model="showDeleteDialog"
       title="Delete Platform"
-      :disabled="isSaving"
+      :disabled="isLoading"
       @cancel="closeDialog"
       @delete="deleteAndCloseDialog"
     >
@@ -155,12 +151,11 @@ import DownloadDialog from '@/components/shared/DownloadDialog.vue'
 import PlatformArchiveDialog from '@/components/platforms/PlatformArchiveDialog.vue'
 import DotMenuActionSensorML from '@/components/DotMenuActionSensorML.vue'
 import DeleteDialog from '@/components/shared/DeleteDialog.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 import { Visibility } from '@/models/Visibility'
 
 @Component({
   components: {
-    ProgressIndicator,
     DeleteDialog,
     DotMenuActionDelete,
     DotMenuActionSensorML,
@@ -173,9 +168,13 @@ import { Visibility } from '@/models/Visibility'
     PlatformArchiveDialog
   },
   computed: {
-    ...mapState('platforms', ['platform'])
+    ...mapState('platforms', ['platform']),
+    ...mapState('progressindicator', ['isLoading'])
   },
-  methods: mapActions('platforms', ['deletePlatform', 'loadPlatform', 'archivePlatform', 'restorePlatform', 'exportAsSensorML', 'getSensorMLUrl'])
+  methods: {
+    ...mapActions('platforms', ['deletePlatform', 'loadPlatform', 'archivePlatform', 'restorePlatform', 'exportAsSensorML', 'getSensorMLUrl']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class PlatformShowBasicPage extends Vue {
   @InjectReactive()
@@ -190,7 +189,6 @@ export default class PlatformShowBasicPage extends Vue {
   @InjectReactive()
     restoreable!: boolean
 
-  private isSaving = false
   private showDeleteDialog: boolean = false
   private showArchiveDialog: boolean = false
   private showDownloadDialog: boolean = false
@@ -203,6 +201,8 @@ export default class PlatformShowBasicPage extends Vue {
   restorePlatform!: RestorePlatformAction
   exportAsSensorML!: ExportAsSensorMLAction
   getSensorMLUrl!: GetSensorMLUrlAction
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
 
   get platformId () {
     return this.$route.params.platformId
@@ -253,14 +253,14 @@ export default class PlatformShowBasicPage extends Vue {
       return
     }
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.deletePlatform(this.platform.id)
       this.$router.push('/platforms')
       this.$store.commit('snackbar/setSuccess', 'Platform deleted')
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Platform could not be deleted')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
       this.showDeleteDialog = false
     }
   }
@@ -279,7 +279,7 @@ export default class PlatformShowBasicPage extends Vue {
       return
     }
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.archivePlatform(this.platform.id)
       await this.loadPlatform({
         platformId: this.platformId,
@@ -291,7 +291,7 @@ export default class PlatformShowBasicPage extends Vue {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Platform could not be archived')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
       this.showArchiveDialog = false
     }
   }
@@ -300,7 +300,7 @@ export default class PlatformShowBasicPage extends Vue {
     if (this.platform === null || this.platform.id === null) {
       return
     }
-    this.isSaving = true
+    this.setLoading(true)
     try {
       await this.restorePlatform(this.platform.id)
       await this.loadPlatform({
@@ -313,7 +313,7 @@ export default class PlatformShowBasicPage extends Vue {
     } catch (error) {
       this.$store.commit('snackbar/setError', 'Platform could not be restored')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/platforms/_platformId/contacts.vue b/pages/platforms/_platformId/contacts.vue
index 48bef454e..4aa151060 100644
--- a/pages/platforms/_platformId/contacts.vue
+++ b/pages/platforms/_platformId/contacts.vue
@@ -33,9 +33,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-    />
     <NuxtChild />
   </div>
 </template>
@@ -47,27 +44,24 @@ import { mapActions } from 'vuex'
 import { LoadPlatformContactRolesAction } from '@/store/platforms'
 import { LoadCvContactRolesAction } from '@/store/vocabulary'
 
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 
 @Component({
-  components: {
-    ProgressIndicator
-  },
   methods: {
     ...mapActions('platforms', ['loadPlatformContactRoles']),
-    ...mapActions('vocabulary', ['loadCvContactRoles'])
+    ...mapActions('vocabulary', ['loadCvContactRoles']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class PlatformContactsPage extends Vue {
-  private isLoading = false
-
   // vuex definition for typescript check
   loadPlatformContactRoles!: LoadPlatformContactRolesAction
   loadCvContactRoles!: LoadCvContactRolesAction
+  setLoading!: SetLoadingAction
 
   async fetch (): Promise<void> {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await Promise.all([
         this.loadPlatformContactRoles(this.platformId),
         this.loadCvContactRoles()
@@ -75,7 +69,7 @@ export default class PlatformContactsPage extends Vue {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to fetch contacts')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/platforms/_platformId/contacts/index.vue b/pages/platforms/_platformId/contacts/index.vue
index 5522bf4c3..a1eacba2d 100644
--- a/pages/platforms/_platformId/contacts/index.vue
+++ b/pages/platforms/_platformId/contacts/index.vue
@@ -33,10 +33,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card-actions>
       <v-spacer />
       <v-btn
@@ -93,7 +89,7 @@ import { mapActions, mapState } from 'vuex'
 import { RemovePlatformContactRoleAction, LoadPlatformContactRolesAction } from '@/store/platforms'
 
 import HintCard from '@/components/HintCard.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import BaseList from '@/components/shared/BaseList.vue'
 import ContactRoleListItem from '@/components/contacts/ContactRoleListItem.vue'
 import DotMenuActionDelete from '@/components/DotMenuActionDelete.vue'
@@ -103,24 +99,25 @@ import DotMenuActionDelete from '@/components/DotMenuActionDelete.vue'
     DotMenuActionDelete,
     ContactRoleListItem,
     BaseList,
-    ProgressIndicator,
     HintCard
   },
   computed: {
     ...mapState('platforms', ['platformContactRoles']),
     ...mapState('vocabulary', ['cvContactRoles'])
   },
-  methods: mapActions('platforms', ['removePlatformContactRole', 'loadPlatformContactRoles'])
+  methods: {
+    ...mapActions('platforms', ['removePlatformContactRole', 'loadPlatformContactRoles']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class PlatformShowContactPage extends Vue {
   @InjectReactive()
     editable!: boolean
 
-  private isSaving = false
-
   // vuex definition for typescript check
   removePlatformContactRole!: RemovePlatformContactRoleAction
   loadPlatformContactRoles!: LoadPlatformContactRolesAction
+  setLoading!: SetLoadingAction
 
   get platformId (): string {
     return this.$route.params.platformId
@@ -128,7 +125,7 @@ export default class PlatformShowContactPage extends Vue {
 
   async removeContactRole (contactRoleId: string) {
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.removePlatformContactRole({
         platformContactRoleId: contactRoleId
       })
@@ -137,7 +134,7 @@ export default class PlatformShowContactPage extends Vue {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Removing contact failed')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/platforms/_platformId/contacts/new.vue b/pages/platforms/_platformId/contacts/new.vue
index 0351d6653..a6d834253 100644
--- a/pages/platforms/_platformId/contacts/new.vue
+++ b/pages/platforms/_platformId/contacts/new.vue
@@ -2,7 +2,7 @@
 Web client of the Sensor Management System software developed within the
 Helmholtz DataHub Initiative by GFZ and UFZ.
 
-Copyright (C) 2020 - 2022
+Copyright (C) 2020 - 2023
 - Kotyba Alhaj Taha (UFZ, kotyba.alhaj-taha@ufz.de)
 - Nils Brinckmann (GFZ, nils.brinckmann@gfz-potsdam.de)
 - Marc Hanisch (GFZ, marc.hanisch@gfz-potsdam.de)
@@ -33,10 +33,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isInProgress"
-      :dark="isSaving"
-    />
     <contact-role-assignment-form
       :contacts="contacts"
       :contact="selectedContact"
@@ -65,31 +61,30 @@ import { LoadCvContactRolesAction } from '@/store/vocabulary'
 import { Contact } from '@/models/Contact'
 import { ContactRole } from '@/models/ContactRole'
 
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 import ContactRoleAssignmentForm from '@/components/shared/ContactRoleAssignmentForm.vue'
 
 @Component({
   components: {
-    ProgressIndicator,
     ContactRoleAssignmentForm
   },
   middleware: ['auth'],
   computed: {
     ...mapState('platforms', ['platformContactRoles']),
     ...mapState('contacts', ['contacts']),
-    ...mapState('vocabulary', ['cvContactRoles'])
+    ...mapState('vocabulary', ['cvContactRoles']),
+    ...mapState('progressindicator', ['isLoading'])
 
   },
   methods: {
     ...mapActions('contacts', ['loadAllContacts']),
     ...mapActions('platforms', ['loadPlatformContactRoles', 'addPlatformContactRole']),
-    ...mapActions('vocabulary', ['loadCvContactRoles'])
+    ...mapActions('vocabulary', ['loadCvContactRoles']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class PlatformAddContactPage extends mixins(CheckEditAccess) {
   private selectedContact: Contact | null = null
-  private isLoading: boolean = false
-  private isSaving: boolean = false
 
   // vuex definition for typescript check
   platformContactRoles!: PlatformsState['platformContactRoles']
@@ -98,6 +93,8 @@ export default class PlatformAddContactPage extends mixins(CheckEditAccess) {
   loadPlatformContactRoles!: LoadPlatformContactRolesAction
   addPlatformContactRole!: AddPlatformContactRoleAction
   loadCvContactRoles!: LoadCvContactRolesAction
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -123,7 +120,7 @@ export default class PlatformAddContactPage extends mixins(CheckEditAccess) {
 
   async fetch (): Promise<void> {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.loadAllContacts()
 
       const redirectContactId = this.$route.query.contact
@@ -133,7 +130,7 @@ export default class PlatformAddContactPage extends mixins(CheckEditAccess) {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to fetch related contacts')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
@@ -141,14 +138,10 @@ export default class PlatformAddContactPage extends mixins(CheckEditAccess) {
     return this.$route.params.platformId
   }
 
-  get isInProgress (): boolean {
-    return this.isLoading || this.isSaving
-  }
-
   async assignContact (contactRole: ContactRole | null) {
     if (this.editable && contactRole) {
       try {
-        this.isSaving = true
+        this.setLoading(true)
         await this.addPlatformContactRole({
           platformId: this.platformId,
           contactRole
@@ -159,7 +152,7 @@ export default class PlatformAddContactPage extends mixins(CheckEditAccess) {
       } catch (e) {
         this.$store.commit('snackbar/setError', 'Failed to add a contact')
       } finally {
-        this.isSaving = false
+        this.setLoading(false)
       }
     }
   }
diff --git a/pages/platforms/_platformId/parameters.vue b/pages/platforms/_platformId/parameters.vue
index c02135465..f8f2317b2 100644
--- a/pages/platforms/_platformId/parameters.vue
+++ b/pages/platforms/_platformId/parameters.vue
@@ -30,9 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-    />
     <NuxtChild />
   </div>
 </template>
@@ -46,27 +43,25 @@ import {
   LoadPlatformParameterChangeActionsAction
 } from '@/store/platforms'
 import { LoadUnitsAction } from '@/store/vocabulary'
-
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 
 @Component({
-  components: { ProgressIndicator },
   methods: {
     ...mapActions('platforms', ['loadPlatformParameters', 'loadPlatformParameterChangeActions']),
-    ...mapActions('vocabulary', ['loadUnits'])
+    ...mapActions('vocabulary', ['loadUnits']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class PlatformParametersPage extends Vue {
-  private isLoading = false
-
   // vuex definition for typescript check
   loadPlatformParameters!: LoadPlatformParametersAction
   loadPlatformParameterChangeActions!: LoadPlatformParameterChangeActionsAction
   loadUnits!: LoadUnitsAction
+  setLoading!: SetLoadingAction
 
   async fetch (): Promise<void> {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await Promise.all([
         this.loadPlatformParameters(this.platformId),
         this.loadPlatformParameterChangeActions(this.platformId),
@@ -75,7 +70,7 @@ export default class PlatformParametersPage extends Vue {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to fetch parameters')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/platforms/_platformId/parameters/_parameterId/copy.vue b/pages/platforms/_platformId/parameters/_parameterId/copy.vue
index c1cb965e9..c90b84af8 100644
--- a/pages/platforms/_platformId/parameters/_parameterId/copy.vue
+++ b/pages/platforms/_platformId/parameters/_parameterId/copy.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card
       flat
     >
@@ -99,8 +95,8 @@ import { Parameter } from '@/models/Parameter'
 import BaseList from '@/components/shared/BaseList.vue'
 import ParameterForm from '@/components/shared/ParameterForm.vue'
 import ParameterListItem from '@/components/shared/ParameterListItem.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 
 @Component({
   middleware: ['auth'],
@@ -108,7 +104,6 @@ import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
     BaseList,
     ParameterForm,
     ParameterListItem,
-    ProgressIndicator,
     SaveAndCancelButtons
   },
   computed: {
@@ -116,12 +111,12 @@ import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
     ...mapState('platforms', ['platformParameter', 'platformParameters'])
   },
   methods: {
-    ...mapActions('platforms', ['addPlatformParameter', 'loadPlatformParameters', 'loadPlatformParameter'])
+    ...mapActions('platforms', ['addPlatformParameter', 'loadPlatformParameters', 'loadPlatformParameter']),
+    ...mapActions('progressindicator', ['setLoading'])
   },
   scrollToTop: true
 })
 export default class ParametersCopyPage extends mixins(CheckEditAccess) {
-  private isSaving = false
   private valueCopy: Parameter = new Parameter()
 
   // vuex definition for typescript check
@@ -131,6 +126,7 @@ export default class ParametersCopyPage extends mixins(CheckEditAccess) {
   loadPlatformParameters!: LoadPlatformParametersAction
   addPlatformParameter!: AddPlatformParameterAction
   units!: VocabularyState['units']
+  setLoading!: SetLoadingAction
 
   mounted () {
     (this.$refs.parameterForm as ParameterForm).focus()
@@ -187,7 +183,7 @@ export default class ParametersCopyPage extends mixins(CheckEditAccess) {
     }
 
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.addPlatformParameter({
         platformId: this.platformId,
         parameter: this.valueCopy
@@ -198,7 +194,7 @@ export default class ParametersCopyPage extends mixins(CheckEditAccess) {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to copy parameter')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/platforms/_platformId/parameters/_parameterId/edit.vue b/pages/platforms/_platformId/parameters/_parameterId/edit.vue
index 4b5650074..2f09cf2b5 100644
--- a/pages/platforms/_platformId/parameters/_parameterId/edit.vue
+++ b/pages/platforms/_platformId/parameters/_parameterId/edit.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card
       flat
     >
@@ -100,8 +96,8 @@ import { Parameter } from '@/models/Parameter'
 import BaseList from '@/components/shared/BaseList.vue'
 import ParameterForm from '@/components/shared/ParameterForm.vue'
 import ParameterListItem from '@/components/shared/ParameterListItem.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 
 @Component({
   middleware: ['auth'],
@@ -109,7 +105,6 @@ import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
     BaseList,
     ParameterForm,
     ParameterListItem,
-    ProgressIndicator,
     SaveAndCancelButtons
   },
   computed: {
@@ -117,12 +112,12 @@ import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
     ...mapState('platforms', ['platformParameter', 'platformParameters'])
   },
   methods: {
-    ...mapActions('platforms', ['updatePlatformParameter', 'loadPlatformParameters', 'loadPlatformParameter'])
+    ...mapActions('platforms', ['updatePlatformParameter', 'loadPlatformParameters', 'loadPlatformParameter']),
+    ...mapActions('progressindicator', ['setLoading'])
   },
   scrollToTop: true
 })
 export default class ParametersEditPage extends mixins(CheckEditAccess) {
-  private isSaving = false
   private valueCopy: Parameter = new Parameter()
 
   // vuex definition for typescript check
@@ -132,6 +127,7 @@ export default class ParametersEditPage extends mixins(CheckEditAccess) {
   loadPlatformParameters!: LoadPlatformParametersAction
   updatePlatformParameter!: UpdatePlatformParameterAction
   units!: VocabularyState['units']
+  setLoading!: SetLoadingAction
 
   mounted () {
     (this.$refs.parameterForm as ParameterForm).focus()
@@ -185,7 +181,7 @@ export default class ParametersEditPage extends mixins(CheckEditAccess) {
       return
     }
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.updatePlatformParameter({
         platformId: this.platformId,
         parameter: this.valueCopy
@@ -196,7 +192,7 @@ export default class ParametersEditPage extends mixins(CheckEditAccess) {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to save parameter')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/platforms/_platformId/parameters/index.vue b/pages/platforms/_platformId/parameters/index.vue
index 6a74a9d69..55763860e 100644
--- a/pages/platforms/_platformId/parameters/index.vue
+++ b/pages/platforms/_platformId/parameters/index.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isInProgress"
-      dark
-    />
     <v-card-actions
       v-if="editable"
     >
@@ -41,7 +37,7 @@ permissions and limitations under the Licence.
       <v-btn
         color="primary"
         small
-        :disabled="isFetching"
+        :disabled="isLoading"
         :to="'/platforms/' + platformId + '/parameters/new'"
       >
         Add Parameter
@@ -112,7 +108,7 @@ permissions and limitations under the Licence.
       v-if="editable && parameterToDelete"
       v-model="showDeleteDialog"
       title="Delete Parameter"
-      :disabled="isInProgress"
+      :disabled="isLoading"
       @cancel="closeDialog"
       @delete="deleteAndCloseDialog"
     >
@@ -122,7 +118,7 @@ permissions and limitations under the Licence.
 </template>
 
 <script lang="ts">
-import { Component, Vue, InjectReactive, Prop } from 'nuxt-property-decorator'
+import { Component, Vue, InjectReactive } from 'nuxt-property-decorator'
 import { mapActions, mapState } from 'vuex'
 
 import {
@@ -144,7 +140,7 @@ import ExpandableText from '@/components/shared/ExpandableText.vue'
 import HintCard from '@/components/HintCard.vue'
 import ParameterListItem from '@/components/shared/ParameterListItem.vue'
 import ParameterValueTable from '@/components/shared/ParameterValueTable.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 
 @Component({
   components: {
@@ -155,30 +151,25 @@ import ProgressIndicator from '@/components/ProgressIndicator.vue'
     ExpandableText,
     HintCard,
     ParameterListItem,
-    ParameterValueTable,
-    ProgressIndicator
+    ParameterValueTable
   },
   computed: {
     ...mapState('vocabulary', ['units']),
-    ...mapState('platforms', ['platformParameters', 'platformParameterChangeActions'])
+    ...mapState('platforms', ['platformParameters', 'platformParameterChangeActions']),
+    ...mapState('progressindicator', ['isLoading'])
   },
   methods: {
-    ...mapActions('platforms', ['deletePlatformParameter', 'loadPlatformParameters'])
+    ...mapActions('platforms', ['deletePlatformParameter', 'loadPlatformParameters']),
+    ...mapActions('progressindicator', ['setLoading'])
   },
   scrollToTop: true
 })
-export default class PlatformPropertyShowPage extends Vue {
+export default class PlatformParameterShowPage extends Vue {
   @InjectReactive()
   private editable!: boolean
 
-  @Prop({
-    type: Boolean
-  })
-  private isFetching!: boolean
-
-  private isInProgress = false
-
   private showDeleteDialog = false
+
   private parameterToDelete: Parameter | null = null
 
   // vuex definition for typescript check
@@ -187,6 +178,8 @@ export default class PlatformPropertyShowPage extends Vue {
   platformParameterChangeActions!: PlatformsState['platformParameterChangeActions']
   loadPlatformParameters!: LoadPlatformParametersAction
   deletePlatformParameter!: DeletePlatformParameterAction
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
 
   get platformId (): string {
     return this.$route.params.platformId
@@ -207,7 +200,7 @@ export default class PlatformPropertyShowPage extends Vue {
       return
     }
     try {
-      this.isInProgress = true
+      this.setLoading(true)
 
       await this.deletePlatformParameter(this.parameterToDelete.id)
       this.loadPlatformParameters(this.platformId)
@@ -219,7 +212,7 @@ export default class PlatformPropertyShowPage extends Vue {
         this.$store.commit('snackbar/setError', 'Failed to delete parameter')
       }
     } finally {
-      this.isInProgress = false
+      this.setLoading(false)
       this.closeDialog()
     }
   }
diff --git a/pages/platforms/_platformId/parameters/new.vue b/pages/platforms/_platformId/parameters/new.vue
index 0b3088aeb..1a756d868 100644
--- a/pages/platforms/_platformId/parameters/new.vue
+++ b/pages/platforms/_platformId/parameters/new.vue
@@ -30,10 +30,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card
       flat
     >
@@ -99,7 +95,7 @@ import { Parameter } from '@/models/Parameter'
 import BaseList from '@/components/shared/BaseList.vue'
 import ParameterForm from '@/components/shared/ParameterForm.vue'
 import ParameterListItem from '@/components/shared/ParameterListItem.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
 
 @Component({
@@ -108,7 +104,6 @@ import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
     BaseList,
     ParameterForm,
     ParameterListItem,
-    ProgressIndicator,
     SaveAndCancelButtons
   },
   computed: {
@@ -116,12 +111,12 @@ import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
     ...mapState('platforms', ['platformParameters', 'platformParameterChangeActions'])
   },
   methods: {
-    ...mapActions('platforms', ['addPlatformParameter', 'loadPlatformParameters'])
+    ...mapActions('platforms', ['addPlatformParameter', 'loadPlatformParameters']),
+    ...mapActions('progressindicator', ['setLoading'])
   },
   scrollToTop: true
 })
 export default class ParametersAddPage extends mixins(CheckEditAccess) {
-  private isSaving = false
   private value: Parameter = new Parameter()
 
   // vuex definition for typescript check
@@ -130,6 +125,7 @@ export default class ParametersAddPage extends mixins(CheckEditAccess) {
   platformParameterChangeActions!: PlatformsState['platformParameterChangeActions']
   addPlatformParameter!: AddPlatformParameterAction
   loadPlatformParameters!: LoadPlatformParametersAction
+  setLoading!: SetLoadingAction
 
   mounted () {
     (this.$refs.parameterForm as ParameterForm).focus()
@@ -168,7 +164,7 @@ export default class ParametersAddPage extends mixins(CheckEditAccess) {
     }
 
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.addPlatformParameter({
         platformId: this.platformId,
         parameter: this.value
@@ -179,7 +175,7 @@ export default class ParametersAddPage extends mixins(CheckEditAccess) {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to save parameter')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/platforms/copy/_platformId.vue b/pages/platforms/copy/_platformId.vue
index 2b8e7f8f9..e75135c2f 100644
--- a/pages/platforms/copy/_platformId.vue
+++ b/pages/platforms/copy/_platformId.vue
@@ -35,10 +35,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isInProgress"
-      :dark="isSaving"
-    />
     <v-card
       flat
     >
@@ -144,7 +140,7 @@ import { PlatformsState, LoadPlatformAction, CopyPlatformAction, CreatePidAction
 import { Platform } from '@/models/Platform'
 
 import PlatformBasicDataForm from '@/components/PlatformBasicDataForm.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
 import NonModelOptionsForm, { NonModelOptions } from '@/components/shared/NonModelOptionsForm.vue'
 
@@ -152,24 +148,23 @@ import NonModelOptionsForm, { NonModelOptions } from '@/components/shared/NonMod
   components: {
     SaveAndCancelButtons,
     PlatformBasicDataForm,
-    ProgressIndicator,
     NonModelOptionsForm
   },
   middleware: ['auth'],
   computed: {
     ...mapGetters('permissions', ['canAccessEntity', 'canModifyEntity', 'userGroups']),
-    ...mapState('platforms', ['platform'])
+    ...mapState('platforms', ['platform']),
+    ...mapState('progressindicator', ['isLoading'])
   },
   methods: {
     ...mapActions('platforms', ['loadPlatform', 'copyPlatform', 'createPid']),
-    ...mapActions('appbar', ['setTitle', 'setTabs'])
+    ...mapActions('appbar', ['setTitle', 'setTabs']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 // @ts-ignore
 export default class PlatformCopyPage extends Vue {
   private platformToCopy: Platform = new Platform()
-  private isSaving = false
-  private isLoading = false
   private copyOptions: NonModelOptions = {
     persistentIdentifierShouldBeCreated: false
   }
@@ -192,6 +187,8 @@ export default class PlatformCopyPage extends Vue {
   setTabs!: SetTabsAction
   setTitle!: SetTitleAction
   createPid!: CreatePidAction
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
 
   created () {
     this.initializeAppBar()
@@ -199,7 +196,7 @@ export default class PlatformCopyPage extends Vue {
 
   async fetch (): Promise<void> {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.loadPlatform({
         platformId: this.platformId,
         includeContacts: true,
@@ -220,7 +217,7 @@ export default class PlatformCopyPage extends Vue {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Loading platform failed')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
@@ -228,10 +225,6 @@ export default class PlatformCopyPage extends Vue {
     return this.$route.params.platformId
   }
 
-  get isInProgress (): boolean {
-    return this.isLoading || this.isSaving
-  }
-
   getPreparedPlatformForCopy (): Platform | null {
     if (!this.platform) {
       return null
@@ -267,7 +260,7 @@ export default class PlatformCopyPage extends Vue {
       return
     }
     try {
-      this.isSaving = true
+      this.setLoading(true)
       const savedPlatformId = await this.copyPlatform({
         platform: this.platformToCopy,
         copyContacts: this.copyContacts,
@@ -283,7 +276,7 @@ export default class PlatformCopyPage extends Vue {
     } catch (_error) {
       this.$store.commit('snackbar/setError', 'Copy failed')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/platforms/index.vue b/pages/platforms/index.vue
index 04596f870..95fe626f2 100644
--- a/pages/platforms/index.vue
+++ b/pages/platforms/index.vue
@@ -50,13 +50,7 @@ permissions and limitations under the Licence.
       </PlatformSearch>
     </div>
 
-    <v-progress-circular
-      v-if="loading"
-      class="progress-spinner"
-      color="primary"
-      indeterminate
-    />
-    <div v-if="platforms.length <=0 && !loading">
+    <div v-if="platforms.length <=0 && !isLoading">
       <p class="text-center">
         There are no platforms that match your search criteria.
       </p>
@@ -74,15 +68,6 @@ permissions and limitations under the Licence.
           <v-subheader>
             <FoundEntries v-model="totalCount" entity-name="platform" />
             <template v-if="platforms.length>0">
-              <v-dialog v-model="processing" max-width="100">
-                <v-card>
-                  <v-card-text>
-                    <div class="text-center pt-2">
-                      <v-progress-circular indeterminate />
-                    </div>
-                  </v-card-text>
-                </v-card>
-              </v-dialog>
               <v-menu
                 close-on-click
                 close-on-content-click
@@ -135,7 +120,7 @@ permissions and limitations under the Licence.
         >
           <v-pagination
             v-model="page"
-            :disabled="loading"
+            :disabled="isLoading"
             :length="totalPages"
             :total-visible="7"
             @input="searchPlatformsPaginated"
@@ -192,7 +177,7 @@ permissions and limitations under the Licence.
       </BaseList>
       <v-pagination
         v-model="page"
-        :disabled="loading"
+        :disabled="isLoading"
         :length="totalPages"
         :total-visible="7"
         @input="searchPlatformsPaginated"
@@ -202,7 +187,7 @@ permissions and limitations under the Licence.
       v-if="platformToDelete"
       v-model="showDeleteDialog"
       title="Delete Platform"
-      :disabled="loading"
+      :disabled="isLoading"
       @cancel="closeDialog"
       @delete="deleteAndCloseDialog"
     >
@@ -269,6 +254,7 @@ import PlatformsListItem from '@/components/platforms/PlatformsListItem.vue'
 import PlatformsBasicSearch from '@/components/platforms/PlatformsBasicSearch.vue'
 import PlatformsBasicSearchField from '@/components/platforms/PlatformsBasicSearchField.vue'
 import PageSizeSelect from '@/components/shared/PageSizeSelect.vue'
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 
 import { Platform } from '@/models/Platform'
 import { Visibility } from '@/models/Visibility'
@@ -300,18 +286,17 @@ import FoundEntries from '@/components/shared/FoundEntries.vue'
   },
   computed: {
     ...mapState('platforms', ['platforms', 'pageNumber', 'pageSize', 'totalPages', 'totalCount', 'platform']),
+    ...mapState('progressindicator', ['isLoading']),
     ...mapGetters('platforms', ['pageSizes']),
     ...mapGetters('permissions', ['canDeleteEntity', 'canAccessEntity', 'canArchiveEntity', 'canRestoreEntity'])
   },
   methods: {
     ...mapActions('platforms', ['searchPlatformsPaginated', 'setPageNumber', 'setPageSize', 'exportAsCsv', 'deletePlatform', 'archivePlatform', 'restorePlatform', 'exportAsSensorML', 'loadPlatform', 'replacePlatformInPlatforms', 'getSensorMLUrl']),
-    ...mapActions('appbar', ['setTitle', 'setTabs'])
+    ...mapActions('appbar', ['setTitle', 'setTabs']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class SearchPlatformsPage extends Vue {
-  private loading: boolean = false
-  private processing: boolean = false
-
   private showDeleteDialog: boolean = false
   private showArchiveDialog: boolean = false
   private showDownloadDialog: boolean = false
@@ -346,6 +331,8 @@ export default class SearchPlatformsPage extends Vue {
   replacePlatformInPlatforms!: ReplacePlatformInPlatformsAction
   platform!: PlatformsState['platform']
   getSensorMLUrl!: GetSensorMLUrlAction
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
 
   created () {
     this.initializeAppBar()
@@ -390,9 +377,9 @@ export default class SearchPlatformsPage extends Vue {
   }
 
   async exportCsvUrl (): Promise<string> {
-    this.processing = true
+    this.setLoading(true)
     const blob = await this.exportAsCsv()
-    this.processing = false
+    this.setLoading(false)
     return window.URL.createObjectURL(blob)
   }
 
@@ -411,14 +398,14 @@ export default class SearchPlatformsPage extends Vue {
       return
     }
     try {
-      this.loading = true
+      this.setLoading(true)
       await this.deletePlatform(this.platformToDelete.id)
       this.searchPlatformsPaginated()
       this.$store.commit('snackbar/setSuccess', 'Platform deleted')
     } catch (_error) {
       this.$store.commit('snackbar/setError', 'Platform could not be deleted')
     } finally {
-      this.loading = false
+      this.setLoading(false)
       this.closeDialog()
     }
   }
@@ -438,7 +425,7 @@ export default class SearchPlatformsPage extends Vue {
       return
     }
     try {
-      this.loading = true
+      this.setLoading(true)
       await this.archivePlatform(this.platformToArchive.id)
       await this.loadPlatform({
         platformId: this.platformToArchive.id,
@@ -450,14 +437,14 @@ export default class SearchPlatformsPage extends Vue {
     } catch (_error) {
       this.$store.commit('snackbar/setError', 'Platform could not be archived')
     } finally {
-      this.loading = false
+      this.setLoading(false)
       this.closeArchiveDialog()
     }
   }
 
   async runRestorePlatform (platform: Platform) {
     if (platform.id) {
-      this.loading = true
+      this.setLoading(true)
       try {
         await this.restorePlatform(platform.id)
         await this.loadPlatform({
@@ -470,7 +457,7 @@ export default class SearchPlatformsPage extends Vue {
       } catch (error) {
         this.$store.commit('snackbar/setError', 'Platform could not be restored')
       } finally {
-        this.loading = false
+        this.setLoading(false)
       }
     }
   }
diff --git a/pages/platforms/new.vue b/pages/platforms/new.vue
index ec9a68289..5e9c3bb11 100644
--- a/pages/platforms/new.vue
+++ b/pages/platforms/new.vue
@@ -35,10 +35,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-      dark
-    />
     <v-card
       flat
     >
@@ -78,7 +74,7 @@ import { mapActions } from 'vuex'
 import { SetTitleAction, SetTabsAction } from '@/store/appbar'
 import { CreatePidAction, SavePlatformAction } from '@/store/platforms'
 
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import PlatformBasicDataForm from '@/components/PlatformBasicDataForm.vue'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
 import NonModelOptionsForm, { NonModelOptions } from '@/components/shared/NonModelOptionsForm.vue'
@@ -90,20 +86,20 @@ import { Platform } from '@/models/Platform'
   components: {
     SaveAndCancelButtons,
     PlatformBasicDataForm,
-    ProgressIndicator,
     NonModelOptionsForm,
     SerialNumberWarningDialog
   },
   middleware: ['auth'],
   methods: {
     ...mapActions('platforms', ['savePlatform', 'createPid']),
-    ...mapActions('appbar', ['setTitle', 'setTabs'])
+    ...mapActions('appbar', ['setTitle', 'setTabs']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 // @ts-ignore
 export default class PlatformNewPage extends Vue {
   private platform: Platform = new Platform()
-  private isLoading: boolean = false
+
   private showSerialNumberWarning = false
   private wantsToSaveWithoutSerialNumber = false
   private createOptions: NonModelOptions = {
@@ -115,6 +111,7 @@ export default class PlatformNewPage extends Vue {
   setTabs!: SetTabsAction
   setTitle!: SetTitleAction
   createPid!: CreatePidAction
+  setLoading!: SetLoadingAction
 
   created () {
     this.initializeAppBar()
@@ -139,7 +136,7 @@ export default class PlatformNewPage extends Vue {
     }
 
     try {
-      this.isLoading = true
+      this.setLoading(true)
       const savedPlatform = await this.savePlatform(this.platform)
       if (this.createOptions.persistentIdentifierShouldBeCreated) {
         savedPlatform.persistentIdentifier = await this.createPid(savedPlatform.id)
@@ -149,7 +146,7 @@ export default class PlatformNewPage extends Vue {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Save failed')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/sites/_siteId.vue b/pages/sites/_siteId.vue
index 659fafd2a..6ff3249aa 100644
--- a/pages/sites/_siteId.vue
+++ b/pages/sites/_siteId.vue
@@ -34,9 +34,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-    />
     <v-card flat>
       <center>
         <v-alert
@@ -70,14 +67,13 @@ import { mapActions, mapState, mapGetters } from 'vuex'
 
 import { SetTitleAction, SetTabsAction } from '@/store/appbar'
 
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import ModificationInfo from '@/components/ModificationInfo.vue'
 import { LoadSiteAction, LoadSiteConfigurationsAction, SitesState } from '@/store/sites'
 import { CanAccessEntityGetter, CanArchiveEntityGetter, CanDeleteEntityGetter, CanModifyEntityGetter, CanRestoreEntityGetter } from '@/store/permissions'
 
 @Component({
   components: {
-    ProgressIndicator,
     ModificationInfo
   },
   computed: {
@@ -87,7 +83,8 @@ import { CanAccessEntityGetter, CanArchiveEntityGetter, CanDeleteEntityGetter, C
   },
   methods: {
     ...mapActions('sites', ['loadSite', 'loadSiteConfigurations']),
-    ...mapActions('appbar', ['setTitle', 'setTabs'])
+    ...mapActions('appbar', ['setTitle', 'setTabs']),
+    ...mapActions('progressindicator', ['setLoading'])
 
   }
 })
@@ -104,8 +101,6 @@ export default class SitePage extends Vue {
   @ProvideReactive()
     restoreable: boolean = false
 
-  private isLoading: boolean = false
-
   // vuex definition for typescript check
   site!: SitesState['site']
   loadSite!: LoadSiteAction
@@ -117,6 +112,7 @@ export default class SitePage extends Vue {
   canRestoreEntity!: CanRestoreEntityGetter
   setTabs!: SetTabsAction
   setTitle!: SetTitleAction
+  setLoading!: SetLoadingAction
 
   mounted () {
     this.initializeAppBar()
@@ -124,7 +120,7 @@ export default class SitePage extends Vue {
 
   async fetch (): Promise<void> {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.loadSite({
         siteId: this.siteId
       })
@@ -146,7 +142,7 @@ export default class SitePage extends Vue {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Loading of site / lab failed')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/sites/_siteId/attachments.vue b/pages/sites/_siteId/attachments.vue
index 195422aee..f5d359214 100644
--- a/pages/sites/_siteId/attachments.vue
+++ b/pages/sites/_siteId/attachments.vue
@@ -31,9 +31,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-    />
     <NuxtChild />
   </div>
 </template>
@@ -44,26 +41,27 @@ import { mapActions } from 'vuex'
 
 import { LoadSiteAttachmentsAction } from '@/store/sites'
 
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 
 @Component({
-  components: { ProgressIndicator },
-  methods: mapActions('sites', ['loadSiteAttachments'])
+  methods: {
+    ...mapActions('sites', ['loadSiteAttachments']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class SiteAttachmentsPage extends Vue {
-  private isLoading = false
-
   // vuex definition for typescript check
   loadSiteAttachments!: LoadSiteAttachmentsAction
+  setLoading!: SetLoadingAction
 
   async created () {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.loadSiteAttachments(this.siteId)
     } catch (e) {
       this.$store.commit('snackbar/setError', 'failed to fetch attachments')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/sites/_siteId/attachments/_attachmentId/edit.vue b/pages/sites/_siteId/attachments/_attachmentId/edit.vue
index ed9f4790f..99e8330b1 100644
--- a/pages/sites/_siteId/attachments/_attachmentId/edit.vue
+++ b/pages/sites/_siteId/attachments/_attachmentId/edit.vue
@@ -32,10 +32,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isInProgress"
-      :dark="isSaving"
-    />
     <v-card-actions>
       <v-spacer />
       <SaveAndCancelButtons
@@ -104,7 +100,7 @@ import { Attachment } from '@/models/Attachment'
 
 import { Rules } from '@/mixins/Rules'
 
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
 
 import { AttachmentsMixin } from '@/mixins/AttachmentsMixin'
@@ -115,17 +111,20 @@ import { AttachmentsMixin } from '@/mixins/AttachmentsMixin'
  */
 @Component({
   components: {
-    SaveAndCancelButtons,
-    ProgressIndicator
+    SaveAndCancelButtons
   },
   middleware: ['auth'],
-  computed: mapState('sites', ['siteAttachment']),
-  methods: mapActions('sites', ['loadSiteAttachment', 'loadSiteAttachments', 'updateSiteAttachment'])
+  computed: {
+    ...mapState('sites', ['siteAttachment']),
+    ...mapState('progressindicator', ['isLoading'])
+  },
+  methods: {
+    ...mapActions('sites', ['loadSiteAttachment', 'loadSiteAttachments', 'updateSiteAttachment']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 // @ts-ignore
 export default class AttachmentEditPage extends mixins(Rules, AttachmentsMixin, CheckEditAccess) {
-  private isSaving = false
-  private isLoading = false
   private valueCopy: Attachment = new Attachment()
 
   // vuex definition for typescript check
@@ -133,6 +132,8 @@ export default class AttachmentEditPage extends mixins(Rules, AttachmentsMixin,
   loadSiteAttachment!: LoadSiteAttachmentAction
   loadSiteAttachments!: LoadSiteAttachmentsAction
   updateSiteAttachment!: UpdateSiteAttachmentAction
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -158,7 +159,7 @@ export default class AttachmentEditPage extends mixins(Rules, AttachmentsMixin,
 
   async fetch (): Promise<void> {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.loadSiteAttachment(this.attachmentId)
       if (this.siteAttachment) {
         this.valueCopy = Attachment.createFromObject(this.siteAttachment)
@@ -166,7 +167,7 @@ export default class AttachmentEditPage extends mixins(Rules, AttachmentsMixin,
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to load attachment')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
@@ -178,17 +179,13 @@ export default class AttachmentEditPage extends mixins(Rules, AttachmentsMixin,
     return this.$route.params.attachmentId
   }
 
-  get isInProgress (): boolean {
-    return this.isLoading || this.isSaving
-  }
-
   async save () {
     if (!(this.$refs.attachmentsEditForm as Vue & { validate: () => boolean }).validate()) {
       this.$store.commit('snackbar/setError', 'Please correct your input')
       return
     }
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.updateSiteAttachment({
         siteId: this.siteId,
         attachment: this.valueCopy
@@ -199,7 +196,7 @@ export default class AttachmentEditPage extends mixins(Rules, AttachmentsMixin,
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to save attachments')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/sites/_siteId/attachments/index.vue b/pages/sites/_siteId/attachments/index.vue
index 31a358776..f2671d6bf 100644
--- a/pages/sites/_siteId/attachments/index.vue
+++ b/pages/sites/_siteId/attachments/index.vue
@@ -32,10 +32,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card-actions
       v-if="editable"
     >
@@ -128,19 +124,21 @@ import DeleteDialog from '@/components/shared/DeleteDialog.vue'
 import DownloadDialog from '@/components/shared/DownloadDialog.vue'
 import HintCard from '@/components/HintCard.vue'
 import DotMenuActionDelete from '@/components/DotMenuActionDelete.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import { getLastPathElement } from '@/utils/urlHelpers'
 
 @Component({
-  components: { ProgressIndicator, DotMenuActionDelete, HintCard, DeleteDialog, AttachmentListItem, BaseList, DownloadDialog },
+  components: { DotMenuActionDelete, HintCard, DeleteDialog, AttachmentListItem, BaseList, DownloadDialog },
   computed: mapState('sites', ['siteAttachments', 'site']),
-  methods: mapActions('sites', ['loadSiteAttachments', 'deleteSiteAttachment', 'downloadAttachment'])
+  methods: {
+    ...mapActions('sites', ['loadSiteAttachments', 'deleteSiteAttachment', 'downloadAttachment']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class SiteAttachmentShowPage extends Vue {
   @InjectReactive()
     editable!: boolean
 
-  private isSaving = false
   private showDeleteDialog = false
   private attachmentToDelete: Attachment | null = null
 
@@ -153,6 +151,7 @@ export default class SiteAttachmentShowPage extends Vue {
   deleteSiteAttachment!: DeleteSiteAttachmentAction
   loadSiteAttachments!: LoadSiteAttachmentsAction
   downloadAttachment!: DownloadAttachmentAction
+  setLoading!: SetLoadingAction
 
   get siteId (): string {
     return this.$route.params.siteId
@@ -173,7 +172,7 @@ export default class SiteAttachmentShowPage extends Vue {
       return
     }
     try {
-      this.isSaving = true
+      this.setLoading(true)
       const attachmendId = this.attachmentToDelete.id
       this.closeDialog()
       await this.deleteSiteAttachment(attachmendId)
@@ -182,7 +181,7 @@ export default class SiteAttachmentShowPage extends Vue {
     } catch (_error) {
       this.$store.commit('snackbar/setError', 'Failed to delete attachment')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/sites/_siteId/attachments/new.vue b/pages/sites/_siteId/attachments/new.vue
index 55b142048..bc10e5aeb 100644
--- a/pages/sites/_siteId/attachments/new.vue
+++ b/pages/sites/_siteId/attachments/new.vue
@@ -32,10 +32,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-form ref="attachmentsForm" class="pb-2" @submit.prevent>
       <v-card-actions>
         <v-spacer />
@@ -135,29 +131,30 @@ import { IUploadResult } from '@/services/sms/UploadApi'
 import { Attachment } from '@/models/Attachment'
 
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 
 import { Rules } from '@/mixins/Rules'
 import { UploadRules } from '@/mixins/UploadRules'
 
 @Component({
-  components: { ProgressIndicator, SaveAndCancelButtons },
+  components: { SaveAndCancelButtons },
   middleware: ['auth'],
   methods: {
     ...mapActions('sites', ['addSiteAttachment', 'loadSiteAttachments']),
-    ...mapActions('files', ['uploadFile'])
+    ...mapActions('files', ['uploadFile']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class SiteAttachmentAddPage extends mixins(Rules, UploadRules, CheckEditAccess) {
   private attachment: Attachment = new Attachment()
   private attachmentType: string = 'file'
   private file: File | null = null
-  private isSaving: boolean = false
 
   // vuex definition for typescript check
   uploadFile!: (file: File) => Promise<IUploadResult>
   addSiteAttachment!: AddSiteAttachmentAction
   loadSiteAttachments!: LoadSiteAttachmentsAction
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -203,7 +200,7 @@ export default class SiteAttachmentAddPage extends mixins(Rules, UploadRules, Ch
 
     let theFailureCanBeFromUpload = true
     try {
-      this.isSaving = true
+      this.setLoading(true)
 
       if (this.attachmentType !== 'url') {
         // Due to the validation we can be sure that the file is not null
@@ -222,7 +219,7 @@ export default class SiteAttachmentAddPage extends mixins(Rules, UploadRules, Ch
     } catch (error: any) {
       this.handleError(error, theFailureCanBeFromUpload)
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/sites/_siteId/basic.vue b/pages/sites/_siteId/basic.vue
index 19fcf6388..b5b2db367 100644
--- a/pages/sites/_siteId/basic.vue
+++ b/pages/sites/_siteId/basic.vue
@@ -40,10 +40,12 @@ import { Component, Vue } from 'nuxt-property-decorator'
 import { mapActions } from 'vuex'
 
 import { LoadSiteUsagesAction, LoadSiteTypesAction } from '@/store/vocabulary'
+import { SetLoadingAction } from '@/store/progressindicator'
 
 @Component({
   methods: {
-    ...mapActions('vocabulary', ['loadSiteUsages', 'loadSiteTypes'])
+    ...mapActions('vocabulary', ['loadSiteUsages', 'loadSiteTypes']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class SiteBasicPage extends Vue {
@@ -53,15 +55,14 @@ export default class SiteBasicPage extends Vue {
     }
   }
 
-  private isLoading = false
-
   // vuex definition for typescript check
   loadSiteUsages!: LoadSiteUsagesAction
   loadSiteTypes!: LoadSiteTypesAction
+  setLoading!: SetLoadingAction
 
   async fetch (): Promise<void> {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await Promise.all([
         this.loadSiteUsages(),
         this.loadSiteTypes()
@@ -69,7 +70,7 @@ export default class SiteBasicPage extends Vue {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to fetch types or usages')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/sites/_siteId/basic/edit.vue b/pages/sites/_siteId/basic/edit.vue
index 1cbbd0817..66bc32f03 100644
--- a/pages/sites/_siteId/basic/edit.vue
+++ b/pages/sites/_siteId/basic/edit.vue
@@ -33,10 +33,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card-actions>
       <v-spacer />
       <SaveAndCancelButtons
@@ -86,7 +82,7 @@ import { SitesState, LoadSiteAction, SaveSiteAction } from '@/store/sites'
 import { Site } from '@/models/Site'
 
 import SiteBasicDataForm from '@/components/sites/SiteBasicDataForm.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
 import NavigationGuardDialog from '@/components/shared/NavigationGuardDialog.vue'
 
@@ -97,7 +93,6 @@ import { LoadSiteUsagesAction, LoadSiteTypesAction, VocabularyState, LoadCountri
   components: {
     SaveAndCancelButtons,
     SiteBasicDataForm,
-    ProgressIndicator,
     NavigationGuardDialog
   },
   middleware: ['auth'],
@@ -108,12 +103,12 @@ import { LoadSiteUsagesAction, LoadSiteTypesAction, VocabularyState, LoadCountri
   },
   methods: {
     ...mapActions('sites', ['saveSite', 'loadSite']),
-    ...mapActions('vocabulary', ['loadSiteUsages', 'loadSiteTypes', 'loadCountries'])
+    ...mapActions('vocabulary', ['loadSiteUsages', 'loadSiteTypes', 'loadCountries']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class SiteEditBasicPage extends mixins(CheckEditAccess) {
   private siteCopy: Site | null = null
-  private isSaving: boolean = false
   private hasSaved: boolean = false
   private showNavigationWarning: boolean = false
   private to: RawLocation | null = null
@@ -128,6 +123,7 @@ export default class SiteEditBasicPage extends mixins(CheckEditAccess) {
   loadSiteTypes!: LoadSiteTypesAction
   countryNames!: CountryNamesGetter
   loadCountries!: LoadCountriesAction
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -200,7 +196,7 @@ export default class SiteEditBasicPage extends mixins(CheckEditAccess) {
     }
 
     try {
-      this.isSaving = true
+      this.setLoading(true)
       const savedSite = await this.saveSite(this.siteCopy)
       await this.loadSite({
         siteId: savedSite.id
@@ -211,7 +207,7 @@ export default class SiteEditBasicPage extends mixins(CheckEditAccess) {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Save failed')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/sites/_siteId/basic/index.vue b/pages/sites/_siteId/basic/index.vue
index 37360241e..9887ba0cc 100644
--- a/pages/sites/_siteId/basic/index.vue
+++ b/pages/sites/_siteId/basic/index.vue
@@ -31,10 +31,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card-actions>
       <v-spacer />
       <v-btn
@@ -147,14 +143,13 @@ import DotMenuActionDelete from '@/components/DotMenuActionDelete.vue'
 import DotMenuActionArchive from '@/components/DotMenuActionArchive.vue'
 import DotMenuActionRestore from '@/components/DotMenuActionRestore.vue'
 import DotMenuActionSensorML from '@/components/DotMenuActionSensorML.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import DownloadDialog from '@/components/shared/DownloadDialog.vue'
 import { ArchiveSiteAction, DeleteSiteAction, ExportAsSensorMLAction, GetSensorMLUrlAction, LoadSiteAction, RestoreSiteAction, SitesState } from '@/store/sites'
 import { Visibility } from '@/models/Visibility'
 
 @Component({
   components: {
-    ProgressIndicator,
     DotMenuActionDelete,
     DotMenuActionCopy,
     DotMenuActionSensorML,
@@ -168,15 +163,17 @@ import { Visibility } from '@/models/Visibility'
   },
 
   computed: mapState('sites', ['site']),
-  methods: mapActions('sites', [
-    'loadSite',
-    'deleteSite',
-    'archiveSite',
-    'restoreSite',
-    'exportAsSensorML',
-    'getSensorMLUrl'
-  ])
-
+  methods: {
+    ...mapActions('sites', [
+      'loadSite',
+      'deleteSite',
+      'archiveSite',
+      'restoreSite',
+      'exportAsSensorML',
+      'getSensorMLUrl'
+    ]),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class SiteShowBasicPage extends Vue {
   @InjectReactive()
@@ -191,8 +188,6 @@ export default class SiteShowBasicPage extends Vue {
   @InjectReactive()
     restoreable!: boolean
 
-  private isSaving = false
-
   private showDeleteDialog: boolean = false
   private showArchiveDialog: boolean = false
   private showDownloadDialog: boolean = false
@@ -205,6 +200,7 @@ export default class SiteShowBasicPage extends Vue {
   restoreSite!: RestoreSiteAction
   exportAsSensorML!: ExportAsSensorMLAction
   getSensorMLUrl!: GetSensorMLUrlAction
+  setLoading!: SetLoadingAction
 
   get siteId () {
     return this.$route.params.siteId
@@ -224,14 +220,14 @@ export default class SiteShowBasicPage extends Vue {
       return
     }
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.deleteSite(this.site.id)
       this.$store.commit('snackbar/setSuccess', 'Site / Lab deleted')
       this.$router.push('/sites')
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Site / Lab could not be deleted')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 
@@ -249,7 +245,7 @@ export default class SiteShowBasicPage extends Vue {
       return
     }
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.archiveSite(this.site.id)
       await this.loadSite({
         siteId: this.siteId
@@ -258,7 +254,7 @@ export default class SiteShowBasicPage extends Vue {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Site / Lab could not be archived')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
       this.showArchiveDialog = false
     }
   }
@@ -267,7 +263,7 @@ export default class SiteShowBasicPage extends Vue {
     if (this.site === null || this.site.id === null) {
       return
     }
-    this.isSaving = true
+    this.setLoading(true)
     try {
       await this.restoreSite(this.site.id)
       await this.loadSite({
@@ -277,7 +273,7 @@ export default class SiteShowBasicPage extends Vue {
     } catch (error) {
       this.$store.commit('snackbar/setError', 'Site / Lab could not be restored')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/sites/_siteId/contacts.vue b/pages/sites/_siteId/contacts.vue
index e7c828550..d55582659 100644
--- a/pages/sites/_siteId/contacts.vue
+++ b/pages/sites/_siteId/contacts.vue
@@ -31,9 +31,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-    />
     <NuxtChild />
   </div>
 </template>
@@ -45,25 +42,24 @@ import { mapActions } from 'vuex'
 import { LoadSiteContactRolesAction } from '@/store/sites'
 import { LoadCvContactRolesAction } from '@/store/vocabulary'
 
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 
 @Component({
-  components: { ProgressIndicator },
   methods: {
     ...mapActions('sites', ['loadSiteContactRoles']),
-    ...mapActions('vocabulary', ['loadCvContactRoles'])
+    ...mapActions('vocabulary', ['loadCvContactRoles']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class SiteContactsPage extends Vue {
-  private isLoading = false
-
   // vuex definition for typescript check
   loadSiteContactRoles!: LoadSiteContactRolesAction
   loadCvContactRoles!: LoadCvContactRolesAction
+  setLoading!: SetLoadingAction
 
   async fetch () {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await Promise.all([
         this.loadSiteContactRoles(this.siteId),
         this.loadCvContactRoles()
@@ -71,7 +67,7 @@ export default class SiteContactsPage extends Vue {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to fetch contacts')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/sites/_siteId/contacts/index.vue b/pages/sites/_siteId/contacts/index.vue
index c3d741f79..4455184a3 100644
--- a/pages/sites/_siteId/contacts/index.vue
+++ b/pages/sites/_siteId/contacts/index.vue
@@ -31,10 +31,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isSaving"
-      dark
-    />
     <v-card-actions>
       <v-spacer />
       <v-btn
@@ -91,7 +87,7 @@ import { mapActions, mapState } from 'vuex'
 import { LoadSiteContactRolesAction, RemoveSiteContactRoleAction } from '@/store/sites'
 
 import HintCard from '@/components/HintCard.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import BaseList from '@/components/shared/BaseList.vue'
 import ContactRoleListItem from '@/components/contacts/ContactRoleListItem.vue'
 import DotMenuActionDelete from '@/components/DotMenuActionDelete.vue'
@@ -101,24 +97,25 @@ import DotMenuActionDelete from '@/components/DotMenuActionDelete.vue'
     DotMenuActionDelete,
     ContactRoleListItem,
     BaseList,
-    ProgressIndicator,
     HintCard
   },
   computed: {
     ...mapState('sites', ['siteContactRoles']),
     ...mapState('vocabulary', ['cvContactRoles'])
   },
-  methods: mapActions('sites', ['loadSiteContactRoles', 'removeSiteContactRole'])
+  methods: {
+    ...mapActions('sites', ['loadSiteContactRoles', 'removeSiteContactRole']),
+    ...mapActions('progressindicator', ['setLoading'])
+  }
 })
 export default class SiteShowContactPage extends Vue {
   @InjectReactive()
     editable!: boolean
 
-  private isSaving = false
-
   // vuex definition for typescript check
   removeSiteContactRole!: RemoveSiteContactRoleAction
   loadSiteContactRoles!: LoadSiteContactRolesAction
+  setLoading!: SetLoadingAction
 
   get siteId (): string {
     return this.$route.params.siteId
@@ -126,7 +123,7 @@ export default class SiteShowContactPage extends Vue {
 
   async removeContactRole (contactRoleId: string) {
     try {
-      this.isSaving = true
+      this.setLoading(true)
       await this.removeSiteContactRole({
         siteContactRoleId: contactRoleId
       })
@@ -135,7 +132,7 @@ export default class SiteShowContactPage extends Vue {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Removing contact failed')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 }
diff --git a/pages/sites/_siteId/contacts/new.vue b/pages/sites/_siteId/contacts/new.vue
index 094b0b059..5b1fe6a05 100644
--- a/pages/sites/_siteId/contacts/new.vue
+++ b/pages/sites/_siteId/contacts/new.vue
@@ -2,7 +2,7 @@
 Web client of the Sensor Management System software developed within the
 Helmholtz DataHub Initiative by GFZ and UFZ.
 
-Copyright (C) 2020 - 2022
+Copyright (C) 2020 - 2023
 - Nils Brinckmann (GFZ, nils.brinckmann@gfz-potsdam.de)
 - Marc Hanisch (GFZ, marc.hanisch@gfz-potsdam.de)
 - Tim Eder (UFZ, tim.eder@ufz.de)
@@ -31,10 +31,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isInProgress"
-      :dark="isSaving"
-    />
     <contact-role-assignment-form
       :contacts="contacts"
       :contact="selectedContact"
@@ -59,30 +55,29 @@ import { LoadCvContactRolesAction } from '@/store/vocabulary'
 import { Contact } from '@/models/Contact'
 import { ContactRole } from '@/models/ContactRole'
 
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 import ContactRoleAssignmentForm from '@/components/shared/ContactRoleAssignmentForm.vue'
 
 @Component({
   components: {
-    ProgressIndicator,
     ContactRoleAssignmentForm
   },
   middleware: ['auth'],
   computed: {
     ...mapState('sites', ['siteContactRoles']),
     ...mapState('contacts', ['contacts']),
-    ...mapState('vocabulary', ['cvContactRoles'])
+    ...mapState('vocabulary', ['cvContactRoles']),
+    ...mapState('progressindicator', ['isLoading'])
   },
   methods: {
     ...mapActions('contacts', ['loadAllContacts']),
     ...mapActions('sites', ['loadSiteContactRoles', 'addSiteContactRole']),
-    ...mapActions('vocabulary', ['loadCvContactRoles'])
+    ...mapActions('vocabulary', ['loadCvContactRoles']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 export default class SiteAssignContactPage extends mixins(CheckEditAccess) {
   private selectedContact: Contact | null = null
-  private isLoading: boolean = false
-  private isSaving: boolean = false
 
   // vuex definition for typescript check
   siteContactRoles!: SitesState['siteContactRoles']
@@ -91,6 +86,8 @@ export default class SiteAssignContactPage extends mixins(CheckEditAccess) {
   addSiteContactRole!: AddSiteContactRoleAction
   loadAllContacts!: LoadAllContactsAction
   loadCvContactRoles!: LoadCvContactRolesAction
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
 
   /**
    * route to which the user is redirected when he is not allowed to access the page
@@ -116,7 +113,7 @@ export default class SiteAssignContactPage extends mixins(CheckEditAccess) {
 
   async fetch (): Promise<void> {
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.loadAllContacts()
 
       const redirectContactId = this.$route.query.contact
@@ -126,7 +123,7 @@ export default class SiteAssignContactPage extends mixins(CheckEditAccess) {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Failed to fetch related contacts')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
@@ -134,14 +131,10 @@ export default class SiteAssignContactPage extends mixins(CheckEditAccess) {
     return this.$route.params.siteId
   }
 
-  get isInProgress (): boolean {
-    return this.isLoading || this.isSaving
-  }
-
   async assignContact (contactRole: ContactRole | null) {
     if (this.editable && contactRole) {
       try {
-        this.isSaving = true
+        this.setLoading(true)
         await this.addSiteContactRole({
           siteId: this.siteId,
           contactRole
@@ -152,7 +145,7 @@ export default class SiteAssignContactPage extends mixins(CheckEditAccess) {
       } catch (e) {
         this.$store.commit('snackbar/setError', 'Failed to add a contact')
       } finally {
-        this.isSaving = false
+        this.setLoading(false)
       }
     }
   }
diff --git a/pages/sites/copy/_siteId.vue b/pages/sites/copy/_siteId.vue
index 07e8efb7e..c69c2554b 100644
--- a/pages/sites/copy/_siteId.vue
+++ b/pages/sites/copy/_siteId.vue
@@ -35,10 +35,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isInProgress"
-      :dark="isSaving"
-    />
     <v-card
       flat
     >
@@ -127,34 +123,33 @@ import { Site } from '@/models/Site'
 
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
 import SiteBasicDataForm from '@/components/sites/SiteBasicDataForm.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 import { hasSelfIntersection } from '@/utils/mapHelpers'
 
 @Component({
   components: {
     SaveAndCancelButtons,
-    SiteBasicDataForm,
-    ProgressIndicator
+    SiteBasicDataForm
   },
   middleware: ['auth'],
   computed: {
     ...mapGetters('permissions', ['canAccessEntity', 'canModifyEntity', 'userGroups']),
     ...mapGetters('vocabulary', ['countryNames']),
     ...mapState('sites', ['site']),
-    ...mapState('vocabulary', ['siteUsages', 'siteTypes'])
+    ...mapState('vocabulary', ['siteUsages', 'siteTypes']),
+    ...mapState('progressindicator', ['isLoading'])
 
   },
   methods: {
     ...mapActions('sites', ['copySite', 'loadSite', 'createPid']),
     ...mapActions('appbar', ['setTitle', 'setTabs']),
-    ...mapActions('vocabulary', ['loadSiteUsages', 'loadSiteTypes', 'loadCountries'])
+    ...mapActions('vocabulary', ['loadSiteUsages', 'loadSiteTypes', 'loadCountries']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 // @ts-ignore
 export default class SiteCopyPage extends Vue {
   private siteToCopy: Site | null = null
-  private isSaving = false
-  private isLoading = false
 
   private copyContacts: boolean = true
   private copyAttachments: boolean = false
@@ -174,11 +169,13 @@ export default class SiteCopyPage extends Vue {
   loadSiteTypes!: LoadSiteTypesAction
   countryNames!: CountryNamesGetter
   loadCountries!: LoadCountriesAction
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
 
   async created () {
     this.initializeAppBar()
     try {
-      this.isLoading = true
+      this.setLoading(true)
       await this.loadSite({
         siteId: this.siteId
       })
@@ -206,7 +203,7 @@ export default class SiteCopyPage extends Vue {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Loading site / lab failed')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
@@ -214,10 +211,6 @@ export default class SiteCopyPage extends Vue {
     return this.$route.params.siteId
   }
 
-  get isInProgress (): boolean {
-    return this.isLoading || this.isSaving
-  }
-
   getPreparedSiteForCopy (): Site | null {
     if (!this.site) {
       return new Site()
@@ -249,7 +242,7 @@ export default class SiteCopyPage extends Vue {
     }
 
     try {
-      this.isSaving = true
+      this.setLoading(true)
       const savedSiteId = await this.copySite({
         site: this.siteToCopy,
         copyContacts: this.copyContacts,
@@ -261,7 +254,7 @@ export default class SiteCopyPage extends Vue {
     } catch (_error) {
       this.$store.commit('snackbar/setError', 'Copy failed')
     } finally {
-      this.isSaving = false
+      this.setLoading(false)
     }
   }
 
diff --git a/pages/sites/index.vue b/pages/sites/index.vue
index 3599b702b..6d2a921d6 100644
--- a/pages/sites/index.vue
+++ b/pages/sites/index.vue
@@ -159,14 +159,7 @@ permissions and limitations under the Licence.
       </v-tab-item>
     </v-tabs-items>
 
-    <v-progress-circular
-      v-if="loading"
-      class="progress-spinner"
-      color="primary"
-      indeterminate
-    />
-
-    <div v-if="sites.length <= 0 && !loading">
+    <div v-if="sites.length <= 0 && !isLoading">
       <p class="text-center">
         There are no sites / labs that match your search criteria.
       </p>
@@ -182,17 +175,7 @@ permissions and limitations under the Licence.
             <FoundEntries v-model="totalCount" entity-name="site / lab" entity-name-plural="sites & labs" />
             <v-spacer />
 
-            <template v-if="sites.length > 0">
-              <v-dialog v-model="processing" max-width="100">
-                <v-card>
-                  <v-card-text>
-                    <div class="text-center pt-2">
-                      <v-progress-circular indeterminate />
-                    </div>
-                  </v-card-text>
-                </v-card>
-              </v-dialog>
-            </template>
+            <template v-if="sites.length > 0" />
           </v-subheader>
         </v-col>
 
@@ -202,7 +185,7 @@ permissions and limitations under the Licence.
         >
           <v-pagination
             v-model="page"
-            :disabled="loading"
+            :disabled="isLoading"
             :length="totalPages"
             :total-visible="7"
             @input="runSearch"
@@ -253,7 +236,7 @@ permissions and limitations under the Licence.
       </BaseList>
       <v-pagination
         v-model="page"
-        :disabled="loading"
+        :disabled="isLoading"
         :length="totalPages"
         :total-visible="7"
         @input="runSearch"
@@ -336,6 +319,7 @@ import { SiteUsage } from '@/models/SiteUsage'
 import { SiteType } from '@/models/SiteType'
 import FoundEntries from '@/components/shared/FoundEntries.vue'
 import { Visibility } from '@/models/Visibility'
+import { SetLoadingAction, LoadingSpinnerState } from '@/store/progressindicator'
 
 @Component({
   components: {
@@ -357,6 +341,7 @@ import { Visibility } from '@/models/Visibility'
   },
   computed: {
     ...mapGetters('permissions', ['canDeleteEntity', 'canArchiveEntity', 'canRestoreEntity', 'canAccessEntity', 'permissionGroups']),
+    ...mapState('progressindicator', ['isLoading']),
     ...mapState('appbar', ['activeTab']),
     ...mapState('sites', ['sites', 'pageNumber', 'pageSize', 'totalPages', 'totalCount', 'site']),
     ...mapGetters('sites', ['pageSizes']),
@@ -378,13 +363,12 @@ import { Visibility } from '@/models/Visibility'
       'getSensorMLUrl'
     ]),
     ...mapActions('permissions', ['loadPermissionGroups']),
-    ...mapActions('vocabulary', ['loadSiteUsages', 'loadSiteTypes'])
+    ...mapActions('vocabulary', ['loadSiteUsages', 'loadSiteTypes']),
+    ...mapActions('progressindicator', ['setLoading'])
 
   }
 })
 export default class SearchSitesPage extends Vue {
-  private loading = false
-  private processing = false
   private selectedSearchSiteUsages: SiteUsage[] = []
   private selectedSearchSiteTypes: SiteType[] = []
   private selectedSearchPermissionGroups: PermissionGroup[] = []
@@ -430,11 +414,13 @@ export default class SearchSitesPage extends Vue {
   replaceSiteInSites !: ReplaceSiteInSitesAction
   exportAsSensorML!: ExportAsSensorMLAction
   getSensorMLUrl!: GetSensorMLUrlAction
+  isLoading!: LoadingSpinnerState['isLoading']
+  setLoading!: SetLoadingAction
 
   async created () {
     this.initializeAppBar()
     try {
-      this.loading = true
+      this.setLoading(true)
       await Promise.all([
         this.loadPermissionGroups(),
         this.loadSiteUsages(),
@@ -445,7 +431,7 @@ export default class SearchSitesPage extends Vue {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Loading of sites & labs failed')
     } finally {
-      this.loading = false
+      this.setLoading(false)
     }
   }
 
@@ -539,14 +525,14 @@ export default class SearchSitesPage extends Vue {
 
   async runSearch (): Promise<void> {
     try {
-      this.loading = true
+      this.setLoading(true)
       this.initUrlQueryParams()
       await this.searchSitesPaginated(this.searchParams)
       this.setPageAndSizeInUrl()
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Loading of sites & labs failed')
     } finally {
-      this.loading = false
+      this.setLoading(false)
     }
   }
 
@@ -565,14 +551,14 @@ export default class SearchSitesPage extends Vue {
       return
     }
     try {
-      this.loading = true
+      this.setLoading(true)
       await this.deleteSite(this.siteToDelete.id)
       this.runSearch()
       this.$store.commit('snackbar/setSuccess', 'Site / Lab deleted')
     } catch (_error) {
       this.$store.commit('snackbar/setError', 'Site / Lab could not be deleted')
     } finally {
-      this.loading = false
+      this.setLoading(false)
       this.closeDialog()
     }
   }
@@ -592,7 +578,7 @@ export default class SearchSitesPage extends Vue {
       return
     }
     try {
-      this.loading = true
+      this.setLoading(true)
       await this.archiveSite(this.siteToArchive.id)
       await this.loadSite({
         siteId: this.siteToArchive.id
@@ -602,14 +588,14 @@ export default class SearchSitesPage extends Vue {
     } catch (_error) {
       this.$store.commit('snackbar/setError', 'Site / Lab could not be archived')
     } finally {
-      this.loading = false
+      this.setLoading(false)
       this.closeArchiveDialog()
     }
   }
 
   async runRestoreSite (site: Site) {
     if (site.id) {
-      this.loading = true
+      this.setLoading(true)
       try {
         await this.restoreSite(site.id)
         await this.loadSite({
@@ -620,7 +606,7 @@ export default class SearchSitesPage extends Vue {
       } catch (error) {
         this.$store.commit('snackbar/setError', 'Site / Lab could not be restored')
       } finally {
-        this.loading = false
+        this.setLoading(false)
       }
     }
   }
diff --git a/pages/sites/new.vue b/pages/sites/new.vue
index 8b32da41f..73d838086 100644
--- a/pages/sites/new.vue
+++ b/pages/sites/new.vue
@@ -34,10 +34,6 @@ permissions and limitations under the Licence.
 -->
 <template>
   <div>
-    <ProgressIndicator
-      v-model="isLoading"
-      dark
-    />
     <v-card
       flat
     >
@@ -74,7 +70,7 @@ import { mapActions, mapState, mapGetters } from 'vuex'
 
 import SaveAndCancelButtons from '@/components/shared/SaveAndCancelButtons.vue'
 import SiteBasicDataForm from '@/components/sites/SiteBasicDataForm.vue'
-import ProgressIndicator from '@/components/ProgressIndicator.vue'
+import { SetLoadingAction } from '@/store/progressindicator'
 import { Site } from '@/models/Site'
 import { SaveSiteAction } from '@/store/sites'
 import { SetTabsAction, SetTitleAction } from '@/store/appbar'
@@ -84,7 +80,7 @@ import { hasSelfIntersection } from '@/utils/mapHelpers'
 
 @Component({
   components: {
-    SaveAndCancelButtons, SiteBasicDataForm, ProgressIndicator
+    SaveAndCancelButtons, SiteBasicDataForm
   },
   middleware: ['auth'],
   computed: {
@@ -95,12 +91,12 @@ import { hasSelfIntersection } from '@/utils/mapHelpers'
     ...mapActions('sites', ['saveSite']),
     ...mapActions('vocabulary', ['loadEpsgCodes', 'loadSiteUsages', 'loadSiteTypes', 'loadCountries']),
 
-    ...mapActions('appbar', ['setTitle', 'setTabs'])
+    ...mapActions('appbar', ['setTitle', 'setTabs']),
+    ...mapActions('progressindicator', ['setLoading'])
   }
 })
 // @ts-ignore
 export default class SiteNewPage extends Vue {
-  private isLoading = false
   private site: Site = new Site()
 
   // vuex definition for typescript check
@@ -114,6 +110,7 @@ export default class SiteNewPage extends Vue {
   loadSiteTypes!: LoadSiteTypesAction
   loadCountries!: LoadCountriesAction
   countryNames!: CountryNamesGetter
+  setLoading!: SetLoadingAction
 
   async created () {
     this.initializeAppBar()
@@ -146,7 +143,7 @@ export default class SiteNewPage extends Vue {
     }
 
     try {
-      this.isLoading = true
+      this.setLoading(true)
       const savedSite = await this.saveSite(this.site)
 
       this.$store.commit('snackbar/setSuccess', 'Site / Lab created')
@@ -154,7 +151,7 @@ export default class SiteNewPage extends Vue {
     } catch (e) {
       this.$store.commit('snackbar/setError', 'Save failed')
     } finally {
-      this.isLoading = false
+      this.setLoading(false)
     }
   }
 
diff --git a/store/progressindicator.ts b/store/progressindicator.ts
new file mode 100644
index 000000000..2409398ad
--- /dev/null
+++ b/store/progressindicator.ts
@@ -0,0 +1,65 @@
+/**
+ * @license
+ * Web client of the Sensor Management System software developed within
+ * the Helmholtz DataHub Initiative by GFZ and UFZ.
+ *
+ * Copyright (C) 2020 - 2023
+ * - Nils Brinckmann (GFZ, nils.brinckmann@gfz-potsdam.de)
+ * - Marc Hanisch (GFZ, marc.hanisch@gfz-potsdam.de)
+ * - Rubankumar Moorthy (FZJ, r.moorthy@fz-juelich.de)
+ * - Helmholtz Centre Potsdam - GFZ German Research Centre for
+ *   Geosciences (GFZ, https://www.gfz-potsdam.de)
+ * - Research Centre Juelich GmbH - Institute of Bio- and Geosciences
+ *   Agrosphere (IBG-3, https://www.fz-juelich.de/en/ibg/ibg-3)
+ *
+ * Parts of this program were developed within the context of the
+ * following publicly funded projects or measures:
+ * - Helmholtz Earth and Environment DataHub
+ *   (https://www.helmholtz.de/en/research/earth_and_environment/initiatives/#h51095)
+ *
+ * Licensed under the HEESIL, Version 1.0 or - as soon they will be
+ * approved by the "Community" - subsequent versions of the HEESIL
+ * (the "Licence").
+ *
+ * You may not use this work except in compliance with the Licence.
+ *
+ * You may obtain a copy of the Licence at:
+ * https://gitext.gfz-potsdam.de/software/heesil
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the Licence is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the Licence for the specific language governing
+ * permissions and limitations under the Licence.
+ */
+import { Commit, ActionTree } from 'vuex/types'
+import { RootState } from '@/store'
+
+export interface LoadingSpinnerState {
+  isLoading: boolean
+}
+
+const state = (): LoadingSpinnerState => ({
+  isLoading: false
+})
+
+export type SetLoadingAction = (isLoading: boolean) => void
+
+const actions: ActionTree<LoadingSpinnerState, RootState> = {
+  setLoading ({ commit }: { commit: Commit }, isLoading: boolean) {
+    commit('setLoading', isLoading)
+  }
+}
+
+const mutations = {
+  setLoading (state: LoadingSpinnerState, isLoading: boolean) {
+    state.isLoading = isLoading
+  }
+}
+
+export default {
+  namespaced: true,
+  state,
+  actions,
+  mutations
+}
diff --git a/store/sites.ts b/store/sites.ts
index b37266cbf..230d84bc9 100644
--- a/store/sites.ts
+++ b/store/sites.ts
@@ -250,7 +250,7 @@ const actions: ActionTree<SitesState, RootState> = {
   async updateSiteAttachment (_, { siteId, attachment }: { siteId: string, attachment: Attachment }): Promise<Attachment> {
     return await this.$api.siteAttachments.update(siteId, attachment)
   },
-  async deleteSiteAttachment (_, { attachmentId }: { attachmentId: string }): Promise<void> {
+  async deleteSiteAttachment (_, attachmentId: string): Promise<void> {
     return await this.$api.siteAttachments.deleteById(attachmentId)
   },
   async downloadAttachment (_, attachmentUrl: string): Promise<Blob> {
-- 
GitLab