feat: add MeasurementForm and NewMeasurementScreen

This commit is contained in:
Eddy de Vink 2026-06-27 22:40:38 +02:00
commit 212bc700c3
2 changed files with 108 additions and 0 deletions

View file

@ -0,0 +1,87 @@
import React, { useState } from 'react'
import { View, Text, TextInput, TouchableOpacity, StyleSheet, KeyboardAvoidingView, Platform, ScrollView } from 'react-native'
import { Measurement } from '../types'
import { generateId } from '../utils/uuid'
type Props = {
onSubmit: (m: Measurement) => void
initialValues?: Partial<Measurement>
}
const fields: { key: keyof Measurement; label: string; placeholder: string; keyboardType: 'decimal-pad' | 'number-pad' }[] = [
{ key: 'weight', label: 'Gewicht (kg)', placeholder: '75.5', keyboardType: 'decimal-pad' },
{ key: 'bmi', label: 'BMI', placeholder: '22.3', keyboardType: 'decimal-pad' },
{ key: 'bodyFatPercentage', label: 'Vetpercentage (%)', placeholder: '15.2', keyboardType: 'decimal-pad' },
{ key: 'fatMass', label: 'Vetmassa (kg)', placeholder: '11.5', keyboardType: 'decimal-pad' },
{ key: 'musclePercentage', label: 'Spierpercentage (%)', placeholder: '40.1', keyboardType: 'decimal-pad' },
{ key: 'muscleMass', label: 'Spiermassa (kg)', placeholder: '30.3', keyboardType: 'decimal-pad' },
{ key: 'visceralFat', label: 'Buikvet (index)', placeholder: '8', keyboardType: 'number-pad' },
{ key: 'bmr', label: 'Stofwisseling (kcal)', placeholder: '1650', keyboardType: 'number-pad' },
]
export default function MeasurementForm({ onSubmit, initialValues }: Props) {
const [values, setValues] = useState<Record<string, string>>(() => {
const init: Record<string, string> = {}
for (const f of fields) {
init[f.key] = initialValues?.[f.key]?.toString() ?? ''
}
return init
})
const handleSubmit = () => {
const now = new Date().toISOString()
const m: Measurement = {
id: generateId(),
weight: parseFloat(values.weight) || 0,
bmi: parseFloat(values.bmi) || 0,
bodyFatPercentage: parseFloat(values.bodyFatPercentage) || 0,
fatMass: parseFloat(values.fatMass) || 0,
musclePercentage: parseFloat(values.musclePercentage) || 0,
muscleMass: parseFloat(values.muscleMass) || 0,
visceralFat: parseInt(values.visceralFat, 10) || 0,
bmr: parseFloat(values.bmr) || 0,
measuredAt: now,
createdAt: now,
updatedAt: now,
syncStatus: 'local',
}
onSubmit(m)
}
const setField = (key: string, value: string) => {
setValues(prev => ({ ...prev, [key]: value }))
}
return (
<KeyboardAvoidingView behavior={Platform.OS === 'ios' ? 'padding' : undefined} style={styles.container}>
<ScrollView contentContainerStyle={styles.scroll}>
{fields.map(f => (
<View key={f.key} style={styles.field}>
<Text style={styles.label}>{f.label}</Text>
<TextInput
style={styles.input}
keyboardType={f.keyboardType}
placeholder={f.placeholder}
value={values[f.key]}
onChangeText={v => setField(f.key, v)}
placeholderTextColor="#999"
/>
</View>
))}
<TouchableOpacity style={styles.button} onPress={handleSubmit}>
<Text style={styles.buttonText}>Opslaan</Text>
</TouchableOpacity>
</ScrollView>
</KeyboardAvoidingView>
)
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: '#f5f5f5' },
scroll: { padding: 16 },
field: { marginBottom: 12 },
label: { fontSize: 14, fontWeight: '600', color: '#333', marginBottom: 4 },
input: { backgroundColor: '#fff', borderRadius: 8, padding: 12, fontSize: 16, borderWidth: 1, borderColor: '#ddd' },
button: { backgroundColor: '#4CAF50', padding: 16, borderRadius: 8, alignItems: 'center', marginTop: 8 },
buttonText: { color: '#fff', fontSize: 16, fontWeight: '700' },
})

View file

@ -0,0 +1,21 @@
import React from 'react'
import { Alert } from 'react-native'
import MeasurementForm from '../components/MeasurementForm'
import { Measurement } from '../types'
import { initDatabase, insertMeasurement } from '../database/db'
type Props = {
navigation: any
}
export default function NewMeasurementScreen({ navigation }: Props) {
const handleSubmit = async (m: Measurement) => {
await initDatabase()
await insertMeasurement(m)
Alert.alert('Opgeslagen', 'Meting is opgeslagen', [
{ text: 'OK', onPress: () => navigation.goBack() },
])
}
return <MeasurementForm onSubmit={handleSubmit} />
}