CLASSROOMIO-T-88: Mark lesson as complete
This commit is contained in:
@@ -2,14 +2,15 @@
|
||||
export let className = '';
|
||||
export let value = '';
|
||||
export let size = '';
|
||||
export let shape = 'rounded-md';
|
||||
</script>
|
||||
|
||||
<p
|
||||
class="{className} {size === 'sm'
|
||||
? 'py-1 px-1'
|
||||
: 'py-2 px-2'} org-avatar {className.includes('bg-')
|
||||
class="{className} {size === 'sm' ? 'py-1 px-1' : 'py-2 px-2'} org-avatar {className.includes(
|
||||
'bg-'
|
||||
)
|
||||
? ''
|
||||
: 'bg-gray-200'} rounded-md text-center"
|
||||
: 'bg-gray-200'} {shape} text-center"
|
||||
>
|
||||
{value}
|
||||
</p>
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import { writable } from 'svelte/store';
|
||||
import type { Writable, Updater } from 'svelte/store';
|
||||
import {
|
||||
createLesson,
|
||||
updateLesson,
|
||||
deleteLesson,
|
||||
} from '$lib/utils/services/courses';
|
||||
import { createLesson, updateLesson, deleteLesson } from '$lib/utils/services/courses';
|
||||
|
||||
import type { Lesson, Course } from '$lib/utils/types';
|
||||
|
||||
@@ -12,12 +8,13 @@ export const lessons: Writable<Lesson[]> = writable([]);
|
||||
export const lesson = writable({
|
||||
id: null,
|
||||
totalExercises: 0,
|
||||
is_complete: false,
|
||||
materials: {
|
||||
note: null,
|
||||
slide_url: null,
|
||||
video_url: null,
|
||||
video_url: null
|
||||
},
|
||||
exercises: [],
|
||||
exercises: []
|
||||
});
|
||||
export const isLessonDirty = writable(false);
|
||||
|
||||
@@ -31,8 +28,9 @@ export function handleAddLesson() {
|
||||
// profile: undefined,
|
||||
call_url: undefined,
|
||||
lesson_at: new Date(),
|
||||
is_complete: false,
|
||||
},
|
||||
is_unlocked: false,
|
||||
is_complete: false
|
||||
}
|
||||
];
|
||||
}) as Updater<any>);
|
||||
}
|
||||
@@ -48,17 +46,12 @@ export function handleDelete(lessonId: Lesson['id']) {
|
||||
}
|
||||
}
|
||||
|
||||
lessons.update((_lessons) =>
|
||||
_lessons.filter((lesson) => lesson.id !== lessonId)
|
||||
);
|
||||
lessons.update((_lessons) => _lessons.filter((lesson) => lesson.id !== lessonId));
|
||||
console.log(`lessonId`, lessonId);
|
||||
};
|
||||
}
|
||||
|
||||
export async function handleSaveLesson(
|
||||
lesson: Lesson,
|
||||
course_id: Course['id']
|
||||
) {
|
||||
export async function handleSaveLesson(lesson: Lesson, course_id: Course['id']) {
|
||||
console.log(`handleSaveLesson lesson`, lesson);
|
||||
const newLesson = {
|
||||
title: lesson.title,
|
||||
@@ -66,10 +59,11 @@ export async function handleSaveLesson(
|
||||
call_url: lesson.call_url,
|
||||
teacher_id: lesson.profile ? lesson.profile.id : undefined,
|
||||
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') {
|
||||
// No need to get the result of update cause we have all in local state
|
||||
@@ -77,26 +71,25 @@ export async function handleSaveLesson(
|
||||
} else {
|
||||
const { data } = await createLesson(newLesson);
|
||||
|
||||
updatedLesson = data;
|
||||
newLessonData = data;
|
||||
}
|
||||
|
||||
lessons.update((l) =>
|
||||
l.map((lesson) => {
|
||||
if (!lesson.id && updatedLesson) {
|
||||
lesson.id = updatedLesson[0].id;
|
||||
l.map((_lesson) => {
|
||||
if (!_lesson.id && newLessonData) {
|
||||
_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(
|
||||
lesson: any,
|
||||
lessonId: Lesson['id']
|
||||
) {
|
||||
export async function handleUpdateLessonMaterials(lesson: any, lessonId: Lesson['id']) {
|
||||
const materials = {
|
||||
...lesson.materials,
|
||||
...lesson.materials
|
||||
};
|
||||
|
||||
delete materials.totalExercises;
|
||||
|
||||
@@ -15,20 +15,13 @@
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<div
|
||||
class="item relative flex items-center {isGroupActive && 'active'}"
|
||||
role="button"
|
||||
<button
|
||||
class="item relative flex items-center {isGroupActive && 'active'} w-full"
|
||||
tabindex="0"
|
||||
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>
|
||||
</div>
|
||||
</button>
|
||||
{#if isExpanded}
|
||||
<div class="flex flex-col">
|
||||
<slot />
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
import { page } from '$app/stores';
|
||||
import hotkeys from 'hotkeys-js';
|
||||
import { browser } from '$app/environment';
|
||||
import LockedIcon from 'carbon-icons-svelte/lib/Locked.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 ChevronLeftIcon from 'carbon-icons-svelte/lib/ChevronLeft.svelte';
|
||||
import ChevronRightIcon from 'carbon-icons-svelte/lib/ChevronRight.svelte';
|
||||
@@ -13,12 +14,9 @@
|
||||
import PageNav from '$lib/components/PageNav/index.svelte';
|
||||
import IconButton from '$lib/components/IconButton/index.svelte';
|
||||
import Avatar from '$lib/components/Avatar/index.svelte';
|
||||
import {
|
||||
getNavItemRoute,
|
||||
getLessonsRoute,
|
||||
getLectureNo,
|
||||
} from '$lib/components/Course/function';
|
||||
import { 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 Settings from '../Settings/index.svelte';
|
||||
import { settingsDialog } from '../Settings/store';
|
||||
@@ -47,7 +45,7 @@
|
||||
|
||||
function getHref(item) {
|
||||
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() {
|
||||
@@ -89,18 +87,18 @@
|
||||
{
|
||||
label: 'Overview',
|
||||
to: getNavItemRoute($course.id),
|
||||
hideSortIcon: true,
|
||||
hideSortIcon: true
|
||||
},
|
||||
{
|
||||
label: 'Lessons',
|
||||
to: getLessonsRoute($course.id),
|
||||
hideSortIcon: false,
|
||||
isLecture: true,
|
||||
isLecture: true
|
||||
},
|
||||
{
|
||||
label: 'Attendance',
|
||||
to: getNavItemRoute($course.id, 'attendance'),
|
||||
hideSortIcon: true,
|
||||
hideSortIcon: true
|
||||
},
|
||||
{
|
||||
label: 'Submissions',
|
||||
@@ -108,12 +106,12 @@
|
||||
hideSortIcon: true,
|
||||
show() {
|
||||
return !isStudent;
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Marks',
|
||||
to: getNavItemRoute($course.id, 'marks'),
|
||||
hideSortIcon: true,
|
||||
hideSortIcon: true
|
||||
},
|
||||
// {
|
||||
// label: 'Scoreboard',
|
||||
@@ -123,12 +121,12 @@
|
||||
{
|
||||
label: 'People',
|
||||
to: getNavItemRoute($course.id, 'people'),
|
||||
hideSortIcon: true,
|
||||
hideSortIcon: true
|
||||
},
|
||||
{
|
||||
label: 'Certificates',
|
||||
to: getNavItemRoute($course.id, 'certificates'),
|
||||
hideSortIcon: true,
|
||||
hideSortIcon: true
|
||||
},
|
||||
{
|
||||
label: 'Landing Page',
|
||||
@@ -136,17 +134,15 @@
|
||||
hideSortIcon: true,
|
||||
show() {
|
||||
return !isStudent;
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
</script>
|
||||
|
||||
<Settings />
|
||||
|
||||
<div
|
||||
class="root z-10 {!show && 'hide'} {$isMobile ? 'fixed shadow-xl' : 'sticky'}"
|
||||
>
|
||||
<div class="root z-10 {!show && 'hide'} {$isMobile ? 'fixed shadow-xl' : 'sticky'}">
|
||||
{#if show}
|
||||
<div class="relative h-full dark:bg-gray-700">
|
||||
<PageNav bind:title={$course.title} paddingClass="pl-2">
|
||||
@@ -155,9 +151,7 @@
|
||||
</slot:fragment>
|
||||
<slot:fragment slot="widget">
|
||||
<RoleBasedSecurity allowedRoles={[1, 2]}>
|
||||
<IconButton
|
||||
onClick={() => ($settingsDialog.open = !$settingsDialog.open)}
|
||||
>
|
||||
<IconButton onClick={() => ($settingsDialog.open = !$settingsDialog.open)}>
|
||||
<SettingsIcon size={20} class="carbon-icon" />
|
||||
</IconButton>
|
||||
</RoleBasedSecurity>
|
||||
@@ -175,13 +169,11 @@
|
||||
{#if navItem.isLecture}
|
||||
{#each $lessons as item, index}
|
||||
<a
|
||||
class="item flex items-center {(
|
||||
path || $page.url.pathname
|
||||
).includes(item.id) && 'active'} pl-7 py-3 {isStudent &&
|
||||
!item.is_complete
|
||||
class="item flex items-center {(path || $page.url.pathname).includes(item.id) &&
|
||||
'active'} pl-7 py-3 {isStudent && !item.is_unlocked
|
||||
? 'cursor-not-allowed'
|
||||
: ''}"
|
||||
href={isStudent && !item.is_complete
|
||||
href={isStudent && !item.is_unlocked
|
||||
? $page.url.pathname
|
||||
: getLessonsRoute($course.id, item.id)}
|
||||
on:click={() => {
|
||||
@@ -189,17 +181,23 @@
|
||||
setTimeout(() => (show = false), 500);
|
||||
}
|
||||
}}
|
||||
aria-disabled={!item.is_unlocked}
|
||||
>
|
||||
<span class="course-counter">
|
||||
{getLectureNo(index + 1)}
|
||||
</span>
|
||||
<TextChip
|
||||
value={getLectureNo(index + 1)}
|
||||
className="bg-blue-200 text-xs mr-2"
|
||||
size="sm"
|
||||
shape="rounded-full"
|
||||
/>
|
||||
<span>{item.title}</span>
|
||||
{#if item.is_complete}
|
||||
<span class="ml-2 success">
|
||||
<CheckmarkFilledIcon size={20} class="carbon-icon" />
|
||||
{#if !item.is_unlocked}
|
||||
<span class="text-md ml-2">
|
||||
<LockedIcon class="carbon-icon" />
|
||||
</span>
|
||||
{:else if item.is_complete}
|
||||
<span class="ml-2">
|
||||
<CheckmarkOutline class="carbon-icon" />
|
||||
</span>
|
||||
{:else}
|
||||
<span class="text-md ml-2">🔒</span>
|
||||
{/if}
|
||||
</a>
|
||||
{/each}
|
||||
@@ -209,9 +207,7 @@
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="w-full footer p-3 absolute bottom-2 flex items-center justify-between"
|
||||
>
|
||||
<div class="w-full footer p-3 absolute bottom-2 flex items-center justify-between">
|
||||
<IconButton
|
||||
value="toggle"
|
||||
onClick={() => goto(`${$currentOrgPath}/courses`)}
|
||||
@@ -236,7 +232,7 @@
|
||||
: {
|
||||
title: 'Toggle sidebar',
|
||||
hotkeys: ['B'],
|
||||
direction: 'right',
|
||||
direction: 'right'
|
||||
}}
|
||||
>
|
||||
{#if show}
|
||||
|
||||
@@ -26,12 +26,12 @@ export function getLessonsRoute(courseId, lessonId) {
|
||||
return `${path}/${lessonId}`;
|
||||
}
|
||||
|
||||
export function getLectureNo(index, initNo = '00') {
|
||||
export function getLectureNo(index, initNo = '0') {
|
||||
if (index <= 9) {
|
||||
return `${initNo}${index}`;
|
||||
}
|
||||
|
||||
return `${initNo.replace('0', '')}${index}`;
|
||||
return index;
|
||||
}
|
||||
|
||||
export function formatAnswers(data) {
|
||||
@@ -46,9 +46,7 @@ export function formatAnswers(data) {
|
||||
const questionName = questionByIdAndName[answer.question_id];
|
||||
|
||||
answers[questionName] =
|
||||
Array.isArray(answer.answers) && answer.answers.length
|
||||
? answer.answers
|
||||
: answer.open_answer;
|
||||
Array.isArray(answer.answers) && answer.answers.length ? answer.answers : answer.open_answer;
|
||||
}
|
||||
|
||||
return answers;
|
||||
|
||||
@@ -8,9 +8,7 @@
|
||||
|
||||
<div class="root mt-4 mx-auto {width} {className} relative {padding}">
|
||||
{#if $$slots.header}
|
||||
<div
|
||||
class="head dark:bg-gray-800 flex items-center justify-between sticky right-0 w-full px-3"
|
||||
>
|
||||
<div class="head dark:bg-gray-800 flex items-center justify-between sticky right-0 w-full px-3">
|
||||
<slot name="header" />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user