changeset 296:b1116c4ce57f usermanagement

merge with default
author Thomas Junk <thomas.junk@intevation.de>
date Tue, 31 Jul 2018 15:56:11 +0200
parents 22deb76dff2c (diff) c2334b5d3dd0 (current diff)
children 57de8b62b47e
files
diffstat 9 files changed, 153 insertions(+), 25 deletions(-) [+]
line wrap: on
line diff
--- a/client/package.json	Tue Jul 31 13:34:37 2018 +0200
+++ b/client/package.json	Tue Jul 31 15:56:11 2018 +0200
@@ -14,6 +14,7 @@
   "dependencies": {
     "axios": "^0.18.0",
     "bootstrap": "^4.1.1",
+    "cxlt-vue2-toastr": "^1.1.0",
     "font-awesome": "^4.7.0",
     "locale2": "^2.2.0",
     "ol": "^5.0.0",
--- a/client/src/components/Sidebar.vue	Tue Jul 31 13:34:37 2018 +0200
+++ b/client/src/components/Sidebar.vue	Tue Jul 31 15:56:11 2018 +0200
@@ -6,7 +6,7 @@
       <a class="nav-link" href="#">Link</a>
       <a class="nav-link" href="#">Link</a>
       <a class="nav-link disabled" href="#">Disabled</a>
-      <div v-if="is_waterway_admin">
+      <div v-if="isSysAdmin">
         <router-link to="users">Users</router-link>
       </div>
     </nav>
@@ -28,12 +28,12 @@
   name: "sidebar",
   props: ["isOverlay"],
   computed: {
-    ...mapGetters("user", ["userinfo", "is_waterway_admin"]),
+    ...mapGetters("user", ["userinfo", "isSysAdmin"]),
     collapseicon() {
       return {
         fa: true,
-        "fa-angle-double-left": !this.collapsed,
-        "fa-angle-double-right": this.collapsed
+        "fa-angle-double-left": !this.isCollapsed,
+        "fa-angle-double-right": this.isCollapsed
       };
     },
     menuStyle() {
@@ -41,37 +41,37 @@
         menu: true,
         nav: true,
         "flex-column": true,
-        "visibility-extended": !this.collapsed,
-        "visibility-collapsed": this.collapsed
+        "visibility-extended": !this.isCollapsed,
+        "visibility-collapsed": this.isCollapsed
       };
     },
     userinfoStyle() {
       return {
         user: true,
         "d-inline-flex": true,
-        "visibility-extended": !this.collapsed,
-        "visibility-collapsed": this.collapsed
+        "visibility-extended": !this.isCollapsed,
+        "visibility-collapsed": this.isCollapsed
       };
     },
     collapseStyle() {
       return {
         collapser: true,
-        collapserextended: !this.collapsed,
-        collapsercollapsed: this.collapsed
+        collapserextended: !this.isCollapsed,
+        collapsercollapsed: this.isCollapsed
       };
     },
     sidebarStyle() {
       return {
         sidebar: true,
         overlay: this.isOverlay,
-        sidebarcollapsed: this.collapsed,
-        sidebarextended: !this.collapsed
+        sidebarcollapsed: this.isCollapsed,
+        sidebarextended: !this.isCollapsed
       };
     }
   },
   methods: {
     collapse() {
-      this.collapsed = !this.collapsed;
+      this.isCollapsed = !this.isCollapsed;
     },
     logoff() {
       this.$store.commit("user/clear_auth");
@@ -80,7 +80,7 @@
   },
   data() {
     return {
-      collapsed: false
+      isCollapsed: false
     };
   }
 };
--- a/client/src/main.js	Tue Jul 31 13:34:37 2018 +0200
+++ b/client/src/main.js	Tue Jul 31 15:56:11 2018 +0200
@@ -5,9 +5,20 @@
 import GetTextPlugin from "vue-gettext";
 import translations from "./translations.json";
 import locale2 from "locale2";
+import CxltToastr from "cxlt-vue2-toastr";
 import "../node_modules/bootstrap/dist/css/bootstrap.min.css";
 import "../node_modules/font-awesome/css/font-awesome.min.css";
 import "../node_modules/ol/ol.css";
+import "../node_modules/cxlt-vue2-toastr/dist/css/cxlt-vue2-toastr.css";
+
+var toastrConfigs = {
+  position: "bottom center",
+  showDuration: 2000,
+  timeOut: 8000,
+  closeButton: true
+};
+
+Vue.use(CxltToastr, toastrConfigs);
 
 let browserLanguage = locale2;
 
@@ -33,8 +44,10 @@
 
 Vue.config.productionTip = false;
 
-new Vue({
+const app = new Vue({
   router,
   store,
   render: h => h(App)
 }).$mount("#app");
+
+export default app;
--- a/client/src/router.js	Tue Jul 31 13:34:37 2018 +0200
+++ b/client/src/router.js	Tue Jul 31 15:56:11 2018 +0200
@@ -23,8 +23,8 @@
         requiresAuth: true
       },
       beforeEnter: (to, from, next) => {
-        const isWaterwayAdmin = store.getters["user/is_waterway_admin"];
-        if (!isWaterwayAdmin) {
+        const isSysadmin = store.getters["user/isSysAdmin"];
+        if (!isSysadmin) {
           next("/");
         } else {
           next();
@@ -62,7 +62,7 @@
 
 router.beforeEach((to, from, next) => {
   const requiresAuth = to.matched.some(record => record.meta.requiresAuth);
-  const loggedIn = store.getters.authenticated;
+  const loggedIn = store.getters["user/isAuthenticated"];
   const expiresFromPastSession = toMillisFromString(
     localStorage.getItem("expires")
   );
--- a/client/src/store.js	Tue Jul 31 13:34:37 2018 +0200
+++ b/client/src/store.js	Tue Jul 31 15:56:11 2018 +0200
@@ -2,12 +2,14 @@
 import Vuex from "vuex";
 import Application from "./stores/application";
 import user from "./stores/user";
+import usermanagement from "./stores/usermanagement";
 
 Vue.use(Vuex);
 
 export default new Vuex.Store({
   modules: {
     application: Application,
-    user: user
+    user: user,
+    usermanagement: usermanagement
   }
 });
--- a/client/src/stores/user.js	Tue Jul 31 13:34:37 2018 +0200
+++ b/client/src/stores/user.js	Tue Jul 31 15:56:11 2018 +0200
@@ -9,7 +9,7 @@
     user: ""
   },
   getters: {
-    authenticated: state => {
+    isAuthenticated: state => {
       return state.authenticated;
     },
     userinfo: state => {
@@ -21,8 +21,11 @@
     expires: state => {
       return state.expires;
     },
-    is_waterway_admin: state => {
+    isWaterwayAdmin: state => {
       return state.roles.includes("waterway_admin");
+    },
+    isSysAdmin: state => {
+      return state.roles.includes("sys_admin");
     }
   },
   mutations: {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/src/stores/usermanagement.js	Tue Jul 31 15:56:11 2018 +0200
@@ -0,0 +1,40 @@
+import { HTTP } from "../lib/http";
+
+const UserManagement = {
+  namespaced: true,
+  state: {
+    users: null
+  },
+  getters: {
+    users: state => {
+      return state.users;
+    },
+    getUserByName: state => name => {
+      return state.users[name];
+    }
+  },
+  mutations: {
+    usersLoaded: (state, data) => {
+      state.users = data.users;
+    }
+  },
+  actions: {
+    loadUsers({ commit }) {
+      return new Promise((resolve, reject) => {
+        HTTP.get("/users", {
+          headers: { "X-Gemma-Auth": localStorage.getItem("token") }
+        })
+          .then(response => {
+            commit("usersLoaded", response.data);
+            resolve(response);
+          })
+          .catch(error => {
+            console.log(error);
+            reject(error);
+          });
+      });
+    }
+  }
+};
+
+export default UserManagement;
--- a/client/src/views/Users.vue	Tue Jul 31 13:34:37 2018 +0200
+++ b/client/src/views/Users.vue	Tue Jul 31 15:56:11 2018 +0200
@@ -2,28 +2,91 @@
   <div class="main d-flex">
     <Sidebar v-bind:isOverlay="false"></Sidebar>
     <div class="d-flex flex-row content">
-      <h1>User Management</h1>
+      <div class="d-flex flex-column">
+        <div>
+          <h1>User Management</h1>
+        </div>
+        <div class="userlist">
+          <div class="card shadow" style="width: 85rem;">
+              <div class="card-header text-white bg-info mb-3">
+                users
+              </div>
+              <div class="card-body">
+                <table class="table">
+                  <thead>
+                    <tr>
+                      <th scope="col">Username</th>
+                      <th scope="col">Country</th>
+                      <th scope="col">Email</th>
+                      <th scope="col">Role</th>
+                    </tr>
+                  </thead>
+                  <tbody>
+                  <tr v-for="user in users" :key="user.user">
+                    <td>{{ user.user }}</td>
+                    <td>{{ user.country }}</td>
+                    <td>{{ user.email}}</td>
+                    <td>{{ user.role }}</td>
+                    </tr>
+                  </tbody>
+              </table>
+            </div>
+          </div>
+        </div>
+      </div>
     </div>
   </div>
 </template>
 
 <style lang="scss">
+@import "../assets/application.scss";
 .main {
   height: 100vh;
 }
 
 .content {
-  width: 99%;
-  margin-left: 1%;
+  margin-top: $large-offset;
+  margin-left: auto;
+  margin-right: auto;
+}
+
+.userlist {
+  margin-top: $large-offset;
+}
+.shadow {
+  box-shadow: $basic-shadow-light !important;
+}
+
+.table th,
+td {
+  border-top: 0px !important;
 }
 </style>
 
 <script>
 import Sidebar from "../components/Sidebar";
+import store from "../store";
+import { mapGetters } from "vuex";
+import app from "../main";
+
 export default {
   name: "userview",
   components: {
     Sidebar
+  },
+  computed: {
+    ...mapGetters("usermanagement", ["users"])
+  },
+  beforeRouteEnter(to, from, next) {
+    store
+      .dispatch("usermanagement/loadUsers")
+      .then(next)
+      .catch(() => {
+        app.$toast.error({
+          title: "Error",
+          message: "Backend Error"
+        });
+      });
   }
 };
 </script>
--- a/client/yarn.lock	Tue Jul 31 13:34:37 2018 +0200
+++ b/client/yarn.lock	Tue Jul 31 15:56:11 2018 +0200
@@ -2709,6 +2709,12 @@
   dependencies:
     array-find-index "^1.0.1"
 
+cxlt-vue2-toastr@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/cxlt-vue2-toastr/-/cxlt-vue2-toastr-1.1.0.tgz#b2cddab6b436f86a9ac3b1d6edcaa7f2cbf5597c"
+  dependencies:
+    vue "^2.2.2"
+
 cyclist@~0.2.2:
   version "0.2.2"
   resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640"
@@ -9004,7 +9010,7 @@
   version "1.6.0"
   resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.6.0.tgz#dc42697133302ce3017524356a6c61b7b69b4a18"
 
-vue@^2.5.16:
+vue@^2.2.2, vue@^2.5.16:
   version "2.5.16"
   resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.16.tgz#07edb75e8412aaeed871ebafa99f4672584a0085"