feat: widgets and ai chat

This commit is contained in:
Rotimi Best
2026-04-27 11:40:38 +01:00
parent ae1801f85f
commit 5717dd8004
391 changed files with 28177 additions and 22467 deletions
@@ -25,6 +25,7 @@
import ContentRow from './content-row.svelte';
import ContentActions from './content-actions.svelte';
import { deleteContent, saveContent, toggleLock } from './content-action-helpers';
import { isSelfPacedLikeCourse } from '$features/course/utils/compliance-utils';
const flipDurationMs = 300;
@@ -45,7 +46,7 @@
const contentData = $derived(getCourseContent(courseApi.course));
const isStudentView = $derived($isOrgStudent === true);
const isLiveCourse = $derived(courseApi.course?.type === 'LIVE_CLASS');
const isSelfPacedCourse = $derived(courseApi.course?.type === 'SELF_PACED');
const isSelfPacedCourse = $derived(isSelfPacedLikeCourse(courseApi.course?.type));
const metaChipClass =
'inline-flex items-center gap-1 rounded-full bg-gray-100 px-2 py-1 text-[10px] text-gray-700 dark:bg-neutral-700 dark:text-gray-200';
@@ -262,7 +263,7 @@
/>
<section
class="mx-auto w-full p-3 lg:w-11/12 lg:px-4 {reorder ? 'cursor-grab active:cursor-grabbing' : ''}"
class="mx-auto w-full p-3 lg:w-11/12 {reorder ? 'cursor-grab active:cursor-grabbing' : ''}"
use:dndzone={{
items: contentItems,
flipDurationMs,
@@ -63,9 +63,7 @@
const showMarkComplete = $derived(!!lessonId && !exerciseId);
const currentLessonItem = $derived(lessonId ? lessonItems.find((l) => l.id === lessonId) : null);
const isLessonLocked = $derived(
$isOrgStudent && currentLessonItem && !(currentLessonItem.isUnlocked ?? false)
);
const isLessonLocked = $derived($isOrgStudent && currentLessonItem && !(currentLessonItem.isUnlocked ?? false));
const isMarkCompleteDisabled = $derived(isMarkingComplete || isLessonLocked || isLessonComplete);
async function markLessonComplete(currentLessonId: string) {
@@ -283,6 +283,7 @@
const wasDeleted = await contentApi.deleteContent(courseId, { sectionId: deletingData.id });
if (wasDeleted) {
removeSection(deletingData.id);
snackbar.success('snackbar.lessons.section_deleted');
}
return;
}
@@ -438,7 +439,7 @@
<DeleteLessonConfirmation bind:openDeleteModal {deleteLesson} />
<section
class="mx-auto w-full p-3 lg:w-11/12 lg:px-4"
class="mx-auto w-full p-3 lg:w-11/12"
use:dndzone={{
items: sections,
flipDurationMs,
@@ -462,7 +463,8 @@
: `${$t('course.navItem.lessons.add_lesson.unlock')} ${$t('course.navItem.lessons.add_lesson.all')}`
: undefined}
<div
class="border-border m-auto mb-3 max-w-xl rounded-md border transition dark:bg-neutral-800"
id={isUngrouped ? undefined : `section-${section.id}`}
class="border-border m-auto mb-3 max-w-xl scroll-mt-24 rounded-md border transition dark:bg-neutral-800"
style={reorder ? 'border: 2px #1d4ed8 solid; border-style: dashed; cursor: grab;' : ''}
>
<SectionHeader
@@ -518,7 +520,7 @@
{@const actionDisabled = Boolean($contentEditingStore && !isEditingItem)}
<div
class="mb-2 flex min-h-[50px] max-w-xl items-start justify-between rounded-md border border-gray-200 px-3 py-1 transition"
class="ui:border mb-2 flex min-h-[50px] max-w-xl items-start justify-between rounded-md px-3 py-1 transition"
style={reorder ? 'border: 2px #1d4ed8 solid; border-style: dashed; cursor: grab;' : ''}
animate:flip={{ duration: flipDurationMs }}
>
@@ -67,7 +67,7 @@
{:else}
<div class="flex w-4/6 items-center gap-2">
<CourseContentIcon type={ContentType.Section} size={16} />
<p class="font-semibold">{section.title}</p>
<p class="text-sm">{section.title}</p>
<ContentCountBadges lessons={sectionCount.lessons} exercises={sectionCount.exercises} />
</div>
{/if}