feat(worker): login-only client (admin creates users)

This commit is contained in:
Bas van Rossem
2026-06-17 17:53:20 +02:00
parent bd2d859e92
commit 7d200eb8fc
3 changed files with 8 additions and 50 deletions

View File

@@ -1,11 +1,10 @@
import { createContext, useCallback, useContext, useState, type ReactNode } from 'react';
import { clearToken, getToken } from '../lib/auth-storage';
import { signIn as apiSignIn, signUp as apiSignUp } from '../lib/api';
import { signIn as apiSignIn } from '../lib/api';
interface AuthContextValue {
isAuthed: boolean;
signIn: (email: string, password: string) => Promise<void>;
signUp: (email: string, password: string) => Promise<void>;
signOut: () => void;
}
@@ -19,25 +18,13 @@ export function AuthProvider({ children }: { children: ReactNode }) {
setIsAuthed(true);
}, []);
const signUp = useCallback(
async (email: string, password: string) => {
// Register, then sign in to obtain the bearer token.
await apiSignUp(email, password);
await apiSignIn(email, password);
setIsAuthed(true);
},
[],
);
const signOut = useCallback(() => {
clearToken();
setIsAuthed(false);
}, []);
return (
<AuthContext.Provider value={{ isAuthed, signIn, signUp, signOut }}>
{children}
</AuthContext.Provider>
<AuthContext.Provider value={{ isAuthed, signIn, signOut }}>{children}</AuthContext.Provider>
);
}

View File

@@ -5,7 +5,7 @@ export const API_URL = import.meta.env.VITE_API_URL ?? 'http://localhost:3000';
export class ApiError extends Error {
constructor(
public status: number,
message: string,
message: string
) {
super(message);
this.name = 'ApiError';
@@ -35,13 +35,3 @@ export async function signIn(email: string, password: string): Promise<void> {
if (!token) throw new ApiError(500, 'Geen token ontvangen');
setToken(token);
}
// Sign up affordance for testing: POST /api/auth/sign-up/email.
export async function signUp(email: string, password: string): Promise<void> {
const res = await fetch(`${API_URL}/api/auth/sign-up/email`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password, name: email.split('@')[0] || 'Worker' }),
});
if (!res.ok) throw new ApiError(res.status, 'Registreren mislukt');
}

View File

@@ -1,30 +1,21 @@
import { useState, type FormEvent } from 'react';
import { useAuth } from '../auth/AuthContext';
type Mode = 'signin' | 'signup';
export default function Login() {
const { signIn, signUp } = useAuth();
const [mode, setMode] = useState<Mode>('signin');
const { signIn } = useAuth();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState<string | null>(null);
const [busy, setBusy] = useState(false);
const submitLabel = mode === 'signin' ? 'Inloggen' : 'Registreren';
async function handleSubmit(e: FormEvent) {
e.preventDefault();
setError(null);
setBusy(true);
try {
if (mode === 'signin') {
await signIn(email, password);
} else {
await signUp(email, password);
}
await signIn(email, password);
} catch {
setError(mode === 'signin' ? 'Inloggen mislukt' : 'Registreren mislukt');
setError('Inloggen mislukt');
} finally {
setBusy(false);
}
@@ -49,26 +40,16 @@ export default function Login() {
<input
className="field-input"
type="password"
autoComplete={mode === 'signin' ? 'current-password' : 'new-password'}
autoComplete="current-password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</label>
{error && <p className="login-error">{error}</p>}
<button className="btn-primary" type="submit" disabled={busy}>
{submitLabel}
Inloggen
</button>
</form>
<button
className="login-toggle"
type="button"
onClick={() => {
setMode((m) => (m === 'signin' ? 'signup' : 'signin'));
setError(null);
}}
>
{mode === 'signin' ? 'Nog geen account? Registreren' : 'Al een account? Inloggen'}
</button>
</div>
);
}