[OCaml] High Intensity Training Online
refactor remove unused workout inline errors
The workout flow reports validation errors through post-redirect-get flash messages, so the unreachable inline-error API only duplicated wording.
lib/presenter/page_kit.ml
@@ -21,15 +21,3 @@
21
21
tag "p"
22
22
[ class_ "warn"; Dream_html.string_attr "role" "alert" ]
23
23
[ Dream_html.txt "%s" message ]
24
Removed:
25
Removed:
let error_text = function
26
Removed:
| "error.required" -> "Enter a value."
27
Removed:
| "error.expected.number" -> "Enter a valid number."
28
Removed:
| "error.expected.int" -> "Enter a valid whole number."
29
Removed:
| "error.reps" -> "Enter at least one repetition."
30
Removed:
| "error.load" -> "Enter a finite, nonnegative load."
31
Removed:
| "error.extension" -> "Select one of the offered endings."
32
Removed:
| key -> key
33
Removed:
34
Removed:
let errors_to_text errors =
35
Removed:
errors |> List.map (fun (_, key) -> error_text key) |> String.concat " "
lib/presenter/pages.mli
@@ -35,8 +35,6 @@
35
35
val workout :
36
36
Dream.request ->
37
37
viewer:View_model.Viewer.t ->
38
Removed:
?errors:(string * string) list ->
39
Removed:
?editing:int ->
40
38
?workout_started:int ->
41
39
record_id:string option ->
42
40
active_slot:int ->
@@ -52,7 +50,8 @@
52
50
and [Some id] for a saved history record. [workout_started] drives the nav
53
51
timer: a live workout uses its own start, so it is ignored there; a
54
52
saved-record view passes the epoch of a separate workout in progress, if
55
Removed:
any. *)
53
Added:
any. A refused record or correction is reported through the flash message on
54
Added:
the post-redirect-get reload, not inline. *)
56
55
57
56
val feedback_factors : (string * string) list
58
57
(** Ordered factor codes and labels. Each is a radio group in the feedback form
lib/presenter/pages_workout.ml
@@ -2,17 +2,7 @@
2
2
open Page_kit
3
3
open Page_shell
4
4
5
Removed:
let error_id input_id = input_id ^ "-error"
6
Removed:
7
Removed:
let extension_select ?(selected = "") ?(invalid = false) ~input_id () =
8
Removed:
let error_attrs =
9
Removed:
if invalid then
10
Removed:
[
11
Removed:
Dream_html.string_attr "aria-invalid" "true";
12
Removed:
Dream_html.string_attr "aria-describedby" "%s" (error_id input_id);
13
Removed:
]
14
Removed:
else []
15
Removed:
in
5
Added:
let extension_select ?(selected = "") ~input_id () =
16
6
let option (code : string) label =
17
7
let attrs = [ Dream_html.string_attr "value" "%s" code ] in
18
8
let attrs =
@@ -22,8 +12,7 @@
22
12
tag "option" attrs [ txt "%s" label ]
23
13
in
24
14
tag "select"
25
Removed:
([ name "extension"; Dream_html.string_attr "id" "%s" input_id ]
26
Removed:
@ error_attrs)
15
Added:
[ name "extension"; Dream_html.string_attr "id" "%s" input_id ]
27
16
[
28
17
option "" "to positive failure";
29
18
option "forced" "then forced reps";
@@ -31,26 +20,12 @@
31
20
option "static" "then a static hold";
32
21
]
33
22
34
Removed:
let error_for ~input_id (field : string) (errors : (string * string) list) =
35
Removed:
match List.assoc_opt field errors with
36
Removed:
| None -> []
37
Removed:
| Some error ->
38
Removed:
[
39
Removed:
tag "p"
40
Removed:
[
41
Removed:
Dream_html.string_attr "id" "%s" (error_id input_id);
42
Removed:
class_ "warn";
43
Removed:
Dream_html.string_attr "role" "alert";
44
Removed:
]
45
Removed:
[ txt "%s" (errors_to_text [ (field, error) ]) ];
46
Removed:
]
47
Removed:
48
23
(* One movement as a grid group: exercise name, load input, reps input. The
49
24
group is a [div] that is [display:contents], so its three cells fall on the
50
25
enclosing grid under the shared Load and Reps headings. A pre-exhaust pair is
51
26
two such groups. *)
52
Removed:
let movement_group ~(errors : (string * string) list) ~name ~load_id ~load_field
53
Removed:
~reps_id ~reps_field ?load_value ?reps_value ?previous ~reps_label () =
27
Added:
let movement_group ~name ~load_id ~load_field ~reps_id ~reps_field ?load_value
28
Added:
?reps_value ?previous ~reps_label () =
54
29
let number_field ~input_id ~field ~value ~step_ ~label =
55
30
tag "div"
56
31
[ class_ "field field-number" ]
@@ -66,13 +41,6 @@
66
41
step "%s" step_;
67
42
required;
68
43
]
69
Removed:
@ (if List.mem_assoc field errors then
70
Removed:
[
71
Removed:
Dream_html.string_attr "aria-invalid" "true";
72
Removed:
Dream_html.string_attr "aria-describedby" "%s"
73
Removed:
(error_id input_id);
74
Removed:
]
75
Removed:
else [])
76
44
@
77
45
match value with
78
46
| None -> []
@@ -103,24 +71,16 @@
103
71
let form_for_stimulus request ~action_path ~slot
104
72
~(movements : View_model.Workout.movement list) ~selected_extension
105
73
~selected_extension_amount ?(enhanced = false) ?(submit = "Record")
106
Removed:
?(correction = false) ~(errors : (string * string) list) () =
74
Added:
?(correction = false) () =
107
75
let field_id field = Printf.sprintf "slot-%d-%s" slot field in
108
76
let selected = selected_extension in
109
77
let movement_of (m : View_model.Workout.movement) =
110
Removed:
movement_group ~errors ~name:m.name ~load_id:(field_id m.load_field)
78
Added:
movement_group ~name:m.name ~load_id:(field_id m.load_field)
111
79
~load_field:m.load_field ~reps_id:(field_id m.reps_field)
112
80
~reps_field:m.reps_field ?load_value:m.load_value ?reps_value:m.reps_value
113
81
?previous:m.previous ~reps_label:m.reps_label ()
114
82
in
115
83
let movement_groups = List.map movement_of movements in
116
Removed:
(* The error rows follow the fields present, so a single-exercise slot reports
117
Removed:
load/reps and a pre-exhaust slot reports the iso/comp fields. *)
118
Removed:
let field_names =
119
Removed:
List.concat_map
120
Removed:
(fun (m : View_model.Workout.movement) -> [ m.load_field; m.reps_field ])
121
Removed:
movements
122
Removed:
@ [ "extension"; "extension_amount" ]
123
Removed:
in
124
84
tag "form"
125
85
([ action_path; post_form ]
126
86
@ (if enhanced then [ Dream_html.attr "data-hito-workout-form" ]
@@ -136,22 +96,25 @@
136
96
grid. The per-input labels carry the accessible names, so the two
137
97
visible headings are decorative. The Ending block is separate,
138
98
below the grid. *)
139
Removed:
tag "div"
140
Removed:
[ class_ "logging-grid" ]
141
Removed:
(tag "div"
142
Removed:
[
143
Removed:
class_ "logging-grid-head";
144
Removed:
Dream_html.string_attr "aria-hidden" "true";
145
Removed:
]
146
Removed:
[
147
Removed:
tag "span" [ class_ "logging-grid-head-name" ] [];
148
Removed:
tag "span"
149
Removed:
[ class_ "logging-grid-head-load" ]
150
Removed:
[ txt "Load (kg)" ];
151
Removed:
tag "span" [ class_ "logging-grid-head-reps" ] [ txt "Reps" ];
152
Removed:
]
153
Removed:
:: movement_groups)
154
Removed:
:: tag "div"
99
Added:
[
100
Added:
tag "div"
101
Added:
[ class_ "logging-grid" ]
102
Added:
(tag "div"
103
Added:
[
104
Added:
class_ "logging-grid-head";
105
Added:
Dream_html.string_attr "aria-hidden" "true";
106
Added:
]
107
Added:
[
108
Added:
tag "span" [ class_ "logging-grid-head-name" ] [];
109
Added:
tag "span"
110
Added:
[ class_ "logging-grid-head-load" ]
111
Added:
[ txt "Load (kg)" ];
112
Added:
tag "span" [ class_ "logging-grid-head-reps" ] [ txt "Reps" ];
113
Added:
]
114
Added:
:: movement_groups);
115
Added:
]
116
Added:
@ [
117
Added:
tag "div"
155
118
[ class_ "ending-block" ]
156
119
[
157
120
tag "div"
@@ -163,9 +126,8 @@
163
126
class_ "sr-only";
164
127
]
165
128
[ txt "Ending" ];
166
Removed:
extension_select ~selected
167
Removed:
~invalid:(List.mem_assoc "extension" errors)
168
Removed:
~input_id:(field_id "extension") ();
129
Added:
extension_select ~selected ~input_id:(field_id "extension")
130
Added:
();
169
131
];
170
132
tag "div"
171
133
[ class_ "field field-extension-amount" ]
@@ -206,10 +168,8 @@
206
168
[ txt "seconds" ];
207
169
];
208
170
];
209
Removed:
]
210
Removed:
:: List.concat_map
211
Removed:
(fun field -> error_for ~input_id:(field_id field) field errors)
212
Removed:
field_names
171
Added:
];
172
Added:
]
213
173
@
214
174
if correction then
215
175
[
@@ -237,8 +197,8 @@
237
197
]);
238
198
]
239
199
240
Removed:
let workout request ~viewer ?(errors = []) ?editing ?workout_started ~record_id
241
Removed:
~active_slot (vm : View_model.Workout.t) =
200
Added:
let workout request ~viewer ?workout_started ~record_id ~active_slot
201
Added:
(vm : View_model.Workout.t) =
242
202
let enhanced = Option.is_none record_id in
243
203
(* A live workout view is itself the workout in progress, so its own start
244
204
drives the nav timer. A saved-record view is history: the timer, if any,
@@ -263,10 +223,9 @@
263
223
summary carries the slot's level-two heading: its number and exercise label,
264
224
struck through once recorded. A recorded slot shows a pre-filled correction
265
225
form (which replaces the slot rather than adding volume); an outstanding
266
Removed:
slot shows the record form. Errors render only under the slot being edited. *)
226
Added:
slot shows the record form. *)
267
227
let slot_section (slot : View_model.Workout.slot) =
268
228
let index = slot.index in
269
Removed:
let errors = if editing = Some index then errors else [] in
270
229
let done_ = Option.is_some slot.recorded_summary in
271
230
let body =
272
231
match slot.recorded_summary with
@@ -276,7 +235,7 @@
276
235
~slot:index ~movements:slot.movements
277
236
~selected_extension:slot.selected_extension
278
237
~selected_extension_amount:slot.selected_extension_amount
279
Removed:
~enhanced ~submit:"Save correction" ~correction:true ~errors ();
238
Added:
~enhanced ~submit:"Save correction" ~correction:true ();
280
239
]
281
240
| None ->
282
241
[
@@ -284,7 +243,7 @@
284
243
~slot:index ~movements:slot.movements
285
244
~selected_extension:slot.selected_extension
286
245
~selected_extension_amount:slot.selected_extension_amount
287
Removed:
~enhanced ~errors ();
246
Added:
~enhanced ();
288
247
]
289
248
in
290
249
tag "details"