feat: improve input validation for register form

This commit is contained in:
kolaente 2021-11-28 16:33:03 +01:00 committed by Dominik Pschenitschni
parent f7eb160509
commit 05e054f501
No known key found for this signature in database
GPG key ID: B257AC0149F43A77
4 changed files with 58 additions and 7 deletions

6
src/helpers/isEmail.ts Normal file
View file

@ -0,0 +1,6 @@
export function isEmail(email: string): Boolean {
const format = /^.+@.+$/
const match = email.match(format)
return match === null ? false : match.length > 0
}

View file

@ -34,7 +34,6 @@
"email": "E-mail address", "email": "E-mail address",
"emailPlaceholder": "e.g. frederic{'@'}vikunja.io", "emailPlaceholder": "e.g. frederic{'@'}vikunja.io",
"password": "Password", "password": "Password",
"passwordRepeat": "Retype your password",
"passwordPlaceholder": "e.g. •••••••••••", "passwordPlaceholder": "e.g. •••••••••••",
"forgotPassword": "Forgot your password?", "forgotPassword": "Forgot your password?",
"resetPassword": "Reset your password", "resetPassword": "Reset your password",
@ -50,7 +49,10 @@
"authenticating": "Authenticating…", "authenticating": "Authenticating…",
"openIdStateError": "State does not match, refusing to continue!", "openIdStateError": "State does not match, refusing to continue!",
"openIdGeneralError": "An error occured while authenticating against the third party.", "openIdGeneralError": "An error occured while authenticating against the third party.",
"logout": "Logout" "logout": "Logout",
"emailInvalid": "Please enter a valid email address.",
"usernameRequired": "Please provide a username.",
"passwordRequired": "Please provide a password."
}, },
"settings": { "settings": {
"title": "Settings", "title": "Settings",

View file

@ -16,6 +16,8 @@ import {
faCocktail, faCocktail,
faCoffee, faCoffee,
faCog, faCog,
faEye,
faEyeSlash,
faEllipsisH, faEllipsisH,
faEllipsisV, faEllipsisV,
faExclamation, faExclamation,
@ -87,6 +89,8 @@ library.add(faCocktail)
library.add(faCoffee) library.add(faCoffee)
library.add(faCog) library.add(faCog)
library.add(faComments) library.add(faComments)
library.add(faEye)
library.add(faEyeSlash)
library.add(faEllipsisH) library.add(faEllipsisH)
library.add(faEllipsisV) library.add(faEllipsisV)
library.add(faExclamation) library.add(faExclamation)

View file

@ -18,8 +18,12 @@
v-focus v-focus
v-model="credentials.username" v-model="credentials.username"
@keyup.enter="submit" @keyup.enter="submit"
@focusout="validateUsername"
/> />
</div> </div>
<p class="help is-danger" v-if="!usernameValid">
{{ $t('user.auth.usernameRequired') }}
</p>
</div> </div>
<div class="field"> <div class="field">
<label class="label" for="email">{{ $t('user.auth.email') }}</label> <label class="label" for="email">{{ $t('user.auth.email') }}</label>
@ -33,12 +37,16 @@
type="email" type="email"
v-model="credentials.email" v-model="credentials.email"
@keyup.enter="submit" @keyup.enter="submit"
@focusout="validateEmail"
/> />
</div> </div>
<p class="help is-danger" v-if="!emailValid">
{{ $t('user.auth.emailInvalid') }}
</p>
</div> </div>
<div class="field"> <div class="field">
<label class="label" for="password">{{ $t('user.auth.password') }}</label> <label class="label" for="password">{{ $t('user.auth.password') }}</label>
<div class="control"> <div class="control is-relative">
<input <input
class="input" class="input"
id="password" id="password"
@ -49,8 +57,12 @@
autocomplete="new-password" autocomplete="new-password"
v-model="credentials.password" v-model="credentials.password"
@keyup.enter="submit" @keyup.enter="submit"
@focusout="validatePassword"
/> />
</div> </div>
<p class="help is-danger" v-if="!passwordValid">
{{ $t('user.auth.passwordRequired') }}
</p>
</div> </div>
<div class="field"> <div class="field">
<label class="label" for="passwordValidation">{{ $t('user.auth.passwordRepeat') }}</label> <label class="label" for="passwordValidation">{{ $t('user.auth.passwordRepeat') }}</label>
@ -76,6 +88,7 @@
id="register-submit" id="register-submit"
@click="submit" @click="submit"
class="mr-2" class="mr-2"
:disabled="!everythingValid"
> >
{{ $t('user.auth.register') }} {{ $t('user.auth.register') }}
</x-button> </x-button>
@ -89,12 +102,14 @@
</template> </template>
<script setup> <script setup>
import {useDebounceFn} from '@vueuse/core'
import {ref, reactive, toRaw, computed, onBeforeMount} from 'vue' import {ref, reactive, toRaw, computed, onBeforeMount} from 'vue'
import {useI18n} from 'vue-i18n' import {useI18n} from 'vue-i18n'
import router from '@/router' import router from '@/router'
import {store} from '@/store' import {store} from '@/store'
import Message from '@/components/misc/message' import Message from '@/components/misc/message'
import {isEmail} from '@/helpers/isEmail'
// FIXME: use the `beforeEnter` hook of vue-router // FIXME: use the `beforeEnter` hook of vue-router
// Check if the user is already logged in, if so, redirect them to the homepage // Check if the user is already logged in, if so, redirect them to the homepage
@ -111,20 +126,44 @@ const credentials = reactive({
email: '', email: '',
password: '', password: '',
}) })
const passwordValidation = ref('')
const loading = computed(() => store.state.loading) const loading = computed(() => store.state.loading)
const errorMessage = ref('') const errorMessage = ref('')
const DEBOUNCE_TIME = 100
// debouncing to prevent error messages when clicking on the log in button
const emailValid = ref(true)
const validateEmail = useDebounceFn(() => {
emailValid.value = isEmail(credentials.email)
}, DEBOUNCE_TIME)
const usernameValid = ref(true)
const validateUsername = useDebounceFn(() => {
usernameValid.value = credentials.username !== ''
}, DEBOUNCE_TIME)
const passwordValid = ref(true)
const validatePassword = useDebounceFn(() => {
passwordValid.value = credentials.password !== ''
}, DEBOUNCE_TIME)
const everythingValid = computed(() => {
return credentials.username !== '' &&
credentials.email !== '' &&
credentials.password !== '' &&
emailValid.value &&
usernameValid.value &&
passwordValid.value
})
async function submit() { async function submit() {
errorMessage.value = '' errorMessage.value = ''
if (credentials.password !== passwordValidation.value) { if (!everythingValid.value) {
errorMessage.value = t('user.auth.passwordsDontMatch')
return return
} }
try { try {
await store.dispatch('auth/register', toRaw(credentials)) await store.dispatch('auth/register', toRaw(credentials))
} catch (e) { } catch (e) {