diff --git a/frontend/components/StudentSidebar.tsx b/frontend/components/StudentSidebar.tsx index 501de44ec..329c18660 100644 --- a/frontend/components/StudentSidebar.tsx +++ b/frontend/components/StudentSidebar.tsx @@ -21,6 +21,8 @@ type StudentsSidebarProps = { setError: (error: string) => void; refresh: [boolean, boolean]; setRefresh: (refresh: [boolean, boolean]) => void; + setStudentBase: (studentBase: StudentBase) => void; + studentBase: StudentBase; }; /** @@ -67,7 +69,7 @@ async function searchStudent( // Fallback for no status selected if (!getStatusFilterList(studentSearchParameters)) { const newState = { ...state }; - newState.page = state.page + 1; + newState.page = 0; newState.hasMoreItems = false; newState.loading = false; setState(newState); @@ -143,6 +145,8 @@ const StudentSidebar: React.FC = ({ setError, refresh, setRefresh, + setStudentBase, + studentBase, }: StudentsSidebarProps) => { const router = useRouter(); const [showFilter, setShowFilter] = useState(true); @@ -238,7 +242,7 @@ const StudentSidebar: React.FC = ({ state.pageSize = prevPageSize; setRefresh([false, refresh[1]]); } - }, [refresh[0]]); + }, [refresh]); /** * Call to refresh students list from page 0 with current filters applied @@ -601,7 +605,12 @@ const StudentSidebar: React.FC = ({ ( - + )} renderWhenEmpty={showBlank} // let user know if initial data is loading or there is no data to show hasMoreItems={state.hasMoreItems} diff --git a/frontend/components/student/StudentHolder.tsx b/frontend/components/student/StudentHolder.tsx index 3254cdc5f..9ed402fc9 100644 --- a/frontend/components/student/StudentHolder.tsx +++ b/frontend/components/student/StudentHolder.tsx @@ -1,16 +1,18 @@ -import { useState } from 'react'; import { ItemTypes, Student, StudentBase } from '../../lib/types'; import { useDrop } from 'react-dnd'; import StudentView from './StudentView'; type StudentHolderProp = { setRefresh: (refresh: [boolean, boolean]) => void; + studentBase: StudentBase; + setStudentBase: (studentBase: StudentBase) => void; }; const StudentHolder: React.FC = ({ setRefresh, + studentBase, + setStudentBase, }: StudentHolderProp) => { - const [studentBase, setStudentBase] = useState({} as StudentBase); /** * This catches the dropped studentTile * The studentTile passes its student as the DragObject to this function on drop @@ -41,7 +43,11 @@ const StudentHolder: React.FC = ({ {/* hold the student information, only load this if an actual studentBase object exists */} {/* studentBase.id is used to check if this is an actual object or just an empty dummy */} {studentBase.id && ( - + )} ); diff --git a/frontend/components/student/StudentView.tsx b/frontend/components/student/StudentView.tsx index 331f9cdc6..1f336c6e3 100644 --- a/frontend/components/student/StudentView.tsx +++ b/frontend/components/student/StudentView.tsx @@ -30,6 +30,7 @@ const x_mark = ; type StudentViewProp = { studentInput: StudentBase; setRefresh: (refresh: [boolean, boolean]) => void; + setOriginalStudentBase: (originalStudentBase: StudentBase) => void; }; type StatusSuggestionProp = { @@ -87,7 +88,6 @@ async function setStudentSuggestion( ) .then(() => { reloadStudent(studentId, setStudentBase, signal, setError, router); - setRefresh([true, true]); }) .catch((err) => { parseError(err, setError, signal, router); @@ -163,6 +163,7 @@ async function getEntireStudent( const StudentView: React.FC = ({ studentInput, setRefresh, + setOriginalStudentBase, }: StudentViewProp) => { const [user] = useUser(); // Needed to reload student when a suggestion is done or status is changed @@ -194,6 +195,7 @@ const StudentView: React.FC = ({ const signal = controller.signal; // This is a safety check, not really needed right now but it avoids accidents if (studentBase.id !== undefined) { + setOriginalStudentBase(studentBase); setStatus({ value: '', label: studentBase.status } as { value: string; label: string; diff --git a/frontend/components/students/StudentTile.tsx b/frontend/components/students/StudentTile.tsx index 4b0747110..c09e96631 100644 --- a/frontend/components/students/StudentTile.tsx +++ b/frontend/components/students/StudentTile.tsx @@ -25,6 +25,8 @@ const tilde_mark = ; */ type StudentProp = { student: StudentBase; + setStudentBase: (studentBase: StudentBase) => void; + studentBase: StudentBase; }; /** @@ -82,14 +84,22 @@ async function getEntireStudent( /** * This creates the tiles show in the StudentSidebar * @param student - The student whose information should be shown + * @param setStudentBase - callback to set studentBase object from dragndrop + * @param studentBase - object */ -const StudentTile: React.FC = ({ student }: StudentProp) => { +const StudentTile: React.FC = ({ + student, + setStudentBase, + studentBase, +}: StudentProp) => { // Need to set a project with all keys present to avoid the render code throwing undefined errors const [myStudent, setMyStudent]: [Student, (myStudent: Student) => void] = useState(convertStudentBase(student) as Student); // using different names to avoid confusion - const [myStudentBase]: [StudentBase, (myStudentBase: StudentBase) => void] = - useState(student as StudentBase); + const [myStudentBase, setMyStudentBase]: [ + StudentBase, + (myStudentBase: StudentBase) => void + ] = useState(student as StudentBase); // TODO find a place to actually show this error const [, setError]: [string, (error: string) => void] = useState(''); @@ -97,6 +107,12 @@ const StudentTile: React.FC = ({ student }: StudentProp) => { useAxiosAuth(); let controller = new AbortController(); + useEffect(() => { + if (studentBase.id == student.id) { + setMyStudentBase(studentBase); + } + }, [studentBase, student]); + useEffect(() => { controller.abort(); controller = new AbortController(); @@ -111,16 +127,23 @@ const StudentTile: React.FC = ({ student }: StudentProp) => { }; }, [myStudentBase]); + useEffect(() => { + const newSuggestionCount = {} as statusSuggestionStatusToNumberDict; + myStudent.statusSuggestions.forEach((suggestion) => { + newSuggestionCount[suggestion.status] = + newSuggestionCount[suggestion.status] + 1 || 1; + }); + setSuggestionCounts(newSuggestionCount); + }, [myStudent]); + /** * This counts the different status suggestions to create the pie chart * The dict is empty without default values so when using suggestionCounts * you should add '|| 0' to avoid getting an undefined error */ - const suggestionCounts = {} as statusSuggestionStatusToNumberDict; - myStudent.statusSuggestions.forEach((suggestion) => { - suggestionCounts[suggestion.status] = - suggestionCounts[suggestion.status] + 1 || 1; - }); + const [suggestionCounts, setSuggestionCounts] = useState( + {} as statusSuggestionStatusToNumberDict + ); /** * This hook allows dragging the StudentTile @@ -139,7 +162,7 @@ const StudentTile: React.FC = ({ student }: StudentProp) => { return ( // TODO add a chevron dropdown to show possible roles, student coach, ... -
+
setStudentBase(student)}>
diff --git a/frontend/pages/[editionName]/projects.tsx b/frontend/pages/[editionName]/projects.tsx index de49b9e83..c4c786ffa 100644 --- a/frontend/pages/[editionName]/projects.tsx +++ b/frontend/pages/[editionName]/projects.tsx @@ -5,7 +5,12 @@ import { Icon } from '@iconify/react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faMagnifyingGlass } from '@fortawesome/free-solid-svg-icons'; import { useEffect, useState } from 'react'; -import { ProjectBase, ProjectData, UserRole } from '../../lib/types'; +import { + ProjectBase, + ProjectData, + StudentBase, + UserRole, +} from '../../lib/types'; import { axiosAuthenticated } from '../../lib/axios'; import Endpoints from '../../lib/endpoints'; import useAxiosAuth from '../../hooks/useAxiosAuth'; @@ -250,6 +255,8 @@ const Projects: NextPage = () => { setError={setError} refresh={refreshStudents} setRefresh={setRefreshStudents} + setStudentBase={() => null} + studentBase={{} as StudentBase} /> diff --git a/frontend/pages/[editionName]/students.tsx b/frontend/pages/[editionName]/students.tsx index 1de24d17a..adcbcd4a4 100644 --- a/frontend/pages/[editionName]/students.tsx +++ b/frontend/pages/[editionName]/students.tsx @@ -3,7 +3,7 @@ import Header from '../../components/Header'; import StudentSidebar from '../../components/StudentSidebar'; import { Icon } from '@iconify/react'; import { useState } from 'react'; -import { UserRole } from '../../lib/types'; +import { StudentBase, UserRole } from '../../lib/types'; import { HTML5Backend } from 'react-dnd-html5-backend'; import { DndProvider } from 'react-dnd'; import useAxiosAuth from '../../hooks/useAxiosAuth'; @@ -20,6 +20,7 @@ const arrow_in = ; const Students: NextPage = () => { // Used to hide / show the students sidebar on screen width below 768px const [showSidebar, setShowSidebar] = useState(false); + const [studentBase, setStudentBase] = useState({} as StudentBase); const [refreshStudents, setRefreshStudents] = useState([false, true] as [ boolean, boolean @@ -52,6 +53,8 @@ const Students: NextPage = () => { setError={setError} refresh={refreshStudents} setRefresh={setRefreshStudents} + setStudentBase={setStudentBase} + studentBase={studentBase} /> @@ -78,7 +81,11 @@ const Students: NextPage = () => { {/* This contains the actual student info */}
- +