File size: 4,635 Bytes
3d4aa49
81c5264
fb78bf0
3d4aa49
 
 
5ee1afb
 
 
2cd7977
 
 
 
 
 
 
 
 
 
 
 
 
359f0ff
 
 
3d4aa49
 
 
 
 
 
 
 
 
 
 
 
 
 
2cd7977
73a5a82
 
 
81c5264
73a5a82
2cd7977
359f0ff
3d4aa49
 
 
5ee1afb
3d4aa49
 
 
 
fb78bf0
5ee1afb
fb78bf0
3d4aa49
 
 
 
 
 
 
 
 
 
 
fb78bf0
5ee1afb
fb78bf0
3d4aa49
 
 
 
 
 
 
 
 
 
 
73a5a82
 
 
 
 
 
81c5264
73a5a82
81c5264
 
73a5a82
 
 
 
 
 
81c5264
73a5a82
 
 
81c5264
 
 
73a5a82
 
 
 
008ec3f
81c5264
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73a5a82
 
 
 
3d4aa49
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
import {
  BarChart, Bar, LineChart, Line, Legend,
  XAxis, YAxis, Tooltip, CartesianGrid, ResponsiveContainer, Label,
} from 'recharts';
import './Charts.css';

const PAIN_DURING_COLOR = '#ef4444';
const PAIN_AFTER_COLOR = '#3b82f6';

const PAIN_CHARTS = [
  {
    location: 'left_knee',
    title: 'Left Knee Pain',
    duringKey: 'left_knee_during',
    afterKey: 'left_knee_after',
  },
  {
    location: 'right_knee',
    title: 'Right Knee Pain',
    duringKey: 'right_knee_during',
    afterKey: 'right_knee_after',
  },
];

function Charts({ data, painData }) {
  if (!data || data.length === 0) {
    return (
      <div className="charts card">
        <p className="empty-message">Log some runs to see your progress over time.</p>
      </div>
    );
  }

  // Shorten week labels for x-axis: "2026-W10" → "W10"
  const chartData = data.map((d) => ({
    ...d,
    label: d.week.replace(/^\d{4}-/, ''),
  }));

  // Determine which locations have data
  const activePainCharts = painData?.locations
    ? PAIN_CHARTS.filter((chart) => {
        const loc = painData.locations[chart.location];
        return loc && loc.points.length > 0;
      })
    : [];

  return (
    <div className="charts">
      <div className="chart-container card">
        <h2>Weekly Distance</h2>
        <ResponsiveContainer width="100%" height={300}>
          <BarChart data={chartData}>
            <CartesianGrid strokeDasharray="3 3" />
            <XAxis dataKey="label" />
            <YAxis>
              <Label value="Km" angle={-90} position="insideLeft" style={{ textAnchor: 'middle', fill: '#6b7280' }} />
            </YAxis>
            <Tooltip />
            <Bar dataKey="distance" fill="var(--color-primary)" radius={[4, 4, 0, 0]} />
          </BarChart>
        </ResponsiveContainer>
      </div>
      <div className="chart-container card">
        <h2>Training Load Over Time</h2>
        <ResponsiveContainer width="100%" height={300}>
          <LineChart data={chartData}>
            <CartesianGrid strokeDasharray="3 3" />
            <XAxis dataKey="label" />
            <YAxis>
              <Label value="Load" angle={-90} position="insideLeft" style={{ textAnchor: 'middle', fill: '#6b7280' }} />
            </YAxis>
            <Tooltip />
            <Line
              type="monotone"
              dataKey="training_load"
              stroke="var(--color-secondary)"
              strokeWidth={2}
              dot={{ r: 4 }}
            />
          </LineChart>
        </ResponsiveContainer>
      </div>
      {activePainCharts.map((chart) => {
        const loc = painData.locations[chart.location];
        return (
          <div key={chart.location} className="chart-container card">
            <h2>{chart.title}</h2>
            <ResponsiveContainer width="100%" height={300}>
              <LineChart data={loc.points}>
                <CartesianGrid strokeDasharray="3 3" />
                <XAxis dataKey="label" padding={{ left: 20, right: 20 }} />
                <YAxis domain={[0, 10]}>
                  <Label value="Pain" angle={-90} position="insideLeft" style={{ textAnchor: 'middle', fill: '#6b7280' }} />
                </YAxis>
                <Tooltip
                  content={({ payload }) => {
                    if (!payload || payload.length === 0) return null;
                    const pt = payload[0].payload;
                    const items = payload.filter((p) => p.value != null);
                    return (
                      <div className="pain-tooltip">
                        <p>{pt.date}</p>
                        {items.map((p) => (
                          <p key={p.name} style={{ color: p.color }}>{p.name}: {p.value}</p>
                        ))}
                      </div>
                    );
                  }}
                />
                <Legend wrapperStyle={{ paddingLeft: '65px' }} />
                <Line
                  name="During"
                  dataKey="during"
                  stroke={PAIN_DURING_COLOR}
                  strokeWidth={1.5}
                  dot={{ fill: PAIN_DURING_COLOR, r: 4 }}
                  connectNulls
                />
                <Line
                  name="After"
                  dataKey="after"
                  stroke={PAIN_AFTER_COLOR}
                  strokeWidth={1.5}
                  strokeDasharray="5 3"
                  dot={{ fill: PAIN_AFTER_COLOR, r: 4 }}
                  connectNulls
                />
              </LineChart>
            </ResponsiveContainer>
          </div>
        );
      })}
    </div>
  );
}

export default Charts;