Merge pull request #103 from BEDOLAGA-DEV/dev

Dev
This commit is contained in:
Egor
2026-01-27 18:01:54 +03:00
committed by GitHub
2 changed files with 151 additions and 177 deletions

View File

@@ -344,14 +344,16 @@ export default function Layout({ children }: LayoutProps) {
// Update favicon
useEffect(() => {
if (!logoUrl) return
if (!logoUrl) return;
const link = document.querySelector<HTMLLinkElement>("link[rel*='icon']") || document.createElement('link')
link.type = 'image/x-icon'
link.rel = 'shortcut icon'
link.href = logoUrl
document.getElementsByTagName('head')[0].appendChild(link)
}, [logoUrl])
const link =
document.querySelector<HTMLLinkElement>("link[rel*='icon']") ||
document.createElement('link');
link.type = 'image/x-icon';
link.rel = 'shortcut icon';
link.href = logoUrl;
document.getElementsByTagName('head')[0].appendChild(link);
}, [logoUrl]);
// Fetch contests and polls counts to determine if they should be shown
const { data: contestsCount } = useQuery({

View File

@@ -27,9 +27,6 @@ export default function Login() {
// Extract referral code from URL
const referralCode = searchParams.get('ref') || '';
const [activeTab, setActiveTab] = useState<'telegram' | 'email'>(() =>
referralCode ? 'email' : 'telegram',
);
const [authMode, setAuthMode] = useState<'login' | 'register'>(() =>
referralCode ? 'register' : 'login',
);
@@ -96,12 +93,6 @@ export default function Login() {
}
}, [referralCode, emailAuthConfig, botUsername]);
// If email auth is disabled but we initially set to email tab, switch back to telegram
useEffect(() => {
if (!isEmailAuthEnabled && activeTab === 'email') {
setActiveTab('telegram');
}
}, [isEmailAuthEnabled, activeTab]);
const appName = branding ? branding.name : import.meta.env.VITE_APP_NAME || 'VPN';
const appLogo = branding?.logo_letter || import.meta.env.VITE_APP_LOGO || 'V';
const logoUrl = branding ? brandingApi.getLogoUrl(branding) : null;
@@ -336,194 +327,175 @@ export default function Login() {
) : (
/* Card */
<div className="card">
{/* Tabs */}
{isEmailAuthEnabled ? (
<div className="mb-6 flex">
<button
className={`flex-1 border-b-2 py-3 text-sm font-medium transition-all ${
activeTab === 'telegram'
? 'border-accent-500 text-accent-400'
: 'border-transparent text-dark-500 hover:text-dark-300'
}`}
onClick={() => setActiveTab('telegram')}
>
Telegram
</button>
<button
className={`flex-1 border-b-2 py-3 text-sm font-medium transition-all ${
activeTab === 'email'
? 'border-accent-500 text-accent-400'
: 'border-transparent text-dark-500 hover:text-dark-300'
}`}
onClick={() => setActiveTab('email')}
>
Email
</button>
</div>
) : (
<div className="mb-6 border-b border-dark-700 pb-3">
<h2 className="text-center text-lg font-medium text-dark-200">Telegram</h2>
</div>
)}
{error && (
<div className="mb-6 rounded-xl border border-error-500/30 bg-error-500/10 px-4 py-3 text-sm text-error-400">
{error}
</div>
)}
{activeTab === 'telegram' || !isEmailAuthEnabled ? (
<div className="space-y-6">
<p className="text-center text-sm text-dark-400">{t('auth.registerHint')}</p>
{/* Telegram auth section */}
<div className="space-y-6">
<p className="text-center text-sm text-dark-400">{t('auth.registerHint')}</p>
{isLoading && isTelegramWebApp ? (
<div className="py-8 text-center">
<div className="mx-auto mb-3 h-8 w-8 animate-spin rounded-full border-2 border-accent-500 border-t-transparent" />
<p className="text-sm text-dark-400">{t('auth.authenticating')}</p>
</div>
) : (
<TelegramLoginButton botUsername={botUsername} />
)}
</div>
) : (
<div className="space-y-5">
{/* Login / Register toggle */}
<div className="flex rounded-lg bg-dark-800 p-1">
<button
type="button"
className={`flex-1 rounded-md py-2 text-sm font-medium transition-all ${
authMode === 'login'
? 'bg-accent-500 text-white'
: 'text-dark-400 hover:text-dark-200'
}`}
onClick={() => setAuthMode('login')}
>
{t('auth.login')}
</button>
<button
type="button"
className={`flex-1 rounded-md py-2 text-sm font-medium transition-all ${
authMode === 'register'
? 'bg-accent-500 text-white'
: 'text-dark-400 hover:text-dark-200'
}`}
onClick={() => setAuthMode('register')}
>
{t('auth.register', 'Register')}
</button>
{isLoading && isTelegramWebApp ? (
<div className="py-8 text-center">
<div className="mx-auto mb-3 h-8 w-8 animate-spin rounded-full border-2 border-accent-500 border-t-transparent" />
<p className="text-sm text-dark-400">{t('auth.authenticating')}</p>
</div>
) : (
<TelegramLoginButton botUsername={botUsername} />
)}
</div>
{/* Email auth section - only when enabled */}
{isEmailAuthEnabled && (
<>
{/* Divider */}
<div className="my-6 flex items-center gap-3">
<div className="h-px flex-1 bg-dark-700" />
<span className="text-xs text-dark-500">{t('auth.or', 'or')}</span>
<div className="h-px flex-1 bg-dark-700" />
</div>
<form className="space-y-4" onSubmit={handleEmailSubmit}>
{/* First name field - only for registration */}
{authMode === 'register' && (
<div className="space-y-5">
{/* Login / Register toggle */}
<div className="flex rounded-lg bg-dark-800 p-1">
<button
type="button"
className={`flex-1 rounded-md py-2 text-sm font-medium transition-all ${
authMode === 'login'
? 'bg-accent-500 text-white'
: 'text-dark-400 hover:text-dark-200'
}`}
onClick={() => setAuthMode('login')}
>
{t('auth.login')}
</button>
<button
type="button"
className={`flex-1 rounded-md py-2 text-sm font-medium transition-all ${
authMode === 'register'
? 'bg-accent-500 text-white'
: 'text-dark-400 hover:text-dark-200'
}`}
onClick={() => setAuthMode('register')}
>
{t('auth.register', 'Register')}
</button>
</div>
<form className="space-y-4" onSubmit={handleEmailSubmit}>
{/* First name field - only for registration */}
{authMode === 'register' && (
<div>
<label htmlFor="firstName" className="label">
{t('auth.firstName', 'First Name')}
</label>
<input
id="firstName"
name="firstName"
type="text"
autoComplete="given-name"
className="input"
placeholder={t('auth.firstNamePlaceholder', 'Your name (optional)')}
value={firstName}
onChange={(e) => setFirstName(e.target.value)}
/>
</div>
)}
<div>
<label htmlFor="firstName" className="label">
{t('auth.firstName', 'First Name')}
<label htmlFor="email" className="label">
{t('auth.email')}
</label>
<input
id="firstName"
name="firstName"
type="text"
autoComplete="given-name"
id="email"
name="email"
type="email"
autoComplete="email"
required
className="input"
placeholder={t('auth.firstNamePlaceholder', 'Your name (optional)')}
value={firstName}
onChange={(e) => setFirstName(e.target.value)}
placeholder="you@example.com"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</div>
)}
<div>
<label htmlFor="email" className="label">
{t('auth.email')}
</label>
<input
id="email"
name="email"
type="email"
autoComplete="email"
required
className="input"
placeholder="you@example.com"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</div>
<div>
<label htmlFor="password" className="label">
{t('auth.password')}
</label>
<input
id="password"
name="password"
type="password"
autoComplete={authMode === 'login' ? 'current-password' : 'new-password'}
required
className="input"
placeholder="••••••••"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</div>
{/* Confirm password - only for registration */}
{authMode === 'register' && (
<div>
<label htmlFor="confirmPassword" className="label">
{t('auth.confirmPassword', 'Confirm Password')}
<label htmlFor="password" className="label">
{t('auth.password')}
</label>
<input
id="confirmPassword"
name="confirmPassword"
id="password"
name="password"
type="password"
autoComplete="new-password"
autoComplete={authMode === 'login' ? 'current-password' : 'new-password'}
required
className="input"
placeholder="••••••••"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</div>
{/* Confirm password - only for registration */}
{authMode === 'register' && (
<div>
<label htmlFor="confirmPassword" className="label">
{t('auth.confirmPassword', 'Confirm Password')}
</label>
<input
id="confirmPassword"
name="confirmPassword"
type="password"
autoComplete="new-password"
required
className="input"
placeholder="••••••••"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
/>
</div>
)}
<button type="submit" disabled={isLoading} className="btn-primary w-full py-3">
{isLoading ? (
<span className="flex items-center justify-center gap-2">
<span className="h-4 w-4 animate-spin rounded-full border-2 border-white/30 border-t-white" />
{t('common.loading')}
</span>
) : authMode === 'login' ? (
t('auth.login')
) : (
t('auth.register', 'Register')
)}
</button>
</form>
{/* Verification notice for registration */}
{authMode === 'register' && (
<p className="text-center text-xs text-dark-500">
{t(
'auth.verificationEmailNotice',
'After registration, a verification email will be sent to your address',
)}
</p>
)}
<button type="submit" disabled={isLoading} className="btn-primary w-full py-3">
{isLoading ? (
<span className="flex items-center justify-center gap-2">
<span className="h-4 w-4 animate-spin rounded-full border-2 border-white/30 border-t-white" />
{t('common.loading')}
</span>
) : authMode === 'login' ? (
t('auth.login')
) : (
t('auth.register', 'Register')
)}
</button>
</form>
{/* Verification notice for registration */}
{authMode === 'register' && (
<p className="text-center text-xs text-dark-500">
{t(
'auth.verificationEmailNotice',
'After registration, a verification email will be sent to your address',
)}
</p>
)}
{/* Forgot password link - only for login */}
{authMode === 'login' && (
<div className="space-y-2 text-center">
<button
type="button"
onClick={() => setShowForgotPassword(true)}
className="text-sm text-accent-400 transition-colors hover:text-accent-300"
>
{t('auth.forgotPassword', 'Forgot password?')}
</button>
</div>
)}
</div>
{/* Forgot password link - only for login */}
{authMode === 'login' && (
<div className="space-y-2 text-center">
<button
type="button"
onClick={() => setShowForgotPassword(true)}
className="text-sm text-accent-400 transition-colors hover:text-accent-300"
>
{t('auth.forgotPassword', 'Forgot password?')}
</button>
</div>
)}
</div>
</>
)}
</div>
)}