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>
import { dndzone } from 'svelte-dnd-action';
import { fly } from 'svelte/transition';
import {
questionnaire,
deleteConfirmation,
@@ -32,6 +30,7 @@
import Select from '../../../../Form/Select.svelte';
import QuestionContainer from '../../../../QuestionContainer/index.svelte';
import DeleteConfirmationModal from './DeleteConfirmation.svelte';
import OrderModal from './OrderModal.svelte';
import {
QUESTION_TYPE,
QUESTION_TYPES,
@@ -50,33 +49,6 @@
let errors = {};
let questionIdToDelete = null;
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) {
const currentQuestionsLength = questions.length;
@@ -125,6 +97,8 @@
onDelete={onFinalDeleteClicked}
/>
<OrderModal />
<Modal
onClose={() => (editDescription = false)}
bind:open={editDescription}
@@ -165,19 +139,7 @@
<ErrorMessage message="You have some errors" />
</div>
{/if}
<div
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"
>
<div class="questions pt-6 px-6">
{#each questions as question (question.id)}
<!-- {#key question.id} -->
<QuestionContainer
@@ -332,11 +294,6 @@
</div>
<style>
.questions {
height: 80vh;
overflow-y: auto;
}
:global(#dnd-action-dragged-el div) {
z-index: 100;
}
@@ -86,7 +86,9 @@
>
<div class="w-full">
<Preview
questions={data.questions || []}
questions={Array.isArray(data.questions)
? data.questions.sort((a, b) => a.order - b.order)
: []}
questionnaireMetaData={{
answers: data.answers || {},
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 renderProps = {};
let submission;
let hasSubmission = null;
function handleStart() {
$questionnaireMetaData.currentQuestionIndex += 1;
@@ -97,21 +98,23 @@
}
async function checkForSubmission(people, profileId, courseId) {
console.log('checkForSubmission');
if (!Array.isArray(people) || !profileId || !courseId || !!submission) {
return;
}
if (hasSubmission) return;
console.log('checkForSubmission');
const args = {
exerciseId,
courseId,
submittedBy: getGroupMemberId(people, profileId),
};
const { data } = await fetchSubmission(args);
hasSubmission = true;
if (Array.isArray(data) && data.length) {
console.log(`submissions`, data);
submission = data[0];
$questionnaireMetaData.answers = formatAnswers({
@@ -248,7 +251,7 @@
</span>
</div>
<Preview
questions={$questionnaire.questions}
questions={$questionnaire.questions.sort((a, b) => a.order - b.order)}
questionnaireMetaData={$questionnaireMetaData}
grades={$questionnaireMetaData.grades}
disableGrading={true}
@@ -1,8 +1,9 @@
<script>
import { onDestroy } from 'svelte';
import AddAlt24 from 'carbon-icons-svelte/lib/AddAlt24';
import Delete24 from 'carbon-icons-svelte/lib/Delete24';
import Settings24 from 'carbon-icons-svelte/lib/Settings24';
import AddAlt32 from 'carbon-icons-svelte/lib/AddAlt32';
// import Delete32 from 'carbon-icons-svelte/lib/Delete32';
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 ViewFilled32 from 'carbon-icons-svelte/lib/ViewFilled32';
import {
@@ -10,6 +11,7 @@
questionnaire,
validateQuestionnaire,
reset,
questionnaireOrder,
} from '../store/exercise';
import PrimaryButton from '../../../../PrimaryButton/index.svelte';
import PageBody from '../../../../PageBody/index.svelte';
@@ -103,25 +105,31 @@
{#if mode === MODES.edit}
<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
onClick={() => (editDescription = true)}
toolTipProps={{ title: 'Edit description', direction: 'bottom' }}
>
<Settings24 class="carbon-icon" />
<Settings32 class="carbon-icon" />
</IconButton>
<!-- <IconButton
onClick={handleDeleteExercise}
toolTipProps={{ title: 'Delete questionnaire', direction: 'bottom' }}
>
<Delete24 class="carbon-icon" />
<Delete32 class="carbon-icon" />
</IconButton> -->
<IconButton
onClick={handleAddQuestion}
toolTipProps={{ title: 'Add question', direction: 'bottom' }}
>
<AddAlt24 class="carbon-icon" />
</IconButton>
</div>
{/if}
</svelte:fragment>
@@ -8,6 +8,7 @@ import { isUUID } from '../../../../../utils/functions/isUUID';
export const isQuestionnaireFetching = writable(false);
export const deleteConfirmation = writable({ open: false });
export const questionnaireOrder = writable({ open: false });
// {'question-id': { option: '', title: '', ... }}
export const questionnaireValidation = writable({});
@@ -71,6 +72,7 @@ export function handleAddQuestion() {
name: `${questions.length + 1}-form`,
value: '',
points: 0,
order: questions.length,
question_type: {
id: QUESTION_TYPE.RADIO,
},
+1 -1
View File
@@ -9,7 +9,7 @@
<div class="root mt-4 mx-auto {width} {className} relative {padding}">
{#if $$slots.header}
<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" />
</div>