This commit is contained in:
Халимов Рустам
2026-03-10 22:00:45 +03:00
parent b4b4b8e0d3
commit be0c3812fc
12 changed files with 232 additions and 46 deletions

View File

@@ -198,6 +198,11 @@ const translations = {
createAccount: 'Создать аккаунт',
testAccounts: 'Тестовые аккаунты: evgeniy, anastasia, artem, polina',
passwordForAll: 'Пароль для всех:',
dontHaveAccount: 'Нет аккаунта?',
alreadyHaveAccount: 'Уже есть аккаунт?',
passwordRequirements: 'Минимум 8 символов, буквы и цифры',
registerNow: 'Зарегистрироваться',
loginNow: 'Войти',
// File/upload
fileTooLarge: 'Файл слишком большой (макс. 50МБ)',
dropFileHere: 'Отпустите файл здесь',
@@ -439,6 +444,11 @@ const translations = {
createAccount: 'Create account',
testAccounts: 'Test accounts: evgeniy, anastasia, artem, polina',
passwordForAll: 'Password for all:',
dontHaveAccount: "Don't have an account?",
alreadyHaveAccount: 'Already have an account?',
passwordRequirements: 'At least 8 characters, letters and numbers',
registerNow: 'Register',
loginNow: 'Login',
fileTooLarge: 'File too large (max 50MB)',
dropFileHere: 'Drop file here',
uploading: 'Uploading...',

View File

@@ -58,7 +58,8 @@ export default function AuthPage() {
className="relative z-10 w-full max-w-md mx-4"
>
<div className="glass-strong rounded-3xl p-8 shadow-2xl shadow-vortex-500/5">
{/* Логотип */}
{/* Заголовок */}
<div className="flex flex-col items-center mb-8">
<motion.div
initial={{ rotate: -180, scale: 0 }}
@@ -72,35 +73,9 @@ export default function AuthPage() {
/>
</motion.div>
<h1 className="text-2xl font-bold gradient-text mt-4">Vortex</h1>
<p className="text-zinc-500 text-sm mt-1">{t('modernMessengerShort')}</p>
</div>
{/* Переключатель Вход/Регистрация */}
<div className="flex rounded-xl bg-white/5 p-1 mb-6">
<button
onClick={() => { setIsLogin(true); setError(''); setPassword(''); }}
className={`flex-1 py-2.5 px-4 rounded-lg text-sm font-medium transition-all duration-200 flex items-center justify-center gap-2 ${
isLogin
? 'bg-gradient-to-r from-vortex-500 to-purple-600 text-white shadow-lg shadow-vortex-500/25'
: 'text-zinc-400 hover:text-zinc-200'
}`}
aria-pressed={isLogin}
>
<LogIn size={16} />
{t('login')}
</button>
<button
onClick={() => { setIsLogin(false); setError(''); setPassword(''); }}
className={`flex-1 py-2.5 px-4 rounded-lg text-sm font-medium transition-all duration-200 flex items-center justify-center gap-2 ${
!isLogin
? 'bg-gradient-to-r from-vortex-500 to-purple-600 text-white shadow-lg shadow-vortex-500/25'
: 'text-zinc-400 hover:text-zinc-200'
}`}
aria-pressed={!isLogin}
>
<UserPlus size={16} />
{t('register')}
</button>
<p className="text-zinc-500 text-sm mt-1">
{isLogin ? t('login') : t('register')}
</p>
</div>
{/* Ошибка */}
@@ -122,14 +97,14 @@ export default function AuthPage() {
<form onSubmit={handleSubmit} className="space-y-4" autoComplete="off">
<div>
<label className="block text-sm font-medium text-zinc-400 mb-1.5">
Username {!isLogin && <span className="text-zinc-600">{t('latinOnly')}</span>}
Username {!isLogin && <span className="text-zinc-600 font-normal">{t('latinOnly')}</span>}
</label>
<input
type="text"
value={username}
onChange={(e) => setUsername(e.target.value.replace(/[^a-zA-Z0-9_]/g, ''))}
placeholder="username"
className="w-full px-4 py-3 rounded-xl bg-white/5 border border-white/10 text-white placeholder-zinc-600 focus:border-vortex-500/50 focus:ring-1 focus:ring-vortex-500/25 transition-all"
className="w-full px-4 py-3 rounded-xl bg-white/5 border border-white/10 text-white placeholder-zinc-600 focus:border-vortex-500/50 focus:ring-1 focus:ring-vortex-500/25 transition-all outline-hidden"
required
autoFocus
autoComplete="off"
@@ -143,17 +118,20 @@ export default function AuthPage() {
animate={{ opacity: 1, height: 'auto' }}
exit={{ opacity: 0, height: 0 }}
transition={{ duration: 0.2 }}
className="space-y-4"
>
<label className="block text-sm font-medium text-zinc-400 mb-1.5">
{t('displayNameLabel')}
</label>
<input
type="text"
value={displayName}
onChange={(e) => setDisplayName(e.target.value)}
placeholder={t('displayNamePlaceholder')}
className="w-full px-4 py-3 rounded-xl bg-white/5 border border-white/10 text-white placeholder-zinc-600 focus:border-vortex-500/50 focus:ring-1 focus:ring-vortex-500/25 transition-all"
/>
<div>
<label className="block text-sm font-medium text-zinc-400 mb-1.5">
{t('displayNameLabel')}
</label>
<input
type="text"
value={displayName}
onChange={(e) => setDisplayName(e.target.value)}
placeholder={t('displayNamePlaceholder')}
className="w-full px-4 py-3 rounded-xl bg-white/5 border border-white/10 text-white placeholder-zinc-600 focus:border-vortex-500/50 focus:ring-1 focus:ring-vortex-500/25 transition-all outline-hidden"
/>
</div>
</motion.div>
)}
</AnimatePresence>
@@ -166,10 +144,10 @@ export default function AuthPage() {
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder={t('passwordPlaceholder')}
className="w-full px-4 py-3 rounded-xl bg-white/5 border border-white/10 text-white placeholder-zinc-600 focus:border-vortex-500/50 focus:ring-1 focus:ring-vortex-500/25 transition-all pr-12"
className="w-full px-4 py-3 rounded-xl bg-white/5 border border-white/10 text-white placeholder-zinc-600 focus:border-vortex-500/50 focus:ring-1 focus:ring-vortex-500/25 transition-all pr-12 outline-hidden"
required
autoComplete={isLogin ? 'current-password' : 'new-password'}
minLength={6}
minLength={8}
/>
<button
type="button"
@@ -179,6 +157,12 @@ export default function AuthPage() {
{showPassword ? <EyeOff size={18} /> : <Eye size={18} />}
</button>
</div>
{!isLogin && (
<p className="mt-1.5 text-xs text-zinc-500 flex items-center gap-1.5">
<div className="w-1 h-1 rounded-full bg-vortex-500" />
{t('passwordRequirements')}
</p>
)}
</div>
<AnimatePresence>
@@ -195,7 +179,7 @@ export default function AuthPage() {
value={bio}
onChange={(e) => setBio(e.target.value)}
placeholder={t('bioPlaceholder')}
className="w-full px-4 py-3 rounded-xl bg-white/5 border border-white/10 text-white placeholder-zinc-600 focus:border-vortex-500/50 focus:ring-1 focus:ring-vortex-500/25 transition-all"
className="w-full px-4 py-3 rounded-xl bg-white/5 border border-white/10 text-white placeholder-zinc-600 focus:border-vortex-500/50 focus:ring-1 focus:ring-vortex-500/25 transition-all outline-hidden"
/>
</motion.div>
)}
@@ -206,7 +190,7 @@ export default function AuthPage() {
whileTap={{ scale: 0.99 }}
disabled={isSubmitting}
type="submit"
className="w-full py-3 px-4 rounded-xl bg-gradient-to-r from-vortex-500 to-purple-600 text-white font-medium shadow-lg shadow-vortex-500/25 hover:shadow-vortex-500/40 transition-all duration-200 flex items-center justify-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed"
className="w-full py-3.5 px-4 rounded-xl bg-gradient-to-r from-vortex-500 to-purple-600 text-white font-medium shadow-lg shadow-vortex-500/25 hover:shadow-vortex-500/40 transition-all duration-200 flex items-center justify-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed mt-2"
>
{isSubmitting ? (
<div className="w-5 h-5 border-2 border-white/30 border-t-white rounded-full animate-spin" />
@@ -219,6 +203,25 @@ export default function AuthPage() {
</motion.button>
</form>
{/* Футер с переключателем */}
<div className="mt-8 pt-6 border-t border-white/5 text-center">
<p className="text-zinc-500 text-sm">
{isLogin ? t('dontHaveAccount') : t('alreadyHaveAccount')}{' '}
<button
onClick={() => {
setIsLogin(!isLogin);
setError('');
setPassword('');
setDisplayName('');
setBio('');
}}
className="text-vortex-400 hover:text-vortex-300 font-medium transition-colors ml-1"
>
{isLogin ? t('registerNow') : t('loginNow')}
</button>
</p>
</div>
</div>
</motion.div>
</motion.div>