feat: mentor dashboard

This commit is contained in:
Reto Aebersold 2023-12-15 10:50:41 +01:00
parent 28542f8540
commit 3b0f562a7a
11 changed files with 230 additions and 38 deletions

View File

@ -1,17 +1,38 @@
<script setup lang="ts">
import log from "loglevel";
import { useRoute } from "vue-router";
const props = defineProps<{
courseSlug: string;
}>();
log.debug("CockpitIndexPage created", props.courseSlug);
const route = useRoute();
</script>
<template>
<div class="bg-gray-200">
<h1>Mentor</h1>
<nav class="border-b bg-white px-4 lg:px-8">
<ul class="flex flex-col lg:flex-row">
<li
class="border-t-2 border-t-transparent"
:class="{
'border-b-2 border-b-blue-900':
route.name === 'mentorCockpitOverview' || route.name === 'mentorCockpit',
}"
>
<router-link :to="{ name: 'mentorCockpitOverview' }" class="block py-3">
{{ $t("a.Übersicht") }}
</router-link>
</li>
<li
class="border-t-2 border-t-transparent lg:ml-12"
:class="{
'border-b-2 border-b-blue-900': route.name === 'mentorCockpitParticipants',
}"
>
<router-link :to="{ name: 'mentorCockpitParticipants' }" class="block py-3">
{{ $t("a.Teilnehmer") }}
</router-link>
</li>
</ul>
</nav>
<main class="container-large lg:mt-4">
<router-view></router-view>
</main>
</div>
</template>
<style scoped></style>

View File

@ -1,22 +0,0 @@
<script setup lang="ts">
import { useCockpitStore } from "@/stores/cockpit";
import { useCurrentCourseSession } from "@/composables";
import CockpitExpertPage from "./CockpitExpertPage.vue";
import CockpitMentorPage from "./CockpitMentorPage.vue";
const cockpitStore = useCockpitStore();
const courseSession = useCurrentCourseSession();
</script>
<template>
<div class="bg-gray-200">
<template v-if="cockpitStore.hasExpertCockpitType">
<CockpitExpertPage :course-slug="courseSession.course.slug" />
</template>
<template v-else-if="cockpitStore.hasMentorCockpitType">
<CockpitMentorPage :course-slug="courseSession.course.slug" />
</template>
</div>
</template>
<style scoped></style>

View File

@ -0,0 +1,7 @@
<script setup lang="ts"></script>
<template>
<router-link to="{name: ''}">{{ $t("a.Zurück") }}</router-link>
<div class="bg-white p-8">Hallo</div>
</template>

View File

@ -0,0 +1,61 @@
<script setup lang="ts">
import { useMentorCockpit } from "@/services/mentorCockpit";
import { useCurrentCourseSession } from "@/composables";
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
import { computed, ref } from "vue";
const courseSession = useCurrentCourseSession();
const { summary } = useMentorCockpit(courseSession.value.id);
const statusFilterValue = ref({ name: "Alle", id: "_all" });
const statusFilter = ref([
{ name: "Alle", id: "_all" },
{ name: "Zu erledigen", id: "todo" },
]);
interface Circle {
id: string;
title: string;
}
const circleFilterValue = ref({ name: "Circle: Alle", id: "_all" });
const circleFilter = computed(() => {
if (!summary.value) return [];
return [
{ name: "Circle: Alle", id: "_all" },
...summary.value.circles.map((circle: Circle) => ({
name: `Circle: ${circle.title}`,
id: circle.id,
})),
];
});
</script>
<template>
{{ summary }}
<div class="bg-white" v-if="summary">
<div class="flex flex-col space-x-2 lg:flex-row">
<ItDropdownSelect
v-model="statusFilterValue"
class="min-w-[10rem]"
:items="statusFilter"
borderless
></ItDropdownSelect>
<ItDropdownSelect
v-model="circleFilterValue"
class="min-w-[18rem]"
:items="circleFilter"
borderless
></ItDropdownSelect>
<!-- <ItDropdownSelect-->
<!-- v-model="circleFilterValue"-->
<!-- class="min-w-[18rem]"-->
<!-- :items="circleFilter"-->
<!-- borderless-->
<!-- ></ItDropdownSelect>-->
</div>
</div>
</template>

View File

@ -0,0 +1,35 @@
<script setup lang="ts">
import { useMentorCockpit } from "@/services/mentorCockpit";
import { useCurrentCourseSession } from "@/composables";
const courseSession = useCurrentCourseSession();
const { summary } = useMentorCockpit(courseSession.value.id);
</script>
<template>
<div v-if="summary" class="bg-white px-4 py-2">
<div
v-for="participant in summary.participants"
:key="participant.id"
class="flex flex-col items-center justify-between gap-4 border-b py-2 last:border-b-0 md:flex-row md:gap-16"
>
<div class="flex items-center space-x-2">
<img
:alt="participant.last_name"
class="h-11 w-11 rounded-full"
:src="participant.avatar_url || '/static/avatars/myvbv-default-avatar.png'"
/>
<div>
<div class="text-bold">
{{ participant.first_name }}
{{ participant.last_name }}
</div>
{{ participant.email }}
</div>
</div>
<!-- <router-link :to="{name: 'cockpitUserProfile', params: {userId: participant.id}}" class="underline">-->
<!-- {{ $t("a.Profil anzeigen") }}-->
<!-- </router-link>-->
</div>
</div>
</template>

View File

@ -0,0 +1,3 @@
<script setup lang="ts"></script>
<template>Praxis</template>

View File

@ -62,10 +62,6 @@ export async function handleCurrentCourseSession(to: RouteLocationNormalized) {
if (!courseSessionsStore.loaded) {
await courseSessionsStore.loadCourseSessionsData();
}
// Must be after loadCourseSessionsData & _currentCourseSlug is set!
const courseId = courseSessionsStore.currentCourseSession?.course.id || null;
await useCockpitStore().fetchCockpitType(courseId);
}
}
@ -102,3 +98,28 @@ export async function handleCourseSessionAsQueryParam(to: RouteLocationNormalize
}
}
}
export async function handleCockpit(to: RouteLocationNormalized) {
const courseSessionsStore = useCourseSessionsStore();
const courseId = courseSessionsStore.currentCourseSession?.course.id || null;
const cockpitStore = useCockpitStore();
await cockpitStore.fetchCockpitType(courseId);
if (to.name === "cockpit") {
if (cockpitStore.hasExpertCockpitType) {
return { name: "expertCockpit", params: to.params };
} else if (cockpitStore.hasMentorCockpitType) {
return { name: "mentorCockpitOverview", params: to.params };
}
}
const cockpitType = to.meta?.cockpitType;
if (!cockpitType) {
return;
}
if (cockpitType !== cockpitStore.cockpitType) {
return "/";
}
}

View File

@ -1,6 +1,7 @@
import DashboardPage from "@/pages/dashboard/DashboardPage.vue";
import LoginPage from "@/pages/LoginPage.vue";
import {
handleCockpit,
handleCourseSessionAsQueryParam,
handleCurrentCourseSession,
redirectToLoginIfRequired,
@ -134,16 +135,71 @@ const router = createRouter({
},
{
path: "/course/:courseSlug/cockpit",
name: "cockpit",
children: [
{
path: "",
component: () => import("@/pages/cockpit/cockpitPage/CockpitPage.vue"),
path: "expert",
component: () => import("@/pages/cockpit/cockpitPage/CockpitExpertPage.vue"),
props: true,
name: "expertCockpit",
meta: {
cockpitType: "expert",
},
},
{
path: "mentor",
component: () => import("@/pages/cockpit/cockpitPage/CockpitMentorPage.vue"),
name: "mentorCockpit",
meta: {
cockpitType: "mentor",
},
children: [
{
path: "",
component: () =>
import("@/pages/cockpit/cockpitPage/mentor/MentorOverview.vue"),
name: "mentorCockpitOverview",
meta: {
cockpitType: "mentor",
},
},
{
path: "participants",
component: () =>
import("@/pages/cockpit/cockpitPage/mentor/MentorParticipants.vue"),
name: "mentorCockpitParticipants",
meta: {
cockpitType: "mentor",
},
},
{
path: "details",
component: () =>
import("@/pages/cockpit/cockpitPage/mentor/MentorDetailParentPage.vue"),
meta: {
cockpitType: "mentor",
},
children: [
{
path: "praxis-assignments",
component: () =>
import(
"@/pages/cockpit/cockpitPage/mentor/MentorPraxisAssignment.vue"
),
name: "mentorCockpitPraxisAssignments",
meta: {
cockpitType: "mentor",
},
},
],
},
],
},
{
path: "profile/:userId",
component: () => import("@/pages/cockpit/CockpitUserProfilePage.vue"),
props: true,
name: "cockpitUserProfile",
},
{
path: "profile/:userId/:circleSlug",
@ -259,6 +315,8 @@ router.beforeEach(redirectToLoginIfRequired);
router.beforeEach(handleCurrentCourseSession);
router.beforeEach(handleCourseSessionAsQueryParam);
router.beforeEach(handleCockpit);
router.beforeEach(addToHistory);
export default router;

View File

@ -32,5 +32,6 @@ export const useCockpitStore = defineStore("cockpit", () => {
hasMentorCockpitType,
hasNoCockpitType,
isLoading,
cockpitType,
};
});

View File

@ -215,6 +215,13 @@ def create_versicherungsvermittlerin_course(
cs = CourseSession.objects.create(course_id=course_id, title=names[language])
for assignment in Assignment.objects.all():
if assignment.get_course().id == course_id:
CourseSessionAssignment.objects.get_or_create(
course_session=cs,
learning_content=assignment.find_attached_learning_content(),
)
if language == "de":
for user_data in default_users:
CourseSessionUser.objects.create(

View File

@ -87,7 +87,7 @@ def get_praxis_assignments(
for course_session_assignment in CourseSessionAssignment.objects.filter(
course_session=course_session,
learning_content__content_assignment__assignment_type__in=[
learning_content__assignment_type__in=[
AssignmentType.PRAXIS_ASSIGNMENT.value,
],
):