feat: students should move to next lesson on click go to lesson (#230)

* completion

* completion

* PR Fixes

* PR Fixes

* fix: progress bug on home page

* chore: fix lesson completion issue

---------

Co-authored-by: Rotimi Best <best@classroomio.com>
This commit is contained in:
Adeyemi Adetunji
2024-04-16 08:51:53 +01:00
committed by GitHub
co-authored by Rotimi Best
parent db38756cbf
commit 1277bcbfc6
7 changed files with 81 additions and 27 deletions
@@ -9,6 +9,7 @@
import { profile } from '$lib/utils/store/user';
import { fetchCourse } from '$lib/utils/services/courses';
import { globalStore } from '$lib/utils/store/app';
import { lessons } from '../Course/components/Lesson/store/lessons';
export let courseId = '';
export let path = '';
@@ -23,6 +24,7 @@
isFetching = true;
course.set(defaultCourse);
lessons.set([]);
const { data: _data } = await fetchCourse(courseId);
@@ -66,7 +66,7 @@
<a
rel="prefetch"
bind:this={target}
href={isOnLandingPage ? `/course/${slug}` : `/courses/${id}`}
href={isOnLandingPage ? `/course/${slug}` : `/courses/${id}${isLMS ? '/lessons?next=true' : ''}`}
class="text-black border border-gray dark:border-neutral-600 rounded w-full max-w-[320px] relative hover:scale-95 transition-all ease-in-out"
>
<div class="p-4">
@@ -122,7 +122,7 @@
{#if isLMS}
<PrimaryButton
label={'Continue Course'}
label="Continue Course"
variant={VARIANTS.OUTLINED}
className="rounded-none text-primary-600"
/>
@@ -7,7 +7,7 @@
const gotoCourse = (id: string | undefined) => {
if (!id) return;
goto(`/courses/${id}`);
goto(`/courses/${id}/lessons?next=true`);
};
</script>
@@ -41,7 +41,9 @@
</span>
<div class="relative bg-[#EAEAEA] w-full h-1">
<div
style="width:{course.progress_rate}%"
style="width: {Math.round(
((course?.progress_rate ?? 0) / (course?.total_lessons ?? 0)) * 100
) || 0}%"
class={`absolute top-0 left-0 bg-primary-700 h-full`}
/>
</div>
@@ -175,8 +175,17 @@ export function updateLesson(lesson: any, lessonId: Lesson['id']) {
.match({ id: lessonId });
}
export function updateLessonCompletion(completion: LessonCompletion) {
return supabase.from('lesson_completion').upsert(completion);
export function updateLessonCompletion(completion: LessonCompletion, shouldUpdate: boolean) {
if (shouldUpdate) {
return supabase
.from('lesson_completion')
.update({
is_complete: completion.is_complete
})
.eq('id', completion.id);
} else {
return supabase.from('lesson_completion').insert(completion);
}
}
export function deleteLesson(lessonId: Lesson['id']) {
@@ -26,13 +26,17 @@
import { course, group } from '$lib/components/Course/store';
import DateField from '$lib/components/Form/Date.svelte';
import type { Lesson } from '$lib/utils/types';
import { goto } from '$app/navigation';
export let data;
const query = new URLSearchParams($page.url.search);
let lessonEditing: string | undefined;
let lessonToDelete: Lesson | undefined;
let isStudent = true;
let openDeleteModal = false;
let isStudent: boolean = true;
let openDeleteModal: boolean = false;
let isFetching: boolean = false;
const flipDurationMs = 300;
@@ -81,6 +85,30 @@
return index + 1;
}
}
function findFirstIncompleteLesson() {
return $lessons.find(
(lesson) =>
lesson.lesson_completion &&
lesson.lesson_completion.length === 0 &&
lesson.is_unlocked === true
);
}
function onNextQuery(lessons) {
if (!isFetching && lessons.length > 0) {
const incompleteLesson = findFirstIncompleteLesson();
if (incompleteLesson) {
goto(`/courses/${data.courseId}/lessons/${incompleteLesson.id}`);
} else {
goto(`/courses/${data.courseId}/lessons`);
}
}
}
$: shouldGoToNextLesson = query.get('next') === 'true';
$: shouldGoToNextLesson && onNextQuery($lessons);
</script>
{#if $handleAddLessonWidget}
@@ -94,7 +122,7 @@
/>
<!-- TODO: Refactor usage of two way binding isStudent, rather use $globalStore.isStudent -->
<CourseContainer bind:isStudent bind:courseId={data.courseId}>
<CourseContainer bind:isFetching bind:isStudent bind:courseId={data.courseId}>
<PageNav title="Lessons">
<div slot="widget">
<RoleBasedSecurity allowedRoles={[1, 2]}>
@@ -104,7 +132,18 @@
</PageNav>
<PageBody width="max-w-6xl" padding="p-0">
{#if $lessons.length}
{#if shouldGoToNextLesson}
<Box className="w-full lg:w-11/12 lg:px-4 m-auto">
<div class="flex flex-col items-center justify-between">
<img src="/images/empty-lesson-icon.svg" alt="Lesson" class="mx-auto my-2.5" />
<h2 class="my-1.5 text-xl font-normal">No lessons yet</h2>
<p class="text-center text-sm text-slate-500">
Share your knowledge with the world by creating engaging lessons. Start by clicking on
the Add button.
</p>
</div>
</Box>
{:else if $lessons.length}
<section
class="m-auto w-full p-3 lg:w-11/12 lg:px-4"
use:dndzone={{
@@ -32,6 +32,7 @@
import type { LessonCompletion } from '$lib/utils/types';
import { profile } from '$lib/utils/store/user';
import { getIsLessonComplete } from '$lib/components/Course/components/Lesson/functions';
import { captureMessage } from '@sentry/browser';
export let data;
@@ -75,29 +76,38 @@
}
async function markLessonComplete(lessonId: string) {
let found = false;
isMarkingComplete = true;
let completion: LessonCompletion = {
let newCompletion: LessonCompletion = {
is_complete: true,
profile_id: $profile.id || '',
lesson_id: lessonId,
created_at: new Date().toDateString(),
updated_at: new Date().toDateString()
};
const completions: LessonCompletion[] = $lesson.lesson_completion.map((_completion) => {
if (_completion.profile_id === $profile.id) {
found = true;
let updatedCompletion: LessonCompletion;
const completions: LessonCompletion[] = $lesson.lesson_completion.map((completion) => {
if (completion.profile_id === $profile.id) {
console.log('shouldUpdate');
completion = {
...completion,
is_complete: _completion.is_complete
is_complete: !completion.is_complete
};
updatedCompletion = completion;
}
return _completion;
return completion;
});
if (!found) {
completions.push(completion);
// @ts-ignore
if (updatedCompletion) {
await updateLessonCompletion(updatedCompletion, true);
} else {
completions.push(newCompletion);
await updateLessonCompletion(newCompletion, false);
}
$lesson.lesson_completion = completions;
@@ -108,8 +118,6 @@
return l;
});
await updateLessonCompletion(completion);
snackbar.success('Marked as complete');
isMarkingComplete = false;
}
-6
View File
@@ -326,12 +326,6 @@ INSERT INTO "public"."lesson_comment" ("id", "created_at", "updated_at", "lesson
-- Data for Name: lesson_completion; Type: TABLE DATA; Schema: public; Owner: postgres
--
INSERT INTO "public"."lesson_completion" ("id", "created_at", "lesson_id", "profile_id", "is_complete", "updated_at") VALUES
(1, '2023-12-18 00:00:00+00', '5e5c8221-4c11-4c40-8664-11743bb79579', '0c256e75-aa40-4f62-8d30-0217ca1c60d9', true, '2023-12-18 00:00:00+00'),
(2, '2023-12-18 00:00:00+00', '829da386-8ccd-4c81-b2fb-b9891102c83c', '0c256e75-aa40-4f62-8d30-0217ca1c60d9', true, '2023-12-18 00:00:00+00'),
(3, '2023-12-19 00:00:00+00', '829da386-8ccd-4c81-b2fb-b9891102c83c', '0c256e75-aa40-4f62-8d30-0217ca1c60d9', true, '2023-12-19 00:00:00+00'),
(4, '2023-12-19 00:00:00+00', '829da386-8ccd-4c81-b2fb-b9891102c83c', '0c256e75-aa40-4f62-8d30-0217ca1c60d9', true, '2023-12-19 00:00:00+00');
--
-- Data for Name: lesson_progress; Type: TABLE DATA; Schema: public; Owner: postgres