view client/src/usermanagement/Userdetail.vue @ 1217:ba8cd80d68b6

made more use of bootstrap classes instead of custom css
author Markus Kottlaender <markus@intevation.de>
date Mon, 19 Nov 2018 15:20:22 +0100
parents b23622905a3f
children 7e2693d49daa
line wrap: on
line source

<template>
    <div class="userdetails h-100 mt-3 mr-auto shadow fadeIn animated">
        <div class="card">
            <div class="card-header shadow-sm text-white bg-info mb-3">
                {{ this.cardHeader }}
                <span @click="closeDetailview" class="pull-right">
                    <i class="fa fa-close"></i>
                </span>
            </div>
            <div class="card-body">
                <form @submit.prevent="save" clas="ml-3">
                    <div class="formfields">
                        <div v-if="currentUser.isNew" class="form-group row">
                            <label for="user">Username</label>
                            <input type="user" :placeholder="userNamePlaceholder" class="form-control form-control-sm" id="user" aria-describedby="userHelp" v-model="currentUser.user">
                            <div v-show="errors.user" class="text-danger">
                                <small>
                                    <i class="fa fa-warning"></i> {{ errors.user }}</small>
                            </div>
                        </div>
                        <div class="form-group row">
                            <label for="country">Country</label>
                            <select class="form-control form-control-sm" v-on:change="validateCountry" v-model="currentUser.country">
                                <option disabled value="">Please select one</option>
                                <option v-for="country in countries" v-bind:value="country" v-bind:key="country">{{country}}</option>
                            </select>
                            <div v-show="errors.country" class="text-danger">
                                <small>
                                    <i class="fa fa-warning"></i> {{ errors.country }}</small>
                            </div>
                        </div>
                        <div class="form-group row">
                            <label for="email">Email address</label>
                            <input type="email" v-on:change="validateEmailaddress" class="form-control form-control-sm" id="email" aria-describedby="emailHelp" v-model="currentUser.email">
                            <div v-show="errors.email" class="text-danger">
                                <small>
                                    <i class="fa fa-warning"></i> {{ errors.email }}</small>
                            </div>
                        </div>
                        <div class="form-group row">
                            <label for="role">Role</label>
                            <select class="form-control form-control-sm" v-on:change="validateRole" v-model="currentUser.role">
                                <option disabled value="">Please select one</option>
                                <option value="sys_admin">Sysadmin</option>
                                <option value="waterway_admin">Waterway Admin</option>
                                <option value="waterway_user">Waterway User</option>
                            </select>
                            <div v-show="errors.role" class="text-danger">
                                <small>
                                    <i class="fa fa-warning"></i> {{ errors.role }}</small>
                            </div>
                        </div>
                        <div class="form-group row">
                            <PasswordField @fieldchange="passwordChanged" :placeholder="passwordPlaceholder" :label="passwordLabel" :passworderrors="errors.password"></PasswordField>
                        </div>
                        <div class="form-group row">
                            <PasswordField @fieldchange="passwordReChanged" :placeholder="passwordRePlaceholder" :label="passwordReLabel" :passworderrors="errors.passwordre"></PasswordField>
                        </div>
                    </div>
                    <div>
                        <button type="submit" :disabled="submitted" class="shadow-sm btn btn-info pull-right">Submit</button>
                    </div>
                    <div v-if="currentUser.role !='waterway_user' " class="form-group row d-flex flex-row justify-content-start mailbutton">
                        <a @click="sendTestMail" class="btn btn-light"><i class="fa fa-telegram"> Send testmail</i></a>
                        <div v-if="mailsent">Mail was sent</div>
                    </div>
                </form>
            </div>
        </div>
    </div>
</template>

<style lang="sass" scoped>
.mailbutton
  width: 12vw

.formfields
  width: 10vw

.userdetails
  min-width: 40vw

form
  font-size: $smaller
</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 { HTTP } from "../application/lib/http";
import { displayError } from "../application/lib/errors.js";
import { mapState } from "vuex";
import PasswordField from "./Passwordfield";

const emptyErrormessages = () => {
  return {
    email: "",
    country: "",
    role: "",
    password: "",
    passwordre: ""
  };
};

const isEmailValid = email => {
  /**
   *
   * For convenience purposes the same regex used as in the go code
   * cf. types.go
   *
   */
  // eslint-disable-next-line
  return /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/.test(
    email
  );
};

const violatedPasswordRules = password => {
  return (
    // rules according to issue 70
    password.length < 7 ||
    /\W/.test(password) == false ||
    /\d/.test(password) == false
  );
};

export default {
  name: "userdetail",
  components: {
    PasswordField
  },
  data() {
    return {
      mailsent: false,
      passwordLabel: "Password",
      passwordReLabel: "Repeat Password",
      passwordPlaceholder: "password",
      passwordRePlaceholder: "password again",
      password: "",
      passwordre: "",
      currentUser: {},
      path: null,
      submitted: false,
      errors: {
        email: "",
        country: "",
        role: "",
        password: "",
        passwordre: ""
      }
    };
  },
  mounted() {
    this.currentUser = { ...this.user };
    this.path = this.user.name;
  },
  watch: {
    user() {
      this.currentUser = { ...this.user };
      this.path = this.user.name;
      this.clearPassword();
      this.clearErrors();
    }
  },
  computed: {
    cardHeader() {
      if (this.currentUser.isNew) return "N.N";
      return this.currentUser.user;
    },
    userNamePlaceholder() {
      if (this.currentUser.isNew) return "N.N";
      return "";
    },
    ...mapState("application", ["countries"]),
    user() {
      return this.$store.getters["usermanagement/currentUser"];
    },
    isFormValid() {
      return (
        isEmailValid(this.currentUser.email) &&
        this.currentUser.country &&
        this.password === this.passwordre &&
        (this.password === "" || !violatedPasswordRules(this.password))
      );
    }
  },
  methods: {
    sendTestMail() {
      if (this.mailsent) return;
      HTTP.get("/testmail/" + this.currentUser.user, {
        headers: {
          "X-Gemma-Auth": localStorage.getItem("token"),
          "Content-type": "text/xml; charset=UTF-8"
        }
      })
        .then(() => {
          this.mailsent = true;
        })
        .catch(error => {
          this.loginFailed = true;
          this.submitted = false;
          const { status, data } = error.response;
          displayError({
            title: "Backend Error",
            message: `${status}: ${data.message || data}`
          });
        });
    },
    passwordChanged(value) {
      this.password = value;
      this.validatePassword();
    },
    passwordReChanged(value) {
      this.passwordre = value;
      this.validatePassword();
    },
    clearErrors() {
      this.errors = emptyErrormessages();
    },
    clearPassword() {
      this.password = "";
      this.passwordre = "";
    },
    closeDetailview() {
      this.$store.commit("usermanagement/clearCurrentUser");
      this.$store.commit("usermanagement/setUserDetailsInvisible");
    },
    validateCountry() {
      this.errors.country = this.currentUser.country
        ? ""
        : "Please choose a country";
    },
    validateRole() {
      this.errors.role = this.currentUser.role ? "" : "Please choose a role";
    },
    validatePassword() {
      this.errors.passwordre =
        this.password === this.passwordre ? "" : "Passwords do not match!";
      this.errors.password =
        this.password === "" || !violatedPasswordRules(this.password)
          ? ""
          : "Password should at least be 8 char long including 1 digit and 1 special char like $";
    },
    validateEmailaddress() {
      this.errors.email = isEmailValid(this.currentUser.email)
        ? ""
        : "invalid email";
    },
    validate() {
      this.validateCountry();
      this.validateRole();
      this.validatePassword();
      this.validateEmailaddress();
    },
    save() {
      this.validate();
      if (!this.isFormValid) return;
      if (this.password) this.currentUser.password = this.password;
      this.submitted = true;
      this.$store
        .dispatch("usermanagement/saveCurrentUser", {
          path: this.user.user,
          user: this.currentUser
        })
        .then(() => {
          this.submitted = false;
          this.$store.dispatch("usermanagement/loadUsers").catch(error => {
            const { status, data } = error.response;
            displayError({
              title: "Backend Error",
              message: `${status}: ${data.message || data}`
            });
          });
        })
        .catch(error => {
          this.submitted = false;
          const { status, data } = error.response;
          displayError({
            title: "Error while saving user",
            message: `${status}: ${data.message || data}`
          });
        });
    }
  }
};
</script>