feat: add MeasurementForm and NewMeasurementScreen
This commit is contained in:
parent
3842e8af63
commit
212bc700c3
2 changed files with 108 additions and 0 deletions
87
src/components/MeasurementForm.tsx
Normal file
87
src/components/MeasurementForm.tsx
Normal 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' },
|
||||
})
|
||||
Loading…
Reference in a new issue