feat: add /admin/login page (v0.14.0)
Password form at /admin/login that calls POST /api/v1/admin/login and redirects to /admin on success. Admin dashboard now redirects to /admin/login instead of /join when unauthenticated. Test guide updated. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -47,7 +47,7 @@
|
||||
const token = getToken();
|
||||
const role = getRole();
|
||||
if (!token || role !== 'admin') {
|
||||
goto('/join');
|
||||
goto('/admin/login');
|
||||
return;
|
||||
}
|
||||
await reload();
|
||||
|
||||
68
frontend/src/routes/admin/login/+page.svelte
Normal file
68
frontend/src/routes/admin/login/+page.svelte
Normal file
@@ -0,0 +1,68 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { api, ApiError } from '$lib/api';
|
||||
import { setAuth, getRole } from '$lib/auth';
|
||||
import { browser } from '$app/environment';
|
||||
|
||||
// Already logged in as admin → go straight to dashboard
|
||||
if (browser && getRole() === 'admin') {
|
||||
goto('/admin');
|
||||
}
|
||||
|
||||
let password = $state('');
|
||||
let error = $state('');
|
||||
let loading = $state(false);
|
||||
|
||||
async function handleLogin() {
|
||||
if (!password) return;
|
||||
loading = true;
|
||||
error = '';
|
||||
try {
|
||||
const res = await api.post<{ jwt: string }>('/admin/login', { password });
|
||||
// Admin sessions have no PIN; pass null so setAuth doesn't overwrite a guest PIN
|
||||
setAuth(res.jwt, null, '');
|
||||
goto('/admin');
|
||||
} catch (e) {
|
||||
if (e instanceof ApiError) {
|
||||
error = e.message;
|
||||
} else {
|
||||
error = 'Ein Fehler ist aufgetreten.';
|
||||
}
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex min-h-screen items-center justify-center bg-gray-50 px-4">
|
||||
<div class="w-full max-w-sm">
|
||||
<h1 class="mb-2 text-center text-2xl font-bold text-gray-900">Admin-Login</h1>
|
||||
<p class="mb-6 text-center text-gray-500 text-sm">Nur für Veranstalter</p>
|
||||
|
||||
<form onsubmit={(e) => { e.preventDefault(); handleLogin(); }}>
|
||||
<input
|
||||
type="password"
|
||||
bind:value={password}
|
||||
placeholder="Passwort"
|
||||
autocomplete="current-password"
|
||||
class="mb-3 w-full rounded-lg border border-gray-300 px-4 py-3 text-lg focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-200"
|
||||
/>
|
||||
|
||||
{#if error}
|
||||
<p class="mb-3 text-sm text-red-600">{error}</p>
|
||||
{/if}
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading || !password}
|
||||
class="w-full rounded-lg bg-blue-600 px-4 py-3 text-lg font-medium text-white transition hover:bg-blue-700 disabled:opacity-50"
|
||||
>
|
||||
{loading ? 'Wird angemeldet…' : 'Anmelden'}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<p class="mt-4 text-center text-sm text-gray-500">
|
||||
<a href="/join" class="text-blue-600 hover:underline">Zurück zum Event</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user