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>