finished login form validation and self-destroying notifications
This commit is contained in:
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user