Sync with Server

This commit is contained in:
rotimi-best
2021-08-18 19:47:02 +03:00
parent e8c74de9ce
commit 60f06d6ab1
77 changed files with 2989 additions and 7610 deletions
-252
View File
@@ -1,252 +0,0 @@
<script context="module">
export async function preload({ params }) {
let [courseId] = params.id;
const res = await this.fetch(`api/course?id=${courseId}`);
const data = await res.json();
if (res.status === 200) {
return { courseId, courseData: data };
} else {
this.error(res.status, data.message);
}
}
</script>
<script>
import { flip } from 'svelte/animate';
import { dndzone } from 'svelte-dnd-action';
import { stores, goto } from '@sapper/app';
import PageNav from '../../../components/PageNav/index.svelte';
import MarkExerciseModal from '../../../components/Course/components/Lesson/Exercise/MarkExerciseModal.svelte';
import Chip from '../../../components/Chip/index.svelte';
import PageBody from '../../../components/PageBody/index.svelte';
import CourseContainer from '../../../components/CourseContainer/index.svelte';
const flipDurationMs = 300;
export let courseData = {};
let sections = [
{
id: 1,
title: 'Submitted',
value: 5,
items: [
{
id: 41,
exerciseTitle: 'Practice props in React.js',
student: {
avatar: 'https://picsum.photos/32/32/?random',
name: 'Brad Traversy',
},
tutor: {
name: 'sergey-semko',
id: 1,
},
lesson: {
number: 1,
},
},
{
id: 42,
exerciseTitle: 'Practice props in React.js',
student: {
avatar: 'https://picsum.photos/32/32/?random',
name: 'James Kola',
},
tutor: {
name: 'sergey-semko',
id: 1,
},
lesson: {
number: 1,
},
},
{
id: 43,
exerciseTitle: 'Practice props in React.js',
student: {
avatar: 'https://picsum.photos/32/32/?random',
name: 'linus-torvalds',
},
tutor: {
name: 'sergey-semko',
id: 1,
},
lesson: {
number: 1,
},
},
{
id: 44,
exerciseTitle: 'Practice props in React.js',
student: {
avatar: 'https://picsum.photos/32/32/?random',
name: 'rotimi-best',
},
tutor: {
name: 'sergey-semko',
id: 1,
},
lesson: {
number: 1,
},
},
{
id: 45,
exerciseTitle: 'Home task: Class and Functional component',
student: {
avatar: 'https://picsum.photos/32/32/?random',
name: 'james-two',
},
tutor: {
name: 'sergey-semko',
id: 1,
},
lesson: {
number: 2,
},
},
{
id: 46,
exerciseTitle: 'Home task: Class and Functional component',
student: {
avatar: 'https://picsum.photos/32/32/?random',
name: 'brarack-obama',
},
tutor: {
name: 'sergey-semko',
id: 1,
},
lesson: {
number: 2,
},
},
{
id: 47,
exerciseTitle: 'Lesson task: Class and Functional component',
student: {
avatar: 'https://picsum.photos/32/32/?random',
name: 'bill-gates',
},
tutor: {
name: 'sergey-semko',
id: 1,
},
lesson: {
number: 3,
},
},
],
},
{
id: 2,
title: 'In Progress',
value: 0,
items: [],
},
{
id: 3,
title: 'Graded',
value: 10,
items: [],
},
];
let openExercise = false;
const { page } = stores();
function handleItemFinalize(columnIdx, newItems) {
sections[columnIdx].items = newItems;
// onFinalUpdate([...sections]);
}
function handleDndConsiderCards(columnIdx) {
return (e) => {
console.warn('got consider');
sections[columnIdx].items = e.detail.items;
};
}
function handleDndFinalizeCards(columnIdx) {
return (e) => handleItemFinalize(columnIdx, e.detail.items);
}
function handleModalClose() {
goto($page.path);
}
$: openExercise = !!$page.query.submissionId;
</script>
<MarkExerciseModal bind:open={openExercise} onClose={handleModalClose} submissionId={$page.query.submissionId}/>
<CourseContainer {courseData}>
<PageNav title="All Exercises" />
<PageBody width="w-11/12 overflow-x-auto overflow-y-hidden">
<div class="flex items-center justify-between w-full">
{#each sections as { id, title, items }, idx (id)}
<div
class="section rounded-md bg-gray-100 border border-gray-50 p-3 h-80 mr-3 overflow-hidden"
animate:flip={{ duration: flipDurationMs }}
>
<div class="flex items-center mb-2">
<Chip value={items.length} />
<p class="ml-2 font-bold">{title}</p>
</div>
<div
class="content pr-2 overflow-y-auto mb-3"
use:dndzone={{
items,
flipDurationMs,
dropTargetStyle: { outline: 'blue' },
}}
on:consider={handleDndConsiderCards(idx)}
on:finalize={handleDndFinalizeCards(idx)}
>
{#each items as item (item.id)}
<div
class="border border-grey-700 w-full my-2 mx-0 rounded-md bg-white py-2 px-3"
animate:flip={{ duration: flipDurationMs }}
>
<a
class="flex items-center no-underline hover:underline text-black mb-2"
href="{$page.path}?submissionId={item.id}"
>
<img
alt="Placeholder"
class="block rounded-full"
width="24"
height="20"
src={item.student.avatar}
/>
<p class="ml-2 text-sm">{item.student.name}</p>
</a>
<a
class="text-blue-700 text-md font-bold"
href="{$page.path}?submissionId={item.id}"
>{item.exerciseTitle}</a
>
<p class="text-grey text-sm">
#{item.lesson.number} created by {item.tutor.name}
</p>
</div>
{/each}
</div>
</div>
{/each}
</div>
</PageBody>
</CourseContainer>
<style>
.section {
max-width: 355px;
min-width: 355px;
height: 80vh;
}
.content {
height: 95%;
}
</style>
+17 -13
View File
@@ -1,25 +1,29 @@
<script context="module">
export async function preload({ params }) {
let [courseId] = params.id;
import { fetchCourse } from '../../../utils/services/courses';
const res = await this.fetch(`api/course?id=${courseId}`);
const data = await res.json();
export async function preload({ params }, session) {
const {
data,
// error
} = await fetchCourse(params.id, session);
if (res.status === 200) {
return { courseId, courseData: data };
} else {
this.error(res.status, data.message);
}
return { courseData: data };
}
</script>
<script>
import Overview from "../../../components/Course/index.svelte";
import CourseContainer from "../../../components/CourseContainer/index.svelte";
import { onMount } from 'svelte';
import Overview from '../../../components/Course/index.svelte';
import CourseContainer from '../../../components/CourseContainer/index.svelte';
import { setCourseData } from '../../../components/Course/store';
export let courseData = {};
export let courseData;
onMount(async () => {
setCourseData(courseData);
});
</script>
<CourseContainer {courseData}>
<CourseContainer>
<Overview />
</CourseContainer>
@@ -1,27 +1,43 @@
<script context="module">
export async function preload({ params }) {
import {
fetchCourse,
fetchLesson,
} from '../../../../../utils/services/courses';
let prevLessonId;
export async function preload({ params }, session) {
const { id: courseId, lessonParams = [] } = params;
const [lessonId, exerciseRouteName, exerciseId] = lessonParams;
let courseData;
let lessonData;
const res = await this.fetch(
`api/course?id=${courseId}&lessonId=${lessonId}&exerciseId=${exerciseId}`
);
const data = await res.json();
if (!process.browser) {
const [course, lesson] = await Promise.all([
fetchCourse(courseId, session),
fetchLesson(lessonId),
]);
if (res.status === 200) {
return {
courseData: data,
lessonId,
isMaterialsTabActive: !exerciseRouteName,
exerciseId,
};
} else {
this.error(res.status, data.message);
courseData = course.data;
lessonData = lesson.data;
} else if (prevLessonId && prevLessonId !== lessonId) {
const lesson = await fetchLesson(lessonId);
lessonData = lesson.data;
}
prevLessonId = lessonId;
return {
courseData,
lessonData,
isMaterialsTabActive: !exerciseRouteName,
lessonId,
exerciseId,
};
}
</script>
<script>
import { onMount, afterUpdate } from 'svelte';
import { stores } from '@sapper/app';
import CourseContainer from '../../../../../components/CourseContainer/index.svelte';
@@ -31,21 +47,42 @@
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';
import { setCourseData } from '../../../../../components/Course/store';
import { lesson } from '../../../../../components/Course/components/Lesson/store/lessons';
export let courseData;
export let lessonData;
export let lessonId;
export let isMaterialsTabActive;
export let exerciseId;
export let isMaterialsTabActive;
let path;
let mode = MODES.view;
const { page } = stores();
$: path = $page.path.replace(/\/exercises[\/ 0-9]*/, '');
function setLesson() {
if (!lessonData) return;
lesson.update((l) => ({
...l,
id: lessonId,
materials: lessonData,
}));
}
onMount(() => {
setCourseData(courseData);
setLesson();
});
afterUpdate(setLesson);
$: path = $page.path.replace(/\/exercises[\/ 0-9 a-z -]*/, '');
</script>
<CourseContainer {courseData} {path}>
<CourseContainer {path} isExercisePage={!isMaterialsTabActive && exerciseId}>
<PageNav
navItems={[
{
@@ -74,11 +111,11 @@
</svelte:fragment>
</PageNav>
<PageBody>
{#if !isMaterialsTabActive}
<Exercises {exerciseId} path={`${path}/exercises`} />
{:else}
{#if !isMaterialsTabActive}
<Exercises {lessonId} {exerciseId} path={`${path}/exercises`} />
{:else}
<PageBody width="w-11/12 m-auto">
<Materials {lessonId} {mode} />
{/if}
</PageBody>
</PageBody>
{/if}
</CourseContainer>
+139 -52
View File
@@ -1,70 +1,113 @@
<script context="module">
export async function preload({ params }) {
let [courseId, courseNavItem, lessonId] = params.id;
const res = await this.fetch(`api/course?id=${courseId}`);
const data = await res.json();
import { fetchCourse } from '../../../../utils/services/courses';
if (res.status === 200) {
return { courseId, courseData: data };
} else {
this.error(res.status, data.message);
export async function preload({ params }, session) {
if (process.browser) {
return {};
}
const {
data,
// error
} = await fetchCourse(params.id, session);
return { courseData: data };
}
</script>
<script>
import Time24 from "carbon-icons-svelte/lib/Time24";
import Edit24 from "carbon-icons-svelte/lib/Edit24";
import Save24 from "carbon-icons-svelte/lib/Save24";
import CheckmarkFilled24 from "carbon-icons-svelte/lib/CheckmarkFilled24";
import PageNav from "../../../../components/PageNav/index.svelte";
import PageBody from "../../../../components/PageBody/index.svelte";
import PrimaryButton from "../../../../components/PrimaryButton/index.svelte";
import TextField from "../../../../components/Form/TextField.svelte";
import IconButton from "../../../../components/IconButton/index.svelte";
import Select from "../../../../components/Form/Select.svelte";
import CourseContainer from "../../../../components/CourseContainer/index.svelte";
import { onMount } from 'svelte';
import Time24 from 'carbon-icons-svelte/lib/Time24';
import Edit24 from 'carbon-icons-svelte/lib/Edit24';
import Save24 from 'carbon-icons-svelte/lib/Save24';
import Phone24 from 'carbon-icons-svelte/lib/Phone24';
import Delete24 from 'carbon-icons-svelte/lib/Delete24';
import CheckmarkFilled24 from 'carbon-icons-svelte/lib/CheckmarkFilled24';
import PageNav from '../../../../components/PageNav/index.svelte';
import Box from '../../../../components/Box/index.svelte';
import PageBody from '../../../../components/PageBody/index.svelte';
import PrimaryButton from '../../../../components/PrimaryButton/index.svelte';
import TextField from '../../../../components/Form/TextField.svelte';
import IconButton from '../../../../components/IconButton/index.svelte';
import Select from '../../../../components/Form/Select.svelte';
import CourseContainer from '../../../../components/CourseContainer/index.svelte';
import {
lessons,
tutors,
handleAddLesson,
} from "../../../../components/Course/components/Lesson/store/lessons";
handleSaveLesson,
handleDelete,
} from '../../../../components/Course/components/Lesson/store/lessons';
import {
course,
setCourseData,
group,
} from '../../../../components/Course/store';
export let courseData;
let lessonEditing;
let ref;
const resources = [
{
label: 'lesson',
value: 1,
},
{
label: 'quiz',
value: 1,
},
{
label: 'home task',
value: 2,
},
];
function formatDate(date) {
const d = new Date(date);
const ye = new Intl.DateTimeFormat("en", { year: "numeric" }).format(d);
const mo = new Intl.DateTimeFormat("en", { month: "2-digit" }).format(d);
const da = new Intl.DateTimeFormat("en", { day: "2-digit" }).format(d);
const ye = new Intl.DateTimeFormat('en', { year: 'numeric' }).format(d);
const mo = new Intl.DateTimeFormat('en', { month: '2-digit' }).format(d);
const da = new Intl.DateTimeFormat('en', { day: '2-digit' }).format(d);
return `${ye}-${mo}-${da}`;
}
// TODO: CRUD of lessons
function addLesson() {
handleAddLesson();
setTimeout(() => {
lessonEditing = $lessons.length - 1;
if (ref) {
ref.scrollIntoView({ block: "end", behavior: "smooth" });
ref.scrollIntoView({
block: 'start',
behavior: 'smooth',
inline: 'nearest',
});
}
}, 100);
}
onMount(() => {
setCourseData(courseData);
});
$: console.log(`$lessons`, $lessons);
</script>
<CourseContainer {courseData}>
<CourseContainer>
<PageNav title="Lessons">
<div slot="widget">
<PrimaryButton label="Add" onClick={addLesson} />
<PrimaryButton
label="Add"
onClick={addLesson}
isDisabled={!!lessonEditing}
/>
</div>
</PageNav>
<PageBody width="w-11/12 m-auto">
<div class="w-4/5 m-auto">
<div class="w-4/5 mt-4 m-auto">
{#each $lessons as lesson, index}
<div
bind:this={ref}
@@ -73,13 +116,16 @@
<!-- Complete or Not complete icon -->
<div class="absolute -left-6 -top-6 success">
<IconButton
onClick={() => (lesson.isComplete = !lesson.isComplete)}
onClick={() => {
lesson.is_complete = !lesson.is_complete;
handleSaveLesson(lesson, $course.id);
}}
toolTipProps={{
title: `Click to ${lesson.isComplete ? "lock" : "unlock"}`,
direction: "right",
title: `Click to ${lesson.is_complete ? 'lock' : 'unlock'}`,
direction: 'right',
}}
>
{#if lesson.isComplete}
{#if lesson.is_complete}
<CheckmarkFilled24 class="carbon-icon" />
{:else}
<span class="text-2xl">🔒</span>
@@ -90,7 +136,12 @@
<!-- Edit/Save -->
<div class="absolute top-2 right-0">
{#if lessonEditing === index}
<IconButton onClick={() => (lessonEditing = null)}>
<IconButton
onClick={() => {
lessonEditing = null;
handleSaveLesson(lesson, $course.id);
}}
>
<Save24 class="carbon-icon" />
</IconButton>
{:else}
@@ -107,62 +158,98 @@
{#if lessonEditing === index}
<TextField bind:value={lesson.title} autofocus={true} />
{:else}
<h3 class="text-3xl font-semibold m-0 flex items-center">
<a href={lesson.to} class="hover:underline text-black">
<h3 class="text-2xl m-0 flex items-center">
<a
href="/courses/{$course.id}/lessons/{lesson.id}"
class="hover:underline text-black"
>
{lesson.title}
</a>
</h3>
{/if}
<div class="flex items-center mb-2">
<!-- <div class="flex items-center mb-2">
<div class="flex items-center mr-2">
{lesson.resources
.map((r) => `${r.value} ${r.label}`)
.join(", ")}
{resources.map((r) => `${r.value} ${r.label}`).join(', ')}
</div>
</div>
</div> -->
<div class="flex items-center justify-between mt-3 w-full">
<div class="flex items-center justify-between mt-6 w-full">
{#if lessonEditing === index}
<Select
bind:value={lesson.tutor.name}
options={$tutors}
valueKey="text"
bind:value={lesson.profile}
options={$group.tutors}
labelKey="fullname"
/>
{:else if !lesson.profile}
<p class="ml-2 text-sm">No tutor added</p>
{:else}
<a
href="courses/1/people"
class="flex items-center hover:underline text-black"
class="flex items-center hover:underline"
>
<img
alt="Placeholder"
class="block rounded-full"
width="24"
height="20"
src={lesson.tutor.avatar}
src={lesson.profile.avatar_url}
/>
<p class="ml-2 text-sm font-bold">{lesson.tutor.name}</p>
<p class="ml-2 text-sm">
{lesson.profile.fullname}
</p>
</a>
{/if}
<div class="flex items-center">
<div class="flex items-cente">
{#if lessonEditing === index}
<input
type="date"
name="lesson-date-picker"
class="p-2 rounded-md w-40"
value={formatDate(lesson.date)}
on:input={(e) => (lesson.date = e.target.value)}
value={formatDate(lesson.lesson_at)}
on:input={(e) => (lesson.lesson_at = e.target.value)}
/>
{:else}
<Time24 class="carbon-icon" />
<p class="text-md font-semibold ml-2">
{new Date(lesson.date).toDateString()}
<p class="text-md ml-2">
{new Date(lesson.lesson_at).toDateString()}
</p>
{/if}
</div>
<div class="flex items-center">
{#if lessonEditing === index}
<TextField
bind:value={lesson.call_url}
autofocus={true}
className="w-40"
placeholder="https://meet.google.com/mga-dsjs-fmb"
/>
{:else}
<Phone24 class="carbon-icon" />
<a
class="text-md ml-2"
href={lesson.call_url || '#'}
target="_blank"
>
{lesson.call_url ? 'Join lesson' : 'No link'}
</a>
{/if}
</div>
</div>
</div>
<div class="absolute bottom-2 right-0">
<IconButton onClick={handleDelete(lesson.id)}>
<Delete24 class="carbon-icon" />
</IconButton>
</div>
</div>
{:else}
<Box>
<h3 class="text-3xl text-gray-500">No lesson added</h3>
<img alt="No lesson added" src="/notfound2.gif" class="w-80" />
</Box>
{/each}
</div>
</PageBody>
+23 -15
View File
@@ -1,33 +1,41 @@
<script context="module">
export async function preload({ params }) {
let [courseId] = params.id;
import { fetchCourse } from '../../../utils/services/courses';
const res = await this.fetch(`api/course?id=${courseId}`);
const data = await res.json();
if (res.status === 200) {
return { courseId, courseData: data };
} else {
this.error(res.status, data.message);
export async function preload({ params }, session) {
if (process.browser) {
return {};
}
const {
data,
// error
} = await fetchCourse(params.id, session);
return { courseData: data };
}
</script>
<script>
import { stores } from "@sapper/app";
import CourseContainer from "../../../components/CourseContainer/index.svelte";
import PageNav from "../../../components/PageNav/index.svelte";
import UserCard from "../../../components/UserCard/index.svelte";
import { onMount } from 'svelte';
import { stores } from '@sapper/app';
import CourseContainer from '../../../components/CourseContainer/index.svelte';
import PageNav from '../../../components/PageNav/index.svelte';
import UserCard from '../../../components/UserCard/index.svelte';
import { setCourseData } from '../../../components/Course/store';
export let courseData = {};
const { page } = stores();
const teachers = courseData.people;
const teachers = [];
const students = [...teachers, ...teachers, ...teachers];
onMount(() => {
setCourseData(courseData);
});
</script>
<CourseContainer {courseData}>
<CourseContainer>
<PageNav title="People" disableSticky={true} />
<div class="m-3">
<h3>
+20 -12
View File
@@ -1,25 +1,33 @@
<script context="module">
export async function preload({ params }) {
let [courseId] = params.id;
import { fetchCourse } from '../../../utils/services/courses';
const res = await this.fetch(`api/course?id=${courseId}`);
const data = await res.json();
if (res.status === 200) {
return { courseId, courseData: data };
} else {
this.error(res.status, data.message);
export async function preload({ params }, session) {
if (process.browser) {
return {};
}
const {
data,
// error
} = await fetchCourse(params.id, session);
return { courseData: data };
}
</script>
<script>
import CourseContainer from "../../../components/CourseContainer/index.svelte";
import PageNav from "../../../components/PageNav/index.svelte";
import { onMount } from 'svelte';
import CourseContainer from '../../../components/CourseContainer/index.svelte';
import PageNav from '../../../components/PageNav/index.svelte';
import { setCourseData } from '../../../components/Course/store';
export let courseData = {};
onMount(() => {
setCourseData(courseData);
});
</script>
<CourseContainer {courseData}>
<CourseContainer>
<PageNav title="Scoreboard" />
</CourseContainer>
+248
View File
@@ -0,0 +1,248 @@
<script context="module">
import { fetchCourse } from '../../../utils/services/courses';
export async function preload({ params }, session) {
if (process.browser) {
return {};
}
const {
data,
// error
} = await fetchCourse(params.id, session);
return { courseData: data };
}
</script>
<script>
import { onMount } from 'svelte';
import { flip } from 'svelte/animate';
import { dndzone } from 'svelte-dnd-action';
import { stores, goto } from '@sapper/app';
import PageNav from '../../../components/PageNav/index.svelte';
import MarkExerciseModal from '../../../components/Course/components/Lesson/Exercise/MarkExerciseModal.svelte';
import Chip from '../../../components/Chip/index.svelte';
import PageBody from '../../../components/PageBody/index.svelte';
import CourseContainer from '../../../components/CourseContainer/index.svelte';
import { setCourseData, course } from '../../../components/Course/store';
import { fetchSubmissions } from '../../../utils/services/submissions';
const flipDurationMs = 300;
export let courseData = {};
let sections = [
{
id: 1,
title: 'Submitted',
value: 0,
items: [],
},
{
id: 2,
title: 'In Progress',
value: 0,
items: [],
},
{
id: 3,
title: 'Graded',
value: 10,
items: [],
},
];
let submissionIdData = {};
let submissionId;
let openExercise = false;
const { page } = stores();
function handleItemFinalize(columnIdx, newItems) {
console.log('handleItemFinalize columnIdx=>', columnIdx, newItems);
sections[columnIdx].items = newItems;
// onFinalUpdate([...sections]);
}
function handleDndConsiderCards(columnIdx) {
return (e) => {
console.warn(
'handleDndConsiderCards columnIdx',
columnIdx,
e.detail.items
);
sections[columnIdx].items = e.detail.items;
};
}
function handleDndFinalizeCards(columnIdx) {
return (e) => handleItemFinalize(columnIdx, e.detail.items);
}
function handleModalClose() {
goto($page.path);
}
function formatAnswers(data) {
const answers = {};
const questionByIdAndName = {};
for (const question of data.questions) {
questionByIdAndName[question.id] = question.name;
}
for (const answer of data.answers) {
const questionName = questionByIdAndName[answer.question_id];
answers[questionName] =
Array.isArray(answer.answers) && answer.answers.length
? answer.answers
: answer.open_answer;
}
return answers;
}
onMount(async () => {
setCourseData(courseData);
console.log(`courseData`, courseData);
console.log(`$course`, $course);
const { data: submissions } = await fetchSubmissions(
courseData.id || $course.id
);
const sectionById = {};
for (const submission of submissions) {
const { id, exercise, course, answers, groupmember, status_id } =
submission;
const submissionItem = {
id,
exercise: {
id: exercise.id,
title: exercise.title,
},
answers,
student: groupmember && groupmember.profile ? groupmember.profile : {},
lesson: {
id: exercise.lesson.id,
title: exercise.lesson.title,
},
};
submissionIdData[id] = {
questions: exercise.questions,
answers: formatAnswers({ questions: exercise.questions, answers }),
};
if (Array.isArray(sectionById[status_id])) {
sectionById[status_id].push(submissionItem);
} else {
sectionById[status_id] = [submissionItem];
}
}
sections = sections.map((section, index) => ({
...section,
value: Array.isArray(sectionById[index + 1])
? sectionById[index + 1].length
: 0,
items: Array.isArray(sectionById[index + 1])
? sectionById[index + 1]
: [],
}));
console.log(`submissionIdData`, submissionIdData);
});
$: {
submissionId = $page.query.submissionId;
openExercise = !!submissionId;
}
</script>
<MarkExerciseModal
bind:open={openExercise}
onClose={handleModalClose}
data={submissionIdData[submissionId] || {}}
{submissionId}
/>
<CourseContainer>
<PageNav title="Submitted Exercises" />
<PageBody width="w-11/12 overflow-x-auto overflow-y-hidden">
<div class="flex items-center justify-between w-full">
{#each sections as { id, title, items }, idx (id)}
<div
class="section rounded-md bg-gray-100 border border-gray-50 p-3 h-80 mr-3 overflow-hidden"
animate:flip={{ duration: flipDurationMs }}
>
<div class="flex items-center mb-2">
<Chip value={items.length} />
<p class="ml-2 font-bold">{title}</p>
</div>
<div
class="content pr-2 overflow-y-auto mb-3"
use:dndzone={{
items,
flipDurationMs,
dropTargetStyle: { outline: 'blue' },
}}
on:consider={handleDndConsiderCards(idx)}
on:finalize={handleDndFinalizeCards(idx)}
>
{#each items as item (item.id)}
<div
class="border border-grey-700 w-full my-2 mx-0 rounded-md bg-white py-2 px-3"
animate:flip={{ duration: flipDurationMs }}
>
<a
class="flex items-center no-underline hover:underline text-black mb-2"
href="{$page.path}?submissionId={item.student.id}"
>
<img
alt="Placeholder"
class="block rounded-full"
width="24"
height="20"
src={item.student.avatar_url}
/>
<p class="ml-2 text-sm">{item.student.username}</p>
</a>
<a
class="text-blue-700 text-md font-bold"
href="{$page.path}?submissionId={item.id}"
>
{item.exercise.title}
</a>
<a
class="flex items-center no-underline hover:underline text-black my-2"
href="{$page.path.replace('submissions', 'lessons')}/{item
.lesson.id}/exercises"
>
<p class="text-grey text-sm">
#{item.lesson.title}
{`${item.tutor ? 'created by' + item.tutor.name : ''}`}
</p>
</a>
</div>
{/each}
</div>
</div>
{/each}
</div>
</PageBody>
</CourseContainer>
<style>
.section {
max-width: 355px;
min-width: 355px;
height: 80vh;
}
.content {
height: 95%;
}
</style>
+20 -12
View File
@@ -1,25 +1,33 @@
<script context="module">
export async function preload({ params }) {
let [courseId] = params.id;
import { fetchCourse } from '../../../utils/services/courses';
const res = await this.fetch(`api/course?id=${courseId}`);
const data = await res.json();
if (res.status === 200) {
return { courseId, courseData: data };
} else {
this.error(res.status, data.message);
export async function preload({ params }, session) {
if (process.browser) {
return {};
}
const {
data,
// error
} = await fetchCourse(params.id, session);
return { courseData: data };
}
</script>
<script>
import CourseContainer from "../../../components/CourseContainer/index.svelte";
import PageNav from "../../../components/PageNav/index.svelte";
import { onMount } from 'svelte';
import CourseContainer from '../../../components/CourseContainer/index.svelte';
import PageNav from '../../../components/PageNav/index.svelte';
import { setCourseData } from '../../../components/Course/store';
export let courseData = {};
onMount(() => {
setCourseData(courseData);
});
</script>
<CourseContainer {courseData}>
<CourseContainer>
<PageNav title="Timetable" />
</CourseContainer>