Make questionnaire Ui reusable
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
<script>
|
||||
import Modal from '../../../../Modal/index.svelte';
|
||||
export let open;
|
||||
export let onClose;
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
modalHeading="Lesson 1: How to use props in React.js"
|
||||
bind:open
|
||||
{onClose}
|
||||
width="w-3/5"
|
||||
>
|
||||
<p class="p-2 mb-2">
|
||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Repellendus debitis
|
||||
quos porro doloremque amet alias modi ducimus delectus cum dolor sunt
|
||||
laborum provident, deserunt magni facilis non nobis illum ipsum. Lorem ipsum
|
||||
dolor sit amet, consectetur adipisicing elit. Deleniti rem aliquid, saepe
|
||||
fuga reiciendis ullam officiis soluta cum, vitae aut eum expedita cumque
|
||||
perspiciatis pariatur necessitatibus, praesentium ea laudantium ut?
|
||||
</p>
|
||||
<p class="p-2 mb-2">
|
||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Repellendus debitis
|
||||
quos porro doloremque amet alias modi ducimus delectus cum dolor sunt
|
||||
laborum provident, deserunt magni facilis non nobis illum ipsum. Lorem ipsum
|
||||
dolor sit amet, consectetur adipisicing elit. Deleniti rem aliquid, saepe
|
||||
fuga reiciendis ullam officiis soluta cum, vitae aut eum expedita cumque
|
||||
perspiciatis pariatur necessitatibus, praesentium ea laudantium ut?
|
||||
</p>
|
||||
<p class="p-2 mb-2">
|
||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Repellendus debitis
|
||||
quos porro doloremque amet alias modi ducimus delectus cum dolor sunt
|
||||
laborum provident, deserunt magni facilis non nobis illum ipsum. Lorem ipsum
|
||||
dolor sit amet, consectetur adipisicing elit. Deleniti rem aliquid, saepe
|
||||
fuga reiciendis ullam officiis soluta cum, vitae aut eum expedita cumque
|
||||
perspiciatis pariatur necessitatibus, praesentium ea laudantium ut?
|
||||
</p>
|
||||
<p class="p-2 mb-2">
|
||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Repellendus debitis
|
||||
quos porro doloremque amet alias modi ducimus delectus cum dolor sunt
|
||||
laborum provident, deserunt magni facilis non nobis illum ipsum. Lorem ipsum
|
||||
dolor sit amet, consectetur adipisicing elit. Deleniti rem aliquid, saepe
|
||||
fuga reiciendis ullam officiis soluta cum, vitae aut eum expedita cumque
|
||||
perspiciatis pariatur necessitatibus, praesentium ea laudantium ut?
|
||||
</p>
|
||||
<p class="p-2 mb-2">
|
||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Repellendus debitis
|
||||
quos porro doloremque amet alias modi ducimus delectus cum dolor sunt
|
||||
laborum provident, deserunt magni facilis non nobis illum ipsum. Lorem ipsum
|
||||
dolor sit amet, consectetur adipisicing elit. Deleniti rem aliquid, saepe
|
||||
fuga reiciendis ullam officiis soluta cum, vitae aut eum expedita cumque
|
||||
perspiciatis pariatur necessitatibus, praesentium ea laudantium ut?
|
||||
</p>
|
||||
</Modal>
|
||||
@@ -0,0 +1,52 @@
|
||||
<script>
|
||||
import RadioQuestion from '../../../../Question/RadioQuestion/index.svelte';
|
||||
import CheckboxQuestion from '../../../../Question/CheckboxQuestion/index.svelte';
|
||||
import TextareaQuestion from '../../../../Question/TextareaQuestion/index.svelte';
|
||||
import { QUESTION_TYPE } from '../../../../Question/constants';
|
||||
import { getPropsForQuestion } from './functions';
|
||||
|
||||
export let questions;
|
||||
export let questionnaireMetaData;
|
||||
export let onSubmit = () => {};
|
||||
export let onPrevious = () => {};
|
||||
</script>
|
||||
|
||||
{#each questions as currentQuestion, currentQuestionIndex}
|
||||
{#if QUESTION_TYPE.RADIO === currentQuestion.type}
|
||||
<RadioQuestion
|
||||
{...getPropsForQuestion(
|
||||
questions,
|
||||
currentQuestion,
|
||||
questionnaireMetaData,
|
||||
currentQuestionIndex + 1,
|
||||
onSubmit,
|
||||
onPrevious,
|
||||
true
|
||||
)}
|
||||
/>
|
||||
{:else if QUESTION_TYPE.CHECKBOX === currentQuestion.type}
|
||||
<CheckboxQuestion
|
||||
{...getPropsForQuestion(
|
||||
questions,
|
||||
currentQuestion,
|
||||
questionnaireMetaData,
|
||||
currentQuestionIndex + 1,
|
||||
onSubmit,
|
||||
onPrevious,
|
||||
true
|
||||
)}
|
||||
/>
|
||||
{:else if QUESTION_TYPE.TEXTAREA === currentQuestion.type}
|
||||
<TextareaQuestion
|
||||
{...getPropsForQuestion(
|
||||
questions,
|
||||
currentQuestion,
|
||||
questionnaireMetaData,
|
||||
currentQuestionIndex + 1,
|
||||
onSubmit,
|
||||
onPrevious,
|
||||
true
|
||||
)}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
@@ -1,6 +1,7 @@
|
||||
<script>
|
||||
import { questionnaire } from './store/index';
|
||||
import { userQuestionniareAnswers } from './store/answers';
|
||||
import { questionnaireMetaData } from './store/answers';
|
||||
import Preview from './Preview.svelte';
|
||||
import RadioQuestion from '../../../../Question/RadioQuestion/index.svelte';
|
||||
import CheckboxQuestion from '../../../../Question/CheckboxQuestion/index.svelte';
|
||||
import TextareaQuestion from '../../../../Question/TextareaQuestion/index.svelte';
|
||||
@@ -9,122 +10,52 @@
|
||||
import { removeDuplicate } from '../../../../../utils/functions/removeDuplicate';
|
||||
import { QUESTION_TYPE } from '../../../../Question/constants';
|
||||
import { STATUS } from './constants';
|
||||
import { wasCorrectAnswerSelected, getPropsForQuestion } from './functions';
|
||||
|
||||
export let preview;
|
||||
|
||||
let currentQuestion = {};
|
||||
let renderProps = {};
|
||||
let isAnswerCorrect;
|
||||
|
||||
const { questions, title, description } = $questionnaire;
|
||||
|
||||
function handleStart() {
|
||||
$userQuestionniareAnswers.currentQuestionIndex += 1;
|
||||
$questionnaireMetaData.currentQuestionIndex += 1;
|
||||
}
|
||||
|
||||
function onSubmit(id, value) {
|
||||
const prevAnswer = $userQuestionniareAnswers.answers[id] || [];
|
||||
const prevAnswer = $questionnaireMetaData.answers[id] || [];
|
||||
const formattedAnswer =
|
||||
typeof value === 'string'
|
||||
? value
|
||||
: removeDuplicate([...prevAnswer, ...(value || [])]);
|
||||
|
||||
$userQuestionniareAnswers.answers = {
|
||||
...$userQuestionniareAnswers.answers,
|
||||
$questionnaireMetaData.answers = {
|
||||
...$questionnaireMetaData.answers,
|
||||
[id]: formattedAnswer,
|
||||
};
|
||||
|
||||
if (isAnswerCorrect) {
|
||||
$userQuestionniareAnswers.currentQuestionIndex += 1;
|
||||
if (
|
||||
wasCorrectAnswerSelected(currentQuestion, $questionnaireMetaData.answers)
|
||||
) {
|
||||
$questionnaireMetaData.currentQuestionIndex += 1;
|
||||
}
|
||||
}
|
||||
|
||||
function onPrevious() {
|
||||
$userQuestionniareAnswers.currentQuestionIndex -= 1;
|
||||
}
|
||||
|
||||
function wasCorrectAnswerSelected(currentQuestion) {
|
||||
const isOpenQuesiton = currentQuestion.type === QUESTION_TYPE.TEXTAREA;
|
||||
|
||||
if (isOpenQuesiton) {
|
||||
return true;
|
||||
}
|
||||
const answer = $userQuestionniareAnswers.answers[currentQuestion.id];
|
||||
const formattedAnswers = typeof answer === 'string' ? [answer] : answer;
|
||||
|
||||
isAnswerCorrect =
|
||||
formattedAnswers &&
|
||||
formattedAnswers.some((answer) => {
|
||||
if (
|
||||
!currentQuestion.answers ||
|
||||
!currentQuestion.answers.length ||
|
||||
currentQuestion.answers.includes(answer)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
return isAnswerCorrect;
|
||||
}
|
||||
|
||||
function getRenderProps(question, questionIndex, answers) {
|
||||
const isLast = questionIndex === questions.length;
|
||||
const isOpenQuesiton = question.type === QUESTION_TYPE.TEXTAREA;
|
||||
|
||||
return {
|
||||
title: questionIndex + '. ' + question.title,
|
||||
name: `${question.id}`,
|
||||
options: question.options,
|
||||
answers: question.answers,
|
||||
code: question.code,
|
||||
defaultValue: isOpenQuesiton
|
||||
? answers[question.id] || ''
|
||||
: answers[question.id] || [],
|
||||
onSubmit,
|
||||
onPrevious,
|
||||
disablePreviousButton: questionIndex === 1,
|
||||
isLast,
|
||||
isPreview: preview || $userQuestionniareAnswers.isFinished,
|
||||
disabled:
|
||||
($userQuestionniareAnswers.isFinished &&
|
||||
wasCorrectAnswerSelected(question)) ||
|
||||
$userQuestionniareAnswers.isFinished,
|
||||
nextButtonProps: isLast
|
||||
? {
|
||||
label: 'Finish',
|
||||
isActive: true,
|
||||
}
|
||||
: isOpenQuesiton
|
||||
? {
|
||||
label: 'Next',
|
||||
isActive: true,
|
||||
}
|
||||
: wasCorrectAnswerSelected(question)
|
||||
? {
|
||||
label: 'Next',
|
||||
isActive: true,
|
||||
disableOptionSelect: true,
|
||||
}
|
||||
: {
|
||||
label: 'Check',
|
||||
isActive: false,
|
||||
},
|
||||
};
|
||||
$questionnaireMetaData.currentQuestionIndex -= 1;
|
||||
}
|
||||
|
||||
function getTotalScores() {
|
||||
let total = 0;
|
||||
for (const score in $userQuestionniareAnswers.scores) {
|
||||
total += parseInt($userQuestionniareAnswers.scores[score]);
|
||||
for (const score in $questionnaireMetaData.scores) {
|
||||
total += parseInt($questionnaireMetaData.scores[score]);
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
function getProgressValue(currentQuestionIndex) {
|
||||
if ($userQuestionniareAnswers.isFinished) {
|
||||
if ($questionnaireMetaData.isFinished) {
|
||||
return 100;
|
||||
}
|
||||
|
||||
@@ -135,62 +66,35 @@
|
||||
|
||||
$: {
|
||||
currentQuestion =
|
||||
questions[$userQuestionniareAnswers.currentQuestionIndex - 1];
|
||||
if (
|
||||
$userQuestionniareAnswers.currentQuestionIndex > 0 &&
|
||||
!currentQuestion
|
||||
) {
|
||||
$userQuestionniareAnswers.isFinished = true;
|
||||
questions[$questionnaireMetaData.currentQuestionIndex - 1];
|
||||
if ($questionnaireMetaData.currentQuestionIndex > 0 && !currentQuestion) {
|
||||
$questionnaireMetaData.isFinished = true;
|
||||
}
|
||||
|
||||
if (currentQuestion) {
|
||||
renderProps = getRenderProps(
|
||||
renderProps = getPropsForQuestion(
|
||||
questions,
|
||||
currentQuestion,
|
||||
$userQuestionniareAnswers.currentQuestionIndex,
|
||||
$userQuestionniareAnswers.answers
|
||||
$questionnaireMetaData,
|
||||
$questionnaireMetaData.currentQuestionIndex,
|
||||
onSubmit,
|
||||
onPrevious,
|
||||
preview
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$: $userQuestionniareAnswers.progressValue = getProgressValue(
|
||||
$userQuestionniareAnswers.currentQuestionIndex
|
||||
$: $questionnaireMetaData.progressValue = getProgressValue(
|
||||
$questionnaireMetaData.currentQuestionIndex
|
||||
);
|
||||
</script>
|
||||
|
||||
<!-- <svelte:window on:keydown={handleKeydown} /> -->
|
||||
|
||||
{#if !preview && questions.length && !$userQuestionniareAnswers.isFinished}
|
||||
<Progress value={$userQuestionniareAnswers.progressValue} />
|
||||
{#if !preview && questions.length && !$questionnaireMetaData.isFinished}
|
||||
<Progress value={$questionnaireMetaData.progressValue} />
|
||||
{/if}
|
||||
|
||||
{#if preview}
|
||||
{#each questions as currentQuestion, currentQuestionIndex}
|
||||
{#if QUESTION_TYPE.RADIO === currentQuestion.type}
|
||||
<RadioQuestion
|
||||
{...getRenderProps(
|
||||
currentQuestion,
|
||||
currentQuestionIndex + 1,
|
||||
$userQuestionniareAnswers.answers
|
||||
)}
|
||||
/>
|
||||
{:else if QUESTION_TYPE.CHECKBOX === currentQuestion.type}
|
||||
<CheckboxQuestion
|
||||
{...getRenderProps(
|
||||
currentQuestion,
|
||||
currentQuestionIndex + 1,
|
||||
$userQuestionniareAnswers.answers
|
||||
)}
|
||||
/>
|
||||
{:else if QUESTION_TYPE.TEXTAREA === currentQuestion.type}
|
||||
<TextareaQuestion
|
||||
{...getRenderProps(
|
||||
currentQuestion,
|
||||
currentQuestionIndex + 1,
|
||||
$userQuestionniareAnswers.answers
|
||||
)}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
<Preview {questions} questionnaireMetaData={$questionnaireMetaData} />
|
||||
{:else if !questions.length}
|
||||
<div
|
||||
class="w-4/5 h-40 m-auto flex items-center justify-center text-center border-2 border-gray-200 rounded-md"
|
||||
@@ -200,7 +104,7 @@
|
||||
<span class="text-blue-700">Edit</span> button to add.
|
||||
</h3>
|
||||
</div>
|
||||
{:else if $userQuestionniareAnswers.currentQuestionIndex === 0}
|
||||
{:else if $questionnaireMetaData.currentQuestionIndex === 0}
|
||||
<div>
|
||||
<h2 class="my-1">{title}</h2>
|
||||
<p>{description}</p>
|
||||
@@ -212,18 +116,18 @@
|
||||
type="button"
|
||||
/>
|
||||
</div>
|
||||
{:else if $userQuestionniareAnswers.isFinished}
|
||||
{:else if $questionnaireMetaData.isFinished}
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex flex-col justify-between">
|
||||
<h2 class="text-xl mb-2 mt-0">{title}</h2>
|
||||
{#if STATUS.IN_PROGRESS === $userQuestionniareAnswers.status}
|
||||
{#if STATUS.IN_PROGRESS === $questionnaireMetaData.status}
|
||||
<span
|
||||
class="bg-yellow-600 text-white rounded-full py-2 px-6 text-center"
|
||||
title="Status: Pending Review"
|
||||
>
|
||||
Pending
|
||||
</span>
|
||||
{:else if STATUS.REVIEWED === $userQuestionniareAnswers.status && getTotalScores() > 0}
|
||||
{:else if STATUS.REVIEWED === $questionnaireMetaData.status && getTotalScores() > 0}
|
||||
<span
|
||||
class="bg-green-700 text-white rounded-full py-2 px-6 text-center"
|
||||
title="Status: Pending Review"
|
||||
@@ -240,33 +144,7 @@
|
||||
{getTotalScores()}/100
|
||||
</span>
|
||||
</div>
|
||||
{#each questions as currentQuestion, currentQuestionIndex}
|
||||
{#if QUESTION_TYPE.RADIO === currentQuestion.type}
|
||||
<RadioQuestion
|
||||
{...getRenderProps(
|
||||
currentQuestion,
|
||||
currentQuestionIndex + 1,
|
||||
$userQuestionniareAnswers.answers
|
||||
)}
|
||||
/>
|
||||
{:else if QUESTION_TYPE.CHECKBOX === currentQuestion.type}
|
||||
<CheckboxQuestion
|
||||
{...getRenderProps(
|
||||
currentQuestion,
|
||||
currentQuestionIndex + 1,
|
||||
$userQuestionniareAnswers.answers
|
||||
)}
|
||||
/>
|
||||
{:else if QUESTION_TYPE.TEXTAREA === currentQuestion.type}
|
||||
<TextareaQuestion
|
||||
{...getRenderProps(
|
||||
currentQuestion,
|
||||
currentQuestionIndex + 1,
|
||||
$userQuestionniareAnswers.answers
|
||||
)}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
<Preview {questions} questionnaireMetaData={$questionnaireMetaData} />
|
||||
{:else if QUESTION_TYPE.RADIO === currentQuestion.type}
|
||||
<RadioQuestion {...renderProps} />
|
||||
{:else if QUESTION_TYPE.CHECKBOX === currentQuestion.type}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
import { QUESTION_TYPE } from '../../../../Question/constants';
|
||||
|
||||
export function wasCorrectAnswerSelected(currentQuestion, answers) {
|
||||
if (currentQuestion.type === QUESTION_TYPE.TEXTAREA) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const answer = answers[currentQuestion.id];
|
||||
const formattedAnswers = typeof answer === 'string' ? [answer] : answer;
|
||||
return (
|
||||
formattedAnswers &&
|
||||
formattedAnswers.some((answer) => {
|
||||
if (
|
||||
!currentQuestion.answers ||
|
||||
!currentQuestion.answers.length ||
|
||||
currentQuestion.answers.includes(answer)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
export function getPropsForQuestion(
|
||||
questions,
|
||||
question,
|
||||
questionnaireMetaData,
|
||||
questionIndex,
|
||||
onSubmit,
|
||||
onPrevious,
|
||||
preview
|
||||
) {
|
||||
const { answers, isFinished } = questionnaireMetaData;
|
||||
|
||||
const isLast = questionIndex === questions.length;
|
||||
const isOpenQuesiton = question.type === QUESTION_TYPE.TEXTAREA;
|
||||
|
||||
return {
|
||||
title: questionIndex + '. ' + question.title,
|
||||
name: `${question.id}`,
|
||||
options: question.options,
|
||||
answers: question.answers,
|
||||
code: question.code,
|
||||
defaultValue: isOpenQuesiton
|
||||
? answers[question.id] || ''
|
||||
: answers[question.id] || [],
|
||||
onSubmit,
|
||||
onPrevious,
|
||||
disablePreviousButton: questionIndex === 1,
|
||||
isLast,
|
||||
isPreview: preview || isFinished,
|
||||
disabled:
|
||||
(isFinished && wasCorrectAnswerSelected(question, answers)) || isFinished,
|
||||
nextButtonProps: isOpenQuesiton
|
||||
? {
|
||||
label: 'Next',
|
||||
isActive: true,
|
||||
}
|
||||
: wasCorrectAnswerSelected(question, answers)
|
||||
? {
|
||||
label: isLast ? 'Finish' : 'Next',
|
||||
isActive: true,
|
||||
disableOptionSelect: true,
|
||||
}
|
||||
: {
|
||||
label: 'Check',
|
||||
isActive: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
<script>
|
||||
import { handleAddQuestion } from './store/index';
|
||||
import { questionnaireMetaData } from './store/answers';
|
||||
import PrimaryButton from '../../../../PrimaryButton/index.svelte';
|
||||
import PageBody from '../../../../PageBody/index.svelte';
|
||||
import ViewMode from './ViewMode.svelte';
|
||||
@@ -15,6 +16,15 @@
|
||||
function handleMode() {
|
||||
mode = mode === MODES.edit ? MODES.view : MODES.edit;
|
||||
preview = false;
|
||||
|
||||
if (mode === MODES.view) {
|
||||
questionnaireMetaData.update((metaData) => {
|
||||
metaData.answers = {};
|
||||
metaData.currentQuestionIndex = 0;
|
||||
metaData.isFinished = false;
|
||||
return metaData;
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { writable } from 'svelte/store';
|
||||
|
||||
import { STATUS } from '../constants';
|
||||
|
||||
// export const userQuestionniareAnswers = writable({
|
||||
// export const questionnaireMetaData = writable({
|
||||
// answers: {},
|
||||
// scores: { gates: 60 },
|
||||
// currentQuestionIndex: 5,
|
||||
@@ -19,4 +19,4 @@ const initAnswerState = {
|
||||
status: STATUS.IN_PROGRESS,
|
||||
};
|
||||
|
||||
export const userQuestionniareAnswers = writable(initAnswerState);
|
||||
export const questionnaireMetaData = writable(initAnswerState);
|
||||
|
||||
@@ -19,7 +19,7 @@ export function handleAddQuestion() {
|
||||
...questions,
|
||||
{
|
||||
...QUESTION_TEMPLATE,
|
||||
id: questions.length + 1,
|
||||
id: `${questions.length + 1}-form`,
|
||||
value: '',
|
||||
answers: [],
|
||||
options: [
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
export let courseId;
|
||||
export let path;
|
||||
|
||||
let show = true;
|
||||
let show = null;
|
||||
|
||||
const { page } = stores();
|
||||
|
||||
@@ -70,6 +70,8 @@
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
show = localStorage.getItem('hideCourseNav') === 'false';
|
||||
|
||||
hotkeys('b', function (event, handler) {
|
||||
event.preventDefault();
|
||||
switch (handler.key) {
|
||||
@@ -79,6 +81,12 @@
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$: {
|
||||
if (process.browser && show !== null) {
|
||||
localStorage.setItem('hideCourseNav', `${!show}`);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="root z-10 {!show && 'hide'}">
|
||||
|
||||
Reference in New Issue
Block a user