Files
gymflow/components/Login.tsx

141 lines
4.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState } from 'react';
import FilledInput from './FilledInput';
import { login, changePassword } from '../services/auth';
import { User, Language } from '../types';
import { Dumbbell, ArrowRight, Lock, Mail, Globe } from 'lucide-react';
import { t } from '../services/i18n';
interface LoginProps {
onLogin: (user: User) => void;
language: Language;
onLanguageChange: (lang: Language) => void;
}
const Login: React.FC<LoginProps> = ({ onLogin, language, onLanguageChange }) => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
// Force Password Change State
const [needsChange, setNeedsChange] = useState(false);
const [tempUser, setTempUser] = useState<User | null>(null);
const [newPassword, setNewPassword] = useState('');
const handleLogin = async (e: React.FormEvent) => {
e.preventDefault();
const res = await login(email, password);
if (res.success && res.user) {
if (res.user.isFirstLogin) {
setTempUser(res.user);
setNeedsChange(true);
} else {
onLogin(res.user);
}
} else {
setError(res.error || t('login_error', language));
}
};
const handleChangePassword = async () => {
if (tempUser && newPassword.length >= 4) {
const res = await changePassword(tempUser.id, newPassword);
if (res.success) {
const updatedUser = { ...tempUser, isFirstLogin: false };
onLogin(updatedUser);
} else {
setError(res.error || t('change_pass_error', language));
}
} else {
setError(t('login_password_short', language));
}
};
if (needsChange) {
return (
<div className="h-screen bg-surface flex items-center justify-center p-4">
<div className="w-full max-w-sm bg-surface-container p-8 rounded-[28px] shadow-elevation-2">
<h2 className="text-2xl text-on-surface mb-2">{t('change_pass_title', language)}</h2>
<p className="text-sm text-on-surface-variant mb-6">{t('change_pass_desc', language)}</p>
<div className="space-y-4">
<FilledInput
label={t('change_pass_new', language)}
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
type="password"
/>
<button
onClick={handleChangePassword}
className="w-full py-3 bg-primary text-on-primary rounded-full font-medium"
>
{t('change_pass_save', language)}
</button>
</div>
</div>
</div>
)
}
return (
<div className="h-screen bg-surface flex flex-col items-center justify-center p-6 relative">
{/* Language Toggle */}
<div className="absolute top-6 right-6 flex items-center bg-surface-container rounded-full px-3 py-1 gap-2 border border-outline-variant/20">
<Globe size={16} className="text-on-surface-variant" />
<select
value={language}
onChange={(e) => onLanguageChange(e.target.value as Language)}
className="bg-transparent text-sm text-on-surface focus:outline-none appearance-none"
>
<option value="en">English</option>
<option value="ru">Русский</option>
</select>
</div>
<div className="flex flex-col items-center mb-12">
<div className="w-20 h-20 bg-primary-container rounded-2xl flex items-center justify-center text-on-primary-container mb-4 shadow-elevation-2 transform rotate-3">
<Dumbbell size={40} />
</div>
<h1 className="text-3xl font-normal text-on-surface tracking-tight">{t('login_title', language)}</h1>
</div>
<form onSubmit={handleLogin} className="w-full max-w-sm space-y-6">
<div className="space-y-4">
<FilledInput
label={t('login_email', language)}
value={email}
onChange={(e) => setEmail(e.target.value)}
type="email"
icon={<Mail size={16} />}
inputMode="email"
/>
<FilledInput
label={t('login_password', language)}
value={password}
onChange={(e) => setPassword(e.target.value)}
type="password"
icon={<Lock size={16} />}
/>
</div>
{error && <div className="text-error text-sm text-center bg-error-container/10 p-2 rounded-lg">{error}</div>}
<button
type="submit"
className="w-full py-4 bg-primary text-on-primary rounded-full font-medium text-lg shadow-elevation-1 flex items-center justify-center gap-2 hover:shadow-elevation-2 transition-all"
>
{t('login_btn', language)} <ArrowRight size={20} />
</button>
</form>
<p className="mt-8 text-xs text-on-surface-variant text-center max-w-xs mx-auto">
{t('login_contact_admin', language)}
</p>
</div>
);
};
export default Login;