CLASSROOMIO-T-46: Forum CRUD MVP
Co-authored-by: Rotimi Best <[email protected]> Merge-request: CLASSROOMIO-MR-13 Merged-by: Digdippa Team <[email protected]>
This commit is contained in:
committed by
Space Cloud
co-authored by
Rotimi Best
parent
779827ba46
commit
c36a49a1ee
@@ -16,10 +16,6 @@
|
||||
|
||||
<svelte:head>
|
||||
<title>{status}</title>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://unpkg.com/carbon-components-svelte/css/g90.css"
|
||||
/>
|
||||
</svelte:head>
|
||||
|
||||
<div class="w-3/5 h-3/5 m-auto flex flex-col items-center justify-center">
|
||||
|
||||
@@ -102,6 +102,9 @@
|
||||
console.error('Error adding user to organisation', error);
|
||||
} else {
|
||||
console.log('Success adding user to organisation', data);
|
||||
const memberId = data?.[0]?.id || '';
|
||||
|
||||
$currentOrg.memberId = memberId;
|
||||
}
|
||||
|
||||
if (params.has('redirect')) {
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
<!-- <script context="module">
|
||||
export async function preload(page, { user, config }) {
|
||||
return { user, config };
|
||||
}
|
||||
</script> -->
|
||||
<script>
|
||||
import GroupPresentation32 from 'carbon-icons-svelte/lib/GroupPresentation32';
|
||||
import UserSpeaker32 from 'carbon-icons-svelte/lib/UserSpeaker32';
|
||||
import Book32 from 'carbon-icons-svelte/lib/Book32';
|
||||
|
||||
let borderBottomGrey = 'border-r-0 border-b border-l-0 border-gray-300';
|
||||
let animate = 'transition delay-75 duration-300 ease-in-out';
|
||||
let boxes = [
|
||||
{
|
||||
label: 'Students',
|
||||
value: 50,
|
||||
icon: 'students',
|
||||
},
|
||||
{
|
||||
label: 'Tutors',
|
||||
value: 10,
|
||||
icon: 'tutor',
|
||||
},
|
||||
{
|
||||
label: 'Courses',
|
||||
value: 20,
|
||||
icon: 'courses',
|
||||
},
|
||||
];
|
||||
|
||||
let topStudents = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Brad Traversy',
|
||||
score: 97.8,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Ebuka Jacobs',
|
||||
score: 96,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'Ngozi Okonjo Iwuala',
|
||||
score: 95,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: 'Nathaniel Bassey',
|
||||
score: 80,
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: 'Barack Obama',
|
||||
score: 79,
|
||||
},
|
||||
];
|
||||
|
||||
let topCourses = [
|
||||
{
|
||||
title: 'CityBIM Basic Module',
|
||||
rating: 5,
|
||||
},
|
||||
{
|
||||
title: 'CityBIM Advanced Module',
|
||||
rating: 4.5,
|
||||
},
|
||||
{
|
||||
title: 'Introduction to ClasroomIO',
|
||||
rating: 4.2,
|
||||
},
|
||||
{
|
||||
title: 'Advanced Builders Module',
|
||||
rating: 4.1,
|
||||
},
|
||||
{
|
||||
title: 'Artificial Intelligence - Basics',
|
||||
rating: 4,
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Admin Dashboard - ClassroomIO</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="root w-full py-10 bg-gray-100 dark:bg-gray-700">
|
||||
<div class="flex items-center justify-evenly max-w-6xl m-auto">
|
||||
{#each boxes as box}
|
||||
<div
|
||||
class="bg-white dark:bg-gray-800 rounded-md py-3 px-16 active shadow-md hover:shadow-2xl border-2 hover:border-blue-700 cursor-pointer {animate}"
|
||||
>
|
||||
{#if box.icon === 'students'}
|
||||
<UserSpeaker32 class="carbon-icon" />
|
||||
{:else if box.icon === 'tutor'}
|
||||
<GroupPresentation32 class="carbon-icon" />
|
||||
{:else if box.icon === 'courses'}
|
||||
<Book32 class="carbon-icon" />
|
||||
{/if}
|
||||
<p class="dark:text-white text-xl">{box.label}</p>
|
||||
<h3 class="dark:text-white text-3xl m-0 my-2">{box.value}</h3>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-evenly mt-16">
|
||||
<div class="table rounded-md bg-white dark:bg-gray-800 shadow-md">
|
||||
<div class="flex border-t-0 {borderBottomGrey} p-3">
|
||||
<h3 class="dark:text-white m-0 py-3 pl-2 text-lg">
|
||||
Outstanding students
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
{#each topStudents as topStudent, index}
|
||||
<div
|
||||
class="flex items-center p-3 cursor-pointer {borderBottomGrey} hover:bg-gray-100 dark:bg-gray-700"
|
||||
>
|
||||
<span class="mr-2">{index + 1}</span>
|
||||
<p class="dark:text-white text-lg">{topStudent.name}</p>
|
||||
<span class="flex-grow" />
|
||||
<p class="dark:text-white text-lg">{topStudent.score}</p>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div class="table rounded-md bg-white dark:bg-gray-800 shadow-md">
|
||||
<div class="flex border-t-0 {borderBottomGrey} p-3">
|
||||
<h3 class="dark:text-white m-0 py-3 pl-2 text-lg">Top rated courses</h3>
|
||||
</div>
|
||||
|
||||
{#each topCourses as topCourse, index}
|
||||
<div
|
||||
class="flex items-center p-3 cursor-pointer {borderBottomGrey} hover:bg-gray-100 dark:bg-gray-700"
|
||||
>
|
||||
<span class="mr-2">{index + 1}</span>
|
||||
<p class="dark:text-white text-lg">{topCourse.title}</p>
|
||||
<span class="flex-grow" />
|
||||
<p class="dark:text-white text-lg">{topCourse.rating}</p>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.root {
|
||||
min-height: 93vh;
|
||||
}
|
||||
|
||||
.table {
|
||||
width: 564px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,399 +0,0 @@
|
||||
import { QUESTION_TYPE } from '../../components/Question/constants';
|
||||
|
||||
const ROLE = {
|
||||
ADMIN: 0,
|
||||
TUTOR: 1,
|
||||
STUDENT: 2,
|
||||
};
|
||||
|
||||
const tutors = [
|
||||
{
|
||||
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',
|
||||
},
|
||||
];
|
||||
|
||||
const people = [
|
||||
{
|
||||
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]',
|
||||
},
|
||||
{
|
||||
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]',
|
||||
},
|
||||
{
|
||||
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]',
|
||||
},
|
||||
{
|
||||
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]',
|
||||
},
|
||||
];
|
||||
|
||||
const courses = [
|
||||
{
|
||||
id: 1,
|
||||
title: 'React.js Fundamentals',
|
||||
status: '',
|
||||
},
|
||||
];
|
||||
|
||||
// many to many: id, tutor_id, course_id
|
||||
const course_tutors = [
|
||||
{
|
||||
id: 1,
|
||||
tutor_id: 1,
|
||||
course_id: 1,
|
||||
},
|
||||
];
|
||||
|
||||
// id, course_id
|
||||
const lessons = [
|
||||
{
|
||||
id: 1,
|
||||
course_id: 1,
|
||||
title: 'Soft skills - Team work',
|
||||
to: '/courses/1/lessons/1',
|
||||
tutor: {
|
||||
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,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
course_id: 1,
|
||||
title: 'Introduction to ReactJS',
|
||||
to: '/courses/1/lessons/2',
|
||||
tutor: {
|
||||
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,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
course_id: 1,
|
||||
title: 'Components - Functional and Class',
|
||||
to: '/courses/1/lessons/3',
|
||||
tutor: {
|
||||
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,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
course_id: 1,
|
||||
title: 'State and Props in components',
|
||||
to: '/courses/1/lessons/4',
|
||||
tutor: {
|
||||
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,
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
course_id: 1,
|
||||
title: 'Life cycle methods',
|
||||
to: '/courses/1/lessons/5',
|
||||
tutor: {
|
||||
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,
|
||||
},
|
||||
];
|
||||
|
||||
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 lesson_exercise = [
|
||||
{
|
||||
id: 1,
|
||||
lesson_id: 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',
|
||||
type: QUESTION_TYPE.CHECKBOX,
|
||||
title: 'Who is the creator of React.js',
|
||||
options: [
|
||||
{
|
||||
id: '1',
|
||||
value: 'Dan Abrahmov',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
value: 'Google',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
value: 'Facebook',
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
value: 'Traversy Media',
|
||||
},
|
||||
],
|
||||
answers: ['3'],
|
||||
},
|
||||
{
|
||||
id: 'vue-founder',
|
||||
type: QUESTION_TYPE.CHECKBOX,
|
||||
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')
|
||||
)
|
||||
`,
|
||||
options: [
|
||||
{
|
||||
id: '1',
|
||||
value: 'Evan Vue',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
value: 'Mark Zukerberg',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
value: 'Prince Charles',
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
value: 'Bill Gates',
|
||||
},
|
||||
],
|
||||
answers: ['3'],
|
||||
},
|
||||
{
|
||||
id: 'svelte-founder',
|
||||
type: QUESTION_TYPE.RADIO,
|
||||
title: 'Who is the creator of Svelte.js',
|
||||
options: [
|
||||
{
|
||||
id: '1',
|
||||
value: 'Hillary Svelte',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
value: 'Mircosoft',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
value: 'Elevate',
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
value: 'Coding Train',
|
||||
},
|
||||
],
|
||||
answers: ['2'],
|
||||
},
|
||||
{
|
||||
id: 'angular-founder',
|
||||
type: QUESTION_TYPE.TEXTAREA,
|
||||
title: 'Who is the creator of Angular.js',
|
||||
value: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const exercise_answers = [
|
||||
{
|
||||
lesson_id: 1,
|
||||
exercise_id: 1,
|
||||
taken_by: {},
|
||||
answers: {},
|
||||
scores: {},
|
||||
currentQuestionIndex: 0,
|
||||
isFinished: false,
|
||||
progressValue: 100,
|
||||
status: 'STATUS.PENDING',
|
||||
},
|
||||
];
|
||||
|
||||
// const lessonLookup = new Map();
|
||||
// lessons.forEach((lesson) => {
|
||||
// lessonLookup.set(`${lesson.id}`, JSON.stringify(lesson));
|
||||
// });
|
||||
|
||||
export function getCourse(id) {
|
||||
return {
|
||||
id,
|
||||
name: 'React.js',
|
||||
people,
|
||||
roles: ROLE,
|
||||
lessons: getLessons(id),
|
||||
};
|
||||
}
|
||||
|
||||
export function getLessons(courseId) {
|
||||
return lessons.filter((lesson) => lesson.course_id === courseId);
|
||||
}
|
||||
|
||||
export function getLesson(id) {
|
||||
return (
|
||||
lessonData.find((lesson) => lesson.lesson_id === id) || {
|
||||
lesson_id: null,
|
||||
materials: {
|
||||
note: '',
|
||||
slide_url: '',
|
||||
video_url: '',
|
||||
},
|
||||
exercises: getExercises(id),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function getExercises(lessonId) {
|
||||
return lesson_exercise.filter((exercise) => exercise.lesson_id === lessonId);
|
||||
}
|
||||
|
||||
export function getExercise(exerciseId) {
|
||||
return {
|
||||
data: lesson_exercise.find((exercise) => exercise.id === exerciseId),
|
||||
answers: exercise_answers.find(
|
||||
(answer) => answer.exercise_id === exerciseId
|
||||
),
|
||||
};
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import { getSupabase } from '../../utils/functions/supabase';
|
||||
|
||||
export function post(req, res) {
|
||||
const supabase = getSupabase();
|
||||
|
||||
supabase.auth.api.setAuthCookie(req, res);
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
import { getCourse, getExercise, getLesson } from './_db';
|
||||
|
||||
function writeJsonHead(res, statusCode) {
|
||||
res.writeHead(statusCode, {
|
||||
'Content-Type': 'application/json',
|
||||
});
|
||||
}
|
||||
|
||||
export function get(req, res, next) {
|
||||
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(result));
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import { getLessons } from './_db';
|
||||
|
||||
function writeJsonHead(res, statusCode) {
|
||||
res.writeHead(statusCode, {
|
||||
'Content-Type': 'application/json',
|
||||
});
|
||||
}
|
||||
|
||||
export function get(req, res, next) {
|
||||
const { id } = req.query;
|
||||
|
||||
writeJsonHead(res, 200);
|
||||
|
||||
if (id) {
|
||||
return res.end(JSON.stringify(getLesson(id)));
|
||||
}
|
||||
|
||||
return res.end(JSON.stringify(getLessons()));
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
function writeJsonHead(res, statusCode) {
|
||||
res.writeHead(statusCode, {
|
||||
'Content-Type': 'application/json',
|
||||
});
|
||||
}
|
||||
|
||||
export function post(req, res) {
|
||||
const { email } = req.body;
|
||||
|
||||
console.log(`req.session`, req.session);
|
||||
|
||||
writeJsonHead(res, 200);
|
||||
|
||||
return res.end(JSON.stringify({ success: true }));
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
<script>
|
||||
import ChevronDown24 from 'carbon-icons-svelte/lib/ChevronDown24';
|
||||
import TextEditor from '../components/TextEditor/index.svelte';
|
||||
|
||||
let questionTitle = '';
|
||||
let questionDescription = '';
|
||||
let facultyFilterOpened = false;
|
||||
|
||||
const faculties = [
|
||||
'ИЭКСУ',
|
||||
'ИМБТ',
|
||||
'ИМИ',
|
||||
'ИПТДМ',
|
||||
'ИИИР',
|
||||
'ГФ',
|
||||
'ХТФ',
|
||||
'ИДЗО',
|
||||
'УИИ',
|
||||
'УНИ',
|
||||
];
|
||||
let checkbox = faculties.reduce((acc, cur) => {
|
||||
acc[cur] = false;
|
||||
return acc;
|
||||
}, {});
|
||||
$: clickedTotal = Object.values(checkbox).filter((box) => !!box).length;
|
||||
|
||||
function handleFacultyFilters() {
|
||||
facultyFilterOpened = facultyFilterOpened ? false : true;
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Спросить собшество вопрос</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="root lg:w-3/5 mx-auto md:mx-10 lg:mb-20">
|
||||
<h2 class="pb-2">Спросить собшество вопрос</h2>
|
||||
<div class="main-title my-1 flex">
|
||||
<div class="faculty-filter mr-1">
|
||||
<button
|
||||
class="flex border rounded-lg border-grey py-3 px-3 focus:outline-none focus:border-gray-400 focus:bg-gray-200 relative"
|
||||
on:click={handleFacultyFilters}
|
||||
>
|
||||
<span class="flex flex-row"
|
||||
>Факултеты <span> ({clickedTotal})</span></span
|
||||
>
|
||||
<ChevronDown24 class="ml-2" />
|
||||
</button>
|
||||
|
||||
{#if facultyFilterOpened}
|
||||
<div
|
||||
class="w-48 bg-white dark:bg-gray-800 border border-grey rounded-lg mt-2 py-3 absolute"
|
||||
>
|
||||
{#each faculties as faculty}
|
||||
<div class="block px-4 py-3 border-b">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="border border-gray checked:bg-blue-600 checked:border-transparent mr-1 cursor-pointer"
|
||||
bind:checked={checkbox[faculty]}
|
||||
/>
|
||||
<span>{faculty}</span>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<input
|
||||
class="title rounded-lg border border-1 border-gray p-2 w-full focus:outline-none"
|
||||
error="false"
|
||||
autocomplete="off"
|
||||
placeholder="Title"
|
||||
aria-label="Title"
|
||||
aria-describedby="title-input-validation"
|
||||
type="text"
|
||||
name="discussion[title]"
|
||||
id="discussion_title"
|
||||
value={questionTitle}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<TextEditor
|
||||
value={questionDescription}
|
||||
placeholder="Задайте вопрос или начните разговор"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.root {
|
||||
max-width: 600px;
|
||||
margin: 10px auto 0;
|
||||
}
|
||||
@media only screen and (max-width: 1002px) {
|
||||
.faculty-filter {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.main-title {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,96 +0,0 @@
|
||||
<script context="module">
|
||||
import { profile } from '../utils/store/user';
|
||||
import { fetchCourses } from '../components/Courses/api';
|
||||
|
||||
export async function preload(page, params) {
|
||||
const parsedUser = JSON.parse(params.user);
|
||||
|
||||
// if (parsedUser && parsedUser.id) {
|
||||
// return fetchCourses(parsedUser.id);
|
||||
// }
|
||||
|
||||
return {
|
||||
allCourses: [],
|
||||
organizationId: null,
|
||||
cantFetch: true,
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
import PageNav from '../components/PageNav/index.svelte';
|
||||
import Courses from '../components/Courses/index.svelte';
|
||||
import NewCourseModal from '../components/Courses/components/NewCourseModal/index.svelte';
|
||||
import PrimaryButton from '../components/PrimaryButton/index.svelte';
|
||||
import {
|
||||
courses,
|
||||
createCourseModal,
|
||||
courseMetaDeta,
|
||||
} from '../components/Courses/store';
|
||||
import { setProfileIdOfGroupMember } from '../utils/services/courses';
|
||||
|
||||
export let allCourses = [];
|
||||
export let cantFetch;
|
||||
|
||||
async function getCourses(userId) {
|
||||
if (process.browser) {
|
||||
if (cantFetch && typeof cantFetch === 'boolean' && !allCourses.length) {
|
||||
$courseMetaDeta.isLoading = true;
|
||||
|
||||
const updatedProfile = await setProfileIdOfGroupMember(
|
||||
$profile.email,
|
||||
userId
|
||||
);
|
||||
console.log(`updatedProfile`, updatedProfile);
|
||||
const coursesResult = await fetchCourses(userId);
|
||||
console.log(`coursesResult`, coursesResult);
|
||||
|
||||
$courseMetaDeta.isLoading = false;
|
||||
if (!coursesResult) return;
|
||||
|
||||
// organizationId = coursesResult.organizationId;
|
||||
courses.set(coursesResult.allCourses);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
courses.set(allCourses);
|
||||
});
|
||||
|
||||
$: getCourses($profile.id);
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Courses - ClassroomIO</title>
|
||||
</svelte:head>
|
||||
|
||||
<section class="root">
|
||||
<PageNav title="All courses">
|
||||
<div slot="widget">
|
||||
{#if $profile.role === 'educator'}
|
||||
<PrimaryButton
|
||||
label="New"
|
||||
onClick={() => ($createCourseModal.open = true)}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
</PageNav>
|
||||
|
||||
<NewCourseModal />
|
||||
|
||||
<Courses />
|
||||
</section>
|
||||
|
||||
<style>
|
||||
.root {
|
||||
width: 100%;
|
||||
padding: 0 10px;
|
||||
min-height: 90vh;
|
||||
/* min-width: 600px;
|
||||
margin: 0 auto;
|
||||
border-left: 1px solid var(--border-color);
|
||||
border-right: 1px solid var(--border-color); */
|
||||
}
|
||||
</style>
|
||||
@@ -1,229 +0,0 @@
|
||||
<script>
|
||||
import { stores } from '@sapper/app';
|
||||
import AddComment20 from 'carbon-icons-svelte/lib/AddComment20';
|
||||
import Vote from '../components/Vote/index.svelte';
|
||||
import Space from '../components/Space/index.svelte';
|
||||
import HashTags from '../components/HashTags/index.svelte';
|
||||
import PageNav from '../components/PageNav/index.svelte';
|
||||
|
||||
function getDiscussion(filterBy, allDiscussions) {
|
||||
switch (filterBy) {
|
||||
// case 'popular':
|
||||
// return allDiscussions.filter((a, i) => i >= 0 && i < 2);
|
||||
case 'recent':
|
||||
return allDiscussions.filter((a, i) => i >= 2 && i < 5);
|
||||
case 'upvoted':
|
||||
return allDiscussions.filter((a, i) => i > 4 && i < 8);
|
||||
default:
|
||||
return allDiscussions;
|
||||
}
|
||||
}
|
||||
|
||||
const { page } = stores();
|
||||
let filterBy = '';
|
||||
let discussions = [];
|
||||
|
||||
$: filterBy = $page.query.filter || 'popular';
|
||||
|
||||
const _allDiscussions = [
|
||||
{
|
||||
id: 1,
|
||||
title: 'How do I pass Anicimov?',
|
||||
author: {
|
||||
name: 'rotimibest',
|
||||
},
|
||||
tags: ['ибэит', 'икс'],
|
||||
answered: false,
|
||||
comments: 10,
|
||||
votes: 10,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title:
|
||||
'Where do I find the nearest restaurant. Where do I find the nearest restaurant?. Where do I find the nearest restaurant? Where do I find the nearest restaurant?',
|
||||
author: {
|
||||
name: 'attronaldo',
|
||||
},
|
||||
tags: ['иибрт', 'иее'],
|
||||
answered: false,
|
||||
comments: 2,
|
||||
votes: 2,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: 'Who is Nikolenko?',
|
||||
author: {
|
||||
name: 'donald',
|
||||
},
|
||||
tags: ['ибэит', 'иее', 'иибрт', 'ИПИГ'],
|
||||
answered: false,
|
||||
comments: 10,
|
||||
votes: 10,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: 'Archive discussion button',
|
||||
author: {
|
||||
name: 'acharlesvv',
|
||||
},
|
||||
tags: [
|
||||
'ИЭКСУ',
|
||||
'ИМБТ',
|
||||
'ИМИ',
|
||||
'ИПТДМ',
|
||||
'ИИИР',
|
||||
'ГФ',
|
||||
'ХТФ',
|
||||
'ИДЗО',
|
||||
'УИИ',
|
||||
'УНИ',
|
||||
],
|
||||
answered: false,
|
||||
comments: 15,
|
||||
votes: 15,
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: 'How do I pass Anicimov?',
|
||||
author: {
|
||||
name: 'rotimibest',
|
||||
},
|
||||
tags: ['ибэит', 'икс'],
|
||||
answered: false,
|
||||
comments: 7,
|
||||
votes: 7,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: 'Where do I find the nearest restaurant?',
|
||||
author: {
|
||||
name: 'attronaldo',
|
||||
},
|
||||
tags: ['иибрт', 'иее'],
|
||||
answered: false,
|
||||
comments: 4,
|
||||
votes: 4,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: 'Who is Nikolenko?',
|
||||
author: {
|
||||
name: 'donald',
|
||||
},
|
||||
tags: ['ибэит', 'иее', 'иибрт', 'ИПИГ'],
|
||||
answered: false,
|
||||
comments: 100,
|
||||
votes: 100,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: 'Archive discussion button',
|
||||
author: {
|
||||
name: 'acharlesvv',
|
||||
},
|
||||
tags: [
|
||||
'ИЭКСУ',
|
||||
'ИМБТ',
|
||||
'ИМИ',
|
||||
'ИПТДМ',
|
||||
'ИИИР',
|
||||
'ГФ',
|
||||
'ХТФ',
|
||||
'ИДЗО',
|
||||
'УИИ',
|
||||
'УНИ',
|
||||
],
|
||||
answered: false,
|
||||
comments: 5,
|
||||
votes: 5,
|
||||
},
|
||||
];
|
||||
const allDiscussions = [];
|
||||
|
||||
_allDiscussions.forEach((d, i) => {
|
||||
allDiscussions.push(d, _allDiscussions[_allDiscussions.length - (i + 1)]);
|
||||
});
|
||||
|
||||
$: discussions = getDiscussion(filterBy, allDiscussions);
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>ClassroomIO - Help students in your university</title>
|
||||
</svelte:head>
|
||||
<div class="root md:w-3/4 mx-auto md:mb-20">
|
||||
<PageNav
|
||||
title="Talks"
|
||||
addButtonHref="/ask"
|
||||
addButtonLabel="Ask"
|
||||
navItems={[
|
||||
{
|
||||
label: 'Popular',
|
||||
isActive: filterBy === 'popular',
|
||||
href: '/discussions?filter=popular',
|
||||
},
|
||||
{
|
||||
label: 'Recent',
|
||||
isActive: filterBy === 'recent',
|
||||
href: '/discussions?filter=recent',
|
||||
},
|
||||
{
|
||||
label: 'Upvoted',
|
||||
isActive: filterBy === 'upvoted',
|
||||
href: '/discussions?filter=upvoted',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
{#each discussions as discussion}
|
||||
<div class="discussion-box">
|
||||
<Vote value={discussion.votes} />
|
||||
<div class="discussion-topic-author">
|
||||
<h4 class="dark:text-white">
|
||||
<a rel="prefetch" href="discussions/{discussion.id}"
|
||||
>{discussion.title}</a
|
||||
>
|
||||
</h4>
|
||||
<span>{discussion.author.name} asked 1 day ago</span>
|
||||
<HashTags tags={discussion.tags} />
|
||||
</div>
|
||||
<Space />
|
||||
<div class="flex items-center">
|
||||
<AddComment20 />
|
||||
<span>{discussion.comments}</span>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.root {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
border-left: 1px solid var(--border-color);
|
||||
border-right: 1px solid var(--border-color);
|
||||
}
|
||||
.discussion-box {
|
||||
/* border: 1px solid var(--border-color); */
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
padding: 10px 15px;
|
||||
/* border-radius: 10px; */
|
||||
margin: 0px auto;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 16px;
|
||||
font-weight: 900;
|
||||
word-break: break-word;
|
||||
overflow-wrap: break-word;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.discussion-topic-author {
|
||||
margin-right: 20px;
|
||||
}
|
||||
.discussion-topic-author span {
|
||||
color: #586069;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,30 +0,0 @@
|
||||
import _discussions from './_discussions.js';
|
||||
|
||||
const lookup = new Map();
|
||||
_discussions.forEach((discussion) => {
|
||||
lookup.set(`${discussion.id}`, JSON.stringify(discussion));
|
||||
});
|
||||
|
||||
export function get(req, res, next) {
|
||||
// the `id` parameter is available because
|
||||
// this file is called [id].json.js
|
||||
const { id } = req.params;
|
||||
|
||||
if (lookup.has(id)) {
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'application/json',
|
||||
});
|
||||
|
||||
res.end(lookup.get(id));
|
||||
} else {
|
||||
res.writeHead(404, {
|
||||
'Content-Type': 'application/json',
|
||||
});
|
||||
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
message: `Not found`,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
<script context="module">
|
||||
export async function preload({ params }) {
|
||||
// the `id` parameter is available because
|
||||
// this file is called [id].svelte
|
||||
const res = await this.fetch(`discussions/${params.id}.json`);
|
||||
const data = await res.json();
|
||||
|
||||
if (res.status === 200) {
|
||||
return { discussion: data };
|
||||
} else {
|
||||
this.error(res.status, data.message);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
import marked from 'marked';
|
||||
import Vote from '../../components/Vote/index.svelte';
|
||||
import HashTags from '../../components/HashTags/index.svelte';
|
||||
import TextEditor from '../../components/TextEditor/index.svelte';
|
||||
import CheckmarkOutline20 from 'carbon-icons-svelte/lib/CheckmarkOutline20';
|
||||
|
||||
export let discussion;
|
||||
|
||||
let userComment = '';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>{discussion.title}</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="root lg:w-3/5 mx-auto md:mx-10 lg:mb-20">
|
||||
<div class="flex items-start">
|
||||
<Vote value={discussion.votes} />
|
||||
<h2 class="text-3xl mb-8">{discussion.title}</h2>
|
||||
</div>
|
||||
<div class="my-1 px-1 rounded-lg border border-1 border-gray">
|
||||
<header class="flex items-center justify-between leading-none p-2">
|
||||
<a
|
||||
class="flex items-center no-underline hover:underline text-black"
|
||||
href="/"
|
||||
>
|
||||
<img
|
||||
alt="Placeholder"
|
||||
class="block rounded-full"
|
||||
src="https://picsum.photos/32/32/?random"
|
||||
/>
|
||||
<p class="dark:text-white ml-2 text-sm">{discussion.author.name}</p>
|
||||
<p
|
||||
class="dark:text-white ml-2 text-sm text-gray-500 dark:text-gray-200"
|
||||
>
|
||||
12h ago
|
||||
</p>
|
||||
</a>
|
||||
</header>
|
||||
<section class="prose prose-sm sm:prose p-2">
|
||||
{@html marked(discussion.question)}
|
||||
</section>
|
||||
<section class="p-2">
|
||||
<HashTags tags={discussion.tags} />
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="my-8"><strong>{discussion.comments.length} ответы</strong></div>
|
||||
|
||||
{#each discussion.comments as comment}
|
||||
<div class="my-5 px-1 flex items-start">
|
||||
<Vote value={comment.votes} />
|
||||
<div class="w-full rounded-lg border border-1 border-gray">
|
||||
<header class="flex items-center justify-between leading-none p-2">
|
||||
<a
|
||||
class="flex items-center no-underline hover:underline text-black"
|
||||
href="/"
|
||||
>
|
||||
<img
|
||||
alt="Placeholder"
|
||||
class="block rounded-full"
|
||||
width="24"
|
||||
height="20"
|
||||
src={comment.avatar}
|
||||
/>
|
||||
<p class="dark:text-white ml-2 text-sm">{comment.name}</p>
|
||||
<p
|
||||
class="dark:text-white ml-2 text-sm text-gray-500 dark:text-gray-200"
|
||||
>
|
||||
12h ago
|
||||
</p>
|
||||
</a>
|
||||
|
||||
<CheckmarkOutline20 />
|
||||
</header>
|
||||
<article class="prose prose-sm sm:prose p-2">
|
||||
{@html marked(comment.comment)}
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
<hr />
|
||||
<TextEditor value={userComment} placeholder="Предлогайте ваш ответ" />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.root {
|
||||
margin: 15px auto 5px;
|
||||
max-width: 800px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,181 +0,0 @@
|
||||
// Ordinarily, you'd generate this data from markdown files in your
|
||||
// repo, or fetch them from a database of some kind. But in order to
|
||||
// avoid unnecessary dependencies in the starter template, and in the
|
||||
// service of obviousness, we're just going to leave it here.
|
||||
|
||||
// This file is called `_discussions.js` rather than `posts.js`, because
|
||||
// we don't want to create an `/blog/posts` route — the leading
|
||||
// underscore tells Sapper not to do that.
|
||||
const question = "\ Hey!\n>\n> I'm working on a dashboard app that's where most things are login only and I like using ### Outlined a draft of the flows\n>\n> ### API Proposals\n> #### Through `getServerSideProps`\n> * Add new return property to `getServerSideProps` called i.e. `fetchPolicy` that can default to `always` being current behaviour and add new option `cache-and-network`/`stale-while-revalidate` as outlined above\n> * Add new router API event, called i.e. `routePropsChanged`, that can be triggered when page props change\n> * Maybe it could also send the `stale-while-revalidate` http header to the browser for instant page reloads?\n> * Problem: needs a way to clear cache when mutating to prevent a flash of unchanged content\n>\n> #### Through router\n> * Add new prop to Link/Router called `fetchPolicy: 'cache-and-network'` which could use a cache of the page's data if it exists and warm it up on load (similar to [`shallow`](https://nextjs.org/docs/routing/shallow-routing).)\n> * If `fetchPolicy: 'cache-and-network` is combined with `prefetch` it could actually preload the data on each page as well and make every route navigation change snappy, even when using getServerSideProps`\n> * This removes the \"flash of unchanged content\" on mutations as you can trigger a route change after a mutation w/o using the cache.\n>\n> Later on, this could be extended to allow for polling as well maybe, which could enable some real-time views without the need of any actual client-side query wiring.\n"
|
||||
export default [
|
||||
{
|
||||
title: "What is Sapper?",
|
||||
id: 1,
|
||||
votes: 100,
|
||||
author: {
|
||||
name: "babdraa",
|
||||
avatar: "https://avatars3.githubusercontent.com/u/24832873?s=60&v=4"
|
||||
},
|
||||
tags: [
|
||||
'ИЭКСУ', 'ИМБТ', 'ИМИ', 'ИПТДМ', 'ИИИР', 'ГФ', 'ХТФ', 'ИДЗО', 'УИИ', 'УНИ'
|
||||
],
|
||||
question: '# Problem\n\nThere is a problem with my code please help me check it out.\n\n```js\nconst name = "dfdf";\n```',
|
||||
createdAt: Date.now(),
|
||||
comments: [
|
||||
{
|
||||
name: "babdraa",
|
||||
avatar: "https://avatars3.githubusercontent.com/u/24832873?s=60&v=4",
|
||||
votes: 5,
|
||||
comment:
|
||||
"getStaticProps must always be called from a page located in pages/*"
|
||||
},
|
||||
{
|
||||
name: "crana",
|
||||
avatar: "https://avatars1.githubusercontent.com/u/31749594?s=60&v=4",
|
||||
votes: 0,
|
||||
comment:
|
||||
" Hello, thank you for reply. Of course I know that :-) I was just thinking if there is some way to do same as getStaticProps for other components too. I don't think such feature is completely necessary, though. Hello, thank you for reply. Of course I know that :-) I was just thinking if there is some way to do same as getStaticProps for other components too. I don't think such feature is completely necessary, though. Hello, thank you for reply. Of course I know that :-) I was just thinking if there is some way to do same as getStaticProps for other components too. I don't think such feature is completely necessary, though. Hello, thank you for reply. Of course I know that :-) I was just thinking if there is some way to do same as getStaticProps for other components too. I don't think such feature is completely necessary, though. Hello, thank you for reply. Of course I know that :-) I was just thinking if there is some way to do same as getStaticProps for other components too. I don't think such feature is completely necessary, though. Hello, thank you for reply. Of course I know that :-) I was just thinking if there is some way to do same as getStaticProps for other components too. I don't think such feature is completely necessary, though. Hello, thank you for reply. Of course I know that :-) I was just thinking if there is some way to do same as getStaticProps for other components too. I don't think such feature is completely necessary, though."
|
||||
},
|
||||
{
|
||||
name: "loris",
|
||||
avatar: "https://avatars3.githubusercontent.com/u/11896398?s=60&v=4",
|
||||
votes: 100,
|
||||
comment:
|
||||
"getStaticProps must always be called from a page located in pages/*"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
title: "How to use Sapper",
|
||||
id: 2,
|
||||
votes: 100,
|
||||
|
||||
author: {
|
||||
name: "babdraa",
|
||||
avatar: "https://avatars3.githubusercontent.com/u/24832873?s=60&v=4"
|
||||
},
|
||||
tags: [
|
||||
'ИЭКСУ', 'ИМБТ', 'ИМИ', 'ИПТДМ', 'ИИИР', 'ГФ', 'ХТФ', 'ИДЗО', 'УИИ', 'УНИ'
|
||||
],
|
||||
question:
|
||||
question,
|
||||
createdAt: Date.now(),
|
||||
comments: [
|
||||
{
|
||||
name: "rawe",
|
||||
avatar: "https://avatars1.githubusercontent.com/u/31749594?s=60&v=4",
|
||||
votes: 10,
|
||||
comment:
|
||||
"Hello, thank you for reply. Of course I know that :-) I was just thinking if there is some way to do same as getStaticProps for other components too. I don't think such feature is completely necessary, though."
|
||||
},
|
||||
{
|
||||
name: "hlaow",
|
||||
avatar: "https://avatars3.githubusercontent.com/u/11896398?s=60&v=4",
|
||||
votes: 5,
|
||||
comment:
|
||||
"getStaticProps must always be called from a page located in pages/*"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
title: "Why the name?",
|
||||
votes: 100,
|
||||
id: 3,
|
||||
author: {
|
||||
name: "babdraa",
|
||||
avatar: "https://avatars3.githubusercontent.com/u/24832873?s=60&v=4"
|
||||
},
|
||||
tags: [
|
||||
'ИЭКСУ', 'ИМБТ', 'ИМИ', 'ИПТДМ', 'ИИИР', 'ГФ', 'ХТФ', 'ИДЗО', 'УИИ', 'УНИ'
|
||||
],
|
||||
question:
|
||||
question,
|
||||
createdAt: Date.now(),
|
||||
comments: [
|
||||
{
|
||||
name: "babdraa",
|
||||
avatar: "https://avatars3.githubusercontent.com/u/24832873?s=60&v=4",
|
||||
votes: 50,
|
||||
comment:
|
||||
"getStaticProps must always be called from a page located in pages/*"
|
||||
},
|
||||
{
|
||||
name: "crana",
|
||||
avatar: "https://avatars1.githubusercontent.com/u/31749594?s=60&v=4",
|
||||
votes: 9,
|
||||
comment:
|
||||
"Hello, thank you for reply. Of course I know that :-) I was just thinking if there is some way to do same as getStaticProps for other components too. I don't think such feature is completely necessary, though."
|
||||
},
|
||||
{
|
||||
name: "loris",
|
||||
avatar: "https://avatars3.githubusercontent.com/u/11896398?s=60&v=4",
|
||||
votes: 5,
|
||||
comment:
|
||||
"getStaticProps must always be called from a page located in pages/*"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
title: "How is Sapper different from Next.js?",
|
||||
id: 4,
|
||||
votes: 10,
|
||||
author: {
|
||||
name: "babdraa",
|
||||
avatar: "https://avatars3.githubusercontent.com/u/24832873?s=60&v=4"
|
||||
},
|
||||
tags: [
|
||||
'ИЭКСУ', 'ИМБТ', 'ИМИ', 'ИПТДМ', 'ИИИР', 'ГФ', 'ХТФ', 'ИДЗО', 'УИИ', 'УНИ'
|
||||
],
|
||||
question: question,
|
||||
createdAt: Date.now(),
|
||||
comments: [
|
||||
{
|
||||
name: "babdraa",
|
||||
avatar: "https://avatars3.githubusercontent.com/u/24832873?s=60&v=4",
|
||||
votes: 0,
|
||||
comment:
|
||||
"getStaticProps must always be called from a page located in pages/*"
|
||||
},
|
||||
{
|
||||
name: "crana",
|
||||
avatar: "https://avatars1.githubusercontent.com/u/31749594?s=60&v=4",
|
||||
votes: 1,
|
||||
comment:
|
||||
"Hello, thank you for reply. Of course I know that :-) I was just thinking if there is some way to do same as getStaticProps for other components too. I don't think such feature is completely necessary, though."
|
||||
},
|
||||
{
|
||||
name: "loris",
|
||||
avatar: "https://avatars3.githubusercontent.com/u/11896398?s=60&v=4",
|
||||
comment:
|
||||
"getStaticProps must always be called from a page located in pages/*"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
title: "How can I get involved?",
|
||||
id: 5,
|
||||
votes: 7,
|
||||
tags: [
|
||||
'ИЭКСУ', 'ИМБТ', 'ИМИ', 'ИПТДМ', 'ИИИР', 'ГФ', 'ХТФ', 'ИДЗО', 'УИИ', 'УНИ'
|
||||
],
|
||||
author: {
|
||||
name: "living",
|
||||
avatar: "https://avatars3.githubusercontent.com/u/24832873?s=60&v=4"
|
||||
},
|
||||
question: question,
|
||||
createdAt: Date.now(),
|
||||
comments: [
|
||||
{
|
||||
name: "gbenga",
|
||||
avatar: "https://avatars3.githubusercontent.com/u/11896398?s=60&v=4",
|
||||
votes: 1000,
|
||||
comment:
|
||||
"getStaticProps must always be called from a page located in pages/*"
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
@@ -1,15 +0,0 @@
|
||||
function writeJsonHead(res, statusCode) {
|
||||
res.writeHead(statusCode, {
|
||||
'Content-Type': 'application/json',
|
||||
});
|
||||
}
|
||||
|
||||
export function post(req, res) {
|
||||
const { email } = req.body;
|
||||
|
||||
console.log(`req.session`, req.session);
|
||||
|
||||
writeJsonHead(res, 200);
|
||||
|
||||
return res.end(JSON.stringify({ success: true }));
|
||||
}
|
||||
@@ -141,8 +141,13 @@
|
||||
});
|
||||
console.log('Create organisation member', data);
|
||||
|
||||
const memberId = Array.isArray(data) && data.length ? data[0].id : '';
|
||||
|
||||
orgs.set([orgData as never]);
|
||||
currentOrg.set(orgData);
|
||||
currentOrg.set({
|
||||
...orgData,
|
||||
memberId: memberId,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
console.log('Error: create organisation member', error);
|
||||
|
||||
@@ -0,0 +1,431 @@
|
||||
<script context="module">
|
||||
export async function preload({ params }) {
|
||||
return { slug: params.slug };
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
import marked from 'marked';
|
||||
import dayjs from 'dayjs';
|
||||
import pluralize from 'pluralize';
|
||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||
import Delete16 from 'carbon-icons-svelte/lib/Delete16';
|
||||
import { SkeletonPlaceholder, SkeletonText } from 'carbon-components-svelte';
|
||||
import ArrowLeft24 from 'carbon-icons-svelte/lib/ArrowLeft24';
|
||||
import Vote from '../../../../components/Vote/index.svelte';
|
||||
import PrimaryButton from '../../../../components/PrimaryButton/index.svelte';
|
||||
import { VARIANTS } from '../../../../components/PrimaryButton/constants';
|
||||
import TextEditor from '../../../../components/TextEditor/index.svelte';
|
||||
import Avatar from '../../../../components/Avatar/index.svelte';
|
||||
import IconButton from '../../../../components/IconButton/index.svelte';
|
||||
import CheckmarkOutline20 from 'carbon-icons-svelte/lib/CheckmarkOutline20';
|
||||
import { currentOrgPath, currentOrg } from '../../../../utils/store/org';
|
||||
import { profile } from '../../../../utils/store/user';
|
||||
import { supabase } from '../../../../utils/functions/supabase';
|
||||
import {
|
||||
askCommunityValidation,
|
||||
commentInCommunityValidation,
|
||||
} from '../../../../utils/functions/validator';
|
||||
import { SNACKBAR_SEVERITY } from '../../../../components/Snackbar/constants';
|
||||
import { snackbarStore } from '../../../../components/Snackbar/store';
|
||||
import TextField from '../../../../components/Form/TextField.svelte';
|
||||
import DeleteCommentModal from '../../../../components/Org/Community/DeleteCommentModal.svelte';
|
||||
|
||||
dayjs.extend(relativeTime);
|
||||
|
||||
export let slug;
|
||||
|
||||
let question;
|
||||
let comment = '';
|
||||
let errors = {};
|
||||
let isValidAnswer = false; // V2 allow admin mark an answer as accepted
|
||||
let resetInput = 1;
|
||||
let voted = { question: false, comment: {} };
|
||||
let isEditMode = false;
|
||||
let deleteComment = {
|
||||
shouldDelete: false,
|
||||
commentId: null,
|
||||
};
|
||||
let editContent = {
|
||||
title: '',
|
||||
body: '',
|
||||
};
|
||||
|
||||
function mapResToQuestion(data) {
|
||||
return {
|
||||
id: data.id,
|
||||
title: data.title,
|
||||
votes: data.votes,
|
||||
author: {
|
||||
id: data?.author?.profile?.id || '',
|
||||
name: data?.author?.profile?.fullname || '',
|
||||
avatar: data?.author?.profile?.avatar_url || '',
|
||||
},
|
||||
body: data.body,
|
||||
createdAt: dayjs(data.created_at).fromNow(true),
|
||||
comments: data.comments.map((c) => ({
|
||||
id: c.id,
|
||||
authorId: c.author?.profile?.id || '',
|
||||
name: c.author?.profile?.fullname || '',
|
||||
avatar: c.author?.profile?.avatar_url || '',
|
||||
votes: c.votes,
|
||||
comment: c.body,
|
||||
createdAt: dayjs(c.created_at).fromNow(true),
|
||||
})),
|
||||
totalComments: 0,
|
||||
};
|
||||
}
|
||||
|
||||
async function fetchCommunityQuestion(slug) {
|
||||
if (!slug) return;
|
||||
|
||||
const { data, error } = await supabase
|
||||
.from('community_question')
|
||||
.select(
|
||||
`
|
||||
id,
|
||||
title,
|
||||
body,
|
||||
votes,
|
||||
created_at,
|
||||
comments:community_answer(
|
||||
id,
|
||||
body,
|
||||
votes,
|
||||
created_at,
|
||||
author:organizationmember!community_answer_author_id_fkey!inner(
|
||||
profile!inner(id, fullname, avatar_url)
|
||||
)
|
||||
),
|
||||
author:organizationmember!community_question_author_id_fkey!inner(
|
||||
profile!inner(id, fullname, avatar_url)
|
||||
)
|
||||
`
|
||||
)
|
||||
.eq('slug', slug)
|
||||
.single();
|
||||
|
||||
if (error) {
|
||||
console.error('Error loading community', error);
|
||||
return goto(`${currentOrgPath}`);
|
||||
}
|
||||
|
||||
question = mapResToQuestion(data);
|
||||
question.totalComments = question.comments.length;
|
||||
}
|
||||
|
||||
async function submitComment() {
|
||||
errors = commentInCommunityValidation({ comment });
|
||||
console.log('submitComment errors', errors);
|
||||
|
||||
if (Object.keys(errors).length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { data, error } = await supabase.from('community_answer').insert({
|
||||
id: undefined,
|
||||
body: comment,
|
||||
question_id: question.id,
|
||||
author_id: $currentOrg.memberId,
|
||||
votes: 0,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
console.error('Error: commenting', error);
|
||||
$snackbarStore.open = true;
|
||||
$snackbarStore.message = 'Error - Please try again later';
|
||||
$snackbarStore.severity = SNACKBAR_SEVERITY.ERROR;
|
||||
} else {
|
||||
console.log('Success: commenting', data);
|
||||
|
||||
// Notification
|
||||
$snackbarStore.open = true;
|
||||
$snackbarStore.message = 'Success';
|
||||
$snackbarStore.severity = SNACKBAR_SEVERITY.SUCCESS;
|
||||
|
||||
// Add to comment
|
||||
const _c = data?.[0];
|
||||
question.comments = [
|
||||
...question.comments,
|
||||
{
|
||||
id: _c.id,
|
||||
name: $profile?.fullname || '',
|
||||
avatar: $profile?.avatar_url || '',
|
||||
votes: 0,
|
||||
comment: _c.body,
|
||||
createdAt: dayjs(_c.created_at).fromNow(true),
|
||||
},
|
||||
];
|
||||
|
||||
// Reset input
|
||||
comment = '';
|
||||
resetInput = new Date().getTime();
|
||||
}
|
||||
}
|
||||
|
||||
async function upvoteQuestion(type, commentId) {
|
||||
const isQuestion = type === 'question';
|
||||
|
||||
if (isQuestion && voted.question) return;
|
||||
if (!isQuestion && voted.comment[commentId]) return;
|
||||
|
||||
const table = isQuestion ? 'community_question' : 'community_answer';
|
||||
const matchId = isQuestion ? question.id : commentId;
|
||||
let votes = 0;
|
||||
|
||||
if (isQuestion) {
|
||||
question.votes = question.votes + 1;
|
||||
votes = question.votes;
|
||||
} else {
|
||||
question.comments = question.comments.map((c) => {
|
||||
if (c.id === commentId) {
|
||||
c.votes = c.votes + 1;
|
||||
votes = c.votes;
|
||||
}
|
||||
return c;
|
||||
});
|
||||
}
|
||||
const { error } = await supabase
|
||||
.from(table)
|
||||
.update({ votes })
|
||||
.match({ id: matchId });
|
||||
if (error) {
|
||||
console.error('Error: upvoteQuestion', error);
|
||||
$snackbarStore.open = true;
|
||||
$snackbarStore.message = 'Error - Please try again later';
|
||||
$snackbarStore.severity = SNACKBAR_SEVERITY.ERROR;
|
||||
} else {
|
||||
if (isQuestion) {
|
||||
voted.question = true;
|
||||
} else {
|
||||
voted.comment[commentId] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function handleQuestionEdit() {
|
||||
if (isEditMode) {
|
||||
const fields = { title: editContent.title, body: editContent.body };
|
||||
errors = askCommunityValidation(fields);
|
||||
console.log('handleQuestionEdit errors', errors);
|
||||
|
||||
if (Object.keys(errors).length) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
isEditMode = !isEditMode;
|
||||
|
||||
if (!isEditMode) {
|
||||
const fields = { title: editContent.title, body: editContent.body };
|
||||
errors = askCommunityValidation(fields);
|
||||
console.log('handleQuestionEdit errors', errors);
|
||||
|
||||
if (Object.keys(errors).length) {
|
||||
return;
|
||||
}
|
||||
const { error } = await supabase
|
||||
.from('community_question')
|
||||
.update(fields)
|
||||
.match({ id: question.id });
|
||||
if (error) {
|
||||
console.error('Error: handleQuestionEdit', error);
|
||||
$snackbarStore.open = true;
|
||||
$snackbarStore.message = 'Error - Please try again later';
|
||||
$snackbarStore.severity = SNACKBAR_SEVERITY.ERROR;
|
||||
} else {
|
||||
question.title = fields.title;
|
||||
question.body = fields.body;
|
||||
|
||||
editContent.title = '';
|
||||
editContent.body = '';
|
||||
}
|
||||
} else {
|
||||
editContent.title = question.title;
|
||||
editContent.body = question.body;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleCommentDelete() {
|
||||
const { error } = await supabase
|
||||
.from('community_answer')
|
||||
.delete()
|
||||
.match({ id: deleteComment.commentId });
|
||||
|
||||
if (!error) {
|
||||
question.comments = question.comments.filter(
|
||||
(c) => c.id !== deleteComment.commentId
|
||||
);
|
||||
|
||||
deleteComment.shouldDelete = false;
|
||||
deleteComment.commentId = null;
|
||||
}
|
||||
}
|
||||
|
||||
$: fetchCommunityQuestion(slug);
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>{question?.title || 'Question'}</title>
|
||||
</svelte:head>
|
||||
|
||||
<DeleteCommentModal
|
||||
bind:open={deleteComment.shouldDelete}
|
||||
onCancel={() => {
|
||||
deleteComment.shouldDelete = false;
|
||||
deleteComment.commentId = null;
|
||||
}}
|
||||
onDelete={handleCommentDelete}
|
||||
/>
|
||||
<section class="max-w-3xl mx-auto md:mx-10 lg:mb-20">
|
||||
{#if !question}
|
||||
<div class="py-10 px-5 mb-3">
|
||||
<SkeletonText style="width: 25%;" />
|
||||
<SkeletonText style="width: 100%; margin-bottom: 2rem" />
|
||||
<SkeletonPlaceholder style="width: 100%; height: 20rem;" />
|
||||
</div>
|
||||
{:else}
|
||||
<div class="py-10 px-5">
|
||||
<a
|
||||
class="text-gray-500 dark:text-gray-200 text-md flex items-center"
|
||||
href={`${$currentOrgPath}/community`}
|
||||
>
|
||||
<ArrowLeft24 class="carbon-icon" /> Go Back
|
||||
</a>
|
||||
<div class="my-5 flex justify-between items-center">
|
||||
{#if isEditMode}
|
||||
<TextField
|
||||
bind:value={editContent.title}
|
||||
className="w-full mr-2"
|
||||
errorMessage={errors.title}
|
||||
/>
|
||||
{:else}
|
||||
<div class="flex items-center">
|
||||
<Vote
|
||||
value={question.votes}
|
||||
upVote={() => upvoteQuestion('question')}
|
||||
disabled={voted.question}
|
||||
/>
|
||||
<h2 class="text-3xl">{question.title}</h2>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if question.author.id === $profile.id}
|
||||
<PrimaryButton
|
||||
label={isEditMode ? 'Save' : 'Edit'}
|
||||
variant={VARIANTS.CONTAINED_INFO}
|
||||
onClick={handleQuestionEdit}
|
||||
className="py-3 px-6 rounded-sm h-fit"
|
||||
disablePadding={true}
|
||||
/>
|
||||
{#if isEditMode}
|
||||
<PrimaryButton
|
||||
label="Cancel"
|
||||
variant={VARIANTS.TEXT}
|
||||
onClick={() => (isEditMode = !isEditMode)}
|
||||
className="py-3 px-6 rounded-sm h-fit"
|
||||
disablePadding={true}
|
||||
/>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
<div class="my-1 px-1 rounded-lg border border-1 border-gray">
|
||||
<header class="flex items-center justify-between leading-none p-2">
|
||||
<div
|
||||
class="flex items-center no-underline hover:underline text-black"
|
||||
>
|
||||
<Avatar
|
||||
src={question.author.avatar}
|
||||
name={question.author.name}
|
||||
width="w-7"
|
||||
height="h-7"
|
||||
/>
|
||||
<p class="dark:text-white ml-2 text-sm">{question.author.name}</p>
|
||||
<p class="dark:text-white ml-2 text-sm text-gray-500">
|
||||
{question.createdAt}
|
||||
</p>
|
||||
</div>
|
||||
</header>
|
||||
{#if isEditMode}
|
||||
<div class="my-2">
|
||||
<TextEditor
|
||||
content={question.body}
|
||||
onChange={(c) => (editContent.body = c)}
|
||||
placeholder="Update question"
|
||||
errorMessage={errors.body}
|
||||
docId={resetInput}
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
<section class="prose prose-sm sm:prose p-2">
|
||||
{@html marked(question.body)}
|
||||
</section>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="my-8 font-bold">
|
||||
{pluralize('answers', question.totalComments, true)}
|
||||
</div>
|
||||
|
||||
{#each question.comments as comment}
|
||||
<div class="my-5 px-1 flex items-start">
|
||||
<Vote
|
||||
value={comment.votes}
|
||||
upVote={() => upvoteQuestion('comment', comment.id)}
|
||||
disabled={voted.comment[comment.id]}
|
||||
/>
|
||||
<div class="w-full rounded-lg border border-1 border-gray">
|
||||
<header class="flex items-center justify-between leading-none p-2">
|
||||
<div class="flex items-center text-black">
|
||||
<Avatar
|
||||
src={comment.avatar}
|
||||
name={comment.name}
|
||||
width="w-7"
|
||||
height="h-7"
|
||||
/>
|
||||
<p class="dark:text-white ml-2 text-sm">{comment.name}</p>
|
||||
<p class="dark:text-white ml-2 text-sm text-gray-500">
|
||||
{comment.createdAt}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{#if isValidAnswer}
|
||||
<CheckmarkOutline20 />
|
||||
{/if}
|
||||
|
||||
{#if comment.authorId === $profile.id}
|
||||
<IconButton
|
||||
value="delete-comment"
|
||||
onClick={() => {
|
||||
deleteComment.shouldDelete = true;
|
||||
deleteComment.commentId = comment.id;
|
||||
}}
|
||||
>
|
||||
<Delete16 class="carbon-icon" />
|
||||
</IconButton>
|
||||
{/if}
|
||||
</header>
|
||||
<article class="prose prose-sm sm:prose p-2">
|
||||
{@html marked(comment.comment)}
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
<hr />
|
||||
|
||||
<div>
|
||||
<TextEditor
|
||||
onChange={(c) => (comment = c)}
|
||||
placeholder="Give an answer"
|
||||
errorMessage={errors.comment}
|
||||
docId={resetInput}
|
||||
/>
|
||||
|
||||
<div class="flex justify-end mr-2">
|
||||
<PrimaryButton label="Comment" onClick={submitComment} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</section>
|
||||
@@ -0,0 +1,96 @@
|
||||
<script>
|
||||
import { goto } from '@sapper/app';
|
||||
import ArrowLeft24 from 'carbon-icons-svelte/lib/ArrowLeft24';
|
||||
import TextEditor from '../../../../components/TextEditor/index.svelte';
|
||||
import { currentOrgPath, currentOrg } from '../../../../utils/store/org';
|
||||
import PrimaryButton from '../../../../components/PrimaryButton/index.svelte';
|
||||
import { askCommunityValidation } from '../../../../utils/functions/validator';
|
||||
import { supabase } from '../../../../utils/functions/supabase';
|
||||
import { SNACKBAR_SEVERITY } from '../../../../components/Snackbar/constants';
|
||||
import { snackbarStore } from '../../../../components/Snackbar/store';
|
||||
import generateSlug from '../../../../utils/functions/generateSlug';
|
||||
|
||||
let errors = {};
|
||||
let fields = {
|
||||
title: '',
|
||||
body: '',
|
||||
};
|
||||
|
||||
async function handleSave() {
|
||||
errors = askCommunityValidation(fields);
|
||||
console.log('handleSave errors', errors);
|
||||
|
||||
if (Object.keys(errors).length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { data: question, error } = await supabase
|
||||
.from('community_question')
|
||||
.insert({
|
||||
...fields,
|
||||
organization_id: $currentOrg.id,
|
||||
author_id: $currentOrg.memberId,
|
||||
votes: 0,
|
||||
slug: generateSlug(fields.title),
|
||||
});
|
||||
|
||||
if (error) {
|
||||
console.error('Error: asking question', error);
|
||||
$snackbarStore.open = true;
|
||||
$snackbarStore.message = 'Error - Please try again later';
|
||||
$snackbarStore.severity = SNACKBAR_SEVERITY.ERROR;
|
||||
} else {
|
||||
console.log('Success: asking question', question);
|
||||
$snackbarStore.open = true;
|
||||
$snackbarStore.message = 'Success';
|
||||
$snackbarStore.severity = SNACKBAR_SEVERITY.SUCCESS;
|
||||
|
||||
goto(`${$currentOrgPath}/community/${question[0].slug}`);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Ask the Community - ClassroomIO</title>
|
||||
</svelte:head>
|
||||
|
||||
<section class="w-full max-w-3xl mx-auto">
|
||||
<div class="p-5">
|
||||
<a
|
||||
class="text-gray-500 dark:text-gray-200 text-md flex items-center"
|
||||
href={`${$currentOrgPath}/community`}
|
||||
>
|
||||
<ArrowLeft24 class="carbon-icon" /> Go Back
|
||||
</a>
|
||||
<div class="flex items-center justify-between">
|
||||
<h1 class="dark:text-white text-3xl font-bold">Ask the community</h1>
|
||||
<PrimaryButton label="Publish" onClick={handleSave} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 p-2">
|
||||
<input
|
||||
class="title rounded-lg border border-1 border-gray p-2 w-full focus:outline-none"
|
||||
error="false"
|
||||
autocomplete="off"
|
||||
placeholder="Title"
|
||||
aria-label="Title"
|
||||
aria-describedby="title-input-validation"
|
||||
type="text"
|
||||
name="discussion_title"
|
||||
id="discussion_title"
|
||||
bind:value={fields.title}
|
||||
/>
|
||||
{#if errors.title}
|
||||
<p class="dark:text-white text-red-500 text-sm">
|
||||
{errors.title}
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<TextEditor
|
||||
onChange={(v) => (fields.body = v)}
|
||||
placeholder="Ask the community any question you might have"
|
||||
errorMessage={errors.body}
|
||||
/>
|
||||
</section>
|
||||
@@ -0,0 +1,25 @@
|
||||
<script>
|
||||
import { goto } from '@sapper/app';
|
||||
import Community from '../../../../components/Org/Community/index.svelte';
|
||||
import PrimaryButton from '../../../../components/PrimaryButton/index.svelte';
|
||||
import { currentOrgPath } from '../../../../utils/store/org';
|
||||
|
||||
function askCommunity() {
|
||||
goto(`${$currentOrgPath}/community/ask`);
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Community - ClassroomIO</title>
|
||||
</svelte:head>
|
||||
|
||||
<section class="w-full max-w-4xl mx-auto">
|
||||
<div class="py-10 px-5">
|
||||
<div class="flex items-center justify-between mb-10">
|
||||
<h1 class="dark:text-white text-3xl font-bold">Community</h1>
|
||||
<PrimaryButton label="Ask Community" onClick={askCommunity} />
|
||||
</div>
|
||||
|
||||
<Community />
|
||||
</div>
|
||||
</section>
|
||||
@@ -65,7 +65,7 @@
|
||||
<title>Courses - ClassroomIO</title>
|
||||
</svelte:head>
|
||||
|
||||
<section class="w-full max-w-7xl mx-auto">
|
||||
<section class="w-full max-w-6xl mx-auto">
|
||||
<div class="py-10 px-5">
|
||||
<div class="flex items-center justify-between mb-10">
|
||||
<h1 class="dark:text-white text-3xl font-bold">Courses</h1>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script>
|
||||
import Domains from '../../../../components/Org/Settings/Domains.svelte';
|
||||
import { currentOrg } from '../../../../utils/store/org';
|
||||
import { currentOrgPath } from '../../../../utils/store/org';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
@@ -11,7 +11,7 @@
|
||||
<div class="py-10 px-5">
|
||||
<a
|
||||
class="text-gray-500 dark:text-gray-200 text-md"
|
||||
href={`/org/${$currentOrg.siteName}/settings`}>Back to Settings</a
|
||||
href={`${$currentOrgPath}/settings`}>Back to Settings</a
|
||||
>
|
||||
<div class="flex items-center justify-between mb-10">
|
||||
<h1 class="dark:text-white text-3xl font-bold">Custom domain</h1>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script>
|
||||
import TeamMembers from '../../../../components/Org/Settings/TeamMembers.svelte';
|
||||
import { currentOrg } from '../../../../utils/store/org';
|
||||
import { currentOrgPath } from '../../../../utils/store/org';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
@@ -11,7 +11,7 @@
|
||||
<div class="py-10 px-5">
|
||||
<a
|
||||
class="text-gray-500 dark:text-gray-200 text-md"
|
||||
href={`/org/${$currentOrg.siteName}/settings`}>Back to Settings</a
|
||||
href={`${$currentOrgPath}/settings`}>Back to Settings</a
|
||||
>
|
||||
<div class="flex items-center justify-between mb-10">
|
||||
<h1 class="dark:text-white text-3xl font-bold">Team members</h1>
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
<script>
|
||||
import AddAlt32 from 'carbon-icons-svelte/lib/AddAlt32';
|
||||
import IconButton from '../components/IconButton/index.svelte';
|
||||
import Avatar from '../components/Avatar/index.svelte';
|
||||
let teachers = [
|
||||
{
|
||||
avatar: 'https://avatars.githubusercontent.com/u/43971542?s=96&v=4',
|
||||
name: 'Imed Jaberi',
|
||||
},
|
||||
{
|
||||
avatar: 'https://avatars.githubusercontent.com/u/3624098?s=96&v=4',
|
||||
name: 'Andrew Clark',
|
||||
},
|
||||
{
|
||||
avatar: 'https://avatars.githubusercontent.com/u/857709?s=96&v=4',
|
||||
name: 'Daniel Lytkin',
|
||||
},
|
||||
];
|
||||
|
||||
let students = [
|
||||
{
|
||||
avatar: 'https://avatars.githubusercontent.com/u/20658625?s=96&v=4',
|
||||
name: 'Aissaoui Ahmed',
|
||||
},
|
||||
{
|
||||
avatar: 'https://avatars.githubusercontent.com/u/26669835?s=96&v=4',
|
||||
name: 'Mahfoudh Arous',
|
||||
},
|
||||
];
|
||||
|
||||
let borderBottom = 'border-r-0 border-b border-l-0';
|
||||
function addTeacher() {}
|
||||
function addStudent() {}
|
||||
</script>
|
||||
|
||||
<div class="root w-full py-5">
|
||||
<div class="max-w-2xl mx-auto my-5">
|
||||
<div
|
||||
class="flex items-center justify-between {borderBottom} border-blue-700"
|
||||
>
|
||||
<h3 class="dark:text-white text-3xl text-blue-700">Teachers</h3>
|
||||
<IconButton
|
||||
toolTipProps={{ title: 'Add', direction: 'right' }}
|
||||
onClick={addTeacher}
|
||||
selected={true}
|
||||
>
|
||||
<AddAlt32 class="carbon-icon" />
|
||||
</IconButton>
|
||||
</div>
|
||||
|
||||
{#each teachers as teacher, index}
|
||||
<div
|
||||
class="flex items-center px-2 py-3 {teachers[index + 1] &&
|
||||
borderBottom} border-gray-300"
|
||||
>
|
||||
<Avatar src={teacher.avatar} name={teacher.name} />
|
||||
<p class="dark:text-white ml-2">{teacher.name}</p>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div class="max-w-2xl mx-auto my-5">
|
||||
<div
|
||||
class="flex items-center justify-between {borderBottom} border-blue-700"
|
||||
>
|
||||
<h3 class="dark:text-white text-3xl text-blue-700">Students</h3>
|
||||
<IconButton
|
||||
toolTipProps={{ title: 'Add', direction: 'right' }}
|
||||
onClick={addStudent}
|
||||
selected={true}
|
||||
>
|
||||
<AddAlt32 class="carbon-icon" />
|
||||
</IconButton>
|
||||
</div>
|
||||
|
||||
{#each students as student, index}
|
||||
<div
|
||||
class="flex items-center px-2 py-3 {students[index + 1] &&
|
||||
borderBottom} border-gray-300"
|
||||
>
|
||||
<Avatar src={student.avatar} name={student.name} />
|
||||
<p class="dark:text-white ml-2">{student.name}</p>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.root {
|
||||
min-height: 93vh;
|
||||
}
|
||||
</style>
|
||||
@@ -179,7 +179,7 @@
|
||||
<div class="flex-grow flex flex-wrap mx-3 w-3/5">
|
||||
<div class="mb-3 w-full">
|
||||
<h3 class="dark:text-white font-bold text-md m-0">Organizations</h3>
|
||||
<p class="dark:text-white text-gray-500 dark:text-gray-200 text-sm">
|
||||
<p class="dark:text-white text-gray-500 text-sm">
|
||||
This are the organizations you work at
|
||||
</p>
|
||||
|
||||
@@ -187,7 +187,7 @@
|
||||
<h4 class="dark:text-white m-0 font-normal">
|
||||
<a class="text-blue-700 font-bold" href="/courses"> CitiBim </a>
|
||||
</h4>
|
||||
<p class="dark:text-white text-gray-500 dark:text-gray-200">
|
||||
<p class="dark:text-white text-gray-500">
|
||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Non aliquam
|
||||
tenetur magnam. Alias cumque quaerat expedita dolores placeat
|
||||
laboriosam culpa, suscipit veritatis adipisci eius magni obcaecati.
|
||||
@@ -210,7 +210,7 @@
|
||||
Climate Change Group
|
||||
</a>
|
||||
</h4>
|
||||
<p class="dark:text-white text-gray-500 dark:text-gray-200">
|
||||
<p class="dark:text-white text-gray-500">
|
||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Illo quasi
|
||||
autem quo possimus illum consequuntur repudiandae nobis error, porro
|
||||
</p>
|
||||
|
||||
Reference in New Issue
Block a user