VBV-514: Termine nur für ausgewählte Circles anzeigen im Cockpit

This commit is contained in:
Daniel Egger 2023-09-15 14:50:52 +02:00
parent f5d44bcb90
commit db40368244
9 changed files with 89 additions and 34 deletions

View File

@ -1,5 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import { formatDate } from "@/components/dueDates/dueDatesUtils"; import { formatDueDate } from "@/components/dueDates/dueDatesUtils";
import type { DueDate } from "@/types"; import type { DueDate } from "@/types";
const props = defineProps<{ const props = defineProps<{
@ -30,7 +30,7 @@ const props = defineProps<{
</div> </div>
</div> </div>
<div> <div>
{{ formatDate(props.dueDate.start, props.dueDate.end) }} {{ formatDueDate(props.dueDate.start, props.dueDate.end) }}
</div> </div>
</div> </div>
</template> </template>

View File

@ -1,38 +1,45 @@
import type { Dayjs } from "dayjs"; import type { Dayjs } from "dayjs";
import dayjs from "dayjs";
import LocalizedFormat from "dayjs/plugin/localizedFormat";
import i18next from "i18next"; import i18next from "i18next";
export const formatDate = (start: Dayjs, end: Dayjs) => { export const formatDueDate = (start: string, end: string) => {
const startDateString = getDateString(start); dayjs.extend(LocalizedFormat);
const endDateString = getDateString(end); const startDayjs = dayjs(start);
const endDayjs = dayjs(end);
const startDateString = getDateString(startDayjs);
const endDateString = getDateString(endDayjs);
// if start isundefined, dont show the day twice // if startDayjs isundefined, dont show the day twice
if (!start.isValid() && !end.isValid()) { if (!startDayjs.isValid() && !endDayjs.isValid()) {
return i18next.t("Termin nicht festgelegt"); return i18next.t("Termin nicht festgelegt");
} }
if (!start || (!start.isValid() && end.isValid())) { if (!startDayjs || (!startDayjs.isValid() && endDayjs.isValid())) {
return `${endDateString} ${getTimeString(end)} ${end.format("[Uhr]")}`; return `${endDateString} ${getTimeString(endDayjs)} ${endDayjs.format("[Uhr]")}`;
} }
if (!end || (!end.isValid() && start.isValid())) { if (!endDayjs || (!endDayjs.isValid() && startDayjs.isValid())) {
return `${startDateString} ${getTimeString(start)} ${start.format("[Uhr]")}`; return `${startDateString} ${getTimeString(startDayjs)} ${startDayjs.format(
"[Uhr]"
)}`;
} }
// if start and end are on the same day, dont show the day twice // if startDayjs and endDayjs are on the same day, dont show the day twice
if (startDateString === endDateString) { if (startDateString === endDateString) {
return `${startDateString} ${getTimeString(start)} - ${getTimeString( return `${startDateString} ${getTimeString(startDayjs)} - ${getTimeString(
end endDayjs
)} ${end.format("[Uhr]")}`; )} ${endDayjs.format("[Uhr]")}`;
} }
return `${startDateString} ${getTimeString(start)} - ${endDateString} ${getTimeString( return `${startDateString} ${getTimeString(
end startDayjs
)}`; )} - ${endDateString} ${getTimeString(endDayjs)}`;
}; };
export const getTimeString = (date?: Dayjs) => { export const getTimeString = (date?: Dayjs) => {
if (date) { if (date) {
return `${date.format("H:mm")}`; return `${date.format("HH:mm")}`;
} }
return ""; return "";
}; };

View File

@ -1,22 +1,33 @@
<script setup lang="ts"> <script setup lang="ts">
import { useCurrentCourseSession } from "@/composables"; import { useCurrentCourseSession } from "@/composables";
import DueDateSingle from "@/components/dueDates/DueDateSingle.vue"; import DueDateSingle from "@/components/dueDates/DueDateSingle.vue";
import { computed } from "vue";
import { useCockpitStore } from "@/stores/cockpit";
const cockpitStore = useCockpitStore();
const courseSession = useCurrentCourseSession(); const courseSession = useCurrentCourseSession();
const dueDates = courseSession.value.due_dates.slice(0, 2);
const circleDates = computed(() => {
const dueDates = courseSession.value.due_dates.filter((dueDate) => {
return cockpitStore.selectedCircles.includes(
dueDate?.circle?.translation_key ?? ""
);
});
return dueDates.slice(0, 4);
});
</script> </script>
<template> <template>
<div class="flex flex-col space-y-2"> <div class="flex flex-col space-y-2">
<h3 class="heading-3">{{ $t("Nächste Termine") }}</h3> <h3 class="heading-3">{{ $t("Nächste Termine") }}</h3>
<div <div
v-for="dueDate in dueDates" v-for="dueDate in circleDates"
:key="dueDate.id" :key="dueDate.id"
class="border-t border-gray-500 pt-2" class="border-t border-gray-500 pt-2"
> >
<DueDateSingle :due-date="dueDate" :single-line="true"></DueDateSingle> <DueDateSingle :due-date="dueDate" :single-line="true"></DueDateSingle>
</div> </div>
<div v-if="dueDates.length === 0">{{ $t("dueDates.noDueDatesAvailable") }}</div> <div v-if="circleDates.length === 0">{{ $t("dueDates.noDueDatesAvailable") }}</div>
<a class="border-t border-gray-500 pt-8 underline" href=""> <a class="border-t border-gray-500 pt-8 underline" href="">
{{ $t("dueDates.showAllDueDates") }} {{ $t("dueDates.showAllDueDates") }}
</a> </a>

View File

@ -2,7 +2,9 @@
<div class="mb-12 grid grid-cols-icon-card gap-x-4 grid-areas-icon-card"> <div class="mb-12 grid grid-cols-icon-card gap-x-4 grid-areas-icon-card">
<it-icon-calendar-light class="w-[60px] grid-in-icon" /> <it-icon-calendar-light class="w-[60px] grid-in-icon" />
<h2 class="text-large font-bold grid-in-title">{{ $t("a.Datum") }}</h2> <h2 class="text-large font-bold grid-in-title">{{ $t("a.Datum") }}</h2>
<p class="grid-in-value">{{ formatDate(start, end) }}</p> <p class="grid-in-value">
{{ formatDueDate(props.attendanceCourse.start, props.attendanceCourse.end) }}
</p>
</div> </div>
<div class="mb-12 grid grid-cols-icon-card gap-x-4 grid-areas-icon-card"> <div class="mb-12 grid grid-cols-icon-card gap-x-4 grid-areas-icon-card">
<it-icon-location class="w-[60px] grid-in-icon" /> <it-icon-location class="w-[60px] grid-in-icon" />
@ -20,7 +22,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { formatDate } from "@/components/dueDates/dueDatesUtils"; import { formatDueDate } from "@/components/dueDates/dueDatesUtils";
import type { CourseSessionAttendanceCourse } from "@/types"; import type { CourseSessionAttendanceCourse } from "@/types";
import dayjs from "dayjs"; import dayjs from "dayjs";
import LocalizedFormat from "dayjs/plugin/localizedFormat"; import LocalizedFormat from "dayjs/plugin/localizedFormat";
@ -33,8 +35,6 @@ export interface Props {
const props = defineProps<Props>(); const props = defineProps<Props>();
dayjs.extend(LocalizedFormat); dayjs.extend(LocalizedFormat);
const start = computed(() => dayjs(props.attendanceCourse.start));
const end = computed(() => dayjs(props.attendanceCourse.end));
const location = computed(() => props.attendanceCourse.location); const location = computed(() => props.attendanceCourse.location);
const trainer = computed(() => props.attendanceCourse.trainer); const trainer = computed(() => props.attendanceCourse.trainer);
</script> </script>

View File

@ -40,10 +40,6 @@ export const useCourseSessionsStore = defineStore("courseSessions", () => {
reload: reload, reload: reload,
})) as CourseSessionUser[]; })) as CourseSessionUser[];
cs.users = users; cs.users = users;
cs.due_dates.forEach((dueDate) => {
dueDate.start = dayjs(dueDate.start);
dueDate.end = dayjs(dueDate.end);
});
sortDueDates(cs.due_dates); sortDueDates(cs.due_dates);
}) })
); );

View File

@ -1,6 +1,5 @@
import type { AssignmentCompletionStatus as AssignmentCompletionStatusGenerated } from "@/gql/graphql"; import type { AssignmentCompletionStatus as AssignmentCompletionStatusGenerated } from "@/gql/graphql";
import type { Circle } from "@/services/circle"; import type { Circle } from "@/services/circle";
import type { Dayjs } from "dayjs";
import type { Component } from "vue"; import type { Component } from "vue";
export type LoginMethod = "local" | "sso"; export type LoginMethod = "local" | "sso";
@ -624,8 +623,8 @@ export interface UserAssignmentCompletionStatus {
export type DueDate = { export type DueDate = {
id: number; id: number;
start: Dayjs; start: string;
end: Dayjs; end: string;
title: string; title: string;
assignment_type_translation_key: string; assignment_type_translation_key: string;
date_type_translation_key: string; date_type_translation_key: string;
@ -633,4 +632,5 @@ export type DueDate = {
url: string; url: string;
course_session: number | null; course_session: number | null;
page: number | null; page: number | null;
circle: CircleLight | null;
}; };

View File

@ -103,6 +103,16 @@ class CourseBasePage(Page):
return course_parent_page.specific.course return course_parent_page.specific.course
return None return None
def get_circle(self):
from vbv_lernwelt.learnpath.models import Circle
try:
return self.get_ancestors().exact_type(Circle).first()
except Exception:
# noop
pass
return None
def get_circles(self): def get_circles(self):
course_parent_page = self.get_course_parent() course_parent_page = self.get_course_parent()

View File

@ -97,6 +97,13 @@ class DueDate(models.Model):
return datetime.timedelta(0) return datetime.timedelta(0)
return self.end - self.start return self.end - self.start
def get_circle(self):
try:
return self.page.specific.get_circle()
except Exception:
# noop
return None
@classmethod @classmethod
def get_users_next_events_qs( def get_users_next_events_qs(
cls, user: User, course_session: CourseSession = None, limit=10 cls, user: User, course_session: CourseSession = None, limit=10

View File

@ -4,6 +4,30 @@ from vbv_lernwelt.duedate.models import DueDate
class DueDateSerializer(serializers.ModelSerializer): class DueDateSerializer(serializers.ModelSerializer):
circle = serializers.SerializerMethodField()
class Meta: class Meta:
model = DueDate model = DueDate
fields = "__all__" fields = [
"start",
"end",
"manual_override_fields",
"title",
"assignment_type_translation_key",
"date_type_translation_key",
"subtitle",
"url",
"course_session",
"page",
"circle",
]
def get_circle(self, obj):
circle = obj.get_circle()
if circle:
return {
"id": circle.id,
"title": circle.title,
"translation_key": circle.translation_key,
}
return None