feat: certificates, landing pages, ai tutor, website update, compliance

This commit is contained in:
Rotimi Best
2026-05-18 03:47:22 +01:00
parent c31ad63955
commit ac3a2c14a7
644 changed files with 56050 additions and 5439 deletions
@@ -0,0 +1,40 @@
<script lang="ts">
import * as Avatar from '@cio/ui/base/avatar';
import * as Sidebar from '@cio/ui/base/sidebar';
import { Skeleton } from '@cio/ui/base/skeleton';
import { resolve } from '$app/paths';
import { currentOrg } from '$lib/utils/store/org';
import { shortenName } from '$lib/utils/functions/string';
import { courseApi } from '$features/course/api';
import { getNavItemRoute } from '$features/course/utils/functions';
const courseTitle = $derived(courseApi.course?.title);
const courseId = $derived(courseApi.course?.id);
</script>
<Sidebar.Menu>
<Sidebar.MenuItem>
<Sidebar.MenuButton
size="sm"
class="ui:data-[state=open]:bg-sidebar-accent ui:data-[state=open]:text-sidebar-accent-foreground"
>
{#snippet child({ props })}
<a href={courseId ? resolve(getNavItemRoute(courseId), {}) : '#'} {...props}>
{#if $currentOrg.name}
<Avatar.Root class="flex size-6! items-center justify-center rounded-md!">
<Avatar.Image src={$currentOrg.avatarUrl} alt={$currentOrg.name} />
<Avatar.Fallback class="rounded-md! text-xs">{shortenName($currentOrg.name)}</Avatar.Fallback>
</Avatar.Root>
{#if courseTitle}
<span class="truncate text-sm font-medium">{courseTitle}</span>
{:else}
<Skeleton class="h-4 w-24" />
{/if}
{:else}
<Skeleton class="h-4 w-24" />
{/if}
</a>
{/snippet}
</Sidebar.MenuButton>
</Sidebar.MenuItem>
</Sidebar.Menu>
@@ -8,6 +8,7 @@
import { BackButton } from '@cio/ui';
import TableOfContentsIcon from '@lucide/svelte/icons/table-of-contents';
import BotIcon from '@lucide/svelte/icons/bot';
import {
AnalyticsIcon,
AttendanceIcon,
@@ -176,6 +177,16 @@
},
icon: getNavIcon(NAV_IDS.PEOPLE)
},
{
id: NAV_IDS.AI_ASSISTANT,
title: $t('course.navItems.nav_ai_tutor'),
url: getNavItemRoute(id, 'ai-tutor'),
isActive: (path || page.url.pathname) === getNavItemRoute(id, 'ai-tutor'),
show() {
return !isStudent;
},
icon: getNavIcon(NAV_IDS.AI_ASSISTANT)
},
{
id: NAV_IDS.SETTINGS,
title: $t('course.navItems.nav_settings'),
@@ -246,6 +257,8 @@
return CertificateIcon;
} else if (id === NAV_IDS.SETTINGS) {
return SettingsIcon;
} else if (id === NAV_IDS.AI_ASSISTANT) {
return BotIcon;
}
return null;
@@ -6,12 +6,11 @@
import { profile } from '$lib/utils/store/user';
import { orgs } from '$lib/utils/store/org';
import { isOrgStudent } from '$lib/utils/store/app';
import { isFreePlan } from '$lib/utils/store/org';
import OrgLogo from '$features/ui/sidebar/org-sidebar/org-logo.svelte';
import CourseSidebarLogo from './course-sidebar-logo.svelte';
import Navigation from './course-sidebar-navigation.svelte';
import SidebarSkeleton from '$features/ui/sidebar/sidebar-skeleton.svelte';
import UpgradePoweredBy from '$features/ui/upgrade-powered-by.svelte';
import PoweredBy from '$features/ui/powered-by.svelte';
import { courseApi } from '$features/course/api';
import { useSidebar } from '@cio/ui/base/sidebar';
import { startResizablePanelDrag } from '$lib/utils/functions/resizable-panel';
import { COURSE_SIDEBAR_DEFAULT_WIDTH, COURSE_SIDEBAR_MAX_WIDTH, COURSE_SIDEBAR_MIN_WIDTH } from './constants';
@@ -45,6 +44,8 @@
let shouldIgnoreRailClick = $state(false);
let stopSidebarResize: (() => void) | null = null;
const attributionCourseSlug = $derived(courseApi.course?.slug ?? null);
function clampSidebarWidth(width: number) {
return Math.min(COURSE_SIDEBAR_MAX_WIDTH, Math.max(COURSE_SIDEBAR_MIN_WIDTH, width));
}
@@ -113,7 +114,7 @@
{:else}
<Sidebar.Root collapsible="icon">
<Sidebar.Header>
<OrgLogo />
<CourseSidebarLogo />
</Sidebar.Header>
<Sidebar.Content>
@@ -141,14 +142,14 @@
<Sidebar.Rail onclick={handleRailClick} onpointerdown={handleRailPointerDown} />
{#if $isFreePlan}
<Sidebar.Footer>
<UpgradePoweredBy
class="static [&_span]:font-normal [&_span]:shadow-none"
showOnlyLogo={!sidebar.open || sidebar.isMobile}
/>
</Sidebar.Footer>
{/if}
<Sidebar.Footer>
<PoweredBy
variant="sidebar"
sidebarUtmSource="lms-course-sidebar"
courseSlug={attributionCourseSlug}
showOnlyLogo={!sidebar.open || sidebar.isMobile}
/>
</Sidebar.Footer>
</Sidebar.Root>
{/if}