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 <[email protected]>
This commit is contained in:
co-authored by
Rotimi Best
parent
381776f388
commit
f69bf920a9
@@ -0,0 +1,56 @@
|
||||
import { type ClassValue, clsx } from 'clsx';
|
||||
import { cubicOut } from 'svelte/easing';
|
||||
import type { TransitionConfig } from 'svelte/transition';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
|
||||
type FlyAndScaleParams = {
|
||||
y?: number;
|
||||
x?: number;
|
||||
start?: number;
|
||||
duration?: number;
|
||||
};
|
||||
|
||||
export const flyAndScale = (
|
||||
node: Element,
|
||||
params: FlyAndScaleParams = { y: -8, x: 0, start: 0.95, duration: 150 }
|
||||
): TransitionConfig => {
|
||||
const style = getComputedStyle(node);
|
||||
const transform = style.transform === 'none' ? '' : style.transform;
|
||||
|
||||
const scaleConversion = (valueA: number, scaleA: [number, number], scaleB: [number, number]) => {
|
||||
const [minA, maxA] = scaleA;
|
||||
const [minB, maxB] = scaleB;
|
||||
|
||||
const percentage = (valueA - minA) / (maxA - minA);
|
||||
const valueB = percentage * (maxB - minB) + minB;
|
||||
|
||||
return valueB;
|
||||
};
|
||||
|
||||
const styleToString = (style: Record<string, number | string | undefined>): string => {
|
||||
return Object.keys(style).reduce((str, key) => {
|
||||
if (style[key] === undefined) return str;
|
||||
return str + `${key}:${style[key]};`;
|
||||
}, '');
|
||||
};
|
||||
|
||||
return {
|
||||
duration: params.duration ?? 200,
|
||||
delay: 0,
|
||||
css: (t) => {
|
||||
const y = scaleConversion(t, [0, 1], [params.y ?? 5, 0]);
|
||||
const x = scaleConversion(t, [0, 1], [params.x ?? 0, 0]);
|
||||
const scale = scaleConversion(t, [0, 1], [params.start ?? 0.95, 1]);
|
||||
|
||||
return styleToString({
|
||||
transform: `${transform} translate3d(${x}px, ${y}px, 0) scale(${scale})`,
|
||||
opacity: t
|
||||
});
|
||||
},
|
||||
easing: cubicOut
|
||||
};
|
||||
};
|
||||
@@ -10,5 +10,9 @@ export const calDateDiff = (date: string | number | Date): string => {
|
||||
export const getGreeting = () => {
|
||||
const date = new Date();
|
||||
const hours = date.getHours();
|
||||
return hours < 12 ? 'dashboard.morning_heading' : hours < 18 ? 'dashboard.afternoon_heading' : 'dashboard.evening_heading';
|
||||
return hours < 12
|
||||
? 'dashboard.morning_heading'
|
||||
: hours < 18
|
||||
? 'dashboard.afternoon_heading'
|
||||
: 'dashboard.evening_heading';
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createClient, SupabaseClient } from '@supabase/supabase-js';
|
||||
import { config } from '$lib/config';
|
||||
import { createClient, SupabaseClient } from '@supabase/supabase-js';
|
||||
|
||||
export let supabase: SupabaseClient;
|
||||
|
||||
@@ -22,3 +22,8 @@ export const isSupabaseTokenInLocalStorage = () => {
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
export const getAccessToken = async () => {
|
||||
const { data } = await getSupabase().auth.getSession();
|
||||
return data.session?.access_token || '';
|
||||
};
|
||||
|
||||
@@ -1,17 +1,10 @@
|
||||
import { LOCALE } from '$lib/utils/types';
|
||||
import i18n from '@sveltekit-i18n/base';
|
||||
import parser from '@sveltekit-i18n/parser-icu';
|
||||
import { writable } from 'svelte/store';
|
||||
import i18n, { type Config } from 'sveltekit-i18n';
|
||||
|
||||
interface Params {
|
||||
dateValue: number;
|
||||
value: number;
|
||||
download: number;
|
||||
award: number;
|
||||
val: Date;
|
||||
}
|
||||
|
||||
export const config: Config<Partial<Params>> = {
|
||||
// parser: parser(),
|
||||
export const config = {
|
||||
parser: parser(),
|
||||
loaders: [
|
||||
{
|
||||
locale: 'en',
|
||||
|
||||
@@ -253,6 +253,17 @@ export function deleteGroupMember(groupMemberId: Groupmember['id']) {
|
||||
return supabase.from('groupmember').delete().match({ id: groupMemberId });
|
||||
}
|
||||
|
||||
export async function getMarks(courseId) {
|
||||
if (!courseId) return;
|
||||
|
||||
// Gets courses for a particular organisation where the current logged in user is a groupmember
|
||||
const { data: marks } = await supabase
|
||||
.rpc('get_marks')
|
||||
.eq('course_id', courseId);
|
||||
|
||||
return { marks };
|
||||
}
|
||||
|
||||
export function fetchLesson(lessonId: Lesson['id']) {
|
||||
return supabase
|
||||
.from('lesson')
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { supabase } from '$lib/utils/functions/supabase';
|
||||
import { getAccessToken } from '$lib/utils/functions/supabase';
|
||||
|
||||
export const NOTIFICATION_NAME = {
|
||||
WELCOME_TO_APP: 'WELCOME TO APP',
|
||||
@@ -29,11 +29,6 @@ const NAME_TO_PATH = {
|
||||
[NOTIFICATION_NAME.NEWSFEED]: '/api/email/course/newsfeed'
|
||||
};
|
||||
|
||||
const getAccessToken = async () => {
|
||||
const { data } = await supabase.auth.getSession();
|
||||
return data.session?.access_token || '';
|
||||
};
|
||||
|
||||
export const triggerSendEmail = async (name: string, body: { [k: string]: unknown }) => {
|
||||
try {
|
||||
const accessToken = await getAccessToken();
|
||||
|
||||
@@ -480,7 +480,9 @@
|
||||
"invite_sent": "Einladung gesendet",
|
||||
"you": "Du",
|
||||
"remove": "Entfernen"
|
||||
}
|
||||
},
|
||||
"view": "Sicht",
|
||||
"delete_profile": "Profil löschen"
|
||||
},
|
||||
"marks": {
|
||||
"title": "Markierungen",
|
||||
@@ -1478,5 +1480,29 @@
|
||||
"content": "Bist du sicher?",
|
||||
"no": "NEIN",
|
||||
"yes": "Ja"
|
||||
},
|
||||
"analytics": {
|
||||
"courses": "Kurse",
|
||||
"progress": "Fortschritt",
|
||||
"progress_description": "{value, number} von {total, number} abgeschlossen",
|
||||
"incomplete": "Unvollständig",
|
||||
"completed": "Vollendet",
|
||||
"complete": "vollständig",
|
||||
"last_seen": "Zuletzt gesehen",
|
||||
"a_while_ago": "vor einiger Zeit",
|
||||
"exercises": "Übungen",
|
||||
"graded": "Benotet",
|
||||
"not_graded": "Nicht benotet",
|
||||
"enrolled_courses": "Eingeschriebene Kurse",
|
||||
"enrolled_courses_description": "Alle eingeschriebenen Kurse",
|
||||
"overall_course_progress": "Gesamtkursfortschritt",
|
||||
"overall_course_progress_description": "Prozentsatz der abgeschlossenen Lektionen",
|
||||
"total_average_grade": "Gesamtdurchschnittsnote",
|
||||
"total_average_grade_description": "Basierend auf den Übungsnoten im gesamten Kurs",
|
||||
"overall_course_progress_user_description": "Basierend auf dem Abschluss der Lektion",
|
||||
"assignment_completion": "Abschluss der Aufgabe",
|
||||
"assignment_completion_description": "Prozentsatz der abgeschlossenen Aufgaben",
|
||||
"average_grade": "Durchschnittsnote",
|
||||
"average_grade_description": "Durchschnittsnote aller abgeschlossenen Aufgaben"
|
||||
}
|
||||
}
|
||||
@@ -889,6 +889,8 @@
|
||||
"role": "Role",
|
||||
"action": "Action",
|
||||
"feedback": "Copied Email to clipboard",
|
||||
"view": "View",
|
||||
"delete_profile": "Delete profile",
|
||||
"invite_modal": {
|
||||
"title": "Invite people",
|
||||
"invite": "Invite Tutors",
|
||||
@@ -1185,6 +1187,31 @@
|
||||
"upgrade": "Upgrade your plan to invite more students"
|
||||
},
|
||||
|
||||
"analytics": {
|
||||
"courses": "Courses",
|
||||
"progress": "Progress",
|
||||
"progress_description": "{value, number} of {total, number} complete",
|
||||
"incomplete": "Incomplete",
|
||||
"completed": "Completed",
|
||||
"complete": "complete",
|
||||
"last_seen": "Last seen",
|
||||
"a_while_ago": "a while ago",
|
||||
"exercises": "Exercises",
|
||||
"graded": "Graded",
|
||||
"not_graded": "Not Graded",
|
||||
"enrolled_courses": "Enrolled Courses",
|
||||
"enrolled_courses_description": "All enrolled courses",
|
||||
"overall_course_progress": "Overall Course Progress",
|
||||
"overall_course_progress_description": "Percentage of completed lessons",
|
||||
"total_average_grade": "Total Average Grade",
|
||||
"total_average_grade_description": "Based on exercise grades across all course",
|
||||
"overall_course_progress_user_description": "Based on lesson completion",
|
||||
"assignment_completion": "Assignment Completion",
|
||||
"assignment_completion_description": "Percentage of completed assignments",
|
||||
"average_grade": "Average Grade",
|
||||
"average_grade_description": "Average grade of all completed assignments"
|
||||
},
|
||||
|
||||
"upgrade": {
|
||||
"certificate": "Upgrade your plan to generate certificates",
|
||||
"team": "Upgrade your plan to add team members",
|
||||
|
||||
@@ -480,7 +480,9 @@
|
||||
"invite_sent": "Invitar enviado",
|
||||
"you": "Tú",
|
||||
"remove": "Eliminar"
|
||||
}
|
||||
},
|
||||
"view": "Vista",
|
||||
"delete_profile": "Eliminar perfil"
|
||||
},
|
||||
"marks": {
|
||||
"title": "Marcas",
|
||||
@@ -1478,5 +1480,29 @@
|
||||
"content": "¿Está seguro?",
|
||||
"no": "No",
|
||||
"yes": "Sí"
|
||||
},
|
||||
"analytics": {
|
||||
"courses": "Cursos",
|
||||
"progress": "Progreso",
|
||||
"progress_description": "{valor, número} de {total, número} completo",
|
||||
"incomplete": "Incompleto",
|
||||
"completed": "Terminado",
|
||||
"complete": "completo",
|
||||
"last_seen": "visto por última vez",
|
||||
"a_while_ago": "hace un tiempo",
|
||||
"exercises": "Ceremonias",
|
||||
"graded": "Calificado",
|
||||
"not_graded": "No calificado",
|
||||
"enrolled_courses": "Cursos inscritos",
|
||||
"enrolled_courses_description": "Todos los cursos inscritos",
|
||||
"overall_course_progress": "Progreso general del curso",
|
||||
"overall_course_progress_description": "Porcentaje de lecciones completadas",
|
||||
"total_average_grade": "Calificación promedio total",
|
||||
"total_average_grade_description": "Basado en calificaciones de ejercicios en todo el curso.",
|
||||
"overall_course_progress_user_description": "Basado en la finalización de la lección",
|
||||
"assignment_completion": "Finalización de la tarea",
|
||||
"assignment_completion_description": "Porcentaje de tareas completadas",
|
||||
"average_grade": "Calificación promedio",
|
||||
"average_grade_description": "Calificación promedio de todas las tareas completadas"
|
||||
}
|
||||
}
|
||||
@@ -480,7 +480,9 @@
|
||||
"invite_sent": "Invitation envoyée",
|
||||
"you": "Toi",
|
||||
"remove": "Retirer"
|
||||
}
|
||||
},
|
||||
"view": "Voir",
|
||||
"delete_profile": "Supprimer le profil"
|
||||
},
|
||||
"marks": {
|
||||
"title": "Des marques",
|
||||
@@ -1478,5 +1480,29 @@
|
||||
"content": "Es-tu sûr?",
|
||||
"no": "Non",
|
||||
"yes": "Oui"
|
||||
},
|
||||
"analytics": {
|
||||
"courses": "Cours",
|
||||
"progress": "Progrès",
|
||||
"progress_description": "{value, number} sur {total, number} terminé",
|
||||
"incomplete": "Incomplet",
|
||||
"completed": "Complété",
|
||||
"complete": "complet",
|
||||
"last_seen": "Vu pour la dernière fois",
|
||||
"a_while_ago": "il y a quelque temps",
|
||||
"exercises": "Exercices",
|
||||
"graded": "Noté",
|
||||
"not_graded": "Non noté",
|
||||
"enrolled_courses": "Cours inscrits",
|
||||
"enrolled_courses_description": "Tous les cours inscrits",
|
||||
"overall_course_progress": "Progression globale du cours",
|
||||
"overall_course_progress_description": "Pourcentage de leçons terminées",
|
||||
"total_average_grade": "Note moyenne totale",
|
||||
"total_average_grade_description": "Basé sur les notes des exercices dans tous les cours",
|
||||
"overall_course_progress_user_description": "Basé sur l'achèvement de la leçon",
|
||||
"assignment_completion": "Achèvement de la mission",
|
||||
"assignment_completion_description": "Pourcentage de missions terminées",
|
||||
"average_grade": "Note moyenne",
|
||||
"average_grade_description": "Note moyenne de tous les devoirs terminés"
|
||||
}
|
||||
}
|
||||
@@ -480,7 +480,9 @@
|
||||
"invite_sent": "आमंत्रण भेज दिया",
|
||||
"you": "आप",
|
||||
"remove": "निकालना"
|
||||
}
|
||||
},
|
||||
"view": "देखना",
|
||||
"delete_profile": "प्रोफ़ाइल हटाएं"
|
||||
},
|
||||
"marks": {
|
||||
"title": "निशान",
|
||||
@@ -1478,5 +1480,29 @@
|
||||
"content": "क्या आपको यकीन है?",
|
||||
"no": "नहीं",
|
||||
"yes": "हाँ"
|
||||
},
|
||||
"analytics": {
|
||||
"courses": "पाठ्यक्रम",
|
||||
"progress": "प्रगति",
|
||||
"progress_description": "{value, number} का {total, number} पूर्ण",
|
||||
"incomplete": "अधूरा",
|
||||
"completed": "पुरा होना।",
|
||||
"complete": "पूरा",
|
||||
"last_seen": "अंतिम बार देखा गया",
|
||||
"a_while_ago": "कुछ देर पहले",
|
||||
"exercises": "अभ्यास",
|
||||
"graded": "ग्रेड दिया गया",
|
||||
"not_graded": "वर्गीकृत नहीं किया",
|
||||
"enrolled_courses": "नामांकित पाठ्यक्रम",
|
||||
"enrolled_courses_description": "सभी नामांकित पाठ्यक्रम",
|
||||
"overall_course_progress": "समग्र पाठ्यक्रम प्रगति",
|
||||
"overall_course_progress_description": "पूर्ण किए गए पाठों का प्रतिशत",
|
||||
"total_average_grade": "कुल औसत ग्रेड",
|
||||
"total_average_grade_description": "संपूर्ण पाठ्यक्रम में व्यायाम ग्रेड के आधार पर",
|
||||
"overall_course_progress_user_description": "पाठ पूरा होने पर आधारित",
|
||||
"assignment_completion": "असाइनमेंट पूरा करना",
|
||||
"assignment_completion_description": "पूर्ण किये गये कार्यों का प्रतिशत",
|
||||
"average_grade": "औसत क्षेणी",
|
||||
"average_grade_description": "सभी पूर्ण किए गए असाइनमेंट का औसत ग्रेड"
|
||||
}
|
||||
}
|
||||
@@ -480,7 +480,9 @@
|
||||
"invite_sent": "Convite enviado",
|
||||
"you": "Você",
|
||||
"remove": "Remover"
|
||||
}
|
||||
},
|
||||
"view": "Visualizar",
|
||||
"delete_profile": "Excluir perfil"
|
||||
},
|
||||
"marks": {
|
||||
"title": "Marcas",
|
||||
@@ -1478,5 +1480,29 @@
|
||||
"content": "Tem certeza?",
|
||||
"no": "Não",
|
||||
"yes": "Sim"
|
||||
},
|
||||
"analytics": {
|
||||
"courses": "Cursos",
|
||||
"progress": "Progresso",
|
||||
"progress_description": "{value, number} de {total, number} concluído",
|
||||
"incomplete": "Incompleto",
|
||||
"completed": "Concluído",
|
||||
"complete": "completo",
|
||||
"last_seen": "Visto pela última vez",
|
||||
"a_while_ago": "há um tempo atrás",
|
||||
"exercises": "Exercícios",
|
||||
"graded": "Classificado",
|
||||
"not_graded": "Não classificado",
|
||||
"enrolled_courses": "Cursos Inscritos",
|
||||
"enrolled_courses_description": "Todos os cursos inscritos",
|
||||
"overall_course_progress": "Progresso geral do curso",
|
||||
"overall_course_progress_description": "Porcentagem de aulas concluídas",
|
||||
"total_average_grade": "Nota média total",
|
||||
"total_average_grade_description": "Com base nas notas dos exercícios em todo o curso",
|
||||
"overall_course_progress_user_description": "Com base na conclusão da lição",
|
||||
"assignment_completion": "Conclusão da tarefa",
|
||||
"assignment_completion_description": "Porcentagem de tarefas concluídas",
|
||||
"average_grade": "Nota Média",
|
||||
"average_grade_description": "Nota média de todas as tarefas concluídas"
|
||||
}
|
||||
}
|
||||
@@ -480,7 +480,9 @@
|
||||
"invite_sent": "Приглашение отправлено",
|
||||
"you": "Ты",
|
||||
"remove": "Удалять"
|
||||
}
|
||||
},
|
||||
"view": "Вид",
|
||||
"delete_profile": "Удалить профиль"
|
||||
},
|
||||
"marks": {
|
||||
"title": "Метки",
|
||||
@@ -1478,5 +1480,29 @@
|
||||
"content": "Вы уверены?",
|
||||
"no": "Нет",
|
||||
"yes": "Да"
|
||||
},
|
||||
"analytics": {
|
||||
"courses": "Курсы",
|
||||
"progress": "Прогресс",
|
||||
"progress_description": "{value, number} из {total, number} завершено",
|
||||
"incomplete": "Неполный",
|
||||
"completed": "Завершенный",
|
||||
"complete": "полный",
|
||||
"last_seen": "Последний раз видели",
|
||||
"a_while_ago": "некоторое время назад",
|
||||
"exercises": "Упражнения",
|
||||
"graded": "Оцененный",
|
||||
"not_graded": "Не оценено",
|
||||
"enrolled_courses": "Зарегистрированные курсы",
|
||||
"enrolled_courses_description": "Все зарегистрированные курсы",
|
||||
"overall_course_progress": "Общий прогресс курса",
|
||||
"overall_course_progress_description": "Процент завершенных уроков",
|
||||
"total_average_grade": "Общая средняя оценка",
|
||||
"total_average_grade_description": "На основе оценок за упражнения по всему курсу",
|
||||
"overall_course_progress_user_description": "По итогам урока",
|
||||
"assignment_completion": "Выполнение задания",
|
||||
"assignment_completion_description": "Процент выполненных заданий",
|
||||
"average_grade": "Средняя оценка",
|
||||
"average_grade_description": "Средняя оценка за все выполненные задания"
|
||||
}
|
||||
}
|
||||
@@ -480,7 +480,9 @@
|
||||
"invite_sent": "Lời mời đã gửi",
|
||||
"you": "Bạn",
|
||||
"remove": "Di dời"
|
||||
}
|
||||
},
|
||||
"view": "Xem",
|
||||
"delete_profile": "Xóa hồ sơ"
|
||||
},
|
||||
"marks": {
|
||||
"title": "Điểm",
|
||||
@@ -1478,5 +1480,29 @@
|
||||
"content": "Bạn có chắc không?",
|
||||
"no": "KHÔNG",
|
||||
"yes": "Đúng"
|
||||
},
|
||||
"analytics": {
|
||||
"courses": "Khóa học",
|
||||
"progress": "Tiến triển",
|
||||
"progress_description": "{value, number} của {total, number} đã hoàn thành",
|
||||
"incomplete": "Chưa hoàn thành",
|
||||
"completed": "Hoàn thành",
|
||||
"complete": "hoàn thành",
|
||||
"last_seen": "Nhìn thấy lần cuối",
|
||||
"a_while_ago": "cách đây một thời gian",
|
||||
"exercises": "Bài tập",
|
||||
"graded": "Đã chấm điểm",
|
||||
"not_graded": "Không được chấm điểm",
|
||||
"enrolled_courses": "Các khóa học đã đăng ký",
|
||||
"enrolled_courses_description": "Tất cả các khóa học đã đăng ký",
|
||||
"overall_course_progress": "Tiến độ chung của khóa học",
|
||||
"overall_course_progress_description": "Tỷ lệ bài học đã hoàn thành",
|
||||
"total_average_grade": "Tổng điểm trung bình",
|
||||
"total_average_grade_description": "Dựa trên điểm bài tập của tất cả các khóa học",
|
||||
"overall_course_progress_user_description": "Căn cứ vào việc hoàn thành bài học",
|
||||
"assignment_completion": "Hoàn thành bài tập",
|
||||
"assignment_completion_description": "Tỷ lệ bài tập đã hoàn thành",
|
||||
"average_grade": "Điểm trung bình",
|
||||
"average_grade_description": "Điểm trung bình của tất cả các bài tập đã hoàn thành"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
interface User {
|
||||
id: string;
|
||||
fullName: string;
|
||||
avatarUrl: string;
|
||||
email: string;
|
||||
lastSeen: string | undefined;
|
||||
}
|
||||
|
||||
export interface UserCourseWithStats {
|
||||
id: string;
|
||||
org_id: string;
|
||||
title: string;
|
||||
slug: string;
|
||||
description: string;
|
||||
logo: string;
|
||||
banner_image: string;
|
||||
cost: number;
|
||||
currency: string;
|
||||
is_published: boolean;
|
||||
total_lessons: number;
|
||||
total_students: number;
|
||||
progress_rate: number;
|
||||
progress_percentage: number;
|
||||
type: string;
|
||||
member_profile_id: string;
|
||||
lessons_count: number;
|
||||
lessons_completed: number;
|
||||
exercises_count: number;
|
||||
exercises_completed: number;
|
||||
average_grade: number;
|
||||
}
|
||||
|
||||
// interface LessonData {
|
||||
// id: string;
|
||||
// title: string;
|
||||
// completed: boolean;
|
||||
// exerciseNo: number;
|
||||
// created_at: any;
|
||||
// }
|
||||
|
||||
// interface Metric {
|
||||
// lesson: {
|
||||
// total: number;
|
||||
// completed: number;
|
||||
// };
|
||||
// exercise: {
|
||||
// total: number;
|
||||
// completed: number;
|
||||
// averageGrade: number;
|
||||
// };
|
||||
// progressPercentage: number;
|
||||
// }
|
||||
|
||||
export interface UserAnalytics {
|
||||
user: User;
|
||||
courses: UserCourseWithStats[];
|
||||
overallCourseProgress: number;
|
||||
overallAverageGrade: number;
|
||||
}
|
||||
|
||||
export interface UserCourseAnalytics {
|
||||
user: User;
|
||||
averageGrade: number;
|
||||
userExercisesStats: UserExercisesStats[];
|
||||
totalExercises: number;
|
||||
completedExercises: number;
|
||||
// pendingLessons: LessonData[];
|
||||
// completedLessons: LessonData[];
|
||||
progressPercentage: number;
|
||||
}
|
||||
|
||||
export interface UserExercisesStats {
|
||||
id: string;
|
||||
lessonId: string;
|
||||
lessonTitle: string;
|
||||
title: string;
|
||||
status: number | string | undefined;
|
||||
score: number;
|
||||
totalPoints: number;
|
||||
isCompleted: boolean;
|
||||
}
|
||||
|
||||
export interface UserExerciseStatsQuery {
|
||||
lesson: {
|
||||
title: string;
|
||||
exercise: {
|
||||
id: string;
|
||||
title: string;
|
||||
lesson_id: string;
|
||||
created_at: string;
|
||||
question: { points: number }[];
|
||||
submission: {
|
||||
groupmember: { id: string; profile_id: string };
|
||||
total: number;
|
||||
id: string;
|
||||
status_id: string;
|
||||
}[];
|
||||
}[];
|
||||
}[];
|
||||
}
|
||||
@@ -421,3 +421,15 @@ interface Tabs {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface StudentStat {
|
||||
username: string;
|
||||
progressPercentage: number;
|
||||
pendingCourses: Course[];
|
||||
completedCourses: Course[];
|
||||
totalLessons: number;
|
||||
totalLessonsCompleted: number;
|
||||
totalExercise: number;
|
||||
totalExercisesCompleted: number;
|
||||
totalLmsEntry: number;
|
||||
}
|
||||
Reference in New Issue
Block a user