CRUD* Lecture
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
<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";
|
||||
@@ -9,6 +10,8 @@
|
||||
import IconButton from "../../IconButton/index.svelte";
|
||||
import { getNavItemRoute, getLectureRoute, getLectureNo } from "../function";
|
||||
|
||||
import { lessons } from "./Lecture/store/lessons";
|
||||
|
||||
// export let lectureId;
|
||||
export let courseId;
|
||||
|
||||
@@ -32,33 +35,7 @@
|
||||
label: "Lectures",
|
||||
to: getLectureRoute(courseId),
|
||||
hideSortIcon: false,
|
||||
items: [
|
||||
{
|
||||
id: 1,
|
||||
title: "Вводный урок. Soft skills",
|
||||
to: getLectureRoute(courseId, 1),
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "Введение в ReactJS",
|
||||
to: getLectureRoute(courseId, 2),
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "Компоненты",
|
||||
to: getLectureRoute(courseId, 3),
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: "Состояние компонентов и пропсы",
|
||||
to: getLectureRoute(courseId, 4),
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: "Жизненный цикл",
|
||||
to: getLectureRoute(courseId, 5),
|
||||
},
|
||||
],
|
||||
isLecture: true,
|
||||
},
|
||||
{
|
||||
label: "People",
|
||||
@@ -92,6 +69,8 @@
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$: console.log("lessons", $lessons);
|
||||
</script>
|
||||
|
||||
<div class="root z-10 {!show && 'hide'}">
|
||||
@@ -105,16 +84,24 @@
|
||||
isGroupActive={$page.path === navItem.to}
|
||||
hideSortIcon={navItem.hideSortIcon}
|
||||
>
|
||||
{#if Array.isArray(navItem.items)}
|
||||
{#each navItem.items as item, index}
|
||||
{#if navItem.isLecture}
|
||||
{#each $lessons as item, index}
|
||||
<a
|
||||
class="item flex items-center {(item.to === $page.path ||
|
||||
!$page.path.length > 3) &&
|
||||
'active'} pl-7 py-2"
|
||||
href={item.to}
|
||||
'active'} pl-7 py-2 {!item.isComplete &&
|
||||
'cursor-not-allowed'}"
|
||||
href={item.isComplete ? item.to : $page.path}
|
||||
>
|
||||
<span class="course-counter"> {getLectureNo(index + 1)} </span>
|
||||
<span>{item.title}</span>
|
||||
{#if item.isComplete}
|
||||
<span class="ml-2 success">
|
||||
<CheckmarkFilled20 class="carbon-icon" />
|
||||
</span>
|
||||
{:else}
|
||||
<span class="text-md ml-2">🔒</span>
|
||||
{/if}
|
||||
</a>
|
||||
{/each}
|
||||
{/if}
|
||||
@@ -146,13 +133,15 @@
|
||||
height: 90vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 360px;
|
||||
min-width: 360px;
|
||||
max-width: 360px;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
border-right: 1px solid var(--border-color);
|
||||
|
||||
&.hide {
|
||||
width: 0px;
|
||||
min-width: 0px;
|
||||
max-width: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,149 @@
|
||||
<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 "../../../../PageNav/index.svelte";
|
||||
import PageBody from "../../../../PageBody/index.svelte";
|
||||
import PrimaryButton from "../../../../PrimaryButton/index.svelte";
|
||||
import TextField from "../../../../Form/TextField.svelte";
|
||||
import IconButton from "../../../../IconButton/index.svelte";
|
||||
import Select from "../../../../Form/Select.svelte";
|
||||
|
||||
import { lessons, lecturers, handleAddLesson } from "../store/lessons";
|
||||
|
||||
// export let path;
|
||||
|
||||
let lectureEditing;
|
||||
let ref;
|
||||
|
||||
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);
|
||||
|
||||
return `${ye}-${mo}-${da}`;
|
||||
}
|
||||
|
||||
function addLesson() {
|
||||
handleAddLesson();
|
||||
setTimeout(() => {
|
||||
lectureEditing = $lessons.length - 1;
|
||||
if (ref) {
|
||||
ref.scrollIntoView({ block: "end", behavior: "smooth" });
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
</script>
|
||||
|
||||
<PageNav title="Lectures" />
|
||||
<PageNav title="Lectures">
|
||||
<div slot="widget">
|
||||
<PrimaryButton label="Add" onClick={addLesson} />
|
||||
</div>
|
||||
</PageNav>
|
||||
|
||||
<PageBody>All lecture will be here</PageBody>
|
||||
<PageBody width="w-11/12 m-auto">
|
||||
<div class="w-4/5 m-auto">
|
||||
{#each $lessons as lecture, index}
|
||||
<div
|
||||
bind:this={ref}
|
||||
class="group relative m-auto rounded-md border-2 border-gray-100 py-3 px-5 mr-4 mb-4 flex items-center hover:shadow-2xl shadow-md transition delay-150 duration-300 ease-in-out"
|
||||
>
|
||||
<!-- Complete or Not complete icon -->
|
||||
<div class="absolute -left-5 -top-5 success">
|
||||
<IconButton
|
||||
onClick={() => (lecture.isComplete = !lecture.isComplete)}
|
||||
>
|
||||
{#if lecture.isComplete}
|
||||
<CheckmarkFilled24 class="carbon-icon" />
|
||||
{:else}
|
||||
<span class="text-2xl">🔒</span>
|
||||
{/if}
|
||||
</IconButton>
|
||||
</div>
|
||||
|
||||
<!-- Edit/Save -->
|
||||
<div class="absolute top-2 right-0">
|
||||
{#if lectureEditing === index}
|
||||
<IconButton onClick={() => (lectureEditing = null)}>
|
||||
<Save24 class="carbon-icon" />
|
||||
</IconButton>
|
||||
{:else}
|
||||
<IconButton onClick={() => (lectureEditing = index)}>
|
||||
<Edit24 class="carbon-icon" />
|
||||
</IconButton>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="">
|
||||
<h3 class="text-4xl font-bold">{index + 1}</h3>
|
||||
</div>
|
||||
<div class="ml-8 w-4/5">
|
||||
{#if lectureEditing === index}
|
||||
<TextField bind:value={lecture.title} autofocus={true} />
|
||||
{:else}
|
||||
<h3 class="text-3xl font-semibold m-0 flex items-center">
|
||||
<a href={lecture.to} class="hover:underline text-black">
|
||||
{lecture.title}
|
||||
</a>
|
||||
</h3>
|
||||
{/if}
|
||||
<div class="flex items-center mb-2">
|
||||
<div class="flex items-center mr-2">
|
||||
{lecture.resources.map((r) => `${r.value} ${r.label}`).join(", ")}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between mt-3 w-full">
|
||||
{#if lectureEditing === index}
|
||||
<Select
|
||||
bind:value={lecture.lecturer.name}
|
||||
options={$lecturers}
|
||||
valueKey="text"
|
||||
/>
|
||||
{:else}
|
||||
<a
|
||||
href="courses/1/people"
|
||||
class="flex items-center hover:underline text-black"
|
||||
>
|
||||
<img
|
||||
alt="Placeholder"
|
||||
class="block rounded-full"
|
||||
width="24"
|
||||
height="20"
|
||||
src={lecture.lecturer.avatar}
|
||||
/>
|
||||
<p class="ml-2 text-sm font-bold">{lecture.lecturer.name}</p>
|
||||
</a>
|
||||
{/if}
|
||||
|
||||
<div class="flex items-center">
|
||||
{#if lectureEditing === index}
|
||||
<input
|
||||
type="date"
|
||||
name="lecture-date-picker"
|
||||
class="p-2 rounded-md w-40"
|
||||
value={formatDate(lecture.date)}
|
||||
on:input={(e) => (lecture.date = e.target.value)}
|
||||
/>
|
||||
{:else}
|
||||
<Time24 class="carbon-icon" />
|
||||
<p class="text-md font-semibold ml-2">
|
||||
{new Date(lecture.date).toDateString()}
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</PageBody>
|
||||
|
||||
<style>
|
||||
.group {
|
||||
min-height: 170px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -7,11 +7,12 @@
|
||||
|
||||
export let path;
|
||||
export let tab;
|
||||
// export let lectureId;
|
||||
export let showLectureHome;
|
||||
</script>
|
||||
|
||||
{#if showLectureHome}
|
||||
<Home />
|
||||
<Home {path} />
|
||||
{:else}
|
||||
<div>
|
||||
<PageNav
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
import { writable } from "svelte/store";
|
||||
|
||||
export const lessons = writable([
|
||||
{
|
||||
title: "Вступительное слово. Софт скилы",
|
||||
to: "/courses/1/lecture/1",
|
||||
lecturer: {
|
||||
userId: 1,
|
||||
avatar: "https://picsum.photos/32/32/?random",
|
||||
name: "Sergey Semko",
|
||||
},
|
||||
resources: [
|
||||
{
|
||||
label: "lesson",
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: "quiz",
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: "home task",
|
||||
value: 2,
|
||||
},
|
||||
],
|
||||
date: new Date(),
|
||||
isComplete: true,
|
||||
},
|
||||
{
|
||||
title: "Введение в ReactJS",
|
||||
to: "/courses/1/lecture/2",
|
||||
lecturer: {
|
||||
userId: 1,
|
||||
avatar: "https://picsum.photos/32/32/?random",
|
||||
name: "Sergey Semko",
|
||||
},
|
||||
resources: [
|
||||
{
|
||||
label: "lesson",
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: "quiz",
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: "home task",
|
||||
value: 2,
|
||||
},
|
||||
],
|
||||
date: new Date(),
|
||||
isComplete: false,
|
||||
},
|
||||
{
|
||||
title: "Компоненты",
|
||||
to: "/courses/1/lecture/3",
|
||||
lecturer: {
|
||||
userId: 1,
|
||||
avatar: "https://picsum.photos/32/32/?random",
|
||||
name: "Sergey Semko",
|
||||
},
|
||||
resources: [
|
||||
{
|
||||
label: "lesson",
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: "quiz",
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: "home task",
|
||||
value: 2,
|
||||
},
|
||||
],
|
||||
date: new Date(),
|
||||
isComplete: false,
|
||||
},
|
||||
{
|
||||
title: "Состояние компонентов и пропсы",
|
||||
to: "/courses/1/lecture/4",
|
||||
lecturer: {
|
||||
userId: 1,
|
||||
avatar: "https://picsum.photos/32/32/?random",
|
||||
name: "Sergey Semko",
|
||||
},
|
||||
resources: [
|
||||
{
|
||||
label: "lesson",
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: "quiz",
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: "home task",
|
||||
value: 2,
|
||||
},
|
||||
],
|
||||
date: new Date(),
|
||||
isComplete: false,
|
||||
},
|
||||
{
|
||||
title: "Жизненный цикл",
|
||||
to: "/courses/1/lecture/5",
|
||||
lecturer: {
|
||||
userId: 1,
|
||||
avatar: "https://picsum.photos/32/32/?random",
|
||||
name: "Sergey Semko",
|
||||
},
|
||||
resources: [
|
||||
{
|
||||
label: "lesson",
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: "quiz",
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: "home task",
|
||||
value: 2,
|
||||
},
|
||||
],
|
||||
date: new Date(),
|
||||
isComplete: false,
|
||||
},
|
||||
]);
|
||||
|
||||
export const lecturers = writable([
|
||||
{
|
||||
id: 1,
|
||||
text: "Sergey Semko",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
text: "Jonathan Nelson",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
text: "Sasha Pokidin",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
text: "Natasha Rudenko",
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
text: "Yulia Marushko",
|
||||
},
|
||||
]);
|
||||
|
||||
|
||||
export function handleAddLesson() {
|
||||
const resources = [
|
||||
{
|
||||
label: "lesson",
|
||||
value: 0,
|
||||
},
|
||||
{
|
||||
label: "quiz",
|
||||
value: 0,
|
||||
},
|
||||
{
|
||||
label: "home task",
|
||||
value: 0,
|
||||
},
|
||||
];
|
||||
|
||||
lessons.update(_lessons => {
|
||||
return [
|
||||
..._lessons,
|
||||
{
|
||||
title: 'Untitled lesson',
|
||||
to: "/courses/1/lecture/" + (_lessons.length + 1),
|
||||
resources,
|
||||
lecturer: {
|
||||
userId: 1,
|
||||
avatar: "https://picsum.photos/32/32/?random",
|
||||
name: "",
|
||||
},
|
||||
date: new Date()
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
@@ -54,7 +54,8 @@
|
||||
avatarUrl={teacher.avatar}
|
||||
title={teacher.title}
|
||||
telegramLink={teacher.telegramLink}
|
||||
mailLink={teacher.mailLink} />
|
||||
mailLink={teacher.mailLink}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
@@ -68,7 +69,8 @@
|
||||
<UserCard
|
||||
name={student.name}
|
||||
avatarUrl={student.avatar}
|
||||
title={student.title} />
|
||||
title={student.title}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
// export let subMenuItems = [];
|
||||
|
||||
function onClick() {
|
||||
isExpanded = !isExpanded;
|
||||
// isExpanded = !isExpanded;
|
||||
handleClick();
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
export let label;
|
||||
export let value;
|
||||
export let options = [];
|
||||
export let valueKey = "id";
|
||||
</script>
|
||||
|
||||
<label class="block">
|
||||
{#if label} <span class="text-gray-700">{label}</span> {/if}
|
||||
<select bind:value class="form-select block w-full mt-1">
|
||||
{#each options as option}
|
||||
<option value={option.id}>
|
||||
<option value={option[valueKey]}>
|
||||
{option.text}
|
||||
</option>
|
||||
{/each}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
export let value;
|
||||
export let onKeyDown;
|
||||
export let className = "";
|
||||
export let autofocus = false;
|
||||
</script>
|
||||
|
||||
<label class="block {className}">
|
||||
@@ -14,5 +15,6 @@
|
||||
on:keydown={onKeyDown}
|
||||
{placeholder}
|
||||
bind:value
|
||||
autofocus
|
||||
/>
|
||||
</label>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<script>
|
||||
// import
|
||||
// border border-gray-300 rounded-md p-3 bg-blue-200 bg-opacity-25
|
||||
export let width = "max-w-3xl";
|
||||
</script>
|
||||
|
||||
<div class="root mt-4 mx-auto max-w-3xl relative pb-5">
|
||||
<div class="mt-4 mx-auto {width} relative pb-5">
|
||||
{#if $$slots.header}
|
||||
<div
|
||||
class="head flex bg-white items-center justify-between sticky right-0 w-full py-3"
|
||||
@@ -16,10 +17,6 @@
|
||||
</div>
|
||||
|
||||
<style>
|
||||
/* .root {
|
||||
max-width: 700px;
|
||||
} */
|
||||
|
||||
.head {
|
||||
top: 61px;
|
||||
z-index: 1;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
export let addButtonHref;
|
||||
export let addButtonLabel;
|
||||
export let disableSticky = false;
|
||||
|
||||
let dynamicRootClass = "";
|
||||
|
||||
$: dynamicRootClass =
|
||||
|
||||
@@ -57,11 +57,11 @@
|
||||
}
|
||||
|
||||
[aria-current] :global(svg.carbon-icon),
|
||||
button.active :global(svg.carbon-icon) {
|
||||
.active :global(svg.carbon-icon) {
|
||||
fill: var(--main-primary-color);
|
||||
}
|
||||
|
||||
button.success :global(svg.carbon-icon) {
|
||||
.success :global(svg.carbon-icon) {
|
||||
fill: green;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -11,27 +11,14 @@
|
||||
export let telegramLink;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.profile {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.profile img {
|
||||
width: 104px;
|
||||
height: 104px;
|
||||
}
|
||||
.description {
|
||||
min-height: 70px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<figure class="profile bg-gray-100 rounded-xl px-4 py-7 mr-4 mb-4">
|
||||
<figure class="w-52 bg-gray-100 px-4 py-7 mr-4 mb-4">
|
||||
<img
|
||||
class="w-32 h-32 rounded-full mx-auto"
|
||||
src={avatarUrl}
|
||||
alt=""
|
||||
width="384"
|
||||
height="512" />
|
||||
height="512"
|
||||
/>
|
||||
<div class="pt-6 text-center space-y-4">
|
||||
<div class="description font-medium flex flex-col justify-around">
|
||||
<div class="text-cyan-600">{name}</div>
|
||||
@@ -45,15 +32,27 @@
|
||||
data={faTelegram}
|
||||
scale="1"
|
||||
class="social-icon"
|
||||
style="color:#4dabf7;margin:0 5px;" />
|
||||
style="color:#4dabf7;margin:0 5px;"
|
||||
/>
|
||||
</a>
|
||||
<a href={mailLink} target="_blank">
|
||||
<Icon
|
||||
data={faEnvelope}
|
||||
scale="1"
|
||||
class="social-icon"
|
||||
style="color:#d44c3d;margin:0 5px;" />
|
||||
style="color:#d44c3d;margin:0 5px;"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
{/if}
|
||||
</figure>
|
||||
|
||||
<style>
|
||||
img {
|
||||
width: 104px;
|
||||
height: 104px;
|
||||
}
|
||||
.description {
|
||||
min-height: 70px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -44,7 +44,12 @@
|
||||
{:else if courseNavItem === "hometasks"}
|
||||
<HomeTask />
|
||||
{:else if courseNavItem === "lecture"}
|
||||
<Lecture path={$page.path} {tab} showLectureHome={!lectureId} />
|
||||
<Lecture
|
||||
path={$page.path}
|
||||
{tab}
|
||||
{lectureId}
|
||||
showLectureHome={!lectureId}
|
||||
/>
|
||||
{:else if courseNavItem === "people"}
|
||||
<People />
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user