From 2dcb9222ead0ef5f2f222a7974bea1d18cfa1041 Mon Sep 17 00:00:00 2001 From: Ben De Meurichy Date: Sun, 19 May 2024 15:46:40 +0200 Subject: [PATCH 1/4] type errors weggekregen --- .../src/pages/groupsPage/GroupsPage.tsx | 188 ++++++++++-------- 1 file changed, 109 insertions(+), 79 deletions(-) diff --git a/frontend/frontend/src/pages/groupsPage/GroupsPage.tsx b/frontend/frontend/src/pages/groupsPage/GroupsPage.tsx index deee1d56..38707a4d 100644 --- a/frontend/frontend/src/pages/groupsPage/GroupsPage.tsx +++ b/frontend/frontend/src/pages/groupsPage/GroupsPage.tsx @@ -136,7 +136,7 @@ export function GroupsPage() { const handleGroupSizeChange = (newValue: number) => { setNewGroupSize(newValue) setAvailableStudents(() => Array.from(studentNames.keys())) - setFilteredStudents(availableStudents); + setFilteredStudents(availableStudents) setCurrentGroup('0') setNewGroups(() => { const newGroups = [] @@ -191,8 +191,8 @@ export function GroupsPage() { newStudentNames.set( student, response.data.first_name + - ' ' + - response.data.last_name + ' ' + + response.data.last_name ) }) .catch((error) => { @@ -206,12 +206,12 @@ export function GroupsPage() { newStudentNames.set( student, response.data.first_name + - ' ' + - response.data.last_name + ' ' + + response.data.last_name ) console.log( 'available names:' + - Array.from(newStudentNames.entries()) + Array.from(newStudentNames.entries()) ) }) } @@ -286,14 +286,14 @@ export function GroupsPage() { ) ) ) - setFilteredStudents(availableStudents); + setFilteredStudents(availableStudents) }, [newGroups, studentNames]) // Create new groups when the group size changes useEffect(() => { if (newGroups.length === 0) { setAvailableStudents(() => Array.from(studentNames.keys())) - setFilteredStudents(availableStudents); + setFilteredStudents(availableStudents) setCurrentGroup('0') setNewGroups(() => { const newGroups = [] @@ -351,7 +351,7 @@ export function GroupsPage() { (student) => student !== studentId ) setAvailableStudents(updatedAvailableStudents) - setFilteredStudents(availableStudents); + setFilteredStudents(availableStudents) // Then, create a new copy of the newGroups array with the updated group const updatedNewGroups = newGroups.map((group, index) => { if (index === groupId) { @@ -392,26 +392,28 @@ export function GroupsPage() { setNewGroups(updatedNewGroups) } - const [filteredStudents, setFilteredStudents] = useState(availableStudents); + const [filteredStudents, setFilteredStudents] = useState(availableStudents) // for filtering students - const handleAutocompleteChange = (_, value) => { - if(value){ + const handleAutocompleteChange = (_: unknown, value: number | null) => { + if (value) { setFilteredStudents([value]) } - }; + } const resetAutocompleteChange = () => { - setFilteredStudents(availableStudents); + setFilteredStudents(availableStudents) } - const filterOptions = (_,{ inputValue }) => { - const filtered = availableStudents.filter((option) => { - const label = studentNames.get(option); - return label?.toLowerCase().startsWith(inputValue.toLowerCase()); - }); - return filtered; - }; + const filterOptions = ( + _: unknown, + { inputValue }: { inputValue: string } + ) => { + return availableStudents.filter((option) => { + const label = studentNames.get(option) + return label?.toLowerCase().startsWith(inputValue.toLowerCase()) + }) + } return ( <> - ) : ( + {newGroups.map( + (_, index) => ( + + {t( 'group' ) + - (index + - 1)} - - ) - )} - ) - } + (index + + 1)} + + ) + )} + + )} @@ -662,15 +665,15 @@ export function GroupsPage() { ) : ( <> {newGroups[ - parseInt( - currentGroup - ) - ] && + parseInt( + currentGroup + ) + ] && newGroups[ parseInt( currentGroup ) - ].studenten.map( + ].studenten.map( (student) => ( - + {t('studenten')} - + {loading ? ( ) : ( studentNames.get(student)} - onChange={handleAutocompleteChange} - filterOptions={filterOptions} - renderInput={(student) => } - />)} + options={ + availableStudents + } + getOptionLabel={( + student + ) => { + const name = + studentNames.get( + student + ) + return name != + null + ? name + : '' // This checks for both null and undefined + }} + onChange={ + handleAutocompleteChange + } + filterOptions={ + filterOptions + } + renderInput={( + student + ) => ( + + )} + /> + )} @@ -757,9 +785,9 @@ export function GroupsPage() { {loading ? ( @@ -784,7 +812,9 @@ export function GroupsPage() { {chunkArray( filteredStudents, Math.ceil( - Math.sqrt(filteredStudents.length) + Math.sqrt( + filteredStudents.length + ) ) ).map( (students) => ( @@ -817,23 +847,23 @@ export function GroupsPage() { '' ? student : studentNames.get( - student - )} + student + )} = - newGroupSize + parseInt( + currentGroup + ) + ] + .studenten + .length >= + newGroupSize : true } onClick={() => { @@ -941,9 +971,9 @@ export function GroupsPage() { } function chunkArray(arr: T[], chunkSize: number): T[][] { - let result: T[][] = [] + const result: T[][] = [] for (let i = 0; i < arr.length; i += chunkSize) { - let chunk: T[] = arr.slice(i, i + chunkSize) + const chunk: T[] = arr.slice(i, i + chunkSize) result.push(chunk) } return result From 3f6ea634864ddea3245f5263bc65d08419bc5bdc Mon Sep 17 00:00:00 2001 From: Ben De Meurichy Date: Sun, 19 May 2024 16:56:03 +0200 Subject: [PATCH 2/4] inladen studenten bij groepen is niet meer onacceptabel traag --- .../src/pages/groupsPage/GroupsPage.tsx | 53 +++++++------------ 1 file changed, 19 insertions(+), 34 deletions(-) diff --git a/frontend/frontend/src/pages/groupsPage/GroupsPage.tsx b/frontend/frontend/src/pages/groupsPage/GroupsPage.tsx index 38707a4d..003057c6 100644 --- a/frontend/frontend/src/pages/groupsPage/GroupsPage.tsx +++ b/frontend/frontend/src/pages/groupsPage/GroupsPage.tsx @@ -30,6 +30,8 @@ import { Add } from '@mui/icons-material' import ClearIcon from '@mui/icons-material/Clear' import SaveIcon from '@mui/icons-material/Save' import WarningPopup from '../../components/WarningPopup.tsx' +import axios, { AxiosResponse } from 'axios' +import { User } from '../subjectsPage/AddChangeSubjectPage.tsx' // group interface export interface Group { @@ -182,39 +184,21 @@ export function GroupsPage() { await instance .get('/vakken/' + courseId) .then(async (response) => { + // This function fetches the names of the students in parallel const newStudentNames = new Map() - - for (const student of response.data.studenten) { - await instance - .get('/gebruikers/' + student) - .then((response) => { - newStudentNames.set( - student, - response.data.first_name + - ' ' + - response.data.last_name - ) - }) - .catch((error) => { - console.log(error) - }) - } - for (const student of response.data.studenten) { - await instance - .get('/gebruikers/' + student) - .then((response) => { - newStudentNames.set( - student, - response.data.first_name + - ' ' + - response.data.last_name - ) - console.log( - 'available names:' + - Array.from(newStudentNames.entries()) - ) - }) - } + const studentPromises: Promise>[] = + response.data.studenten.map((id: number) => + instance.get('/gebruikers/' + id) + ) + const studentResponses = await axios.all(studentPromises) + + studentResponses.forEach((response) => { + const student: User = response.data + newStudentNames.set( + student.user, + student.first_name + ' ' + student.last_name + ) + }) setStudentNames(() => newStudentNames) }) @@ -275,7 +259,7 @@ export function GroupsPage() { .catch((error) => { console.log(error) }) - }, [assignmentId, courseId, newGroupSize, studentNames.size]) + }, [assignmentId, courseId]) useEffect(() => { setAvailableStudents(() => @@ -287,7 +271,7 @@ export function GroupsPage() { ) ) setFilteredStudents(availableStudents) - }, [newGroups, studentNames]) + }, [availableStudents, newGroups, studentNames]) // Create new groups when the group size changes useEffect(() => { @@ -312,6 +296,7 @@ export function GroupsPage() { } }, [ assignmentId, + availableStudents, availableStudents.length, newGroupSize, newGroups.length, From 3130bd3d5020e984b8fc4cfebd54a1fd01a831bd Mon Sep 17 00:00:00 2001 From: Ben De Meurichy Date: Sun, 19 May 2024 17:08:25 +0200 Subject: [PATCH 3/4] run npm format --- .../src/components/CustomComponents.tsx | 45 +- .../src/components/DeadlineCalendar.tsx | 62 +- .../SubmissionListItemStudentPage.tsx | 56 +- .../SubmissionListItemTeacherPage.tsx | 150 +-- .../AddChangeAssignmentPage.tsx | 1139 +++++++++-------- .../pages/assignmentPage/AssignmentPage.tsx | 339 +++-- .../src/pages/groupsPage/ChooseGroup.tsx | 784 ++++++++---- .../pages/scoresPage/ProjectScoresPage.tsx | 331 ++--- .../pages/scoresPage/StudentScoreListItem.tsx | 48 +- .../subjectsPage/AddChangeSubjectPage.tsx | 323 ++--- .../AssignmentListItemSubjectsPage.tsx | 124 +- .../src/pages/subjectsPage/ProjectsView.tsx | 133 +- .../src/pages/subjectsPage/StudentPopUp.tsx | 32 +- .../src/pages/subjectsPage/SubjectsPage.tsx | 56 +- .../pages/submissionPage/SubmissionPage.tsx | 13 +- frontend/frontend/src/routes.tsx | 2 +- 16 files changed, 2101 insertions(+), 1536 deletions(-) diff --git a/frontend/frontend/src/components/CustomComponents.tsx b/frontend/frontend/src/components/CustomComponents.tsx index 848b299a..f6edd286 100644 --- a/frontend/frontend/src/components/CustomComponents.tsx +++ b/frontend/frontend/src/components/CustomComponents.tsx @@ -1,9 +1,9 @@ -import { darken,lighten } from '@mui/system'; +import { darken, lighten } from '@mui/system' import { Button as BaseButton, Card as BaseCard, Divider as BaseDivider, - Box + Box, } from '@mui/material' import theme from '../Theme.ts' @@ -19,7 +19,7 @@ export const Button = ({ children, ...props }: any) => { textTransform: 'none', color: 'primary.contrastText', '&:hover': { - backgroundColor: darken(theme.palette.primary.main,0.5), + backgroundColor: darken(theme.palette.primary.main, 0.5), }, }} {...props} @@ -41,7 +41,7 @@ export const SecundaryButton = ({ children, ...props }: any) => { textTransform: 'none', color: 'secondary.contrastText', '&:hover': { - backgroundColor: darken(theme.palette.secondary.main,0.2), + backgroundColor: darken(theme.palette.secondary.main, 0.2), }, }} {...props} @@ -81,33 +81,30 @@ export const Divider = ({ children, ...props }: any) => { ) } -export const EvenlySpacedRow = ({items}) => { +export const EvenlySpacedRow = ({ items }) => { return ( - + {items.map((item, index) => ( - - - {item} - + width={ + index == 0 || index == items.length - 1 + ? 50 / items.length + '%' + : (100 - 100 / items.length) / (items.length - 2) + + '%' + } + sx={{ + //border: '1px solid red', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + }} + > + {item} ))} - + ) } - - export default { Button, Card, Divider, EvenlySpacedRow } diff --git a/frontend/frontend/src/components/DeadlineCalendar.tsx b/frontend/frontend/src/components/DeadlineCalendar.tsx index 8772029d..bc87c398 100644 --- a/frontend/frontend/src/components/DeadlineCalendar.tsx +++ b/frontend/frontend/src/components/DeadlineCalendar.tsx @@ -5,7 +5,17 @@ import { PickersDayProps, } from '@mui/x-date-pickers' import dayjs, { Dayjs } from 'dayjs' -import { Badge, SxProps, Stack, Typography, Box, List, ListItem, ListItemButton, ListItemText } from '@mui/material' +import { + Badge, + SxProps, + Stack, + Typography, + Box, + List, + ListItem, + ListItemButton, + ListItemText, +} from '@mui/material' import { useEffect, useRef, useState } from 'react' import AssignmentIcon from '@mui/icons-material/Assignment' import { useNavigate } from 'react-router-dom' @@ -95,22 +105,26 @@ function DeadlineMenu({ assignments, selectedDay }: DeadlineMenuProps) { {assignments - .filter((assignment: project) => - dayjs(assignment.deadline).isSame(selectedDay, 'day') - ).map((assignment: project) => - - handleProjectClick(assignment.vak, assignment.project_id)} - > - - - - )} + .filter((assignment: project) => + dayjs(assignment.deadline).isSame(selectedDay, 'day') + ) + .map((assignment: project) => ( + + + handleProjectClick( + assignment.vak, + assignment.project_id + ) + } + > + + + + ))} ) @@ -121,7 +135,10 @@ interface DeadlineCalendarProps { assignments: project[] } -export function DeadlineCalendar({ deadlines, assignments }: DeadlineCalendarProps) { +export function DeadlineCalendar({ + deadlines, + assignments, +}: DeadlineCalendarProps) { const requestAbortController = useRef(null) const [isLoading, setIsLoading] = useState(false) const [highlightedDays, setHighlightedDays] = useState([]) @@ -173,9 +190,7 @@ export function DeadlineCalendar({ deadlines, assignments }: DeadlineCalendarPro return ( <> - + {/*Calendar*/} - + ) diff --git a/frontend/frontend/src/components/SubmissionListItemStudentPage.tsx b/frontend/frontend/src/components/SubmissionListItemStudentPage.tsx index 68840f6f..aafada4e 100644 --- a/frontend/frontend/src/components/SubmissionListItemStudentPage.tsx +++ b/frontend/frontend/src/components/SubmissionListItemStudentPage.tsx @@ -1,4 +1,4 @@ -import { EvenlySpacedRow} from "./CustomComponents.tsx"; +import { EvenlySpacedRow } from './CustomComponents.tsx' import { ListItem, ListItemButton, @@ -53,31 +53,35 @@ export function SubmissionListItemStudentPage({ <> - , - , - - - {status ? ( - - ) : ( - - )} - - - ]}/> + , + , + + + {status ? ( + + ) : ( + + )} + + , + ]} + /> diff --git a/frontend/frontend/src/components/SubmissionListItemTeacherPage.tsx b/frontend/frontend/src/components/SubmissionListItemTeacherPage.tsx index cfaa2dc4..f1d82bcc 100644 --- a/frontend/frontend/src/components/SubmissionListItemTeacherPage.tsx +++ b/frontend/frontend/src/components/SubmissionListItemTeacherPage.tsx @@ -3,7 +3,7 @@ import { ListItemButton, ListItemIcon, ListItemText, - Box + Box, } from '@mui/material' import { useNavigate } from 'react-router-dom' import DownloadIcon from '@mui/icons-material/Download' @@ -14,7 +14,7 @@ import instance from '../axiosConfig' import { Submission } from '../pages/submissionPage/SubmissionPage.tsx' import { t } from 'i18next' import dayjs from 'dayjs' -import {EvenlySpacedRow} from "./CustomComponents.tsx"; +import { EvenlySpacedRow } from './CustomComponents.tsx' interface SubmissionListItemTeacherPageProps { relative_group_id: string @@ -63,17 +63,21 @@ export function SubmissionListItemTeacherPage({ submissionsResponse.data[ submissionsResponse.data.length - 1 ] - const lastSubmissionResponse = await instance.get(`indieningen/${lastSubmission.indiening_id}/`) + const lastSubmissionResponse = await instance.get( + `indieningen/${lastSubmission.indiening_id}/` + ) //Get the submission file const newSubmission: Submission = lastSubmissionResponse.data - newSubmission.filename = lastSubmissionResponse.data.bestand.replace( - /^.*[\\/]/, - '' - ) + newSubmission.filename = + lastSubmissionResponse.data.bestand.replace(/^.*[\\/]/, '') newSubmission.bestand = await instance - .get(`/indieningen/${lastSubmission.indiening_id}/indiening_bestand`, { - responseType: 'blob', - }).then((res) => { + .get( + `/indieningen/${lastSubmission.indiening_id}/indiening_bestand`, + { + responseType: 'blob', + } + ) + .then((res) => { let filename = 'indiening.zip' if (newSubmission.filename) { filename = newSubmission.filename @@ -107,9 +111,7 @@ export function SubmissionListItemTeacherPage({ const url = window.URL.createObjectURL(submitted?.bestand) const a = document.createElement('a') a.href = url - a.download = submitted.filename - ? submitted.filename - : 'opgave.zip' + a.download = submitted.filename ? submitted.filename : 'opgave.zip' document.body.appendChild(a) a.click() a.remove() @@ -128,70 +130,72 @@ export function SubmissionListItemTeacherPage({ return ( <> - - + + , - , - , - - - {!submitted?.status ? ( - - ) : ( - submitted !== undefined && ( - - ) - )} - - , - - -
- {submitted ? ( - , + , + , + + + {!submitted?.status ? ( + ) : ( - + submitted !== undefined && ( + + ) )} -
-
-
- ]}> -
+ + , + + +
+ {submitted ? ( + + ) : ( + + )} +
+
+
, + ]} + >
diff --git a/frontend/frontend/src/pages/addChangeAssignmentPage/AddChangeAssignmentPage.tsx b/frontend/frontend/src/pages/addChangeAssignmentPage/AddChangeAssignmentPage.tsx index 33d50574..5ed43e36 100644 --- a/frontend/frontend/src/pages/addChangeAssignmentPage/AddChangeAssignmentPage.tsx +++ b/frontend/frontend/src/pages/addChangeAssignmentPage/AddChangeAssignmentPage.tsx @@ -191,15 +191,11 @@ export function AddChangeAssignmentPage() { setVisible(assignment.zichtbaar) if (assignment.deadline !== null) { - setDueDate( - dayjs(assignment.deadline) - ) + setDueDate(dayjs(assignment.deadline)) console.log('deadline' + assignment.deadline) } if (assignment.extra_deadline !== null) { - setExtraDueDate( - dayjs(assignment.extra_deadline) - ) + setExtraDueDate(dayjs(assignment.extra_deadline)) console.log( 'extra deadline' + assignment.extra_deadline ) @@ -472,529 +468,622 @@ export function AddChangeAssignmentPage() { <> {user?.is_lesgever ? ( // Rendering UI for teacher - <> - {/* Stack container for layout */} - - {/*very ugly but it works functionally*/} - {loading ? ( -
- ) : ( -
- )} - {/* Form for submitting assignment */} - - - - {/* Here the user gets to specify the assignment name */} - - {t('assignmentName')} - - {loading ? ( - - ) : ( - - setTitle(event.target.value) - } - /> - )} - - {/* File Upload button */} - - {loading ? ( - { - console.log('loading') - }} - fileTypes={['.pdf', '.zip']} - path={new File([], 'loading...')} - /> - ) : ( - - )} - - - {/* Deadline section. + <> + {/* Stack container for layout */} + + {/*very ugly but it works functionally*/} + {loading ? ( +
+ ) : ( +
+ )} + {/* Form for submitting assignment */} + + + + {/* Here the user gets to specify the assignment name */} + + {t('assignmentName')} + + {loading ? ( + + ) : ( + + setTitle( + event.target.value + ) + } + /> + )} + + {/* File Upload button */} + + {loading ? ( + { + console.log('loading') + }} + fileTypes={['.pdf', '.zip']} + path={ + new File( + [], + 'loading...' + ) + } + /> + ) : ( + + )} + + + {/* Deadline section. There is both the normal deadline, and an extra deadline in case people need more time. */} - - - {/* This section renders the normal deadline. */} - - Deadline: - - {loading ? ( - - ) : ( - - - SetDeadlineError(newError) - } - slotProps={{ - field: { - clearable: true, - onClear: () => setCleared(true), - }, - textField: { - helperText: errorMessage, - }, - }} - onChange={(newValue) => - setDueDate(newValue) - } - /> - - )} - - - {/* This section renders the extra deadline. */} - - Extra Deadline: - - {loading ? ( - - ) : ( - - setCleared(true), - }, - textField: { - error: deadlineCheckError, - helperText: deadlineCheckError - ? t('deadlineCheck') - : '', - }, + - setExtraDueDate(newValue) - } - /> - - )} - - - {/* Description section */} - - - - {t('description')} - - {loading ? ( - - ) : ( - - setDescription(event.target.value) - } - fullWidth - error={assignmentErrors.description} - // Show an error message if the description is not filled in. - helperText={ - assignmentErrors.description - ? t('descriptionName') + - ' ' + - t('is_required') - : '' - } - sx={{ - overflowY: 'auto', - maxHeight: '25svh', - }} - /> - )} - - - {/* Restrictions section */} - - - - {t('restrictions')} - - - {/*This list will render the restrictions that are added to the assignment.*/} - - {loading ? ( - - ) : ( - <> - {restrictions.map( - (restriction, index) => { - return ( - - - - ) - } - )} - - )} - - - - - - setRestrictions(newRestrictions) - } - > - - - - - {/* Main actions section */} - - - - {visible ? ( - setVisible(!visible)} + gap={5} > - - - ) : ( - setVisible(!visible)} + + {/* This section renders the normal deadline. */} + + Deadline: + + {loading ? ( + + ) : ( + + + SetDeadlineError( + newError + ) + } + slotProps={{ + field: { + clearable: true, + onClear: () => + setCleared( + true + ), + }, + textField: { + helperText: + errorMessage, + }, + }} + onChange={(newValue) => + setDueDate(newValue) + } + /> + + )} + + + {/* This section renders the extra deadline. */} + + Extra Deadline: + + {loading ? ( + + ) : ( + + + setCleared( + true + ), + }, + textField: { + error: deadlineCheckError, + helperText: + deadlineCheckError + ? t( + 'deadlineCheck' + ) + : '', + }, + }} + onChange={(newValue) => + setExtraDueDate( + newValue + ) + } + /> + + )} + + + {/* Description section */} + + + + {t('description')} + + {loading ? ( + + ) : ( + + setDescription( + event.target.value + ) + } + fullWidth + error={ + assignmentErrors.description + } + // Show an error message if the description is not filled in. + helperText={ + assignmentErrors.description + ? t( + 'descriptionName' + ) + + ' ' + + t('is_required') + : '' + } + sx={{ + overflowY: 'auto', + maxHeight: '25svh', + }} + /> + )} + + + {/* Restrictions section */} + - - - )} - - + + {t('restrictions')} + + + {/*This list will render the restrictions that are added to the assignment.*/} + + {loading ? ( + + ) : ( + <> + {restrictions.map( + ( + restriction, + index + ) => { + return ( + + + + ) + } + )} + + )} + + + + + + setRestrictions( + newRestrictions + ) + } + > + + + + + {/* Main actions section */} + - - - - - {/* This section allows the teacher to set the maximum score for the assignment.*/} - - - Max Score - - {loading ? ( - - ) : ( - { - if (event.target.value !== '') { - const newScore = Math.max( - parseInt( - event.target.value - ), - 0 - ) - SetMaxScore - ? SetMaxScore(newScore) - : undefined - } else { - SetMaxScore - ? SetMaxScore( - parseInt( - event.target.value - ) - ) - : undefined - } - }} - /> - )} - - - {/* Submit and Cancel buttons */} - - - setCancelConfirmation(true)} - sx={{ - backgroundColor: 'secondary.main', - borderRadius: 2, - }} - > - - - - - - - - - - - - - {/* Popup for adding restrictions */} - - {/* Confirmation popup for deleting project */} - - - {/* Confirmation popup for saving project */} - - {/* Confirmation popup for canceling changes*/} - - + + + {visible ? ( + + setVisible(!visible) + } + > + + + ) : ( + + setVisible(!visible) + } + > + + + )} + + + + + + + {/* This section allows the teacher to set the maximum score for the assignment.*/} + + + Max Score + + {loading ? ( + + ) : ( + { + if ( + event.target + .value !== + '' + ) { + const newScore = + Math.max( + parseInt( + event + .target + .value + ), + 0 + ) + SetMaxScore + ? SetMaxScore( + newScore + ) + : undefined + } else { + SetMaxScore + ? SetMaxScore( + parseInt( + event + .target + .value + ) + ) + : undefined + } + }} + /> + )} + + + {/* Submit and Cancel buttons */} + + + + setCancelConfirmation( + true + ) + } + sx={{ + backgroundColor: + 'secondary.main', + borderRadius: 2, + }} + > + + + + + + + + + + + + + {/* Popup for adding restrictions */} + + {/* Confirmation popup for deleting project */} + + + {/* Confirmation popup for saving project */} + + {/* Confirmation popup for canceling changes*/} + + + + ) : ( + navigate('*') + )} + + )} - ):( - navigate('*') - )} - -)} - -)} + ) +} diff --git a/frontend/frontend/src/pages/assignmentPage/AssignmentPage.tsx b/frontend/frontend/src/pages/assignmentPage/AssignmentPage.tsx index 9e8000f8..48950032 100644 --- a/frontend/frontend/src/pages/assignmentPage/AssignmentPage.tsx +++ b/frontend/frontend/src/pages/assignmentPage/AssignmentPage.tsx @@ -2,10 +2,17 @@ import { Header } from '../../components/Header.tsx' import FileUploadButton from '../../components/FileUploadButton.tsx' import { SubmissionListItemStudentPage } from '../../components/SubmissionListItemStudentPage.tsx' import { SubmissionListItemTeacherPage } from '../../components/SubmissionListItemTeacherPage.tsx' -import {Card, Button, Divider, EvenlySpacedRow, SecundaryButton} from '../../components/CustomComponents.tsx' +import { + Button, + Card, + Divider, + EvenlySpacedRow, + SecundaryButton, +} from '../../components/CustomComponents.tsx' import { Box, - CircularProgress, Grid, + CircularProgress, + Grid, List, Skeleton, Stack, @@ -141,41 +148,44 @@ export function AssignmentPage() { const downloadAllSubmissions = () => { const zip = new JSZip() const downloadPromises: Promise[] = [] - submissions.forEach(submission => { + submissions.forEach((submission) => { downloadPromises.push( new Promise(async (resolve, reject) => { try { // Get the submission details const submissionResponse = await instance.get( `/indieningen/${submission.indiening_id}/` - ); - const newSubmission = submissionResponse.data; + ) + const newSubmission = submissionResponse.data // Get the submission file const fileResponse = await instance.get( `/indieningen/${submission.indiening_id}/indiening_bestand/`, { responseType: 'blob' } - ); - let filename = 'indiening.zip'; + ) + let filename = 'indiening.zip' if (newSubmission.bestand) { - filename = newSubmission.bestand.replace(/^.*[\\/]/, ''); + filename = newSubmission.bestand.replace( + /^.*[\\/]/, + '' + ) } const blob = new Blob([fileResponse.data], { type: fileResponse.headers['content-type'], - }); + }) const file = new File([blob], filename, { type: fileResponse.headers['content-type'], - }); - newSubmission.bestand = file; - newSubmission.filename = filename; + }) + newSubmission.bestand = file + newSubmission.filename = filename // Add the file to the zip - zip.file(filename, fileResponse.data); - resolve(newSubmission); + zip.file(filename, fileResponse.data) + resolve(newSubmission) } catch (err) { - console.error(`Error downloading submission:`, err); - reject(err); + console.error(`Error downloading submission:`, err) + reject(err) } }) - ); + ) }) Promise.all(downloadPromises) .then(() => { @@ -184,7 +194,8 @@ export function AssignmentPage() { const url = window.URL.createObjectURL(blob) const a = document.createElement('a') a.href = url - a.download = 'all_submissions_' + assignment?.titel + '_.zip' + a.download = + 'all_submissions_' + assignment?.titel + '_.zip' document.body.appendChild(a) a.click() a.remove() @@ -365,7 +376,7 @@ export function AssignmentPage() { {/* Assignment description */} - + - - {t('group')} - , - - {t('time')} - , - - Score - , - - Status - , - - {t('download')} - - ]} - /> - - - + {t('group')} + , + + {t('time')} + , + + Score + , + + Status + , + + {t('download')} + , + ]} + /> + + + - - - {loading ? ( - [...Array(3)].map((_, index) => ( - - )) - ) : ( - <> - {groups.map((group,index) => ( + > + + + {loading ? ( + [...Array(3)].map( + (_, index) => ( + + ) + ) + ) : ( <> - {index != 0 ? : <>} - - group.groep_id - ) - ) + - 1 - ).toString()} - group_id={group.groep_id.toString()} - assignment_id={ - assignmentId - ? assignmentId - : '' - } - course_id={ - courseId - ? courseId - : '' - } - /> + {groups.map( + (group, index) => ( + <> + {index != + 0 ? ( + + ) : ( + <> + )} + + group.groep_id + ) + ) + + 1 + ).toString()} + group_id={group.groep_id.toString()} + assignment_id={ + assignmentId + ? assignmentId + : '' + } + course_id={ + courseId + ? courseId + : '' + } + /> + + ) + )} - ))} - - )} - + )} + @@ -717,22 +762,34 @@ export function AssignmentPage() { padding: 3, }} > - - {t('submission')} - , - - {t('time')} - , - - Status - - ]}/> + + {t('submission')} + , + + {t('time')} + , + + Status + , + ]} + /> - + @@ -766,13 +823,24 @@ export function AssignmentPage() { ? 1 : -1 ) - .map((submission,index) => ( + .map( + ( + submission, + index + ) => ( - {index != 0 ? : <>} + {index != + 0 ? ( + + ) : ( + <> + + + )} - - - - - - - + + + + - - {t('submit')} - - - - - + + + + {t('submit')} + + + + + void,handleLeave: ()=> void){ - if(isin){ +function joinLeaveButton( + isin: boolean, + handleJoin: () => void, + handleLeave: () => void +) { + if (isin) { return ( <> + {/* Main content box */} - - - - - {loading ? ( - - ) : ( - <> - setOpenSaveScoresPopup(true)} + - - - - )} - setOpenDeleteScoresPopup(true)} - sx={{ - backgroundColor: 'secondary.main', - borderRadius: 2, - }} - > - - - - - {/* Popup for confirming saving scores */} - setOpenSaveScoresPopup(false)} - doAction={saveScores} - /> - {/* Popup for confirming deletion of scores */} - setOpenDeleteScoresPopup(false)} - doAction={deleteScores} - /> - + {/* Render StudentsView component if project is defined */} + {loading ? ( + + + + ) : ( + <> + {project && ( + + )} + + )} + + {/* Footer section with action buttons */} + + + + + + + + + + {loading ? ( + + ) : ( + <> + + setOpenSaveScoresPopup( + true + ) + } + sx={{ + color: 'background.default', + '&:hover': { + color: 'text.primary', + }, + backgroundColor: + 'primary.main', + borderRadius: 2, + }} + > + + + + )} + + setOpenDeleteScoresPopup(true) + } + sx={{ + backgroundColor: + 'secondary.main', + borderRadius: 2, + }} + > + + + + + {/* Popup for confirming saving scores */} + + setOpenSaveScoresPopup(false) + } + doAction={saveScores} + /> + {/* Popup for confirming deletion of scores */} + + setOpenDeleteScoresPopup(false) + } + doAction={deleteScores} + /> + + + ) : ( + navigate('*') + )} + + )} - ):( - navigate('*') - )} - -)} - -)} + ) +} diff --git a/frontend/frontend/src/pages/scoresPage/StudentScoreListItem.tsx b/frontend/frontend/src/pages/scoresPage/StudentScoreListItem.tsx index 7805f909..0a6ac60a 100644 --- a/frontend/frontend/src/pages/scoresPage/StudentScoreListItem.tsx +++ b/frontend/frontend/src/pages/scoresPage/StudentScoreListItem.tsx @@ -65,22 +65,22 @@ export function StudentScoreListItem({ // Get the submission details const submissionResponse = await instance.get( `/indieningen/${lastSubmission?.indiening_id}/` - ); - const newSubmission = submissionResponse.data; + ) + const newSubmission = submissionResponse.data // Get the submission file const fileResponse = await instance.get( `/indieningen/${lastSubmission?.indiening_id}/indiening_bestand/`, { responseType: 'blob' } - ); + ) if (newSubmission.bestand) { - filename = newSubmission.bestand.replace(/^.*[\\/]/, ''); + filename = newSubmission.bestand.replace(/^.*[\\/]/, '') } const blob = new Blob([fileResponse.data], { type: fileResponse.headers['content-type'], - }); + }) const file = new File([blob], filename, { type: fileResponse.headers['content-type'], - }); + }) const url = window.URL.createObjectURL(file) const a = document.createElement('a') a.href = url @@ -155,24 +155,24 @@ export function StudentScoreListItem({ )} {/* Display download icon */} - - -
- {lastSubmission ? ( - - ) : ( - - )} -
-
-
+ + +
+ {lastSubmission ? ( + + ) : ( + + )} +
+
+
diff --git a/frontend/frontend/src/pages/subjectsPage/AddChangeSubjectPage.tsx b/frontend/frontend/src/pages/subjectsPage/AddChangeSubjectPage.tsx index 7868a6fc..946cccde 100644 --- a/frontend/frontend/src/pages/subjectsPage/AddChangeSubjectPage.tsx +++ b/frontend/frontend/src/pages/subjectsPage/AddChangeSubjectPage.tsx @@ -9,7 +9,7 @@ import { Stack, TextField, Typography, - CircularProgress + CircularProgress, } from '@mui/material' import { Header } from '../../components/Header' import { Button } from '../../components/CustomComponents.tsx' @@ -572,157 +572,176 @@ export function AddChangeSubjectPage() { <> {user?.is_lesgever ? ( // Rendering UI for teacher - <> - -
- - - - - {t('subject_name') + ':'} - - {loading ? ( - + +
- ) : ( - - setTitle(event.target.value) - } - sx={{ height: 60 }} - /> - )} - - - - - - - - - {t('students')} - - - - {UserList( - loading, - students, - setSelectedStudent, - setOpenStudent - )} - - - - {UploadPart( - studentFile, - handleStudentFileChange, - setEmailStudent, - handleAddStudent, - t('upload_students') - )} - - - {DialogWindow( - handleCloseStudent, - openStudent, - handleRemoveStudent, - t('delete_student') - )} - - - - - {t('teachers')} - - - - {UserList( - loading, - teachers, - setSelectedTeacher, - setOpenTeacher - )} - - - - {UploadPart( - teacherFile, - handleTeacherFileChange, - setEmailTeacher, - handleAddTeacher, - t('upload_teachers') - )} - - - {DialogWindow( - handleCloseTeacher, - openTeacher, - handleRemoveTeacher, - t('delete_teacher') + + + + + {t('subject_name') + ':'} + + {loading ? ( + + ) : ( + + setTitle( + event.target.value + ) + } + sx={{ height: 60 }} + /> + )} + + + + + + + + + {t('students')} + + + + {UserList( + loading, + students, + setSelectedStudent, + setOpenStudent + )} + + + + {UploadPart( + studentFile, + handleStudentFileChange, + setEmailStudent, + handleAddStudent, + t('upload_students') + )} + + + {DialogWindow( + handleCloseStudent, + openStudent, + handleRemoveStudent, + t('delete_student') + )} + + + + + {t('teachers')} + + + + {UserList( + loading, + teachers, + setSelectedTeacher, + setOpenTeacher + )} + + + + {UploadPart( + teacherFile, + handleTeacherFileChange, + setEmailTeacher, + handleAddTeacher, + t('upload_teachers') + )} + + + {DialogWindow( + handleCloseTeacher, + openTeacher, + handleRemoveTeacher, + t('delete_teacher') + )} + + + + ) : ( + navigate('*') )} - - + + )} - ):( - navigate('*') - )} - -)} - -)} + ) +} diff --git a/frontend/frontend/src/pages/subjectsPage/AssignmentListItemSubjectsPage.tsx b/frontend/frontend/src/pages/subjectsPage/AssignmentListItemSubjectsPage.tsx index 7c3f81ec..4872d9a6 100644 --- a/frontend/frontend/src/pages/subjectsPage/AssignmentListItemSubjectsPage.tsx +++ b/frontend/frontend/src/pages/subjectsPage/AssignmentListItemSubjectsPage.tsx @@ -13,7 +13,7 @@ import DeleteOutlinedIcon from '@mui/icons-material/DeleteOutlined' import React, { useState } from 'react' import dayjs, { Dayjs } from 'dayjs' import { Score } from '../../components/SubmissionListItemTeacherPage.tsx' -import {EvenlySpacedRow} from "../../components/CustomComponents.tsx"; +import { EvenlySpacedRow } from '../../components/CustomComponents.tsx' /** * This component is used to display a single assignment in the list of assignments. @@ -72,75 +72,79 @@ export function AssignmentListItemSubjectsPage({ return ( <> - {isStudent ? ( - , - , - 0 - ? submissions > 1 - ? submissions + - ' ' + - t('submissions') - : submissions + - ' ' + - t('submission') - : t('no_submissions') - } - />, - <> - {submissions > 0 ? ( + , - ) : ( + />, - )} - - ]}/> + primary={ + submissions > 0 + ? submissions > 1 + ? submissions + + ' ' + + t('submissions') + : submissions + + ' ' + + t('submission') + : t('no_submissions') + } + />, + <> + {submissions > 0 ? ( + + ) : ( + + )} + , + ]} + /> ) : ( <> {/* In case of the user being the teacher: */} - , - , - ]}/> + , + , + , + ]} + /> )} diff --git a/frontend/frontend/src/pages/subjectsPage/ProjectsView.tsx b/frontend/frontend/src/pages/subjectsPage/ProjectsView.tsx index 09f1e01c..0e7a64e4 100644 --- a/frontend/frontend/src/pages/subjectsPage/ProjectsView.tsx +++ b/frontend/frontend/src/pages/subjectsPage/ProjectsView.tsx @@ -1,4 +1,8 @@ -import {Card, Divider, EvenlySpacedRow} from '../../components/CustomComponents.tsx' +import { + Card, + Divider, + EvenlySpacedRow, +} from '../../components/CustomComponents.tsx' import { Box, Skeleton, Typography } from '@mui/material' import List from '@mui/material/List' import { t } from 'i18next' @@ -151,56 +155,81 @@ export function ProjectsView({ return ( <> - - {!gebruiker.is_lesgever ? ( - <> - {/* Show the UI from the perspective of a student. */} - - Project - , - - Deadline - , - - {t('submissions')} - , - - Score - - ]}/> - - ) : ( - <> - {/* Show the UI from the perspective of a teacher. */} - - Project - , - - Deadline - , - - {t('edit')} - ]} - /> - - )} - - + + {!gebruiker.is_lesgever ? ( + <> + {/* Show the UI from the perspective of a student. */} + + Project + , + + Deadline + , + + {t('submissions')} + , + + Score + , + ]} + /> + + ) : ( + <> + {/* Show the UI from the perspective of a teacher. */} + + Project + , + + Deadline + , + + {t('edit')} + , + ]} + /> + + )} + + {/* The list below will display the projects with their information */} @@ -233,7 +262,7 @@ export function ProjectsView({ ) .map((project) => ( <> - + {/* List of Students */} {students.length > 0 ? ( - - {students.map((student) => ( - - - - ))} - + + {students.map((student) => ( + + + + ))} + ) : ( - {t('loading') + ' ' + t('students') + '...'} + + {t('loading') + ' ' + t('students') + '...'} + )} diff --git a/frontend/frontend/src/pages/subjectsPage/SubjectsPage.tsx b/frontend/frontend/src/pages/subjectsPage/SubjectsPage.tsx index 3770ae4b..1be3d95a 100644 --- a/frontend/frontend/src/pages/subjectsPage/SubjectsPage.tsx +++ b/frontend/frontend/src/pages/subjectsPage/SubjectsPage.tsx @@ -71,7 +71,7 @@ export function SubjectsPage() { // State for loading the page const [loading, setLoading] = useState(true) const [userLoading, setUserLoading] = useState(true) - const [studentsLoading, setStudentsLoading] = useState(true); + const [studentsLoading, setStudentsLoading] = useState(true) useEffect(() => { // Get the data for this course. @@ -136,27 +136,27 @@ export function SubjectsPage() { useEffect(() => { async function fetchStudents() { - setStudentsLoading(true); - const temp_students = []; + setStudentsLoading(true) + const temp_students = [] for (const s of course?.studenten || []) { try { - const userResponse = await instance.get(`/gebruikers/${s}/`); - temp_students.push(userResponse.data); + const userResponse = await instance.get(`/gebruikers/${s}/`) + temp_students.push(userResponse.data) } catch (error) { - console.error('Error fetching student data:', error); - setFetchError(true); + console.error('Error fetching student data:', error) + setFetchError(true) } } // Update the state with the fetched data - setStudents(temp_students); - setStudentsLoading(false); + setStudents(temp_students) + setStudentsLoading(false) } - + // Fetch students fetchStudents().catch((error) => console.error('Error fetching students data:', error) - ); - }, [course]); + ) + }, [course]) const addProject = () => { console.log('add project') @@ -261,15 +261,13 @@ export function SubjectsPage() { }} > {/* Give the student the option to select current or archived projects. */} - {course.gearchiveerd? + {course.gearchiveerd ? ( - : + ) : ( , ]} /> - } + )} - {course.gearchiveerd? + {course.gearchiveerd ? ( - undefined - } - archiveAssignment={() => - undefined - } + deleteAssignment={() => undefined} + archiveAssignment={() => undefined} changeVisibilityAssignment={() => undefined } courseId={courseID} /> - : + ) : ( , ]} /> - } + )} - + diff --git a/frontend/frontend/src/pages/submissionPage/SubmissionPage.tsx b/frontend/frontend/src/pages/submissionPage/SubmissionPage.tsx index 3f5af53d..988d2b4e 100644 --- a/frontend/frontend/src/pages/submissionPage/SubmissionPage.tsx +++ b/frontend/frontend/src/pages/submissionPage/SubmissionPage.tsx @@ -129,17 +129,18 @@ export function SubmissionPage() { ) setRestrictions(restrictions.data) - const submissionResponse = await instance.get(`indieningen/${submissionId}/`) + const submissionResponse = await instance.get( + `indieningen/${submissionId}/` + ) //Get the submission file const newSubmission: Submission = submissionResponse.data - newSubmission.filename = submissionResponse.data.bestand.replace( - /^.*[\\/]/, - '' - ) + newSubmission.filename = + submissionResponse.data.bestand.replace(/^.*[\\/]/, '') newSubmission.bestand = await instance .get(`/indieningen/${submissionId}/indiening_bestand`, { responseType: 'blob', - }).then((res) => { + }) + .then((res) => { let filename = 'indiening.zip' if (newSubmission.filename) { filename = newSubmission.filename diff --git a/frontend/frontend/src/routes.tsx b/frontend/frontend/src/routes.tsx index d6bca41d..7762149f 100644 --- a/frontend/frontend/src/routes.tsx +++ b/frontend/frontend/src/routes.tsx @@ -8,7 +8,7 @@ import ErrorPage from './pages/ErrorPage.tsx' import MainPage from './pages/mainPage/MainPage.tsx' import { GroupsPage } from './pages/groupsPage/GroupsPage.tsx' import { AssignmentPage } from './pages/assignmentPage/AssignmentPage.tsx' -import {ChooseGroup} from "./pages/groupsPage/ChooseGroup.tsx"; +import { ChooseGroup } from './pages/groupsPage/ChooseGroup.tsx' //TODO: add change/add course page when implemented const router = createBrowserRouter([ From 9dedf3b21c234fd51286b7b5ececab2791f68602 Mon Sep 17 00:00:00 2001 From: Ben De Meurichy Date: Sun, 19 May 2024 17:11:36 +0200 Subject: [PATCH 4/4] Revert "inladen studenten bij groepen is niet meer onacceptabel traag" This reverts commit 3f6ea634864ddea3245f5263bc65d08419bc5bdc. --- .../src/pages/groupsPage/GroupsPage.tsx | 53 ++++++++++++------- 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/frontend/frontend/src/pages/groupsPage/GroupsPage.tsx b/frontend/frontend/src/pages/groupsPage/GroupsPage.tsx index 003057c6..38707a4d 100644 --- a/frontend/frontend/src/pages/groupsPage/GroupsPage.tsx +++ b/frontend/frontend/src/pages/groupsPage/GroupsPage.tsx @@ -30,8 +30,6 @@ import { Add } from '@mui/icons-material' import ClearIcon from '@mui/icons-material/Clear' import SaveIcon from '@mui/icons-material/Save' import WarningPopup from '../../components/WarningPopup.tsx' -import axios, { AxiosResponse } from 'axios' -import { User } from '../subjectsPage/AddChangeSubjectPage.tsx' // group interface export interface Group { @@ -184,21 +182,39 @@ export function GroupsPage() { await instance .get('/vakken/' + courseId) .then(async (response) => { - // This function fetches the names of the students in parallel const newStudentNames = new Map() - const studentPromises: Promise>[] = - response.data.studenten.map((id: number) => - instance.get('/gebruikers/' + id) - ) - const studentResponses = await axios.all(studentPromises) - - studentResponses.forEach((response) => { - const student: User = response.data - newStudentNames.set( - student.user, - student.first_name + ' ' + student.last_name - ) - }) + + for (const student of response.data.studenten) { + await instance + .get('/gebruikers/' + student) + .then((response) => { + newStudentNames.set( + student, + response.data.first_name + + ' ' + + response.data.last_name + ) + }) + .catch((error) => { + console.log(error) + }) + } + for (const student of response.data.studenten) { + await instance + .get('/gebruikers/' + student) + .then((response) => { + newStudentNames.set( + student, + response.data.first_name + + ' ' + + response.data.last_name + ) + console.log( + 'available names:' + + Array.from(newStudentNames.entries()) + ) + }) + } setStudentNames(() => newStudentNames) }) @@ -259,7 +275,7 @@ export function GroupsPage() { .catch((error) => { console.log(error) }) - }, [assignmentId, courseId]) + }, [assignmentId, courseId, newGroupSize, studentNames.size]) useEffect(() => { setAvailableStudents(() => @@ -271,7 +287,7 @@ export function GroupsPage() { ) ) setFilteredStudents(availableStudents) - }, [availableStudents, newGroups, studentNames]) + }, [newGroups, studentNames]) // Create new groups when the group size changes useEffect(() => { @@ -296,7 +312,6 @@ export function GroupsPage() { } }, [ assignmentId, - availableStudents, availableStudents.length, newGroupSize, newGroups.length,