From 82648439b945cc5d049886f7e79c2f0dd9d14ff9 Mon Sep 17 00:00:00 2001 From: Udo Walter Date: Mon, 26 Nov 2018 19:12:31 +0000 Subject: [webapp+server] Add first implementation of a websocket to alert webclients of events and to synchronize notification across multiple webapp instances of the same user --- webapp/config/index.js | 6 +- webapp/package-lock.json | 227 ++++++++++++++++++++++- webapp/package.json | 1 + webapp/src/components/AccountModule.vue | 9 +- webapp/src/components/DashboardPage.vue | 6 +- webapp/src/components/GroupModuleDialog.vue | 34 +++- webapp/src/components/LoginPage.vue | 2 + webapp/src/components/NotificationsAlerts.vue | 37 ++-- webapp/src/components/NotificationsSnackbars.vue | 2 +- webapp/src/main.js | 25 ++- webapp/src/store/groups.js | 18 +- webapp/src/store/notifications.js | 7 +- 12 files changed, 340 insertions(+), 34 deletions(-) (limited to 'webapp') diff --git a/webapp/config/index.js b/webapp/config/index.js index e283935..c1ed721 100644 --- a/webapp/config/index.js +++ b/webapp/config/index.js @@ -42,9 +42,13 @@ module.exports = { cssSourceMap: true, proxyTable: { - // proxy all requests starting with /api to express server + // proxy all requests starting with /api and /socket.io to the express server '/api': { target: 'https://localhost:' + process.env.API_PORT + '/' + }, + '/socket.io': { + target: 'https://localhost:' + process.env.API_PORT + '/', + ws: true } } }, diff --git a/webapp/package-lock.json b/webapp/package-lock.json index 83b8532..1989e9f 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -216,6 +216,11 @@ } } }, + "after": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", + "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=" + }, "ajv": { "version": "5.5.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", @@ -369,6 +374,11 @@ "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "dev": true }, + "arraybuffer.slice": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", + "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==" + }, "arrify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", @@ -419,8 +429,7 @@ "async-limiter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", - "dev": true + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" }, "atob": { "version": "2.1.1", @@ -1306,6 +1315,11 @@ "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", "dev": true }, + "backo2": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", + "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -1372,6 +1386,11 @@ } } }, + "base64-arraybuffer": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", + "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=" + }, "base64-js": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", @@ -1384,6 +1403,14 @@ "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", "dev": true }, + "better-assert": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", + "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", + "requires": { + "callsite": "1.0.0" + } + }, "bfj-node4": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/bfj-node4/-/bfj-node4-5.3.1.tgz", @@ -1407,6 +1434,11 @@ "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=", "dev": true }, + "blob": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", + "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==" + }, "bluebird": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", @@ -1705,6 +1737,11 @@ "callsites": "^0.2.0" } }, + "callsite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", + "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=" + }, "callsites": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", @@ -2084,11 +2121,20 @@ "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", "dev": true }, + "component-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", + "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=" + }, "component-emitter": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" + }, + "component-inherit": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", + "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=" }, "compressible": { "version": "2.0.15", @@ -3034,6 +3080,56 @@ "once": "^1.4.0" } }, + "engine.io-client": { + "version": "3.2.1", + "resolved": "http://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz", + "integrity": "sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==", + "requires": { + "component-emitter": "1.2.1", + "component-inherit": "0.0.3", + "debug": "~3.1.0", + "engine.io-parser": "~2.1.1", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "parseqs": "0.0.5", + "parseuri": "0.0.5", + "ws": "~3.3.1", + "xmlhttprequest-ssl": "~1.5.4", + "yeast": "0.1.2" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "ws": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "requires": { + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" + } + } + } + }, + "engine.io-parser": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.3.tgz", + "integrity": "sha512-6HXPre2O4Houl7c4g7Ic/XzPnHBvaEmN90vtRO9uLmwtRqQmTOw0QMevL1TOfL2Cpu1VzsaTmMotQgMdkzGkVA==", + "requires": { + "after": "0.8.2", + "arraybuffer.slice": "~0.0.7", + "base64-arraybuffer": "0.1.5", + "blob": "0.0.5", + "has-binary2": "~1.0.2" + } + }, "enhanced-resolve": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz", @@ -4863,6 +4959,26 @@ "ansi-regex": "^2.0.0" } }, + "has-binary2": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", + "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", + "requires": { + "isarray": "2.0.1" + }, + "dependencies": { + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" + } + } + }, + "has-cors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", + "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=" + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -5330,8 +5446,7 @@ "indexof": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", - "dev": true + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=" }, "inflight": { "version": "1.0.6", @@ -6598,6 +6713,11 @@ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true }, + "object-component": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", + "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=" + }, "object-copy": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", @@ -6897,6 +7017,22 @@ "error-ex": "^1.2.0" } }, + "parseqs": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", + "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", + "requires": { + "better-assert": "~1.0.0" + } + }, + "parseuri": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", + "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", + "requires": { + "better-assert": "~1.0.0" + } + }, "parseurl": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", @@ -9930,8 +10066,7 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "safe-regex": { "version": "1.1.0", @@ -10264,6 +10399,62 @@ "kind-of": "^3.2.0" } }, + "socket.io-client": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.1.1.tgz", + "integrity": "sha512-jxnFyhAuFxYfjqIgduQlhzqTcOEQSn+OHKVfAxWaNWa7ecP7xSNk2Dx/3UEsDcY7NcFafxvNvKPmmO7HTwTxGQ==", + "requires": { + "backo2": "1.0.2", + "base64-arraybuffer": "0.1.5", + "component-bind": "1.0.0", + "component-emitter": "1.2.1", + "debug": "~3.1.0", + "engine.io-client": "~3.2.0", + "has-binary2": "~1.0.2", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "object-component": "0.0.3", + "parseqs": "0.0.5", + "parseuri": "0.0.5", + "socket.io-parser": "~3.2.0", + "to-array": "0.1.4" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + } + } + }, + "socket.io-parser": { + "version": "3.2.0", + "resolved": "http://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz", + "integrity": "sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==", + "requires": { + "component-emitter": "1.2.1", + "debug": "~3.1.0", + "isarray": "2.0.1" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" + } + } + }, "sockjs": { "version": "0.3.19", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz", @@ -10692,6 +10883,11 @@ "os-tmpdir": "~1.0.2" } }, + "to-array": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", + "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=" + }, "to-arraybuffer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", @@ -10890,6 +11086,11 @@ } } }, + "ultron": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==" + }, "union-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", @@ -11931,6 +12132,11 @@ "safe-buffer": "~5.1.0" } }, + "xmlhttprequest-ssl": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", + "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=" + }, "xtend": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", @@ -12042,6 +12248,11 @@ "dev": true } } + }, + "yeast": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", + "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=" } } } diff --git a/webapp/package.json b/webapp/package.json index 73d6809..d27666a 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -12,6 +12,7 @@ }, "dependencies": { "axios": "^0.18.0", + "socket.io-client": "^2.1.1", "vue": "^2.5.17", "vue-codemirror": "^4.0.6", "vue-i18n": "^7.8.1", diff --git a/webapp/src/components/AccountModule.vue b/webapp/src/components/AccountModule.vue index 8d93dbe..e9a4cce 100644 --- a/webapp/src/components/AccountModule.vue +++ b/webapp/src/components/AccountModule.vue @@ -9,7 +9,9 @@ @@ -19,9 +21,14 @@ export default { name: 'AccountPage', data () { return { + testId: 0 } }, methods: { + newAlert () { + this.$alert({ type: 'success', text: 'test ' + this.testId }) + this.testId++ + } } } diff --git a/webapp/src/components/DashboardPage.vue b/webapp/src/components/DashboardPage.vue index 89c8c0c..53d19be 100644 --- a/webapp/src/components/DashboardPage.vue +++ b/webapp/src/components/DashboardPage.vue @@ -65,7 +65,7 @@ language invert_colors - + @@ -125,7 +125,8 @@ export default { drawerTranslateX: 0, drawerMini: localStorage.getItem('drawerMini') === 'true', drawerOpen: localStorage.getItem('drawerOpen') !== 'false', - userFullName: '' + userFullName: '', + clientCount: 0 } }, computed: { @@ -190,6 +191,7 @@ export default { this.$http.post('/api/logout').then(response => { this.setLoginRedirect(this.$route.fullPath) this.$router.push('/login') + this.$socket.close() }) } }, diff --git a/webapp/src/components/GroupModuleDialog.vue b/webapp/src/components/GroupModuleDialog.vue index 7c0905d..8f88f29 100644 --- a/webapp/src/components/GroupModuleDialog.vue +++ b/webapp/src/components/GroupModuleDialog.vue @@ -15,6 +15,20 @@ "client": "Add clients | Add this client? | Add these {0} clients?" } }, + "success": { + "delete": { + "client": "Successfully deleted client | Successfully deleted {0} clients", + "group": "Successfully deleted group | Successfully deleted {0} groups" + }, + "remove": { + "client": "Successfully removed client | Successfully removed {0} clients", + "group": "Successfully removed group | Successfully removed {0} groups" + }, + "add": { + "client": "Successfully added client | Successfully added {0} clients", + "group": "Successfully added group | Successfully added {0} groups" + } + }, "deletePermanently": { "group": "Permanently delete group | Permanently delete groups", "client": "Permanently delete client | Permanently delete clients" @@ -38,6 +52,20 @@ "client": "Clienten hinzufügen | Diesen Clienten hinzufügen? | Diese {0} Clienten hinzufügen?" } }, + "success": { + "delete": { + "client": "Client erfolgreich gelöscht | {0} Clienten erfolgreich gelöscht", + "group": "Gruppe erfolgreich gelöscht | {0} Gruppen erfolgreich gelöscht" + }, + "remove": { + "client": "Client erfolgreich entfernt | {0} Clienten erfolgreich entfernt", + "group": "Gruppe erfolgreich entfernt | {0} Gruppen erfolgreich entfernt" + }, + "add": { + "client": "Client erfolgreich hinzugefügt | {0} Clienten erfolgreich hinzugefügt", + "group": "Gruppe erfolgreich hinzugefügt | {0} Gruppen erfolgreich hinzugefügt" + } + }, "deletePermanently": { "group": "Gruppe dauerhaft löschen | Gruppen dauerhaft löschen", "client": "Client dauerhaft löschen | Clienten dauerhaft löschen" @@ -157,7 +185,11 @@ export default { 'remove': { 'group': 'removeSubroups', 'client': 'removeClients' }, 'add': { 'group': 'addSubgroups', 'client': 'addClients' } } - var data = { ...this.dialog.info } + var count = this.action === 'add' ? this.selected.length : this.dialog.info.selected.length + var data = { ...this.dialog.info, callback: () => this.$snackbar({ + color: 'success', + text: this.$tc('success.' + this.action + '.' + this.dialog.info.type, count, [count]) + }) } if (this.action === 'add') data.selected = this.selected this.$store.dispatch('groups/' + actionMap[this.action][this.dialog.info.type], data) this.setDialog({ show: false }) diff --git a/webapp/src/components/LoginPage.vue b/webapp/src/components/LoginPage.vue index aeaf66e..40b08ee 100644 --- a/webapp/src/components/LoginPage.vue +++ b/webapp/src/components/LoginPage.vue @@ -48,6 +48,7 @@ diff --git a/webapp/src/components/NotificationsSnackbars.vue b/webapp/src/components/NotificationsSnackbars.vue index ce768d6..c01c5c4 100644 --- a/webapp/src/components/NotificationsSnackbars.vue +++ b/webapp/src/components/NotificationsSnackbars.vue @@ -14,7 +14,7 @@ v-model="showSnackbar" bottom right - :timeout="currentSnackbar.timeout || 2000" + :timeout="currentSnackbar.timeout || 4000" :color="currentSnackbar.color" > {{ currentSnackbar.text }} diff --git a/webapp/src/main.js b/webapp/src/main.js index 3f59ede..8637c96 100644 --- a/webapp/src/main.js +++ b/webapp/src/main.js @@ -10,6 +10,7 @@ import Vuex from 'vuex' import globalStore from '@/store/global' import storeModules from '@/config/store' import axios from 'axios' +import io from 'socket.io-client' import { router, registerRouterGuards } from '@/router' import VueI18n from 'vue-i18n' import i18nMessages from '@/config/i18n' @@ -60,8 +61,30 @@ axios.interceptors.response.use(null, error => { return Promise.reject(error) }) +const socket = io({ + autoConnect: false +}); + +socket.on('error', function(err) { + console.log('Socket.io error: ' + err); + if (err === 'TOKEN_MISSING' || err === 'TOKEN_INVALID') { + socket.close() + console.log('Closing socket.'); + } +}); + +socket.on('hello', data => { + console.log(data) +}) + +socket.open() + Vue.prototype.$http = axios -Vue.prototype.$alert = function (data) { +Vue.prototype.$socket = socket + +Vue.prototype.$alert = function (data, emit = true) { + if (!data.id) data.id = '_' + Math.random().toString(36).substr(2, 9) + if (emit) socket.emit('notifications newAlert', data) store.commit('notifications/newAlert', data) } Vue.prototype.$snackbar = function (data) { diff --git a/webapp/src/store/groups.js b/webapp/src/store/groups.js index 1bbded9..adc78f8 100644 --- a/webapp/src/store/groups.js +++ b/webapp/src/store/groups.js @@ -130,7 +130,7 @@ export default { } }) }, - deleteGroups (context, { selected }) { + deleteGroups (context, { selected, callback }) { const ids = selected.map(x => x.id) axios.post('/api/groups/delete', { ids }).then(() => { var i = 1 @@ -142,18 +142,20 @@ export default { i++ } context.dispatch('reload') + if (callback) callback() }) }, - deleteClients (context, { selected }) { + deleteClients (context, { selected, callback }) { const ids = selected.map(x => x.id) axios.post('/api/clients/delete', { ids }).then(() => { const index = context.state.tabChain.length - 1 const item = context.state.tabChain[index] if (item.tabType === 'client' && ids.includes(item.id)) context.commit('deleteFromTabChain', { index, count: 1 }) context.dispatch('reload') + if (callback) callback() }) }, - removeSubroups (context, { tabIndex, selected }) { + removeSubroups (context, { tabIndex, selected,callback }) { const id = context.state.tabChain[tabIndex].id const ids = selected.map(x => x.id) axios.post('/api/groups/removeSubgroups', { id, ids }).then(() => { @@ -163,9 +165,10 @@ export default { context.commit('deleteFromTabChain', { index: tabIndex + 1, count: context.state.tabChain.length - (tabIndex + 1) }) } context.dispatch('reload') + if (callback) callback() }) }, - removeClients (context, { tabIndex, selected }) { + removeClients (context, { tabIndex, selected, callback }) { const id = context.state.tabChain[tabIndex].id const ids = selected.map(x => x.id) axios.post('/api/groups/removeClients', { id, ids }).then(() => { @@ -175,20 +178,23 @@ export default { context.commit('deleteFromTabChain', { index: tabIndex + 1, count: 1 }) } context.dispatch('reload') + if (callback) callback() }) }, - addSubgroups (context, { tabIndex, selected }) { + addSubgroups (context, { tabIndex, selected, callback }) { const id = context.state.tabChain[tabIndex].id const ids = selected.map(x => x.id) axios.post('/api/groups/addSubgroups', { id, ids }).then(() => { context.dispatch('reload') + if (callback) callback() }) }, - addClients (context, { tabIndex, selected }) { + addClients (context, { tabIndex, selected, callback }) { const id = context.state.tabChain[tabIndex].id const ids = selected.map(x => x.id) axios.post('/api/groups/addClients', { id, ids }).then(() => { context.dispatch('reload') + if (callback) callback() }) } } diff --git a/webapp/src/store/notifications.js b/webapp/src/store/notifications.js index 9f41d07..4687b78 100644 --- a/webapp/src/store/notifications.js +++ b/webapp/src/store/notifications.js @@ -23,10 +23,13 @@ export default { state.newAlertCount++ state.alerts.unshift(data) }, - removeAlert (state, a) { + removeAlert (state, id) { + const a = state.alerts.find(el => el.id === id) a.show = false setTimeout(function () { - state.alerts.splice(state.alerts.indexOf(a), 1) + var index = state.alerts.indexOf(a) + state.alerts.splice(index, 1) + if (index < state.newAlertCount && index >= 0) state.newAlertCount-- }, 200) }, resetNewAlertCount (state) { -- cgit v1.2.3-55-g7522