import React, { useState, useEffect, useRef } from 'react'; import { backendApi } from '../services/apiClient'; interface LoginPageProps { onLoginSuccess: (user: { id: string; name: string; role: string; avatar?: string | null }, token: string) => void; apiError: string | null; loading: boolean; } declare global { interface Window { turnstile?: { render: (container: string | HTMLElement, options: { sitekey: string; callback?: (token: string) => void; 'expired-callback'?: () => void }) => string; reset: (widgetId?: string) => void; remove: (widgetId?: string) => void; }; } } export const LoginPage: React.FC = ({ onLoginSuccess, apiError, loading: externalLoading }) => { const [login, setLogin] = useState(''); const [password, setPassword] = useState(''); const [captchaToken, setCaptchaToken] = useState(undefined); const [localError, setLocalError] = useState(null); const [submitting, setSubmitting] = useState(false); const [turnstileSiteKey, setTurnstileSiteKey] = useState(null); const captchaContainerRef = useRef(null); const turnstileWidgetIdRef = useRef(null); const loading = externalLoading || submitting; useEffect(() => { backendApi.getCaptchaSiteKey().then((r) => setTurnstileSiteKey(r.siteKey || null)).catch(() => setTurnstileSiteKey(null)); }, []); useEffect(() => { if (!turnstileSiteKey || !captchaContainerRef.current) return; const renderTurnstile = () => { if (captchaContainerRef.current && window.turnstile && !turnstileWidgetIdRef.current) { turnstileWidgetIdRef.current = window.turnstile.render(captchaContainerRef.current, { sitekey: turnstileSiteKey, callback: (token) => setCaptchaToken(token), 'expired-callback': () => setCaptchaToken(undefined), }); } }; if (window.turnstile) { renderTurnstile(); } else { const t = setInterval(() => { if (window.turnstile) { clearInterval(t); renderTurnstile(); } }, 100); return () => clearInterval(t); } return () => { if (turnstileWidgetIdRef.current != null && window.turnstile?.remove) { window.turnstile.remove(turnstileWidgetIdRef.current); turnstileWidgetIdRef.current = null; } }; }, [turnstileSiteKey]); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setLocalError(null); if (!login.trim()) { setLocalError('Введите логин'); return; } if (!password) { setLocalError('Введите пароль'); return; } setSubmitting(true); try { const { setAuthToken } = await import('../services/apiClient'); const res = await backendApi.login({ login: login.trim(), password, captchaToken: captchaToken || undefined, }); setAuthToken(res.token); onLoginSuccess(res.user, res.token); } catch (err: any) { setLocalError(err?.message || 'Ошибка входа'); if (turnstileSiteKey && window.turnstile?.reset && turnstileWidgetIdRef.current != null) { window.turnstile.reset(turnstileWidgetIdRef.current); setCaptchaToken(undefined); } } finally { setSubmitting(false); } }; const error = localError || apiError; return (

Центр управления
домами

Войдите в свой аккаунт

{error && (
{error}
)}
setLogin(e.target.value)} autoComplete="username" placeholder="Введите логин" className="w-full px-4 py-3 bg-white border border-slate-200 rounded-xl text-slate-800 placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent transition-all" disabled={loading} />
setPassword(e.target.value)} autoComplete="current-password" placeholder="Введите пароль" className="w-full px-4 py-3 bg-white border border-slate-200 rounded-xl text-slate-800 placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent transition-all" disabled={loading} />
{turnstileSiteKey && (
)}

Используйте учётные данные, выданные администратором

); };