Spaces:
Sleeping
Sleeping
Replace injury tracking dropdown with checkboxes for left/right knee
Browse filesAllows tracking pain for both knees simultaneously instead of
requiring a single selection from a dropdown.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- src/components/RunForm.css +30 -1
- src/components/RunForm.js +50 -32
src/components/RunForm.css
CHANGED
|
@@ -107,11 +107,40 @@
|
|
| 107 |
box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.15);
|
| 108 |
}
|
| 109 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
.pain-inputs {
|
| 111 |
display: grid;
|
| 112 |
grid-template-columns: 1fr 1fr;
|
| 113 |
gap: 0.75rem;
|
| 114 |
-
margin-bottom: 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 115 |
}
|
| 116 |
|
| 117 |
.btn-primary {
|
|
|
|
| 107 |
box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.15);
|
| 108 |
}
|
| 109 |
|
| 110 |
+
.injury-checkboxes {
|
| 111 |
+
display: flex;
|
| 112 |
+
gap: 1.25rem;
|
| 113 |
+
margin-top: 0.25rem;
|
| 114 |
+
}
|
| 115 |
+
|
| 116 |
+
.injury-checkbox-label {
|
| 117 |
+
display: flex !important;
|
| 118 |
+
align-items: center;
|
| 119 |
+
gap: 0.375rem;
|
| 120 |
+
font-size: 0.875rem;
|
| 121 |
+
font-weight: 500;
|
| 122 |
+
color: var(--color-text);
|
| 123 |
+
cursor: pointer;
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
.injury-checkbox-label input[type="checkbox"] {
|
| 127 |
+
width: auto;
|
| 128 |
+
accent-color: var(--color-primary);
|
| 129 |
+
cursor: pointer;
|
| 130 |
+
}
|
| 131 |
+
|
| 132 |
.pain-inputs {
|
| 133 |
display: grid;
|
| 134 |
grid-template-columns: 1fr 1fr;
|
| 135 |
gap: 0.75rem;
|
| 136 |
+
margin-bottom: 0.75rem;
|
| 137 |
+
}
|
| 138 |
+
|
| 139 |
+
.pain-location-label {
|
| 140 |
+
grid-column: 1 / -1;
|
| 141 |
+
font-size: 0.8rem;
|
| 142 |
+
font-weight: 600;
|
| 143 |
+
color: var(--color-text-muted);
|
| 144 |
}
|
| 145 |
|
| 146 |
.btn-primary {
|
src/components/RunForm.js
CHANGED
|
@@ -26,9 +26,9 @@ function RunForm({ onAddRun }) {
|
|
| 26 |
const [time, setTime] = useState('');
|
| 27 |
const [rpe, setRpe] = useState(5);
|
| 28 |
const [notes, setNotes] = useState('');
|
| 29 |
-
const [
|
| 30 |
-
|
| 31 |
-
|
| 32 |
|
| 33 |
function handleSubmit(e) {
|
| 34 |
e.preventDefault();
|
|
@@ -44,9 +44,12 @@ function RunForm({ onAddRun }) {
|
|
| 44 |
notes: notes.trim(),
|
| 45 |
};
|
| 46 |
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
|
|
|
|
|
|
|
|
|
| 50 |
}
|
| 51 |
|
| 52 |
onAddRun(runData);
|
|
@@ -55,9 +58,9 @@ function RunForm({ onAddRun }) {
|
|
| 55 |
setTime('');
|
| 56 |
setRpe(5);
|
| 57 |
setNotes('');
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
}
|
| 62 |
|
| 63 |
return (
|
|
@@ -119,43 +122,58 @@ function RunForm({ onAddRun }) {
|
|
| 119 |
</div>
|
| 120 |
</div>
|
| 121 |
<div className="form-group">
|
| 122 |
-
<label
|
| 123 |
-
<
|
| 124 |
-
id="injury-location"
|
| 125 |
-
value={injuryLocation}
|
| 126 |
-
onChange={(e) => {
|
| 127 |
-
setInjuryLocation(e.target.value);
|
| 128 |
-
if (!e.target.value) { setPainDuring(''); setPainAfter(''); }
|
| 129 |
-
}}
|
| 130 |
-
>
|
| 131 |
-
<option value="">None</option>
|
| 132 |
{INJURY_LOCATIONS.map((loc) => (
|
| 133 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 134 |
))}
|
| 135 |
-
</
|
| 136 |
</div>
|
| 137 |
-
{
|
| 138 |
-
<div className="pain-inputs">
|
|
|
|
| 139 |
<div className="form-group pain-field">
|
| 140 |
-
<label htmlFor=
|
| 141 |
<input
|
| 142 |
-
id=
|
| 143 |
type="number" min="1" max="10" step="1" placeholder="1β10"
|
| 144 |
-
value={
|
| 145 |
-
onChange={(e) =>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 146 |
/>
|
| 147 |
</div>
|
| 148 |
<div className="form-group pain-field">
|
| 149 |
-
<label htmlFor=
|
| 150 |
<input
|
| 151 |
-
id=
|
| 152 |
type="number" min="1" max="10" step="1" placeholder="1β10"
|
| 153 |
-
value={
|
| 154 |
-
onChange={(e) =>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 155 |
/>
|
| 156 |
</div>
|
| 157 |
</div>
|
| 158 |
-
)}
|
| 159 |
<div className="form-group">
|
| 160 |
<label htmlFor="run-notes">Notes</label>
|
| 161 |
<textarea
|
|
|
|
| 26 |
const [time, setTime] = useState('');
|
| 27 |
const [rpe, setRpe] = useState(5);
|
| 28 |
const [notes, setNotes] = useState('');
|
| 29 |
+
const [injuries, setInjuries] = useState(
|
| 30 |
+
Object.fromEntries(INJURY_LOCATIONS.map((loc) => [loc.key, { enabled: false, during: '', after: '' }]))
|
| 31 |
+
);
|
| 32 |
|
| 33 |
function handleSubmit(e) {
|
| 34 |
e.preventDefault();
|
|
|
|
| 44 |
notes: notes.trim(),
|
| 45 |
};
|
| 46 |
|
| 47 |
+
for (const loc of INJURY_LOCATIONS) {
|
| 48 |
+
const inj = injuries[loc.key];
|
| 49 |
+
if (inj.enabled) {
|
| 50 |
+
runData[`${loc.key}_during`] = inj.during ? Number(inj.during) : null;
|
| 51 |
+
runData[`${loc.key}_after`] = inj.after ? Number(inj.after) : null;
|
| 52 |
+
}
|
| 53 |
}
|
| 54 |
|
| 55 |
onAddRun(runData);
|
|
|
|
| 58 |
setTime('');
|
| 59 |
setRpe(5);
|
| 60 |
setNotes('');
|
| 61 |
+
setInjuries(
|
| 62 |
+
Object.fromEntries(INJURY_LOCATIONS.map((loc) => [loc.key, { enabled: false, during: '', after: '' }]))
|
| 63 |
+
);
|
| 64 |
}
|
| 65 |
|
| 66 |
return (
|
|
|
|
| 122 |
</div>
|
| 123 |
</div>
|
| 124 |
<div className="form-group">
|
| 125 |
+
<label>Injury Tracking</label>
|
| 126 |
+
<div className="injury-checkboxes">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 127 |
{INJURY_LOCATIONS.map((loc) => (
|
| 128 |
+
<label key={loc.key} className="injury-checkbox-label">
|
| 129 |
+
<input
|
| 130 |
+
type="checkbox"
|
| 131 |
+
checked={injuries[loc.key].enabled}
|
| 132 |
+
onChange={(e) =>
|
| 133 |
+
setInjuries((prev) => ({
|
| 134 |
+
...prev,
|
| 135 |
+
[loc.key]: { ...prev[loc.key], enabled: e.target.checked, ...(!e.target.checked && { during: '', after: '' }) },
|
| 136 |
+
}))
|
| 137 |
+
}
|
| 138 |
+
/>
|
| 139 |
+
{loc.label}
|
| 140 |
+
</label>
|
| 141 |
))}
|
| 142 |
+
</div>
|
| 143 |
</div>
|
| 144 |
+
{INJURY_LOCATIONS.filter((loc) => injuries[loc.key].enabled).map((loc) => (
|
| 145 |
+
<div key={loc.key} className="pain-inputs">
|
| 146 |
+
<span className="pain-location-label">{loc.label}</span>
|
| 147 |
<div className="form-group pain-field">
|
| 148 |
+
<label htmlFor={`pain-during-${loc.key}`}>Pain During (1β10)</label>
|
| 149 |
<input
|
| 150 |
+
id={`pain-during-${loc.key}`}
|
| 151 |
type="number" min="1" max="10" step="1" placeholder="1β10"
|
| 152 |
+
value={injuries[loc.key].during}
|
| 153 |
+
onChange={(e) =>
|
| 154 |
+
setInjuries((prev) => ({
|
| 155 |
+
...prev,
|
| 156 |
+
[loc.key]: { ...prev[loc.key], during: e.target.value },
|
| 157 |
+
}))
|
| 158 |
+
}
|
| 159 |
/>
|
| 160 |
</div>
|
| 161 |
<div className="form-group pain-field">
|
| 162 |
+
<label htmlFor={`pain-after-${loc.key}`}>Pain After (1β10)</label>
|
| 163 |
<input
|
| 164 |
+
id={`pain-after-${loc.key}`}
|
| 165 |
type="number" min="1" max="10" step="1" placeholder="1β10"
|
| 166 |
+
value={injuries[loc.key].after}
|
| 167 |
+
onChange={(e) =>
|
| 168 |
+
setInjuries((prev) => ({
|
| 169 |
+
...prev,
|
| 170 |
+
[loc.key]: { ...prev[loc.key], after: e.target.value },
|
| 171 |
+
}))
|
| 172 |
+
}
|
| 173 |
/>
|
| 174 |
</div>
|
| 175 |
</div>
|
| 176 |
+
))}
|
| 177 |
<div className="form-group">
|
| 178 |
<label htmlFor="run-notes">Notes</label>
|
| 179 |
<textarea
|