mirror of
https://github.com/UniversityOfHelsinkiCS/oodikone.git
synced 2026-10-12 19:03:39 +03:00
[Hooks] Add curriculum context
use to avoid excess prop drilling and desynced state between the view and editor modal
This commit is contained in:
@@ -31,7 +31,7 @@ import { AdvancedSettings } from '@/components/PopulationDetails/AdvancedSetting
|
||||
import { PopulationQueryCard } from '@/components/PopulationDetails/PopulationQueryCard'
|
||||
import { useColumns as columnsGeneralTab } from '@/components/PopulationDetails/studentColumns'
|
||||
import { useDebouncedState } from '@/hooks/debouncedState'
|
||||
import { useCurriculumState } from '@/hooks/useCurriculums'
|
||||
import { CurriculumProvider, useCurriculum } from '@/hooks/useCurriculum'
|
||||
import { useGetAuthorizedUserQuery } from '@/redux/auth'
|
||||
import { KeyboardBackspaceIcon } from '@/theme'
|
||||
import { PopulationQuery } from '@/types/populationSearch'
|
||||
@@ -48,7 +48,13 @@ type PopulationDetailsProps = {
|
||||
idToGroupIdMap: Record<string, string>
|
||||
}
|
||||
|
||||
export const PopulationDetails = ({
|
||||
export const PopulationDetails = (props: PopulationDetailsProps) => (
|
||||
<CurriculumProvider programmeCode={props.query.programme} year={props.query?.years?.[0]}>
|
||||
<PopulationDetailsContent {...props} />
|
||||
</CurriculumProvider>
|
||||
)
|
||||
|
||||
const PopulationDetailsContent = ({
|
||||
isLoading,
|
||||
query,
|
||||
populationTags,
|
||||
@@ -64,7 +70,7 @@ export const PopulationDetails = ({
|
||||
const [showAdvancedSettings, setShowAdvancedSettings] = useState(false)
|
||||
const [courseTableMode, setCourseTableMode] = useState<'curriculum' | 'all'>('curriculum')
|
||||
const [studentAmountLimit, setStudentAmountLimit] = useDebouncedState(0, 1000)
|
||||
const [curriculum, curriculumList, setCurriculum] = useCurriculumState(programme, query?.years?.[0])
|
||||
const { curriculum, curriculumList, setSelectedCurriculum: setCurriculum } = useCurriculum()
|
||||
const [showModules, setShowModules] = useState(false) // Shows courses if modules not selected
|
||||
|
||||
const primaryHopsCreditFilter = useFilterSelector(hopsFilter.selectors.isPrimarySelected(undefined))
|
||||
|
||||
+22
-9
@@ -21,7 +21,7 @@ import { useFormat as formatGeneralTab } from '@/components/PopulationComponents
|
||||
import { useColumns as columnsGeneralTab } from '@/components/StudyGuidanceGroups/studentColumns'
|
||||
import { StudyGuidanceGroupPopulationCourses } from '@/components/StudyGuidanceGroups/StudyGuidanceGroupPopulationCourses'
|
||||
import { createAcademicYearStartDate } from '@/components/StudyGuidanceGroups/utils'
|
||||
import { useCurriculumState } from '@/hooks/useCurriculums'
|
||||
import { CurriculumProvider, useCurriculum } from '@/hooks/useCurriculum'
|
||||
import { KeyboardBackspaceIcon } from '@/theme'
|
||||
import { FilteredCourse } from '@/util/coursesOfPopulation'
|
||||
import { FormattedStudent } from '@oodikone/shared/types'
|
||||
@@ -29,17 +29,30 @@ import { GroupsWithTags } from '@oodikone/shared/types/studyGuidanceGroup'
|
||||
|
||||
dayjsExtend(isBetween)
|
||||
|
||||
export const SingleStudyGuidanceGroupPanels = ({
|
||||
filteredStudents,
|
||||
filteredCourses,
|
||||
group,
|
||||
idToGroupIdMap,
|
||||
}: {
|
||||
type SingleStudyGuidanceGroupPanelsProps = {
|
||||
filteredStudents: FormattedStudent[]
|
||||
filteredCourses: FilteredCourse[]
|
||||
group: GroupsWithTags
|
||||
idToGroupIdMap: Record<string, string>
|
||||
}) => {
|
||||
}
|
||||
|
||||
export const SingleStudyGuidanceGroupPanels = (props: SingleStudyGuidanceGroupPanelsProps) => {
|
||||
const [programme] = props.group.tags?.studyProgramme?.split('+') ?? []
|
||||
const year = props.group.tags?.year ?? undefined
|
||||
|
||||
return (
|
||||
<CurriculumProvider programmeCode={programme} year={year}>
|
||||
<SingleStudyGuidanceGroupPanelsContent {...props} />
|
||||
</CurriculumProvider>
|
||||
)
|
||||
}
|
||||
|
||||
const SingleStudyGuidanceGroupPanelsContent = ({
|
||||
filteredStudents,
|
||||
filteredCourses,
|
||||
group,
|
||||
idToGroupIdMap,
|
||||
}: SingleStudyGuidanceGroupPanelsProps) => {
|
||||
const { useFilterSelector, useFilterDispatch: filterDispatch } = useFilters()
|
||||
|
||||
const groupYear = group.tags?.year
|
||||
@@ -53,7 +66,7 @@ export const SingleStudyGuidanceGroupPanels = ({
|
||||
years: groupYear ? [Number(groupYear)] : [],
|
||||
}
|
||||
|
||||
const [curriculum, curriculumList, setCurriculum] = useCurriculumState(programme, groupYear!) // TODO: fix year
|
||||
const { curriculum, curriculumList, setSelectedCurriculum: setCurriculum } = useCurriculum()
|
||||
|
||||
const creditDateFilterActive = useFilterSelector(creditDateFilter.selectors.isActive(undefined))
|
||||
const studyPlanFilterIsActive = useFilterSelector(studyPlanFilter.selectors.isActive(undefined))
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import { createContext, Dispatch, ReactNode, SetStateAction, useContext, useMemo } from 'react'
|
||||
|
||||
import { ExtendedCurriculumDetails, useCurriculumState } from '@/hooks/useCurriculums'
|
||||
import { CurriculumOption } from '@oodikone/shared/types'
|
||||
|
||||
type CurriculumContextType = {
|
||||
curriculum: ExtendedCurriculumDetails | null
|
||||
curriculumList: CurriculumOption[]
|
||||
setSelectedCurriculum: Dispatch<SetStateAction<CurriculumOption | null>>
|
||||
}
|
||||
|
||||
const CurriculumContext = createContext<CurriculumContextType | null>(null)
|
||||
CurriculumContext.displayName = 'Curriculum'
|
||||
|
||||
export const CurriculumProvider = ({
|
||||
programmeCode,
|
||||
year,
|
||||
children,
|
||||
}: {
|
||||
programmeCode: string
|
||||
year: string | number | undefined
|
||||
children: ReactNode
|
||||
}) => {
|
||||
const [curriculum, curriculumList, setSelectedCurriculum] = useCurriculumState(programmeCode, year)
|
||||
const value = useMemo(
|
||||
() => ({ curriculum, curriculumList, setSelectedCurriculum }),
|
||||
[curriculum, curriculumList, setSelectedCurriculum]
|
||||
)
|
||||
|
||||
return <CurriculumContext.Provider value={value}>{children}</CurriculumContext.Provider>
|
||||
}
|
||||
|
||||
/** Context variant of the curriculum hook */
|
||||
export const useCurriculum = () => {
|
||||
const context = useContext(CurriculumContext)
|
||||
if (!context) {
|
||||
throw new Error('useCurriculum must be used within a CurriculumProvider')
|
||||
}
|
||||
return context
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useState, Dispatch, SetStateAction } from 'react'
|
||||
import { useMemo, useState, Dispatch, SetStateAction } from 'react'
|
||||
|
||||
import { useGetCurriculumsQuery, useGetCurriculumOptionsQuery } from '@/redux/curriculum'
|
||||
import { CurriculumOption, CurriculumDetails } from '@oodikone/shared/types'
|
||||
@@ -7,10 +7,9 @@ export type ExtendedCurriculumDetails = CurriculumDetails & CurriculumOption
|
||||
|
||||
export const useCurriculumState = (
|
||||
programmeCode: string,
|
||||
year: string | number
|
||||
year: string | number | undefined
|
||||
): [ExtendedCurriculumDetails | null, CurriculumOption[], Dispatch<SetStateAction<CurriculumOption | null>>] => {
|
||||
const [selectedCurriculum, setSelectedCurriculum] = useState<CurriculumOption | null>(null)
|
||||
const [curriculum, setCurriculum] = useState<ExtendedCurriculumDetails | null>(null)
|
||||
|
||||
const { data: curriculumList = [], isFetching: curriculumsLoading } = useGetCurriculumOptionsQuery(
|
||||
{ code: programmeCode },
|
||||
@@ -19,7 +18,7 @@ export const useCurriculumState = (
|
||||
|
||||
const chosenCurriculum: CurriculumOption | null =
|
||||
selectedCurriculum ??
|
||||
curriculumList.find(curriculum => new Date(curriculum.validFrom) <= new Date(`${year}-08-01`)) ??
|
||||
(year ? curriculumList.find(curriculum => new Date(curriculum.validFrom) <= new Date(`${year}-08-01`)) : null) ??
|
||||
curriculumList[0] ??
|
||||
null
|
||||
|
||||
@@ -31,11 +30,10 @@ export const useCurriculumState = (
|
||||
{ skip: curriculumsLoading || !chosenCurriculum?.periodIds }
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (curriculumData) {
|
||||
setCurriculum({ ...curriculumData, ...chosenCurriculum })
|
||||
}
|
||||
}, [curriculumData, chosenCurriculum])
|
||||
const curriculum = useMemo<ExtendedCurriculumDetails | null>(
|
||||
() => (curriculumData ? { ...curriculumData, ...chosenCurriculum } : null),
|
||||
[curriculumData, chosenCurriculum]
|
||||
)
|
||||
|
||||
return [curriculum, curriculumList, setSelectedCurriculum]
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import Stack from '@mui/material/Stack'
|
||||
import { orderBy } from 'lodash-es'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
import { useCurriculumState } from '@/hooks/useCurriculums'
|
||||
import { useCurriculum } from '@/hooks/useCurriculum'
|
||||
import { CreditCriteriaSection } from '@/pages/StudyProgramme/DegreeCoursesTab/CreditCriteriaSection'
|
||||
import { CurriculumSection } from '@/pages/StudyProgramme/DegreeCoursesTab/CurriculumSection'
|
||||
import { DegreeCourseTable } from '@/pages/StudyProgramme/DegreeCoursesTab/DegreeCourseTable'
|
||||
@@ -23,9 +23,9 @@ export const DegreeCoursesTab = ({
|
||||
}) => {
|
||||
const [defaultProgrammeModules, setDefaultProgrammeModules] = useState<Module[]>([])
|
||||
const [secondProgrammeModules, setSecondProgrammeModules] = useState<Module[]>([])
|
||||
const [curriculum, curriculumList, setCurriculum] = useCurriculumState(degreeProgramme, new Date().getFullYear())
|
||||
const { data: criteria } = useGetProgressCriteriaQuery({ programmeCode: degreeProgramme })
|
||||
const { getTextIn } = useLanguage()
|
||||
const { curriculum, curriculumList, setSelectedCurriculum: setCurriculum } = useCurriculum()
|
||||
|
||||
const getModules = (courses: ProgrammeCourse[]): Module[] => {
|
||||
const modules: Record<string, ProgrammeCourse[]> = {}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { PageLayout } from '@/components/common/PageLayout'
|
||||
import { PageTitle } from '@/components/common/PageTitle'
|
||||
import { GetTextIn, useLanguage } from '@/components/LanguagePicker/useLanguage'
|
||||
import { useTabs } from '@/hooks/tabs'
|
||||
import { CurriculumProvider } from '@/hooks/useCurriculum'
|
||||
import { useTitle } from '@/hooks/title'
|
||||
import { BasicInformationTab } from '@/pages/StudyProgramme/BasicInformationTab'
|
||||
import { DegreeCoursesTab } from '@/pages/StudyProgramme/DegreeCoursesTab'
|
||||
@@ -136,11 +137,9 @@ export const StudyProgramme = () => {
|
||||
key: 'DegreeCoursesTab',
|
||||
label: 'Degree courses',
|
||||
component: (
|
||||
<DegreeCoursesTab
|
||||
combinedProgramme={secondProgrammeId}
|
||||
degreeProgramme={programmeId}
|
||||
key="DegreeCoursesTabContent"
|
||||
/>
|
||||
<CurriculumProvider key="DegreeCoursesTabContent" programmeCode={programmeId} year={new Date().getFullYear()}>
|
||||
<DegreeCoursesTab combinedProgramme={secondProgrammeId} degreeProgramme={programmeId} />
|
||||
</CurriculumProvider>
|
||||
),
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user