view client/src/components/usermanagement/Usermanagement.vue @ 2624:9dbaf69c7a66

Improve geoserver config to better calculate bounding boxes * Disable the use of estimated extents for the postgis storage configuration for geoserver, which is set via the gemma middleware. This way we are able to get better bounding boxes for many layers where the postgis function `ST_EstimatedExtent()` would be far off.
author Bernhard Reiter <bernhard@intevation.de>
date Wed, 13 Mar 2019 16:18:39 +0100
parents dda4cec8e67b
children 4bcb26542767
line wrap: on
line source

<template>
  <div class="main d-flex flex-row" style="position: relative;">
    <Spacer></Spacer>
    <div class="d-flex content py-2">
      <div :class="userlistStyle">
        <div class="card shadow-xs">
          <UIBoxHeader icon="users-cog" title="Users" />
          <UITableHeader
            :columns="[
              { id: 'role', title: 'Role', class: 'col-1' },
              { id: 'user', title: 'Username', class: 'col-3' },
              { id: 'country', title: 'Country', class: 'col-2' },
              { id: 'email', title: 'Email', class: 'col-3' }
            ]"
            @sortingChanged="sortBy"
          />
          <UITableBody
            :data="sortedUsers"
            maxHeight="47rem"
            :active="currentUser"
            v-slot="{ item: user }"
          >
            <div class="py-2 col-1" @click="selectUser(user.user)">
              <font-awesome-icon
                v-tooltip="roleLabel(user.role)"
                :icon="roleIcon(user.role)"
                class="fa-lg"
              ></font-awesome-icon>
            </div>
            <div class="py-2 col-3" @click="selectUser(user.user)">
              {{ user.user }}
            </div>
            <div class="py-2 col-2" @click="selectUser(user.user)">
              {{ user.country }}
            </div>
            <div class="py-2 col-3" @click="selectUser(user.user)">
              {{ user.email }}
            </div>
            <div class="py-2 col text-right">
              <button
                @click="sendTestMail(user.user)"
                class="btn btn-sm btn-dark mr-1"
                v-tooltip="sendMailLabel"
                v-if="user.email"
              >
                <font-awesome-icon icon="paper-plane"></font-awesome-icon>
              </button>
              <button
                @click="deleteUser(user.user)"
                class="btn btn-sm btn-dark"
                v-tooltip="deleteUserLabel"
              >
                <font-awesome-icon icon="trash" />
              </button>
            </div>
          </UITableBody>
          <div class="d-flex mx-auto align-items-center">
            <button
              @click="prevPage"
              v-if="this.currentPage !== 1"
              class="mr-2 btn btn-sm btn-light align-self-center"
            >
              <font-awesome-icon icon="angle-left"></font-awesome-icon>
            </button>
            {{ this.currentPage }} / {{ this.pages }}
            <button
              @click="nextPage"
              v-if="this.currentPage !== this.pages"
              class="ml-2 btn btn-sm btn-light align-self-center"
            >
              <font-awesome-icon icon="angle-right"></font-awesome-icon>
            </button>
          </div>
          <div class="mr-3 py-3 text-right">
            <button @click="addUser" class="btn btn-info addbutton shadow-sm">
              <translate>Add User</translate>
            </button>
          </div>
        </div>
      </div>
      <Userdetail v-if="isUserDetailsVisible"></Userdetail>
    </div>
  </div>
</template>

<style lang="sass" scoped>
.addbutton
  position: absolute
  bottom: $offset
  right: $offset

.content
  width: 100%

.userdetails
  width: 50%

.main
  height: 100%

.icon
  font-size: large

.userlist
  min-width: 520px
  height: 100%

.userlistsmall
  width: 100%

.userlistextended
  width: 100%

.table-body
  .row
    > div
      transition: background-color 0.3s, color 0.3s
    &.active
      > div
        background-color: $color-info
        color: #fff
        a
          color: #fff !important
</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):
 * Thomas Junk <thomas.junk@intevation.de>
 */
import store from "@/store";
import { mapGetters, mapState } from "vuex";
import { displayError, displayInfo } from "@/lib/errors.js";
import { HTTP } from "@/lib/http";
import Vue from "vue";
import { VTooltip, VPopover, VClosePopover } from "v-tooltip";

Vue.directive("tooltip", VTooltip);
Vue.directive("close-popover", VClosePopover);
Vue.component("v-popover", VPopover);

export default {
  name: "userview",
  data() {
    return {
      sortColumn: "user",
      sortDirection: "ASC",
      pageSize: 15,
      currentPage: 1
    };
  },
  components: {
    Userdetail: () => import("./Userdetail"),
    Spacer: () => import("@/components/Spacer")
  },
  computed: {
    ...mapGetters("usermanagement", [
      "isUserDetailsVisible",
      "users",
      "currentUser"
    ]),
    ...mapState("application", ["showSidebar"]),
    sendMailLabel() {
      return this.$gettext("Send testmail");
    },
    deleteUserLabel() {
      return this.$gettext("Delete user");
    },
    sortedUsers() {
      const start = (this.currentPage - 1) * this.pageSize;
      return this.users
        .filter(u => u) // to clone the array and leave the original store value intact
        .sort((a, b) => {
          if (
            a[this.sortColumn].toLowerCase() < b[this.sortColumn].toLowerCase()
          )
            return this.sortDirection === "ASC" ? -1 : 1;
          if (
            a[this.sortColumn].toLowerCase() > b[this.sortColumn].toLowerCase()
          )
            return this.sortDirection === "ASC" ? 1 : -1;
          return 0;
        })
        .slice(start, start + this.pageSize);
    },
    pages() {
      return Math.ceil(this.users.length / this.pageSize);
    },
    tableStyle() {
      return {
        table: true,
        "table-hover": true,
        "table-sm": this.isUserDetailsVisible,
        fadeIn: true,
        animated: true
      };
    },
    userlistStyle() {
      return [
        "userlist mr-2",
        {
          userlistsmall: this.isUserDetailsVisible,
          userlistextended: !this.isUserDetailsVisible
        }
      ];
    }
  },
  methods: {
    sendTestMail(user) {
      HTTP.get("/testmail/" + user, {
        headers: {
          "X-Gemma-Auth": localStorage.getItem("token"),
          "Content-type": "text/xml; charset=UTF-8"
        }
      })
        .then(() => {
          displayInfo({
            message: this.$gettext("Testmail sent")
          });
        })
        .catch(error => {
          this.loginFailed = true;
          const { status, data } = error.response;
          displayError({
            title: this.$gettext("Backend Error"),
            message: `${status}: ${data.message || data}`
          });
        });
    },
    nextPage() {
      if (this.currentPage < this.pages) {
        this.currentPage += 1;
      }
    },
    prevPage() {
      if (this.currentPage > 0) {
        this.currentPage -= 1;
      }
    },
    sortBy(sorting) {
      this.sortColumn = sorting.sortColumn;
      this.sortDirection = sorting.sortDirection;
    },
    deleteUser(name) {
      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");
      this.$store.commit("usermanagement/setUserDetailsVisible");
    },
    selectUser(name) {
      const user = this.$store.getters["usermanagement/getUserByName"](name);
      this.$store.commit("usermanagement/setCurrentUser", user);
    },
    roleIcon(role) {
      if (role === "sys_admin") return "star";
      if (role === "waterway_admin") return ["fab", "adn"];
      return "user";
    },
    roleLabel(role) {
      const labels = {
        sys_admin: this.$gettext("System-Administrator"),
        waterway_admin: this.$gettext("Waterway Admin"),
        waterway_user: this.$gettext("Waterway User")
      };
      return labels[role];
    }
  },
  beforeRouteEnter(to, from, next) {
    store
      .dispatch("usermanagement/loadUsers")
      .then(next)
      .catch(error => {
        const { status, data } = error.response;
        displayError({
          title: this.$gettext("Backend Error"),
          message: `${status}: ${data}`
        });
      });
  },
  beforeRouteLeave(to, from, next) {
    store.commit("usermanagement/clearCurrentUser");
    store.commit("usermanagement/setUserDetailsInvisible");
    next();
  }
};
</script>