continued the kita-planer

This commit is contained in:
t.indorf
2026-05-08 14:32:14 +02:00
parent b686e714ff
commit 7aff691803
85 changed files with 9434 additions and 588 deletions
+126
View File
@@ -0,0 +1,126 @@
import { format } from "date-fns";
import { de } from "date-fns/locale";
import { FileText, Megaphone } from "lucide-react";
import { UserRole } from "@prisma/client";
import { requireRole } from "@/lib/auth-utils";
import { prisma } from "@/lib/prisma";
import { Badge } from "@/components/ui/badge";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { NewsForm } from "./news-form";
export const metadata = { title: "Schwarzes Brett · Kita-Planer" };
export default async function AdminNewsPage() {
const session = await requireRole([UserRole.ADMIN]);
const kitaId = session.user.kitaId;
if (!kitaId) {
throw new Error("Kein Mandant zugeordnet.");
}
const announcements = await prisma.announcement.findMany({
where: { kitaId },
select: {
id: true,
title: true,
createdAt: true,
attachments: {
select: {
id: true,
fileName: true,
},
},
author: {
select: {
firstName: true,
lastName: true,
},
},
},
orderBy: { createdAt: "desc" },
take: 8,
});
return (
<div className="px-8 py-8">
<div className="mb-8">
<Badge className="mb-3 bg-emerald-100 text-emerald-800 hover:bg-emerald-100">
Offizielle Kita-Kommunikation
</Badge>
<h1 className="flex items-center gap-3 text-2xl font-semibold tracking-tight">
<Megaphone className="h-6 w-6 text-emerald-700" />
Digitales Schwarzes Brett
</h1>
<p className="mt-2 max-w-2xl text-sm leading-6 text-muted-foreground">
Veröffentliche verbindliche Ankündigungen für deine Kita. Anhänge
werden geschützt gespeichert und nur für eingeloggte Nutzer derselben
Kita ausgeliefert.
</p>
</div>
<div className="grid gap-6 xl:grid-cols-[minmax(0,1fr)_380px]">
<Card>
<CardHeader>
<CardTitle>Neue Ankündigung</CardTitle>
<CardDescription>
Markdown wird sicher gerendert; PDF, JPG und PNG können als
geschützte Anhänge hinzugefügt werden.
</CardDescription>
</CardHeader>
<CardContent>
<NewsForm />
</CardContent>
</Card>
<Card className="h-fit">
<CardHeader>
<CardTitle className="text-base">Zuletzt veröffentlicht</CardTitle>
<CardDescription>
Die neuesten Meldungen auf dem Schwarzen Brett.
</CardDescription>
</CardHeader>
<CardContent className="grid gap-3">
{announcements.length === 0 ? (
<p className="rounded-md border border-dashed p-4 text-sm text-muted-foreground">
Noch keine Ankündigungen veröffentlicht.
</p>
) : (
announcements.map((announcement) => (
<div
key={announcement.id}
className="rounded-md border bg-background p-3"
>
<div className="mb-2 flex items-start justify-between gap-2">
<p className="font-medium leading-snug">
{announcement.title}
</p>
{announcement.attachments.length > 0 && (
<Badge variant="secondary" className="shrink-0">
<FileText className="h-3 w-3" />
{announcement.attachments.length}
</Badge>
)}
</div>
<p className="text-xs text-muted-foreground">
{format(announcement.createdAt, "dd.MM.yyyy HH:mm", {
locale: de,
})}{" "}
· {announcement.author.firstName}{" "}
{announcement.author.lastName}
</p>
</div>
))
)}
</CardContent>
</Card>
</div>
</div>
);
}