import { useCallback, useEffect, useMemo, useState } from 'react'; import { ActivityIndicator, Pressable, RefreshControl, ScrollView, StyleSheet, Text, TextInput, View, } from 'react-native'; import { router } from 'expo-router'; import { EventCreateModal } from '@/components/event-create-modal'; import { CalendarEvent, fetchEvents, fetchProjects, Project } from '@/src/lib/api'; import { useAuth } from '@/src/providers/auth-provider'; const PRIMARY = '#69c350'; function formatEventDate(event: CalendarEvent): string { const start = new Date(event.startDate); const end = event.endDate ? new Date(event.endDate) : null; if (Number.isNaN(start.getTime())) return String(event.startDate || '-'); const date = start.toLocaleDateString('de-DE', { weekday: 'short', day: '2-digit', month: '2-digit', year: 'numeric' }); const startTime = start.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' }); const endTime = end && !Number.isNaN(end.getTime()) ? end.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' }) : null; return `${date} · ${startTime}${endTime ? `–${endTime}` : ''}`; } function projectName(event: CalendarEvent): string | null { if (!event.project) return null; if (typeof event.project === 'object') return event.project.name || (event.project.id ? `Projekt #${event.project.id}` : null); return `Projekt #${event.project}`; } export default function EventsScreen() { const { token } = useAuth(); const [events, setEvents] = useState([]); const [projects, setProjects] = useState([]); const [search, setSearch] = useState(''); const [showPast, setShowPast] = useState(false); const [createOpen, setCreateOpen] = useState(false); const [loading, setLoading] = useState(true); const [refreshing, setRefreshing] = useState(false); const [error, setError] = useState(null); const load = useCallback(async (showSpinner = true) => { if (!token) return; if (showSpinner) setLoading(true); setError(null); try { const [eventRows, projectRows] = await Promise.all([fetchEvents(token), fetchProjects(token)]); setEvents(eventRows); setProjects(projectRows); } catch (err) { setError(err instanceof Error ? err.message : 'Termine konnten nicht geladen werden.'); } finally { setLoading(false); setRefreshing(false); } }, [token]); useEffect(() => { void load(true); }, [load]); const visibleEvents = useMemo(() => { const now = Date.now(); const terms = search.trim().toLowerCase().split(/\s+/).filter(Boolean); return events .filter((event) => showPast || new Date(event.endDate || event.startDate).getTime() >= now) .filter((event) => { if (terms.length === 0) return true; const haystack = [event.name, event.notes, event.eventtype, projectName(event)] .map((value) => String(value || '').toLowerCase()) .join(' '); return terms.every((term) => haystack.includes(term)); }) .sort((a, b) => new Date(a.startDate).getTime() - new Date(b.startDate).getTime()); }, [events, search, showPast]); async function onRefresh() { setRefreshing(true); await load(false); } return ( setShowPast((value) => !value)}> Vergangene anzeigen router.push('/more/calendar' as any)}> Kalender }> {error ? {error} : null} {loading ? ( Termine werden geladen... ) : null} {!loading && visibleEvents.length === 0 ? Keine Termine gefunden. : null} {!loading ? visibleEvents.map((event) => ( [styles.eventRow, pressed ? styles.eventRowPressed : null]} onPress={() => router.push(`/more/event/${event.id}` as any)}> {event.name} {formatEventDate(event)} {projectName(event) ? {projectName(event)} : null} {event.notes ? {String(event.notes)} : null} )) : null} setCreateOpen(true)}> + setCreateOpen(false)} onCreated={() => load(false)} /> ); } const styles = StyleSheet.create({ screen: { flex: 1, backgroundColor: '#ffffff' }, toolbar: { padding: 14, gap: 10, borderBottomWidth: 1, borderBottomColor: '#e5e7eb' }, searchInput: { borderWidth: 1, borderColor: '#d1d5db', borderRadius: 10, paddingHorizontal: 12, paddingVertical: 10, fontSize: 15, color: '#111827', }, toolbarActions: { flexDirection: 'row', justifyContent: 'space-between', gap: 8 }, filterButton: { borderWidth: 1, borderColor: '#d1d5db', borderRadius: 999, paddingHorizontal: 10, paddingVertical: 7 }, filterButtonActive: { borderColor: PRIMARY, backgroundColor: '#eff9ea' }, filterButtonText: { color: '#374151', fontSize: 12, fontWeight: '600' }, filterButtonTextActive: { color: '#3d7a30' }, calendarButton: { borderRadius: 8, backgroundColor: PRIMARY, paddingHorizontal: 13, paddingVertical: 7 }, calendarButtonText: { color: '#ffffff', fontSize: 13, fontWeight: '700' }, list: { flex: 1 }, listContent: { paddingBottom: 96 }, eventRow: { minHeight: 82, flexDirection: 'row', alignItems: 'stretch', borderBottomWidth: 1, borderBottomColor: '#e5e7eb' }, eventRowPressed: { backgroundColor: '#f3f4f6' }, colorBar: { width: 5 }, eventMain: { flex: 1, paddingHorizontal: 12, paddingVertical: 11, gap: 2 }, eventName: { color: '#111827', fontSize: 15, fontWeight: '700' }, eventDate: { color: '#374151', fontSize: 13 }, eventProject: { color: '#3d7a30', fontSize: 12, fontWeight: '600' }, eventNotes: { color: '#6b7280', fontSize: 12 }, arrow: { alignSelf: 'center', color: '#9ca3af', fontSize: 25, paddingRight: 12 }, loadingBox: { paddingVertical: 24, alignItems: 'center', gap: 8 }, loadingText: { color: '#6b7280' }, empty: { color: '#6b7280', textAlign: 'center', paddingVertical: 28 }, error: { color: '#dc2626', fontSize: 13, padding: 14 }, fab: { position: 'absolute', right: 18, bottom: 20, width: 56, height: 56, borderRadius: 28, backgroundColor: PRIMARY, alignItems: 'center', justifyContent: 'center', shadowColor: '#111827', shadowOpacity: 0.25, shadowRadius: 8, shadowOffset: { width: 0, height: 4 }, elevation: 5, }, fabText: { color: '#ffffff', fontSize: 30, lineHeight: 30, marginTop: -2 }, });