Spaces:
Runtime error
Runtime error
| import React, { useState } from 'react' | |
| import { setToken } from '../api.js' | |
| export default function Login({ onSuccess }) { | |
| const [username, setUsername] = useState('') | |
| const [password, setPassword] = useState('') | |
| const [error, setError] = useState(null) | |
| const [busy, setBusy] = useState(false) | |
| async function submit(e) { | |
| e.preventDefault() | |
| setBusy(true) | |
| setError(null) | |
| try { | |
| const res = await fetch('/api/login', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ username, password }), | |
| }) | |
| if (!res.ok) throw new Error(res.status === 401 ? 'Wrong username or password' : `Login failed (${res.status})`) | |
| const { token } = await res.json() | |
| setToken(token) | |
| onSuccess() | |
| } catch (err) { | |
| setError(err.message) | |
| } finally { | |
| setBusy(false) | |
| } | |
| } | |
| return ( | |
| <div className="empty"> | |
| <form className="empty-card login-card" onSubmit={submit}> | |
| <div className="brand login-brand"> | |
| <span className="brand-mark">§</span> | |
| <strong>Contract Obligation & Risk Extractor</strong> | |
| </div> | |
| <p className="muted">This workspace is password-protected. Contracts never leave this server.</p> | |
| <input | |
| type="text" | |
| placeholder="Username" | |
| autoComplete="username" | |
| value={username} | |
| autoFocus | |
| onChange={(e) => setUsername(e.target.value)} | |
| /> | |
| <input | |
| type="password" | |
| placeholder="Password" | |
| autoComplete="current-password" | |
| value={password} | |
| onChange={(e) => setPassword(e.target.value)} | |
| /> | |
| {error && <div className="login-error">⚠ {error}</div>} | |
| <button type="submit" className="upload-btn login-submit" disabled={busy || !username || !password}> | |
| {busy ? 'Signing in…' : 'Sign in'} | |
| </button> | |
| </form> | |
| </div> | |
| ) | |
| } | |