Skip to content

Commit 73e69b5

Browse files
authored
Merge pull request #4834 from UniversityOfHelsinkiCS/filtering
Improve filtering
2 parents 613aa14 + 6a4a7b5 commit 73e69b5

31 files changed

+275
-326
lines changed

services/frontend/src/components/CoursePopulation/index.jsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -168,12 +168,12 @@ export const CoursePopulation = () => {
168168
courses={population?.coursestatistics ?? []}
169169
displayTray={!isFetching}
170170
filters={[
171-
genderFilter,
172-
studentNumberFilter,
173-
ageFilter,
171+
genderFilter(),
172+
studentNumberFilter(),
173+
ageFilter(),
174174
courseFilter({ courses: population?.coursestatistics }),
175-
creditsEarnedFilter,
176-
startYearAtUniFilter,
175+
creditsEarnedFilter(),
176+
startYearAtUniFilter(),
177177
programmeFilter({
178178
additionalModes: [
179179
{

services/frontend/src/components/CustomPopulation/index.jsx

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -57,15 +57,15 @@ export const CustomPopulation = () => {
5757

5858
const filters = useMemo(() => {
5959
const filtersList = [
60-
genderFilter,
61-
ageFilter,
60+
genderFilter(),
61+
ageFilter(),
6262
courseFilter({ courses: population?.coursestatistics ?? [] }),
63-
creditsEarnedFilter,
64-
transferredToProgrammeFilter,
65-
startYearAtUniFilter,
66-
tagsFilter,
67-
programmeFilter,
68-
creditDateFilter,
63+
creditsEarnedFilter(),
64+
transferredToProgrammeFilter(),
65+
startYearAtUniFilter(),
66+
tagsFilter(),
67+
programmeFilter(),
68+
creditDateFilter(),
6969
enrollmentStatusFilter({
7070
allSemesters,
7171
programme: associatedProgramme,
@@ -82,6 +82,7 @@ export const CustomPopulation = () => {
8282
courses={population?.coursestatistics ?? []}
8383
displayTray={custompop.length > 0}
8484
filters={filters}
85+
initialOptions={[]}
8586
name="CustomPopulation"
8687
students={custompop}
8788
>

services/frontend/src/components/FilterView/FilterTray.tsx

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,9 @@ import type { FilterContext } from './context'
1010
import { FilterCard } from './filters/common/FilterCard'
1111

1212
export type FilterTrayProps = {
13-
options: FilterContext['options']
14-
onOptionsChange: (options) => void
1513
students: Student[]
16-
}
14+
onOptionsChange: (options: FilterContext['options']) => void
15+
} & FilterContext
1716

1817
export const FilterTray = () => {
1918
const {
@@ -27,24 +26,25 @@ export const FilterTray = () => {
2726
areOptionsDirty,
2827
} = useContext(FilterViewContext)
2928

30-
const haveOptionsBeenChanged = filters.some(({ key }) => areOptionsDirty(key))
29+
const filterOptionsSet = filters.some(({ key }) => areOptionsDirty(key))
3130
const filterSet = filters
3231
.sort(({ title: a }, { title: b }) => a.localeCompare(b))
3332
.map(filter => {
34-
const { key, render } = filter
33+
const { key, isActive, render } = filter
3534
const ctx = getContextByKey(key)
3635

36+
const active = isActive(ctx.options)
37+
const onClear = () => resetFilter(key)
38+
3739
const props: FilterTrayProps = {
38-
options: ctx.options,
39-
onOptionsChange: options => {
40-
setFilterOptions(key, options)
41-
},
4240
students: allStudents.slice(), // Copy instead of move
41+
onOptionsChange: options => setFilterOptions(key, options),
42+
...ctx,
4343
}
4444

4545
return (
46-
<FilterCard filter={filter} key={key} onClear={() => resetFilter(key)} options={ctx.options}>
47-
{render(props, ctx)}
46+
<FilterCard active={active} filter={filter} key={key} onClear={onClear}>
47+
{render(props)}
4848
</FilterCard>
4949
)
5050
})
@@ -68,7 +68,7 @@ export const FilterTray = () => {
6868
<Typography component="span" fontSize="1.1em">
6969
Showing {filteredStudents.length} out of {allStudents.length} students
7070
</Typography>
71-
{haveOptionsBeenChanged && (
71+
{filterOptionsSet && (
7272
<Button
7373
color="inherit"
7474
data-cy="reset-all-filters"

services/frontend/src/components/FilterView/context.ts

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,11 @@ import type { Student } from '.'
44
import type { Filter } from './filters/createFilter'
55

66
export type FilterContext = {
7-
students: Student[]
87
precomputed: any // can be null
98
options: Record<string, any>
109
args: any // can be null
1110
}
1211

13-
export const getDefaultFilterContext = () => ({ ...defaultFilterContext })
14-
const defaultFilterContext: FilterContext = {
15-
students: [],
16-
precomputed: null,
17-
options: {},
18-
args: null,
19-
}
20-
2112
export type FilterViewContextState = {
2213
viewName: string
2314
allStudents: Student[]
@@ -36,7 +27,11 @@ const defaultState: FilterViewContextState = {
3627
allStudents: [],
3728
filters: [],
3829
filteredStudents: [],
39-
getContextByKey: () => getDefaultFilterContext(),
30+
getContextByKey: () => ({
31+
precomputed: null,
32+
options: {},
33+
args: undefined,
34+
}),
4035
setFilterOptions: () => {},
4136
resetFilter: () => {},
4237
resetFilters: () => {},

services/frontend/src/components/FilterView/filters/admissionType.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { ADMISSION_TYPES } from '@/common'
2+
import { FilterTrayProps } from '../FilterTray'
23
import { FilterSelect } from './common/FilterSelect'
34
import { createFilter } from './createFilter'
45

@@ -18,7 +19,8 @@ export const filter = code => value => student => {
1819
)
1920
}
2021

21-
const AdmissionTypeFilterCard = ({ options, onOptionsChange, students, code }) => {
22+
const AdmissionTypeFilterCard = ({ args, options, onOptionsChange, students }: FilterTrayProps) => {
23+
const code = args.programme
2224
const { selected } = options
2325
const count = (admissionType: string | null): number => students.filter(filter(code)(admissionType)).length
2426

@@ -66,5 +68,5 @@ export const admissionTypeFilter = createFilter({
6668
return filter(args.programme)(selected)(student)
6769
},
6870

69-
render: (props, { args }) => <AdmissionTypeFilterCard {...props} code={args.programme} />,
71+
render: AdmissionTypeFilterCard,
7072
})

services/frontend/src/components/FilterView/filters/age.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ import { useCallback, useMemo } from 'react'
22

33
import { useDebounce } from '@/hooks/debounce'
44
import { getAge } from '@/util/timeAndDate'
5+
import { FilterTrayProps } from '../FilterTray'
56
import { FilterRange } from './common/FilterRange'
67
import { createFilter } from './createFilter'
78

8-
const AgeFilterCard = ({ options, onOptionsChange, bounds }) => {
9+
const AgeFilterCard = ({ options, onOptionsChange, precomputed: bounds }: FilterTrayProps) => {
910
const { min, max } = bounds
1011

1112
const onChange = useCallback(
@@ -36,6 +37,8 @@ const AgeFilterCard = ({ options, onOptionsChange, bounds }) => {
3637
export const ageFilter = createFilter({
3738
key: 'Age',
3839

40+
title: 'Age',
41+
3942
defaultOptions: {
4043
min: null,
4144
max: null,
@@ -59,5 +62,5 @@ export const ageFilter = createFilter({
5962
return !(min !== null && min > age) && !(max !== null && max < age)
6063
},
6164

62-
render: (props, { precomputed }) => <AgeFilterCard {...props} bounds={precomputed} />,
65+
render: AgeFilterCard,
6366
})

services/frontend/src/components/FilterView/filters/citizenship.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ const CitizenshipFilterCard = ({ options, onOptionsChange, students }) => {
5353
export const citizenshipFilter = createFilter({
5454
key: 'Citizenship',
5555

56+
title: 'Citizenship',
57+
5658
defaultOptions: {
5759
selected: '',
5860
},

services/frontend/src/components/FilterView/filters/common/FilterCard.tsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,17 @@ import { FC, useState } from 'react'
1010

1111
import { InfoBox } from '@/components/material/InfoBox'
1212

13-
import { FilterContext, FilterViewContextState } from '../../context'
13+
import { FilterViewContextState } from '../../context'
1414
import type { Filter } from '../createFilter'
1515

1616
export const FilterCard: FC<{
17+
active: boolean
1718
filter: Filter
18-
options: FilterContext['options']
1919
children: ReturnType<Filter['render']>
2020
onClear: () => ReturnType<FilterViewContextState['resetFilter']>
21-
}> = ({ filter, options, children, onClear }) => {
22-
const { info, key, title, isActive } = filter
23-
const active = isActive(options)
24-
21+
}> = ({ active, filter, children, onClear }) => {
2522
const [opened, setOpened] = useState<boolean>(active)
23+
const { info, key, title } = filter
2624

2725
return (
2826
<Stack data-active={active} data-cy={`${key}-filter-card`} spacing={1.2} sx={{ width: '100%' }}>

services/frontend/src/components/FilterView/filters/courses/index.tsx

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,16 @@
1-
import { produce } from 'immer'
21
import { keyBy } from 'lodash'
3-
import { FC } from 'react'
42
import { Dropdown, type DropdownProps } from 'semantic-ui-react'
53

64
import { useLanguage } from '@/components/LanguagePicker/useLanguage'
7-
import type { FilterContext } from '../../context'
85
import type { FilterTrayProps } from '../../FilterTray'
96
import { createFilter } from '../createFilter'
107
import { CourseCard } from './CourseCard'
118
import { FilterType } from './filterType'
129

1310
type CourseStats = Record<string, any>
1411

15-
const CourseFilterCard: FC<{
16-
courseStats: CourseStats
17-
options: FilterTrayProps['options']
18-
onOptionsChange: FilterContext['precomputed']
19-
}> = ({ courseStats, options, onOptionsChange }) => {
12+
const CourseFilterCard = ({ precomputed, options, onOptionsChange }: FilterTrayProps) => {
13+
const courseStats: CourseStats = precomputed
2014
const { courseFilters } = options ?? {}
2115
const { getTextIn } = useLanguage()
2216

@@ -32,10 +26,13 @@ const CourseFilterCard: FC<{
3226

3327
const setCourseFilter = (code, type) =>
3428
onOptionsChange(
35-
produce(options, draft => {
36-
draft.courseFilters[code] = type
37-
if (type === null) delete draft.courseFilters[code]
38-
})
29+
(() => {
30+
const newOpts = structuredClone(options)
31+
newOpts.courseFilters[code] = type
32+
if (type === null) delete newOpts.courseFilters[code]
33+
34+
return newOpts
35+
})()
3936
)
4037

4138
const onChange: NonNullable<DropdownProps['onChange']> = (_, { value }) => {
@@ -74,6 +71,8 @@ const CourseFilterCard: FC<{
7471
export const courseFilter = createFilter({
7572
key: 'Courses',
7673

74+
title: 'Courses',
75+
7776
defaultOptions: {
7877
courseFilters: {},
7978
},
@@ -100,7 +99,7 @@ export const courseFilter = createFilter({
10099
return true
101100
},
102101

103-
render: (props, { precomputed }) => <CourseFilterCard {...props} courseStats={precomputed} />,
102+
render: CourseFilterCard,
104103

105104
selectors: {
106105
isCourseSelected: ({ courseFilters }, course) => !!courseFilters[course],
@@ -113,6 +112,8 @@ export const courseFilter = createFilter({
113112
} else {
114113
delete options.courseFilters[code]
115114
}
115+
116+
return options
116117
},
117118
},
118119
})

0 commit comments

Comments
 (0)