changeset 2384:c06b001dc26b

client: improved popup implementation For deleting users and templates there was a more or less quick n' dirty implementation of a confirmation dialog/popup. Since we need this kind of dialog in several more places I generalized the implementation a bit and made it more robust.
author Markus Kottlaender <markus@intevation.de>
date Mon, 25 Feb 2019 13:11:30 +0100
parents 8d025f85a3fe
children 279334be495c
files client/src/assets/application.scss client/src/components/App.vue client/src/components/ImportStretches.vue client/src/components/Popup.vue client/src/components/importschedule/Importschedule.vue client/src/components/systemconfiguration/PDFTemplates.vue client/src/components/usermanagement/Usermanagement.vue client/src/store/application.js
diffstat 8 files changed, 389 insertions(+), 224 deletions(-) [+]
line wrap: on
line diff
--- a/client/src/assets/application.scss	Mon Feb 25 08:47:59 2019 +0100
+++ b/client/src/assets/application.scss	Mon Feb 25 13:11:30 2019 +0100
@@ -99,17 +99,6 @@
   margin: 0 0.5rem 1rem 0.5rem;
 }
 
-.popup {
-  width: 300px;
-  max-width: 300px;
-  @extend %fully-centered;
-}
-
-.popup.show {
-  margin: 0.5rem 0 0 0;
-  max-height: 999px;
-}
-
 // needed to fix the whitespace problem of
 //   https://github.com/Polyconseil/vue-gettext/issues/80;
 // use like
@@ -131,10 +120,10 @@
   font-weight: bold;
 }
 
-.list-fade-enter-active, .list-fade-leave-active {
+.fade-enter-active, .fade-leave-active {
   transition: opacity .3s;
 }
-.list-fade-enter, .list-fade-leave-to {
+.fade-enter, .fade-leave-to {
   opacity: 0;
 }
 
--- a/client/src/components/App.vue	Mon Feb 25 08:47:59 2019 +0100
+++ b/client/src/components/App.vue	Mon Feb 25 13:11:30 2019 +0100
@@ -29,6 +29,7 @@
     </div>
     <div class="d-flex flex-column"><router-view /></div>
     <vue-snotify></vue-snotify>
+    <Popup />
   </div>
 </template>
 
@@ -114,7 +115,8 @@
     Sidebar: () => import("./Sidebar"),
     Search: () => import("./Search"),
     Contextbox: () => import("./Contextbox"),
-    Toolbar: () => import("./toolbar/Toolbar")
+    Toolbar: () => import("./toolbar/Toolbar"),
+    Popup: () => import("./Popup")
   }
 };
 </script>
--- a/client/src/components/ImportStretches.vue	Mon Feb 25 08:47:59 2019 +0100
+++ b/client/src/components/ImportStretches.vue	Mon Feb 25 13:11:30 2019 +0100
@@ -11,8 +11,7 @@
             <th class="header"><translate>Name</translate></th>
             <th class="header"><translate>Datum</translate></th>
             <th class="header"><translate>Source organization</translate></th>
-            <th class="tools">&nbsp;</th>
-            <th class="tools">&nbsp;</th>
+            <th></th>
           </tr>
         </thead>
         <tbody>
@@ -39,21 +38,19 @@
               {{ formatSurveyDate(stretch.properties["date_info"]) }}
             </td>
             <td>{{ stretch.properties["source_organization"] }}</td>
-            <td>
-              <font-awesome-icon
-                class="pointer"
+            <td class="text-right">
+              <button
+                class="btn btn-sm btn-dark mr-1"
                 @click="editStretch(index)"
-                icon="pencil-alt"
-                fixed-width
-              ></font-awesome-icon>
-            </td>
-            <td>
-              <font-awesome-icon
-                class="pointer"
-                @click="deleteStretch(index)"
-                icon="trash"
-                fixed-width
-              ></font-awesome-icon>
+              >
+                <font-awesome-icon icon="pencil-alt" fixed-width />
+              </button>
+              <button
+                class="btn btn-sm btn-dark"
+                @click="deleteStretch(stretch)"
+              >
+                <font-awesome-icon icon="trash" fixed-width />
+              </button>
             </td>
           </tr>
         </tbody>
@@ -352,10 +349,30 @@
       this.endrhm = this.sanitizeRHM(properties.upper);
       this.idEditable = false;
     },
-    deleteStretch(index) {
-      displayInfo({
-        title: this.$gettext("Not implemented"),
-        message: this.$gettext("Deleting " + this.stretches[index].id)
+    deleteStretch(stretch) {
+      this.$store.commit("application/popup", {
+        icon: "trash",
+        title: this.$gettext("Delete Stretch"),
+        content:
+          this.$gettext("Do you really want to delete this stretch:") +
+          `<br>
+        <b>${stretch.properties.name}, ${
+            stretch.properties.source_organization
+          } (${stretch.properties.countries})</b>`,
+        confirm: {
+          label: this.$gettext("Delete"),
+          icon: "trash",
+          callback: () => {
+            displayInfo({
+              title: this.$gettext("Not implemented"),
+              message: this.$gettext("Deleting " + stretch.id)
+            });
+          }
+        },
+        cancel: {
+          label: this.$gettext("Cancel"),
+          icon: "times"
+        }
       });
     },
     moveMapToStretch(index) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/src/components/Popup.vue	Mon Feb 25 13:11:30 2019 +0100
@@ -0,0 +1,137 @@
+<template>
+  <transition name="fade">
+    <div
+      class="overlay d-flex justify-content-center align-items-center"
+      v-if="popup"
+    >
+      <div class="popup">
+        <h6 class="popup-header">
+          <span class="popup-title">
+            <font-awesome-icon :icon="popup.icon" class="popup-icon" />
+            {{ popup.title }}
+          </span>
+          <span class="popup-close" @click="close()">
+            <font-awesome-icon icon="times" />
+          </span>
+        </h6>
+        <div class="popup-content" v-html="popup.content"></div>
+        <div class="popup-footer" v-if="popup.cancel || popup.confirm">
+          <button
+            class="btn btn-sm btn-warning"
+            @click="cancel"
+            v-if="popup.cancel"
+          >
+            <font-awesome-icon
+              :icon="popup.cancel.icon"
+              class="fa-fw"
+              v-if="popup.cancel.icon"
+            />
+            {{ popup.cancel.label || $gettext("Cancel") }}
+          </button>
+          <span />
+          <button
+            class="btn btn-sm btn-info"
+            @click="confirm"
+            v-if="popup.confirm"
+          >
+            <font-awesome-icon
+              :icon="popup.confirm.icon"
+              class="fa-fw"
+              v-if="popup.confirm.icon"
+            />
+            {{ popup.confirm.label || $gettext("Confirm") }}
+          </button>
+        </div>
+      </div>
+    </div>
+  </transition>
+</template>
+
+<style lang="sass" scoped>
+.overlay
+  position: fixed
+  z-index: 9
+  top: 0
+  right: 0
+  bottom: 0
+  left: 0
+  background: rgba(0, 0, 0, .3)
+  .popup
+    display: flex
+    flex-direction: column
+    box-shadow: 0 .5rem 1rem rgba(0,0,0,.15)
+    background-color: #fff
+    border-radius: 0.25rem
+    max-width: 320px
+    .popup-header
+      display: flex
+      justify-content: space-between
+      align-items: center
+      padding-left: .5rem
+      border-bottom: 1px solid #dee2e6
+      color: $color-info
+      margin-bottom: 0
+      padding: 0.25rem
+      font-weight: bold
+      .popup-title
+        padding-left: 0.25rem
+        .popup-icon
+          margin-right: 0.25rem
+      .popup-close
+        color: #aaa
+        padding: 3px 5px
+        border-radius: 0.25rem
+        cursor: pointer
+        transition: background-color 0.3s, color 0.3s
+        &:hover
+          color: #888
+          background-color: #eee
+    .popup-content
+      padding: 1rem
+    .popup-footer
+      display: flex
+      justify-content: space-between
+      align-items: center
+      border-top: 1px solid #dee2e6
+      padding: 0.25rem
+</style>
+
+<script>
+/* This is Free Software under GNU Affero General Public License v >= 3.0
+ * without warranty, see README.md and license for details.
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ * License-Filename: LICENSES/AGPL-3.0.txt
+ *
+ * Copyright (C) 2018 by via donau
+ *   – Österreichische Wasserstraßen-Gesellschaft mbH
+ * Software engineering by Intevation GmbH
+ *
+ * Author(s):
+ * Markus Kottländer <markus.kottlaender@intevation.de>
+ */
+
+import { mapState } from "vuex";
+
+export default {
+  name: "popup",
+  computed: {
+    ...mapState("application", ["popup"])
+  },
+  methods: {
+    confirm() {
+      if (this.popup.confirm && this.popup.confirm.callback)
+        this.popup.confirm.callback();
+      this.close();
+    },
+    cancel() {
+      if (this.popup.cancel && this.popup.cancel.callback)
+        this.popup.cancel.callback();
+      this.close();
+    },
+    close() {
+      this.$store.commit("application/popup", null);
+    }
+  }
+};
+</script>
--- a/client/src/components/importschedule/Importschedule.vue	Mon Feb 25 08:47:59 2019 +0100
+++ b/client/src/components/importschedule/Importschedule.vue	Mon Feb 25 13:11:30 2019 +0100
@@ -28,69 +28,71 @@
                 />
               </div>
             </div>
-            <table v-if="schedules.length" class="table table-hover">
-              <thead>
-                <tr>
-                  <th><translate>ID</translate></th>
-                  <th><translate>Type</translate></th>
-                  <th><translate>Author</translate></th>
-                  <th><translate>Schedule</translate></th>
-                  <th><translate>Email</translate></th>
-                  <th style="width: 140px"></th>
-                </tr>
-              </thead>
-              <tbody>
-                <tr v-for="schedule in schedules" :key="schedule.id">
-                  <td>{{ schedule.id }}</td>
-                  <td>{{ schedule.kind.toUpperCase() }}</td>
-                  <td>{{ schedule.user }}</td>
-                  <td>{{ schedule.config.cron }}</td>
-                  <td>
-                    <font-awesome-icon
-                      v-if="schedule.config['send-email']"
-                      class="fa-fw mr-2"
-                      fixed-width
-                      icon="check"
-                    ></font-awesome-icon>
-                  </td>
-                  <td class="text-right">
-                    <button
-                      @click="editSchedule(schedule.id)"
-                      class="btn btn-sm btn-dark mr-1"
-                      :disabled="importScheduleDetailVisible"
-                    >
+            <transition name="fade">
+              <table v-if="schedules.length" class="table table-hover">
+                <thead>
+                  <tr>
+                    <th><translate>ID</translate></th>
+                    <th><translate>Type</translate></th>
+                    <th><translate>Author</translate></th>
+                    <th><translate>Schedule</translate></th>
+                    <th><translate>Email</translate></th>
+                    <th style="width: 140px"></th>
+                  </tr>
+                </thead>
+                <transition-group name="fade" tag="tbody">
+                  <tr v-for="schedule in schedules" :key="schedule.id">
+                    <td>{{ schedule.id }}</td>
+                    <td>{{ schedule.kind.toUpperCase() }}</td>
+                    <td>{{ schedule.user }}</td>
+                    <td>{{ schedule.config.cron }}</td>
+                    <td>
                       <font-awesome-icon
-                        icon="pencil-alt"
+                        v-if="schedule.config['send-email']"
+                        class="fa-fw mr-2"
                         fixed-width
-                      ></font-awesome-icon>
-                    </button>
-                    <button
-                      @click="deleteSchedule(schedule.id)"
-                      class="btn btn-sm btn-dark mr-1"
-                      :disabled="importScheduleDetailVisible"
-                    >
-                      <font-awesome-icon
-                        icon="trash"
-                        fixed-width
+                        icon="check"
                       ></font-awesome-icon>
-                    </button>
-                    <button
-                      @click="triggerManualImport(schedule.id)"
-                      class="btn btn-sm btn-dark"
-                      :disabled="importScheduleDetailVisible"
-                    >
-                      <font-awesome-icon
-                        icon="play"
-                        fixed-width
-                      ></font-awesome-icon>
-                    </button>
-                  </td>
-                </tr>
-              </tbody>
-            </table>
-            <div v-else class="mt-4 small text-center py-3">
-              <translate>No scheduled imports</translate>
-            </div>
+                    </td>
+                    <td class="text-right">
+                      <button
+                        @click="editSchedule(schedule.id)"
+                        class="btn btn-sm btn-dark mr-1"
+                        :disabled="importScheduleDetailVisible"
+                      >
+                        <font-awesome-icon
+                          icon="pencil-alt"
+                          fixed-width
+                        ></font-awesome-icon>
+                      </button>
+                      <button
+                        @click="deleteSchedule(schedule)"
+                        class="btn btn-sm btn-dark mr-1"
+                        :disabled="importScheduleDetailVisible"
+                      >
+                        <font-awesome-icon
+                          icon="trash"
+                          fixed-width
+                        ></font-awesome-icon>
+                      </button>
+                      <button
+                        @click="triggerManualImport(schedule.id)"
+                        class="btn btn-sm btn-dark"
+                        :disabled="importScheduleDetailVisible"
+                      >
+                        <font-awesome-icon
+                          icon="play"
+                          fixed-width
+                        ></font-awesome-icon>
+                      </button>
+                    </td>
+                  </tr>
+                </transition-group>
+              </table>
+              <div v-else class="mt-4 small text-center py-3">
+                <translate>No scheduled imports</translate>
+              </div>
+            </transition>
             <div class="text-right">
               <button
                 :disabled="importScheduleDetailVisible"
@@ -121,6 +123,7 @@
  *
  * Author(s):
  * Thomas Junk <thomas.junk@intevation.de>
+ * Markus Kottländer <markus.kottlaender@intevation.de>
  */
 
 import { mapState } from "vuex";
@@ -187,24 +190,43 @@
     newImport() {
       this.$store.commit("importschedule/setImportScheduleDetailVisible");
     },
-    deleteSchedule(index) {
-      if (this.importScheduleDetailVisible) return;
-      this.$store
-        .dispatch("importschedule/deleteSchedule", index)
-        .then(() => {
-          this.getSchedules();
-          displayInfo({
-            title: this.$gettext("Imports"),
-            message: this.$gettext("Deleted import: #") + index
-          });
-        })
-        .catch(error => {
-          const { status, data } = error.response;
-          displayError({
-            title: this.$gettext("Backend Error"),
-            message: `${status}: ${data.message || data}`
-          });
-        });
+    deleteSchedule(schedule) {
+      console.log(schedule);
+      this.$store.commit("application/popup", {
+        icon: "trash",
+        title: this.$gettext("Delete Import"),
+        content: this.$gettext(
+          `Do you really want to delete the import with ID <b>${
+            schedule.id
+          }</b> of type <b>${schedule.kind.toUpperCase()}</b>?`
+        ),
+        confirm: {
+          label: this.$gettext("Delete"),
+          icon: "trash",
+          callback: () => {
+            this.$store
+              .dispatch("importschedule/deleteSchedule", schedule.id)
+              .then(() => {
+                this.getSchedules();
+                displayInfo({
+                  title: this.$gettext("Imports"),
+                  message: this.$gettext("Deleted import: #") + schedule.id
+                });
+              })
+              .catch(error => {
+                const { status, data } = error.response;
+                displayError({
+                  title: this.$gettext("Backend Error"),
+                  message: `${status}: ${data.message || data}`
+                });
+              });
+          }
+        },
+        cancel: {
+          label: this.$gettext("Cancel"),
+          icon: "times"
+        }
+      });
     }
   },
   computed: {
--- a/client/src/components/systemconfiguration/PDFTemplates.vue	Mon Feb 25 08:47:59 2019 +0100
+++ b/client/src/components/systemconfiguration/PDFTemplates.vue	Mon Feb 25 13:11:30 2019 +0100
@@ -3,7 +3,7 @@
     <div class="d-flex flex-row justify-content-between">
       <h5><translate>PDF-Templates</translate></h5>
       <input
-        @change="upload"
+        @change="uploadTemplate"
         id="uploadTemplate"
         ref="uploadTemplate"
         type="file"
@@ -11,35 +11,34 @@
       />
     </div>
     <div class="mt-1 border-bottom pb-4">
-      <table class="table table-sm table-hover" v-if="templates.length">
-        <thead>
-          <tr>
-            <th><translate>Name</translate></th>
-            <th><translate>Date</translate></th>
-            <th><translate>Country</translate></th>
-            <th></th>
-          </tr>
-        </thead>
-        <transition-group name="list-fade" tag="tbody">
-          <tr v-for="template in templates" :key="template.name">
-            <td>{{ template.name }}</td>
-            <td>{{ template.time }}</td>
-            <td v-if="template.country">{{ template.country }}</td>
-            <td v-else><i>global</i></td>
-            <td class="text-right">
-              <button
-                class="btn btn-sm btn-dark"
-                @click="
-                  showDeleteTemplatePrompt = true;
-                  templateToDelete = template;
-                "
-              >
-                <font-awesome-icon icon="trash" />
-              </button>
-            </td>
-          </tr>
-        </transition-group>
-      </table>
+      <transition name="fade">
+        <table class="table table-sm table-hover" v-if="templates.length">
+          <thead>
+            <tr>
+              <th><translate>Name</translate></th>
+              <th><translate>Date</translate></th>
+              <th><translate>Country</translate></th>
+              <th></th>
+            </tr>
+          </thead>
+          <transition-group name="fade" tag="tbody">
+            <tr v-for="template in templates" :key="template.name">
+              <td>{{ template.name }}</td>
+              <td>{{ template.time }}</td>
+              <td v-if="template.country">{{ template.country }}</td>
+              <td v-else><i>global</i></td>
+              <td class="text-right">
+                <button
+                  class="btn btn-sm btn-dark"
+                  @click="deleteTemplate(template)"
+                >
+                  <font-awesome-icon icon="trash" />
+                </button>
+              </td>
+            </tr>
+          </transition-group>
+        </table>
+      </transition>
       <button class="btn btn-info mt-2" @click="$refs.uploadTemplate.click()">
         <font-awesome-icon
           icon="spinner"
@@ -50,44 +49,6 @@
         <translate>Upload new template</translate>
       </button>
     </div>
-
-    <div
-      :class="[
-        'box popup ui-element rounded bg-white',
-        { show: showDeleteTemplatePrompt }
-      ]"
-    >
-      <div>
-        <h6 class="mb-0 py-2 px-3 border-bottom d-flex align-items-center">
-          <font-awesome-icon icon="trash" class="mr-2"></font-awesome-icon>
-          <translate>Delete PDF Template</translate>
-          <font-awesome-icon
-            icon="times"
-            class="ml-auto text-muted"
-            @click="showDeleteTemplatePrompt = false"
-          ></font-awesome-icon>
-        </h6>
-        <div class="p-3 text-left">
-          <translate class="text-center d-block">
-            Do you really want to delete the following template:
-          </translate>
-          <h5 class="mt-3 text-center">{{ templateToDelete.name }}</h5>
-        </div>
-        <div
-          class="py-2 px-3 border-top d-flex align-items-center justify-content-between"
-        >
-          <button
-            class="btn btn-sm btn-warning"
-            @click="showDeleteTemplatePrompt = false"
-          >
-            no
-          </button>
-          <button class="btn btn-sm btn-info" @click="remove(templateToDelete)">
-            yes
-          </button>
-        </div>
-      </div>
-    </div>
   </div>
 </template>
 
@@ -123,13 +84,11 @@
   data() {
     return {
       templates: [],
-      uploading: false,
-      templateToDelete: "",
-      showDeleteTemplatePrompt: false
+      uploading: false
     };
   },
   methods: {
-    upload() {
+    uploadTemplate() {
       const reader = new FileReader();
       reader.onload = event => {
         let template;
@@ -201,19 +160,38 @@
           });
         });
     },
-    remove(template) {
-      this.showDeleteTemplatePrompt = false;
-      HTTP.delete("/templates/print/" + template.name, {
-        headers: {
-          "X-Gemma-Auth": localStorage.getItem("token"),
-          "Content-type": "text/xml; charset=UTF-8"
-        }
-      }).then(() => {
-        let removeIndex = this.templates.findIndex(
-          t => t.name === template.name
-        );
-        if (removeIndex !== -1) {
-          this.templates.splice(removeIndex, 1);
+    deleteTemplate(template) {
+      this.$store.commit("application/popup", {
+        icon: "trash",
+        title: this.$gettext("Delete Template"),
+        content:
+          this.$gettext(
+            "Do you really want to delete the following template:"
+          ) +
+          `<br>
+        <b>${template.name}</b>`,
+        confirm: {
+          label: this.$gettext("Delete"),
+          icon: "trash",
+          callback: () => {
+            HTTP.delete("/templates/print/" + template.name, {
+              headers: {
+                "X-Gemma-Auth": localStorage.getItem("token"),
+                "Content-type": "text/xml; charset=UTF-8"
+              }
+            }).then(() => {
+              let removeIndex = this.templates.findIndex(
+                t => t.name === template.name
+              );
+              if (removeIndex !== -1) {
+                this.templates.splice(removeIndex, 1);
+              }
+            });
+          }
+        },
+        cancel: {
+          label: this.$gettext("Cancel"),
+          icon: "times"
         }
       });
     }
--- a/client/src/components/usermanagement/Usermanagement.vue	Mon Feb 25 08:47:59 2019 +0100
+++ b/client/src/components/usermanagement/Usermanagement.vue	Mon Feb 25 13:11:30 2019 +0100
@@ -56,7 +56,7 @@
                   <th scope="col"></th>
                 </tr>
               </thead>
-              <tbody>
+              <transition-group name="fade" tag="tbody">
                 <tr v-for="user in users" :key="user.user">
                   <td @click="selectUser(user.user)">
                     <font-awesome-icon
@@ -78,10 +78,7 @@
                       <font-awesome-icon icon="paper-plane"></font-awesome-icon>
                     </button>
                     <button
-                      @click="
-                        showDeleteUserPrompt = true;
-                        userToDelete = user.user;
-                      "
+                      @click="deleteUser(user.user)"
                       class="btn btn-sm btn-dark"
                       v-tooltip="$gettext('Delete user')"
                     >
@@ -89,7 +86,7 @@
                     </button>
                   </td>
                 </tr>
-              </tbody>
+              </transition-group>
             </table>
           </div>
           <div class="d-flex mx-auto align-items-center">
@@ -324,7 +321,6 @@
         })
         .catch(error => {
           this.loginFailed = true;
-          this.submitted = false;
           const { status, data } = error.response;
           displayError({
             title: this.$gettext("Backend Error"),
@@ -357,26 +353,46 @@
       this.sortCriterion = criterion;
     },
     deleteUser(name) {
-      this.$store
-        .dispatch("usermanagement/deleteUser", { name: name })
-        .then(() => {
-          this.showDeleteUserPrompt = false;
-          this.submitted = false;
-          this.$store.dispatch("usermanagement/loadUsers").catch(error => {
-            const { status, data } = error.response;
-            displayError({
-              title: this.$gettext("Backend Error"),
-              message: `${status}: ${data.message || data}`
-            });
-          });
-        })
-        .catch(error => {
-          const { status, data } = error.response;
-          displayError({
-            title: this.$gettext("Backend Error"),
-            message: `${status}: ${data.message || data}`
-          });
-        });
+      this.$store.commit("application/popup", {
+        icon: "trash",
+        title: this.$gettext("Delete User"),
+        content:
+          this.$gettext(
+            "Do you really want to delete the following user account:"
+          ) +
+          `<br>
+        <b>${name}</b>`,
+        confirm: {
+          label: this.$gettext("Delete"),
+          icon: "trash",
+          callback: () => {
+            this.$store
+              .dispatch("usermanagement/deleteUser", { name })
+              .then(() => {
+                this.$store
+                  .dispatch("usermanagement/loadUsers")
+                  .catch(error => {
+                    const { status, data } = error.response;
+                    displayError({
+                      title: this.$gettext("Backend Error"),
+                      message: `${status}: ${data.message || data}`
+                    });
+                  });
+              })
+              .catch(error => {
+                const { status, data } = error.response;
+                displayError({
+                  title: this.$gettext("Backend Error"),
+                  message: `${status}: ${data.message || data}`
+                });
+              });
+          }
+        },
+        cancel: {
+          label: this.$gettext("Cancel"),
+          icon: "times"
+        }
+      });
     },
     addUser() {
       this.$store.commit("usermanagement/clearCurrentUser");
--- a/client/src/store/application.js	Mon Feb 25 08:47:59 2019 +0100
+++ b/client/src/store/application.js	Mon Feb 25 13:11:30 2019 +0100
@@ -22,6 +22,7 @@
     appTitle: process.env.VUE_APP_TITLE,
     secondaryLogo: process.env.VUE_APP_SECONDARY_LOGO_URL,
     logoForPDF: process.env.VUE_APP_LOGO_FOR_PDF_URL,
+    popup: null,
     showSidebar: false,
     showUsermenu: false,
     showSplitscreen: false,
@@ -65,6 +66,9 @@
     }
   },
   mutations: {
+    popup: (state, popup) => {
+      state.popup = popup;
+    },
     showSidebar: (state, show) => {
       state.showSidebar = show;
     },