CLASSROOMIO-T-180: LMS dashboard improvements
This commit is contained in:
@@ -17,8 +17,6 @@
|
||||
export let isLMS = false;
|
||||
export let progressRate = 45;
|
||||
|
||||
$: console.log(progressRate);
|
||||
|
||||
$: formatter = getCurrencyFormatter(currency);
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { writable } from 'svelte/store';
|
||||
import { writable, derived } from 'svelte/store';
|
||||
import type { Course } from '$lib/utils/types';
|
||||
|
||||
export const courses = writable<Course[]>([]);
|
||||
export const courseInProgress = derived(courses, ($courses) =>
|
||||
$courses.length > 0 ? $courses.slice(0, 3) : []
|
||||
);
|
||||
export const courseMetaDeta = writable({
|
||||
isLoading: true
|
||||
});
|
||||
@@ -23,4 +26,5 @@ export const copyCourseModalInitialState = {
|
||||
isSaving: false,
|
||||
error: null
|
||||
};
|
||||
|
||||
export const copyCourseModal = writable({ ...copyCourseModalInitialState });
|
||||
|
||||
@@ -1,72 +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 { courseCompleted } from './store';
|
||||
|
||||
const getCourse = () => {
|
||||
goto(`/courses/${title}`);
|
||||
};
|
||||
</script>
|
||||
|
||||
<section>
|
||||
{#if courseCompleted.length > 0}
|
||||
<div
|
||||
class="flex items-center justify-center md:justify-start flex-wrap gap-4 dark:text-white mt-2"
|
||||
>
|
||||
{#each courseCompleted as data}s
|
||||
<div
|
||||
class="w-80 min-h-[350px] flex items-start justify-start flex-col border rounded mr-2 mb-5 border-gray-200"
|
||||
>
|
||||
<div class="w-full h-[180px]">
|
||||
<img src={data.src} alt="course" class="w-full h-full object-cover" />
|
||||
</div>
|
||||
<div class="w-full p-3">
|
||||
<p
|
||||
class="line-clamp-2 text-xl lg:text-2xl font-normal text-[#262626] dark:text-white py-1"
|
||||
>
|
||||
{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-col-reverse lg:flex-row items-start lg:items-center gap-2 justify-between"
|
||||
>
|
||||
<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={getCourse}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
<Box className="flex flex-col items-center mt-2">
|
||||
<CoursesEmptyIcon />
|
||||
<h3 class="text-center">No courses completed yet</h3>
|
||||
<p class="w-[70%] md:w-[35%] text-center">
|
||||
Courses you complete will appear here. We believe in you , finish hard 💪🏾.
|
||||
</p>
|
||||
</Box>
|
||||
{/if}
|
||||
</section>
|
||||
@@ -1,35 +0,0 @@
|
||||
<script>
|
||||
import Box from '$lib/components/Box/index.svelte';
|
||||
import CoursesEmptyIcon from '$lib/components/Icons/CoursesEmptyIcon.svelte';
|
||||
import { courseInProgress } from './store';
|
||||
import Card from '$lib/components/Courses/components/Card/index.svelte';
|
||||
</script>
|
||||
|
||||
<section>
|
||||
{#if courseInProgress.length > 0}
|
||||
<div
|
||||
class="flex items-center justify-center md:justify-start flex-wrap gap-4 dark:text-white mt-2"
|
||||
>
|
||||
{#each courseInProgress as data}
|
||||
{#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}
|
||||
<Box className="flex flex-col items-center mt-2">
|
||||
<CoursesEmptyIcon />
|
||||
<h3 class="text-center">No course progress 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,13 @@
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import { VARIANTS } from '$lib/components/PrimaryButton/constants';
|
||||
import { goto } from '$app/navigation';
|
||||
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
|
||||
import { course } from '$lib/components/Course/store';
|
||||
import Box from '$lib/components/Box/index.svelte';
|
||||
import CoursesEmptyIcon from '$lib/components/Icons/CoursesEmptyIcon.svelte';
|
||||
import { courseInProgress } from './store';
|
||||
import { courseInProgress } from '$lib/components/Courses/store';
|
||||
|
||||
const gotoCourse = (title) => {
|
||||
goto(`/courses/${title}`);
|
||||
const gotoCourse = (id: string | undefined) => {
|
||||
if (!id) return;
|
||||
goto(`/courses/${id}`);
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -17,27 +16,34 @@
|
||||
<div
|
||||
class="flex items-center flex-col border border-[#EAEAEA] dark:bg-neutral-800 gap-2 rounded w-full h-fit xl:h-[516px] p-3"
|
||||
>
|
||||
{#if courseInProgress.length > 0}
|
||||
{#if $courseInProgress.length > 0}
|
||||
<div class="w-full h-full xl:h-[516px] flex flex-col justify-start">
|
||||
{#each courseInProgress as data, index}
|
||||
{#each $courseInProgress as course}
|
||||
<div class="p-5">
|
||||
<span class="flex flex-col md:flex-row gap-3 items-start pb-5">
|
||||
<img src={data.src} alt="course" class="hidden md:block md:w-[60px] md:h-[60px]" />
|
||||
<img
|
||||
src={course.logo || '/images/classroomio-course-img-template.jpg'}
|
||||
alt="course"
|
||||
class="hidden md:block md:w-[60px] md:h-[60px]"
|
||||
/>
|
||||
<div class="w-full">
|
||||
<p class="text-base font-semibold dark:text-white">{data.title}</p>
|
||||
<p class="text-base font-semibold dark:text-white">{course.title}</p>
|
||||
<p class="line-clamp-2 text-xs font-normal text-[#656565] dark:text-white">
|
||||
{data.description}
|
||||
{course.description}
|
||||
</p>
|
||||
</div>
|
||||
<PrimaryButton
|
||||
label="Continue"
|
||||
variant={VARIANTS.OUTLINED}
|
||||
className="rounded-none text-[#0233BD]"
|
||||
onClick={() => gotoCourse(data.title)}
|
||||
onClick={() => gotoCourse(course.id)}
|
||||
/>
|
||||
</span>
|
||||
<div class="relative bg-[#EAEAEA] w-full h-1">
|
||||
<div class={`absolute top-0 left-0 bg-primary-700 h-full ${data.progress} ]`} />
|
||||
<div
|
||||
style="width:{course.progress_rate}%"
|
||||
class={`absolute top-0 left-0 bg-primary-700 h-full`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
@@ -45,7 +51,7 @@
|
||||
{:else}
|
||||
<div class="flex flex-col items-center">
|
||||
<CoursesEmptyIcon />
|
||||
<h3 class="text-center">No courses progress yet</h3>
|
||||
<h3 class="text-center">No courses in progress yet</h3>
|
||||
<p class="w-[75%] text-center">Once you start a course, your progress will reflect here</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
async function logout() {
|
||||
const { error } = await supabase.auth.signOut();
|
||||
console.log('error logout', error);
|
||||
goto('/login');
|
||||
}
|
||||
|
||||
|
||||
-2
@@ -23,8 +23,6 @@
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// just copy and paste
|
||||
</script>
|
||||
|
||||
<section class="w-full h-full mt-2">
|
||||
@@ -1,45 +0,0 @@
|
||||
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 ...',
|
||||
author: 'Derrick Amenuvue',
|
||||
lessons: 8,
|
||||
inProgress: true,
|
||||
completed: false,
|
||||
src: '/digital.svg',
|
||||
progress: 'w-[45%]',
|
||||
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 ...',
|
||||
author: 'Derrick Amenuvue',
|
||||
inProgress: true,
|
||||
completed: false,
|
||||
lessons: 5,
|
||||
src: '/painting.svg',
|
||||
progress: 'w-[30%]',
|
||||
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 ...',
|
||||
author: 'Derrick Amenuvue',
|
||||
inProgress: false,
|
||||
completed: false,
|
||||
lessons: 18,
|
||||
src: '/digital.svg',
|
||||
progress: 'w-[45%]',
|
||||
progressRate: 45
|
||||
}
|
||||
];
|
||||
export const courseInProgress = mockData.filter((course) => course.inProgress);
|
||||
export const courseCompleted = mockData.filter((course) => course.completed);
|
||||
export const totalCourses = mockData.length;
|
||||
export const progressPercentage = Math.round((courseInProgress.length / totalCourses) * 100);
|
||||
+42
-11
@@ -1,14 +1,45 @@
|
||||
<script>
|
||||
import Student from '$lib/components/LMS/components/Student.svelte';
|
||||
<script lang="ts">
|
||||
import Schedule from '$lib/components/LMS/components/Schedule.svelte';
|
||||
import Learning from '$lib/components/LMS/components/Learning.svelte';
|
||||
import {
|
||||
courseInProgress,
|
||||
totalCourses,
|
||||
progressPercentage
|
||||
} from '$lib/components/LMS/components/store';
|
||||
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
|
||||
import { VARIANTS } from '$lib/components/PrimaryButton/constants';
|
||||
import { goto } from '$app/navigation';
|
||||
import { profile } from '$lib/utils/store/user';
|
||||
import { fetchCourses } from '$lib/components/Courses/api';
|
||||
import { currentOrg } from '$lib/utils/store/org';
|
||||
import { courses, courseMetaDeta } from '$lib/components/Courses/store';
|
||||
import type { Course } from '$lib/utils/types';
|
||||
|
||||
let hasFetched = false;
|
||||
let progressPercentage = 0;
|
||||
let totalLessons = 0;
|
||||
let totalComPleted = 0;
|
||||
|
||||
async function getCourses(userId: string | null, orgId: string) {
|
||||
if (hasFetched || !userId || !orgId) {
|
||||
return;
|
||||
}
|
||||
$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;
|
||||
}
|
||||
|
||||
function calcTotalProgress(courses: Course[]) {
|
||||
totalComPleted = courses.reduce((acc, cur) => acc + (cur.progress_rate || 0), 0);
|
||||
totalLessons = courses.reduce((acc, cur) => acc + (cur.total_lessons || 0), 0);
|
||||
|
||||
progressPercentage = Math.round((totalComPleted / totalLessons) * 100);
|
||||
}
|
||||
|
||||
$: getCourses($profile.id, $currentOrg.id);
|
||||
$: calcTotalProgress($courses);
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
@@ -41,7 +72,7 @@
|
||||
</div>
|
||||
<main class="flex w-full h-full flex-col xl:flex-row xl:gap-5">
|
||||
<div class="w-full h-full">
|
||||
<Student />
|
||||
<Schedule />
|
||||
</div>
|
||||
|
||||
<div class="w-full h-full">
|
||||
@@ -58,12 +89,12 @@
|
||||
</div>
|
||||
<span class="text-center xl:text-start">
|
||||
<p class="text-base font-semibold py-2 text-[#040F2D] dark:text-white">Your Progress</p>
|
||||
{#if courseInProgress.length > 0}
|
||||
{#if totalLessons > 0}
|
||||
<p class="text-xs font-normal text-[#656565] dark:text-white">
|
||||
{courseInProgress.length}/{totalCourses} lessons in progress
|
||||
{totalComPleted}/{totalLessons} lessons completed
|
||||
</p>
|
||||
{:else}
|
||||
<p class="text-xs font-normal text-[#656565] dark:text-white">No lessons started</p>
|
||||
<p class="text-xs font-normal text-[#656565] dark:text-white">No courses started</p>
|
||||
{/if}
|
||||
</span>
|
||||
<h1 class="text-5xl md:text-6xl font-bold text-[#262626] dark:text-white my-0">
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
<script lang="ts">
|
||||
import { Search } from 'carbon-components-svelte';
|
||||
import Completed from '$lib/components/LMS/components/Completed.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';
|
||||
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';
|
||||
import { courses } from '$lib/components/Courses/store';
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
@@ -21,29 +16,11 @@
|
||||
}
|
||||
];
|
||||
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">
|
||||
@@ -58,7 +35,7 @@
|
||||
<Courses courses={$courses} />
|
||||
</TabContent>
|
||||
<TabContent value={tabs[1].value} index={currentTab}>
|
||||
<Completed />
|
||||
<Courses courses={[]} />
|
||||
</TabContent>
|
||||
</slot:fragment>
|
||||
</Tabs>
|
||||
|
||||
Reference in New Issue
Block a user