import React from 'react'; import { Link, useNavigate } from 'react-router-dom'; import { AlertTriangle, ArrowRight, CalendarDays, Plus, Share2 } from 'lucide-react'; import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { TenantHeroCard, FrostedCard, FrostedSurface, tenantHeroPrimaryButtonClass, tenantHeroSecondaryButtonClass, } from '../components/tenant'; import { AdminLayout } from '../components/AdminLayout'; import { getEvents, TenantEvent } from '../api'; import { isAuthError } from '../auth/tokens'; import { getApiErrorMessage } from '../lib/apiError'; import { adminPath, ADMIN_SETTINGS_PATH, ADMIN_EVENT_VIEW_PATH, ADMIN_EVENT_EDIT_PATH, ADMIN_EVENT_PHOTOS_PATH, ADMIN_EVENT_MEMBERS_PATH, ADMIN_EVENT_TASKS_PATH, ADMIN_EVENT_INVITES_PATH, ADMIN_EVENT_TOOLKIT_PATH, } from '../constants'; import { buildLimitWarnings, type EventLimitSummary } from '../lib/limitWarnings'; import { useTranslation } from 'react-i18next'; export default function EventsPage() { const { t } = useTranslation('management'); const { t: tCommon } = useTranslation('common'); const [rows, setRows] = React.useState([]); const [loading, setLoading] = React.useState(true); const [error, setError] = React.useState(null); const navigate = useNavigate(); React.useEffect(() => { (async () => { try { setRows(await getEvents()); } catch (err) { if (!isAuthError(err)) { setError(getApiErrorMessage(err, t('events.errors.loadFailed', 'Event konnte nicht geladen werden.'))); } } finally { setLoading(false); } })(); }, []); const translateManagement = React.useCallback( (key: string, fallback?: string, options?: Record) => t(key, { defaultValue: fallback, ...(options ?? {}) }), [t], ); const translateCommon = React.useCallback( (key: string, fallback?: string, options?: Record) => tCommon(key, { defaultValue: fallback, ...(options ?? {}) }), [tCommon], ); const pageTitle = translateManagement('events.list.title', 'Deine Events'); const pageSubtitle = translateManagement( 'events.list.subtitle', 'Plane Momente, die in Erinnerung bleiben. Hier verwaltest du alles rund um deine Veranstaltungen.' ); const totalEvents = rows.length; const publishedEvents = React.useMemo(() => rows.filter((event) => event.status === 'published').length, [rows]); const nextEvent = React.useMemo(() => { return rows .filter((event) => event.event_date) .slice() .sort((a, b) => { const dateA = a.event_date ? new Date(a.event_date).getTime() : Infinity; const dateB = b.event_date ? new Date(b.event_date).getTime() : Infinity; return dateA - dateB; })[0] ?? null; }, [rows]); const heroDescription = t( 'events.list.hero.description', 'Aktiviere Storytelling, Moderation und Galerie-Workflows für jeden Anlass in wenigen Minuten.' ); const heroSummaryCopy = totalEvents > 0 ? t('events.list.hero.summary', ':count Events aktiv verwaltet – halte Aufgaben und Uploads im Blick.', { count: totalEvents }) : t('events.list.hero.summary_empty', 'Noch keine Events – starte jetzt mit deinem ersten Konzept.'); const heroSecondaryCopy = t( 'events.list.hero.secondary', 'Erstelle Events im Admin, begleite Gäste live vor Ort und prüfe Kennzahlen im Marketing-Dashboard.' ); const heroBadge = t('events.list.badge.dashboard', 'Tenant Dashboard'); const heroPrimaryAction = ( ); const heroSecondaryAction = ( ); const heroAside = (

{t('events.list.hero.published_label', 'Veröffentlichte Events')}

{publishedEvents}

{t('events.list.hero.total_label', ':count insgesamt', { count: totalEvents })}

{nextEvent ? (

{t('events.list.hero.next_label', 'Nächstes Event')}

{renderName(nextEvent.name)}

{formatDate(nextEvent.event_date)}

{nextEvent.slug ? ( ) : null}
) : (

{t('events.list.hero.no_upcoming', 'Plane ein Datum, um hier die nächste Station zu sehen.')}

)}
); return ( {error && ( Fehler beim Laden {error} )}
{t('events.list.overview.title', 'Übersicht')} {loading ? t('events.list.overview.loading', 'Wir sammeln gerade deine Event-Details …') : totalEvents === 0 ? t('events.list.overview.empty', 'Noch keine Events - starte jetzt und lege dein erstes Event an.') : t('events.list.overview.count', '{{count}} Events aktiv verwaltet.', { count: totalEvents })}
{t('events.list.badge.dashboard', 'Tenant Dashboard')}
{loading ? ( ) : totalEvents === 0 ? ( navigate(adminPath('/events/new'))} /> ) : (
{rows.map((event) => ( ))}
)}
); } function EventCard({ event, translate, translateCommon, }: { event: TenantEvent; translate: (key: string, fallback?: string, options?: Record) => string; translateCommon: (key: string, fallback?: string, options?: Record) => string; }) { const slug = event.slug; const isPublished = event.status === 'published'; const photoCount = event.photo_count ?? 0; const likeCount = event.like_count ?? 0; const limitWarnings = React.useMemo( () => buildLimitWarnings(event.limits ?? null, (key, opts) => translateCommon(`limits.${key}`, undefined, opts)), [event.limits, translateCommon], ); return ( {limitWarnings.length > 0 && (
{limitWarnings.map((warning) => ( {warning.message} ))}
)}

{renderName(event.name)}

{formatDate(event.event_date)} {translate('events.list.badges.photos', 'Fotos')}: {photoCount} {translate('events.list.badges.likes', 'Likes')}: {likeCount}
{isPublished ? translateCommon('events.status.published', 'Veröffentlicht') : translateCommon('events.status.draft', 'Entwurf')}
); } function LoadingState() { return (
{Array.from({ length: 3 }).map((_, index) => ( ))}
); } function EmptyState({ onCreate }: { onCreate: () => void }) { return (

Noch kein Event angelegt

Starte jetzt mit deinem ersten Event und lade Gäste in dein farbenfrohes Erlebnisportal ein.

); } function formatDate(iso: string | null): string { if (!iso) return 'Noch kein Datum'; const date = new Date(iso); if (Number.isNaN(date.getTime())) { return 'Unbekanntes Datum'; } return date.toLocaleDateString('de-DE', { day: '2-digit', month: 'short', year: 'numeric', }); } function renderName(name: TenantEvent['name']): string { if (typeof name === 'string') return name; if (name && typeof name === 'object') { return name.de ?? name.en ?? Object.values(name)[0] ?? 'Unbenanntes Event'; } return 'Unbenanntes Event'; }