[OCaml] High Intensity Training Online
feat gate logbook feedback form behind a button
The logbook showed the subjective feedback form outright, competing with the workout list. Wrap it in a native <details> disclosure whose <summary> is a dedicated "Record feedback" button, so the form appears only when the trainee reveals it. The disclosure needs no script and stays reachable without JavaScript, preserving progressive enhancement. A fresh-workout suggestion still opens the disclosure, so the post-workout prompt lands on a visible form. No Heavy Duty doctrine bearing: this is a pure presentation change. The feedback form and its route are unchanged; only its visibility is gated.
Changed files
lib/web/assets/hito.css
@@ -532,6 +532,28 @@
532
532
border-top: 1px solid var(--rule);
533
533
padding-top: 1rem;
534
534
}
535
Added:
/* The feedback form is gated behind a disclosure. The summary reads as a
536
Added:
secondary button so the reveal control matches the rest of the UI. */
537
Added:
.feedback-toggle {
538
Added:
display: inline-block;
539
Added:
cursor: pointer;
540
Added:
padding: 0.55rem 1.1rem;
541
Added:
border: 1px solid var(--brass);
542
Added:
border-radius: var(--radius);
543
Added:
background: var(--brass-wash);
544
Added:
color: #47350f;
545
Added:
font-weight: 600;
546
Added:
list-style: none;
547
Added:
}
548
Added:
.feedback-toggle::-webkit-details-marker {
549
Added:
display: none;
550
Added:
}
551
Added:
.feedback-toggle:hover {
552
Added:
background: var(--brass);
553
Added:
}
554
Added:
.feedback-disclosure[open] .feedback-toggle {
555
Added:
margin-bottom: 1rem;
556
Added:
}
535
557
.feedback-flag {
536
558
display: flex;
537
559
align-items: center;
lib/web/pages.ml
@@ -1112,13 +1112,25 @@
1112
1112
[ class_ "eyebrow" ]
1113
1113
[ txt "Workout saved — add feedback while it is fresh." ]
1114
1114
else txt "");
1115
Removed:
tag "p" []
1115
Added:
(* The form is gated behind a dedicated disclosure button rather than
1116
Added:
shown outright. A native [details] needs no script and stays
1117
Added:
accessible without JavaScript. After a workout the suggestion opens
1118
Added:
it so the prompt is not buried; otherwise it stays collapsed. *)
1119
Added:
tag "details"
1120
Added:
([ class_ "feedback-disclosure" ]
1121
Added:
@ if suggest_feedback then [ Dream_html.attr "open" ] else [])
1116
1122
[
1117
Removed:
txt
1118
Removed:
"Record subjective feedback at any time. Report only what you \
1119
Removed:
mean to; leave the rest blank.";
1123
Added:
tag "summary"
1124
Added:
[ class_ "feedback-toggle" ]
1125
Added:
[ txt "Record feedback" ];
1126
Added:
tag "p" []
1127
Added:
[
1128
Added:
txt
1129
Added:
"Record subjective feedback at any time. Report only what \
1130
Added:
you mean to; leave the rest blank.";
1131
Added:
];
1132
Added:
feedback_form request;
1120
1133
];
1121
Removed:
feedback_form request;
1122
1134
]
1123
1135
@ feedback_list feedback);
1124
1136
]
test/test_web.ml
@@ -909,18 +909,32 @@
909
909
Alcotest.(check bool)
910
910
"shows bottom navigation" true
911
911
(contains ~substring:"bottom-nav" record_page) );
912
Removed:
( "the logbook offers a standalone feedback form",
912
Added:
( "the logbook gates the feedback form behind a disclosure button",
913
913
`Quick,
914
914
fun () ->
915
915
let c = client () in
916
916
let _ = sign_in_new c in
917
917
let logbook_page = body (get c "/logbook") in
918
918
Alcotest.(check bool)
919
Removed:
"renders the feedback form" true
919
Added:
"still renders the feedback form" true
920
920
(contains ~substring:"feedback-form" logbook_page);
921
921
Alcotest.(check bool)
922
922
"posts feedback to its own route" true
923
Removed:
(contains ~substring:"action=\"/feedback\"" logbook_page) );
923
Added:
(contains ~substring:"action=\"/feedback\"" logbook_page);
924
Added:
(* The form is not shown outright: it is wrapped in a native
925
Added:
disclosure whose summary is the dedicated reveal button, and the
926
Added:
disclosure is collapsed (no [open]) on a plain logbook visit. *)
927
Added:
Alcotest.(check bool)
928
Added:
"wraps the form in a disclosure" true
929
Added:
(contains ~substring:"feedback-disclosure" logbook_page);
930
Added:
Alcotest.(check bool)
931
Added:
"offers a dedicated reveal button" true
932
Added:
(contains ~substring:"class=\"feedback-toggle\"" logbook_page
933
Added:
&& contains ~substring:"<summary" logbook_page);
934
Added:
Alcotest.(check bool)
935
Added:
"keeps the disclosure collapsed by default" false
936
Added:
(contains ~substring:"<details class=\"feedback-disclosure\" open"
937
Added:
logbook_page) );
924
938
( "submitting feedback stores it and it appears on the logbook",
925
939
`Quick,
926
940
fun () ->
@@ -960,7 +974,13 @@
960
974
let prompted = body (get c "/logbook?prompt=feedback") in
961
975
Alcotest.(check bool)
962
976
"shows the feedback suggestion" true
963
Removed:
(contains ~substring:"add feedback while it is fresh" prompted) );
977
Added:
(contains ~substring:"add feedback while it is fresh" prompted);
978
Added:
(* The gated form opens on a suggestion, so the prompt lands on a
979
Added:
visible form rather than a collapsed disclosure. *)
980
Added:
Alcotest.(check bool)
981
Added:
"opens the feedback disclosure on a suggestion" true
982
Added:
(contains ~substring:"<details class=\"feedback-disclosure\" open"
983
Added:
prompted) );
964
984
] );
965
985
]
966
986