summaryrefslogtreecommitdiffstats
path: root/webapp
diff options
context:
space:
mode:
authorUdo Walter2018-11-26 20:12:31 +0100
committerUdo Walter2018-11-26 20:12:31 +0100
commit82648439b945cc5d049886f7e79c2f0dd9d14ff9 (patch)
treeebeb33ba47bef73306d004b230726ddad5d16bfa /webapp
parent[webapp] small bugfix (diff)
downloadbas-82648439b945cc5d049886f7e79c2f0dd9d14ff9.tar.gz
bas-82648439b945cc5d049886f7e79c2f0dd9d14ff9.tar.xz
bas-82648439b945cc5d049886f7e79c2f0dd9d14ff9.zip
[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
Diffstat (limited to 'webapp')
-rw-r--r--webapp/config/index.js6
-rw-r--r--webapp/package-lock.json227
-rw-r--r--webapp/package.json1
-rw-r--r--webapp/src/components/AccountModule.vue9
-rw-r--r--webapp/src/components/DashboardPage.vue6
-rw-r--r--webapp/src/components/GroupModuleDialog.vue34
-rw-r--r--webapp/src/components/LoginPage.vue2
-rw-r--r--webapp/src/components/NotificationsAlerts.vue37
-rw-r--r--webapp/src/components/NotificationsSnackbars.vue2
-rw-r--r--webapp/src/main.js25
-rw-r--r--webapp/src/store/groups.js18
-rw-r--r--webapp/src/store/notifications.js7
12 files changed, 340 insertions, 34 deletions
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 @@
<template>
<div class="account-page">
-
+ <div style="display: flex; justify-content: center; margin-top: 40px">
+ <v-btn color="primary" @click="newAlert">New Alert</v-btn>
+ </div>
</div>
</template>
@@ -19,9 +21,14 @@ export default {
name: 'AccountPage',
data () {
return {
+ testId: 0
}
},
methods: {
+ newAlert () {
+ this.$alert({ type: 'success', text: 'test ' + this.testId })
+ this.testId++
+ }
}
}
</script>
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 @@
<v-spacer></v-spacer>
<v-btn v-if="devMode" icon @click="$store.commit('saveSetting', { name: 'locale', value: settings.locale === 'en' ? 'de' : 'en' })"><v-icon>language</v-icon></v-btn>
<v-btn v-if="devMode" icon @click="$store.commit('saveSetting', { name: 'dark', value: !settings.dark })"><v-icon>invert_colors</v-icon></v-btn>
- <notifications-alerts></notifications-alerts>
+ <notifications-alerts style="margin-right: 20px"></notifications-alerts>
<v-toolbar-items>
<v-menu offset-y>
<v-btn class="user-button" flat slot="activator">
@@ -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 @@
</template>
<script>
+
export default {
name: 'LoginPage',
data () {
@@ -83,6 +84,7 @@ export default {
const nextRoute = this.$store.state.loginRedirect
if (nextRoute) this.$router.replace(nextRoute)
else this.$router.replace({ name: 'dashboard' })
+ this.$socket.open()
})
.catch(error => {
if (error.response.data.status === 'USER_NOTFOUND') {
diff --git a/webapp/src/components/NotificationsAlerts.vue b/webapp/src/components/NotificationsAlerts.vue
index 058cc4f..c16ad58 100644
--- a/webapp/src/components/NotificationsAlerts.vue
+++ b/webapp/src/components/NotificationsAlerts.vue
@@ -19,7 +19,7 @@
<div>
<v-menu v-model="menu" offset-y left :close-on-content-click="false" :nudge-bottom="10" content-class="notifications">
<v-btn icon slot="activator">
- <v-badge :value="newAlertCount" color="red darken-3" bottom>
+ <v-badge :value="!menu && newAlertCount" color="red darken-3" bottom>
<span slot="badge">{{newAlertCount}}</span>
<v-icon>notifications</v-icon>
</v-badge>
@@ -30,7 +30,7 @@
<div style="max-height: 70vh; overflow: auto">
<div v-if="alerts.length === 0" class="text-xs-center" style="width: 100%">{{ $t('noNotifications') }}</div>
<div v-else class="flex-between-center">
- <v-subheader v-if="newCount > 0">{{ $t('new') }}</v-subheader>
+ <v-subheader v-if="newAlertCount > 0">{{ $t('new') }}</v-subheader>
<v-subheader v-else>{{ $t('notifications') }}</v-subheader>
<v-btn @click="clearAll" icon small style="margin-right: 16px"><v-icon>clear_all</v-icon></v-btn>
</div>
@@ -48,7 +48,7 @@
<v-btn @click="closeAlert(a)" small :ripple="false" style="margin: 0; margin-top: -4px" icon><v-icon>close</v-icon></v-btn>
</div>
</v-alert>
- <v-subheader :key="'subheader' + index" v-if="newCount < alerts.length && index == newCount - 1">{{ $t('old') }}</v-subheader>
+ <v-subheader :key="'subheader' + index" v-if="newAlertCount < alerts.length && index == newAlertCount - 1">{{ $t('old') }}</v-subheader>
</template>
</div>
</v-card-text>
@@ -64,8 +64,7 @@ export default {
name: 'NotificationsAlerts',
data () {
return {
- menu: false,
- newCount: 0
+ menu: false
}
},
computed: {
@@ -76,32 +75,48 @@ export default {
},
watch: {
menu (value) {
- if (value) {
- this.newCount = this.newAlertCount
+ if (!value) {
+ this.$socket.emit('notifications resetNewAlertCount')
this.resetNewAlertCount()
}
}
},
methods: {
...mapMutations('notifications', ['resetNewAlertCount', 'removeAlert']),
- closeAlert (a) {
- this.removeAlert(a)
+ closeAlert (a, emit = true) {
+ if (emit) this.$socket.emit('notifications closeAlert', a)
+ this.removeAlert(a.id)
if (!this.alertsShown) this.menu = false
},
- clearAll () {
+ clearAll (emit = true) {
+ if (emit) this.$socket.emit('notifications clearAll')
var timeout = this.alerts.length * 50
setTimeout(this.closeMenu, timeout)
var removeAlert = this.removeAlert
this.alerts.forEach(a => {
timeout -= 50
setTimeout(function () {
- removeAlert(a)
+ removeAlert(a.id)
}, timeout)
})
},
closeMenu () {
this.menu = false
}
+ },
+ created () {
+ this.$socket.on('notifications clearAll', () => {
+ this.clearAll(false)
+ })
+ this.$socket.on('notifications newAlert', data => {
+ this.$alert(data, false)
+ })
+ this.$socket.on('notifications closeAlert', id => {
+ this.closeAlert(id, false)
+ })
+ this.$socket.on('notifications resetNewAlertCount', () => {
+ this.resetNewAlertCount()
+ })
}
}
</script>
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) {