diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/absences/ManageStudentsDialog.vue b/aleksis/apps/alsijil/frontend/components/coursebook/absences/ManageStudentsDialog.vue index e6098ee01021742369909e83210135c47f572473..a9f4fb0c7411407a5e98cdfe4a386f3b2c7cf678 100644 --- a/aleksis/apps/alsijil/frontend/components/coursebook/absences/ManageStudentsDialog.vue +++ b/aleksis/apps/alsijil/frontend/components/coursebook/absences/ManageStudentsDialog.vue @@ -1,4 +1,5 @@ <script> +import AbsenceReasonButtons from "aleksis.apps.kolego/components/AbsenceReasonButtons.vue"; import AbsenceReasonChip from "aleksis.apps.kolego/components/AbsenceReasonChip.vue"; import AbsenceReasonGroupSelect from "aleksis.apps.kolego/components/AbsenceReasonGroupSelect.vue"; import CancelButton from "aleksis.core/components/generic/buttons/CancelButton.vue"; @@ -15,6 +16,7 @@ export default { components: { AbsenceReasonChip, AbsenceReasonGroupSelect, + AbsenceReasonButtons, CancelButton, LessonInformation, MobileFullscreenDialog, @@ -25,6 +27,7 @@ export default { return { dialog: false, search: "", + loadSelected: false, selected: [], isExpanded: false, }; @@ -42,29 +45,29 @@ export default { }, }, methods: { - sendToServer(participation, field, value) { + sendToServer(participations, field, value) { if (field !== "absenceReason") return; this.mutate( updateParticipationStatuses, { - input: [ - { - id: participation.id, - absenceReason: value === "present" ? null : value, - }, - ], + input: participations.map((participation) => ({ + id: participation.id, + absenceReason: value === "present" ? null : value, + })), }, (storedDocumentations, incomingStatuses) => { - const newStatus = incomingStatuses[0]; const documentation = storedDocumentations.find( - (doc) => doc.id === newStatus.relatedDocumentation.id, + (doc) => doc.id === this.documentation.id, ); - const participationStatus = documentation.participations.find( - (part) => part.id === newStatus.id, - ); - participationStatus.absenceReason = newStatus.absenceReason; - participationStatus.isOptimistic = newStatus.isOptimistic; + + incomingStatuses.forEach((newStatus) => { + const participationStatus = documentation.participations.find( + (part) => part.id === newStatus.id, + ); + participationStatus.absenceReason = newStatus.absenceReason; + participationStatus.isOptimistic = newStatus.isOptimistic; + }); return storedDocumentations; }, @@ -94,6 +97,16 @@ export default { // }, ); }, + handleMultipleAction(absenceReasonId) { + this.loadSelected = true; + this.sendToServer(this.selected, "absenceReason", absenceReasonId); + this.$once("save", this.resetMultipleAction); + }, + resetMultipleAction() { + this.loadSelected = false; + this.$set(this.selected, []); + this.$refs.iterator.selected = []; + }, }, }; </script> @@ -110,27 +123,36 @@ export default { </template> <template #title> - <lesson-information v-bind="documentationPartProps" /> - <v-slide-x-transition leave-absolute> + <lesson-information v-bind="documentationPartProps" :compact="false" /> + <v-scroll-x-transition leave-absolute> <v-text-field v-show="!isExpanded" type="search" v-model="search" clearable rounded - filled hide-details single-line prepend-inner-icon="$search" dense outlined :placeholder="$t('actions.search')" - class="pt-4" + class="pt-4 full-width" /> - </v-slide-x-transition> + </v-scroll-x-transition> + <v-scroll-x-transition> + <div v-show="selected.length > 0" class="full-width mt-4"> + <absence-reason-buttons + allow-empty + empty-value="present" + @input="handleMultipleAction" + /> + </div> + </v-scroll-x-transition> </template> <template #content> <slide-iterator + ref="iterator" v-model="selected" :items="items" :search="search" @@ -138,7 +160,9 @@ export default { (item) => 'documentation-' + documentation.id + '-student-' + item.id " :is-expanded.sync="isExpanded" - :loading="loadingIndicator" + :loading="loadingIndicator || loadSelected" + :load-only-selected="loadSelected" + :disabled="loading" > <template #listItemContent="{ item }"> <v-list-item-title> @@ -167,7 +191,7 @@ export default { empty-value="present" :loadSelectedChip="loading" :value="item.absenceReason?.id || 'present'" - @input="sendToServer(item, 'absenceReason', $event)" + @input="sendToServer([item], 'absenceReason', $event)" /> </v-card-text> </template> diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/absences/participationStatus.graphql b/aleksis/apps/alsijil/frontend/components/coursebook/absences/participationStatus.graphql index 4c3f8df66e8236bb126aff29ccf8361931b35f8e..2f15ef02dbc47faa9d0787b21b13f3ba9d1ad3a6 100644 --- a/aleksis/apps/alsijil/frontend/components/coursebook/absences/participationStatus.graphql +++ b/aleksis/apps/alsijil/frontend/components/coursebook/absences/participationStatus.graphql @@ -12,6 +12,7 @@ mutation updateParticipationStatuses( id name shortName + colour } } } @@ -31,6 +32,7 @@ mutation touchDocumentation($documentationId: ID!) { id name shortName + colour } isOptimistic } diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/coursebook.graphql b/aleksis/apps/alsijil/frontend/components/coursebook/coursebook.graphql index b93cd7844ee5bff8cf3e4d5c0a065bc2f1bb7166..1e00bab2ee180c843fa99b3ec0b6e5574bb4409d 100644 --- a/aleksis/apps/alsijil/frontend/components/coursebook/coursebook.graphql +++ b/aleksis/apps/alsijil/frontend/components/coursebook/coursebook.graphql @@ -76,6 +76,7 @@ query documentationsForCoursebook( id name shortName + colour } isOptimistic } diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/documentation/LessonInformation.vue b/aleksis/apps/alsijil/frontend/components/coursebook/documentation/LessonInformation.vue index 09a04bcb67c6ae618fa0b1546a0171af30323885..652609dccaf430d3a4ab138f80ee2f810b84b4af 100644 --- a/aleksis/apps/alsijil/frontend/components/coursebook/documentation/LessonInformation.vue +++ b/aleksis/apps/alsijil/frontend/components/coursebook/documentation/LessonInformation.vue @@ -61,7 +61,7 @@ import PersonChip from "aleksis.core/components/person/PersonChip.vue"; v-for="teacher in documentation.teachers" :key="documentation.id + '-teacher-' + teacher.id" :person="teacher" - no-link + :no-link="compact" v-bind="compact ? dialogActivator.attrs : {}" v-on="compact ? dialogActivator.on : {}" /> @@ -69,7 +69,7 @@ import PersonChip from "aleksis.core/components/person/PersonChip.vue"; v-for="teacher in amendedTeachers" :key="documentation.id + '-amendedTeacher-' + teacher.id" :person="teacher" - no-link + :no-link="compact" v-bind="compact ? dialogActivator.attrs : {}" v-on="compact ? dialogActivator.on : {}" class="text-decoration-line-through" diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/documentation/LessonNotes.vue b/aleksis/apps/alsijil/frontend/components/coursebook/documentation/LessonNotes.vue index 8f40f71b60199a8c10e577d0ae006fbb1af5a7c5..880e92f4c03eb41a042f1ace474611de0ab5617e 100644 --- a/aleksis/apps/alsijil/frontend/components/coursebook/documentation/LessonNotes.vue +++ b/aleksis/apps/alsijil/frontend/components/coursebook/documentation/LessonNotes.vue @@ -1,33 +1,24 @@ +<script setup> +import AbsenceReasonChip from "aleksis.apps.kolego/components/AbsenceReasonChip.vue"; +</script> + <template> <div class="d-flex align-center justify-space-between justify-md-end flex-wrap gap" > - <!-- eslint-disable @intlify/vue-i18n/no-raw-text --> - <v-chip dense color="success"> - <v-chip small dense class="mr-2" color="green darken-3 white--text"> - {{ documentation.participations.length }} - </v-chip> - Schüler - </v-chip> - <v-chip dense color="warning"> - <v-chip small dense class="mr-2" color="orange darken-3 white--text" - >3</v-chip - > - entschuldigt + <v-chip dense color="success" outlined v-if="total > 0"> + {{ $t("alsijil.coursebook.present_number", { present, total })}} </v-chip> - <v-chip dense color="error"> - <v-chip small dense class="mr-2" color="red darken-3 white--text" - >1</v-chip - > - unentschuldigt - </v-chip> - <v-chip dense color="grey lighten-1"> - <v-chip small dense class="mr-2" color="grey darken-1 white--text" - >4</v-chip - > - Hausaufgaben vergessen - </v-chip> - <!-- eslint-enable @intlify/vue-i18n/no-raw-text --> + <absence-reason-chip + v-for="participation in absences" + :absence-reason="participation.absenceReason" + dense + > + <template #prepend> + {{ participation.person.fullName }}: + </template> + </absence-reason-chip> + <manage-students-trigger v-bind="documentationPartProps" /> </div> </template> @@ -40,6 +31,18 @@ export default { name: "LessonNotes", components: { ManageStudentsTrigger }, mixins: [documentationPartMixin], + computed: { + total() { + return this.documentation.participations.length; + }, + present() { + return this.documentation.participations.filter(p => p.absenceReason === null).length; + }, + absences() { + // Get all course attendants who have an absence reason + return this.documentation.participations.filter(p => p.absenceReason !== null); + }, + }, }; </script> diff --git a/aleksis/apps/alsijil/frontend/messages/de.json b/aleksis/apps/alsijil/frontend/messages/de.json index 672bfe5f0d806921482505eb1cde16c5ebef9b1c..31ae2d9763a98f946aa896a9ab79d5fc8d514a0f 100644 --- a/aleksis/apps/alsijil/frontend/messages/de.json +++ b/aleksis/apps/alsijil/frontend/messages/de.json @@ -49,13 +49,27 @@ } } }, - "title_plural": "Kursbuch" + "title_plural": "Kursbuch", + "present_number": "{present}/{total} anwesend" }, "excuse_types": { "menu_title": "Entschuldigungsarten" }, "extra_marks": { - "menu_title": "Zusätzliche Markierungen" + "menu_title": "Zusätzliche Markierungen", + "create": "Markierung erstellen", + "name": "Markierung", + "short_name": "Abkürzung", + "colour_fg": "Schriftfarbe", + "colour_bg": "Hintergrundfarbe", + "show_in_coursebook": "In Kursbuch-Übersicht zeigen", + "show_in_coursebook_helptext": "Wenn aktiviert tauchen diese Markierungen in den Zeilen im Kursbuch auf." + }, + "personal_notes": { + "note": "Notiz", + "create_personal_note": "Weitere Notiz", + "confirm_delete": "Notiz wirklich löschen?", + "confirm_delete_explanation": "Die Notiz \"{note}\" für {name} wird entfernt." }, "group_roles": { "menu_title_assign": "Gruppenrollen zuweisen", diff --git a/aleksis/apps/alsijil/frontend/messages/en.json b/aleksis/apps/alsijil/frontend/messages/en.json index 4f17201251a67936819498514b54d3ca3b592fbe..71a509cfec66659437760a1c2ab2fc41ad35cd3c 100644 --- a/aleksis/apps/alsijil/frontend/messages/en.json +++ b/aleksis/apps/alsijil/frontend/messages/en.json @@ -74,6 +74,7 @@ "courses": "Courses", "filter_for_obj": "Filter for group and course" }, + "present_number": "{present}/{total} present", "no_data": "No lessons for the selected groups and courses in this period", "no_results": "No search results for {search}" }