File size: 6,414 Bytes
3d4aa49
 
 
359f0ff
6bddf9c
 
359f0ff
 
49699c7
 
 
 
 
 
 
 
 
 
 
 
 
3d4aa49
 
 
 
 
 
49699c7
7b3fdb0
 
 
3d4aa49
 
 
 
 
 
 
359f0ff
3d4aa49
 
 
 
49699c7
359f0ff
 
7b3fdb0
 
 
 
 
 
359f0ff
 
 
3d4aa49
 
 
 
49699c7
7b3fdb0
 
 
3d4aa49
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49699c7
 
 
3d4aa49
 
 
 
 
 
 
 
 
 
49699c7
 
3d4aa49
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49699c7
 
 
 
 
359f0ff
7b3fdb0
 
bd289d0
7b3fdb0
 
 
 
 
 
 
 
 
 
 
 
 
bd289d0
7b3fdb0
359f0ff
7b3fdb0
 
 
bd289d0
7b3fdb0
bd289d0
7b3fdb0
bd289d0
7b3fdb0
 
 
 
 
 
 
bd289d0
 
 
7b3fdb0
bd289d0
7b3fdb0
bd289d0
7b3fdb0
 
 
 
 
 
 
bd289d0
 
 
7b3fdb0
49699c7
 
 
 
 
 
 
 
 
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
import { useState } from 'react';
import './RunForm.css';

const INJURY_LOCATIONS = [
  { key: 'left_knee', label: 'Left Knee' },
  { key: 'right_knee', label: 'Right Knee' },
];

const RPE_DESCRIPTIONS = {
  1: { label: 'Very easy', detail: 'Gentle warm-up, recovery jog' },
  2: { label: 'Very easy', detail: 'Full conversation easy' },
  3: { label: 'Easy', detail: 'Easy run, full sentences' },
  4: { label: 'Comfortable', detail: 'Can talk in full sentences' },
  5: { label: 'Moderate', detail: 'Talking becomes slightly harder' },
  6: { label: 'Comfortably hard', detail: 'Tempo effort, short sentences only' },
  7: { label: 'Hard', detail: 'Threshold pace, conversation difficult' },
  8: { label: 'Very hard', detail: 'Interval pace, a few words at a time' },
  9: { label: 'Extremely hard', detail: 'Near max, sustain only briefly' },
  10: { label: 'All-out', detail: 'Max sprint, lasts only seconds' },
};

function RunForm({ onAddRun }) {
  const today = new Date().toISOString().split('T')[0];
  const [date, setDate] = useState(today);
  const [distance, setDistance] = useState('');
  const [time, setTime] = useState('');
  const [rpe, setRpe] = useState(5);
  const [notes, setNotes] = useState('');
  const [injuries, setInjuries] = useState(
    Object.fromEntries(INJURY_LOCATIONS.map((loc) => [loc.key, { enabled: false, during: '', after: '' }]))
  );

  function handleSubmit(e) {
    e.preventDefault();
    const dist = parseFloat(distance);
    const mins = parseFloat(time);
    if (!date || isNaN(dist) || dist <= 0 || isNaN(mins) || mins <= 0) return;

    const runData = {
      date,
      distance_km: dist,
      time_minutes: mins,
      rpe: Number(rpe),
      notes: notes.trim(),
    };

    for (const loc of INJURY_LOCATIONS) {
      const inj = injuries[loc.key];
      if (inj.enabled) {
        runData[`${loc.key}_during`] = inj.during ? Number(inj.during) : null;
        runData[`${loc.key}_after`] = inj.after ? Number(inj.after) : null;
      }
    }

    onAddRun(runData);

    setDistance('');
    setTime('');
    setRpe(5);
    setNotes('');
    setInjuries(
      Object.fromEntries(INJURY_LOCATIONS.map((loc) => [loc.key, { enabled: false, during: '', after: '' }]))
    );
  }

  return (
    <form className="run-form card" onSubmit={handleSubmit}>
      <h2>Log a Run</h2>
      <div className="form-group">
        <label htmlFor="run-date">Date</label>
        <input
          id="run-date"
          type="date"
          value={date}
          onChange={(e) => setDate(e.target.value)}
          required
        />
      </div>
      <div className="form-group">
        <label htmlFor="run-distance">Distance (km)</label>
        <input
          id="run-distance"
          type="number"
          step="0.01"
          min="0.01"
          placeholder="e.g. 5.25"
          value={distance}
          onChange={(e) => setDistance(e.target.value)}
          required
        />
      </div>
      <div className="form-group">
        <label htmlFor="run-time">Time (minutes)</label>
        <input
          id="run-time"
          type="number"
          step="0.01"
          min="0.01"
          placeholder="e.g. 30"
          value={time}
          onChange={(e) => setTime(e.target.value)}
          required
        />
      </div>
      <div className="form-group">
        <label htmlFor="run-rpe">Effort (RPE): <strong>{rpe}</strong></label>
        <input
          id="run-rpe"
          type="range"
          min="1"
          max="10"
          value={rpe}
          onChange={(e) => setRpe(e.target.value)}
        />
        <div className="rpe-labels">
          <span>Easy</span>
          <span>Max</span>
        </div>
        <div className="rpe-description">
          <span className="rpe-effort">{RPE_DESCRIPTIONS[rpe].label}</span>
          <span className="rpe-detail">{RPE_DESCRIPTIONS[rpe].detail}</span>
        </div>
      </div>
      <div className="form-group">
        <label>Injury Tracking</label>
        <div className="injury-checkboxes">
          {INJURY_LOCATIONS.map((loc) => (
            <label key={loc.key} className="injury-checkbox-label">
              <input
                type="checkbox"
                checked={injuries[loc.key].enabled}
                onChange={(e) =>
                  setInjuries((prev) => ({
                    ...prev,
                    [loc.key]: { ...prev[loc.key], enabled: e.target.checked, ...(!e.target.checked && { during: '', after: '' }) },
                  }))
                }
              />
              {loc.label}
            </label>
          ))}
        </div>
      </div>
      {INJURY_LOCATIONS.filter((loc) => injuries[loc.key].enabled).map((loc) => (
        <div key={loc.key} className="pain-inputs">
          <span className="pain-location-label">{loc.label}</span>
          <div className="form-group pain-field">
            <label htmlFor={`pain-during-${loc.key}`}>Pain During (1–10)</label>
            <input
              id={`pain-during-${loc.key}`}
              type="number" min="1" max="10" step="1" placeholder="1–10"
              value={injuries[loc.key].during}
              onChange={(e) =>
                setInjuries((prev) => ({
                  ...prev,
                  [loc.key]: { ...prev[loc.key], during: e.target.value },
                }))
              }
            />
          </div>
          <div className="form-group pain-field">
            <label htmlFor={`pain-after-${loc.key}`}>Pain After (1–10)</label>
            <input
              id={`pain-after-${loc.key}`}
              type="number" min="1" max="10" step="1" placeholder="1–10"
              value={injuries[loc.key].after}
              onChange={(e) =>
                setInjuries((prev) => ({
                  ...prev,
                  [loc.key]: { ...prev[loc.key], after: e.target.value },
                }))
              }
            />
          </div>
        </div>
      ))}
      <div className="form-group">
        <label htmlFor="run-notes">Notes</label>
        <textarea
          id="run-notes"
          placeholder="How did it feel? Any observations..."
          value={notes}
          onChange={(e) => setNotes(e.target.value)}
          rows="3"
        />
      </div>
      <button type="submit" className="btn-primary">Add Run</button>
    </form>
  );
}

export default RunForm;