Add people + Move fetching to client side
This commit is contained in:
@@ -47,6 +47,7 @@
|
||||
}
|
||||
|
||||
async function getExercises() {
|
||||
if (!lessonId) return;
|
||||
const exercisesData = await supabase
|
||||
.from('exercise')
|
||||
.select(`id, title, created_at`)
|
||||
@@ -125,7 +126,7 @@
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
<PageBody width="w-11/12 m-auto">
|
||||
<PageBody>
|
||||
<slot:fragment slot="header">
|
||||
<PrimaryButton
|
||||
className="mr-2"
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
import CheckmarkFilled20 from 'carbon-icons-svelte/lib/CheckmarkFilled20';
|
||||
import { stores, goto } from '@sapper/app';
|
||||
import hotkeys from 'hotkeys-js';
|
||||
import CheckmarkFilled20 from 'carbon-icons-svelte/lib/CheckmarkFilled20';
|
||||
import Settings20 from 'carbon-icons-svelte/lib/Settings20';
|
||||
import ChevronLeft32 from 'carbon-icons-svelte/lib/ChevronLeft32';
|
||||
import ChevronRight32 from 'carbon-icons-svelte/lib/ChevronRight32';
|
||||
import { stores, goto } from '@sapper/app';
|
||||
import Expandable from '../../../Expandable/index.svelte';
|
||||
import PageNav from '../../../PageNav/index.svelte';
|
||||
import IconButton from '../../../IconButton/index.svelte';
|
||||
@@ -14,6 +15,8 @@
|
||||
getLectureNo,
|
||||
} from '../../function';
|
||||
|
||||
import Settings from '../Settings/index.svelte';
|
||||
import { settingsDialog } from '../Settings/store';
|
||||
import { lessons } from '../Lesson/store/lessons';
|
||||
import { course } from '../../store';
|
||||
import { updateCourse } from '../../../../utils/services/courses';
|
||||
@@ -78,32 +81,42 @@
|
||||
to: getNavItemRoute($course.id, 'submissions'),
|
||||
hideSortIcon: true,
|
||||
},
|
||||
{
|
||||
label: 'Scoreboard',
|
||||
to: getNavItemRoute($course.id, 'scoreboard'),
|
||||
hideSortIcon: true,
|
||||
},
|
||||
// {
|
||||
// label: 'Scoreboard',
|
||||
// to: getNavItemRoute($course.id, 'scoreboard'),
|
||||
// hideSortIcon: true,
|
||||
// },
|
||||
{
|
||||
label: 'People',
|
||||
to: getNavItemRoute($course.id, 'people'),
|
||||
hideSortIcon: true,
|
||||
},
|
||||
{
|
||||
label: 'Timetable',
|
||||
to: getNavItemRoute($course.id, 'timetable'),
|
||||
hideSortIcon: true,
|
||||
},
|
||||
// {
|
||||
// label: 'Timetable',
|
||||
// to: getNavItemRoute($course.id, 'timetable'),
|
||||
// hideSortIcon: true,
|
||||
// },
|
||||
];
|
||||
}
|
||||
</script>
|
||||
|
||||
<Settings />
|
||||
<div class="root z-10 {!show && 'hide'}">
|
||||
{#if show}
|
||||
<PageNav
|
||||
bind:title={$course.title}
|
||||
onEditComplete={handleSaveTitle}
|
||||
isTitleEditable={true}
|
||||
/>
|
||||
paddingClass="pl-2"
|
||||
>
|
||||
<slot:fragment slot="widget">
|
||||
<IconButton
|
||||
onClick={() => ($settingsDialog.open = !$settingsDialog.open)}
|
||||
>
|
||||
<Settings20 class="carbon-icon" />
|
||||
</IconButton>
|
||||
</slot:fragment>
|
||||
</PageNav>
|
||||
<div>
|
||||
{#each navItems as navItem}
|
||||
<Expandable
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
<script>
|
||||
import Modal from '../../../Modal/index.svelte';
|
||||
import PrimaryButton from '../../../PrimaryButton/index.svelte';
|
||||
import TextArea from '../../../Form/TextArea.svelte';
|
||||
import { ROLE } from '../../../../utils/constants/roles';
|
||||
import {
|
||||
addGroupMember,
|
||||
fetchGroup,
|
||||
} from '../../../../utils/services/courses';
|
||||
import { invitationModal } from './store';
|
||||
import { validateForm } from '../../../Courses/functions';
|
||||
import { course, setCourseData } from '../../../Course/store';
|
||||
|
||||
let errors = {
|
||||
emails: '',
|
||||
};
|
||||
|
||||
function addPerson() {
|
||||
const { hasError, fieldErrors, students, tutors } = validateForm({
|
||||
...$invitationModal,
|
||||
title: 'sfdsd',
|
||||
description: 'dsfdsf',
|
||||
});
|
||||
|
||||
errors = fieldErrors;
|
||||
if (hasError) return;
|
||||
|
||||
let membersStack = [];
|
||||
console.log(`$course`, $course);
|
||||
// Create groupmemebers from group_id and add teachers and students
|
||||
for (const student of students) {
|
||||
membersStack.push({
|
||||
email: student,
|
||||
group_id: $course.group.id,
|
||||
role_id: ROLE.STUDENT,
|
||||
});
|
||||
}
|
||||
|
||||
for (const tutor of tutors) {
|
||||
membersStack.push({
|
||||
email: tutor,
|
||||
group_id: $course.group.id,
|
||||
role_id: ROLE.TUTOR,
|
||||
});
|
||||
}
|
||||
|
||||
addGroupMember(membersStack).then(async (membersAdded) => {
|
||||
console.log(`membersAdded`, membersAdded);
|
||||
const group = await fetchGroup($course.group.id);
|
||||
|
||||
setCourseData(
|
||||
{
|
||||
...$course,
|
||||
group: group.data,
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
$invitationModal.open = false;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
onClose={() => ($invitationModal.open = false)}
|
||||
bind:open={$invitationModal.open}
|
||||
width="w-2/5"
|
||||
modalHeading="Create a Course"
|
||||
>
|
||||
<form on:submit|preventDefault={addPerson}>
|
||||
<TextArea
|
||||
label="Invite Tutors"
|
||||
bind:value={$invitationModal.tutors}
|
||||
rows="2"
|
||||
maxRows="3"
|
||||
placeholder="tutor@company.com, john@gmail.com"
|
||||
helperMessage="To invite people, add their emails separated by a comma"
|
||||
errorMessage={errors.tutors}
|
||||
className="mb-4"
|
||||
/>
|
||||
<TextArea
|
||||
label="Invite Students"
|
||||
bind:value={$invitationModal.students}
|
||||
rows="2"
|
||||
maxRows="3"
|
||||
placeholder="student@gmail.com, john@gmail.com"
|
||||
helperMessage="To invite people, add their emails separated by a comma"
|
||||
errorMessage={errors.students}
|
||||
className="mb-4"
|
||||
/>
|
||||
|
||||
<div class="mt-5 flex items-center">
|
||||
<PrimaryButton className="px-6 py-2" label="Finish" type="submit" />
|
||||
</div>
|
||||
</form>
|
||||
</Modal>
|
||||
@@ -0,0 +1,7 @@
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export const invitationModal = writable({
|
||||
open: false,
|
||||
tutors: '',
|
||||
students: '',
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
export interface Profile {
|
||||
id: string;
|
||||
fullname: string;
|
||||
username: string;
|
||||
email: string;
|
||||
}
|
||||
|
||||
export interface Person {
|
||||
id: string;
|
||||
email?: string;
|
||||
role_id: number;
|
||||
profile?: Profile;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<script>
|
||||
import Modal from '../../../Modal/index.svelte';
|
||||
import TextArea from '../../../Form/TextArea.svelte';
|
||||
import PrimaryButton from '../../../PrimaryButton/index.svelte';
|
||||
|
||||
import { VARIANTS } from '../../../PrimaryButton/constants';
|
||||
import { settingsDialog } from './store';
|
||||
import { course } from '../../store';
|
||||
import {
|
||||
updateCourse,
|
||||
deleteCourse,
|
||||
} from '../../../../utils/services/courses';
|
||||
|
||||
let shouldDelete = false;
|
||||
|
||||
async function updateDescription() {
|
||||
console.log('update');
|
||||
|
||||
$settingsDialog.open = false;
|
||||
await updateCourse($course.id, {
|
||||
description: $course.description,
|
||||
});
|
||||
}
|
||||
|
||||
async function handleDelete() {
|
||||
console.log('delete');
|
||||
|
||||
$settingsDialog.open = false;
|
||||
await deleteCourse($course.id);
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
onClose={() => ($settingsDialog.open = false)}
|
||||
bind:open={$settingsDialog.open}
|
||||
width="w-96"
|
||||
modalHeading="Update Course"
|
||||
>
|
||||
{#if shouldDelete}
|
||||
<form on:submit|preventDefault={() => (shouldDelete = false)}>
|
||||
<h1 class="text-2xl">Are you sure?</h1>
|
||||
|
||||
<div class="mt-5 flex items-center justify-between">
|
||||
<PrimaryButton
|
||||
className="px-6 py-2"
|
||||
variant={VARIANTS.OUTLINED}
|
||||
label="No, cancel"
|
||||
type="submit"
|
||||
/>
|
||||
<PrimaryButton
|
||||
className="px-6 py-2"
|
||||
variant={VARIANTS.OUTLINED}
|
||||
label="Yes, delete"
|
||||
onClick={handleDelete}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
{:else}
|
||||
<form on:submit|preventDefault={updateDescription}>
|
||||
<TextArea
|
||||
label="Short description"
|
||||
bind:value={$course.description}
|
||||
rows="4"
|
||||
maxRows="4"
|
||||
placeholder="A little description about this course"
|
||||
className="mb-4"
|
||||
isRequired={true}
|
||||
/>
|
||||
|
||||
<div class="mt-5 flex items-center justify-between">
|
||||
<PrimaryButton
|
||||
className="px-6 py-2"
|
||||
variant={VARIANTS.CONTAINED_DANGER}
|
||||
label="Delete"
|
||||
onClick={() => (shouldDelete = true)}
|
||||
/>
|
||||
<PrimaryButton className="px-6 py-2" label="Update" type="submit" />
|
||||
</div>
|
||||
</form>
|
||||
{/if}
|
||||
</Modal>
|
||||
@@ -0,0 +1,5 @@
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export const settingsDialog = writable({
|
||||
open: false,
|
||||
});
|
||||
@@ -3,6 +3,7 @@
|
||||
import PageNav from '../PageNav/index.svelte';
|
||||
import MODES from '../../utils/constants/mode';
|
||||
import PrimaryButton from '../PrimaryButton/index.svelte';
|
||||
import PageBody from '../PageBody/index.svelte';
|
||||
import EditContent from '../EditContent/index.svelte';
|
||||
import { course } from '../Course/store';
|
||||
import { updateCourse } from '../../utils/services/courses';
|
||||
@@ -37,7 +38,7 @@
|
||||
</svelte:fragment>
|
||||
</PageNav>
|
||||
|
||||
<div class="px-3">
|
||||
<PageBody>
|
||||
{#if mode === MODES.edit}
|
||||
<EditContent
|
||||
writeLabel="Note"
|
||||
@@ -51,4 +52,4 @@
|
||||
{:else}
|
||||
{@html marked($course.overview || '', { breaks: true, gfm: true })}
|
||||
{/if}
|
||||
</div>
|
||||
</PageBody>
|
||||
|
||||
@@ -26,9 +26,9 @@ export const group = writable({
|
||||
students: [],
|
||||
});
|
||||
|
||||
export async function setCourseData(data) {
|
||||
export async function setCourseData(data, setLesson = true) {
|
||||
if (!data || !(Object.values(data) && Object.values(data).length)) return;
|
||||
console.log(`data`, data);
|
||||
console.log(`setCourseData`, data);
|
||||
|
||||
if (data.group) {
|
||||
const groupData = Object.assign(data.group, { tutors: [], students: [] });
|
||||
@@ -37,7 +37,8 @@ export async function setCourseData(data) {
|
||||
for (const member of groupData.members) {
|
||||
if (member.role_id === ROLE.STUDENT) {
|
||||
groupData.students.push(member);
|
||||
} else if (member.profile) {
|
||||
// } else if (member.profile) {
|
||||
} else {
|
||||
groupData.tutors.push({
|
||||
...member,
|
||||
...member.profile,
|
||||
@@ -51,12 +52,13 @@ export async function setCourseData(data) {
|
||||
group.set(groupData);
|
||||
}
|
||||
|
||||
const orderedLessons = (data.lessons || []).sort(
|
||||
(a, b) => new Date(a.created_at) - new Date(b.created_at)
|
||||
);
|
||||
// .map();
|
||||
|
||||
lessons.set(orderedLessons);
|
||||
if (setLesson) {
|
||||
const orderedLessons = (data.lessons || []).sort(
|
||||
(a, b) => new Date(a.created_at) - new Date(b.created_at)
|
||||
);
|
||||
// .map();
|
||||
lessons.set(orderedLessons);
|
||||
}
|
||||
|
||||
delete data.lessons;
|
||||
course.set(data);
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import { course } from '../Course/store';
|
||||
import Confetti from '../Confetti/index.svelte';
|
||||
|
||||
export let path;
|
||||
export let path = '';
|
||||
export let isExercisePage = false;
|
||||
|
||||
function setStoreWithData() {
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
<script>
|
||||
// import
|
||||
// border border-gray-300 rounded-md p-3 bg-blue-200 bg-opacity-25
|
||||
export let width = 'max-w-3xl';
|
||||
export let width = 'max-w-3xl w-11/12';
|
||||
export let className = '';
|
||||
</script>
|
||||
|
||||
<div class="root mt-4 mx-auto {width} relative pb-5 pr-5">
|
||||
<div class="root mt-4 mx-auto {width} {className} relative pb-5">
|
||||
{#if $$slots.header}
|
||||
<div
|
||||
class="head flex bg-white items-center justify-between sticky right-0 w-full py-3"
|
||||
|
||||
@@ -4,11 +4,12 @@
|
||||
export let title = '';
|
||||
export let overidableStyle = '';
|
||||
export let navItems = [];
|
||||
export let addButtonHref;
|
||||
export let addButtonLabel;
|
||||
export let addButtonHref = '';
|
||||
export let addButtonLabel = '';
|
||||
export let disableSticky = false;
|
||||
export let isTitleEditable = false;
|
||||
export let onEditComplete = () => {};
|
||||
export let paddingClass = 'px-2';
|
||||
|
||||
let dynamicRootClass = '';
|
||||
let enterEditTitleMode = false;
|
||||
@@ -19,7 +20,7 @@
|
||||
|
||||
<div
|
||||
class="header flex items-center justify-between {!disableSticky &&
|
||||
'sticky'} {dynamicRootClass}"
|
||||
'sticky'} {paddingClass} {dynamicRootClass}"
|
||||
style={overidableStyle}
|
||||
>
|
||||
{#if !!title}
|
||||
@@ -66,7 +67,6 @@
|
||||
<style>
|
||||
.header {
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
padding: 0 15px;
|
||||
min-height: 61px;
|
||||
background-color: #fff;
|
||||
z-index: 1;
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
export const VARIANTS = {
|
||||
CONTAINED: "CONTAINED",
|
||||
OUTLINED: "OUTLINED",
|
||||
CONTAINED: 'CONTAINED',
|
||||
CONTAINED_DANGER: 'CONTAINED_DANGER',
|
||||
OUTLINED: 'OUTLINED',
|
||||
};
|
||||
|
||||
export const VARIANTS_CLASS = {
|
||||
[VARIANTS.CONTAINED]: "bg-blue-700 hover:bg-blue-900 text-white font-bold",
|
||||
[VARIANTS.CONTAINED]: 'bg-blue-700 hover:bg-blue-900 text-white font-bold',
|
||||
[VARIANTS.CONTAINED_DANGER]:
|
||||
'bg-red-700 hover:bg-red-900 text-white font-bold',
|
||||
[VARIANTS.OUTLINED]:
|
||||
"border border-gray-700 hover:border-black-300 hover:bg-gray-200 text-black",
|
||||
'border border-gray-700 hover:border-black-300 hover:bg-gray-200 text-black',
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user