vbv/client/src/pages/cockpit/assignmentsPage/AssignmentDetails.vue

216 lines
7.2 KiB
Vue

<script setup lang="ts">
import ItPersonRow from "@/components/ui/ItPersonRow.vue";
import type { StatusCount } from "@/components/ui/ItProgress.vue";
import type { GradedUser } from "@/services/assignmentService";
import { loadAssignmentCompletionStatusData } from "@/services/assignmentService";
import type {
CourseSession,
CourseSessionUser,
LearningContentAssignment,
LearningContentEdoniqTest,
} from "@/types";
import log from "loglevel";
import { computed, onMounted, reactive } from "vue";
import AssignmentSubmissionProgress from "@/components/assignment/AssignmentSubmissionProgress.vue";
import { useCourseSessionDetailQuery } from "@/composables";
import { formatDueDate } from "../../../components/dueDates/dueDatesUtils";
import { stringifyParse } from "@/utils/utils";
import { useTranslation } from "i18next-vue";
const { t } = useTranslation();
export interface Props {
courseSession: CourseSession;
learningContent: LearningContentAssignment | LearningContentEdoniqTest;
userSelectionIds?: string[];
linkToResults?: boolean;
}
const props = defineProps<Props>();
log.debug("AssignmentDetails created", stringifyParse(props));
const courseSessionDetailResult = useCourseSessionDetailQuery();
const state = reactive({
progressStatusCount: {} as StatusCount,
gradedUsers: [] as GradedUser[],
assignmentSubmittedUsers: [] as CourseSessionUser[],
});
const assignmentDetail = computed(() => {
return courseSessionDetailResult.findAssignment(props.learningContent.id);
});
const isPraxisAssignment = computed(() => {
return (
props.learningContent.content_assignment.assignment_type === "PRAXIS_ASSIGNMENT"
);
});
onMounted(async () => {
log.debug("AssignmentDetails mounted", props.learningContent, props.userSelectionIds);
const { gradedUsers, assignmentSubmittedUsers } =
await loadAssignmentCompletionStatusData(
props.learningContent.content_assignment.id,
props.courseSession.id,
props.learningContent.id,
props.userSelectionIds ?? []
);
state.gradedUsers = gradedUsers;
state.assignmentSubmittedUsers = assignmentSubmittedUsers;
});
function findGradedUser(userId: string) {
return state.gradedUsers.find((gu) => gu.user.user_id === userId);
}
function findUserPointsHtml(userId: string) {
let result = "";
const gradedUser = findGradedUser(userId);
if (gradedUser) {
result = `${gradedUser.points} ${t("assignment.von x Punkten", {
x: gradedUser.maxPoints,
})}`;
result +=
" (" +
(((gradedUser.points ?? 0) / (gradedUser.maxPoints ?? 1)) * 100).toFixed(0) +
"%)";
if (!gradedUser.passed) {
result += ` <span class="my-2 rounded-md bg-error-red-200 px-2.5 py-0.5 inline-block leading-5">${t(
"a.Nicht bestanden"
)}</span>`;
}
}
return result;
}
function generateCertificatesLink(userId: string) {
const parts = props.learningContent.competence_certificate?.frontend_url?.split("/");
if (!parts) {
return "";
}
const certificatePart = parts[parts.length - 1];
return `/course/${props.courseSession.course.slug}/profile/${userId}/competence/certificates/${certificatePart}`;
}
</script>
<template>
<div>
<h2 class="heading-2 font-bold">
{{ learningContent.title }}
</h2>
<div class="pt-1 underline">
{{ $t("a.Circle") }} «{{ learningContent.circle?.title }}»
</div>
<div v-if="assignmentDetail">
<span v-if="assignmentDetail.submission_deadline?.start">
{{ $t("Abgabetermin Ergebnisse:") }}
{{ formatDueDate(assignmentDetail.submission_deadline?.start) }}
</span>
<template v-if="assignmentDetail.evaluation_deadline?.start">
<br />
{{ $t("Freigabetermin Bewertungen:") }}
{{ formatDueDate(assignmentDetail.evaluation_deadline?.start) }}
</template>
</div>
<div v-else>
{{ $t("Keine Auftragsdetails verfügbar.") }}
</div>
<div class="mt-4">
<!-- how to determine assignment-type? how to get AssignmentLearningContent? -->
<AssignmentSubmissionProgress
:course-session="courseSession"
:learning-content="learningContent"
:show-title="false"
:user-selection-ids="props.userSelectionIds"
/>
</div>
<div v-if="courseSessionDetailResult.filterMembers().length" class="mt-6">
<ul>
<ItPersonRow
v-for="csu in courseSessionDetailResult.filterMembers(props.userSelectionIds)"
:key="csu.user_id"
:name="`${csu.first_name} ${csu.last_name}`"
:avatar-url="csu.avatar_url"
:data-cy="csu.last_name"
>
<template #center>
<section class="flex w-full flex-col justify-between lg:flex-row lg:gap-8">
<div
v-if="
state.gradedUsers.map((gradedUser) => gradedUser.user).includes(csu)
"
class="flex items-center"
>
<div
class="relative flex h-7 w-7 items-center justify-center rounded-full border border-green-500 bg-green-500"
>
<it-icon-check class="h-4/5 w-4/5"></it-icon-check>
</div>
<div class="ml-2">
{{
isPraxisAssignment
? $t("a.Feedback freigegeben")
: $t("a.Bewertung freigegeben")
}}
</div>
</div>
<div
v-else-if="state.assignmentSubmittedUsers.includes(csu)"
class="flex items-center"
>
<div
class="relative flex h-7 w-7 items-center justify-center rounded-full border border-green-500"
>
<it-icon-check class="h-6 w-6"></it-icon-check>
</div>
<div class="ml-2">{{ $t("a.Ergebnisse abgegeben") }}</div>
</div>
<!-- eslint-disable vue/no-v-html -->
<p
v-if="findGradedUser(csu.user_id) && !isPraxisAssignment"
class="text-left md:text-right"
v-html="findUserPointsHtml(csu.user_id)"
></p>
</section>
</template>
<template #link>
<div class="lg:ml-4">
<router-link
v-if="
state.assignmentSubmittedUsers.includes(csu) &&
props.learningContent.content_type !==
'learnpath.LearningContentEdoniqTest'
"
:to="
props.linkToResults
? `/course/${props.courseSession.course.slug}/assignment-evaluation/${learningContent.content_assignment.id}/${csu.user_id}`
: generateCertificatesLink(csu.user_id)
"
class="link lg:w-full lg:text-right"
data-cy="show-results"
>
{{
props.linkToResults
? $t("a.Ergebnisse anschauen")
: $t("a.Profil anzeigen")
}}
</router-link>
</div>
</template>
</ItPersonRow>
</ul>
</div>
</div>
</template>
<style scoped></style>