[OCaml] High Intensity Training Online
feat standardize logbook nav and add a profile button
The logbook tab always reads 'Logbook' rather than the username. The username moves to a dedicated profile control in the masthead that links to the logbook; on mobile it is the top-bar profile button the bottom nav had no room for, and it joins the inline nav at desktop widths.
ENHANCEMENTS.org
@@ -66,7 +66,7 @@
66
66
When cancellation is confirmed, return the Current Workout tab to the Routine tab, save the workout as a partial logbook entry, and keep it editable later.
67
67
** DONE Style form dropdowns to match the UI
68
68
Style form dropdown menus so they match the overall UI aesthetic.
69
Removed:
** TODO Standardize Logbook navigation and add a mobile profile button
69
Added:
** DONE Standardize Logbook navigation and add a mobile profile button
70
70
Always label the logbook tab `Logbook`. Add a dedicated profile button for the user name in the mobile top navigation bar.
71
71
** TODO Use shared load and reps column headings
72
72
In exercise logging fieldsets, show Load and Reps as column headings instead of repeating them above every input in a multi-exercise group.
lib/web/assets/hito.css
@@ -92,6 +92,30 @@
92
92
text-decoration: none;
93
93
}
94
94
95
Added:
/* The profile control: the signed-in username, linking to the logbook. Right
96
Added:
of the brand, it is the mobile top-bar profile button and joins the inline
97
Added:
nav at desktop widths. It reads as a compact pill. */
98
Added:
.profile {
99
Added:
display: inline-flex;
100
Added:
align-items: center;
101
Added:
min-height: 2.75rem;
102
Added:
padding: 0.5rem 0.9rem;
103
Added:
border: 1px solid var(--rule-strong);
104
Added:
border-radius: var(--radius);
105
Added:
color: var(--ink);
106
Added:
font-weight: 700;
107
Added:
text-decoration: none;
108
Added:
}
109
Added:
.profile:hover {
110
Added:
color: var(--oxblood-dark);
111
Added:
background: #ece4d5;
112
Added:
}
113
Added:
.profile[aria-current] {
114
Added:
color: var(--paper-raised);
115
Added:
background: var(--oxblood);
116
Added:
border-color: var(--oxblood-dark);
117
Added:
}
118
Added:
95
119
.eyebrow,
96
120
.ledger-meta {
97
121
font: 700 var(--font-size-small)/1.35 var(--sans);
lib/web/pages.ml
@@ -40,15 +40,37 @@
40
40
in
41
41
(* Three always-available actions. The middle slot points to the routine
42
42
normally and to the workout in progress while logging, so a running
43
Removed:
workout stays one tap away without adding a fourth destination. *)
44
Removed:
let navigation_links username =
43
Added:
workout stays one tap away without adding a fourth destination. The logbook
44
Added:
tab always reads "Logbook"; the username lives on its own profile control. *)
45
Added:
let navigation_links () =
45
46
[
46
47
nav_link "home" Routes.home "Home";
47
48
(if logging then nav_link "workout" Routes.workout "Current workout"
48
49
else nav_link "routine" Routes.routine "Routine");
49
Removed:
nav_link "logbook" Routes.logbook username;
50
Added:
nav_link "logbook" Routes.logbook "Logbook";
50
51
]
51
52
in
53
Added:
(* The profile control names the signed-in trainee and links to the logbook.
54
Added:
It sits in the masthead, and on mobile it is the top-bar profile button the
55
Added:
bottom nav has no room for. *)
56
Added:
let profile_area =
57
Added:
match trainee with
58
Added:
| Some (trainee : Trainee.t) ->
59
Added:
let attrs =
60
Added:
[ class_ "profile"; href Routes.logbook ]
61
Added:
@ if spa_client then [ Dream_html.attr "data-hito-app-link" ] else []
62
Added:
in
63
Added:
let attrs =
64
Added:
if String.equal "logbook" active then
65
Added:
Dream_html.string_attr "aria-current" "page" :: attrs
66
Added:
else attrs
67
Added:
in
68
Added:
[
69
Added:
tag "a" attrs
70
Added:
[ txt "%s" (Trainee.username_to_string trainee.username) ];
71
Added:
]
72
Added:
| None -> []
73
Added:
in
52
74
let account_area =
53
75
match (trainee, request) with
54
76
| Some _, Some request ->
@@ -73,24 +95,20 @@
73
95
in
74
96
let primary_nav =
75
97
match (trainee, request) with
76
Removed:
| Some (trainee : Trainee.t), Some _ ->
77
Removed:
[
78
Removed:
tag "nav"
79
Removed:
[ class_ "primary-nav" ]
80
Removed:
(navigation_links (Trainee.username_to_string trainee.username));
81
Removed:
]
98
Added:
| Some (_ : Trainee.t), Some _ ->
99
Added:
[ tag "nav" [ class_ "primary-nav" ] (navigation_links ()) ]
82
100
| _ -> []
83
101
in
84
102
let bottom_nav =
85
103
match (trainee, request) with
86
Removed:
| Some (trainee : Trainee.t), Some _ ->
104
Added:
| Some (_ : Trainee.t), Some _ ->
87
105
[
88
106
tag "nav"
89
107
[
90
108
class_ "bottom-nav";
91
109
Dream_html.string_attr "aria-label" "Mobile navigation";
92
110
]
93
Removed:
(navigation_links (Trainee.username_to_string trainee.username));
111
Added:
(navigation_links ());
94
112
]
95
113
| _ -> []
96
114
in
@@ -137,7 +155,7 @@
137
155
else [])
138
156
[ txt "hito" ];
139
157
]
140
Removed:
@ primary_nav @ account_area);
158
Added:
@ primary_nav @ profile_area @ account_area);
141
159
tag "main"
142
160
[ id "main-content"; tabindex "-1" ]
143
161
[
test/test_web.ml
@@ -173,8 +173,12 @@
173
173
"starts the overview with a page-level heading" true
174
174
(contains ~substring:"<h1>Routines</h1>" (body overview));
175
175
Alcotest.(check bool)
176
Removed:
"shows the username in logbook navigation" true
177
Removed:
(contains ~substring:"href=\"/logbook\"" (body overview)
176
Added:
"labels the logbook tab Logbook" true
177
Added:
(contains ~substring:">Logbook</a>" (body overview));
178
Added:
Alcotest.(check bool)
179
Added:
"shows the username on a profile control linking to the logbook"
180
Added:
true
181
Added:
(contains ~substring:"class=\"profile\"" (body overview)
178
182
&& contains ~substring:">lifter</a>" (body overview));
179
183
Alcotest.(check bool)
180
184
"omits the header username" false