diff --git a/aleksis/apps/chronos/frontend/components/LessonEventSubject.vue b/aleksis/apps/chronos/frontend/components/LessonEventSubject.vue index 9a739936f497ad6f63b1d2780d244ab3b5b74092..7bbb83712b0cd556ca4e7d7fb07d43e59a8766c3 100644 --- a/aleksis/apps/chronos/frontend/components/LessonEventSubject.vue +++ b/aleksis/apps/chronos/frontend/components/LessonEventSubject.vue @@ -36,7 +36,7 @@ export default { {{ event.meta.amends.subject[attr] }} </span> <span v-else> - {{ event[attr] }} + {{ event["name"] }} </span> </span> </template> diff --git a/aleksis/apps/chronos/frontend/components/SelectTimetable.vue b/aleksis/apps/chronos/frontend/components/SelectTimetable.vue index 6e1931e71f89a0fc608ce6714866c5174592947d..76543ac322a63cff1af8e948bbab5f81dd088aa1 100644 --- a/aleksis/apps/chronos/frontend/components/SelectTimetable.vue +++ b/aleksis/apps/chronos/frontend/components/SelectTimetable.vue @@ -106,6 +106,11 @@ export default { </v-list-item-group> </v-list> </template> + <template #loading> + <v-skeleton-loader + type="list-item-avatar,list-item-avatar,list-item-avatar" + /> + </template> </v-data-iterator> </div> </template> diff --git a/aleksis/apps/chronos/frontend/components/calendar_feeds/details/LessonDetails.vue b/aleksis/apps/chronos/frontend/components/calendar_feeds/details/LessonDetails.vue index 0c9311450f0c7d8c3e922743f1403382da1148ce..627dd2fe2e8395da29d8d6c4162e29b99b2f1d1e 100644 --- a/aleksis/apps/chronos/frontend/components/calendar_feeds/details/LessonDetails.vue +++ b/aleksis/apps/chronos/frontend/components/calendar_feeds/details/LessonDetails.vue @@ -5,13 +5,17 @@ without-location > <template #title> - <div - :style="{ - color: currentSubject ? currentSubject.colour_fg || 'white' : 'white', - }" - > - <lesson-event-subject :event="selectedEvent" /> - </div> + <slot name="title"> + <div + :style="{ + color: currentSubject + ? currentSubject.colour_fg || 'white' + : 'white', + }" + > + <lesson-event-subject :event="selectedEvent" /> + </div> + </slot> </template> <template #badge> <cancelled-calendar-status-chip diff --git a/aleksis/apps/chronos/frontend/components/calendar_feeds/details/SupervisionDetails.vue b/aleksis/apps/chronos/frontend/components/calendar_feeds/details/SupervisionDetails.vue index 7d6455a054fd8d7a46433ea5ad4693e4c40ff43e..2aa6f026bc3745d71dcd0dd6a8f823b4a8f1af42 100644 --- a/aleksis/apps/chronos/frontend/components/calendar_feeds/details/SupervisionDetails.vue +++ b/aleksis/apps/chronos/frontend/components/calendar_feeds/details/SupervisionDetails.vue @@ -1,109 +1,20 @@ <template> - <base-calendar-feed-details - v-bind="$props" - :color="currentSubject ? currentSubject.colour_bg : null" - without-location - > + <lesson-details v-bind="$attrs" v-on="$listeners"> <template #title> - <div - :style="{ - color: currentSubject ? currentSubject.colour_fg || 'white' : 'white', - }" - > - <lesson-event-subject :event="selectedEvent" /> - </div> + <v-icon class="mr-1">mdi-coffee</v-icon> + {{ $t("chronos.supervisions.title") }} </template> - <template #badge> - <cancelled-calendar-status-chip - v-if="selectedEvent.meta.cancelled" - class="ml-4" - /> - <calendar-status-chip - color="warning" - icon="mdi-clipboard-alert-outline" - v-else-if="selectedEvent.meta.amended" - class="ml-4" - > - {{ $t("chronos.event.current_changes") }} - </calendar-status-chip> - </template> - <template #description> - <v-divider inset /> - <v-list-item> - <v-list-item-icon> - <v-icon color="primary">mdi-human-male-board </v-icon> - </v-list-item-icon> - <v-list-item-content> - <v-list-item-title> - <span v-if="teachers.length === 0" class="body-2 text--secondary">{{ - $t("chronos.event.no_teacher") - }}</span> - <lesson-related-object-chip - v-for="teacher in teachers" - :status="teacher.status" - :key="teacher.id" - new-icon="mdi-account-plus-outline" - >{{ teacher.full_name }}</lesson-related-object-chip - > - </v-list-item-title> - </v-list-item-content> - </v-list-item> - <v-list-item> - <v-list-item-icon> - <v-icon color="primary">mdi-door </v-icon> - </v-list-item-icon> - <v-list-item-content> - <v-list-item-title> - <span v-if="rooms.length === 0" class="body-2 text--secondary">{{ - $t("chronos.event.no_room") - }}</span> - <lesson-related-object-chip - v-for="room in rooms" - :status="room.status" - :key="room.id" - new-icon="mdi-door-open" - >{{ room.name }}</lesson-related-object-chip - > - </v-list-item-title> - </v-list-item-content> - </v-list-item> - <v-divider inset /> - <v-list-item v-if="selectedEvent.meta.comment"> - <v-list-item-content> - <v-list-item-title> - <v-alert - dense - outlined - type="warning" - icon="mdi-information-outline" - > - {{ selectedEvent.meta.comment }} - </v-alert> - </v-list-item-title> - </v-list-item-content> - </v-list-item> - </template> - </base-calendar-feed-details> + </lesson-details> </template> <script> -import calendarFeedDetailsMixin from "aleksis.core/mixins/calendarFeedDetails.js"; -import BaseCalendarFeedDetails from "aleksis.core/components/calendar/BaseCalendarFeedDetails.vue"; -import CalendarStatusChip from "aleksis.core/components/calendar/CalendarStatusChip.vue"; -import CancelledCalendarStatusChip from "aleksis.core/components/calendar/CancelledCalendarStatusChip.vue"; +import LessonDetails from "./LessonDetails.vue"; -import LessonRelatedObjectChip from "../../LessonRelatedObjectChip.vue"; -import lessonEvent from "../mixins/lessonEvent"; -import LessonEventSubject from "../../LessonEventSubject.vue"; export default { - name: "LessonDetails", + name: "SupervisionDetails", + extends: [LessonDetails], components: { - LessonEventSubject, - LessonRelatedObjectChip, - BaseCalendarFeedDetails, - CalendarStatusChip, - CancelledCalendarStatusChip, + LessonDetails, }, - mixins: [calendarFeedDetailsMixin, lessonEvent], }; </script> diff --git a/aleksis/apps/chronos/frontend/components/calendar_feeds/event_bar/LessonEventBar.vue b/aleksis/apps/chronos/frontend/components/calendar_feeds/event_bar/LessonEventBar.vue index 1f0eb6527d0859ead2edb1cf120ec5ef84b5c6c3..b63154d6e8017cb4cb6cf07a9c1fb4475d499994 100644 --- a/aleksis/apps/chronos/frontend/components/calendar_feeds/event_bar/LessonEventBar.vue +++ b/aleksis/apps/chronos/frontend/components/calendar_feeds/event_bar/LessonEventBar.vue @@ -11,9 +11,10 @@ class="d-flex justify-start" :class="{ 'px-1': true, - 'orange-border': + 'current-changes': selectedEvent.meta.amended && !selectedEvent.meta.cancelled, - 'red-border': selectedEvent.meta.cancelled, + cancelled: selectedEvent.meta.cancelled, + 'text-decoration-line-through': selectedEvent.meta.cancelled, }" :style="{ color: currentSubject ? currentSubject.colour_fg || 'white' : 'white', @@ -31,6 +32,8 @@ class="d-flex justify-center align-center flex-grow-1 text-truncate" > <div class="d-flex justify-center align-center flex-wrap text"> + <slot name="additionalElements"></slot> + <lesson-event-link-iterator v-if="!selectedEvent.meta.is_member" :items="selectedEvent.meta.groups" @@ -46,6 +49,7 @@ /> <lesson-event-subject + v-if="withSubject" :event="selectedEvent" attr="short_name" class="font-weight-medium mr-1" @@ -83,16 +87,23 @@ export default { return this.event; }, }, + props: { + withSubject: { + type: Boolean, + default: true, + required: false, + }, + }, mixins: [calendarFeedEventBarMixin, lessonEvent], }; </script> <style scoped> -.orange-border { +.current-changes { border: 3px orange solid; } -.red-border { +.cancelled { border: 3px red solid; } diff --git a/aleksis/apps/chronos/frontend/components/calendar_feeds/event_bar/SupervisionEventBar.vue b/aleksis/apps/chronos/frontend/components/calendar_feeds/event_bar/SupervisionEventBar.vue new file mode 100644 index 0000000000000000000000000000000000000000..59894990ee7d74358dc43a5c9719fb7d543c81a0 --- /dev/null +++ b/aleksis/apps/chronos/frontend/components/calendar_feeds/event_bar/SupervisionEventBar.vue @@ -0,0 +1,19 @@ +<template> + <lesson-event-bar :with-subject="false" v-bind="$attrs" v-on="$listeners"> + <template #additionalElements> + <v-icon size="12" color="white" class="mr-1">mdi-coffee</v-icon> + </template> + </lesson-event-bar> +</template> + +<script> +import LessonEventBar from "./LessonEventBar.vue"; + +export default { + name: "SupervisionEventBar", + components: { + LessonEventBar, + }, + extends: [LessonEventBar], +}; +</script> diff --git a/aleksis/apps/chronos/frontend/index.js b/aleksis/apps/chronos/frontend/index.js index a6a93183bca2cd7e07a5bc5a86a16a9c12e80938..cce4671639c62c7229657e9d54b1ed6fceab9833 100644 --- a/aleksis/apps/chronos/frontend/index.js +++ b/aleksis/apps/chronos/frontend/index.js @@ -22,6 +22,7 @@ export default { titleKey: "chronos.timetable.menu_title", icon: "mdi-grid", permission: "chronos.view_timetable_overview_rule", + fullWidth: true, }, }, { @@ -30,6 +31,7 @@ export default { name: "chronos.timetableWithId", meta: { permission: "chronos.view_timetable_overview_rule", + fullWidth: true, }, }, ], diff --git a/aleksis/apps/chronos/frontend/messages/en.json b/aleksis/apps/chronos/frontend/messages/en.json index 859b8bb8fe95a5fabd0cc5a747d14098ac302725..e38a4e03df2f494032be278d85d1c4fcec880984 100644 --- a/aleksis/apps/chronos/frontend/messages/en.json +++ b/aleksis/apps/chronos/frontend/messages/en.json @@ -26,6 +26,7 @@ "menu_title": "Substitutions" }, "supervisions": { + "title": "Supervision", "menu_title_daily": "Daily supervisions" }, "event": { diff --git a/aleksis/apps/chronos/models.py b/aleksis/apps/chronos/models.py index b5c2cc6c22a1c349ff3ca4751613d9749c169e70..7f01d7f67c9bff1458804e93a538daa340e014b3 100644 --- a/aleksis/apps/chronos/models.py +++ b/aleksis/apps/chronos/models.py @@ -67,7 +67,7 @@ from aleksis.core.mixins import ( SchoolTermRelatedExtensibleModel, ) from aleksis.core.models import CalendarEvent, DashboardWidget, Group, Person, Room, SchoolTerm -from aleksis.core.util.core_helpers import has_person +from aleksis.core.util.core_helpers import get_site_preferences, has_person class ValidityRange(ExtensibleModel): @@ -1478,6 +1478,10 @@ class LessonEvent(CalendarEvent): """Get the description of the lesson event.""" return render_to_string("chronos/lesson_event_description.txt", {"event": reference_object}) + @classmethod + def get_color(cls, request: HttpRequest | None = None) -> str: + return get_site_preferences()["chronos__lesson_color"] + @classmethod def value_color(cls, reference_object: LessonEvent, request: HttpRequest | None = None) -> str: """Get the color of the lesson event.""" @@ -1525,6 +1529,7 @@ class LessonEvent(CalendarEvent): "amends": cls.value_meta(reference_object.amends, request) if reference_object.amends else None, + "title": reference_object.title, "teachers": [ { "id": t.pk, @@ -1630,6 +1635,10 @@ class SupervisionEvent(LessonEvent): "chronos/supervision_event_description.txt", {"event": reference_object} ) + @classmethod + def get_color(cls, request: HttpRequest | None = None) -> str: + return get_site_preferences()["chronos__supervision_color"] + @classmethod def get_objects( cls, request: HttpRequest | None = None, params: dict[str, any] | None = None diff --git a/aleksis/apps/chronos/preferences.py b/aleksis/apps/chronos/preferences.py index 5fac608e79e12474784682a438890b511834c2b2..d319001c58641c5e68f6c8d86260a29606b3677f 100644 --- a/aleksis/apps/chronos/preferences.py +++ b/aleksis/apps/chronos/preferences.py @@ -2,8 +2,14 @@ from datetime import time from django.utils.translation import gettext_lazy as _ +from colorfield.widgets import ColorWidget from dynamic_preferences.preferences import Section -from dynamic_preferences.types import BooleanPreference, IntegerPreference, TimePreference +from dynamic_preferences.types import ( + BooleanPreference, + IntegerPreference, + StringPreference, + TimePreference, +) from aleksis.core.registries import person_preferences_registry, site_preferences_registry @@ -107,3 +113,27 @@ class SendNotificationsPerson(BooleanPreference): name = "send_notifications" default = True verbose_name = _("Send notifications for current timetable changes") + + +@site_preferences_registry.register +class LessonEventFeedColor(StringPreference): + """Color for the lesson calendar feed.""" + + section = chronos + name = "lesson_color" + default = "#a7ffeb" + verbose_name = _("Lesson calendar feed color") + widget = ColorWidget + required = True + + +@site_preferences_registry.register +class SupervisionEventFeedColor(StringPreference): + """Color for the supervision calendar feed.""" + + section = chronos + name = "supervision_color" + default = "#e6ee9c" + verbose_name = _("Supervision calendar feed color") + widget = ColorWidget + required = True