feat: setup drizzle (#594)

* chore: migration to SvelteKit 2

* stage 1: migrate to svelte 5 and fix rerendering issues

* stage 2: fix more $: reactivity across dashboard

* fix merge conflicts

* chore: migrate code in main to svelte 5

* chore: update preventDefault

* chore: simplify tsconfig and remove unused mobileMenuRef in MobileMenu.svelte

* chore: enforce .ts across dashboard

* rename from classroomio-com to website

* stage-3: Replace carbon icon svelte with lucide icon

* update lock file

* chore: finish icon replacement

* fix: settings and setup page multiple rerender

* stage 3: More icon and rerendering fixes

* feat: setup drizzle and better auth

* fix: onboarding validation error showing translation key #586 (#593)

* fix: onboarding validation error showing translation key #586

* fix: remove debug log for profile update errors

* chore: add a clean command

* fix: remove message from email verification response
This commit is contained in:
Rotimi Best
2025-11-01 15:49:29 +01:00
committed by GitHub
parent 97fb446df5
commit b96d91cd7c
801 changed files with 22279 additions and 10834 deletions
@@ -13,6 +13,7 @@ import type { PostgrestError, PostgrestSingleResponse } from '@supabase/supabase
import { GenericUploader } from './presign';
import { QUESTION_TYPE } from '$lib/components/Question/constants';
import type { Question } from '$lib/components/Course/types';
import { STATUS } from '$lib/utils/constants/course';
import { get } from 'svelte/store';
import { isOrgAdmin } from '$lib/utils/store/org';
@@ -61,10 +62,7 @@ export async function fetchProfileCourseProgress(
return { data, error };
}
export async function checkExercisesComplete(
lessonId: Lesson['id'],
groupMemberId: Groupmember['id']
) {
export async function checkExercisesComplete(lessonId: Lesson['id'], groupMemberId: Groupmember['id']) {
const { data, error } = await supabase.rpc('check_if_student_completed_exercises', {
lesson_id_arg: lessonId,
groupmember_id_arg: groupMemberId
@@ -206,11 +204,7 @@ export async function uploadAvatar(courseId: string, avatar: string) {
return logo;
}
export async function updateCourse(
courseId: Course['id'],
avatar: string | undefined,
course: Partial<Course>
) {
export async function updateCourse(courseId: Course['id'], avatar: string | undefined, course: Partial<Course>) {
if (avatar && courseId) {
const filename = `course/${courseId + Date.now()}.webp`;
@@ -237,7 +231,7 @@ export async function deleteCourse(courseId: Course['id']) {
return await supabase.from('course').update({ status: 'DELETED' }).match({ id: courseId });
}
export function addGroupMember(member: any) {
export function addGroupMember(member) {
return supabase.from('groupmember').insert(member).select();
}
@@ -245,7 +239,7 @@ export function addDefaultNewsFeed(feed) {
return supabase.from('course_newsfeed').insert(feed);
}
export function updatedGroupMember(update: any, match: any) {
export function updatedGroupMember(update, match) {
return supabase.from('groupmember').update(update).match(match);
}
@@ -284,8 +278,7 @@ export async function fetchLesson(lessonId: Lesson['id']) {
.single();
if (data) {
const videoKeys =
data.videos?.filter((video) => video.type === 'upload')?.map((video) => video.key) || [];
const videoKeys = data.videos?.filter((video) => video.type === 'upload')?.map((video) => video.key) || [];
const docKeys = data.documents?.map((doc) => doc.key) || [];
@@ -324,8 +317,12 @@ export function fetchLesssonLanguageHistory(lessonId: string, locale: string, en
.order('timestamp', { ascending: false });
}
export function createLesson(lesson: any) {
return supabase.from('lesson').insert(lesson).select();
export async function createLesson(
lesson: Partial<Lesson>
): Promise<{ data: Lesson[] | null; error: PostgrestError | null }> {
const { data, error } = await supabase.from('lesson').insert(lesson).select();
return { data, error };
}
export function createLessonSection(section: any) {
return supabase.from('lesson_section').insert(section).select();
@@ -414,15 +411,8 @@ export async function createExerciseFromTemplate(
}
export async function upsertExercise(questionnaire: any, exerciseId: Exercise['id']) {
const {
questions,
title,
description,
due_by,
is_title_dirty,
is_description_dirty,
is_due_by_dirty
} = questionnaire;
const { questions, title, description, due_by, is_title_dirty, is_description_dirty, is_due_by_dirty } =
questionnaire;
if (is_description_dirty || is_title_dirty || is_due_by_dirty) {
await supabase
@@ -438,8 +428,7 @@ export async function upsertExercise(questionnaire: any, exerciseId: Exercise['i
const updatedQuestions = [];
for (const question of questions) {
const { title, id, name, question_type, options, deleted_at, order, points, is_dirty } =
question;
const { title, id, name, question_type, options, deleted_at, order, points, is_dirty } = question;
// "DELETE" /delete/:questionId - Don't delete if answer already given
if (deleted_at) {
@@ -546,8 +535,8 @@ interface LooseObject {
}
export async function submitExercise(
answers: Array<string>,
questions: Array<{ name: string; id: string }>,
answers: Record<string, string>,
questions: Question[],
exerciseId: Exercise['id'],
courseId: Course['id'],
groupMemberId: Groupmember['id'] | undefined
@@ -556,10 +545,7 @@ export async function submitExercise(
return;
}
const questionsByName = questions.reduce(
(acc, q) => ({ ...acc, [q.name]: q.id }),
{}
) as LooseObject;
const questionsByName = questions.reduce((acc, q) => ({ ...acc, [q.name]: q.id }), {}) as LooseObject;
const questionAnswers = [];
const { data: submission } = await supabase