stadtwerke/innungsapp/apps/mobile/app/(app)/news/index.tsx

158 lines
4.1 KiB
TypeScript

import {
View, Text, FlatList, TouchableOpacity, RefreshControl, ScrollView, StyleSheet,
} from 'react-native'
import { SafeAreaView } from 'react-native-safe-area-context'
import { useState } from 'react'
import { useRouter } from 'expo-router'
import { useNewsList } from '@/hooks/useNews'
import { NewsCard } from '@/components/news/NewsCard'
import { EmptyState } from '@/components/ui/EmptyState'
import { LoadingSpinner } from '@/components/ui/LoadingSpinner'
const FILTERS = [
{ value: undefined, label: 'Alle' },
{ value: 'Wichtig', label: 'Wichtig' },
{ value: 'Pruefung', label: 'Pruefung' },
{ value: 'Foerderung', label: 'Foerderung' },
{ value: 'Veranstaltung', label: 'Veranstaltung' },
]
export default function NewsScreen() {
const router = useRouter()
const [kategorie, setKategorie] = useState<string | undefined>(undefined)
const { data, isLoading, refetch, isRefetching } = useNewsList(kategorie)
const unreadCount = data?.filter((n) => !n.isRead).length ?? 0
return (
<SafeAreaView style={styles.safeArea} edges={['top']}>
<View style={styles.header}>
<View style={styles.titleRow}>
<Text style={styles.screenTitle}>Aktuelles</Text>
{unreadCount > 0 && (
<View style={styles.unreadBadge}>
<Text style={styles.unreadBadgeText}>{unreadCount} neu</Text>
</View>
)}
</View>
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
contentContainerStyle={styles.filterScroll}
>
{FILTERS.map((opt) => {
const active = kategorie === opt.value
return (
<TouchableOpacity
key={String(opt.value)}
onPress={() => setKategorie(opt.value)}
style={[styles.chip, active && styles.chipActive]}
activeOpacity={0.85}
>
<Text style={[styles.chipLabel, active && styles.chipLabelActive]}>
{opt.label}
</Text>
</TouchableOpacity>
)
})}
</ScrollView>
</View>
<View style={styles.divider} />
{isLoading ? (
<LoadingSpinner />
) : (
<FlatList
data={data ?? []}
keyExtractor={(item) => item.id}
contentContainerStyle={styles.list}
refreshControl={
<RefreshControl refreshing={isRefetching} onRefresh={refetch} tintColor="#003B7E" />
}
renderItem={({ item }) => (
<NewsCard
news={item}
onPress={() => router.push(`/(app)/news/${item.id}` as never)}
/>
)}
ListEmptyComponent={
<EmptyState icon="N" title="Keine News" subtitle="Noch keine Beitraege veroeffentlicht." />
}
/>
)}
</SafeAreaView>
)
}
const styles = StyleSheet.create({
safeArea: {
flex: 1,
backgroundColor: '#F8FAFC',
},
header: {
backgroundColor: '#FFFFFF',
paddingHorizontal: 20,
paddingTop: 14,
paddingBottom: 0,
},
titleRow: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
marginBottom: 14,
},
screenTitle: {
fontSize: 28,
fontWeight: '800',
color: '#0F172A',
letterSpacing: -0.5,
},
unreadBadge: {
backgroundColor: '#EFF6FF',
paddingHorizontal: 10,
paddingVertical: 4,
borderRadius: 99,
},
unreadBadgeText: {
color: '#003B7E',
fontSize: 12,
fontWeight: '700',
},
filterScroll: {
paddingBottom: 14,
gap: 8,
paddingRight: 20,
},
chip: {
paddingHorizontal: 14,
paddingVertical: 7,
borderRadius: 99,
borderWidth: 1,
borderColor: '#E2E8F0',
backgroundColor: '#FFFFFF',
},
chipActive: {
backgroundColor: '#003B7E',
borderColor: '#003B7E',
},
chipLabel: {
fontSize: 13,
fontWeight: '600',
color: '#64748B',
},
chipLabelActive: {
color: '#FFFFFF',
},
divider: {
height: 1,
backgroundColor: '#E2E8F0',
},
list: {
padding: 16,
gap: 10,
paddingBottom: 30,
},
})