Files
kalendartool/app/admin/calendars/[id]/page.tsx

115 lines
4.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Admin: Kalenderverwaltung (plan.md Abschnitt 13: /admin/calendars/[id]).
* Bearbeiten, deaktivieren, Invite-Links erstellen, Benutzer einsehen.
*/
import { redirect } from 'next/navigation';
import { prisma } from '@/lib/db/client';
import { requireCalendarContext } from '@/lib/api/context';
import { canManageCalendar } from '@/lib/permissions/permissions';
import { displayName } from '@/lib/privacy/display';
import { Card } from '@/components/ui/ui';
import { EditCalendarForm } from '@/components/admin/EditCalendarForm';
import { InviteManager } from '@/components/admin/InviteManager';
import { AppShell } from '@/components/layout/AppShell';
interface PageProps {
params: Promise<{ id: string }>;
}
export default async function AdminCalendarPage({ params }: PageProps) {
const { id } = await params;
const context = await requireCalendarContext(id).catch(() => null);
if (!context) {
redirect('/login');
}
if (!canManageCalendar(context.user, context.calendar)) {
redirect('/dashboard');
}
const [members, invites, reservations] = await Promise.all([
prisma.calendarMember.findMany({
where: { calendarId: id },
select: { user: { select: { email: true, username: true, createdAt: true } } },
}),
prisma.invite.findMany({
where: { calendarId: id },
orderBy: { createdAt: 'desc' },
select: { id: true, expiresAt: true, usedAt: true, createdAt: true },
}),
prisma.reservation.findMany({
where: { calendarId: id, status: 'CONFIRMED' },
orderBy: { startAt: 'desc' },
take: 50,
select: {
id: true,
title: true,
startAt: true,
endAt: true,
user: { select: { email: true, username: true } },
},
}),
]);
return (
<AppShell role={context.user.role} userEmail={context.user.email} username={context.user.username}>
<h1 className="text-2xl font-bold">{context.calendar.title} verwalten</h1>
<EditCalendarForm
calendarId={id}
initialTitle={context.calendar.title}
initialDescription={context.calendar.description ?? ''}
initialTimezone={context.calendar.timezone}
initialIsActive={context.calendar.isActive}
/>
<InviteManager calendarId={id} invites={invites} />
<Card>
<h2 className="mb-3 text-lg font-semibold">Mitglieder ({members.length})</h2>
{members.length === 0 ? (
<p className="text-sm text-slate-500">
Noch keine Mitglieder. Erstelle einen Invite-Link.
</p>
) : (
<ul className="space-y-1 text-sm">
{members.map((member) => (
<li key={member.user.email}>
{displayName(member.user.username, member.user.email)}
{member.user.username && (
<span className="ml-2 text-xs text-slate-500">
{member.user.email}
</span>
)}
</li>
))}
</ul>
)}
</Card>
<Card>
<h2 className="mb-3 text-lg font-semibold">Letzte Reservierungen</h2>
{reservations.length === 0 ? (
<p className="text-sm text-slate-500">Noch keine Reservierungen.</p>
) : (
<ul className="space-y-2 text-sm">
{reservations.map((reservation) => (
<li key={reservation.id} className="rounded bg-slate-100 px-3 py-2">
<span className="font-medium">
{reservation.startAt.toLocaleString('de-DE')} –{' '}
{reservation.endAt.toLocaleTimeString('de-DE', {
hour: '2-digit',
minute: '2-digit',
})}
</span>{' '}
{reservation.title}
<span className="ml-2 text-xs text-slate-500">
{displayName(reservation.user.username, reservation.user.email)}
</span>
</li>
))}
</ul>
)}
</Card>
</AppShell>
);
}