CLASSROOMIO-T-23: Fix reodering of questions

This commit is contained in:
rotimi-best
2021-10-09 20:38:21 +03:00
parent 8745769f70
commit ea3ce7dd69
7 changed files with 137 additions and 65 deletions
@@ -1,6 +1,4 @@
<script> <script>
import { dndzone } from 'svelte-dnd-action';
import { fly } from 'svelte/transition';
import { import {
questionnaire, questionnaire,
deleteConfirmation, deleteConfirmation,
@@ -32,6 +30,7 @@
import Select from '../../../../Form/Select.svelte'; import Select from '../../../../Form/Select.svelte';
import QuestionContainer from '../../../../QuestionContainer/index.svelte'; import QuestionContainer from '../../../../QuestionContainer/index.svelte';
import DeleteConfirmationModal from './DeleteConfirmation.svelte'; import DeleteConfirmationModal from './DeleteConfirmation.svelte';
import OrderModal from './OrderModal.svelte';
import { import {
QUESTION_TYPE, QUESTION_TYPE,
QUESTION_TYPES, QUESTION_TYPES,
@@ -50,33 +49,6 @@
let errors = {}; let errors = {};
let questionIdToDelete = null; let questionIdToDelete = null;
let questions = []; let questions = [];
let isDragging = false;
function handleDndConsider(e) {
questions = e.detail.items;
}
function handleDndFinalize(e) {
const prevQuestionsByOrder = questions.reduce(
(acc, l, index) => ({ ...acc, [l.id]: l.order }),
{}
);
questions = e.detail.items;
// // Only update the lesson order that changed
e.detail.items.forEach((item, index) => {
const order = index + 1;
if (order !== prevQuestionsByOrder[item.id]) {
$questionnaire.questions.some((q) => {
if (q.id === item.id) {
q.is_dirty = true;
q.order = order;
}
});
}
});
}
function shouldScrollToLast(questionId, questions) { function shouldScrollToLast(questionId, questions) {
const currentQuestionsLength = questions.length; const currentQuestionsLength = questions.length;
@@ -125,6 +97,8 @@
onDelete={onFinalDeleteClicked} onDelete={onFinalDeleteClicked}
/> />
<OrderModal />
<Modal <Modal
onClose={() => (editDescription = false)} onClose={() => (editDescription = false)}
bind:open={editDescription} bind:open={editDescription}
@@ -165,19 +139,7 @@
<ErrorMessage message="You have some errors" /> <ErrorMessage message="You have some errors" />
</div> </div>
{/if} {/if}
<div <div class="questions pt-6 px-6">
use:dndzone={{
items: questions,
flipDurationMs: 300,
dropTargetStyle: {
border: '2px #1d4ed8 solid',
'border-style': 'dashed',
},
}}
on:consider={handleDndConsider}
on:finalize={handleDndFinalize}
class="questions pt-6 px-6"
>
{#each questions as question (question.id)} {#each questions as question (question.id)}
<!-- {#key question.id} --> <!-- {#key question.id} -->
<QuestionContainer <QuestionContainer
@@ -332,11 +294,6 @@
</div> </div>
<style> <style>
.questions {
height: 80vh;
overflow-y: auto;
}
:global(#dnd-action-dragged-el div) { :global(#dnd-action-dragged-el div) {
z-index: 100; z-index: 100;
} }
@@ -86,7 +86,9 @@
> >
<div class="w-full"> <div class="w-full">
<Preview <Preview
questions={data.questions || []} questions={Array.isArray(data.questions)
? data.questions.sort((a, b) => a.order - b.order)
: []}
questionnaireMetaData={{ questionnaireMetaData={{
answers: data.answers || {}, answers: data.answers || {},
isFinished: true, isFinished: true,
@@ -0,0 +1,100 @@
<script lang="ts">
import { dndzone } from 'svelte-dnd-action';
import CheckboxCheckedFilled16 from 'carbon-icons-svelte/lib/CheckboxCheckedFilled16';
import RadioButtonChecked16 from 'carbon-icons-svelte/lib/RadioButtonChecked16';
import TextAlignJustify16 from 'carbon-icons-svelte/lib/TextAlignJustify16';
import { flip } from 'svelte/animate';
import Modal from '../../../../Modal/index.svelte';
import { questionnaireOrder, questionnaire } from '../store/exercise';
import { filterOutDeleted } from './functions';
const flipDurationMs = 300;
interface Question {
id: number;
name: string;
type: number;
}
let items: Array<Question> = [];
function handleDndConsider(e) {
items = e.detail.items;
}
function handleDndFinalize(e) {
items = e.detail.items;
}
function handleClose() {
items.forEach((item, index) => {
const order = index + 1;
$questionnaire.questions.some((q) => {
if (q.id === item.id) {
q.is_dirty = true;
q.order = order;
return true;
}
});
});
$questionnaire.questions = $questionnaire.questions.sort(
(a, b) => a.order - b.order
);
$questionnaireOrder.open = false;
}
$: items = filterOutDeleted($questionnaire.questions).map((q) => ({
id: q.id,
name: q.title,
type: q.question_type.id,
}));
</script>
<Modal
onClose={handleClose}
bind:open={$questionnaireOrder.open}
width="w-96"
modalHeading="Order questions"
>
<section
use:dndzone={{
items,
flipDurationMs,
dropTargetStyle: {
border: '2px #1d4ed8 solid',
'border-style': 'dashed',
},
}}
on:consider={handleDndConsider}
on:finalize={handleDndFinalize}
class="w-full"
>
{#each items as item (item.id)}
<div
animate:flip={{ duration: flipDurationMs }}
class="flex items-center rounded-md p-4 border border-blue-600"
>
{#if item.type === 1}
<RadioButtonChecked16 class="carbon-icon active" />
{:else if item.type === 2}
<CheckboxCheckedFilled16 class="carbon-icon active" />
{:else}
<TextAlignJustify16 class="carbon-icon active" />
{/if}
{` ${item.name}`}
</div>
{/each}
</section>
</Modal>
<style>
section {
padding: 0.3em;
overflow: auto;
}
div {
margin: 0.5em 0;
}
</style>
@@ -26,6 +26,7 @@
let currentQuestion = {}; let currentQuestion = {};
let renderProps = {}; let renderProps = {};
let submission; let submission;
let hasSubmission = null;
function handleStart() { function handleStart() {
$questionnaireMetaData.currentQuestionIndex += 1; $questionnaireMetaData.currentQuestionIndex += 1;
@@ -97,21 +98,23 @@
} }
async function checkForSubmission(people, profileId, courseId) { async function checkForSubmission(people, profileId, courseId) {
console.log('checkForSubmission');
if (!Array.isArray(people) || !profileId || !courseId || !!submission) { if (!Array.isArray(people) || !profileId || !courseId || !!submission) {
return; return;
} }
if (hasSubmission) return;
console.log('checkForSubmission');
const args = { const args = {
exerciseId, exerciseId,
courseId, courseId,
submittedBy: getGroupMemberId(people, profileId), submittedBy: getGroupMemberId(people, profileId),
}; };
const { data } = await fetchSubmission(args); const { data } = await fetchSubmission(args);
hasSubmission = true;
if (Array.isArray(data) && data.length) { if (Array.isArray(data) && data.length) {
console.log(`submissions`, data);
submission = data[0]; submission = data[0];
$questionnaireMetaData.answers = formatAnswers({ $questionnaireMetaData.answers = formatAnswers({
@@ -248,7 +251,7 @@
</span> </span>
</div> </div>
<Preview <Preview
questions={$questionnaire.questions} questions={$questionnaire.questions.sort((a, b) => a.order - b.order)}
questionnaireMetaData={$questionnaireMetaData} questionnaireMetaData={$questionnaireMetaData}
grades={$questionnaireMetaData.grades} grades={$questionnaireMetaData.grades}
disableGrading={true} disableGrading={true}
@@ -1,8 +1,9 @@
<script> <script>
import { onDestroy } from 'svelte'; import { onDestroy } from 'svelte';
import AddAlt24 from 'carbon-icons-svelte/lib/AddAlt24'; import AddAlt32 from 'carbon-icons-svelte/lib/AddAlt32';
import Delete24 from 'carbon-icons-svelte/lib/Delete24'; // import Delete32 from 'carbon-icons-svelte/lib/Delete32';
import Settings24 from 'carbon-icons-svelte/lib/Settings24'; import Table32 from 'carbon-icons-svelte/lib/Table32';
import Settings32 from 'carbon-icons-svelte/lib/Settings32';
import View32 from 'carbon-icons-svelte/lib/View32'; import View32 from 'carbon-icons-svelte/lib/View32';
import ViewFilled32 from 'carbon-icons-svelte/lib/ViewFilled32'; import ViewFilled32 from 'carbon-icons-svelte/lib/ViewFilled32';
import { import {
@@ -10,6 +11,7 @@
questionnaire, questionnaire,
validateQuestionnaire, validateQuestionnaire,
reset, reset,
questionnaireOrder,
} from '../store/exercise'; } from '../store/exercise';
import PrimaryButton from '../../../../PrimaryButton/index.svelte'; import PrimaryButton from '../../../../PrimaryButton/index.svelte';
import PageBody from '../../../../PageBody/index.svelte'; import PageBody from '../../../../PageBody/index.svelte';
@@ -103,25 +105,31 @@
{#if mode === MODES.edit} {#if mode === MODES.edit}
<div class="flex items-center"> <div class="flex items-center">
<IconButton
onClick={handleAddQuestion}
toolTipProps={{ title: 'Add question', direction: 'bottom' }}
>
<AddAlt32 class="carbon-icon" />
</IconButton>
<IconButton
onClick={() => ($questionnaireOrder.open = true)}
toolTipProps={{ title: 'Order questionnaire', direction: 'bottom' }}
>
<Table32 class="carbon-icon" />
</IconButton>
<IconButton <IconButton
onClick={() => (editDescription = true)} onClick={() => (editDescription = true)}
toolTipProps={{ title: 'Edit description', direction: 'bottom' }} toolTipProps={{ title: 'Edit description', direction: 'bottom' }}
> >
<Settings24 class="carbon-icon" /> <Settings32 class="carbon-icon" />
</IconButton> </IconButton>
<!-- <IconButton <!-- <IconButton
onClick={handleDeleteExercise} onClick={handleDeleteExercise}
toolTipProps={{ title: 'Delete questionnaire', direction: 'bottom' }} toolTipProps={{ title: 'Delete questionnaire', direction: 'bottom' }}
> >
<Delete24 class="carbon-icon" /> <Delete32 class="carbon-icon" />
</IconButton> --> </IconButton> -->
<IconButton
onClick={handleAddQuestion}
toolTipProps={{ title: 'Add question', direction: 'bottom' }}
>
<AddAlt24 class="carbon-icon" />
</IconButton>
</div> </div>
{/if} {/if}
</svelte:fragment> </svelte:fragment>
@@ -8,6 +8,7 @@ import { isUUID } from '../../../../../utils/functions/isUUID';
export const isQuestionnaireFetching = writable(false); export const isQuestionnaireFetching = writable(false);
export const deleteConfirmation = writable({ open: false }); export const deleteConfirmation = writable({ open: false });
export const questionnaireOrder = writable({ open: false });
// {'question-id': { option: '', title: '', ... }} // {'question-id': { option: '', title: '', ... }}
export const questionnaireValidation = writable({}); export const questionnaireValidation = writable({});
@@ -71,6 +72,7 @@ export function handleAddQuestion() {
name: `${questions.length + 1}-form`, name: `${questions.length + 1}-form`,
value: '', value: '',
points: 0, points: 0,
order: questions.length,
question_type: { question_type: {
id: QUESTION_TYPE.RADIO, id: QUESTION_TYPE.RADIO,
}, },
+1 -1
View File
@@ -9,7 +9,7 @@
<div class="root mt-4 mx-auto {width} {className} relative {padding}"> <div class="root mt-4 mx-auto {width} {className} relative {padding}">
{#if $$slots.header} {#if $$slots.header}
<div <div
class="head flex items-center justify-between sticky right-0 w-full p-3" class="head flex items-center justify-between sticky right-0 w-full px-3"
> >
<slot name="header" /> <slot name="header" />
</div> </div>