style: linters for vue files

This commit is contained in:
Jannat Patel
2024-04-01 22:01:55 +05:30
parent 0cf9ad5228
commit 5f000d8017
6 changed files with 508 additions and 352 deletions

View File

@@ -1,12 +1,23 @@
<template>
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1584_1676)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.17474 0.625C2.34632 0.625 1.67474 1.29657 1.67474 2.125V7.475C1.67474 8.30343 2.34632 8.975 3.17474 8.975H14.8247C15.6532 8.975 16.3247 8.30343 16.3247 7.475V2.125C16.3247 1.29657 15.6532 0.625 14.8247 0.625H3.17474ZM2.67474 2.125C2.67474 1.84886 2.8986 1.625 3.17474 1.625H14.8247C15.1009 1.625 15.3247 1.84886 15.3247 2.125V7.475C15.3247 7.75114 15.1009 7.975 14.8247 7.975H3.17474C2.8986 7.975 2.67474 7.75114 2.67474 7.475V2.125ZM4.27478 10.0749C3.99864 10.0749 3.77478 10.2987 3.77478 10.5749V12.6749C3.77478 12.951 3.99864 13.1749 4.27478 13.1749C4.55092 13.1749 4.77478 12.951 4.77478 12.6749V11.0749H6.92478V12.6749C6.92478 12.951 7.14864 13.1749 7.42478 13.1749C7.70092 13.1749 7.92478 12.951 7.92478 12.6749V10.5749C7.92478 10.2987 7.70092 10.0749 7.42478 10.0749H4.27478ZM10.0749 10.5749C10.0749 10.2987 10.2987 10.0749 10.5749 10.0749H13.7249C14.001 10.0749 14.2249 10.2987 14.2249 10.5749V12.6749C14.2249 12.951 14.001 13.1749 13.7249 13.1749C13.4487 13.1749 13.2249 12.951 13.2249 12.6749V11.0749H11.0749V12.6749C11.0749 12.951 10.851 13.1749 10.5749 13.1749C10.2987 13.1749 10.0749 12.951 10.0749 12.6749V10.5749ZM1.125 14.275C0.848858 14.275 0.625 14.4988 0.625 14.775V16.875C0.625 17.1511 0.848858 17.375 1.125 17.375C1.40114 17.375 1.625 17.1511 1.625 16.875V15.275H3.775V16.875C3.775 17.1511 3.99886 17.375 4.275 17.375C4.55114 17.375 4.775 17.1511 4.775 16.875V14.775C4.775 14.4988 4.55114 14.275 4.275 14.275H1.125ZM13.2252 14.775C13.2252 14.4988 13.4491 14.275 13.7252 14.275H16.8752C17.1514 14.275 17.3752 14.4988 17.3752 14.775V16.875C17.3752 17.1511 17.1514 17.375 16.8752 17.375C16.5991 17.375 16.3752 17.1511 16.3752 16.875V15.275H14.2252V16.875C14.2252 17.1511 14.0014 17.375 13.7252 17.375C13.4491 17.375 13.2252 17.1511 13.2252 16.875V14.775ZM7.42511 14.275C7.14897 14.275 6.92511 14.4988 6.92511 14.775V16.875C6.92511 17.1511 7.14897 17.375 7.42511 17.375C7.70125 17.375 7.92511 17.1511 7.92511 16.875V15.275H10.0751V16.875C10.0751 17.1511 10.299 17.375 10.5751 17.375C10.8513 17.375 11.0751 17.1511 11.0751 16.875V14.775C11.0751 14.4988 10.8513 14.275 10.5751 14.275H7.42511Z" fill="#525252"/>
</g>
<defs>
<clipPath id="clip0_1584_1676">
<rect width="18" height="18" fill="white"/>
</clipPath>
</defs>
</svg>
</template>
<svg
width="18"
height="18"
viewBox="0 0 18 18"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clip-path="url(#clip0_1584_1676)">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M3.17474 0.625C2.34632 0.625 1.67474 1.29657 1.67474 2.125V7.475C1.67474 8.30343 2.34632 8.975 3.17474 8.975H14.8247C15.6532 8.975 16.3247 8.30343 16.3247 7.475V2.125C16.3247 1.29657 15.6532 0.625 14.8247 0.625H3.17474ZM2.67474 2.125C2.67474 1.84886 2.8986 1.625 3.17474 1.625H14.8247C15.1009 1.625 15.3247 1.84886 15.3247 2.125V7.475C15.3247 7.75114 15.1009 7.975 14.8247 7.975H3.17474C2.8986 7.975 2.67474 7.75114 2.67474 7.475V2.125ZM4.27478 10.0749C3.99864 10.0749 3.77478 10.2987 3.77478 10.5749V12.6749C3.77478 12.951 3.99864 13.1749 4.27478 13.1749C4.55092 13.1749 4.77478 12.951 4.77478 12.6749V11.0749H6.92478V12.6749C6.92478 12.951 7.14864 13.1749 7.42478 13.1749C7.70092 13.1749 7.92478 12.951 7.92478 12.6749V10.5749C7.92478 10.2987 7.70092 10.0749 7.42478 10.0749H4.27478ZM10.0749 10.5749C10.0749 10.2987 10.2987 10.0749 10.5749 10.0749H13.7249C14.001 10.0749 14.2249 10.2987 14.2249 10.5749V12.6749C14.2249 12.951 14.001 13.1749 13.7249 13.1749C13.4487 13.1749 13.2249 12.951 13.2249 12.6749V11.0749H11.0749V12.6749C11.0749 12.951 10.851 13.1749 10.5749 13.1749C10.2987 13.1749 10.0749 12.951 10.0749 12.6749V10.5749ZM1.125 14.275C0.848858 14.275 0.625 14.4988 0.625 14.775V16.875C0.625 17.1511 0.848858 17.375 1.125 17.375C1.40114 17.375 1.625 17.1511 1.625 16.875V15.275H3.775V16.875C3.775 17.1511 3.99886 17.375 4.275 17.375C4.55114 17.375 4.775 17.1511 4.775 16.875V14.775C4.775 14.4988 4.55114 14.275 4.275 14.275H1.125ZM13.2252 14.775C13.2252 14.4988 13.4491 14.275 13.7252 14.275H16.8752C17.1514 14.275 17.3752 14.4988 17.3752 14.775V16.875C17.3752 17.1511 17.1514 17.375 16.8752 17.375C16.5991 17.375 16.3752 17.1511 16.3752 16.875V15.275H14.2252V16.875C14.2252 17.1511 14.0014 17.375 13.7252 17.375C13.4491 17.375 13.2252 17.1511 13.2252 16.875V14.775ZM7.42511 14.275C7.14897 14.275 6.92511 14.4988 6.92511 14.775V16.875C6.92511 17.1511 7.14897 17.375 7.42511 17.375C7.70125 17.375 7.92511 17.1511 7.92511 16.875V15.275H10.0751V16.875C10.0751 17.1511 10.299 17.375 10.5751 17.375C10.8513 17.375 11.0751 17.1511 11.0751 16.875V14.775C11.0751 14.4988 10.8513 14.275 10.5751 14.275H7.42511Z"
fill="#525252"
/>
</g>
<defs>
<clipPath id="clip0_1584_1676">
<rect width="18" height="18" fill="white" />
</clipPath>
</defs>
</svg>
</template>

View File

@@ -1,8 +1,27 @@
<template>
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.875 9.06223L3 9.06232" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" />
<path d="M6.74537 5.31699L3 9.06236L6.74527 12.8076" stroke="currentColor" stroke-linecap="round"
stroke-linejoin="round" />
<path d="M14.1423 4L14.1423 14.125" stroke="currentColor" stroke-linecap="round" />
</svg>
<svg
width="18"
height="18"
viewBox="0 0 18 18"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M10.875 9.06223L3 9.06232"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M6.74537 5.31699L3 9.06236L6.74527 12.8076"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M14.1423 4L14.1423 14.125"
stroke="currentColor"
stroke-linecap="round"
/>
</svg>
</template>

View File

@@ -1,13 +1,36 @@
<template>
<svg width="118" height="118" viewBox="0 0 118 118" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M93.9278 0H23.1013C10.3428 0 0 10.3428 0 23.1013V93.9278C0 106.686 10.3428 117.029 23.1013 117.029H93.9278C106.686 117.029 117.029 106.686 117.029 93.9278V23.1013C117.029 10.3428 106.686 0 93.9278 0Z" fill="url(#paint0_radial_174_336)"/>
<path d="M93.9278 0H23.1013C10.3428 0 0 10.3428 0 23.1013V93.9278C0 106.686 10.3428 117.029 23.1013 117.029H93.9278C106.686 117.029 117.029 106.686 117.029 93.9278V23.1013C117.029 10.3428 106.686 0 93.9278 0Z" fill="#0B3D3D" fill-opacity="0.8"/>
<path d="M95.1879 33.1294L91.4077 32.0268C80.1721 28.7716 67.9389 30.9242 58.5409 37.7496C52.083 33.0769 43.9975 30.5042 36.1746 30.5042H21.8938V41.0048H36.2796C42.2649 41.0048 48.1978 42.9999 52.923 46.6226L58.5934 50.9279L64.2637 46.6226C70.144 42.1599 77.5469 40.2698 84.7923 41.2673V76.1818C75.5518 75.2367 66.2063 77.7044 58.6459 83.2172C51.0854 77.7044 41.6349 75.2367 32.4994 76.1818V52.8705H21.9988V86.4724H95.3454V33.1294H95.1879Z" fill="#58FF9B"/>
<defs>
<radialGradient id="paint0_radial_174_336" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(117.24 -101.5) rotate(105.042) scale(226.282)">
<stop offset="0.445162" stop-color="#1F7676"/>
<stop offset="1" stop-color="#0A4B4B"/>
</radialGradient>
</defs>
</svg>
</template>
<svg
width="118"
height="118"
viewBox="0 0 118 118"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M93.9278 0H23.1013C10.3428 0 0 10.3428 0 23.1013V93.9278C0 106.686 10.3428 117.029 23.1013 117.029H93.9278C106.686 117.029 117.029 106.686 117.029 93.9278V23.1013C117.029 10.3428 106.686 0 93.9278 0Z"
fill="url(#paint0_radial_174_336)"
/>
<path
d="M93.9278 0H23.1013C10.3428 0 0 10.3428 0 23.1013V93.9278C0 106.686 10.3428 117.029 23.1013 117.029H93.9278C106.686 117.029 117.029 106.686 117.029 93.9278V23.1013C117.029 10.3428 106.686 0 93.9278 0Z"
fill="#0B3D3D"
fill-opacity="0.8"
/>
<path
d="M95.1879 33.1294L91.4077 32.0268C80.1721 28.7716 67.9389 30.9242 58.5409 37.7496C52.083 33.0769 43.9975 30.5042 36.1746 30.5042H21.8938V41.0048H36.2796C42.2649 41.0048 48.1978 42.9999 52.923 46.6226L58.5934 50.9279L64.2637 46.6226C70.144 42.1599 77.5469 40.2698 84.7923 41.2673V76.1818C75.5518 75.2367 66.2063 77.7044 58.6459 83.2172C51.0854 77.7044 41.6349 75.2367 32.4994 76.1818V52.8705H21.9988V86.4724H95.3454V33.1294H95.1879Z"
fill="#58FF9B"
/>
<defs>
<radialGradient
id="paint0_radial_174_336"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(117.24 -101.5) rotate(105.042) scale(226.282)"
>
<stop offset="0.445162" stop-color="#1F7676" />
<stop offset="1" stop-color="#0A4B4B" />
</radialGradient>
</defs>
</svg>
</template>

View File

@@ -1,340 +1,436 @@
<template>
<div v-if="quiz.doc">
<div class="bg-blue-100 py-2 px-2 mb-4 rounded-md text-sm text-blue-800">
<div class="leading-relaxed">
{{ __("This quiz consists of {0} questions.").format(quiz.doc.questions.length) }}
</div>
<div v-if="quiz.doc.passing_percentage" class="leading-relaxed">
{{ __("You will have to get {0}% correct answers in order to pass the quiz.").format(quiz.doc.passing_percentage) }}
</div>
<div v-if="quiz.doc.max_attempts" class="leading-relaxed">
{{ __("You can attempt this quiz {0}.").format(quiz.doc.max_attempts == 1 ? "1 time" : `${quiz.doc.max_attempts} times`) }}
</div>
<div v-if="quiz.doc.time" class="leading-relaxed">
{{ __("The quiz has a time limit.For each question you will be given { 0} seconds.").format(quiz.doc.time) }}
</div>
</div>
<div v-if="activeQuestion == 0">
<div class="border text-center p-20 rounded-md">
<div class="font-semibold text-lg">
{{ quiz.doc.title }}
</div>
<Button v-if="!quiz.doc.max_attempts || attempts.data?.length < quiz.doc.max_attempts" @click="startQuiz" class="mt-2">
<span>
{{ __("Start") }}
</span>
</Button>
<div v-else>
{{ __("You have already exceeded the maximum number of attempts allowed for this quiz.") }}
</div>
</div>
</div>
<div v-else-if="!quizSubmission.data">
<div v-for="(question, qtidx) in quiz.doc.questions">
<div v-if="qtidx == activeQuestion - 1 && questionDetails.data" class="border rounded-md p-5">
<div class="flex justify-between">
<div class="text-sm">
<span class="mr-2">
{{ __("Question {0}").format(activeQuestion) }}:
</span>
<span>
{{ questionDetails.data.multiple ? __("Choose all answers that apply") : __("Choose one answer") }}
</span>
</div>
<div class="text-gray-900 text-sm font-semibold item-left">
{{ question.marks }} {{ question.marks == 1 ? __("Mark") : __("Marks") }}
</div>
</div>
<div class="text-gray-900 font-semibold mt-2">
{{ questionDetails.data.question }}
</div>
<div v-if="questionDetails.data.type == 'Choices'" v-for="index in 4">
<label v-if="questionDetails.data[`option_${index}`]"
class="flex items-center bg-gray-200 rounded-md p-3 mt-4 w-full cursor-pointer focus:border-blue-600">
<div v-if="quiz.doc">
<div class="bg-blue-100 py-2 px-2 mb-4 rounded-md text-sm text-blue-800">
<div class="leading-relaxed">
{{
__('This quiz consists of {0} questions.').format(
quiz.doc.questions.length
)
}}
</div>
<div v-if="quiz.doc.passing_percentage" class="leading-relaxed">
{{
__(
'You will have to get {0}% correct answers in order to pass the quiz.'
).format(quiz.doc.passing_percentage)
}}
</div>
<div v-if="quiz.doc.max_attempts" class="leading-relaxed">
{{
__('You can attempt this quiz {0}.').format(
quiz.doc.max_attempts == 1
? '1 time'
: `${quiz.doc.max_attempts} times`
)
}}
</div>
<div v-if="quiz.doc.time" class="leading-relaxed">
{{
__(
'The quiz has a time limit.For each question you will be given { 0} seconds.'
).format(quiz.doc.time)
}}
</div>
</div>
<div v-if="activeQuestion == 0">
<div class="border text-center p-20 rounded-md">
<div class="font-semibold text-lg">
{{ quiz.doc.title }}
</div>
<Button
v-if="
!quiz.doc.max_attempts ||
attempts.data?.length < quiz.doc.max_attempts
"
@click="startQuiz"
class="mt-2"
>
<span>
{{ __('Start') }}
</span>
</Button>
<div v-else>
{{
__(
'You have already exceeded the maximum number of attempts allowed for this quiz.'
)
}}
</div>
</div>
</div>
<div v-else-if="!quizSubmission.data">
<div v-for="(question, qtidx) in quiz.doc.questions">
<div
v-if="qtidx == activeQuestion - 1 && questionDetails.data"
class="border rounded-md p-5"
>
<div class="flex justify-between">
<div class="text-sm">
<span class="mr-2">
{{ __('Question {0}').format(activeQuestion) }}:
</span>
<span>
{{
questionDetails.data.multiple
? __('Choose all answers that apply')
: __('Choose one answer')
}}
</span>
</div>
<div class="text-gray-900 text-sm font-semibold item-left">
{{ question.marks }}
{{ question.marks == 1 ? __('Mark') : __('Marks') }}
</div>
</div>
<div class="text-gray-900 font-semibold mt-2">
{{ questionDetails.data.question }}
</div>
<div v-if="questionDetails.data.type == 'Choices'" v-for="index in 4">
<label
v-if="questionDetails.data[`option_${index}`]"
class="flex items-center bg-gray-200 rounded-md p-3 mt-4 w-full cursor-pointer focus:border-blue-600"
>
<input
v-if="!showAnswers.length && !questionDetails.data.multiple"
type="radio"
:name="encodeURIComponent(questionDetails.data.question)"
class="w-3.5 h-3.5 text-gray-900 focus:ring-gray-200"
@change="markAnswer(index)"
/>
<input v-if="!showAnswers.length && !questionDetails.data.multiple" type="radio"
:name="encodeURIComponent(questionDetails.data.question)"
class="w-3.5 h-3.5 text-gray-900 focus:ring-gray-200"
@change="markAnswer(index)">
<input
v-else-if="!showAnswers.length && questionDetails.data.multiple"
type="checkbox"
:name="encodeURIComponent(questionDetails.data.question)"
class="w-3.5 h-3.5 text-gray-900 rounded-sm focus:ring-gray-200"
@change="markAnswer(index)"
/>
<input v-else-if="!showAnswers.length && questionDetails.data.multiple" type="checkbox"
:name="encodeURIComponent(questionDetails.data.question)"
class="w-3.5 h-3.5 text-gray-900 rounded-sm focus:ring-gray-200"
@change="markAnswer(index)">
<div v-else-if="quiz.doc.show_answers" v-for="(answer, idx) in showAnswers">
<div v-if="index - 1 == idx">
<CheckCircle v-if="answer" class="w-4 h-4 text-green-500"/>
<MinusCircle v-else-if="questionDetails.data[`is_correct_${index}`]"
class="w-4 h-4 text-green-500"/>
<XCircle v-else-if="answer == 0" class="w-4 h-4 text-red-500"/>
<MinusCircle v-else class="w-4 h-4"/>
</div>
</div>
<span class="ml-2">
{{ questionDetails.data[`option_${index}`] }}
</span>
</label>
<div v-if="questionDetails.data[`explanation_${index}`]" class="mt-2 text-sm hidden">
{{ questionDetails.data[`explanation_${index}`] }}
</div>
</div>
<div class="flex items-center justify-between mt-8">
<div>
{{ __("Question {0} of {1}").format(activeQuestion, quiz.doc.questions.length) }}
</div>
<Button v-if="quiz.doc.show_answers && !showAnswers.length" @click="checkAnswer()">
<span>
{{ __("Check") }}
</span>
</Button>
<Button v-else-if="activeQuestion != quiz.doc.questions.length" @click="nextQuetion()">
<span>
{{ __("Next") }}
</span>
</Button>
<Button v-else @click="submitQuiz()">
<span>
{{ __("Submit") }}
</span>
</Button>
</div>
</div>
</div>
</div>
<div v-else class="border rounded-md p-20 text-center">
<div class="text-lg font-semibold">
{{ __("Quiz Summary") }}
</div>
<div>
{{ __("You got {0}% correct answers with a score of {1} out of {2}").format(Math.ceil(quizSubmission.data.percentage), quizSubmission.data.score, quizSubmission.data.score_out_of) }}
</div>
<Button @click="resetQuiz()" class="mt-2"
v-if="!quiz.doc.max_attempts || attempts?.data.length < quiz.doc.max_attempts">
<span>
{{ __("Try Again") }}
</span>
</Button>
</div>
<div v-if="quiz.doc.show_submission_history && attempts?.data" class="mt-10">
<ListView :columns="getSubmissionColumns()" :rows="attempts?.data" row-key="name"
:options="{selectable: false, showTooltip: false}">
</ListView>
</div>
</div>
<div
v-else-if="quiz.doc.show_answers"
v-for="(answer, idx) in showAnswers"
>
<div v-if="index - 1 == idx">
<CheckCircle v-if="answer" class="w-4 h-4 text-green-500" />
<MinusCircle
v-else-if="questionDetails.data[`is_correct_${index}`]"
class="w-4 h-4 text-green-500"
/>
<XCircle
v-else-if="answer == 0"
class="w-4 h-4 text-red-500"
/>
<MinusCircle v-else class="w-4 h-4" />
</div>
</div>
<span class="ml-2">
{{ questionDetails.data[`option_${index}`] }}
</span>
</label>
<div
v-if="questionDetails.data[`explanation_${index}`]"
class="mt-2 text-sm hidden"
>
{{ questionDetails.data[`explanation_${index}`] }}
</div>
</div>
<div class="flex items-center justify-between mt-8">
<div>
{{
__('Question {0} of {1}').format(
activeQuestion,
quiz.doc.questions.length
)
}}
</div>
<Button
v-if="quiz.doc.show_answers && !showAnswers.length"
@click="checkAnswer()"
>
<span>
{{ __('Check') }}
</span>
</Button>
<Button
v-else-if="activeQuestion != quiz.doc.questions.length"
@click="nextQuetion()"
>
<span>
{{ __('Next') }}
</span>
</Button>
<Button v-else @click="submitQuiz()">
<span>
{{ __('Submit') }}
</span>
</Button>
</div>
</div>
</div>
</div>
<div v-else class="border rounded-md p-20 text-center">
<div class="text-lg font-semibold">
{{ __('Quiz Summary') }}
</div>
<div>
{{
__(
'You got {0}% correct answers with a score of {1} out of {2}'
).format(
Math.ceil(quizSubmission.data.percentage),
quizSubmission.data.score,
quizSubmission.data.score_out_of
)
}}
</div>
<Button
@click="resetQuiz()"
class="mt-2"
v-if="
!quiz.doc.max_attempts ||
attempts?.data.length < quiz.doc.max_attempts
"
>
<span>
{{ __('Try Again') }}
</span>
</Button>
</div>
<div
v-if="quiz.doc.show_submission_history && attempts?.data"
class="mt-10"
>
<ListView
:columns="getSubmissionColumns()"
:rows="attempts?.data"
row-key="name"
:options="{ selectable: false, showTooltip: false }"
>
</ListView>
</div>
</div>
</template>
<script setup>
import { createDocumentResource, Button, createResource, ListView } from 'frappe-ui';
import { ref, watch, reactive, inject } from 'vue';
import { createToast } from "@/utils/"
import { CheckCircle, XCircle, MinusCircle } from "lucide-vue-next"
import { timeAgo } from "@/utils"
const user = inject("$user");
import {
createDocumentResource,
Button,
createResource,
ListView,
} from 'frappe-ui'
import { ref, watch, reactive, inject } from 'vue'
import { createToast } from '@/utils/'
import { CheckCircle, XCircle, MinusCircle } from 'lucide-vue-next'
import { timeAgo } from '@/utils'
const user = inject('$user')
const activeQuestion = ref(0);
const currentQuestion = ref("");
const selectedOptions = reactive([0,0,0,0]);
const activeQuestion = ref(0)
const currentQuestion = ref('')
const selectedOptions = reactive([0, 0, 0, 0])
const showAnswers = reactive([])
const props = defineProps({
quizName: {
type: String,
required: true,
},
});
quizName: {
type: String,
required: true,
},
})
const quiz = createDocumentResource({
doctype: "LMS Quiz",
name: props.quizName,
cache: ["quiz", props.quizName],
auto: true,
});
doctype: 'LMS Quiz',
name: props.quizName,
cache: ['quiz', props.quizName],
auto: true,
})
const attempts = createResource({
url: "frappe.client.get_list",
makeParams(values) {
return {
doctype: "LMS Quiz Submission",
filters: {
member: user.data?.name,
quiz: quiz.doc?.name,
},
fields: ["name", "creation", "score", "score_out_of", "percentage", "passing_percentage"],
order_by: "creation desc",
}
},
auto: true,
transform(data) {
data.forEach((submission, index) => {
submission.creation = timeAgo(submission.creation);
submission.idx = index + 1;
});
}
url: 'frappe.client.get_list',
makeParams(values) {
return {
doctype: 'LMS Quiz Submission',
filters: {
member: user.data?.name,
quiz: quiz.doc?.name,
},
fields: [
'name',
'creation',
'score',
'score_out_of',
'percentage',
'passing_percentage',
],
order_by: 'creation desc',
}
},
auto: true,
transform(data) {
data.forEach((submission, index) => {
submission.creation = timeAgo(submission.creation)
submission.idx = index + 1
})
},
})
const quizSubmission = createResource({
url: "lms.lms.doctype.lms_quiz.lms_quiz.quiz_summary",
makeParams(values) {
return {
quiz: quiz.doc.name,
results: localStorage.getItem(quiz.doc.title),
}
}
url: 'lms.lms.doctype.lms_quiz.lms_quiz.quiz_summary',
makeParams(values) {
return {
quiz: quiz.doc.name,
results: localStorage.getItem(quiz.doc.title),
}
},
})
const questionDetails = createResource({
url: "lms.lms.utils.get_question_details",
makeParams(values) {
return {
question: currentQuestion.value,
}
}
url: 'lms.lms.utils.get_question_details',
makeParams(values) {
return {
question: currentQuestion.value,
}
},
})
watch(activeQuestion, (value) => {
if (value > 0) {
currentQuestion.value = quiz.doc.questions[value - 1].question;
questionDetails.reload();
}
if (value > 0) {
currentQuestion.value = quiz.doc.questions[value - 1].question
questionDetails.reload()
}
})
const startQuiz = () => {
activeQuestion.value = 1;
localStorage.removeItem(quiz.doc.title);
activeQuestion.value = 1
localStorage.removeItem(quiz.doc.title)
}
const markAnswer = (index) => {
if (!questionDetails.data.multiple)
selectedOptions.splice(0, selectedOptions.length, ...[0,0,0,0]);
selectedOptions[index - 1] = selectedOptions[index - 1] ? 0 : 1;
if (!questionDetails.data.multiple)
selectedOptions.splice(0, selectedOptions.length, ...[0, 0, 0, 0])
selectedOptions[index - 1] = selectedOptions[index - 1] ? 0 : 1
}
const getAnswers = () => {
let answers = [];
selectedOptions.forEach((value, index) => {
if (selectedOptions[index])
answers.push(questionDetails.data[`option_${index + 1}`])
});
return answers;
};
let answers = []
selectedOptions.forEach((value, index) => {
if (selectedOptions[index])
answers.push(questionDetails.data[`option_${index + 1}`])
})
return answers
}
const checkAnswer = () => {
let answers = getAnswers();
if (!answers.length) {
createToast({
title: "Please select an option",
icon: 'alert-circle',
iconClasses: 'text-yellow-600 bg-yellow-100',
});
return;
}
let answers = getAnswers()
if (!answers.length) {
createToast({
title: 'Please select an option',
icon: 'alert-circle',
iconClasses: 'text-yellow-600 bg-yellow-100',
})
return
}
createResource({
url: "lms.lms.doctype.lms_quiz.lms_quiz.check_answer",
params: {
"question": currentQuestion.value,
"type": questionDetails.data.type,
"answers": JSON.stringify(answers)
},
auto: true,
onSuccess(data) {
selectedOptions.forEach((option, index) => {
if (option) {
showAnswers[index] = option && data[index]
} else if (questionDetails.data[`is_correct_${index + 1}`]) {
showAnswers[index] = 0
} else {
showAnswers[index] = undefined
}
});
addToLocalStorage();
if (!quiz.doc.show_answers) {
resetQuestion();
}
}
})
};
createResource({
url: 'lms.lms.doctype.lms_quiz.lms_quiz.check_answer',
params: {
question: currentQuestion.value,
type: questionDetails.data.type,
answers: JSON.stringify(answers),
},
auto: true,
onSuccess(data) {
selectedOptions.forEach((option, index) => {
if (option) {
showAnswers[index] = option && data[index]
} else if (questionDetails.data[`is_correct_${index + 1}`]) {
showAnswers[index] = 0
} else {
showAnswers[index] = undefined
}
})
addToLocalStorage()
if (!quiz.doc.show_answers) {
resetQuestion()
}
},
})
}
const addToLocalStorage = () => {
let quizData = JSON.parse(localStorage.getItem(quiz.doc.title));
let questionData = {
"question_index": activeQuestion.value,
"answers": getAnswers().join(),
"is_correct": showAnswers.filter((answer) => {
return answer != undefined
})
}
quizData ? quizData.push(questionData) : (quizData = [questionData]);
localStorage.setItem(quiz.doc.title, JSON.stringify(quizData));
let quizData = JSON.parse(localStorage.getItem(quiz.doc.title))
let questionData = {
question_index: activeQuestion.value,
answers: getAnswers().join(),
is_correct: showAnswers.filter((answer) => {
return answer != undefined
}),
}
quizData ? quizData.push(questionData) : (quizData = [questionData])
localStorage.setItem(quiz.doc.title, JSON.stringify(quizData))
}
const nextQuetion = () => {
if (!quiz.doc.show_answers) {
checkAnswer();
} else {
resetQuestion();
}
if (!quiz.doc.show_answers) {
checkAnswer()
} else {
resetQuestion()
}
}
const resetQuestion = () => {
if (activeQuestion.value == quiz.doc.questions.length)
return;
activeQuestion.value = activeQuestion.value + 1
selectedOptions.splice(0, selectedOptions.length, ...[0, 0, 0, 0]);
showAnswers.length = 0;
if (activeQuestion.value == quiz.doc.questions.length) return
activeQuestion.value = activeQuestion.value + 1
selectedOptions.splice(0, selectedOptions.length, ...[0, 0, 0, 0])
showAnswers.length = 0
}
const submitQuiz = () => {
if (!quiz.doc.show_answers) {
checkAnswer();
setTimeout(() => {
createSubmission();
}, 500);
return;
}
createSubmission();
if (!quiz.doc.show_answers) {
checkAnswer()
setTimeout(() => {
createSubmission()
}, 500)
return
}
createSubmission()
}
const createSubmission = () => {
quizSubmission.reload().then(() => {
attempts.reload();
});
quizSubmission.reload().then(() => {
attempts.reload()
})
}
const resetQuiz = () => {
activeQuestion.value = 0;
selectedOptions.splice(0, selectedOptions.length, ...[0,0,0,0]);
showAnswers.length = 0;
quizSubmission.reset();
activeQuestion.value = 0
selectedOptions.splice(0, selectedOptions.length, ...[0, 0, 0, 0])
showAnswers.length = 0
quizSubmission.reset()
}
const getSubmissionColumns = () => {
return [
{
label: "No.",
key: "idx",
},
{
label: "Date",
key: "creation",
},
{
label: "Score",
key: "score",
align: "center"
},
{
label: "Score out of",
key: "score_out_of",
align: "center"
},
{
label: "Percentage",
key: "percentage",
align: "center"
},
]
return [
{
label: 'No.',
key: 'idx',
},
{
label: 'Date',
key: 'creation',
},
{
label: 'Score',
key: 'score',
align: 'center',
},
{
label: 'Score out of',
key: 'score_out_of',
align: 'center',
},
{
label: 'Percentage',
key: 'percentage',
align: 'center',
},
]
}
</script>
</script>

View File

@@ -1,15 +1,22 @@
<template>
<Avatar class="avatar border border-gray-300" v-if="user" :label="user.full_name" :image="user.user_image" :size="size" v-bind="$attrs" />
<Avatar
class="avatar border border-gray-300"
v-if="user"
:label="user.full_name"
:image="user.user_image"
:size="size"
v-bind="$attrs"
/>
</template>
<script setup>
import { Avatar } from 'frappe-ui'
const props = defineProps({
user: {
type: Object,
default: null,
},
size: {
type: String,
},
});
user: {
type: Object,
default: null,
},
size: {
type: String,
},
})
</script>

View File

@@ -1,39 +1,39 @@
<template>
<div class="max-w-3xl py-12 mx-auto">
<Button
icon-left="code"
@click="$resources.ping.fetch"
:loading="$resources.ping.loading"
>
Click to send 'ping' request
</Button>
<div>
{{ $resources.ping.data }}
</div>
<pre>{{ $resources.ping }}</pre>
<div class="max-w-3xl py-12 mx-auto">
<Button
icon-left="code"
@click="$resources.ping.fetch"
:loading="$resources.ping.loading"
>
Click to send 'ping' request
</Button>
<div>
{{ $resources.ping.data }}
</div>
<pre>{{ $resources.ping }}</pre>
<Button @click="showDialog = true">Open Dialog</Button>
<Dialog title="Title" v-model="showDialog"> Dialog content </Dialog>
</div>
<Button @click="showDialog = true">Open Dialog</Button>
<Dialog title="Title" v-model="showDialog"> Dialog content </Dialog>
</div>
</template>
<script>
import { Dialog } from 'frappe-ui'
export default {
name: 'Home',
data() {
return {
showDialog: false,
}
},
resources: {
ping: {
url: 'ping',
},
},
components: {
Dialog,
},
name: 'Home',
data() {
return {
showDialog: false,
}
},
resources: {
ping: {
url: 'ping',
},
},
components: {
Dialog,
},
}
</script>