CLASSROOMIO-T-88: Mark lesson as complete

This commit is contained in:
Rotimi Best
2023-08-03 02:51:02 +01:00
parent f09db20dda
commit 8cf4148703
15 changed files with 190 additions and 279 deletions
+5 -4
View File
@@ -2,14 +2,15 @@
export let className = ''; export let className = '';
export let value = ''; export let value = '';
export let size = ''; export let size = '';
export let shape = 'rounded-md';
</script> </script>
<p <p
class="{className} {size === 'sm' class="{className} {size === 'sm' ? 'py-1 px-1' : 'py-2 px-2'} org-avatar {className.includes(
? 'py-1 px-1' 'bg-'
: 'py-2 px-2'} org-avatar {className.includes('bg-') )
? '' ? ''
: 'bg-gray-200'} rounded-md text-center" : 'bg-gray-200'} {shape} text-center"
> >
{value} {value}
</p> </p>
@@ -1,10 +1,6 @@
import { writable } from 'svelte/store'; import { writable } from 'svelte/store';
import type { Writable, Updater } from 'svelte/store'; import type { Writable, Updater } from 'svelte/store';
import { import { createLesson, updateLesson, deleteLesson } from '$lib/utils/services/courses';
createLesson,
updateLesson,
deleteLesson,
} from '$lib/utils/services/courses';
import type { Lesson, Course } from '$lib/utils/types'; import type { Lesson, Course } from '$lib/utils/types';
@@ -12,12 +8,13 @@ export const lessons: Writable<Lesson[]> = writable([]);
export const lesson = writable({ export const lesson = writable({
id: null, id: null,
totalExercises: 0, totalExercises: 0,
is_complete: false,
materials: { materials: {
note: null, note: null,
slide_url: null, slide_url: null,
video_url: null, video_url: null
}, },
exercises: [], exercises: []
}); });
export const isLessonDirty = writable(false); export const isLessonDirty = writable(false);
@@ -31,8 +28,9 @@ export function handleAddLesson() {
// profile: undefined, // profile: undefined,
call_url: undefined, call_url: undefined,
lesson_at: new Date(), lesson_at: new Date(),
is_complete: false, is_unlocked: false,
}, is_complete: false
}
]; ];
}) as Updater<any>); }) as Updater<any>);
} }
@@ -48,17 +46,12 @@ export function handleDelete(lessonId: Lesson['id']) {
} }
} }
lessons.update((_lessons) => lessons.update((_lessons) => _lessons.filter((lesson) => lesson.id !== lessonId));
_lessons.filter((lesson) => lesson.id !== lessonId)
);
console.log(`lessonId`, lessonId); console.log(`lessonId`, lessonId);
}; };
} }
export async function handleSaveLesson( export async function handleSaveLesson(lesson: Lesson, course_id: Course['id']) {
lesson: Lesson,
course_id: Course['id']
) {
console.log(`handleSaveLesson lesson`, lesson); console.log(`handleSaveLesson lesson`, lesson);
const newLesson = { const newLesson = {
title: lesson.title, title: lesson.title,
@@ -66,10 +59,11 @@ export async function handleSaveLesson(
call_url: lesson.call_url, call_url: lesson.call_url,
teacher_id: lesson.profile ? lesson.profile.id : undefined, teacher_id: lesson.profile ? lesson.profile.id : undefined,
course_id, course_id,
is_complete: lesson.is_complete, is_unlocked: lesson.is_unlocked,
is_complete: lesson.is_complete
}; };
let updatedLesson: any[] | null = null; let newLessonData: any[] | null = null;
if (typeof lesson.id === 'string') { if (typeof lesson.id === 'string') {
// No need to get the result of update cause we have all in local state // No need to get the result of update cause we have all in local state
@@ -77,26 +71,25 @@ export async function handleSaveLesson(
} else { } else {
const { data } = await createLesson(newLesson); const { data } = await createLesson(newLesson);
updatedLesson = data; newLessonData = data;
} }
lessons.update((l) => lessons.update((l) =>
l.map((lesson) => { l.map((_lesson) => {
if (!lesson.id && updatedLesson) { if (!_lesson.id && newLessonData) {
lesson.id = updatedLesson[0].id; _lesson.id = newLessonData[0].id;
} else if (typeof lesson.id === 'string' && lesson.id === _lesson.id) {
return { ..._lesson, is_complete: lesson.is_complete };
} }
return lesson; return _lesson;
}) })
); );
} }
export async function handleUpdateLessonMaterials( export async function handleUpdateLessonMaterials(lesson: any, lessonId: Lesson['id']) {
lesson: any,
lessonId: Lesson['id']
) {
const materials = { const materials = {
...lesson.materials, ...lesson.materials
}; };
delete materials.totalExercises; delete materials.totalExercises;
@@ -15,20 +15,13 @@
</script> </script>
<div> <div>
<div <button
class="item relative flex items-center {isGroupActive && 'active'}" class="item relative flex items-center {isGroupActive && 'active'} w-full"
role="button"
tabindex="0" tabindex="0"
on:click={onClick} on:click={onClick}
> >
{#if !hideSortIcon}
<CaretSortDownIcon
size={20}
class="carbon-icon-sort {isExpanded ? '' : 'carbon-icon-minimize'}"
/>
{/if}
<span class="dark:text-white font-bold">{label}</span> <span class="dark:text-white font-bold">{label}</span>
</div> </button>
{#if isExpanded} {#if isExpanded}
<div class="flex flex-col"> <div class="flex flex-col">
<slot /> <slot />
@@ -4,8 +4,9 @@
import { page } from '$app/stores'; import { page } from '$app/stores';
import hotkeys from 'hotkeys-js'; import hotkeys from 'hotkeys-js';
import { browser } from '$app/environment'; import { browser } from '$app/environment';
import LockedIcon from 'carbon-icons-svelte/lib/Locked.svelte';
import HomeIcon from 'carbon-icons-svelte/lib/Home.svelte'; import HomeIcon from 'carbon-icons-svelte/lib/Home.svelte';
import CheckmarkFilledIcon from 'carbon-icons-svelte/lib/CheckmarkFilled.svelte'; import CheckmarkOutline from 'carbon-icons-svelte/lib/CheckmarkOutline.svelte';
import SettingsIcon from 'carbon-icons-svelte/lib/Settings.svelte'; import SettingsIcon from 'carbon-icons-svelte/lib/Settings.svelte';
import ChevronLeftIcon from 'carbon-icons-svelte/lib/ChevronLeft.svelte'; import ChevronLeftIcon from 'carbon-icons-svelte/lib/ChevronLeft.svelte';
import ChevronRightIcon from 'carbon-icons-svelte/lib/ChevronRight.svelte'; import ChevronRightIcon from 'carbon-icons-svelte/lib/ChevronRight.svelte';
@@ -13,12 +14,9 @@
import PageNav from '$lib/components/PageNav/index.svelte'; import PageNav from '$lib/components/PageNav/index.svelte';
import IconButton from '$lib/components/IconButton/index.svelte'; import IconButton from '$lib/components/IconButton/index.svelte';
import Avatar from '$lib/components/Avatar/index.svelte'; import Avatar from '$lib/components/Avatar/index.svelte';
import { import { getNavItemRoute, getLessonsRoute, getLectureNo } from '$lib/components/Course/function';
getNavItemRoute,
getLessonsRoute,
getLectureNo,
} from '$lib/components/Course/function';
import TextChip from '$lib/components/Chip/Text.svelte';
import RoleBasedSecurity from '$lib/components/RoleBasedSecurity/index.svelte'; import RoleBasedSecurity from '$lib/components/RoleBasedSecurity/index.svelte';
import Settings from '../Settings/index.svelte'; import Settings from '../Settings/index.svelte';
import { settingsDialog } from '../Settings/store'; import { settingsDialog } from '../Settings/store';
@@ -47,7 +45,7 @@
function getHref(item) { function getHref(item) {
return item.to; return item.to;
// return item.is_complete ? item.to : path || $page.url.pathname // return item.is_unlocked ? item.to : path || $page.url.pathname
} }
function handleSaveTitle() { function handleSaveTitle() {
@@ -89,18 +87,18 @@
{ {
label: 'Overview', label: 'Overview',
to: getNavItemRoute($course.id), to: getNavItemRoute($course.id),
hideSortIcon: true, hideSortIcon: true
}, },
{ {
label: 'Lessons', label: 'Lessons',
to: getLessonsRoute($course.id), to: getLessonsRoute($course.id),
hideSortIcon: false, hideSortIcon: false,
isLecture: true, isLecture: true
}, },
{ {
label: 'Attendance', label: 'Attendance',
to: getNavItemRoute($course.id, 'attendance'), to: getNavItemRoute($course.id, 'attendance'),
hideSortIcon: true, hideSortIcon: true
}, },
{ {
label: 'Submissions', label: 'Submissions',
@@ -108,12 +106,12 @@
hideSortIcon: true, hideSortIcon: true,
show() { show() {
return !isStudent; return !isStudent;
}, }
}, },
{ {
label: 'Marks', label: 'Marks',
to: getNavItemRoute($course.id, 'marks'), to: getNavItemRoute($course.id, 'marks'),
hideSortIcon: true, hideSortIcon: true
}, },
// { // {
// label: 'Scoreboard', // label: 'Scoreboard',
@@ -123,12 +121,12 @@
{ {
label: 'People', label: 'People',
to: getNavItemRoute($course.id, 'people'), to: getNavItemRoute($course.id, 'people'),
hideSortIcon: true, hideSortIcon: true
}, },
{ {
label: 'Certificates', label: 'Certificates',
to: getNavItemRoute($course.id, 'certificates'), to: getNavItemRoute($course.id, 'certificates'),
hideSortIcon: true, hideSortIcon: true
}, },
{ {
label: 'Landing Page', label: 'Landing Page',
@@ -136,17 +134,15 @@
hideSortIcon: true, hideSortIcon: true,
show() { show() {
return !isStudent; return !isStudent;
}, }
}, }
]; ];
} }
</script> </script>
<Settings /> <Settings />
<div <div class="root z-10 {!show && 'hide'} {$isMobile ? 'fixed shadow-xl' : 'sticky'}">
class="root z-10 {!show && 'hide'} {$isMobile ? 'fixed shadow-xl' : 'sticky'}"
>
{#if show} {#if show}
<div class="relative h-full dark:bg-gray-700"> <div class="relative h-full dark:bg-gray-700">
<PageNav bind:title={$course.title} paddingClass="pl-2"> <PageNav bind:title={$course.title} paddingClass="pl-2">
@@ -155,9 +151,7 @@
</slot:fragment> </slot:fragment>
<slot:fragment slot="widget"> <slot:fragment slot="widget">
<RoleBasedSecurity allowedRoles={[1, 2]}> <RoleBasedSecurity allowedRoles={[1, 2]}>
<IconButton <IconButton onClick={() => ($settingsDialog.open = !$settingsDialog.open)}>
onClick={() => ($settingsDialog.open = !$settingsDialog.open)}
>
<SettingsIcon size={20} class="carbon-icon" /> <SettingsIcon size={20} class="carbon-icon" />
</IconButton> </IconButton>
</RoleBasedSecurity> </RoleBasedSecurity>
@@ -175,13 +169,11 @@
{#if navItem.isLecture} {#if navItem.isLecture}
{#each $lessons as item, index} {#each $lessons as item, index}
<a <a
class="item flex items-center {( class="item flex items-center {(path || $page.url.pathname).includes(item.id) &&
path || $page.url.pathname 'active'} pl-7 py-3 {isStudent && !item.is_unlocked
).includes(item.id) && 'active'} pl-7 py-3 {isStudent &&
!item.is_complete
? 'cursor-not-allowed' ? 'cursor-not-allowed'
: ''}" : ''}"
href={isStudent && !item.is_complete href={isStudent && !item.is_unlocked
? $page.url.pathname ? $page.url.pathname
: getLessonsRoute($course.id, item.id)} : getLessonsRoute($course.id, item.id)}
on:click={() => { on:click={() => {
@@ -189,17 +181,23 @@
setTimeout(() => (show = false), 500); setTimeout(() => (show = false), 500);
} }
}} }}
aria-disabled={!item.is_unlocked}
> >
<span class="course-counter"> <TextChip
{getLectureNo(index + 1)} value={getLectureNo(index + 1)}
</span> className="bg-blue-200 text-xs mr-2"
size="sm"
shape="rounded-full"
/>
<span>{item.title}</span> <span>{item.title}</span>
{#if item.is_complete} {#if !item.is_unlocked}
<span class="ml-2 success"> <span class="text-md ml-2">
<CheckmarkFilledIcon size={20} class="carbon-icon" /> <LockedIcon class="carbon-icon" />
</span>
{:else if item.is_complete}
<span class="ml-2">
<CheckmarkOutline class="carbon-icon" />
</span> </span>
{:else}
<span class="text-md ml-2">🔒</span>
{/if} {/if}
</a> </a>
{/each} {/each}
@@ -209,9 +207,7 @@
{/each} {/each}
</div> </div>
<div <div class="w-full footer p-3 absolute bottom-2 flex items-center justify-between">
class="w-full footer p-3 absolute bottom-2 flex items-center justify-between"
>
<IconButton <IconButton
value="toggle" value="toggle"
onClick={() => goto(`${$currentOrgPath}/courses`)} onClick={() => goto(`${$currentOrgPath}/courses`)}
@@ -236,7 +232,7 @@
: { : {
title: 'Toggle sidebar', title: 'Toggle sidebar',
hotkeys: ['B'], hotkeys: ['B'],
direction: 'right', direction: 'right'
}} }}
> >
{#if show} {#if show}
+3 -5
View File
@@ -26,12 +26,12 @@ export function getLessonsRoute(courseId, lessonId) {
return `${path}/${lessonId}`; return `${path}/${lessonId}`;
} }
export function getLectureNo(index, initNo = '00') { export function getLectureNo(index, initNo = '0') {
if (index <= 9) { if (index <= 9) {
return `${initNo}${index}`; return `${initNo}${index}`;
} }
return `${initNo.replace('0', '')}${index}`; return index;
} }
export function formatAnswers(data) { export function formatAnswers(data) {
@@ -46,9 +46,7 @@ export function formatAnswers(data) {
const questionName = questionByIdAndName[answer.question_id]; const questionName = questionByIdAndName[answer.question_id];
answers[questionName] = answers[questionName] =
Array.isArray(answer.answers) && answer.answers.length Array.isArray(answer.answers) && answer.answers.length ? answer.answers : answer.open_answer;
? answer.answers
: answer.open_answer;
} }
return answers; return answers;
+1 -3
View File
@@ -8,9 +8,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 dark:bg-gray-800 flex items-center justify-between sticky right-0 w-full px-3">
class="head dark:bg-gray-800 flex items-center justify-between sticky right-0 w-full px-3"
>
<slot name="header" /> <slot name="header" />
</div> </div>
{/if} {/if}
+11 -18
View File
@@ -70,7 +70,7 @@ async function cloneGroupAndBasicCourse(
lessons: undefined, lessons: undefined,
id: undefined, id: undefined,
updated_at: undefined, updated_at: undefined,
created_at: undefined, created_at: undefined
}) })
.select(); .select();
@@ -85,34 +85,30 @@ async function cloneGroupAndBasicCourse(
profile_id: profileStoreValue.id, profile_id: profileStoreValue.id,
email: profileStoreValue.email, email: profileStoreValue.email,
group_id, group_id,
role_id: ROLE.TUTOR, role_id: ROLE.TUTOR
}); });
console.log('profileStoreValue', profileStoreValue); console.log('profileStoreValue', profileStoreValue);
return { return {
newCourse: newCourse?.[0], newCourse: newCourse?.[0]
}; };
} }
async function cloneLessons(lessons: Lesson[], courseId: Course['id']) { async function cloneLessons(lessons: Lesson[], courseId: Course['id']) {
const clonedLessons = lessons const clonedLessons = lessons
.sort( .sort((a, b) => new Date(a.created_at || '').getTime() - new Date(b.created_at || '').getTime())
(a, b) =>
new Date(a.created_at || '').getTime() -
new Date(b.created_at || '').getTime()
)
.map((lesson, index) => ({ .map((lesson, index) => ({
call_url: lesson.call_url, call_url: lesson.call_url,
course_id: courseId, course_id: courseId,
is_complete: lesson.is_complete, is_unlocked: lesson.is_unlocked,
lesson_at: new Date(), lesson_at: new Date(),
note: lesson.note, note: lesson.note,
order: lesson.order || index + 1, order: lesson.order || index + 1,
public: lesson.public, public: lesson.public,
slide_url: lesson.slide_url, slide_url: lesson.slide_url,
title: lesson.title, title: lesson.title,
video_url: lesson.video_url, video_url: lesson.video_url
})); }));
const { data } = await supabase.from('lesson').insert(clonedLessons).select(); const { data } = await supabase.from('lesson').insert(clonedLessons).select();
@@ -121,7 +117,7 @@ async function cloneLessons(lessons: Lesson[], courseId: Course['id']) {
return { return {
...dataItem, ...dataItem,
// @ts-ignore // @ts-ignore
exercise: lessons[dataIndex].exercise, exercise: lessons[dataIndex].exercise
}; };
}); });
@@ -147,7 +143,7 @@ async function cloneExercises(lessons: Lesson[]) {
description, description,
due_by: new Date(), due_by: new Date(),
lesson_id: lessonId, lesson_id: lessonId,
title, title
}) })
.select(); .select();
const newExercise = Array.isArray(data) ? data[0] : data; const newExercise = Array.isArray(data) ? data[0] : data;
@@ -166,7 +162,7 @@ async function cloneExercises(lessons: Lesson[]) {
points: question.points, points: question.points,
order: question.order, order: question.order,
question_type_id: question.question_type_id, question_type_id: question.question_type_id,
exercise_id: newExercise.id, exercise_id: newExercise.id
}) })
.select(); .select();
const newQuestion = Array.isArray(data) ? data[0] : data; const newQuestion = Array.isArray(data) ? data[0] : data;
@@ -179,7 +175,7 @@ async function cloneExercises(lessons: Lesson[]) {
value: option.value, value: option.value,
label: option.label, label: option.label,
is_correct: option.is_correct, is_correct: option.is_correct,
question_id: newQuestion.id, question_id: newQuestion.id
})); }));
// Insert[Batch] // Insert[Batch]
@@ -192,10 +188,7 @@ async function cloneExercises(lessons: Lesson[]) {
return true; return true;
} }
export async function cloneCourse( export async function cloneCourse(courseId: string, newTitle: string): Promise<Course> {
courseId: string,
newTitle: string
): Promise<Course> {
const { data: course, error } = await fetchCourseData(courseId); const { data: course, error } = await fetchCourseData(courseId);
console.log(`course`, course); console.log(`course`, course);
+32 -78
View File
@@ -1,18 +1,9 @@
import { supabase } from '$lib/utils/functions/supabase'; import { supabase } from '$lib/utils/functions/supabase';
import { isUUID } from '$lib/utils/functions/isUUID'; import { isUUID } from '$lib/utils/functions/isUUID';
import { QUESTION_TYPE } from '$lib/components/Question/constants'; import { QUESTION_TYPE } from '$lib/components/Question/constants';
import type { import type { Lesson, Course, Group, Groupmember, Exercise } from '$lib/utils/types';
Lesson,
Course,
Group,
Groupmember,
Exercise,
} from '$lib/utils/types';
export async function fetchCourse( export async function fetchCourse(courseId?: Course['id'], slug?: Course['slug']) {
courseId?: Course['id'],
slug?: Course['slug']
) {
let match: { slug?: string; id?: string } = {}; let match: { slug?: string; id?: string } = {};
if (slug) { if (slug) {
@@ -42,7 +33,7 @@ export async function fetchCourse(
is_certificate_downloadable, is_certificate_downloadable,
certificate_theme, certificate_theme,
lessons:lesson( lessons:lesson(
id, title,public, lesson_at, call_url, is_complete, order, created_at, id, title,public, lesson_at, call_url, is_unlocked, is_complete, order, created_at,
note, video_url, slide_url, call_url, totalExercises:exercise(count), note, video_url, slide_url, call_url, totalExercises:exercise(count),
profile:teacher_id(id, avatar_url, fullname) profile:teacher_id(id, avatar_url, fullname)
), ),
@@ -61,7 +52,7 @@ export async function fetchCourse(
return { return {
data, data,
error, error
}; };
} }
@@ -74,25 +65,22 @@ export async function fetchGroup(groupId: Group['id']) {
return { return {
data, data,
error, error
}; };
} }
export async function setProfileIdOfGroupMember( export async function setProfileIdOfGroupMember(email: string, profileId: string) {
email: string,
profileId: string
) {
const { data, error } = await supabase const { data, error } = await supabase
.from('groupmember') .from('groupmember')
.update({ .update({
email: null, email: null,
profile_id: profileId, profile_id: profileId
}) })
.match({ email: email }); .match({ email: email });
return { return {
data, data,
error, error
}; };
} }
@@ -100,17 +88,13 @@ export async function uploadAvatar(courseId: string, avatar: string) {
const filename = `course/${courseId + Date.now()}.webp`; const filename = `course/${courseId + Date.now()}.webp`;
let logo; let logo;
const { data } = await supabase.storage const { data } = await supabase.storage.from('avatars').upload(filename, avatar, {
.from('avatars') cacheControl: '3600',
.upload(filename, avatar, { upsert: false
cacheControl: '3600', });
upsert: false,
});
if (data && data.Key) { if (data && data.Key) {
const { publicURL } = supabase.storage const { publicURL } = supabase.storage.from('avatars').getPublicUrl(filename);
.from('avatars')
.getPublicUrl(filename);
if (!publicURL) return; if (!publicURL) return;
@@ -128,17 +112,13 @@ export async function updateCourse(
if (avatar) { if (avatar) {
const filename = `course/${courseId + Date.now()}.webp`; const filename = `course/${courseId + Date.now()}.webp`;
const { data } = await supabase.storage const { data } = await supabase.storage.from('avatars').upload(filename, avatar, {
.from('avatars') cacheControl: '3600',
.upload(filename, avatar, { upsert: false
cacheControl: '3600', });
upsert: false,
});
if (data && data.Key) { if (data && data.Key) {
const { publicURL } = supabase.storage const { publicURL } = supabase.storage.from('avatars').getPublicUrl(filename);
.from('avatars')
.getPublicUrl(filename);
if (!publicURL) return; if (!publicURL) return;
@@ -170,9 +150,7 @@ export function deleteGroupMember(groupMemberId: Groupmember['id']) {
export function fetchLesson(lessonId: Lesson['id']) { export function fetchLesson(lessonId: Lesson['id']) {
return supabase return supabase
.from('lesson') .from('lesson')
.select( .select(`id, note, video_url, slide_url, call_url, totalExercises:exercise(count)`)
`id, note, video_url, slide_url, call_url, totalExercises:exercise(count)`
)
.eq('id', lessonId) .eq('id', lessonId)
.single(); .single();
} }
@@ -206,10 +184,7 @@ function isNew(item: any) {
} }
// TODO: Add questionnaire type // TODO: Add questionnaire type
export async function upsertExercise( export async function upsertExercise(questionnaire: any, exerciseId: Exercise['id']) {
questionnaire: any,
exerciseId: Exercise['id']
) {
const { const {
questions, questions,
title, title,
@@ -217,7 +192,7 @@ export async function upsertExercise(
due_by, due_by,
is_title_dirty, is_title_dirty,
is_description_dirty, is_description_dirty,
is_due_by_dirty, is_due_by_dirty
} = questionnaire; } = questionnaire;
if (is_description_dirty || is_title_dirty || is_due_by_dirty) { if (is_description_dirty || is_title_dirty || is_due_by_dirty) {
@@ -226,7 +201,7 @@ export async function upsertExercise(
.update({ .update({
title, title,
description, description,
due_by, due_by
}) })
.match({ id: exerciseId }); .match({ id: exerciseId });
} }
@@ -234,31 +209,16 @@ export async function upsertExercise(
const updatedQuestions = []; const updatedQuestions = [];
for (const question of questions) { for (const question of questions) {
const { const { title, id, name, question_type, options, deleted_at, order, points, is_dirty } =
title, question;
id,
name,
question_type,
options,
deleted_at,
order,
points,
is_dirty,
} = question;
// DELETE /delete/:questionId - Don't delete if answer already given // DELETE /delete/:questionId - Don't delete if answer already given
if (deleted_at) { if (deleted_at) {
// Delete from server only if this question exists in the database // Delete from server only if this question exists in the database
if (!isNew(id)) { if (!isNew(id)) {
await supabase.from('option').delete().match({ question_id: id }); await supabase.from('option').delete().match({ question_id: id });
await supabase await supabase.from('question_answer').delete().match({ question_id: id });
.from('question_answer') const { error } = await supabase.from('question').delete().match({ id });
.delete()
.match({ question_id: id });
const { error } = await supabase
.from('question')
.delete()
.match({ id });
if (error) { if (error) {
console.error('Cannot delete this question', error); console.error('Cannot delete this question', error);
@@ -278,7 +238,7 @@ export async function upsertExercise(
points, points,
order, order,
question_type_id: question_type.id, question_type_id: question_type.id,
exercise_id: exerciseId, exercise_id: exerciseId
}; };
let questionSupabaseRes; let questionSupabaseRes;
@@ -329,7 +289,7 @@ export async function upsertExercise(
is_dirty: undefined, is_dirty: undefined,
id: isNew(option.id) ? undefined : option.id, id: isNew(option.id) ? undefined : option.id,
value: isUUID(option.value) ? option.value : undefined, // this value is of UUID type value: isUUID(option.value) ? option.value : undefined, // this value is of UUID type
question_id: newQuestion.id, question_id: newQuestion.id
}; };
if (is_dirty || isNew(option.id)) { if (is_dirty || isNew(option.id)) {
@@ -378,7 +338,7 @@ export async function submitExercise(
.insert({ .insert({
submitted_by: groupMemberId, submitted_by: groupMemberId,
exercise_id: exerciseId, exercise_id: exerciseId,
course_id: courseId, course_id: courseId
}) })
.select(); .select();
@@ -390,7 +350,7 @@ export async function submitExercise(
question_id: questionsByName[questionName], question_id: questionsByName[questionName],
open_answer: '', open_answer: '',
answers: [], answers: [],
submission_id: Array.isArray(submission) ? submission[0].id : null, submission_id: Array.isArray(submission) ? submission[0].id : null
}; };
if (typeof value === 'string') { if (typeof value === 'string') {
@@ -402,17 +362,11 @@ export async function submitExercise(
questionAnswers.push(questionAnswer); questionAnswers.push(questionAnswer);
} }
const res = await supabase const res = await supabase.from('question_answer').insert(questionAnswers).select();
.from('question_answer')
.insert(questionAnswers)
.select();
console.log(`res`, res); console.log(`res`, res);
} }
export async function deleteExercise( export async function deleteExercise(questions: Array<{ id: string }>, exerciseId: Exercise['id']) {
questions: Array<{ id: string }>,
exerciseId: Exercise['id']
) {
for (const question of questions) { for (const question of questions) {
const { id } = question; const { id } = question;
+1 -1
View File
@@ -5,7 +5,7 @@ export type UserLessonDataType = {
lesson_at: string; lesson_at: string;
lesson_id: string; lesson_id: string;
lesson_title: string; lesson_title: string;
is_complete: boolean; is_unlocked: boolean;
}; };
type LessonByDayType = { type LessonByDayType = {
+2 -1
View File
@@ -141,13 +141,14 @@ export interface Lesson {
video_url?: any; // type unknown; video_url?: any; // type unknown;
slide_url?: any; // type unknown; slide_url?: any; // type unknown;
course_id: string /* foreign key to course.id */; course_id: string /* foreign key to course.id */;
id: string /* primary key */; id?: string /* primary key */;
created_at?: string; created_at?: string;
updated_at?: string; updated_at?: string;
title: any; // type unknown; title: any; // type unknown;
public?: boolean; public?: boolean;
lesson_at?: string; lesson_at?: string;
teacher_id?: string /* foreign key to profile.id */; teacher_id?: string /* foreign key to profile.id */;
is_unlocked?: boolean;
is_complete?: boolean; is_complete?: boolean;
call_url?: string; call_url?: string;
order?: number; order?: number;
+3 -2
View File
@@ -181,8 +181,9 @@
$appStore.isStudentDomain = debug || answer; $appStore.isStudentDomain = debug || answer;
$appStore.siteNameFromDomain = debug ? 'codingdojo' : subdomain; $appStore.siteNameFromDomain = debug ? 'codingdojo' : subdomain;
} else if (subdomain === 'play' || debug) {
skipAuth = true;
} }
skipAuth = debug || subdomain === 'play';
} }
} }
@@ -236,7 +237,7 @@
if (event === 'SIGNED_IN') { if (event === 'SIGNED_IN') {
$user.fetchingUser = true; $user.fetchingUser = true;
getProfile(); getProfile();
} else { } else if (!['INITIAL_SESSION', 'TOKEN_REFRESHED'].includes(event)) {
console.log('not logged in, go to login'); console.log('not logged in, go to login');
return goto('/login'); return goto('/login');
} }
+9 -8
View File
@@ -2,12 +2,13 @@
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { page } from '$app/stores'; import { page } from '$app/stores';
import { dndzone } from 'svelte-dnd-action'; import { dndzone } from 'svelte-dnd-action';
import UnlockedIcon from 'carbon-icons-svelte/lib/Unlocked.svelte';
import LockedIcon from 'carbon-icons-svelte/lib/Locked.svelte';
import TimeIcon from 'carbon-icons-svelte/lib/Time.svelte'; import TimeIcon from 'carbon-icons-svelte/lib/Time.svelte';
import EditIcon from 'carbon-icons-svelte/lib/Edit.svelte'; import EditIcon from 'carbon-icons-svelte/lib/Edit.svelte';
import SaveIcon from 'carbon-icons-svelte/lib/Save.svelte'; import SaveIcon from 'carbon-icons-svelte/lib/Save.svelte';
import PhoneIcon from 'carbon-icons-svelte/lib/Phone.svelte'; import PhoneIcon from 'carbon-icons-svelte/lib/Phone.svelte';
import TrashCanIcon from 'carbon-icons-svelte/lib/TrashCan.svelte'; import TrashCanIcon from 'carbon-icons-svelte/lib/TrashCan.svelte';
import CheckmarkFilled24 from 'carbon-icons-svelte/lib/CheckmarkFilled.svelte';
import PageNav from '$lib/components/PageNav/index.svelte'; import PageNav from '$lib/components/PageNav/index.svelte';
import RoleBasedSecurity from '$lib/components/RoleBasedSecurity/index.svelte'; import RoleBasedSecurity from '$lib/components/RoleBasedSecurity/index.svelte';
import Box from '$lib/components/Box/index.svelte'; import Box from '$lib/components/Box/index.svelte';
@@ -137,20 +138,20 @@
<IconButton <IconButton
disabled={isStudent} disabled={isStudent}
onClick={() => { onClick={() => {
lesson.is_complete = !lesson.is_complete; lesson.is_unlocked = !lesson.is_unlocked;
handleSaveLesson(lesson, $course.id); handleSaveLesson(lesson, $course.id);
}} }}
toolTipProps={isStudent toolTipProps={isStudent
? {} ? {}
: { : {
title: `Click to ${lesson.is_complete ? 'lock' : 'unlock'}`, title: `Click to ${lesson.is_unlocked ? 'lock' : 'unlock'}`,
direction: 'right' direction: 'right'
}} }}
> >
{#if lesson.is_complete} {#if lesson.is_unlocked}
<CheckmarkFilled24 class="carbon-icon" /> <UnlockedIcon size={24} class="carbon-icon" />
{:else} {:else}
<span class="text-2xl">🔒</span> <LockedIcon size={24} class="carbon-icon" />
{/if} {/if}
</IconButton> </IconButton>
</div> </div>
@@ -186,11 +187,11 @@
{:else} {:else}
<h3 class="dark:text-white text-2xl m-0 flex items-center"> <h3 class="dark:text-white text-2xl m-0 flex items-center">
<a <a
href={isStudent && !lesson.is_complete href={isStudent && !lesson.is_unlocked
? $page.url.pathname ? $page.url.pathname
: '/courses/' + $course.id + '/lessons/' + lesson.id} : '/courses/' + $course.id + '/lessons/' + lesson.id}
class="dark:text-white hover:underline text-black {isStudent && class="dark:text-white hover:underline text-black {isStudent &&
!lesson.is_complete !lesson.is_unlocked
? 'cursor-not-allowed' ? 'cursor-not-allowed'
: ''}" : ''}"
> >
@@ -2,6 +2,7 @@
import { page } from '$app/stores'; import { page } from '$app/stores';
import CourseContainer from '$lib/components/CourseContainer/index.svelte'; import CourseContainer from '$lib/components/CourseContainer/index.svelte';
import { fetchCourse, fetchLesson } from '$lib/utils/services/courses'; import { fetchCourse, fetchLesson } from '$lib/utils/services/courses';
import CheckmarkOutlineIcon from 'carbon-icons-svelte/lib/CheckmarkOutline.svelte';
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte'; import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
import RoleBasedSecurity from '$lib/components/RoleBasedSecurity/index.svelte'; import RoleBasedSecurity from '$lib/components/RoleBasedSecurity/index.svelte';
import PageNav from '$lib/components/PageNav/index.svelte'; import PageNav from '$lib/components/PageNav/index.svelte';
@@ -10,7 +11,7 @@
import Exercises from '$lib/components/Course/components/Lesson/Exercises/index.svelte'; import Exercises from '$lib/components/Course/components/Lesson/Exercises/index.svelte';
import MODES from '$lib/utils/constants/mode.js'; import MODES from '$lib/utils/constants/mode.js';
import { setCourse, course } from '$lib/components/Course/store'; import { setCourse, course } from '$lib/components/Course/store';
import { lesson } from '$lib/components/Course/components/Lesson/store/lessons'; import { lesson, handleSaveLesson } from '$lib/components/Course/components/Lesson/store/lessons';
export let data; export let data;
@@ -19,6 +20,7 @@
let prevMode = ''; let prevMode = '';
let prevLessonId = ''; let prevLessonId = '';
let isFetching = false; let isFetching = false;
let isMarkingComplete = false;
async function fetchReqData(lessonId = '') { async function fetchReqData(lessonId = '') {
isFetching = true; isFetching = true;
@@ -41,6 +43,13 @@
isFetching = false; isFetching = false;
} }
async function markLessonComplete() {
isMarkingComplete = true;
$lesson.is_complete = !$lesson.is_complete;
await handleSaveLesson($lesson, $course.id);
isMarkingComplete = false;
}
function setLesson(lessonData, totalExercises) { function setLesson(lessonData, totalExercises) {
if (!lessonData) return; if (!lessonData) return;
@@ -99,6 +108,18 @@
{:else if !!data.lessonId} {:else if !!data.lessonId}
<PageBody> <PageBody>
<Materials lessonId={data.lessonId} {mode} {prevMode} /> <Materials lessonId={data.lessonId} {mode} {prevMode} />
<div class="w-full flex flex-row-reverse">
<PrimaryButton
onClick={markLessonComplete}
isLoading={isMarkingComplete}
isDisabled={isMarkingComplete}
className="mt-10"
>
<CheckmarkOutlineIcon size={24} class="carbon-icon mr-2" />
Mark as {$lesson.is_complete ? 'Incomplete' : 'Complete'}
</PrimaryButton>
</div>
</PageBody> </PageBody>
{/if} {/if}
</CourseContainer> </CourseContainer>
+14 -34
View File
@@ -3,23 +3,17 @@
import { InlineCalendar, Datepicker } from 'svelte-calendar'; import { InlineCalendar, Datepicker } from 'svelte-calendar';
import CheckmarkFilledIcon from 'carbon-icons-svelte/lib/CheckmarkFilled.svelte'; import CheckmarkFilledIcon from 'carbon-icons-svelte/lib/CheckmarkFilled.svelte';
import { import { formatUserUpcomingData, formatDate } from '$lib/utils/functions/routes/dashboard';
formatUserUpcomingData,
formatDate,
} from '$lib/utils/functions/routes/dashboard';
import { user } from '$lib/utils/store/user'; import { user } from '$lib/utils/store/user';
import { fetchUserUpcomingData } from '$lib/utils/services/dashboard'; import { fetchUserUpcomingData } from '$lib/utils/services/dashboard';
import { isMobile } from '$lib/utils/store/useMobile'; import { isMobile } from '$lib/utils/store/useMobile';
import type { import type { UserLessonDataType, LessonsByMonthIndexType } from '$lib/utils/types';
UserLessonDataType,
LessonsByMonthIndexType,
} from '$lib/utils/types';
const theme = { const theme = {
calendar: { calendar: {
width: '400px', width: '400px',
shadow: '0px 0px 5px rgba(0, 0, 0, 0.25)', shadow: '0px 0px 5px rgba(0, 0, 0, 0.25)'
}, }
}; };
let store; let store;
@@ -37,8 +31,7 @@
// ); // );
const renderedMonths = const renderedMonths =
calendar?.querySelectorAll('.contents .container .stage .grid .grid') || calendar?.querySelectorAll('.contents .container .stage .grid .grid') || [];
[];
// console.log(`renderedMonths`, renderedMonths); // console.log(`renderedMonths`, renderedMonths);
const currentMonthIndex = new Date($store?.selected).getMonth(); const currentMonthIndex = new Date($store?.selected).getMonth();
const currentMonth = renderedMonths[currentMonthIndexInRenderedMonth]; const currentMonth = renderedMonths[currentMonthIndexInRenderedMonth];
@@ -63,10 +56,7 @@
if (!!currentMonthData[dateIndex]) { if (!!currentMonthData[dateIndex]) {
// console.log(`dateIndex`, dateIndex); // console.log(`dateIndex`, dateIndex);
// console.log(`lesson data`, currentMonthData[dateIndex]); // console.log(`lesson data`, currentMonthData[dateIndex]);
dayElement.insertAdjacentHTML( dayElement.insertAdjacentHTML('beforeend', '<span class="active-day">•</span>');
'beforeend',
'<span class="active-day">•</span>'
);
} }
} }
}, 500); }, 500);
@@ -84,9 +74,7 @@
} }
} }
function getDataOfSelectedDate( function getDataOfSelectedDate(_selectedDate: string): UserLessonDataType[] | [] {
_selectedDate: string
): UserLessonDataType[] | [] {
if (!_selectedDate) return []; if (!_selectedDate) return [];
const selectedDate = new Date(_selectedDate).toUTCString(); const selectedDate = new Date(_selectedDate).toUTCString();
@@ -97,8 +85,7 @@
// If we changed the month, update the calendar dots // If we changed the month, update the calendar dots
const prevMonthIndex = new Date(prevSelectedDate).getMonth(); const prevMonthIndex = new Date(prevSelectedDate).getMonth();
if (prevMonthIndex !== monthIndex) { if (prevMonthIndex !== monthIndex) {
const currentMonthIndexInRenderedMonth = const currentMonthIndexInRenderedMonth = monthIndex < prevMonthIndex ? 1 : 2;
monthIndex < prevMonthIndex ? 1 : 2;
addDotsToCalendar(currentMonthIndexInRenderedMonth); addDotsToCalendar(currentMonthIndexInRenderedMonth);
} }
@@ -163,9 +150,7 @@
</Datepicker> </Datepicker>
</div> </div>
{:else} {:else}
<div <div class="header text-lg font-bold text-blue-700 flex px-5 items-center">
class="header text-lg font-bold text-blue-700 flex px-5 items-center"
>
{formatDate($store?.selected)} {formatDate($store?.selected)}
</div> </div>
{/if} {/if}
@@ -176,11 +161,11 @@
<a <a
class="text-black-700 text-lg font-bold flex items-center" class="text-black-700 text-lg font-bold flex items-center"
href={`/courses/${lessonData.course_id}/lessons/${ href={`/courses/${lessonData.course_id}/lessons/${
lessonData.is_complete ? lessonData.lesson_id : '' lessonData.is_unlocked ? lessonData.lesson_id : ''
}`} }`}
> >
{lessonData.lesson_title} {lessonData.lesson_title}
{#if lessonData.is_complete} {#if lessonData.is_unlocked}
<span class="ml-2 success"> <span class="ml-2 success">
<CheckmarkFilledIcon size={20} class="carbon-icon" /> <CheckmarkFilledIcon size={20} class="carbon-icon" />
</span> </span>
@@ -189,15 +174,12 @@
{/if} {/if}
</a> </a>
<p class="dark:text-white text-grey text-sm flex items-center"> <p class="dark:text-white text-grey text-sm flex items-center">
<a <a class="underline text-blue-700 my-2" href="/courses/{lessonData.course_id}">
class="underline text-blue-700 my-2"
href="/courses/{lessonData.course_id}"
>
{` ${lessonData.course_title}`} {` ${lessonData.course_title}`}
</a> </a>
</p> </p>
</div> </div>
{#if lessonData.is_complete} {#if lessonData.is_unlocked}
<a <a
class="join-call rounded-3xl bg-blue-600 text-white py-3 font-bold shadow-lg {!lessonData.call_url && class="join-call rounded-3xl bg-blue-600 text-white py-3 font-bold shadow-lg {!lessonData.call_url &&
'opacity-50 pointer-events-none cursor-not-allowed'}" 'opacity-50 pointer-events-none cursor-not-allowed'}"
@@ -212,9 +194,7 @@
{/if} {/if}
</div> </div>
{:else} {:else}
<p <p class="dark:text-white flex items-center justify-center w-full no-data">
class="dark:text-white flex items-center justify-center w-full no-data"
>
No lesson on this day No lesson on this day
</p> </p>
{/each} {/each}
+28 -47
View File
@@ -3,20 +3,15 @@
// import { supabase } from '$lib/utils/functions/supabase'; // import { supabase } from '$lib/utils/functions/supabase';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte'; import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
import UnlockedIcon from 'carbon-icons-svelte/lib/Unlocked.svelte';
import LockedIcon from 'carbon-icons-svelte/lib/Locked.svelte';
import Avatar from '$lib/components/Avatar/index.svelte'; import Avatar from '$lib/components/Avatar/index.svelte';
import { InlineCalendar, Datepicker } from 'svelte-calendar'; import { InlineCalendar, Datepicker } from 'svelte-calendar';
import CheckmarkFilledIcon from 'carbon-icons-svelte/lib/CheckmarkFilled.svelte'; import { formatUserUpcomingData, formatDate } from '$lib/utils/functions/routes/dashboard';
import {
formatUserUpcomingData,
formatDate,
} from '$lib/utils/functions/routes/dashboard';
import { user } from '$lib/utils/store/user'; import { user } from '$lib/utils/store/user';
import { fetchUserUpcomingData } from '$lib/utils/services/dashboard'; import { fetchUserUpcomingData } from '$lib/utils/services/dashboard';
import { isMobile } from '$lib/utils/store/useMobile'; import { isMobile } from '$lib/utils/store/useMobile';
import type { import type { UserLessonDataType, LessonsByMonthIndexType } from '$lib/utils/types';
UserLessonDataType,
LessonsByMonthIndexType,
} from '$lib/utils/types';
import WelcomeModal from '$lib/components/WelcomeModal/WelcomeModal.svelte'; import WelcomeModal from '$lib/components/WelcomeModal/WelcomeModal.svelte';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
@@ -31,8 +26,8 @@
calendar: { calendar: {
width: '300px', width: '300px',
height: '300px', height: '300px',
shadow: '0px 0px 5px rgba(0, 0, 0, 0.25)', shadow: '0px 0px 5px rgba(0, 0, 0, 0.25)'
}, }
}; };
let store; let store;
@@ -44,20 +39,20 @@
const boxes = [ const boxes = [
{ {
label: 'Revenue (NGN)', label: 'Revenue (NGN)',
value: '0.00', value: '0.00'
}, },
{ {
label: 'Number of sales', label: 'Number of sales',
value: 0, value: 0
}, },
{ {
label: 'Number of courses', label: 'Number of courses',
value: 0, value: 0
}, },
{ {
label: 'Total students', label: 'Total students',
value: 0, value: 0
}, }
]; ];
const activities = [ const activities = [
{ {
@@ -66,7 +61,7 @@
name: 'Nnancy Okoye', name: 'Nnancy Okoye',
time: '1 hour ago', time: '1 hour ago',
description: 'Submitted an assignment for lesson Figma prototyping', description: 'Submitted an assignment for lesson Figma prototyping',
link: '/', link: '/'
}, },
{ {
avatar: avatar:
@@ -74,7 +69,7 @@
name: 'Bro Shagi', name: 'Bro Shagi',
time: '2 days ago', time: '2 days ago',
description: 'Submitted an assignment for lesson Figma prototyping', description: 'Submitted an assignment for lesson Figma prototyping',
link: '/', link: '/'
}, },
{ {
avatar: avatar:
@@ -82,7 +77,7 @@
name: 'Bill Gates', name: 'Bill Gates',
time: '13 hours ago', time: '13 hours ago',
description: 'Submitted an assignment for lesson Figma prototyping', description: 'Submitted an assignment for lesson Figma prototyping',
link: '/', link: '/'
}, },
{ {
avatar: avatar:
@@ -90,8 +85,8 @@
name: 'Steve Jobs', name: 'Steve Jobs',
time: 'yesterday', time: 'yesterday',
description: 'Submitted an assignment for lesson Figma prototyping', description: 'Submitted an assignment for lesson Figma prototyping',
link: '/', link: '/'
}, }
]; ];
const addDotsToCalendar = (currentMonthIndexInRenderedMonth: number) => const addDotsToCalendar = (currentMonthIndexInRenderedMonth: number) =>
@@ -103,8 +98,7 @@
// ); // );
const renderedMonths = const renderedMonths =
calendar?.querySelectorAll('.contents .container .stage .grid .grid') || calendar?.querySelectorAll('.contents .container .stage .grid .grid') || [];
[];
// console.log(`renderedMonths`, renderedMonths); // console.log(`renderedMonths`, renderedMonths);
const currentMonthIndex = new Date($store?.selected).getMonth(); const currentMonthIndex = new Date($store?.selected).getMonth();
const currentMonth = renderedMonths[currentMonthIndexInRenderedMonth]; const currentMonth = renderedMonths[currentMonthIndexInRenderedMonth];
@@ -129,10 +123,7 @@
if (!!currentMonthData[dateIndex]) { if (!!currentMonthData[dateIndex]) {
// console.log(`dateIndex`, dateIndex); // console.log(`dateIndex`, dateIndex);
// console.log(`lesson data`, currentMonthData[dateIndex]); // console.log(`lesson data`, currentMonthData[dateIndex]);
dayElement.insertAdjacentHTML( dayElement.insertAdjacentHTML('beforeend', '<span class="active-day">•</span>');
'beforeend',
'<span class="active-day">•</span>'
);
} }
} }
}, 500); }, 500);
@@ -150,9 +141,7 @@
} }
} }
function getDataOfSelectedDate( function getDataOfSelectedDate(_selectedDate: string): UserLessonDataType[] | [] {
_selectedDate: string
): UserLessonDataType[] | [] {
if (!_selectedDate) return []; if (!_selectedDate) return [];
const selectedDate = new Date(_selectedDate).toUTCString(); const selectedDate = new Date(_selectedDate).toUTCString();
@@ -163,8 +152,7 @@
// If we changed the month, update the calendar dots // If we changed the month, update the calendar dots
const prevMonthIndex = new Date(prevSelectedDate).getMonth(); const prevMonthIndex = new Date(prevSelectedDate).getMonth();
if (prevMonthIndex !== monthIndex) { if (prevMonthIndex !== monthIndex) {
const currentMonthIndexInRenderedMonth = const currentMonthIndexInRenderedMonth = monthIndex < prevMonthIndex ? 1 : 2;
monthIndex < prevMonthIndex ? 1 : 2;
addDotsToCalendar(currentMonthIndexInRenderedMonth); addDotsToCalendar(currentMonthIndexInRenderedMonth);
} }
@@ -212,9 +200,7 @@
<div class="flex items-start flex-wrap"> <div class="flex items-start flex-wrap">
{#each boxes as box} {#each boxes as box}
<div <div class="box flex flex-col rounded border border-gray-200 justify-center px-5 mr-5 mb-5">
class="box flex flex-col rounded border border-gray-200 justify-center px-5 mr-5 mb-5"
>
<p class="dark:text-white mb-2 text-sm">{box.label}</p> <p class="dark:text-white mb-2 text-sm">{box.label}</p>
<p class="dark:text-white text-xl font-bold">{box.value}</p> <p class="dark:text-white text-xl font-bold">{box.value}</p>
</div> </div>
@@ -259,28 +245,25 @@
<a <a
class="text-black-700 text-lg font-bold flex items-center" class="text-black-700 text-lg font-bold flex items-center"
href={`/courses/${lessonData.course_id}/lessons/${ href={`/courses/${lessonData.course_id}/lessons/${
lessonData.is_complete ? lessonData.lesson_id : '' lessonData.is_unlocked ? lessonData.lesson_id : ''
}`} }`}
> >
{lessonData.lesson_title} {lessonData.lesson_title}
{#if lessonData.is_complete} {#if lessonData.is_unlocked}
<span class="ml-2 success"> <span class="ml-2 success">
<CheckmarkFilledIcon size={20} class="carbon-icon" /> <LockedIcon class="carbon-icon" />
</span> </span>
{:else} {:else}
<span class="text-md ml-2">🔒</span> <UnlockedIcon class="carbon-icon" />
{/if} {/if}
</a> </a>
<p class="dark:text-white text-grey text-sm flex items-center"> <p class="dark:text-white text-grey text-sm flex items-center">
<a <a class="underline text-blue-700 my-2" href="/courses/{lessonData.course_id}">
class="underline text-blue-700 my-2"
href="/courses/{lessonData.course_id}"
>
{` ${lessonData.course_title}`} {` ${lessonData.course_title}`}
</a> </a>
</p> </p>
</div> </div>
{#if lessonData.is_complete} {#if lessonData.is_unlocked}
<a <a
class="join-call rounded-3xl bg-blue-600 text-white py-3 font-bold shadow-lg {!lessonData.call_url && class="join-call rounded-3xl bg-blue-600 text-white py-3 font-bold shadow-lg {!lessonData.call_url &&
'opacity-50 pointer-events-none cursor-not-allowed'}" 'opacity-50 pointer-events-none cursor-not-allowed'}"
@@ -295,9 +278,7 @@
{/if} {/if}
</div> </div>
{:else} {:else}
<p <p class="dark:text-white flex items-center justify-center w-full no-data">
class="dark:text-white flex items-center justify-center w-full no-data"
>
No lesson on this day No lesson on this day
</p> </p>
{/each} {/each}