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:
+3
-4
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user