feat: widgets and ai chat
This commit is contained in:
@@ -1,31 +1,52 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { Badge } from '@cio/ui/base/badge';
|
||||
import { Button } from '@cio/ui/base/button';
|
||||
import { Progress } from '@cio/ui/base/progress';
|
||||
import { t } from '$lib/utils/functions/translations';
|
||||
import { Empty } from '@cio/ui/custom/empty';
|
||||
import BookOpenIcon from '@lucide/svelte/icons/book-open';
|
||||
import { coursesApi } from '$features/course/api';
|
||||
import { calcCourseProgress } from '$features/course/utils/functions';
|
||||
import {
|
||||
getStudentCourseComplianceDate,
|
||||
getStudentCourseComplianceStatusKey,
|
||||
getStudentCourseComplianceStatusVariant,
|
||||
getStudentCourseProgressPercent
|
||||
} from '$features/course/utils/compliance-utils';
|
||||
|
||||
const gotoCourse = (id: string | undefined) => {
|
||||
if (!id) return;
|
||||
goto(`/courses/${id}/lessons?next=true`);
|
||||
};
|
||||
|
||||
let last3Courses = $derived(coursesApi.enrolledCourses.length > 0 ? coursesApi.enrolledCourses.slice(0, 3) : []);
|
||||
let highlightedCourses = $derived.by(() => {
|
||||
const courses = [...coursesApi.enrolledCourses];
|
||||
|
||||
function getCourseProgress(course: (typeof coursesApi.enrolledCourses)[number]): number {
|
||||
const exercisesCompleted =
|
||||
'exercisesCompleted' in course && typeof course.exercisesCompleted === 'number' ? course.exercisesCompleted : 0;
|
||||
const totalExercises =
|
||||
'exerciseCount' in course && typeof course.exerciseCount === 'number' ? course.exerciseCount : 0;
|
||||
return calcCourseProgress({
|
||||
lessonsCompleted: course.progressRate ?? 0,
|
||||
totalLessons: course.lessonCount ?? 0,
|
||||
exercisesCompleted,
|
||||
totalExercises
|
||||
});
|
||||
return courses
|
||||
.sort((leftCourse, rightCourse) => {
|
||||
const leftComplianceDate = getStudentCourseComplianceDate(leftCourse)?.value;
|
||||
const rightComplianceDate = getStudentCourseComplianceDate(rightCourse)?.value;
|
||||
const leftTimestamp = leftComplianceDate ? new Date(leftComplianceDate).getTime() : Number.POSITIVE_INFINITY;
|
||||
const rightTimestamp = rightComplianceDate ? new Date(rightComplianceDate).getTime() : Number.POSITIVE_INFINITY;
|
||||
|
||||
return leftTimestamp - rightTimestamp;
|
||||
})
|
||||
.slice(0, 3);
|
||||
});
|
||||
|
||||
function formatDate(value: string | null | undefined) {
|
||||
if (!value) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const date = new Date(value);
|
||||
if (Number.isNaN(date.getTime())) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return new Intl.DateTimeFormat('en-US', {
|
||||
dateStyle: 'medium'
|
||||
}).format(date);
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -36,9 +57,9 @@
|
||||
<div
|
||||
class="flex w-full flex-col items-center gap-2 rounded border border-[#EAEAEA] p-3 lg:h-[40vh] dark:bg-neutral-800"
|
||||
>
|
||||
{#if last3Courses.length > 0}
|
||||
{#if highlightedCourses.length > 0}
|
||||
<div class="flex h-full w-full flex-col justify-start overflow-y-auto">
|
||||
{#each last3Courses as course}
|
||||
{#each highlightedCourses as course}
|
||||
<div class="p-5">
|
||||
<span class="flex flex-col items-start gap-3 pb-5 lg:flex-row">
|
||||
<img
|
||||
@@ -51,12 +72,30 @@
|
||||
<p class="line-clamp-2 text-xs font-normal text-[#656565] dark:text-white">
|
||||
{course.description}
|
||||
</p>
|
||||
|
||||
{#if course.type === 'COMPLIANCE'}
|
||||
{@const complianceStatusKey = getStudentCourseComplianceStatusKey(course)}
|
||||
{@const complianceDate = getStudentCourseComplianceDate(course)}
|
||||
<div class="mt-2 flex flex-wrap items-center gap-2">
|
||||
{#if complianceStatusKey}
|
||||
<Badge variant={getStudentCourseComplianceStatusVariant(course)}>
|
||||
{$t(complianceStatusKey)}
|
||||
</Badge>
|
||||
{/if}
|
||||
|
||||
{#if complianceDate?.value}
|
||||
<p class="ui:text-muted-foreground text-xs dark:text-white">
|
||||
{$t(complianceDate.labelKey)}: {formatDate(complianceDate.value)}
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<Button variant="outline" onclick={() => gotoCourse(course.id)}>
|
||||
{$t('dashboard.continue')}
|
||||
</Button>
|
||||
</span>
|
||||
<Progress value={getCourseProgress(course)} max={100} class="ui:h-1" />
|
||||
<Progress value={getStudentCourseProgressPercent(course)} max={100} class="ui:h-1" />
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user