Add Feel slider to Rest Day section and include F: in rest markdown#23
Add Feel slider to Rest Day section and include F: in rest markdown#23
Conversation
The spec requires the Feel (W/P/N/G/S) slider to appear in the Rest Day
section for rest entries, and for the selected feel value to be output
as `F: <value>` in the rest markdown alongside recovery metrics and weight.
Updates utils.test.ts to match: adds two new feel tests for rest output
and removes the stale `expect(md).not.toContain('F:')` assertion that
treated feel as cycling-only.
https://claude.ai/code/session_01FXEJfqBTt5cUkoQkFmrzEo
Greptile SummaryThis PR extends the Feel (W/P/N/G/S) slider to the Rest Day section of the form and emits
Confidence Score: 3/5The markdown generation and tests are correct, but the Rest Day feel slider has a visual bug that could confuse users about whether feel is actually set. When a rest entry has no feel value set, the slider thumb renders at the Weak position (index 0 clamped to min=1) while the label simultaneously shows Normal (N). A user who hasn't touched the slider sees contradictory signals and may not realize feel is actually undefined and will be absent from the markdown output. client/src/pages/home.tsx — specifically the new Feel FormField block added to the Rest Day section and the section's hasData computation. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[User opens form] --> B{entryType?}
B -- cycling --> C[Core Metrics section\nGoal / RPE / Feel required]
B -- rest --> D[Rest Day section\nWeight + Feel optional + restNotes]
B -- other --> E[Activity section\nactivityGoal + activityNotes]
D --> F{feel defined?}
F -- yes --> G[Slider at correct position\nLabel shows value]
F -- no / undefined --> H[findIndex returns -1\nvalue=0, min=1 clamped to Weak\nLabel shows Normal N]
G --> I[generateRestMarkdown]
H --> I
I --> J{data.feel truthy?}
J -- yes --> K[append F: value]
J -- no --> L[omit F: line]
K --> M[Markdown output]
L --> M
|
| <FormField | ||
| control={form.control} | ||
| name="feel" | ||
| render={({ field }) => ( | ||
| <FormItem> | ||
| <FormLabel className="flex items-center gap-2"> | ||
| <Smile className="w-4 h-4 text-green-500" /> | ||
| F (Feel): {feelOptions.find(opt => opt.value === field.value)?.label || 'Normal (N)'} | ||
| </FormLabel> | ||
| <FormControl> | ||
| <div className="px-2 py-4"> | ||
| <Slider | ||
| value={[feelOptions.findIndex(opt => opt.value === field.value) + 1]} | ||
| onValueChange={(value) => field.onChange(feelOptions[value[0] - 1]?.value || 'N')} | ||
| max={5} | ||
| min={1} | ||
| step={1} | ||
| className="w-full" | ||
| /> | ||
| <div className="flex justify-between text-xs text-gray-500 mt-2"> | ||
| <span>Weak</span> | ||
| <span>Poor</span> | ||
| <span>Normal</span> | ||
| <span>Good</span> | ||
| <span>Strong</span> | ||
| </div> | ||
| </div> | ||
| </FormControl> | ||
| <FormMessage /> | ||
| </FormItem> | ||
| )} | ||
| /> | ||
|
|
||
| <FormField | ||
| control={form.control} | ||
| name="restNotes" |
There was a problem hiding this comment.
When
feel is undefined (the user hasn't moved the slider on a rest entry), feelOptions.findIndex(...) returns -1, making value={[0]}. Because min={1}, Radix clamps the thumb to position 1 (Weak) — yet the label fallback shows "Normal (N)". The thumb and label disagree, misleading the user about what value (if any) will be written to the markdown. The cycling slider never encounters this because feel is required there; rest entries need an explicit "not set" state.
| <FormField | |
| control={form.control} | |
| name="feel" | |
| render={({ field }) => ( | |
| <FormItem> | |
| <FormLabel className="flex items-center gap-2"> | |
| <Smile className="w-4 h-4 text-green-500" /> | |
| F (Feel): {feelOptions.find(opt => opt.value === field.value)?.label || 'Normal (N)'} | |
| </FormLabel> | |
| <FormControl> | |
| <div className="px-2 py-4"> | |
| <Slider | |
| value={[feelOptions.findIndex(opt => opt.value === field.value) + 1]} | |
| onValueChange={(value) => field.onChange(feelOptions[value[0] - 1]?.value || 'N')} | |
| max={5} | |
| min={1} | |
| step={1} | |
| className="w-full" | |
| /> | |
| <div className="flex justify-between text-xs text-gray-500 mt-2"> | |
| <span>Weak</span> | |
| <span>Poor</span> | |
| <span>Normal</span> | |
| <span>Good</span> | |
| <span>Strong</span> | |
| </div> | |
| </div> | |
| </FormControl> | |
| <FormMessage /> | |
| </FormItem> | |
| )} | |
| /> | |
| <FormField | |
| control={form.control} | |
| name="restNotes" | |
| <FormField | |
| control={form.control} | |
| name="feel" | |
| render={({ field }) => ( | |
| <FormItem> | |
| <FormLabel className="flex items-center gap-2"> | |
| <Smile className="w-4 h-4 text-green-500" /> | |
| F (Feel): {feelOptions.find(opt => opt.value === field.value)?.label || 'Not set'} | |
| </FormLabel> | |
| <FormControl> | |
| <div className="px-2 py-4"> | |
| <Slider | |
| value={[Math.max(1, feelOptions.findIndex(opt => opt.value === field.value) + 1)]} | |
| onValueChange={(value) => field.onChange(feelOptions[value[0] - 1]?.value || 'N')} |
The spec requires the Feel (W/P/N/G/S) slider to appear in the Rest Day
section for rest entries, and for the selected feel value to be output
as
F: <value>in the rest markdown alongside recovery metrics and weight.Updates utils.test.ts to match: adds two new feel tests for rest output
and removes the stale
expect(md).not.toContain('F:')assertion thattreated feel as cycling-only.
https://claude.ai/code/session_01FXEJfqBTt5cUkoQkFmrzEo