Skip to content

Commit

Permalink
Auth pages (#181)
Browse files Browse the repository at this point in the history
## Изменения
Добавлены новые страницы авторизации, а именно:

- **Страница авторизации по почте**
- **Страница авторизации через OAuth**
- **Страница регистрации по почте**
- **Страница регистрации через OAuth**
- **Страница для восстановления забытого**
- **Страница для смены пароля**
- **Страница для смены почты**
- **Страница настроек профиля**

## Детали реализации
Теперь на странице профиля есть всего две кнопки: настройки и
редактирование профиля.
<!-- Здесь можно описать технические детали по пунктам. -->

## Check-List
<!-- После сохранения у следующих полей появятся галочки, которые нужно
проставить мышкой -->
- [x] Вы проверили свой код перед отправкой запроса?
- [x] Вы написали тесты к реализованным функциям? - **не требуются**
- [x] Вы не забыли применить форматирование `black` и `isort` для
_Back-End_ или `Prettier` для _Front-End_?

---------

Co-authored-by: Dyakov Roman <[email protected]>
  • Loading branch information
Temmmmmo and dyakovri authored Nov 23, 2023
1 parent 6b9c0ec commit a0940c6
Show file tree
Hide file tree
Showing 26 changed files with 1,120 additions and 125 deletions.
3 changes: 1 addition & 2 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ onMounted(async () => {
<template>
<v-app>
<RouterView />
<IrdomToastList />
<IrdomNavbar :items="navbarItems" />
</v-app>

<IrdomToastList />
</template>
15 changes: 11 additions & 4 deletions src/api/auth/AuthEmailApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,16 @@ interface ApproveEmailParams {
}

interface RequestResetPasswordBody {
email: string;
password: string;
new_password: string;
}

interface RequestResetForgottenPasswordBody {
email: string;
}

interface ResetPasswordParams {
'reset-token': string;
new_password: string;
}

interface LoginResponse {
Expand Down Expand Up @@ -73,8 +76,12 @@ class AuthEmailApi extends AuthBaseApi {
return this.post<DefaultResponse, RequestResetPasswordBody>('/reset/password/request', body);
}

public async resetPassword(params: ResetPasswordParams) {
return this.post<DefaultResponse, ResetPasswordParams>('/reset/password', params);
public async requestResetForgottenPassword(body: RequestResetForgottenPasswordBody) {
return this.post<DefaultResponse, RequestResetForgottenPasswordBody>('/reset/password/restore', body);
}

public async resetPassword(body: ResetPasswordParams, token: string) {
return this.post<DefaultResponse, ResetPasswordParams>('/reset/password', body, { 'reset-token': token });
}
}

Expand Down
64 changes: 51 additions & 13 deletions src/api/controllers/auth/AuthApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,15 @@ export class AuthApi {
[scoped, scopename.auth.user.read],
);

static logout = apply(
async () => {
const profileStore = useProfileStore();
const toastStore = useToastStore();
profileStore.deleteToken();
router.push('/auth');
toastStore.push({
title: 'Вы успешно вышли из аккаунта!',
});
},
[showErrorToast],
[checkToken],
);
static logout = apply(async () => {
const profileStore = useProfileStore();
const toastStore = useToastStore();
profileStore.deleteToken();
router.push('/auth');
toastStore.push({
title: 'Вы успешно вышли из аккаунта!',
});
}, [showErrorToast]);

static getMe = apply(
async (info?: MySessionInfo[]) => {
Expand Down Expand Up @@ -134,4 +130,46 @@ export class AuthApi {
[checkToken],
[showErrorToast],
);

static requestResetForgottenPassword = apply(
async (email: string) => {
const data = await authEmailApi.requestResetForgottenPassword({ email });
return data;
},
[showErrorToast],
);

static requestResetPassword = apply(
async (password: string, new_password: string) => {
const data = await authEmailApi.requestResetPassword({ password, new_password });
return data;
},
[showErrorToast],
[checkToken],
);

static resetPassword = apply(
async (new_password: string, token: string) => {
const data = await authEmailApi.resetPassword({ new_password }, token);
return data;
},
[showErrorToast],
);

static resetEmail = apply(
async (token: string) => {
const data = await authEmailApi.resetEmail({ token });
return data;
},
[showErrorToast],
);

static requestResetEmail = apply(
async (email: string) => {
const data = await authEmailApi.requestResetEmail({ email });
return data;
},
[checkToken],
[showErrorToast],
);
}
106 changes: 91 additions & 15 deletions src/components/EmailPasswordForm.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
<script setup lang="ts">
import { computed } from 'vue';
import { ToastType } from '@/models';
import { useToastStore } from '@/store';
import { computed, ref } from 'vue';
const checkPasswords = ref(false);
checkPasswords.value = false;
export interface SubmitData {
email: string;
Expand All @@ -14,18 +19,32 @@ const emits = defineEmits<{
submit: [data: SubmitData];
}>();
const toastStore = useToastStore();
const submitHandler = async (event: Event) => {
const form = event.target as HTMLFormElement;
const formData = new FormData(form);
const email = formData.get('email')?.toString();
const password = formData.get('password')?.toString();
const repeat_password = formData.get('repeat-password')?.toString();
if (email && password) {
emits('submit', { email, password });
if (password === repeat_password || props.mode === 'login') {
if (email && password) {
emits('submit', { email, password });
}
} else {
checkPasswords.value = true;
}
if (repeat_password !== password) {
toastStore.push({
title: 'Изменение пароля',
type: ToastType.Error,
description: 'Пароли не совпадают',
});
}
};
const buttonText = computed(() => (props.mode === 'login' ? 'Вход' : 'Регистрация'));
const buttonText = computed(() => (props.mode === 'login' ? 'Войти' : 'Зарегистрироваться'));
</script>

<template>
Expand All @@ -34,46 +53,103 @@ const buttonText = computed(() => (props.mode === 'login' ? 'Вход' : 'Рег
type="email"
name="email"
autocomplete="email"
placeholder="Введите почту"
class="input"
density="compact"
required
variant="outlined"
label="Email"
/>
<v-text-field
variant="outlined"
type="password"
name="password"
:autocomplete="mode === 'login' ? 'current-password' : 'new-password'"
placeholder="Введите пароль"
class="input"
density="compact"
label="Пароль"
required
/>
<v-btn type="submit" class="submit">{{ buttonText }}</v-btn>

<div v-if="$props.mode === 'login'" class="forgot-pass">
<router-link to="/auth/forgot-password">Не помню пароль</router-link>
</div>

<v-text-field
v-if="$props.mode === 'register'"
variant="outlined"
type="password"
name="repeat-password"
class="input"
density="compact"
label="Повтор пароля"
required
/>
<div v-if="checkPasswords" class="password-validate">Пароли должны совпадать</div>
<v-btn v-if="$props.mode === 'register'" type="submit" class="submit-register" color="#fff">{{
buttonText
}}</v-btn>
<v-btn v-if="$props.mode === 'login'" type="submit" class="submit-login" color="#fff">{{ buttonText }}</v-btn>
</form>
</template>

<style scoped>
.submit {
.field {
text-align: left;
width: 100%;
max-width: 400px;
align-self: center;
background: rgb(var(--v-theme-primary));
color: rgb(var(--v-theme-on-primary));
margin: 0 auto 16px;
}
.forgot-pass {
align-self: center;
padding-bottom: 10px;
max-width: 333px;
text-align: right;
width: 100%;
color: #18185c;
font-size: 14px;
font-style: normal;
font-weight: 700;
line-height: normal;
}
.submit-login {
width: 100%;
max-width: 150px;
align-self: center;
margin: 5px auto 16px;
border-radius: 8px !important;
}
.submit-register {
width: 100%;
max-width: 200px;
align-self: center;
margin: 20px auto 16px;
border-radius: 8px !important;
}
.input {
align-self: center;
align-items: center;
width: 100%;
max-width: 400px;
margin: 0 auto 24px;
margin: 0 auto 5px;
}
.form {
display: flex;
flex-direction: column;
align-items: center;
}
@media screen and (width <= 575px) {
margin-top: 50%;
}
.password-validate {
color: #f19035;
font-size: 14px;
font-style: normal;
font-weight: 700;
line-height: normal;
margin: 0 auto;
text-align: center;
}
</style>
1 change: 0 additions & 1 deletion src/components/IrdomAuthButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ const clickHandler = async () => {

<style scoped>
.icon {
filter: grayscale(1);
margin-right: 11px;
}
</style>
20 changes: 10 additions & 10 deletions src/components/IrdomLayout.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ defineProps<{
touch?: unknown;
height?: string;
className?: string;
centeredToolbar?: boolean;
}>();
</script>

Expand All @@ -24,25 +25,23 @@ defineProps<{
:title="title"
:share="share"
:class="className"
:centered="centeredToolbar"
>
<slot name="toolbar" />
</IrdomToolbar>

<div v-touch="touch" class="v-main-wrapper">
<v-main>
<v-container>
<v-row>
<v-col class="d-flex flex-column">
<slot />
</v-col>
</v-row>
<v-main class="main">
<v-container class="container">
<slot />
</v-container>
</v-main>
</div>
</template>

<style scoped>
v-main,
.main,
.container,
.v-main-wrapper {
height: 100%;
width: 100%;
Expand All @@ -55,7 +54,8 @@ v-main,
text-align: center !important;
}
.profile-toolbar :global(.v-toolbar-title__placeholder) {
margin-left: 3%;
.container {
display: flex;
flex-direction: column;
}
</style>
2 changes: 1 addition & 1 deletion src/components/IrdomToastList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const toastStore = useToastStore();
position: fixed;
left: 0;
right: 0;
bottom: var(--navbar-height);
top: 70%;
z-index: 10;
width: min(100%, 344px);
margin: 0 auto;
Expand Down
Loading

0 comments on commit a0940c6

Please sign in to comment.