Files
classroomio/apps/dashboard/src/lib/features/course/components/exercise/preview.svelte
T

115 lines
3.7 KiB
Svelte
Raw Normal View History

2026-03-09 11:18:22 +01:00
<script lang="ts">
import { getExerciseQuestionContractKey } from '@cio/question-types';
import type { AnswerData } from '@cio/question-types';
import { Grade, ReasonBox } from '$features/ui/question';
import { toExerciseQuestionModel } from './question-type-utils';
import { getExerciseQuestionLabels } from './question-labels';
import { ExerciseQuestion } from '@cio/ui';
import type { QuestionnaireMetaData } from './store';
interface Props {
questions?: any;
questionnaireMetaData?: Partial<QuestionnaireMetaData>;
grades?: Record<string, number>;
onSubmit?: any;
onPrevious?: any;
disableGrading?: boolean;
isGradeWithAI?: boolean;
isLoading?: boolean;
reasons?: any;
}
let {
questions = [],
questionnaireMetaData: _questionnaireMetaData = {},
grades = $bindable({}),
disableGrading = true,
isGradeWithAI = $bindable(false),
isLoading = $bindable(false),
reasons = $bindable({})
}: Props = $props();
function acceptGradeSuggestion() {
isGradeWithAI = false;
}
function rejectGradeSuggestion(questionId) {
isGradeWithAI = false;
grades[questionId] = 0;
}
function getAnswerForQuestion(questionModel, fallbackIndex = 0) {
const questionKey = getExerciseQuestionContractKey(questionModel, fallbackIndex);
return _questionnaireMetaData?.answers?.[questionKey];
}
const questionModels = $derived(questions.map((question) => toExerciseQuestionModel(question)));
const questionLabels = $derived(getExerciseQuestionLabels());
const answersByKey = $derived(
questionModels.reduce(
(acc, questionModel, index) => {
const questionKey = getExerciseQuestionContractKey(questionModel, index);
acc[questionKey] = _questionnaireMetaData?.answers?.[questionKey];
return acc;
},
{} as Record<string, AnswerData>
)
);
</script>
{#if disableGrading && !Object.keys(grades || {}).length}
<ExerciseQuestion.QuestionList
contract={{ mode: 'view', questions: questionModels, answersByKey, labels: questionLabels, disabled: true }}
itemClass="mb-4"
/>
{:else if disableGrading && Object.keys(grades || {}).length}
{#each questions as currentQuestion, index}
{@const questionModel = questionModels[index]}
<div class="mb-4 space-y-3">
<div class="flex items-center justify-between">
<p class="text-base font-semibold text-gray-700 dark:text-gray-200">Q{index + 1}</p>
<Grade grade={grades[currentQuestion.id]} gradeMax={currentQuestion.points} disableGrading={true} />
</div>
<ExerciseQuestion.QuestionRenderer
contract={{
mode: 'view',
question: questionModel,
answer: getAnswerForQuestion(questionModel, index),
labels: questionLabels,
disabled: true
}}
/>
</div>
{/each}
{:else}
{#each questions as currentQuestion, index}
{@const questionModel = questionModels[index]}
<div class="mb-4 space-y-3">
<div class="flex items-center justify-between">
<p class="text-base font-semibold text-gray-700 dark:text-gray-200">Q{index + 1}</p>
<Grade bind:grade={grades[currentQuestion.id]} gradeMax={currentQuestion.points} {disableGrading} />
</div>
<ExerciseQuestion.QuestionRenderer
contract={{
mode: 'view',
question: questionModel,
answer: getAnswerForQuestion(questionModel, index),
labels: questionLabels,
disabled: true
}}
/>
{#if isGradeWithAI}
<ReasonBox
reason={reasons[currentQuestion.id]}
{isLoading}
acceptGrade={acceptGradeSuggestion}
rejectGrade={() => rejectGradeSuggestion(currentQuestion.id)}
/>
{/if}
</div>
{/each}
{/if}