xref: /openbmc/webui-vue/src/views/Login/Login.vue (revision 365bce5f)
1<template>
2  <main>
3    <b-container class="login-container" fluid>
4      <b-row class="login-row" align-v="center">
5        <b-col class="login-branding mt-5 mb-5" md="6">
6          <div class="login-branding__container">
7            <img
8              class="logo"
9              width="200px"
10              src="@/assets/images/openbmc-logo.svg"
11              alt=""
12            />
13            <h1>OpenBMC</h1>
14          </div>
15        </b-col>
16
17        <b-col md="6">
18          <b-form class="login-form" novalidate @submit.prevent="login">
19            <b-alert class="login-error" :show="authError" variant="danger">
20              <p id="login-error-alert">
21                <strong>{{ errorMsg.title }}</strong>
22                <span>{{ errorMsg.action }}</span>
23              </p>
24            </b-alert>
25            <div class="login-form__section">
26              <label for="username">Username</label>
27              <b-form-input
28                id="username"
29                v-model="userInfo.username"
30                :aria-describedby="authError ? 'login-error-alert' : ''"
31                :state="getValidationState($v.userInfo.username)"
32                type="text"
33                autofocus="autofocus"
34                @input="$v.userInfo.username.$touch()"
35              >
36              </b-form-input>
37              <b-form-invalid-feedback role="alert">
38                <template v-if="!$v.userInfo.username.required">
39                  Field required
40                </template>
41              </b-form-invalid-feedback>
42            </div>
43
44            <div class="login-form__section">
45              <label for="password">Password</label>
46              <b-form-input
47                id="password"
48                v-model="userInfo.password"
49                :aria-describedby="authError ? 'login-error-alert' : ''"
50                :state="getValidationState($v.userInfo.password)"
51                type="password"
52                @input="$v.userInfo.password.$touch()"
53              >
54              </b-form-input>
55              <b-form-invalid-feedback role="alert">
56                <template v-if="!$v.userInfo.password.required">
57                  Field required
58                </template>
59              </b-form-invalid-feedback>
60            </div>
61
62            <b-button
63              block
64              class="mt-5"
65              type="submit"
66              variant="primary"
67              :disabled="disableSubmitButton"
68              >Log in</b-button
69            >
70          </b-form>
71        </b-col>
72      </b-row>
73    </b-container>
74  </main>
75</template>
76
77<script>
78import { required } from 'vuelidate/lib/validators';
79import VuelidateMixin from '../../components/Mixins/VuelidateMixin.js';
80
81export default {
82  name: 'Login',
83  mixins: [VuelidateMixin],
84  data() {
85    return {
86      errorMsg: {
87        title: 'Invalid username or password.',
88        action: 'Please try again.'
89      },
90      userInfo: {
91        username: null,
92        password: null
93      },
94      disableSubmitButton: false
95    };
96  },
97  computed: {
98    authError() {
99      return this.$store.getters['authentication/authError'];
100    }
101  },
102  validations: {
103    userInfo: {
104      username: {
105        required
106      },
107      password: {
108        required
109      }
110    }
111  },
112  methods: {
113    login: function() {
114      this.$v.$touch();
115      if (this.$v.$invalid) return;
116      this.disableSubmitButton = true;
117      const username = this.userInfo.username;
118      const password = this.userInfo.password;
119      this.$store
120        .dispatch('authentication/login', [username, password])
121        .then(() => this.$router.push('/'))
122        .catch(error => console.log(error))
123        .finally(() => (this.disableSubmitButton = false));
124    }
125  }
126};
127</script>
128
129<style lang="scss" scoped>
130.login-container {
131  @include media-breakpoint-up(md) {
132    background: linear-gradient(
133      to right,
134      var(--light) 50%,
135      var(--secondary-light) 50%
136    );
137  }
138}
139
140.login-row {
141  @include media-breakpoint-up(md) {
142    min-height: 100vh;
143  }
144}
145
146.login-branding__container {
147  @include media-breakpoint-up(md) {
148    float: right;
149    margin-right: 4rem;
150  }
151}
152
153.login-form {
154  max-width: 360px;
155  margin-right: auto;
156  margin-left: auto;
157
158  @include media-breakpoint-up(md) {
159    margin-left: 4rem;
160  }
161}
162
163.login-form__section {
164  margin-bottom: $spacer;
165}
166
167.login-error {
168  margin-bottom: $spacer * 2;
169
170  p {
171    margin-bottom: 0;
172  }
173
174  strong {
175    display: block;
176    font-size: 1rem;
177    font-weight: 600;
178    margin-bottom: 0;
179  }
180
181  strong + span {
182    margin-top: $spacer / 2;
183    margin-bottom: 0;
184  }
185}
186
187.login-branding {
188  text-align: center;
189}
190</style>
191