mirror of
https://github.com/fromchat-messenger/web.git
synced 2026-09-22 19:15:08 +03:00
Extract auth header into a separate component
This commit is contained in:
@@ -8,4 +8,32 @@ export function AuthContainer({ children }: { children?: React.ReactNode }) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export type IconType = "filled" | "outlined";
|
||||||
|
|
||||||
|
export interface AuthHeaderIcon {
|
||||||
|
name: string;
|
||||||
|
type: IconType
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AuthHeaderProps {
|
||||||
|
title: string;
|
||||||
|
icon: string | AuthHeaderIcon;
|
||||||
|
subtitle: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AuthHeader({ title, icon, subtitle }: AuthHeaderProps) {
|
||||||
|
const iconType = typeof icon == "string" ? "filled" : icon.type;
|
||||||
|
const iconName = typeof icon == "string" ? icon : icon.name;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="auth-header">
|
||||||
|
<h2>
|
||||||
|
<span className={`material-symbols ${iconType} large`}>{iconName}</span>
|
||||||
|
{title}
|
||||||
|
</h2>
|
||||||
|
<p>{subtitle}</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,14 @@
|
|||||||
import { useImmer } from "use-immer";
|
import { useImmer } from "use-immer";
|
||||||
import { showChat, showRegister } from "../../navigation";
|
import { showChat, showRegister } from "../../navigation";
|
||||||
import { AlertsContainer, type Alert, type AlertType } from "../components/Alerts";
|
import { AlertsContainer, type Alert, type AlertType } from "../components/Alerts";
|
||||||
import { AuthContainer } from "../components/Auth";
|
import { AuthContainer, AuthHeader } from "../components/Auth";
|
||||||
import type { ErrorResponse, LoginRequest, LoginResponse } from "../../core/types";
|
import type { ErrorResponse, LoginRequest, LoginResponse } from "../../core/types";
|
||||||
import { setUser } from "../../auth/api";
|
import { setUser } from "../../auth/api";
|
||||||
import { ensureKeysOnLogin } from "../../auth/crypto";
|
import { ensureKeysOnLogin } from "../../auth/crypto";
|
||||||
import { API_BASE_URL } from "../../core/config";
|
import { API_BASE_URL } from "../../core/config";
|
||||||
import { initializeProfile } from "../../userPanel/profile/profile";
|
import { initializeProfile } from "../../userPanel/profile/profile";
|
||||||
import { useRef } from "react";
|
import { useRef } from "react";
|
||||||
|
import type { TextField } from "mdui/components/text-field";
|
||||||
|
|
||||||
export default function LoginScreen() {
|
export default function LoginScreen() {
|
||||||
const [alerts, updateAlerts] = useImmer<Alert[]>([]);
|
const [alerts, updateAlerts] = useImmer<Alert[]>([]);
|
||||||
@@ -16,18 +17,12 @@ export default function LoginScreen() {
|
|||||||
updateAlerts((alerts) => alerts.push({type: type, message: message}));
|
updateAlerts((alerts) => alerts.push({type: type, message: message}));
|
||||||
}
|
}
|
||||||
|
|
||||||
const usernameElement = useRef<HTMLInputElement>(null);
|
const usernameElement = useRef<TextField>(null);
|
||||||
const passwordElement = useRef<HTMLInputElement>(null);
|
const passwordElement = useRef<TextField>(null);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AuthContainer>
|
<AuthContainer>
|
||||||
<div className="auth-header">
|
<AuthHeader icon="login" title="Добро пожаловать!" subtitle="Войдите в свой аккаунт" />
|
||||||
<h2>
|
|
||||||
<span className="material-symbols filled large">login</span>
|
|
||||||
Добро пожаловать!
|
|
||||||
</h2>
|
|
||||||
<p>Войдите в свой аккаунт</p>
|
|
||||||
</div>
|
|
||||||
<div className="auth-body">
|
<div className="auth-body">
|
||||||
<AlertsContainer alerts={alerts} />
|
<AlertsContainer alerts={alerts} />
|
||||||
|
|
||||||
@@ -84,7 +79,7 @@ export default function LoginScreen() {
|
|||||||
icon="person--filled"
|
icon="person--filled"
|
||||||
autocomplete="username"
|
autocomplete="username"
|
||||||
required
|
required
|
||||||
ref={usernameElement}>
|
ref={usernameElement as React.RefObject<HTMLElement & TextField>}>
|
||||||
</mdui-text-field>
|
</mdui-text-field>
|
||||||
<mdui-text-field
|
<mdui-text-field
|
||||||
label="Пароль"
|
label="Пароль"
|
||||||
@@ -96,7 +91,7 @@ export default function LoginScreen() {
|
|||||||
icon="password--filled"
|
icon="password--filled"
|
||||||
autocomplete="current-password"
|
autocomplete="current-password"
|
||||||
required
|
required
|
||||||
ref={passwordElement}>
|
ref={passwordElement as React.RefObject<HTMLElement & TextField>}>
|
||||||
</mdui-text-field>
|
</mdui-text-field>
|
||||||
|
|
||||||
<mdui-button type="submit">Войти</mdui-button>
|
<mdui-button type="submit">Войти</mdui-button>
|
||||||
|
|||||||
@@ -1,20 +1,85 @@
|
|||||||
|
import { useImmer } from "use-immer";
|
||||||
import { showLogin } from "../../navigation";
|
import { showLogin } from "../../navigation";
|
||||||
import { AuthContainer } from "../components/Auth";
|
import { AuthContainer, AuthHeader } from "../components/Auth";
|
||||||
|
import { AlertsContainer, type Alert, type AlertType } from "../components/Alerts";
|
||||||
|
import { useRef } from "react";
|
||||||
|
import { TextField } from "mdui/components/text-field";
|
||||||
|
import type { ErrorResponse, RegisterRequest } from "../../core/types";
|
||||||
|
import { API_BASE_URL } from "../../core/config";
|
||||||
|
import { delay } from "../../utils/utils";
|
||||||
|
|
||||||
export default function RegisterScreen() {
|
export default function RegisterScreen() {
|
||||||
|
const [alerts, updateAlerts] = useImmer<Alert[]>([]);
|
||||||
|
|
||||||
|
function showAlert(type: AlertType, message: string) {
|
||||||
|
updateAlerts((alerts) => alerts.push({type: type, message: message}));
|
||||||
|
}
|
||||||
|
|
||||||
|
const usernameElement = useRef<TextField>(null);
|
||||||
|
const passwordElement = useRef<TextField>(null);
|
||||||
|
const confirmPasswordElement = useRef<TextField>(null);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AuthContainer>
|
<AuthContainer>
|
||||||
<div className="auth-header">
|
<AuthHeader icon="person_add" title="Регистрация" subtitle="Создайте новый аккаунт" />
|
||||||
<h2>
|
|
||||||
<span className="material-symbols filled large">person_add</span>
|
|
||||||
Регистрация
|
|
||||||
</h2>
|
|
||||||
<p>Создайте новый аккаунт</p>
|
|
||||||
</div>
|
|
||||||
<div className="auth-body">
|
<div className="auth-body">
|
||||||
<div id="register-alerts"></div>
|
<AlertsContainer alerts={alerts} />
|
||||||
|
|
||||||
<form id="register-form-element">
|
<form onSubmit={async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const username = usernameElement.current!.value.trim();
|
||||||
|
const password = passwordElement.current!.value.trim();
|
||||||
|
const confirmPassword = confirmPasswordElement.current!.value.trim();
|
||||||
|
|
||||||
|
if (!username || !password || !confirmPassword) {
|
||||||
|
showAlert("danger", "Пожалуйста, заполните все поля");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (password !== confirmPassword) {
|
||||||
|
showAlert("danger", "Пароли не совпадают");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (username.length < 3 || username.length > 20) {
|
||||||
|
showAlert("danger", "Имя пользователя должно быть от 3 до 20 символов");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (password.length < 5 || password.length > 50) {
|
||||||
|
showAlert("danger", "Пароль должен быть от 5 до 50 символов");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const request: RegisterRequest = {
|
||||||
|
username: username,
|
||||||
|
password: password,
|
||||||
|
confirm_password: confirmPassword
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(`${API_BASE_URL}/register`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(request)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
// Registration successful
|
||||||
|
showAlert("success", "Регистрация прошла успешно! Теперь вы можете войти.");
|
||||||
|
await delay(2000);
|
||||||
|
showLogin();
|
||||||
|
} else {
|
||||||
|
const data: ErrorResponse = await response.json();
|
||||||
|
showAlert("danger", data.message || "Ошибка при регистрации");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
showAlert("danger", "Ошибка соединения с сервером");
|
||||||
|
}
|
||||||
|
}}>
|
||||||
<mdui-text-field
|
<mdui-text-field
|
||||||
label="Имя пользователя"
|
label="Имя пользователя"
|
||||||
id="register-username"
|
id="register-username"
|
||||||
@@ -24,7 +89,8 @@ export default function RegisterScreen() {
|
|||||||
autocomplete="username"
|
autocomplete="username"
|
||||||
maxlength={20}
|
maxlength={20}
|
||||||
counter
|
counter
|
||||||
required>
|
required
|
||||||
|
ref={usernameElement as React.RefObject<HTMLElement & TextField>}>
|
||||||
</mdui-text-field>
|
</mdui-text-field>
|
||||||
<mdui-text-field
|
<mdui-text-field
|
||||||
label="Пароль"
|
label="Пароль"
|
||||||
@@ -35,7 +101,8 @@ export default function RegisterScreen() {
|
|||||||
toggle-password
|
toggle-password
|
||||||
icon="password--filled"
|
icon="password--filled"
|
||||||
autocomplete="new-password"
|
autocomplete="new-password"
|
||||||
required>
|
required
|
||||||
|
ref={passwordElement as React.RefObject<HTMLElement & TextField>}>
|
||||||
</mdui-text-field>
|
</mdui-text-field>
|
||||||
<mdui-text-field
|
<mdui-text-field
|
||||||
label="Подтвердите пароль"
|
label="Подтвердите пароль"
|
||||||
@@ -46,7 +113,8 @@ export default function RegisterScreen() {
|
|||||||
toggle-password
|
toggle-password
|
||||||
icon="password--filled"
|
icon="password--filled"
|
||||||
autocomplete="new-password"
|
autocomplete="new-password"
|
||||||
required>
|
required
|
||||||
|
ref={confirmPasswordElement as React.RefObject<HTMLElement & TextField>}>
|
||||||
</mdui-text-field>
|
</mdui-text-field>
|
||||||
|
|
||||||
<mdui-button type="submit">Зарегистрироваться</mdui-button>
|
<mdui-button type="submit">Зарегистрироваться</mdui-button>
|
||||||
|
|||||||
Reference in New Issue
Block a user