[Study programme] Extract util isBachelorOrLicentiateProgramme

This commit is contained in:
Riku Rauhala
2025-03-17 10:26:26 +02:00
parent 0fe018bb96
commit d1d4e3105e
6 changed files with 119 additions and 146 deletions
@@ -4,15 +4,18 @@ import { Button, Dropdown, Icon, Label, Table } from 'semantic-ui-react'
import { useLanguage } from '@/components/LanguagePicker/useLanguage'
import { useRemoveCourseExclusionMutation, useSetCourseExclusionMutation } from '@/redux/courseExclusions'
import { useAddProgressCriteriaCourseMutation } from '@/redux/progressCriteria'
import { Module } from '@/shared/types'
import { Module, ProgressCriteria } from '@/shared/types'
import { isBachelorOrLicentiateProgramme } from '@/util/studyProgramme'
const getYear = criterionYear => {
if (criterionYear === 1) return 'yearOne'
if (criterionYear === 2) return 'yearTwo'
if (criterionYear === 3) return 'yearThree'
if (criterionYear === 4) return 'yearFour'
if (criterionYear === 5) return 'yearFive'
return 'yearSix'
const getYear = (criterionYear: number) => {
return {
1: 'yearOne',
2: 'yearTwo',
3: 'yearThree',
4: 'yearFour',
5: 'yearFive',
6: 'yearSix',
}[criterionYear]
}
const moduleVisibilityColor = visibility => {
@@ -29,13 +32,13 @@ const moduleVisibilityColor = visibility => {
export const DegreeCourseTable = ({
combinedProgramme,
criteria,
curriculum,
curriculumVersion,
modules: initialModules,
studyProgramme,
}: {
combinedProgramme: string
criteria
curriculum
criteria: ProgressCriteria
curriculumVersion: string[]
modules: Module[]
studyProgramme: string
}) => {
@@ -46,14 +49,14 @@ export const DegreeCourseTable = ({
const [removeExclusion] = useRemoveCourseExclusionMutation()
const [addProgressCriteriaCourse] = useAddProgressCriteriaCourseMutation()
const version = curriculum.version?.join(',')
const version = curriculumVersion.join(',')
useEffect(() => {
setModules(initialModules)
}, [initialModules])
const setModuleVisibility = (code, newVisibility) => {
// Same course can be in many modules, so change them all
const courseCodes = modules.find(({ module }) => module === code).courses.map(course => course.code)
const courseCodes = modules.find(module => module.moduleName === code).courses.map(course => course.code)
setModules(
modules.map(module => ({
...module,
@@ -94,7 +97,7 @@ export const DegreeCourseTable = ({
}
const excludeAll = code => {
const module = modules.find(({ module }) => module === code)
const module = modules.find(module => module.moduleName === code)
const excludeFromProgramme = combinedProgramme === '' ? studyProgramme : combinedProgramme
setExclusion({
programmeCode: excludeFromProgramme,
@@ -105,7 +108,7 @@ export const DegreeCourseTable = ({
}
const deleteAll = code => {
const module = modules.find(({ module }) => module === code)
const module = modules.find(module => module.moduleName === code)
removeExclusion({
programmeCode: studyProgramme,
courseCodes: module.courses.map(course => course.code),
@@ -132,7 +135,7 @@ export const DegreeCourseTable = ({
}
const calculateModuleVisibility = code => {
const module = modules.find(({ module }) => module === code)
const module = modules.find(module => module.moduleName === code)
if (!module.courses) {
return 'no courses'
}
@@ -185,11 +188,11 @@ export const DegreeCourseTable = ({
onClick={() => {
const year = getYear(criterionYear)
const courses = criteria.courses ? [...criteria.courses[year], course.code] : [course.code]
addProgressCriteriaCourse({
void addProgressCriteriaCourse({
programmeCode: studyProgramme,
courses,
year: criterionYear,
version: curriculum.version,
version: curriculumVersion,
})
}}
>
@@ -202,7 +205,7 @@ export const DegreeCourseTable = ({
onClick={() => {
const year = getYear(criterionYear)
const filteredCourses = criteria?.courses[year]?.filter(courseCode => courseCode !== course.code)
addProgressCriteriaCourse({ programmeCode: studyProgramme, courses: filteredCourses, year: criterionYear })
void addProgressCriteriaCourse({ programmeCode: studyProgramme, courses: filteredCourses, year: criterionYear })
}}
>
{`Remove criterion for year ${criterionYear}`}
@@ -271,9 +274,7 @@ export const DegreeCourseTable = ({
<Table.HeaderCell>Name</Table.HeaderCell>
<Table.HeaderCell>Code</Table.HeaderCell>
<Table.HeaderCell>Visibility label</Table.HeaderCell>
{(studyProgramme.includes('KH') || ['MH30_001', 'MH30_003'].includes(studyProgramme)) && (
<Table.HeaderCell>Criterion labels</Table.HeaderCell>
)}
{isBachelorOrLicentiateProgramme(studyProgramme) && <Table.HeaderCell>Criterion labels</Table.HeaderCell>}
<Table.HeaderCell>
{studyProgramme.includes('KH') || ['MH30_001', 'MH30_003'].includes(studyProgramme)
? 'Set labels'
@@ -282,30 +283,32 @@ export const DegreeCourseTable = ({
</Table.Row>
</Table.Header>
<Table.Body>
{modules.map(({ module, courses }) => (
<Fragment key={`fragment-${module}`}>
<Table.Row key={module}>
<Table.Cell onClick={() => toggleVisible(module)} style={{ cursor: 'pointer' }}>
<Icon name={isVisible(module) ? 'angle down' : 'angle right'} />
<b>{courses[0] && courses[0].parent_name ? getTextIn(courses[0].parent_name) : module}</b>
{modules.map(({ moduleName, courses }) => (
<Fragment key={`fragment-${moduleName}`}>
<Table.Row key={moduleName}>
<Table.Cell onClick={() => toggleVisible(moduleName)} style={{ cursor: 'pointer' }}>
<Icon name={isVisible(moduleName) ? 'angle down' : 'angle right'} />
<b>{courses[0] && courses[0].parent_name ? getTextIn(courses[0].parent_name) : moduleName}</b>
</Table.Cell>
<Table.Cell>{module}</Table.Cell>
<Table.Cell>{moduleName}</Table.Cell>
<Table.Cell>
<Label
color={moduleVisibilityColor(calculateModuleVisibility(module))}
content={calculateModuleVisibility(module)}
color={moduleVisibilityColor(calculateModuleVisibility(moduleName))}
content={calculateModuleVisibility(moduleName)}
/>
</Table.Cell>
{(studyProgramme.includes('KH') || ['MH30_001', 'MH30_003'].includes(studyProgramme)) && <Table.Cell />}
{isBachelorOrLicentiateProgramme(studyProgramme) && <Table.Cell />}
<Table.Cell>
{calculateModuleVisibility(module) === 'hidden' ? showAllButton(module) : hideAllButton(module)}
{calculateModuleVisibility(moduleName) === 'hidden'
? showAllButton(moduleName)
: hideAllButton(moduleName)}
</Table.Cell>
</Table.Row>
{isVisible(module) &&
{isVisible(moduleName) &&
courses
.sort((a, b) => a.code.localeCompare(b.code))
.map(course => (
<Table.Row key={`${module}/${course.code}`}>
<Table.Row key={`${moduleName}/${course.code}`}>
<Table.Cell>{getTextIn(course.name)}</Table.Cell>
<Table.Cell>{course.code}</Table.Cell>
<Table.Cell>
@@ -314,7 +317,7 @@ export const DegreeCourseTable = ({
content={course.visible.visibility ? 'visible' : 'hidden'}
/>
</Table.Cell>
{(studyProgramme.includes('KH') || ['MH30_001', 'MH30_003'].includes(studyProgramme)) && (
{isBachelorOrLicentiateProgramme(studyProgramme) && (
<Table.Cell>
{criteria?.courses?.yearOne?.includes(course.code) && <Label color="blue" content="year 1" />}
{criteria?.courses?.yearTwo?.includes(course.code) && <Label color="blue" content="year 2" />}
@@ -4,6 +4,7 @@ import { useEffect, useState } from 'react'
import { useGetProgressCriteriaQuery } from '@/redux/progressCriteria'
import { CurriculumDetails, Module, ProgrammeCourse, ProgressCriteria } from '@/shared/types'
import { isBachelorOrLicentiateProgramme } from '@/util/studyProgramme'
import { CreditCriteriaSection } from './CreditCriteriaSection'
import { CurriculumSection } from './CurriculumSection'
import { DegreeCourseTable } from './DegreeCourseTable'
@@ -28,49 +29,35 @@ export const DegreeCoursesTab = ({
}
const criteria = progressCriteria ?? emptyCriteria
const getModules = (courses: ProgrammeCourse[]): Module[] => {
const modules: Record<string, ProgrammeCourse[]> = {}
courses.forEach(course => {
const code = course.parent_code!
if (!modules[code]) {
modules[code] = []
}
modules[code].push(course)
})
return orderBy(
Object.entries(modules).map(([moduleName, courses]) => ({
courses,
moduleName,
moduleOrder: courses[0].module_order,
})),
module => module.moduleName
)
}
useEffect(() => {
if (!curriculum?.defaultProgrammeCourses?.length) {
return
}
const defaultModules: Record<string, ProgrammeCourse[]> = {}
const secondProgrammeModules: Record<string, ProgrammeCourse[]> = {}
curriculum.defaultProgrammeCourses.forEach(course => {
const code = course.parent_code
if (!defaultModules[code]) {
defaultModules[code] = []
}
defaultModules[code].push(course)
})
if (combinedProgramme) {
curriculum.secondProgrammeCourses.forEach(course => {
const code = course.parent_code
if (!secondProgrammeModules[code]) {
secondProgrammeModules[code] = []
}
secondProgrammeModules[code].push(course)
})
}
setDefaultProgrammeModules(
orderBy(
Object.entries(defaultModules).map(([module, courses]) => ({
module,
courses,
module_order: courses[0].module_order,
})),
module => module.module
)
)
setSecondProgrammeModules(
orderBy(
Object.entries(secondProgrammeModules).map(([module, courses]) => ({
module,
courses,
module_order: courses[0].module_order,
})),
module => module.module
)
)
setDefaultProgrammeModules(getModules(curriculum.defaultProgrammeCourses))
if (combinedProgramme) {
setSecondProgrammeModules(getModules(curriculum.secondProgrammeCourses))
}
}, [combinedProgramme, curriculum])
return (
@@ -80,23 +67,23 @@ export const DegreeCoursesTab = ({
setCurriculum={setCurriculum}
year={year}
/>
{(studyProgramme.includes('KH') || ['MH30_001', 'MH30_003'].includes(studyProgramme)) && (
{isBachelorOrLicentiateProgramme(studyProgramme) && (
<CreditCriteriaSection criteria={criteria} studyProgramme={studyProgramme} />
)}
{defaultProgrammeModules && defaultProgrammeModules.length > 1 && (
{defaultProgrammeModules.length > 1 && curriculum && (
<DegreeCourseTable
combinedProgramme=""
criteria={criteria}
curriculum={curriculum}
curriculumVersion={curriculum.version}
modules={defaultProgrammeModules}
studyProgramme={studyProgramme}
/>
)}
{secondProgrammeModules.length > 0 && (
{secondProgrammeModules.length > 0 && curriculum && (
<DegreeCourseTable
combinedProgramme={combinedProgramme}
criteria={criteria}
curriculum={curriculum}
curriculumVersion={curriculum.version}
modules={secondProgrammeModules}
studyProgramme={studyProgramme}
/>
@@ -9,3 +9,7 @@ export const getGraduationGraphTitle = (studyProgramme: string, doCombo = false)
if (studyProgramme.includes('MH')) return 'Master study right'
return 'Doctoral study right'
}
export const isBachelorOrLicentiateProgramme = (studyProgramme: string) => {
return studyProgramme.includes('KH') || ['MH30_001', 'MH30_003'].includes(studyProgramme)
}
+1 -59
View File
@@ -1,4 +1,5 @@
import { Name } from './name'
import { ProgrammeCourse, ProgrammeModule } from './studyProgramme'
export type CurriculumOption = {
id: string
@@ -7,65 +8,6 @@ export type CurriculumOption = {
validFrom: Date
}
export type Module = {
courses: ProgrammeCourse[]
module: string // ? Should this be called code or moduleCode?
module_order: number
}
// TODO: Format for frontend and drop irrelevant fields
export type ProgrammeCourse = {
code: string
created_at: string
curriculum_period_ids: string[]
degree_programme_type: string | null
group_id: string
id: string
label: {
id: string
label: string
orderNumber: number
}
minimum_credits: number | null
module_order: number
name: Name
order: number
organization_id: string | null
parent_code: string
parent_id: string | null
study_level: string | null
type: string
updated_at: string
valid_from: string
valid_to: string | null
visible: {
id: string | null
visible: boolean
}
}
// TODO: Format for frontend and drop irrelevant fields
export type ProgrammeModule = {
code: string
created_at: string
curriculum_period_ids: string[]
degree_programme_type: string | null
group_id: string
id: string
minimum_credits: number | null
module_order: number
name: Name
order: number
organization_id: string | null
parent_code: string | null
parent_id: string | null
study_level: string | null
type: string
updated_at: string
valid_from: string
valid_to: string | null
}
export type CurriculumDetails = {
defaultProgrammeCourses: ProgrammeCourse[]
defaultProgrammeModules: ProgrammeModule[]
+4 -8
View File
@@ -1,11 +1,4 @@
export type {
CurriculumDetails,
CurriculumOption,
CurriculumPeriod,
Module,
ProgrammeCourse,
ProgrammeModule,
} from './curriculum'
export type { CurriculumDetails, CurriculumOption, CurriculumPeriod } from './curriculum'
export type { DetailedProgrammeRights } from './detailedProgrammeRights'
export type { Graduated, SpecialGroups, ProgrammeFilter, StatsType, YearType } from './faculty'
export type {
@@ -22,6 +15,9 @@ export type { Role } from './role'
export type {
GraduationTimes,
MedianEntry,
Module,
ProgrammeCourse,
ProgrammeModule,
ProgrammeOrStudyTrackGraduationStats,
StudyTrackStats,
} from './studyProgramme'
+41
View File
@@ -43,3 +43,44 @@ export type StudyTrackStats = {
studyTracks: Record<string, string | Name>
years: string[]
}
// TODO: Format for frontend and drop irrelevant fields
export type ProgrammeModule = {
code: string
created_at: string
curriculum_period_ids: string[]
degree_programme_type: string | null
group_id: string
id: string
minimum_credits: number | null
module_order: number
name: Name
order: number
organization_id: string | null
parent_code: string | null
parent_id: string | null
study_level: string | null
type: string
updated_at: string
valid_from: string
valid_to: string | null
}
// TODO: Format for frontend and drop irrelevant fields
export type ProgrammeCourse = ProgrammeModule & {
label: {
id: string
label: string
orderNumber: number
}
visibility: {
id: string | null
visible: boolean
}
}
export type Module = {
courses: ProgrammeCourse[]
moduleName: string
moduleOrder: number
}