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
@@ -1,4 +1,6 @@
<script lang="ts">
import { preventDefault } from '$lib/utils/functions/svelte';
import CodeSnippet from '$lib/components/CodeSnippet/index.svelte';
import Checkbox from '$lib/components/Form/Checkbox.svelte';
import HtmlRender from '$lib/components/HTMLRender/HTMLRender.svelte';
@@ -9,31 +11,57 @@
import QuestionTitle from '../QuestionTitle.svelte';
import ReasonBox from '../ReasonBox.svelte';
export let title = '';
export let index = 1;
export let code;
export let name = '';
export let options: { value: string; label: string }[] = [];
export let onSubmit = (a: string, b: string[]) => {};
export let onPrevious = () => {};
export let defaultValue: string[] = [];
export let disablePreviousButton = false;
export let isLast = false;
export let disabled = false;
export let isPreview = false;
export let nextButtonProps = {
isDisabled: false,
isActive: false
};
export let grade: number | undefined;
export let gradeMax = 0;
export let disableGrading = false;
export let isGradeWithAI = false;
export let reason;
export let isLoading = false;
export let hideGrading = false;
interface Props {
key: string | number;
title?: string;
index?: number | string;
code: any;
name?: string;
options?: { value: string; label: string }[];
onSubmit?: any;
onPrevious?: any;
defaultValue?: string[];
disablePreviousButton?: boolean;
isLast?: boolean;
disabled?: boolean;
isPreview?: boolean;
nextButtonProps?: any;
grade?: number;
gradeMax?: number;
disableGrading?: boolean;
isGradeWithAI?: boolean;
reason?: string;
isLoading?: boolean;
hideGrading?: boolean;
}
let gradeWithAI = false;
let {
title = '',
index = 1,
code,
name = '',
options = [],
onSubmit = (_a: string, _b: string[]) => {},
onPrevious = () => {},
defaultValue = [],
disablePreviousButton = false,
isLast = false,
disabled = false,
isPreview = false,
nextButtonProps = {
isDisabled: false,
isActive: false
},
grade = $bindable(),
gradeMax = 0,
disableGrading = false,
isGradeWithAI = false,
reason,
isLoading = false,
hideGrading = false
}: Props = $props();
let gradeWithAI = $derived(isGradeWithAI);
function getVal(form, name) {
let values: string[] = [];
@@ -79,16 +107,12 @@
gradeWithAI = false;
grade = 0;
}
$: gradeWithAI = isGradeWithAI;
</script>
<form on:submit|preventDefault={handleFormSubmit}>
<form onsubmit={preventDefault(handleFormSubmit)}>
<div class="flex items-center justify-between">
<HtmlRender className="mt-4 {typeof grade === 'number' && 'w-4/5'}" disableMaxWidth>
<svelte:fragment slot="content">
<QuestionTitle {index} {title} />
</svelte:fragment>
<QuestionTitle {index} {title} />
</HtmlRender>
{#if !hideGrading}
<Grade {gradeMax} bind:grade {disableGrading} />
@@ -3,14 +3,20 @@
import { t } from '$lib/utils/functions/translations';
import { snackbar } from '../Snackbar/store';
export let gradeMax = 0;
export let disableGrading = false;
export let grade: number | undefined = 0;
$: if (grade && grade > gradeMax) {
snackbar.error('grade cant be more than max value');
grade = gradeMax;
interface Props {
gradeMax?: number;
disableGrading?: boolean;
grade?: number | undefined;
}
let { gradeMax = 0, disableGrading = false, grade = $bindable(0) }: Props = $props();
$effect(() => {
if (grade && grade > gradeMax) {
snackbar.error('grade cant be more than max value');
grade = gradeMax;
}
});
</script>
<div class="flex items-center font-semibold">
@@ -24,7 +30,7 @@
isDisabled={disableGrading}
/>
<p class="dark:text-white ml-2 text-base flex items-center font-semibold">
<p class="ml-2 flex items-center text-base font-semibold dark:text-white">
<span class="mr-1">/</span> <span>{gradeMax}</span>
</p>
</div>
@@ -1,6 +1,10 @@
<script lang="ts">
export let index: number;
export let title: string;
interface Props {
index: number | string;
title: string;
}
let { index, title }: Props = $props();
</script>
<span class="flex gap-2">
@@ -1,4 +1,6 @@
<script lang="ts">
import { preventDefault } from '$lib/utils/functions/svelte';
import CodeSnippet from '$lib/components/CodeSnippet/index.svelte';
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
import { VARIANTS } from '$lib/components/PrimaryButton/constants';
@@ -9,32 +11,59 @@
import ReasonBox from '../ReasonBox.svelte';
import QuestionTitle from '../QuestionTitle.svelte';
export let title = '';
export let index = 1;
export let code = '';
export let name = '';
export let options: { value: string; label: string; is_correct: boolean }[] = [];
export let onSubmit = (a: string, b: string[]) => {};
export let onPrevious = () => {};
export let defaultValue = '';
export let disablePreviousButton = false;
export let disabled = false;
export let isPreview = false;
export let nextButtonProps = {
isDisabled: false,
isActive: false
};
export let isLast = false;
export let grade: number;
export let gradeMax = 0;
export let disableGrading = false;
export let disableOptContainerMargin = false;
export let isGradeWithAI = false;
export let reason;
export let isLoading = false;
export let hideGrading = false;
interface Props {
key: string | number;
title?: string;
index?: number | string;
code?: string;
name?: string;
options?: { value: string; label: string; is_correct: boolean }[];
onSubmit?: any;
onPrevious?: any;
defaultValue?: string;
disablePreviousButton?: boolean;
disabled?: boolean;
isPreview?: boolean;
nextButtonProps?: any;
isLast?: boolean;
grade?: number;
gradeMax?: number;
disableGrading?: boolean;
disableOptContainerMargin?: boolean;
isGradeWithAI?: boolean;
reason?: string;
isLoading?: boolean;
hideGrading?: boolean;
}
let gradeWithAI = false;
let {
title = '',
index = 1,
code = '',
name = '',
options = [],
onSubmit = (_a: string, _b: string[]) => {},
onPrevious = () => {},
defaultValue = '',
disablePreviousButton = false,
disabled = false,
isPreview = false,
nextButtonProps = {
isDisabled: false,
isActive: false
},
isLast = false,
grade = $bindable(),
gradeMax = 0,
disableGrading = false,
disableOptContainerMargin = false,
isGradeWithAI = false,
reason,
isLoading = false,
hideGrading = false
}: Props = $props();
let gradeWithAI = $derived(isGradeWithAI);
function getRadioVal(form, name): string {
let val;
@@ -80,16 +109,12 @@
gradeWithAI = false;
grade = 0;
}
$: gradeWithAI = isGradeWithAI;
</script>
<form on:submit|preventDefault={handleFormSubmit}>
<form onsubmit={preventDefault(handleFormSubmit)}>
<div class="flex items-center justify-between">
<HtmlRender className="mt-4 {typeof grade === 'number' && 'w-4/5'}" disableMaxWidth>
<svelte:fragment slot="content">
<QuestionTitle {index} {title} />
</svelte:fragment>
<QuestionTitle {index} {title} />
</HtmlRender>
{#if !hideGrading}
<Grade {gradeMax} bind:grade {disableGrading} />
@@ -103,7 +128,7 @@
<div class={disableOptContainerMargin ? '' : 'ml-4'}>
{#each options as option}
<button
class="cursor-pointer text-left my-2 border-2 border-gray-300 rounded-md hover:bg-gray-200 dark:hover:bg-neutral-800 w-full {getValidationClassName(
class="my-2 w-full cursor-pointer rounded-md border-2 border-gray-300 text-left hover:bg-gray-200 dark:hover:bg-neutral-800 {getValidationClassName(
option
)}"
type="button"
@@ -124,7 +149,7 @@
{/if}
{#if !isPreview}
<div class="mt-3 flex items-center justify-between w-full">
<div class="mt-3 flex w-full items-center justify-between">
<PrimaryButton
onClick={handlePrevious}
label={$t('course.navItem.lessons.exercises.all_exercises.previous')}
@@ -1,19 +1,23 @@
<script>
<script lang="ts">
import { VARIANTS } from '../PrimaryButton/constants';
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
import { t } from '$lib/utils/functions/translations';
import { SkeletonPlaceholder, SkeletonText } from 'carbon-components-svelte';
export let reason = '';
export let isLoading = false;
export let rejectGrade = () => {};
export let acceptGrade = () => {};
interface Props {
reason?: string;
isLoading?: boolean;
rejectGrade?: any;
acceptGrade?: any;
}
let { reason = '', isLoading = false, rejectGrade = () => {}, acceptGrade = () => {} }: Props = $props();
</script>
<div class="border rounded-md">
<div class="rounded-md border">
{#if isLoading}
<div class="flex w-full px-2 py-4">
<div class="w-full flex flex-row items-start space-x-4">
<div class="flex w-full flex-row items-start space-x-4">
<img src="/ai.svg" alt="alt" class="animate-pulse" />
<div class="w-full">
<SkeletonText width="100%" paragraph lines={3} />
@@ -28,7 +32,7 @@
<div class="flex items-start justify-between px-2 py-4">
<div class="flex items-center space-x-4">
<img src="/ai.svg" alt="alt" />
<p class="font-normal text-sm">
<p class="text-sm font-normal">
{reason}
</p>
</div>
@@ -1,4 +1,6 @@
<script lang="ts">
import { preventDefault } from '$lib/utils/functions/svelte';
import CodeSnippet from '$lib/components/CodeSnippet/index.svelte';
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
import { VARIANTS } from '$lib/components/PrimaryButton/constants';
@@ -9,28 +11,52 @@
import ReasonBox from '../ReasonBox.svelte';
import QuestionTitle from '../QuestionTitle.svelte';
export let title = '';
export let index = 1;
export let code = '';
export let name = '';
export let onSubmit = (a: string, b: string) => {};
export let onPrevious = () => {};
export let defaultValue = '';
export let disablePreviousButton = false;
export let isLast = false;
export let isPreview = false;
export let disabled = false;
export let grade: number | undefined;
export let gradeMax = 0;
export let disableGrading = false;
export let isGradeWithAI = false;
export let reason;
export let isLoading = false;
export let hideGrading = false;
interface Props {
key: string | number;
title?: string;
index?: number | string;
code?: string;
name?: string;
onSubmit?: any;
onPrevious?: any;
defaultValue?: string;
disablePreviousButton?: boolean;
isLast?: boolean;
isPreview?: boolean;
disabled?: boolean;
grade?: number;
gradeMax?: number;
disableGrading?: boolean;
isGradeWithAI?: boolean;
reason?: string;
isLoading?: boolean;
hideGrading?: boolean;
}
let gradeWithAI = false;
let {
title = '',
index = 1,
code = '',
name = '',
onSubmit = (_a: string, _b: string) => {},
onPrevious = () => {},
defaultValue = $bindable(''),
disablePreviousButton = false,
isLast = false,
isPreview = false,
disabled = false,
grade = $bindable(),
gradeMax = 0,
disableGrading = false,
isGradeWithAI = false,
reason,
isLoading = false,
hideGrading = false
}: Props = $props();
function handleFormSubmit(event) {
let gradeWithAI = $derived(isGradeWithAI);
function handleFormSubmit() {
if (isPreview) return;
onSubmit(name, defaultValue);
@@ -49,16 +75,12 @@
gradeWithAI = false;
grade = 0;
}
$: gradeWithAI = isGradeWithAI;
</script>
<form on:submit|preventDefault={handleFormSubmit}>
<div class="flex items-center justify-between mb-2">
<form onsubmit={preventDefault(handleFormSubmit)}>
<div class="mb-2 flex items-center justify-between">
<HtmlRender className="mt-4 {typeof grade === 'number' && 'w-4/5'}" disableMaxWidth>
<svelte:fragment slot="content">
<QuestionTitle {index} {title} />
</svelte:fragment>
<QuestionTitle {index} {title} />
</HtmlRender>
{#if !hideGrading}
<Grade {gradeMax} bind:grade {disableGrading} />
@@ -71,10 +93,8 @@
<div class="ml-4">
{#if disabled}
<div class="bg-gray-200 dark:bg-gray-500 py-3 px-5 rounded-md mb-3">
{defaultValue === ''
? $t('course.navItem.lessons.exercises.all_exercises.no_answer')
: defaultValue}
<div class="mb-3 rounded-md bg-gray-200 px-5 py-3 dark:bg-gray-500">
{defaultValue === '' ? $t('course.navItem.lessons.exercises.all_exercises.no_answer') : defaultValue}
</div>
{#if gradeWithAI}
<ReasonBox {reason} {isLoading} {acceptGrade} {rejectGrade} />
@@ -89,7 +109,7 @@
</div>
{#if !isPreview}
<div class="mt-3 flex items-center justify-between w-full">
<div class="mt-3 flex w-full items-center justify-between">
<PrimaryButton
variant={VARIANTS.OUTLINED}
onClick={handlePrevious}