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'}">
|
||||
|
||||
@@ -5,10 +5,11 @@
|
||||
export let name;
|
||||
export let isEditable = false;
|
||||
export let disabled = false;
|
||||
export let className = '';
|
||||
</script>
|
||||
|
||||
<label
|
||||
class="inline-flex items-center w-full {disabled
|
||||
class="{className} inline-flex items-center w-full {disabled
|
||||
? 'cursor-not-allowed'
|
||||
: 'cursor-pointer'}"
|
||||
>
|
||||
|
||||
@@ -5,10 +5,11 @@
|
||||
export let name;
|
||||
export let isEditable = false;
|
||||
export let disabled = false;
|
||||
export let className = '';
|
||||
</script>
|
||||
|
||||
<label
|
||||
class="inline-flex items-center w-full {disabled
|
||||
class="{className} inline-flex items-center w-full {disabled
|
||||
? 'cursor-not-allowed'
|
||||
: 'cursor-pointer'}"
|
||||
>
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
<script>
|
||||
import { afterUpdate } from 'svelte';
|
||||
import CloseButton from '../Buttons/Close/index.svelte';
|
||||
|
||||
export let open;
|
||||
export let onClose;
|
||||
export let modalHeading;
|
||||
export let width;
|
||||
|
||||
afterUpdate(() => {
|
||||
if (open) {
|
||||
document.getElementsByTagName('body')[0].style.overflow = 'hidden';
|
||||
} else {
|
||||
document.getElementsByTagName('body')[0].style.overflow = 'auto';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if open}
|
||||
@@ -11,7 +21,7 @@
|
||||
class="dialog fixed overflow-x-hidden overflow-y-auto inset-0 flex justify-center items-center z-50 bg-gray-100 bg-opacity-50"
|
||||
>
|
||||
<div aria-hidden="true" class="backdrop" />
|
||||
<div class="bg-white mx-auto w-3xl shadow-lg p-5 pt-2 rounded-md">
|
||||
<div class="bg-white mx-auto {width} shadow-lg p-5 pt-2 rounded-md">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h3 class="text-xl m-0">
|
||||
{modalHeading}
|
||||
@@ -19,7 +29,9 @@
|
||||
<CloseButton onClick={onClose} contained={true} />
|
||||
</div>
|
||||
|
||||
<slot />
|
||||
<div class="body h-4/5 overflow-y-auto">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -28,4 +40,7 @@
|
||||
.dialog {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
.body {
|
||||
max-height: 60vh;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
if (isPreview) return;
|
||||
const values = getVal(event.target, name);
|
||||
onSubmit(name, values);
|
||||
// event.target.reset();
|
||||
event.target.reset();
|
||||
}
|
||||
|
||||
function handlePrevious(event) {
|
||||
@@ -65,13 +65,14 @@
|
||||
<div class="ml-4">
|
||||
{#each options as option}
|
||||
<button
|
||||
class="cursor-pointer text-left my-2 border-2 border-gray-300 p-2 rounded-md cursor-pointer hover:bg-gray-200 w-full {getValidationClassName(
|
||||
class="cursor-pointer text-left my-2 border-2 border-gray-300 rounded-md cursor-pointer hover:bg-gray-200 w-full {getValidationClassName(
|
||||
option
|
||||
)}"
|
||||
type="button"
|
||||
>
|
||||
<Checkbox
|
||||
{name}
|
||||
className="p-2"
|
||||
value={option.id}
|
||||
checked={defaultValue.includes(option.id)}
|
||||
label={option.label || option.value}
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
if (isPreview) return;
|
||||
const value = getRadioVal(event.target, name);
|
||||
onSubmit(name, [value]);
|
||||
// event.target.reset();
|
||||
event.target.reset();
|
||||
}
|
||||
|
||||
function handlePrevious(event) {
|
||||
@@ -65,12 +65,13 @@
|
||||
<div class="ml-4">
|
||||
{#each options as option}
|
||||
<button
|
||||
class="cursor-pointer text-left my-2 border-2 border-gray-300 p-2 rounded-md cursor-pointer hover:bg-gray-200 w-full {getValidationClassName(
|
||||
class="cursor-pointer text-left my-2 border-2 border-gray-300 rounded-md cursor-pointer hover:bg-gray-200 w-full {getValidationClassName(
|
||||
option
|
||||
)}"
|
||||
type="button"
|
||||
>
|
||||
<RadioItem
|
||||
className="p-2"
|
||||
{name}
|
||||
value={option.id}
|
||||
checked={defaultValue.includes(option.id) &&
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-track {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script>
|
||||
import ArrowUp16 from "carbon-icons-svelte/lib/ArrowUp16";
|
||||
import Chip from "../Chip/index.svelte";
|
||||
import ArrowUp16 from 'carbon-icons-svelte/lib/ArrowUp16';
|
||||
import Chip from '../Chip/index.svelte';
|
||||
|
||||
export let value = 0;
|
||||
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
<script>
|
||||
import { flip } from 'svelte/animate';
|
||||
import { dndzone } from 'svelte-dnd-action';
|
||||
import { stores, goto } from '@sapper/app';
|
||||
import PageNav from '../../../components/PageNav/index.svelte';
|
||||
import MarkExerciseModal from '../../../components/Course/components/Lesson/Exercise/MarkExerciseModal.svelte';
|
||||
import Chip from '../../../components/Chip/index.svelte';
|
||||
import PageBody from '../../../components/PageBody/index.svelte';
|
||||
import CourseContainer from '../../../components/CourseContainer/index.svelte';
|
||||
@@ -29,15 +31,111 @@
|
||||
title: 'Submitted',
|
||||
value: 5,
|
||||
items: [
|
||||
{ id: 41, name: 'item41' },
|
||||
{ id: 42, name: 'item42' },
|
||||
{ id: 43, name: 'item43' },
|
||||
{ id: 44, name: 'item44' },
|
||||
{ id: 45, name: 'item45' },
|
||||
{ id: 46, name: 'item46' },
|
||||
{ id: 47, name: 'item47' },
|
||||
{ id: 48, name: 'item48' },
|
||||
{ id: 49, name: 'item49' },
|
||||
{
|
||||
id: 41,
|
||||
exerciseTitle: 'Practice props in React.js',
|
||||
student: {
|
||||
avatar: 'https://picsum.photos/32/32/?random',
|
||||
name: 'Brad Traversy',
|
||||
},
|
||||
tutor: {
|
||||
name: 'sergey-semko',
|
||||
id: 1,
|
||||
},
|
||||
lesson: {
|
||||
number: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 42,
|
||||
exerciseTitle: 'Practice props in React.js',
|
||||
student: {
|
||||
avatar: 'https://picsum.photos/32/32/?random',
|
||||
name: 'James Kola',
|
||||
},
|
||||
tutor: {
|
||||
name: 'sergey-semko',
|
||||
id: 1,
|
||||
},
|
||||
lesson: {
|
||||
number: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 43,
|
||||
exerciseTitle: 'Practice props in React.js',
|
||||
student: {
|
||||
avatar: 'https://picsum.photos/32/32/?random',
|
||||
name: 'linus-torvalds',
|
||||
},
|
||||
tutor: {
|
||||
name: 'sergey-semko',
|
||||
id: 1,
|
||||
},
|
||||
lesson: {
|
||||
number: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 44,
|
||||
exerciseTitle: 'Practice props in React.js',
|
||||
student: {
|
||||
avatar: 'https://picsum.photos/32/32/?random',
|
||||
name: 'rotimi-best',
|
||||
},
|
||||
tutor: {
|
||||
name: 'sergey-semko',
|
||||
id: 1,
|
||||
},
|
||||
lesson: {
|
||||
number: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 45,
|
||||
exerciseTitle: 'Home task: Class and Functional component',
|
||||
student: {
|
||||
avatar: 'https://picsum.photos/32/32/?random',
|
||||
name: 'james-two',
|
||||
},
|
||||
tutor: {
|
||||
name: 'sergey-semko',
|
||||
id: 1,
|
||||
},
|
||||
lesson: {
|
||||
number: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 46,
|
||||
exerciseTitle: 'Home task: Class and Functional component',
|
||||
student: {
|
||||
avatar: 'https://picsum.photos/32/32/?random',
|
||||
name: 'brarack-obama',
|
||||
},
|
||||
tutor: {
|
||||
name: 'sergey-semko',
|
||||
id: 1,
|
||||
},
|
||||
lesson: {
|
||||
number: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 47,
|
||||
exerciseTitle: 'Lesson task: Class and Functional component',
|
||||
student: {
|
||||
avatar: 'https://picsum.photos/32/32/?random',
|
||||
name: 'bill-gates',
|
||||
},
|
||||
tutor: {
|
||||
name: 'sergey-semko',
|
||||
id: 1,
|
||||
},
|
||||
lesson: {
|
||||
number: 3,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -53,6 +151,9 @@
|
||||
items: [],
|
||||
},
|
||||
];
|
||||
let openExercise = false;
|
||||
|
||||
const { page } = stores();
|
||||
|
||||
function handleItemFinalize(columnIdx, newItems) {
|
||||
sections[columnIdx].items = newItems;
|
||||
@@ -69,16 +170,26 @@
|
||||
function handleDndFinalizeCards(columnIdx) {
|
||||
return (e) => handleItemFinalize(columnIdx, e.detail.items);
|
||||
}
|
||||
|
||||
function handleModalClose() {
|
||||
console.log('closing', page.path);
|
||||
|
||||
goto($page.path);
|
||||
}
|
||||
|
||||
$: openExercise = !!$page.query.submissionId;
|
||||
</script>
|
||||
|
||||
<MarkExerciseModal bind:open={openExercise} onClose={handleModalClose} />
|
||||
|
||||
<CourseContainer {courseData}>
|
||||
<PageNav title="All Exercises" />
|
||||
|
||||
<PageBody width="w-11/12 overflow-x-auto overflow-y-hidden h-3/4">
|
||||
<div class="flex items-center justify-between h-full w-full">
|
||||
<PageBody width="w-11/12 overflow-x-auto overflow-y-hidden">
|
||||
<div class="flex items-center justify-between w-full">
|
||||
{#each sections as { id, title, items }, idx (id)}
|
||||
<div
|
||||
class="section rounded-md bg-gray-100 border border-gray-50 p-3 h-full mr-3 overflow-hidden"
|
||||
class="section rounded-md bg-gray-100 border border-gray-50 p-3 h-80 mr-3 overflow-hidden"
|
||||
animate:flip={{ duration: flipDurationMs }}
|
||||
>
|
||||
<div class="flex items-center mb-2">
|
||||
@@ -100,24 +211,27 @@
|
||||
class="border border-grey-700 w-full my-2 mx-0 rounded-md bg-white py-2 px-3"
|
||||
animate:flip={{ duration: flipDurationMs }}
|
||||
>
|
||||
<a class="text-blue-700 text-md font-bold" href="courses/1"
|
||||
>{item.name}</a
|
||||
>
|
||||
<p class="text-grey text-sm mb-2">#2 created by sergey-semko</p>
|
||||
|
||||
<a
|
||||
class="flex items-center no-underline hover:underline text-black"
|
||||
href="/"
|
||||
class="flex items-center no-underline hover:underline text-black mb-2"
|
||||
href="{$page.path}?submissionId={item.id}"
|
||||
>
|
||||
<img
|
||||
alt="Placeholder"
|
||||
class="block rounded-full"
|
||||
width="24"
|
||||
height="20"
|
||||
src="https://picsum.photos/32/32/?random"
|
||||
src={item.student.avatar}
|
||||
/>
|
||||
<p class="ml-2 text-sm">rotimi-best</p>
|
||||
<p class="ml-2 text-sm">{item.student.name}</p>
|
||||
</a>
|
||||
<a
|
||||
class="text-blue-700 text-md font-bold"
|
||||
href="{$page.path}?submissionId={item.id}"
|
||||
>{item.exerciseTitle}</a
|
||||
>
|
||||
<p class="text-grey text-sm">
|
||||
#{item.lesson.number} created by {item.tutor.name}
|
||||
</p>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
@@ -131,9 +245,10 @@
|
||||
.section {
|
||||
max-width: 355px;
|
||||
min-width: 355px;
|
||||
height: 80vh;
|
||||
}
|
||||
|
||||
.content {
|
||||
height: 90%;
|
||||
height: 95%;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user