202 lines
6.5 KiB
Svelte
202 lines
6.5 KiB
Svelte
<!-- src/routes/admin/anmeldungen/+page.svelte -->
|
|
<script lang="ts">
|
|
import { onMount } from 'svelte';
|
|
import { goto } from '$app/navigation';
|
|
import AnmeldungenTable from '$lib/components/AnmeldungenTable.svelte';
|
|
import DienststellenDialog from '$lib/components/DienststellenDialog.svelte';
|
|
import AdminHeader from '$lib/components/AdminHeader.svelte';
|
|
|
|
interface Anmeldung {
|
|
pdfs: { pfad: string }[];
|
|
anrede: string;
|
|
vorname: string;
|
|
nachname: string;
|
|
email: string;
|
|
noteDeutsch?: string;
|
|
noteMathe?: string;
|
|
sozialverhalten?: string;
|
|
wunsch1?: { id: number; name: string };
|
|
wunsch2?: { id: number; name: string };
|
|
wunsch3?: { id: number; name: string };
|
|
timestamp: number;
|
|
id: number;
|
|
}
|
|
|
|
let anmeldungen: Anmeldung[] = [];
|
|
let isLoading = true;
|
|
let error = '';
|
|
|
|
// Dialog state
|
|
let showDialog = false;
|
|
let selectedAnmeldungId: number | null = null;
|
|
let selectedDienststelleId: number | null = null;
|
|
let availableWishes: { id: number, name: string }[] = [];
|
|
|
|
async function loadAnmeldungen() {
|
|
try {
|
|
isLoading = true;
|
|
error = '';
|
|
|
|
const res = await fetch('/api/admin/anmeldungen');
|
|
|
|
if (!res.ok) {
|
|
throw new Error(`Fehler beim Laden: ${res.status}`);
|
|
}
|
|
|
|
anmeldungen = await res.json();
|
|
} catch (err) {
|
|
error = err instanceof Error ? err.message : 'Unbekannter Fehler';
|
|
console.error('Fehler beim Laden der Anmeldungen:', err);
|
|
} finally {
|
|
isLoading = false;
|
|
}
|
|
}
|
|
|
|
function handleAccept(event: CustomEvent<{id: number}>) {
|
|
const anmeldung = anmeldungen.find(a => a.id === event.detail.id);
|
|
if (!anmeldung) return;
|
|
|
|
availableWishes = [
|
|
anmeldung.wunsch1 && { id: anmeldung.wunsch1.id, name: `1. Wunsch: ${anmeldung.wunsch1.name}` },
|
|
anmeldung.wunsch2 && { id: anmeldung.wunsch2.id, name: `2. Wunsch: ${anmeldung.wunsch2.name}` },
|
|
anmeldung.wunsch3 && { id: anmeldung.wunsch3.id, name: `3. Wunsch: ${anmeldung.wunsch3.name}` }
|
|
].filter(Boolean) as { id: number, name: string }[];
|
|
|
|
selectedDienststelleId = availableWishes[0]?.id ?? null;
|
|
selectedAnmeldungId = event.detail.id;
|
|
showDialog = true;
|
|
}
|
|
|
|
async function handleConfirmAccept(event: CustomEvent<{dienststelleId: number}>) {
|
|
if (!selectedAnmeldungId) return;
|
|
|
|
try {
|
|
const res = await fetch(`/api/admin/anmeldungen?id=${selectedAnmeldungId}`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ dienststelleId: event.detail.dienststelleId })
|
|
});
|
|
|
|
if (!res.ok) {
|
|
const errorText = await res.text();
|
|
throw new Error(`Fehler beim Annehmen (${res.status}): ${errorText}`);
|
|
}
|
|
|
|
showDialog = false;
|
|
selectedAnmeldungId = null;
|
|
await loadAnmeldungen();
|
|
} catch (err) {
|
|
error = err instanceof Error ? err.message : 'Fehler beim Annehmen';
|
|
console.error(err);
|
|
}
|
|
}
|
|
|
|
async function handleReject(event: CustomEvent<{id: number}>) {
|
|
if (!confirm('Diese Anmeldung wirklich ablehnen?')) return;
|
|
|
|
try {
|
|
const res = await fetch(`/api/admin/anmeldungen?id=${event.detail.id}`, {
|
|
method: 'PATCH',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ action: 'reject' })
|
|
});
|
|
|
|
if (!res.ok) {
|
|
throw new Error(`Fehler beim Ablehnen: ${res.status}`);
|
|
}
|
|
|
|
await loadAnmeldungen();
|
|
} catch (err) {
|
|
error = err instanceof Error ? err.message : 'Fehler beim Ablehnen';
|
|
console.error(err);
|
|
}
|
|
}
|
|
|
|
async function handleDelete(event: CustomEvent<{id: number}>) {
|
|
if (!confirm('Diese Anmeldung wirklich löschen?')) return;
|
|
|
|
try {
|
|
const res = await fetch(`/api/admin/anmeldungen?id=${event.detail.id}`, {
|
|
method: 'DELETE'
|
|
});
|
|
|
|
if (!res.ok) {
|
|
throw new Error(`Fehler beim Löschen: ${res.status}`);
|
|
}
|
|
|
|
await loadAnmeldungen();
|
|
} catch (err) {
|
|
error = err instanceof Error ? err.message : 'Fehler beim Löschen';
|
|
console.error(err);
|
|
}
|
|
}
|
|
|
|
function closeDialog() {
|
|
showDialog = false;
|
|
selectedAnmeldungId = null;
|
|
}
|
|
|
|
onMount(loadAnmeldungen);
|
|
</script>
|
|
|
|
<svelte:head>
|
|
<title>Anmeldungen verwalten - Admin</title>
|
|
</svelte:head>
|
|
|
|
<div class="min-h-screen bg-gray-50">
|
|
<AdminHeader
|
|
title="Anmeldungen verwalten"
|
|
showBackButton={true}
|
|
/>
|
|
|
|
<main class="max-w-7xl mx-auto px-4 py-6">
|
|
{#if error}
|
|
<div class="bg-red-50 border border-red-200 rounded-md p-4 mb-6">
|
|
<div class="flex">
|
|
<div class="flex-shrink-0">
|
|
<svg class="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor">
|
|
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd" />
|
|
</svg>
|
|
</div>
|
|
<div class="ml-3">
|
|
<h3 class="text-sm font-medium text-red-800">Fehler</h3>
|
|
<p class="mt-1 text-sm text-red-700">{error}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
|
|
{#if isLoading}
|
|
<div class="flex justify-center items-center h-64">
|
|
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
|
|
<span class="ml-3 text-gray-600">Lade Anmeldungen...</span>
|
|
</div>
|
|
{:else if anmeldungen.length === 0}
|
|
<div class="text-center py-12">
|
|
<svg class="mx-auto h-12 w-12 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
|
</svg>
|
|
<h3 class="mt-2 text-sm font-medium text-gray-900">Keine Anmeldungen</h3>
|
|
<p class="mt-1 text-sm text-gray-500">Es sind noch keine Praktikumsanmeldungen eingegangen.</p>
|
|
</div>
|
|
{:else}
|
|
<div class="bg-white shadow-sm rounded-lg overflow-hidden">
|
|
<AnmeldungenTable
|
|
{anmeldungen}
|
|
on:accept={handleAccept}
|
|
on:reject={handleReject}
|
|
on:delete={handleDelete}
|
|
/>
|
|
</div>
|
|
{/if}
|
|
</main>
|
|
</div>
|
|
|
|
{#if showDialog}
|
|
<DienststellenDialog
|
|
wishes={availableWishes}
|
|
selectedId={selectedDienststelleId}
|
|
on:confirm={handleConfirmAccept}
|
|
on:cancel={closeDialog}
|
|
/>
|
|
{/if} |