103 lines
4 KiB
TypeScript
103 lines
4 KiB
TypeScript
import React, { useRef } from 'react'
|
|
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native'
|
|
import ViewShot from 'react-native-view-shot'
|
|
import * as Sharing from 'expo-sharing'
|
|
import { MaterialIcons } from '@expo/vector-icons'
|
|
import { Measurement } from '../types'
|
|
|
|
type Props = { measurements: Measurement[] }
|
|
|
|
const rows: { label: string; key: keyof Measurement; suffix: string }[] = [
|
|
{ label: 'Gewicht', key: 'weight', suffix: 'kg' },
|
|
{ label: 'BMI', key: 'bmi', suffix: '' },
|
|
{ label: 'Vetpercentage', key: 'bodyFatPercentage', suffix: '%' },
|
|
{ label: 'Vetmassa', key: 'fatMass', suffix: 'kg' },
|
|
{ label: 'Spierpercentage', key: 'musclePercentage', suffix: '%' },
|
|
{ label: 'Spiermassa', key: 'muscleMass', suffix: 'kg' },
|
|
{ label: 'Buikvet', key: 'visceralFat', suffix: '' },
|
|
{ label: 'Stofwisseling', key: 'bmr', suffix: 'kcal' },
|
|
]
|
|
|
|
export default function LatestMeasurementCard({ measurements }: Props) {
|
|
const viewShotRef = useRef<ViewShot>(null)
|
|
|
|
if (measurements.length === 0) {
|
|
return (
|
|
<View style={styles.card}>
|
|
<Text style={styles.emptyText}>Nog geen metingen</Text>
|
|
</View>
|
|
)
|
|
}
|
|
|
|
const latest = measurements[0]
|
|
const previous = measurements[1]
|
|
const diff = previous ? (latest.weight - previous.weight) : null
|
|
|
|
const handleShare = async () => {
|
|
try {
|
|
const uri = await viewShotRef.current?.capture?.()
|
|
if (uri) {
|
|
await Sharing.shareAsync(uri, { mimeType: 'image/png' })
|
|
}
|
|
} catch {}
|
|
}
|
|
|
|
return (
|
|
<View style={styles.card}>
|
|
<View style={styles.header}>
|
|
<View style={{ flex: 1 }} />
|
|
<TouchableOpacity onPress={handleShare} hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}>
|
|
<MaterialIcons name="share" size={24} color="#2196F3" />
|
|
</TouchableOpacity>
|
|
</View>
|
|
|
|
<ViewShot ref={viewShotRef} options={{ format: 'png', quality: 1 }}>
|
|
<View style={styles.shotContent}>
|
|
{previous && (
|
|
<View style={styles.previous}>
|
|
<Text style={styles.previousTitle}>Voorlaatste meting</Text>
|
|
<Text style={styles.previousDate}>
|
|
{new Date(previous.measuredAt).toLocaleDateString('nl-NL')} — {previous.weight.toFixed(1)} kg
|
|
</Text>
|
|
<Text style={styles.diffLabel}>
|
|
Verschil {diff !== null && (diff > 0 ? '+' : '')}{diff?.toFixed(1)} kg
|
|
</Text>
|
|
<View style={styles.divider} />
|
|
</View>
|
|
)}
|
|
|
|
<Text style={styles.title}>Laatste meting</Text>
|
|
<Text style={styles.date}>{new Date(latest.measuredAt).toLocaleDateString('nl-NL')}</Text>
|
|
{rows.map(r => (
|
|
<View key={r.key} style={styles.row}>
|
|
<Text style={styles.rowLabel}>{r.label}</Text>
|
|
<Text style={styles.rowValue}>
|
|
{typeof latest[r.key] === 'number'
|
|
? (latest[r.key] as number).toFixed(1)
|
|
: latest[r.key]}{' '}
|
|
{r.suffix}
|
|
</Text>
|
|
</View>
|
|
))}
|
|
</View>
|
|
</ViewShot>
|
|
</View>
|
|
)
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
card: { backgroundColor: '#fff', borderRadius: 12, padding: 16, margin: 16, shadowColor: '#000', shadowOpacity: 0.1, shadowRadius: 8, elevation: 3 },
|
|
header: { flexDirection: 'row', alignItems: 'center', marginBottom: 8 },
|
|
shotContent: { backgroundColor: '#fff', padding: 16 },
|
|
title: { fontSize: 18, fontWeight: '700', marginBottom: 4 },
|
|
date: { fontSize: 14, color: '#666', marginBottom: 12 },
|
|
row: { flexDirection: 'row', paddingVertical: 4 },
|
|
rowLabel: { fontSize: 14, color: '#555', flex: 1 },
|
|
rowValue: { fontSize: 14, fontWeight: '600' },
|
|
emptyText: { fontSize: 16, color: '#999', textAlign: 'center', padding: 20 },
|
|
previous: { marginBottom: 4 },
|
|
divider: { height: 1, backgroundColor: '#eee', marginVertical: 12 },
|
|
previousTitle: { fontSize: 18, fontWeight: '700', marginBottom: 4 },
|
|
previousDate: { fontSize: 14, color: '#666', marginBottom: 4 },
|
|
diffLabel: { fontSize: 16, fontWeight: '700', color: '#2196F3' },
|
|
})
|