diff options
| author | l3wdfut4pwr <l3wdfut4pwr@gmail.com> | 2026-03-17 14:06:58 +0200 |
|---|---|---|
| committer | l3wdfut4pwr <l3wdfut4pwr@gmail.com> | 2026-03-17 14:06:58 +0200 |
| commit | 646c1168349643eb01db53b5e06bf986a16b86d7 (patch) | |
| tree | 1a47fed631b06179383ed75d1bacc3c6b785a974 /src/components | |
| parent | 9edce4dfa5f7c4efecd0f39fb4fd4a4c9863fe6e (diff) | |
simple registration prototype
Diffstat (limited to 'src/components')
| -rw-r--r-- | src/components/header/AuthDialog.tsx | 49 | ||||
| -rw-r--r-- | src/components/header/authdialog/LoginForm.tsx | 23 | ||||
| -rw-r--r-- | src/components/header/authdialog/RegisterForm.tsx | 115 | ||||
| -rw-r--r-- | src/components/header/authdialog/ResetForm.tsx | 23 | ||||
| -rw-r--r-- | src/components/header/authdialog/register.ts | 69 | ||||
| -rw-r--r-- | src/components/ui/inputfield.tsx | 11 |
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> ); |
