Admin Bereich an das neuen layout geaendert.
This commit is contained in:
@@ -1,19 +1,42 @@
|
||||
<!-- src/routes/admin/change-password/+page.svelte -->
|
||||
<script lang="ts">
|
||||
import AdminHeader from '$lib/components/AdminHeader.svelte';
|
||||
|
||||
let oldPassword = '';
|
||||
let newPassword = '';
|
||||
let confirmPassword = '';
|
||||
let message = '';
|
||||
let error = '';
|
||||
let isLoading = false;
|
||||
|
||||
async function changePassword() {
|
||||
message = '';
|
||||
error = '';
|
||||
|
||||
// Validierung
|
||||
if (!oldPassword.trim()) {
|
||||
error = 'Altes Passwort ist erforderlich.';
|
||||
return;
|
||||
}
|
||||
|
||||
if (!newPassword.trim()) {
|
||||
error = 'Neues Passwort ist erforderlich.';
|
||||
return;
|
||||
}
|
||||
|
||||
if (newPassword.length < 6) {
|
||||
error = 'Das neue Passwort muss mindestens 6 Zeichen lang sein.';
|
||||
return;
|
||||
}
|
||||
|
||||
if (newPassword !== confirmPassword) {
|
||||
error = 'Die neuen Passwörter stimmen nicht überein.';
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
isLoading = true;
|
||||
|
||||
const res = await fetch('/api/admin/change-password', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
@@ -26,57 +49,180 @@
|
||||
if (!res.ok) {
|
||||
error = data.error || 'Fehler beim Ändern des Passworts.';
|
||||
} else {
|
||||
message = '✅ Passwort erfolgreich geändert.';
|
||||
oldPassword = newPassword = confirmPassword = '';
|
||||
message = 'Passwort erfolgreich geändert.';
|
||||
oldPassword = '';
|
||||
newPassword = '';
|
||||
confirmPassword = '';
|
||||
}
|
||||
} catch (err) {
|
||||
error = err instanceof Error ? err.message : 'Unbekannter Fehler beim Ändern des Passworts.';
|
||||
console.error('Fehler beim Passwort ändern:', err);
|
||||
} finally {
|
||||
isLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
oldPassword = '';
|
||||
newPassword = '';
|
||||
confirmPassword = '';
|
||||
message = '';
|
||||
error = '';
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<div class="max-w-lg mx-auto bg-white p-6 rounded-2xl shadow-md space-y-6 border border-gray-200">
|
||||
<svelte:head>
|
||||
<title>Passwort ändern - Admin</title>
|
||||
</svelte:head>
|
||||
|
||||
|
||||
<div class="min-h-screen bg-gray-50">
|
||||
<AdminHeader
|
||||
title="Admin-Passwort ändern"
|
||||
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 message}
|
||||
<div class="bg-green-50 border border-green-200 rounded-md p-4 mb-6">
|
||||
<div class="flex">
|
||||
<div class="flex-shrink-0">
|
||||
<svg class="h-5 w-5 text-green-400" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="ml-3">
|
||||
<h3 class="text-sm font-medium text-green-800">Erfolg</h3>
|
||||
<p class="mt-1 text-sm text-green-700">{message}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Passwort ändern Formular -->
|
||||
<div class="max-w-2xl mx-auto">
|
||||
<div class="bg-white shadow-sm rounded-lg p-6">
|
||||
<div class="flex items-center mb-6">
|
||||
<div class="flex-shrink-0">
|
||||
<svg class="h-8 w-8 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="ml-4">
|
||||
<h2 class="text-lg font-medium text-gray-900">Passwort ändern</h2>
|
||||
<p class="text-sm text-gray-500">Aus Sicherheitsgründen sollten Sie Ihr Passwort regelmäßig ändern.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<div>
|
||||
<label for="old-password" class="block text-sm font-medium text-gray-700 mb-2">
|
||||
Aktuelles Passwort
|
||||
</label>
|
||||
<input
|
||||
id="old-password"
|
||||
type="password"
|
||||
bind:value={oldPassword}
|
||||
bind:value={oldPassword}
|
||||
placeholder="Geben Sie Ihr aktuelles Passwort ein"
|
||||
class="w-full border border-gray-300 rounded-md px-3 py-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
disabled={isLoading}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<label for="new-password" class="block text-sm font-medium text-gray-700 mb-2">
|
||||
Neues Passwort
|
||||
</label>
|
||||
<input
|
||||
id="new-password"
|
||||
type="password"
|
||||
bind:value={newPassword}
|
||||
bind:value={newPassword}
|
||||
placeholder="Geben Sie Ihr neues Passwort ein (mindestens 6 Zeichen)"
|
||||
class="w-full border border-gray-300 rounded-md px-3 py-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
disabled={isLoading}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<label for="confirm-password" class="block text-sm font-medium text-gray-700 mb-2">
|
||||
Neues Passwort wiederholen
|
||||
</label>
|
||||
<input
|
||||
id="confirm-password"
|
||||
type="password"
|
||||
bind:value={confirmPassword}
|
||||
bind:value={confirmPassword}
|
||||
placeholder="Wiederholen Sie Ihr neues Passwort"
|
||||
class="w-full border border-gray-300 rounded-md px-3 py-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
disabled={isLoading}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{#if error}
|
||||
<div class="text-red-600 text-sm font-medium">{error}</div>
|
||||
{/if}
|
||||
{#if message}
|
||||
<div class="text-green-600 text-sm font-medium">{message}</div>
|
||||
{/if}
|
||||
|
||||
<div class="flex items-center justify-between pt-4">
|
||||
<button
|
||||
<button
|
||||
on:click={changePassword}
|
||||
type="button"
|
||||
on:click={resetForm}
|
||||
class="px-4 py-2 text-sm text-gray-600 hover:text-gray-800 hover:underline"
|
||||
disabled={isLoading}
|
||||
>
|
||||
Formular zurücksetzen
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
on:click={changePassword}
|
||||
disabled={isLoading}
|
||||
class="bg-blue-600 hover:bg-blue-700 disabled:bg-blue-400 text-white px-6 py-2 rounded-md text-sm font-medium inline-flex items-center"
|
||||
>
|
||||
{#if isLoading}
|
||||
<svg class="animate-spin -ml-1 mr-2 h-4 w-4 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
Wird geändert...
|
||||
{:else}
|
||||
Passwort ändern
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sicherheitshinweise -->
|
||||
<div class="mt-8 p-4 bg-blue-50 rounded-md">
|
||||
<div class="flex">
|
||||
<div class="flex-shrink-0">
|
||||
<svg class="h-5 w-5 text-blue-400" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="ml-3">
|
||||
<h3 class="text-sm font-medium text-blue-800">Sicherheitshinweise</h3>
|
||||
<div class="mt-2 text-sm text-blue-700">
|
||||
<ul class="list-disc pl-5 space-y-1">
|
||||
<li>Verwenden Sie ein starkes Passwort mit mindestens 6 Zeichen</li>
|
||||
<li>Kombinieren Sie Buchstaben, Zahlen und Sonderzeichen</li>
|
||||
<li>Verwenden Sie dieses Passwort nicht für andere Dienste</li>
|
||||
<li>Ändern Sie Ihr Passwort regelmäßig</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
@@ -1,14 +1,33 @@
|
||||
<!-- src/routes/admin/dienststellen/+page.svelte -->
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import AdminHeader from '$lib/components/AdminHeader.svelte';
|
||||
|
||||
let dienststellen: { id: number; name: string; plaetze: number }[] = [];
|
||||
let neuerName = '';
|
||||
let neuePlaetze = 0;
|
||||
let fehlermeldung = '';
|
||||
let bearbeiteId: number | null = null;
|
||||
let isLoading = true;
|
||||
|
||||
async function ladeDienststellen() {
|
||||
try {
|
||||
isLoading = true;
|
||||
fehlermeldung = '';
|
||||
|
||||
const res = await fetch('/api/admin/dienststellen');
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(`Fehler beim Laden: ${res.status}`);
|
||||
}
|
||||
|
||||
dienststellen = await res.json();
|
||||
} catch (err) {
|
||||
fehlermeldung = err instanceof Error ? err.message : 'Unbekannter Fehler';
|
||||
console.error('Fehler beim Laden der Dienststellen:', err);
|
||||
} finally {
|
||||
isLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
function bearbeiten(d: { id: number; name: string; plaetze: number }) {
|
||||
@@ -19,8 +38,12 @@
|
||||
|
||||
async function speichern() {
|
||||
fehlermeldung = '';
|
||||
if (!neuerName.trim()) return;
|
||||
if (!neuerName.trim()) {
|
||||
fehlermeldung = 'Name ist erforderlich';
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const method = bearbeiteId ? 'PATCH' : 'POST';
|
||||
const body = bearbeiteId
|
||||
? { id: bearbeiteId, name: neuerName, plaetze: neuePlaetze }
|
||||
@@ -41,110 +64,181 @@
|
||||
const err = await res.json();
|
||||
fehlermeldung = err.error || 'Fehler beim Speichern';
|
||||
}
|
||||
} catch (err) {
|
||||
fehlermeldung = err instanceof Error ? err.message : 'Fehler beim Speichern';
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
async function loeschen(id: number) {
|
||||
if (!confirm('Diese Dienststelle wirklich löschen?')) return;
|
||||
await fetch(`/api/admin/dienststellen?id=${id}`, { method: 'DELETE' });
|
||||
|
||||
try {
|
||||
const res = await fetch(`/api/admin/dienststellen?id=${id}`, { method: 'DELETE' });
|
||||
|
||||
if (!res.ok) {
|
||||
const err = await res.json();
|
||||
fehlermeldung = err.error || 'Fehler beim Löschen';
|
||||
return;
|
||||
}
|
||||
|
||||
await ladeDienststellen();
|
||||
} catch (err) {
|
||||
fehlermeldung = err instanceof Error ? err.message : 'Fehler beim Löschen';
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
neuerName = '';
|
||||
neuePlaetze = 0;
|
||||
bearbeiteId = null;
|
||||
}
|
||||
|
||||
onMount(ladeDienststellen);
|
||||
</script>
|
||||
|
||||
|
||||
<div class="p-6 max-w-6xl mx-auto space-y-8">
|
||||
<svelte:head>
|
||||
<title>Dienststellen verwalten - Admin</title>
|
||||
</svelte:head>
|
||||
|
||||
|
||||
<!-- Eingabefelder -->
|
||||
<div class="flex flex-wrap gap-4 items-center">
|
||||
<input
|
||||
bind:value={neuerName}
|
||||
placeholder="Dienststelle"
|
||||
<div class="min-h-screen bg-gray-50">
|
||||
<AdminHeader
|
||||
title="Dienststellen verwalten"
|
||||
showBackButton={true}
|
||||
/>
|
||||
|
||||
<main class="max-w-7xl mx-auto px-4 py-6">
|
||||
{#if fehlermeldung}
|
||||
<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">{fehlermeldung}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Eingabeformular -->
|
||||
<div class="bg-white shadow-sm rounded-lg p-6 mb-6">
|
||||
<h2 class="text-lg font-medium text-gray-900 mb-4">
|
||||
{bearbeiteId !== null ? 'Dienststelle bearbeiten' : 'Neue Dienststelle hinzufügen'}
|
||||
</h2>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div>
|
||||
<label for="name" class="block text-sm font-medium text-gray-700 mb-2">
|
||||
Dienststelle
|
||||
</label>
|
||||
<input
|
||||
id="name"
|
||||
type="text"
|
||||
bind:value={neuerName}
|
||||
placeholder="Name der Dienststelle"
|
||||
class="w-full border border-gray-300 rounded-md px-3 py-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="plaetze" class="block text-sm font-medium text-gray-700 mb-2">
|
||||
Anzahl Plätze
|
||||
</label>
|
||||
<input
|
||||
id="plaetze"
|
||||
type="number"
|
||||
bind:value={neuePlaetze}
|
||||
bind:value={neuePlaetze}
|
||||
placeholder="Anzahl Plätze"
|
||||
placeholder="0"
|
||||
min="0"
|
||||
class="w-full border border-gray-300 rounded-md px-3 py-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex items-end gap-2">
|
||||
<button
|
||||
<button
|
||||
on:click={() => {
|
||||
neuerName = '';
|
||||
neuePlaetze = 0;
|
||||
bearbeiteId = null;
|
||||
}}
|
||||
on:click={resetForm}
|
||||
class="px-4 py-2 text-sm text-gray-600 hover:text-gray-800 hover:underline"
|
||||
>
|
||||
Zurücksetzen
|
||||
</button>
|
||||
<button
|
||||
on:click={speichern}
|
||||
on:click={speichern}
|
||||
class="bg-blue-600 hover:bg-blue-700 text-white px-6 py-2 rounded-md text-sm font-medium"
|
||||
>
|
||||
{bearbeiteId !== null ? 'Ändern' : 'Hinzufügen'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Fehlermeldung -->
|
||||
{#if fehlermeldung}
|
||||
<p class="text-red-600 text-sm">{fehlermeldung}</p>
|
||||
{/if}
|
||||
|
||||
<!-- Tabellenähnliche Anzeige -->
|
||||
<div class="overflow-x-auto">
|
||||
<div class="min-w-[600px] divide-y border rounded">
|
||||
<!-- Kopfzeile -->
|
||||
<div class="grid grid-cols-3 gap-x-8 font-semibold text-sm bg-gray-100 p-2">
|
||||
<div>Dienststelle</div>
|
||||
<div class="text-right">Plätze</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{#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 Dienststellen...</span>
|
||||
</div>
|
||||
{:else if dienststellen.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="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-4m-5 0H9m0 0H7m2 0v-5a2 2 0 012-2h2a2 2 0 012 2v5M7 7h.01M7 11h.01M11 7h.01M11 11h.01" />
|
||||
</svg>
|
||||
<h3 class="mt-2 text-sm font-medium text-gray-900">Keine Dienststellen</h3>
|
||||
<p class="mt-1 text-sm text-gray-500">Erstellen Sie Ihre erste Dienststelle über das Formular oben.</p>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="bg-white shadow-sm rounded-lg overflow-hidden">
|
||||
<div class="px-6 py-4 border-b border-gray-200">
|
||||
<h3 class="text-lg font-medium text-gray-900">Alle Dienststellen</h3>
|
||||
</div>
|
||||
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Dienststelle
|
||||
</th>
|
||||
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Plätze
|
||||
</th>
|
||||
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Aktionen
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white divide-y divide-gray-200">
|
||||
{#each dienststellen as d}
|
||||
{#each dienststellen as d}
|
||||
<div class="grid grid-cols-3 gap-x-8 items-center p-2 text-sm">
|
||||
<div>{d.name}</div>
|
||||
<div class="text-right">{d.plaetze}</div>
|
||||
<tr class="hover:bg-gray-50">
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
|
||||
{d.name}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 text-right">
|
||||
{d.plaetze}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||
<button
|
||||
<button
|
||||
on:click={() => {
|
||||
neuerName = d.name;
|
||||
neuePlaetze = d.plaetze;
|
||||
bearbeiteId = d.id;
|
||||
}}
|
||||
on:click={() => bearbeiten(d)}
|
||||
class="text-blue-600 hover:text-blue-900 mr-4"
|
||||
>
|
||||
Bearbeiten
|
||||
</button>
|
||||
<button
|
||||
on:click={() => loeschen(d.id)}
|
||||
on:click={() => loeschen(d.id)}
|
||||
class="text-red-600 hover:text-red-900"
|
||||
>
|
||||
Löschen
|
||||
</button>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Logout-Button -->
|
||||
<div class="pt-4 text-center">
|
||||
<button
|
||||
on:click={async () => {
|
||||
await fetch('/api/admin/logout', { method: 'POST' });
|
||||
location.reload();
|
||||
}}
|
||||
class="bg-red-600 hover:bg-red-700 text-white px-6 py-2 rounded"
|
||||
>
|
||||
Logout
|
||||
{/if}
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<style>
|
||||
/* You can add custom styles here if needed, or rely on Tailwind classes in your markup */
|
||||
|
||||
@@ -1,41 +1,63 @@
|
||||
<!-- src/routes/admin/zeitraeume/+page.svelte -->
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte'
|
||||
let bezeichnung = '';
|
||||
let startDatum = '';
|
||||
let endDatum = '';
|
||||
import { onMount } from 'svelte';
|
||||
import AdminHeader from '$lib/components/AdminHeader.svelte';
|
||||
|
||||
interface Zeitraum {
|
||||
id: number;
|
||||
bezeichnung: string;
|
||||
startDatum: string;
|
||||
endDatum: string;
|
||||
}
|
||||
|
||||
let zeitraeume: Zeitraum[] = [];
|
||||
let neuerBezeichnung = '';
|
||||
let neuerstartDatum = '';
|
||||
let neuerendDatum = '';
|
||||
let fehlermeldung = '';
|
||||
let bearbeiteId: number | null = null;
|
||||
let isLoading = true;
|
||||
|
||||
async function ladeZeitraeume() {
|
||||
try {
|
||||
isLoading = true;
|
||||
fehlermeldung = '';
|
||||
|
||||
const res = await fetch('/api/admin/zeitraeume');
|
||||
zeitraeume = await res.json();
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(`Fehler beim Laden: ${res.status}`);
|
||||
}
|
||||
|
||||
function bearbeiten(d: { id: number; bezeichnung: string; startDatum: Date; endDatum: Date }) {
|
||||
zeitraeume = await res.json();
|
||||
} catch (err) {
|
||||
fehlermeldung = err instanceof Error ? err.message : 'Unbekannter Fehler';
|
||||
console.error('Fehler beim Laden der Zeiträume:', err);
|
||||
} finally {
|
||||
isLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
function bearbeiten(d: { id: number; bezeichnung: string; startDatum: string; endDatum: string }) {
|
||||
neuerBezeichnung = d.bezeichnung;
|
||||
neuerstartDatum = d.startDatum instanceof Date
|
||||
? d.startDatum.toISOString().slice(0, 10)
|
||||
: d.startDatum;
|
||||
neuerendDatum = d.endDatum instanceof Date
|
||||
? d.endDatum.toISOString().slice(0, 10)
|
||||
: d.endDatum;
|
||||
neuerstartDatum = d.startDatum ? d.startDatum.slice(0, 10) : '';
|
||||
neuerendDatum = d.endDatum ? d.endDatum.slice(0, 10) : '';
|
||||
bearbeiteId = d.id;
|
||||
}
|
||||
|
||||
async function speichern() {
|
||||
fehlermeldung = '';
|
||||
if (!neuerBezeichnung.trim()) return;
|
||||
if (!neuerBezeichnung.trim()) {
|
||||
fehlermeldung = 'Bezeichnung ist erforderlich';
|
||||
return;
|
||||
}
|
||||
|
||||
if (!neuerstartDatum || !neuerendDatum) {
|
||||
fehlermeldung = 'Start- und Enddatum sind erforderlich';
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const method = bearbeiteId ? 'PATCH' : 'POST';
|
||||
const body = bearbeiteId
|
||||
? { id: bearbeiteId, bezeichnung: neuerBezeichnung, startDatum: neuerstartDatum, endDatum: neuerendDatum }
|
||||
@@ -57,119 +79,206 @@
|
||||
const err = await res.json();
|
||||
fehlermeldung = err.error || 'Fehler beim Speichern';
|
||||
}
|
||||
} catch (err) {
|
||||
fehlermeldung = err instanceof Error ? err.message : 'Fehler beim Speichern';
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
async function loeschen(id: number) {
|
||||
if (!confirm('Diese Zeitraum wirklich löschen?')) return;
|
||||
await fetch(`/api/admin/zeitraeume?id=${id}`, { method: 'DELETE' });
|
||||
await ladeZeitraeume();
|
||||
if (!confirm('Diesen Zeitraum wirklich löschen?')) return;
|
||||
|
||||
try {
|
||||
const res = await fetch(`/api/admin/zeitraeume?id=${id}`, { method: 'DELETE' });
|
||||
|
||||
if (!res.ok) {
|
||||
const err = await res.json();
|
||||
fehlermeldung = err.error || 'Fehler beim Löschen';
|
||||
return;
|
||||
}
|
||||
|
||||
onMount(ladeZeitraeume);
|
||||
await ladeZeitraeume();
|
||||
} catch (err) {
|
||||
fehlermeldung = err instanceof Error ? err.message : 'Fehler beim Löschen';
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<div class="p-6 max-w-6xl mx-auto space-y-8">
|
||||
<h1 class="text-2xl font-bold text-center">Praktikumszeiträume verwalten</h1>
|
||||
|
||||
<!-- Eingabefelder -->
|
||||
<div class="flex flex-wrap gap-4 items-center">
|
||||
<input
|
||||
bind:value={neuerBezeichnung}
|
||||
placeholder="Bezeichnung"
|
||||
class="input w-full sm:w-[35%] border rounded px-3 py-2"
|
||||
/>
|
||||
<input
|
||||
type="date"
|
||||
bind:value={neuerstartDatum}
|
||||
placeholder="Startdatum"
|
||||
class="input w-full sm:w-[20%] border rounded px-3 py-2"
|
||||
/>
|
||||
<input
|
||||
type="date"
|
||||
bind:value={neuerendDatum}
|
||||
placeholder="Enddatum"
|
||||
class="input w-full sm:w-[20%] border rounded px-3 py-2"
|
||||
/>
|
||||
<button
|
||||
on:click={() => {
|
||||
function resetForm() {
|
||||
neuerBezeichnung = '';
|
||||
neuerstartDatum = '';
|
||||
neuerendDatum = '';
|
||||
bearbeiteId = null;
|
||||
}}
|
||||
class="text-sm text-gray-500 hover:underline"
|
||||
}
|
||||
|
||||
onMount(ladeZeitraeume);
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Praktikumszeiträume verwalten - Admin</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="min-h-screen bg-gray-50">
|
||||
<AdminHeader
|
||||
title="Praktikumszeiträume verwalten"
|
||||
showBackButton={true}
|
||||
/>
|
||||
|
||||
<main class="max-w-7xl mx-auto px-4 py-6">
|
||||
{#if fehlermeldung}
|
||||
<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">{fehlermeldung}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Eingabeformular -->
|
||||
<div class="bg-white shadow-sm rounded-lg p-6 mb-6">
|
||||
<h2 class="text-lg font-medium text-gray-900 mb-4">
|
||||
{bearbeiteId !== null ? 'Praktikumszeitraum bearbeiten' : 'Neuen Praktikumszeitraum hinzufügen'}
|
||||
</h2>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||
<div>
|
||||
<label for="bezeichnung" class="block text-sm font-medium text-gray-700 mb-2">
|
||||
Bezeichnung
|
||||
</label>
|
||||
<input
|
||||
id="bezeichnung"
|
||||
type="text"
|
||||
bind:value={neuerBezeichnung}
|
||||
placeholder="z.B. Sommerpraktikum 2024"
|
||||
class="w-full border border-gray-300 rounded-md px-3 py-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="startdatum" class="block text-sm font-medium text-gray-700 mb-2">
|
||||
Startdatum
|
||||
</label>
|
||||
<input
|
||||
id="startdatum"
|
||||
type="date"
|
||||
bind:value={neuerstartDatum}
|
||||
class="w-full border border-gray-300 rounded-md px-3 py-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="enddatum" class="block text-sm font-medium text-gray-700 mb-2">
|
||||
Enddatum
|
||||
</label>
|
||||
<input
|
||||
id="enddatum"
|
||||
type="date"
|
||||
bind:value={neuerendDatum}
|
||||
class="w-full border border-gray-300 rounded-md px-3 py-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex items-end gap-2">
|
||||
<button
|
||||
on:click={resetForm}
|
||||
class="px-4 py-2 text-sm text-gray-600 hover:text-gray-800 hover:underline"
|
||||
>
|
||||
Zurücksetzen
|
||||
</button>
|
||||
<button
|
||||
on:click={speichern}
|
||||
class="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700 text-sm"
|
||||
class="bg-blue-600 hover:bg-blue-700 text-white px-6 py-2 rounded-md text-sm font-medium"
|
||||
>
|
||||
{bearbeiteId !== null ? 'Ändern' : 'Hinzufügen'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Fehlermeldung -->
|
||||
{#if fehlermeldung}
|
||||
<p class="text-red-600 text-sm">{fehlermeldung}</p>
|
||||
{/if}
|
||||
|
||||
<!-- Tabellenähnliche Anzeige -->
|
||||
<div class="overflow-x-auto">
|
||||
<div class="min-w-[600px] divide-y border rounded">
|
||||
<!-- Kopfzeile -->
|
||||
<div class="grid grid-cols-4 gap-x-8 font-semibold text-sm bg-gray-100 p-2">
|
||||
<div>Bezeichnung</div>
|
||||
<div class="text-right">Startdatum</div>
|
||||
<div class="text-right">Enddatum</div>
|
||||
<div class="text-right">Aktionen</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Einträge -->
|
||||
{#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 Praktikumszeiträume...</span>
|
||||
</div>
|
||||
{:else if zeitraeume.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="M8 7V3a4 4 0 118 0v4m-4 6v6m-1 0h2m-1 0V9a4 4 0 00-8 0v2M7 9h2m-2 0v6" />
|
||||
</svg>
|
||||
<h3 class="mt-2 text-sm font-medium text-gray-900">Keine Praktikumszeiträume</h3>
|
||||
<p class="mt-1 text-sm text-gray-500">Erstellen Sie Ihren ersten Praktikumszeitraum über das Formular oben.</p>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="bg-white shadow-sm rounded-lg overflow-hidden">
|
||||
<div class="px-6 py-4 border-b border-gray-200">
|
||||
<h3 class="text-lg font-medium text-gray-900">Alle Praktikumszeiträume</h3>
|
||||
</div>
|
||||
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Bezeichnung
|
||||
</th>
|
||||
<th class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Startdatum
|
||||
</th>
|
||||
<th class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Enddatum
|
||||
</th>
|
||||
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Aktionen
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white divide-y divide-gray-200">
|
||||
{#each zeitraeume as d}
|
||||
<div class="grid grid-cols-4 gap-x-8 items-center p-2 text-sm">
|
||||
<div>{d.bezeichnung}</div>
|
||||
<div class="text-right">{new Date(d.startDatum).toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' })}</div>
|
||||
<div class="text-right">{new Date(d.endDatum).toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' })}</div>
|
||||
<div class="flex justify-end gap-3">
|
||||
<tr class="hover:bg-gray-50">
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
|
||||
{d.bezeichnung}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 text-center">
|
||||
{new Date(d.startDatum).toLocaleDateString('de-DE', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric'
|
||||
})}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 text-center">
|
||||
{new Date(d.endDatum).toLocaleDateString('de-DE', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric'
|
||||
})}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||
<button
|
||||
on:click={() => {
|
||||
neuerBezeichnung = d.bezeichnung;
|
||||
//neuerstartDatum = d.startDatum;
|
||||
neuerstartDatum = d.startDatum ? d.startDatum.slice(0, 10) : '';
|
||||
neuerendDatum = d.endDatum ? d.endDatum.slice(0, 10) : '';
|
||||
bearbeiteId = d.id;
|
||||
}}
|
||||
class="text-blue-600 hover:underline"
|
||||
on:click={() => bearbeiten(d)}
|
||||
class="text-blue-600 hover:text-blue-900 mr-4"
|
||||
>
|
||||
Bearbeiten
|
||||
</button>
|
||||
<button
|
||||
on:click={() => loeschen(d.id)}
|
||||
class="text-red-600 hover:underline"
|
||||
class="text-red-600 hover:text-red-900"
|
||||
>
|
||||
Löschen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Logout-Button -->
|
||||
<div class="pt-4 text-center">
|
||||
<button
|
||||
on:click={async () => {
|
||||
await fetch('/api/admin/logout', { method: 'POST' });
|
||||
location.reload();
|
||||
}}
|
||||
class="bg-red-600 hover:bg-red-700 text-white px-6 py-2 rounded"
|
||||
>
|
||||
Logout
|
||||
</button>
|
||||
{/if}
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
@@ -6,8 +6,9 @@ const prisma = new PrismaClient();
|
||||
|
||||
import type { Cookies } from '@sveltejs/kit';
|
||||
|
||||
// Korrigierte Auth-Funktion mit neuem Cookie-Namen
|
||||
function checkAuth(cookies: Cookies) {
|
||||
return cookies.get('admin_session') === 'true';
|
||||
return cookies.get('admin-auth') === 'authenticated';
|
||||
}
|
||||
|
||||
function isValidDate(date: string | Date) {
|
||||
@@ -16,73 +17,144 @@ function isValidDate(date: string | Date) {
|
||||
}
|
||||
|
||||
export const GET: RequestHandler = async ({ cookies }) => {
|
||||
if (!checkAuth(cookies)) return new Response('Nicht erlaubt', { status: 401 });
|
||||
if (!checkAuth(cookies)) {
|
||||
return new Response(
|
||||
JSON.stringify({ error: 'Nicht autorisiert' }),
|
||||
{
|
||||
status: 401,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const zeitraeume = await prisma.praktikumszeitraum.findMany();
|
||||
return json(zeitraeume);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Praktikumszeiträume:', error);
|
||||
return json({ error: 'Fehler beim Laden der Praktikumszeiträume' }, { status: 500 });
|
||||
}
|
||||
};
|
||||
|
||||
export const POST: RequestHandler = async ({ cookies, request }) => {
|
||||
if (!checkAuth(cookies)) return new Response('Nicht erlaubt', { status: 401 });
|
||||
const { bezeichnung, startDatum, endDatum } = await request.json();
|
||||
if (!isValidDate(startDatum) || !isValidDate(endDatum)) {
|
||||
return json({ error: 'Ungültige Datum' }, { status: 400 });
|
||||
if (!checkAuth(cookies)) {
|
||||
return json({ error: 'Nicht autorisiert' }, { status: 401 });
|
||||
}
|
||||
|
||||
try {
|
||||
const created = await prisma.praktikumszeitraum.create({ data: {
|
||||
bezeichnung,
|
||||
const { bezeichnung, startDatum, endDatum } = await request.json();
|
||||
|
||||
// Validierung
|
||||
if (!bezeichnung || typeof bezeichnung !== 'string' || bezeichnung.trim().length === 0) {
|
||||
return json({ error: 'Bezeichnung ist erforderlich' }, { status: 400 });
|
||||
}
|
||||
|
||||
if (!isValidDate(startDatum) || !isValidDate(endDatum)) {
|
||||
return json({ error: 'Ungültiges Datum' }, { status: 400 });
|
||||
}
|
||||
|
||||
const created = await prisma.praktikumszeitraum.create({
|
||||
data: {
|
||||
bezeichnung: bezeichnung.trim(),
|
||||
startDatum: new Date(startDatum),
|
||||
endDatum: new Date(endDatum)
|
||||
} });
|
||||
return json(created);
|
||||
} catch (e) {
|
||||
console.error('Fehler beim Hinzufuegen:', e);
|
||||
return json({ error: 'Zeitraum existiert bereits' }, { status: 400 });
|
||||
}
|
||||
});
|
||||
return json(created, { status: 201 });
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Erstellen des Praktikumszeitraums:', error);
|
||||
return json({ error: 'Fehler beim Erstellen des Praktikumszeitraums' }, { status: 500 });
|
||||
}
|
||||
};
|
||||
|
||||
export const PATCH: RequestHandler = async ({ cookies, request }) => {
|
||||
if (!checkAuth(cookies)) return new Response('Nicht erlaubt', { status: 401 });
|
||||
|
||||
const { id, bezeichnung, startDatum, endDatum } = await request.json();
|
||||
|
||||
if (typeof id !== 'number' || isNaN(id) || !bezeichnung || !isValidDate(startDatum) || !isValidDate(endDatum)) {
|
||||
return json({ error: 'Ungültige Eingabedaten' }, { status: 400 });
|
||||
}
|
||||
|
||||
const existing = await prisma.praktikumszeitraum.findUnique({ where: { id } });
|
||||
if (!existing) {
|
||||
return json({ error: 'Zeitraum nicht gefunden' }, { status: 404 });
|
||||
}
|
||||
|
||||
const konflikt = await prisma.praktikumszeitraum.findFirst({
|
||||
where: {
|
||||
bezeichnung,
|
||||
NOT: { id },
|
||||
},
|
||||
});
|
||||
if (konflikt) {
|
||||
return json({ error: 'Eine andere Praktikumszeitraum mit diesem Namen existiert bereits' }, { status: 400 });
|
||||
if (!checkAuth(cookies)) {
|
||||
return json({ error: 'Nicht autorisiert' }, { status: 401 });
|
||||
}
|
||||
|
||||
try {
|
||||
const { id, bezeichnung, startDatum, endDatum } = await request.json();
|
||||
|
||||
// Validierung
|
||||
if (typeof id !== 'number' || isNaN(id)) {
|
||||
return json({ error: 'Ungültige ID' }, { status: 400 });
|
||||
}
|
||||
|
||||
if (!bezeichnung || typeof bezeichnung !== 'string' || bezeichnung.trim().length === 0) {
|
||||
return json({ error: 'Bezeichnung ist erforderlich' }, { status: 400 });
|
||||
}
|
||||
|
||||
if (!isValidDate(startDatum) || !isValidDate(endDatum)) {
|
||||
return json({ error: 'Ungültiges Datum' }, { status: 400 });
|
||||
}
|
||||
|
||||
// Prüfe ob Praktikumszeitraum existiert
|
||||
const existing = await prisma.praktikumszeitraum.findUnique({ where: { id } });
|
||||
if (!existing) {
|
||||
return json({ error: 'Praktikumszeitraum nicht gefunden' }, { status: 404 });
|
||||
}
|
||||
|
||||
// Prüfe ob neue Bezeichnung bereits bei anderem Zeitraum existiert
|
||||
const konflikt = await prisma.praktikumszeitraum.findFirst({
|
||||
where: {
|
||||
bezeichnung: bezeichnung.trim(),
|
||||
NOT: { id },
|
||||
},
|
||||
});
|
||||
|
||||
if (konflikt) {
|
||||
return json({ error: 'Ein anderer Praktikumszeitraum mit dieser Bezeichnung existiert bereits' }, { status: 400 });
|
||||
}
|
||||
|
||||
const updated = await prisma.praktikumszeitraum.update({
|
||||
where: { id },
|
||||
data: {
|
||||
bezeichnung,
|
||||
bezeichnung: bezeichnung.trim(),
|
||||
startDatum: new Date(startDatum),
|
||||
endDatum: new Date(endDatum)
|
||||
},
|
||||
});
|
||||
|
||||
return json(updated);
|
||||
} catch (e) {
|
||||
console.error('Fehler beim Update:', e);
|
||||
return json({ error: 'Update fehlgeschlagen' }, { status: 400 });
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Aktualisieren des Praktikumszeitraums:', error);
|
||||
return json({ error: 'Fehler beim Aktualisieren des Praktikumszeitraums' }, { status: 500 });
|
||||
}
|
||||
};
|
||||
|
||||
export const DELETE: RequestHandler = async ({ cookies, url }) => {
|
||||
if (!checkAuth(cookies)) return new Response('Nicht erlaubt', { status: 401 });
|
||||
if (!checkAuth(cookies)) {
|
||||
return json({ error: 'Nicht autorisiert' }, { status: 401 });
|
||||
}
|
||||
|
||||
const id = Number(url.searchParams.get('id'));
|
||||
|
||||
if (isNaN(id)) {
|
||||
return json({ error: 'Ungültige ID' }, { status: 400 });
|
||||
}
|
||||
|
||||
try {
|
||||
// Prüfe ob Praktikumszeitraum existiert
|
||||
const existing = await prisma.praktikumszeitraum.findUnique({ where: { id } });
|
||||
if (!existing) {
|
||||
return json({ error: 'Praktikumszeitraum nicht gefunden' }, { status: 404 });
|
||||
}
|
||||
|
||||
// Hier könntest du prüfen, ob noch Anmeldungen mit diesem Zeitraum verknüpft sind
|
||||
// const assignedCount = await prisma.anmeldung.count({
|
||||
// where: { praktikumszeitraumId: id }
|
||||
// });
|
||||
//
|
||||
// if (assignedCount > 0) {
|
||||
// return json({
|
||||
// error: 'Praktikumszeitraum kann nicht gelöscht werden. Es sind noch Anmeldungen damit verknüpft.'
|
||||
// }, { status: 400 });
|
||||
// }
|
||||
|
||||
await prisma.praktikumszeitraum.delete({ where: { id } });
|
||||
return json({ success: true });
|
||||
return json({ success: true, message: 'Praktikumszeitraum erfolgreich gelöscht' });
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Löschen des Praktikumszeitraums:', error);
|
||||
return json({ error: 'Fehler beim Löschen des Praktikumszeitraums' }, { status: 500 });
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user