Skip to content

Commit

Permalink
Fix flickering with data loader (#348)
Browse files Browse the repository at this point in the history
* added loader

* added loader

* linter

* fix name
  • Loading branch information
warreprovoost authored May 12, 2024
1 parent cbd7f83 commit 1d0b4f8
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 60 deletions.
11 changes: 8 additions & 3 deletions frontend/src/components/Courses/AllCoursesTeacher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useState } from "react";
import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";
import { SideScrollableCourses } from "./CourseUtilComponents";
import { Course, callToApiToCreateCourse } from "./CourseUtils";
import {Course, callToApiToCreateCourse, ProjectDetail} from "./CourseUtils";
import { Title } from "../Header/Title";
import { useLoaderData } from "react-router-dom";

Expand All @@ -12,7 +12,12 @@ import { useLoaderData } from "react-router-dom";
*/
export function AllCoursesTeacher(): JSX.Element {
const [open, setOpen] = useState(false);
const courses = (useLoaderData() as Course[]);
const loader = useLoaderData() as {
courses: Course[];
projects: { [courseId: string]: ProjectDetail[] }
};
const courses = loader.courses;
const projects = loader.projects;

const [courseName, setCourseName] = useState('');
const [error, setError] = useState('');
Expand Down Expand Up @@ -49,7 +54,7 @@ export function AllCoursesTeacher(): JSX.Element {
<>
<Title title={t('title')}></Title>
<Grid container direction={'column'} style={{marginTop: '1rem', width:'100vw', height: '80vh'}}>
<SideScrollableCourses courses={courses}></SideScrollableCourses>
<SideScrollableCourses courses={courses} projects={projects}></SideScrollableCourses>
<Dialog open={open} onClose={handleClose}>
<DialogTitle>{t('courseForm')}</DialogTitle>
<form style={{ margin: "2rem" }} onSubmit={handleSubmit}>
Expand Down
58 changes: 2 additions & 56 deletions frontend/src/components/Courses/CourseUtilComponents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,14 @@ import {
} from "@mui/material";
import {
Course,
Project,
ProjectDetail,
apiHost,
getIdFromLink,
getNearestFutureDate,
} from "./CourseUtils";
import { Link, useNavigate, useLocation } from "react-router-dom";
import { useState, useEffect, useMemo } from "react";
import { useTranslation } from "react-i18next";
import debounce from "debounce";
import { authenticatedFetch } from "../../utils/authenticated-fetch";

/**
* @param text - The text to be displayed
Expand Down Expand Up @@ -79,9 +76,10 @@ export function SearchBox({
* @returns A component to display courses in horizontal scroller where each course is a card containing its name.
*/
export function SideScrollableCourses({
courses,
courses,projects
}: {
courses: Course[];
projects: {[courseId: string]: ProjectDetail[];};
}): JSX.Element {
//const navigate = useNavigate();
const location = useLocation();
Expand All @@ -101,9 +99,6 @@ export function SideScrollableCourses({
const [teacherNameFilter, setTeacherNameFilter] = useState(
initialTeacherNameFilter
);
const [projects, setProjects] = useState<{
[courseId: string]: ProjectDetail[];
}>({});

const debouncedHandleSearchChange = useMemo(
() =>
Expand Down Expand Up @@ -150,55 +145,6 @@ export function SideScrollableCourses({
setTeacherNameFilter(newTeacherNameFilter);
};

useEffect(() => {
// Fetch projects for each course
const fetchProjects = async () => {
const projectPromises = courses.map((course) =>
authenticatedFetch(
`${apiHost}/projects?course_id=${getIdFromLink(course.course_id)}`
).then((response) => response.json())
);

const projectResults = await Promise.all(projectPromises);
const projectsMap: { [courseId: string]: ProjectDetail[] } = {};

projectResults.forEach((result, index) => {
const detailProjectPromises = result.data.map(async (item: Project) => {
const projectRes = await authenticatedFetch(item.project_id);
if (projectRes.status !== 200) {
throw new Response("Failed to fetch project data", {
status: projectRes.status,
});
}
const projectJson = await projectRes.json();
const projectData = projectJson.data;
let projectDeadlines = [];
if (projectData.deadlines) {
projectDeadlines = projectData.deadlines.map(
([description, dateString]: [string, string]) => ({
description,
date: new Date(dateString),
})
);
}
const project: ProjectDetail = {
...item,
deadlines: projectDeadlines,
};
return project;
});
Promise.all(detailProjectPromises).then((projects) => {
projectsMap[getIdFromLink(courses[index].course_id)] = projects;
setProjects({ ...projectsMap });
});
});

setProjects(projectsMap);
};

fetchProjects();
}, [courses]);

const filteredCourses = courses.filter(
(course) =>
course.name.toLowerCase().includes(searchTerm.toLowerCase()) &&
Expand Down
52 changes: 51 additions & 1 deletion frontend/src/components/Courses/CourseUtils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,59 @@ const fetchData = async (url: string, params?: URLSearchParams) => {

export const dataLoaderCourses = async () => {
//const params = new URLSearchParams({ 'teacher': loggedInUid() });
return fetchData(`courses`);

const courses = await fetchData(`courses`);
const projects = await fetchProjectsCourse(courses);
for( const c of courses){
const teacher = await fetchData(`users/${c.teacher}`)
c.teacher = teacher.display_name
}
return {courses, projects}
};

/**
* Fetch the projects for the Course component
* @param courses - All the courses
* @returns the projects
*/
export async function fetchProjectsCourse (courses:Course[]) {
const projectPromises = courses.map((course) =>
authenticatedFetch(
`${apiHost}/projects?course_id=${getIdFromLink(course.course_id)}`
).then((response) => response.json())
);

const projectResults = await Promise.all(projectPromises);
const projectsMap: { [courseId: string]: ProjectDetail[] } = {};
for await (const [index, result] of projectResults.entries()) {
projectsMap[getIdFromLink(courses[index].course_id)] = await Promise.all(result.data.map(async (item: Project) => {
const projectRes = await authenticatedFetch(item.project_id);
if (projectRes.status !== 200) {
throw new Response("Failed to fetch project data", {
status: projectRes.status,
});
}
const projectJson = await projectRes.json();
const projectData = projectJson.data;
let projectDeadlines = [];
if (projectData.deadlines) {
projectDeadlines = projectData.deadlines.map(
([description, dateString]: [string, string]) => ({
description,
date: new Date(dateString),
})
);
}
const project: ProjectDetail = {
...item,
deadlines: projectDeadlines,
};
return project;
}));
}
return { ...projectsMap };
}

const dataLoaderCourse = async (courseId: string) => {
return fetchData(`courses/${courseId}`);
};
Expand Down

0 comments on commit 1d0b4f8

Please sign in to comment.