feat: scaffold React frontend with login/register UI
This commit is contained in:
96
frontend/src/App.tsx
Normal file
96
frontend/src/App.tsx
Normal file
@@ -0,0 +1,96 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { login, logout, me, register, type User } from './api'
|
||||
|
||||
function App() {
|
||||
const [user, setUser] = useState<User | null>(null)
|
||||
const [username, setUsername] = useState('')
|
||||
const [password, setPassword] = useState('')
|
||||
const [mode, setMode] = useState<'login' | 'register'>('login')
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [checkingSession, setCheckingSession] = useState(true)
|
||||
|
||||
useEffect(() => {
|
||||
me()
|
||||
.then(setUser)
|
||||
.catch(() => setUser(null))
|
||||
.finally(() => setCheckingSession(false))
|
||||
}, [])
|
||||
|
||||
async function handleSubmit(event: React.FormEvent) {
|
||||
event.preventDefault()
|
||||
setError(null)
|
||||
try {
|
||||
if (mode === 'register') {
|
||||
await register(username, password)
|
||||
}
|
||||
const loggedInUser = await login(username, password)
|
||||
setUser(loggedInUser)
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Something went wrong')
|
||||
}
|
||||
}
|
||||
|
||||
async function handleLogout() {
|
||||
await logout()
|
||||
setUser(null)
|
||||
}
|
||||
|
||||
if (checkingSession) {
|
||||
return (
|
||||
<main className="app">
|
||||
<p>Listening for a signal...</p>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<main className="app">
|
||||
<h1>Quantumancy</h1>
|
||||
<p className="tagline">Something is listening on the other side.</p>
|
||||
|
||||
{user ? (
|
||||
<section>
|
||||
<p>Contact established as {user.username}.</p>
|
||||
<button onClick={handleLogout}>End session</button>
|
||||
</section>
|
||||
) : (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="mode-toggle">
|
||||
<button
|
||||
type="button"
|
||||
className={mode === 'login' ? 'active' : ''}
|
||||
onClick={() => setMode('login')}
|
||||
>
|
||||
Reconnect
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={mode === 'register' ? 'active' : ''}
|
||||
onClick={() => setMode('register')}
|
||||
>
|
||||
First contact
|
||||
</button>
|
||||
</div>
|
||||
<label>
|
||||
Username
|
||||
<input value={username} onChange={(e) => setUsername(e.target.value)} required />
|
||||
</label>
|
||||
<label>
|
||||
Password
|
||||
<input
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
required
|
||||
minLength={8}
|
||||
/>
|
||||
</label>
|
||||
{error && <p className="error">{error}</p>}
|
||||
<button type="submit">{mode === 'login' ? 'Enter' : 'Begin'}</button>
|
||||
</form>
|
||||
)}
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
Reference in New Issue
Block a user