feat: lint codebase and run precommit hook

This commit is contained in:
Rotimi Best
2025-12-25 09:01:51 +00:00
parent 334c67f22a
commit b31ccf3037
214 changed files with 6191 additions and 2880 deletions
@@ -13,8 +13,8 @@
<Skeleton class="h-[48px] w-[48px]" />
<div class="flex-1">
<Skeleton class="h-[48px] w-[48px]" />
<Skeleton class="h-[16px] w-[80px] mb-2" />
<Skeleton class="h-[24px] w-[40px] mb-1" />
<Skeleton class="mb-2 h-[16px] w-[80px]" />
<Skeleton class="mb-1 h-[24px] w-[40px]" />
<Skeleton class="h-[12px] w-[60px]" />
</div>
</div>
@@ -26,10 +26,10 @@
<!-- Charts Section Skeleton -->
<div class="mb-12">
<div class="rounded-lg border border-gray-200 bg-white p-6 dark:border-neutral-700 dark:bg-neutral-800">
<Skeleton class="h-[24px] w-[200px] mb-6" />
<Skeleton class="mb-6 h-[24px] w-[200px]" />
<div class="grid grid-cols-1 gap-6 lg:grid-cols-2">
<Skeleton class="w-full h-[300px]" />
<Skeleton class="w-full h-[300px]" />
<Skeleton class="h-[300px] w-full" />
<Skeleton class="h-[300px] w-full" />
</div>
</div>
</div>
@@ -37,7 +37,7 @@
<!-- Students Table Skeleton -->
<div class="rounded-lg border border-gray-200 bg-white dark:border-neutral-700 dark:bg-neutral-800">
<div class="border-b border-gray-200 px-6 py-4 dark:border-neutral-700">
<Skeleton class="h-[24px] w-[200px] mb-6" />
<Skeleton class="mb-6 h-[24px] w-[200px]" />
</div>
<div class="p-6">
<div class="space-y-4">
@@ -45,7 +45,7 @@
<div class="flex items-center space-x-4">
<Skeleton class="h-[32px] w-[32px]" />
<div class="flex-1">
<Skeleton class="h-[16px] w-[150px] mb-1" />
<Skeleton class="mb-1 h-[16px] w-[150px]" />
<Skeleton class="h-[12px] w-[200px]" />
</div>
<Skeleton class="h-[16px] w-[80px]" />
@@ -45,7 +45,7 @@
isSaving = true;
try {
// Prevent free plan users from bypassing UI restrictions
// Prevent free plan users from bypassing UI restrictions
if ($isFreePlan) {
errors.description = 'Certificate customization is only available on paid plans';
throw new Error(errors.description);
@@ -28,47 +28,47 @@
<Dialog.Title>Send Certificate</Dialog.Title>
</Dialog.Header>
<main>
<div>
<div class="mb-4 flex items-center space-x-2">
<Switch id="auto-certificate" bind:checked={isAutomatic} />
<Label for="auto-certificate" class="text-sm font-medium">Automatic</Label>
</div>
<p class="my-4 text-sm font-medium">
If you set this as automatic, certificates will be issued after the learner completes the course.
</p>
</div>
<p class="my-4 text-xs font-normal text-gray-500">or send a personalised/ custom certificate below:</p>
<form onsubmit={preventDefault(issueCertificate)}>
<div class="flex w-full flex-col gap-2 md:flex-row">
<InputField
label="Email address of the student"
className="w-full my-4"
labelClassName="text-xs font-normal"
placeholder="email,comma seperated"
bind:value={$issueCertificateModal.email}
/>
<InputField
label="Schedule date"
className="w-full my-4"
labelClassName="text-xs font-normal"
placeholder="12/06/2023"
bind:value={$issueCertificateModal.date}
/>
<div>
<div class="mb-4 flex items-center space-x-2">
<Switch id="auto-certificate" bind:checked={isAutomatic} />
<Label for="auto-certificate" class="text-sm font-medium">Automatic</Label>
</div>
<p class="my-4 text-sm font-medium">
If you set this as automatic, certificates will be issued after the learner completes the course.
</p>
</div>
<p class="my-4 text-xs font-normal text-gray-500">or send a personalised/ custom certificate below:</p>
<form onsubmit={preventDefault(issueCertificate)}>
<div class="flex w-full flex-col gap-2 md:flex-row">
<InputField
label="Email address of the student"
className="w-full my-4"
labelClassName="text-xs font-normal"
placeholder="email,comma seperated"
bind:value={$issueCertificateModal.email}
/>
<InputField
label="Schedule date"
className="w-full my-4"
labelClassName="text-xs font-normal"
placeholder="12/06/2023"
bind:value={$issueCertificateModal.date}
/>
</div>
<TextareaField
label="Add a personalized message"
labelClassName="text-xs font-normal"
bind:value={$issueCertificateModal.message}
rows={2}
placeholder="your message here"
className="mb-4"
/>
<TextareaField
label="Add a personalized message"
labelClassName="text-xs font-normal"
bind:value={$issueCertificateModal.message}
rows={2}
placeholder="your message here"
className="mb-4"
/>
<div class="mt-5 flex w-full items-end justify-end">
<Button type="submit">Issue certificate</Button>
</div>
</form>
</main>
<div class="mt-5 flex w-full items-end justify-end">
<Button type="submit">Issue certificate</Button>
</div>
</form>
</main>
</Dialog.Content>
</Dialog.Root>
@@ -39,34 +39,28 @@
if (!isOpen) handleClose();
}}
>
<Dialog.Content class="w-[80%] md:w-[65%] max-w-xl">
<Dialog.Content class="w-[80%] max-w-xl md:w-[65%]">
<Dialog.Header>
<Dialog.Title>{$t(`course.navItem.lessons.section_prompt.header`)}</Dialog.Title>
</Dialog.Header>
<div class="flex w-full flex-col items-center">
<div class="mb-8">
<h3 class="text-center text-2xl">
{$t('course.navItem.lessons.section_prompt.title')}
</h3>
<p class="max-w-md text-center">
{$t('course.navItem.lessons.section_prompt.description')}
</p>
</div>
<div class="mb-8">
<h3 class="text-center text-2xl">
{$t('course.navItem.lessons.section_prompt.title')}
</h3>
<p class="max-w-md text-center">
{$t('course.navItem.lessons.section_prompt.description')}
</p>
</div>
<div class="flex gap-2">
<Button
variant="outline"
onclick={handleClose}
>
{$t('course.navItem.lessons.section_prompt.cancel')}
</Button>
<Button
onclick={activate}
loading={isActivating}
>
{$t('course.navItem.lessons.section_prompt.activate')}
</Button>
<div class="flex gap-2">
<Button variant="outline" onclick={handleClose}>
{$t('course.navItem.lessons.section_prompt.cancel')}
</Button>
<Button onclick={activate} loading={isActivating}>
{$t('course.navItem.lessons.section_prompt.activate')}
</Button>
</div>
</div>
</div>
</Dialog.Content>
</Dialog.Root>
@@ -22,29 +22,23 @@
if (!isOpen) openDeleteModal = false;
}}
>
<Dialog.Content class="w-96 px-6 pt-2 pb-6">
<Dialog.Content class="w-96 px-6 pb-6 pt-2">
<Dialog.Header>
<Dialog.Title>{$t('delete_modal.label')}</Dialog.Title>
</Dialog.Header>
<div>
<h1 class="text-lg dark:text-white">
{$t('delete_modal.content')}?
</h1>
<h1 class="text-lg dark:text-white">
{$t('delete_modal.content')}?
</h1>
<div class="mt-5 flex items-center justify-between">
<Button
variant="outline"
onclick={() => (openDeleteModal = false)}
>
{$t('delete_modal.no')}
</Button>
<Button
variant="outline"
onclick={handleDelete}
>
{$t('delete_modal.yes')}
</Button>
<div class="mt-5 flex items-center justify-between">
<Button variant="outline" onclick={() => (openDeleteModal = false)}>
{$t('delete_modal.no')}
</Button>
<Button variant="outline" onclick={handleDelete}>
{$t('delete_modal.yes')}
</Button>
</div>
</div>
</div>
</Dialog.Content>
</Dialog.Root>
@@ -7,37 +7,35 @@
let { onDelete = () => {}, onCancel = () => {} } = $props();
</script>
<Dialog.Root
bind:open={$deleteConfirmation.open}
>
<Dialog.Root bind:open={$deleteConfirmation.open}>
<Dialog.Content class="w-96">
<Dialog.Header>
<Dialog.Title>{$t('course.navItem.lessons.exercises.all_exercises.delete_confirmation.title')}</Dialog.Title>
</Dialog.Header>
<div>
<p class="mt-0 text-base dark:text-white">
{$t('course.navItem.lessons.exercises.all_exercises.delete_confirmation.sure')}
</p>
<p class="mt-0 text-base dark:text-white">
{$t('course.navItem.lessons.exercises.all_exercises.delete_confirmation.sure')}
</p>
<div class="mt-5 flex items-center justify-between">
<Button
variant="outline"
onclick={() => {
$deleteConfirmation.open = false;
onCancel();
}}
>
{$t('course.navItem.lessons.exercises.all_exercises.delete_confirmation.no')}
</Button>
<Button
onclick={() => {
onDelete();
$deleteConfirmation.open = false;
}}
>
{$t('course.navItem.lessons.exercises.all_exercises.delete_confirmation.yes')}
</Button>
<div class="mt-5 flex items-center justify-between">
<Button
variant="outline"
onclick={() => {
$deleteConfirmation.open = false;
onCancel();
}}
>
{$t('course.navItem.lessons.exercises.all_exercises.delete_confirmation.no')}
</Button>
<Button
onclick={() => {
onDelete();
$deleteConfirmation.open = false;
}}
>
{$t('course.navItem.lessons.exercises.all_exercises.delete_confirmation.yes')}
</Button>
</div>
</div>
</div>
</Dialog.Content>
</Dialog.Root>
@@ -236,108 +236,108 @@
if (!isOpen) onClose();
}}
>
<Dialog.Content class="w-4/5 h-[90%] flex items-start !max-h-full h-[85%] py-0 px-4">
<Dialog.Content class="flex h-[85%] h-[90%] !max-h-full w-4/5 items-start px-4 py-0">
<Dialog.Header class="py-2">
<Dialog.Title class="text-base font-semibold">{data.title}</Dialog.Title>
</Dialog.Header>
<div class="mt-2 h-full w-full">
{#if openDeletePrompt}
<div class="mx-auto w-96 rounded-md border border-gray-300 p-3">
<h1 class="text-lg dark:text-white">
{$t('delete_modal.content')}
</h1>
{#if openDeletePrompt}
<div class="mx-auto w-96 rounded-md border border-gray-300 p-3">
<h1 class="text-lg dark:text-white">
{$t('delete_modal.content')}
</h1>
<div class="mt-5 flex items-center justify-between">
<Button variant="outline" onclick={() => (openDeletePrompt = false)} disabled={isDeleting}>
{$t('delete_modal.no')}
</Button>
<Button variant="outline" onclick={handleDeleteSubmission} loading={isDeleting}>
{$t('delete_modal.yes')}
</Button>
<div class="mt-5 flex items-center justify-between">
<Button variant="outline" onclick={() => (openDeletePrompt = false)} disabled={isDeleting}>
{$t('delete_modal.no')}
</Button>
<Button variant="outline" onclick={handleDeleteSubmission} loading={isDeleting}>
{$t('delete_modal.yes')}
</Button>
</div>
</div>
</div>
{:else}
<Preview
questions={sortedQuestions}
questionnaireMetaData={{
answers: data.answers || {},
isFinished: true
}}
bind:grades={data.questionAnswerByPoint}
bind:reasons
bind:isGradeWithAI
bind:isLoading
disableGrading={false}
/>
{/if}
</div>
<div class="sticky top-0 ml-4 mt-2 w-2/5">
<div class="rounded-md border border-gray-300">
<div
class="flex items-center justify-between gap-1 border-b border-l-0 border-r-0 border-t-0 border-gray-300 p-3"
>
<p class="text-base dark:text-white">
{$t('course.navItem.submissions.grading_modal.details')}
{#if data.isEarly}
<span class="badge ml-2 rounded-sm bg-green-500 px-2 text-sm text-white">
{$t('course.navItem.submissions.grading_modal.early')}</span
{:else}
<Preview
questions={sortedQuestions}
questionnaireMetaData={{
answers: data.answers || {},
isFinished: true
}}
bind:grades={data.questionAnswerByPoint}
bind:reasons
bind:isGradeWithAI
bind:isLoading
disableGrading={false}
/>
{/if}
</div>
<div class="sticky top-0 ml-4 mt-2 w-2/5">
<div class="rounded-md border border-gray-300">
<div
class="flex items-center justify-between gap-1 border-b border-l-0 border-r-0 border-t-0 border-gray-300 p-3"
>
<p class="text-base dark:text-white">
{$t('course.navItem.submissions.grading_modal.details')}
{#if data.isEarly}
<span class="badge ml-2 rounded-sm bg-green-500 px-2 text-sm text-white">
{$t('course.navItem.submissions.grading_modal.early')}</span
>
{:else}
<span class="badge ml-2 rounded-sm bg-red-500 px-2 text-sm text-white">
{$t('course.navItem.submissions.grading_modal.late')}
</span>
{/if}
</p>
<DropdownMenu.Root>
<DropdownMenu.Trigger
class="flex h-8 w-8 items-center justify-center rounded-md hover:bg-gray-100 dark:hover:bg-neutral-700"
>
{:else}
<span class="badge ml-2 rounded-sm bg-red-500 px-2 text-sm text-white">
{$t('course.navItem.submissions.grading_modal.late')}
</span>
{/if}
</p>
<EllipsisVerticalIcon class="h-5 w-5" />
</DropdownMenu.Trigger>
<DropdownMenu.Content align="end">
<DropdownMenu.Item
class="text-red-600"
onclick={() => {
openDeletePrompt = true;
}}
>
{$t('delete_modal.label')}
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Root>
</div>
<DropdownMenu.Root>
<DropdownMenu.Trigger
class="flex h-8 w-8 items-center justify-center rounded-md hover:bg-gray-100 dark:hover:bg-neutral-700"
>
<EllipsisVerticalIcon class="h-5 w-5" />
</DropdownMenu.Trigger>
<DropdownMenu.Content align="end">
<DropdownMenu.Item
class="text-red-600"
onclick={() => {
openDeletePrompt = true;
}}
>
{$t('delete_modal.label')}
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Root>
</div>
<div class="flex items-center space-x-4 px-3 py-2 text-sm">
<p class="text-sm font-semibold text-gray-500 dark:text-white">
{$t('course.navItem.submissions.grading_modal.total_grade')}:
</p>
<div class="flex items-center space-x-4 px-3 py-2 text-sm">
<p class="text-sm font-semibold text-gray-500 dark:text-white">
{$t('course.navItem.submissions.grading_modal.total_grade')}:
</p>
<Badge class="w-fit rounded-md bg-gray-100 font-semibold text-black dark:bg-neutral-700 dark:text-white">
{total}/{maxPoints}
</Badge>
</div>
<!-- <div class="flex items-center text-sm p-3">
<Badge class="w-fit rounded-md bg-gray-100 font-semibold text-black dark:bg-neutral-700 dark:text-white">
{total}/{maxPoints}
</Badge>
</div>
<!-- <div class="flex items-center text-sm p-3">
<p class="dark:text-white w-1/2">Status</p>
<div class="flex items-center">
<p class="dark:text-white rounded-full w-5 h-5 bg-yellow-300 mr-2" />
<p class="dark:text-white">Grading</p>
</div>
</div> -->
<div class="flex items-center space-x-4 px-3 py-2 text-sm">
<p class="text-sm font-semibold text-gray-500 dark:text-white">
{$t('course.navItem.submissions.grading_modal.student')}:
</p>
{#if data.student}
<div class="flex flex-row items-center justify-center rounded-md bg-gray-100 p-[6px] dark:bg-neutral-700">
<img alt="Student avatar" class="flex h-5 w-5 rounded-full" src={data.student.avatar_url} />
<p class="ml-2 line-clamp-1 text-sm font-semibold dark:text-white">
{data.student.fullname}
</p>
</div>
{/if}
</div>
<!-- <div class="flex items-center space-x-4 text-sm px-3 py-2">
<div class="flex items-center space-x-4 px-3 py-2 text-sm">
<p class="text-sm font-semibold text-gray-500 dark:text-white">
{$t('course.navItem.submissions.grading_modal.student')}:
</p>
{#if data.student}
<div class="flex flex-row items-center justify-center rounded-md bg-gray-100 p-[6px] dark:bg-neutral-700">
<img alt="Student avatar" class="flex h-5 w-5 rounded-full" src={data.student.avatar_url} />
<p class="ml-2 line-clamp-1 text-sm font-semibold dark:text-white">
{data.student.fullname}
</p>
</div>
{/if}
</div>
<!-- <div class="flex items-center space-x-4 text-sm px-3 py-2">
<p class="dark:text-white text-sm text-gray-500 font-semibold">Assesment Type:</p>
<Tag
class="dark:text-white font-semibold bg-gray-100 dark:bg-neutral-700 rounded-md text-black w-fit"
@@ -345,68 +345,68 @@
>
</div> -->
<div class="flex flex-col items-start px-3 py-2 text-sm">
<p class="font-semibold text-gray-500 dark:text-white">
{$t('course.navItem.submissions.grading_modal.status')}:
</p>
<Select.Root
type="single"
value={status.id.toString()}
onValueChange={(value) => {
if (value) {
handleStatusChange(value);
}
}}
>
<Select.Trigger class="w-full">
{status.text}
</Select.Trigger>
<Select.Content>
{#each SELECTABLE_STATUS as statusOption}
<Select.Item value={statusOption.id.toString()}>
{statusOption.text}
</Select.Item>
{/each}
</Select.Content>
</Select.Root>
</div>
<div class="flex flex-col items-start px-3 py-2 text-sm">
<p class="font-semibold text-gray-500 dark:text-white">
{$t('course.navItem.submissions.grading_modal.add_comment')}:
</p>
<TextareaField
bgColor="bg-gray-100 dark:bg-neutral-700"
className="font-semibold"
placeholder={$t('course.navItem.submissions.grading_modal.add_comment_placeholder')}
bind:value={data.feedback}
/>
</div>
<div class="flex w-full flex-col space-y-3 px-3 py-2">
<Button variant="outline" onclick={gradeWithAI} class="w-full">
<img src="/ai.svg" alt="ai" />
<p class="text-sm font-semibold">
{$t('course.navItem.submissions.grading_modal.grade_with_ai')}
<div class="flex flex-col items-start px-3 py-2 text-sm">
<p class="font-semibold text-gray-500 dark:text-white">
{$t('course.navItem.submissions.grading_modal.status')}:
</p>
</Button>
<Button
onclick={() => {
handleSave(data);
// onClose();
}}
loading={isSaving}
class="w-full"
>
{$t('course.navItem.submissions.grading_modal.submit_grades')}
</Button>
</div>
<!-- <div class="flex items-center text-sm p-3">
<Select.Root
type="single"
value={status.id.toString()}
onValueChange={(value) => {
if (value) {
handleStatusChange(value);
}
}}
>
<Select.Trigger class="w-full">
{status.text}
</Select.Trigger>
<Select.Content>
{#each SELECTABLE_STATUS as statusOption}
<Select.Item value={statusOption.id.toString()}>
{statusOption.text}
</Select.Item>
{/each}
</Select.Content>
</Select.Root>
</div>
<div class="flex flex-col items-start px-3 py-2 text-sm">
<p class="font-semibold text-gray-500 dark:text-white">
{$t('course.navItem.submissions.grading_modal.add_comment')}:
</p>
<TextareaField
bgColor="bg-gray-100 dark:bg-neutral-700"
className="font-semibold"
placeholder={$t('course.navItem.submissions.grading_modal.add_comment_placeholder')}
bind:value={data.feedback}
/>
</div>
<div class="flex w-full flex-col space-y-3 px-3 py-2">
<Button variant="outline" onclick={gradeWithAI} class="w-full">
<img src="/ai.svg" alt="ai" />
<p class="text-sm font-semibold">
{$t('course.navItem.submissions.grading_modal.grade_with_ai')}
</p>
</Button>
<Button
onclick={() => {
handleSave(data);
// onClose();
}}
loading={isSaving}
class="w-full"
>
{$t('course.navItem.submissions.grading_modal.submit_grades')}
</Button>
</div>
<!-- <div class="flex items-center text-sm p-3">
<p class="dark:text-white w-1/2">Teacher</p>
<p class="dark:text-white">rotimi-best</p>
</div> -->
</div>
</div>
</div>
</Dialog.Content>
</Dialog.Root>
@@ -71,34 +71,34 @@
<Dialog.Title>{$t('course.navItem.lessons.exercises.all_exercises.order_questions')}</Dialog.Title>
</Dialog.Header>
<section
use:dndzone={{
items,
flipDurationMs,
dropTargetStyle: {
border: '2px #1d4ed8 solid',
'border-style': 'dashed'
}
}}
onconsider={handleDndConsider}
onfinalize={handleDndFinalize}
class="w-full"
>
{#each items as item (item.id)}
<div
animate:flip={{ duration: flipDurationMs }}
class="ui:border-primary flex items-center rounded-md border p-4"
>
{#if item.type === 1}
<SquareCheckIcon size={16} />
{:else if item.type === 2}
<CircleDotIcon size={16} />
{:else}
<TablePropertiesIcon size={16} />
{/if}
{` ${item.name}`}
</div>
{/each}
</section>
use:dndzone={{
items,
flipDurationMs,
dropTargetStyle: {
border: '2px #1d4ed8 solid',
'border-style': 'dashed'
}
}}
onconsider={handleDndConsider}
onfinalize={handleDndFinalize}
class="w-full"
>
{#each items as item (item.id)}
<div
animate:flip={{ duration: flipDurationMs }}
class="ui:border-primary flex items-center rounded-md border p-4"
>
{#if item.type === 1}
<SquareCheckIcon size={16} />
{:else if item.type === 2}
<CircleDotIcon size={16} />
{:else}
<TablePropertiesIcon size={16} />
{/if}
{` ${item.name}`}
</div>
{/each}
</section>
</Dialog.Content>
</Dialog.Root>
@@ -153,7 +153,7 @@
if (!isOpen) handleCancelAddExercise();
}}
>
<Dialog.Content class="max-w-2xl w-4/5">
<Dialog.Content class="w-4/5 max-w-2xl">
<Dialog.Header>
<Dialog.Title>{$t('course.navItem.lessons.exercises.new_exercise_modal.heading')}</Dialog.Title>
</Dialog.Header>
@@ -161,147 +161,147 @@
<Confetti />
{/if}
{#if step === 0}
<div>
<h2 class="my-5 text-2xl font-medium">
{$t('course.navItem.lessons.exercises.new_exercise_modal.how')}?
</h2>
<div class="my-8 flex justify-between gap-2">
{#each options as option}
<button
class="h-60 w-[261px] rounded-md border-2 p-5 dark:bg-neutral-700 {option.type === type
? 'border-primary-400'
: `border-gray-200 dark:border-neutral-600 ${
!option.isDisabled && 'hover:scale-95'
}`} flex flex-col {option.isDisabled && 'cursor-not-allowed opacity-60'} transition-all ease-in-out"
type="button"
onclick={!option.isDisabled ? () => (type = option.type) : undefined}
>
<div class="flex h-[70%] w-full flex-row-reverse">
<CircleCheckIcon size={16} filled={option.type === type} />
</div>
<div>
<p class="flex items-center text-start">
<span class="mr-2 text-sm">{option.title}</span>
{#if option.isDisabled}
<ComingSoon />
{/if}
</p>
<p class="text-start text-xs font-light">{option.subtitle}</p>
</div>
</button>
{/each}
</div>
<div class="mt-8 flex flex-row-reverse items-center">
<Button onclick={handleNext}>
{$t('course.navItem.lessons.exercises.new_exercise_modal.next')}
</Button>
</div>
</div>
{:else if step === 1}
{#if type === Type.SCRATCH}
<div class="m-auto flex min-h-[300px] w-96 items-center justify-center">
<div class="w-full">
<h2 class="my-5 text-2xl font-medium">
{$t('course.navItem.lessons.exercises.new_exercise_modal.title')}
</h2>
<InputField
bind:value={title}
autoFocus={true}
placeholder={$t('course.navItem.lessons.exercises.new_exercise_modal.title_placeholder')}
className="my-4"
/>
<div class="mt-5 flex items-center justify-between">
<Button variant="outline" onclick={handleBack}>
{$t('course.navItem.lessons.exercises.new_exercise_modal.back')}
</Button>
<Button onclick={handleAddExercise}>
{$t('course.navItem.lessons.exercises.new_exercise_modal.finish')}
</Button>
</div>
</div>
</div>
{:else if type === Type.TEMPLATE}
<div>
<h2 class="m-0 mb-2 text-2xl font-medium">
{$t('course.navItem.lessons.exercises.new_exercise_modal.select_template')}
<h2 class="my-5 text-2xl font-medium">
{$t('course.navItem.lessons.exercises.new_exercise_modal.how')}?
</h2>
<div>
<div class="mb-5 flex items-center gap-2">
{#each tags as tag}
<Badge class={selectedTag === tag ? 'bg-primary-400' : ''} onclick={() => handleTagSelection(tag)}
>{tag}</Badge
>
{/each}
</div>
{#if exerciseTemplateApi.isLoading}
<div class="grid grid-cols-2 items-start gap-4 lg:grid-cols-3 xl:grid-cols-4">
{#each Array(16) as _}
<div
class="h-[140px] w-full rounded-md border-2 border-gray-200 p-5 dark:border-neutral-600 dark:bg-neutral-700"
>
<div class="flex h-full flex-col justify-evenly">
<Skeleton class="h-4 w-3/4" />
<div class="flex flex-col items-start justify-between gap-1">
<Skeleton class="h-3 w-20" />
<Skeleton class="h-3 w-16" />
</div>
</div>
</div>
{/each}
</div>
{:else if exerciseTemplateApi.templates.length > 0}
<div class="grid grid-cols-2 items-start gap-4 lg:grid-cols-3 xl:grid-cols-4">
{#each exerciseTemplateApi.templates as template}
<button
class="h-[140px] w-full rounded-md border-2 p-5 hover:scale-95 dark:bg-neutral-700 {template.id ===
selectedTemplateId
? 'border-primary-400'
: `border-gray-200 dark:border-neutral-600 `} flex flex-col transition-all ease-in-out"
type="button"
onclick={() => (selectedTemplateId = template.id)}
>
<div class="flex h-full flex-col justify-evenly">
<p class="flex items-center text-start text-sm">
{template.title}
</p>
<div class="flex flex-col items-start justify-between gap-1">
<p class="text-xs font-normal">
{template.questions}
{$t('course.navItem.lessons.exercises.new_exercise_modal.questions')}
</p>
<p class="text-start text-xs font-normal">
{template.points}
{$t('course.navItem.lessons.exercises.new_exercise_modal.points')}
</p>
</div>
</div>
</button>
{/each}
</div>
{/if}
<div class="mt-5 flex items-center justify-between">
<Button variant="outline" onclick={handleBack}>
{$t('course.navItem.lessons.exercises.new_exercise_modal.back')}
</Button>
<Button
disabled={!selectedTemplateId || exerciseTemplateApi.templates.length === 0}
loading={isTemplateFinishedLoading}
onclick={handleTemplateSelection}
<div class="my-8 flex justify-between gap-2">
{#each options as option}
<button
class="h-60 w-[261px] rounded-md border-2 p-5 dark:bg-neutral-700 {option.type === type
? 'border-primary-400'
: `border-gray-200 dark:border-neutral-600 ${
!option.isDisabled && 'hover:scale-95'
}`} flex flex-col {option.isDisabled && 'cursor-not-allowed opacity-60'} transition-all ease-in-out"
type="button"
onclick={!option.isDisabled ? () => (type = option.type) : undefined}
>
{$t('course.navItem.lessons.exercises.new_exercise_modal.finish')}
</Button>
</div>
<div class="flex h-[70%] w-full flex-row-reverse">
<CircleCheckIcon size={16} filled={option.type === type} />
</div>
<div>
<p class="flex items-center text-start">
<span class="mr-2 text-sm">{option.title}</span>
{#if option.isDisabled}
<ComingSoon />
{/if}
</p>
<p class="text-start text-xs font-light">{option.subtitle}</p>
</div>
</button>
{/each}
</div>
<div class="mt-8 flex flex-row-reverse items-center">
<Button onclick={handleNext}>
{$t('course.navItem.lessons.exercises.new_exercise_modal.next')}
</Button>
</div>
</div>
<!-- {:else if type === Type.AI} -->
<!-- <div>
{:else if step === 1}
{#if type === Type.SCRATCH}
<div class="m-auto flex min-h-[300px] w-96 items-center justify-center">
<div class="w-full">
<h2 class="my-5 text-2xl font-medium">
{$t('course.navItem.lessons.exercises.new_exercise_modal.title')}
</h2>
<InputField
bind:value={title}
autoFocus={true}
placeholder={$t('course.navItem.lessons.exercises.new_exercise_modal.title_placeholder')}
className="my-4"
/>
<div class="mt-5 flex items-center justify-between">
<Button variant="outline" onclick={handleBack}>
{$t('course.navItem.lessons.exercises.new_exercise_modal.back')}
</Button>
<Button onclick={handleAddExercise}>
{$t('course.navItem.lessons.exercises.new_exercise_modal.finish')}
</Button>
</div>
</div>
</div>
{:else if type === Type.TEMPLATE}
<div>
<h2 class="m-0 mb-2 text-2xl font-medium">
{$t('course.navItem.lessons.exercises.new_exercise_modal.select_template')}
</h2>
<div>
<div class="mb-5 flex items-center gap-2">
{#each tags as tag}
<Badge class={selectedTag === tag ? 'bg-primary-400' : ''} onclick={() => handleTagSelection(tag)}
>{tag}</Badge
>
{/each}
</div>
{#if exerciseTemplateApi.isLoading}
<div class="grid grid-cols-2 items-start gap-4 lg:grid-cols-3 xl:grid-cols-4">
{#each Array(16) as _}
<div
class="h-[140px] w-full rounded-md border-2 border-gray-200 p-5 dark:border-neutral-600 dark:bg-neutral-700"
>
<div class="flex h-full flex-col justify-evenly">
<Skeleton class="h-4 w-3/4" />
<div class="flex flex-col items-start justify-between gap-1">
<Skeleton class="h-3 w-20" />
<Skeleton class="h-3 w-16" />
</div>
</div>
</div>
{/each}
</div>
{:else if exerciseTemplateApi.templates.length > 0}
<div class="grid grid-cols-2 items-start gap-4 lg:grid-cols-3 xl:grid-cols-4">
{#each exerciseTemplateApi.templates as template}
<button
class="h-[140px] w-full rounded-md border-2 p-5 hover:scale-95 dark:bg-neutral-700 {template.id ===
selectedTemplateId
? 'border-primary-400'
: `border-gray-200 dark:border-neutral-600 `} flex flex-col transition-all ease-in-out"
type="button"
onclick={() => (selectedTemplateId = template.id)}
>
<div class="flex h-full flex-col justify-evenly">
<p class="flex items-center text-start text-sm">
{template.title}
</p>
<div class="flex flex-col items-start justify-between gap-1">
<p class="text-xs font-normal">
{template.questions}
{$t('course.navItem.lessons.exercises.new_exercise_modal.questions')}
</p>
<p class="text-start text-xs font-normal">
{template.points}
{$t('course.navItem.lessons.exercises.new_exercise_modal.points')}
</p>
</div>
</div>
</button>
{/each}
</div>
{/if}
<div class="mt-5 flex items-center justify-between">
<Button variant="outline" onclick={handleBack}>
{$t('course.navItem.lessons.exercises.new_exercise_modal.back')}
</Button>
<Button
disabled={!selectedTemplateId || exerciseTemplateApi.templates.length === 0}
loading={isTemplateFinishedLoading}
onclick={handleTemplateSelection}
>
{$t('course.navItem.lessons.exercises.new_exercise_modal.finish')}
</Button>
</div>
</div>
</div>
<!-- {:else if type === Type.AI} -->
<!-- <div>
<div class="flex max-h-[500px] flex-row justify-between">
<div class="mr-1 w-[60%] rounded-md border px-3 py-2">
{#if note.length}
@@ -369,7 +369,7 @@
</Button>
</div>
</div> -->
{/if}
{/if}
{/if}
</Dialog.Content>
</Dialog.Root>
@@ -101,14 +101,14 @@
async function uploadDocument() {
if (!selectedFile) return;
// Prevent free plan users from bypassing UI restrictions
if ($isFreePlan) {
$lessonDocUpload.error = $t('upgrade.required');
snackbar.error($lessonDocUpload.error);
return;
}
$lessonDocUpload.isUploading = true;
try {
@@ -39,7 +39,7 @@
// Prevent free plan users from bypassing UI restrictions
if ($isFreePlan) {
formRes = {
type: 'AUTHORIZATION_ERROR',
type: 'AUTHORIZATION_ERROR',
status: 403,
message: $t('upgrade.required')
};
@@ -15,27 +15,18 @@
}
</script>
<Dialog.Root
bind:open={openDeleteModal}
>
<Dialog.Root bind:open={openDeleteModal}>
<Dialog.Content class="w-96">
<Dialog.Header>
<Dialog.Title>Delete Feed</Dialog.Title>
</Dialog.Header>
<div>
<h1 class="text-lg dark:text-white">Are you sure you want to delete this feed?</h1>
<h1 class="text-lg dark:text-white">Are you sure you want to delete this feed?</h1>
<div class="mt-5 flex items-center justify-between">
<Button
variant="outline"
onclick={() => (openDeleteModal = false)}
>
No
</Button>
<Button variant="outline" onclick={handleDelete}>
Yes
</Button>
<div class="mt-5 flex items-center justify-between">
<Button variant="outline" onclick={() => (openDeleteModal = false)}>No</Button>
<Button variant="outline" onclick={handleDelete}>Yes</Button>
</div>
</div>
</div>
</Dialog.Content>
</Dialog.Root>
@@ -127,30 +127,27 @@
</Dialog.Title>
</Dialog.Header>
<section class="w-2/ flex h-full flex-col rounded-xl pb-3">
<TextEditor
content={newPost}
onChange={(text) => {
newPost = text;
}}
editorClass="max-h-[400px]"
placeholder={$t('course.navItem.news_feed.heading_button.placeholder')}
/>
{#if errors.newPost}
<p class="text-sm text-red-500">{errors.newPost}</p>
{/if}
<div class="flex items-center justify-end py-2">
<div class="flex gap-2">
<Button
variant="outline"
onclick={resetEditor}
>
{$t('course.navItem.news_feed.heading_button.cancel')}
</Button>
<Button loading={isLoading} onclick={onPost}>
{$t('course.navItem.news_feed.heading_button.post')}
</Button>
<TextEditor
content={newPost}
onChange={(text) => {
newPost = text;
}}
editorClass="max-h-[400px]"
placeholder={$t('course.navItem.news_feed.heading_button.placeholder')}
/>
{#if errors.newPost}
<p class="text-sm text-red-500">{errors.newPost}</p>
{/if}
<div class="flex items-center justify-end py-2">
<div class="flex gap-2">
<Button variant="outline" onclick={resetEditor}>
{$t('course.navItem.news_feed.heading_button.cancel')}
</Button>
<Button loading={isLoading} onclick={onPost}>
{$t('course.navItem.news_feed.heading_button.post')}
</Button>
</div>
</div>
</div>
</section>
</section>
</Dialog.Content>
</Dialog.Root>
@@ -12,32 +12,24 @@
}
</script>
<Dialog.Root
bind:open={$deleteMemberModal.open}
>
<Dialog.Root bind:open={$deleteMemberModal.open}>
<Dialog.Content class="w-96 pt-3">
<Dialog.Header class="py-2 px-5">
<Dialog.Header class="px-5 py-2">
<Dialog.Title>{$t('course.navItem.people.delete_confirmation.title')}</Dialog.Title>
</Dialog.Header>
<div>
<p class="mt-0 text-base dark:text-white">
{$t('course.navItem.people.delete_confirmation.sure')} <strong>{email}</strong>?
</p>
<p class="mt-0 text-base dark:text-white">
{$t('course.navItem.people.delete_confirmation.sure')} <strong>{email}</strong>?
</p>
<div class="mt-5 flex items-center justify-between">
<Button
variant="outline"
onclick={() => ($deleteMemberModal.open = false)}
>
{$t('course.navItem.people.delete_confirmation.no')}
</Button>
<Button
variant="outline"
onclick={handleDelete}
>
{$t('course.navItem.people.delete_confirmation.yes')}
</Button>
<div class="mt-5 flex items-center justify-between">
<Button variant="outline" onclick={() => ($deleteMemberModal.open = false)}>
{$t('course.navItem.people.delete_confirmation.no')}
</Button>
<Button variant="outline" onclick={handleDelete}>
{$t('course.navItem.people.delete_confirmation.yes')}
</Button>
</div>
</div>
</div>
</Dialog.Content>
</Dialog.Root>