diff --git a/services/frontend/src/common/InfoToolTips/closeToGraduation.ts b/services/frontend/src/common/InfoToolTips/closeToGraduation.ts index fe37e28acf..d75c1cc65c 100644 --- a/services/frontend/src/common/InfoToolTips/closeToGraduation.ts +++ b/services/frontend/src/common/InfoToolTips/closeToGraduation.ts @@ -1,10 +1,7 @@ export const closeToGraduationToolTips = ` - You can filter students by choosing a degree programme and/or a faculty. - You can also select multiple programmes or faculties. - Please note that if you select a faculty, only the degree programmes of that faculty will be shown. + You can use the dropdowns in the ’Faculty’ and ’Programme’ column headers to filter students. You can also select multiple programmes or faculties. Please note that if you select a faculty, only the programmes of that faculty will be shown. - Students are included in this list based on the number of credits they have completed in their primary study plan. - The limits for each study programme are as follows: + Students are included in this list based on the number of credits they have completed in their primary study plan. The limits for each study programme are as follows: - All bachelor’s programmes: 140 credits - Degree Programme in Veterinary Medicine (eläinlääketieteen lisensiaatin koulutusohjelma): 150 credits @@ -13,6 +10,5 @@ export const closeToGraduationToolTips = ` - Master’s Programme in Psychology (psykologian maisteriohjelma): 115 credits - Other master’s programmes: 70 credits - If you have access to all student data in Oodikone, you can also see all students here. - Otherwise you will only see the students in your study guidance groups. + If you have access to all student data in Oodikone, you can also see all students here. Otherwise you will only see the students in your study guidance groups. ` diff --git a/services/frontend/src/components/CloseToGraduation/index.jsx b/services/frontend/src/components/CloseToGraduation/index.jsx deleted file mode 100644 index 49bb261444..0000000000 --- a/services/frontend/src/components/CloseToGraduation/index.jsx +++ /dev/null @@ -1,398 +0,0 @@ -import { range } from 'lodash' -import { useState } from 'react' -import { Divider, Dropdown, Form, Icon, Loader, Message, Radio, Tab } from 'semantic-ui-react' - -import { createLocaleComparator, getCurrentSemester, getEnrollmentTypeTextForExcel, isFall } from '@/common' -import { useTitle } from '@/common/hooks' -import { closeToGraduationToolTips } from '@/common/InfoToolTips' -import { StudentInfoItem } from '@/components/common/StudentInfoItem' -import { InfoBox } from '@/components/InfoBox' -import { useLanguage } from '@/components/LanguagePicker/useLanguage' -import { getSemestersPresentFunctions } from '@/components/PopulationStudents/StudentTable/GeneralTab/columnHelpers/semestersPresent' -import { PaginatedSortableTable } from '@/components/SortableTable/PaginatedSortableTable' -import { StudentNameVisibilityToggle, useStudentNameVisibility } from '@/components/StudentNameVisibilityToggle' -import { ISO_DATE_FORMAT, LONG_DATE_TIME_FORMAT } from '@/constants/date' -import { useGetStudentsCloseToGraduationQuery } from '@/redux/closeToGraduation' -import { useGetFacultiesQuery } from '@/redux/facultyStats' -import { useGetSemestersQuery } from '@/redux/semesters' -import { useFilteredAndFormattedStudyProgrammes } from '@/redux/studyProgramme' -import { reformatDate } from '@/util/timeAndDate' - -const NUMBER_OF_DISPLAYED_SEMESTERS = 6 - -const getColumns = ( - getTextIn, - namesVisible, - semesterEnrollmentsVisible, - studyTrackVisible, - allSemestersMap, - bachelorStudentsAreDisplayed, - semesterEnrollmentFunctions -) => { - const { getSemesterEnrollmentsContent, getSemesterEnrollmentsVal } = semesterEnrollmentFunctions - const currentSemesterCode = getCurrentSemester(allSemestersMap)?.semestercode - const semestersToInclude = range( - isFall(currentSemesterCode) - ? currentSemesterCode - NUMBER_OF_DISPLAYED_SEMESTERS + 2 - : currentSemesterCode - NUMBER_OF_DISPLAYED_SEMESTERS + 1, - isFall(currentSemesterCode) ? currentSemesterCode + 2 : currentSemesterCode + 1 - ) - return [ - { - key: 'studentNumber', - title: 'Student number', - getRowVal: row => row.student.studentNumber, - getRowContent: row => , - filterable: false, - }, - { - key: 'name', - title: 'Name', - getRowVal: row => row.student.name, - filterable: false, - displayColumn: namesVisible, - }, - { - key: 'phoneNumber', - title: 'Phone number', - getRowVal: row => row.student.phoneNumber, - displayColumn: false, - }, - { - key: 'email', - title: 'Email', - getRowVal: row => row.student.email, - displayColumn: false, - }, - { - key: 'secondaryEmail', - title: 'Secondary email', - getRowVal: row => row.student.secondaryEmail, - displayColumn: false, - }, - { - key: 'programme', - title: 'Programme', - getRowVal: row => getTextIn(row.programme.name), - filterable: false, - }, - { - key: 'studytrack', - title: 'Study track', - getRowVal: row => getTextIn(row.programme.studyTrack), - displayColumn: studyTrackVisible, - }, - { - key: 'startOfStudyright', - title: 'Start of\nstudy right', - getRowVal: row => row.studyright.startDate, - formatValue: date => reformatDate(date, ISO_DATE_FORMAT), - filterType: 'date', - }, - { - key: 'startedInProgramme', - title: 'Started in programme', - getRowVal: row => row.programme.startedAt, - formatValue: date => reformatDate(date, ISO_DATE_FORMAT), - filterType: 'date', - displayColumn: !bachelorStudentsAreDisplayed, - helpText: - "For students with only a study right in the master’s programme, this date is the same as 'Start of study right'. For students with study rights in both the bachelor’s and master’s programmes, this date represents when they started in the master’s programme (i.e. one day after graduating from the bachelor’s programme).", - }, - { - key: 'credits', - title: 'Completed credits', - children: [ - { - key: 'creditsHops', - title: 'HOPS', - getRowVal: row => row.credits.hops, - filterType: 'range', - helpText: "The credits earned from courses in the student's primary study plan", - }, - { - key: 'creditsTotal', - title: 'Total', - getRowVal: row => row.credits.all, - filterType: 'range', - helpText: 'The total number of credits the student has earned at the university', - }, - ], - }, - { - key: 'isBaMa', - title: 'Continued\nfrom\nbachelor’s', - getRowVal: row => (row.studyright.isBaMa ? 'Continued from bachelor’s' : 'Not continued from bachelor’s'), - getRowContent: row => (row.studyright.isBaMa ? : null), - getRowExportVal: row => (row.studyright.isBaMa ? '1' : null), - cellProps: () => ({ style: { textAlign: 'center' } }), - displayColumn: !bachelorStudentsAreDisplayed, - helpText: 'Indicates whether the student has continued their studies from a bachelor’s degree', - }, - { - key: 'curriculumPeriod', - title: 'Curriculum\nperiod', - getRowVal: row => row.curriculumPeriod, - helpText: 'The curriculum period the student has chosen for their primary study plan', - }, - { - key: 'semesterEnrollments', - title: 'Semesters\npresent', - filterType: 'range', - displayColumn: semesterEnrollmentsVisible, - export: false, - getRowContent: row => getSemesterEnrollmentsContent(row.student, row.studyright), - getRowVal: row => getSemesterEnrollmentsVal(row.student, row.studyright), - }, - { - key: 'semestersAbsent', - title: 'Semesters\nabsent', - getRowVal: row => row.numberOfAbsentSemesters, - helpText: - 'The number of semesters the student has been absent (both statutory and non-statutory absences) during their study right', - }, - { - key: 'semesterEnrollmentsForExcel', - title: 'Enrollment status', - displayColumn: false, - children: semestersToInclude.map(semester => ({ - key: `${semester}`, - title: getTextIn(allSemestersMap[`${semester}`]?.name), - displayColumn: false, - getRowVal: student => { - if (!student.studyright.semesterEnrollments) { - return 'Not enrolled' - } - const enrollment = student.studyright.semesterEnrollments.find(enrollment => enrollment.semester === semester) - return getEnrollmentTypeTextForExcel(enrollment?.type, enrollment?.statutoryAbsence) - }, - })), - }, - { - key: 'thesisCompleted', - title: 'Thesis\ncompleted', - getRowVal: row => (row.thesisInfo ? 'Thesis completed' : 'Thesis not completed'), - getRowContent: row => (row.thesisInfo ? : null), - getRowExportVal: row => (row.thesisInfo ? '1' : null), - cellProps: row => - row.thesisInfo - ? { - style: { textAlign: 'center' }, - title: [ - `Attainment date: ${reformatDate(row.thesisInfo.attainmentDate, ISO_DATE_FORMAT)}`, - `Course code: ${row.thesisInfo.courseCode}`, - `Grade: ${row.thesisInfo.grade}`, - ].join('\n'), - } - : {}, - helpText: - 'The thesis attainment must be linked to the correct study right. You can see the attainment date, course code, and grade by hovering over the check mark.', - }, - { - key: 'latestAttainmentDates', - title: 'Latest attainment date', - children: [ - { - key: 'latestAttainmentHops', - title: 'HOPS', - getRowVal: row => reformatDate(row.attainmentDates.latestHops, ISO_DATE_FORMAT), - filterType: 'date', - helpText: 'The date when the student last completed a course in their primary study plan', - }, - { - key: 'latestAttainmentTotal', - title: 'Total', - getRowVal: row => reformatDate(row.attainmentDates.latestTotal, ISO_DATE_FORMAT), - filterType: 'date', - helpText: 'The date when the student last completed any course at the university', - }, - ], - }, - { - key: 'earliestAttainmentDates', - title: 'Earliest\nattainment date', - children: [ - { - key: 'earliestAttainmentHops', - title: 'HOPS', - getRowVal: row => reformatDate(row.attainmentDates.earliestHops, ISO_DATE_FORMAT), - filterType: 'date', - helpText: 'The date when the student first completed a course in their primary study plan', - }, - ], - }, - ] -} - -export const CloseToGraduation = () => { - useTitle('Students close to graduation') - - const { data: students = {}, isError, isLoading } = useGetStudentsCloseToGraduationQuery() - const { data: faculties = [] } = useGetFacultiesQuery() - const { data: semesterData = [] } = useGetSemestersQuery() - const studyProgrammes = useFilteredAndFormattedStudyProgrammes() - const { getTextIn } = useLanguage() - const { visible: namesVisible } = useStudentNameVisibility() - const [chosenFaculties, setChosenFaculties] = useState([]) - const [chosenProgrammes, setChosenProgrammes] = useState([]) - const [activeTabIndex, setActiveTabIndex] = useState(0) - const [rowCount, setRowCount] = useState(0) - const [semesterEnrollmentsVisible, setSemesterEnrollmentsVisible] = useState(false) - const allSemesters = Object.entries(semesterData?.semesters || {}).map(item => item[1]) - const allSemestersMap = allSemesters.reduce((obj, cur, index) => { - obj[index + 1] = cur - return obj - }, {}) - const { getSemesterEnrollmentsContent, getSemesterEnrollmentsVal } = getSemestersPresentFunctions({ - getTextIn, - allSemesters, - allSemestersMap, - filteredStudents: students, - year: `${new Date().getFullYear() - Math.floor(NUMBER_OF_DISPLAYED_SEMESTERS / 2)}`, - }) - const { bachelor = [], masterAndLicentiate = [] } = students - - const bachelorStudentsAreDisplayed = activeTabIndex === 0 - const displayedStudents = bachelorStudentsAreDisplayed ? bachelor : masterAndLicentiate - - const handleDisplayedDataChange = students => setRowCount(students.length) - - const filteredStudents = displayedStudents.filter(s => { - if (chosenProgrammes.length > 0) return chosenProgrammes.includes(s.programme.code) - return chosenFaculties.length === 0 || chosenFaculties.includes(s.programme.code.slice(1, 4)) - }) - - const studyTrackVisible = - chosenProgrammes.length === 1 && filteredStudents.some(student => student.programme.studyTrack != null) - - const columns = getColumns( - getTextIn, - namesVisible, - semesterEnrollmentsVisible, - studyTrackVisible, - allSemestersMap, - bachelorStudentsAreDisplayed, - { - getSemesterEnrollmentsContent, - getSemesterEnrollmentsVal, - } - ) - - const onTabChange = (_event, { activeIndex }) => { - setActiveTabIndex(activeIndex) - setChosenFaculties([]) - setChosenProgrammes([]) - } - - const renderContent = () => { - if (isError) { - return ( - - ) - } - - if (isLoading) return - - const facultyOptions = faculties.map(faculty => ({ - key: faculty.code, - text: getTextIn(faculty.name), - value: faculty.code, - description: faculty.code, - })) - const programmeOptions = studyProgrammes - .filter( - programme => - !programme.value.includes('+') && programme.value.startsWith(bachelorStudentsAreDisplayed ? 'KH' : 'MH') - ) - .filter(programme => (chosenFaculties.length > 0 ? chosenFaculties.includes(programme.value.slice(1, 4)) : true)) - .sort(createLocaleComparator('text')) - - return ( - -
- -
- - - { - setChosenFaculties([...value]) - setChosenProgrammes(chosenProgrammes.filter(p => value.includes(p.slice(1, 4)))) - }} - options={facultyOptions} - placeholder="Choose faculties" - search - selection - value={chosenFaculties} - /> - - - - setChosenProgrammes([...value])} - options={programmeOptions} - placeholder="Choose degree programmes" - search - selection - value={chosenProgrammes} - /> - -
- - {`Last updated: ${reformatDate(students.lastUpdated, LONG_DATE_TIME_FORMAT)}`} - - -
-
- ) - } - - const panes = [ - { - menuItem: "Bachelor's programmes", - render: renderContent, - }, - { - menuItem: "Master's and licentiate's programmes", - render: renderContent, - }, - ] - - return ( -
- - Students close to graduation - -
- - setSemesterEnrollmentsVisible(!semesterEnrollmentsVisible)} - toggle - /> -
- -
- ) -} diff --git a/services/frontend/src/components/Routes/index.jsx b/services/frontend/src/components/Routes/index.jsx index d8f9138b6c..3088ee0ad1 100644 --- a/services/frontend/src/components/Routes/index.jsx +++ b/services/frontend/src/components/Routes/index.jsx @@ -2,7 +2,6 @@ import { Suspense } from 'react' import { Redirect, Route, Switch } from 'react-router-dom' import { isDefaultServiceProvider } from '@/common' -import { CloseToGraduation } from '@/components/CloseToGraduation' import { CompletedCourses } from '@/components/CompletedCoursesSearch' import { CoursePopulation } from '@/components/CoursePopulation' import { CourseStatistics } from '@/components/CourseStatistics' @@ -20,7 +19,7 @@ import { Updater } from '@/components/Updater' import { Users } from '@/components/Users' import { languageCenterViewEnabled } from '@/conf' import { Changelog } from '@/pages/Changelog' -import { CloseToGraduation as NewCloseToGraduation } from '@/pages/CloseToGraduation' +import { CloseToGraduation } from '@/pages/CloseToGraduation' import { Feedback } from '@/pages/Feedback' import { FrontPage } from '@/pages/FrontPage' import { University } from '@/pages/University' @@ -131,7 +130,6 @@ export const Routes = () => ( /> )} - { - const [activePage, setActivePage] = useState(1) - const { rowsPerPage } = props - - useEffect(() => { - setActivePage(1) - }, [rowCount]) - - const showPagination = rowCount > rowsPerPage - - return ( - <> - {showPagination && ( - , icon: true }} - firstItem={{ content: , icon: true }} - lastItem={{ content: , icon: true }} - nextItem={{ content: , icon: true }} - onPageChange={(_, { activePage }) => setActivePage(activePage)} - prevItem={{ content: , icon: true }} - totalPages={Math.ceil(rowCount / rowsPerPage)} - /> - )} - - - ) -} diff --git a/services/frontend/src/components/SortableTable/README.md b/services/frontend/src/components/SortableTable/README.md index b46e5560c2..c930b71399 100644 --- a/services/frontend/src/components/SortableTable/README.md +++ b/services/frontend/src/components/SortableTable/README.md @@ -25,8 +25,6 @@ Properties are optional unless they are in **bold**. | maxHeight | Overwrite the maximum height. Defaults to 80vh if not set. | | useFilteredDataOnExport | If true, uses filtered data (the data that's shown after applying all the selected filters) on exports. Defaults to true. | | handleDisplayedDataChange | A function that is called with the displayed data when it changes. Can be used to update the state in the parent component. | -| pageNumber | The current page number, used for pagination (should be used through the component PaginatedSortableTable). | -| rowsPerPage | The number of rows per page, used for pagination (should be used through the component PaginatedSortableTable). | ### Column/header settings diff --git a/services/frontend/src/components/SortableTable/index.jsx b/services/frontend/src/components/SortableTable/index.jsx index 8d4e3e3904..4a1451c440 100644 --- a/services/frontend/src/components/SortableTable/index.jsx +++ b/services/frontend/src/components/SortableTable/index.jsx @@ -40,8 +40,6 @@ export const SortableTable = ({ hideHeaderBar, maxHeight = '80vh', onlyExportColumns = [], - pageNumber, - rowsPerPage, singleLine = true, stretch, striped = true, @@ -142,14 +140,11 @@ export const SortableTable = ({ } const content = () => { - const indexOfFirstColumn = (pageNumber - 1) * rowsPerPage - const dataBeingDisplayed = - rowsPerPage && pageNumber ? sortedData.slice(indexOfFirstColumn, indexOfFirstColumn + rowsPerPage) : sortedData return ( {headers} - {dataBeingDisplayed.map(item => ( + {sortedData.map(item => ( ))}