CLASSROOMIO-T-83: Class Fix CLASSROOMIO-T-83: TextChip Fix CLASSROOMIO-T-83: Redesign of People Component CLASSROOMIO-T-83: Redesign of Page Component CLASSROOMIO-T-83: Add copy button for teacher CLASSROOMIO-T-83: Add copy button and show email CLASSROOMIO-T-83: Make fullname lowercase CLASSROOMIO-T-83: Fix filtering issue CLASSROOMIO-T-30:Developed the UI for the video upload CLASSROOMIO-T-30: UI cleanup CLASSROOMIO-T-30: Improve UX Merge branch 'CLASSROOMIO-T-30' of https://gitlab.com/thenextgreat/classroomio into CLASSROOMIO-T-30 CLASSROOMIO-T-30:Made the input clear after upload and also chnged the icons CLASSROOMIO-T-30: Remove console log CLASSROOMIO-T-30: Better trim out white spaces in url CLASSROOMIO-T-30:Fixed the bug between the save and edit CLASSROOMIO-T-30:Trying to fix the bug between edit and save Merge branch 'main' of https://gitlab.com/thenextgreat/classroomio CLASSROOMIO-T-30:Stil working on the reviews CLASSROOMIO-T-30:Still working on some of the reviews and updates CLASSROOMIO-T-30: Remove unnessecary values in store CLASSROOMIO-T-30: Change button to div in modal CLASSROOMIO-T-100: Select only courses belonging to current org and current user is a member CLASSROOMIO-T-100: Update condition for fetchCourses CLASSROOMIO-T-100: Select only courses belonging to current org and current user is a member Co-authored-by: Rotimi Best <rotimiibitoyeemma@gmail.com> Merge-request: CLASSROOMIO-MR-35 Merged-by: Best Admin <best@digdippa.com> Remove netlify app from redirect rule Add redirect to layout's load Redirect to about domain CLASSROOMIO-T-97: UX again? 😇 CLASSROOMIO-T-97: Better course UX 😍😍 CLASSROOMIO-T-97: Better button style and fix route not found CLASSROOMIO-T-97: Course UI improvement 😁😁😁 CLASSROOMIO-T-30: Add plyr CLASSROOMIO-T-30:Merged main into branch Merge branch 'main' of https://gitlab.com/thenextgreat/classroomio CLASSROOMIO-T-30:Wotking on the lms dashboard CLASSROOMIO-T-30:Made some changes based on the reviews Merge branch 'main' of https://gitlab.com/thenextgreat/classroomio CLASSROOMIO-T-30:Made some refactoring to keep the code DRY CLASSROOMIO-T-30:Developed the UI for the video upload Merge branch 'main' of https://gitlab.com/thenextgreat/classroomio Merge branch 'main' of https://gitlab.com/thenextgreat/classroomio CLASSROOMIO-T-27: Review Section for Instructors on landing page CLASSROOMIO-T-27: Remove unused variable Merge branch 'CLASSROOMIO-T-27' of https://gitlab.com/thenextgreat/classroomio into CLASSROOMIO-T-27 CLASSROOMIO-T-27: Complete reviews functionality CLASSROOMIO-T-27: Review Render Update CLASSROOMIO-T-27: - Resolved Feedbacks CLASSROOMIO-T-27: Fixed Ratings Data CLASSROOMIO-T-27: - Added Ratings Svg - Fixed avatar_url feedback CLASSROOMIO-T-27: Import Adjustment CLASSROOMIO-T-27: Code Review CLASSROOM-T-27: Added Add Review Functionality CLASSROOMIO-T-27: - Unresolved Review - Enhance Code Redability CLASSROOMIO-T-27: Completed the delete functionality CLASSROOMIO-T-27: Hide/Show functionality CLASSROOMIO-T-27: Double Quotation Update CLASSROOMIO-T-27: Testing toggle functinality CLASSROOMIO-T-27: Added the icons CLASSROOMIO-T-27: Completion of the hide/show functionality CLASSROOMIO-T-27: Display the store data on the review page for styling CLASSROOMIO-T-27: Styling of the review component & connecting it's data CLASSROOMIO-T-27: - Add ReviewsForm - Completion of the Reviews Form Structure/Styling CLASSROOMIO-T-27: Added a reviews modal CLASSROOMIO-T-27: Logic update for review section CLASSROOMIO-T-50: Logic for overlapping reviews CLASSROOMIO-T-27: - Completion of the structure & styling with mock data. - Addition of custom ratings assets CLASSROOMIO-T-27: - Review Section Code Structure - Import Profile Picture Assets Co-authored-by: Rotimi Best <rotimiibitoyeemma@gmail.com> Co-authored-by: Tunji Adeyemi <tunji@digdippa.com> Co-authored-by: tunny17 <tvnji17@gmail.com> Merge-request: CLASSROOMIO-MR-22 Merged-by: Best Admin <best@digdippa.com> CLASSROOMIO-T-62: Dark Mode Bug Fix CLASSROOMIO-T-62: Delete Unused class CLASSROOMIO-T-62: Dark Mode Bug Fix Co-authored-by: Tunji Adeyemi <tunji@digdippa.com> Merge-request: CLASSROOMIO-MR-23 Merged-by: Best Admin <best@digdippa.com> Co-authored-by: Ifeanyi Nwosu <ifeanyi@digdippa.com> Co-authored-by: Rotimi Best <rotimiibitoyeemma@gmail.com> Co-authored-by: Tunji Adeyemi <tunji@digdippa.com> Co-authored-by: tunny17 <tvnji17@gmail.com> Merge-request: CLASSROOMIO-MR-33 Merged-by: Best Admin <best@digdippa.com> Remove redirect from svelteapp CLASSROOMIO-T-83: Avatar fox for people page CLASSROOMIO-T-83: Search Component Test CLASSROOMIO-T-83: Redesign of the people component CLASSROOMIO-T-83: Select Box Test CLASSROOMIO-T-83: People Component Redesign Co-authored-by: Ibitoye Rotimi Best <rotimiibitoyebest@yahoo.co.uk> Co-authored-by: Rotimi Best <rotimiibitoyeemma@gmail.com> Co-authored-by: Tunji Adeyemi <tunji@digdippa.com> Co-authored-by: tunny17 <tvnji17@gmail.com> Merge-request: CLASSROOMIO-MR-36 Merged-by: Best Admin <best@digdippa.com>
269 lines
9.6 KiB
Svelte
269 lines
9.6 KiB
Svelte
<script lang="ts">
|
|
import TextChip from '$lib/components/Chip/Text.svelte';
|
|
import ComingSoon from '$lib/components/ComingSoon/index.svelte';
|
|
import { onMount } from 'svelte';
|
|
import copy from 'copy-to-clipboard';
|
|
import {
|
|
Pagination,
|
|
CopyButton,
|
|
Search,
|
|
StructuredList,
|
|
StructuredListBody,
|
|
StructuredListCell,
|
|
StructuredListHead,
|
|
StructuredListRow
|
|
} from 'carbon-components-svelte';
|
|
import CourseContainer from '$lib/components/CourseContainer/index.svelte';
|
|
import { fetchCourse, deleteGroupMember, updatedGroupMember } from '$lib/utils/services/courses';
|
|
import TrashCanIcon from 'carbon-icons-svelte/lib/TrashCan.svelte';
|
|
import PageNav from '$lib/components/PageNav/index.svelte';
|
|
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
|
|
import Select from '$lib/components/Form/Select.svelte';
|
|
import IconButton from '$lib/components/IconButton/index.svelte';
|
|
import RoleBasedSecurity from '$lib/components/RoleBasedSecurity/index.svelte';
|
|
import { setCourse, course, group } from '$lib/components/Course/store';
|
|
import InviationModal from '$lib/components/Course/components/People/InviationModal.svelte';
|
|
import DeleteConfirmation from '$lib/components/Course/components/People/DeleteConfirmation.svelte';
|
|
import {
|
|
invitationModal,
|
|
deleteMemberModal
|
|
} from '$lib/components/Course/components/People/store';
|
|
import type { Person, ProfileRole } from '$lib/components/Course/components/People/types';
|
|
import { ROLE_LABEL, ROLES } from '$lib/utils/constants/roles';
|
|
import { profile } from '$lib/utils/store/user';
|
|
import { snackbarStore } from '$lib/components/Snackbar/store';
|
|
import Avatar from '$lib/components/Avatar/index.svelte';
|
|
|
|
export let data;
|
|
const { courseId } = data;
|
|
|
|
let people: Array<Person> = [];
|
|
let member: { id?: string; email?: string; profile?: { email: string } } = {};
|
|
let shouldEditMemberId: string | null = null;
|
|
let filterBy: ProfileRole = ROLES[0];
|
|
let isStudent = false;
|
|
let searchValue = '';
|
|
|
|
// function setPeople(people: Array<Person>) {
|
|
// if (!Array.isArray(people)) return;
|
|
|
|
// people = (people || []).sort((a, b) => a.role_id - b.role_id);
|
|
// console.log(`people changed`, people);
|
|
// }
|
|
|
|
// function for the searchbar
|
|
function filterPeople(_query, people) {
|
|
const query = _query.toLowerCase();
|
|
return people.filter((person) => {
|
|
const { profile, email } = person;
|
|
return profile?.fullname?.toLowerCase()?.includes(query) || email?.includes(query);
|
|
});
|
|
}
|
|
|
|
async function deletePerson() {
|
|
$group.people = $group.people.filter((person: { id: string }) => person.id !== member.id);
|
|
|
|
await deleteGroupMember(member.id);
|
|
}
|
|
|
|
async function editMemberId(person: Person) {
|
|
await updatedGroupMember(
|
|
{ assigned_student_id: person.assigned_student_id },
|
|
{ id: person.id }
|
|
);
|
|
shouldEditMemberId = null;
|
|
}
|
|
|
|
function sortAndFilterPeople(_people: Array<Person>, filterBy: ProfileRole) {
|
|
people = (_people || [])
|
|
.filter((person) => {
|
|
if (filterBy.value === 'all') return true;
|
|
|
|
return person.role_id === filterBy.value;
|
|
})
|
|
.sort((a: Person, b: Person) => new Date(a.created_at) - new Date(b.created_at))
|
|
.sort((a: Person, b: Person) => a.role_id - b.role_id);
|
|
}
|
|
|
|
function getEmail(person) {
|
|
const { profile, email } = person;
|
|
|
|
return profile ? profile.email : email;
|
|
}
|
|
|
|
function handleEditStudentIdMode(personId: string) {
|
|
shouldEditMemberId = personId;
|
|
}
|
|
|
|
function copyToClipboard(studentAssignedId: string | null) {
|
|
$snackbarStore.open = true;
|
|
$snackbarStore.message = `Copied to clipboard`;
|
|
|
|
if (studentAssignedId) {
|
|
copy(studentAssignedId);
|
|
}
|
|
}
|
|
|
|
onMount(async () => {
|
|
if ($course.id) return;
|
|
const { data } = await fetchCourse(courseId);
|
|
setCourse(data);
|
|
});
|
|
|
|
$: sortAndFilterPeople($group.people, filterBy);
|
|
</script>
|
|
|
|
<InviationModal />
|
|
<DeleteConfirmation
|
|
email={member.email || (member.profile && member.profile.email)}
|
|
{deletePerson}
|
|
/>
|
|
|
|
<CourseContainer bind:isStudent>
|
|
<PageNav title="People" disableSticky={true}>
|
|
<slot:fragment slot="widget">
|
|
<RoleBasedSecurity allowedRoles={[1, 2]}>
|
|
<PrimaryButton
|
|
className="mr-2"
|
|
label="Add"
|
|
onClick={() => ($invitationModal.open = true)}
|
|
/>
|
|
</RoleBasedSecurity>
|
|
</slot:fragment>
|
|
</PageNav>
|
|
<section class="my-5 mx-9">
|
|
<div class="flex flex-row flex-end justify-end items-center mb-7">
|
|
<div class="mr-5 w-80">
|
|
<Search
|
|
class="dark:text-slate-950 border-0 bg-zinc-100 w-full"
|
|
placeholder="Search people"
|
|
bind:value={searchValue}
|
|
/>
|
|
</div>
|
|
<div class="mb-3">
|
|
<Select bind:value={filterBy} options={ROLES} className="dark:text-black mt-3 w-20" />
|
|
<!-- <select bind:value={filterBy} class="mt-3">
|
|
{#each ROLES as option}
|
|
<option value={option.value}>{option.label}</option>
|
|
{/each}
|
|
</select> -->
|
|
</div>
|
|
<RoleBasedSecurity allowedRoles={[1, 2]}>
|
|
<p class="dark:text-white hidden lg:block text-lg w-20" />
|
|
</RoleBasedSecurity>
|
|
</div>
|
|
|
|
<StructuredList class="m-0">
|
|
<StructuredListHead
|
|
class="bg-slate-100 dark:bg-neutral-800 dark:border-2 dark:border-neutral-800"
|
|
>
|
|
<StructuredListRow head class="mx-7">
|
|
<StructuredListCell head class="text-blue-700 py-3 dark:text-white"
|
|
>Name</StructuredListCell
|
|
>
|
|
<StructuredListCell head class="text-blue-700 py-3 dark:text-white"
|
|
>Role</StructuredListCell
|
|
>
|
|
<StructuredListCell head class="text-blue-700 py-3 dark:text-white"
|
|
>Action</StructuredListCell
|
|
>
|
|
<RoleBasedSecurity allowedRoles={[1, 2]}>
|
|
<p class="dark:text-white hidden lg:block text-lg w-20" />
|
|
</RoleBasedSecurity>
|
|
</StructuredListRow>
|
|
</StructuredListHead>
|
|
|
|
{#each filterPeople(searchValue, people) as person}
|
|
<StructuredListBody>
|
|
<StructuredListRow class="relative">
|
|
<!-- first column -->
|
|
<StructuredListCell class="w-3/6">
|
|
{#if person.profile}
|
|
<div class="flex items-center absolute bottom-3">
|
|
<Avatar
|
|
src={person.profile.avatar_url}
|
|
name={person.profile.fullname}
|
|
width="w-8"
|
|
height="h-8"
|
|
className="mr-3"
|
|
/>
|
|
<div class="flex items-center">
|
|
<div class="mr-2">
|
|
<p class="dark:text-white text-base font-normal">
|
|
{person.profile.fullname}
|
|
</p>
|
|
<p class="text-xs text-blue-600">
|
|
{getEmail(person)}
|
|
</p>
|
|
</div>
|
|
<RoleBasedSecurity allowedRoles={[1, 2]}>
|
|
<div class="hidden lg:flex lg:justify-between lg:items-center mr-2">
|
|
<CopyButton text={getEmail(person)} feedback="Copied Email to clipboard" />
|
|
</div>
|
|
</RoleBasedSecurity>
|
|
{#if person.profile_id == $profile.id}
|
|
<ComingSoon label="You" />
|
|
{/if}
|
|
</div>
|
|
</div>
|
|
{:else}
|
|
<div class="flex items-center w-2/4 absolute bottom-3">
|
|
<TextChip
|
|
value={person.email.substring(0, 2).toUpperCase()}
|
|
className="bg-blue-200 text-black font-semibold text-xs mr-3"
|
|
shape="rounded-full"
|
|
/>
|
|
<a
|
|
href="mailto:{person.email}"
|
|
class="text-md text-blue-600 mr-2 dark:text-white"
|
|
>
|
|
{person.email}
|
|
</a>
|
|
|
|
<RoleBasedSecurity allowedRoles={[1, 2]}>
|
|
<div class="hidden lg:flex lg:justify-between lg:items-center w-20">
|
|
<CopyButton text={getEmail(person)} feedback="Copied Email to clipboard" />
|
|
</div>
|
|
</RoleBasedSecurity>
|
|
|
|
<TextChip
|
|
value="Pending"
|
|
className="text-xs bg-yellow-200 text-yellow-700 h-fit"
|
|
size="sm"
|
|
/>
|
|
</div>
|
|
{/if}
|
|
</StructuredListCell>
|
|
|
|
<!-- second column -->
|
|
<StructuredListCell class="w-1/4 py-4 px-3">
|
|
<p class="dark:text-white font-normal text-base w-1/4 my-auto">
|
|
{ROLE_LABEL[person.role_id]}
|
|
</p>
|
|
</StructuredListCell>
|
|
|
|
<!-- third column -->
|
|
<StructuredListCell class="p-0 w-1/4">
|
|
<RoleBasedSecurity allowedRoles={[1, 2]}>
|
|
<div class="hidden lg:flex lg:justify-between lg:items-center w-20">
|
|
{#if person.profile_id !== $profile.id}
|
|
<IconButton
|
|
onClick={() => {
|
|
member = person;
|
|
$deleteMemberModal.open = true;
|
|
}}
|
|
>
|
|
<TrashCanIcon size={16} class="carbon-icon dark:text-white" />
|
|
</IconButton>
|
|
{/if}
|
|
</div>
|
|
</RoleBasedSecurity>
|
|
</StructuredListCell>
|
|
</StructuredListRow>
|
|
</StructuredListBody>
|
|
{/each}
|
|
</StructuredList>
|
|
<Pagination totalItems={10} pageSizes={[10, 15, 20]} />
|
|
</section>
|
|
</CourseContainer>
|