146 lines
5.4 KiB
TypeScript
146 lines
5.4 KiB
TypeScript
import { format } from "date-fns";
|
|
import { de } from "date-fns/locale";
|
|
import { FileText, Megaphone, Send } 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="mx-auto max-w-[1280px] px-11 py-9 pb-16 max-[760px]:px-5">
|
|
<header className="mb-7 flex items-end justify-between gap-8 max-[900px]:flex-col max-[900px]:items-start">
|
|
<div>
|
|
<p className="text-[11px] font-semibold uppercase text-[var(--accent-deep)] [letter-spacing:0.18em]">
|
|
KOMMUNIKATION · SCHWARZES BRETT
|
|
</p>
|
|
<h1 className="mt-2 text-[36px] font-normal leading-tight tracking-[-0.025em] text-[var(--ink)]">
|
|
Meldungen{" "}
|
|
<em className="font-normal italic text-[var(--accent-deep)]">
|
|
veröffentlichen
|
|
</em>
|
|
</h1>
|
|
<p className="mt-2 max-w-2xl text-[15px] leading-6 text-[var(--ink-soft)]">
|
|
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>
|
|
<span className="inline-flex h-9 items-center gap-2 rounded-full border border-[var(--hairline)] bg-[var(--surface)] px-4 text-[13px] font-semibold text-[var(--ink-soft)] tabular-nums">
|
|
<Megaphone className="h-4 w-4 text-[var(--accent-deep)] [stroke-width:1.7]" />
|
|
{announcements.length} zuletzt
|
|
</span>
|
|
</header>
|
|
|
|
<div className="grid gap-6 xl:grid-cols-[minmax(0,1fr)_380px]">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center gap-2">
|
|
<Send className="h-4 w-4 text-[var(--accent-deep)] [stroke-width:1.7]" />
|
|
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 ? (
|
|
<div className="flex max-h-[280px] min-h-[220px] flex-col items-center justify-center rounded-lg border border-dashed border-[var(--hairline)] bg-[var(--surface-2)] p-6 text-center">
|
|
<div className="mb-4 flex h-12 w-12 items-center justify-center rounded-2xl bg-[var(--accent-soft)] text-[var(--accent-deep)]">
|
|
<Megaphone className="h-6 w-6 [stroke-width:1.7]" />
|
|
</div>
|
|
<p className="text-sm font-medium text-[var(--ink)]">
|
|
Noch keine Ankündigungen
|
|
</p>
|
|
<p className="mt-1 text-sm text-[var(--ink-soft)]">
|
|
Veröffentlichte Meldungen erscheinen hier.
|
|
</p>
|
|
</div>
|
|
) : (
|
|
announcements.map((announcement) => (
|
|
<div
|
|
key={announcement.id}
|
|
className="rounded-lg border border-[var(--hairline)] bg-[var(--surface-2)] p-3"
|
|
>
|
|
<div className="mb-2 flex items-start justify-between gap-2">
|
|
<p className="font-medium leading-snug text-[var(--ink)]">
|
|
{announcement.title}
|
|
</p>
|
|
{announcement.attachments.length > 0 && (
|
|
<Badge variant="secondary" className="shrink-0">
|
|
<FileText className="h-3 w-3 [stroke-width:1.7]" />
|
|
{announcement.attachments.length}
|
|
</Badge>
|
|
)}
|
|
</div>
|
|
<p className="text-xs text-[var(--ink-muted)]">
|
|
{format(announcement.createdAt, "dd.MM.yyyy HH:mm", {
|
|
locale: de,
|
|
})}{" "}
|
|
· {announcement.author.firstName}{" "}
|
|
{announcement.author.lastName}
|
|
</p>
|
|
</div>
|
|
))
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|