- setDeletedAt update nu ook updatedAt (bug 2) - getAllMeasurements filtert deletedAt IS NULL, nieuwe getAllMeasurementsForSync voor sync - mergeMeasurements detecteert server deleted:true → deletedAt (bug 1) - syncAll: pull-push split met remoteUpdatedMap, geen double-push (bug 3) - hard delete alleen na API-succes (bug 4) - DashboardScreen: refreshCache() na syncAll zodat UI niet op Laden blijft hangen - deleteMeasurement() dead code verwijderd (bug 5) - tests: 3 nieuwe testcases voor deleted → deletedAt mapping, 15/15 pass
113 lines
3.5 KiB
TypeScript
113 lines
3.5 KiB
TypeScript
import React, { useCallback, useState } from 'react'
|
|
import { View, FlatList, StyleSheet, Text, RefreshControl, TouchableOpacity, Alert } from 'react-native'
|
|
import { useFocusEffect } from '@react-navigation/native'
|
|
import { initDatabase, getAllMeasurements, setDeletedAt, removeMeasurement } from '../database/db'
|
|
import { removeFromCache } from '../store/measurementStore'
|
|
import { Measurement } from '../types'
|
|
import { createApi } from '../sync/api'
|
|
import { getApiConfig } from './SettingsScreen'
|
|
import MeasurementRow from '../components/MeasurementRow'
|
|
|
|
type Props = { navigation: any }
|
|
|
|
export default function HistoryScreen({ navigation }: Props) {
|
|
const [measurements, setMeasurements] = useState<Measurement[]>([])
|
|
const [refreshing, setRefreshing] = useState(false)
|
|
|
|
const load = useCallback(async () => {
|
|
await initDatabase()
|
|
const all = await getAllMeasurements()
|
|
setMeasurements(all.filter(m => !m.deletedAt))
|
|
}, [])
|
|
|
|
useFocusEffect(
|
|
useCallback(() => {
|
|
load()
|
|
}, [load])
|
|
)
|
|
|
|
const onRefresh = useCallback(async () => {
|
|
setRefreshing(true)
|
|
await load()
|
|
setRefreshing(false)
|
|
}, [load])
|
|
|
|
const handlePress = (m: Measurement) => {
|
|
const date = new Date(m.measuredAt).toLocaleDateString('nl-NL')
|
|
Alert.alert(
|
|
date,
|
|
`${m.weight.toFixed(1)} kg · BMI ${m.bmi.toFixed(1)}`,
|
|
[
|
|
{ text: 'Annuleren', style: 'cancel' },
|
|
{
|
|
text: 'Wijzigen',
|
|
onPress: () => navigation.navigate('NewMeasurement', { measurement: m }),
|
|
},
|
|
{
|
|
text: 'Verwijderen',
|
|
style: 'destructive',
|
|
onPress: () => confirmDelete(m),
|
|
},
|
|
]
|
|
)
|
|
}
|
|
|
|
const confirmDelete = (m: Measurement) => {
|
|
const date = new Date(m.measuredAt).toLocaleDateString('nl-NL')
|
|
Alert.alert(
|
|
'Verwijderen?',
|
|
`Meting van ${date} definitief verwijderen?`,
|
|
[
|
|
{ text: 'Annuleren', style: 'cancel' },
|
|
{
|
|
text: 'Ja, verwijderen',
|
|
style: 'destructive',
|
|
onPress: async () => {
|
|
await initDatabase()
|
|
await setDeletedAt(m.id)
|
|
removeFromCache(m.id)
|
|
try {
|
|
const config = await getApiConfig()
|
|
if (config) {
|
|
await createApi(config).deleteMeasurement(m.id)
|
|
await removeMeasurement(m.id)
|
|
}
|
|
} catch {
|
|
await load()
|
|
return
|
|
}
|
|
load()
|
|
},
|
|
},
|
|
]
|
|
)
|
|
}
|
|
|
|
return (
|
|
<View style={styles.container}>
|
|
<Text style={styles.count}>{measurements.length} metingen</Text>
|
|
<FlatList
|
|
data={measurements}
|
|
keyExtractor={item => item.id}
|
|
renderItem={({ item }) => (
|
|
<MeasurementRow
|
|
measurement={item}
|
|
onPress={() => navigation.navigate('MeasurementDetail', { measurementId: item.id })}
|
|
/>
|
|
)}
|
|
contentContainerStyle={measurements.length === 0 ? styles.empty : undefined}
|
|
ListEmptyComponent={<Text style={styles.emptyText}>Nog geen metingen</Text>}
|
|
refreshControl={
|
|
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
|
|
}
|
|
/>
|
|
</View>
|
|
)
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: { flex: 1, backgroundColor: '#f5f5f5' },
|
|
count: { fontSize: 14, color: '#666', padding: 16, paddingBottom: 8 },
|
|
empty: { flex: 1, justifyContent: 'center', alignItems: 'center' },
|
|
emptyText: { fontSize: 16, color: '#999' },
|
|
})
|