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