summaryrefslogtreecommitdiff
path: root/src/components
diff options
context:
space:
mode:
authorl3wdfut4pwr <l3wdfut4pwr@gmail.com>2026-03-17 14:06:58 +0200
committerl3wdfut4pwr <l3wdfut4pwr@gmail.com>2026-03-17 14:06:58 +0200
commit646c1168349643eb01db53b5e06bf986a16b86d7 (patch)
tree1a47fed631b06179383ed75d1bacc3c6b785a974 /src/components
parent9edce4dfa5f7c4efecd0f39fb4fd4a4c9863fe6e (diff)
simple registration prototype
Diffstat (limited to 'src/components')
-rw-r--r--src/components/header/AuthDialog.tsx49
-rw-r--r--src/components/header/authdialog/LoginForm.tsx23
-rw-r--r--src/components/header/authdialog/RegisterForm.tsx115
-rw-r--r--src/components/header/authdialog/ResetForm.tsx23
-rw-r--r--src/components/header/authdialog/register.ts69
-rw-r--r--src/components/ui/inputfield.tsx11
6 files changed, 241 insertions, 49 deletions
diff --git a/src/components/header/AuthDialog.tsx b/src/components/header/AuthDialog.tsx
index c125c66..0b4bc74 100644
--- a/src/components/header/AuthDialog.tsx
+++ b/src/components/header/AuthDialog.tsx
@@ -4,6 +4,9 @@ import { Input } from '@/components/ui/input';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import GoogleIcon from '../../../public/icons/google.svg';
import { InputField } from '../ui/inputfield';
+import LoginForm from './authdialog/LoginForm';
+import RegisterForm from './authdialog/RegisterForm';
+import ResetForm from './authdialog/ResetForm';
export function AuthDialog() {
return (
<div className="absolute">
@@ -30,58 +33,20 @@ export function AuthDialog() {
value="Login"
className="w-[350px] min-h-[250px] p-5 gap-5 flex flex-col items-center justify-center rounded-[15px] border-[2px] bg-background"
>
- <div className="gap-5 flex flex-col h-[148px] w-[310px]">
- <Button className="w-full bg-white hover:bg-white hover:text-black">
- <GoogleIcon />
- <span className="text-black text-sm">
- Войти через Google
- </span>
- </Button>
- <div className="gap-[10px] flex-col flex h-[88px]">
- <InputField placeholder="Никнейм или почта" />
- <InputField placeholder="Пароль" />
- </div>
- </div>
- <Button className="w-full gap-[10px]">Войти</Button>
+ <LoginForm />
</TabsContent>
<TabsContent
value="Register"
- className="w-[350px] max-h-[348px] p-5 gap-5 flex flex-col items-center justify-center rounded-[15px] border-[2px] bg-background"
+ className="w-[350px] min-h-[348px] h-fit p-5 gap-5 flex flex-col items-center justify-center rounded-[15px] border-[2px] bg-background"
>
- <div className="gap-5 flex flex-col w-[310px]">
- <Button className="w-full bg-white hover:bg-white hover:text-black">
- <GoogleIcon />
- <span className="text-black">
- Войти через Google
- </span>
- </Button>
- <div className="gap-2.5 flex flex-col">
- <InputField placeholder="Никнейм" />
- <InputField placeholder="E-mail" />
- <InputField placeholder="Пароль" />
- <InputField placeholder="Повторите пароль" />
- </div>
- </div>
- <Button className="w-full">Войти</Button>
+ <RegisterForm />
</TabsContent>
</div>
<TabsContent
value="Reset"
className="w-[350px] max-h-[231px] p-5 gap-5 flex-col items-center justify-center rounded-[15px] border-[2px] bg-background"
>
- <div className="flex gap-5 flex-col">
- <div className="flex flex-col w-[310px] gap-5">
- <span className="">ВОССТАНОВЛЕНИЕ ПАРОЛЯ</span>
- <div className=" gap-2.5 flex flex-col">
- <span className="text-sm">
- Введите никнейм или почту, с помощью
- которых входите в аккаунт.
- </span>
- <InputField placeholder="Никнейм или почта"></InputField>
- </div>
- </div>
- <Button className="w-full">Восстановить</Button>
- </div>
+ <ResetForm />
</TabsContent>
</Tabs>
</DialogContent>
diff --git a/src/components/header/authdialog/LoginForm.tsx b/src/components/header/authdialog/LoginForm.tsx
new file mode 100644
index 0000000..c5b606c
--- /dev/null
+++ b/src/components/header/authdialog/LoginForm.tsx
@@ -0,0 +1,23 @@
+import { Button } from '@/components/ui/button';
+import GoogleIcon from '../../../../public/icons/google.svg';
+import { InputField } from '@/components/ui/inputfield';
+import { Input } from '@/components/ui/input';
+export default function LoginForm() {
+ return (
+ <>
+ <div className="gap-5 flex flex-col h-[148px] w-[310px]">
+ <Button className="w-full bg-white hover:bg-white hover:text-black">
+ <GoogleIcon />
+ <span className="text-black text-sm">
+ Войти через Google
+ </span>
+ </Button>
+ <div className="gap-[10px] flex-col flex h-[88px]">
+ <InputField placeholder="Никнейм или почта" />
+ <InputField placeholder="Пароль" />
+ </div>
+ </div>
+ <Button className="w-full gap-[10px]">Войти</Button>
+ </>
+ );
+}
diff --git a/src/components/header/authdialog/RegisterForm.tsx b/src/components/header/authdialog/RegisterForm.tsx
new file mode 100644
index 0000000..20a7c14
--- /dev/null
+++ b/src/components/header/authdialog/RegisterForm.tsx
@@ -0,0 +1,115 @@
+'use client';
+import { useState } from 'react';
+import { Button } from '@/components/ui/button';
+import GoogleIcon from '../../../../public/icons/google.svg';
+import { InputField } from '@/components/ui/inputfield';
+import { validate, registerUser } from './register';
+import { useAuthContext } from '@/lib/contexts/Auth.context';
+
+export default function RegisterForm() {
+ const [errors, setErrors] = useState<any>({});
+ const [loading, setLoading] = useState(false);
+ const { setUser } = useAuthContext();
+ const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
+ e.preventDefault();
+ setLoading(true);
+ setErrors({});
+
+ const formData = new FormData(e.currentTarget);
+ const username = formData.get('username')?.toString() || '';
+ const email = formData.get('email')?.toString() || '';
+ const password = formData.get('password')?.toString() || '';
+ const passwordConfirm =
+ formData.get('passwordConfirm')?.toString() || '';
+
+ const validationErrors = validate(
+ username,
+ email,
+ password,
+ passwordConfirm,
+ );
+
+ if (Object.keys(validationErrors).length > 0) {
+ setErrors(validationErrors);
+ setLoading(false);
+ return;
+ }
+
+ const { data, error } = await registerUser(username, email, password);
+
+ if (error) {
+ setErrors(error);
+ } else if (data) {
+ setUser({
+ id: data.id,
+ username: data.username,
+ });
+ }
+
+ setLoading(false);
+ };
+
+ return (
+ <form
+ onSubmit={handleSubmit}
+ className="flex flex-col gap-5 min-w-[310px] w-fit"
+ >
+ <Button className="w-full bg-white hover:bg-white hover:text-black">
+ <GoogleIcon />
+ <span className="text-black">Войти через Google</span>
+ </Button>
+
+ <div className="flex flex-col gap-2.5">
+ <div className="flex flex-col ">
+ <InputField placeholder="Никнейм" name="username" />
+ {errors.username && (
+ <p className="text-red pl-5 text-[12px] leading-[16px] mt-[5px]">
+ {errors.username}
+ </p>
+ )}
+ </div>
+
+ <div className="flex flex-col">
+ <InputField placeholder="E-mail" name="email" />
+ {errors.email && (
+ <p className="text-red pl-5 text-[12px] leading-[16px] mt-[5px]">
+ {errors.email}
+ </p>
+ )}
+ </div>
+
+ <div className="flex flex-col ">
+ <InputField
+ placeholder="Пароль"
+ type="password"
+ name="password"
+ />
+ {errors.password && (
+ <p className="text-red pl-5 text-[12px] leading-[16px] mt-[5px]">
+ {errors.password}
+ </p>
+ )}
+ </div>
+
+ <div className="flex flex-col ">
+ <InputField
+ placeholder="Повторите пароль"
+ type="password"
+ name="passwordConfirm"
+ />
+ {errors.passwordConfirm && (
+ <p className="text-red pl-5 text-[12px] leading-[16px] mt-[5px]">
+ {errors.passwordConfirm}
+ </p>
+ )}
+ </div>
+ </div>
+
+ {errors.general && <p className="text-red ">{errors.general}</p>}
+
+ <Button type="submit" className="w-full" disabled={loading}>
+ {loading ? 'Регистрация...' : 'Зарегистрироваться'}
+ </Button>
+ </form>
+ );
+}
diff --git a/src/components/header/authdialog/ResetForm.tsx b/src/components/header/authdialog/ResetForm.tsx
new file mode 100644
index 0000000..42ca1b2
--- /dev/null
+++ b/src/components/header/authdialog/ResetForm.tsx
@@ -0,0 +1,23 @@
+import { Button } from '@/components/ui/button';
+import GoogleIcon from '../../../../public/icons/google.svg';
+import { InputField } from '@/components/ui/inputfield';
+import { Input } from '@/components/ui/input';
+export default function ResetForm() {
+ return (
+ <>
+ <div className="flex gap-5 flex-col">
+ <div className="flex flex-col w-[310px] gap-5">
+ <span className="">ВОССТАНОВЛЕНИЕ ПАРОЛЯ</span>
+ <div className=" gap-2.5 flex flex-col">
+ <span className="text-sm">
+ Введите никнейм или почту, с помощью которых входите
+ в аккаунт.
+ </span>
+ <InputField placeholder="Никнейм или почта"></InputField>
+ </div>
+ </div>
+ <Button className="w-full">Восстановить</Button>
+ </div>
+ </>
+ );
+}
diff --git a/src/components/header/authdialog/register.ts b/src/components/header/authdialog/register.ts
new file mode 100644
index 0000000..bcbc21b
--- /dev/null
+++ b/src/components/header/authdialog/register.ts
@@ -0,0 +1,69 @@
+export const validate = (
+ username: string,
+ email: string,
+ password: string,
+ passwordConfirm: string,
+) => {
+ const newErrors: any = {};
+
+ if (!username) {
+ newErrors.username = 'Введите никнейм';
+ } else if (username.length < 3) {
+ newErrors.username = 'Минимум 3 символа.';
+ } else if (username.length > 32) {
+ newErrors.username = 'Максимум 32 символа';
+ }
+
+ if (!email) {
+ newErrors.email = 'Введите email.';
+ } else {
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+ if (!emailRegex.test(email)) newErrors.email = 'Некорректный email';
+ if (email.length > 254) newErrors.email = 'Слишком длинный email.';
+ }
+
+ if (!password) {
+ newErrors.password = 'Введите пароль.';
+ } else if (password.length < 8) {
+ newErrors.password = 'Необходимо минимум 8 символов.';
+ } else {
+ const hasLetter = /[A-Za-z]/.test(password);
+ const hasNumber = /\d/.test(password);
+ const hasSymbol = /[^\w\s]/.test(password);
+
+ if (!(hasLetter && hasNumber && hasSymbol)) {
+ newErrors.password = 'Попробуйте сочетание букв, цифр и символов.';
+ }
+ }
+
+ if (password !== passwordConfirm) {
+ newErrors.passwordConfirm = 'пароли не совпадают.';
+ }
+
+ return newErrors;
+};
+
+export const registerUser = async (
+ username: string,
+ email: string,
+ password: string,
+) => {
+ try {
+ const res = await fetch('http://localhost:8000/api/auth/register', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ username, email, password }),
+ });
+
+ const data = await res.json();
+
+ if (!res.ok) {
+ const { field, message } = data.detail;
+ return { error: { [field]: message } };
+ }
+
+ return { data };
+ } catch (err: any) {
+ return { error: { general: err.message } };
+ }
+};
diff --git a/src/components/ui/inputfield.tsx b/src/components/ui/inputfield.tsx
index 40d5206..6b585a9 100644
--- a/src/components/ui/inputfield.tsx
+++ b/src/components/ui/inputfield.tsx
@@ -1,15 +1,12 @@
import { Input } from '@/components/ui/input';
+import React from 'react';
-interface InputFIeldProps {
- placeholder: string;
-}
-
-export function InputField({ placeholder }: InputFIeldProps) {
+export function InputField(props: React.InputHTMLAttributes<HTMLInputElement>) {
return (
<div className="rounded-[20px] border-violet border-[2px] h-[39px] flex items-center justify-between py-[10px] px-[20px]">
<Input
- placeholder={placeholder}
- className="w-full flex text-white placeholder:text-light-violet text-sm min-w-[270px] h-[19px]"
+ {...props}
+ className={`w-full flex text-white placeholder:text-light-violet text-sm min-w-[270px] h-[19px] ${props.className ?? ''}`}
/>
</div>
);