[OCaml] High Intensity Training Online
style comma-separate progress deltas; harmonize the calendar widget
Join a progress delta with a comma ("+2 kg, +1 rep") instead of a middot, which read oddly. Restructure the calendar into one standard bordered widget: a header row with prev/next arrows flanking the month and year dropdowns, a Today link, then the weekday grid.
lib/presenter/pages_logbook.ml
@@ -152,7 +152,7 @@
152
152
(if abs reps = 1 then "" else "s");
153
153
]
154
154
in
155
Removed:
if pieces = [] then "no change" else String.concat " · " pieces
155
Added:
if pieces = [] then "no change" else String.concat ", " pieces
156
156
157
157
(* The colour class for a per-exercise verdict: progress oxblood, regress green,
158
158
stagnation brass. A first performance (no verdict) is neutral. *)
@@ -197,7 +197,7 @@
197
197
(if abs reps = 1 then "" else "s");
198
198
]
199
199
in
200
Removed:
if pieces = [] then "no change" else String.concat " · " pieces
200
Added:
if pieces = [] then "no change" else String.concat ", " pieces
201
201
| _ -> "first performance"
202
202
203
203
let imported_effort_line (e : View_model.Imported_effort.t) =
@@ -304,23 +304,6 @@
304
304
@ if value = selected then [ Dream_html.attr "selected" ] else [])
305
305
[ txt "%s" label ]
306
306
in
307
Removed:
let picker =
308
Removed:
tag "form"
309
Removed:
[ action Routes.logbook; class_ "calendar-picker" ]
310
Removed:
[
311
Removed:
tag "select"
312
Removed:
[ name "month"; Dream_html.string_attr "aria-label" "Month" ]
313
Removed:
(List.map
314
Removed:
(fun (n, nm) -> option_ ~selected:cal.selected_month n nm)
315
Removed:
cal.months);
316
Removed:
tag "select"
317
Removed:
[ name "year"; Dream_html.string_attr "aria-label" "Year" ]
318
Removed:
(List.map
319
Removed:
(fun y -> option_ ~selected:cal.selected_year y (string_of_int y))
320
Removed:
cal.years);
321
Removed:
tag "button" [ type_ "submit" ] [ txt "Show" ];
322
Removed:
]
323
Removed:
in
324
307
let prev_month =
325
308
if cal.selected_month = 1 then (cal.selected_year - 1, 12)
326
309
else (cal.selected_year, cal.selected_month - 1)
@@ -329,38 +312,60 @@
329
312
if cal.selected_month = 12 then (cal.selected_year + 1, 1)
330
313
else (cal.selected_year, cal.selected_month + 1)
331
314
in
332
Removed:
let nav_link ~label (y, m) rel =
315
Added:
let arrow ~label (y, m) rel =
333
316
tag "a"
334
317
[
335
318
Dream_html.string_attr "href" "/logbook?year=%d&month=%d" y m;
336
Removed:
class_ "calendar-nav-link";
319
Added:
class_ "calendar-arrow";
337
320
Dream_html.attr "data-hito-app-link";
338
321
Dream_html.string_attr "aria-label" "%s" rel;
339
322
]
340
323
[ txt "%s" label ]
341
324
in
342
Removed:
let nav =
343
Removed:
tag "div"
344
Removed:
[ class_ "calendar-nav" ]
325
Added:
(* The picker doubles as the title: month and year dropdowns between the prev
326
Added:
and next arrows, submitting on Go. *)
327
Added:
let picker =
328
Added:
tag "form"
329
Added:
[ action Routes.logbook; class_ "calendar-picker" ]
345
330
[
346
Removed:
nav_link ~label:"‹" prev_month "Previous month";
347
Removed:
tag "a"
348
Removed:
[
349
Removed:
href Routes.logbook;
350
Removed:
class_ "calendar-nav-now";
351
Removed:
Dream_html.attr "data-hito-app-link";
352
Removed:
]
353
Removed:
[ txt "Now" ];
354
Removed:
nav_link ~label:"›" next_month "Next month";
331
Added:
tag "select"
332
Added:
[ name "month"; Dream_html.string_attr "aria-label" "Month" ]
333
Added:
(List.map
334
Added:
(fun (n, nm) -> option_ ~selected:cal.selected_month n nm)
335
Added:
cal.months);
336
Added:
tag "select"
337
Added:
[ name "year"; Dream_html.string_attr "aria-label" "Year" ]
338
Added:
(List.map
339
Added:
(fun y -> option_ ~selected:cal.selected_year y (string_of_int y))
340
Added:
cal.years);
341
Added:
tag "button"
342
Added:
[ type_ "submit"; Dream_html.string_attr "aria-label" "Go" ]
343
Added:
[ txt "Go" ];
355
344
]
356
345
in
357
346
tag "div"
358
Removed:
[ class_ "calendar-block" ]
347
Added:
[ class_ "calendar" ]
359
348
[
360
Removed:
nav;
361
Removed:
picker;
349
Added:
tag "div"
350
Added:
[ class_ "calendar-header" ]
351
Added:
[
352
Added:
arrow ~label:"‹" prev_month "Previous month";
353
Added:
picker;
354
Added:
arrow ~label:"›" next_month "Next month";
355
Added:
];
356
Added:
tag "div"
357
Added:
[ class_ "calendar-subhead" ]
358
Added:
[
359
Added:
tag "a"
360
Added:
[
361
Added:
href Routes.logbook;
362
Added:
class_ "calendar-today-link";
363
Added:
Dream_html.attr "data-hito-app-link";
364
Added:
]
365
Added:
[ txt "Today" ];
366
Added:
];
362
367
tag "table"
363
Removed:
[ class_ "calendar" ]
368
Added:
[ class_ "calendar-grid" ]
364
369
[
365
370
tag "thead" []
366
371
[
lib/web/assets/hito.css
@@ -733,53 +733,70 @@
733
733
/* The action bar under the logbook heading. */
734
734
.logbook-actions-bar { margin: 0.5rem 0 1rem; }
735
735
736
Removed:
/* The month calendar above the logbook. A fixed seven-column grid; days with a
737
Removed:
workout are filled oxblood, today is ringed. */
738
Removed:
.calendar-block {
736
Added:
/* The month calendar above the logbook: a self-contained widget. A bordered
737
Added:
card holds a header row (prev arrow, month/year picker, next arrow), a Today
738
Added:
link, and the seven-column day grid. Days with a workout are filled oxblood;
739
Added:
today is ringed. */
740
Added:
.calendar {
739
741
max-width: 22rem;
740
742
margin: 0 auto 1.5rem;
743
Added:
padding: 0.6rem 0.75rem 0.75rem;
744
Added:
border: 1px solid var(--rule-strong);
745
Added:
border-radius: var(--radius);
746
Added:
background: var(--paper-raised);
741
747
}
742
Removed:
.calendar-picker {
748
Added:
.calendar-header {
743
749
display: flex;
744
Removed:
gap: 0.5rem;
745
750
align-items: center;
746
Removed:
margin-bottom: 0.5rem;
751
Added:
gap: 0.4rem;
747
752
}
748
Removed:
.calendar-picker select { flex: 1 1 auto; }
749
Removed:
.calendar-picker button { flex: 0 0 auto; }
750
Removed:
.calendar-nav {
751
Removed:
display: flex;
752
Removed:
align-items: center;
753
Removed:
justify-content: space-between;
754
Removed:
gap: 0.5rem;
755
Removed:
margin-bottom: 0.4rem;
756
Removed:
}
757
Removed:
.calendar-nav-link,
758
Removed:
.calendar-nav-now {
753
Added:
.calendar-arrow {
759
754
display: inline-flex;
760
755
align-items: center;
761
Removed:
min-height: 2.25rem;
762
Removed:
padding: 0 0.6rem;
756
Added:
justify-content: center;
757
Added:
flex: 0 0 auto;
758
Added:
width: 2.25rem;
759
Added:
height: 2.25rem;
763
760
border-radius: var(--radius);
761
Added:
color: var(--ink);
762
Added:
font-size: 1.35rem;
763
Added:
line-height: 1;
764
Added:
text-decoration: none;
765
Added:
}
766
Added:
.calendar-arrow:hover { background: var(--hover-surface); }
767
Added:
.calendar-picker {
768
Added:
display: flex;
769
Added:
flex: 1 1 auto;
770
Added:
gap: 0.35rem;
771
Added:
align-items: center;
772
Added:
justify-content: center;
773
Added:
}
774
Added:
.calendar-picker select { flex: 1 1 auto; min-width: 0; }
775
Added:
.calendar-picker button { flex: 0 0 auto; }
776
Added:
.calendar-subhead {
777
Added:
margin: 0.35rem 0 0.5rem;
778
Added:
text-align: center;
779
Added:
}
780
Added:
.calendar-today-link {
764
781
color: var(--oxblood-dark);
782
Added:
font-size: var(--font-size-small);
765
783
font-weight: 700;
766
784
text-decoration: none;
767
785
}
768
Removed:
.calendar-nav-link:hover,
769
Removed:
.calendar-nav-now:hover { background: var(--hover-surface); }
770
Removed:
.calendar {
786
Added:
.calendar-today-link:hover { text-decoration: underline; }
787
Added:
.calendar-grid {
771
788
width: 100%;
772
789
border-collapse: collapse;
773
790
table-layout: fixed;
774
791
}
775
Removed:
.calendar th {
792
Added:
.calendar-grid th {
776
793
padding: 0.25rem 0;
777
794
color: var(--muted-ink);
778
795
font: 700 var(--font-size-small)/1 var(--sans);
779
796
text-align: center;
780
797
}
781
Removed:
.calendar td {
782
Removed:
padding: 0.15rem;
798
Added:
.calendar-grid td {
799
Added:
padding: 0.1rem;
783
800
text-align: center;
784
801
vertical-align: middle;
785
802
}
test/web/logbook.ml
@@ -104,7 +104,7 @@
104
104
"18 min";
105
105
"Laterals";
106
106
"10 kg × 8 → 12 kg × 9";
107
Removed:
"+2 kg · +1 rep";
107
Added:
"+2 kg, +1 rep";
108
108
"verdict-progress";
109
109
] );
110
110
( "each saved workout row has an edit and confirmation-gated delete menu",
@@ -760,8 +760,8 @@
760
760
"a month/year picker is present" true
761
761
(Option.is_some (index_from ~needle:"calendar-picker" page 0));
762
762
Alcotest.(check bool)
763
Removed:
"prev/next/now navigation is present" true
764
Removed:
(Option.is_some (index_from ~needle:"calendar-nav" page 0));
763
Added:
"prev/next/today navigation is present" true
764
Added:
(Option.is_some (index_from ~needle:"calendar-arrow" page 0));
765
765
Alcotest.(check bool)
766
766
"a workout day is highlighted" true
767
767
(Option.is_some (index_from ~needle:"calendar-workout" page 0));