refactor Cross the logbook boundary with a view model

The logbook record list read the workout name, stimuli count, and completeness straight off Repository.record and Evidence.Workout. Add a Logbook_entry DTO, map it in the controller, and render only the DTO. This was the last entity crossing: the presenter now names no core entity or repository type, so `open Hito_app` is gone from both pages.ml and pages.mli. Only plain view models cross into the presenter. 243 tests pass.

Commit
4a8a930764fdf9974b73d2aa6d9afefbc2e877e9
Author
Marius Peter <dev@marius-peter.com>
Author date
Committer
Marius Peter <dev@marius-peter.com>
Committer date
Changed files
lib/web/handlers.ml
index ec5c1e20..d0466d1b 100644..100644
@@ -798,6 +798,19 @@
798 798 in
799 799 List.find_map of_signal (Evidence.Feedback.signals report)
800 800
801 Added: let entry_view (record : Repository.record) : View_model.Logbook_entry.t =
802 Added: let workout = record.Repository.workout in
803 Added: {
804 Added: id = (record.Repository.id :> string);
805 Added: workout_name =
806 Added: Prescription.Workout.name (Evidence.Workout.prescription workout);
807 Added: stimuli_count = List.length (Evidence.Workout.stimuli workout);
808 Added: complete =
809 Added: (match Evidence.Workout.completeness workout with
810 Added: | Evidence.Workout.Complete -> true
811 Added: | Incomplete -> false);
812 Added: }
813 Added:
801 814 let feedback_view (report : Evidence.Feedback.t) :
802 815 View_model.Feedback.report =
803 816 {
@@ -820,6 +833,7 @@
820 833 Service.history t.service trainee.Trainee.id >>= fun records ->
821 834 Service.feedback t.service trainee.Trainee.id >>= fun feedback ->
822 835 let feedback = List.map feedback_view feedback in
836 Added: let records = List.map entry_view records in
823 837 let saved_flow = feedback_flow request in
824 838 let feedback_open =
825 839 match
lib/web/pages.ml
index b795ffaa..4f3fddd2 100644..100644
@@ -1,5 +1,4 @@
1 1 open Dream_html
2 Removed: open Hito_app
3 2
4 3 type page = Dream_html.node
5 4
@@ -1383,27 +1382,24 @@
1383 1382 else
1384 1383 tag "ul" []
1385 1384 (List.map
1386 Removed: (fun record ->
1387 Removed: let workout = record.Repository.workout in
1388 Removed: let status =
1389 Removed: match Evidence.Workout.completeness workout with
1390 Removed: | Evidence.Workout.Complete -> "complete"
1391 Removed: | Incomplete -> "incomplete"
1392 Removed: in
1385 Added: (fun (entry : View_model.Logbook_entry.t) ->
1393 1386 tag "li" []
1394 1387 [
1395 1388 tag "a"
1396 1389 [
1397 Removed: href Routes.record (record.Repository.id :> string);
1390 Added: href Routes.record entry.id;
1398 1391 Dream_html.attr "data-hito-app-link";
1399 1392 ]
1400 1393 [
1401 Removed: txt "%s — %d stimuli"
1402 Removed: (Prescription.Workout.name
1403 Removed: (Evidence.Workout.prescription workout))
1404 Removed: (List.length (Evidence.Workout.stimuli workout));
1394 Added: txt "%s — %d stimuli" entry.workout_name
1395 Added: entry.stimuli_count;
1405 1396 ];
1406 Removed: tag "p" [ class_ "done" ] [ txt "%s" status ];
1397 Added: tag "p"
1398 Added: [ class_ "done" ]
1399 Added: [
1400 Added: txt "%s"
1401 Added: (if entry.complete then "complete" else "incomplete");
1402 Added: ];
1407 1403 ])
1408 1404 records));
1409 1405 tag "section"
lib/web/pages.mli
index 45eee8c3..ba94da8e 100644..100644
@@ -1,5 +1,3 @@
1 Removed: open Hito_app
2 Removed:
3 1 type page = Dream_html.node
4 2
5 3 val login :
@@ -63,7 +61,7 @@
63 61 ?suggest_feedback:bool ->
64 62 ?feedback_flow:feedback_flow ->
65 63 ?feedback_open:bool ->
66 Removed: Repository.record list ->
64 Added: View_model.Logbook_entry.t list ->
67 65 page
68 66 (** The logbook: recorded workouts and a modal sequential subjective feedback
69 67 flow. [feedback] lists past reports, most recent first. [suggest_feedback]
lib/web/view_model.ml
index 4b691e5e..6dd98cc2 100644..100644
@@ -120,3 +120,15 @@
120 120 Prescription entity. Extension/ending selection stays in the presenter
121 121 form, since its option set is fixed UI vocabulary, not domain data. *)
122 122 end
123 Added:
124 Added: module Logbook_entry = struct
125 Added: type t = {
126 Added: id : string; (** Record id for the link. *)
127 Added: workout_name : string;
128 Added: stimuli_count : int;
129 Added: complete : bool;
130 Added: }
131 Added: (** One saved workout as the logbook list shows it: a link, a name, how many
132 Added: stimuli it holds, and whether it is complete. No Repository record and no
133 Added: Evidence.Workout entity. *)
134 Added: end