Skip to content
This repository has been archived by the owner on Jan 22, 2025. It is now read-only.

Commit

Permalink
Project overview page (#222)
Browse files Browse the repository at this point in the history
* start homepage

* added calender functions and titlecard

* homepage changes

* homepage changes

* Added: Homepage and Student Homepage

* homepage changes

* project projects parser

* test passed

* linter

* pr review

* Project card refactor

* homepage change fix

* Revert "Merge remote-tracking branch 'origin/backend/projectendpoint-fix' into frontend/feature/homepage"

This reverts commit 645443a, reversing
changes made to e5451b6.

* homepage changes

* rm comment

* pr changes

* project overview

* project overview

* project overview done

* pr changes

* project page changes

* added support for no deadline projects

* link changed

* link changed

* pr changes

* merged with HomeStudent.tsx

* projects overview page

* wording change

* homepage change

* homepage change

* API URL to API_HOST

* end point with /me

* project pages

* home

* create button

* create button

* opt

* deadline fix

* deadline fix

* deadline fix

* deadline fix

* creds

---------

Co-authored-by: gerwoud <gerwoud@hotmail.be>
  • Loading branch information
warreprovoost and Gerwoud authored Apr 18, 2024
1 parent c5e9fbc commit 9b61937
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 33 deletions.
6 changes: 6 additions & 0 deletions frontend/public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,5 +95,11 @@
"no_submission_yet" : "No submission yet",
"loading": "Loading...",
"no_projects": "There are no projects here."
},
"projectsOverview": {
"past_deadline": "Past Projects",
"future_deadline": "Upcoming Deadlines",
"no_projects": "There are no projects here.",
"new_project": "New Project"
}
}
6 changes: 6 additions & 0 deletions frontend/public/locales/nl/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@
"minutesAgo": "minuten geleden",
"justNow": "Zonet"
},
"projectsOverview": {
"past_deadline": "Verlopen Projecten",
"future_deadline": "Opkomende Deadlines",
"no_projects": "Er zijn hier geen projecten.",
"new_project": "Nieuw Project"
},
"error": {
"pageNotFound": "Pagina Niet Gevonden",
"pageNotFoundMessage": "De opgevraagde pagina werd niet gevonden.",
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import ProjectCreateHome from "./pages/create_project/ProjectCreateHome.tsx";
import SubmissionsOverview from "./pages/submission_overview/SubmissionsOverview.tsx";
import {fetchProjectPage} from "./pages/project/FetchProjects.tsx";
import HomePages from "./pages/home/HomePages.tsx";
import ProjectOverView from "./pages/project/projectOverview.tsx";

const router = createBrowserRouter(
createRoutesFromElements(
Expand All @@ -20,6 +21,7 @@ const router = createBrowserRouter(
</Route>
</Route>
<Route path="projects">
<Route index element={<ProjectOverView/>} loader={fetchProjectPage}/>
<Route path="create" element={<ProjectCreateHome />} />
</Route>
</Route>
Expand Down
22 changes: 11 additions & 11 deletions frontend/src/pages/project/FetchProjects.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import {Project, ProjectDeadline, ShortSubmission} from "./projectDeadline/ProjectDeadline.tsx";
const API_URL = import.meta.env.VITE_APP_API_HOST
const header = {
"Authorization": "teacher2"
}

export const fetchProjectPage = async () => {
const projects = await fetchProjects()
const me = await fetchMe()
Expand All @@ -12,7 +10,7 @@ export const fetchProjectPage = async () => {
export const fetchMe = async () => {
try {
const response = await fetch(`${API_URL}/me`, {
headers:header
credentials: 'include'
})
if(response.status == 200){
const data = await response.json()
Expand All @@ -29,32 +27,34 @@ export const fetchProjects = async () => {

try{
const response = await fetch(`${API_URL}/projects`, {
headers:header
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}`), {
headers: header
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
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}`), {
headers:header
// 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}`), {
headers: header
credentials: 'include'
})).json()
const course = {
course_id: response_courses.data.course_id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,6 @@ export interface Course {
export interface ShortSubmission {
submission_id:number,
submission_time:Date,
submission_status:string
submission_status:string,
grading: number
}
48 changes: 27 additions & 21 deletions frontend/src/pages/project/projectDeadline/ProjectDeadlineCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import {CardActionArea, Card, CardContent, Typography, Box, Button} from '@mui/m
import {Link } from "react-router-dom";
import { useTranslation } from "react-i18next";
import dayjs from "dayjs";
import {ProjectDeadline, Deadline} from "./ProjectDeadline.tsx";
import {ProjectDeadline} from "./ProjectDeadline.tsx";
import React from "react";
import { useNavigate } from 'react-router-dom';

interface ProjectCardProps{
deadlines:ProjectDeadline[],
pred?: (deadline:Deadline) => boolean
showCourse?:boolean
}

/**
Expand All @@ -17,7 +17,7 @@ interface ProjectCardProps{
* @param pred - A predicate to filter the deadlines
* @returns Element
*/
export const ProjectDeadlineCard: React.FC<ProjectCardProps> = ({ deadlines }) => {
export const ProjectDeadlineCard: React.FC<ProjectCardProps> = ({ deadlines, showCourse = true }) => {
const { t } = useTranslation('translation', { keyPrefix: 'student' });
const { i18n } = useTranslation();
const navigate = useNavigate();
Expand All @@ -34,23 +34,25 @@ export const ProjectDeadlineCard: React.FC<ProjectCardProps> = ({ deadlines })
(project.short_submission.submission_status === 'SUCCESS' ? 'green' : 'red') : '#686868'}}>
{project.title}
</Typography>
<Typography variant="subtitle1">
{t('course')}:
<Button
style={{
color: 'inherit',
textTransform: 'none'
}}
onMouseDown={event => event.stopPropagation()}
onClick={(event) => {
event.stopPropagation(); // stops the event from reaching CardActionArea
event.preventDefault();
navigate(`/${i18n.language}/courses/${project.course.course_id}`)
}}
>
{project.course.name}
</Button>
</Typography>
{showCourse && (
<Typography variant="subtitle1">
{t('course')}:
<Button
style={{
color: 'inherit',
textTransform: 'none'
}}
onMouseDown={event => event.stopPropagation()}
onClick={(event) => {
event.stopPropagation(); // stops the event from reaching CardActionArea
event.preventDefault();
navigate(`/${i18n.language}/courses/${project.course.course_id}`)
}}
>
{project.course.name}
</Button>
</Typography>
)}
<Typography variant="body2" color="textSecondary">
{t('last_submission')}: {project.short_submission ?
t(project.short_submission.submission_status.toString()) : t('no_submission_yet')}
Expand All @@ -60,7 +62,11 @@ export const ProjectDeadlineCard: React.FC<ProjectCardProps> = ({ deadlines })
Deadline: {dayjs(project.deadline).format('MMMM D, YYYY')}
</Typography>
)}

{project.short_submission?.grading && (
<Typography variant="body2" align="right">
{project.short_submission.grading}/20
</Typography>
)}
</CardContent>
</CardActionArea>
</Card>
Expand Down
97 changes: 97 additions & 0 deletions frontend/src/pages/project/projectOverview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import {ProjectDeadline} from "./projectDeadline/ProjectDeadline.tsx";
import {Button, Card, CardContent, Container, Grid, Typography, Link} from "@mui/material";
import {ProjectDeadlineCard} from "./projectDeadline/ProjectDeadlineCard.tsx";
import { useTranslation } from "react-i18next";
import {Title} from "../../components/Header/Title.tsx";
import {useLoaderData, Link as RouterLink} from "react-router-dom";
import dayjs from "dayjs";

/**
* Displays all the projects
* @returns the project page
*/
export default function ProjectOverView() {
const {i18n} = useTranslation()
const { t } = useTranslation('translation', { keyPrefix: 'projectsOverview' });
const loader = useLoaderData() as {
projects: ProjectDeadline[],
me: string
}
const projects = loader.projects
const me = loader.me

const projectReducer = (acc: {[key: string]: ProjectDeadline[]}, project: ProjectDeadline) => {
(acc[project.course.course_id] = acc[project.course.course_id] || []).push(project);
return acc;
}
const futureProjectsByCourse = projects
.filter((p) => (p.deadline && dayjs(dayjs()).isBefore(p.deadline)))
.sort((a, b) => dayjs(a.deadline).diff(dayjs(b.deadline)))
.reduce(projectReducer, {});
const pastProjectsByCourse = projects
.filter((p) => p.deadline && (dayjs()).isAfter(p.deadline))
.sort((a, b) => dayjs(b.deadline).diff(dayjs(a.deadline)))
.reduce(projectReducer, {});
const noDeadlineProject = projects.filter((p) => p.deadline === undefined)
.reduce(projectReducer,{});

const projectItem = ([index, courseProjects] : [string, ProjectDeadline[]]) =>{
return (
<Grid container spacing={2} key={index}>
<Grid item xs={12}>
<Link href={`/${i18n.language}/course/${courseProjects[0].course.course_id}`} style={{color: 'inherit'}}
underline={'none'}>
<Typography variant="h6">{courseProjects[0].course.name} {courseProjects[0].course.ufora_id}</Typography>
</Link>
</Grid>
<Grid item xs={8}>
<ProjectDeadlineCard deadlines={courseProjects} showCourse={false} />
</Grid>
</Grid>
)
}
return (
<Container style={{ paddingTop: '50px' }}>
<Title title={"Projects Overview"}/>
<Grid container spacing={2}>
<Grid item xs={2}>
{me === 'TEACHER' && (
<Button component={RouterLink} to={`/${i18n.language}/projects/create`}>{t('new_project')}</Button>
)}
</Grid>
<Grid item xs={5}>
<Card>
<CardContent>
<Typography variant="h5" style={{ color: '#3f51b5' }}>{t("future_deadline")}:</Typography>
{Object.keys(futureProjectsByCourse).length + Object.keys(noDeadlineProject).length === 0 ? (
<Typography variant="body1">
{t('no_projects')}
</Typography>
) :(
[...Object.entries(futureProjectsByCourse),
...Object.entries(noDeadlineProject)].map(projectItem)
)}
</CardContent>
</Card>

</Grid>
<Grid item xs={5}>
<Card>
<CardContent>
<Typography variant="h5" style={{ color: '#3f51b5' }}>{t("past_deadline")}:</Typography>
{
Object.keys(pastProjectsByCourse).length === 0 ? (
<Typography variant="body1">
{t('no_projects')}
</Typography>
):(
Object.entries(pastProjectsByCourse).map(projectItem)
)
}
</CardContent>
</Card>
</Grid>
</Grid>
</Container>
)
}

0 comments on commit 9b61937

Please sign in to comment.