153 lines
5.3 KiB
TypeScript
153 lines
5.3 KiB
TypeScript
import {
|
||
View,
|
||
Text,
|
||
ScrollView,
|
||
TouchableOpacity,
|
||
Linking,
|
||
ActivityIndicator,
|
||
Alert,
|
||
} from 'react-native'
|
||
import { SafeAreaView } from 'react-native-safe-area-context'
|
||
import { useLocalSearchParams, useRouter } from 'expo-router'
|
||
import { trpc } from '@/lib/trpc'
|
||
import { AnmeldeButton } from '@/components/termine/AnmeldeButton'
|
||
import { Badge } from '@/components/ui/Badge'
|
||
import { TERMIN_TYP_LABELS } from '@innungsapp/shared'
|
||
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 } = trpc.termine.byId.useQuery({ id })
|
||
const toggleMutation = trpc.termine.toggleAnmeldung.useMutation({
|
||
onSuccess: () => {
|
||
// Invalidate queries
|
||
},
|
||
})
|
||
|
||
async function addToCalendar() {
|
||
if (!termin) return
|
||
const { status } = await Calendar.requestCalendarPermissionsAsync()
|
||
if (status !== 'granted') {
|
||
Alert.alert('Keine Berechtigung', 'Bitte erlauben Sie den Kalender-Zugriff in den Einstellungen.')
|
||
return
|
||
}
|
||
|
||
const calendars = await Calendar.getCalendarsAsync(Calendar.EntityTypes.EVENT)
|
||
const defaultCal = calendars.find((c) => c.isPrimary) ?? calendars[0]
|
||
|
||
if (!defaultCal) {
|
||
Alert.alert('Kein Kalender', 'Es wurde kein Kalender gefunden.')
|
||
return
|
||
}
|
||
|
||
const startDate = new Date(termin.datum)
|
||
if (termin.uhrzeit) {
|
||
const [h, m] = termin.uhrzeit.split(':').map(Number)
|
||
startDate.setHours(h, m)
|
||
}
|
||
|
||
await Calendar.createEventAsync(defaultCal.id, {
|
||
title: termin.titel,
|
||
startDate,
|
||
endDate: startDate,
|
||
location: termin.adresse ?? termin.ort ?? undefined,
|
||
notes: termin.beschreibung ?? undefined,
|
||
})
|
||
|
||
Alert.alert('Termin gespeichert', 'Der Termin wurde in Ihren Kalender eingetragen.')
|
||
}
|
||
|
||
if (isLoading) {
|
||
return (
|
||
<SafeAreaView className="flex-1 bg-white items-center justify-center">
|
||
<ActivityIndicator size="large" color="#E63946" />
|
||
</SafeAreaView>
|
||
)
|
||
}
|
||
|
||
if (!termin) return null
|
||
|
||
const datumFormatted = format(new Date(termin.datum), 'EEEE, dd. MMMM yyyy', { locale: de })
|
||
|
||
return (
|
||
<SafeAreaView className="flex-1 bg-gray-50" edges={['top']}>
|
||
<View className="flex-row items-center px-4 py-3 bg-white border-b border-gray-100">
|
||
<TouchableOpacity onPress={() => router.back()} className="mr-3">
|
||
<Text className="text-brand-500 text-base">← Zurück</Text>
|
||
</TouchableOpacity>
|
||
</View>
|
||
|
||
<ScrollView>
|
||
<View className="bg-white px-4 py-6 border-b border-gray-100">
|
||
<Badge label={TERMIN_TYP_LABELS[termin.typ]} typ={termin.typ} />
|
||
<Text className="text-2xl font-bold text-gray-900 mt-3">{termin.titel}</Text>
|
||
<Text className="text-gray-500 mt-2 capitalize">{datumFormatted}</Text>
|
||
{termin.uhrzeit && (
|
||
<Text className="text-gray-500">
|
||
{termin.uhrzeit}{termin.endeUhrzeit ? ` – ${termin.endeUhrzeit}` : ''} Uhr
|
||
</Text>
|
||
)}
|
||
</View>
|
||
|
||
<View className="bg-white mx-4 mt-4 rounded-2xl overflow-hidden border border-gray-100">
|
||
{termin.ort && (
|
||
<TouchableOpacity
|
||
onPress={() =>
|
||
termin.adresse &&
|
||
Linking.openURL(
|
||
`https://maps.google.com/?q=${encodeURIComponent(termin.adresse)}`
|
||
)
|
||
}
|
||
className="flex-row items-center px-4 py-3 border-b border-gray-50"
|
||
>
|
||
<Text className="text-2xl mr-3">📍</Text>
|
||
<View>
|
||
<Text className="font-medium text-gray-900">{termin.ort}</Text>
|
||
{termin.adresse && (
|
||
<Text className="text-sm text-brand-500 mt-0.5">{termin.adresse}</Text>
|
||
)}
|
||
</View>
|
||
</TouchableOpacity>
|
||
)}
|
||
<View className="flex-row items-center px-4 py-3">
|
||
<Text className="text-2xl mr-3">👥</Text>
|
||
<Text className="text-gray-700">
|
||
{termin.teilnehmerAnzahl} Anmeldungen
|
||
{termin.maxTeilnehmer ? ` / ${termin.maxTeilnehmer} Plätze` : ''}
|
||
</Text>
|
||
</View>
|
||
</View>
|
||
|
||
{termin.beschreibung && (
|
||
<View className="bg-white mx-4 mt-4 rounded-2xl p-4 border border-gray-100">
|
||
<Text className="font-semibold text-gray-900 mb-2">Beschreibung</Text>
|
||
<Text className="text-gray-600 leading-6">{termin.beschreibung}</Text>
|
||
</View>
|
||
)}
|
||
|
||
{/* Actions */}
|
||
<View className="mx-4 mt-4 gap-3">
|
||
<AnmeldeButton
|
||
terminId={id}
|
||
isAngemeldet={termin.isAngemeldet}
|
||
onToggle={() => toggleMutation.mutate({ terminId: id })}
|
||
isLoading={toggleMutation.isPending}
|
||
/>
|
||
<TouchableOpacity
|
||
onPress={addToCalendar}
|
||
className="bg-white border border-gray-200 rounded-2xl py-4 flex-row items-center justify-center gap-2"
|
||
>
|
||
<Text className="text-gray-900 text-xl">📅</Text>
|
||
<Text className="text-gray-900 font-semibold">Zum Kalender hinzufügen</Text>
|
||
</TouchableOpacity>
|
||
</View>
|
||
|
||
<View className="h-8" />
|
||
</ScrollView>
|
||
</SafeAreaView>
|
||
)
|
||
}
|