From bd20c4c417f97cfb9339c8d1ae39a159b3b5fd15 Mon Sep 17 00:00:00 2001 From: belanglos Date: Tue, 21 May 2024 06:39:43 +0200 Subject: [PATCH] Refactor and bug fixes (#339) This commit contains a lot of refactoring and bug fixes KUI-1176 fix: remove fade in/out refactor context, make it read-only and object instead of array containing an object refactor call to ug-rest-api return 400 if given courseCode has wrong length * fix(KUI-1176): remove fades * feat(KUI-1176): remove reordering of courseOfferings * ref(KUI-1176): refactor context * chore(git): add coverage to .gitignore * ref(KUI-1176): consolidate and make use of semesterUtils * ref(KUI-1176): s/keyList/employees and s/lang/language * ref(KUI-1176): restructure webContextUtil * fix(KUI-1176): page renders even if examiners throw * ref(KUI-1176): extract calculation of initiallySelectedSemester * ref(KUI-1176): useApi and useCourseEmployees * ref(KUI-1176): remove addClientFunctionsToWebContext * ref(KUI-1176): make webContext readOnly * ref(KUI-1176): make webContext object instead of array * ref(KUI-1176): change employee endpoint * ref(KUI-1176): s/term/semester in semesterutils * fix(KUI-1176): return 400 if given courseCode has wrong length * fix(KUI-1176) bug in useSemesterRoundState * fix(KUI-1176): cleaning * fix(KUI-1176): remove createServerSideContext * ref(KUI-1176): rename filteredData * ref(KUI-1176): remove unnecessary CSS * ref(KUI-1176): improve naming, remove duplicate code * ref(KUI-1176): rename roundList to roundsBySemester --- .eslintrc | 2 +- .gitignore | 5 +- i18n/messages.en.js | 1 - i18n/messages.se.js | 1 - package.json | 2 +- public/css/kursinfo-web.scss | 24 - .../addClientFunctionsToWebContext.jsx | 31 - .../addClientFunctionsToWebContext.test.js | 12 - .../js/app/components/CourseSectionList.jsx | 17 +- public/js/app/components/DropdownRounds.jsx | 101 +-- .../js/app/components/DropdownSemesters.jsx | 84 +-- .../app/components/RoundApplicationInfo.jsx | 17 +- .../app/components/RoundInformationOneCol.jsx | 96 ++- .../js/app/components/SyllabusInformation.jsx | 9 +- .../__tests__/CourseFileLinks.test.js | 8 +- .../__tests__/CourseSectionList.test.js | 27 +- .../components/__tests__/CourseTitle.test.js | 4 +- .../__tests__/RoundInformationOneCol.test.js | 85 ++- .../components/statistics/CheckboxOption.jsx | 2 +- .../components/statistics/DropdownOption.jsx | 2 +- .../components/statistics/MemosSummary.jsx | 2 +- .../components/statistics/RadioboxOption.jsx | 2 +- .../statistics/StatisticsDataTable.jsx | 2 +- .../statistics/TableSummaryRows.jsx | 2 +- public/js/app/context/WebContext.jsx | 5 +- public/js/app/hooks/__tests__/useApi.test.js | 219 ++++++ .../hooks/__tests__/usePlannedModules.test.js | 123 +--- .../__tests__/useSemesterRoundState.test.js | 441 ++++++++++++ public/js/app/hooks/api/getCourseEmployees.js | 45 ++ public/js/app/hooks/api/getPlannedModules.js | 18 +- public/js/app/hooks/api/status.js | 4 + .../app/hooks/getValidSyllabusForSemester.js | 14 + public/js/app/hooks/statisticsUseAsync.js | 3 +- public/js/app/hooks/useApi.js | 32 + public/js/app/hooks/useCourseEmployees.js | 31 + public/js/app/hooks/useLanguage.js | 2 +- public/js/app/hooks/usePlannedModules.js | 44 +- public/js/app/hooks/useRoundUtils.js | 2 +- public/js/app/hooks/useSemesterRoundState.js | 107 +++ public/js/app/pages/CoursePage.jsx | 215 +++--- .../js/app/pages/__tests__/CoursePage.test.js | 668 ++++++++---------- server/apiCalls/getFilteredData.js | 288 ++++++++ server/apiCalls/timeTable/timeTableApi.js | 2 +- .../__tests__/timeTableDateUtils.test.js} | 4 +- .../apiCalls/timeTable/utils/findOffering.js | 2 +- .../timeTable/utils/timeTableDateUtils.js | 54 ++ server/apiCalls/ugRestApi.js | 5 +- .../controllers/__tests__/courseCtrl.test.js | 92 +-- .../__tests__/courseCtrlHelpers.test.js | 182 +++++ .../__tests__/createSyllabusList.test.js | 253 +++++++ server/controllers/courseCtrl.js | 633 ++--------------- server/controllers/courseCtrlHelpers.js | 72 ++ server/controllers/createSyllabusList.js | 131 ++++ server/controllers/employeesCtrl.js | 13 + server/controllers/index.js | 2 + server/controllers/koppsCourseCtrl.js | 28 + server/server.js | 8 +- server/ssr-context/createServerSideContext.js | 51 -- server/util/__tests__/semesterUtils.test.js | 69 ++ server/util/constants.js | 4 - server/util/semesterUtils.js | 80 +++ server/util/webContextUtil.js | 39 + server/utils/semesterUtils.js | 98 --- 63 files changed, 2876 insertions(+), 1745 deletions(-) delete mode 100644 public/js/app/client-context/addClientFunctionsToWebContext.jsx delete mode 100644 public/js/app/client-context/addClientFunctionsToWebContext.test.js create mode 100644 public/js/app/hooks/__tests__/useApi.test.js create mode 100644 public/js/app/hooks/__tests__/useSemesterRoundState.test.js create mode 100644 public/js/app/hooks/api/getCourseEmployees.js create mode 100644 public/js/app/hooks/api/status.js create mode 100644 public/js/app/hooks/getValidSyllabusForSemester.js create mode 100644 public/js/app/hooks/useApi.js create mode 100644 public/js/app/hooks/useCourseEmployees.js create mode 100644 public/js/app/hooks/useSemesterRoundState.js create mode 100644 server/apiCalls/getFilteredData.js rename server/{utils/__tests__/semesterUtils.test.js => apiCalls/timeTable/utils/__tests__/timeTableDateUtils.test.js} (96%) create mode 100644 server/apiCalls/timeTable/utils/timeTableDateUtils.js create mode 100644 server/controllers/__tests__/courseCtrlHelpers.test.js create mode 100644 server/controllers/__tests__/createSyllabusList.test.js create mode 100644 server/controllers/courseCtrlHelpers.js create mode 100644 server/controllers/createSyllabusList.js create mode 100644 server/controllers/employeesCtrl.js create mode 100644 server/controllers/koppsCourseCtrl.js delete mode 100644 server/ssr-context/createServerSideContext.js create mode 100644 server/util/__tests__/semesterUtils.test.js create mode 100644 server/util/semesterUtils.js create mode 100644 server/util/webContextUtil.js delete mode 100644 server/utils/semesterUtils.js diff --git a/.eslintrc b/.eslintrc index 7f627918..520ea965 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,4 +1,4 @@ { "root": true, - "extends": ["@kth/eslint-config-kth"], + "extends": ["plugin:react-hooks/recommended", "@kth/eslint-config-kth"], } diff --git a/.gitignore b/.gitignore index 6b4dcd7f..8947aa9e 100644 --- a/.gitignore +++ b/.gitignore @@ -48,4 +48,7 @@ yarn.lock notes* # Files downloaded during unit test -course-information-statistics-* \ No newline at end of file +course-information-statistics-* + +# Jest coverage +coverage \ No newline at end of file diff --git a/i18n/messages.en.js b/i18n/messages.en.js index afddba4b..1f932e61 100644 --- a/i18n/messages.en.js +++ b/i18n/messages.en.js @@ -144,7 +144,6 @@ module.exports = { course_language: 'Language of instruction', course_required_equipment: 'Equipment', course_level_code: 'Education cycle', - course_establishment: 'Fastställande', course_decision_to_discontinue: 'Avvecklingsbeslut', course_transitional_reg: 'Transitional regulations', course_ethical: 'Ethical approach', diff --git a/i18n/messages.se.js b/i18n/messages.se.js index 5d7954d5..1583004c 100644 --- a/i18n/messages.se.js +++ b/i18n/messages.se.js @@ -146,7 +146,6 @@ module.exports = { course_language: 'Undervisningsspråk', course_required_equipment: 'Utrustning', course_level_code: 'Utbildningsnivå', - course_establishment: 'Fastställande', course_decision_to_discontinue: 'Avvecklingsbeslut', course_transitional_reg: 'Övergångsbestämmelser', course_ethical: 'Etiskt förhållningssätt', diff --git a/package.json b/package.json index 9186725e..64975ca8 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "test:file:watch": "jest ./server/apiCalls/transformers/offerings-over-several-semesters.test.js --watch", "test:notify": "jest --watch --notify", "docker": "npm install --development && npm run build && npm prune --production", - "lint": "eslint public server && prettier-eslint --list-different **/*.js", + "lint": "eslint \"{public,server}/**/*.{js,jsx}\"", "prepare": "husky", "start": "bash -c 'cat /KTH_NODEJS; NODE_ENV=production node app.js'", "start-dev": "bash -c 'NODE_ENV=development concurrently --kill-others -n build,app \"npm run build-dev\" \"nodemon app.js\"'" diff --git a/public/css/kursinfo-web.scss b/public/css/kursinfo-web.scss index c36442c9..a045befb 100644 --- a/public/css/kursinfo-web.scss +++ b/public/css/kursinfo-web.scss @@ -134,30 +134,6 @@ sup { } } -/************************************************/ - -/* Animation */ -#roundInformationOneCol, -#roundKeyInformation, -#coreContent { - @keyframes fadeOutIn { - 0% { - opacity: 1; - } - 50% { - opacity: 0; - } - 100% { - opacity: 1; - } - } - - .fadeOutIn { - animation-name: fadeOutIn; - animation-duration: 800ms; - } -} - ///******************** MODAL ****************************/// .modal { .modal-title { diff --git a/public/js/app/client-context/addClientFunctionsToWebContext.jsx b/public/js/app/client-context/addClientFunctionsToWebContext.jsx deleted file mode 100644 index 00a847f2..00000000 --- a/public/js/app/client-context/addClientFunctionsToWebContext.jsx +++ /dev/null @@ -1,31 +0,0 @@ -'use strict' - -import axios from 'axios' - -function getCourseEmployees() { - const ladokRound = this.courseData.roundList[this.activeSemester][this.activeRoundIndex] - const { round_application_code: applicationCode } = ladokRound - const data = { - courseCode: this.courseCode, - semester: this.activeSemester, - applicationCodes: [applicationCode], - } - return axios.post(this.paths.ug.rest.api.uri, data).then(response => { - const { examiners, responsibles, teachers } = response.data - const courseRoundEmployees = { - examiners, - responsibles, - teachers, - } - return courseRoundEmployees - }) -} - -function addClientFunctionsToWebContext() { - const functions = { - getCourseEmployees, - } - return functions -} - -export { addClientFunctionsToWebContext } diff --git a/public/js/app/client-context/addClientFunctionsToWebContext.test.js b/public/js/app/client-context/addClientFunctionsToWebContext.test.js deleted file mode 100644 index ac9aec98..00000000 --- a/public/js/app/client-context/addClientFunctionsToWebContext.test.js +++ /dev/null @@ -1,12 +0,0 @@ -import { addClientFunctionsToWebContext } from './addClientFunctionsToWebContext' - -describe('Client functions for webContext addClientFunctionsToWebContext', () => { - test('create a new RouterStore', () => { - const clientSideContext = addClientFunctionsToWebContext() - expect(clientSideContext).toMatchInlineSnapshot(` - { - "getCourseEmployees": [Function], - } - `) - }) -}) diff --git a/public/js/app/components/CourseSectionList.jsx b/public/js/app/components/CourseSectionList.jsx index 590ebc52..b1f012a6 100644 --- a/public/js/app/components/CourseSectionList.jsx +++ b/public/js/app/components/CourseSectionList.jsx @@ -7,14 +7,8 @@ import { useMissingInfo } from '../hooks/useMissingInfo' import CourseSection from './CourseSections' import SyllabusInformation from './SyllabusInformation' -function CourseSectionList({ - courseInfo = {}, - partToShow, - syllabusList: syllabus = {}, - syllabusName, - syllabusSemesterList, -}) { - const [context] = useWebContext() +function CourseSectionList({ courseInfo = {}, partToShow, syllabus = {}, syllabusName, hasSyllabus }) { + const context = useWebContext() const { translation } = useLanguage() const { isMissingInfoLabel, missingInfoLabel } = useMissingInfo() @@ -193,12 +187,7 @@ function CourseSectionList({ id={partToShow} aria-label={`${translation.courseLabels.label_course_information} ${syllabusName}`} > - + { - const [context, setWebContext] = useWebContext() - const { roundDisabled } = context - const [roundSelectedIndex, setRoundSelectIndex] = React.useState(0) +const DROPDOWN_ID = 'roundsDropdown' +const EMPTY_OPTION = -1 +const RoundOptions = ({ roundsForSelectedSemester }) => { const { createRoundLabel } = useRoundUtils() - const dropdownID = 'roundsDropdown' + return roundsForSelectedSemester.map((round, roundIndex) => { + const optionLabel = createRoundLabel(round) - async function handleDropdownSelect(e) { - e.preventDefault() + const uniqueKey = `${optionLabel}${round.round_application_code}${round.round_start_date}` - const eTarget = e.target - const selectedOption = eTarget[eTarget.selectedIndex] + return ( + + ) + }) +} - const selectInfo = selectedOption.id.split('_') +const DropdownRounds = ({ roundsForSelectedSemester, semesterRoundState }) => { + const { setSelectedRoundIndex, resetSelectedRoundIndex } = semesterRoundState - const newContext = { - activeRoundIndex: eTarget.selectedIndex === 0 ? 0 : selectInfo[1], - showRoundData: eTarget.selectedIndex !== 0, - roundSelectedIndex: eTarget.selectedIndex, - } - setWebContext({ ...context, ...newContext }) + const { translation } = useLanguage() + const label = translation.courseLabels.label_round_select - setRoundSelectIndex(eTarget.selectedIndex) - } + const [selectedOptionIndex, setSelectedOptionIndex] = React.useState(EMPTY_OPTION) + + useEffect(() => { + setSelectedOptionIndex(EMPTY_OPTION) + }, [roundsForSelectedSemester]) + + const handleDropdownSelect = React.useCallback( + ({ target }) => { + const { value } = target + + const selectedOption = parseInt(value) + + const isEmptyOption = selectedOption === EMPTY_OPTION - if (courseRoundList && courseRoundList.length < 2) { - return '' + const newActiveRoundIndex = isEmptyOption ? 0 : selectedOption + + if (isEmptyOption) { + resetSelectedRoundIndex() + } else { + setSelectedRoundIndex(newActiveRoundIndex) + } + + setSelectedOptionIndex(selectedOption) + }, + [setSelectedRoundIndex, resetSelectedRoundIndex, setSelectedOptionIndex] + ) + + if (!roundsForSelectedSemester || roundsForSelectedSemester.length < 2) { + return null } + return (
-