Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Redesign of Student Application Page and Application Filtering #695

Merged
merged 8 commits into from
Oct 2, 2024
205 changes: 112 additions & 93 deletions client/src/management/ApplicationsOverview/ApplicationOverview.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
import { Group, TextInput, Checkbox, Stack, Button, Menu, Tooltip } from '@mantine/core'
import { IconAdjustments, IconSearch } from '@tabler/icons-react'
import { Group, TextInput, Stack, Button, Menu, Tooltip, Tabs, Flex } from '@mantine/core'
import { IconPlus, IconSearch, IconUpload } from '@tabler/icons-react'
import { useState, useMemo, useEffect } from 'react'
import { TechnicalChallengeAssessmentModal } from './components/TechnicalChallengeAssessmentModal'
import { ApplicationDatatable } from './components/ApplicationDatatable'
import { MatchingResultsUploadModal } from './components/MatchingResultsUploadModal'
import { useApplicationStore } from '../../state/zustand/useApplicationStore'
import { useQuery } from '@tanstack/react-query'
import { Application, ApplicationType } from '../../interface/application'
import { Application, ApplicationType, Gender } from '../../interface/application'
import { Query } from '../../state/query'
import { getApplications } from '../../network/application'
import { useCourseIterationStore } from '../../state/zustand/useCourseIterationStore'
import { FilterChips } from './components/FilterChips'
import { FilterMenu } from './components/FilterMenu'

export interface Filters {
male: boolean
female: boolean
gender: Gender[]
status: string[]
applicationType: ApplicationType[]
assessment: {
noScore: boolean
minScore: number
maxScore: number
}
applicationType: ApplicationType
}

export const StudentApplicationOverview = (): JSX.Element => {
Expand All @@ -33,10 +39,14 @@ export const StudentApplicationOverview = (): JSX.Element => {
const [matchingResultsUploadModalOpened, setMatchingResultsUploadModalOpened] = useState(false)
const [searchQuery, setSearchQuery] = useState('')
const [filters, setFilters] = useState<Filters>({
male: false,
female: false,
gender: [],
status: [],
applicationType: [ApplicationType.DEVELOPER],
assessment: {
noScore: false,
minScore: 0,
maxScore: 100,
},
applicationType: ApplicationType.DEVELOPER,
})

const { data: fetchedDeveloperApplications, isLoading: isLoadingDeveloperApplications } =
Expand Down Expand Up @@ -93,19 +103,19 @@ export const StudentApplicationOverview = (): JSX.Element => {

const tumIdToApplicationMap = useMemo(() => {
const map = new Map<string, string>()
if (filters.applicationType.includes(ApplicationType.DEVELOPER)) {
if (filters.applicationType === ApplicationType.DEVELOPER) {
developerApplications.forEach((application) => {
if (application.student.tumId) {
map.set(application.student.tumId, application.id)
}
})
} else if (filters.applicationType.includes(ApplicationType.COACH)) {
} else if (filters.applicationType === ApplicationType.COACH) {
coachApplications.forEach((application) => {
if (application.student.tumId) {
map.set(application.student.tumId, application.id)
}
})
} else if (filters.applicationType.includes(ApplicationType.TUTOR)) {
} else if (filters.applicationType === ApplicationType.TUTOR) {
tutorApplications.forEach((application) => {
if (application.student.tumId) {
map.set(application.student.tumId, application.id)
Expand All @@ -117,19 +127,19 @@ export const StudentApplicationOverview = (): JSX.Element => {

const matriculationNumberToApplicationMap = useMemo(() => {
const map = new Map<string, string>()
if (filters.applicationType.includes(ApplicationType.DEVELOPER)) {
if (filters.applicationType === ApplicationType.DEVELOPER) {
developerApplications.forEach((application) => {
if (application.student.matriculationNumber) {
map.set(application.student.matriculationNumber, application.id)
}
})
} else if (filters.applicationType.includes(ApplicationType.COACH)) {
} else if (filters.applicationType === ApplicationType.COACH) {
coachApplications.forEach((application) => {
if (application.student.matriculationNumber) {
map.set(application.student.matriculationNumber, application.id)
}
})
} else if (filters.applicationType.includes(ApplicationType.TUTOR)) {
} else if (filters.applicationType === ApplicationType.TUTOR) {
tutorApplications.forEach((application) => {
if (application.student.matriculationNumber) {
map.set(application.student.matriculationNumber, application.id)
Expand All @@ -142,93 +152,102 @@ export const StudentApplicationOverview = (): JSX.Element => {
return (
<Stack>
<Group>
<TextInput
style={{ margin: '1vh 0', width: '40vw' }}
placeholder='Search applications...'
leftSection={<IconSearch size={16} />}
value={searchQuery}
onChange={(e) => {
setSearchQuery(e.currentTarget.value)
}}
/>
<Menu withArrow closeOnItemClick={false}>
<Menu.Target>
<IconAdjustments />
</Menu.Target>
<Menu.Dropdown>
<Menu.Item>
<Checkbox
label='Male'
checked={filters.male}
onChange={(e) => {
setFilters({ ...filters, male: e.currentTarget.checked })
}}
/>
</Menu.Item>
<Menu.Item>
<Checkbox
label='Female'
checked={filters.female}
onChange={(e) => {
setFilters({ ...filters, female: e.currentTarget.checked })
}}
/>
</Menu.Item>
</Menu.Dropdown>
</Menu>
<Menu withArrow>
<Menu.Target>
<Button>Actions</Button>
</Menu.Target>
<h1>Student Applications</h1>

<Menu.Dropdown>
<Menu.Item
disabled={filters.applicationType.length !== 1}
onClick={() => {
setMatchingResultsUploadModalOpened(true)
}}
>
Upload Matching Results
</Menu.Item>
<Tooltip
withArrow
color='blue'
label="To start automatic assessment of developer applications based on the
technical challenge score, please select solely 'Developer' sorting filter"
>
<Menu.Item
disabled={
filters.applicationType.length > 1 ||
!filters.applicationType.includes(ApplicationType.DEVELOPER)
}
onClick={() => {
setTechnicalChallengeAssessmentModalOpened(true)
}}
>
Technical Challenge Assessment
</Menu.Item>
</Tooltip>
</Menu.Dropdown>
</Menu>
<TechnicalChallengeAssessmentModal
opened={technicalChallengeAssessmentModalOpened}
onClose={() => {
setTechnicalChallengeAssessmentModalOpened(false)
}}
tumIdToDeveloperApplicationMap={tumIdToApplicationMap}
/>
{filters.applicationType.length === 1 && (
<MatchingResultsUploadModal
opened={matchingResultsUploadModalOpened}
onClose={() => {
setMatchingResultsUploadModalOpened(false)
}}
tumIdToApplicationMap={tumIdToApplicationMap}
matriculationNumberToApplicationMap={matriculationNumberToApplicationMap}
applicationType={filters.applicationType[0]}
/>
)}
<MatchingResultsUploadModal
opened={matchingResultsUploadModalOpened}
onClose={() => {
setMatchingResultsUploadModalOpened(false)
}}
tumIdToApplicationMap={tumIdToApplicationMap}
matriculationNumberToApplicationMap={matriculationNumberToApplicationMap}
applicationType={filters.applicationType}
/>
</Group>

<Tabs
defaultValue='DEVELOPER'
value={filters.applicationType.toUpperCase() as keyof typeof ApplicationType}
onChange={(selectedApplication) => {
if (selectedApplication === 'button') {
return // ignore button clicks in the tabs
}
setFilters((oldFilters) => {
return {
...oldFilters,
applicationType: ApplicationType[selectedApplication ?? 'DEVELOPER'],
}
})
}}
>
<Tabs.List>
{Object.keys(ApplicationType).map((applicationKey) => (
<Tabs.Tab key={applicationKey} value={applicationKey}>
{ApplicationType[applicationKey].charAt(0).toUpperCase() +
ApplicationType[applicationKey].slice(1).toLowerCase()}
</Tabs.Tab>
))}

<Flex ml='auto' justify='flex-end' gap='12px' mb='12px'>
<TextInput
placeholder='Search applications...'
leftSection={<IconSearch size={16} />}
value={searchQuery}
onChange={(e) => {
setSearchQuery(e.currentTarget.value)
}}
/>
<FilterMenu filters={filters} setFilters={setFilters} />
{/**This menu will be re-designed in the very near future */}
<Menu withArrow>
<Menu.Target>
<Button leftSection={<IconUpload size={18} />} variant='default'>
Upload
</Button>
</Menu.Target>

<Menu.Dropdown>
<Menu.Item
//disabled={filters.applicationType.length !== 1}
onClick={() => {
setMatchingResultsUploadModalOpened(true)
}}
>
Upload Matching Results
</Menu.Item>
<Tooltip
withArrow
color='blue'
label="To start automatic assessment of developer applications based on the
technical challenge score, please select solely 'Developer' sorting filter"
>
<Menu.Item
disabled={filters.applicationType !== ApplicationType.DEVELOPER}
onClick={() => {
setTechnicalChallengeAssessmentModalOpened(true)
}}
>
Technical Challenge Assessment
</Menu.Item>
</Tooltip>
</Menu.Dropdown>
</Menu>
<Button leftSection={<IconPlus size={18} />} variant='light'>
Add Student
</Button>
</Flex>
</Tabs.List>
</Tabs>

<FilterChips filters={filters} setFilters={setFilters} />

<ApplicationDatatable
developerApplications={developerApplications}
coachApplications={coachApplications}
Expand Down
Loading