Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Homepage student + landing page #194

Merged
merged 39 commits into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
5b684e7
start homepage
warreprovoost Mar 29, 2024
326a82a
added calender functions and titlecard
warreprovoost Apr 2, 2024
2a4df69
merge with development
warreprovoost Apr 4, 2024
4bcad68
Merge branch 'development' into frontend/feature/homepage
warreprovoost Apr 4, 2024
7455a33
homepage changes
warreprovoost Apr 7, 2024
1ff6295
homepage changes
warreprovoost Apr 7, 2024
252ed38
Added: Homepage and Student Homepage
warreprovoost Apr 7, 2024
34ed7cb
Merge remote-tracking branch 'origin/frontend/feature/homepage' into …
warreprovoost Apr 8, 2024
a9b96ae
Merge branch 'development' into frontend/feature/homepage
warreprovoost Apr 8, 2024
5a973dc
Merge branch 'development' into frontend/feature/homepage
warreprovoost Apr 10, 2024
01b878f
homepage changes
warreprovoost Apr 12, 2024
e2a548a
Merge branch 'development' into frontend/feature/homepage
warreprovoost Apr 12, 2024
bacf393
project projects parser
Gerwoud Apr 12, 2024
cc58511
test passed
Gerwoud Apr 12, 2024
32991c7
linter
Gerwoud Apr 12, 2024
0dcb46e
pr review
Gerwoud Apr 12, 2024
e5451b6
Project card refactor
warreprovoost Apr 13, 2024
645443a
Merge remote-tracking branch 'origin/backend/projectendpoint-fix' int…
warreprovoost Apr 13, 2024
b634374
homepage change fix
warreprovoost Apr 13, 2024
bc59766
Revert "Merge remote-tracking branch 'origin/backend/projectendpoint-…
warreprovoost Apr 13, 2024
77ed924
Merge remote-tracking branch 'origin/development' into frontend/featu…
warreprovoost Apr 13, 2024
b76dec9
homepage changes
warreprovoost Apr 13, 2024
7b0e99c
rm comment
warreprovoost Apr 13, 2024
96b4f65
pr changes
warreprovoost Apr 13, 2024
48956fa
pr changes
warreprovoost Apr 14, 2024
4e2d4f6
added support for no deadline projects
warreprovoost Apr 14, 2024
2c2654f
link changed
warreprovoost Apr 14, 2024
18bce9d
link changed
warreprovoost Apr 14, 2024
0df208b
Merge branch 'development' into frontend/feature/homepage
warreprovoost Apr 15, 2024
6fdf715
pr changes
warreprovoost Apr 16, 2024
4f07f24
homepage change
warreprovoost Apr 17, 2024
b4f46f3
homepage change
warreprovoost Apr 17, 2024
77f56f4
API URL to API_HOST
warreprovoost Apr 18, 2024
b249b52
Merge branch 'development' into frontend/feature/homepage
warreprovoost Apr 18, 2024
cf23151
end point with /me
warreprovoost Apr 18, 2024
03d3fbe
home
warreprovoost Apr 18, 2024
94ef7cb
deadline fix
warreprovoost Apr 18, 2024
3a686a5
deadline fix
warreprovoost Apr 18, 2024
36fa3d3
deadline fix
warreprovoost Apr 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added frontend/public/img/logo_app.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 18 additions & 2 deletions frontend/public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
"home": {
"home": "Home",
"tag": "en",
"homepage": "Homepage"
"homepage": "Homepage",
"welcomeDescription": "Welcome to Peristerónas, the online submission platform of UGent",
"login": "Login"
},
"courseForm": {
"courseName": "Course Name",
Expand Down Expand Up @@ -79,5 +81,19 @@
"noRegexPlaceholder": "No regex added yet",
"clearSelected": "Clear Selection",
"faultySubmission": "Some fields were left open or there is no valid runner/file combination"
},
"student" : {
"myProjects": "My Projects",
"myCourses": "My Courses",
"deadlines": "Past deadlines",
"last_submission" : "Last submission",
"course": "Course",
"SUCCESS": "Success",
"FAIL": "Fail",
"deadlinesOnDay": "Deadlines on: ",
"noDeadline": "No deadlines",
"no_submission_yet" : "No submission yet",
"loading": "Loading...",
"no_projects": "There are no projects here."
}
}
}
23 changes: 18 additions & 5 deletions frontend/public/locales/nl/translation.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
{
"home": {
"title": "Homepagina"
},
"header": {
"myProjects": "Mijn Projecten",
"myCourses": "Mijn Vakken",
Expand All @@ -12,16 +9,32 @@
"projectUploadForm": "Project uploaden"
},
"home": {
"login": "Aanmelden",
"home": "Home",
"tag": "nl",
"homepage": "Homepage"
"homepage": "Homepagina",
"welcomeDescription": "Welkom bij Peristerónas, het online indieningsplatform van UGent",
"login": "Aanmelden"
},
"courseForm": {
"courseName": "Vak Naam",
"submit": "Opslaan",
"emptyCourseNameError": "Vak naam mag niet leeg zijn"
},
"student": {
"myProjects": "Mijn Projecten",
"myCourses": "Mijn Vakken",
"deadlines": "Verlopen Deadlines",
"course": "Vak",
"last_submission": "Laatste indiening",
"SUCCESS": "Geslaagd",
"FAIL": "Gefaald",
"deadlinesOnDay": "Deadlines op: ",
"noDeadline": "Geen deadlines",
"no_submission_yet" : "Nog geen indiening",
"loading": "Laden...",
"no_projects": "Er zijn hier geen projecten."

},
"projectForm": {
"projectTitle": "Titel",
"projectDescription": "Beschrijving",
Expand Down
7 changes: 4 additions & 3 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import { Route, RouterProvider, createBrowserRouter, createRoutesFromElements } from "react-router-dom";
import Layout from "./components/Header/Layout";
import Home from "./pages/home/Home";
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 {fetchProjectPage} from "./pages/project/FetchProjects.tsx";
import HomePages from "./pages/home/HomePages.tsx";

const router = createBrowserRouter(
createRoutesFromElements(
<Route path="/" element={<Layout />} errorElement={<ErrorBoundary />}>
<Route index element={<Home />} />
<Route index element={<HomePages />} loader={fetchProjectPage}/>
<Route path=":lang" element={<LanguagePath/>}>
<Route path="home" element={<Home />} />
<Route path="home" element={<HomePages />} loader={fetchProjectPage} />
<Route path="project" >
<Route path=":projectId" element={<ProjectView />}/>
</Route>
Expand Down
50 changes: 42 additions & 8 deletions frontend/src/pages/home/Home.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,50 @@
import { useTranslation } from "react-i18next";
import { Title } from "../../components/Header/Title";
import { Button, Container, Typography, Box } from "@mui/material";
import {Link } from "react-router-dom";

/**
* 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' });
const login_redirect:string =import.meta.env.VITE_LOGIN_LINK
return (
<>
<Title title={t('title')} />
<div>
</div>
</>
);
<Container maxWidth="sm">
<Box
sx={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
height: '100vh',
textAlign: 'center',
gap: 3,
}}
>
<Box component="img"src="/img/logo_ugent.png" alt="University Logo"
sx={{ width: 100, height: 100 }} />

<Typography variant="h2" component="h1" gutterBottom >
<Box
warreprovoost marked this conversation as resolved.
Show resolved Hide resolved
component="img"
src="/img/logo_app.png"
alt="University Logo"
sx={{
position: 'relative',
top: '14px',
width: 90,
height: 90,
}}
/>
Peristerónas
</Typography>
<Typography variant="h6" component="p" >
{t('welcomeDescription', 'Welcome to Peristeronas.')}
</Typography>
<Button variant="contained" color="primary" size="large" component={Link} to={login_redirect}>
{t('login', 'Login')}
</Button>
</Box>
</Container> );
}
197 changes: 197 additions & 0 deletions frontend/src/pages/home/HomePage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
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";

interface DeadlineInfoProps {
selectedDay: Dayjs;
deadlines: ProjectDeadline[];
}

type ExtendedPickersDayProps = PickersDayProps<Dayjs> & { highlightedDays?: number[] };

/**
* Displays the deadlines on a given day
* @param selectedDay - The day of interest
* @param deadlines - All the deadlines to consider
* @returns Element
*/
const DeadlineInfo: React.FC<DeadlineInfoProps> = ({ selectedDay, deadlines }) => {
const { t } = useTranslation('translation', { keyPrefix: 'student' });
const deadlinesOnSelectedDay = deadlines.filter(
project => (project.deadline && dayjs(project.deadline).isSame(selectedDay, 'day'))
);
//list of the corresponding assignment
return (
<div>
{deadlinesOnSelectedDay.length === 0 ? (
<Card style={{margin: '10px 0'}}>
<CardContent>
<Typography variant="body1">
{t('noDeadline')}
</Typography>
</CardContent>
</Card>
) : <ProjectDeadlineCard deadlines={deadlinesOnSelectedDay}/>}
</div>
);
};

/**
*
* @param props - The day and the deadlines
* @returns - The ServerDay component that displays a badge for specific days
*/
function ServerDay(props: PickersDayProps<Dayjs> & { highlightedDays?: number[] }) {
const { highlightedDays = [], day, outsideCurrentMonth, ...other } = props;

const isSelected =
!props.outsideCurrentMonth && highlightedDays.indexOf(props.day.date()) >= 0;

return (
<Badge
key={props.day.toString()}
overlap="circular"
badgeContent={isSelected ? '🔴' : undefined}
sx={{
'.MuiBadge-badge': {
fontSize: '0.5em',
top: 8,
right: 8,
},
}}
>
<PickersDay {...other} outsideCurrentMonth={outsideCurrentMonth} day={day} />
</Badge>
);
}
const handleMonthChange =(
date: Dayjs,
projects:ProjectDeadline[],
setHighlightedDays: React.Dispatch<React.SetStateAction<number[]>>,
) => {

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())
}

}
);
setHighlightedDays(hDays)

};

/**
* This component is the home page component that will be rendered when on the index route.
* @returns - The home page component
*/
export default function HomePage() {
const { t } = useTranslation('translation', { keyPrefix: 'student' });

const [highlightedDays, setHighlightedDays] = React.useState<number[]>([]);

const [selectedDay, setSelectedDay] = useState<Dayjs>(dayjs(Date.now()));
const loader = useLoaderData() as {
projects: ProjectDeadline[],
me: string
}
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)))
.sort((a, b) => dayjs(a.deadline).diff(dayjs(b.deadline)))
.slice(0, 3) // only show the first 3

const pastDeadlines = projects
.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)
return (
<Container style={{ paddingTop: '50px' }}>
<Grid container spacing={2} wrap="nowrap">
<Grid item xs={6}>
<Card>
<CardContent>
<Typography variant="body1">
{t('myProjects')}
</Typography>
{futureProjects.length + noDeadlineProject.length > 0? (
<>
<ProjectDeadlineCard deadlines={futureProjects} />
<ProjectDeadlineCard deadlines={noDeadlineProject}/>
</>
) : (
<Typography variant="body1">
{t('no_projects')}
</Typography>
)}
</CardContent>
</Card>
</Grid>

<Grid item xs={6}>
<Card>

<CardContent>
<Typography variant="body1">
{t('deadlines')}
</Typography>
{pastDeadlines.length > 0 ? (
<ProjectDeadlineCard deadlines={pastDeadlines} />
) : (
<Typography variant="body1">
{t('no_projects')}
</Typography>
)}
</CardContent>
</Card>
</Grid>

<Grid item xs={6}>
<Card>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<DateCalendar
value={selectedDay}
onMonthChange={(date: Dayjs) => { handleMonthChange(date, projects, setHighlightedDays) }}
onChange={handleDaySelect}
renderLoading={() => <DayCalendarSkeleton />}
slots={{
day: ServerDay,
}}
slotProps={{
day: {
highlightedDays,
} as ExtendedPickersDayProps,
}}
/>
</LocalizationProvider>
<CardContent>
<Typography variant="body2">
{t('deadlinesOnDay')} {selectedDay.format('MMMM D, YYYY')}
</Typography>
<DeadlineInfo selectedDay={selectedDay} deadlines={projects} />
</CardContent>

</Card>
</Grid>

</Grid>
</Container>
);
}
21 changes: 21 additions & 0 deletions frontend/src/pages/home/HomePages.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import HomePage from './HomePage.tsx';
import Home from "./Home.tsx";
import {useLoaderData} from "react-router-dom";
import {ProjectDeadline} from "../project/projectDeadline/ProjectDeadline.tsx";

/**
* Gives the requested home page based on the login status
* @returns - The home page component
*/
export default function HomePages() {
const loader = useLoaderData() as {
projects: ProjectDeadline[],
me: string
}
const me = loader.me
if (me === 'UNKNOWN') {
return <Home />;
} else {
return <HomePage />;
}
}
Loading