import React from 'react'; import { useNavigate, useParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { ArrowLeft, BarChart2, Camera, CheckCircle2, ChevronLeft, Circle, QrCode, Settings, Sparkles, Users } from 'lucide-react'; import { YStack, XStack } from '@tamagui/stacks'; import { SizableText as Text } from '@tamagui/text'; import { Button } from '@tamagui/button'; import { Pressable } from '@tamagui/react-native-web-lite'; import { AdminLayout } from '../components/AdminLayout'; import { AppCard, PrimaryCTA, StatusPill, BottomNav } from '../tamagui/primitives'; import { TenantEvent, EventStats, EventToolkit, getEvent, getEventStats, getEventToolkit, toggleEvent, } from '../api'; import { getApiErrorMessage } from '../lib/apiError'; import { isAuthError } from '../auth/tokens'; import { adminPath, ADMIN_EVENTS_PATH, ADMIN_EVENT_BRANDING_PATH, ADMIN_EVENT_INVITES_PATH, ADMIN_EVENT_MEMBERS_PATH, ADMIN_EVENT_PHOTOBOOTH_PATH, ADMIN_EVENT_PHOTOS_PATH, ADMIN_EVENT_TASKS_PATH, ADMIN_EVENT_VIEW_PATH, } from '../constants'; type DetailState = { event: TenantEvent | null; stats: EventStats | null; toolkit: EventToolkit | null; loading: boolean; busy: boolean; error: string | null; }; export default function EventDetailPage() { const { slug: slugParam } = useParams<{ slug?: string }>(); const slug = slugParam ?? null; const navigate = useNavigate(); const { t } = useTranslation('management'); const { t: tCommon } = useTranslation('common'); const [state, setState] = React.useState({ event: null, stats: null, toolkit: null, loading: true, busy: false, error: null, }); const load = React.useCallback(async () => { if (!slug) { setState({ event: null, stats: null, toolkit: null, loading: false, busy: false, error: t('events.errors.missingSlug', 'Kein Event ausgewählt.') }); return; } setState((prev) => ({ ...prev, loading: true, error: null })); try { const [eventData, statsData, toolkitData] = await Promise.all([getEvent(slug), getEventStats(slug), getEventToolkit(slug)]); setState((prev) => ({ ...prev, event: eventData, stats: statsData, toolkit: toolkitData, loading: false, })); } catch (error) { if (!isAuthError(error)) { setState((prev) => ({ ...prev, loading: false, error: getApiErrorMessage(error, t('events.errors.loadFailed', 'Event konnte nicht geladen werden.')), })); } else { setState((prev) => ({ ...prev, loading: false })); } } }, [slug, t]); React.useEffect(() => { void load(); }, [load]); const { event, stats, toolkit, loading, busy, error } = state; const eventName = resolveName(event?.name) ?? t('events.placeholders.untitled', 'Unbenanntes Event'); const tasksSummary = toolkit?.tasks?.summary; const inviteSummary = toolkit?.invites?.summary; const kpis = [ { label: t('events.detail.kpi.tasks', 'Tasks Completed'), value: tasksSummary ? `${tasksSummary.completed}/${tasksSummary.total}` : '—', icon: Sparkles, tone: '#22c55e', }, { label: t('events.detail.kpi.guests', 'Guests Registered'), value: inviteSummary?.total ?? event?.active_invites_count ?? '—', icon: Users, tone: '#2563eb', }, { label: t('events.detail.kpi.photos', 'Images Uploaded'), value: stats?.uploads_total ?? event?.photo_count ?? '—', icon: Camera, tone: '#8b5cf6', }, ]; const actions = [ { key: 'tasks', label: t('events.quick.tasks', 'Tasks & Checklists'), icon: Sparkles, to: ADMIN_EVENT_TASKS_PATH(event?.slug ?? ''), color: '#60a5fa', }, { key: 'qr', label: t('events.quick.qr', 'QR Code Layouts'), icon: QrCode, to: `${ADMIN_EVENT_INVITES_PATH(event?.slug ?? '')}?tab=layout`, color: '#fbbf24', }, { key: 'images', label: t('events.quick.images', 'Image Management'), icon: Camera, to: ADMIN_EVENT_PHOTOS_PATH(event?.slug ?? ''), color: '#a855f7', }, { key: 'guests', label: t('events.quick.guests', 'Guest Management'), icon: Users, to: ADMIN_EVENT_MEMBERS_PATH(event?.slug ?? ''), color: '#4ade80', }, { key: 'branding', label: t('events.quick.branding', 'Branding & Theme'), icon: Sparkles, to: ADMIN_EVENT_BRANDING_PATH(event?.slug ?? ''), color: '#fb7185', }, { key: 'photobooth', label: t('events.quick.moderation', 'Photo Moderation'), icon: BarChart2, to: ADMIN_EVENT_PHOTOBOOTH_PATH(event?.slug ?? ''), color: '#38bdf8', }, ]; async function handleToggle() { if (!event?.slug) { return; } setState((prev) => ({ ...prev, busy: true })); try { const updated = await toggleEvent(event.slug); setState((prev) => ({ ...prev, busy: false, event: updated })); } catch (err) { setState((prev) => ({ ...prev, busy: false, error: isAuthError(err) ? prev.error : getApiErrorMessage(err, t('events.errors.toggleFailed', 'Status konnte nicht angepasst werden.')), })); } } if (!slug) { return ( {t('events.errors.notFoundBody', 'Ohne gültige Kennung können wir keine Daten laden. Kehre zur Eventliste zurück und wähle dort ein Event aus.')} navigate(ADMIN_EVENTS_PATH)} /> ); } return ( navigate(ADMIN_EVENTS_PATH)}> {t('events.actions.back', 'Back')} {eventName} navigate(adminPath('/settings'))}> {error ? ( {t('events.alerts.failedTitle', 'Aktion fehlgeschlagen')} {error} ) : null} {t('events.workspace.hero.badge', 'Event')} {eventName} {formatDate(event?.event_date) ?? t('events.workspace.hero.description', 'Konzentriere dich auf Aufgaben, Moderation und QR-Code für dieses Event.')} {statusLabel(event, tCommon)} {resolveLocation(event)} navigate(ADMIN_EVENT_VIEW_PATH(event?.slug ?? ''))} /> {loading ? ( {Array.from({ length: 3 }).map((_, idx) => ( ))} ) : ( {kpis.map((kpi) => ( ))} )} {t('events.detail.managementTitle', 'Event Management')} {t('events.detail.managementSubtitle', 'Schnelle Aktionen für Aufgaben, Gäste und Layouts.')} {actions.map((action) => ( navigate(action.to)}> {action.label} {t('events.detail.open', 'Open')} ))} { if (key === 'analytics') { navigate(adminPath('/dashboard')); } else if (key === 'settings') { navigate(adminPath('/settings')); } else { navigate(ADMIN_EVENTS_PATH); } }} /> ); } function resolveName(name: TenantEvent['name'] | undefined | null): string | null { if (!name) return null; if (typeof name === 'string') return name; if (typeof name === 'object') { return name.de ?? name.en ?? Object.values(name)[0] ?? null; } return null; } function resolveLocation(event: TenantEvent | null): string { if (!event?.settings) return 'Location tbd'; const maybeAddress = (event.settings as Record).location ?? (event.settings as Record).address ?? (event.settings as Record).city; if (typeof maybeAddress === 'string' && maybeAddress.trim()) { return maybeAddress; } return 'Location tbd'; } function formatDate(value?: string | null): string | null { if (!value) return null; const date = new Date(value); if (Number.isNaN(date.getTime())) return null; return date.toLocaleDateString(undefined, { day: '2-digit', month: 'short', year: 'numeric' }); } function statusLabel(event: TenantEvent | null, t: ReturnType['t']): string { if (!event) return t('events.status.draft', 'Entwurf'); if (event.status === 'published') { return t('events.status.published', 'Live'); } if (event.status === 'archived') { return t('events.status.archived', 'Archiviert'); } return t('events.status.draft', 'Entwurf'); } function KpiCard({ label, value, tone, icon: IconCmp, }: { label: string; value: string | number; tone: string; icon: React.ComponentType<{ size?: number; color?: string }>; }) { return ( {label} {value} ); }