Sync lesson/id w/ server + edit & view mode

This commit is contained in:
Ibitoye Rotimi Best
2021-07-14 09:05:09 +03:00
parent c9908d6af5
commit f84bbc6016
14 changed files with 415 additions and 317 deletions
@@ -5,21 +5,11 @@
import TextField from '../../../../Form/TextField.svelte';
import Exercise from '../Exercise/index.svelte';
import { lesson } from '../store/lessons';
export let path;
export let exerciseId;
let exercises = [
{
id: 1,
title: 'Practice Exercise',
link: `${path}/1`,
},
{
id: 2,
title: 'Home task',
link: `${path}/2`,
},
];
let open = false;
let newExercise = {
id: 1,
@@ -28,13 +18,16 @@
};
function handleAddExercise() {
exercises = [
...exercises,
{
...newExercise,
link: `${path}/${exercises.length + 1}`,
},
];
lesson.update((_lesson) => ({
..._lesson,
exercises: [
..._lesson.exercises,
{
...newExercise,
link: `${path}/${_lesson.exercises.length + 1}`,
},
],
}));
handleCancelAddExercise();
}
@@ -42,7 +35,7 @@
function handleCancelAddExercise() {
open = false;
newExercise = {
id: exercises.length + 1,
id: $lesson.exercises.length + 1,
title: '',
link: '',
};
@@ -81,7 +74,7 @@
</slot:fragment>
<div class="flex flex-wrap">
{#each exercises as exercise, index}
{#each $lesson.exercises as exercise}
<a class="w-52 bg-gray-100 px-4 py-7 mr-4 mb-4" href={exercise.link}>
<h3 class="text-xl">{exercise.title}</h3>
<p class="mt-4 text-sm">Created Jul 3, 2021</p>
@@ -0,0 +1,28 @@
<script>
import { Tabs, Tab, TabContent } from 'carbon-components-svelte';
import EditContent from '../../../../EditContent/index.svelte';
import TextField from '../../../../Form/TextField.svelte';
import { lesson } from '../store/lessons';
</script>
<Tabs class="flex justify-center" type="container">
<Tab label="Note" />
<Tab label="Slide" />
<Tab label="Video" />
<div slot="content">
<TabContent>
<EditContent
writeLabel="Note"
bind:value={$lesson.materials.note}
placeholder="Start typing your lesson"
/>
</TabContent>
<TabContent>
<TextField label="Slide link" bind:value={$lesson.materials.slide} />
</TabContent>
<TabContent>
<TextField label="Youtube link" bind:value={$lesson.materials.video} />
</TabContent>
</div>
</Tabs>
@@ -0,0 +1,96 @@
<script>
import { Tabs, Tab, TabContent } from 'carbon-components-svelte';
import MarkdownRender from '../../../../MarkdownRender/index.svelte';
import { lesson } from '../store/lessons';
</script>
<Tabs class="flex justify-center" type="container">
<Tab label="Note" />
<Tab label="Slide" />
<Tab label="Video" />
<div slot="content">
<TabContent>
<MarkdownRender content={$lesson.materials.note} />
</TabContent>
<TabContent>
<iframe
title="Embeded Slides"
src={$lesson.materials.slide}
frameborder="0"
width="100%"
height="569"
allowfullscreen="true"
mozallowfullscreen="true"
webkitallowfullscreen="true"
/>
</TabContent>
<TabContent>
<iframe
width="100%"
height="569"
src={$lesson.materials.video}
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen
/>
</TabContent>
</div>
</Tabs>
<!-- <div class="flex mb-2">
<button
on:click={handleTabClick(0)}
class="focus:outline-none text-lg font-bold border-b-2 border-transparent {currentTab ===
0 && `${activeClassName}`}"
>
Note
</button>
<button
on:click={handleTabClick(1)}
class="focus:outline-none text-lg font-bold border-b-2 border-transparent {currentTab ===
1 && `${activeClassName}`}"
>
</button>
<button
on:click={handleTabClick(2)}
class="focus:outline-none text-lg font-bold border-b-2 border-transparent {currentTab ===
2 && `${activeClassName}`}"
>
Video
</button>
</div>
<div class="m-2 mb-0 p-0">
{#if currentTab === 0}
<MarkdownRender content={$lesson.materials.note} />
{:else if currentTab === 1}
<iframe
title="Embeded Slides"
src={$lesson.materials.slide}
frameborder="0"
width="100%"
height="569"
allowfullscreen="true"
mozallowfullscreen="true"
webkitallowfullscreen="true"
/>
{:else if currentTab === 2}
<iframe
width="100%"
height="569"
src={$lesson.materials.video}
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen
/>
{/if}
</div> -->
<style>
button {
padding: 12px 12px 8px 12px;
}
</style>
@@ -1,16 +0,0 @@
<script>
// import EditContent from "../../../../EditContent/index.svelte";
import TextField from "../../../../Form/TextField.svelte";
// import readme from "../../readme.js";
// let value = readme;
</script>
<TextField label="Slide link" />
<TextField label="Youtube link" />
<!-- <EditContent
writeLabel="Lecture Note"
{value}
placeholder="Start typing your lesson"
/> -->
@@ -1,62 +0,0 @@
<!--
import marked from "marked";
{@html marked(readme, { breaks: true, gfm: true })}
-->
<script>
const activeClassName = "border-blue-700";
let slideLink = 'https://docs.google.com/presentation/d/e/2PACX-1vS7mulzY5v1X_D0Gz68M2sggq2zjHCuoyY5JI07hcsOA4svDV5rxhLD9V3ckO_Rz7TK1YniQJtu0CXM/embed?start=false&loop=false&delayms=3000';
let currentTab = 1;
function handleTabClick(tab) {
return () => {
currentTab = tab;
}
}
</script>
<div class="flex mb-2">
<button
on:click={handleTabClick(1)}
class="focus:outline-none text-lg font-bold border-b-2 border-transparent {currentTab === 1 && `${activeClassName}`}"
>
Slide
</button>
<button
on:click={handleTabClick(2)}
class="focus:outline-none text-lg font-bold border-b-2 border-transparent {currentTab === 2 && `${activeClassName}`}"
>
Video
</button>
</div>
<div class="m-2 mb-0 p-0">
{#if currentTab === 1}
<iframe
title="Embeded Slides"
src={slideLink}
frameborder="0"
width="100%"
height="569"
allowfullscreen="true"
mozallowfullscreen="true"
webkitallowfullscreen="true"
>
</iframe>
{:else if currentTab === 2}
<iframe
width="100%"
height="569"
src="https://www.youtube.com/embed/luM5uobewhA"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen>
</iframe>
{/if}
</div>
<style>
button {
padding: 12px 12px 8px 12px;
}
</style>
@@ -1,40 +0,0 @@
<script>
import PageNav from '../../../PageNav/index.svelte';
import Exercises from './Exercises/index.svelte';
import Readme from './Readme/index.svelte';
export let path;
export let tab;
export let exerciseId;
</script>
<div>
<PageNav
navItems={[
{
label: 'README.md',
isActive: tab === 'lessons',
href: `${path}?tab=lessons`,
},
{
label: 'Exercises',
isActive: tab === 'exercises',
href: `${path}?tab=exercises`,
},
]}
/>
<div class="course relative">
{#if tab === 'lessons'}
<Readme />
{:else if tab === 'exercises'}
<Exercises {exerciseId} path={`${path}?tab=exercises`} />
{/if}
</div>
</div>
<style>
.course {
padding: 0 40px;
}
</style>
@@ -1,42 +1,51 @@
import { writable } from "svelte/store";
import { writable } from 'svelte/store';
export const lessons = writable([]);
export const lesson = writable({
lesson_id: null,
materials: {
note: '',
slide: '',
video: '',
},
exercises: [],
});
export const tutors = writable([
{
id: 1,
text: "Sergey Semko",
text: 'Sergey Semko',
},
{
id: 3,
text: "Jonathan Nelson",
text: 'Jonathan Nelson',
},
{
id: 4,
text: "Sasha Pokidin",
text: 'Sasha Pokidin',
},
{
id: 5,
text: "Natasha Rudenko",
text: 'Natasha Rudenko',
},
{
id: 6,
text: "Yulia Marushko",
text: 'Yulia Marushko',
},
]);
export function handleAddLesson() {
const resources = [
{
label: "lesson",
label: 'lesson',
value: 0,
},
{
label: "quiz",
label: 'quiz',
value: 0,
},
{
label: "home task",
label: 'home task',
value: 0,
},
];
@@ -45,13 +54,13 @@ export function handleAddLesson() {
return [
..._lessons,
{
title: "Untitled lesson",
to: "/courses/1/lessons/" + (_lessons.length + 1),
title: 'Untitled lesson',
to: '/courses/1/lessons/' + (_lessons.length + 1),
resources,
tutor: {
userId: 1,
avatar: "https://picsum.photos/32/32/?random",
name: "",
avatar: 'https://picsum.photos/32/32/?random',
name: '',
},
date: new Date(),
},
@@ -1,20 +1,20 @@
<script>
import { onMount } from "svelte";
import CheckmarkFilled20 from "carbon-icons-svelte/lib/CheckmarkFilled20";
import hotkeys from "hotkeys-js";
import ChevronLeft32 from "carbon-icons-svelte/lib/ChevronLeft32";
import ChevronRight32 from "carbon-icons-svelte/lib/ChevronRight32";
import { stores, goto } from "@sapper/app";
import Expandable from "../../../Expandable/index.svelte";
import PageNav from "../../../PageNav/index.svelte";
import IconButton from "../../../IconButton/index.svelte";
import { onMount } from 'svelte';
import CheckmarkFilled20 from 'carbon-icons-svelte/lib/CheckmarkFilled20';
import hotkeys from 'hotkeys-js';
import ChevronLeft32 from 'carbon-icons-svelte/lib/ChevronLeft32';
import ChevronRight32 from 'carbon-icons-svelte/lib/ChevronRight32';
import { stores, goto } from '@sapper/app';
import Expandable from '../../../Expandable/index.svelte';
import PageNav from '../../../PageNav/index.svelte';
import IconButton from '../../../IconButton/index.svelte';
import {
getNavItemRoute,
getLessonsRoute,
getLectureNo,
} from "../../function";
} from '../../function';
import { lessons } from "../Lesson/store/lessons";
import { lessons } from '../Lesson/store/lessons';
// export let lessonId;
export let courseId;
@@ -32,43 +32,48 @@
const navItems = [
{
label: "Overview",
label: 'Overview',
to: getNavItemRoute(courseId),
hideSortIcon: true,
},
{
label: "Lessons",
label: 'Lessons',
to: getLessonsRoute(courseId),
hideSortIcon: false,
isLecture: true,
},
{
label: "All Exercises",
to: getNavItemRoute(courseId, "allexercises"),
label: 'All Exercises',
to: getNavItemRoute(courseId, 'allexercises'),
hideSortIcon: true,
},
{
label: "Scoreboard",
to: getNavItemRoute(courseId, "scoreboard"),
label: 'Scoreboard',
to: getNavItemRoute(courseId, 'scoreboard'),
hideSortIcon: true,
},
{
label: "People",
to: getNavItemRoute(courseId, "people"),
label: 'People',
to: getNavItemRoute(courseId, 'people'),
hideSortIcon: true,
},
{
label: "Timetable",
to: getNavItemRoute(courseId, "timetable"),
label: 'Timetable',
to: getNavItemRoute(courseId, 'timetable'),
hideSortIcon: true,
},
];
function getHref(item) {
return item.to;
// return item.isComplete ? item.to : path || $page.path
}
onMount(() => {
hotkeys("b", function (event, handler) {
hotkeys('b', function (event, handler) {
event.preventDefault();
switch (handler.key) {
case "b":
case 'b':
show = !show;
break;
}
@@ -95,7 +100,7 @@
!(path || $page.path).length > 3) &&
'active'} pl-7 py-2 {!item.isComplete &&
'cursor-not-allowed'}"
href={item.isComplete ? item.to : path || $page.path}
href={getHref(item)}
>
<span class="course-counter"> {getLectureNo(index + 1)} </span>
<span>{item.title}</span>
@@ -118,9 +123,9 @@
value="toggle"
onClick={() => (show = !show)}
toolTipProps={{
title: "Toggle sidebar",
hotkeys: ["B"],
direction: "right",
title: 'Toggle sidebar',
hotkeys: ['B'],
direction: 'right',
}}
>
{#if show}
+14 -5
View File
@@ -1,15 +1,24 @@
<script>
import { onMount } from "svelte";
import { onMount, afterUpdate } from 'svelte';
import Navigation from "../Course/components/Navigation/index.svelte";
import { lessons } from "../Course/components/Lesson/store/lessons";
import Navigation from '../Course/components/Navigation/index.svelte';
import { lessons, lesson } from '../Course/components/Lesson/store/lessons';
export let courseData = {};
export let path;
onMount(() => {
if (!$lessons.length) {
lessons.set(courseData.lessons);
lessons.set(courseData.lessons);
if (courseData.lesson) {
lesson.set(courseData.lesson);
}
});
afterUpdate(() => {
lessons.set(courseData.lessons);
if (courseData.lesson) {
lesson.set(courseData.lesson);
}
});
</script>
@@ -0,0 +1,9 @@
<script>
import marked from 'marked';
export let content;
</script>
<article class="prose prose-sm sm:prose p-2">
{@html marked(content, { breaks: true, gfm: true })}
</article>
+147 -112
View File
@@ -1,5 +1,5 @@
import { STATUS } from "../../utils/constants/answers";
import { QUESTION_TYPE } from "../../components/Question/constants";
import { STATUS } from '../../utils/constants/answers';
import { QUESTION_TYPE } from '../../components/Question/constants';
const ROLE = {
ADMIN: 0,
@@ -10,66 +10,66 @@ const ROLE = {
const tutors = [
{
id: 1,
text: "Sergey Semko",
text: 'Sergey Semko',
},
{
id: 3,
text: "Jonathan Nelson",
text: 'Jonathan Nelson',
},
{
id: 4,
text: "Sasha Pokidin",
text: 'Sasha Pokidin',
},
{
id: 5,
text: "Natasha Rudenko",
text: 'Natasha Rudenko',
},
{
id: 6,
text: "Yulia Marushko",
text: 'Yulia Marushko',
},
];
const people = [
{
name: "Alexander Pokidin",
title: "Senior Software Engineer",
avatar: "https://i.pravatar.cc/150?img=1",
name: 'Alexander Pokidin',
title: 'Senior Software Engineer',
avatar: 'https://i.pravatar.cc/150?img=1',
role: ROLE.TUTOR,
telegramLink: "https://t.me/seed_of_abraham",
mailLink: "mailto:[email protected]",
telegramLink: 'https://t.me/seed_of_abraham',
mailLink: 'mailto:[email protected]',
},
{
name: "Andrey Filatov",
title: "Fullstack Software Engineer",
avatar: "https://i.pravatar.cc/150?img=2",
name: 'Andrey Filatov',
title: 'Fullstack Software Engineer',
avatar: 'https://i.pravatar.cc/150?img=2',
role: ROLE.TUTOR,
telegramLink: "https://t.me/seed_of_abraham",
mailLink: "mailto:[email protected]",
telegramLink: 'https://t.me/seed_of_abraham',
mailLink: 'mailto:[email protected]',
},
{
name: "Sergey Semko",
title: "Senior Software Engineer",
avatar: "https://i.pravatar.cc/150?img=3",
name: 'Sergey Semko',
title: 'Senior Software Engineer',
avatar: 'https://i.pravatar.cc/150?img=3',
role: ROLE.TUTOR,
telegramLink: "https://t.me/seed_of_abraham",
mailLink: "mailto:[email protected]",
telegramLink: 'https://t.me/seed_of_abraham',
mailLink: 'mailto:[email protected]',
},
{
name: "Natalia Fedii",
title: "Project Manager",
avatar: "https://i.pravatar.cc/150?img=4",
name: 'Natalia Fedii',
title: 'Project Manager',
avatar: 'https://i.pravatar.cc/150?img=4',
role: ROLE.TUTOR,
telegramLink: "https://t.me/seed_of_abraham",
mailLink: "mailto:[email protected]",
telegramLink: 'https://t.me/seed_of_abraham',
mailLink: 'mailto:[email protected]',
},
];
const courses = [
{
id: 1,
title: "React.js Fundamentals",
status: "",
title: 'React.js Fundamentals',
status: '',
},
];
@@ -87,24 +87,24 @@ const lessons = [
{
id: 1,
course_id: 1,
title: "Вступительное слово. Софт скилы",
to: "/courses/1/lessons/1",
title: 'Вступительное слово. Софт скилы',
to: '/courses/1/lessons/1',
tutor: {
userId: 1,
avatar: "https://picsum.photos/32/32/?random",
name: "Sergey Semko",
avatar: 'https://picsum.photos/32/32/?random',
name: 'Sergey Semko',
},
resources: [
{
label: "lesson",
label: 'lesson',
value: 1,
},
{
label: "quiz",
label: 'quiz',
value: 1,
},
{
label: "home task",
label: 'home task',
value: 2,
},
],
@@ -114,24 +114,24 @@ const lessons = [
{
id: 2,
course_id: 1,
title: "Введение в ReactJS",
to: "/courses/1/lessons/2",
title: 'Введение в ReactJS',
to: '/courses/1/lessons/2',
tutor: {
userId: 1,
avatar: "https://picsum.photos/32/32/?random",
name: "Sergey Semko",
avatar: 'https://picsum.photos/32/32/?random',
name: 'Sergey Semko',
},
resources: [
{
label: "lesson",
label: 'lesson',
value: 1,
},
{
label: "quiz",
label: 'quiz',
value: 1,
},
{
label: "home task",
label: 'home task',
value: 2,
},
],
@@ -141,24 +141,24 @@ const lessons = [
{
id: 3,
course_id: 1,
title: "Компоненты",
to: "/courses/1/lessons/3",
title: 'Компоненты',
to: '/courses/1/lessons/3',
tutor: {
userId: 1,
avatar: "https://picsum.photos/32/32/?random",
name: "Sergey Semko",
avatar: 'https://picsum.photos/32/32/?random',
name: 'Sergey Semko',
},
resources: [
{
label: "lesson",
label: 'lesson',
value: 1,
},
{
label: "quiz",
label: 'quiz',
value: 1,
},
{
label: "home task",
label: 'home task',
value: 2,
},
],
@@ -168,24 +168,24 @@ const lessons = [
{
id: 4,
course_id: 1,
title: "Состояние компонентов и пропсы",
to: "/courses/1/lessons/4",
title: 'Состояние компонентов и пропсы',
to: '/courses/1/lessons/4',
tutor: {
userId: 1,
avatar: "https://picsum.photos/32/32/?random",
name: "Sergey Semko",
avatar: 'https://picsum.photos/32/32/?random',
name: 'Sergey Semko',
},
resources: [
{
label: "lesson",
label: 'lesson',
value: 1,
},
{
label: "quiz",
label: 'quiz',
value: 1,
},
{
label: "home task",
label: 'home task',
value: 2,
},
],
@@ -195,24 +195,24 @@ const lessons = [
{
id: 5,
course_id: 1,
title: "Жизненный цикл",
to: "/courses/1/lessons/5",
title: 'Жизненный цикл',
to: '/courses/1/lessons/5',
tutor: {
userId: 1,
avatar: "https://picsum.photos/32/32/?random",
name: "Sergey Semko",
avatar: 'https://picsum.photos/32/32/?random',
name: 'Sergey Semko',
},
resources: [
{
label: "lesson",
label: 'lesson',
value: 1,
},
{
label: "quiz",
label: 'quiz',
value: 1,
},
{
label: "home task",
label: 'home task',
value: 2,
},
],
@@ -221,43 +221,65 @@ const lessons = [
},
];
const lessonData = [
{
lesson_id: 1,
materials: {
note: `# Introduction\n\nWelcome to this course, we are glad you made it here\n\n## What you'l learn\n\n1. Build powerful, fast, user-friendly and reactive web apps.\n2. Provide amazing user experiences by leveraging the power of JavaScript with ease.\n3. Apply for high-paid jobs or work as a freelancer in one the most-demanded sectors you can find in web dev right now.\n4. Learn all about React Hooks and React Components.`,
slide:
'https://docs.google.com/presentation/d/e/2PACX-1vS7mulzY5v1X_D0Gz68M2sggq2zjHCuoyY5JI07hcsOA4svDV5rxhLD9V3ckO_Rz7TK1YniQJtu0CXM/embed?start=false&loop=false&delayms=3000',
video: 'https://www.youtube.com/embed/luM5uobewhA',
},
exercises: [
{
id: 1,
title: 'Practice Exercise',
},
{
id: 2,
title: 'Home task',
},
],
},
];
// id, lesson_id
const exercises = [
const lesson_exercise = [
{
id: 1,
lesson_id: 1,
title: "Home task 1",
title: 'Home task 1',
description:
"You will be to answer 10 questions, it isn't timed so you can take your time to answer. You can also continue from where you left off, you don't need to worry cause everything is automatically syncronized in the cloud.",
questions: [
{
id: "react-founder",
id: 'react-founder',
type: QUESTION_TYPE.CHECKBOX,
title: "Who is the creator of React.js",
title: 'Who is the creator of React.js',
options: [
{
id: "1",
value: "Dan Abrahmov",
id: '1',
value: 'Dan Abrahmov',
},
{
id: "2",
value: "Google",
id: '2',
value: 'Google',
},
{
id: "3",
value: "Facebook",
id: '3',
value: 'Facebook',
},
{
id: "4",
value: "Traversy Media",
id: '4',
value: 'Traversy Media',
},
],
answers: ["3"],
answers: ['3'],
},
{
id: "vue-founder",
id: 'vue-founder',
type: QUESTION_TYPE.CHECKBOX,
title: "Who is the creator of Vue.js",
title: 'Who is the creator of Vue.js',
code: `const name = 'Josh Perez';\nconst element = <h1>Hello, {name}</h1>;\n\nReactDOM.render(\n
element,\,
document.getElementById('root')
@@ -265,62 +287,63 @@ const exercises = [
`,
options: [
{
id: "1",
value: "Evan Vue",
id: '1',
value: 'Evan Vue',
},
{
id: "2",
value: "Mark Zukerberg",
id: '2',
value: 'Mark Zukerberg',
},
{
id: "3",
value: "Prince Charles",
id: '3',
value: 'Prince Charles',
},
{
id: "4",
value: "Bill Gates",
id: '4',
value: 'Bill Gates',
},
],
answers: ["3"],
answers: ['3'],
},
{
id: "svelte-founder",
id: 'svelte-founder',
type: QUESTION_TYPE.RADIO,
title: "Who is the creator of Svelte.js",
title: 'Who is the creator of Svelte.js',
options: [
{
id: "1",
value: "Hillary Svelte",
id: '1',
value: 'Hillary Svelte',
},
{
id: "2",
value: "Mircosoft",
id: '2',
value: 'Mircosoft',
},
{
id: "3",
value: "Elevate",
id: '3',
value: 'Elevate',
},
{
id: "4",
value: "Coding Train",
id: '4',
value: 'Coding Train',
},
],
answers: ["2"],
answers: ['2'],
},
{
id: "angular-founder",
id: 'angular-founder',
type: QUESTION_TYPE.TEXTAREA,
title: "Who is the creator of Angular.js",
value: "",
title: 'Who is the creator of Angular.js',
value: '',
},
],
},
];
const answers = [
const exercise_answers = [
{
lesson_id: 1,
exercise_id: 1,
taken_by: {},
answers: {},
scores: {},
currentQuestionIndex: 0,
@@ -330,15 +353,15 @@ const answers = [
},
];
const lessonLookup = new Map();
lessons.forEach((lesson) => {
lessonLookup.set(`${lesson.id}`, JSON.stringify(lesson));
});
// const lessonLookup = new Map();
// lessons.forEach((lesson) => {
// lessonLookup.set(`${lesson.id}`, JSON.stringify(lesson));
// });
export function getCourse(id) {
return {
id,
name: "React.js",
name: 'React.js',
people,
roles: ROLE,
lessons: getLessons(id),
@@ -350,16 +373,28 @@ export function getLessons(courseId) {
}
export function getLesson(id) {
return lookup.get(id);
return (
lessonData.find((lesson) => lesson.lesson_id === id) || {
lesson_id: null,
materials: {
note: '',
slide: '',
video: '',
},
exercises: [],
}
);
}
export function getExercises(lessonId) {
return exercises.filter((exercise) => exercise.lesson_id === lessonId);
return lesson_exercise.filter((exercise) => exercise.lesson_id === lessonId);
}
export function getExercise(exerciseId) {
return {
exercise: exercises.find((exercise) => exercise.id === exerciseId),
answers: answers.find((answer) => answer.exercise_id === exerciseId),
exercise: lesson_exercise.find((exercise) => exercise.id === exerciseId),
answers: exercise_answers.find(
(answer) => answer.exercise_id === exerciseId
),
};
}
+16 -4
View File
@@ -1,15 +1,27 @@
import { getCourse } from "./_db";
import { getCourse, getExercise, getLesson } from './_db';
function writeJsonHead(res, statusCode) {
res.writeHead(statusCode, {
"Content-Type": "application/json",
'Content-Type': 'application/json',
});
}
export function get(req, res, next) {
const { id } = req.query;
const { id, lessonId, exerciseId } = req.query;
const result = {
...getCourse(parseInt(id, 2)),
};
if (lessonId) {
result.lesson = getLesson(parseInt(lessonId, 2));
}
if (exerciseId) {
result.exercise = getExercise(parseInt(exerciseId, 2));
}
writeJsonHead(res, 200);
return res.end(JSON.stringify(getCourse(parseInt(id, 2))));
return res.end(JSON.stringify(result));
}
@@ -3,11 +3,18 @@
const { id: courseId, lessonParams = [] } = params;
const [lessonId, exerciseRouteName, exerciseId] = lessonParams;
const res = await this.fetch(`api/course?id=${courseId}`);
const res = await this.fetch(
`api/course?id=${courseId}&lessonId=${lessonId}&exerciseId=${exerciseId}`
);
const data = await res.json();
if (res.status === 200) {
return { courseData: data, lessonId, exerciseRouteName, exerciseId };
return {
courseData: data,
lessonId,
isMaterialsTabActive: !exerciseRouteName,
exerciseId,
};
} else {
this.error(res.status, data.message);
}
@@ -15,47 +22,60 @@
</script>
<script>
import { stores } from "@sapper/app";
import CourseContainer from "../../../../../components/CourseContainer/index.svelte";
import { stores } from '@sapper/app';
import CourseContainer from '../../../../../components/CourseContainer/index.svelte';
import PageNav from "../../../../../components/PageNav/index.svelte";
import PageBody from "../../../../../components/PageBody/index.svelte";
import Readme from "../../../../../components/Course/components/Lesson/Readme/index.svelte";
import Exercises from "../../../../../components/Course/components/Lesson/Exercises/index.svelte";
import PrimaryButton from '../../../../../components/PrimaryButton/index.svelte';
import PageNav from '../../../../../components/PageNav/index.svelte';
import PageBody from '../../../../../components/PageBody/index.svelte';
import Materials from '../../../../../components/Course/components/Lesson/Materials/index.svelte';
import Exercises from '../../../../../components/Course/components/Lesson/Exercises/index.svelte';
import MODES from '../../../../../utils/constants/mode.js';
export let courseData;
export let lessonId;
export let exerciseRouteName;
export let isMaterialsTabActive;
export let exerciseId;
let path;
let mode = MODES.view;
const { page } = stores();
$: path = $page.path.replace(/\/exercises[\/ 0-9]*/, "");
$: path = $page.path.replace(/\/exercises[\/ 0-9]*/, '');
</script>
<CourseContainer {courseData} {path}>
<PageNav
navItems={[
{
label: "README.md",
isActive: !exerciseRouteName,
label: 'Materials',
isActive: isMaterialsTabActive,
href: path,
},
{
label: "Exercises",
isActive: !!exerciseRouteName,
label: 'Exercises',
isActive: !isMaterialsTabActive,
href: `${path}/exercises`,
},
]}
/>
>
<svelte:fragment slot="widget">
<div class="flex items-center">
<PrimaryButton
className="mr-2"
label={mode === MODES.edit ? 'Save' : 'Edit'}
onClick={() => (mode = mode === MODES.edit ? MODES.view : MODES.edit)}
/>
</div>
</svelte:fragment>
</PageNav>
<PageBody>
{#if !!exerciseRouteName}
{#if !isMaterialsTabActive}
<Exercises {exerciseId} path={`${path}/exercises`} />
{:else}
<Readme {lessonId} />
<Materials {lessonId} {mode} />
{/if}
</PageBody>
</CourseContainer>