-
Notifications
You must be signed in to change notification settings - Fork 17
/
Copy pathSearchModule.tsx
109 lines (96 loc) · 4.13 KB
/
SearchModule.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import React, { useState, useEffect, Component, FC } from 'react';
import './SearchModule.scss';
import wfs from 'websoc-fuzzy-search';
import axios from 'axios';
import Form from 'react-bootstrap/Form';
import InputGroup from 'react-bootstrap/InputGroup';
import { Search } from 'react-bootstrap-icons';
import { useAppDispatch, useAppSelector } from '../../store/hooks';
import { setNames, setPageNumber, setResults } from '../../store/slices/searchSlice';
import { searchAPIResults } from '../../helpers/util';
import { SearchIndex, BatchCourseData, CourseGQLResponse, ProfessorGQLResponse, BatchProfessorData } from '../../types/types';
import { PAGE_SIZE } from 'src/helpers/constants';
const SEARCH_TIMEOUT_MS = 500;
interface SearchModuleProps {
index: SearchIndex;
}
const SearchModule: FC<SearchModuleProps> = ({ index }) => {
const dispatch = useAppDispatch();
const courseSearch = useAppSelector(state => state.search.courses);
const professorSearch = useAppSelector(state => state.search.professors);
let pendingRequest: NodeJS.Timeout | null = null;
// Search empty string to load some results
useEffect(() => {
searchNames('');
}, [])
// Refresh search results when names and page number changes
useEffect(() => {
searchResults('courses', courseSearch.pageNumber, courseSearch.names);
}, [courseSearch.names, courseSearch.pageNumber])
useEffect(() => {
searchResults('professors', professorSearch.pageNumber, professorSearch.names);
}, [professorSearch.names, professorSearch.pageNumber])
let searchNames = (query: string) => {
try {
/*
TODO: Search optimization
- Currently sending a query request for every input change
- Goal is to have only one query request pending
- Use setTimeout/clearTimeout to keep track of pending query request
*/
let nameResults = wfs({
query: query,
// numResults: PAGE_SIZE * MAX_PAGE_NUMBER,
resultType: index === 'courses' ? 'COURSE' : 'INSTRUCTOR',
filterOptions: {
}
})
let names: string[] = [];
if (index == 'courses') {
names = Object.keys(nameResults);
}
else if (index == 'professors') {
names = Object.keys(nameResults).map(n => nameResults[n].metadata.ucinetid) as string[];
}
console.log('From frontend search', names)
dispatch(setNames({ index, names }));
// reset page number
dispatch(setPageNumber({ index, pageNumber: 0 }));
}
catch (e) {
console.log(e)
}
}
let searchResults = async (index: SearchIndex, pageNumber: number, names: string[]) => {
// Get the subset of names based on the page
let pageNames = names.slice(PAGE_SIZE * pageNumber, PAGE_SIZE * (pageNumber + 1))
let results = await searchAPIResults(index, pageNames);
dispatch(setResults({ index, results: Object.values(results) }));
}
let searchNamesAfterTimeout = (query: string) => {
if (pendingRequest) {
clearTimeout(pendingRequest);
}
let timeout = setTimeout(() => {
searchNames(query);
pendingRequest = null;
}, SEARCH_TIMEOUT_MS);
pendingRequest = timeout;
}
let coursePlaceholder = 'Search a course number or department';
let professorPlaceholder = 'Search a professor';
let placeholder = index == 'courses' ? coursePlaceholder : professorPlaceholder;
return <div className='search-module'>
<Form.Group className="mb-3">
<InputGroup>
<InputGroup.Prepend>
<InputGroup.Text>
<Search />
</InputGroup.Text>
</InputGroup.Prepend>
<Form.Control className='search-bar' type="text" placeholder={placeholder} onChange={(e) => searchNamesAfterTimeout(e.target.value)} />
</InputGroup>
</Form.Group>
</div>
}
export default SearchModule;