feat: lesson sections - v1 (#431)

* add todo

* update sidebar with sections

* add course versioning

* fix db reset issue

* v1 of working sections feature

* remove
This commit is contained in:
Rotimi Best
2024-08-14 10:27:37 +01:00
committed by GitHub
parent e7e2bc632b
commit ad421acf8d
30 changed files with 979 additions and 393 deletions
@@ -26,7 +26,7 @@ const lessonSchema = z.object({
title: z.string().nonempty({ message: `${t.get('validations.lesson_schema.empty_title')}` }),
lesson_at: z.string().optional(),
call_url: z.string().nullable().optional(),
is_unlocked: z.boolean()
is_unlocked: z.boolean().optional()
});
const createQuizValidationSchema = z.object({
@@ -148,8 +148,8 @@ export const getConfirmPasswordError = ({ password, confirmPassword }) => {
: undefined;
};
export const processErrors = (error, mapToId) => {
const errors = {};
export const processErrors = (error, mapToId?: boolean) => {
const errors: Record<string, string> = {};
if (Array.isArray(error?.issues)) {
for (const issue of error.issues) {
@@ -162,7 +162,6 @@ export const processErrors = (error, mapToId) => {
if (mapToId) {
let value = '';
path.forEach((p, i) => {
const isLast = !path[i + 1];
const formatP = isNumber(p) ? `[${p}]` : p;
value += !value ? formatP : `.${formatP}`;
@@ -9,7 +9,8 @@ import type {
Groupmember,
Exercise,
LessonCompletion,
ExerciseTemplate
ExerciseTemplate,
LessonSection
} from '$lib/utils/types';
import { STATUS } from '$lib/utils/constants/course';
import type { PostgrestSingleResponse, PostgrestError } from '@supabase/supabase-js';
@@ -70,10 +71,12 @@ const SLUG_QUERY = `
is_published,
slug,
cost,
version,
currency,
metadata,
is_certificate_downloadable,
certificate_theme,
lesson_section(id, title, order),
lessons:lesson(
id, title, order
)
@@ -87,6 +90,7 @@ const ID_QUERY = `
overview,
logo,
is_published,
version,
group(*,
members:groupmember(*,
profile(*)
@@ -98,8 +102,9 @@ const ID_QUERY = `
metadata,
is_certificate_downloadable,
certificate_theme,
lesson_section(id, title, order, created_at),
lessons:lesson(
id, title,public, lesson_at, is_unlocked, order, created_at,
id, title, public, lesson_at, is_unlocked, order, created_at, section_id,
note, videos, slide_url, call_url, totalExercises:exercise(count), totalComments:lesson_comment(count),
profile:teacher_id(id, avatar_url, fullname),
lesson_completion(id, profile_id, is_complete)
@@ -244,6 +249,7 @@ export function fetchLesson(lessonId: Lesson['id']) {
.from('lesson')
.select(
`id,
title,
note,
videos,
slide_url,
@@ -271,6 +277,15 @@ export function fetchLesssonLanguageHistory(lessonId: string, locale: string, en
export function createLesson(lesson: any) {
return supabase.from('lesson').insert(lesson).select();
}
export function createLessonSection(section: any) {
return supabase.from('lesson_section').insert(section).select();
}
export function updateLessonSection(section: any, sectionId: LessonSection['id']) {
return supabase
.from('lesson_section')
.update({ ...section, id: undefined })
.match({ id: sectionId });
}
export function updateLesson(lesson: any, lessonId: Lesson['id']) {
return supabase
@@ -297,6 +312,11 @@ export function deleteLesson(lessonId: Lesson['id']) {
return supabase.from('lesson').delete().match({ id: lessonId });
}
export function deleteLessonSection(id: LessonSection['id']) {
// Need to implement soft delete
return supabase.from('lesson_section').delete().match({ id });
}
export function createExercise(exercise: any) {
return supabase.from('exercise').insert(exercise).select();
}
@@ -754,7 +754,10 @@
"lock": "Sperren",
"unlock": "Freischalten",
"start_reorder": "Neu anordnen",
"end_reorder": "Nachbestellung beenden"
"end_reorder": "Nachbestellung beenden",
"modal_heading_section": "Neuen Abschnitt hinzufügen",
"lesson_section_title": "Abschnittstitel",
"cancel": "Stornieren"
},
"disabled": "Funktion ist deaktiviert",
"next": "Nächste",
@@ -512,14 +512,17 @@
"start_reorder": "Reorder",
"end_reorder": "End Reorder",
"modal_heading": "Add New Lesson",
"modal_heading_section": "Add New Section",
"lesson_title": "Lesson Title",
"lesson_section_title": "Section Title",
"click_to": "Click to",
"lock": "Lock",
"unlock": "Unlock",
"fullname": "fullname",
"save": "Save",
"edit": "Edit",
"delete": "Delete"
"delete": "Delete",
"cancel": "Cancel"
},
"join_lesson": "Join lesson",
"no_link": "No link",
@@ -754,7 +754,10 @@
"lock": "Cerrar",
"unlock": "desbloquear",
"start_reorder": "Reordenar",
"end_reorder": "Finalizar reorden"
"end_reorder": "Finalizar reorden",
"modal_heading_section": "Agregar nueva sección",
"lesson_section_title": "Título de la sección",
"cancel": "Cancelar"
},
"disabled": "La característica está deshabilitada",
"next": "Próximo",
@@ -754,7 +754,10 @@
"lock": "Verrouillage",
"unlock": "Ouvrir",
"start_reorder": "Réorganiser",
"end_reorder": "Terminer la réapprovisionnement"
"end_reorder": "Terminer la réapprovisionnement",
"modal_heading_section": "Ajouter une nouvelle section",
"lesson_section_title": "Titre de la section",
"cancel": "Annuler"
},
"disabled": "La fonctionnalité est désactivée",
"next": "Suivant",
@@ -754,7 +754,10 @@
"lock": "ताला",
"unlock": "अनलॉक",
"start_reorder": "पुन: व्यवस्थित करें",
"end_reorder": "पुनः क्रम समाप्त करें"
"end_reorder": "पुनः क्रम समाप्त करें",
"modal_heading_section": "नया अनुभाग जोड़ें",
"lesson_section_title": "अनुभाग शीर्षक",
"cancel": "रद्द करना"
},
"disabled": "सुविधा अक्षम है",
"next": "अगला",
@@ -754,7 +754,10 @@
"lock": "Trancar",
"unlock": "Desbloquear",
"start_reorder": "Reordenar",
"end_reorder": "Finalizar novo pedido"
"end_reorder": "Finalizar novo pedido",
"modal_heading_section": "Adicionar nova seção",
"lesson_section_title": "Título da seção",
"cancel": "Cancelar"
},
"disabled": "O recurso está desativado",
"next": "Próximo",
@@ -754,7 +754,10 @@
"lock": "Замок",
"unlock": "Разблокировать",
"start_reorder": "Изменение порядка",
"end_reorder": "Завершить изменение порядка"
"end_reorder": "Завершить изменение порядка",
"modal_heading_section": "Добавить новый раздел",
"lesson_section_title": "Название раздела",
"cancel": "Отмена"
},
"disabled": "Функция отключена",
"next": "Следующий",
@@ -754,7 +754,10 @@
"lock": "Khóa",
"unlock": "Mở khóa",
"start_reorder": "Sắp xếp lại",
"end_reorder": "Kết thúc sắp xếp lại"
"end_reorder": "Kết thúc sắp xếp lại",
"modal_heading_section": "Thêm phần mới",
"lesson_section_title": "Tiêu đề phần",
"cancel": "Hủy bỏ"
},
"disabled": "Tính năng bị vô hiệu hóa",
"next": "Kế tiếp",
@@ -194,10 +194,15 @@ export enum COURSE_TYPE {
SELF_PACED = 'SELF_PACED',
LIVE_CLASS = 'LIVE_CLASS'
}
export enum COURSE_VERSION {
V1 = 'V1', // with only lesson
V2 = 'V2' // lessons are grouped into sections
}
export interface Course {
title?: any; // type unknown;
description: string; // type unknown;
type: COURSE_TYPE;
version: COURSE_VERSION;
overview?: any; // type unknown;
id?: string /* primary key */;
created_at: string;
@@ -225,6 +230,7 @@ export interface Course {
is_present: boolean;
id: number;
}[];
lesson_section?: LessonSection[];
lessons?: Lesson[];
polls: { status: string }[];
}
@@ -271,6 +277,7 @@ export enum VideoType {
export interface LessonPage {
id?: string | null;
title: '';
totalExercises: number;
totalComments: number;
locale: LOCALE;
@@ -305,6 +312,7 @@ export interface Lesson {
videos?: []; // type unknown;
slide_url?: any; // type unknown;
course_id: string /* foreign key to course.id */;
section_id?: string /* foreign key to course.id */;
id: string /* primary key */;
created_at: string;
updated_at?: string;
@@ -318,6 +326,16 @@ export interface Lesson {
course?: Course;
profile?: Profile;
lesson_completion: LessonCompletion[];
totalExercises?: { count: number }[];
}
export interface LessonSection {
id: string;
title: string;
order: number;
course_id: string /* foreign key to course.id */;
lessons: Lesson[];
created_at: string;
}
export interface Exercise {