diff --git a/src/components/WeightChart.tsx b/src/components/WeightChart.tsx index 7ea663f..f2f743d 100644 --- a/src/components/WeightChart.tsx +++ b/src/components/WeightChart.tsx @@ -11,50 +11,11 @@ const COLORS = { muscleMass: '#2196F3', } -function SingleChart({ - title, - data, - labels, - color, - unit, -}: { - title: string - data: number[] - labels: string[] - color: string - unit: string -}) { - return ( - - {title} - color, strokeWidth: 2 }] }} - width={Dimensions.get('window').width - 32} - height={180} - yAxisSuffix={unit} - chartConfig={{ - backgroundColor: '#ffffff', - backgroundGradientFrom: '#ffffff', - backgroundGradientTo: '#ffffff', - decimalPlaces: 1, - color: (opacity = 1) => `rgba(${hexToRgb(color)}, ${opacity})`, - labelColor: () => '#666', - propsForDots: { r: '3' }, - }} - bezier - style={styles.chart} - withInnerLines={false} - fromZero={false} - /> - - ) -} - -function hexToRgb(hex: string): string { - const r = parseInt(hex.slice(1, 3), 16) - const g = parseInt(hex.slice(3, 5), 16) - const b = parseInt(hex.slice(5, 7), 16) - return `${r}, ${g}, ${b}` +function normalize(values: number[]): number[] { + const min = Math.min(...values) + const max = Math.max(...values) + if (max === min) return values.map(() => 50) + return values.map(v => ((v - min) / (max - min)) * 100) } export default function WeightChart({ measurements }: Props) { @@ -70,6 +31,8 @@ export default function WeightChart({ measurements }: Props) { cutoff.setDate(cutoff.getDate() - 30) const filtered = sorted.filter(m => new Date(m.measuredAt) >= cutoff) const dataPoints = filtered.length >= 2 ? filtered : sorted + const latest = dataPoints[dataPoints.length - 1] + const previous = dataPoints.length > 1 ? dataPoints[dataPoints.length - 2] : null const labels = dataPoints.map(m => { const d = new Date(m.measuredAt) @@ -79,33 +42,96 @@ export default function WeightChart({ measurements }: Props) { const labelStep = labels.length > 8 ? Math.ceil(labels.length / 8) : 1 const sparseLabels = labels.map((l, i) => (i % labelStep === 0 ? l : '')) + const weightData = dataPoints.map(m => m.weight) + const fatData = dataPoints.map(m => m.fatMass) + const muscleData = dataPoints.map(m => m.muscleMass) + return ( Voortgang (30 dagen) - - m.weight)} - labels={sparseLabels} - color={COLORS.weight} - unit=" kg" + COLORS.weight, strokeWidth: 2 }, + { data: normalize(fatData), color: () => COLORS.fatMass, strokeWidth: 2 }, + { data: normalize(muscleData), color: () => COLORS.muscleMass, strokeWidth: 2 }, + ], + }} + width={Dimensions.get('window').width - 32} + height={220} + chartConfig={{ + backgroundColor: '#ffffff', + backgroundGradientFrom: '#ffffff', + backgroundGradientTo: '#ffffff', + decimalPlaces: 0, + color: (opacity = 1) => `rgba(0, 0, 0, ${opacity})`, + labelColor: () => 'transparent', + propsForDots: { r: '3' }, + propsForLabels: { fontSize: 9 }, + }} + bezier + style={styles.chart} + withInnerLines={false} + fromZero={false} /> - m.fatMass)} - labels={sparseLabels} - color={COLORS.fatMass} - unit=" kg" - /> + + + + + + + ) +} - m.muscleMass)} - labels={sparseLabels} - color={COLORS.muscleMass} - unit=" kg" - /> +function MetricInfo({ + color, + label, + value, + prev, + unit, +}: { + color: string + label: string + value: number + prev?: number + unit: string +}) { + const diff = prev !== undefined ? value - prev : 0 + const sign = diff > 0 ? '+' : '' + return ( + + + + {label} + + + {value.toFixed(1)} {unit} + {prev !== undefined && ( + + {' '}{sign}{diff.toFixed(1)} + + )} + ) } @@ -113,7 +139,12 @@ export default function WeightChart({ measurements }: Props) { const styles = StyleSheet.create({ container: { marginHorizontal: 16, marginBottom: 16 }, title: { fontSize: 18, fontWeight: '700', marginBottom: 8 }, - chartBlock: { marginBottom: 12 }, - chartTitle: { fontSize: 13, fontWeight: '600', color: '#555', marginBottom: 4, marginLeft: 4 }, chart: { borderRadius: 12 }, + legend: { flexDirection: 'row', justifyContent: 'space-around', marginTop: 8, gap: 8 }, + metricItem: { alignItems: 'center', flex: 1 }, + metricRow: { flexDirection: 'row', alignItems: 'center', gap: 4, marginBottom: 2 }, + dot: { width: 8, height: 8, borderRadius: 4 }, + metricLabel: { fontSize: 11, color: '#888' }, + metricValue: { fontSize: 13, fontWeight: '600', color: '#333' }, + metricDiff: { fontSize: 11, color: '#999' }, })