[OCaml] High Intensity Training Online
feat use inline subjective feedback choices
Changed files
lib/web/assets/hito.css
@@ -865,3 +865,53 @@
865
865
@media (prefers-reduced-motion: reduce) {
866
866
.toast[data-hito-toast-done] { transition: none; }
867
867
}
868
Added:
869
Added:
/* Subjective feedback uses one inline three-choice group per signal. The radio
870
Added:
input keeps native keyboard and form behavior; its adjacent span is the
871
Added:
visible button. */
872
Added:
.feedback-group {
873
Added:
margin: 1rem 0;
874
Added:
}
875
Added:
.feedback-group-label {
876
Added:
margin: 0 0 0.45rem;
877
Added:
font-weight: 700;
878
Added:
}
879
Added:
.feedback-buttons {
880
Added:
display: inline-flex;
881
Added:
flex-wrap: wrap;
882
Added:
gap: 0.45rem;
883
Added:
}
884
Added:
.feedback-choice {
885
Added:
position: relative;
886
Added:
display: inline-flex;
887
Added:
min-height: 2.75rem;
888
Added:
align-items: center;
889
Added:
}
890
Added:
.feedback-choice input {
891
Added:
position: absolute;
892
Added:
width: 1px;
893
Added:
height: 1px;
894
Added:
opacity: 0;
895
Added:
}
896
Added:
.feedback-choice span {
897
Added:
display: inline-flex;
898
Added:
min-height: 2.75rem;
899
Added:
align-items: center;
900
Added:
border: 1px solid var(--rule-strong);
901
Added:
border-radius: var(--radius);
902
Added:
padding: 0.55rem 0.9rem;
903
Added:
color: var(--ink);
904
Added:
background: var(--paper-raised);
905
Added:
cursor: pointer;
906
Added:
font-weight: 700;
907
Added:
}
908
Added:
.feedback-choice span:hover,
909
Added:
.feedback-choice input:focus-visible + span {
910
Added:
border-color: var(--brass);
911
Added:
box-shadow: 0 0 0 3px var(--brass-wash);
912
Added:
}
913
Added:
.feedback-choice input:checked + span {
914
Added:
border-color: var(--oxblood-dark);
915
Added:
color: var(--paper-raised);
916
Added:
background: var(--oxblood);
917
Added:
}
lib/web/pages.ml
@@ -1117,21 +1117,35 @@
1117
1117
report pain, injury, and insufficient preparation. A field left blank reports
1118
1118
nothing, so the trainee submits only what they mean to. Feedback is
1119
1119
standalone — recorded from the Logbook at any time. *)
1120
Removed:
let feedback_level_select field label =
1120
Added:
(* Each subjective metric uses three unselected radio buttons styled as inline buttons.
1121
Added:
Leaving a group untouched records no signal for that metric. *)
1122
Added:
let feedback_level_buttons field label =
1123
Added:
let choice code text =
1124
Added:
tag "label"
1125
Added:
[ class_ "feedback-choice" ]
1126
Added:
[
1127
Added:
void "input"
1128
Added:
[
1129
Added:
type_ "radio";
1130
Added:
Dream_html.string_attr "name" "%s" field;
1131
Added:
Dream_html.string_attr "value" "%s" code;
1132
Added:
Dream_html.string_attr "id" "%s-%s" field code;
1133
Added:
];
1134
Added:
tag "span" [] [ txt "%s" text ];
1135
Added:
]
1136
Added:
in
1121
1137
tag "div"
1122
Removed:
[ class_ "field" ]
1138
Added:
[ class_ "feedback-group" ]
1123
1139
[
1124
Removed:
tag "label" [ Dream_html.string_attr "for" "%s" field ] [ txt "%s" label ];
1125
Removed:
tag "select"
1140
Added:
tag "p" [ class_ "feedback-group-label" ] [ txt "%s" label ];
1141
Added:
tag "div"
1126
1142
[
1127
Removed:
Dream_html.string_attr "name" "%s" field;
1128
Removed:
Dream_html.string_attr "id" "%s" field;
1143
Added:
class_ "feedback-buttons";
1144
Added:
Dream_html.string_attr "role" "group";
1145
Added:
Dream_html.string_attr "aria-label" "%s" label;
1129
1146
]
1130
1147
[
1131
Removed:
tag "option" [ value "" ] [ txt "Not reported" ];
1132
Removed:
tag "option" [ value "below" ] [ txt "Below usual" ];
1133
Removed:
tag "option" [ value "usual" ] [ txt "Usual" ];
1134
Removed:
tag "option" [ value "above" ] [ txt "Above usual" ];
1148
Added:
choice "below" "Worse"; choice "usual" "Same"; choice "above" "Better";
1135
1149
];
1136
1150
]
1137
1151
@@ -1160,11 +1174,11 @@
1160
1174
Dream_html.csrf_tag request;
1161
1175
tag "fieldset" []
1162
1176
[
1163
Removed:
feedback_level_select "sleep" "Sleep";
1164
Removed:
feedback_level_select "appetite" "Appetite";
1165
Removed:
feedback_level_select "readiness" "Readiness";
1166
Removed:
feedback_level_select "motivation" "Motivation";
1167
Removed:
feedback_level_select "difficulty" "Perceived difficulty";
1177
Added:
feedback_level_buttons "sleep" "Sleep";
1178
Added:
feedback_level_buttons "appetite" "Appetite";
1179
Added:
feedback_level_buttons "readiness" "Readiness";
1180
Added:
feedback_level_buttons "motivation" "Motivation";
1181
Added:
feedback_level_buttons "difficulty" "Perceived difficulty";
1168
1182
tag "div"
1169
1183
[ class_ "field" ]
1170
1184
[
test/test_web.ml
@@ -969,9 +969,14 @@
969
969
(contains ~substring:"class=\"feedback-toggle\"" logbook_page
970
970
&& contains ~substring:"<summary" logbook_page);
971
971
Alcotest.(check bool)
972
Removed:
"keeps the disclosure collapsed by default" false
973
Removed:
(contains ~substring:"<details class=\"feedback-disclosure\" open"
974
Removed:
logbook_page) );
972
Added:
"renders inline subjective button groups" true
973
Added:
(contains ~substring:"feedback-buttons" logbook_page
974
Added:
&& contains ~substring:">Worse</span>" logbook_page
975
Added:
&& contains ~substring:">Same</span>" logbook_page
976
Added:
&& contains ~substring:">Better</span>" logbook_page);
977
Added:
Alcotest.(check bool)
978
Added:
"does not render subjective selects" false
979
Added:
(contains ~substring:"<select" logbook_page) );
975
980
( "submitting feedback stores it and it appears on the logbook",
976
981
`Quick,
977
982
fun () ->