Make questionnaire Ui reusable

This commit is contained in:
Ibitoye Rotimi Best
2021-07-22 08:04:48 +03:00
parent d92d0c8a30
commit 29d3e6b7e0
16 changed files with 401 additions and 194 deletions
@@ -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'}">
+2 -1
View File
@@ -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'}"
>
+2 -1
View File
@@ -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'}"
>
+17 -2
View File
@@ -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) &&
+1 -1
View File
@@ -14,7 +14,7 @@
}
*::-webkit-scrollbar {
width: 5px;
width: 10px;
}
*::-webkit-scrollbar-track {
+2 -2
View File
@@ -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;
+137 -22
View File
@@ -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>