finished login form validation and self-destroying notifications

This commit is contained in:
Sockenklaus
2021-10-25 15:58:30 +02:00
parent bc56f65abd
commit a65d01c36d
3 changed files with 96 additions and 37 deletions

View File

@@ -14,7 +14,7 @@ export const useNotifications = defineStore('notifications', {
this.notifications.delete(id) this.notifications.delete(id)
}, },
add(type : string, text: string) : void { add(type : string, text: string, timeout: number = 1000) : void {
const id = uuidv4() const id = uuidv4()
this.notifications.set( this.notifications.set(
id, id,
@@ -23,7 +23,7 @@ export const useNotifications = defineStore('notifications', {
text:text, text:text,
} }
) )
setTimeout(() => this.remove(id), 1500) if (timeout !== -1) setTimeout(() => this.remove(id), timeout)
} }
} }
}) })

View File

@@ -5,23 +5,11 @@ import { AxiosError } from 'axios'
import axios from 'axios' import axios from 'axios'
type AuthSuccResult = { type AuthSuccResult = {
notification: {
type: string,
text: string
}
user: string, user: string,
role: string, role: string,
token: string token: string
} }
type AuthErrResult = {
notification: {
text: string,
type: string
}
}
export const useUser = defineStore({ export const useUser = defineStore({
id: 'storeUser', id: 'storeUser',
@@ -38,16 +26,14 @@ export const useUser = defineStore({
getters: { getters: {
isAdmin: (state) => state.role === 'admin', isAdmin: (state) => state.role === 'admin',
preferredStorage: (state) => {
return localStorage
/* if (state.rememberMe) return localStorage
else return sessionStorage */
}
}, },
actions: { actions: {
async login(username: string, password: string): Promise<boolean> { async login(username: string, password: string): Promise<
boolean |
'E_INVALID_AUTH_PASSWORD: Password mis-match' |
'E_INVALID_AUTH_UID: User not found'
> {
const notifications = useNotifications() const notifications = useNotifications()
@@ -64,18 +50,26 @@ export const useUser = defineStore({
this.role = response.data.role this.role = response.data.role
this.token = response.data.token this.token = response.data.token
notifications.add(response.data.notification.type, response.data.notification.text) notifications.add('success', 'Login successful.')
return true return true
} catch(err : unknown) { } catch(err : unknown) {
if (axios.isAxiosError(err) && err.response && err.response.data){ if (axios.isAxiosError(err) && err.response && err.response.data){
const data = err.response.data as AuthErrResult const data = err.response.data
notifications.add(data.notification.type, data.notification.text)
if(
data === 'E_INVALID_AUTH_PASSWORD: Password mis-match' ||
data === 'E_INVALID_AUTH_UID: User not found'
) return data
} }
else if(err instanceof Error){
notifications.add('danger', err.message, -1)
return false
}
notifications.add('danger', err as string, -1)
return false return false
} }
@@ -105,7 +99,7 @@ export const useUser = defineStore({
catch(error) { catch(error) {
if(error instanceof Error) { if(error instanceof Error) {
notifications.add('danger', error.message) notifications.add('danger', error.message, -1)
} }
return false return false
} }

View File

@@ -1,29 +1,36 @@
<template> <template>
<img src="/src/assets/logo.png"> <img src="/src/assets/logo.png">
<form class="m-auto needs-validation" novalidate> <form class="m-auto" novalidate>
<h1 class="h3 mb-4">Bitte einloggen</h1> <h1 class="h3 mb-4">Bitte einloggen</h1>
<div class="form-floating"> <div class="form-floating">
<input <input
type="text" type="text"
class="form-control" class="form-control"
:class="validation.username"
id="usernameInput" id="usernameInput"
v-model="input.username" v-model="input.username"
placeholder="Benutzername" placeholder="Benutzername"
required aria-labeledby="username-feedback"
> >
<label id="username-feedback" for="usernameInput">Benutzername</label>
<label for="usernameInput">Benutzername</label> <div class="invalid-feedback">
{{valMessages.username}}
</div>
</div> </div>
<div class="form-floating mb-4"> <div class="form-floating mb-4">
<input <input
type="password" type="password"
class="form-control" class="form-control"
:class="validation.password"
id="passwordInput" id="passwordInput"
v-model="input.password" v-model="input.password"
placeholder="Passwort" placeholder="Passwort"
required aria-describedby="passwordFeedback"
> >
<label for="passwordInput">Passwort</label> <label for="passwordInput">Passwort</label>
<div id="passwordFeedback" class="invalid-feedback">
{{valMessages.password}}
</div>
</div> </div>
<button <button
@@ -38,25 +45,83 @@
<script setup lang="ts"> <script setup lang="ts">
import { useUser } from '@/stores/user' import { useUser } from '@/stores/user'
import { reactive } from 'vue' import { truncate } from 'fs/promises'
import { reactive, watch } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
const userStore = useUser() const userStore = useUser()
const router = useRouter() const router = useRouter()
const validation = reactive({
failure: false,
username: {
'is-invalid': false,
},
password: {
'is-invalid': false
}
})
const valMessages = reactive({
username: '',
password: ''
})
const input = reactive({ const input = reactive({
username: '', username: '',
password: '', password: '',
}) })
watch(() => input.username, (username) => {
if(username.length > 0) validation.username['is-invalid'] = false
})
watch(() => input.password, (password) => {
if(password.length > 0) validation.password['is-invalid'] = false
})
async function onClick() { async function onClick() {
/**TODO #20 Use sessionStorage or localStorage based on rememberMe! */ resetFailureState()
if(await userStore.login(input.username, input.password)) {
router.push({name: 'Home'})
}
if(input.username === '') {
validation.username['is-invalid'] = true
valMessages.username = 'Benutzername kann nicht leer sein.'
validation.failure = true
}
if(input.password === ''){
validation.password['is-invalid'] = true
valMessages.password = 'Passwort kann nicht leer sein'
validation.failure = true
}
if(validation.failure) return
const result = await userStore.login(input.username, input.password)
switch(result) {
case true:
router.push({name: 'Home'})
break;
case 'E_INVALID_AUTH_PASSWORD: Password mis-match':
validation.password['is-invalid'] = true
valMessages.password = 'Passwort falsch.'
break;
case 'E_INVALID_AUTH_UID: User not found':
validation.username['is-invalid'] = true
valMessages.username = 'Benutzer nicht gefunden'
break;
}
} }
const resetFailureState = () => {
validation.failure = false
validation.password['is-invalid'] = false
validation.username['is-invalid'] = false
valMessages.password = ''
valMessages.username = ''
}
</script> </script>