CLASSROOMIO-T-31: Add audience page
Co-authored-by: Rotimi Best <rotimiibitoyeemma@gmail.com> Merge-request: CLASSROOMIO-MR-6 Merged-by: Digdippa Team <digdippa@gmail.com>
This commit is contained in:
committed by
Space Cloud
co-authored by
Rotimi Best
parent
ae8f9ca222
commit
01a1f0f907
@@ -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
|
||||||
|
/>
|
||||||
@@ -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 { addGroupMember } from '../../../../utils/services/courses';
|
||||||
import { supabase } from '../../../../utils/functions/supabase';
|
import { supabase } from '../../../../utils/functions/supabase';
|
||||||
import { profile } from '../../../../utils/store/user';
|
import { profile } from '../../../../utils/store/user';
|
||||||
|
import { currentOrg } from '../../../../utils/store/org';
|
||||||
|
|
||||||
let errors = {
|
let errors = {
|
||||||
emails: '',
|
emails: '',
|
||||||
@@ -25,7 +26,7 @@
|
|||||||
// 1. Create group
|
// 1. Create group
|
||||||
const { data: newGroup } = await supabase
|
const { data: newGroup } = await supabase
|
||||||
.from('group')
|
.from('group')
|
||||||
.insert({ name: title, description });
|
.insert({ name: title, description, organization_id: $currentOrg.id });
|
||||||
|
|
||||||
console.log(`newGroup`, newGroup);
|
console.log(`newGroup`, newGroup);
|
||||||
const { id: group_id } = newGroup[0];
|
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 |
@@ -248,4 +248,8 @@
|
|||||||
:global(.app-background-color) {
|
:global(.app-background-color) {
|
||||||
background-color: var(--app-background-color);
|
background-color: var(--app-background-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:global(.bx--data-table-container) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -65,7 +65,7 @@
|
|||||||
<title>Courses - ClassroomIO</title>
|
<title>Courses - ClassroomIO</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<section class="w-full">
|
<section class="w-full max-w-7xl mx-auto">
|
||||||
<div class="py-10 px-5">
|
<div class="py-10 px-5">
|
||||||
<div class="flex items-center justify-between mb-10">
|
<div class="flex items-center justify-between mb-10">
|
||||||
<h1 class="text-3xl font-bold">Courses</h1>
|
<h1 class="text-3xl font-bold">Courses</h1>
|
||||||
|
|||||||
@@ -209,7 +209,7 @@
|
|||||||
<title>Dashboard - ClassroomIO</title>
|
<title>Dashboard - ClassroomIO</title>
|
||||||
</svelte:head>
|
</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">
|
<div class="flex items-center justify-between mb-10">
|
||||||
<h1 class="text-3xl font-bold">Dashboard</h1>
|
<h1 class="text-3xl font-bold">Dashboard</h1>
|
||||||
<PrimaryButton label="Create Course" onClick={createCourse} />
|
<PrimaryButton label="Create Course" onClick={createCourse} />
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { supabase } from '../../functions/supabase';
|
import { supabase } from '../../functions/supabase';
|
||||||
import { orgs, currentOrg } from '../../store/org';
|
import { orgs, currentOrg, orgAudience } from '../../store/org';
|
||||||
|
|
||||||
export async function getOrganizations(userId) {
|
export async function getOrganizations(userId) {
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
@@ -39,3 +39,42 @@ export async function getOrganizations(userId) {
|
|||||||
error,
|
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,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,3 +7,4 @@ export const currentOrg = writable({
|
|||||||
shortName: '',
|
shortName: '',
|
||||||
siteName: '',
|
siteName: '',
|
||||||
});
|
});
|
||||||
|
export const orgAudience = writable([]);
|
||||||
|
|||||||
Reference in New Issue
Block a user