Download src/components/forms/LoginForm.tsx from HonzaH/Letschat: direct link, hf CLI and curl.
- Browser
- Download file 4.21 kB
-
https://huggingface.co/spaces/HonzaH/Letschat/resolve/main/src/components/forms/LoginForm.tsx
- Command line
-
hf download hf://spaces/HonzaH/Letschat/src/components/forms/LoginForm.tsx
-
curl -L -o LoginForm.tsx https://huggingface.co/spaces/HonzaH/Letschat/resolve/main/src/components/forms/LoginForm.tsx
4.21 kB
| 'use client' | |
| import { useState } from 'react' | |
| import { useForm } from 'react-hook-form' | |
| import { zodResolver } from '@hookform/resolvers/zod' | |
| import { useRouter } from 'next/navigation' | |
| import { loginSchema, type LoginForm } from 'lib/validations/auth' | |
| import { emitAuthChanged } from 'hooks/useAuth' | |
| import { getSupabaseBrowserConfig, isSupabaseBrowserConfigured } from 'lib/supabase/publicEnv' | |
| export function LoginForm() { | |
| const [loading, setLoading] = useState(false) | |
| const [error, setError] = useState<string | null>(null) | |
| const supabaseReady = isSupabaseBrowserConfigured() | |
| const supabaseEnv = getSupabaseBrowserConfig() | |
| const router = useRouter() | |
| const { | |
| register, | |
| handleSubmit, | |
| formState: { errors } | |
| } = useForm<LoginForm>({ | |
| resolver: zodResolver(loginSchema) | |
| }) | |
| const onSubmit = async (data: LoginForm) => { | |
| console.info('[login] submit', { supabaseReady, email: data.email, supabaseUrl: supabaseEnv.url }) | |
| try { | |
| setLoading(true) | |
| setError(null) | |
| const res = await fetch('/api/auth/login', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify(data), | |
| credentials: 'include' | |
| }) | |
| const json = await res.json().catch(() => ({})) | |
| if (!res.ok) { | |
| setError(json?.error || 'Neplatné přihlašovací údaje') | |
| return | |
| } | |
| // store token from public cookie as fallback for environments where HttpOnly cookie isn't sent | |
| try { | |
| if (typeof window !== 'undefined') { | |
| const match = document.cookie.split(';').map(s => s.trim()).find(c => c.startsWith('letschat_session_pub=')) | |
| if (match) { | |
| const token = match.split('=')[1] | |
| window.localStorage.setItem('letschat_session', token) | |
| } | |
| } | |
| } catch (e) { | |
| // ignore | |
| } | |
| emitAuthChanged() | |
| router.push('/dashboard') | |
| } catch (e: any) { | |
| console.error('Login error:', e) | |
| setError(e?.message || 'Došlo k neočekávané chybě při přihlášení') | |
| } finally { | |
| setLoading(false) | |
| } | |
| } | |
| return ( | |
| <form id="form-login" action="#" method="post" className="form" onSubmit={handleSubmit(onSubmit)}> | |
| <div className="input"> | |
| <div className="text">E-mail</div> | |
| <div className="insert"> | |
| <input | |
| {...register('email')} | |
| type="email" | |
| maxLength={255} | |
| placeholder="E-mailová adresa" | |
| autoComplete="email" | |
| /> | |
| <div className="error">{errors.email?.message}</div> | |
| </div> | |
| </div> | |
| <div className="input"> | |
| <div className="text">Heslo</div> | |
| <div className="insert"> | |
| <input | |
| {...register('password')} | |
| type="password" | |
| maxLength={255} | |
| placeholder="Heslo k přihlášení" | |
| autoComplete="current-password" | |
| /> | |
| <div className="error">{errors.password?.message}</div> | |
| <div className="visible" data-input="password"></div> | |
| </div> | |
| </div> | |
| <div className="input"> | |
| <div className="text"></div> | |
| <div className="insert"> | |
| {error ? ( | |
| <div | |
| className="error" | |
| style={{ | |
| marginBottom: 12, | |
| padding: '10px 12px', | |
| background: '#FEE2E2', | |
| color: '#991B1B', | |
| borderRadius: 8, | |
| fontWeight: 600 | |
| }} | |
| > | |
| {error} | |
| </div> | |
| ) : null} | |
| <input | |
| type="submit" | |
| value={loading ? 'Přihlašuji…' : 'Přihlásit'} | |
| disabled={loading} | |
| onClick={() => console.info('[login] button click', { supabaseReady, supabaseUrl: supabaseEnv.url })} | |
| /> | |
| {!supabaseReady ? ( | |
| <div className="error" style={{ marginTop: 8 }}> | |
| Přidejte Supabase klíče do prostředí Space (NEXT_PUBLIC_SUPABASE_URL a NEXT_PUBLIC_SUPABASE_ANON_KEY) a znovu | |
| spusťte build. | |
| </div> | |
| ) : null} | |
| </div> | |
| </div> | |
| </form> | |
| ) | |
| } | |