feat: feedback received screen
This commit is contained in:
parent
bc78fe2533
commit
654ccb0d47
|
|
@ -0,0 +1,79 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from "vue";
|
||||||
|
import type { FeedbackRequest } from "@/services/selfEvaluationFeedback";
|
||||||
|
import { t } from "i18next";
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
feedback: FeedbackRequest;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
console.log(props.feedback);
|
||||||
|
|
||||||
|
const feedbackProviderAvatar = computed(() => {
|
||||||
|
return props.feedback.feedback_provider_user.avatar_url;
|
||||||
|
});
|
||||||
|
|
||||||
|
const feedbackProviderName = computed(() => {
|
||||||
|
return `${props.feedback.feedback_provider_user.first_name} ${props.feedback.feedback_provider_user.last_name}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
const getSmiley = (assessment: "FAIL" | "SUCCESS" | "UNKNOWN") => {
|
||||||
|
switch (assessment) {
|
||||||
|
case "SUCCESS":
|
||||||
|
return "it-icon-smiley-happy";
|
||||||
|
case "FAIL":
|
||||||
|
return "it-icon-smiley-thinking";
|
||||||
|
default:
|
||||||
|
return "it-icon-smiley-neutral";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getCaption = (assessment: "FAIL" | "SUCCESS" | "UNKNOWN") => {
|
||||||
|
switch (assessment) {
|
||||||
|
case "SUCCESS":
|
||||||
|
return t("selfEvaluation.yes");
|
||||||
|
case "FAIL":
|
||||||
|
return t("selfEvaluation.no");
|
||||||
|
default:
|
||||||
|
return t("a.Nicht bewertet");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
v-for="criteria in props.feedback.criteria"
|
||||||
|
:key="criteria.course_completion_id"
|
||||||
|
class="mb-10"
|
||||||
|
>
|
||||||
|
<span>{{ criteria.title }}</span>
|
||||||
|
<div class="mt-3 grid grid-cols-2 border-2 border-gray-200">
|
||||||
|
<!-- Feedback requester assessment -->
|
||||||
|
<div class="flex h-12 items-center pl-4">
|
||||||
|
<b>{{ $t("a.Deine Selbsteinschätzung") }}</b>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center justify-start space-x-2 bg-white">
|
||||||
|
<component :is="getSmiley(criteria.self_assessment)" class="h-6 w-6" />
|
||||||
|
<span>{{ getCaption(criteria.self_assessment) }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Feedback provider assessment -->
|
||||||
|
<div class="flex h-12 items-center bg-gray-200 pl-4">
|
||||||
|
<b>
|
||||||
|
{{
|
||||||
|
$t("a.Fremdeinschätzung von FEEDBACK_PROVIDER_NAME", {
|
||||||
|
FEEDBACK_PROVIDER_NAME: feedbackProviderName,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</b>
|
||||||
|
<img class="ml-2 h-7 w-7 rounded-full" :src="feedbackProviderAvatar" />
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center justify-start space-x-2 bg-gray-200">
|
||||||
|
<component :is="getSmiley(criteria.feedback_assessment)" class="h-6 w-6" />
|
||||||
|
<span>{{ getCaption(criteria.feedback_assessment) }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { LearningUnit, LearningUnitPerformanceCriteria } from "@/types";
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
learningUnit: LearningUnit;
|
||||||
|
criteria: LearningUnitPerformanceCriteria[];
|
||||||
|
showEditLink: boolean;
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
v-for="(completion, index) in criteria"
|
||||||
|
:key="completion.id"
|
||||||
|
class="flex flex-col space-y-4 border-t border-gray-400 py-8"
|
||||||
|
>
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<b>{{ completion.title }}</b>
|
||||||
|
<router-link
|
||||||
|
v-if="showEditLink"
|
||||||
|
:to="`${learningUnit.evaluate_url}?step=${index}`"
|
||||||
|
class="underline"
|
||||||
|
>
|
||||||
|
{{ $t("a.Bearbeiten") }}
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="completion.completion_status == 'SUCCESS'"
|
||||||
|
class="flex flex-row items-center space-x-2"
|
||||||
|
>
|
||||||
|
<it-icon-smiley-happy class="h-6 w-6" />
|
||||||
|
<span>{{ $t("selfEvaluation.yes") }}</span>
|
||||||
|
</div>
|
||||||
|
<div v-else class="flex flex-row items-center space-x-2">
|
||||||
|
<it-icon-smiley-thinking class="h-6 w-6" />
|
||||||
|
<span>{{ $t("selfEvaluation.no") }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
defineProps<{
|
||||||
|
feedbackMentorName: string;
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex space-x-2 bg-green-200 p-4">
|
||||||
|
<it-icon-check class="it-icon h-6 w-6 text-green-700" />
|
||||||
|
<div>
|
||||||
|
{{
|
||||||
|
$t("a.Du hast deine Selbsteinschätzung erfolgreich mit FULL_NAME geteilt.", {
|
||||||
|
FULL_NAME: feedbackMentorName,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="pt-6">
|
||||||
|
{{
|
||||||
|
$t(
|
||||||
|
"a.FULL_NAME wird eine Fremdeinschätzung für dich vornehmen. Du wirst per Benachrichtigung informiert, sobald die Fremdeinschätzung für dich freigegeben wurde.",
|
||||||
|
{ FULL_NAME: feedbackMentorName }
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -28,8 +28,8 @@ import type {
|
||||||
import { useQuery } from "@urql/vue";
|
import { useQuery } from "@urql/vue";
|
||||||
import orderBy from "lodash/orderBy";
|
import orderBy from "lodash/orderBy";
|
||||||
import log from "loglevel";
|
import log from "loglevel";
|
||||||
import type { ComputedRef, Ref } from "vue";
|
import type { ComputedRef } from "vue";
|
||||||
import { computed, onMounted, ref, toValue, watchEffect } from "vue";
|
import { computed, onMounted, ref, watchEffect } from "vue";
|
||||||
|
|
||||||
export function useCurrentCourseSession() {
|
export function useCurrentCourseSession() {
|
||||||
/**
|
/**
|
||||||
|
|
@ -487,34 +487,3 @@ export function useLearningMentors() {
|
||||||
loading,
|
loading,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useSelfEvaluationFeedback(learningUnitId: Ref<string> | string) {
|
|
||||||
const feedback = ref({});
|
|
||||||
const loading = ref(false);
|
|
||||||
const exists = ref(false);
|
|
||||||
|
|
||||||
const url = `/api/self-evaluation-feedback/requester/${toValue(
|
|
||||||
learningUnitId
|
|
||||||
)}/feedback`;
|
|
||||||
|
|
||||||
const fetchSelfEvaluationFeedback = async () => {
|
|
||||||
loading.value = true;
|
|
||||||
const { data, statusCode } = await useCSRFFetch(url).json();
|
|
||||||
loading.value = false;
|
|
||||||
|
|
||||||
if (statusCode.value === 404) {
|
|
||||||
exists.value = false;
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
exists.value = true;
|
|
||||||
feedback.value = data.value;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
onMounted(fetchSelfEvaluationFeedback);
|
|
||||||
|
|
||||||
return {
|
|
||||||
feedback,
|
|
||||||
exists,
|
|
||||||
loading,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -15,24 +15,16 @@ import SelfEvaluationSubmit from "@/pages/learningPath/selfEvaluationPage/SelfEv
|
||||||
|
|
||||||
log.debug("LearningContent.vue setup");
|
log.debug("LearningContent.vue setup");
|
||||||
|
|
||||||
const circleStore = useCircleStore();
|
|
||||||
const courseSession = useCurrentCourseSession();
|
|
||||||
const courseCompletionData = useCourseDataWithCompletion();
|
|
||||||
|
|
||||||
const questionIndex = useRouteQuery("step", "0", { transform: Number, mode: "push" });
|
|
||||||
const previousRoute = getPreviousRoute();
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
learningUnit: LearningUnit;
|
learningUnit: LearningUnit;
|
||||||
circle: CircleType;
|
circle: CircleType;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const learningUnitHasFeedbackPage = computed(
|
const circleStore = useCircleStore();
|
||||||
() => props.learningUnit?.feedback_user !== "NO_FEEDBACK"
|
const courseSession = useCurrentCourseSession();
|
||||||
);
|
const courseCompletionData = useCourseDataWithCompletion();
|
||||||
|
|
||||||
const questions = computed(() => props.learningUnit?.performance_criteria ?? []);
|
const questions = computed(() => props.learningUnit?.performance_criteria ?? []);
|
||||||
|
|
||||||
const numPages = computed(() => {
|
const numPages = computed(() => {
|
||||||
if (learningUnitHasFeedbackPage.value) {
|
if (learningUnitHasFeedbackPage.value) {
|
||||||
return questions.value.length + 1;
|
return questions.value.length + 1;
|
||||||
|
|
@ -41,6 +33,13 @@ const numPages = computed(() => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const questionIndex = useRouteQuery("step", "0", { transform: Number, mode: "push" });
|
||||||
|
const previousRoute = getPreviousRoute();
|
||||||
|
|
||||||
|
const learningUnitHasFeedbackPage = computed(
|
||||||
|
() => props.learningUnit?.feedback_user !== "NO_FEEDBACK"
|
||||||
|
);
|
||||||
|
|
||||||
const currentQuestion = computed(() => questions.value[questionIndex.value]);
|
const currentQuestion = computed(() => questions.value[questionIndex.value]);
|
||||||
const showPreviousButton = computed(() => questionIndex.value != 0);
|
const showPreviousButton = computed(() => questionIndex.value != 0);
|
||||||
const showNextButton = computed(
|
const showNextButton = computed(
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,29 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { LearningUnit, LearningUnitPerformanceCriteria } from "@/types";
|
import type { LearningUnit, LearningUnitPerformanceCriteria } from "@/types";
|
||||||
import { useLearningMentors, useSelfEvaluationFeedback } from "@/composables";
|
import { useLearningMentors } from "@/composables";
|
||||||
import { computed, ref } from "vue";
|
import { computed, ref } from "vue";
|
||||||
import ItButton from "@/components/ui/ItButton.vue";
|
import ItButton from "@/components/ui/ItButton.vue";
|
||||||
import NoMentorInformationPanel from "@/components/mentor/NoMentorInformationPanel.vue";
|
import NoMentorInformationPanel from "@/components/mentor/NoMentorInformationPanel.vue";
|
||||||
import { useCSRFFetch } from "@/fetchHelpers";
|
import { useCSRFFetch } from "@/fetchHelpers";
|
||||||
|
import { useSelfEvaluationFeedback } from "@/services/selfEvaluationFeedback";
|
||||||
|
import FeedbackRequestedInformationPanel from "@/components/selfEvaluationFeedback/FeedbackRequestedInformationPanel.vue";
|
||||||
|
import FeedbackReceived from "@/components/selfEvaluationFeedback/FeedbackReceived.vue";
|
||||||
|
import FeedbackRequested from "@/components/selfEvaluationFeedback/FeedbackRequested.vue";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
learningUnit: LearningUnit;
|
learningUnit: LearningUnit;
|
||||||
criteria: LearningUnitPerformanceCriteria[];
|
criteria: LearningUnitPerformanceCriteria[];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const {
|
const selfEvaluationFeedback = useSelfEvaluationFeedback(props.learningUnit.id);
|
||||||
feedback: storedFeedback,
|
const storedFeedback = computed(() => selfEvaluationFeedback.feedback.value);
|
||||||
exists: isStoredFeedbackAvailable,
|
const isStoredFeedbackLoading = computed(() => selfEvaluationFeedback.loading.value);
|
||||||
loading: isStoredFeedbackLoading,
|
|
||||||
} = useSelfEvaluationFeedback(props.learningUnit.id);
|
// if no feedback is stored "current session" state management (mentor selection etc.)
|
||||||
|
const learningMentors = useLearningMentors();
|
||||||
|
const mentors = computed(() => learningMentors.learningMentors.value);
|
||||||
|
const isMentorsLoading = computed(() => learningMentors.loading.value);
|
||||||
|
|
||||||
// if no feedback is stored "current session"
|
|
||||||
// state management (mentor selection etc.)
|
|
||||||
const { learningMentors, loading: isLearningMentorsLoading } = useLearningMentors();
|
|
||||||
const isCurrentSessionFeedbackRequested = ref(false);
|
const isCurrentSessionFeedbackRequested = ref(false);
|
||||||
const currentSessionRequestedMentor = ref();
|
const currentSessionRequestedMentor = ref();
|
||||||
|
|
||||||
|
|
@ -27,34 +31,43 @@ const VisualState = {
|
||||||
LOADING: "LOADING",
|
LOADING: "LOADING",
|
||||||
NO_MENTOR: "NO_MENTOR",
|
NO_MENTOR: "NO_MENTOR",
|
||||||
HAS_REQUESTED_FEEDBACK: "HAS_REQUESTED_FEEDBACK",
|
HAS_REQUESTED_FEEDBACK: "HAS_REQUESTED_FEEDBACK",
|
||||||
|
HAS_RECEIVED_FEEDBACK: "HAS_RECEIVED_FEEDBACK",
|
||||||
HAS_NOT_REQUESTED_FEEDBACK: "HAS_NOT_REQUESTED_FEEDBACK",
|
HAS_NOT_REQUESTED_FEEDBACK: "HAS_NOT_REQUESTED_FEEDBACK",
|
||||||
};
|
};
|
||||||
|
|
||||||
const currentVisualState = computed(() => {
|
const currentVisualState = computed(() => {
|
||||||
if (isLearningMentorsLoading.value || isStoredFeedbackLoading.value) {
|
if (isMentorsLoading.value || isStoredFeedbackLoading.value) {
|
||||||
return VisualState.LOADING;
|
return VisualState.LOADING;
|
||||||
} else if (learningMentors.value.length == 0) {
|
} else if (mentors.value.length == 0) {
|
||||||
return VisualState.NO_MENTOR;
|
return VisualState.NO_MENTOR;
|
||||||
} else if (
|
} else if (isCurrentSessionFeedbackRequested.value) {
|
||||||
isStoredFeedbackAvailable.value ||
|
|
||||||
isCurrentSessionFeedbackRequested.value
|
|
||||||
) {
|
|
||||||
return VisualState.HAS_REQUESTED_FEEDBACK;
|
return VisualState.HAS_REQUESTED_FEEDBACK;
|
||||||
|
} else if (storedFeedback.value && !storedFeedback.value.feedback_submitted) {
|
||||||
|
return VisualState.HAS_REQUESTED_FEEDBACK;
|
||||||
|
} else if (storedFeedback.value && storedFeedback.value.feedback_submitted) {
|
||||||
|
return VisualState.HAS_RECEIVED_FEEDBACK;
|
||||||
} else {
|
} else {
|
||||||
return VisualState.HAS_NOT_REQUESTED_FEEDBACK;
|
return VisualState.HAS_NOT_REQUESTED_FEEDBACK;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const feedbackMentorName = computed(() => {
|
const feedbackMentorName = computed(() => {
|
||||||
const mentor = isStoredFeedbackAvailable.value
|
let mentor = null;
|
||||||
? (storedFeedback.value as any).feedback_provider_user
|
|
||||||
: isCurrentSessionFeedbackRequested.value
|
if (storedFeedback.value) {
|
||||||
? currentSessionRequestedMentor.value
|
mentor = storedFeedback.value.feedback_provider_user;
|
||||||
: null;
|
} else if (isCurrentSessionFeedbackRequested.value) {
|
||||||
return mentor ? `${mentor.first_name} ${mentor.last_name}` : "";
|
mentor = currentSessionRequestedMentor.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mentor) {
|
||||||
|
return `${mentor.first_name} ${mentor.last_name}`;
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const onSubmitForMentorFeedback = async () => {
|
const onRequestFeedback = async () => {
|
||||||
const url = `/api/self-evaluation-feedback/requester/${props.learningUnit.id}/feedback/start`;
|
const url = `/api/self-evaluation-feedback/requester/${props.learningUnit.id}/feedback/start`;
|
||||||
const { error } = await useCSRFFetch(url).post({
|
const { error } = await useCSRFFetch(url).post({
|
||||||
feedback_provider_user_id: currentSessionRequestedMentor.value.id,
|
feedback_provider_user_id: currentSessionRequestedMentor.value.id,
|
||||||
|
|
@ -66,104 +79,77 @@ const onSubmitForMentorFeedback = async () => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="currentVisualState != VisualState.LOADING" class="mb-10 w-full pt-8">
|
<div v-if="currentVisualState != VisualState.LOADING">
|
||||||
<div class="w-full border border-gray-400">
|
<div class="mb-10 w-full pt-8">
|
||||||
<div class="m-6 space-y-6">
|
<div
|
||||||
<h3 class="heading-3">
|
v-if="currentVisualState != VisualState.HAS_RECEIVED_FEEDBACK"
|
||||||
{{ $t("a.Selbsteinschätzung teilen") }}
|
class="w-full border border-gray-400"
|
||||||
</h3>
|
>
|
||||||
<div v-if="currentVisualState == VisualState.NO_MENTOR">
|
<div class="m-6 space-y-6">
|
||||||
<NoMentorInformationPanel />
|
<h3 class="heading-3">
|
||||||
</div>
|
{{ $t("a.Selbsteinschätzung teilen") }}
|
||||||
<div v-if="currentVisualState == VisualState.HAS_REQUESTED_FEEDBACK">
|
</h3>
|
||||||
<div class="flex space-x-2 bg-green-200 p-4">
|
<NoMentorInformationPanel
|
||||||
<it-icon-check class="it-icon h-6 w-6 text-green-700" />
|
v-if="currentVisualState == VisualState.NO_MENTOR"
|
||||||
<div>
|
/>
|
||||||
|
<FeedbackRequestedInformationPanel
|
||||||
|
v-if="currentVisualState == VisualState.HAS_REQUESTED_FEEDBACK"
|
||||||
|
:feedback-mentor-name="feedbackMentorName"
|
||||||
|
/>
|
||||||
|
<div v-else-if="currentVisualState == VisualState.HAS_NOT_REQUESTED_FEEDBACK">
|
||||||
|
<p>
|
||||||
{{
|
{{
|
||||||
$t(
|
$t(
|
||||||
"a.Du hast deine Selbsteinschätzung erfolgreich mit FULL_NAME geteilt.",
|
"a.Du kannst deine Selbsteinschätzung mit deiner Lernbegleitung teilen, damit sie eine Fremdeinschätzung vornimmt."
|
||||||
{
|
|
||||||
FULL_NAME: feedbackMentorName,
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
</div>
|
</p>
|
||||||
</div>
|
<select
|
||||||
<div class="pt-6">
|
v-model="currentSessionRequestedMentor"
|
||||||
{{
|
data-cy="select-learning-mentor"
|
||||||
$t(
|
|
||||||
"a.FULL_NAME wird eine Fremdeinschätzung für dich vornehmen. Du wirst per Benachrichtigung informiert, sobald die Fremdeinschätzung für dich freigegeben wurde.",
|
|
||||||
{ FULL_NAME: feedbackMentorName }
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-else-if="currentVisualState == VisualState.HAS_NOT_REQUESTED_FEEDBACK">
|
|
||||||
<p>
|
|
||||||
{{
|
|
||||||
$t(
|
|
||||||
"a.Du kannst deine Selbsteinschätzung mit deiner Lernbegleitung teilen, damit sie eine Fremdeinschätzung vornimmt."
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</p>
|
|
||||||
<select
|
|
||||||
v-model="currentSessionRequestedMentor"
|
|
||||||
data-cy="select-learning-mentor"
|
|
||||||
>
|
|
||||||
<option
|
|
||||||
v-for="learningMentor in learningMentors"
|
|
||||||
:key="learningMentor.id"
|
|
||||||
:value="learningMentor.mentor"
|
|
||||||
>
|
>
|
||||||
{{ learningMentor.mentor.first_name }}
|
<option
|
||||||
{{ learningMentor.mentor.last_name }}
|
v-for="learningMentor in mentors"
|
||||||
</option>
|
:key="learningMentor.id"
|
||||||
</select>
|
:value="learningMentor.mentor"
|
||||||
<ItButton
|
>
|
||||||
class="mt-6"
|
{{ learningMentor.mentor.first_name }}
|
||||||
variant="primary"
|
{{ learningMentor.mentor.last_name }}
|
||||||
size="large"
|
</option>
|
||||||
:disabled="!currentSessionRequestedMentor"
|
</select>
|
||||||
data-cy="submit-assignment"
|
<ItButton
|
||||||
@click="onSubmitForMentorFeedback"
|
class="mt-6"
|
||||||
>
|
variant="primary"
|
||||||
<p v-if="!currentSessionRequestedMentor">
|
size="large"
|
||||||
{{ $t("a.Selbsteinschätzung teilen") }}
|
:disabled="!currentSessionRequestedMentor"
|
||||||
</p>
|
data-cy="submit-assignment"
|
||||||
<p v-else>
|
@click="onRequestFeedback"
|
||||||
{{
|
>
|
||||||
$t("a.Selbsteinschätzung mit MENTOR_NAME teilen", {
|
<p v-if="!currentSessionRequestedMentor">
|
||||||
MENTOR_NAME: feedbackMentorName,
|
{{ $t("a.Selbsteinschätzung teilen") }}
|
||||||
})
|
</p>
|
||||||
}}
|
<p v-else>
|
||||||
</p>
|
{{
|
||||||
</ItButton>
|
$t("a.Selbsteinschätzung mit MENTOR_NAME teilen", {
|
||||||
|
MENTOR_NAME: feedbackMentorName,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</p>
|
||||||
|
</ItButton>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<FeedbackReceived
|
||||||
<div
|
v-if="currentVisualState == VisualState.HAS_RECEIVED_FEEDBACK && storedFeedback"
|
||||||
v-for="(completion, index) in criteria"
|
:feedback="storedFeedback"
|
||||||
:key="completion.id"
|
/>
|
||||||
class="flex flex-col space-y-4 border-t border-gray-400 py-8"
|
<FeedbackRequested
|
||||||
>
|
v-else
|
||||||
<div class="flex justify-between">
|
:criteria="props.criteria"
|
||||||
<b>{{ completion.title }}</b>
|
:learning-unit="props.learningUnit"
|
||||||
<router-link :to="`${learningUnit.evaluate_url}?step=${index}`" class="underline">
|
:show-edit-link="currentVisualState != VisualState.HAS_REQUESTED_FEEDBACK"
|
||||||
{{ $t("a.Bearbeiten") }}
|
/>
|
||||||
</router-link>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-if="completion.completion_status == 'SUCCESS'"
|
|
||||||
class="flex flex-row items-center space-x-2"
|
|
||||||
>
|
|
||||||
<it-icon-smiley-happy class="h-6 w-6" />
|
|
||||||
<span>{{ $t("selfEvaluation.yes") }}</span>
|
|
||||||
</div>
|
|
||||||
<div v-else class="flex flex-row items-center space-x-2">
|
|
||||||
<it-icon-smiley-thinking class="h-6 w-6" />
|
|
||||||
<span>{{ $t("selfEvaluation.no") }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
import { useCSRFFetch } from "@/fetchHelpers";
|
||||||
|
import type { User } from "@/types";
|
||||||
|
import type { Ref } from "vue";
|
||||||
|
import { computed, onMounted, ref, toValue } from "vue";
|
||||||
|
|
||||||
|
export interface FeedbackRequest {
|
||||||
|
learning_unit_id: number;
|
||||||
|
circle_name: string;
|
||||||
|
// submitted => provider submitted (released) his/her feedback
|
||||||
|
feedback_submitted: boolean;
|
||||||
|
feedback_requester_user: User;
|
||||||
|
feedback_provider_user: User;
|
||||||
|
criteria: Criterion[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Criterion {
|
||||||
|
course_completion_id: string;
|
||||||
|
title: string;
|
||||||
|
self_assessment: "FAIL" | "SUCCESS" | "UNKNOWN";
|
||||||
|
feedback_assessment: "FAIL" | "SUCCESS" | "UNKNOWN";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useSelfEvaluationFeedback(learningUnitId: Ref<string> | string) {
|
||||||
|
const feedback = ref<FeedbackRequest>();
|
||||||
|
const loading = ref(false);
|
||||||
|
const error = ref();
|
||||||
|
|
||||||
|
const url = computed(
|
||||||
|
() => `/api/self-evaluation-feedback/requester/${toValue(learningUnitId)}/feedback`
|
||||||
|
);
|
||||||
|
|
||||||
|
const fetchSelfEvaluationFeedback = async () => {
|
||||||
|
feedback.value = undefined;
|
||||||
|
error.value = undefined;
|
||||||
|
|
||||||
|
loading.value = true;
|
||||||
|
const { data, statusCode, error: _error } = await useCSRFFetch(url.value).json();
|
||||||
|
loading.value = false;
|
||||||
|
|
||||||
|
if (_error.value) {
|
||||||
|
error.value = _error;
|
||||||
|
feedback.value = undefined;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (statusCode.value === 404) {
|
||||||
|
feedback.value = undefined;
|
||||||
|
} else {
|
||||||
|
feedback.value = data.value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(fetchSelfEvaluationFeedback);
|
||||||
|
|
||||||
|
return {
|
||||||
|
feedback,
|
||||||
|
error,
|
||||||
|
loading,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -589,3 +589,16 @@ export interface FeedbackData {
|
||||||
};
|
};
|
||||||
feedbackType: FeedbackType;
|
feedbackType: FeedbackType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type User = {
|
||||||
|
id: string;
|
||||||
|
first_name: string;
|
||||||
|
last_name: string;
|
||||||
|
email: string;
|
||||||
|
username: string;
|
||||||
|
avatar_url: string;
|
||||||
|
organisation: string | null;
|
||||||
|
is_superuser: boolean;
|
||||||
|
course_session_experts: any[];
|
||||||
|
language: string;
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -4,20 +4,33 @@ from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('assignment', '0011_assignment_solution_sample'),
|
("assignment", "0011_assignment_solution_sample"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='assignment',
|
model_name="assignment",
|
||||||
name='assignment_type',
|
name="assignment_type",
|
||||||
field=models.CharField(choices=[('PRAXIS_ASSIGNMENT', 'PRAXIS_ASSIGNMENT'), ('CASEWORK', 'CASEWORK'), ('PREP_ASSIGNMENT', 'PREP_ASSIGNMENT'), ('REFLECTION', 'REFLECTION'), ('CONDITION_ACCEPTANCE', 'CONDITION_ACCEPTANCE'), ('EDONIQ_TEST', 'EDONIQ_TEST')], default='CASEWORK', max_length=50),
|
field=models.CharField(
|
||||||
|
choices=[
|
||||||
|
("PRAXIS_ASSIGNMENT", "PRAXIS_ASSIGNMENT"),
|
||||||
|
("CASEWORK", "CASEWORK"),
|
||||||
|
("PREP_ASSIGNMENT", "PREP_ASSIGNMENT"),
|
||||||
|
("REFLECTION", "REFLECTION"),
|
||||||
|
("CONDITION_ACCEPTANCE", "CONDITION_ACCEPTANCE"),
|
||||||
|
("EDONIQ_TEST", "EDONIQ_TEST"),
|
||||||
|
],
|
||||||
|
default="CASEWORK",
|
||||||
|
max_length=50,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='assignment',
|
model_name="assignment",
|
||||||
name='needs_expert_evaluation',
|
name="needs_expert_evaluation",
|
||||||
field=models.BooleanField(default=False, help_text='Muss der Auftrag durch eine/n Experten/in oder eine Lernbegleitung beurteilt werden?'),
|
field=models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
help_text="Muss der Auftrag durch eine/n Experten/in oder eine Lernbegleitung beurteilt werden?",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -346,11 +346,11 @@ def create_default_users(default_password="test"):
|
||||||
_create_user(
|
_create_user(
|
||||||
_id=TEST_MENTOR1_USER_ID,
|
_id=TEST_MENTOR1_USER_ID,
|
||||||
email="test-mentor1@example.com",
|
email="test-mentor1@example.com",
|
||||||
first_name="[Mentor]",
|
first_name="Micheala",
|
||||||
last_name="Mentor",
|
last_name="Weber-Mentor",
|
||||||
password=default_password,
|
password=default_password,
|
||||||
language="de",
|
language="de",
|
||||||
avatar_url="",
|
avatar_url="/static/avatars/uk1.patrizia.huggel.jpg",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,36 @@
|
||||||
# Generated by Django 3.2.20 on 2024-01-24 09:04
|
# Generated by Django 3.2.20 on 2024-01-24 09:04
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('duedate', '0008_auto_20231108_0747'),
|
("duedate", "0008_auto_20231108_0747"),
|
||||||
('course_session', '0005_auto_20230825_1723'),
|
("course_session", "0005_auto_20230825_1723"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='coursesessionassignment',
|
model_name="coursesessionassignment",
|
||||||
name='evaluation_deadline',
|
name="evaluation_deadline",
|
||||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='assignment_evaluation_deadline', to='duedate.duedate'),
|
field=models.OneToOneField(
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name="assignment_evaluation_deadline",
|
||||||
|
to="duedate.duedate",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='coursesessionassignment',
|
model_name="coursesessionassignment",
|
||||||
name='submission_deadline',
|
name="submission_deadline",
|
||||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='assignment_submission_deadline', to='duedate.duedate'),
|
field=models.OneToOneField(
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name="assignment_submission_deadline",
|
||||||
|
to="duedate.duedate",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue