Zeitraeume Verwaltung in Admin Bereich

This commit is contained in:
titver968
2025-05-21 17:27:07 +02:00
parent 78942e95e1
commit 72055bfb4b
6 changed files with 253 additions and 74 deletions

View File

@@ -39,8 +39,8 @@
<a href="/admin/dienststellen" class="bg-green-600 text-white px-4 py-3 rounded text-center hover:bg-green-800"> <a href="/admin/dienststellen" class="bg-green-600 text-white px-4 py-3 rounded text-center hover:bg-green-800">
🏢 Dienststellen verwalten 🏢 Dienststellen verwalten
</a> </a>
<a href="/admin/zeitraum" class="bg-yellow-600 text-white px-4 py-3 rounded text-center hover:bg-yellow-800"> <a href="/admin/zeitraeume" class="bg-yellow-600 text-white px-4 py-3 rounded text-center hover:bg-yellow-800">
🗓 Zeitraum verwaltung 🗓 Praktikumzeiträume verwalten
</a> </a>
<a href="/admin/change-password" class="bg-cyan-600 text-white px-4 py-3 rounded text-center hover:bg-cyan-800"> <a href="/admin/change-password" class="bg-cyan-600 text-white px-4 py-3 rounded text-center hover:bg-cyan-800">
👨‍💼 Passwort ädern 👨‍💼 Passwort ädern

View File

@@ -0,0 +1,167 @@
<script lang="ts">
import { onMount } from 'svelte'
let bezeichnung = '';
let startDatum = '';
let endDatum = '';
let zeitraeume = [];
let neuerBezeichnung = '';
let neuerstartDatum = '';
let neuerendDatum = '';
let fehlermeldung = '';
let bearbeiteId: number | null = null;
async function ladeZeitraeume() {
const res = await fetch('/api/admin/zeitraeume');
zeitraeume = await res.json();
}
function bearbeiten(d: { id: number; bezeichnung: string; startDatum: Date; endDatum: Date }) {
neuerBezeichnung = d.bezeichnung;
neuerstartDatum = d.startDatum;
neuerendDatum = d.endDatum;
bearbeiteId = d.id;
}
async function speichern() {
fehlermeldung = '';
if (!neuerBezeichnung.trim()) return;
const method = bearbeiteId ? 'PATCH' : 'POST';
const body = bearbeiteId
? { id: bearbeiteId, bezeichnung: neuerBezeichnung, startDatum: neuerstartDatum, endDatum: neuerendDatum }
: { bezeichnung: neuerBezeichnung, startDatum: neuerstartDatum, endDatum: neuerendDatum };
const res = await fetch('/api/admin/zeitraeume', {
method,
body: JSON.stringify(body),
headers: { 'Content-Type': 'application/json' }
});
if (res.ok) {
neuerBezeichnung = '';
neuerstartDatum = '';
neuerendDatum = '';
bearbeiteId = null;
await ladeZeitraeume();
} else {
const err = await res.json();
fehlermeldung = err.error || 'Fehler beim Speichern';
}
}
async function loeschen(id: number) {
if (!confirm('Diese Zeitraum wirklich löschen?')) return;
await fetch(`/api/admin/zeitraeume?id=${id}`, { method: 'DELETE' });
await ladeZeitraeume();
}
onMount(ladeZeitraeume);
</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-[30%] border rounded px-3 py-2"
/>
<input
type="date"
bind:value={neuerstartDatum}
placeholder="Startdatum"
class="input w-full sm:w-[10%] border rounded px-3 py-2"
/>
<input
type="date"
bind:value={neuerendDatum}
placeholder="Enddatum"
class="input w-full sm:w-[10%] border rounded px-3 py-2"
/>
<button
on:click={() => {
neuerBezeichnung = '';
neuerstartDatum = '';
neuerendDatum = '';
bearbeiteId = null;
}}
class="text-sm text-gray-500 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"
>
{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>
<!-- Einträge -->
{#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">
<button
on:click={() => {
neuerBezeichnung = d.bezeichnung;
neuerstartDatum = d.startDatum;
neuerendDatum = d.endDatum;
bearbeiteId = d.id;
}}
class="text-blue-600 hover:underline"
>
Bearbeiten
</button>
<button
on:click={() => loeschen(d.id)}
class="text-red-600 hover:underline"
>
Löschen
</button>
</div>
</div>
{/each}
</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>
</div>
</div>
<style>
.input {
@apply border p-2 rounded w-full;
}
</style>

View File

@@ -1,50 +0,0 @@
<script lang="ts">
import { onMount } from 'svelte'
let bezeichnung = ''
let startDatum = ''
let endDatum = ''
let zeitraeume = []
onMount(async () => {
zeitraeume = await fetchZeitraeume()
})
async function handleSubmit() {
await createPraktikumszeitraum({ bezeichnung, startDatum, endDatum })
zeitraeume = await fetchZeitraeume()
// Felder zurücksetzen
bezeichnung = ''
startDatum = ''
endDatum = ''
}
</script>
<h1 class="text-2xl font-bold mb-4">🗓 Praktikumszeiträume verwalten</h1>
<form on:submit|preventDefault={handleSubmit} class="grid gap-4 max-w-xl bg-white p-4 rounded shadow">
<input type="text" bind:value={bezeichnung} placeholder="Bezeichnung (z.B. Frühjahr 2025)" required class="input" />
<div class="flex gap-2">
<input type="date" bind:value={startDatum} required class="input flex-1" />
<input type="date" bind:value={endDatum} required class="input flex-1" />
</div>
<button type="submit" class="bg-green-600 text-white py-2 rounded hover:bg-green-700">✅ Zeitraum speichern</button>
</form>
<h2 class="text-xl font-semibold mt-8 mb-2">📋 Bereits erfasste Zeiträume</h2>
<div class="grid gap-4">
{#each zeitraeume as z}
<div class="p-4 bg-gray-100 rounded shadow">
<h3 class="font-bold">{z.bezeichnung}</h3>
<p class="text-sm text-gray-700">
{new Date(z.startDatum).toLocaleDateString()} {new Date(z.endDatum).toLocaleDateString()}
</p>
</div>
{/each}
</div>
<style>
.input {
@apply border p-2 rounded w-full;
}
</style>

View File

@@ -0,0 +1,84 @@
import { PrismaClient } from '@prisma/client';
import { json } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
const prisma = new PrismaClient();
import type { Cookies } from '@sveltejs/kit';
function checkAuth(cookies: Cookies) {
return cookies.get('admin_session') === 'true';
}
function isValidDate(date: string | Date) {
const parsed = new Date(date)
return !isNaN(parsed.getTime())
}
export const GET: RequestHandler = async ({ cookies }) => {
if (!checkAuth(cookies)) return new Response('Nicht erlaubt', { status: 401 });
const zeitraeume = await prisma.praktikumszeitraum.findMany();
return json(zeitraeume);
};
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 });
}
try {
const created = await prisma.praktikumszeitraum.create({ data: {
bezeichnung,
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 });
}
};
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) || !name || 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 });
}
try {
const updated = await prisma.praktikumszeitraum.update({
where: { id },
data: { bezeichnung, startDatum, endDatum },
});
return json(updated);
} catch (e) {
console.error('Fehler beim Update:', e);
return json({ error: 'Update fehlgeschlagen' }, { status: 400 });
}
};
export const DELETE: RequestHandler = async ({ cookies, url }) => {
if (!checkAuth(cookies)) return new Response('Nicht erlaubt', { status: 401 });
const id = Number(url.searchParams.get('id'));
await prisma.praktikumszeitraum.delete({ where: { id } });
return json({ success: true });
};

View File

@@ -1,22 +0,0 @@
import { db } from '$lib/server/prisma'
export async function GET() {
const zeitraeume = await db.praktikumszeitraum.findMany({
orderBy: { startDatum: 'asc' }
})
return new Response(JSON.stringify(zeitraeume))
}
export async function POST({ request }) {
const { bezeichnung, startDatum, endDatum } = await request.json()
await db.praktikumszeitraum.create({
data: {
bezeichnung,
startDatum: new Date(startDatum),
endDatum: new Date(endDatum)
}
})
return new Response('OK')
}