Make lesson draggable

This commit is contained in:
rotimi-best
2021-09-14 07:01:44 +03:00
parent ef08b48a6e
commit 8c14b348f7
4 changed files with 76 additions and 22 deletions
+3 -3
View File
@@ -57,9 +57,9 @@ export async function setCourseData(data, setLesson = true) {
}
if (setLesson) {
const orderedLessons = (data.lessons || []).sort(
(a, b) => new Date(a.created_at) - new Date(b.created_at)
);
const orderedLessons = (data.lessons || [])
.sort((a, b) => new Date(a.created_at) - new Date(b.created_at))
.sort((a, b) => a.order - b.order);
// .map((lesson) => ({
// ...lesson,
// profile: lesson.profile && tutorsById[lesson.profile.id],
+2 -1
View File
@@ -2,10 +2,11 @@
// import
// border border-gray-300 rounded-md p-3 bg-blue-200 bg-opacity-25
export let width = 'max-w-3xl w-11/12';
export let padding = 'pb-5 px-4';
export let className = '';
</script>
<div class="root mt-4 mx-auto {width} {className} relative pb-5 px-4">
<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 py-3"
+70 -17
View File
@@ -7,6 +7,7 @@
<script>
import { onMount } from 'svelte';
import { stores } from '@sapper/app';
import { dndzone } from 'svelte-dnd-action';
import Time24 from 'carbon-icons-svelte/lib/Time24';
import Edit24 from 'carbon-icons-svelte/lib/Edit24';
import Save24 from 'carbon-icons-svelte/lib/Save24';
@@ -21,7 +22,10 @@
import TextField from '../../../../components/Form/TextField.svelte';
import IconButton from '../../../../components/IconButton/index.svelte';
import Select from '../../../../components/Form/Select.svelte';
import { fetchCourse } from '../../../../utils/services/courses';
import {
fetchCourse,
updateLesson,
} from '../../../../utils/services/courses';
import CourseContainer from '../../../../components/CourseContainer/index.svelte';
import { profile } from '../../../../utils/store/user';
import {
@@ -43,6 +47,7 @@
let isStudent = true;
const { page } = stores();
const flipDurationMs = 300;
function formatDate(date) {
const d = new Date(date);
@@ -69,6 +74,36 @@
}, 100);
}
function handleDndConsider(e) {
$lessons = e.detail.items;
}
function handleDndFinalize(e) {
const prevLessonsByOrder = $lessons.reduce(
(acc, l) => ({ ...acc, [l.id]: l.order }),
{}
);
$lessons = e.detail.items;
// Only update the lesson order that changed
e.detail.items.forEach((item, index) => {
const order = index + 1;
if (order !== prevLessonsByOrder[item.id]) {
$lessons[index].order = order;
updateLesson({ order }, item.id).then((update) =>
console.log(`updated lesson order`, update)
);
}
});
}
function getLessonOrder(id) {
const index = $lessons.findIndex((lesson) => lesson.id === id);
return index + 1;
}
onMount(async () => {
if ($course.id) return;
const { data } = await fetchCourse(courseId);
@@ -88,23 +123,35 @@
<CourseContainer>
<PageNav title="Lessons">
<RoleBasedSecurity allowedRoles="[1,2]">
<div slot="widget">
<div slot="widget">
<RoleBasedSecurity allowedRoles="[1,2]">
<PrimaryButton
label="Add"
onClick={addLesson}
isDisabled={!!lessonEditing}
/>
</div>
</RoleBasedSecurity>
</RoleBasedSecurity>
</div>
</PageNav>
<PageBody>
<div class="w-11/12 mt-4 m-auto">
{#each $lessons as lesson, index}
<PageBody padding="p-0">
<section
class="w-11/12 py-3 px-4 m-auto lesson-list"
use:dndzone={{
items: $lessons,
flipDurationMs,
dropTargetStyle: {
border: '2px #1d4ed8 solid',
'border-style': 'dashed',
},
}}
on:consider={handleDndConsider}
on:finalize={handleDndFinalize}
>
{#each $lessons as lesson (lesson.id)}
<div
bind:this={ref}
class="group relative m-auto rounded-md border-2 border-gray-100 py-3 px-5 mb-4 flex items-center hover:shadow-2xl shadow-md transition delay-150 duration-300 ease-in-out"
class="group cursor-move relative m-auto rounded-md border-2 border-gray-100 py-3 px-5 mb-4 flex items-center hover:shadow-2xl shadow-md transition delay-150 duration-300 ease-in-out"
>
<!-- Complete or Not complete icon -->
<div class="absolute -left-6 -top-6 success">
@@ -132,7 +179,7 @@
<RoleBasedSecurity allowedRoles="[1,2]">
<!-- Edit/Save -->
<div class="absolute top-2 right-0">
{#if lessonEditing === index}
{#if lessonEditing === lesson.id}
<IconButton
onClick={() => {
lessonEditing = null;
@@ -142,7 +189,7 @@
<Save24 class="carbon-icon" />
</IconButton>
{:else}
<IconButton onClick={() => (lessonEditing = index)}>
<IconButton onClick={() => (lessonEditing = lesson.id)}>
<Edit24 class="carbon-icon" />
</IconButton>
{/if}
@@ -150,10 +197,12 @@
</RoleBasedSecurity>
<div class="">
<h3 class="text-3xl font-bold">{index + 1}.</h3>
<h3 class="text-3xl font-bold">
{getLessonOrder(lesson.id)}.
</h3>
</div>
<div class="ml-8 w-4/5">
{#if lessonEditing === index}
{#if lessonEditing === lesson.id}
<TextField bind:value={lesson.title} autofocus={true} />
{:else}
<h3 class="text-2xl m-0 flex items-center">
@@ -177,7 +226,7 @@
</div> -->
<div class="flex items-center justify-between mt-6 w-full">
{#if lessonEditing === index}
{#if lessonEditing === lesson.id}
<Select
bind:value={lesson.profile}
options={$group.tutors}
@@ -201,7 +250,7 @@
{/if}
<div class="flex items-cente">
{#if lessonEditing === index}
{#if lessonEditing === lesson.id}
<input
type="date"
name="lesson-date-picker"
@@ -218,7 +267,7 @@
</div>
<div class="flex items-center">
{#if lessonEditing === index}
{#if lessonEditing === lesson.id}
<TextField
bind:value={lesson.call_url}
autofocus={true}
@@ -253,11 +302,15 @@
<img alt="No lesson added" src="/notfound2.gif" class="w-80" />
</Box>
{/each}
</div>
</section>
</PageBody>
</CourseContainer>
<style>
.lesson-list {
overflow-y: auto;
height: 84vh;
}
.group {
min-height: 170px;
background-color: #fafafa;
+1 -1
View File
@@ -34,7 +34,7 @@ export async function fetchCourse(courseId, session) {
)
),
lessons:lesson(
id, title,public, lesson_at, call_url, is_complete,created_at,
id, title,public, lesson_at, call_url, is_complete, order, created_at,
profile:teacher_id(id, avatar_url, fullname)
),
attendance:group_attendance(*)