Mercurial > gemma
diff client/src/components/usermanagement/Userdetail.vue @ 1558:0ded4c56978e
refac: component filestructure. remove admin/map hierarchy
author | Thomas Junk <thomas.junk@intevation.de> |
---|---|
date | Wed, 12 Dec 2018 09:22:20 +0100 |
parents | client/src/components/admin/usermanagement/Userdetail.vue@a679f765d7b0 |
children | f2d24dceecc7 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/src/components/usermanagement/Userdetail.vue Wed Dec 12 09:22:20 2018 +0100 @@ -0,0 +1,393 @@ +<template> + <div class="userdetails mt-3 shadow fadeIn animated card"> + <h6 + class="mb-0 py-2 px-3 border-bottom d-flex text-info align-items-center" + > + {{ this.cardHeader }} + <span @click="closeDetailview" class="closebutton"> + <font-awesome-icon icon="times"></font-awesome-icon> + </span> + </h6> + <div class="card-body"> + <form @submit.prevent="save" class="ml-3"> + <div class="formfields"> + <div v-if="currentUser.isNew" class="form-group row"> + <label for="user"> <translate>Username</translate> </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> + <font-awesome-icon + icon="exclamation-triangle" + ></font-awesome-icon> + {{ errors.user }} + </small> + </div> + </div> + <div class="form-group row"> + <label for="country"> <translate>Country</translate> </label> + <select + class="form-control form-control-sm" + v-on:change="validateCountry" + v-model="currentUser.country" + > + <option disabled value> + <translate>Please select one</translate> + </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> + <font-awesome-icon + icon="exclamation-triangle" + ></font-awesome-icon> + {{ errors.country }} + </small> + </div> + </div> + <div class="form-group row"> + <label for="email"> <translate>Email address</translate> </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> + <font-awesome-icon + icon="exclamation-triangle" + ></font-awesome-icon> + {{ errors.email }} + </small> + </div> + </div> + <div class="form-group row"> + <label for="role"> <translate>Role</translate> </label> + <select + class="form-control form-control-sm" + v-on:change="validateRole" + v-model="currentUser.role" + > + <option disabled value> + <translate>Please select one</translate> + </option> + <option value="sys_admin"> + <translate>Sysadmin</translate> + </option> + <option value="waterway_admin"> + <translate>Waterway Admin</translate> + </option> + <option value="waterway_user"> + <translate>Waterway User</translate> + </option> + </select> + <div v-show="errors.role" class="text-danger"> + <small> + <font-awesome-icon + icon="exclamation-triangle" + ></font-awesome-icon> + {{ 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 submit-button" + > + <translate>Submit</translate> + </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"> + <font-awesome-icon icon="paper-plane"></font-awesome-icon> + <translate>Send testmail</translate> + </a> + <div v-if="mailsent"><translate>Mail was sent</translate></div> + </div> + </form> + </div> + </div> +</template> + +<style lang="scss" scoped> +.submit-button { + position: absolute; + right: $offset; + bottom: $offset; +} +.mailbutton { + width: 12vw; + position: absolute; + left: $large-offset; + bottom: 0; +} + +.formfields { + width: 60%; +} + +.userdetails { + height: 600px; + margin-top: $offset; + margin-left: $offset; + margin-right: $offset; +} + +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 "../../lib/http"; +import { displayError } from "../../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: this.$gettext("Password"), + passwordReLabel: this.$gettext("Repeat Password"), + passwordPlaceholder: this.$gettext("password"), + passwordRePlaceholder: this.$gettext("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: this.$gettext("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 + ? "" + : this.$gettext("Please choose a country"); + }, + validateRole() { + this.errors.role = this.currentUser.role + ? "" + : this.$gettext("Please choose a role"); + }, + validatePassword() { + this.errors.passwordre = + this.password === this.passwordre + ? "" + : this.$gettext("Passwords do not match!"); + this.errors.password = + this.password === "" || !violatedPasswordRules(this.password) + ? "" + : this.$gettext( + "Password should at least be 8 char long including 1 digit and 1 special char like $" + ); + }, + validateEmailaddress() { + this.errors.email = isEmailValid(this.currentUser.email) + ? "" + : this.$gettext("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: this.$gettext("Backend Error"), + message: `${status}: ${data.message || data}` + }); + }); + }) + .catch(error => { + this.submitted = false; + const { status, data } = error.response; + displayError({ + title: this.$gettext("Error while saving user"), + message: `${status}: ${data.message || data}` + }); + }); + } + } +}; +</script>