From c3d46d69327ede5623b5b8b0f552bf9ebcd7fb3f Mon Sep 17 00:00:00 2001
From: Urban Suppiger <urban@suppiger.net>
Date: Sun, 29 Sep 2024 12:15:51 +0200
Subject: [PATCH] render checklists in react pdf

---
 .../scheduleEntry/contentNode/Checklist.vue   | 98 +++++++++++++++++++
 .../scheduleEntry/contentNode/ContentNode.vue |  2 +
 pdf/src/prepareInMainThread.js                | 13 ++-
 3 files changed, 112 insertions(+), 1 deletion(-)
 create mode 100644 pdf/src/campPrint/scheduleEntry/contentNode/Checklist.vue

diff --git a/pdf/src/campPrint/scheduleEntry/contentNode/Checklist.vue b/pdf/src/campPrint/scheduleEntry/contentNode/Checklist.vue
new file mode 100644
index 0000000000..cd7dec4a51
--- /dev/null
+++ b/pdf/src/campPrint/scheduleEntry/contentNode/Checklist.vue
@@ -0,0 +1,98 @@
+<template>
+  <View class="content-node">
+    <InstanceName :content-node="contentNode" />
+
+    <View v-for="entry in checklistsWithItems" class="checklist">
+      <Text class="checklist-title">{{ entry.checklist.name }}</Text>
+      <View v-for="item in entry.items" class="checklist-item">
+        <View class="checklist-item-column checklist-item-column-1">
+          <Text>{{ item.number }}</Text>
+        </View>
+        <View class="checklist-item-column checklist-item-column-2">
+          <Text>{{ item.text }}</Text>
+        </View>
+      </View>
+    </View>
+  </View>
+</template>
+
+<script setup>
+import uniqWith from 'lodash/uniqWith.js'
+import sortBy from 'lodash/sortBy.js'
+
+const props = defineProps({
+  contentNode: { type: Object, required: true },
+})
+
+function calculateItemNumber(item) {
+  if (!item.parent) {
+    return item.position + 1
+  }
+
+  return calculateItemNumber(item.parent()) + '.' + (item.position + 1)
+}
+const items = props.contentNode.checklistItems().items.map((item) => {
+  const number = calculateItemNumber(item)
+  return {
+    ...item,
+    number,
+  }
+})
+
+const checklists = uniqWith(
+  props.contentNode
+    .checklistItems()
+    .items.map((checklistItem) => checklistItem.checklist()),
+  function (checklist1, checklist2) {
+    return checklist1._meta.self === checklist2._meta.self
+  }
+)
+
+const checklistsWithItems = checklists.map((checklist) => ({
+  checklist,
+  items: sortBy(
+    items.filter((item) => item.checklist()._meta.self === checklist._meta.self),
+    'number'
+  ),
+}))
+</script>
+
+<script>
+import PdfComponent from '@/PdfComponent.js'
+import InstanceName from '../InstanceName.vue'
+
+export default {
+  name: 'Checklist',
+  components: { InstanceName },
+  extends: PdfComponent,
+}
+</script>
+<pdf-style>
+.checklist {
+  display: flex;
+  flex-direction: column;
+  margin-bottom:8pt;
+}
+.checklist-title{
+  font-weight:bold;
+  margin-bottom:3pt;
+  margin-top:2pt;
+}
+.checklist-item {
+  display: flex;
+  flex-direction: row;
+  padding-bottom:5pt;
+}
+.checklist-item-column {
+  flex-grow: 1;
+}
+.checklist-item-column-1 {
+  flex-basis: 300pt;
+  padding-right: 2pt;
+  font-variant-numeric: tabular-nums;
+}
+.checklist-item-column-2 {
+  flex-basis: 9700pt;
+  padding-left: 2pt;
+}
+</pdf-style>
diff --git a/pdf/src/campPrint/scheduleEntry/contentNode/ContentNode.vue b/pdf/src/campPrint/scheduleEntry/contentNode/ContentNode.vue
index 29d11ccc12..df1d078f29 100644
--- a/pdf/src/campPrint/scheduleEntry/contentNode/ContentNode.vue
+++ b/pdf/src/campPrint/scheduleEntry/contentNode/ContentNode.vue
@@ -18,6 +18,7 @@ import Notes from './Notes.vue'
 import SafetyConsiderations from './SafetyConsiderations.vue'
 import Material from './Material.vue'
 import Storycontext from './Storycontext.vue'
+import Checklist from './Checklist.vue'
 
 export default {
   name: 'ContentNode',
@@ -41,6 +42,7 @@ export default {
         SafetyConsiderations,
         Material,
         Storycontext,
+        Checklist,
       }[this.contentTypeName]
     },
   },
diff --git a/pdf/src/prepareInMainThread.js b/pdf/src/prepareInMainThread.js
index 15d71e252e..17e57a74ec 100644
--- a/pdf/src/prepareInMainThread.js
+++ b/pdf/src/prepareInMainThread.js
@@ -47,7 +47,11 @@ export async function prepareInMainThread(config) {
   }
 
   const activityData = (config) => {
-    if (!config.contents.some((c) => ['Program', 'Activity'].includes(c.type))) {
+    if (
+      !config.contents.some((c) =>
+        ['Program', 'Activity', 'ActivityList'].includes(c.type)
+      )
+    ) {
       return []
     }
 
@@ -89,6 +93,13 @@ export async function prepareInMainThread(config) {
         }),
       camp.materialLists().$loadItems(),
       config.apiGet().contentTypes().$loadItems(),
+      camp.checklists().$loadItems(),
+      config
+        .apiGet()
+        .checklistItems({
+          'checklist.camp': camp._meta.self,
+        })
+        .$loadItems(),
     ]
   }