CLASSROOMIO-T-205: Improve lesson page UX
This commit is contained in:
@@ -26,6 +26,7 @@
|
|||||||
|
|
||||||
export let exerciseId;
|
export let exerciseId;
|
||||||
export let goBack = () => {};
|
export let goBack = () => {};
|
||||||
|
export let isStudent = true;
|
||||||
|
|
||||||
let preview = false;
|
let preview = false;
|
||||||
let editDescription = false;
|
let editDescription = false;
|
||||||
@@ -66,7 +67,11 @@
|
|||||||
$: $questionnaire.questions.length < 1 && handleAddQuestion();
|
$: $questionnaire.questions.length < 1 && handleAddQuestion();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<PageBody padding="px-4 overflow-x-hidden" headerClassName="bg-gray-100 rounded-md mb-3">
|
<PageBody
|
||||||
|
bind:isPageNavHidden={isStudent}
|
||||||
|
padding="px-4 overflow-x-hidden"
|
||||||
|
headerClassName="bg-gray-100 rounded-md mb-3"
|
||||||
|
>
|
||||||
<svelte:fragment slot="header">
|
<svelte:fragment slot="header">
|
||||||
<RoleBasedSecurity allowedRoles={[1, 2]}>
|
<RoleBasedSecurity allowedRoles={[1, 2]}>
|
||||||
<div class="flex items-center py-2 w-">
|
<div class="flex items-center py-2 w-">
|
||||||
@@ -139,5 +144,4 @@
|
|||||||
{:else}
|
{:else}
|
||||||
<ViewMode {preview} {exerciseId} />
|
<ViewMode {preview} {exerciseId} />
|
||||||
{/if}
|
{/if}
|
||||||
<!-- </div> -->
|
|
||||||
</PageBody>
|
</PageBody>
|
||||||
|
|||||||
@@ -150,7 +150,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if exerciseId}
|
{#if exerciseId}
|
||||||
<Exercise {exerciseId} {goBack} />
|
<Exercise {exerciseId} {goBack} bind:isStudent />
|
||||||
{:else}
|
{:else}
|
||||||
<NewExerciseModal
|
<NewExerciseModal
|
||||||
bind:open
|
bind:open
|
||||||
@@ -160,7 +160,7 @@
|
|||||||
bind:title={newExercise.title}
|
bind:title={newExercise.title}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<PageBody>
|
<PageBody bind:isPageNavHidden={isStudent}>
|
||||||
<slot:fragment slot="header">
|
<slot:fragment slot="header">
|
||||||
<RoleBasedSecurity allowedRoles={[1, 2]}>
|
<RoleBasedSecurity allowedRoles={[1, 2]}>
|
||||||
<PrimaryButton className="mr-2 mb-2" label="Add" onClick={() => (open = !open)} />
|
<PrimaryButton className="mr-2 mb-2" label="Add" onClick={() => (open = !open)} />
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export const lessons: Writable<Lesson[]> = writable([]);
|
|||||||
export const lesson = writable<LessonPage>({
|
export const lesson = writable<LessonPage>({
|
||||||
id: null,
|
id: null,
|
||||||
totalExercises: 0,
|
totalExercises: 0,
|
||||||
|
comments: 0,
|
||||||
isSaving: false,
|
isSaving: false,
|
||||||
materials: {
|
materials: {
|
||||||
note: '',
|
note: '',
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
<script>
|
<script>
|
||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
|
|
||||||
import { Moon } from 'svelte-loading-spinners';
|
import { Moon } from 'svelte-loading-spinners';
|
||||||
import Navigation from '../Course/components/Navigation/index.svelte';
|
import Navigation from '../Course/components/Navigation/index.svelte';
|
||||||
import Backdrop from '$lib/components/Backdrop/index.svelte';
|
import Backdrop from '$lib/components/Backdrop/index.svelte';
|
||||||
// import { lessons, lesson } from '../Course/components/Lesson/store/lessons';
|
|
||||||
// import { questionnaire } from '../Course/components/Lesson/Exercise/store/exercise';
|
|
||||||
// import { questionnaireMetaData } from '../Course/components/Lesson/store/answers';
|
|
||||||
import { course, group } from '../Course/store';
|
import { course, group } from '../Course/store';
|
||||||
import Confetti from '../Confetti/index.svelte';
|
import Confetti from '../Confetti/index.svelte';
|
||||||
import { isMobile } from '$lib/utils/store/useMobile';
|
import { isMobile } from '$lib/utils/store/useMobile';
|
||||||
|
|||||||
@@ -30,7 +30,8 @@
|
|||||||
direction={toolTipProps.direction}
|
direction={toolTipProps.direction}
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="root {color} {selected && 'active'} {size} {contained && 'contained '} {buttonClassName}"
|
class="root {color} {selected && 'active'} {size} {contained &&
|
||||||
|
'contained dark:bg-neutral-700'} {buttonClassName}"
|
||||||
{disabled}
|
{disabled}
|
||||||
{type}
|
{type}
|
||||||
on:click={handleClick}
|
on:click={handleClick}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
import CourseIcon from '$lib/components/Icons/CourseIcon.svelte';
|
import CourseIcon from '$lib/components/Icons/CourseIcon.svelte';
|
||||||
import CommunityIcon from '$lib/components/Icons/CommunityIcon.svelte';
|
import CommunityIcon from '$lib/components/Icons/CommunityIcon.svelte';
|
||||||
import Avatar from '$lib/components/Avatar/index.svelte';
|
import Avatar from '$lib/components/Avatar/index.svelte';
|
||||||
import { currentOrg } from '$lib/utils/store/org';
|
|
||||||
import { profile } from '$lib/utils/store/user';
|
import { profile } from '$lib/utils/store/user';
|
||||||
import { NavClasses } from '$lib/utils/constants/reusableClass';
|
import { NavClasses } from '$lib/utils/constants/reusableClass';
|
||||||
import { menu } from '$lib/components/Org/store';
|
import { menu } from '$lib/components/Org/store';
|
||||||
|
|||||||
@@ -6,9 +6,15 @@
|
|||||||
export let className = '';
|
export let className = '';
|
||||||
export let headerClassName = '';
|
export let headerClassName = '';
|
||||||
export let onClick = () => {};
|
export let onClick = () => {};
|
||||||
|
export let isPageNavHidden = false;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="root mt-4 mx-auto {width} {className} relative {padding}" on:click={onClick}>
|
<div
|
||||||
|
class="overflow-y-auto {isPageNavHidden
|
||||||
|
? 'h-[calc(100vh-65px)] lg:h-[calc(100vh-127px)]'
|
||||||
|
: 'h-[calc(100vh-127px)]'} mt-4 mx-auto {width} {className} relative {padding}"
|
||||||
|
on:click={onClick}
|
||||||
|
>
|
||||||
{#if $$slots.header}
|
{#if $$slots.header}
|
||||||
<div
|
<div
|
||||||
class="head dark:bg-black flex items-center justify-between sticky right-0 w-full px-3 {headerClassName}"
|
class="head dark:bg-black flex items-center justify-between sticky right-0 w-full px-3 {headerClassName}"
|
||||||
@@ -21,10 +27,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.root {
|
|
||||||
overflow-y: auto;
|
|
||||||
height: calc(100vh - 127px);
|
|
||||||
}
|
|
||||||
.head {
|
.head {
|
||||||
top: 0px;
|
top: 0px;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
export let isTitleEditable = false;
|
export let isTitleEditable = false;
|
||||||
export let onEditComplete = () => {};
|
export let onEditComplete = () => {};
|
||||||
export let paddingClass = 'px-2';
|
export let paddingClass = 'px-2';
|
||||||
|
export let hideOnMobile = false;
|
||||||
|
|
||||||
let dynamicRootClass = '';
|
let dynamicRootClass = '';
|
||||||
let enterEditTitleMode = false;
|
let enterEditTitleMode = false;
|
||||||
@@ -20,7 +21,9 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="header dark:bg-black dark:border-neutral-600 bg-white flex items-center justify-between {!disableSticky &&
|
class="{hideOnMobile
|
||||||
|
? 'hidden lg:flex'
|
||||||
|
: ''} header dark:bg-black dark:border-neutral-600 bg-white flex items-center justify-between {!disableSticky &&
|
||||||
'sticky'} {paddingClass} {dynamicRootClass}"
|
'sticky'} {paddingClass} {dynamicRootClass}"
|
||||||
style={overidableStyle}
|
style={overidableStyle}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -196,6 +196,7 @@ export enum VideoType {
|
|||||||
export interface LessonPage {
|
export interface LessonPage {
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
totalExercises: Number;
|
totalExercises: Number;
|
||||||
|
comments: Number;
|
||||||
isSaving: Boolean;
|
isSaving: Boolean;
|
||||||
materials: {
|
materials: {
|
||||||
note: string;
|
note: string;
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import CourseContainer from '$lib/components/CourseContainer/index.svelte';
|
import CourseContainer from '$lib/components/CourseContainer/index.svelte';
|
||||||
import { fetchCourse, fetchLesson, updateLessonCompletion } from '$lib/utils/services/courses';
|
import { fetchCourse, fetchLesson, updateLessonCompletion } from '$lib/utils/services/courses';
|
||||||
import CheckmarkOutlineIcon from 'carbon-icons-svelte/lib/CheckmarkOutline.svelte';
|
import CheckmarkOutlineIcon from 'carbon-icons-svelte/lib/CheckmarkOutline.svelte';
|
||||||
|
import CheckmarkFilledIcon from 'carbon-icons-svelte/lib/CheckmarkFilled.svelte';
|
||||||
|
import ListChecked from 'carbon-icons-svelte/lib/ListChecked.svelte';
|
||||||
|
|
||||||
|
import SendAlt from 'carbon-icons-svelte/lib/SendAlt.svelte';
|
||||||
|
import CourseIcon from '$lib/components/Icons/CourseIcon.svelte';
|
||||||
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
|
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
|
||||||
import { VARIANTS } from '$lib/components/PrimaryButton/constants';
|
import { VARIANTS } from '$lib/components/PrimaryButton/constants';
|
||||||
import { Loading } from 'carbon-components-svelte';
|
import { Loading } from 'carbon-components-svelte';
|
||||||
@@ -17,15 +23,11 @@
|
|||||||
import OverflowMenuVertical from 'carbon-icons-svelte/lib/OverflowMenuVertical.svelte';
|
import OverflowMenuVertical from 'carbon-icons-svelte/lib/OverflowMenuVertical.svelte';
|
||||||
import { apps } from '$lib/components/Apps/store';
|
import { apps } from '$lib/components/Apps/store';
|
||||||
import IconButton from '$lib/components/IconButton/index.svelte';
|
import IconButton from '$lib/components/IconButton/index.svelte';
|
||||||
import {
|
import { lesson, lessons } from '$lib/components/Course/components/Lesson/store/lessons';
|
||||||
lesson,
|
|
||||||
lessons,
|
|
||||||
handleSaveLesson
|
|
||||||
} from '$lib/components/Course/components/Lesson/store/lessons';
|
|
||||||
import { browser } from '$app/environment';
|
import { browser } from '$app/environment';
|
||||||
import { currentOrg } from '$lib/utils/store/org';
|
import { currentOrg } from '$lib/utils/store/org';
|
||||||
import { snackbar } from '$lib/components/Snackbar/store';
|
import { snackbar } from '$lib/components/Snackbar/store';
|
||||||
import type { LessonCompletion, LessonPage } from '$lib/utils/types';
|
import type { LessonCompletion } from '$lib/utils/types';
|
||||||
import { profile } from '$lib/utils/store/user.js';
|
import { profile } from '$lib/utils/store/user.js';
|
||||||
import { getIsLessonComplete } from '$lib/components/Course/components/Lesson/functions';
|
import { getIsLessonComplete } from '$lib/components/Course/components/Lesson/functions';
|
||||||
|
|
||||||
@@ -51,7 +53,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchReqData(lessonId = '', isMaterialsTabActive: false) {
|
async function fetchReqData(lessonId = '', isMaterialsTabActive: boolean) {
|
||||||
isFetching = true;
|
isFetching = true;
|
||||||
|
|
||||||
let lessonData;
|
let lessonData;
|
||||||
@@ -109,6 +111,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
await updateLessonCompletion(completion);
|
await updateLessonCompletion(completion);
|
||||||
|
snackbar.success('Marked as complete');
|
||||||
isMarkingComplete = false;
|
isMarkingComplete = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,7 +170,7 @@
|
|||||||
isLoading = false;
|
isLoading = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
function setLesson(lessonData, totalExercises) {
|
function setLesson(lessonData, totalExercises: number) {
|
||||||
if (!lessonData) return;
|
if (!lessonData) return;
|
||||||
|
|
||||||
let lesson_completion: LessonCompletion[] = [];
|
let lesson_completion: LessonCompletion[] = [];
|
||||||
@@ -208,6 +211,7 @@
|
|||||||
isExercisePage={!data.isMaterialsTabActive && data.exerciseId}
|
isExercisePage={!data.isMaterialsTabActive && data.exerciseId}
|
||||||
>
|
>
|
||||||
<PageNav
|
<PageNav
|
||||||
|
bind:hideOnMobile={isStudent}
|
||||||
navItems={[
|
navItems={[
|
||||||
{
|
{
|
||||||
label: 'Materials',
|
label: 'Materials',
|
||||||
@@ -272,30 +276,54 @@
|
|||||||
/>
|
/>
|
||||||
{:else if !!data.lessonId}
|
{:else if !!data.lessonId}
|
||||||
<PageBody
|
<PageBody
|
||||||
|
bind:isPageNavHidden={isStudent}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
$apps.open = false;
|
$apps.open = false;
|
||||||
$apps.dropdown = false;
|
$apps.dropdown = false;
|
||||||
}}
|
}}
|
||||||
width="lg:w-full xl:w-11/12"
|
width="lg:w-full xl:w-11/12"
|
||||||
|
className="overflow-x-hidden"
|
||||||
>
|
>
|
||||||
<Materials lessonId={data.lessonId} {mode} {prevMode} {toggleMode} bind:isSaving />
|
<Materials lessonId={data.lessonId} {mode} {prevMode} {toggleMode} bind:isSaving />
|
||||||
|
|
||||||
<div class="w-full flex flex-row-reverse mt-10">
|
|
||||||
<PrimaryButton
|
|
||||||
onClick={markLessonComplete}
|
|
||||||
isLoading={isMarkingComplete}
|
|
||||||
isDisabled={isMarkingComplete}
|
|
||||||
variant={VARIANTS.OUTLINED}
|
|
||||||
className="mt-10"
|
|
||||||
>
|
|
||||||
<CheckmarkOutlineIcon size={24} class="carbon-icon mr-2" />
|
|
||||||
Mark as {getIsLessonComplete($lesson.lesson_completion, $profile.id)
|
|
||||||
? 'Incomplete'
|
|
||||||
: 'Complete'}
|
|
||||||
</PrimaryButton>
|
|
||||||
</div>
|
|
||||||
</PageBody>
|
</PageBody>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
<!-- Mobile Navigation -->
|
||||||
|
<div class="fixed bottom-5 w-[90%] flex items-center justify-center lg:hidden">
|
||||||
|
<div
|
||||||
|
class="flex items-center gap-2 w-fit rounded-full shadow-xl bg-gray-100 dark:bg-neutral-700 px-5 py-1"
|
||||||
|
>
|
||||||
|
{#if data.isMaterialsTabActive}
|
||||||
|
<button
|
||||||
|
class="px-2 my-2 pr-4 border-t-0 border-b-0 border-l-0 border border-gray-300 flex items-center"
|
||||||
|
on:click={() => goto(`${path}/exercises`)}
|
||||||
|
>
|
||||||
|
<ListChecked size={24} class="carbon-icon" />
|
||||||
|
<span class="ml-1">{$lesson.totalExercises}</span>
|
||||||
|
</button>
|
||||||
|
{:else}
|
||||||
|
<button
|
||||||
|
class="px-2 my-2 pr-4 border-t-0 border-b-0 border-l-0 border border-gray-300 flex items-center"
|
||||||
|
on:click={() => goto(path)}
|
||||||
|
>
|
||||||
|
<CourseIcon color="#fff" />
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
<button
|
||||||
|
class="px-2 my-2 pr-4 border-t-0 border-b-0 border-l-0 border border-gray-300 flex items-center"
|
||||||
|
>
|
||||||
|
<SendAlt size={24} class="carbon-icon" />
|
||||||
|
<span class="ml-1">{$lesson.comments}</span>
|
||||||
|
</button>
|
||||||
|
<button class="px-2 my-2" on:click={markLessonComplete} disabled={isMarkingComplete}>
|
||||||
|
{#if getIsLessonComplete($lesson.lesson_completion, $profile.id)}
|
||||||
|
<CheckmarkFilledIcon size={24} class="carbon-icon text-primary-600" />
|
||||||
|
{:else}
|
||||||
|
<CheckmarkOutlineIcon size={24} class="carbon-icon" />
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</CourseContainer>
|
</CourseContainer>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
Reference in New Issue
Block a user