CLASSROOMIO-T-31: Add audience page

Co-authored-by: Rotimi Best <[email protected]>

Merge-request: CLASSROOMIO-MR-6
Merged-by: Digdippa Team <[email protected]>
This commit is contained in:
Rotimi Best
2023-04-23 22:28:18 +00:00
committed by Space Cloud
co-authored by Rotimi Best
parent ae8f9ca222
commit 01a1f0f907
10 changed files with 227 additions and 5 deletions
@@ -0,0 +1,47 @@
<script>
import {
DataTable,
Toolbar,
ToolbarContent,
ToolbarSearch,
Pagination,
DataTableSkeleton,
} from 'carbon-components-svelte';
import { orgAudience } from '../../utils/store/org';
export let isLoading = false;
const headers = [
{ key: 'name', value: 'Name' },
{ key: 'email', value: 'Email' },
{ key: 'date_joined', value: 'Date Joined' },
];
let pageSize = 5;
let page = 1;
let filteredRowIds = [];
let searchValue = '';
</script>
{#if isLoading}
<DataTableSkeleton {headers} rows={pageSize} />
{:else}
<DataTable size="tall" {headers} rows={$orgAudience} {pageSize} {page}>
<Toolbar>
<ToolbarContent>
<ToolbarSearch
persistent
value={searchValue}
shouldFilterRows
bind:filteredRowIds
/>
</ToolbarContent>
</Toolbar>
</DataTable>
{/if}
<Pagination
bind:pageSize
bind:page
totalItems={filteredRowIds.length}
pageSizeInputDisabled
/>
+31
View File
@@ -0,0 +1,31 @@
<script>
import { onMount } from 'svelte';
import Box from '../Box/index.svelte';
import AudienceList from './AudienceList.svelte';
import AudienceEmptyIcon from '../Icons/AudienceEmptyIcon.svelte';
import { getOrgAudience } from '../../utils/services/org';
import { orgAudience, currentOrg } from '../../utils/store/org';
let isLoading = false;
onMount(async () => {
isLoading = true;
await getOrgAudience($currentOrg.id);
isLoading = false;
});
</script>
<div
class="flex items-center justify-center lg:justify-start flex-wrap my-4 m-auto"
>
{#if $orgAudience.length || isLoading}
<AudienceList {isLoading} />
{:else}
<Box>
<AudienceEmptyIcon />
<h3 class="text-2xl my-5">No audience to manage!</h3>
<p class="w-1/3 text-center">
Manage all your students here and stay connected with them.
</p>
</Box>
{/if}
</div>
@@ -9,6 +9,7 @@
import { addGroupMember } from '../../../../utils/services/courses';
import { supabase } from '../../../../utils/functions/supabase';
import { profile } from '../../../../utils/store/user';
import { currentOrg } from '../../../../utils/store/org';
let errors = {
emails: '',
@@ -25,7 +26,7 @@
// 1. Create group
const { data: newGroup } = await supabase
.from('group')
.insert({ name: title, description });
.insert({ name: title, description, organization_id: $currentOrg.id });
console.log(`newGroup`, newGroup);
const { id: group_id } = newGroup[0];
@@ -0,0 +1,69 @@
<svg
width="121"
height="120"
viewBox="0 0 121 120"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<circle
cx="79.7922"
cy="32.4026"
r="12.5649"
stroke="#0F62FE"
stroke-width="1.67532"
/>
<path
d="M69.7402 40.7794L74.2444 37.7766C77.6914 35.4786 82.0846 35.1362 85.8461 36.8722L90.6818 39.1041"
stroke="#0F62FE"
stroke-width="1.67532"
/>
<circle
cx="79.4199"
cy="28.68"
r="3.81602"
stroke="#0F62FE"
stroke-width="1.67532"
/>
<circle
cx="106.597"
cy="62.5588"
r="12.5649"
stroke="#0F62FE"
stroke-width="1.67532"
/>
<path
d="M96.5455 70.9357L101.05 67.9329C104.497 65.6349 108.89 65.2924 112.651 67.0285L117.487 69.2603"
stroke="#0F62FE"
stroke-width="1.67532"
/>
<circle
cx="106.225"
cy="58.8363"
r="3.81602"
stroke="#0F62FE"
stroke-width="1.67532"
/>
<circle
cx="71.4156"
cy="79.3128"
r="12.5649"
stroke="#0F62FE"
stroke-width="1.67532"
/>
<path
d="M61.3636 87.6896L65.8678 84.6868C69.3148 82.3888 73.708 82.0463 77.4695 83.7824L82.3052 86.0143"
stroke="#0F62FE"
stroke-width="1.67532"
/>
<circle
cx="71.0433"
cy="75.5902"
r="3.81602"
stroke="#0F62FE"
stroke-width="1.67532"
/>
<rect x="5.5" y="49.5" width="24.5" height="5" rx="2.5" stroke="#1339A6" />
<rect x="5.5" y="61.5" width="44" height="5" rx="2.5" stroke="#1339A6" />
<rect x="5.5" y="73.5" width="44" height="5" rx="2.5" stroke="#1339A6" />
<rect x="5.5" y="85.5" width="44" height="5" rx="2.5" stroke="#1339A6" />
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

+4
View File
@@ -248,4 +248,8 @@
:global(.app-background-color) {
background-color: var(--app-background-color);
}
:global(.bx--data-table-container) {
width: 100%;
}
</style>
+31 -1
View File
@@ -1 +1,31 @@
Audience Page
<script>
import PrimaryButton from '../../../components/PrimaryButton/index.svelte';
import Audience from '../../../components/Audience/index.svelte';
let isLoading = false;
function exportAudience() {
isLoading = true;
alert('This feature is coming soon');
isLoading = false;
}
</script>
<svelte:head>
<title>Audience - ClassroomIO</title>
</svelte:head>
<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="text-3xl font-bold">Audience</h1>
<PrimaryButton
label="Export"
onClick={exportAudience}
isDisabled={isLoading}
{isLoading}
/>
</div>
<Audience />
</div>
</section>
+1 -1
View File
@@ -65,7 +65,7 @@
<title>Courses - ClassroomIO</title>
</svelte:head>
<section class="w-full">
<section class="w-full max-w-7xl mx-auto">
<div class="py-10 px-5">
<div class="flex items-center justify-between mb-10">
<h1 class="text-3xl font-bold">Courses</h1>
+1 -1
View File
@@ -209,7 +209,7 @@
<title>Dashboard - ClassroomIO</title>
</svelte:head>
<div class="py-10 px-5">
<div class="py-10 px-5 w-full max-w-7xl mx-auto">
<div class="flex items-center justify-between mb-10">
<h1 class="text-3xl font-bold">Dashboard</h1>
<PrimaryButton label="Create Course" onClick={createCourse} />
+40 -1
View File
@@ -1,5 +1,5 @@
import { supabase } from '../../functions/supabase';
import { orgs, currentOrg } from '../../store/org';
import { orgs, currentOrg, orgAudience } from '../../store/org';
export async function getOrganizations(userId) {
const { data, error } = await supabase
@@ -39,3 +39,42 @@ export async function getOrganizations(userId) {
error,
};
}
export async function getOrgAudience(orgId) {
// get all students who are participants in any course belonging to an org
const { data, error } = await supabase
.from('profile')
.select(
`
id,
fullname,
email,
avatar_url,
created_at,
groupmember!inner(
role_id,
group!inner(
organization!inner(id)
)
)
`
)
.eq('groupmember.group.organization.id', orgId)
.eq('groupmember.role_id', 3); // is a student, tutor is 2 and admin is 1
console.log('data', data);
const audience = (data || []).map((profile) => ({
id: profile.id,
name: profile.fullname,
email: profile.email,
avatar_url: profile.avatar_url,
date_joined: new Date(profile.created_at).toDateString(),
}));
orgAudience.set(audience);
return {
audience: audience,
error,
};
}
+1
View File
@@ -7,3 +7,4 @@ export const currentOrg = writable({
shortName: '',
siteName: '',
});
export const orgAudience = writable([]);