Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions services/frontend/src/components/CoursePopulation/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -168,12 +168,12 @@ export const CoursePopulation = () => {
courses={population?.coursestatistics ?? []}
displayTray={!isFetching}
filters={[
genderFilter,
studentNumberFilter,
ageFilter,
genderFilter(),
studentNumberFilter(),
ageFilter(),
courseFilter({ courses: population?.coursestatistics }),
creditsEarnedFilter,
startYearAtUniFilter,
creditsEarnedFilter(),
startYearAtUniFilter(),
programmeFilter({
additionalModes: [
{
Expand Down
17 changes: 9 additions & 8 deletions services/frontend/src/components/CustomPopulation/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,15 @@ export const CustomPopulation = () => {

const filters = useMemo(() => {
const filtersList = [
genderFilter,
ageFilter,
genderFilter(),
ageFilter(),
courseFilter({ courses: population?.coursestatistics ?? [] }),
creditsEarnedFilter,
transferredToProgrammeFilter,
startYearAtUniFilter,
tagsFilter,
programmeFilter,
creditDateFilter,
creditsEarnedFilter(),
transferredToProgrammeFilter(),
startYearAtUniFilter(),
tagsFilter(),
programmeFilter(),
creditDateFilter(),
enrollmentStatusFilter({
allSemesters,
programme: associatedProgramme,
Expand All @@ -82,6 +82,7 @@ export const CustomPopulation = () => {
courses={population?.coursestatistics ?? []}
displayTray={custompop.length > 0}
filters={filters}
initialOptions={[]}
name="CustomPopulation"
students={custompop}
>
Expand Down
24 changes: 12 additions & 12 deletions services/frontend/src/components/FilterView/FilterTray.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@ import type { FilterContext } from './context'
import { FilterCard } from './filters/common/FilterCard'

export type FilterTrayProps = {
options: FilterContext['options']
onOptionsChange: (options) => void
students: Student[]
}
onOptionsChange: (options: FilterContext['options']) => void
} & FilterContext

export const FilterTray = () => {
const {
Expand All @@ -27,24 +26,25 @@ export const FilterTray = () => {
areOptionsDirty,
} = useContext(FilterViewContext)

const haveOptionsBeenChanged = filters.some(({ key }) => areOptionsDirty(key))
const filterOptionsSet = filters.some(({ key }) => areOptionsDirty(key))
const filterSet = filters
.sort(({ title: a }, { title: b }) => a.localeCompare(b))
.map(filter => {
const { key, render } = filter
const { key, isActive, render } = filter
const ctx = getContextByKey(key)

const active = isActive(ctx.options)
const onClear = () => resetFilter(key)

const props: FilterTrayProps = {
options: ctx.options,
onOptionsChange: options => {
setFilterOptions(key, options)
},
students: allStudents.slice(), // Copy instead of move
onOptionsChange: options => setFilterOptions(key, options),
...ctx,
}

return (
<FilterCard filter={filter} key={key} onClear={() => resetFilter(key)} options={ctx.options}>
{render(props, ctx)}
<FilterCard active={active} filter={filter} key={key} onClear={onClear}>
{render(props)}
</FilterCard>
)
})
Expand All @@ -68,7 +68,7 @@ export const FilterTray = () => {
<Typography component="span" fontSize="1.1em">
Showing {filteredStudents.length} out of {allStudents.length} students
</Typography>
{haveOptionsBeenChanged && (
{filterOptionsSet && (
<Button
color="inherit"
data-cy="reset-all-filters"
Expand Down
15 changes: 5 additions & 10 deletions services/frontend/src/components/FilterView/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,11 @@ import type { Student } from '.'
import type { Filter } from './filters/createFilter'

export type FilterContext = {
students: Student[]
precomputed: any // can be null
options: Record<string, any>
args: any // can be null
}

export const getDefaultFilterContext = () => ({ ...defaultFilterContext })
const defaultFilterContext: FilterContext = {
students: [],
precomputed: null,
options: {},
args: null,
}

export type FilterViewContextState = {
viewName: string
allStudents: Student[]
Expand All @@ -36,7 +27,11 @@ const defaultState: FilterViewContextState = {
allStudents: [],
filters: [],
filteredStudents: [],
getContextByKey: () => getDefaultFilterContext(),
getContextByKey: () => ({
precomputed: null,
options: {},
args: undefined,
}),
setFilterOptions: () => {},
resetFilter: () => {},
resetFilters: () => {},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ADMISSION_TYPES } from '@/common'
import { FilterTrayProps } from '../FilterTray'
import { FilterSelect } from './common/FilterSelect'
import { createFilter } from './createFilter'

Expand All @@ -18,7 +19,8 @@ export const filter = code => value => student => {
)
}

const AdmissionTypeFilterCard = ({ options, onOptionsChange, students, code }) => {
const AdmissionTypeFilterCard = ({ args, options, onOptionsChange, students }: FilterTrayProps) => {
const code = args.programme
const { selected } = options
const count = (admissionType: string | null): number => students.filter(filter(code)(admissionType)).length

Expand Down Expand Up @@ -66,5 +68,5 @@ export const admissionTypeFilter = createFilter({
return filter(args.programme)(selected)(student)
},

render: (props, { args }) => <AdmissionTypeFilterCard {...props} code={args.programme} />,
render: AdmissionTypeFilterCard,
})
7 changes: 5 additions & 2 deletions services/frontend/src/components/FilterView/filters/age.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import { useCallback, useMemo } from 'react'

import { useDebounce } from '@/hooks/debounce'
import { getAge } from '@/util/timeAndDate'
import { FilterTrayProps } from '../FilterTray'
import { FilterRange } from './common/FilterRange'
import { createFilter } from './createFilter'

const AgeFilterCard = ({ options, onOptionsChange, bounds }) => {
const AgeFilterCard = ({ options, onOptionsChange, precomputed: bounds }: FilterTrayProps) => {
const { min, max } = bounds

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

title: 'Age',

defaultOptions: {
min: null,
max: null,
Expand All @@ -59,5 +62,5 @@ export const ageFilter = createFilter({
return !(min !== null && min > age) && !(max !== null && max < age)
},

render: (props, { precomputed }) => <AgeFilterCard {...props} bounds={precomputed} />,
render: AgeFilterCard,
})
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ const CitizenshipFilterCard = ({ options, onOptionsChange, students }) => {
export const citizenshipFilter = createFilter({
key: 'Citizenship',

title: 'Citizenship',

defaultOptions: {
selected: '',
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,17 @@ import { FC, useState } from 'react'

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

import { FilterContext, FilterViewContextState } from '../../context'
import { FilterViewContextState } from '../../context'
import type { Filter } from '../createFilter'

export const FilterCard: FC<{
active: boolean
filter: Filter
options: FilterContext['options']
children: ReturnType<Filter['render']>
onClear: () => ReturnType<FilterViewContextState['resetFilter']>
}> = ({ filter, options, children, onClear }) => {
const { info, key, title, isActive } = filter
const active = isActive(options)

}> = ({ active, filter, children, onClear }) => {
const [opened, setOpened] = useState<boolean>(active)
const { info, key, title } = filter

return (
<Stack data-active={active} data-cy={`${key}-filter-card`} spacing={1.2} sx={{ width: '100%' }}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,16 @@
import { produce } from 'immer'
import { keyBy } from 'lodash'
import { FC } from 'react'
import { Dropdown, type DropdownProps } from 'semantic-ui-react'

import { useLanguage } from '@/components/LanguagePicker/useLanguage'
import type { FilterContext } from '../../context'
import type { FilterTrayProps } from '../../FilterTray'
import { createFilter } from '../createFilter'
import { CourseCard } from './CourseCard'
import { FilterType } from './filterType'

type CourseStats = Record<string, any>

const CourseFilterCard: FC<{
courseStats: CourseStats
options: FilterTrayProps['options']
onOptionsChange: FilterContext['precomputed']
}> = ({ courseStats, options, onOptionsChange }) => {
const CourseFilterCard = ({ precomputed, options, onOptionsChange }: FilterTrayProps) => {
const courseStats: CourseStats = precomputed
const { courseFilters } = options ?? {}
const { getTextIn } = useLanguage()

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

const setCourseFilter = (code, type) =>
onOptionsChange(
produce(options, draft => {
draft.courseFilters[code] = type
if (type === null) delete draft.courseFilters[code]
})
(() => {
const newOpts = structuredClone(options)
newOpts.courseFilters[code] = type
if (type === null) delete newOpts.courseFilters[code]

return newOpts
})()
)

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

title: 'Courses',

defaultOptions: {
courseFilters: {},
},
Expand All @@ -100,7 +99,7 @@ export const courseFilter = createFilter({
return true
},

render: (props, { precomputed }) => <CourseFilterCard {...props} courseStats={precomputed} />,
render: CourseFilterCard,

selectors: {
isCourseSelected: ({ courseFilters }, course) => !!courseFilters[course],
Expand All @@ -113,6 +112,8 @@ export const courseFilter = createFilter({
} else {
delete options.courseFilters[code]
}

return options
},
},
})
Expand Down
Loading