From 4a4d8ccad2587cbbc94c5a30b9fe2574ab25fc6d Mon Sep 17 00:00:00 2001 From: Eamonn de Leastar Date: Thu, 7 Nov 2024 16:28:57 +0000 Subject: [PATCH 01/11] remove course reader + home routes --- src/routes/(course-reader)/+layout.svelte | 32 ------- .../course/[courseid]/+page.svelte | 10 -- .../course/[courseid]/+page.ts | 14 --- .../lab/[courseid]/[...loid]/+page.svelte | 21 ---- .../lab/[courseid]/[...loid]/+page.ts | 20 ---- .../note/[courseid]/[...loid]/+page.svelte | 14 --- .../note/[courseid]/[...loid]/+page.ts | 11 --- .../search/[courseid]/+page.svelte | 95 ------------------- .../search/[courseid]/+page.ts | 14 --- .../talk/[courseid]/[...loid]/+page.svelte | 14 --- .../talk/[courseid]/[...loid]/+page.ts | 11 --- .../topic/[courseid]/[...loid]/+page.svelte | 11 --- .../topic/[courseid]/[...loid]/+page.ts | 14 --- .../video/[courseid]/[...loid]/+page.svelte | 14 --- .../video/[courseid]/[...loid]/+page.ts | 21 ---- .../wall/[type]/[courseid]/+page.svelte | 11 --- .../wall/[type]/[courseid]/+page.ts | 26 ----- src/routes/(home)/+page.svelte | 15 --- src/routes/(home)/+page.ts | 1 - src/routes/(home)/TutorsCredits.svelte | 26 ----- src/routes/(home)/TutorsLinks.svelte | 33 ------- src/routes/(home)/TutorsValues.svelte | 21 ---- .../(home)/values/DeveloperExperience.svelte | 13 --- .../(home)/values/EducatorExperience.svelte | 13 --- .../(home)/values/ExperienceCard.svelte | 24 ----- .../(home)/values/LearnerExperience.svelte | 13 --- 26 files changed, 512 deletions(-) delete mode 100644 src/routes/(course-reader)/+layout.svelte delete mode 100644 src/routes/(course-reader)/course/[courseid]/+page.svelte delete mode 100644 src/routes/(course-reader)/course/[courseid]/+page.ts delete mode 100644 src/routes/(course-reader)/lab/[courseid]/[...loid]/+page.svelte delete mode 100644 src/routes/(course-reader)/lab/[courseid]/[...loid]/+page.ts delete mode 100644 src/routes/(course-reader)/note/[courseid]/[...loid]/+page.svelte delete mode 100644 src/routes/(course-reader)/note/[courseid]/[...loid]/+page.ts delete mode 100644 src/routes/(course-reader)/search/[courseid]/+page.svelte delete mode 100644 src/routes/(course-reader)/search/[courseid]/+page.ts delete mode 100644 src/routes/(course-reader)/talk/[courseid]/[...loid]/+page.svelte delete mode 100644 src/routes/(course-reader)/talk/[courseid]/[...loid]/+page.ts delete mode 100644 src/routes/(course-reader)/topic/[courseid]/[...loid]/+page.svelte delete mode 100644 src/routes/(course-reader)/topic/[courseid]/[...loid]/+page.ts delete mode 100644 src/routes/(course-reader)/video/[courseid]/[...loid]/+page.svelte delete mode 100644 src/routes/(course-reader)/video/[courseid]/[...loid]/+page.ts delete mode 100644 src/routes/(course-reader)/wall/[type]/[courseid]/+page.svelte delete mode 100644 src/routes/(course-reader)/wall/[type]/[courseid]/+page.ts delete mode 100644 src/routes/(home)/+page.svelte delete mode 100644 src/routes/(home)/+page.ts delete mode 100644 src/routes/(home)/TutorsCredits.svelte delete mode 100644 src/routes/(home)/TutorsLinks.svelte delete mode 100644 src/routes/(home)/TutorsValues.svelte delete mode 100644 src/routes/(home)/values/DeveloperExperience.svelte delete mode 100644 src/routes/(home)/values/EducatorExperience.svelte delete mode 100644 src/routes/(home)/values/ExperienceCard.svelte delete mode 100644 src/routes/(home)/values/LearnerExperience.svelte diff --git a/src/routes/(course-reader)/+layout.svelte b/src/routes/(course-reader)/+layout.svelte deleted file mode 100644 index 869f3fc..0000000 --- a/src/routes/(course-reader)/+layout.svelte +++ /dev/null @@ -1,32 +0,0 @@ - - - - {currentLo?.value?.title} - - - - {@render children()} - diff --git a/src/routes/(course-reader)/course/[courseid]/+page.svelte b/src/routes/(course-reader)/course/[courseid]/+page.svelte deleted file mode 100644 index a1c9cf4..0000000 --- a/src/routes/(course-reader)/course/[courseid]/+page.svelte +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/src/routes/(course-reader)/course/[courseid]/+page.ts b/src/routes/(course-reader)/course/[courseid]/+page.ts deleted file mode 100644 index 2e9a5c5..0000000 --- a/src/routes/(course-reader)/course/[courseid]/+page.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { courseService } from "$lib/services/course.svelte"; -import { currentLo } from "$lib/runes"; - -export const ssr = false; - -export const load = async ({ params, fetch }) => { - const course = await courseService.readCourse(params.courseid, fetch); - currentLo.value = course; - - return { - course, - lo: course - }; -}; diff --git a/src/routes/(course-reader)/lab/[courseid]/[...loid]/+page.svelte b/src/routes/(course-reader)/lab/[courseid]/[...loid]/+page.svelte deleted file mode 100644 index 5c7497e..0000000 --- a/src/routes/(course-reader)/lab/[courseid]/[...loid]/+page.svelte +++ /dev/null @@ -1,21 +0,0 @@ - - -{#if data.lab.lab.pdf} - - - -{:else} - - - -{/if} diff --git a/src/routes/(course-reader)/lab/[courseid]/[...loid]/+page.ts b/src/routes/(course-reader)/lab/[courseid]/[...loid]/+page.ts deleted file mode 100644 index 9ecf331..0000000 --- a/src/routes/(course-reader)/lab/[courseid]/[...loid]/+page.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { PageLoad } from "./$types"; -import { courseService } from "$lib/services/course.svelte"; -import { currentLabStepIndex } from "$lib/runes"; - -export const ssr = false; - -export const load: PageLoad = async ({ url, params, fetch }) => { - const liveLab = await courseService.readLab(params.courseid, url.pathname, fetch); - - const lastSegment = url.pathname.substring(url.pathname.lastIndexOf("/") + 1); - if (lastSegment.startsWith("book")) { - liveLab.setFirstPageActive(); - } else { - liveLab.setActivePage(lastSegment); - } - currentLabStepIndex.value = liveLab.index; - return { - lab: liveLab - }; -}; diff --git a/src/routes/(course-reader)/note/[courseid]/[...loid]/+page.svelte b/src/routes/(course-reader)/note/[courseid]/[...loid]/+page.svelte deleted file mode 100644 index e9a215e..0000000 --- a/src/routes/(course-reader)/note/[courseid]/[...loid]/+page.svelte +++ /dev/null @@ -1,14 +0,0 @@ - - - - - diff --git a/src/routes/(course-reader)/note/[courseid]/[...loid]/+page.ts b/src/routes/(course-reader)/note/[courseid]/[...loid]/+page.ts deleted file mode 100644 index 3d05f24..0000000 --- a/src/routes/(course-reader)/note/[courseid]/[...loid]/+page.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { PageLoad } from "./$types"; -import { courseService } from "$lib/services/course.svelte"; - -export const ssr = false; - -export const load: PageLoad = async ({ url, params, fetch }) => { - const lo = await courseService.readLo(params.courseid, url.pathname, fetch); - return { - lo: lo - }; -}; diff --git a/src/routes/(course-reader)/search/[courseid]/+page.svelte b/src/routes/(course-reader)/search/[courseid]/+page.svelte deleted file mode 100644 index 089a45a..0000000 --- a/src/routes/(course-reader)/search/[courseid]/+page.svelte +++ /dev/null @@ -1,95 +0,0 @@ - - -
- -
- {#each searchResults as result} - - {/each} -
-
- - diff --git a/src/routes/(course-reader)/search/[courseid]/+page.ts b/src/routes/(course-reader)/search/[courseid]/+page.ts deleted file mode 100644 index fd35616..0000000 --- a/src/routes/(course-reader)/search/[courseid]/+page.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { PageLoad } from "./$types"; -import { courseService } from "$lib/services/course.svelte"; -import type { Course } from "$lib/services/models/lo-types"; -import { currentLo } from "$lib/runes"; - -export const ssr = false; - -export const load: PageLoad = async ({ params, fetch }) => { - const course: Course = await courseService.readCourse(params.courseid, fetch); - currentLo.value = course; - return { - course: course - }; -}; diff --git a/src/routes/(course-reader)/talk/[courseid]/[...loid]/+page.svelte b/src/routes/(course-reader)/talk/[courseid]/[...loid]/+page.svelte deleted file mode 100644 index 1186375..0000000 --- a/src/routes/(course-reader)/talk/[courseid]/[...loid]/+page.svelte +++ /dev/null @@ -1,14 +0,0 @@ - - - - - diff --git a/src/routes/(course-reader)/talk/[courseid]/[...loid]/+page.ts b/src/routes/(course-reader)/talk/[courseid]/[...loid]/+page.ts deleted file mode 100644 index 3d05f24..0000000 --- a/src/routes/(course-reader)/talk/[courseid]/[...loid]/+page.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { PageLoad } from "./$types"; -import { courseService } from "$lib/services/course.svelte"; - -export const ssr = false; - -export const load: PageLoad = async ({ url, params, fetch }) => { - const lo = await courseService.readLo(params.courseid, url.pathname, fetch); - return { - lo: lo - }; -}; diff --git a/src/routes/(course-reader)/topic/[courseid]/[...loid]/+page.svelte b/src/routes/(course-reader)/topic/[courseid]/[...loid]/+page.svelte deleted file mode 100644 index 209ae04..0000000 --- a/src/routes/(course-reader)/topic/[courseid]/[...loid]/+page.svelte +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/src/routes/(course-reader)/topic/[courseid]/[...loid]/+page.ts b/src/routes/(course-reader)/topic/[courseid]/[...loid]/+page.ts deleted file mode 100644 index 0bca8d4..0000000 --- a/src/routes/(course-reader)/topic/[courseid]/[...loid]/+page.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { PageLoad } from "./$types"; -import { courseService } from "$lib/services/course.svelte"; -import type { Topic } from "$lib/services/models/lo-types"; - -export const ssr = false; - -export const load: PageLoad = async ({ params, url, fetch }) => { - const topicId = url.pathname; - const topic = (await courseService.readTopic(params.courseid, topicId, fetch)) as Topic; - - return { - topic: topic - }; -}; diff --git a/src/routes/(course-reader)/video/[courseid]/[...loid]/+page.svelte b/src/routes/(course-reader)/video/[courseid]/[...loid]/+page.svelte deleted file mode 100644 index 6e7ed39..0000000 --- a/src/routes/(course-reader)/video/[courseid]/[...loid]/+page.svelte +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/src/routes/(course-reader)/video/[courseid]/[...loid]/+page.ts b/src/routes/(course-reader)/video/[courseid]/[...loid]/+page.ts deleted file mode 100644 index a76f064..0000000 --- a/src/routes/(course-reader)/video/[courseid]/[...loid]/+page.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { PageLoad } from "./$types"; -import { courseService } from "$lib/services/course.svelte"; - -export const ssr = false; - -export const load: PageLoad = async ({ url, params, fetch }) => { - let videoId = url.pathname; - let videoStartEnd = url.searchParams.toString(); - if (videoStartEnd.endsWith("=")) { - videoStartEnd = videoStartEnd.slice(0, -1); - videoId = `${url.pathname}?${videoStartEnd}`; - } else if (videoStartEnd) { - videoId = `${url.pathname}?${videoStartEnd}`; - } else { - videoId = `${url.pathname}`; - } - const lo = await courseService.readLo(params.courseid, videoId, fetch); - return { - lo: lo - }; -}; diff --git a/src/routes/(course-reader)/wall/[type]/[courseid]/+page.svelte b/src/routes/(course-reader)/wall/[type]/[courseid]/+page.svelte deleted file mode 100644 index 130c49e..0000000 --- a/src/routes/(course-reader)/wall/[type]/[courseid]/+page.svelte +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/src/routes/(course-reader)/wall/[type]/[courseid]/+page.ts b/src/routes/(course-reader)/wall/[type]/[courseid]/+page.ts deleted file mode 100644 index 0f10b8a..0000000 --- a/src/routes/(course-reader)/wall/[type]/[courseid]/+page.ts +++ /dev/null @@ -1,26 +0,0 @@ -import type { PageLoad } from "./$types"; -import { courseService } from "$lib/services/course.svelte"; -import { currentLo } from "$lib/runes"; - -export const ssr = false; - -export const load: PageLoad = async ({ params, fetch }) => { - const course = await courseService.readCourse(params.courseid, fetch); - const los = await courseService.readWall(params.courseid, params.type, fetch); - const type = params.type; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error - currentLo.value = { - breadCrumbs: [course], - title: `All ${params.type}s in Module`, - type: type, - parentLo: course, - parentCourse: course, - route: "wall" - }; - return { - type: params.type, - lo: course, - los: los - }; -}; diff --git a/src/routes/(home)/+page.svelte b/src/routes/(home)/+page.svelte deleted file mode 100644 index 4f00e4f..0000000 --- a/src/routes/(home)/+page.svelte +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - diff --git a/src/routes/(home)/+page.ts b/src/routes/(home)/+page.ts deleted file mode 100644 index a3d1578..0000000 --- a/src/routes/(home)/+page.ts +++ /dev/null @@ -1 +0,0 @@ -export const ssr = false; diff --git a/src/routes/(home)/TutorsCredits.svelte b/src/routes/(home)/TutorsCredits.svelte deleted file mode 100644 index 75ae3ad..0000000 --- a/src/routes/(home)/TutorsCredits.svelte +++ /dev/null @@ -1,26 +0,0 @@ -
-
-
-

- A fully Open Source project -

-

- Tutors is an open source project available for free under the MIT license on GitHub. -

- - github contributors list - - -
-
-
diff --git a/src/routes/(home)/TutorsLinks.svelte b/src/routes/(home)/TutorsLinks.svelte deleted file mode 100644 index 541496e..0000000 --- a/src/routes/(home)/TutorsLinks.svelte +++ /dev/null @@ -1,33 +0,0 @@ - - -
-
-
-

- An - Open Learning - - Web Toolkit -

-

- A collection of open source components & services supporting the creation of transformative learning experiences - using open web standards. Developed to support the - Higher Diploma in Computer Science - - at SETU -

- -
-
- tutors reader screenshot -
-
-
diff --git a/src/routes/(home)/TutorsValues.svelte b/src/routes/(home)/TutorsValues.svelte deleted file mode 100644 index ada8fb4..0000000 --- a/src/routes/(home)/TutorsValues.svelte +++ /dev/null @@ -1,21 +0,0 @@ - - -
-
-

- The Values of the project -

-
-
- - - -
-
diff --git a/src/routes/(home)/values/DeveloperExperience.svelte b/src/routes/(home)/values/DeveloperExperience.svelte deleted file mode 100644 index 7419de6..0000000 --- a/src/routes/(home)/values/DeveloperExperience.svelte +++ /dev/null @@ -1,13 +0,0 @@ - - - diff --git a/src/routes/(home)/values/EducatorExperience.svelte b/src/routes/(home)/values/EducatorExperience.svelte deleted file mode 100644 index 9ea860c..0000000 --- a/src/routes/(home)/values/EducatorExperience.svelte +++ /dev/null @@ -1,13 +0,0 @@ - - - diff --git a/src/routes/(home)/values/ExperienceCard.svelte b/src/routes/(home)/values/ExperienceCard.svelte deleted file mode 100644 index 0d15c63..0000000 --- a/src/routes/(home)/values/ExperienceCard.svelte +++ /dev/null @@ -1,24 +0,0 @@ - - - -
-
-

{title}

-
-
-

- {@html contentHtml} -

-
-
-
diff --git a/src/routes/(home)/values/LearnerExperience.svelte b/src/routes/(home)/values/LearnerExperience.svelte deleted file mode 100644 index 6550dee..0000000 --- a/src/routes/(home)/values/LearnerExperience.svelte +++ /dev/null @@ -1,13 +0,0 @@ - - - From 61bf8857ab5927f45e29e953d983bea6b5bab856 Mon Sep 17 00:00:00 2001 From: Eamonn de Leastar Date: Thu, 7 Nov 2024 16:29:21 +0000 Subject: [PATCH 02/11] remove uneeded ux + introduce time --- src/lib/ui/app-shells/CourseShell.svelte | 36 -- .../learning-objects/content/Calendar.svelte | 37 -- .../ui/learning-objects/content/Lab.svelte | 73 ---- .../ui/learning-objects/content/Note.svelte | 27 -- .../ui/learning-objects/content/Talk.svelte | 183 ---------- .../ui/learning-objects/content/Video.svelte | 95 ------ .../content/support/pdf-utils.ts | 29 -- .../ui/learning-objects/layout/Card.svelte | 63 ---- .../ui/learning-objects/layout/Cards.svelte | 65 ---- .../layout/LoContextPanel.svelte | 28 -- .../ui/learning-objects/layout/Panels.svelte | 21 -- .../ui/learning-objects/layout/Units.svelte | 31 -- .../ui/learning-objects/layout/Wall.svelte | 37 -- .../structure/Composite.svelte | 30 -- .../learning-objects/structure/Context.svelte | 31 -- .../structure/CourseContext.svelte | 22 -- .../structure/LoContext.svelte | 38 --- src/lib/ui/navigators/Links.svelte | 11 - src/lib/ui/navigators/MainNavigator.svelte | 43 --- .../ui/navigators/SecondaryNavigator.svelte | 29 -- .../navigators/buttons/CalendarButton.svelte | 25 -- .../navigators/buttons/EditCoursButton.svelte | 49 --- .../ui/navigators/buttons/InfoButton.svelte | 15 - .../ui/navigators/buttons/SearchButton.svelte | 11 - .../ui/navigators/buttons/TocButton.svelte | 15 - src/lib/ui/navigators/sidebars/Sidebar.svelte | 19 -- .../ui/navigators/sidebars/Sidebars.svelte | 31 -- .../ui/navigators/titles/CourseTitle.svelte | 38 --- .../tutors-connect/ConnectedProfile.svelte | 61 +--- .../ui/time/supabase/analytics/calendar.ts | 231 +++++++++++++ .../analytics/heatmap/base-heat-map.ts | 323 ++++++++++++++++++ .../analytics/heatmap/lab-heat-map-chart.ts | 47 +++ .../analytics/heatmap/topic-heat-map-chart.ts | 44 +++ .../time/supabase/analytics/lab-box-plot.ts | 164 +++++++++ .../supabase/analytics/live-student-feed.ts | 82 +++++ .../analytics/piechart/base-pie-chart.ts | 130 +++++++ .../analytics/piechart/lab-pie-chart.ts | 69 ++++ .../analytics/piechart/topic-pie-chart.ts | 105 ++++++ .../time/supabase/analytics/topic-box-plot.ts | 170 +++++++++ src/lib/ui/time/supabase/charts/barchart.ts | 37 ++ .../ui/time/supabase/charts/boxplot-chart.ts | 169 +++++++++ .../ui/time/supabase/charts/calendar-chart.ts | 200 +++++++++++ .../ui/time/supabase/charts/heatmap-chart.ts | 250 ++++++++++++++ .../time/supabase/charts/personlised-logo.ts | 63 ++++ src/lib/ui/time/supabase/charts/piechart.ts | 90 +++++ .../charts/tutors-charts-background-url.ts | 5 + .../time/supabase/views/CalendarView.svelte | 54 +++ src/lib/ui/time/supabase/views/Chart.svelte | 45 +++ .../time/supabase/views/HeatMapChart.svelte | 126 +++++++ .../views/InstructorCalendarView.svelte | 55 +++ .../supabase/views/InstructorLabView.svelte | 13 + .../views/InstructorLabViewBoxPlot.svelte | 89 +++++ .../supabase/views/InstructorTopicView.svelte | 13 + .../views/InstructorTopicViewBoxPlot.svelte | 94 +++++ .../views/InstructorTopicViewPieChart.svelte | 12 + src/lib/ui/time/supabase/views/LabView.svelte | 12 + .../supabase/views/LabViewPieChart.svelte | 10 + .../supabase/views/LiveStudentFeed.svelte | 32 ++ .../ui/time/supabase/views/TopicView.svelte | 12 + .../supabase/views/TopicViewPieChart.svelte | 12 + 60 files changed, 2765 insertions(+), 1186 deletions(-) delete mode 100644 src/lib/ui/app-shells/CourseShell.svelte delete mode 100644 src/lib/ui/learning-objects/content/Calendar.svelte delete mode 100644 src/lib/ui/learning-objects/content/Lab.svelte delete mode 100644 src/lib/ui/learning-objects/content/Note.svelte delete mode 100644 src/lib/ui/learning-objects/content/Talk.svelte delete mode 100644 src/lib/ui/learning-objects/content/Video.svelte delete mode 100644 src/lib/ui/learning-objects/content/support/pdf-utils.ts delete mode 100644 src/lib/ui/learning-objects/layout/Card.svelte delete mode 100644 src/lib/ui/learning-objects/layout/Cards.svelte delete mode 100644 src/lib/ui/learning-objects/layout/LoContextPanel.svelte delete mode 100644 src/lib/ui/learning-objects/layout/Panels.svelte delete mode 100644 src/lib/ui/learning-objects/layout/Units.svelte delete mode 100644 src/lib/ui/learning-objects/layout/Wall.svelte delete mode 100644 src/lib/ui/learning-objects/structure/Composite.svelte delete mode 100644 src/lib/ui/learning-objects/structure/Context.svelte delete mode 100644 src/lib/ui/learning-objects/structure/CourseContext.svelte delete mode 100644 src/lib/ui/learning-objects/structure/LoContext.svelte delete mode 100644 src/lib/ui/navigators/Links.svelte delete mode 100644 src/lib/ui/navigators/MainNavigator.svelte delete mode 100644 src/lib/ui/navigators/SecondaryNavigator.svelte delete mode 100644 src/lib/ui/navigators/buttons/CalendarButton.svelte delete mode 100644 src/lib/ui/navigators/buttons/EditCoursButton.svelte delete mode 100644 src/lib/ui/navigators/buttons/InfoButton.svelte delete mode 100644 src/lib/ui/navigators/buttons/SearchButton.svelte delete mode 100644 src/lib/ui/navigators/buttons/TocButton.svelte delete mode 100644 src/lib/ui/navigators/sidebars/Sidebar.svelte delete mode 100644 src/lib/ui/navigators/sidebars/Sidebars.svelte delete mode 100644 src/lib/ui/navigators/titles/CourseTitle.svelte create mode 100644 src/lib/ui/time/supabase/analytics/calendar.ts create mode 100644 src/lib/ui/time/supabase/analytics/heatmap/base-heat-map.ts create mode 100644 src/lib/ui/time/supabase/analytics/heatmap/lab-heat-map-chart.ts create mode 100644 src/lib/ui/time/supabase/analytics/heatmap/topic-heat-map-chart.ts create mode 100644 src/lib/ui/time/supabase/analytics/lab-box-plot.ts create mode 100644 src/lib/ui/time/supabase/analytics/live-student-feed.ts create mode 100644 src/lib/ui/time/supabase/analytics/piechart/base-pie-chart.ts create mode 100644 src/lib/ui/time/supabase/analytics/piechart/lab-pie-chart.ts create mode 100644 src/lib/ui/time/supabase/analytics/piechart/topic-pie-chart.ts create mode 100644 src/lib/ui/time/supabase/analytics/topic-box-plot.ts create mode 100644 src/lib/ui/time/supabase/charts/barchart.ts create mode 100644 src/lib/ui/time/supabase/charts/boxplot-chart.ts create mode 100644 src/lib/ui/time/supabase/charts/calendar-chart.ts create mode 100644 src/lib/ui/time/supabase/charts/heatmap-chart.ts create mode 100644 src/lib/ui/time/supabase/charts/personlised-logo.ts create mode 100644 src/lib/ui/time/supabase/charts/piechart.ts create mode 100644 src/lib/ui/time/supabase/charts/tutors-charts-background-url.ts create mode 100644 src/lib/ui/time/supabase/views/CalendarView.svelte create mode 100644 src/lib/ui/time/supabase/views/Chart.svelte create mode 100644 src/lib/ui/time/supabase/views/HeatMapChart.svelte create mode 100644 src/lib/ui/time/supabase/views/InstructorCalendarView.svelte create mode 100644 src/lib/ui/time/supabase/views/InstructorLabView.svelte create mode 100644 src/lib/ui/time/supabase/views/InstructorLabViewBoxPlot.svelte create mode 100644 src/lib/ui/time/supabase/views/InstructorTopicView.svelte create mode 100644 src/lib/ui/time/supabase/views/InstructorTopicViewBoxPlot.svelte create mode 100644 src/lib/ui/time/supabase/views/InstructorTopicViewPieChart.svelte create mode 100644 src/lib/ui/time/supabase/views/LabView.svelte create mode 100644 src/lib/ui/time/supabase/views/LabViewPieChart.svelte create mode 100644 src/lib/ui/time/supabase/views/LiveStudentFeed.svelte create mode 100644 src/lib/ui/time/supabase/views/TopicView.svelte create mode 100644 src/lib/ui/time/supabase/views/TopicViewPieChart.svelte diff --git a/src/lib/ui/app-shells/CourseShell.svelte b/src/lib/ui/app-shells/CourseShell.svelte deleted file mode 100644 index f75ffdc..0000000 --- a/src/lib/ui/app-shells/CourseShell.svelte +++ /dev/null @@ -1,36 +0,0 @@ - - - - - {#snippet header()} - - - {/snippet} - - {#key transitionKey.value} -
-
-
-
- {@render children()} -
-
-
- {/key} - - {#snippet pageFooter()} - - - {/each} - - diff --git a/src/routes/(auth)/dashboard/Welcome.svelte b/src/routes/(auth)/dashboard/Welcome.svelte deleted file mode 100644 index 5c72c1c..0000000 --- a/src/routes/(auth)/dashboard/Welcome.svelte +++ /dev/null @@ -1,17 +0,0 @@ - - -
-
-
-

- Welcome, {tutorsConnectService.tutorsId.value?.name}! -

-
-
- -
-
-
From b4cb77dc0070a81b6f7e7c56f703f21774d17ce6 Mon Sep 17 00:00:00 2001 From: Eamonn de Leastar Date: Thu, 7 Nov 2024 16:30:00 +0000 Subject: [PATCH 04/11] introduce core metrics --- src/lib/services/analytics.svelte.ts | 56 --- src/lib/services/connect.svelte.ts | 71 +--- src/lib/services/metrics/db/client.ts | 4 + src/lib/services/metrics/metrics-types.ts | 208 ++++++++++ src/lib/services/metrics/supabase-metrics.ts | 77 ++++ src/lib/services/metrics/supabase-utils.ts | 374 ++++++++++++++++++ src/lib/services/presence.svelte.ts | 110 ------ .../services/profiles/localStorageProfile.ts | 48 --- src/lib/services/profiles/supabase-client.ts | 172 -------- .../profiles/supabaseProfile.svelte.ts | 67 ---- src/lib/services/types.svelte.ts | 77 +--- src/lib/services/utils/search.ts | 311 --------------- 12 files changed, 666 insertions(+), 909 deletions(-) delete mode 100644 src/lib/services/analytics.svelte.ts create mode 100644 src/lib/services/metrics/db/client.ts create mode 100644 src/lib/services/metrics/metrics-types.ts create mode 100644 src/lib/services/metrics/supabase-metrics.ts create mode 100644 src/lib/services/metrics/supabase-utils.ts delete mode 100644 src/lib/services/presence.svelte.ts delete mode 100644 src/lib/services/profiles/localStorageProfile.ts delete mode 100644 src/lib/services/profiles/supabase-client.ts delete mode 100644 src/lib/services/profiles/supabaseProfile.svelte.ts delete mode 100644 src/lib/services/utils/search.ts diff --git a/src/lib/services/analytics.svelte.ts b/src/lib/services/analytics.svelte.ts deleted file mode 100644 index 3132b24..0000000 --- a/src/lib/services/analytics.svelte.ts +++ /dev/null @@ -1,56 +0,0 @@ -import type { Course, Lo } from "$lib/services/models/lo-types"; -import type { AnalyticsService, TutorsId } from "./types.svelte"; - -import { - storeStudentCourseLearningObjectInSupabase, - updateLearningRecordsDuration, - updateCalendarDuration, - addOrUpdateStudent, - formatDate -} from "./profiles/supabase-client"; - -export const analyticsService: AnalyticsService = { - loRoute: "", - - learningEvent(course: Course, params: Record, lo: Lo, student: TutorsId) { - try { - if (params.loid) { - const targetRouteParts = lo.route.split("/"); - const trimmedTargetRoute = targetRouteParts.slice(0, 3).join("/"); - this.loRoute = trimmedTargetRoute + "/" + params.loid; - } else { - this.loRoute = lo.route; - } - this.reportPageLoad(course, lo, student); - } catch (error: any) { - console.log(`TutorStore Error: ${error.message}`); - } - }, - - reportPageLoad(course: Course, lo: Lo, student: TutorsId) { - try { - storeStudentCourseLearningObjectInSupabase(course, this.loRoute, lo, student); - } catch (error: any) { - console.log(`TutorStore Error: ${error.message}`); - } - }, - - updatePageCount(course: Course, lo: Lo, student: TutorsId) { - try { - if (student) { - if (lo.route) updateLearningRecordsDuration(course.courseId, student.login, this.loRoute); - updateCalendarDuration(formatDate(new Date()), student.login, course.courseId); - } - } catch (error: any) { - console.error(`TutorStore Error: ${error.message}`); - } - }, - - async updateLogin(courseId: string, session: any) { - try { - await addOrUpdateStudent(session.user); - } catch (error: any) { - console.log(`TutorStore Error: ${error.message}`); - } - } -}; diff --git a/src/lib/services/connect.svelte.ts b/src/lib/services/connect.svelte.ts index b252ffc..6bce3e7 100644 --- a/src/lib/services/connect.svelte.ts +++ b/src/lib/services/connect.svelte.ts @@ -2,101 +2,34 @@ import { signOut } from "@auth/sveltekit/client"; import { signIn } from "@auth/sveltekit/client"; import { rune } from "./utils/runes.svelte"; import { browser } from "$app/environment"; -import type { CourseVisit, TutorsConnectService, TutorsId } from "./types.svelte"; +import type { TutorsConnectService, TutorsId } from "./types.svelte"; import { goto } from "$app/navigation"; -import type { Course } from "./models/lo-types"; -import { localStorageProfile } from "./profiles/localStorageProfile"; -import { supabaseProfile } from "./profiles/supabaseProfile.svelte"; -import { currentCourse, currentLo } from "$lib/runes"; -import { analyticsService } from "./analytics.svelte"; -import { presenceService } from "./presence.svelte"; export const tutorsConnectService: TutorsConnectService = { tutorsId: rune(null), - profile: localStorageProfile, - intervalId: null, async connect(redirectStr: string) { - if (redirectStr === "/") { - redirectStr = "/dashboard"; - } return await signIn("github", { callbackUrl: redirectStr }); }, reconnect(user: TutorsId) { if (user) { this.tutorsId.value = user; - this.profile = supabaseProfile; if (browser) { if (!localStorage.share) { localStorage.share = true; } this.tutorsId.value.share = localStorage.share; - console.log("sharing is in connect: " + this.tutorsId.value.share); if (localStorage.loginCourse) { const courseId = localStorage.loginCourse; localStorage.removeItem("loginCourse"); - goto(`/course/${courseId}`); + goto(`/${courseId}`); } } } }, disconnect(redirectStr: string) { - if (redirectStr === "/") { - redirectStr = "/dashboard"; - } signOut({ callbackUrl: redirectStr }); - }, - - toggleShare() { - if (this.tutorsId.value && browser) { - if (this.tutorsId.value.share === "true") { - localStorage.share = this.tutorsId.value.share = "false"; - } else { - localStorage.share = this.tutorsId.value.share = "true"; - } - } - }, - - courseVisit(course: Course, student: TutorsId) { - this.profile.logCourseVisit(course); - presenceService.startPresenceListener(course.courseId); - if (course.authLevel! > 0 && !this.tutorsId.value?.login) { - localStorage.loginCourse = course.courseId; - goto(`/auth`); - } - }, - - deleteCourseVisit(courseId: string) { - this.profile.deleteCourseVisit(courseId); - }, - - getCourseVisits(): Promise { - return this.profile.getCourseVisits(); - }, - - learningEvent(params: Record): void { - if (currentCourse.value && currentLo.value && this.tutorsId.value) { - analyticsService.learningEvent(currentCourse.value, params, currentLo.value, this.tutorsId.value); - if (this.tutorsId.value.share === "true") { - presenceService.sendLoEvent(currentCourse.value, currentLo.value, this.tutorsId.value); - } - } - }, - - startTimer() { - this.intervalId = setInterval(() => { - if (!document.hidden && currentCourse.value && currentLo.value && this.tutorsId.value) { - analyticsService.updatePageCount(currentCourse.value, currentLo.value, this.tutorsId.value); - } - }, 30 * 1000); - }, - - stopTimer() { - if (this.intervalId) { - clearInterval(this.intervalId); - this.intervalId = null; - } } }; diff --git a/src/lib/services/metrics/db/client.ts b/src/lib/services/metrics/db/client.ts new file mode 100644 index 0000000..a96d76d --- /dev/null +++ b/src/lib/services/metrics/db/client.ts @@ -0,0 +1,4 @@ +import { createClient } from "@supabase/supabase-js"; +import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_SERVICEROLE_KEY } from "$env/static/public"; + +export const db = createClient(PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_SERVICEROLE_KEY); diff --git a/src/lib/services/metrics/metrics-types.ts b/src/lib/services/metrics/metrics-types.ts new file mode 100644 index 0000000..061ad77 --- /dev/null +++ b/src/lib/services/metrics/metrics-types.ts @@ -0,0 +1,208 @@ +export interface CalendarMap { + date: string; + timeActive: number; +} + +export interface LearningObject { + route: string; + loTitle: string; + parentLoTitle: string | undefined; + date: Date; + pageLoads: number; + timeActive: number; + nickname: string; +} + +export interface LearningInteraction { + id?: Date; + loid?: string; + courseid: string; + studentid: string; + date: Date; + pageloads: number; + timeactive: number; + fullname?: string; + avatarurl?: string; +} + +export interface GridConfig { + left: string | number; + right: string | number; + bottom: string | number; + top: string | number; + width: string | number; + height: string | number; + containLabel: boolean; +} + +export type ChartType = { + type: + | "line" + | "bar" + | "pie" + | "scatter" + | "effectScatter" + | "radar" + | "tree" + | "treemap" + | "sunburst" + | "boxplot" + | "candlestick" + | "heatmap" + | "parallel" + | "lines" + | "graph" + | "sankey" + | "funnel" + | "gauge" + | "pictorialBar" + | "themeRiver" + | "calendar" + | "map" + | "custom"; +}; + +export interface BackgroundColor { + image: HTMLImageElement; + repeat: "repeat"; +} + +export type HeatmapShowBoolean = { + show: boolean; +}; + +export interface AxisLabel { + interval: number; + fontSize: number; + margin?: number; // Adjust margin to control spacing + padding?: number[]; +} + +export interface AxisTick { + alignWithLabel: boolean; +} + +export interface axisPointer { + type: "line" | "shadow" | "none"; +} + +export interface XAxis { + type: "value" | "category"; + data: string[] | number[]; + boundaryGap?: number[]; + nameLocation?: "start" | "middle" | "center" | "end"; + splitArea?: HeatmapShowBoolean; + axisLabel?: AxisLabel; + axisTick?: AxisTick; + axisPointer?: axisPointer; + position?: "top" | "bottom"; +} + +export interface YAxis { + type: "value" | "category"; + data: string[]; + splitArea?: HeatmapShowBoolean; + axisLabel?: AxisLabel; +} + +export interface Color { + image: HTMLImageElement; + repeat: string; +} + +export interface ItemStyle { + color: Color; + borderWidth: number; + borderColor: string; +} + +export interface Tooltip { + position: "top" | "bottom" | "left" | "right"; + trigger?: "item"; + formatter?: (param: string | number) => string; + //formatter: string; //"{a}
{b}: {c} mins" +} + +export interface Series { + type: ChartType; + data: number[]; + itemStyle: ItemStyle; +} + +export interface BoxplotChartConfig { + backgroundColor: BackgroundColor; + xAxis: XAxis; + yAxis: YAxis; + series: Series[]; + tooltip: Tooltip; +} + +export type ChartTitle = { + top: string; + left: string; + text: string; +}; + +export interface VisualMap { + min: number; + max: number; + calculable: boolean; + orient: "horizontal" | "vertical"; + left: number | string | "center" | "left" | "right"; + align: "auto" | "left" | "right" | "center"; + bottom: number | string | "center" | "top" | "bottom"; +} + +export interface HeatMapChartConfig { + title: ChartTitle; + tooltip: Tooltip; + backgroundColor: BackgroundColor; + grid: GridConfig; + xAxis: XAxis; + yAxis: YAxis; + visualMap: VisualMap; + series: HeatMapSeriesData[]; +} + +export interface BoxplotTooltip { + trigger: "item"; + formatter: (param: string | number) => string; + //formatter: string; //"{a}
{b}: {c} mins" +} + +export interface HeatmapTooltip { + position: "top" | "bottom" | "left" | "right"; + trigger: "item"; + formatter: string; //"{a}
{b}: {c} mins" +} + +export type NameTypeData = { + name: string; + type?: string; +}; + +export interface HeatMapSeriesData { + top: string | number; + name: string; + type: "heatmap"; + selectedMode: "single" | "multiple"; + data: number[][]; + label: HeatmapShowBoolean; +} + +// export type HeatMapSeriesData = NameTypeData & { +// data: number[][]; +// top: string; +// selectedMode?: string; +// label: { show: boolean }; +// }; + +export type BoxplotData = NameTypeData & { + value: [number, number, number, number, number]; + lowNickname: string; + highNickname: string; +}; + +export type DrilledDownData = NameTypeData & { + value: number; +}; diff --git a/src/lib/services/metrics/supabase-metrics.ts b/src/lib/services/metrics/supabase-metrics.ts new file mode 100644 index 0000000..df488c5 --- /dev/null +++ b/src/lib/services/metrics/supabase-metrics.ts @@ -0,0 +1,77 @@ +import { db } from "$lib/services/metrics/db/client"; +import type { Course, LearningRecord } from "../models/lo-types"; +import type { LearningInteraction } from "./metrics-types"; +import { formatDate } from "./supabase-utils"; + +export async function fetchLearningUserInteractions(course: Course): Promise { + const { data: metrics, error: studentsError } = await db.rpc("get_all_learner_user_records", { + course_base: course.courseId + }); + + if (studentsError) { + console.error(studentsError); + return []; + } + return metrics; +} + +export async function fetchLearningInteractions(course: Course): Promise { + const { data: metrics, error: studentsError } = await db.rpc("get_all_learner_records", { + course_base: course.courseId + }); + + if (studentsError) { + console.error(studentsError); + return []; + } + return metrics; +} + +export async function aggregateTimeActiveByDate( + records: LearningInteraction[] +): Promise>> { + const timeActiveMap = new Map>(); + + // Iterate over the records + records?.forEach((record) => { + const formattedDate = formatDate(record.id); + const studentId = record.studentid; + const timeActive = record.timeactive; + + // If the studentId is not in the map, add it with a new Map + if (!timeActiveMap.has(studentId)) { + timeActiveMap.set(studentId, new Map()); + } + + // Get the dateMap for the studentId + const dateMap = timeActiveMap.get(studentId)!; + dateMap.set(formattedDate, timeActive); + }); + + return timeActiveMap; +} + +export async function decorateLearningRecords(course: Course, metrics: LearningInteraction[]): Promise { + if (metrics && metrics.length > 0 && course.loIndex) { + course.loIndex.forEach((lo) => { + lo.learningRecords = new Map(); + + metrics.forEach((learningInteraction: LearningInteraction) => { + if (learningInteraction.loid === lo.route) { + // Check if a learning record already exists for the user on the same route + if (!lo.learningRecords?.has(learningInteraction.studentid)) { + // Create a new LearningRecord object for the user + const attachLearningRecord: LearningRecord = { + date: learningInteraction.date, + pageLoads: learningInteraction.pageloads, + timeActive: learningInteraction.timeactive + }; + // Associate the learning record with the user ID + lo.learningRecords?.set(learningInteraction.studentid, attachLearningRecord); + } + } + }); + }); + course.loIndex = new Map(course.los.map((lo) => [lo.route, lo])); + } +} diff --git a/src/lib/services/metrics/supabase-utils.ts b/src/lib/services/metrics/supabase-utils.ts new file mode 100644 index 0000000..f3e8df8 --- /dev/null +++ b/src/lib/services/metrics/supabase-utils.ts @@ -0,0 +1,374 @@ +import { db } from "$lib/services/metrics/db/client"; +import type { Course, Lo } from "../models/lo-types"; +import { filterByType } from "../models/lo-utils"; +import type { TutorsId } from "../types.svelte"; +import type { LearningInteraction } from "./metrics-types"; + +export async function getNumOfLearningRecordsIncrements( + fieldName: string, + courseId: string, + studentId: string, + loId: string +) { + if (!courseId || !studentId || !loId) return 0; + + const { data: student, error } = await db.rpc("get_count_learning_records", { + field_name: fieldName, + course_base: courseId, + user_name: studentId, + lo_key: loId + }); + + if (error) { + console.error("Error fetching student interaction:", error); + return 0; + } + return student ? student[0].increment + 1 : 1; +} + +export async function getMedianTimeActivePerDate(courseId: string) { + if (!courseId) return 0; + + const { data, error } = await db.rpc("get_median_time_active_per_date", { + course_base: courseId + }); + + if (error) { + console.error("Error fetching calendar data:", error); + return 0; + } + return data; +} + +export async function updateStudentsStatus(key: string, status: string) { + const { data, error } = await db.from("users").update({ online_status: status }).eq("id", key); + if (error) { + throw error; + } +} + +export async function readValue(key: string): Promise { + const { data, error } = await db.from("users").select().eq("id", key); + return data; +} + +export async function getCalendarDataPerStudent(courseId: string, studentId: string): Promise { + const { data, error } = await db.from("calendar").select().eq("student_id", studentId).eq("course_id", courseId); + return data; +} + +export async function getStudentData(id: string): Promise { + const { data, error } = await db.from("users").select().eq("id", id); + return data; +} + +export async function getCalendarDataForAll(courseId: string): Promise { + const { data, error } = await db.from("calendar").select().eq("courseid", courseId); + if (error) { + throw error; + } + return data; +} + +export async function getCalendarDuration(id: string, studentId: string, courseId: string): Promise { + const { data } = await db + .from("calendar") + .select("timeactive") + .eq("id", id) + .eq("studentid", studentId) + .eq("courseid", courseId) + .maybeSingle(); + return data?.timeactive ? data.timeactive + 1 : 1; +} + +export async function getCalendarCount(id: string, studentId: string, courseId: string): Promise { + const { data } = await db + .from("calendar") + .select("pageloads") + .eq("id", id) + .eq("studentid", studentId) + .eq("courseid", courseId) + .maybeSingle(); + return data?.pageloads ? data.pageloads + 1 : 1; +} + +export async function getDurationTotal(key: string, table: string, id: string): Promise { + const { data } = await db.from(table).select("duration").eq(key, id).single(); + return data?.duration || 1; +} + +export async function insertOrUpdateCalendar(studentId: string, courseId: string) { + if (!studentId || !courseId) return; + const durationPromise = getCalendarDuration(formatDate(new Date()), studentId, courseId); + const countPromise = getCalendarCount(formatDate(new Date()), studentId, courseId); + const [timeActive, pageLoads] = await Promise.all([durationPromise, countPromise]); + await db.from("calendar").upsert( + { + id: formatDate(new Date()), + studentid: studentId, + timeactive: timeActive, + pageloads: pageLoads, + courseid: courseId + }, + { + onConflict: "id, studentid, courseid" + } + ); +} + +export async function insertOrUpdateCourse(course: Course) { + const durationPromise = getDurationTotal("course_id", "course", course.courseId); + const countPromise = updateCount("course_id", "course", course.courseId); + const [duration, count] = await Promise.all([durationPromise, countPromise]); + await db.from("course").upsert( + { + course_id: course.courseId, + title: course.title, + duration: duration, + count: count, + date_last_accessed: new Date().toISOString(), + img: course.img, + icon: course.icon + }, + { + onConflict: "course_id" + } + ); +} + +export async function addOrUpdateStudent(student: TutorsId) { + if (!student) return; + + try { + const { error } = await db.from("users").upsert( + { + github_id: student.login, + avatar_url: student.image, + full_name: student.name, + email: student.email, + date_last_accessed: new Date().toISOString() + }, + { onConflict: "id" } + ); + + if (error) { + console.error("Upsert failed:", error); + throw error; + } + } catch (error) { + console.error("An error occurred in addOrUpdateUserProfile:", error); + throw error; + } +} + +export async function updateLastAccess(key: string, id: string, table: any): Promise { + const { error: updateError } = await db + .from(table) + .update({ date_last_accessed: new Date().toISOString() }) + .eq(key, id); + if (updateError) { + throw updateError; + } +} + +export async function addOrUpdateLo(loid: string, currentLo: Lo, loTitle: string) { + if (!loid) return; + const { error } = await db.from("learningobject").upsert( + { + id: loid, + type: currentLo?.type, + name: loTitle, + date_last_accessed: new Date().toISOString(), + parent: currentLo.parentLo ? currentLo.parentLo.route : null, + child: currentLo.route, + lo_img: currentLo.img, + icon: currentLo.icon, + topic_title: currentLo.parentTopic ? currentLo.parentTopic.title : null, + lab_title: currentLo.type === "lab" ? currentLo.title : null + }, + { + onConflict: "id" + } + ); + + if (error) throw error; +} + +export async function updateLearningRecordsDuration(courseId: string, studentId: string, loId: string) { + const numOfDuration = await getNumOfLearningRecordsIncrements("duration", courseId, studentId, loId); + //const dateLastAccessed = await getDateLastAccessed(courseId, studentId, loId); + + //if (dateLastAccessed) { + //const now = new Date(); + //const lastAccessTime = new Date(dateLastAccessed); + //const minutesSinceLastAccess = (now.getTime() - lastAccessTime.getTime()) / (1000 * 60); + + // if (minutesSinceLastAccess <= 200) { + await db + .from("learning_records") + .update({ duration: numOfDuration }) + .eq("student_id", studentId) + .eq("course_id", courseId) + .eq("lo_id", loId); + //} + //} +} + +export async function updateCount(key: string, table: string, id: string) { + if (!key || !table || !id) return; + await db.rpc("increment_field", { table_name: table, field_name: "count", key: key, row_id: id }); +} + +export async function updateDuration(key: string, table: string, id: string) { + if (!key || !table || !id) return; + await db.rpc("increment_field", { table_name: table, field_name: "duration", key: key, row_id: id }); +} + +export const updateCalendarCount = async (id: string, studentId: string, courseId: string) => { + if (!id || !studentId || !courseId) return; + await db.rpc("increment_calendar", { + field_name: "pageloads", + row_id: id, + student_id_value: studentId, + course_id_value: courseId + }); +}; + +export const updateCalendarDuration = async (id: string, studentId: string, courseId: string) => { + if (!id || !studentId || !courseId) return; + await db.rpc("increment_calendar", { + field_name: "timeactive", + row_id: id, + student_id_value: studentId, + course_id_value: courseId + }); +}; + +export async function storeStudentCourseLearningObjectInSupabase( + course: Course, + loid: string, + lo: Lo, + userDetails: User +) { + // const loTitle = getLoTitle(params) + if (userDetails?.user_metadata?.full_name === "Anon") return; + // await insertOrUpdateCourse(course); + // await addOrUpdateStudent(userDetails); + // await addOrUpdateLo(loid, lo, lo.title); + await handleInteractionData(course.courseId, userDetails?.user_metadata?.user_name, loid, lo); + await insertOrUpdateCalendar(userDetails?.user_metadata?.user_name, course.courseId); +} + +export async function handleInteractionData(courseId: string, studentId: string, loId: string, lo: Lo) { + if (!courseId || !studentId || !loId) return; + await manageStudentCourseLo(courseId, studentId, loId, lo); +} + +export function studentInteractionsUpdates(callback: (arg: any) => void) { + db.channel("schema-db-changes") + .on( + "postgres_changes", + { + event: "*", + schema: "public", + table: "calendar" + }, + (payload) => { + callback(payload.new); + } + ) + .subscribe(); +} + +export async function manageStudentCourseLo(courseId: string, studentId: string, loId: string, lo: Lo) { + const durationPromise = getNumOfLearningRecordsIncrements("duration", courseId, studentId, loId); + const countPromise = getNumOfLearningRecordsIncrements("count", courseId, studentId, loId); + const [duration, count] = await Promise.all([durationPromise, countPromise]); + const { error } = await db.from("learning_records").upsert( + { + course_id: courseId, + student_id: studentId, + lo_id: loId, + date_last_accessed: new Date().toISOString(), + duration: duration, + count: count, + type: lo.type + }, + { + onConflict: "student_id, course_id, lo_id", + ignoreDuplicates: false + } + ); + if (error) throw error; +} + +export function formatDate(date: Date): string { + const d = new Date(date); + const year = d.getFullYear().toString(); + let month = (d.getMonth() + 1).toString(); + let day = d.getDate().toString(); + if (month.length < 2) month = "0" + month; + if (day.length < 2) day = "0" + day; + return [year, month, day].join("-"); +} + +export function getCompositeValues(los: Lo[]) { + const units = filterByType(los, "unit"); + const sides = filterByType(los, "side"); + const topics = filterByType(los, "topic"); + + return [...units, ...sides, ...topics]; +} + +export function getSimpleTypesValues(los: Lo[]) { + const notes = filterByType(los, "note"); + const archives = filterByType(los, "archive"); + const webs = filterByType(los, "web"); + const githubs = filterByType(los, "github"); + const panelnotes = filterByType(los, "panelnote"); + const paneltalks = filterByType(los, "paneltalk"); + const panelVideos = filterByType(los, "panelvideo"); + const talks = filterByType(los, "talk"); + const books = filterByType(los, "book"); + const labs = filterByType(los, "lab"); + const steps = filterByType(los, "step"); + + return [ + ...notes, + ...archives, + ...webs, + ...githubs, + ...panelnotes, + ...paneltalks, + ...panelVideos, + ...talks, + ...books, + ...labs, + ...steps + ]; +} + +export async function getUserNames(usernames: string[]): Promise> { + const userMap = new Map(); + for (const username of usernames) { + const response = await fetch(`https://api.github.com/users/${username}`); + const user = await response.json(); + if (user.name) { + userMap.set(username, user.name); + } + } + return userMap; +} + +export async function getGithubAvatarUrl(usernames: string[]): Promise> { + const imageMap = new Map(); + for (const username of usernames) { + const url = `https://api.github.com/users/${username}`; + const response = await fetch(url); + const data = await response.json(); + if (data.avatar_url) { + imageMap.set(username, data.avatar_url); + } + } + return imageMap; +} diff --git a/src/lib/services/presence.svelte.ts b/src/lib/services/presence.svelte.ts deleted file mode 100644 index e0d32a6..0000000 --- a/src/lib/services/presence.svelte.ts +++ /dev/null @@ -1,110 +0,0 @@ -import PartySocket from "partysocket"; -import { PUBLIC_party_kit_main_room } from "$env/static/public"; -import { rune } from "./utils/runes.svelte"; -import { LoRecord, type LoUser, type PresenceService, type TutorsId } from "./types.svelte"; -import type { Course, Lo } from "./models/lo-types"; -import { tutorsConnectService } from "./connect.svelte"; - -const partyKitServer = PUBLIC_party_kit_main_room; - -export const presenceService: PresenceService = { - partyKitAll: new PartySocket({ - host: partyKitServer, - room: "tutors-all-course-access" - }), - partyKitCourse: {}, - listeningTo: "", - studentsOnline: rune([]), - studentEventMap: new Map(), - - studentListener(event: any) { - const nextCourseEvent = JSON.parse(event.data); - if ( - nextCourseEvent.courseId === this.listeningTo && - nextCourseEvent.user.id !== tutorsConnectService.tutorsId.value.login - ) { - const studentEvent = this.studentEventMap.get(nextCourseEvent.user.id); - if (!studentEvent) { - const latestLo = new LoRecord(nextCourseEvent); - this.studentsOnline.value.push(latestLo); - this.studentEventMap.set(nextCourseEvent.user.id, latestLo); - } else { - refreshLoRecord(studentEvent, nextCourseEvent); - } - } - }, - - startPresenceListener(courseId: string) { - this.listeningTo = courseId; - this.partyKitCourse = new PartySocket({ - host: partyKitServer, - room: courseId - }); - this.partyKitCourse.addEventListener("message", this.studentListener.bind(this)); - }, - - sendLoEvent(course: Course, lo: Lo, student: TutorsId) { - const loRecord: LoRecord = { - courseId: course.courseId, - courseUrl: course.courseUrl, - img: lo.img, - title: lo.title, - courseTitle: course.title, - loRoute: lo.route, - user: getUser(student), - type: lo.type, - isPrivate: (course.properties?.private as unknown as number) === 1 - }; - if (lo.icon) { - loRecord.icon = lo.icon; - } - const loRecordJson = JSON.stringify(loRecord); - this.partyKitAll.send(loRecordJson); - this.partyKitCourse.room = course.courseId; - if (this.listeningTo !== "") { - this.partyKitCourse.send(loRecordJson); - } - } -}; - -export function refreshLoRecord(loEvent: LoRecord, nextLoEvent: LoRecord) { - loEvent.loRoute = `https://tutors.dev${nextLoEvent.loRoute}`; - loEvent.title = nextLoEvent.title; - loEvent.type = nextLoEvent.type; - if (nextLoEvent.icon) { - loEvent.icon = nextLoEvent.icon; - loEvent.img = undefined; - } else { - loEvent.img = nextLoEvent.img; - loEvent.icon = undefined; - } -} - -function getUser(tutorsId: TutorsId): LoUser { - const user: LoUser = { - fullName: "Anon", - avatar: "https://tutors.dev/logo.svg", - id: getTutorsTimeId() - }; - if (tutorsId.share) { - user.fullName = tutorsId.name; - user.avatar = tutorsId.image; - user.id = tutorsId.login; - } - return user; -} - -function generateTutorsTimeId() { - return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => { - const r = (Math.random() * 16) | 0; - const v = c === "x" ? r : (r & 0x3) | 0x8; - return v.toString(16); - }); -} - -function getTutorsTimeId() { - if (!window.localStorage.tutorsTimeId) { - window.localStorage.tutorsTimeId = generateTutorsTimeId(); - } - return window.localStorage.tutorsTimeId; -} diff --git a/src/lib/services/profiles/localStorageProfile.ts b/src/lib/services/profiles/localStorageProfile.ts deleted file mode 100644 index b78447d..0000000 --- a/src/lib/services/profiles/localStorageProfile.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { browser } from "$app/environment"; -import type { Course, IconType } from "../models/lo-types"; -import type { CourseVisit, ProfileStore } from "../types.svelte"; - -export const localStorageProfile: ProfileStore = { - courseVisits: [] as CourseVisit[], - - reload(): void {}, - save(): void {}, - - logCourseVisit(course: Course) { - if (browser && localStorage.courseVisits) { - this.courseVisits = JSON.parse(localStorage.courseVisits); - } - const visit = this.courseVisits.find((c) => c.id === course.courseId); - if (visit) { - visit.visits++; - visit.lastVisit = new Date(); - } else { - const courseVisit: CourseVisit = { - id: course.courseId, - title: course.title, - lastVisit: new Date(), - credits: course.properties.credits, - visits: 1 - }; - if (course.properties.icon) { - courseVisit.icon = course.properties.icon as unknown as IconType; - } else { - courseVisit.image = course.img; - } - this.courseVisits.unshift(courseVisit); - } - if (browser) localStorage.courseVisits = JSON.stringify(this.courseVisits); - }, - - deleteCourseVisit(courseId: string) { - this.courseVisits = this.courseVisits.filter((c) => c.id !== courseId); - if (browser) localStorage.courseVisits = JSON.stringify(this.courseVisits); - }, - - getCourseVisits(): Promise { - if (browser && localStorage.courseVisits) { - this.courseVisits = JSON.parse(localStorage.courseVisits); - } - return this.courseVisits; - } -}; diff --git a/src/lib/services/profiles/supabase-client.ts b/src/lib/services/profiles/supabase-client.ts deleted file mode 100644 index 53ffd08..0000000 --- a/src/lib/services/profiles/supabase-client.ts +++ /dev/null @@ -1,172 +0,0 @@ -import { createClient, type SupabaseClient } from "@supabase/supabase-js"; -import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_SERVICEROLE_KEY } from "$env/static/public"; - -export const supabase: SupabaseClient = createClient(PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_SERVICEROLE_KEY); - -export async function getNumOfLearningRecordsIncrements( - fieldName: string, - courseId: string, - studentId: string, - loId: string -) { - if (!courseId || !studentId || !loId) return 0; - - const { data: student, error } = await supabase.rpc("get_count_learning_records", { - field_name: fieldName, - course_base: courseId, - user_name: studentId, - lo_key: loId - }); - - if (error) { - console.error("Error fetching student interaction:", error); - return 0; - } - return student ? student[0].increment + 1 : 1; -} - -export async function manageStudentCourseLo(courseId: string, studentId: string, loId: string, lo: Lo) { - const durationPromise = getNumOfLearningRecordsIncrements("duration", courseId, studentId, loId); - const countPromise = getNumOfLearningRecordsIncrements("count", courseId, studentId, loId); - const [duration, count] = await Promise.all([durationPromise, countPromise]); - const { error } = await supabase.from("learning_records").upsert( - { - course_id: courseId, - student_id: studentId, - lo_id: loId, - date_last_accessed: new Date().toISOString(), - duration: duration, - count: count, - type: lo.type - }, - { - onConflict: "student_id, course_id, lo_id", - ignoreDuplicates: false - } - ); - if (error) { - console.log(error); - throw error; - } -} - -export function formatDate(date: Date): string { - const d = new Date(date); - const year = d.getFullYear().toString(); - let month = (d.getMonth() + 1).toString(); - let day = d.getDate().toString(); - if (month.length < 2) month = "0" + month; - if (day.length < 2) day = "0" + day; - return [year, month, day].join("-"); -} - -export const updateCalendarDuration = async (id: string, studentId: string, courseId: string) => { - if (!id || !studentId || !courseId) return; - await supabase.rpc("increment_calendar", { - field_name: "timeactive", - row_id: id, - student_id_value: studentId, - course_id_value: courseId - }); -}; - -export async function getCalendarDuration(id: string, studentId: string, courseId: string): Promise { - const { data } = await supabase - .from("calendar") - .select("timeactive") - .eq("id", id) - .eq("studentid", studentId) - .eq("courseid", courseId) - .maybeSingle(); - return data?.timeactive ? data.timeactive + 1 : 1; -} - -export async function getCalendarCount(id: string, studentId: string, courseId: string): Promise { - const { data } = await supabase - .from("calendar") - .select("pageloads") - .eq("id", id) - .eq("studentid", studentId) - .eq("courseid", courseId) - .maybeSingle(); - return data?.pageloads ? data.pageloads + 1 : 1; -} - -export async function getDurationTotal(key: string, table: string, id: string): Promise { - const { data } = await supabase.from(table).select("duration").eq(key, id).single(); - return data?.duration || 1; -} - -export async function insertOrUpdateCalendar(studentId: string, courseId: string) { - if (!studentId || !courseId) return; - const durationPromise = getCalendarDuration(formatDate(new Date()), studentId, courseId); - const countPromise = getCalendarCount(formatDate(new Date()), studentId, courseId); - const [timeActive, pageLoads] = await Promise.all([durationPromise, countPromise]); - await supabase.from("calendar").upsert( - { - id: formatDate(new Date()), - studentid: studentId, - timeactive: timeActive, - pageloads: pageLoads, - courseid: courseId - }, - { - onConflict: "id, studentid, courseid" - } - ); -} - -export async function handleInteractionData(courseId: string, studentId: string, loId: string, lo: Lo) { - if (!courseId || !studentId || !loId) return; - await manageStudentCourseLo(courseId, studentId, loId, lo); -} - -export async function storeStudentCourseLearningObjectInSupabase( - course: Course, - loid: string, - lo: Lo, - student: TutorsId -) { - // const loTitle = getLoTitle(params) - //if (userDetails?.user_metadata?.full_name === "Anon") return; - // await insertOrUpdateCourse(course); - // await addOrUpdateStudent(userDetails); - // await addOrUpdateLo(loid, lo, lo.title); - await handleInteractionData(course.courseId, student.login, loid, lo); - await insertOrUpdateCalendar(student.login, course.courseId); -} - -export async function updateLearningRecordsDuration(courseId: string, studentId: string, loId: string) { - const numOfDuration = await getNumOfLearningRecordsIncrements("duration", courseId, studentId, loId); - await supabase - .from("learning_records") - .update({ duration: numOfDuration }) - .eq("student_id", studentId) - .eq("course_id", courseId) - .eq("lo_id", loId); -} - -export async function addOrUpdateStudent(student: TutorsId) { - if (!student) return; - - try { - const { error } = await supabase.from("users").upsert( - { - github_id: student.login, - avatar_url: student.image, - full_name: student.name, - email: student.email, - date_last_accessed: new Date().toISOString() - }, - { onConflict: "id" } - ); - - if (error) { - console.error("Upsert failed:", error); - throw error; - } - } catch (error) { - console.error("An error occurred in addOrUpdateUserProfile:", error); - throw error; - } -} diff --git a/src/lib/services/profiles/supabaseProfile.svelte.ts b/src/lib/services/profiles/supabaseProfile.svelte.ts deleted file mode 100644 index 1474579..0000000 --- a/src/lib/services/profiles/supabaseProfile.svelte.ts +++ /dev/null @@ -1,67 +0,0 @@ -import type { Course, IconType } from "../models/lo-types"; -import { tutorsConnectService } from "$lib/services/connect.svelte.js"; -import type { CourseVisit, ProfileStore } from "../types.svelte"; -import { supabase } from "./supabase-client"; - -export const supabaseProfile: ProfileStore = { - courseVisits: [] as CourseVisit[], - - async reload() { - if (tutorsConnectService.tutorsId.value?.login) { - const { data: profile } = await supabase - .from("tutors-connect-profiles") - .select("profile") - .eq("tutorId", tutorsConnectService.tutorsId.value?.login); - if (profile.length > 0) { - this.courseVisits = profile[0].profile as unknown as CourseVisit[]; - } - } - }, - - async save() { - const id = tutorsConnectService.tutorsId.value?.login; - if (id) { - const { error } = await supabase - .from("tutors-connect-profiles") - .upsert({ tutorId: tutorsConnectService.tutorsId.value?.login, profile: this.courseVisits }); - if (error) { - console.log(error); - } - } - }, - - async logCourseVisit(course: Course) { - await this.reload(); - const visit = this.courseVisits.find((c) => c.id === course.courseId); - if (visit) { - visit.visits++; - visit.lastVisit = new Date(); - } else { - const courseVisit: CourseVisit = { - id: course.courseId, - title: course.title, - lastVisit: new Date(), - credits: course.properties.credits, - visits: 1 - }; - if (course.properties.icon) { - courseVisit.icon = course.properties.icon as unknown as IconType; - } else { - courseVisit.image = course.img; - } - this.courseVisits.unshift(courseVisit); - } - await this.save(); - }, - - async deleteCourseVisit(courseId: string) { - await this.reload(); - this.courseVisits = this.courseVisits.filter((c) => c.id !== courseId); - await this.save(); - }, - - async getCourseVisits(): Promise { - await this.reload(); - return this.courseVisits; - } -}; diff --git a/src/lib/services/types.svelte.ts b/src/lib/services/types.svelte.ts index a68ac32..e401cc7 100644 --- a/src/lib/services/types.svelte.ts +++ b/src/lib/services/types.svelte.ts @@ -1,5 +1,5 @@ import type { LiveLab } from "./models/live-lab"; -import type { Course, IconType, Lo } from "./models/lo-types"; +import type { Course, Lo } from "./models/lo-types"; export type TutorsId = { name: string; @@ -9,39 +9,6 @@ export type TutorsId = { share: string; }; -export type CourseVisit = { - id: string; - title: string; - image?: string; - icon?: IconType; - lastVisit: Date; - credits: string; - visits: number; -}; - -export interface LoUser { - fullName: string; - avatar: string; - id: string; -} - -export class LoRecord { - courseId: string = $state(""); - courseUrl: string = $state(""); - courseTitle: string = $state(""); - loRoute: string = $state(""); - title: string = $state(""); - img?: string = $state(""); - icon?: IconType = $state(); - isPrivate: boolean = $state(false); - user?: LoUser = $state(); - type: string = $state(""); - - constructor(data: any) { - Object.assign(this, data); - } -} - export interface CourseService { courses: Map; labs: Map; @@ -55,53 +22,11 @@ export interface CourseService { readLo(courseId: string, loId: string, fetchFunction: typeof fetch): Promise; } -export interface ProfileStore { - courseVisits: CourseVisit[]; - - reload(): void; - save(): void; - logCourseVisit(course: Course): void; - deleteCourseVisit(courseId: string): void; - getCourseVisits(): Promise; -} - export interface TutorsConnectService { // eslint-disable-next-line @typescript-eslint/no-explicit-any tutorsId: any; - profile: ProfileStore; - intervalId: any; connect(redirectStr: string): void; reconnect(user: TutorsId): void; disconnect(redirectStr: string): void; - toggleShare(): void; - - courseVisit(course: Course, user: TutorsId): void; - deleteCourseVisit(courseId: string): void; - getCourseVisits(): void; - - learningEvent(params: Record): void; - startTimer(): void; - stopTimer(): void; -} - -export interface AnalyticsService { - loRoute: string; - - learningEvent(course: Course, params: Record, lo: Lo, student: TutorsId): void; - reportPageLoad(course: Course, lo: Lo, student: TutorsId): void; - updatePageCount(course: Course, lo: Lo, student: TutorsId): void; - updateLogin(courseId: string, session: any): void; -} - -export interface PresenceService { - studentsOnline: any; - partyKitAll: any; - partyKitCourse: any; - studentEventMap: Map; - listeningTo: string; - - studentListener(event: any): void; - sendLoEvent(course: Course, lo: Lo, student: TutorsId): void; - startPresenceListener(courseId: string): void; } diff --git a/src/lib/services/utils/search.ts b/src/lib/services/utils/search.ts deleted file mode 100644 index 7155a22..0000000 --- a/src/lib/services/utils/search.ts +++ /dev/null @@ -1,311 +0,0 @@ -import type { Lo } from "$lib/services/models/lo-types"; -import { removeLeadingHashes } from "$lib/services/models/lo-utils"; - -const maxNumberHits = 100; -const fenceTick = "```"; -const fenceTilde = "~~~"; - -type ContentType = { - content: string; - style: string; //fenced, unfenced - language: string; - fence: string; // ``` or ~~~ -}; - -export type ResultType = { - fenced: boolean; - language: string; - contentMd: string; - lab: Lo; - html: string; - title: string; - link: string; -}; -/** - * Searches an array of nested Lo arrays for presence of searchTerm. - * On 31.10.19 resolved the issue whereby only first instance of a searchTerm on any - * web page returned. Now all instances of the search term identified. - * - * Fenced code: the following approach used to determing if content resides with a fence pair. - * An array of the indices of the first character of each fence is created. Opening fence - * indices are even. Fencing may comprise tildes or ticks. A fence pair must comprise either tildes - * or ticks. Mixing types in the fence pair is assumed not to be present. A page may contain either - * tilde or tick fence pairs or both. - * - * Language: The fenced language is determined by checking for its presence at the end of - * the opening fence pair. - * - * @param los The nested arrays of Lo objects. - * @param searchTerm The term whose presence is searched for. - * @return string array of search term hits truncated to maxNumberHits length. - */ -export function searchHits(los: Lo[], searchTerm: string): ResultType[] { - //const flatLos = flattenNestedLosArrays(los); - //let result : string[] = []; - const results: ResultType[] = []; - los.forEach((lo) => { - if (lo.contentMd) { - const text = lo.contentMd; - const contents: ContentType[] = arrayLinesSearchTermHits(text, searchTerm); - for (const content of contents) { - const result = { - fenced: content.style !== "unfenced", - language: content.language, - contentMd: content.content, - lab: lo, - title: `${lo.parentLo?.title}/${removeLeadingHashes(lo.title)}`, - link: lo?.route, - html: "" - }; - result.link = result.link.substring(1); - results.push(result); - } - } - }); - return results.slice(0, maxNumberHits); -} -/** - * Returns an array of substrings of content comprising lines on which searchTerm located. - * - * Example: content = 'syeasy and hard synchronized syncsy'; - * searchTerm = 'sy'; - * arStrings: ["syeasy", "syeasy and", "ard synchr", "zed syncsy", "syncsy"] - * - * @param content The string within which one or more searchTerm substrings exist. - * @param searchTerm The substring, at lease one instance of which exists. - * @return string array of lines of content containing search term. - */ -function arrayLinesSearchTermHits(content: string, searchTerm: string): ContentType[] { - const arStrings: ContentType[] = []; - const arIndx = indicesOf(content, searchTerm); // indices of searchTerm occurences - for (let i = 0; i < arIndx.length; i += 1) { - const str = currentline(searchTerm, arIndx[i], content); - const style = isFenced(content, arIndx[i]); - let language = ""; - if (style === "fenced") { - language = getLanguage(content, arIndx[i]); - } - const fence = getFenceType(content, arIndx[i]); - arStrings.push({ content: str, style, fence, language }); - } - return arStrings; -} - -/** - * Extracts from the web page the language for the current fenced item. - * Language types may vary with a page. - * @param content The webpage text - * @param indexSearchTerm the index of the first character of the search term. - * @returns the language for the current fenced section. - */ -function getLanguage(content: string, indexSearchTerm: number): string { - const ar = arStartFenceIndices(content); - const indexFence = findNearestPreviousIndex(ar, indexSearchTerm); - let language = ""; - let index = indexFence[1] + 3; // fence comprises 3 tickks or tildes. - while (content.charAt(index) !== "\n") { - language += content.charAt(index); - index += 1; - } - return language; -} - -/** - * - * @param content the fence type may comprise tildes or ticks. - * @param indexSearchTerm - * @returns fence type: string comprising 3 tildes or 3 ticks. - */ -function getFenceType(content: string, indexSearchTerm: number): string { - const ar = arStartFenceIndices(content); - const indexFence = findNearestPreviousIndex(ar, indexSearchTerm); - if (content.charAt(indexFence[1]) === "~") { - return "~~~"; - } - return "```"; -} - -/* - * Extract and return the path from parameter 'astring'. - * astring format: - Simple - * ... - * - * Modify 'astring' by inserting / following #. - * For example, replace this: "#lab/... with: "#/lab..." - * - * @param astring String to parse for path (route). - * @return The path - */ -export function extractPath(astring: string) { - const regex = /#/; - astring = astring.replace(regex, "#/"); - const start = astring.indexOf("#"); - const end = astring.indexOf(">") - 1; // exclude preceeding double quote. - return astring.substring(start, end); -} - -/** - * Validate a string: is valid if it is not undefined and - * does not comprise only whitespace else it is invalid. - * @param str A string being validated. - * @returns true if valid else false. - */ - -function onlySpaces(str: string) { - return str.trim().length === 0; -} -export function isValid(str: string) { - // return str != undefined && /\S/.test(str) == true; - return !onlySpaces(str); -} - -/** - * indicesOf: This method uses the Javascript indexOf method. - * indexOf is invoked recursively to locate the start indices of an optionally recurring substring within a string. - * The method is valid even if the specified substring is empty. - * Since indexOf is case sensitive then it follows that indicesOf is case sensitive. - * Example: str = syeasy and hard synchronized syncsy' - * Substring: 'sy' - * Output: [0, 4, 16, 29, 33] - * @author: jfitzgerald - * @param str The target or specified string within which the first index of each substring is sought. - * @param substr The substring(s) whose indices are being determined. - * @return An array of the indices of positions of first character of each substring. If no mon-empty substring is - * present then empty array returned. - */ -function indicesOf(str: string, substr: string): number[] { - const arIndx: number[] = []; - function indicesOf(str: string, substr: string, arIndx: number[]): number[] { - let n = str.indexOf(substr); - if (n != -1) { - const prev_n = n; - if (arIndx.length) { - n += arIndx[arIndx.length - 1] + substr.length; - } - arIndx.push(n); - indicesOf(str.slice(prev_n + substr.length), substr, arIndx); - } else { - return; - } - } - indicesOf(str, substr, arIndx); - return arIndx; -} - -/** - * Generate an array of indices of first character in each opening fence. - * @param content - * @returns array of indices - */ -function arStartFenceIndices(content: string): number[] { - const ar = arrayStartFenceIndices(content, fenceTick).concat(arrayStartFenceIndices(content, fenceTilde)); - return numericSort(ar); -} - -/** - * Utility function to sort an array of integers in ascending order. - * @param ar - * @returns - */ -function numericSort(ar: number[]) { - return ar.sort((n1, n2) => n1 - n2); -} - -/** - * - * Determines if a searchTerm resides within fenced section of page. - * The fence may be defined as ticks or tildes. - * @param content The string containing the searchTerm. - * @param searchTermIndex The index of the searchTerm. - * @returns A string: fenced or unfenced. - */ -function isFenced(content: string, searchTermIndex: number): string { - //let ar : number[] = arrayStartFenceIndices(content, fenceTick).concat(arrayStartFenceIndices(content, fenceTilde)); - const ar = arStartFenceIndices(content); - // If length array of start fence indices is zero this means searchTerm is in html range. - // That is, no fenced content. - if (ar.length == 0) { - return "unfenced"; - } - const prevSmaller = findNearestPreviousIndex(ar, searchTermIndex); - if (isEven(prevSmaller[0])) { - return "fenced"; - } else { - return "unfenced"; - } -} - -/** - * Generates an array of indices of the first character of opening fence. - * @param content - * @param fenceType ticks or tildes - * @returns array of indices - */ -function arrayStartFenceIndices(content: string, fenceType: string): number[] { - const ar = indicesOf(content, fenceType); - return ar; -} - -/** - * Utility function: determines of integer is even. - * @param aninteger - * @returns true if aninteger even else false. - */ -function isEven(aninteger: number): boolean { - return aninteger % 2 == 0; -} - -/** - * The current line is bounded by \n characters, excepting conditions which are allowd for. - * @param searchTerm - * @param indexSearchTerm - * @param content The current page. - * @returns A string representing the line in which the searchTerm is located. - */ -function currentline(searchTerm: string, indexSearchTerm: number, content: string): string { - const arrayIndicesSeparators = indicesOf(content, separator()); - const indexStartLine = findNearestPreviousIndex(arrayIndicesSeparators, indexSearchTerm); - const indexEndLine = findNearestNextIndex(arrayIndicesSeparators, indexSearchTerm, content.length); - const currentLine = content.substring(indexStartLine[1], indexEndLine[1]); - return currentLine; -} -/** - * Given an array of indices and a current index obtain the nearest smaller index to the current index. - * @param indices An array of indices - * @param currIndex The index of the searchTerm - * @returns An array of tuples, each tuple containing the array index and the index immediately previous to current index. - */ -function findNearestPreviousIndex(indices: number[], currIndex: number): [number, number] { - for (let i = indices.length - 1; i >= 0; i--) { - if (indices[i] < currIndex) { - return [i, indices[i]]; - } - } - return [-1, -1]; -} -/** - * - * @param indices An array of indices of separators (such as \n). - * @param currIndex The index of the searchTerm. - * @param contentLen The index of the last character of the current page. - * @returns An array of tuples, each tuple containing the array index and the index immediately following the current index. - * - */ -function findNearestNextIndex(indices: number[], currIndex: number, contentLen: number): [number, number] { - for (let i = 0; i < indices.length; i += 1) { - if (currIndex > indices[indices.length - 1]) { - return [i, contentLen]; // Edge condition where no eof character exists. - } else if (indices[i] > currIndex) { - return [i, indices[i]]; - } - } - return [-1, -1]; //Something's up doc and it isn't good. -} - -/** - * - * @returns new line character as string. - */ -function separator(): string { - return "\n"; -} From 94dfe5ca807706e687be4dba42ff43c79b8afd88 Mon Sep 17 00:00:00 2001 From: Eamonn de Leastar Date: Thu, 7 Nov 2024 16:30:17 +0000 Subject: [PATCH 05/11] metrics ux --- src/routes/+layout.svelte | 3 +- src/routes/+page.svelte | 6 + src/routes/[courseid]/+layout.svelte | 14 ++ src/routes/[courseid]/+page.svelte | 209 +++++++++++++++++++++++++++ src/routes/[courseid]/+page.ts | 46 ++++++ 5 files changed, 277 insertions(+), 1 deletion(-) create mode 100644 src/routes/+page.svelte create mode 100644 src/routes/[courseid]/+layout.svelte create mode 100644 src/routes/[courseid]/+page.svelte create mode 100644 src/routes/[courseid]/+page.ts diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 85cfa70..da84b2b 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -4,6 +4,7 @@ import { computePosition, autoUpdate, flip, shift, offset, arrow } from "@floating-ui/dom"; import { initializeStores, storePopup } from "@skeletonlabs/skeleton"; import type { PageData } from "./$types"; + import TutorsShell from "$lib/ui/app-shells/TutorsShell.svelte"; interface Props { data: PageData; @@ -14,7 +15,7 @@ initializeStores(); storePopup.set({ computePosition, autoUpdate, flip, shift, offset, arrow }); - if (data.user) { + if (data?.user) { tutorsConnectService.reconnect(data.user); } diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte new file mode 100644 index 0000000..69492bb --- /dev/null +++ b/src/routes/+page.svelte @@ -0,0 +1,6 @@ + + + diff --git a/src/routes/[courseid]/+layout.svelte b/src/routes/[courseid]/+layout.svelte new file mode 100644 index 0000000..2d94bfd --- /dev/null +++ b/src/routes/[courseid]/+layout.svelte @@ -0,0 +1,14 @@ + + + + {@render children()} + diff --git a/src/routes/[courseid]/+page.svelte b/src/routes/[courseid]/+page.svelte new file mode 100644 index 0000000..d923277 --- /dev/null +++ b/src/routes/[courseid]/+page.svelte @@ -0,0 +1,209 @@ + + +
+ +
+ + + + {#if instructorMode} + + + + + + + + {:else} + + + + {/if} + {#if instructorMode} + + + + + + {:else} + + + + {/if} +
+ + +
+ {#if $storeTab === "Calendar"} + {#if instructorMode} + + {:else} + + {/if} + {/if} + {#if instructorMode} + {#if $storeTab === "InstructorTopicView"} + + {:else if $storeTab === "InstructorTopicViewPieChart"} + + {:else if $storeTab === "InstructorTopicViewBoxPlot"} + + {:else if $storeTab === "InstructorLabView"} + + {:else if $storeTab === "InstructorLabViewBoxPlot"} + + {/if} + {:else if $storeTab === "TopicView"} + + {:else if $storeTab === "TopicViewPieChart"} + + {:else if $storeTab === "LabView"} + + {:else if $storeTab === "LabViewPieChart"} + + {/if} +
+
+ + diff --git a/src/routes/[courseid]/+page.ts b/src/routes/[courseid]/+page.ts new file mode 100644 index 0000000..c591a44 --- /dev/null +++ b/src/routes/[courseid]/+page.ts @@ -0,0 +1,46 @@ +import type { PageLoad } from "./$types"; +import type { Course } from "$lib/services/models/lo-types"; +import { + aggregateTimeActiveByDate, + decorateLearningRecords, + fetchLearningUserInteractions +} from "$lib/services/metrics/supabase-metrics"; + +import { getCalendarDataForAll, getMedianTimeActivePerDate } from "$lib/services/metrics/supabase-utils"; +import { courseService } from "$lib/services/course.svelte"; +import type { LearningInteraction } from "$lib/services/metrics/metrics-types"; +import { goto } from "$app/navigation"; +import { tutorsConnectService } from "$lib/services/connect.svelte"; + +export const ssr = false; + +export const load: PageLoad = async ({ params, fetch }) => { + const course: Course = await courseService.readCourse(params.courseid, fetch); + + if (!tutorsConnectService.tutorsId.value) { + localStorage.loginCourse = course.courseId; + goto(`/auth`); + } + + const userMetrics: LearningInteraction[] = await fetchLearningUserInteractions(course); + const userIds: string[] = [...new Set(userMetrics.map((m: LearningInteraction) => m.studentid))] as string[]; + const userNamesAvatars: Map = new Map( + userMetrics.map((m: LearningInteraction) => [m.studentid, [m.fullname, m.avatarurl]]) + ); + const records: LearningInteraction[] = await getCalendarDataForAll(course.courseId); + const aggregatedData = await aggregateTimeActiveByDate(records); + const medianCalendarTime = await getMedianTimeActivePerDate(course.courseId); + const calendarIds: string[] = [ + ...new Set(Array.from(aggregatedData.entries()).map(([studentId]) => studentId)) + ] as string[]; + await decorateLearningRecords(course, userMetrics); + + return { + course: course, + userIds: userIds, + userNamesAvatars: userNamesAvatars, + calendarIds: calendarIds, // Because of mismatch data in the dbs (reintroduce calendar) + timeActiveMap: aggregatedData, + medianTime: medianCalendarTime + }; +}; From c1c9a0648322c7b950fa73dae198d3d0927cf0e2 Mon Sep 17 00:00:00 2001 From: Eamonn de Leastar Date: Thu, 7 Nov 2024 16:30:39 +0000 Subject: [PATCH 06/11] remove unused items --- package-lock.json | 1086 +++++++++++++++++++++------------------------ package.json | 6 +- 2 files changed, 516 insertions(+), 576 deletions(-) diff --git a/package-lock.json b/package-lock.json index cd06561..d496708 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,8 @@ "@types/eslint": "^9.6.0", "@types/node": "^22.9.0", "autoprefixer": "^10.4.20", + "d3": "^7.9.0", + "echarts": "^5.5.1", "eslint": "^9.7.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-svelte": "^2.36.0", @@ -40,8 +42,6 @@ "markdown-it-sub": "^2.0.0", "markdown-it-sup": "^2.0.0", "markdown-it-table-of-contents": "^0.8.0", - "partysocket": "^1.0.2", - "pdfjs-dist": "^4.8.69", "prettier": "^3.3.2", "prettier-plugin-svelte": "^3.2.6", "prettier-plugin-tailwindcss": "^0.6.5", @@ -1928,28 +1928,6 @@ "dev": true, "license": "MIT" }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "optional": true - }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -1963,19 +1941,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -2033,32 +1998,6 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "optional": true, - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -2100,23 +2039,6 @@ ], "license": "CC-BY-4.0" }, - "node_modules/canvas": { - "version": "3.0.0-rc2", - "resolved": "https://registry.npmjs.org/canvas/-/canvas-3.0.0-rc2.tgz", - "integrity": "sha512-esx4bYDznnqgRX4G8kaEaf0W3q8xIc51WpmrIitDzmcoEgwnv9wSKdzT6UxWZ4wkVu5+ileofppX0TpyviJRdQ==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "dependencies": { - "node-addon-api": "^7.0.0", - "prebuild-install": "^7.1.1", - "simple-get": "^3.0.3" - }, - "engines": { - "node": "^18.12.0 || >= 20.9.0" - } - }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -2150,14 +2072,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true, - "license": "ISC", - "optional": true - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -2233,47 +2147,464 @@ "node": ">=4" } }, - "node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "node_modules/d3": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "ms": "^2.1.3" + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" }, "engines": { - "node": ">=6.0" + "node": ">=12" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "dev": true, + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" } }, - "node_modules/decompress-response": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", - "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "dev": true, + "license": "ISC", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", "dev": true, "license": "MIT", - "optional": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "dev": true, + "license": "ISC", "dependencies": { - "mimic-response": "^2.0.0" + "d3-dsv": "1 - 3" }, "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dev": true, "license": "MIT", - "optional": true, + "dependencies": { + "ms": "^2.1.3" + }, "engines": { - "node": ">=4.0.0" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, "node_modules/deep-is": { @@ -2293,15 +2624,14 @@ "node": ">=0.10.0" } }, - "node_modules/detect-libc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", - "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "node_modules/delaunator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", + "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", "dev": true, - "license": "Apache-2.0", - "optional": true, - "engines": { - "node": ">=8" + "license": "ISC", + "dependencies": { + "robust-predicates": "^3.0.2" } }, "node_modules/devalue": { @@ -2332,6 +2662,17 @@ "dev": true, "license": "MIT" }, + "node_modules/echarts": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.5.1.tgz", + "integrity": "sha512-Fce8upazaAXUVUVsjgV6mBnGuqgO+JNDlcgF79Dksy4+wgGpQB2lmYoO4TSweFg/mZITdpGHomw/cNBJZj1icA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "2.3.0", + "zrender": "5.6.0" + } + }, "node_modules/electron-to-chromium": { "version": "1.5.51", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.51.tgz", @@ -2346,17 +2687,6 @@ "dev": true, "license": "MIT" }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "once": "^1.4.0" - } - }, "node_modules/entities": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", @@ -2683,30 +3013,6 @@ "node": ">=0.10.0" } }, - "node_modules/event-target-shim": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-6.0.2.tgz", - "integrity": "sha512-8q3LsZjRezbFZ2PN+uP+Q7pnHUMmAOziU2vA2OwoFaKIXxlxl38IylhSSgUorWu/rf4er67w0ikBqjBFk/pomA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "dev": true, - "license": "(MIT OR WTFPL)", - "optional": true, - "engines": { - "node": ">=6" - } - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -2885,14 +3191,6 @@ "url": "https://github.com/sponsors/rawify" } }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true, - "license": "MIT", - "optional": true - }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -2918,14 +3216,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", - "dev": true, - "license": "MIT", - "optional": true - }, "node_modules/glob": { "version": "10.4.5", "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", @@ -3053,27 +3343,18 @@ "node": ">=12.0.0" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause", - "optional": true + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } }, "node_modules/ignore": { "version": "5.3.2", @@ -3123,21 +3404,15 @@ "node": ">=0.8.19" } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, - "license": "ISC", - "optional": true - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", "dev": true, "license": "ISC", - "optional": true + "engines": { + "node": ">=12" + } }, "node_modules/is-binary-path": { "version": "2.1.0", @@ -3579,20 +3854,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/mimic-response": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", - "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/mini-svg-data-uri": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", @@ -3616,17 +3877,6 @@ "node": "*" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "license": "MIT", - "optional": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/minipass": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", @@ -3637,14 +3887,6 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "dev": true, - "license": "MIT", - "optional": true - }, "node_modules/mri": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", @@ -3703,14 +3945,6 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/napi-build-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", - "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", - "dev": true, - "license": "MIT", - "optional": true - }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -3718,28 +3952,6 @@ "dev": true, "license": "MIT" }, - "node_modules/node-abi": { - "version": "3.71.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.71.0.tgz", - "integrity": "sha512-SZ40vRiy/+wRTf21hxkkEjPJZpARzUMVcJoQse2EF8qkUWbbO2z7vd5oA/H6bVH6SZQ5STGcu0KRDS7biNRfxw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-addon-api": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", - "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", - "dev": true, - "license": "MIT", - "optional": true - }, "node_modules/node-releases": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", @@ -3797,17 +4009,6 @@ "node": ">= 6" } }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "license": "ISC", - "optional": true, - "dependencies": { - "wrappy": "1" - } - }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -3878,16 +4079,6 @@ "node": ">=6" } }, - "node_modules/partysocket": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/partysocket/-/partysocket-1.0.2.tgz", - "integrity": "sha512-rAFOUKImaq+VBk2B+2RTBsWEvlnarEP53nchoUHzpVs8V6fG2/estihOTslTQUWHVuHEKDL5k8htG8K3TngyFA==", - "dev": true, - "license": "ISC", - "dependencies": { - "event-target-shim": "^6.0.2" - } - }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -3932,31 +4123,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/path2d": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/path2d/-/path2d-0.2.1.tgz", - "integrity": "sha512-Fl2z/BHvkTNvkuBzYTpTuirHZg6wW9z8+4SND/3mDTEcYbbNKWAy21dz9D3ePNNwrrK8pqZO5vLPZ1hLF6T7XA==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/pdfjs-dist": { - "version": "4.8.69", - "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-4.8.69.tgz", - "integrity": "sha512-IHZsA4T7YElCKNNXtiLgqScw4zPd3pG9do8UrznC757gMd7UPeHSL2qwNNMJo4r79fl8oj1Xx+1nh2YkzdMpLQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "canvas": "^3.0.0-rc2", - "path2d": "^0.2.1" - } - }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -4222,92 +4388,6 @@ "preact": ">=10" } }, - "node_modules/prebuild-install": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz", - "integrity": "sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "detect-libc": "^2.0.0", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", - "node-abi": "^3.3.0", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^4.0.0", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "bin": { - "prebuild-install": "bin.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/prebuild-install/node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/prebuild-install/node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/prebuild-install/node_modules/simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "optional": true, - "dependencies": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -4424,18 +4504,6 @@ } } }, - "node_modules/pump": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", - "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -4477,34 +4545,6 @@ ], "license": "MIT" }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", - "optional": true, - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -4515,22 +4555,6 @@ "pify": "^2.3.0" } }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/readdirp": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", @@ -4584,6 +4608,13 @@ "node": ">=0.10.0" } }, + "node_modules/robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==", + "dev": true, + "license": "Unlicense" + }, "node_modules/rollup": { "version": "4.24.4", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.4.tgz", @@ -4646,6 +4677,13 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/sade": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", @@ -4659,27 +4697,12 @@ "node": ">=6" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/semver": { "version": "7.6.3", @@ -4737,41 +4760,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "optional": true - }, - "node_modules/simple-get": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", - "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "decompress-response": "^4.2.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, "node_modules/sirv": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.0.tgz", @@ -4797,17 +4785,6 @@ "node": ">=0.10.0" } }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -5277,38 +5254,6 @@ "node": ">= 14" } }, - "node_modules/tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -5400,19 +5345,12 @@ "dev": true, "license": "Apache-2.0" }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", "dev": true, - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } + "license": "0BSD" }, "node_modules/type-check": { "version": "0.4.0", @@ -5745,14 +5683,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, - "license": "ISC", - "optional": true - }, "node_modules/ws": { "version": "8.18.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", @@ -5804,6 +5734,16 @@ "integrity": "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==", "dev": true, "license": "MIT" + }, + "node_modules/zrender": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/zrender/-/zrender-5.6.0.tgz", + "integrity": "sha512-uzgraf4njmmHAbEUxMJ8Oxg+P3fT04O+9p7gY+wJRVxo8Ge+KmYv0WJev945EH4wFuc4OY2NLXz46FZrWS9xJg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tslib": "2.3.0" + } } } } diff --git a/package.json b/package.json index 3153a68..2f592e8 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,6 @@ "@auth/sveltekit": "^1.7.3", "@floating-ui/dom": "^1.6.12", "@iconify/svelte": "^4.0.2", - "@iktakahiro/markdown-it-katex": "^4.0.1", "@skeletonlabs/skeleton": "^2.10.3", "@skeletonlabs/tw-plugin": "^0.4.0", "@supabase/supabase-js": "^2.46.1", @@ -33,6 +32,8 @@ "eslint-plugin-svelte": "^2.36.0", "file-saver": "^2.0.5", "globals": "^15.0.0", + "d3": "^7.9.0", + "echarts": "^5.5.1", "highlight.js": "^11.10.0", "markdown-it": "^14.1.0", "markdown-it-anchor": "^9.2.0", @@ -44,8 +45,7 @@ "markdown-it-sub": "^2.0.0", "markdown-it-sup": "^2.0.0", "markdown-it-table-of-contents": "^0.8.0", - "partysocket": "^1.0.2", - "pdfjs-dist": "^4.8.69", + "@iktakahiro/markdown-it-katex": "^4.0.1", "prettier": "^3.3.2", "prettier-plugin-svelte": "^3.2.6", "prettier-plugin-tailwindcss": "^0.6.5", From 0a2414552350148f363d8f7f77b7c57f2885acc7 Mon Sep 17 00:00:00 2001 From: Eamonn de Leastar Date: Fri, 8 Nov 2024 06:25:19 +0000 Subject: [PATCH 07/11] use anon_key instead of service_role for supabase --- src/lib/services/metrics/db/client.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/services/metrics/db/client.ts b/src/lib/services/metrics/db/client.ts index a96d76d..44a4b0c 100644 --- a/src/lib/services/metrics/db/client.ts +++ b/src/lib/services/metrics/db/client.ts @@ -1,4 +1,4 @@ import { createClient } from "@supabase/supabase-js"; -import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_SERVICEROLE_KEY } from "$env/static/public"; +import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY } from "$env/static/public"; -export const db = createClient(PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_SERVICEROLE_KEY); +export const db = createClient(PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY); From 4ff96b462c4b1eda19acd3f53a2baf3df2e91844 Mon Sep 17 00:00:00 2001 From: Michael Kelly <20041540@mail.wit.ie> Date: Sun, 10 Nov 2024 18:50:03 +0000 Subject: [PATCH 08/11] Fix: median calendar --- package-lock.json | 43 ------------------- .../ui/time/supabase/analytics/calendar.ts | 8 +++- .../time/supabase/views/CalendarView.svelte | 8 ++-- src/routes/[courseid]/+page.svelte | 3 +- 4 files changed, 12 insertions(+), 50 deletions(-) diff --git a/package-lock.json b/package-lock.json index d496708..1a0fe5e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1452,34 +1452,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/linkify-it": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", - "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/@types/markdown-it": { - "version": "14.1.2", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", - "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@types/linkify-it": "^5", - "@types/mdurl": "^2" - } - }, - "node_modules/@types/mdurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", - "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/@types/node": { "version": "22.9.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", @@ -4130,21 +4102,6 @@ "dev": true, "license": "ISC" }, - "node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", diff --git a/src/lib/ui/time/supabase/analytics/calendar.ts b/src/lib/ui/time/supabase/analytics/calendar.ts index 6437ddc..cbd3ff4 100644 --- a/src/lib/ui/time/supabase/analytics/calendar.ts +++ b/src/lib/ui/time/supabase/analytics/calendar.ts @@ -80,7 +80,7 @@ export class CalendarChart { const chart = echarts.init(chartContainer); if (!sessionStorage.getItem("logoShown")) { - chart.setOption(tutorsAnalyticsLogo("Next Tutors Analytics")); + chart.setOption(tutorsAnalyticsLogo("Tutors Analytics")); sessionStorage.setItem("logoShown", "true"); setTimeout(() => { const option = calendar(session, callendarMapCollection, bgPatternImg, currentRange); @@ -176,7 +176,7 @@ export class CalendarChart { }, visualMap: { min: 0, - max: Math.max(...medianTime.map((item) => item.mediantimeactive)), + max: Math.max(...Array.from(medianTime.values())), type: "piecewise", orient: "horizontal", left: "center", @@ -229,3 +229,7 @@ export class CalendarChart { this.medianCalendarRendered = true; } } +function getDefaultAvatar(): string { + throw new Error("Function not implemented."); +} + diff --git a/src/lib/ui/time/supabase/views/CalendarView.svelte b/src/lib/ui/time/supabase/views/CalendarView.svelte index 3ad469a..b2ff08f 100644 --- a/src/lib/ui/time/supabase/views/CalendarView.svelte +++ b/src/lib/ui/time/supabase/views/CalendarView.svelte @@ -16,7 +16,7 @@ onMount(() => { renderChart(); - }); + }); // Destroy the chart instance when the component unmounts onDestroy(() => { @@ -36,7 +36,7 @@ if (calendarChart) { calendarChart.createChartContainer(tutorsConnectService.tutorsId.value.login); calendarChart.renderChart(timeActiveMap, session); - // calendarChart.renderMedianTimeCalendar(medianTime); + calendarChart.renderMedianTimeCalendar(medianTime); } }; @@ -45,8 +45,8 @@
- {#if session} -
+ {#if tutorsConnectService.tutorsId.value.login} +
{:else}
diff --git a/src/routes/[courseid]/+page.svelte b/src/routes/[courseid]/+page.svelte index d923277..e3a0ff2 100644 --- a/src/routes/[courseid]/+page.svelte +++ b/src/routes/[courseid]/+page.svelte @@ -150,7 +150,8 @@ userNamesAvatars={data.userNamesAvatars} /> {:else} - + {/if} {/if} {#if instructorMode} From 11fa5c63dccc18c426dea3a1b8a0a089c3002207 Mon Sep 17 00:00:00 2001 From: Eamonn de Leastar Date: Sat, 30 Nov 2024 12:24:34 +0000 Subject: [PATCH 09/11] skeleton v3 upgrade --- package-lock.json | 636 +++++++++++++++++- package.json | 4 +- src/app.css | 3 - src/app.postcss | 9 + src/lib/runes.ts | 1 + src/lib/ui/TutorsShell.svelte | 20 + src/lib/ui/app-shells/TutorsShell.svelte | 20 - src/lib/ui/navigators/HomeNavigator.svelte | 39 -- src/lib/ui/navigators/MainNavigator.svelte | 42 ++ .../ui/navigators/titles/CourseTitle.svelte | 33 + .../tutors-connect/AnonProfile.svelte | 41 +- .../tutors-connect/ConnectedProfile.svelte | 76 +-- src/lib/ui/themes/Image.svelte | 7 +- src/lib/ui/themes/LayoutMenu.svelte | 57 ++ src/lib/ui/themes/icons/Breadcrumbs.svelte | 28 +- src/lib/ui/themes/icons/Icon.svelte | 45 +- src/lib/ui/themes/icons/IconBar.svelte | 2 +- src/lib/ui/themes/icons/fluent-icons.ts | 20 +- src/lib/ui/themes/icons/hero-icons.ts | 5 +- src/lib/ui/themes/menu/DarkModeToggle.svelte | 19 - src/lib/ui/themes/menu/LayoutMenu.svelte | 62 -- src/lib/ui/themes/menu/LayoutToggle.svelte | 17 - src/lib/ui/themes/styles/classic.ts | 229 +++++++ src/lib/ui/themes/styles/dyslexia.ts | 322 ++++++--- src/lib/ui/themes/styles/icon-lib.svelte.ts | 76 ++- src/lib/ui/themes/styles/tutors.ts | 304 ++++++--- src/lib/ui/utils/Loading.svelte | 15 + src/lib/ui/utils/Menu.svelte | 23 + src/lib/ui/utils/MenuItem.svelte | 26 + src/lib/ui/utils/Metric.svelte | 14 + src/lib/ui/utils/NotFound.svelte | 14 + src/lib/ui/utils/SetuIcon.svelte | 6 + src/lib/ui/utils/Sidebar.svelte | 41 ++ src/lib/ui/utils/TutorsIcon.svelte | 17 + .../(auth)/auth/SigninWithGithub.svelte | 61 +- .../auth/{Terms.svelte => TutorsTerms.svelte} | 0 src/routes/+layout.svelte | 9 +- src/routes/+page.svelte | 6 +- src/routes/[courseid]/+layout.svelte | 4 +- tailwind.config.ts | 57 +- 40 files changed, 1867 insertions(+), 543 deletions(-) delete mode 100644 src/app.css create mode 100644 src/app.postcss create mode 100644 src/lib/ui/TutorsShell.svelte delete mode 100644 src/lib/ui/app-shells/TutorsShell.svelte delete mode 100644 src/lib/ui/navigators/HomeNavigator.svelte create mode 100644 src/lib/ui/navigators/MainNavigator.svelte create mode 100644 src/lib/ui/navigators/titles/CourseTitle.svelte create mode 100644 src/lib/ui/themes/LayoutMenu.svelte delete mode 100644 src/lib/ui/themes/menu/DarkModeToggle.svelte delete mode 100644 src/lib/ui/themes/menu/LayoutMenu.svelte delete mode 100644 src/lib/ui/themes/menu/LayoutToggle.svelte create mode 100644 src/lib/ui/themes/styles/classic.ts create mode 100644 src/lib/ui/utils/Loading.svelte create mode 100644 src/lib/ui/utils/Menu.svelte create mode 100644 src/lib/ui/utils/MenuItem.svelte create mode 100644 src/lib/ui/utils/Metric.svelte create mode 100644 src/lib/ui/utils/NotFound.svelte create mode 100644 src/lib/ui/utils/SetuIcon.svelte create mode 100644 src/lib/ui/utils/Sidebar.svelte create mode 100644 src/lib/ui/utils/TutorsIcon.svelte rename src/routes/(auth)/auth/{Terms.svelte => TutorsTerms.svelte} (100%) diff --git a/package-lock.json b/package-lock.json index 1a0fe5e..861be56 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,8 +13,8 @@ "@floating-ui/dom": "^1.6.12", "@iconify/svelte": "^4.0.2", "@iktakahiro/markdown-it-katex": "^4.0.1", - "@skeletonlabs/skeleton": "^2.10.3", - "@skeletonlabs/tw-plugin": "^0.4.0", + "@skeletonlabs/skeleton": "^3.0.0-next.9", + "@skeletonlabs/skeleton-svelte": "^1.0.0-next.14", "@supabase/supabase-js": "^2.46.1", "@sveltejs/adapter-auto": "^3.0.0", "@sveltejs/kit": "^2.0.0", @@ -1195,33 +1195,58 @@ ] }, "node_modules/@skeletonlabs/skeleton": { - "version": "2.10.3", - "resolved": "https://registry.npmjs.org/@skeletonlabs/skeleton/-/skeleton-2.10.3.tgz", - "integrity": "sha512-O1RecF68zEVvZl3GgRS4emqWMUIQLHvTOFoqGOw/2OXCPE06IxUQrHQf2hnxCPxtGZNXY2YX8UNV38l+eH8GNQ==", + "version": "3.0.0-next.9", + "resolved": "https://registry.npmjs.org/@skeletonlabs/skeleton/-/skeleton-3.0.0-next.9.tgz", + "integrity": "sha512-xZxXkKgdTGeWPVLH7oQEYd7T9yLJ4p9gm8JS7egJzTzR/iC+n52Pez4LzWVYtPo+cUz4HUDEFREuLEeNfzypcg==", "dev": true, "license": "MIT", "dependencies": { - "esm-env": "1.0.0" + "postcss-js": "4.0.1", + "postcss-selector-parser": "^7.0.0" }, "peerDependencies": { - "svelte": "^3.56.0 || ^4.0.0 || ^5.0.0" + "tailwindcss": ">=3.0.0" } }, - "node_modules/@skeletonlabs/skeleton/node_modules/esm-env": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.0.0.tgz", - "integrity": "sha512-Cf6VksWPsTuW01vU9Mk/3vRue91Zevka5SjyNf3nEpokFRuqt/KjUQoGAwq9qMmhpLTHmXzSIrFRw8zxWzmFBA==", - "dev": true, - "license": "MIT" + "node_modules/@skeletonlabs/skeleton-svelte": { + "version": "1.0.0-next.14", + "resolved": "https://registry.npmjs.org/@skeletonlabs/skeleton-svelte/-/skeleton-svelte-1.0.0-next.14.tgz", + "integrity": "sha512-sGVzDdhFCdf1u911DDbu2eex+L1bczMGSSWdvy4LbxtPjrFnQUFsN2+6CUteK7PuFuOyIERnS4BWqwSDxk2FNg==", + "dev": true, + "dependencies": { + "@zag-js/accordion": "^0.75.0", + "@zag-js/avatar": "^0.75.0", + "@zag-js/combobox": "^0.75.0", + "@zag-js/dialog": "^0.75.0", + "@zag-js/file-upload": "^0.75.0", + "@zag-js/pagination": "^0.75.0", + "@zag-js/popover": "^0.75.0", + "@zag-js/progress": "^0.75.0", + "@zag-js/radio-group": "^0.75.0", + "@zag-js/rating-group": "^0.75.0", + "@zag-js/slider": "^0.75.0", + "@zag-js/svelte": "^0.75.0", + "@zag-js/switch": "^0.75.0", + "@zag-js/tabs": "^0.75.0", + "@zag-js/tags-input": "^0.75.0", + "@zag-js/tooltip": "^0.75.0" + }, + "peerDependencies": { + "svelte": "^5.0.0" + } }, - "node_modules/@skeletonlabs/tw-plugin": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@skeletonlabs/tw-plugin/-/tw-plugin-0.4.0.tgz", - "integrity": "sha512-v6Y4deBq9ByRx3kTRGgekhhYkWEYgNNNu8UXOwJngCStB7w8SwmbNFSeHkluxMbgCgMnJyp220EMpw9nj/rEsQ==", + "node_modules/@skeletonlabs/skeleton/node_modules/postcss-selector-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", + "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", "dev": true, "license": "MIT", - "peerDependencies": { - "tailwindcss": ">=3.0.0" + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" } }, "node_modules/@supabase/auth-js": { @@ -1452,6 +1477,34 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/markdown-it": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/@types/node": { "version": "22.9.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", @@ -1708,6 +1761,497 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@zag-js/accordion": { + "version": "0.75.0", + "resolved": "https://registry.npmjs.org/@zag-js/accordion/-/accordion-0.75.0.tgz", + "integrity": "sha512-ZqwI4/EO6lA8fRSce7MvATDVniea6TruA3XsBeb/KyDVea360laIJYKeo5giZJR5MEsNqOmmDIK2Uzkcq1uUnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "0.75.0", + "@zag-js/core": "0.75.0", + "@zag-js/dom-event": "0.75.0", + "@zag-js/dom-query": "0.75.0", + "@zag-js/types": "0.75.0", + "@zag-js/utils": "0.75.0" + } + }, + "node_modules/@zag-js/anatomy": { + "version": "0.75.0", + "resolved": "https://registry.npmjs.org/@zag-js/anatomy/-/anatomy-0.75.0.tgz", + "integrity": "sha512-YtVz2387Xy3cXeL86/jWtZ+NT9wojFsXZbOnRoXG5P5QaiD7tKeWYWJZBzxNBPYfWhSy1N93cAxhL65xJah1Qw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@zag-js/aria-hidden": { + "version": "0.75.0", + "resolved": "https://registry.npmjs.org/@zag-js/aria-hidden/-/aria-hidden-0.75.0.tgz", + "integrity": "sha512-CjZGQycYVNFSYffI7IsBvTd2rLGPTLpJvP9gDAPCR9pQpyTauI2teDi7TZry12Cybmtimj3emq2L23hPcfnm3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "aria-hidden": "1.2.4" + } + }, + "node_modules/@zag-js/auto-resize": { + "version": "0.75.0", + "resolved": "https://registry.npmjs.org/@zag-js/auto-resize/-/auto-resize-0.75.0.tgz", + "integrity": "sha512-nCP6jIkROG1qW8juBgQZ4WuFhP80ZHq4Vl4h5nTzxvHtePJ6+JG9YyjZWg3CTBaasiLXkcKH33L8Hf2ZQiNuqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "0.75.0" + } + }, + "node_modules/@zag-js/avatar": { + "version": "0.75.0", + "resolved": "https://registry.npmjs.org/@zag-js/avatar/-/avatar-0.75.0.tgz", + "integrity": "sha512-20LKdAFi/Nozb526gutfH9nGMk0GEkbCYe0zcBchVm/IC69V3VBttJthc2nVxjaEEdkZ14b2ZTeEtWbRSDbJzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "0.75.0", + "@zag-js/core": "0.75.0", + "@zag-js/dom-query": "0.75.0", + "@zag-js/types": "0.75.0", + "@zag-js/utils": "0.75.0" + } + }, + "node_modules/@zag-js/collection": { + "version": "0.75.0", + "resolved": "https://registry.npmjs.org/@zag-js/collection/-/collection-0.75.0.tgz", + "integrity": "sha512-XcUzc7Dqy32CibI1T9Fwfcf3aJuvyD5eMIAnBMCY9ktgImCjbG3q9pvG+2G9wTmiPow+dJWbb0ZTTaevhY3asw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@zag-js/utils": "0.75.0" + } + }, + "node_modules/@zag-js/combobox": { + "version": "0.75.0", + "resolved": "https://registry.npmjs.org/@zag-js/combobox/-/combobox-0.75.0.tgz", + "integrity": "sha512-5rxQGmMTJXg4WRQ1BIAXYGT7lZ1H3Sbh7UO/p999hC9oVMUrYU5sH9jfJPKhJvfOQTSHQdARBdz7V96IPjr3BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "0.75.0", + "@zag-js/aria-hidden": "0.75.0", + "@zag-js/collection": "0.75.0", + "@zag-js/core": "0.75.0", + "@zag-js/dismissable": "0.75.0", + "@zag-js/dom-event": "0.75.0", + "@zag-js/dom-query": "0.75.0", + "@zag-js/popper": "0.75.0", + "@zag-js/types": "0.75.0", + "@zag-js/utils": "0.75.0" + } + }, + "node_modules/@zag-js/core": { + "version": "0.75.0", + "resolved": "https://registry.npmjs.org/@zag-js/core/-/core-0.75.0.tgz", + "integrity": "sha512-nkfHHWLTHiHW/hvk/O4y9sbtxE5Bxbj1vetElAlXqCDrQMgqNGOBEtxcjQVX65CJMjx1wHHcjG7lV0JOY6cpCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@zag-js/store": "0.75.0", + "@zag-js/utils": "0.75.0", + "klona": "2.0.6" + } + }, + "node_modules/@zag-js/dialog": { + "version": "0.75.0", + "resolved": "https://registry.npmjs.org/@zag-js/dialog/-/dialog-0.75.0.tgz", + "integrity": "sha512-wYtedDPT8y3fHT5lO4BAOxjtrePowFsB3nCNpupxxsMlozjAbDNI+DHp/CvFLcCAYbPvce8DSnT/y/1fy0LYIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "0.75.0", + "@zag-js/aria-hidden": "0.75.0", + "@zag-js/core": "0.75.0", + "@zag-js/dismissable": "0.75.0", + "@zag-js/dom-query": "0.75.0", + "@zag-js/remove-scroll": "0.75.0", + "@zag-js/types": "0.75.0", + "@zag-js/utils": "0.75.0", + "focus-trap": "7.6.0" + } + }, + "node_modules/@zag-js/dismissable": { + "version": "0.75.0", + "resolved": "https://registry.npmjs.org/@zag-js/dismissable/-/dismissable-0.75.0.tgz", + "integrity": "sha512-s+VK/9xtx7md7iKy/StrYCUUKegFwkecWIO/K/U52gQN3UdCQRX9bjvENI3LVzzme6XEBz91b3O8K83bfG4Ojw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@zag-js/dom-event": "0.75.0", + "@zag-js/dom-query": "0.75.0", + "@zag-js/interact-outside": "0.75.0", + "@zag-js/utils": "0.75.0" + } + }, + "node_modules/@zag-js/dom-event": { + "version": "0.75.0", + "resolved": "https://registry.npmjs.org/@zag-js/dom-event/-/dom-event-0.75.0.tgz", + "integrity": "sha512-xPgalQ6fvVH9JKaCEdNCWJGp+0+AHM7DvTXe1PVgvuBb7//QBGzYaEUhhmdt3LXyG79o7RaIEVEgGTjwH2vDTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "0.75.0", + "@zag-js/text-selection": "0.75.0", + "@zag-js/types": "0.75.0" + } + }, + "node_modules/@zag-js/dom-query": { + "version": "0.75.0", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-0.75.0.tgz", + "integrity": "sha512-PrCX29VbxKW4YQCAo6juttlwW563Os03dId/0V7WCN4daj+ocpI169mMm/XxgXfI9kgebZuYvY9ngIJeHdJHSA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@zag-js/element-rect": { + "version": "0.75.0", + "resolved": "https://registry.npmjs.org/@zag-js/element-rect/-/element-rect-0.75.0.tgz", + "integrity": "sha512-r9BTJL2kVPcyIxY7ZWewaCbfMSWhMYoSjK6ImgAZYpjApdk9cNspMkQl3m/felbBSsFDRp+DcLmadLlhqY6m7Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@zag-js/element-size": { + "version": "0.75.0", + "resolved": "https://registry.npmjs.org/@zag-js/element-size/-/element-size-0.75.0.tgz", + "integrity": "sha512-N1t36zdLvL5B+6aFU95cIlyw2HqxgP8Wq5dWAPZDn3ATH3r6/Yv8kYZFvf0ACfORDv/uInByEPXta1+QE418yg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@zag-js/file-upload": { + "version": "0.75.0", + "resolved": "https://registry.npmjs.org/@zag-js/file-upload/-/file-upload-0.75.0.tgz", + "integrity": "sha512-Xn14aOZ2u5UuJjrOtZpxV9Lqcqv6bOjlcu/iU6PI76JpPdI0/AaVXU7CYItLrPBbUsctBsjYW79/HOcHqZwhLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "0.75.0", + "@zag-js/core": "0.75.0", + "@zag-js/dom-query": "0.75.0", + "@zag-js/file-utils": "0.75.0", + "@zag-js/i18n-utils": "0.75.0", + "@zag-js/types": "0.75.0", + "@zag-js/utils": "0.75.0" + } + }, + "node_modules/@zag-js/file-utils": { + "version": "0.75.0", + "resolved": "https://registry.npmjs.org/@zag-js/file-utils/-/file-utils-0.75.0.tgz", + "integrity": "sha512-ORCEM0LN516luYmF4PYegieksNWolDlcZ1jiiLSXr6ylTyUNYFmPDs/UFByZx4FsXgRTu2FJMAaG6jBRpp50dQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@zag-js/i18n-utils": "0.75.0" + } + }, + "node_modules/@zag-js/focus-visible": { + "version": "0.75.0", + "resolved": "https://registry.npmjs.org/@zag-js/focus-visible/-/focus-visible-0.75.0.tgz", + "integrity": "sha512-2Uin+VcveAT+dwD02Krn1L+cCA6U/YHGQeu8SJ9H06hnqjzVqE2maeHc6tyTqH0K1wQKvWHZFcWIpfF64QviMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "0.75.0" + } + }, + "node_modules/@zag-js/form-utils": { + "version": "0.75.0", + "resolved": "https://registry.npmjs.org/@zag-js/form-utils/-/form-utils-0.75.0.tgz", + "integrity": "sha512-IXyUzpY/e+80e6UWmBmtIS5wJ3elxWR6smO6Ot0FLcN5HWabfxdY7op1OLrd6czj/VVC0zD+UGwCG6yasUYTyw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@zag-js/i18n-utils": { + "version": "0.75.0", + "resolved": "https://registry.npmjs.org/@zag-js/i18n-utils/-/i18n-utils-0.75.0.tgz", + "integrity": "sha512-5mIVuSmEfK/9Pk66R5Njbxu9l+PqUfRYrtr9HJItW8ijjmIrISfW9D0xRazKcVExVarYuHEk6Fzk1nsJnzqZmg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "0.75.0" + } + }, + "node_modules/@zag-js/interact-outside": { + "version": "0.75.0", + "resolved": "https://registry.npmjs.org/@zag-js/interact-outside/-/interact-outside-0.75.0.tgz", + "integrity": "sha512-F3+BMMx57EaY1CPW7007MEJCBe3hgR1NJuDzvDmqPyjy+M0BwNzd7Tqg5YxaDSuvAA4r71AlxqRpBU5RNh44lA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@zag-js/dom-event": "0.75.0", + "@zag-js/dom-query": "0.75.0", + "@zag-js/utils": "0.75.0" + } + }, + "node_modules/@zag-js/live-region": { + "version": "0.75.0", + "resolved": "https://registry.npmjs.org/@zag-js/live-region/-/live-region-0.75.0.tgz", + "integrity": "sha512-x5C3us0UyjGAzg0QV6EvwSuAuO0RxWevlSvEFRzFCi3VVG35FyqF4eryN8iP6kfRdeZrJFJnjzX5p0Gj1C6Mkw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@zag-js/numeric-range": { + "version": "0.75.0", + "resolved": "https://registry.npmjs.org/@zag-js/numeric-range/-/numeric-range-0.75.0.tgz", + "integrity": "sha512-8xbFhC1RIj5AwoZ2hr+E5H+/Jx9AoDAU8rPm1slD8eV+21ACqMqRCWtKwk+9lsueT4ZksBjnkTL+ou/ru5J0qQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@zag-js/pagination": { + "version": "0.75.0", + "resolved": "https://registry.npmjs.org/@zag-js/pagination/-/pagination-0.75.0.tgz", + "integrity": "sha512-oW3Y0y+lchNU1JVBfF7gsU2G+3B4hkNnOa8rjNY3ONgVM95EauAaAdsv2eopfPykFyVUntGi7xYvQqtUYCrKRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "0.75.0", + "@zag-js/core": "0.75.0", + "@zag-js/dom-query": "0.75.0", + "@zag-js/types": "0.75.0", + "@zag-js/utils": "0.75.0" + } + }, + "node_modules/@zag-js/popover": { + "version": "0.75.0", + "resolved": "https://registry.npmjs.org/@zag-js/popover/-/popover-0.75.0.tgz", + "integrity": "sha512-wQLHfVGBFq34clGFI9+duGkMKwhtrrHlOjBnFLZIIPMUWiBY1WaU2XKycPKEW1bYK65u8e0yRv9dH+Z4oOYCVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "0.75.0", + "@zag-js/aria-hidden": "0.75.0", + "@zag-js/core": "0.75.0", + "@zag-js/dismissable": "0.75.0", + "@zag-js/dom-query": "0.75.0", + "@zag-js/popper": "0.75.0", + "@zag-js/remove-scroll": "0.75.0", + "@zag-js/types": "0.75.0", + "@zag-js/utils": "0.75.0", + "focus-trap": "7.6.0" + } + }, + "node_modules/@zag-js/popper": { + "version": "0.75.0", + "resolved": "https://registry.npmjs.org/@zag-js/popper/-/popper-0.75.0.tgz", + "integrity": "sha512-B3Xlb+iH+Xux086XmWkpxG/tL4dyO9XbOFrQj1AuHkPC4Bgfc6DGPC1yGnHLdlYYm1Yyv8SR5LvPLyv4e1Ogcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "1.6.11", + "@zag-js/dom-query": "0.75.0", + "@zag-js/utils": "0.75.0" + } + }, + "node_modules/@zag-js/popper/node_modules/@floating-ui/dom": { + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.11.tgz", + "integrity": "sha512-qkMCxSR24v2vGkhYDo/UzxfJN3D4syqSjyuTFz6C7XcpU1pASPRieNI0Kj5VP3/503mOfYiGY891ugBX1GlABQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.6.0", + "@floating-ui/utils": "^0.2.8" + } + }, + "node_modules/@zag-js/progress": { + "version": "0.75.0", + "resolved": "https://registry.npmjs.org/@zag-js/progress/-/progress-0.75.0.tgz", + "integrity": "sha512-kCEPtx6R2RVLq/RT7Fa19DJP9VecfH6dyRvWa5W3iOsT4kpGYYRGQI0q4e32hmK/D4iIFPQeaK1vtmo6O7JSFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "0.75.0", + "@zag-js/core": "0.75.0", + "@zag-js/dom-query": "0.75.0", + "@zag-js/types": "0.75.0", + "@zag-js/utils": "0.75.0" + } + }, + "node_modules/@zag-js/radio-group": { + "version": "0.75.0", + "resolved": "https://registry.npmjs.org/@zag-js/radio-group/-/radio-group-0.75.0.tgz", + "integrity": "sha512-mctay6B5TxuuUGgwhoqd586DXLhQu/EYP2FLoRYhStKD0wU/LbmvYob8EYMC9arQhv1DQS+Wa712sQc2/Uuw+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "0.75.0", + "@zag-js/core": "0.75.0", + "@zag-js/dom-query": "0.75.0", + "@zag-js/element-rect": "0.75.0", + "@zag-js/focus-visible": "0.75.0", + "@zag-js/form-utils": "0.75.0", + "@zag-js/types": "0.75.0", + "@zag-js/utils": "0.75.0" + } + }, + "node_modules/@zag-js/rating-group": { + "version": "0.75.0", + "resolved": "https://registry.npmjs.org/@zag-js/rating-group/-/rating-group-0.75.0.tgz", + "integrity": "sha512-zfBeXETzOpaAzsZQf9Wd6CtaQ4F4/g7L04aj+eX9OP3VmHBHNwoe8SA/dNUJNB4QIoQeCpC2NQOiPtEV68ZENQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "0.75.0", + "@zag-js/core": "0.75.0", + "@zag-js/dom-event": "0.75.0", + "@zag-js/dom-query": "0.75.0", + "@zag-js/form-utils": "0.75.0", + "@zag-js/types": "0.75.0", + "@zag-js/utils": "0.75.0" + } + }, + "node_modules/@zag-js/remove-scroll": { + "version": "0.75.0", + "resolved": "https://registry.npmjs.org/@zag-js/remove-scroll/-/remove-scroll-0.75.0.tgz", + "integrity": "sha512-atbqJevyvuVQ8ydTGtlweNvpaBj+lqv0QQaC9mjIXbndPNF2/9PKx9cXZINUG1bfdQ2DN3BSzWlrSn05DOmj3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "0.75.0" + } + }, + "node_modules/@zag-js/slider": { + "version": "0.75.0", + "resolved": "https://registry.npmjs.org/@zag-js/slider/-/slider-0.75.0.tgz", + "integrity": "sha512-xW/SVaxcR4EisoMW0MImtiK8+DJWmpQ7dhb9kn+GwQ+heonUnIHh3ChvKboK+Nv/86J/sCIuUUcX8zz85FhXJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "0.75.0", + "@zag-js/core": "0.75.0", + "@zag-js/dom-event": "0.75.0", + "@zag-js/dom-query": "0.75.0", + "@zag-js/element-size": "0.75.0", + "@zag-js/form-utils": "0.75.0", + "@zag-js/numeric-range": "0.75.0", + "@zag-js/types": "0.75.0", + "@zag-js/utils": "0.75.0" + } + }, + "node_modules/@zag-js/store": { + "version": "0.75.0", + "resolved": "https://registry.npmjs.org/@zag-js/store/-/store-0.75.0.tgz", + "integrity": "sha512-kAzqLKfCLRIYfnsLuQdHOdrIwBdO4nqJ6kjCYlWvhSe81oDWCrC5MY2fv7mIYFjW+ZPpH3xmgGvj5ugqfAsqoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "proxy-compare": "3.0.0" + } + }, + "node_modules/@zag-js/svelte": { + "version": "0.75.0", + "resolved": "https://registry.npmjs.org/@zag-js/svelte/-/svelte-0.75.0.tgz", + "integrity": "sha512-MK4TX4vTg04cLYv8f7a7waIS0FzWjXLMe8mHeTg8Je/Pj6BKRZdXVdv5wHy8le7l+To8vOZjOQKFo61yjAuzyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@zag-js/core": "0.75.0", + "@zag-js/types": "0.75.0" + }, + "peerDependencies": { + "svelte": "^5.0.0-next.1" + } + }, + "node_modules/@zag-js/switch": { + "version": "0.75.0", + "resolved": "https://registry.npmjs.org/@zag-js/switch/-/switch-0.75.0.tgz", + "integrity": "sha512-A1TCQa43HYtIl5iNJ96FG7taW9isN39AhDd33CEoqyr4VH2iMNtkzRhkf9VxLQHWuOsommoVD1faUWlHObeBlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "0.75.0", + "@zag-js/core": "0.75.0", + "@zag-js/dom-event": "0.75.0", + "@zag-js/dom-query": "0.75.0", + "@zag-js/focus-visible": "0.75.0", + "@zag-js/form-utils": "0.75.0", + "@zag-js/types": "0.75.0", + "@zag-js/utils": "0.75.0" + } + }, + "node_modules/@zag-js/tabs": { + "version": "0.75.0", + "resolved": "https://registry.npmjs.org/@zag-js/tabs/-/tabs-0.75.0.tgz", + "integrity": "sha512-F7OYMWeayWWkG3KRsM0n6hdnRq7b4pY/cEJ9OAG0Fz4PFq2dD6cLDsC/hBhiR5TUmKK9G0+orTXxijB2Uqc1cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "0.75.0", + "@zag-js/core": "0.75.0", + "@zag-js/dom-event": "0.75.0", + "@zag-js/dom-query": "0.75.0", + "@zag-js/element-rect": "0.75.0", + "@zag-js/types": "0.75.0", + "@zag-js/utils": "0.75.0" + } + }, + "node_modules/@zag-js/tags-input": { + "version": "0.75.0", + "resolved": "https://registry.npmjs.org/@zag-js/tags-input/-/tags-input-0.75.0.tgz", + "integrity": "sha512-nZjJuxzs4bOA8KqLWWKHDF9cTwdfIhtvbFGvoOuqxyCqLpLo2kgfwOnRSiny0MrYo0GY+drNcHIqFiznh0OOUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "0.75.0", + "@zag-js/auto-resize": "0.75.0", + "@zag-js/core": "0.75.0", + "@zag-js/dom-event": "0.75.0", + "@zag-js/dom-query": "0.75.0", + "@zag-js/form-utils": "0.75.0", + "@zag-js/interact-outside": "0.75.0", + "@zag-js/live-region": "0.75.0", + "@zag-js/types": "0.75.0", + "@zag-js/utils": "0.75.0" + } + }, + "node_modules/@zag-js/text-selection": { + "version": "0.75.0", + "resolved": "https://registry.npmjs.org/@zag-js/text-selection/-/text-selection-0.75.0.tgz", + "integrity": "sha512-ZSbX6bb8b75DAg8zGPiEfHGn2d72991vAIvojqzCI1tRG7KpAL66XVurkR/m+MmPoH5fYESB7JstgBD8+n2MtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "0.75.0" + } + }, + "node_modules/@zag-js/tooltip": { + "version": "0.75.0", + "resolved": "https://registry.npmjs.org/@zag-js/tooltip/-/tooltip-0.75.0.tgz", + "integrity": "sha512-K4GD6J7bo/BOsFIAAfQ2StF8kZA/DwRLjEPIaie3SlX5rGDu+NtwcREXhJ+CyZq4RvkhI4W6LEpWPXIGZePRYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "0.75.0", + "@zag-js/core": "0.75.0", + "@zag-js/dom-event": "0.75.0", + "@zag-js/dom-query": "0.75.0", + "@zag-js/focus-visible": "0.75.0", + "@zag-js/popper": "0.75.0", + "@zag-js/types": "0.75.0", + "@zag-js/utils": "0.75.0" + } + }, + "node_modules/@zag-js/types": { + "version": "0.75.0", + "resolved": "https://registry.npmjs.org/@zag-js/types/-/types-0.75.0.tgz", + "integrity": "sha512-NG2wJeFzgSX7TKg015r1nxV3oSy9RJ94iQpoQLv66hH5XDvAJbV4jCf0yAo8awwK+5x8Opz3awJrGVnbo0B/eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "3.1.3" + } + }, + "node_modules/@zag-js/utils": { + "version": "0.75.0", + "resolved": "https://registry.npmjs.org/@zag-js/utils/-/utils-0.75.0.tgz", + "integrity": "sha512-68zu+3zBs5QtgYcdcoUOpYMdyf5tecoaNbttjAR3Xnmgva3PYnS/l08ZnV8wyGvixGFSRg1gJNZfWFm2WGCmIw==", + "dev": true, + "license": "MIT" + }, "node_modules/acorn": { "version": "8.14.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", @@ -1835,6 +2379,19 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/aria-hidden": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz", + "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/aria-query": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", @@ -2119,6 +2676,13 @@ "node": ">=4" } }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true, + "license": "MIT" + }, "node_modules/d3": { "version": "7.9.0", "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", @@ -3132,6 +3696,16 @@ "dev": true, "license": "ISC" }, + "node_modules/focus-trap": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.6.0.tgz", + "integrity": "sha512-1td0l3pMkWJLFipobUcGaf+5DTY4PLDDrcqoSaKP8ediO/CoWCCYk/fT/Y2A4e6TNB+Sh6clRJCjOPPnKoNHnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tabbable": "^6.2.0" + } + }, "node_modules/foreground-child": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", @@ -3585,6 +4159,16 @@ "node": ">=6" } }, + "node_modules/klona": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, "node_modules/known-css-properties": { "version": "0.35.0", "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.35.0.tgz", @@ -4461,6 +5045,13 @@ } } }, + "node_modules/proxy-compare": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/proxy-compare/-/proxy-compare-3.0.0.tgz", + "integrity": "sha512-y44MCkgtZUCT9tZGuE278fB7PWVf7fRYy0vbRXAts2o5F0EfC4fIQrvQQGBJo1WJbFcVLXzApOscyJuZqHQc1w==", + "dev": true, + "license": "MIT" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -5033,6 +5624,13 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", + "dev": true, + "license": "MIT" + }, "node_modules/tailwindcss": { "version": "3.4.14", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.14.tgz", diff --git a/package.json b/package.json index 2f592e8..31e1ce0 100644 --- a/package.json +++ b/package.json @@ -16,8 +16,8 @@ "@auth/sveltekit": "^1.7.3", "@floating-ui/dom": "^1.6.12", "@iconify/svelte": "^4.0.2", - "@skeletonlabs/skeleton": "^2.10.3", - "@skeletonlabs/tw-plugin": "^0.4.0", + "@skeletonlabs/skeleton": "^3.0.0-next.9", + "@skeletonlabs/skeleton-svelte": "^1.0.0-next.14", "@supabase/supabase-js": "^2.46.1", "@sveltejs/adapter-auto": "^3.0.0", "@sveltejs/kit": "^2.0.0", diff --git a/src/app.css b/src/app.css deleted file mode 100644 index 76fcadc..0000000 --- a/src/app.css +++ /dev/null @@ -1,3 +0,0 @@ -@import "tailwindcss/base"; -@import "tailwindcss/components"; -@import "tailwindcss/utilities"; diff --git a/src/app.postcss b/src/app.postcss new file mode 100644 index 0000000..f3fef83 --- /dev/null +++ b/src/app.postcss @@ -0,0 +1,9 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; +@tailwind variants; + +html, +body { + @apply h-full overflow-hidden; +} diff --git a/src/lib/runes.ts b/src/lib/runes.ts index 3b1feb6..d53fb77 100644 --- a/src/lib/runes.ts +++ b/src/lib/runes.ts @@ -9,3 +9,4 @@ export const currentCourse = rune(null); export const courseUrl = rune(""); export const currentLabStepIndex = rune(0); export const currentTheme = rune("tutors"); +export const lightMode = rune("light"); diff --git a/src/lib/ui/TutorsShell.svelte b/src/lib/ui/TutorsShell.svelte new file mode 100644 index 0000000..022ceea --- /dev/null +++ b/src/lib/ui/TutorsShell.svelte @@ -0,0 +1,20 @@ + + +
+
+ +
+
+ {@render children()} +
+
+
+
+
diff --git a/src/lib/ui/app-shells/TutorsShell.svelte b/src/lib/ui/app-shells/TutorsShell.svelte deleted file mode 100644 index ac8d767..0000000 --- a/src/lib/ui/app-shells/TutorsShell.svelte +++ /dev/null @@ -1,20 +0,0 @@ - - - - {#snippet header()} - - {/snippet} - {@render children?.()} - {#snippet pageFooter()} -
- {/snippet} - diff --git a/src/lib/ui/navigators/HomeNavigator.svelte b/src/lib/ui/navigators/HomeNavigator.svelte deleted file mode 100644 index 5a30b8b..0000000 --- a/src/lib/ui/navigators/HomeNavigator.svelte +++ /dev/null @@ -1,39 +0,0 @@ - - - - {#snippet lead()} - - {/snippet} - {@render header?.()} - {#snippet trail()} - - - -
- {#if tutorsConnectService.tutorsId.value?.login} - - {:else} - - {/if} -
- - {/snippet} -
diff --git a/src/lib/ui/navigators/MainNavigator.svelte b/src/lib/ui/navigators/MainNavigator.svelte new file mode 100644 index 0000000..3bac4c8 --- /dev/null +++ b/src/lib/ui/navigators/MainNavigator.svelte @@ -0,0 +1,42 @@ + + + + {#snippet lead()} + {#if currentCourse?.value} + + {:else} + + + + {/if} + {/snippet} + {#snippet trail()} + + + + {#if !currentCourse?.value?.isPrivate} +
+ {#if !tutorsConnectService.tutorsId.value?.login} + + {:else} + + {/if} +
+ {/if} + {/snippet} +
diff --git a/src/lib/ui/navigators/titles/CourseTitle.svelte b/src/lib/ui/navigators/titles/CourseTitle.svelte new file mode 100644 index 0000000..525fa36 --- /dev/null +++ b/src/lib/ui/navigators/titles/CourseTitle.svelte @@ -0,0 +1,33 @@ + + +{#if currentLo?.value} +
+
+ {#if currentLo?.value?.img} + + {:else} + + {/if} +
+
+
+
+ +
+ +
+{:else} +
+ +
+{/if} diff --git a/src/lib/ui/navigators/tutors-connect/AnonProfile.svelte b/src/lib/ui/navigators/tutors-connect/AnonProfile.svelte index 99cff48..6cd3add 100644 --- a/src/lib/ui/navigators/tutors-connect/AnonProfile.svelte +++ b/src/lib/ui/navigators/tutors-connect/AnonProfile.svelte @@ -1,7 +1,9 @@ - +{/snippet} -
+{/snippet} + + diff --git a/src/lib/ui/navigators/tutors-connect/ConnectedProfile.svelte b/src/lib/ui/navigators/tutors-connect/ConnectedProfile.svelte index cb2daa7..e2060ac 100644 --- a/src/lib/ui/navigators/tutors-connect/ConnectedProfile.svelte +++ b/src/lib/ui/navigators/tutors-connect/ConnectedProfile.svelte @@ -1,60 +1,48 @@ - - - +{/snippet} + + diff --git a/src/lib/ui/themes/Image.svelte b/src/lib/ui/themes/Image.svelte index d383c4e..2dad0f1 100644 --- a/src/lib/ui/themes/Image.svelte +++ b/src/lib/ui/themes/Image.svelte @@ -1,7 +1,6 @@ @@ -21,5 +20,5 @@ {#if lo?.icon} {:else} - + {lo?.title} {/if} diff --git a/src/lib/ui/themes/LayoutMenu.svelte b/src/lib/ui/themes/LayoutMenu.svelte new file mode 100644 index 0000000..c190679 --- /dev/null +++ b/src/lib/ui/themes/LayoutMenu.svelte @@ -0,0 +1,57 @@ + + +{#snippet menuSelector()} + +{/snippet} + +{#snippet menuContent()} +
Toggles
+
    + + +
+
+
Themes
+
    + {#each themes as theme} + changeTheme(theme.name)}/> + {/each} +
+{/snippet} + + diff --git a/src/lib/ui/themes/icons/Breadcrumbs.svelte b/src/lib/ui/themes/icons/Breadcrumbs.svelte index 040ba51..6400c97 100644 --- a/src/lib/ui/themes/icons/Breadcrumbs.svelte +++ b/src/lib/ui/themes/icons/Breadcrumbs.svelte @@ -35,27 +35,25 @@
-
- +
+ + diff --git a/src/routes/(auth)/auth/Terms.svelte b/src/routes/(auth)/auth/TutorsTerms.svelte similarity index 100% rename from src/routes/(auth)/auth/Terms.svelte rename to src/routes/(auth)/auth/TutorsTerms.svelte diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index da84b2b..1d68ab7 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -1,10 +1,8 @@ - - diff --git a/src/routes/[courseid]/+layout.svelte b/src/routes/[courseid]/+layout.svelte index 2d94bfd..ae01b79 100644 --- a/src/routes/[courseid]/+layout.svelte +++ b/src/routes/[courseid]/+layout.svelte @@ -1,6 +1,6 @@