feat: add MeasurementDetailScreen, 365-day charts, and edit support
- New MeasurementDetailScreen with wijzigen/verwijderen - WeightChart now supports 30 and 365 days - NewMeasurementScreen now handles both create and edit - Updated README to match current code - Add build-toolchain documentation - Ignore *.apk build artifacts
This commit is contained in:
parent
c499f7df13
commit
ed714af8d1
15 changed files with 730 additions and 130 deletions
|
|
@ -8,6 +8,7 @@ import { generateId } from '../utils/uuid'
|
|||
type Props = {
|
||||
onSubmit: (m: Measurement) => void
|
||||
initialValues?: Partial<Measurement>
|
||||
isEdit?: boolean
|
||||
}
|
||||
|
||||
type FieldDef = {
|
||||
|
|
@ -29,13 +30,10 @@ const fields: FieldDef[] = [
|
|||
{ key: 'bmr', label: 'Stofwisseling (kcal)', placeholder: '1650', keyboardType: 'number-pad' },
|
||||
]
|
||||
|
||||
export default function MeasurementForm({ onSubmit, initialValues }: Props) {
|
||||
export default function MeasurementForm({ onSubmit, initialValues, isEdit }: Props) {
|
||||
const insets = useSafeAreaInsets()
|
||||
const [pickerOpen, setPickerOpen] = useState(false)
|
||||
const [measurementDate, setMeasurementDate] = useState<Date>(() => {
|
||||
if (initialValues?.measuredAt) return new Date(initialValues.measuredAt)
|
||||
return new Date()
|
||||
})
|
||||
const [measurementDate, setMeasurementDate] = useState<Date>(new Date())
|
||||
const [values, setValues] = useState<Record<string, string>>(() => {
|
||||
const init: Record<string, string> = {}
|
||||
for (const f of fields) {
|
||||
|
|
@ -47,9 +45,18 @@ export default function MeasurementForm({ onSubmit, initialValues }: Props) {
|
|||
|
||||
|
||||
|
||||
const weight = parseFloat(values.weight) || 0
|
||||
const bodyFatPct = parseFloat(values.bodyFatPercentage) || 0
|
||||
const musclePct = parseFloat(values.musclePercentage) || 0
|
||||
function toFloat(s: string): number {
|
||||
return parseFloat(s.replace(',', '.')) || 0
|
||||
}
|
||||
|
||||
function toInt(s: string): number {
|
||||
return parseInt(s.replace(',', '.'), 10) || 0
|
||||
}
|
||||
|
||||
|
||||
const weight = toFloat(values.weight)
|
||||
const bodyFatPct = toFloat(values.bodyFatPercentage)
|
||||
const musclePct = toFloat(values.musclePercentage)
|
||||
|
||||
const computed = useMemo(() => ({
|
||||
fatMass: weight > 0 && bodyFatPct > 0 ? ((weight * bodyFatPct) / 100).toFixed(1) : '',
|
||||
|
|
@ -59,17 +66,17 @@ export default function MeasurementForm({ onSubmit, initialValues }: Props) {
|
|||
const handleSubmit = () => {
|
||||
const now = new Date().toISOString()
|
||||
const m: Measurement = {
|
||||
id: generateId(),
|
||||
id: isEdit && initialValues?.id ? initialValues.id : generateId(),
|
||||
weight,
|
||||
bmi: parseFloat(values.bmi) || 0,
|
||||
bmi: toFloat(values.bmi),
|
||||
bodyFatPercentage: bodyFatPct,
|
||||
fatMass: parseFloat(computed.fatMass) || 0,
|
||||
fatMass: toFloat(computed.fatMass),
|
||||
musclePercentage: musclePct,
|
||||
muscleMass: parseFloat(computed.muscleMass) || 0,
|
||||
visceralFat: parseInt(values.visceralFat, 10) || 0,
|
||||
bmr: parseFloat(values.bmr) || 0,
|
||||
muscleMass: toFloat(computed.muscleMass),
|
||||
visceralFat: toInt(values.visceralFat),
|
||||
bmr: toFloat(values.bmr),
|
||||
measuredAt: measurementDate.toISOString(),
|
||||
createdAt: now,
|
||||
createdAt: isEdit && initialValues?.createdAt ? initialValues.createdAt : now,
|
||||
updatedAt: now,
|
||||
syncStatus: 'local',
|
||||
}
|
||||
|
|
@ -140,7 +147,7 @@ export default function MeasurementForm({ onSubmit, initialValues }: Props) {
|
|||
)
|
||||
})}
|
||||
</ScrollView>
|
||||
<TouchableOpacity style={[styles.button, { marginBottom: insets.bottom + 16 }]} onPress={handleSubmit}>
|
||||
<TouchableOpacity style={[styles.button, { marginBottom: insets.bottom + 48 }]} onPress={handleSubmit}>
|
||||
<Text style={styles.buttonText}>Opslaan</Text>
|
||||
</TouchableOpacity>
|
||||
</KeyboardAvoidingView>
|
||||
|
|
@ -149,7 +156,7 @@ export default function MeasurementForm({ onSubmit, initialValues }: Props) {
|
|||
|
||||
const styles = StyleSheet.create({
|
||||
container: { flex: 1, backgroundColor: '#f5f5f5' },
|
||||
scroll: { padding: 16, paddingBottom: 0 },
|
||||
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' },
|
||||
|
|
|
|||
Loading…
Reference in a new issue