import React from 'react'; import { useNavigate } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { CalendarDays, MapPin, Settings, Plus, Bell, ListTodo, Image as ImageIcon } from 'lucide-react'; import { YStack, XStack } from '@tamagui/stacks'; import { SizableText as Text } from '@tamagui/text'; import { Pressable } from '@tamagui/react-native-web-lite'; import { MobileScaffold } from './components/Scaffold'; import { MobileCard, PillBadge, CTAButton, KpiTile, ActionTile } from './components/Primitives'; import { BottomNav } from './components/BottomNav'; import { getEvents, TenantEvent } from '../api'; import { adminPath } from '../constants'; import { isAuthError } from '../auth/tokens'; import { getApiErrorMessage } from '../lib/apiError'; import { useMobileNav } from './hooks/useMobileNav'; import { getEventStats, EventStats } from '../api'; export default function MobileDashboardPage() { const navigate = useNavigate(); const { t } = useTranslation('management'); const [events, setEvents] = React.useState([]); const [stats, setStats] = React.useState>({}); const [loading, setLoading] = React.useState(true); const [error, setError] = React.useState(null); const { go } = useMobileNav(); React.useEffect(() => { (async () => { try { setEvents(await getEvents()); const fetched: Record = {}; const list = await getEvents(); await Promise.all( (list || []).map(async (ev) => { if (!ev.slug) return; try { fetched[ev.slug] = await getEventStats(ev.slug); } catch { // ignore per-event stat failures } }) ); setStats(fetched); setError(null); } catch (err) { if (!isAuthError(err)) { setError(getApiErrorMessage(err, t('events.errors.loadFailed', 'Events konnten nicht geladen werden.'))); } } finally { setLoading(false); } })(); }, [t]); return ( navigate(-1)} rightSlot={ navigate(adminPath('/settings'))}> } footer={ } > {error ? ( {error} ) : null} navigate(adminPath('/mobile/events/new'))} /> {loading ? ( {Array.from({ length: 3 }).map((_, idx) => ( ))} ) : events.length === 0 ? ( {t('events.list.empty.title', 'Noch kein Event angelegt')} {t('events.list.empty.description', 'Starte jetzt mit deinem ersten Event.')} navigate(adminPath('/mobile/events/new'))} /> ) : ( {t('dashboard.kpis', 'Key Performance Indicators')} {events.map((event) => ( {renderName(event.name)} {formatDate(event.event_date)} {resolveLocation(event)} {resolveStatus(event, t)} navigate(adminPath(`/mobile/events/${event.slug}`))}> ˅ navigate(adminPath(`/mobile/events/${event.slug}/tasks`))} width="32%" /> navigate(adminPath(`/mobile/events/${event.slug}/photos`))} width="32%" /> navigate(adminPath(`/mobile/alerts?event=${event.slug}`))} width="32%" /> ))} )} ); } 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'; } function formatDate(iso: string | null): string { if (!iso) return 'Date tbd'; const date = new Date(iso); if (Number.isNaN(date.getTime())) { return 'Date tbd'; } return date.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' }); } function resolveLocation(event: TenantEvent): string { const settings = (event.settings ?? {}) as Record; const candidate = (settings.location as string | undefined) ?? (settings.address as string | undefined) ?? (settings.city as string | undefined); if (candidate && candidate.trim()) { return candidate; } return 'Location'; } function resolveStatus(event: TenantEvent, t: ReturnType['t']): string { if (event.status === 'published') return t('events.status.published', 'Upcoming'); if (event.status === 'draft') return t('events.status.draft', 'Draft'); return t('events.status.archived', 'Past'); } function resolveTone(event: TenantEvent): 'success' | 'warning' | 'muted' { if (event.status === 'published') return 'success'; if (event.status === 'draft') return 'warning'; return 'muted'; }