[OCaml] High Intensity Training Online
1
open Dream_html
2
open Page_kit
3
open Page_shell
4
5
(* The review of a provisional import batch. It renders the parse warnings, then
6
each workout with its sets, blockers, and forms. Every form is a plain POST
7
carrying its CSRF token, so cleaning and promotion need no script. The page
8
names only the view model: the controller has already phrased set types,
9
blockers, and warnings, so no core entity crosses the boundary here. *)
10
let import_review request ~viewer (vm : View_model.Import_review.t) =
11
let warnings_block =
12
match vm.View_model.Import_review.warnings with
13
| [] -> []
14
| warnings ->
15
[
16
tag "section"
17
[ class_ "import-warnings" ]
18
[
19
tag "h2" [] [ txt "Dropped rows" ];
20
tag "ul" []
21
(List.map
22
(fun (w : View_model.Import_warning.t) ->
23
tag "li" []
24
[
25
txt "Row %d: %s" w.View_model.Import_warning.row
26
w.View_model.Import_warning.detail;
27
])
28
warnings);
29
];
30
]
31
in
32
(* One row per set. A mapped set shows the catalog name it resolves to. *)
33
let set_row (s : View_model.Import_set.t) =
34
let mapping =
35
match s.View_model.Import_set.mapping with
36
| Some name -> Printf.sprintf " → %s" name
37
| None -> " → unmapped"
38
in
39
tag "li" []
40
[
41
txt "Row %d: %s (%s) %s kg × %s reps%s" s.View_model.Import_set.row
42
s.View_model.Import_set.exercise_name s.View_model.Import_set.set_type
43
s.View_model.Import_set.weight s.View_model.Import_set.reps mapping;
44
]
45
in
46
(* One mapping form per distinct source name in the workout. The source name
47
is fixed by a hidden field; the trainee supplies the catalog exercise id. *)
48
let mapping_form batch_id workout_id source_name =
49
tag "form"
50
[
51
action Routes.import_map batch_id workout_id;
52
post_form;
53
class_ "import-map-form";
54
]
55
[
56
Dream_html.csrf_tag request;
57
void "input"
58
[
59
type_ "hidden";
60
name "source_name";
61
Dream_html.string_attr "value" "%s" source_name;
62
];
63
tag "div"
64
[ class_ "field" ]
65
[
66
tag "label" [] [ txt "Map %s to exercise id" source_name ];
67
void "input" [ type_ "text"; name "exercise_id"; required ];
68
];
69
void "input" [ type_ "submit"; value "Map exercise" ];
70
]
71
in
72
let confirm_form batch_id workout_id acknowledgement =
73
tag "form"
74
[
75
action Routes.import_confirm batch_id workout_id;
76
post_form;
77
class_ "import-confirm-form";
78
]
79
[
80
Dream_html.csrf_tag request;
81
tag "div"
82
[ class_ "field" ]
83
[
84
tag "label" [] [ txt "Confirm normal sets" ];
85
void "input"
86
([ type_ "text"; name "acknowledgement"; required ]
87
@
88
match acknowledgement with
89
| Some ack -> [ Dream_html.string_attr "value" "%s" ack ]
90
| None -> []);
91
];
92
void "input" [ type_ "submit"; value "Confirm normal sets" ];
93
]
94
in
95
let promote_workout_form batch_id workout_id =
96
tag "form"
97
[
98
action Routes.import_promote_workout batch_id workout_id;
99
post_form;
100
class_ "import-promote-workout-form";
101
]
102
[
103
Dream_html.csrf_tag request;
104
void "input" [ type_ "submit"; value "Promote workout" ];
105
]
106
in
107
let blockers_block (w : View_model.Import_workout.t) =
108
match w.View_model.Import_workout.blockers with
109
| [] -> [ tag "p" [ class_ "import-ready" ] [ txt "Ready to promote." ] ]
110
| blockers ->
111
[
112
tag "p" [] [ txt "Blocked:" ];
113
tag "ul"
114
[ class_ "import-blockers" ]
115
(List.map (fun b -> tag "li" [] [ txt "%s" b ]) blockers);
116
]
117
in
118
let workout_section (w : View_model.Import_workout.t) =
119
let batch_id = vm.View_model.Import_review.batch_id in
120
let workout_id = w.View_model.Import_workout.id in
121
(* Every distinct source name, in first-appearance order. *)
122
let source_names =
123
List.fold_left
124
(fun acc (s : View_model.Import_set.t) ->
125
let n = s.View_model.Import_set.exercise_name in
126
if List.mem n acc then acc else acc @ [ n ])
127
[] w.View_model.Import_workout.sets
128
in
129
tag "section"
130
[ class_ "import-workout" ]
131
([
132
tag "h2" [] [ txt "%s" w.View_model.Import_workout.title ];
133
tag "ul"
134
[ class_ "import-sets" ]
135
(List.map set_row w.View_model.Import_workout.sets);
136
]
137
@ blockers_block w
138
@ List.map (mapping_form batch_id workout_id) source_names
139
@ [
140
confirm_form batch_id workout_id
141
w.View_model.Import_workout.acknowledgement;
142
promote_workout_form batch_id workout_id;
143
])
144
in
145
let batch_promote_form =
146
tag "form"
147
[
148
action Routes.import_promote_batch vm.View_model.Import_review.batch_id;
149
post_form;
150
class_ "import-promote-batch-form";
151
]
152
[
153
Dream_html.csrf_tag request;
154
void "input" [ type_ "submit"; value "Promote whole batch" ];
155
]
156
in
157
html_page ~viewer ~request ~active:"profile" "Import review"
158
([ tag "h1" [] [ txt "Import review" ] ]
159
@ warnings_block
160
@ List.map workout_section vm.View_model.Import_review.workouts
161
@ [ batch_promote_form ])
162