+ {t('LOGIN.NO_ACCOUNT')}{' '} + + {t('REGISTRATION.TITLE')} + +
+ ); diff --git a/src/pages/RegistrationPage.jsx b/src/pages/RegistrationPage.jsx new file mode 100644 index 0000000..611f738 --- /dev/null +++ b/src/pages/RegistrationPage.jsx @@ -0,0 +1,185 @@ +import {useState} from 'react'; +import {Link, useNavigate} from 'react-router-dom'; +import {useTranslation} from 'react-i18next'; +import {useForm} from 'react-hook-form'; +import {zodResolver} from '@hookform/resolvers/zod'; + +import {registrationSchema} from '../utils/validation.js'; + +const BACKEND_ERROR_KEY = { + EMAIL_ALREADY_IN_USE: 'REGISTRATION.ERROR_EMAIL_IN_USE', + INVALID_INVITATION_CODE: 'REGISTRATION.ERROR_INVALID_INVITE_CODE', + STEAM_ID_ALREADY_IN_USE: 'REGISTRATION.ERROR_STEAM_ID_IN_USE', +}; + +const inputClass = + 'w-full px-4 py-2 border border-slate-300 dark:border-slate-600 rounded-lg bg-slate-50 dark:bg-slate-700 text-slate-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:outline-none'; +const inputErrorClass = + 'w-full px-4 py-2 border border-red-500 dark:border-red-500 rounded-lg bg-slate-50 dark:bg-slate-700 text-slate-900 dark:text-white focus:ring-2 focus:ring-red-500 focus:outline-none'; + +export default function RegistrationPage() { + const {t} = useTranslation(); + const navigate = useNavigate(); + const [feedback, setFeedback] = useState({message: '', isError: false}); + + const { + register, + handleSubmit, + formState: {errors, isSubmitting}, + } = useForm({ + resolver: zodResolver(registrationSchema), + defaultValues: { + email: '', + password: '', + passwordConfirmation: '', + inviteCode: '', + steamId: '', + }, + }); + + const onSubmit = async (values) => { + setFeedback({message: '', isError: false}); + try { + const apiUrl = `${import.meta.env.VITE_API_URL}/api/registration/new`; + const response = await fetch(apiUrl, { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify({ + email: values.email, + password: values.password, + inviteCode: values.inviteCode, + steamId: values.steamId, + }), + }); + + if (response.ok) { + setFeedback({message: t('REGISTRATION.SUCCESS'), isError: false}); + setTimeout(() => navigate('/login'), 800); + return; + } + + const data = await response.json().catch(() => ({})); + const mappedKey = BACKEND_ERROR_KEY[data.code]; + setFeedback({ + message: mappedKey ? t(mappedKey) : data.message || t('REGISTRATION.ERROR_GENERIC'), + isError: true, + }); + } catch (err) { + console.error('Registration API error:', err); + setFeedback({message: t('INFO.ERROR.NETWORK_FAILURE'), isError: true}); + } + }; + + const renderError = (field) => + errors[field] && ( +{t(errors[field].message)}
+ ); + + return ( ++ {t('REGISTRATION.HAVE_ACCOUNT')}{' '} + + {t('MAIN.LOGIN')} + +
+{t(errors[field].message)}
+ ); + + if (isLoading) { + return ( +