view client/src/views/Login.vue @ 539:924490b3395b

refac: Loginmask reworked The login is now more stable. The user gets better visual feedback for his actions.
author Thomas Junk <thomas.junk@intevation.de>
date Thu, 30 Aug 2018 12:39:09 +0200
parents f96d18e53369
children ee86ab038a7e
line wrap: on
line source

(<template>
  <div class="d-flex flex-column login shadow-lg">
    <div class="loginmask">
      <!-- logo section -->
      <div class="d-flex flex-row justify-content-center mb-3">
        <div class="logo"><img src="../assets/logo.png"></div>
        <div class="title">
          <h1>{{ appTitle }}</h1>
        </div>
      </div>
      <!-- end logo section -->
      <div id="alert" :style="errorMessageStyle" :class="errorMessageClass" role="alert">
        <span>{{ errorMessage }}</span>
      </div>
      <form @submit.prevent="login">
        <div class="input-group mb-3">
          <input type="text" v-model="user" id="inputUsername" class="form-control shadow-sm" :placeholder="usernameLabel" required autofocus>
        </div>
        <div class="input-group mb-3">
          <input :type="isPasswordVisible" v-model="password" id="inputPassword" class="form-control shadow-sm" :placeholder='passwordLabel' :required='!showPasswordReset' :disabled='showPasswordReset'>
          <div class="input-group-append">
            <span class="input-group-text disabled" id="basic-addon2" @click="showPassword">
              <i :class="eyeIcon"></i>
            </span>
          </div>
        </div>
        <button v-if="showPasswordReset==false" class="btn btn-primary btn-block shadow-sm" :disabled="submitted || showPasswordReset" type="submit">
          <translate>Login</translate>
        </button>
      </form>

      <!-- password forgotten part -->
      <form class="">
        <div v-if="showPasswordReset" class="passwordreset">
          <button class="btn btn-block btn-info" type="button" @click="resetPassword">
            <translate>Request password reset!</translate>
          </button>
          <div class="pull-right">
            <a href="#" @click.prevent="togglePasswordReset">
              <translate>back to login</translate>
            </a>
          </div>
        </div>
        <div v-else class="pull-right">
          <a href="#" @click.prevent="togglePasswordReset">
            <translate>Forgot password</translate>
          </a>
        </div>
      </form>

      <!-- bottom logo section -->
      <div><img :src="secondaryLogo"></div>
    </div>
  </div>
</template>)

<style lang="scss">
@import "../assets/application.scss";

.login {
  background-color: white;
  min-width: 375px;
  height: 500px;
  @extend %fully-centered;
}

.loginmask {
  margin-left: $large-offset;
  margin-right: $large-offset;
  margin-top: $large-offset;
}

.logo {
  margin-right: $offset;
}

.alert {
  padding: 0.5rem;
}
</style>

<script>
import { mapGetters } from "vuex";
import { HTTP } from "../lib/http";

export default {
  name: "login",
  data() {
    return {
      user: "",
      password: "",
      submitted: false,
      loginFailed: false,
      passwordJustResetted: false,
      readablePassword: false,
      showPasswordReset: false,
      usernameToReset: ""
    };
  },
  computed: {
    errorMessage() {
      if (this.loginFailed) return this.$gettext("Login failed");
      if (this.passwordJustResetted)
        return this.$gettext("Password reset requested!");
      return "&npsp;";
    },
    passwordLabel() {
      return this.$gettext("Enter passphrase");
    },
    usernameLabel() {
      return this.$gettext("Enter username");
    },
    isPasswordVisible() {
      return this.readablePassword ? "text" : "password";
    },
    eyeIcon() {
      return {
        fa: true,
        "fa-eye": !this.readablePassword,
        "fa-eye-slash": this.readablePassword
      };
    },
    errorMessageStyle() {
      if (this.loginFailed || this.passwordJustResetted) {
        return "visibility:visible";
      }
      return "visibility:hidden";
    },
    errorMessageClass() {
      let result = {
        "mb-3": true,
        errormessage: true,
        alert: true
      };
      if (this.loginFailed) {
        result["alert-danger"] = true;
      }
      if (this.passwordJustResetted) {
        result["alert-info"] = true;
      }
      return result;
    },
    ...mapGetters("application", ["appTitle", "secondaryLogo"])
  },
  methods: {
    login() {
      this.submitted = true;
      this.passwordJustResetted = false;
      const { user, password } = this;
      this.$store
        .dispatch("user/login", { user, password })
        .then(() => {
          this.loginFailed = false;
          this.$router.push("/");
        })
        .catch(() => {
          this.loginFailed = true;
          this.submitted = false;
        });
    },
    showPassword() {
      // disable button when in reset mode
      if (!this.showPasswordReset) {
        this.readablePassword = !this.readablePassword;
      }
    },
    togglePasswordReset() {
      this.passwordJustResetted = false;
      this.showPasswordReset = !this.showPasswordReset;
      this.loginFailed = false;
    },
    resetPassword() {
      if (this.user) {
        HTTP.post("/users/passwordreset", { user: this.user }).catch(error => {
          console.log("backend problem", error);
        });
        this.togglePasswordReset();
        this.passwordJustResetted = true;
      }
    }
  }
};
</script>