From 646c1168349643eb01db53b5e06bf986a16b86d7 Mon Sep 17 00:00:00 2001 From: l3wdfut4pwr Date: Tue, 17 Mar 2026 14:06:58 +0200 Subject: simple registration prototype --- src/components/header/AuthDialog.tsx | 49 ++------- src/components/header/authdialog/LoginForm.tsx | 23 +++++ src/components/header/authdialog/RegisterForm.tsx | 115 ++++++++++++++++++++++ src/components/header/authdialog/ResetForm.tsx | 23 +++++ src/components/header/authdialog/register.ts | 69 +++++++++++++ src/components/ui/inputfield.tsx | 11 +-- src/lib/contexts/Auth.context.tsx | 27 +++-- 7 files changed, 253 insertions(+), 64 deletions(-) create mode 100644 src/components/header/authdialog/LoginForm.tsx create mode 100644 src/components/header/authdialog/RegisterForm.tsx create mode 100644 src/components/header/authdialog/ResetForm.tsx create mode 100644 src/components/header/authdialog/register.ts (limited to 'src') 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 (
@@ -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" > -
- -
- - -
-
- + -
- -
- - - - -
-
- +
-
-
- ВОССТАНОВЛЕНИЕ ПАРОЛЯ -
- - Введите никнейм или почту, с помощью - которых входите в аккаунт. - - -
-
- -
+
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 ( + <> +
+ +
+ + +
+
+ + + ); +} 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({}); + const [loading, setLoading] = useState(false); + const { setUser } = useAuthContext(); + const handleSubmit = async (e: React.FormEvent) => { + 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 ( +
+ + +
+
+ + {errors.username && ( +

+ {errors.username} +

+ )} +
+ +
+ + {errors.email && ( +

+ {errors.email} +

+ )} +
+ +
+ + {errors.password && ( +

+ {errors.password} +

+ )} +
+ +
+ + {errors.passwordConfirm && ( +

+ {errors.passwordConfirm} +

+ )} +
+
+ + {errors.general &&

{errors.general}

} + + +
+ ); +} 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 ( + <> +
+
+ ВОССТАНОВЛЕНИЕ ПАРОЛЯ +
+ + Введите никнейм или почту, с помощью которых входите + в аккаунт. + + +
+
+ +
+ + ); +} 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) { return (
); diff --git a/src/lib/contexts/Auth.context.tsx b/src/lib/contexts/Auth.context.tsx index ff2d369..d93ca92 100644 --- a/src/lib/contexts/Auth.context.tsx +++ b/src/lib/contexts/Auth.context.tsx @@ -1,36 +1,33 @@ 'use client'; - -import React, { createContext, use } from 'react'; - +import React, { createContext, use, useState, useContext } from 'react'; type User = { - id: string; - avatar?: string; - // Чёто там ещё + id: number; + username: string; }; -interface AuthContext { +interface AuthContextType { user: User | null; + setUser: (user: User | null) => void; } const AuthContext = createContext(null); export const AuthContextProvider = ({ children }: React.PropsWithChildren) => { - // TODO: подключить бэк - const user = null; + const [user, setUser] = useState(null); + return ( - {children} + + {children} + ); }; export const useAuthContext = () => { - const context = use(AuthContext); - - if (!context) { + const context = useContext(AuthContext); + if (!context) throw new Error( 'useAuthContext must be used within AuthContextProvider', ); - } - return context; }; -- cgit v1.3-3-g829e