diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 8fc1e44f..d8c820b3 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -23,6 +23,7 @@ "i18next-browser-languagedetector": "^7.2.0", "i18next-http-backend": "^2.5.0", "jszip": "^3.10.1", + "prettier": "^3.2.5", "react": "^18.2.0", "react-dom": "^18.2.0", "react-i18next": "^14.1.0", @@ -6450,6 +6451,20 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", + "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index b1903b29..8c03f123 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -15,8 +15,8 @@ "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", "@mui/icons-material": "^5.15.15", - "@mui/material": "^5.15.15", "@mui/lab": "^5.0.0-alpha.170", + "@mui/material": "^5.15.15", "@mui/styled-engine-sc": "^6.0.0-alpha.16", "@mui/x-data-grid": "^7.1.1", "@mui/x-date-pickers": "^7.1.1", @@ -27,6 +27,7 @@ "i18next-browser-languagedetector": "^7.2.0", "i18next-http-backend": "^2.5.0", "jszip": "^3.10.1", + "prettier": "^3.2.5", "react": "^18.2.0", "react-dom": "^18.2.0", "react-i18next": "^14.1.0", @@ -37,10 +38,10 @@ }, "devDependencies": { "@types/downloadjs": "^1.4.6", + "@types/history": "^4.7.11", "@types/react": "^18.2.55", "@types/react-dom": "^18.2.19", "@types/react-router-dom": "^5.3.3", - "@types/history": "^4.7.11", "@types/scheduler": "^0.23.0", "@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/parser": "^6.21.0", diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 5149d2e9..6fe3217b 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,40 +1,67 @@ -import { Route, RouterProvider, createBrowserRouter, createRoutesFromElements } from "react-router-dom"; +import { + Route, + RouterProvider, + createBrowserRouter, + createRoutesFromElements, +} from "react-router-dom"; import Layout from "./components/Header/Layout"; import { AllCoursesTeacher } from "./components/Courses/AllCoursesTeacher"; import { CourseDetailTeacher } from "./components/Courses/CourseDetailTeacher"; -import { dataLoaderCourseDetail, dataLoaderCourses } from "./components/Courses/CourseUtils"; +import { + dataLoaderCourseDetail, + dataLoaderCourses, +} from "./components/Courses/CourseUtils"; import LanguagePath from "./components/LanguagePath"; import ProjectView from "./pages/project/projectView/ProjectView"; import { ErrorBoundary } from "./pages/error/ErrorBoundary.tsx"; import ProjectCreateHome from "./pages/create_project/ProjectCreateHome.tsx"; import SubmissionsOverview from "./pages/submission_overview/SubmissionsOverview.tsx"; -import {fetchProjectPage} from "./utils/fetches/FetchProjects.tsx"; +import { fetchProjectPage } from "./utils/fetches/FetchProjects.tsx"; import HomePages from "./pages/home/HomePages.tsx"; import ProjectOverView from "./pages/project/projectOverview.tsx"; -import {fetchMe} from "./utils/fetches/FetchMe.ts"; +import { fetchMe } from "./utils/fetches/FetchMe.ts"; const router = createBrowserRouter( createRoutesFromElements( - } errorElement={} loader={fetchMe}> - } loader={fetchProjectPage}/> - }> + } + errorElement={} + loader={fetchMe} + > + } loader={fetchProjectPage} /> + }> } loader={fetchProjectPage} /> - }/> + } + /> - }> - + }> - } loader={dataLoaderCourses}/> - } loader={dataLoaderCourseDetail} /> + } + loader={dataLoaderCourses} + /> + } + loader={dataLoaderCourseDetail} + /> - } loader={fetchProjectPage}/> + } + loader={fetchProjectPage} + /> } /> - - ) + , + ), ); /** diff --git a/frontend/src/components/Header/Header.tsx b/frontend/src/components/Header/Header.tsx index 109f60fa..47d26da9 100644 --- a/frontend/src/components/Header/Header.tsx +++ b/frontend/src/components/Header/Header.tsx @@ -10,27 +10,27 @@ import { Drawer, Grid, ListItemButton, - ListItemText + ListItemText, } from "@mui/material"; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from "react-i18next"; import MenuIcon from "@mui/icons-material/Menu"; -import React,{useState } from "react"; +import React, { useState } from "react"; import LanguageIcon from "@mui/icons-material/Language"; -import AccountCircleIcon from '@mui/icons-material/AccountCircle'; -import {Link} from "react-router-dom"; +import AccountCircleIcon from "@mui/icons-material/AccountCircle"; +import { Link } from "react-router-dom"; import { TitlePortal } from "./TitlePortal"; -import {Me} from "../../types/me.ts"; +import { Me } from "../../types/me.ts"; -interface HeaderProps{ - me:Me +interface HeaderProps { + me: Me; } /** * The header component for the application that will be rendered at the top of the page. * @returns - The header component */ -export function Header({me}:HeaderProps): JSX.Element { - const API_URL = import.meta.env.VITE_APP_API_HOST - const { t, i18n } = useTranslation('translation', { keyPrefix: 'header' }); +export function Header({ me }: HeaderProps): JSX.Element { + const API_URL = import.meta.env.VITE_APP_API_HOST; + const { t, i18n } = useTranslation("translation", { keyPrefix: "header" }); const [languageMenuAnchor, setLanguageMenuAnchor] = useState(null); @@ -49,10 +49,12 @@ export function Header({me}:HeaderProps): JSX.Element { const [open, setOpen] = useState(false); const [listItems, setListItems] = useState([ - { link: "/", text: t("homepage") } + { link: "/", text: t("homepage") }, ]); - const [anchorEl, setAnchorEl] = React.useState(null); + const [anchorEl, setAnchorEl] = React.useState( + null, + ); const handleClick = (event: React.MouseEvent) => { setAnchorEl(event.currentTarget); @@ -62,12 +64,11 @@ export function Header({me}:HeaderProps): JSX.Element { const baseItems = [{ link: "/", text: t("homepage") }]; const additionalItems = [ { link: "/projects", text: t("myProjects") }, - { link: "/courses", text: t("myCourses") } + { link: "/courses", text: t("myCourses") }, ]; if (me.role !== "UNKNOWN") { setListItems([...baseItems, ...additionalItems]); - } - else { + } else { setListItems(baseItems); } }, [me, t]); @@ -76,15 +77,23 @@ export function Header({me}:HeaderProps): JSX.Element { - setOpen(!open)} sx={{ color: "white", marginLeft: 0 }}> - + setOpen(!open)} + sx={{ color: "white", marginLeft: 0 }} + > + - + {me.role !== "UNKNOWN" && ( <> - + - + {me.display_name} @@ -93,10 +102,14 @@ export function Header({me}:HeaderProps): JSX.Element { open={Boolean(anchorEl)} onClose={() => setAnchorEl(null)} > - + {me.display_name} - - window.location.href = `${API_URL}/logout`}>{t("logout")} + + (window.location.href = `${API_URL}/logout`)} + > + {t("logout")} + > )} @@ -122,7 +135,11 @@ export function Header({me}:HeaderProps): JSX.Element { - setOpen(false)} listItems={listItems}/> + setOpen(false)} + listItems={listItems} + /> ); } @@ -134,26 +151,46 @@ export function Header({me}:HeaderProps): JSX.Element { * @param listItems - Array of objects representing the list items in the drawer menu. * @returns The Side Bar */ -function DrawerMenu({ open, onClose, listItems }: { open: boolean, onClose: () => void, listItems: { link: string, text: string }[] }) { - +function DrawerMenu({ + open, + onClose, + listItems, +}: { + open: boolean; + onClose: () => void; + listItems: { link: string; text: string }[]; +}) { return ( - + - - + + {listItems.map((listItem, index) => ( - - + + ))} diff --git a/frontend/src/components/Header/Layout.tsx b/frontend/src/components/Header/Layout.tsx index e07bfa7f..b7852b91 100644 --- a/frontend/src/components/Header/Layout.tsx +++ b/frontend/src/components/Header/Layout.tsx @@ -1,13 +1,13 @@ -import {Outlet, useLoaderData} from "react-router-dom"; +import { Outlet, useLoaderData } from "react-router-dom"; import { Header } from "./Header.tsx"; -import {Me} from "../../types/me.ts" +import { Me } from "../../types/me.ts"; /** * Basic layout component that will be used on all routes. * @returns The Layout component */ export default function Layout(): JSX.Element { - const meData:Me = useLoaderData() as Me + const meData: Me = useLoaderData() as Me; return ( <> @@ -15,4 +15,4 @@ export default function Layout(): JSX.Element { > ); -} \ No newline at end of file +} diff --git a/frontend/src/components/Header/Login.tsx b/frontend/src/components/Header/Login.tsx index 906e7715..3f068ff2 100644 --- a/frontend/src/components/Header/Login.tsx +++ b/frontend/src/components/Header/Login.tsx @@ -1,6 +1,6 @@ -import {Button} from "@mui/material"; -import { Link } from 'react-router-dom'; -import {useTranslation} from "react-i18next"; +import { Button } from "@mui/material"; +import { Link } from "react-router-dom"; +import { useTranslation } from "react-i18next"; const CLIENT_ID = import.meta.env.VITE_APP_CLIENT_ID; const REDIRECT_URI = encodeURI(import.meta.env.VITE_APP_API_HOST + "/auth"); @@ -12,7 +12,12 @@ const TENANT_ID = import.meta.env.VITE_APP_TENANT_ID; */ export function LoginButton(): JSX.Element { const link = `https://login.microsoftonline.com/${TENANT_ID}/oauth2/v2.0/authorize?prompt=select_account&response_type=code&client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}&scope=.default`; - const { t } = useTranslation('translation', { keyPrefix: 'home' }); + const { t } = useTranslation("translation", { keyPrefix: "home" }); - return {t('login', 'Login')} + return ( + + {" "} + {t("login", "Login")} + + ); } diff --git a/frontend/src/pages/home/Home.tsx b/frontend/src/pages/home/Home.tsx index 3e64e9bd..953060ff 100644 --- a/frontend/src/pages/home/Home.tsx +++ b/frontend/src/pages/home/Home.tsx @@ -1,47 +1,52 @@ import { useTranslation } from "react-i18next"; import { Container, Typography, Box } from "@mui/material"; -import {LoginButton} from "../../components/Header/Login.tsx"; +import { LoginButton } from "../../components/Header/Login.tsx"; /** * This component is the home page component that will be rendered when on the index route. * @returns - The home page component */ export default function Home() { - const { t } = useTranslation('translation', { keyPrefix: 'home' }); + const { t } = useTranslation("translation", { keyPrefix: "home" }); return ( - + - + Peristerónas - - {t('welcomeDescription', 'Welcome to Peristeronas.')} + + {t("welcomeDescription", "Welcome to Peristeronas.")} - + - ); + + ); } diff --git a/frontend/src/pages/home/HomePage.tsx b/frontend/src/pages/home/HomePage.tsx index b3ba4bd3..e8df53ed 100644 --- a/frontend/src/pages/home/HomePage.tsx +++ b/frontend/src/pages/home/HomePage.tsx @@ -1,22 +1,31 @@ import { useTranslation } from "react-i18next"; -import {Card, CardContent, Typography, Grid, Container, Badge} from '@mui/material'; -import { DateCalendar } from '@mui/x-date-pickers/DateCalendar'; -import {DayCalendarSkeleton, LocalizationProvider} from '@mui/x-date-pickers'; -import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; -import React, {useState} from 'react'; -import dayjs, {Dayjs} from "dayjs"; -import { PickersDay, PickersDayProps } from '@mui/x-date-pickers/PickersDay'; -import {ProjectDeadlineCard} from "../project/projectDeadline/ProjectDeadlineCard.tsx"; -import {ProjectDeadline} from "../project/projectDeadline/ProjectDeadline.tsx"; -import {useLoaderData} from "react-router-dom"; -import {Me} from "../../types/me.ts"; +import { + Card, + CardContent, + Typography, + Grid, + Container, + Badge, +} from "@mui/material"; +import { DateCalendar } from "@mui/x-date-pickers/DateCalendar"; +import { DayCalendarSkeleton, LocalizationProvider } from "@mui/x-date-pickers"; +import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; +import React, { useState } from "react"; +import dayjs, { Dayjs } from "dayjs"; +import { PickersDay, PickersDayProps } from "@mui/x-date-pickers/PickersDay"; +import { ProjectDeadlineCard } from "../project/projectDeadline/ProjectDeadlineCard.tsx"; +import { ProjectDeadline } from "../project/projectDeadline/ProjectDeadline.tsx"; +import { useLoaderData } from "react-router-dom"; +import { Me } from "../../types/me.ts"; interface DeadlineInfoProps { selectedDay: Dayjs; deadlines: ProjectDeadline[]; } -type ExtendedPickersDayProps = PickersDayProps & { highlightedDays?: number[] }; +type ExtendedPickersDayProps = PickersDayProps & { + highlightedDays?: number[]; +}; /** * Displays the deadlines on a given day @@ -24,23 +33,27 @@ type ExtendedPickersDayProps = PickersDayProps & { highlightedDays?: numb * @param deadlines - All the deadlines to consider * @returns Element */ -const DeadlineInfo: React.FC = ({ selectedDay, deadlines }) => { - const { t } = useTranslation('translation', { keyPrefix: 'student' }); +const DeadlineInfo: React.FC = ({ + selectedDay, + deadlines, +}) => { + const { t } = useTranslation("translation", { keyPrefix: "student" }); const deadlinesOnSelectedDay = deadlines.filter( - project => (project.deadline && dayjs(project.deadline).isSame(selectedDay, 'day')) + (project) => + project.deadline && dayjs(project.deadline).isSame(selectedDay, "day"), ); //list of the corresponding assignment return ( {deadlinesOnSelectedDay.length === 0 ? ( - + - - {t('noDeadline')} - + {t("noDeadline")} - ) : } + ) : ( + + )} ); }; @@ -50,47 +63,54 @@ const DeadlineInfo: React.FC = ({ selectedDay, deadlines }) = * @param props - The day and the deadlines * @returns - The ServerDay component that displays a badge for specific days */ -function ServerDay(props: PickersDayProps & { highlightedDays?: number[] }) { +function ServerDay( + props: PickersDayProps & { highlightedDays?: number[] }, +) { const { highlightedDays = [], day, outsideCurrentMonth, ...other } = props; const isSelected = - !props.outsideCurrentMonth && highlightedDays.indexOf(props.day.date()) >= 0; + !props.outsideCurrentMonth && + highlightedDays.indexOf(props.day.date()) >= 0; return ( - + ); } -const handleMonthChange =( +const handleMonthChange = ( date: Dayjs, - projects:ProjectDeadline[], + projects: ProjectDeadline[], setHighlightedDays: React.Dispatch>, ) => { - setHighlightedDays([]); // projects are now only fetched on page load - const hDays:number[] = [] - projects.map((project, ) => { - if(project.deadline && project.deadline.getMonth() == date.month() && project.deadline.getFullYear() == date.year()){ - hDays.push(project.deadline.getDate()) + const hDays: number[] = []; + projects.map((project) => { + if ( + project.deadline && + project.deadline.getMonth() == date.month() && + project.deadline.getFullYear() == date.year() + ) { + hDays.push(project.deadline.getDate()); } - - } - ); - setHighlightedDays(hDays) - + }); + setHighlightedDays(hDays); }; /** @@ -98,49 +118,45 @@ const handleMonthChange =( * @returns - The home page component */ export default function HomePage() { - const { t } = useTranslation('translation', { keyPrefix: 'student' }); + const { t } = useTranslation("translation", { keyPrefix: "student" }); const [highlightedDays, setHighlightedDays] = React.useState([]); const [selectedDay, setSelectedDay] = useState(dayjs(Date.now())); const loader = useLoaderData() as { - projects: ProjectDeadline[], - me: Me - } - const projects = loader.projects + projects: ProjectDeadline[]; + me: Me; + }; + const projects = loader.projects; // Update selectedDay state when a day is selected const handleDaySelect = (day: Dayjs) => { setSelectedDay(day); }; const futureProjects = projects - .filter((p) => (p.deadline && dayjs(dayjs()).isBefore(p.deadline))) + .filter((p) => p.deadline && dayjs(dayjs()).isBefore(p.deadline)) .sort((a, b) => dayjs(a.deadline).diff(dayjs(b.deadline))) - .slice(0, 3) // only show the first 3 + .slice(0, 3); // only show the first 3 const pastDeadlines = projects - .filter((p) => p.deadline && (dayjs()).isAfter(p.deadline)) + .filter((p) => p.deadline && dayjs().isAfter(p.deadline)) .sort((a, b) => dayjs(b.deadline).diff(dayjs(a.deadline))) - .slice(0, 3) // only show the first 3 - const noDeadlineProject = projects.filter((p) => p.deadline === undefined) + .slice(0, 3); // only show the first 3 + const noDeadlineProject = projects.filter((p) => p.deadline === undefined); return ( - + - - {t('myProjects')} - - {futureProjects.length + noDeadlineProject.length > 0? ( + {t("myProjects")} + {futureProjects.length + noDeadlineProject.length > 0 ? ( <> - + > ) : ( - - {t('no_projects')} - + {t("no_projects")} )} @@ -148,17 +164,12 @@ export default function HomePage() { - - - {t('deadlines')} - + {t("deadlines")} {pastDeadlines.length > 0 ? ( ) : ( - - {t('no_projects')} - + {t("no_projects")} )} @@ -169,7 +180,9 @@ export default function HomePage() { { handleMonthChange(date, projects, setHighlightedDays) }} + onMonthChange={(date: Dayjs) => { + handleMonthChange(date, projects, setHighlightedDays); + }} onChange={handleDaySelect} renderLoading={() => } slots={{ @@ -184,14 +197,12 @@ export default function HomePage() { - {t('deadlinesOnDay')} {selectedDay.format('MMMM D, YYYY')} + {t("deadlinesOnDay")} {selectedDay.format("MMMM D, YYYY")} - - ); diff --git a/frontend/src/pages/home/HomePages.tsx b/frontend/src/pages/home/HomePages.tsx index b950f724..6feaca41 100644 --- a/frontend/src/pages/home/HomePages.tsx +++ b/frontend/src/pages/home/HomePages.tsx @@ -10,11 +10,11 @@ import {Me} from "../../types/me.ts" */ export default function HomePages() { const loader = useLoaderData() as { - projects: ProjectDeadline[], - me: Me - } - const me = loader.me.role - if (me === 'UNKNOWN') { + projects: ProjectDeadline[]; + me: Me; + }; + const me = loader.me.role; + if (me === "UNKNOWN") { return ; } else { return ; diff --git a/frontend/src/types/me.ts b/frontend/src/types/me.ts index 8ee4bcc7..77b57d62 100644 --- a/frontend/src/types/me.ts +++ b/frontend/src/types/me.ts @@ -1,6 +1,5 @@ export interface Me { - role:string, - display_name:string, - uid:string - -} \ No newline at end of file + role: string; + display_name: string; + uid: string; +} diff --git a/frontend/src/utils/fetches/FetchMe.ts b/frontend/src/utils/fetches/FetchMe.ts index dc18dfdd..5c1607e7 100644 --- a/frontend/src/utils/fetches/FetchMe.ts +++ b/frontend/src/utils/fetches/FetchMe.ts @@ -1,17 +1,16 @@ - export const fetchMe = async () => { - const API_URL = import.meta.env.VITE_APP_API_HOST + const API_URL = import.meta.env.VITE_APP_API_HOST; try { - const response = await fetch(`${API_URL}/me`,{ - credentials: 'include' - }) + const response = await fetch(`${API_URL}/me`, { + credentials: "include", + }); if (response.status == 200) { - const data = await response.json() - return data.data + const data = await response.json(); + return data.data; } else { - return {role:"UNKNOWN"} + return { role: "UNKNOWN" }; } } catch (e) { - return {role:"UNKNOWN"} + return { role: "UNKNOWN" }; } -} \ No newline at end of file +}; diff --git a/frontend/src/utils/fetches/FetchProjects.tsx b/frontend/src/utils/fetches/FetchProjects.tsx index cbe1eaac..57535f1c 100644 --- a/frontend/src/utils/fetches/FetchProjects.tsx +++ b/frontend/src/utils/fetches/FetchProjects.tsx @@ -1,99 +1,122 @@ import { fetchMe } from "./FetchMe.ts"; -import {Project, ProjectDeadline, ShortSubmission} from "../../pages/project/projectDeadline/ProjectDeadline.tsx"; -const API_URL = import.meta.env.VITE_APP_API_HOST +import { + Project, + ProjectDeadline, + ShortSubmission, +} from "../../pages/project/projectDeadline/ProjectDeadline.tsx"; +const API_URL = import.meta.env.VITE_APP_API_HOST; export const fetchProjectPage = async () => { - const projects = await fetchProjects() - const me = await fetchMe() - return {projects, me} -} + const projects = await fetchProjects(); + const me = await fetchMe(); + return { projects, me }; +}; export const fetchProjects = async () => { - - try{ + try { const response = await fetch(`${API_URL}/projects`, { - credentials: 'include' - - }) + credentials: "include", + }); const jsonData = await response.json(); - let formattedData: ProjectDeadline[] = await Promise.all( jsonData.data.map(async (item:Project) => { - try{ - const url_split = item.project_id.split('/') - const project_id = url_split[url_split.length -1] - const response_submissions = await (await fetch(encodeURI(`${API_URL}/submissions?project_id=${project_id}`), { - credentials: 'include' + let formattedData: ProjectDeadline[] = await Promise.all( + jsonData.data.map(async (item: Project) => { + try { + const url_split = item.project_id.split("/"); + const project_id = url_split[url_split.length - 1]; + const response_submissions = await ( + await fetch( + encodeURI(`${API_URL}/submissions?project_id=${project_id}`), + { + credentials: "include", + }, + ) + ).json(); - })).json() - - //get the latest submission - const latest_submission = response_submissions.data.map((submission:ShortSubmission) => ({ - submission_id: submission.submission_id,//this is the path - submission_time: new Date(submission.submission_time), - submission_status: submission.submission_status, - grading: submission.grading - } - )).sort((a:ShortSubmission, b:ShortSubmission) => b.submission_time.getTime() - a.submission_time.getTime())[0]; - // fetch the course id of the project - const project_item = await (await fetch(encodeURI(`${API_URL}/projects/${project_id}`), { credentials: 'include' - })).json() + //get the latest submission + const latest_submission = response_submissions.data + .map((submission: ShortSubmission) => ({ + submission_id: submission.submission_id, //this is the path + submission_time: new Date(submission.submission_time), + submission_status: submission.submission_status, + grading: submission.grading, + })) + .sort( + (a: ShortSubmission, b: ShortSubmission) => + b.submission_time.getTime() - a.submission_time.getTime(), + )[0]; + // fetch the course id of the project + const project_item = await ( + await fetch(encodeURI(`${API_URL}/projects/${project_id}`), { + credentials: "include", + }) + ).json(); - //fetch the course - const response_courses = await (await fetch(encodeURI(`${API_URL}/courses/${project_item.data.course_id}`), { - credentials: 'include' - })).json() - const course = { - course_id: response_courses.data.course_id, - name: response_courses.data.name, - teacher: response_courses.data.teacher, - ufora_id: response_courses.data.ufora_id - } - if(project_item.data.deadlines){ - return project_item.data.deadlines.map((d:string[]) => { - return { + //fetch the course + const response_courses = await ( + await fetch( + encodeURI(`${API_URL}/courses/${project_item.data.course_id}`), + { + credentials: "include", + }, + ) + ).json(); + const course = { + course_id: response_courses.data.course_id, + name: response_courses.data.name, + teacher: response_courses.data.teacher, + ufora_id: response_courses.data.ufora_id, + }; + if (project_item.data.deadlines) { + return project_item.data.deadlines.map((d: string[]) => { + return { + project_id: project_id, + title: project_item.data.title, + description: project_item.data.description, + assignment_file: project_item.data.assignment_file, + deadline: new Date(d[1]), + deadline_description: d[0], + course_id: Number(project_item.data.course_id), + visible_for_students: Boolean( + project_item.data.visible_for_students, + ), + archived: Boolean(project_item.data.archived), + test_path: project_item.data.test_path, + script_name: project_item.data.script_name, + regex_expressions: project_item.data.regex_expressions, + short_submission: latest_submission, + course: course, + }; + }); + } + // contains no dealine: + return [ + { project_id: project_id, title: project_item.data.title, description: project_item.data.description, assignment_file: project_item.data.assignment_file, - deadline: new Date(d[1]), - deadline_description: d[0], + deadline: undefined, + deadline_description: undefined, course_id: Number(project_item.data.course_id), - visible_for_students: Boolean(project_item.data.visible_for_students), + visible_for_students: Boolean( + project_item.data.visible_for_students, + ), archived: Boolean(project_item.data.archived), test_path: project_item.data.test_path, script_name: project_item.data.script_name, regex_expressions: project_item.data.regex_expressions, short_submission: latest_submission, - course: course - } - }) + course: course, + }, + ]; + } catch (e) { + return []; } - // contains no dealine: - return [{ - project_id: project_id, - title: project_item.data.title, - description: project_item.data.description, - assignment_file: project_item.data.assignment_file, - deadline: undefined, - deadline_description: undefined, - course_id: Number(project_item.data.course_id), - visible_for_students: Boolean(project_item.data.visible_for_students), - archived: Boolean(project_item.data.archived), - test_path: project_item.data.test_path, - script_name: project_item.data.script_name, - regex_expressions: project_item.data.regex_expressions, - short_submission: latest_submission, - course: course - }] - - }catch (e){ - return [] - } - } - - )); - formattedData = formattedData.flat() - return formattedData + }), + ); + formattedData = formattedData.flat(); + return formattedData; } catch (e) { - return [] + return []; } -} +};