mirror of
https://github.com/UniversityOfHelsinkiCS/oodikone.git
synced 2026-10-12 19:03:39 +03:00
[CourseStat][Search] Use substitution_groups when searching for courses
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import IconButton from '@mui/material/IconButton'
|
||||
import Stack from '@mui/material/Stack'
|
||||
import Table from '@mui/material/Table'
|
||||
import TableBody from '@mui/material/TableBody'
|
||||
import TableCell from '@mui/material/TableCell'
|
||||
@@ -9,6 +10,7 @@ import Typography from '@mui/material/Typography'
|
||||
|
||||
import { useLanguage } from '@/components/LanguagePicker/useLanguage'
|
||||
import { DeleteIcon } from '@/theme'
|
||||
import { SearchResultCourse } from '@/types/api/courses'
|
||||
import { getActiveYears } from '../util'
|
||||
|
||||
export const CourseTable = ({
|
||||
@@ -29,37 +31,55 @@ export const CourseTable = ({
|
||||
|
||||
const getEmptyListRow = () => (
|
||||
<TableRow>
|
||||
<TableCell align="center" colSpan={2}>
|
||||
<TableCell align="center" colSpan={3}>
|
||||
No results.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
|
||||
const toCourseRow = course => (
|
||||
<TableRow
|
||||
data-cy={`course-${course.code}`}
|
||||
hover
|
||||
key={course.id}
|
||||
onClick={() => (course.min_attainment_date ? onSelectCourse(course) : null)}
|
||||
style={{ cursor: course.min_attainment_date ? 'pointer' : 'default' }}
|
||||
>
|
||||
<TableCell>
|
||||
<Typography variant="subtitle1">{getTextIn(course.name)}</Typography>
|
||||
<Typography color="text.secondary" variant="body2">
|
||||
{getActiveYears(course)}
|
||||
const formatSubstitutionGroups = (course: SearchResultCourse): string[] =>
|
||||
course.substitution_groups?.map(sb => (sb.length !== 1 ? `${sb.join(', ')}` : sb[0])) ?? ['???']
|
||||
|
||||
const SubstitutionGroups = (course: SearchResultCourse) => (
|
||||
<Stack>
|
||||
{formatSubstitutionGroups(course).map(sg => (
|
||||
<Typography fontSize="0.9rem" key={sg}>
|
||||
{sg}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell>{combineSubstitutions ? [course.code, ...course.substitutions].join(', ') : course.code}</TableCell>
|
||||
{title === 'Selected courses' && (
|
||||
<TableCell align="right">
|
||||
<IconButton>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
)}
|
||||
</TableRow>
|
||||
))}
|
||||
</Stack>
|
||||
)
|
||||
|
||||
const toCourseRow = (course: SearchResultCourse) => {
|
||||
return (
|
||||
<TableRow
|
||||
data-cy={`course-${course.code}`}
|
||||
hover
|
||||
key={course.id}
|
||||
onClick={() => (course.min_attainment_date ? onSelectCourse(course) : null)}
|
||||
style={{ cursor: course.min_attainment_date ? 'pointer' : 'default' }}
|
||||
>
|
||||
<TableCell>
|
||||
<Typography variant="subtitle1">{getTextIn(course.name)}</Typography>
|
||||
<Typography color="text.secondary" variant="body2">
|
||||
{getActiveYears(course)}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Typography fontSize="0.95rem">{course.code}</Typography>
|
||||
</TableCell>
|
||||
<TableCell>{combineSubstitutions ? SubstitutionGroups(course) : course.code}</TableCell>
|
||||
{title === 'Selected courses' && (
|
||||
<TableCell align="right">
|
||||
<IconButton>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
)}
|
||||
</TableRow>
|
||||
)
|
||||
}
|
||||
|
||||
if (hidden) {
|
||||
return null
|
||||
}
|
||||
@@ -70,7 +90,8 @@ export const CourseTable = ({
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>{title}</TableCell>
|
||||
<TableCell>Code(s)</TableCell>
|
||||
<TableCell>Main course code</TableCell>
|
||||
<TableCell>Equivalent groups</TableCell>
|
||||
{title === 'Selected courses' && <TableCell />}
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
|
||||
@@ -21,6 +21,7 @@ import { useSearchHistory } from '@/hooks/searchHistory'
|
||||
import { useToggle } from '@/hooks/toggle'
|
||||
import { useGetCourseSearchResultQuery } from '@/redux/courseSearch'
|
||||
import { AddIcon, SearchIcon } from '@/theme'
|
||||
import { SearchResultCourse } from '@/types/api/courses'
|
||||
import { SearchHistoryItem } from '@/types/searchHistory'
|
||||
import { queryParamsToString } from '@/util/queryparams'
|
||||
import { CourseTable } from './CourseTable'
|
||||
@@ -37,7 +38,7 @@ export const SearchForm = () => {
|
||||
const [courseCode, setCourseCode] = useState('')
|
||||
const [debouncedCourseName, setDebouncedCourseName] = useDebouncedState('')
|
||||
const [debouncedCourseCode, setDebouncedCourseCode] = useDebouncedState('')
|
||||
const [selectedCourses, setSelectedCourses] = useState({})
|
||||
const [selectedCourses, setSelectedCourses] = useState<Record<string, SearchResultCourse>>({})
|
||||
|
||||
const isInputValid = validateInputLength(courseName, 5) || validateInputLength(courseCode, 2)
|
||||
const isDebouncedInputValid =
|
||||
@@ -65,7 +66,7 @@ export const SearchForm = () => {
|
||||
setDebouncedCourseCode(event.target.value)
|
||||
}
|
||||
|
||||
const onSelectCourse = course => {
|
||||
const onSelectCourse = (course: SearchResultCourse) => {
|
||||
const isSelected = !!selectedCourses[course.code]
|
||||
|
||||
if (isSelected) {
|
||||
@@ -148,7 +149,7 @@ export const SearchForm = () => {
|
||||
const noSelectedCourses = !selected.length
|
||||
|
||||
const addAllCourses = () => {
|
||||
const newSelectedCourses = courses.reduce((newSelected, course) => {
|
||||
const newSelectedCourses = courses.reduce<Record<string, SearchResultCourse>>((newSelected, course) => {
|
||||
newSelected[course.code] = { ...course }
|
||||
return newSelected
|
||||
}, {})
|
||||
|
||||
@@ -1,21 +1,10 @@
|
||||
import { Name } from '@oodikone/shared/types'
|
||||
import { Unarray } from '@oodikone/shared/types'
|
||||
import { CourseWithSubsId } from '@oodikone/shared/types/course'
|
||||
|
||||
export type SearchResultCourse = Unarray<GetCourseSearchResultResponse['courses']>
|
||||
|
||||
export type GetCourseSearchResultResponse = {
|
||||
courses: Array<{
|
||||
code: string
|
||||
course_unit_type: string
|
||||
coursetypecode: string
|
||||
createdAt: string
|
||||
id: string
|
||||
is_study_module: boolean
|
||||
mainCourseCode: string | null
|
||||
max_attainment_date: string
|
||||
min_attainment_date: string
|
||||
name: Name
|
||||
subsId: number
|
||||
substitutions: string[]
|
||||
updatedAt: string
|
||||
}>
|
||||
courses: Array<Omit<CourseWithSubsId, 'courseType' | 'credits' | 'enrollments' | 'organizations'>>
|
||||
}
|
||||
|
||||
export type GetCourseSearchResultRequest = {
|
||||
|
||||
@@ -128,7 +128,7 @@ const updateCourses = async (courseIdToAttainments, groupIdToCourse) => {
|
||||
|
||||
course.substitution_groups = course.substitution_groups
|
||||
.map(subGroup => subGroup.map(id => idToCodeMap.get(id) ?? null))
|
||||
.map(subGroup => subGroup.filter(substitution_groups => !!substitution_groups))
|
||||
.map(subGroup => subGroup.filter(substitution_groups => !!substitution_groups)) // Filter out undefined/null entries
|
||||
|
||||
course.mainCourseCode = [course.code, ...course.substitution_groups]
|
||||
.sort((a, b) => getSubstitutionPriority(b) - getSubstitutionPriority(a))
|
||||
|
||||
Reference in New Issue
Block a user