CLASSROOMIO-T-108: Connect my learning page to BE
This commit is contained in:
@@ -142,11 +142,20 @@
|
||||
}
|
||||
isSaving = true;
|
||||
try {
|
||||
const { course_title, course_description, image, tabs, grading, lesson_download } = $settings;
|
||||
const {
|
||||
course_title,
|
||||
course_description,
|
||||
image,
|
||||
tabs,
|
||||
grading,
|
||||
lesson_download,
|
||||
is_published
|
||||
} = $settings;
|
||||
await updateCourse($course.id, avatar, {
|
||||
title: course_title,
|
||||
description: course_description,
|
||||
logo: image,
|
||||
is_published,
|
||||
metadata: {
|
||||
...(isObject($course.metadata) ? $course.metadata : {}),
|
||||
lessonTabsOrder: tabs,
|
||||
@@ -158,6 +167,7 @@
|
||||
$course.title = course_title;
|
||||
$course.description = course_description;
|
||||
$course.logo = image;
|
||||
$course.is_published = is_published;
|
||||
$course.metadata = {
|
||||
...(isObject($course.metadata) ? $course.metadata : {}),
|
||||
lessonTabsOrder: tabs,
|
||||
@@ -173,6 +183,7 @@
|
||||
};
|
||||
|
||||
function setDefault(course) {
|
||||
console.log('course.is_published', course.is_published);
|
||||
if (course && Object.keys(course).length) {
|
||||
$settings = {
|
||||
course_title: course.title,
|
||||
@@ -180,7 +191,8 @@
|
||||
image: course.logo,
|
||||
tabs: course.metadata.lessonTabsOrder || $settings.tabs,
|
||||
grading: course.metadata.grading,
|
||||
lesson_download: course.metadata.lessonDownload
|
||||
lesson_download: course.metadata.lessonDownload,
|
||||
is_published: course.is_published
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -305,6 +317,21 @@
|
||||
/>
|
||||
</Column>
|
||||
</Row>
|
||||
|
||||
<!-- Publish Course -->
|
||||
<Row class="flex lg:flex-row flex-col py-7 border-bottom-c">
|
||||
<Column sm={8} md={8} lg={8}>
|
||||
<SectionTitle>Publish Course</SectionTitle>
|
||||
<p>This determines if your course displays on your landing page</p>
|
||||
</Column>
|
||||
<Column sm={8} md={8} lg={8}>
|
||||
<Toggle size="sm" bind:toggled={$settings.is_published}>
|
||||
<span slot="labelA" style="color: gray">Unpublished</span>
|
||||
<span slot="labelB" style="color: gray">Published</span>
|
||||
</Toggle>
|
||||
</Column>
|
||||
</Row>
|
||||
|
||||
<Row class="flex lg:flex-row flex-col py-7 border-bottom-c">
|
||||
<Column sm={8} md={8} lg={8}>
|
||||
<SectionTitle>Delete course</SectionTitle>
|
||||
|
||||
@@ -10,5 +10,6 @@ export const settings = writable({
|
||||
{ id: 2, name: 'Slide' },
|
||||
{ id: 3, name: 'Video' }
|
||||
],
|
||||
lesson_download: false
|
||||
lesson_download: false,
|
||||
is_published: false
|
||||
});
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { writable } from 'svelte/store';
|
||||
import type { Writable } from 'svelte/store';
|
||||
import { lessons } from './components/Lesson/store/lessons';
|
||||
//@ts-ignore
|
||||
import { ROLE } from '$lib/utils/constants/roles';
|
||||
import type { Course } from '$lib/utils/types';
|
||||
|
||||
@@ -14,6 +13,9 @@ export const course: Writable<Course> = writable({
|
||||
status: 'ACTIVE',
|
||||
is_certificate_downloadable: false,
|
||||
certificate_theme: 'professional',
|
||||
is_published: false,
|
||||
created_at: new Date().toDateString(),
|
||||
updated_at: new Date().toDateString(),
|
||||
metadata: {
|
||||
requirements: '',
|
||||
description: '',
|
||||
|
||||
@@ -6,7 +6,8 @@ export async function fetchCourses(profileId, orgId) {
|
||||
// Gets courses for a particular organisation where the current logged in user is a groupmember
|
||||
const { data: allCourses } = await supabase
|
||||
.rpc('get_courses', {
|
||||
org_id_arg: orgId
|
||||
org_id_arg: orgId,
|
||||
profile_id_arg: profileId
|
||||
})
|
||||
.eq('profile_id', profileId);
|
||||
|
||||
|
||||
@@ -1,50 +1,25 @@
|
||||
<script lang="ts">
|
||||
import { Tag, ImageLoader, SkeletonPlaceholder } from 'carbon-components-svelte';
|
||||
// import { ROLE } from '$lib/utils/constants/roles';
|
||||
import getCurrencyFormatter from '$lib/utils/functions/getCurrencyFormatter';
|
||||
import { VARIANTS } from '$lib/components/PrimaryButton/constants';
|
||||
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
|
||||
|
||||
export let bannerImage: string | undefined;
|
||||
export let id = '';
|
||||
export let slug = '';
|
||||
export let title = '';
|
||||
export let description = '';
|
||||
// export let role_id = ROLE.STUDENT;
|
||||
export let isPublished = false;
|
||||
export let cost = 0;
|
||||
export let totalLessons = 0;
|
||||
export let currency = 'NGN';
|
||||
export let isOnLandingPage = false;
|
||||
// export let onDelete = (id: string) => {};
|
||||
export let isLMS = false;
|
||||
export let progressRate = 45;
|
||||
|
||||
let formatter = getCurrencyFormatter(currency);
|
||||
// function getOptions() {
|
||||
// return [
|
||||
// {
|
||||
// label: 'Make a copy',
|
||||
// // Show this option only if user has admin control over this course.
|
||||
// show: () => role_id === ROLE.ADMIN || role_id === ROLE.TUTOR,
|
||||
// onClick() {
|
||||
// // IIFI of copyCourseModal
|
||||
// (() => {
|
||||
// $copyCourseModal.id = id;
|
||||
// $copyCourseModal.open = true;
|
||||
// $copyCourseModal.title = `Copy of ${title}`;
|
||||
// })();
|
||||
// }
|
||||
// }
|
||||
// // {
|
||||
// // label: 'Delete',
|
||||
// // show: () => role_id === ROLE.TUTOR,
|
||||
// // onClick() {
|
||||
// // (async () => {
|
||||
// // await deleteCourse(id);
|
||||
// // onDelete(id);
|
||||
// // console.log('Deleting');
|
||||
// // })();
|
||||
// // }
|
||||
// // }
|
||||
// ].filter((option) => option.show());
|
||||
// }
|
||||
$: console.log(progressRate);
|
||||
|
||||
$: formatter = getCurrencyFormatter(currency);
|
||||
</script>
|
||||
|
||||
<a
|
||||
@@ -72,21 +47,43 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="px-4 border border-gray border-b-0 border-l-0 border-r-0 footer">
|
||||
<p class="text-xs pt-2 pl-2 dark:text-white">{totalLessons} lessons</p>
|
||||
<p class="text-xs py-2">
|
||||
{#if isOnLandingPage}
|
||||
<span class="px-2">{!cost ? 'Free' : formatter.format(cost)}</span>
|
||||
{:else}
|
||||
<Tag type={isPublished ? 'green' : 'cool-gray'}>
|
||||
{#if isPublished}
|
||||
Published
|
||||
{:else}
|
||||
Unpublished
|
||||
{/if}
|
||||
</Tag>
|
||||
{/if}
|
||||
</p>
|
||||
<div
|
||||
class="px-4 border border-gray border-b-0 border-l-0 border-r-0 flex justify-between items-center"
|
||||
>
|
||||
<div>
|
||||
<p class="text-xs pt-2 {!isLMS && 'pl-2'} dark:text-white">{totalLessons} lessons</p>
|
||||
<p class="text-xs py-2">
|
||||
{#if isOnLandingPage}
|
||||
<span class="px-2">{!cost ? 'Free' : formatter.format(cost)}</span>
|
||||
{:else if isLMS}
|
||||
<div class="flex items-center gap-2">
|
||||
<div class=" relative bg-[#EAEAEA] w-[50px] h-1">
|
||||
<div
|
||||
style="width:{progressRate}%"
|
||||
class={`absolute top-0 left-0 bg-primary-700 h-full`}
|
||||
/>
|
||||
</div>
|
||||
<p class="text-xs text-[#656565] dark:text-white">{progressRate}%</p>
|
||||
</div>
|
||||
{:else}
|
||||
<Tag type={isPublished ? 'green' : 'cool-gray'}>
|
||||
{#if isPublished}
|
||||
Published
|
||||
{:else}
|
||||
Unpublished
|
||||
{/if}
|
||||
</Tag>
|
||||
{/if}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{#if isLMS}
|
||||
<PrimaryButton
|
||||
label={'Continue Course'}
|
||||
variant={VARIANTS.OUTLINED}
|
||||
className="rounded-none text-primary-600"
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
</a>
|
||||
|
||||
@@ -97,7 +94,7 @@
|
||||
}
|
||||
.title,
|
||||
.description {
|
||||
height: 40px;
|
||||
height: 42px;
|
||||
line-height: 20px;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
|
||||
@@ -3,14 +3,23 @@
|
||||
import Card from './components/Card/index.svelte';
|
||||
import CardLoader from './components/Card/Loader.svelte';
|
||||
import CoursesEmptyIcon from '../Icons/CoursesEmptyIcon.svelte';
|
||||
import CopyCourseModal from './components/CopyCourseModal/index.svelte';
|
||||
// import CopyCourseModal from './components/CopyCourseModal/index.svelte';
|
||||
import { courseMetaDeta } from './store';
|
||||
import type { Course } from '$lib/utils/types';
|
||||
import { appStore } from '$lib/utils/store/app';
|
||||
|
||||
export let courses: Course[] = [];
|
||||
|
||||
function calcProgressRate(progressRate?: number, totalLessons?: number): number {
|
||||
if (!progressRate || !totalLessons) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Math.round((progressRate / totalLessons) * 100);
|
||||
}
|
||||
</script>
|
||||
|
||||
<CopyCourseModal />
|
||||
<!-- <CopyCourseModal /> -->
|
||||
|
||||
<div class={`w-full ${$courseMetaDeta.isLoading || courses ? 'cards-container' : ''} my-4 mx-auto`}>
|
||||
{#if $courseMetaDeta.isLoading}
|
||||
@@ -30,6 +39,8 @@
|
||||
cost={courseData.cost}
|
||||
currency={courseData.currency}
|
||||
totalLessons={courseData.total_lessons}
|
||||
isLMS={$appStore.isOrgSite}
|
||||
progressRate={calcProgressRate(courseData.progress_rate, courseData.total_lessons)}
|
||||
/>
|
||||
{/key}
|
||||
{/each}
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
<script>
|
||||
import { goto } from '$app/navigation';
|
||||
import Box from '$lib/components/Box/index.svelte';
|
||||
import CoursesEmptyIcon from '$lib/components/Icons/CoursesEmptyIcon.svelte';
|
||||
import { VARIANTS } from '$lib/components/PrimaryButton/constants';
|
||||
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
|
||||
import { mockData } from './store';
|
||||
|
||||
const gotoCourse = (title) => {
|
||||
goto(`/courses/${title}`);
|
||||
};
|
||||
</script>
|
||||
|
||||
<section>
|
||||
{#if mockData.length > 0}
|
||||
<div
|
||||
class="flex items-center justify-center md:justify-start flex-wrap gap-4 dark:text-white mt-2"
|
||||
>
|
||||
{#each mockData as data}
|
||||
<div
|
||||
class="w-80 min-h-[350px] flex items-start justify-start flex-wrap flex-col border rounded mr-2 mb-5 border-gray-200"
|
||||
>
|
||||
<img src={data.src} alt="course" class="w-80 h-[180px] object-cover" />
|
||||
|
||||
<div class="w-full p-3">
|
||||
<p
|
||||
class="line-clamp-2 text-xl lg:text-2xl font-normal text-[#262626] py-1 dark:text-white"
|
||||
>
|
||||
{data.title}
|
||||
</p>
|
||||
<p class="line-clamp-2 text-base text-[#656565] dark:text-white py-1">
|
||||
{data.description}
|
||||
</p>
|
||||
<p class="py-1 text-[#7888B7] dark:text-white">By {data.author}</p>
|
||||
<span class="flex flex-wrap flex-row items-start gap-2 justify-between mt-8">
|
||||
<span>
|
||||
<p>{data.lessons} lessons</p>
|
||||
{#if data.inProgress}
|
||||
<div class="flex items-center gap-2">
|
||||
<div class=" relative bg-[#EAEAEA] w-full h-1">
|
||||
<div
|
||||
class={`absolute top-0 left-0 bg-primary-700 h-full ${data.progress} ]`}
|
||||
/>
|
||||
</div>
|
||||
<p class="text-xs text-[#656565] dark:text-white">{data.progressRate}%</p>
|
||||
</div>
|
||||
{/if}
|
||||
</span>
|
||||
<PrimaryButton
|
||||
label={data.inProgress ? 'Continue Course' : 'Start Course'}
|
||||
variant={VARIANTS.OUTLINED}
|
||||
className="rounded-none text-[#0233BD]"
|
||||
onClick={() => gotoCourse(data.title)}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
<Box className="flex flex-col items-center mt-2">
|
||||
<CoursesEmptyIcon />
|
||||
<h3 class="text-center">You haven't started any course yet</h3>
|
||||
<p class="w-[70%] md:w-[35%] text-center">
|
||||
Once you start a course, your progress will reflect here
|
||||
</p>
|
||||
</Box>
|
||||
{/if}
|
||||
</section>
|
||||
@@ -1,14 +1,8 @@
|
||||
<script>
|
||||
import { goto } from '$app/navigation';
|
||||
import Box from '$lib/components/Box/index.svelte';
|
||||
import CoursesEmptyIcon from '$lib/components/Icons/CoursesEmptyIcon.svelte';
|
||||
import { VARIANTS } from '$lib/components/PrimaryButton/constants';
|
||||
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
|
||||
import { mockData, courseInProgress } from './store';
|
||||
|
||||
const gotoCourse = (title) => {
|
||||
goto(`/courses/${title}`);
|
||||
};
|
||||
import { courseInProgress } from './store';
|
||||
import Card from '$lib/components/Courses/components/Card/index.svelte';
|
||||
</script>
|
||||
|
||||
<section>
|
||||
@@ -17,44 +11,16 @@
|
||||
class="flex items-center justify-center md:justify-start flex-wrap gap-4 dark:text-white mt-2"
|
||||
>
|
||||
{#each courseInProgress as data}
|
||||
<div
|
||||
class="w-80 min-h-[350px] flex items-start justify-start flex-wrap flex-col border rounded mr-2 mb-5 border-gray-200"
|
||||
>
|
||||
<img src={data.src} alt="course" class="w-80 h-[180px] object-cover" />
|
||||
|
||||
<div class="w-full p-3">
|
||||
<p
|
||||
class="line-clamp-2 text-xl lg:text-2xl font-normal text-[#262626] py-1 dark:text-white"
|
||||
>
|
||||
{data.title}
|
||||
</p>
|
||||
<p class="line-clamp-2 text-base text-[#656565] dark:text-white py-1">
|
||||
{data.description}
|
||||
</p>
|
||||
<p class="py-1 text-[#7888B7] dark:text-white">By {data.author}</p>
|
||||
<span class="flex flex-wrap flex-row items-start gap-2 justify-between mt-8">
|
||||
<span>
|
||||
<p>{data.lessons} lessons</p>
|
||||
{#if data.inProgress}
|
||||
<div class="flex items-center gap-2">
|
||||
<div class=" relative bg-[#EAEAEA] w-full h-1">
|
||||
<div
|
||||
class={`absolute top-0 left-0 bg-primary-700 h-full ${data.progress} ]`}
|
||||
/>
|
||||
</div>
|
||||
<p class="text-xs text-[#656565] dark:text-white">{data.progressRate}%</p>
|
||||
</div>
|
||||
{/if}
|
||||
</span>
|
||||
<PrimaryButton
|
||||
label={data.inProgress ? 'Continue Course' : 'Start Course'}
|
||||
variant={VARIANTS.OUTLINED}
|
||||
className="rounded-none text-[#0233BD]"
|
||||
onClick={() => gotoCourse(data.title)}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{#key data.id}
|
||||
<Card
|
||||
id={data.id}
|
||||
bannerImage={data.src || '/images/classroomio-course-img-template.jpg'}
|
||||
title={data.title}
|
||||
description={data.description}
|
||||
isLMS={true}
|
||||
progressRate={data.progressRate}
|
||||
/>
|
||||
{/key}
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export const mockData = [
|
||||
{
|
||||
id: '1',
|
||||
title: 'Photo editing 101',
|
||||
description:
|
||||
'Learn the different techniques to edit photos using Adobe Lightroom.Learn the basics of figma and start design web apps for the world ...Learn the basics of figma and start design web apps for the world ...',
|
||||
@@ -12,6 +13,7 @@ export const mockData = [
|
||||
progressRate: 45
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
title: 'Colorful digital painting',
|
||||
description:
|
||||
'Learn the different techniques to edit photos using Adobe Lightroom.Learn the basics of figma and start design web apps for the world ...Learn the basics of figma and start design web apps for the world ...',
|
||||
@@ -24,6 +26,7 @@ export const mockData = [
|
||||
progressRate: 45
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
title: 'AI for dummys',
|
||||
description:
|
||||
'Learn the different techniques to edit photos using Adobe Lightroom.Learn the basics of figma and start design web apps for the world ...Learn the basics of figma and start design web apps for the world ...',
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
import Close from 'carbon-icons-svelte/lib/Close.svelte';
|
||||
|
||||
import IconButton from '$lib/components/IconButton/index.svelte';
|
||||
import { course } from '$lib/components/Course/store';
|
||||
import { appStore } from '$lib/utils/store/app';
|
||||
import { currentOrgPath } from '$lib/utils/store/org';
|
||||
import { toggleBodyByMode } from '$lib/utils/functions/app';
|
||||
@@ -33,13 +32,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
function getPathName(_isCoursePage = false, slug = '') {
|
||||
if (!_isCoursePage) {
|
||||
return '/';
|
||||
}
|
||||
|
||||
return slug ? `/course/${slug}` : `${$currentOrgPath}/courses`;
|
||||
}
|
||||
$: coursesPath = $appStore.isOrgSite ? '/lms/mylearning' : `${$currentOrgPath}/courses`;
|
||||
</script>
|
||||
|
||||
<nav
|
||||
@@ -61,7 +54,7 @@
|
||||
<div class="hidden md:block">
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
goto(`${$currentOrgPath}/courses`);
|
||||
goto(coursesPath);
|
||||
}}
|
||||
size="small"
|
||||
>
|
||||
@@ -70,8 +63,7 @@
|
||||
</div>
|
||||
{/if}
|
||||
<a
|
||||
href={getPathName(isCoursePage, $course.slug)}
|
||||
target={isCoursePage && $course.slug ? '_blank' : ''}
|
||||
href={coursesPath}
|
||||
title="Go to {isCoursePage ? 'Courses' : 'ClassroomIO Home'}"
|
||||
id="logo"
|
||||
class="text-lg {isCoursePage && '-ml-2 md:ml-2'} line-clamp-1"
|
||||
|
||||
@@ -21,9 +21,9 @@ interface CourseMetadata {
|
||||
imgUrl: string;
|
||||
};
|
||||
reviews?: Array<Review>;
|
||||
lessonTabsOrder: Array<Tabs>;
|
||||
grading: boolean;
|
||||
lessonDownload: boolean;
|
||||
lessonTabsOrder?: Array<Tabs>;
|
||||
grading?: boolean;
|
||||
lessonDownload?: boolean;
|
||||
}
|
||||
|
||||
// Generated from https://supabase-schema.vercel.app/
|
||||
@@ -106,6 +106,9 @@ export interface Course {
|
||||
is_certificate_downloadable?: boolean;
|
||||
certificate_theme?: string;
|
||||
status: string;
|
||||
is_published?: boolean;
|
||||
progress_rate?: number;
|
||||
total_lessons?: number;
|
||||
}
|
||||
|
||||
export interface Groupmember {
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
progressPercentage
|
||||
} from '$lib/components/LMS/components/store';
|
||||
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
|
||||
import Box from '$lib/components/Box/index.svelte';
|
||||
import { VARIANTS } from '$lib/components/PrimaryButton/constants';
|
||||
import { goto } from '$app/navigation';
|
||||
</script>
|
||||
|
||||
@@ -1,34 +1,49 @@
|
||||
<script>
|
||||
import TextField from '$lib/components/Form/TextField.svelte';
|
||||
|
||||
<script lang="ts">
|
||||
import { Search } from 'carbon-components-svelte';
|
||||
import AllLearning from '$lib/components/LMS/components/AllLearning.svelte';
|
||||
import Completed from '$lib/components/LMS/components/Completed.svelte';
|
||||
import InProgress from '$lib/components/LMS/components/InProgress.svelte';
|
||||
import { courseInProgress } from '$lib/components/LMS/components/store';
|
||||
import Tabs from '$lib/components/Tabs/index.svelte';
|
||||
import TabContent from '$lib/components/TabContent/index.svelte';
|
||||
|
||||
// let tabs = CONSTANTS.tabs;
|
||||
import Courses from '$lib/components/Courses/index.svelte';
|
||||
import { courses, courseMetaDeta } from '$lib/components/Courses/store';
|
||||
import { profile } from '$lib/utils/store/user';
|
||||
import { fetchCourses } from '$lib/components/Courses/api';
|
||||
import { currentOrg } from '$lib/utils/store/org';
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
label: 'All',
|
||||
label: 'In progress',
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
label: 'In progress',
|
||||
value: 2
|
||||
},
|
||||
{
|
||||
label: 'Complete',
|
||||
value: 3
|
||||
value: 2
|
||||
}
|
||||
];
|
||||
let currentTab = tabs[0].value;
|
||||
let hasFetched = false;
|
||||
|
||||
const onChange =
|
||||
(tab = 0) =>
|
||||
() =>
|
||||
(currentTab = tab);
|
||||
|
||||
async function getCourses(userId: string | null, orgId: string) {
|
||||
if (!hasFetched) {
|
||||
$courseMetaDeta.isLoading = true;
|
||||
|
||||
const coursesResult = await fetchCourses(userId, orgId);
|
||||
console.log(`coursesResult`, coursesResult);
|
||||
|
||||
$courseMetaDeta.isLoading = false;
|
||||
if (!coursesResult) return;
|
||||
|
||||
courses.set(coursesResult.allCourses);
|
||||
hasFetched = true;
|
||||
}
|
||||
}
|
||||
|
||||
$: getCourses($profile.id, $currentOrg.id);
|
||||
</script>
|
||||
|
||||
<section class="max-w-6xl mx-auto">
|
||||
@@ -40,12 +55,9 @@
|
||||
<Tabs {tabs} {currentTab} {onChange}>
|
||||
<slot:fragment slot="content">
|
||||
<TabContent value={tabs[0].value} index={currentTab}>
|
||||
<AllLearning />
|
||||
<Courses courses={$courses} />
|
||||
</TabContent>
|
||||
<TabContent value={tabs[1].value} index={currentTab}>
|
||||
<InProgress />
|
||||
</TabContent>
|
||||
<TabContent value={tabs[2].value} index={currentTab}>
|
||||
<Completed />
|
||||
</TabContent>
|
||||
</slot:fragment>
|
||||
|
||||
Reference in New Issue
Block a user