From 9e566fe4daafe87ac08b08141e2337835b1bc753 Mon Sep 17 00:00:00 2001
From: Michael Bauer <michael-bauer@posteo.de>
Date: Fri, 9 Feb 2024 20:34:16 +0100
Subject: [PATCH] Implement modal lesson summary component

---
 .../frontend/components/Coursebook.vue        |  6 +-
 .../documentation/Documentation.vue           | 48 ++++++++---
 .../documentation/DocumentationModal.vue      | 39 +++++++++
 .../documentation/LessonSummary.vue           | 84 ++++++++++++-------
 4 files changed, 134 insertions(+), 43 deletions(-)
 create mode 100644 aleksis/apps/alsijil/frontend/components/documentation/DocumentationModal.vue

diff --git a/aleksis/apps/alsijil/frontend/components/Coursebook.vue b/aleksis/apps/alsijil/frontend/components/Coursebook.vue
index 718ea3dbb..0d27a6e38 100644
--- a/aleksis/apps/alsijil/frontend/components/Coursebook.vue
+++ b/aleksis/apps/alsijil/frontend/components/Coursebook.vue
@@ -12,7 +12,7 @@
           <v-list-item-title>{{ $d(day[0], "short") }}</v-list-item-title>
           <v-list>
             <v-list-item v-for="doc in day.slice(1)">
-              <documentation :documentation="doc" />
+              <documentation-modal :documentation="doc" />
             </v-list-item>
           </v-list>
         </v-list-item-content>
@@ -23,7 +23,7 @@
 
 <script>
 import CRUDIterator from "aleksis.core/components/generic/CRUDIterator.vue";
-import Documentation from "./documentation/Documentation.vue";
+import DocumentationModal from "./documentation/DocumentationModal.vue";
 import { documentationsForCoursebook } from "./coursebook.graphql";
 import { DateTime } from "luxon";
 
@@ -31,7 +31,7 @@ export default {
   name: "Coursebook",
   components: {
     CRUDIterator,
-    Documentation,
+    DocumentationModal,
   },
   props: {
     // Either as props OR route params
diff --git a/aleksis/apps/alsijil/frontend/components/documentation/Documentation.vue b/aleksis/apps/alsijil/frontend/components/documentation/Documentation.vue
index de75c7ef4..6b38b4c60 100644
--- a/aleksis/apps/alsijil/frontend/components/documentation/Documentation.vue
+++ b/aleksis/apps/alsijil/frontend/components/documentation/Documentation.vue
@@ -1,21 +1,39 @@
 <template>
   <v-card class="my-2 full-width">
-    <div class="full-width d-flex flex-column flex-md-row align-stretch">
+    <!-- flex-md-row zeile ab medium -->
+    <!-- align-stretch - stretch full-width -->
+    <div
+      class="full-width d-flex flex-column align-stretch"
+      :class="{ 'flex-md-row': ('compact' in $attrs) }"
+    >
       <lesson-information
-        :documentation="documentation"
+        class="flex-grow-1"
+        :documentation="$attrs.documentation"
       />
       <lesson-summary
         class="flex-grow-1"
-        :documentation="documentation"
+        ref="summary"
+        v-bind="$attrs"
         :is-create="false"
         :gql-patch-mutation="documentationsMutation"
-        @edit="popup"
+        @open="$emit('open')"
+        @loading="loading = $event"
+        @save="$emit('close')"
       />
       <lesson-notes
         class="flex-grow-1"
-        :documentation="documentation"
+        :documentation="$attrs.documentation"
       />
     </div>
+    <v-divider/>
+    <v-card-actions v-if="!('compact' in $attrs)">
+      <v-spacer/>
+      <cancel-button @click="$emit('close')" :disabled="loading" />
+      <save-button
+        @click="save"
+        :loading="loading"
+      />
+    </v-card-actions>
   </v-card>
 </template>
 
@@ -24,7 +42,9 @@ import LessonInformation from "./LessonInformation.vue";
 import LessonSummary from "./LessonSummary.vue";
 import LessonNotes from "./LessonNotes.vue";
 
-// or pass from Coursebook?
+import SaveButton from "aleksis.core/components/generic/buttons/SaveButton.vue";
+import CancelButton from "aleksis.core/components/generic/buttons/CancelButton.vue";
+
 import { createOrUpdateDocumentations } from "../coursebook.graphql";
 
 export default {
@@ -33,17 +53,21 @@ export default {
     LessonInformation,
     LessonSummary,
     LessonNotes,
+    SaveButton,
+    CancelButton,
   },
-  props: {
-    documentation: {
-      type: Object,
-      required: true,
-    },
-  },
+  emits: ["open", "close"],
   data() {
     return {
+      loading: false,
       documentationsMutation: createOrUpdateDocumentations,
     };
   },
+  methods: {
+    save() {
+      this.$refs.summary.save();
+      this.$emit('close');
+    },
+  },
 };
 </script>
diff --git a/aleksis/apps/alsijil/frontend/components/documentation/DocumentationModal.vue b/aleksis/apps/alsijil/frontend/components/documentation/DocumentationModal.vue
new file mode 100644
index 000000000..95ce18a01
--- /dev/null
+++ b/aleksis/apps/alsijil/frontend/components/documentation/DocumentationModal.vue
@@ -0,0 +1,39 @@
+<!-- Wrapper around Documentation.vue -->
+<!-- That uses it either as list item or as editable modal dialog. -->
+<template>
+  <mobile-fullscreen-dialog v-model="popup" max-width="500px">
+    <template #activator="{ on, attrs }">
+      <!-- list view -> activate dialog -->
+      <documentation
+        compact
+        @open="popup = true"
+        v-bind="{...$attrs, ...attrs}"
+        v-on="on"
+      />
+    </template>
+    <!-- dialog view -> deactivate dialog -->
+    <!-- cancel | save (through lesson-summary) -->
+    <documentation
+      v-bind="$attrs"
+      @close="popup = false"
+    />
+  </mobile-fullscreen-dialog>
+</template>
+
+<script>
+import MobileFullscreenDialog from "aleksis.core/components/generic/dialogs/MobileFullscreenDialog.vue"
+import Documentation from "./Documentation.vue";
+
+export default {
+  name: "DocumentationModal",
+  components: {
+    MobileFullscreenDialog,
+    Documentation,
+  },
+  data() {
+    return {
+      popup: false,
+    };
+  },
+};
+</script>
diff --git a/aleksis/apps/alsijil/frontend/components/documentation/LessonSummary.vue b/aleksis/apps/alsijil/frontend/components/documentation/LessonSummary.vue
index 230bb1716..2d0599841 100644
--- a/aleksis/apps/alsijil/frontend/components/documentation/LessonSummary.vue
+++ b/aleksis/apps/alsijil/frontend/components/documentation/LessonSummary.vue
@@ -1,41 +1,55 @@
 <template>
   <v-card-text>
-    <!-- Are focusout & enter enough trigger? -->
-    <!-- TODO: focusout on enter -->
+    <!-- compact -->
     <v-text-field
+      dense
       filled
+      v-if="compact"
       label="Thema"
       :value="documentation.topic"
       @input="topic=$event"
-      @focusout="saveTopic"
-      @keydown.enter="saveTopic"
+      @focusout="save"
+      @keydown.enter="saveAndBlur"
     />
-    <v-text-field
-      filled
-      v-if="!compact"
-      label="Hausaufgaben"
-      :value="documentation.homework"
-      @input="homework=$event"
-    />
-    <v-text-field
-      filled
-      v-if="!compact"
-      label="Gruppennotiz"
-      :value="documentation.groupnote"
-      @input="groupnote=$event"
-      />
     <v-chip
       v-if="compact"
       outlined
+      @click="$emit('open')"
     >
       Hausaufgaben: {{ truncate(documentation.homework) }}
     </v-chip>
     <v-chip
       v-if="compact"
       outlined
+      @click="$emit('open')"
     >
-      Gruppennotiz: {{ truncate(documentation.groupnote) }}
+      Gruppennotiz: {{ truncate(documentation.groupNote) }}
     </v-chip>
+    <!-- not compact -->
+    <!-- Are focusout & enter enough trigger? -->
+    <v-text-field
+      filled
+      v-if="!compact"
+      label="Thema"
+      :value="documentation.topic"
+      @input="topic=$event"
+    />
+    <v-textarea
+      filled
+      auto-grow
+      v-if="!compact"
+      label="Hausaufgaben"
+      :value="documentation.homework"
+      @input="homework=$event"
+    />
+    <v-textarea
+      filled
+      auto-grow
+      v-if="!compact"
+      label="Gruppennotiz"
+      :value="documentation.groupNote"
+      @input="groupNote=$event"
+      />
   </v-card-text>
 </template>
 
@@ -54,30 +68,44 @@ export default {
     compact: {
       type: Boolean,
       required: false,
-      default: true,
+      default: false,
     },
   },
+  emits: ["open"],
   data() {
     return {
       topic: "",
       homework: "",
-      groupnote: "",
+      groupNote: "",
     };
   },
   methods: {
-    saveTopic() {
-      if (this.topic) {
+    truncate(str) {
+      return str ?
+        (str.length > 25) ? str.slice(0, 24) + ' …' : str
+      : "";
+    },
+    save() {
+      if (this.topic || this.homework || this.groupNote) {
+        const topic = this.topic ? { topic: this.topic } : {};
+        const homework = this.homework ? { homework: this.homework } : {};
+        const groupNote = this.groupNote ? { groupNote: this.groupNote } : {};
+
         this.createOrPatch([{
           id: this.documentation.id,
-          topic: this.topic,
+          ...topic,
+          ...homework,
+          ...groupNote,
         }]);
+
         this.topic = "";
+        this.homework = "";
+        this.groupNote = "";
       }
     },
-    truncate(str) {
-      return str ?
-        (str.length > 30) ? str.slice(0, 29) + '&hellip;' : str
-      : "";
+    saveAndBlur(event) {
+      this.save();
+      event.target.blur();
     },
   },
 };
-- 
GitLab