78 lines
3.3 KiB
TypeScript
78 lines
3.3 KiB
TypeScript
import React from 'react';
|
|
import { useNavigate, useSearchParams } from 'react-router-dom';
|
|
import { getEvent, getEventStats, toggleEvent, createInviteLink } from '../api';
|
|
import { Button } from '@/components/ui/button';
|
|
|
|
export default function EventDetailPage() {
|
|
const [sp] = useSearchParams();
|
|
const id = Number(sp.get('id'));
|
|
const nav = useNavigate();
|
|
const [ev, setEv] = React.useState<any | null>(null);
|
|
const [stats, setStats] = React.useState<{ total: number; featured: number; likes: number } | null>(null);
|
|
const [invite, setInvite] = React.useState<string | null>(null);
|
|
|
|
async function load() {
|
|
const e = await getEvent(id);
|
|
setEv(e);
|
|
setStats(await getEventStats(id));
|
|
}
|
|
React.useEffect(() => { load(); }, [id]);
|
|
|
|
async function onToggle() {
|
|
const isActive = await toggleEvent(id);
|
|
setEv((o: any) => ({ ...(o || {}), is_active: isActive }));
|
|
}
|
|
|
|
async function onInvite() {
|
|
const link = await createInviteLink(id);
|
|
setInvite(link);
|
|
try { await navigator.clipboard.writeText(link); } catch {}
|
|
}
|
|
|
|
if (!ev) return <div className="p-4">Lade…</div>;
|
|
const joinLink = `${location.origin}/e/${ev.slug}`;
|
|
const qrUrl = `/admin/qr?data=${encodeURIComponent(joinLink)}`;
|
|
|
|
return (
|
|
<div className="mx-auto max-w-3xl p-4 space-y-4">
|
|
<div className="flex items-center justify-between">
|
|
<h1 className="text-lg font-semibold">Event: {renderName(ev.name)}</h1>
|
|
<div className="flex gap-2">
|
|
<Button variant="secondary" onClick={onToggle}>{ev.is_active ? 'Deaktivieren' : 'Aktivieren'}</Button>
|
|
<Button variant="secondary" onClick={() => nav(`/admin/events/photos?id=${id}`)}>Fotos moderieren</Button>
|
|
</div>
|
|
</div>
|
|
<div className="rounded border p-3 text-sm">
|
|
<div>Slug: {ev.slug}</div>
|
|
<div>Datum: {ev.date ?? '-'}</div>
|
|
<div>Status: {ev.is_active ? 'Aktiv' : 'Inaktiv'}</div>
|
|
</div>
|
|
<div className="grid grid-cols-3 gap-3 text-center text-sm">
|
|
<div className="rounded border p-3"><div className="text-2xl font-semibold">{stats?.total ?? 0}</div><div>Fotos</div></div>
|
|
<div className="rounded border p-3"><div className="text-2xl font-semibold">{stats?.featured ?? 0}</div><div>Featured</div></div>
|
|
<div className="rounded border p-3"><div className="text-2xl font-semibold">{stats?.likes ?? 0}</div><div>Likes gesamt</div></div>
|
|
</div>
|
|
<div className="rounded border p-3">
|
|
<div className="mb-2 text-sm font-medium">Join-Link</div>
|
|
<div className="mb-2 flex items-center gap-2">
|
|
<input className="w-full rounded border p-2 text-sm" value={joinLink} readOnly />
|
|
<Button variant="secondary" onClick={() => navigator.clipboard.writeText(joinLink)}>Kopieren</Button>
|
|
</div>
|
|
<div className="mb-2 text-sm font-medium">QR</div>
|
|
<img src={qrUrl} alt="QR" width={200} height={200} className="rounded border" />
|
|
<div className="mt-3">
|
|
<Button variant="secondary" onClick={onInvite}>Einladungslink erzeugen</Button>
|
|
{invite && <div className="mt-2 text-xs text-muted-foreground">Erzeugt und kopiert: {invite}</div>}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function renderName(name: any): string {
|
|
if (typeof name === 'string') return name;
|
|
if (name && (name.de || name.en)) return name.de || name.en;
|
|
return JSON.stringify(name);
|
|
}
|
|
|