Merge branch 'feature/cockpit-ui' into develop

This commit is contained in:
Christian Cueni 2022-12-12 13:34:33 +01:00
commit 2248ad4396
9 changed files with 88 additions and 33 deletions

View File

@ -183,7 +183,20 @@ const profileDropdownData: DropdownListItem[] = [
class="nav-item" class="nav-item"
:class="{ 'nav-item--active': inCompetenceProfile() }" :class="{ 'nav-item--active': inCompetenceProfile() }"
> >
KompetenzNavi {{ $t("competences.title") }}
</router-link>
<router-link
v-if="
inCourse() &&
courseSessionsStore.courseSessionForRoute &&
courseSessionsStore.hasCockpit
"
:to="`${courseSessionsStore.courseSessionForRoute.course_url}/cockpit`"
class="nav-item"
:class="{ 'nav-item--active': inCockpit() }"
>
{{ $t("cockpit.title") }}
</router-link> </router-link>
<div class="hidden lg:block flex-auto"></div> <div class="hidden lg:block flex-auto"></div>

View File

@ -59,8 +59,8 @@ const clickLink = (to: string | undefined) => {
</button> </button>
</li> </li>
<li class="mt-6"> <li class="mt-6">
<button @click="clickLink(courseSession?.competence_url)"> <button @click="clickLink(`${courseSession?.course_url}/cockpit`)">
KompetenzNavi {{ $t("cockpit.title") }}
</button> </button>
</li> </li>
</ul> </ul>

View File

@ -77,6 +77,7 @@
"faq": "FAQ" "faq": "FAQ"
}, },
"cockpit": { "cockpit": {
"title": "Cockpit",
"tasksDone": "Erledigte Transferaufträge von Teilnehmer.", "tasksDone": "Erledigte Transferaufträge von Teilnehmer.",
"feedbacksDone": "Abgeschickte Feedbacks von Teilnehmer.", "feedbacksDone": "Abgeschickte Feedbacks von Teilnehmer.",
"examsDone": "Abgelegte Prüfungen von Teilnehmer.", "examsDone": "Abgelegte Prüfungen von Teilnehmer.",

View File

@ -37,14 +37,14 @@ const data = {
const selectedCircle = ref(1); const selectedCircle = ref(1);
function setActiveClasses(index: number) { function setActiveClasses(id: number) {
return selectedCircle.value === index return cockpitStore.selectedCircle === id
? ["bg-blue-900", "text-white"] ? ["bg-blue-900", "text-white"]
: ["text-bg-900"]; : ["text-bg-900"];
} }
function setActiveCircle(index: number) { function setActiveCircle(id: number) {
selectedCircle.value = index; cockpitStore.selectedCircle = id;
} }
</script> </script>
@ -55,16 +55,16 @@ function setActiveCircle(index: number) {
<h1 class="heading-3">{{ $t("general.circles") }}:</h1> <h1 class="heading-3">{{ $t("general.circles") }}:</h1>
<ul class="flex flex-row leading-7 text-base font-bold ml-4"> <ul class="flex flex-row leading-7 text-base font-bold ml-4">
<li <li
v-for="(circle, index) in data.circles" v-for="circle in cockpitStore.circles"
:key="circle" :key="circle.id"
class="mr-4 last:mr-0" class="mr-4 last:mr-0"
> >
<button <button
class="border-2 border-blue-900 rounded-full px-4 mr-4 last:mr-0" class="border-2 border-blue-900 rounded-full px-4 mr-4 last:mr-0"
:class="setActiveClasses(index)" :class="setActiveClasses(circle.id)"
@click="setActiveCircle(index)" @click="setActiveCircle(circle.id)"
> >
{{ circle }} {{ circle.title }}
</button> </button>
</li> </li>
</ul> </ul>

View File

@ -1,11 +1,13 @@
import { itGetCached } from "@/fetchHelpers"; import { itGetCached } from "@/fetchHelpers";
import type { CourseSessionUser } from "@/types"; import type { CourseSessionUser, ExpertSessionUser } from "@/types";
import log from "loglevel"; import log from "loglevel";
import { defineStore } from "pinia"; import { defineStore } from "pinia";
export type CockpitStoreState = { export type CockpitStoreState = {
courseSessionUsers: CourseSessionUser[] | undefined; courseSessionUsers: CourseSessionUser[] | undefined;
cockpitSessionUser: ExpertSessionUser | undefined;
selectedCircle: number;
}; };
export const useCockpitStore = defineStore({ export const useCockpitStore = defineStore({
@ -13,18 +15,27 @@ export const useCockpitStore = defineStore({
state: () => { state: () => {
return { return {
courseSessionUsers: undefined, courseSessionUsers: undefined,
cockpitSessionUser: undefined,
selectedCircle: -1,
} as CockpitStoreState; } as CockpitStoreState;
}, },
getters: {}, getters: {
circles: (state) => state.cockpitSessionUser?.circles,
},
actions: { actions: {
async loadCourseSessionUsers(courseSlug: string, reload = false) { async loadCourseSessionUsers(courseSlug: string, reload = false) {
log.debug("loadCockpitData called"); log.debug("loadCockpitData called");
this.courseSessionUsers = await itGetCached( const data = await itGetCached(`/api/course/sessions/${courseSlug}/users/`, {
`/api/course/sessions/${courseSlug}/users/`, reload: reload,
{ });
reload: reload,
} this.courseSessionUsers = data.users;
); this.cockpitSessionUser = data.cockpit_user;
if (this.cockpitSessionUser.circles?.length > 0) {
this.selectedCircle = this.cockpitSessionUser.circles[0].id;
}
if (!this.courseSessionUsers) { if (!this.courseSessionUsers) {
throw `No courseSessionUsers data found for user`; throw `No courseSessionUsers data found for user`;
} }

View File

@ -5,6 +5,7 @@ import log from "loglevel";
import { defineStore } from "pinia"; import { defineStore } from "pinia";
import { useRoute } from "vue-router"; import { useRoute } from "vue-router";
import { useUserStore } from "./user";
export type CourseSessionsStoreState = { export type CourseSessionsStoreState = {
courseSessions: CourseSession[] | undefined; courseSessions: CourseSession[] | undefined;
@ -30,6 +31,14 @@ export const useCourseSessionsStore = defineStore({
return _.uniqBy(state.courseSessions, "course.id"); return _.uniqBy(state.courseSessions, "course.id");
} }
}, },
hasCockpit() {
const userStore = useUserStore();
return (
this.courseSessionForRoute.experts.filter(
(expert) => expert.user_id === userStore.id
).length > 0
);
},
}, },
actions: { actions: {
async loadCourseSessionsData(reload = false) { async loadCourseSessionsData(reload = false) {

View File

@ -329,6 +329,8 @@ export interface CourseSession {
experts: CircleExpert[]; experts: CircleExpert[];
} }
export type Role = "MEMBER" | "EXPERT" | "TUTOR";
export interface CourseSessionUser { export interface CourseSessionUser {
session_title: string; session_title: string;
user_id: number; user_id: number;
@ -336,5 +338,13 @@ export interface CourseSessionUser {
last_name: string; last_name: string;
email: string; email: string;
avatar_url: string; avatar_url: string;
role: "MEMBER" | "EXPERT" | "TUTOR"; role: Role;
}
export interface ExpertSessionUser extends CourseSessionUser {
role: "EXPERT";
circles: {
id: number;
title: string;
}[];
} }

View File

@ -224,3 +224,14 @@ class CourseSessionUser(models.Model):
name="course_session_user_unique_course_session_user", name="course_session_user_unique_course_session_user",
) )
] ]
def to_dict(self):
return {
"session_title": self.course_session.title,
"user_id": self.user.id,
"first_name": self.user.first_name,
"last_name": self.user.last_name,
"email": self.user.email,
"avatar_url": self.user.avatar_url,
"role": self.role,
}

View File

@ -129,18 +129,18 @@ def get_course_session_users(request, course_slug):
course__slug=course_slug course__slug=course_slug
) )
qs = CourseSessionUser.objects.filter(course_session__in=course_sessions) qs = CourseSessionUser.objects.filter(course_session__in=course_sessions)
data = [ cockpit_user_csu = qs.filter(user_id=request.user.id)
{
"session_title": csu.course_session.title, if len(cockpit_user_csu) == 0:
"user_id": csu.user.id, return Response({"error": "User not found"}, status=404)
"first_name": csu.user.first_name,
"last_name": csu.user.last_name, user_data = [csu.to_dict() for csu in qs.exclude(user_id=request.user.id)]
"email": csu.user.email,
"avatar_url": csu.user.avatar_url, data = {
"role": csu.role, "cockpit_user": cockpit_user_csu[0].to_dict()
} | {"circles": cockpit_user_csu[0].expert.all().values("id", "title")},
for csu in qs "users": user_data,
] }
return Response(status=200, data=data) return Response(status=200, data=data)
except PermissionDenied as e: except PermissionDenied as e: