dnr-one/src/components/User/LoginRegisterPage.vue

394 lines
8.8 KiB
Vue

<template>
<div
class="login-register"
:class="isLogin ? 'login-register--login' : 'login-register--register'"
>
<form class="login-register__form">
<input
class="login-register__input"
v-if="!isLogin"
v-model="userData.email"
type="email"
placeholder="почта"
name="email"
/>
<input
class="login-register__input"
v-model="userData.username"
type="text"
placeholder="имя пользователя"
name="username"
/>
<input
class="login-register__input"
v-model="userData.password"
type="password"
placeholder="пароль"
name="password"
/>
<input
class="login-register__input"
v-if="!isLogin"
v-model="userData.password2"
type="password"
placeholder="повторите пароль"
name="password2"
/>
<ButtonComp
class="login-register__button"
:value="buttonLabel"
@click="submit()"
:disabled="pending"
/>
<div class="login-register__redirect">
{{ redirectText }}
<router-link
class="login-register__redirect-link"
v-if="isLogin"
to="/user/register"
@click="reset()"
>
Регистрация
</router-link>
<router-link
class="login-register__redirect-link"
v-else
to="/user/login"
@click="reset()"
>Войти</router-link
>
</div>
</form>
<div class="login-register__notifications">
<TransitionGroup name="login-register__notifications--transition">
<div
class="login-register__notification"
v-for="(notification, index) in notifications"
:key="notification"
:class="`login-register__notification--${notification.type}`"
>
<font-awesome-icon
v-if="notification.type === 'success'"
class="login-register__notification-icon"
:icon="['far', 'circle-check']"
/>
<font-awesome-icon
v-else
class="login-register__notification-icon"
:icon="['far', 'circle-xmark']"
/>
<div class="login-register__notification-info">
<div
class="login-register__notification-title"
v-html="notification.title"
></div>
<div
class="login-register__notification-text"
v-html="notification.text"
></div>
</div>
<font-awesome-icon
class="login-register__notification-close"
icon="xmark"
@click="removeNotification(index)"
/>
</div>
</TransitionGroup>
</div>
</div>
</template>
<script setup>
import { ref, defineProps, computed } from 'vue'
import { FieldsContainer, FieldChecks } from '@/js/validator'
// composables
import { useUserApi } from '@/composables/api/user'
const api = useUserApi()
// stores
import { useUser } from '@/stores'
const userStore = useUser()
// router
import { onBeforeRouteLeave, useRouter } from 'vue-router'
const router = useRouter()
const props = defineProps(['type'])
const userData = ref({
email: '',
username: '',
password: '',
password2: ''
})
const notifications = ref([])
const pending = ref(false)
const isLogin = computed(() => props.type === 'login')
const buttonLabel = computed(() => (isLogin.value ? 'Войти' : 'Регистрация'))
const redirectText = computed(() =>
isLogin.value ? 'Нет аккаунта?' : 'Уже есть аккаунт?'
)
function submit() {
notifications.value = []
let errors = getFieldsErrors()
if (errors.length == 0) {
if (isLogin.value) {
login()
} else {
register()
}
} else {
setTimeout(() => {
for (let i = 0; i < errors.length; i++) {
setTimeout(() => {
notifications.value.push({
type: 'error',
title: errors[i].title,
text: errors[i].text
})
}, i * 50)
}
}, 200)
}
}
function getFieldsErrors() {
const container = new FieldsContainer()
if (!isLogin.value) {
container.addField('почта', userData.value.email, [
FieldChecks.nonEmpty,
FieldChecks.email
])
}
container.addField('имя пользователя', userData.value.username, [
FieldChecks.nonEmpty,
FieldChecks.onlyLettersNumbersUnderscores,
FieldChecks.minLength(4),
FieldChecks.maxLength(20)
])
container.addField('пароль', userData.value.password, [
FieldChecks.nonEmpty,
FieldChecks.minLength(6),
FieldChecks.maxLength(32)
])
let errors = []
for (let key in container.fields) {
let error = container.fields[key].tryGetFirstError()
if (error !== '') {
errors.push({ title: key, text: error })
}
}
if (!isLogin.value) {
if (userData.value.password !== userData.value.password2) {
errors.push({ title: 'пароль', text: 'пароли не совпадают' })
}
}
return errors
}
async function login() {
if (pending.value) return
pending.value = true
let resp = await api.login({
username: userData.value.username,
password: userData.value.password
})
if (resp.hasErrors) {
for (let key in resp.error) {
notifications.value.push({
type: 'error',
title: key,
text: resp.error[key][0]
})
}
} else {
userStore.setUserData(resp.data)
userStore.saveUserData()
notifications.value.push({
type: 'success',
title: 'вход',
text: 'Вход выполнен успешно.'
})
redirectAfterTimeout()
}
pending.value = false
}
async function register() {
if (pending.value) return
pending.value = true
let resp = await api.register({
email: userData.value.email,
username: userData.value.username,
password: userData.value.password
})
if (resp.hasErrors) {
for (let key in resp) {
notifications.value.push({
type: 'error',
title: key,
text: resp.error[key][0]
})
}
} else {
userStore.setUserData(resp.data)
userStore.saveUserData()
notifications.value.push({
type: 'success',
title: 'регистрация',
text: 'Регистрация прошла успешно.'
})
redirectAfterTimeout()
}
pending.value = false
}
function reset() {
userData.value = {
email: '',
username: '',
password: '',
password2: ''
}
notifications.value = []
}
function removeNotification(index) {
notifications.value.splice(index, 1)
}
function redirectAfterTimeout() {
setTimeout(() => {
router.push('/')
}, 1000)
}
onBeforeRouteLeave(() => {
reset()
})
</script>
<style lang="scss">
.login-register {
$p: &;
width: 240px;
max-width: 100%;
margin: 0 auto;
padding-top: 25px;
color: color('second');
&__form {
display: flex;
flex-direction: column;
justify-content: flex-end;
height: 50vh;
gap: 5px;
}
&__input {
@include grey-input;
}
&__button {
}
&__redirect {
display: flex;
justify-content: center;
&-link {
margin-left: 5px;
color: color('second');
}
}
&__notifications {
margin-top: 5px;
&--transition {
&-enter-active,
&-leave-active {
transition: all 0.18s ease;
}
&-enter-from {
opacity: 0;
margin-top: 10px;
}
&-leave-to {
opacity: 0;
}
}
}
&__notification {
$p: &;
position: relative;
display: flex;
align-items: center;
gap: 5px;
padding: 5px 5px;
margin-bottom: 3px;
border: 1px solid color('border');
&-icon {
width: 25px;
height: auto;
display: block;
}
&-info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
gap: 3px;
height: 100%;
}
&-title {
font-size: 15px;
line-height: 13px;
}
&-text {
font-size: 13px;
line-height: 15px;
}
&-close {
cursor: pointer;
height: 100%;
width: 10px;
}
&--error {
#{$p}-icon {
color: color('warn');
}
}
&--success {
#{$p}-icon {
color: color('success');
}
}
}
&--login {
#{$p}__input--email {
display: none;
}
}
}
</style>