376 lines
11 KiB
Vue
376 lines
11 KiB
Vue
<script setup lang="ts">
|
|
import ItSuccessAlert from "@/components/ui/ItSuccessAlert.vue";
|
|
import ItTextarea from "@/components/ui/ItTextarea.vue";
|
|
import RichText from "@/components/ui/RichText.vue";
|
|
import { useCurrentCourseSession } from "@/composables";
|
|
import { UPSERT_ASSIGNMENT_COMPLETION_MUTATION } from "@/graphql/mutations";
|
|
import {
|
|
maxAssignmentPoints,
|
|
userAssignmentPoints,
|
|
} from "@/services/assignmentService";
|
|
import type {
|
|
Assignment,
|
|
AssignmentCompletion,
|
|
AssignmentEvaluationTask,
|
|
CourseSessionUser,
|
|
} from "@/types";
|
|
import { useMutation } from "@urql/vue";
|
|
import dayjs, { Dayjs } from "dayjs";
|
|
import * as log from "loglevel";
|
|
import { computed, reactive, ref } from "vue";
|
|
|
|
const props = defineProps<{
|
|
assignmentUser: CourseSessionUser;
|
|
assignment: Assignment;
|
|
assignmentCompletion: AssignmentCompletion;
|
|
showEvaluationUser?: boolean;
|
|
dueDate?: Dayjs;
|
|
}>();
|
|
|
|
const emit = defineEmits(["editTask"]);
|
|
|
|
const state = reactive({
|
|
showSuccessInfo: false,
|
|
});
|
|
|
|
log.debug("EvaluationSummary setup");
|
|
|
|
const courseSession = useCurrentCourseSession();
|
|
|
|
const upsertAssignmentCompletionMutation = useMutation(
|
|
UPSERT_ASSIGNMENT_COMPLETION_MUTATION
|
|
);
|
|
|
|
const evaluationComment = ref(
|
|
props.assignmentCompletion.completion_data.expert_evaluation_comment?.text ?? ""
|
|
);
|
|
|
|
const text = computed(() => {
|
|
if (props.assignment.assignment_type === "CASEWORK") {
|
|
return {
|
|
evaluationCriteria: "a.Beurteilungskriterium",
|
|
evaluationReason: "assignment.evaluationReason",
|
|
evaluationSubmit: "a.Bewertung freigeben",
|
|
evaluationSubmission: "a.Bewertung Freigabe",
|
|
evaluationFromUser: "a.Bewertung von x y",
|
|
evaluationSuccess: "a.Deine Bewertung für x y wurde freigegeben.",
|
|
};
|
|
} else if (props.assignment.assignment_type === "PRAXIS_ASSIGNMENT") {
|
|
return {
|
|
evaluationCriteria: "Feedback",
|
|
evaluationReason: "assignment.evaluationFeedback",
|
|
evaluationSubmit: "a.Feedback freigeben",
|
|
evaluationSubmission: "a.Feedback Freigabe",
|
|
evaluationFromUser: "a.Feedback von x y",
|
|
evaluationSuccess: "a.Dein Feedback für x y wurde freigegeben.",
|
|
};
|
|
} else {
|
|
return {
|
|
evaluationCriteria: "UNKNOWN ASSIGNMENT TYPE",
|
|
evaluationReason: "UNKNOWN ASSIGNMENT TYPE",
|
|
evaluationSubmit: "UNKNOWN ASSIGNMENT TYPE",
|
|
evaluationSubmission: "UNKNOWN ASSIGNMENT TYPE",
|
|
evaluationFromUser: "UNKNOWN ASSIGNMENT TYPE",
|
|
evaluationSuccess: "UNKNOWN ASSIGNMENT TYPE",
|
|
};
|
|
}
|
|
});
|
|
|
|
async function submitEvaluation() {
|
|
upsertAssignmentCompletionMutation.executeMutation({
|
|
assignmentId: props.assignment.id,
|
|
courseSessionId: courseSession.value.id,
|
|
assignmentUserId: props.assignmentUser.id,
|
|
completionStatus: "EVALUATION_SUBMITTED",
|
|
completionDataString: JSON.stringify({
|
|
expert_evaluation_comment: {
|
|
text: evaluationComment.value,
|
|
},
|
|
}),
|
|
evaluationPoints: userPoints.value,
|
|
// next line used for urql
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
// @ts-ignore
|
|
id: props.assignmentCompletion?.id,
|
|
});
|
|
log.debug("submitEvaluation");
|
|
state.showSuccessInfo = true;
|
|
}
|
|
|
|
function subTaskByPoints(task: AssignmentEvaluationTask, points = 0) {
|
|
return task.value.sub_tasks !== undefined
|
|
? task.value.sub_tasks.find((subTask) => subTask.value.points === points)
|
|
: null;
|
|
}
|
|
|
|
function evaluationForTask(task: AssignmentEvaluationTask) {
|
|
const expertData = props.assignmentCompletion.completion_data[task.id]?.expert_data;
|
|
if (!expertData) {
|
|
return {
|
|
points: 0,
|
|
text: "",
|
|
};
|
|
}
|
|
|
|
return expertData;
|
|
}
|
|
|
|
const maxPoints = computed(() => {
|
|
if (props.assignmentCompletion.completion_status === "EVALUATION_SUBMITTED") {
|
|
return props.assignmentCompletion.evaluation_max_points;
|
|
}
|
|
return maxAssignmentPoints(props.assignment);
|
|
});
|
|
|
|
const userPoints = computed(() =>
|
|
userAssignmentPoints(props.assignment, props.assignmentCompletion)
|
|
);
|
|
|
|
const userPointsWithDeduction = computed(() => {
|
|
const points = userPoints.value;
|
|
if (points && props.assignmentCompletion.evaluation_points_deducted > 0) {
|
|
return points - props.assignmentCompletion.evaluation_points_deducted;
|
|
}
|
|
return points;
|
|
});
|
|
|
|
const percentage = computed(() => {
|
|
if (props.assignmentCompletion.completion_status === "EVALUATION_SUBMITTED") {
|
|
return (
|
|
((props.assignmentCompletion?.evaluation_points_final ??
|
|
userPointsWithDeduction.value ??
|
|
0) /
|
|
(props.assignmentCompletion?.evaluation_max_points ?? 1)) *
|
|
100
|
|
);
|
|
} else {
|
|
return ((userPointsWithDeduction.value ?? 0) / (maxPoints.value ?? 1)) * 100;
|
|
}
|
|
});
|
|
|
|
const showNotPassed = computed(() => {
|
|
if (props.assignment.assignment_type === "CASEWORK") {
|
|
return percentage.value < 55;
|
|
}
|
|
|
|
return false;
|
|
});
|
|
|
|
const showPassed = computed(() => {
|
|
if (props.assignment.assignment_type === "CASEWORK") {
|
|
return percentage.value >= 55;
|
|
}
|
|
|
|
return false;
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<!-- eslint-disable vue/no-v-html -->
|
|
<div>
|
|
<h3
|
|
v-if="assignmentCompletion.evaluation_user && props.showEvaluationUser"
|
|
class="mb-6"
|
|
>
|
|
{{
|
|
$t(text.evaluationFromUser, {
|
|
x: assignmentCompletion.evaluation_user.first_name,
|
|
y: assignmentCompletion.evaluation_user.last_name,
|
|
})
|
|
}}
|
|
</h3>
|
|
<h3 v-else class="mb-6" data-cy="sub-title">{{ $t(text.evaluationSubmission) }}</h3>
|
|
<section class="mb-6 border p-6" data-cy="result-section">
|
|
<section v-if="props.assignment.assignment_type === 'CASEWORK'">
|
|
<div class="flex items-center">
|
|
<div class="heading-1 py-4" data-cy="user-points">
|
|
{{ userPointsWithDeduction }}
|
|
</div>
|
|
<div class="pl-2" data-cy="total-points">
|
|
{{ $t("assignment.von x Punkten", { x: maxPoints }) }}
|
|
({{ percentage.toFixed(0) }}%)
|
|
</div>
|
|
|
|
<div v-if="showNotPassed" class="ml-2">
|
|
<span class="my-2 rounded-md bg-error-red-200 px-2.5 py-0.5">
|
|
{{ $t("a.Nicht Bestanden") }}
|
|
</span>
|
|
</div>
|
|
|
|
<div v-if="showPassed" class="ml-2">
|
|
<span class="my-2 rounded-md bg-green-200 px-2.5 py-0.5">
|
|
{{ $t("a.Bestanden") }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
v-if="assignmentCompletion.evaluation_points_deducted > 0"
|
|
data-cy="points-deducted"
|
|
class="mb-4 text-gray-900"
|
|
>
|
|
<div>
|
|
{{ $t("a.Punkte aus Bewertung") }}:
|
|
{{ assignmentCompletion.evaluation_points }}
|
|
</div>
|
|
<div>
|
|
{{ $t("a.Abgezogene Punkte") }}:
|
|
{{ assignmentCompletion.evaluation_points_deducted }}
|
|
</div>
|
|
<div>
|
|
{{ $t("a.Grund") }}:
|
|
{{ assignmentCompletion.evaluation_points_deducted_reason }}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<div
|
|
v-if="
|
|
props.assignment.assignment_type === 'CASEWORK' &&
|
|
props.assignmentCompletion.completion_status === 'EVALUATION_SUBMITTED' &&
|
|
!props.assignmentCompletion.evaluation_passed
|
|
"
|
|
>
|
|
<span class="my-2 rounded-md bg-error-red-200 px-2.5 py-0.5">
|
|
{{ $t("a.Nicht bestanden") }}
|
|
</span>
|
|
</div>
|
|
|
|
<div v-if="props.assignment.assignment_type === 'CASEWORK'">
|
|
<p class="my-4">
|
|
{{ $t("assignment.evaluationInstrumentDescriptionText") }}
|
|
</p>
|
|
<p class="my-4">
|
|
<a
|
|
:href="props.assignment.evaluation_document_url"
|
|
class="link"
|
|
target="_blank"
|
|
>
|
|
{{ $t("a.Beurteilungsinstrument anzeigen") }}
|
|
</a>
|
|
</p>
|
|
</div>
|
|
|
|
<p v-if="props.dueDate" class="my-4" data-cy="evaluation-duedate">
|
|
{{ "a.Freigabetermin Bewertung" }}: {{ props.dueDate.format("DD.MM.YYYY") }}
|
|
{{ props.dueDate.format("HH.mm") }}
|
|
</p>
|
|
|
|
<p
|
|
v-if="
|
|
props.assignment.assignment_type === 'PRAXIS_ASSIGNMENT' &&
|
|
props.assignmentCompletion.completion_status !== 'EVALUATION_SUBMITTED'
|
|
"
|
|
>
|
|
{{ $t("a.assignment.evaluationFeedbackDescriptionText") }}
|
|
</p>
|
|
|
|
<section>
|
|
<div
|
|
v-for="historyEntry in props.assignmentCompletion.additional_json_data
|
|
?.submission_history ?? []"
|
|
:key="historyEntry.timestamp"
|
|
>
|
|
{{ dayjs(historyEntry.timestamp).format("DD.MM.YYYY HH.mm") }}:
|
|
{{ $t(historyEntry.translation_key) }}
|
|
({{ historyEntry.user_display_name }})
|
|
</div>
|
|
</section>
|
|
|
|
<div
|
|
v-if="props.assignmentCompletion.completion_status !== 'EVALUATION_SUBMITTED'"
|
|
class="mt-8"
|
|
>
|
|
<button
|
|
class="btn-primary text-large"
|
|
data-cy="submit-evaluation"
|
|
@click="submitEvaluation()"
|
|
>
|
|
{{ $t(text.evaluationSubmit) }}
|
|
</button>
|
|
|
|
<ItTextarea
|
|
v-model="evaluationComment"
|
|
class="mt-8"
|
|
:placeholder="`${$t('a.Kommentar erfassen')}...`"
|
|
data-cy="reason-text"
|
|
></ItTextarea>
|
|
</div>
|
|
<div v-else-if="evaluationComment" class="mt-4">
|
|
{{ evaluationComment }}
|
|
</div>
|
|
|
|
<div v-if="state.showSuccessInfo" class="mt-4">
|
|
<ItSuccessAlert
|
|
:text="
|
|
$t(text.evaluationSuccess, {
|
|
x: props.assignmentUser.first_name,
|
|
y: props.assignmentUser.last_name,
|
|
})
|
|
"
|
|
></ItSuccessAlert>
|
|
</div>
|
|
</section>
|
|
|
|
<section>
|
|
<div v-for="(task, index) in props.assignment.evaluation_tasks" :key="task.id">
|
|
<article class="border-t py-4">
|
|
<div class="flex flex-row justify-between">
|
|
<div class="mb-4 text-gray-900">
|
|
{{ $t(text.evaluationCriteria) }} {{ index + 1 }}:
|
|
{{ task.value.title }}
|
|
</div>
|
|
<div
|
|
v-if="
|
|
props.assignmentCompletion.completion_status !== 'EVALUATION_SUBMITTED'
|
|
"
|
|
>
|
|
<button
|
|
class="link pl-2text-sm whitespace-nowrap"
|
|
@click="emit('editTask', task)"
|
|
>
|
|
{{ $t("assignment.edit") }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<RichText
|
|
class="mb-2 font-bold"
|
|
:content="task.value.description"
|
|
open-links-in-new-tab
|
|
/>
|
|
|
|
<section class="mb-4">
|
|
<div
|
|
v-html="
|
|
subTaskByPoints(task, evaluationForTask(task).points)?.value.title
|
|
"
|
|
></div>
|
|
|
|
<RichText
|
|
:content="
|
|
subTaskByPoints(task, evaluationForTask(task).points)?.value.description
|
|
"
|
|
open-links-in-new-tab
|
|
/>
|
|
|
|
<div
|
|
v-if="assignment.assignment_type === 'CASEWORK'"
|
|
class="text-sm text-gray-800"
|
|
>
|
|
{{ evaluationForTask(task).points }} Punkte
|
|
</div>
|
|
</section>
|
|
|
|
<div>
|
|
<span class="font-bold">{{ $t(text.evaluationReason) }}:</span>
|
|
{{ evaluationForTask(task).text }}
|
|
</div>
|
|
</article>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</template>
|
|
|
|
<style lang="scss" scoped></style>
|