гітхаб + гугл + пошта з верифікацією (без прив'язки акаунтів)
翻译
| import { | |
| signInWithPopup, | |
| signOut, | |
| GithubAuthProvider, | |
| GoogleAuthProvider, | |
| onAuthStateChanged, | |
| createUserWithEmailAndPassword, | |
| signInWithEmailAndPassword, | |
| sendEmailVerification, | |
| sendPasswordResetEmail | |
| } from "firebase/auth"; | |
| import { auth } from "./firebase"; | |
| const githubProvider = new GithubAuthProvider(); | |
| const googleProvider = new GoogleAuthProvider(); | |
| // логін через GitHub | |
| export async function loginWithGitHub() { | |
| try { | |
| const result = await signInWithPopup(auth, githubProvider); | |
| console.log("Успішний вхід через GitHub:", result.user); | |
| return result.user; | |
| } catch (error) { | |
| console.error("Помилка входу через GitHub:", error); | |
| throw error; | |
| } | |
| } | |
| // логін через Google | |
| export async function loginWithGoogle() { | |
| try { | |
| const result = await signInWithPopup(auth, googleProvider); | |
| console.log("Успішний вхід через Google:", result.user); | |
| return result.user; | |
| } catch (error) { | |
| console.error("Помилка входу через Google:", error); | |
| throw error; | |
| } | |
| } | |
| // реєстрація через email та пароль | |
| export async function registerWithEmail(email, password) { | |
| try { | |
| const result = await createUserWithEmailAndPassword( | |
| auth, | |
| email, | |
| password | |
| ); | |
| await sendEmailVerification(result.user); | |
| await signOut(auth); | |
| console.log( | |
| "Користувача зареєстровано. Лист для підтвердження email надіслано:", | |
| result.user.email | |
| ); | |
| return result.user; | |
| } catch (error) { | |
| console.error( | |
| "Помилка реєстрації через email:", | |
| error | |
| ); | |
| throw error; | |
| } | |
| } | |
| // логін через email та пароль | |
| export async function loginWithEmail(email, password) { | |
| try { | |
| const result = await signInWithEmailAndPassword( | |
| auth, | |
| email, | |
| password | |
| ); | |
| if (!result.user.emailVerified) { | |
| await signOut(auth); | |
| const error = new Error( | |
| "Email користувача не підтверджено." | |
| ); | |
| error.code = "auth/email-not-verified"; | |
| throw error; | |
| } | |
| console.log( | |
| "Успішний вхід через email:", | |
| result.user | |
| ); | |
| return result.user; | |
| } catch (error) { | |
| console.error( | |
| "Помилка входу через email:", | |
| error | |
| ); | |
| throw error; | |
| } | |
| } | |
| // повторне надсилання листа підтвердження | |
| export async function resendEmailVerification(email, password) { | |
| try { | |
| const result = await signInWithEmailAndPassword( | |
| auth, | |
| email, | |
| password | |
| ); | |
| if (result.user.emailVerified) { | |
| await signOut(auth); | |
| return; | |
| } | |
| await sendEmailVerification(result.user); | |
| await signOut(auth); | |
| console.log( | |
| "Лист для підтвердження email надіслано повторно:", | |
| ); | |
| } catch (error) { | |
| console.error( | |
| "Помилка повторного надсилання листа:", | |
| error | |
| ); | |
| try { | |
| await signOut(auth); | |
| } catch { | |
| // користувач уже вийшов із системи | |
| } | |
| throw error; | |
| } | |
| } | |
| // відновлення пароля | |
| export async function resetPassword(email) { | |
| try { | |
| await sendPasswordResetEmail(auth, email); | |
| } catch (error) { | |
| console.error( | |
| "Помилка відновлення пароля:", | |
| error | |
| ); | |
| throw error; | |
| } | |
| } | |
| // вихід | |
| export async function logout() { | |
| await signOut(auth); | |
| } | |
| // отримати Firebase ID Token для бекенду | |
| export async function getIdToken(forceRefresh = false) { | |
| const user = auth.currentUser; | |
| if (!user) { | |
| return null; | |
| } | |
| return await user.getIdToken(forceRefresh); | |
| } | |
| // підписка на зміну користувача | |
| export function onUserChanged(callback) { | |
| return onAuthStateChanged(auth, callback); | |
| } |
| .login-page { | |
| min-height: 100vh; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| padding: 24px; | |
| background: radial-gradient( 1200px 600px at 15% -10%, rgba(0, 77, 152, 0.35), transparent 60% ), radial-gradient( 1000px 500px at 110% 0%, rgba(230, 10, 62, 0.25), transparent 55% ), #050914; | |
| } | |
| .login-card { | |
| width: 100%; | |
| max-width: 420px; | |
| background: linear-gradient( 180deg, #10182c 0%, rgba(16, 24, 44, 0.85) 100% ); | |
| border: 1px solid #23304a; | |
| border-radius: 20px; | |
| overflow: hidden; | |
| box-shadow: 0 25px 60px rgba(0, 0, 0, 0.5); | |
| animation: card-in 0.4s cubic-bezier(0.2, 0.9, 0.3, 1.1); | |
| } | |
| @keyframes card-in { | |
| from { | |
| opacity: 0; | |
| transform: translateY(20px) scale(0.97); | |
| } | |
| to { | |
| opacity: 1; | |
| transform: translateY(0) scale(1); | |
| } | |
| } | |
| .login-logo { | |
| text-align: center; | |
| font-size: 3.2rem; | |
| padding: 34px 32px 10px; | |
| filter: drop-shadow( 0 6px 14px rgba(230, 10, 62, 0.5) ); | |
| } | |
| .login-card h1 { | |
| text-align: center; | |
| font-family: 'Archivo Black', 'Inter', sans-serif; | |
| font-size: 1.7rem; | |
| letter-spacing: -0.5px; | |
| color: #fff; | |
| text-transform: uppercase; | |
| margin: 0; | |
| } | |
| .login-description { | |
| color: #8b96b3; | |
| font-size: 0.92rem; | |
| line-height: 1.5; | |
| text-align: center; | |
| margin: 10px 32px 28px; | |
| } | |
| .login-buttons { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 12px; | |
| padding: 0 32px; | |
| } | |
| .login-btn { | |
| width: 100%; | |
| min-height: 52px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 12px; | |
| padding: 14px 24px; | |
| border-radius: 12px; | |
| font-family: 'Inter', system-ui, sans-serif; | |
| font-weight: 700; | |
| font-size: 0.95rem; | |
| cursor: pointer; | |
| transition: transform 0.25s ease, box-shadow 0.25s ease, background 0.25s ease, border-color 0.25s ease; | |
| } | |
| .login-btn:disabled { | |
| opacity: 0.7; | |
| cursor: not-allowed; | |
| transform: none; | |
| } | |
| .login-provider-icon { | |
| width: 20px; | |
| height: 20px; | |
| flex-shrink: 0; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-weight: 700; | |
| font-size: 1rem; | |
| } | |
| .login-btn.google { | |
| background: #fff; | |
| border: 1px solid #dadce0; | |
| color: #202124; | |
| box-shadow: 0 10px 28px rgba(0, 0, 0, 0.25); | |
| } | |
| .login-btn.google:hover:not(:disabled) { | |
| background: #f8f9fa; | |
| transform: translateY(-2px); | |
| box-shadow: 0 14px 32px rgba(0, 0, 0, 0.35); | |
| } | |
| .login-btn.google:active:not(:disabled) { | |
| transform: translateY(0); | |
| } | |
| .login-btn.github { | |
| background: #24292e; | |
| border: 1px solid #3b4147; | |
| color: #fff; | |
| box-shadow: 0 10px 28px rgba(0, 0, 0, 0.35); | |
| } | |
| .login-btn.github:hover:not(:disabled) { | |
| background: #2f363d; | |
| border-color: #4a5158; | |
| transform: translateY(-2px); | |
| box-shadow: 0 14px 32px rgba(0, 0, 0, 0.45); | |
| } | |
| .login-btn.github:active:not(:disabled) { | |
| transform: translateY(0); | |
| } | |
| .github-icon { | |
| color: #fff; | |
| } | |
| .login-divider { | |
| display: flex; | |
| align-items: center; | |
| gap: 12px; | |
| margin: 24px 32px; | |
| color: #5a6680; | |
| font-size: 0.75rem; | |
| } | |
| .login-divider::before, | |
| .login-divider::after { | |
| content: ''; | |
| flex: 1; | |
| height: 1px; | |
| background: #23304a; | |
| } | |
| .login-divider span { | |
| flex-shrink: 0; | |
| } | |
| .email-form { | |
| padding: 0 32px; | |
| } | |
| .form-field { | |
| margin-bottom: 16px; | |
| } | |
| .form-field label { | |
| display: block; | |
| margin-bottom: 7px; | |
| color: #c2c9d8; | |
| font-size: 0.8rem; | |
| font-weight: 600; | |
| text-align: left; | |
| } | |
| .form-field input { | |
| width: 100%; | |
| box-sizing: border-box; | |
| min-height: 48px; | |
| padding: 12px 14px; | |
| border: 1px solid #303c56; | |
| border-radius: 10px; | |
| outline: none; | |
| background: #0b1222; | |
| color: #fff; | |
| font-family: 'Inter', system-ui, sans-serif; | |
| font-size: 0.9rem; | |
| transition: border-color 0.2s ease, box-shadow 0.2s ease, background 0.2s ease; | |
| } | |
| .form-field input::placeholder { | |
| color: #59657e; | |
| } | |
| .form-field input:focus { | |
| border-color: #5379b8; | |
| background: #0d1528; | |
| box-shadow: 0 0 0 3px rgba(83, 121, 184, 0.12); | |
| } | |
| .form-field input:disabled { | |
| opacity: 0.65; | |
| cursor: not-allowed; | |
| } | |
| .login-btn.email { | |
| margin-top: 4px; | |
| background: #e60a3e; | |
| border: 1px solid #e60a3e; | |
| color: #fff; | |
| box-shadow: 0 10px 28px rgba(230, 10, 62, 0.22); | |
| } | |
| .login-btn.email:hover:not(:disabled) { | |
| background: #f0184d; | |
| border-color: #f0184d; | |
| transform: translateY(-2px); | |
| box-shadow: 0 14px 32px rgba(230, 10, 62, 0.3); | |
| } | |
| .login-btn.email:active:not(:disabled) { | |
| transform: translateY(0); | |
| } | |
| .forgot-password { | |
| display: block; | |
| margin: 14px auto 0; | |
| padding: 0; | |
| border: none; | |
| background: none; | |
| color: #8b96b3; | |
| font-family: 'Inter', system-ui, sans-serif; | |
| font-size: 0.78rem; | |
| cursor: pointer; | |
| } | |
| .forgot-password:hover:not(:disabled) { | |
| color: #c2c9d8; | |
| } | |
| .forgot-password:disabled { | |
| opacity: 0.5; | |
| cursor: not-allowed; | |
| } | |
| .login-loading { | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 10px; | |
| margin: 18px 32px 0; | |
| color: #8b96b3; | |
| font-size: 0.82rem; | |
| } | |
| .login-loading .spinner { | |
| width: 18px; | |
| height: 18px; | |
| border-width: 2px; | |
| } | |
| .login-message { | |
| margin: 16px 32px 0; | |
| background: rgba(50, 190, 120, 0.1); | |
| border: 1px solid rgba(50, 190, 120, 0.3); | |
| color: #8ee0b3; | |
| padding: 12px 16px; | |
| border-radius: 10px; | |
| font-size: 0.82rem; | |
| font-weight: 500; | |
| line-height: 1.45; | |
| text-align: center; | |
| } | |
| .login-error { | |
| margin: 16px 32px 0; | |
| background: rgba(255, 77, 94, 0.12); | |
| border: 1px solid rgba(255, 77, 94, 0.4); | |
| color: #ff9aa5; | |
| padding: 12px 16px; | |
| border-radius: 10px; | |
| font-size: 0.82rem; | |
| font-weight: 500; | |
| line-height: 1.45; | |
| text-align: center; | |
| } | |
| .verification-button { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| gap: 3px; | |
| width: calc(100% - 64px); | |
| margin: 14px 32px 0; | |
| padding: 11px 16px; | |
| border: 1px solid rgba(83, 121, 184, 0.35); | |
| border-radius: 10px; | |
| background: rgba(83, 121, 184, 0.08); | |
| color: #8faedc; | |
| font-family: 'Inter', system-ui, sans-serif; | |
| font-size: 0.78rem; | |
| font-weight: 600; | |
| cursor: pointer; | |
| transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease; | |
| } | |
| .verification-button span { | |
| color: #c2c9d8; | |
| font-size: 0.72rem; | |
| font-weight: 500; | |
| } | |
| .verification-button:hover:not(:disabled) { | |
| background: rgba(83, 121, 184, 0.14); | |
| border-color: rgba(83, 121, 184, 0.55); | |
| color: #b5c9eb; | |
| } | |
| .verification-button:hover:not(:disabled) span { | |
| color: #e0e5ef; | |
| } | |
| .verification-button:disabled { | |
| opacity: 0.5; | |
| cursor: not-allowed; | |
| } | |
| .login-mode-switch { | |
| display: block; | |
| margin: 22px auto 0; | |
| padding: 0; | |
| border: none; | |
| background: none; | |
| color: #ffc72c; | |
| font-family: 'Inter', system-ui, sans-serif; | |
| font-size: 0.82rem; | |
| font-weight: 600; | |
| cursor: pointer; | |
| } | |
| .login-mode-switch:hover:not(:disabled) { | |
| color: #ffd95f; | |
| } | |
| .login-mode-switch:disabled { | |
| opacity: 0.5; | |
| cursor: not-allowed; | |
| } | |
| .login-footer { | |
| margin: 28px 32px 30px; | |
| padding-top: 18px; | |
| border-top: 1px solid #23304a; | |
| font-size: 0.75rem; | |
| color: #5a6680; | |
| letter-spacing: 0.3px; | |
| text-align: center; | |
| } | |
| @media (max-width: 520px) { | |
| .login-page { | |
| padding: 16px; | |
| } | |
| .login-card { | |
| max-width: 420px; | |
| } | |
| .login-logo { | |
| padding-top: 28px; | |
| } | |
| .login-buttons, | |
| .email-form { | |
| padding-left: 24px; | |
| padding-right: 24px; | |
| } | |
| .login-divider { | |
| margin-left: 24px; | |
| margin-right: 24px; | |
| } | |
| .login-message, | |
| .login-error, | |
| .login-loading { | |
| margin-left: 24px; | |
| margin-right: 24px; | |
| } | |
| .verification-button { | |
| width: calc(100% - 48px); | |
| margin-left: 24px; | |
| margin-right: 24px; | |
| } | |
| .login-footer { | |
| margin-left: 24px; | |
| margin-right: 24px; | |
| } | |
| .login-description { | |
| margin-left: 24px; | |
| margin-right: 24px; | |
| } | |
| } |
| import { useState } from 'react'; | |
| import { | |
| loginWithGitHub, | |
| loginWithGoogle, | |
| loginWithEmail, | |
| registerWithEmail, | |
| resendEmailVerification, | |
| resetPassword | |
| } from '../firebase/authService'; | |
| import './styles/Login.css'; | |
| export default function Login() { | |
| const [isRegistering, setIsRegistering] = useState(false); | |
| const [email, setEmail] = useState(''); | |
| const [password, setPassword] = useState(''); | |
| const [loading, setLoading] = useState(false); | |
| const [error, setError] = useState(''); | |
| const [message, setMessage] = useState(''); | |
| const clearMessages = () => { | |
| setError(''); | |
| setMessage(''); | |
| }; | |
| const handleGoogleLogin = async () => { | |
| setLoading(true); | |
| clearMessages(); | |
| try { | |
| await loginWithGoogle(); | |
| } catch (error) { | |
| console.error(error); | |
| if (error.code === 'auth/popup-closed-by-user') { | |
| setError('Вікно входу було закрито.'); | |
| } else if (error.code === 'auth/popup-blocked') { | |
| setError('Браузер заблокував спливаюче вікно.'); | |
| } else if ( | |
| error.code === 'auth/account-exists-with-different-credential' | |
| ) { | |
| setError( | |
| 'Акаунт із таким email уже існує через інший спосіб входу.' | |
| ); | |
| } else { | |
| setError('Не вдалося виконати вхід через Google.'); | |
| } | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| const handleGitHubLogin = async () => { | |
| setLoading(true); | |
| clearMessages(); | |
| try { | |
| await loginWithGitHub(); | |
| } catch (error) { | |
| console.error(error); | |
| if (error.code === 'auth/popup-closed-by-user') { | |
| setError('Вікно входу було закрито.'); | |
| } else if (error.code === 'auth/popup-blocked') { | |
| setError('Браузер заблокував спливаюче вікно.'); | |
| } else if ( | |
| error.code === 'auth/account-exists-with-different-credential' | |
| ) { | |
| setError( | |
| 'Акаунт із таким email уже існує через інший спосіб входу.' | |
| ); | |
| } else { | |
| setError('Не вдалося виконати вхід через GitHub.'); | |
| } | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| const handleEmailSubmit = async (event) => { | |
| event.preventDefault(); | |
| clearMessages(); | |
| if (!email.trim()) { | |
| setError('Введіть email.'); | |
| return; | |
| } | |
| if (!password) { | |
| setError('Введіть пароль.'); | |
| return; | |
| } | |
| setLoading(true); | |
| try { | |
| if (isRegistering) { | |
| await registerWithEmail(email, password); | |
| setMessage( | |
| 'Акаунт створено. Лист для підтвердження email надіслано на вашу електронну пошту. Перейдіть за посиланням у листі, а потім увійдіть.' | |
| ); | |
| } else { | |
| await loginWithEmail(email, password); | |
| } | |
| } catch (error) { | |
| console.error(error); | |
| if (error.code === 'auth/email-not-verified') { | |
| setError( | |
| 'Email ще не підтверджено. Перевірте пошту та перейдіть за посиланням у листі.' | |
| ); | |
| } else if (error.code === 'auth/email-already-in-use') { | |
| setError( | |
| 'Користувач із таким email уже існує. Спробуйте увійти.' | |
| ); | |
| } else if (error.code === 'auth/invalid-credential') { | |
| setError('Неправильний email або пароль.'); | |
| } else if (error.code === 'auth/invalid-email') { | |
| setError('Введіть коректну адресу електронної пошти.'); | |
| } else if (error.code === 'auth/weak-password') { | |
| setError('Пароль має містити щонайменше 6 символів.'); | |
| } else if (error.code === 'auth/too-many-requests') { | |
| setError('Занадто багато спроб. Спробуйте пізніше.'); | |
| } else if (error.code === 'auth/network-request-failed') { | |
| setError( | |
| 'Помилка мережі. Перевірте підключення до інтернету.' | |
| ); | |
| } else { | |
| setError( | |
| isRegistering | |
| ? 'Не вдалося створити акаунт.' | |
| : 'Не вдалося виконати вхід.' | |
| ); | |
| } | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| const handleResendVerification = async () => { | |
| clearMessages(); | |
| if (!email.trim()) { | |
| setError('Введіть email.'); | |
| return; | |
| } | |
| if (!password) { | |
| setError( | |
| 'Введіть пароль, щоб повторно надіслати лист підтвердження.' | |
| ); | |
| return; | |
| } | |
| setLoading(true); | |
| try { | |
| await resendEmailVerification(email, password); | |
| setMessage( | |
| 'Лист для підтвердження email надіслано повторно. Перевірте пошту та папку «Спам».' | |
| ); | |
| } catch (error) { | |
| console.error(error); | |
| if (error.code === 'auth/invalid-credential') { | |
| setError('Неправильний email або пароль.'); | |
| } else if (error.code === 'auth/invalid-email') { | |
| setError('Введіть коректну адресу електронної пошти.'); | |
| } else if (error.code === 'auth/too-many-requests') { | |
| setError('Занадто багато запитів. Спробуйте пізніше.'); | |
| } else if (error.code === 'auth/network-request-failed') { | |
| setError( | |
| 'Помилка мережі. Перевірте підключення до інтернету.' | |
| ); | |
| } else { | |
| setError( | |
| 'Не вдалося повторно надіслати лист підтвердження.' | |
| ); | |
| } | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| const handlePasswordReset = async () => { | |
| clearMessages(); | |
| if (!email.trim()) { | |
| setError('Введіть email для відновлення пароля.'); | |
| return; | |
| } | |
| setLoading(true); | |
| try { | |
| await resetPassword(email); | |
| setMessage( | |
| 'Лист для відновлення пароля надіслано на вашу електронну пошту.' | |
| ); | |
| } catch (error) { | |
| console.error(error); | |
| if (error.code === 'auth/invalid-email') { | |
| setError('Введіть коректну адресу електронної пошти.'); | |
| } else if (error.code === 'auth/too-many-requests') { | |
| setError('Занадто багато запитів. Спробуйте пізніше.'); | |
| } else if (error.code === 'auth/network-request-failed') { | |
| setError( | |
| 'Помилка мережі. Перевірте підключення до інтернету.' | |
| ); | |
| } else { | |
| setError( | |
| 'Не вдалося надіслати лист для відновлення пароля.' | |
| ); | |
| } | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| const toggleMode = () => { | |
| setIsRegistering((value) => !value); | |
| clearMessages(); | |
| }; | |
| return ( | |
| ⚽ | |
| </div> | |
Футбольна ліга | |
| Менеджер гравців та команд | |
| </p> | |
| <button | |
| className="login-btn google" | |
| onClick={handleGoogleLogin} | |
| disabled={loading} | |
| > | |
| <svg | |
| className="login-provider-icon" | |
| viewBox="0 0 24 24" | |
| aria-hidden="true" | |
| > | |
| <path | |
| fill="#4285F4" | |
| d="M21.35 12.27c0-.79-.07-1.55-.23-2.27H12v4.3h5.22a4.46 4.46 0 0 1-1.94 2.93v2.44h3.14c1.84-1.69 2.93-4.18 2.93-7.4z" | |
| /> | |
| <path | |
| fill="#34A853" | |
| d="M12 21.7c2.63 0 4.84-.87 6.45-2.36l-3.14-2.44c-.87.58-1.98.92-3.31.92-2.54 0-4.69-1.72-5.46-4.03H3.3v2.52A9.74 9.74 0 0 0 12 21.7z" | |
| /> | |
| <path | |
| fill="#FBBC05" | |
| d="M6.54 13.79A5.85 5.85 0 0 1 6.24 12c0-.62.11-1.22.3-1.79V7.69H3.3A9.75 9.75 0 0 0 2.25 12c0 1.57.38 3.05 1.05 4.31l3.24-2.52z" | |
| /> | |
| <path | |
| fill="#EA4335" | |
| d="M12 6.18c1.43 0 2.72.49 3.73 1.45l2.8-2.8C16.83 3.15 14.63 2.3 12 2.3a9.74 9.74 0 0 0-8.7 5.39l3.24 2.52C7.31 7.9 9.46 6.18 12 6.18z" | |
| /> | |
| </svg> | |
| Увійти через Google | |
| </button> | |
| <button | |
| className="login-btn github" | |
| onClick={handleGitHubLogin} | |
| disabled={loading} | |
| > | |
| <svg | |
| className="login-provider-icon github-icon" | |
| viewBox="0 0 24 24" | |
| aria-hidden="true" | |
| > | |
| <path | |
| fill="currentColor" | |
| d="M12 .5C5.65.5.5 5.65.5 12c0 5.08 3.29 9.39 7.86 10.91.57.1.78-.25.78-.55v-2.1c-3.2.7-3.88-1.54-3.88-1.54-.52-1.33-1.28-1.68-1.28-1.68-1.04-.71.08-.7.08-.7 1.15.08 1.76 1.18 1.76 1.18 1.02 1.75 2.67 1.24 3.32.95.1-.74.4-1.24.73-1.53-2.55-.29-5.23-1.28-5.23-5.68 0-1.26.45-2.29 1.18-3.1-.12-.29-.51-1.47.11-3.06 0 0 .96-.31 3.15 1.18a10.9 10.9 0 0 1 5.73 0c2.19-1.49 3.15-1.18 3.15-1.18.62 1.59.23 2.77.11 3.06.73.81 1.18 1.84 1.18 3.1 0 4.41-2.69 5.39-5.25 5.67.41.36.78 1.08.78 2.18v3.23c0 .3.21.65.79.54A11.51 11.51 0 0 0 23.5 12C23.5 5.65 18.35.5 12 .5z" | |
| /> | |
| </svg> | |
| Увійти через GitHub | |
| </button> | |
| </div> | |
| або | |
| </div> | |
| <form | |
| className="email-form" | |
| onSubmit={handleEmailSubmit} | |
| > | |
| </label> | |
| <input | |
| id="email" | |
| type="email" | |
| value={email} | |
| onChange={(event) => | |
| setEmail(event.target.value) | |
| } | |
| placeholder="example@email.com" | |
| autoComplete="email" | |
| disabled={loading} | |
| /> | |
| </div> | |
| Пароль | |
| </label> | |
| <input | |
| id="password" | |
| type="password" | |
| value={password} | |
| onChange={(event) => | |
| setPassword(event.target.value) | |
| } | |
| placeholder="Введіть пароль" | |
| autoComplete={ | |
| isRegistering | |
| ? 'new-password' | |
| : 'current-password' | |
| } | |
| disabled={loading} | |
| /> | |
| </div> | |
| {!isRegistering && ( | |
| <button | |
| type="button" | |
| className="forgot-password" | |
| onClick={handlePasswordReset} | |
| disabled={loading} | |
| > | |
| Забули пароль? | |
| </button> | |
| )} | |
| <button | |
| type="submit" | |
| className="login-btn email" | |
| disabled={loading} | |
| > | |
| {isRegistering | |
| ? 'Зареєструватися' | |
| : 'Увійти'} | |
| </button> | |
| </form> | |
| {loading && ( | |
| Зачекайте... | |
| </div> | |
| )} | |
| {error && ( | |
| {error} | |
| </div> | |
| )} | |
| {message && ( | |
| {message} | |
| </div> | |
| )} | |
| {!isRegistering && ( | |
| <button | |
| type="button" | |
| className="verification-button" | |
| onClick={handleResendVerification} | |
| disabled={loading} | |
| > | |
| Підтвердьте email | |
| Надіслати лист повторно | |
| </span> | |
| </button> | |
| )} | |
| <button | |
| type="button" | |
| className="login-mode-switch" | |
| onClick={toggleMode} | |
| disabled={loading} | |
| > | |
| {isRegistering | |
| ? 'Вже маєте акаунт? Увійти' | |
| : 'Ще не маєте акаунта? Зареєструватися'} | |
| </button> | |
| Авторизація через Firebase | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| } |
评论
?
参与讨论