feat: org and course student analytics page (#509)

* UI initialization

* feat: structure API

* feat: audience & people analytics

* feat: quick changes

* feat: add comments

* redesign course student page

* chore: make requests to db more efficient

* clean up analytics page for course

* feat: finish up audience analytics page

* chore: minor UI tweaks

* translate all

* replace inserts of log in events to update

* catch error if last_login isn't inserted

---------

Co-authored-by: Rotimi Best <best@classroomio.com>
This commit is contained in:
Adeyemi Adetunji
2024-12-30 02:59:10 +01:00
committed by GitHub
co-authored by Rotimi Best
parent 381776f388
commit f69bf920a9
46 changed files with 1916 additions and 437 deletions
@@ -0,0 +1,34 @@
<script lang="ts">
interface Activity {
icon: any;
title: string;
percentage: number;
description: string;
hidePercentage?: boolean;
}
export let activity: Activity;
</script>
<div
class="flex flex-col items-center justify-center gap-5 rounded-xl border p-3 dark:border-neutral-600 md:flex-row md:p-5"
>
<div class="bg-primary-200 w-fit rounded-full p-4 text-black">
<svelte:component this={activity.icon} size={24} />
</div>
<div>
<p class="text-center text-sm font-medium text-gray-600 dark:text-gray-200 md:text-left">
{activity.title}
</p>
<p class="text-center text-2xl font-bold md:text-left">
{activity.percentage}
{#if !activity.hidePercentage}
%
{/if}
</p>
<p class="text-center text-xs text-gray-500 dark:text-gray-300 md:text-left md:text-sm">
{activity.description}
</p>
</div>
</div>
@@ -0,0 +1,36 @@
<script lang="ts">
import Avatar from '$lib/components/Avatar/index.svelte';
import { calDateDiff } from '$lib/utils/functions/date';
import { t } from '$lib/utils/functions/translations';
import type { UserAnalytics } from '$lib/utils/types/analytics';
import Alarm from 'carbon-icons-svelte/lib/Alarm.svelte';
import Email from 'carbon-icons-svelte/lib/Email.svelte';
export let user: UserAnalytics['user'];
</script>
<div class="rounded-md border p-5 dark:border-neutral-600">
<div class="flex w-full flex-col items-center justify-start gap-4 text-start md:flex-row">
<Avatar src={user.avatarUrl} name={user.fullName} width="w-16" height="h-16" />
<div class="flex flex-col space-y-2">
<p class="text-center text-2xl font-bold dark:text-white md:text-left">
{user.fullName}
</p>
<div class="flex flex-col items-center gap-1 md:flex-row md:gap-4">
<p class="flex items-center gap-1 text-sm text-gray-500 dark:text-gray-300">
<Email />
{user.email}
</p>
<p class="flex items-center gap-1 text-sm text-gray-500 dark:text-gray-300">
<Alarm />
{$t('analytics.last_seen')}:
<span class="italic">
{user.lastSeen ? calDateDiff(user.lastSeen) : $t('analytics.a_while_ago')}
</span>
</p>
</div>
</div>
</div>
</div>
@@ -0,0 +1,12 @@
<script>
import { SkeletonPlaceholder } from 'carbon-components-svelte';
</script>
<div class="mx-auto max-w-4xl pt-14">
<SkeletonPlaceholder style="width: 100%; height: 10rem;" />
<div class="mt-10 grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3">
<SkeletonPlaceholder style="width: 270px; height: 10rem;" />
<SkeletonPlaceholder style="width: 270px; height: 10rem;" />
<SkeletonPlaceholder style="width: 270px; height: 10rem;" />
</div>
</div>
@@ -0,0 +1,3 @@
export { default as ActivityCard } from './ActivityCard.svelte';
export { default as HeroProfileCard } from './HeroProfileCard.svelte';
export { default as LoadingPage } from './LoadingPage.svelte';