diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/absences/ManageStudentsDialog.vue b/aleksis/apps/alsijil/frontend/components/coursebook/absences/ManageStudentsDialog.vue index 1e9988be2a02b91eab0eceb0a11156ce31a39ce1..3248becfa4ddeaaaf998794f5398d5845f38d21f 100644 --- a/aleksis/apps/alsijil/frontend/components/coursebook/absences/ManageStudentsDialog.vue +++ b/aleksis/apps/alsijil/frontend/components/coursebook/absences/ManageStudentsDialog.vue @@ -334,6 +334,15 @@ export default { <extra-mark-buttons @input="handleMultipleAction('extraMark', $event)" /> + <h4>{{ $t("alsijil.personal_notes.tardiness") }}</h4> + <tardiness-field + v-bind="documentationPartProps" + :loading="loading" + :disabled="loading" + :value="0" + :participations="selected" + @input="handleMultipleAction('tardiness', $event)" + /> </div> </v-scroll-y-reverse-transition> </template> diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/absences/TardinessField.vue b/aleksis/apps/alsijil/frontend/components/coursebook/absences/TardinessField.vue index 74a85ae09018fcc810950e6b10a52a8e5a948607..774decab0a88867af0fdb510ac8813edefec9a14 100644 --- a/aleksis/apps/alsijil/frontend/components/coursebook/absences/TardinessField.vue +++ b/aleksis/apps/alsijil/frontend/components/coursebook/absences/TardinessField.vue @@ -2,20 +2,20 @@ import { DateTime } from "luxon"; import documentationPartMixin from "../documentation/documentationPartMixin"; import ConfirmDialog from "aleksis.core/components/generic/dialogs/ConfirmDialog.vue"; -import PositiveSmallIntegerField from "aleksis.core/components/generic/forms/PositiveSmallIntegerField.vue"; +import formRulesMixin from "aleksis.core/mixins/formRulesMixin.js"; export default { name: "TardinessField", - components: { ConfirmDialog, PositiveSmallIntegerField }, - mixins: [documentationPartMixin], + components: { ConfirmDialog }, + mixins: [documentationPartMixin, formRulesMixin], props: { value: { type: Number, default: null, required: false, }, - participation: { - type: Object, + participations: { + type: Array, required: true, }, }, @@ -27,6 +27,40 @@ export default { let diff = lessonEnd.diff(lessonStart, "minutes"); return diff.toObject().minutes; }, + defaultTimes() { + const lessonStart = DateTime.fromISO(this.documentation.datetimeStart); + const lessonEnd = DateTime.fromISO(this.documentation.datetimeEnd); + const now = DateTime.now(); + + let current = []; + + if (now >= lessonStart && now <= lessonEnd) { + const diff = parseInt( + now.diff(lessonStart, "minutes").toObject().minutes, + ); + + current.push({ + text: diff, + value: diff, + current: true, + }); + } + + return current.concat([ + { + text: 5, + value: 5, + }, + { + text: 10, + value: 10, + }, + { + text: 15, + value: 15, + }, + ]); + }, }, methods: { lessonLengthRule(time) { @@ -46,14 +80,23 @@ export default { cancel() { this.saveValue(this.previousValue); }, + processValueObjectOptional(value) { + if (Object.hasOwn(value, "value")) { + return value.value; + } + + return value; + }, set(newValue) { - if (!newValue) { + newValue = this.processValueObjectOptional(newValue); + + if (!newValue || parseInt(newValue) === 0) { // this is a DELETE action, show the dialog, ... this.showDeleteConfirm = true; return; } - this.saveValue(newValue); + this.saveValue(parseInt(newValue)); }, }, data() { @@ -69,17 +112,40 @@ export default { </script> <template> - <positive-small-integer-field + <v-combobox outlined class="mt-1" prepend-inner-icon="mdi-clock-alert-outline" :suffix="$t('time.minutes')" :label="$t('alsijil.personal_notes.tardiness')" - :rules="[lessonLengthRule]" + :rules=" + $rules() + .isANumber.isAWholeNumber.isGreaterThan(0) + .build([lessonLengthRule]) + .map((f) => (v) => f(this.processValueObjectOptional(v))) + " + :items="defaultTimes" :value="value" @change="set($event)" v-bind="$attrs" > + <template #item="{ item }"> + <v-list-item-icon v-if="item.current"> + <v-icon>mdi-shimmer</v-icon> + </v-list-item-icon> + <v-list-item-content> + <v-list-item-title> + {{ + $tc( + item.current + ? "alsijil.personal_notes.minutes_late_current" + : "time.minutes_n", + item.value, + ) + }} + </v-list-item-title> + </v-list-item-content> + </template> <template #append> <confirm-dialog v-model="showDeleteConfirm" @@ -93,13 +159,13 @@ export default { {{ $t("alsijil.personal_notes.confirm_delete_tardiness", { tardiness: previousValue, - name: participation.person.fullName, + name: participations.map((p) => p.person.firstName).join(", "), }) }} </template> </confirm-dialog> </template> - </positive-small-integer-field> + </v-combobox> </template> <style scoped> diff --git a/aleksis/apps/alsijil/frontend/messages/en.json b/aleksis/apps/alsijil/frontend/messages/en.json index 706ffbea77429f694154d96390aab829148e35db..f3e347855a84bcaa2f734ebf60f316edf1fa8493 100644 --- a/aleksis/apps/alsijil/frontend/messages/en.json +++ b/aleksis/apps/alsijil/frontend/messages/en.json @@ -131,6 +131,7 @@ "tardiness": "Tardiness", "late": "Late", "minutes_late": "on time | one minute late | {n} minutes late", + "minutes_late_current": "on time (based on current time) | one minute late (based on current time) | {n} minutes late (based on current time)", "lesson_length_exceeded": "The tardiness exceeds the length of the lesson", "confirm_delete": "Delete note?", "confirm_delete_explanation": "The note \"{note}\" for {name} will be removed.", @@ -141,6 +142,7 @@ "back_to_overview": "Back to overview" }, "time": { - "minutes": "minutes" + "minutes": "minutes", + "minutes_n": "no minutes | one minute | {n} minutes" } }