From b1aad9e903e5b2aeb0a9086e0bf1407c4e3828a8 Mon Sep 17 00:00:00 2001 From: warre Date: Tue, 23 Apr 2024 11:45:28 +0200 Subject: [PATCH 01/12] login buttons --- frontend/src/components/Header/Header.tsx | 55 ++++++++++++++++------- frontend/src/components/Header/Login.tsx | 4 +- frontend/src/pages/home/Home.tsx | 9 ++-- 3 files changed, 46 insertions(+), 22 deletions(-) diff --git a/frontend/src/components/Header/Header.tsx b/frontend/src/components/Header/Header.tsx index b15d188c..e140bcc3 100644 --- a/frontend/src/components/Header/Header.tsx +++ b/frontend/src/components/Header/Header.tsx @@ -12,10 +12,11 @@ import { ListItemButton, ListItemText } from "@mui/material"; -import MenuIcon from "@mui/icons-material/Menu"; import { useTranslation } from 'react-i18next'; -import { useEffect, useState } from "react"; +import MenuIcon from "@mui/icons-material/Menu"; +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 { TitlePortal } from "./TitlePortal"; import {LoginButton} from "./Login"; @@ -47,19 +48,27 @@ export function Header(): JSX.Element { { link: "/", text: t("homepage") } ]); - useEffect(() => { - const baseItems = [{ link: "/", text: t("homepage") }]; - const additionalItems = [ - { link: "/projects", text: t("myProjects") }, - { link: "/courses", text: t("myCourses") } - ]; - if (isLoggedIn()) { - setListItems([...baseItems, ...additionalItems]); - } - else { - setListItems(baseItems); - } - }, [t]); + const [anchorEl, setAnchorEl] = React.useState(null); + + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + + const handleClose = () => { + setAnchorEl(null); + }; + + const baseItems = [{ link: "/", text: t("homepage") }]; + const additionalItems = [ + { link: "/projects", text: t("myProjects") }, + { link: "/courses", text: t("myCourses") } + ]; + if (isLoggedIn()) { + setListItems([...baseItems, ...additionalItems]); + } + else { + setListItems(baseItems); + } return ( @@ -69,6 +78,22 @@ export function Header(): JSX.Element { + + + + {"Test Name"} + + + + + {"Test Name"} + + Logout +
diff --git a/frontend/src/components/Header/Login.tsx b/frontend/src/components/Header/Login.tsx index d06bf021..906e7715 100644 --- a/frontend/src/components/Header/Login.tsx +++ b/frontend/src/components/Header/Login.tsx @@ -1,5 +1,6 @@ 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"); @@ -11,6 +12,7 @@ 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' }); - return + return } diff --git a/frontend/src/pages/home/Home.tsx b/frontend/src/pages/home/Home.tsx index 54de654d..3e64e9bd 100644 --- a/frontend/src/pages/home/Home.tsx +++ b/frontend/src/pages/home/Home.tsx @@ -1,6 +1,6 @@ import { useTranslation } from "react-i18next"; -import { Button, Container, Typography, Box } from "@mui/material"; -import {Link } from "react-router-dom"; +import { Container, Typography, Box } from "@mui/material"; +import {LoginButton} from "../../components/Header/Login.tsx"; /** * This component is the home page component that will be rendered when on the index route. @@ -8,7 +8,6 @@ import {Link } from "react-router-dom"; */ export default function Home() { const { t } = useTranslation('translation', { keyPrefix: 'home' }); - const login_redirect:string =import.meta.env.VITE_LOGIN_LINK return ( {t('welcomeDescription', 'Welcome to Peristeronas.')} - + ); } From 057e9f0d77b2fd38b7eef7a60024264507013a7e Mon Sep 17 00:00:00 2001 From: warre Date: Tue, 23 Apr 2024 13:46:51 +0200 Subject: [PATCH 02/12] Me auth --- frontend/public/locales/en/translation.json | 3 +- frontend/public/locales/nl/translation.json | 3 +- frontend/src/App.tsx | 5 +- frontend/src/components/Header/Header.tsx | 81 +++++++++---------- frontend/src/components/Header/Layout.tsx | 7 +- frontend/src/pages/home/HomePage.tsx | 3 +- frontend/src/pages/home/HomePages.tsx | 5 +- frontend/src/types/me.ts | 6 ++ frontend/src/utils/fetches/FetchMe.ts | 17 ++++ .../fetches}/FetchProjects.tsx | 19 +---- 10 files changed, 82 insertions(+), 67 deletions(-) create mode 100644 frontend/src/types/me.ts create mode 100644 frontend/src/utils/fetches/FetchMe.ts rename frontend/src/{pages/project => utils/fetches}/FetchProjects.tsx (90%) diff --git a/frontend/public/locales/en/translation.json b/frontend/public/locales/en/translation.json index 212bd4cc..af275341 100644 --- a/frontend/public/locales/en/translation.json +++ b/frontend/public/locales/en/translation.json @@ -7,7 +7,8 @@ "home": "Home", "tag": "en", "homepage": "Homepage", - "projectUploadForm": "Project upload form" + "projectUploadForm": "Project upload form", + "logout": "Logout" }, "home": { "home": "Home", diff --git a/frontend/public/locales/nl/translation.json b/frontend/public/locales/nl/translation.json index 7addc7e6..21d33f59 100644 --- a/frontend/public/locales/nl/translation.json +++ b/frontend/public/locales/nl/translation.json @@ -6,7 +6,8 @@ "home": "Home", "tag": "nl", "homepage": "Homepage", - "projectUploadForm": "Project uploaden" + "projectUploadForm": "Project uploaden", + "logout": "Afmelden" }, "home": { "home": "Home", diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index efe17395..5149d2e9 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -8,13 +8,14 @@ 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 "./pages/project/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"; const router = createBrowserRouter( createRoutesFromElements( - } errorElement={}> + } errorElement={} loader={fetchMe}> } loader={fetchProjectPage}/> }> } loader={fetchProjectPage} /> diff --git a/frontend/src/components/Header/Header.tsx b/frontend/src/components/Header/Header.tsx index e140bcc3..f047a9e3 100644 --- a/frontend/src/components/Header/Header.tsx +++ b/frontend/src/components/Header/Header.tsx @@ -17,15 +17,19 @@ import MenuIcon from "@mui/icons-material/Menu"; 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 {Link} from "react-router-dom"; import { TitlePortal } from "./TitlePortal"; -import {LoginButton} from "./Login"; +import {me} from "../../types/me.ts"; +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(): JSX.Element { +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); @@ -54,21 +58,19 @@ export function Header(): JSX.Element { setAnchorEl(event.currentTarget); }; - const handleClose = () => { - setAnchorEl(null); - }; - - const baseItems = [{ link: "/", text: t("homepage") }]; - const additionalItems = [ - { link: "/projects", text: t("myProjects") }, - { link: "/courses", text: t("myCourses") } - ]; - if (isLoggedIn()) { - setListItems([...baseItems, ...additionalItems]); - } - else { - setListItems(baseItems); - } + React.useEffect(() => { + const baseItems = [{ link: "/", text: t("homepage") }]; + const additionalItems = [ + { link: "/projects", text: t("myProjects") }, + { link: "/courses", text: t("myCourses") } + ]; + if (me.role !== "UNKNOWN") { + setListItems([...baseItems, ...additionalItems]); + } + else { + setListItems(baseItems); + } + }, [me, t]); return ( @@ -78,23 +80,26 @@ export function Header(): JSX.Element { - - - - {"Test Name"} - - - - - {"Test Name"} - - Logout - - + {me.role !== "UNKNOWN" && ( + <> + + + + {me.display_name} + + + setAnchorEl(null)} + > + + {me.display_name} + + window.location.href = `${API_URL}/logout`}>{t("logout")} + + + )}
@@ -121,12 +126,6 @@ export function Header(): JSX.Element { ); } -/** - * @returns Whether a user is logged in or not. - */ -function isLoggedIn() { - return true; -} /** * Renders the drawer menu component. diff --git a/frontend/src/components/Header/Layout.tsx b/frontend/src/components/Header/Layout.tsx index e63283c8..56877564 100644 --- a/frontend/src/components/Header/Layout.tsx +++ b/frontend/src/components/Header/Layout.tsx @@ -1,14 +1,17 @@ -import { Outlet } from "react-router-dom"; +import {Outlet, useLoaderData} from "react-router-dom"; import { Header } from "./Header.tsx"; +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 me = useLoaderData() as me + return ( <> -
+
); diff --git a/frontend/src/pages/home/HomePage.tsx b/frontend/src/pages/home/HomePage.tsx index 0fbb9c45..e6b7c692 100644 --- a/frontend/src/pages/home/HomePage.tsx +++ b/frontend/src/pages/home/HomePage.tsx @@ -9,6 +9,7 @@ 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; @@ -104,7 +105,7 @@ export default function HomePage() { const [selectedDay, setSelectedDay] = useState(dayjs(Date.now())); const loader = useLoaderData() as { projects: ProjectDeadline[], - me: string + me: me } const projects = loader.projects diff --git a/frontend/src/pages/home/HomePages.tsx b/frontend/src/pages/home/HomePages.tsx index 140be48c..a13a0932 100644 --- a/frontend/src/pages/home/HomePages.tsx +++ b/frontend/src/pages/home/HomePages.tsx @@ -2,6 +2,7 @@ import HomePage from './HomePage.tsx'; import Home from "./Home.tsx"; import {useLoaderData} from "react-router-dom"; import {ProjectDeadline} from "../project/projectDeadline/ProjectDeadline.tsx"; +import {me} from "../../types/me.ts" /** * Gives the requested home page based on the login status @@ -10,9 +11,9 @@ import {ProjectDeadline} from "../project/projectDeadline/ProjectDeadline.tsx"; export default function HomePages() { const loader = useLoaderData() as { projects: ProjectDeadline[], - me: string + me: me } - const me = loader.me + const me = loader.me.role if (me === 'UNKNOWN') { return ; } else { diff --git a/frontend/src/types/me.ts b/frontend/src/types/me.ts new file mode 100644 index 00000000..47c14175 --- /dev/null +++ b/frontend/src/types/me.ts @@ -0,0 +1,6 @@ +export interface me{ + role:string, + display_name:string, + uid:string + +} \ No newline at end of file diff --git a/frontend/src/utils/fetches/FetchMe.ts b/frontend/src/utils/fetches/FetchMe.ts new file mode 100644 index 00000000..2db0a20f --- /dev/null +++ b/frontend/src/utils/fetches/FetchMe.ts @@ -0,0 +1,17 @@ + +export const fetchMe = async () => { + const API_URL = import.meta.env.VITE_APP_API_HOST + try { + const response = await fetch(`${API_URL}/me`, { + credentials: 'include' + }) + if(response.status == 200){ + const data = await response.json() + return data.data + }else { + return {role:"UNKNOWN"} + } + } catch (e){ + return {role:"UNKNOWN"} + } +} \ No newline at end of file diff --git a/frontend/src/pages/project/FetchProjects.tsx b/frontend/src/utils/fetches/FetchProjects.tsx similarity index 90% rename from frontend/src/pages/project/FetchProjects.tsx rename to frontend/src/utils/fetches/FetchProjects.tsx index 81444303..cbe1eaac 100644 --- a/frontend/src/pages/project/FetchProjects.tsx +++ b/frontend/src/utils/fetches/FetchProjects.tsx @@ -1,4 +1,5 @@ -import {Project, ProjectDeadline, ShortSubmission} from "./projectDeadline/ProjectDeadline.tsx"; +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 export const fetchProjectPage = async () => { @@ -7,22 +8,6 @@ export const fetchProjectPage = async () => { return {projects, me} } -export const fetchMe = async () => { - try { - const response = await fetch(`${API_URL}/me`, { - credentials: 'include' - }) - if(response.status == 200){ - const data = await response.json() - return data.role - }else { - return "UNKNOWN" - } - } catch (e){ - return "UNKNOWN" - } - -} export const fetchProjects = async () => { try{ From 82bec5e32225c115b75ac2182c851058aeb75c5e Mon Sep 17 00:00:00 2001 From: warre Date: Tue, 23 Apr 2024 13:53:06 +0200 Subject: [PATCH 03/12] me --- frontend/src/components/Header/Layout.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/Header/Layout.tsx b/frontend/src/components/Header/Layout.tsx index 56877564..cc53f984 100644 --- a/frontend/src/components/Header/Layout.tsx +++ b/frontend/src/components/Header/Layout.tsx @@ -7,7 +7,7 @@ import {me} from "../../types/me.ts" * @returns The Layout component */ export default function Layout(): JSX.Element { - const me = useLoaderData() as me + const me:me = useLoaderData() as me return ( <> From 54d798362e9bbf92a97be0f1c3a444a5ec4782d8 Mon Sep 17 00:00:00 2001 From: warre Date: Tue, 23 Apr 2024 13:57:11 +0200 Subject: [PATCH 04/12] me --- frontend/src/components/Header/Layout.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/Header/Layout.tsx b/frontend/src/components/Header/Layout.tsx index cc53f984..90f0c6b6 100644 --- a/frontend/src/components/Header/Layout.tsx +++ b/frontend/src/components/Header/Layout.tsx @@ -7,11 +7,11 @@ import {me} from "../../types/me.ts" * @returns The Layout component */ export default function Layout(): JSX.Element { - const me:me = useLoaderData() as me + const meData:me = useLoaderData() as me return ( <> -
+
); From eb9e816b27052a1617575e2229a94dbd5ae4f70e Mon Sep 17 00:00:00 2001 From: warre Date: Tue, 23 Apr 2024 14:00:20 +0200 Subject: [PATCH 05/12] me changed --- frontend/src/components/Header/Header.tsx | 4 ++-- frontend/src/components/Header/Layout.tsx | 4 ++-- frontend/src/pages/home/HomePage.tsx | 4 ++-- frontend/src/pages/home/HomePages.tsx | 4 ++-- frontend/src/types/me.ts | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/frontend/src/components/Header/Header.tsx b/frontend/src/components/Header/Header.tsx index f047a9e3..0d1c0e8d 100644 --- a/frontend/src/components/Header/Header.tsx +++ b/frontend/src/components/Header/Header.tsx @@ -19,10 +19,10 @@ import LanguageIcon from "@mui/icons-material/Language"; 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 + me:Me } /** * The header component for the application that will be rendered at the top of the page. diff --git a/frontend/src/components/Header/Layout.tsx b/frontend/src/components/Header/Layout.tsx index 90f0c6b6..e07bfa7f 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 { 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 ( <> diff --git a/frontend/src/pages/home/HomePage.tsx b/frontend/src/pages/home/HomePage.tsx index e6b7c692..b3ba4bd3 100644 --- a/frontend/src/pages/home/HomePage.tsx +++ b/frontend/src/pages/home/HomePage.tsx @@ -9,7 +9,7 @@ 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 {Me} from "../../types/me.ts"; interface DeadlineInfoProps { selectedDay: Dayjs; @@ -105,7 +105,7 @@ export default function HomePage() { const [selectedDay, setSelectedDay] = useState(dayjs(Date.now())); const loader = useLoaderData() as { projects: ProjectDeadline[], - me: me + me: Me } const projects = loader.projects diff --git a/frontend/src/pages/home/HomePages.tsx b/frontend/src/pages/home/HomePages.tsx index a13a0932..b950f724 100644 --- a/frontend/src/pages/home/HomePages.tsx +++ b/frontend/src/pages/home/HomePages.tsx @@ -2,7 +2,7 @@ import HomePage from './HomePage.tsx'; import Home from "./Home.tsx"; import {useLoaderData} from "react-router-dom"; import {ProjectDeadline} from "../project/projectDeadline/ProjectDeadline.tsx"; -import {me} from "../../types/me.ts" +import {Me} from "../../types/me.ts" /** * Gives the requested home page based on the login status @@ -11,7 +11,7 @@ import {me} from "../../types/me.ts" export default function HomePages() { const loader = useLoaderData() as { projects: ProjectDeadline[], - me: me + me: Me } const me = loader.me.role if (me === 'UNKNOWN') { diff --git a/frontend/src/types/me.ts b/frontend/src/types/me.ts index 47c14175..8ee4bcc7 100644 --- a/frontend/src/types/me.ts +++ b/frontend/src/types/me.ts @@ -1,4 +1,4 @@ -export interface me{ +export interface Me { role:string, display_name:string, uid:string From 644c1d721a0f7b47060129201c3106039a142bf6 Mon Sep 17 00:00:00 2001 From: warre Date: Tue, 23 Apr 2024 14:02:41 +0200 Subject: [PATCH 06/12] warning --- frontend/src/components/Header/Header.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/Header/Header.tsx b/frontend/src/components/Header/Header.tsx index 0d1c0e8d..109f60fa 100644 --- a/frontend/src/components/Header/Header.tsx +++ b/frontend/src/components/Header/Header.tsx @@ -84,7 +84,7 @@ export function Header({me}:HeaderProps): JSX.Element { <> - + {me.display_name} From 9493f88fa35acac594e3b92773630d36b2e83088 Mon Sep 17 00:00:00 2001 From: warre Date: Tue, 23 Apr 2024 14:06:59 +0200 Subject: [PATCH 07/12] del hanging file --- frontend/src/Layout.tsx | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 frontend/src/Layout.tsx diff --git a/frontend/src/Layout.tsx b/frontend/src/Layout.tsx deleted file mode 100644 index 2184d5c7..00000000 --- a/frontend/src/Layout.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { Outlet } from "react-router-dom"; -import { Header } from "./components/Header/Header.tsx"; - -/** - * Basic layout component that will be used on all routes. - * @returns The Layout component - */ -export function Layout(): JSX.Element { - return ( - <> -
- - - ); -} \ No newline at end of file From 8df96e750b8a7b82ca69d7cb03ac61d14ffd33cd Mon Sep 17 00:00:00 2001 From: warre Date: Tue, 23 Apr 2024 14:27:50 +0200 Subject: [PATCH 08/12] linting --- frontend/src/utils/fetches/FetchMe.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/src/utils/fetches/FetchMe.ts b/frontend/src/utils/fetches/FetchMe.ts index 2db0a20f..dc18dfdd 100644 --- a/frontend/src/utils/fetches/FetchMe.ts +++ b/frontend/src/utils/fetches/FetchMe.ts @@ -2,16 +2,16 @@ export const fetchMe = async () => { const API_URL = import.meta.env.VITE_APP_API_HOST try { - const response = await fetch(`${API_URL}/me`, { + const response = await fetch(`${API_URL}/me`,{ credentials: 'include' }) - if(response.status == 200){ + if (response.status == 200) { const data = await response.json() return data.data - }else { + } else { return {role:"UNKNOWN"} } - } catch (e){ + } catch (e) { return {role:"UNKNOWN"} } } \ No newline at end of file From 4b6a7437897289d26c879169c09f64c12a6f3f76 Mon Sep 17 00:00:00 2001 From: warre Date: Tue, 23 Apr 2024 15:30:26 +0200 Subject: [PATCH 09/12] formatting --- frontend/package-lock.json | 15 ++ frontend/package.json | 5 +- frontend/src/App.tsx | 57 ++++-- frontend/src/components/Header/Header.tsx | 113 ++++++++---- frontend/src/components/Header/Layout.tsx | 8 +- frontend/src/components/Header/Login.tsx | 15 +- frontend/src/pages/home/Home.tsx | 39 ++-- frontend/src/pages/home/HomePage.tsx | 147 ++++++++------- frontend/src/pages/home/HomePages.tsx | 10 +- frontend/src/types/me.ts | 9 +- frontend/src/utils/fetches/FetchMe.ts | 19 +- frontend/src/utils/fetches/FetchProjects.tsx | 177 +++++++++++-------- 12 files changed, 368 insertions(+), 246 deletions(-) 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 + return ( + + ); } 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 []; } -} +}; From 3d683c506a9c03472ff884e57ff8a37481646bdc Mon Sep 17 00:00:00 2001 From: warre Date: Wed, 24 Apr 2024 14:04:57 +0200 Subject: [PATCH 10/12] pr changes --- frontend/src/components/Header/Header.tsx | 8 ++++++-- frontend/src/components/Header/Login.tsx | 1 - frontend/src/pages/home/HomePages.tsx | 3 +-- frontend/src/types/me.ts | 1 + frontend/src/utils/fetches/FetchMe.ts | 5 +++-- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/frontend/src/components/Header/Header.tsx b/frontend/src/components/Header/Header.tsx index 47d26da9..171bb03a 100644 --- a/frontend/src/components/Header/Header.tsx +++ b/frontend/src/components/Header/Header.tsx @@ -20,6 +20,7 @@ import AccountCircleIcon from "@mui/icons-material/AccountCircle"; import { Link } from "react-router-dom"; import { TitlePortal } from "./TitlePortal"; import { Me } from "../../types/me.ts"; +import {LoginButton} from "./Login.tsx"; interface HeaderProps { me: Me; @@ -66,7 +67,7 @@ export function Header({ me }: HeaderProps): JSX.Element { { link: "/projects", text: t("myProjects") }, { link: "/courses", text: t("myCourses") }, ]; - if (me.role !== "UNKNOWN") { + if (me.loggedIn) { setListItems([...baseItems, ...additionalItems]); } else { setListItems(baseItems); @@ -85,7 +86,10 @@ export function Header({ me }: HeaderProps): JSX.Element { - {me.role !== "UNKNOWN" && ( + {!me.loggedIn && ( + + )} + {me.loggedIn && ( <> - {" "} {t("login", "Login")} ); diff --git a/frontend/src/pages/home/HomePages.tsx b/frontend/src/pages/home/HomePages.tsx index 6feaca41..60d26c68 100644 --- a/frontend/src/pages/home/HomePages.tsx +++ b/frontend/src/pages/home/HomePages.tsx @@ -13,8 +13,7 @@ export default function HomePages() { projects: ProjectDeadline[]; me: Me; }; - const me = loader.me.role; - if (me === "UNKNOWN") { + if (!loader.me.loggedIn) { return ; } else { return ; diff --git a/frontend/src/types/me.ts b/frontend/src/types/me.ts index 77b57d62..6788d7b4 100644 --- a/frontend/src/types/me.ts +++ b/frontend/src/types/me.ts @@ -2,4 +2,5 @@ export interface Me { role: string; display_name: string; uid: string; + loggedIn: boolean; } diff --git a/frontend/src/utils/fetches/FetchMe.ts b/frontend/src/utils/fetches/FetchMe.ts index 5c1607e7..29efb9ee 100644 --- a/frontend/src/utils/fetches/FetchMe.ts +++ b/frontend/src/utils/fetches/FetchMe.ts @@ -6,11 +6,12 @@ export const fetchMe = async () => { }); if (response.status == 200) { const data = await response.json(); + data.data.loggedIn = true return data.data; } else { - return { role: "UNKNOWN" }; + return {loggedIn: false }; } } catch (e) { - return { role: "UNKNOWN" }; + return { loggedIn: false }; } }; From 2dedeabc07a6905becd99dd72098364ec8b6ac70 Mon Sep 17 00:00:00 2001 From: warre Date: Fri, 26 Apr 2024 11:25:54 +0200 Subject: [PATCH 11/12] conflicts --- frontend/src/pages/project/FetchProjects.tsx | 127 ------------------- frontend/src/utils/fetches/FetchMe.ts | 4 +- frontend/src/utils/fetches/FetchProjects.tsx | 27 ++-- 3 files changed, 13 insertions(+), 145 deletions(-) delete mode 100644 frontend/src/pages/project/FetchProjects.tsx diff --git a/frontend/src/pages/project/FetchProjects.tsx b/frontend/src/pages/project/FetchProjects.tsx deleted file mode 100644 index dd4ba700..00000000 --- a/frontend/src/pages/project/FetchProjects.tsx +++ /dev/null @@ -1,127 +0,0 @@ -import { authenticatedFetch } from "../../utils/authenticated-fetch.ts"; -import { - Project, - ProjectDeadline, - ShortSubmission, -} from "./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 }; -}; - -export const fetchMe = async () => { - try { - const response = await authenticatedFetch(`${API_URL}/me`); - if (response.status == 200) { - const data = await response.json(); - return data.role; - } else { - return "UNKNOWN"; - } - } catch (e) { - return "UNKNOWN"; - } -}; -export const fetchProjects = async () => { - try { - const response = await authenticatedFetch(`${API_URL}/projects`); - 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 authenticatedFetch( - encodeURI(`${API_URL}/submissions?project_id=${project_id}`) - ) - ).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 authenticatedFetch( - encodeURI(`${API_URL}/projects/${project_id}`) - ) - ).json(); - - //fetch the course - const response_courses = await ( - await authenticatedFetch( - encodeURI(`${API_URL}/courses/${project_item.data.course_id}`) - ) - ).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: 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; - } catch (_) { - return []; - } -}; diff --git a/frontend/src/utils/fetches/FetchMe.ts b/frontend/src/utils/fetches/FetchMe.ts index 29efb9ee..ff7ab13d 100644 --- a/frontend/src/utils/fetches/FetchMe.ts +++ b/frontend/src/utils/fetches/FetchMe.ts @@ -1,7 +1,9 @@ +import {authenticatedFetch} from "../authenticated-fetch.ts"; + export const fetchMe = async () => { const API_URL = import.meta.env.VITE_APP_API_HOST; try { - const response = await fetch(`${API_URL}/me`, { + const response = await authenticatedFetch(`${API_URL}/me`, { credentials: "include", }); if (response.status == 200) { diff --git a/frontend/src/utils/fetches/FetchProjects.tsx b/frontend/src/utils/fetches/FetchProjects.tsx index 57535f1c..64bbbad0 100644 --- a/frontend/src/utils/fetches/FetchProjects.tsx +++ b/frontend/src/utils/fetches/FetchProjects.tsx @@ -1,4 +1,5 @@ import { fetchMe } from "./FetchMe.ts"; +import { authenticatedFetch } from "../authenticated-fetch.ts"; import { Project, ProjectDeadline, @@ -14,9 +15,7 @@ export const fetchProjectPage = async () => { export const fetchProjects = async () => { try { - const response = await fetch(`${API_URL}/projects`, { - credentials: "include", - }); + const response = await authenticatedFetch(`${API_URL}/projects`); const jsonData = await response.json(); let formattedData: ProjectDeadline[] = await Promise.all( jsonData.data.map(async (item: Project) => { @@ -24,11 +23,8 @@ export const fetchProjects = async () => { 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", - }, + await authenticatedFetch( + encodeURI(`${API_URL}/submissions?project_id=${project_id}`) ) ).json(); @@ -46,18 +42,15 @@ export const fetchProjects = async () => { )[0]; // fetch the course id of the project const project_item = await ( - await fetch(encodeURI(`${API_URL}/projects/${project_id}`), { - credentials: "include", - }) + await authenticatedFetch( + encodeURI(`${API_URL}/projects/${project_id}`) + ) ).json(); //fetch the course const response_courses = await ( - await fetch( - encodeURI(`${API_URL}/courses/${project_item.data.course_id}`), - { - credentials: "include", - }, + await authenticatedFetch( + encodeURI(`${API_URL}/courses/${project_item.data.course_id}`) ) ).json(); const course = { @@ -116,7 +109,7 @@ export const fetchProjects = async () => { ); formattedData = formattedData.flat(); return formattedData; - } catch (e) { + } catch (_) { return []; } }; From bfd3b6a2697d81a65c0d910beb37bf88ff1752b9 Mon Sep 17 00:00:00 2001 From: warre Date: Fri, 26 Apr 2024 12:46:24 +0200 Subject: [PATCH 12/12] make it a link --- frontend/src/components/Header/Header.tsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/Header/Header.tsx b/frontend/src/components/Header/Header.tsx index 171bb03a..6853b745 100644 --- a/frontend/src/components/Header/Header.tsx +++ b/frontend/src/components/Header/Header.tsx @@ -109,10 +109,9 @@ export function Header({ me }: HeaderProps): JSX.Element { {me.display_name} - (window.location.href = `${API_URL}/logout`)} - > - {t("logout")} + + + {t("logout")}