import { View, Text, ScrollView, TouchableOpacity, Linking, ActivityIndicator, Alert, Share, StyleSheet, Platform, } from 'react-native' import { SafeAreaView } from 'react-native-safe-area-context' import { useLocalSearchParams, useRouter, Stack } from 'expo-router' import { Ionicons } from '@expo/vector-icons' import { useTerminDetail, useToggleAnmeldung } from '@/hooks/useTermine' import { AnmeldeButton } from '@/components/termine/AnmeldeButton' import { Badge } from '@/components/ui/Badge' import { TERMIN_TYP_LABELS } from '@innungsapp/shared/types' import { format } from 'date-fns' import { de } from 'date-fns/locale' import * as Calendar from 'expo-calendar' export default function TerminDetailScreen() { const { id } = useLocalSearchParams<{ id: string }>() const router = useRouter() const { data: termin, isLoading } = useTerminDetail(id) const { mutate, isPending } = useToggleAnmeldung() if (isLoading) { return ( ) } if (!termin) return null const datum = new Date(termin.datum) const isPast = datum < new Date() const handleAddToCalendar = async () => { try { const { status } = await Calendar.requestCalendarPermissionsAsync() if (status !== 'granted') { Alert.alert('Fehler', 'Kalender-Berechtigung wurde verweigert.') return } const startDate = new Date(termin.datum) let endDate = new Date(termin.datum) if (termin.uhrzeit) { const [hours, minutes] = termin.uhrzeit.split(':').map(Number) startDate.setHours(hours || 0, minutes || 0) if (termin.endeUhrzeit) { const [endHours, endMinutes] = termin.endeUhrzeit.split(':').map(Number) endDate.setHours(endHours || 0, endMinutes || 0) } else { endDate.setHours((hours || 0) + 1, minutes || 0) } } else { endDate.setDate(startDate.getDate() + 1) } let calendarId if (Platform.OS === 'ios') { const defaultCalendar = await Calendar.getDefaultCalendarAsync() calendarId = defaultCalendar.id } else { const calendars = await Calendar.getCalendarsAsync(Calendar.EntityTypes.EVENT) // Try to prefer primary or typical default calendar const primary = calendars.find(c => c.isPrimary) || calendars.find(c => c.accessLevel === 'owner') || calendars[0] calendarId = primary?.id } if (!calendarId) { Alert.alert('Fehler', 'Kein beschreibbarer Kalender gefunden.') return } await Calendar.createEventAsync(calendarId, { title: termin.titel, startDate, endDate, allDay: !termin.uhrzeit, location: [termin.ort, termin.adresse].filter(Boolean).join(', '), notes: termin.beschreibung || undefined, }) Alert.alert('Erfolg', 'Der Termin wurde in den Kalender eingetragen.') } catch (e) { console.error(e) Alert.alert('Fehler', 'Der Termin konnte nicht eingetragen werden.') } } return ( <> {/* Header */} router.back()} style={styles.backButton}> { const lines: string[] = [] lines.push(`📅 ${termin.titel}`) lines.push(format(datum, 'EEEE, d. MMMM yyyy', { locale: de })) if (termin.uhrzeit) { lines.push(`🕐 ${termin.uhrzeit}${termin.endeUhrzeit ? ` – ${termin.endeUhrzeit}` : ''} Uhr`) } if (termin.ort) lines.push(`📍 ${termin.ort}`) if (termin.adresse) lines.push(` ${termin.adresse}`) if (termin.beschreibung) lines.push(`\n${termin.beschreibung}`) Share.share({ message: lines.join('\n') }) }} > {/* Date Badge */} {format(datum, 'EEEE, d. MMMM yyyy', { locale: de })} {termin.titel} {termin.isAngemeldet && ( Angemeldet )} {/* Info Card */} {/* Time */} {termin.uhrzeit && ( Uhrzeit {termin.uhrzeit} {termin.endeUhrzeit ? ` – ${termin.endeUhrzeit}` : ''} Uhr )} {termin.uhrzeit && termin.ort && } {/* Location */} {termin.ort && ( termin.adresse && Linking.openURL(`https://maps.google.com/?q=${encodeURIComponent(termin.adresse)}`) } activeOpacity={termin.adresse ? 0.7 : 1} > Ort {termin.ort} {termin.adresse && ( {termin.adresse} )} {termin.adresse && ( )} )} {(termin.uhrzeit || termin.ort) && (termin.teilnehmerAnzahl !== undefined) && } {/* Participants */} Teilnehmer {termin.teilnehmerAnzahl} Anmeldungen {termin.maxTeilnehmer && ( {termin.maxTeilnehmer} PlĂ€tze verfĂŒgbar )} {/* Description */} {termin.beschreibung && ( Beschreibung {termin.beschreibung} )} {/* Bottom Action Bar – only for upcoming events */} {isPast ? ( Vergangener Termin – keine Anmeldung möglich ) : ( mutate({ terminId: id })} isLoading={isPending} maxTeilnehmer={termin.maxTeilnehmer} teilnehmerAnzahl={termin.teilnehmerAnzahl} /> )} ) } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#F8FAFC', }, loadingContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', }, header: { paddingHorizontal: 16, paddingVertical: 12, flexDirection: 'row', alignItems: 'center', backgroundColor: '#F8FAFC', }, backButton: { padding: 8, borderRadius: 12, backgroundColor: '#FFFFFF', shadowColor: '#000', shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.05, shadowRadius: 2, elevation: 1, }, headerSpacer: { flex: 1, }, shareButton: { padding: 8, }, scrollContent: { padding: 24, paddingBottom: 100, }, dateline: { flexDirection: 'row', alignItems: 'center', marginBottom: 12, gap: 8, }, calendarIcon: { width: 32, height: 32, borderRadius: 8, backgroundColor: '#EFF6FF', justifyContent: 'center', alignItems: 'center', }, dateText: { fontSize: 15, fontWeight: '600', color: '#003B7E', }, title: { fontSize: 28, fontWeight: '800', color: '#0F172A', lineHeight: 34, marginBottom: 16, letterSpacing: -0.5, }, badgesRow: { flexDirection: 'row', alignItems: 'center', gap: 12, marginBottom: 32, }, registeredBadge: { flexDirection: 'row', alignItems: 'center', gap: 6, paddingHorizontal: 10, paddingVertical: 4, backgroundColor: '#ECFDF5', borderRadius: 6, borderWidth: 1, borderColor: '#A7F3D0', }, registeredText: { fontSize: 12, fontWeight: '600', color: '#047857', }, card: { backgroundColor: '#FFFFFF', borderRadius: 20, padding: 20, shadowColor: '#64748B', shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.04, shadowRadius: 12, elevation: 2, marginBottom: 32, }, infoRow: { flexDirection: 'row', alignItems: 'flex-start', gap: 16, }, iconBox: { width: 40, height: 40, borderRadius: 10, backgroundColor: '#F1F5F9', justifyContent: 'center', alignItems: 'center', }, infoContent: { flex: 1, paddingTop: 2, }, infoLabel: { fontSize: 12, fontWeight: '600', color: '#94A3B8', marginBottom: 2, textTransform: 'uppercase', letterSpacing: 0.5, }, infoValue: { fontSize: 16, fontWeight: '600', color: '#0F172A', lineHeight: 22, }, infoSub: { fontSize: 14, color: '#64748B', marginTop: 2, lineHeight: 20, }, divider: { height: 1, backgroundColor: '#F1F5F9', marginVertical: 16, marginLeft: 56, }, section: { marginBottom: 24, }, sectionTitle: { fontSize: 18, fontWeight: '700', color: '#0F172A', marginBottom: 12, }, description: { fontSize: 16, lineHeight: 26, color: '#334155', }, actionBar: { position: 'absolute', bottom: 0, left: 0, right: 0, backgroundColor: '#FFFFFF', paddingHorizontal: 20, paddingVertical: 16, paddingBottom: Platform.OS === 'ios' ? 32 : 16, flexDirection: 'row', gap: 12, borderTopWidth: 1, borderTopColor: '#F1F5F9', shadowColor: '#000', shadowOffset: { width: 0, height: -4 }, shadowOpacity: 0.02, shadowRadius: 8, elevation: 4, }, pastBar: { position: 'absolute', bottom: 0, left: 0, right: 0, backgroundColor: '#F8FAFC', paddingHorizontal: 20, paddingVertical: 14, paddingBottom: Platform.OS === 'ios' ? 28 : 14, flexDirection: 'row', alignItems: 'center', gap: 8, borderTopWidth: 1, borderTopColor: '#E2E8F0', }, pastBarText: { fontSize: 13, color: '#94A3B8', fontWeight: '500', }, actionButtonContainer: { flex: 1, }, calendarButton: { width: 52, height: 52, borderRadius: 14, backgroundColor: '#F1F5F9', justifyContent: 'center', alignItems: 'center', }, })