Skip to content

Commit

Permalink
Course Search Bar (#103)
Browse files Browse the repository at this point in the history
resolves #103
currently the course search bar is fetching data from backend running on localhost, remember to change to Production API URL when merging to prod branch
  • Loading branch information
blu3eee committed Apr 9, 2024
1 parent 59160ac commit 8416199
Showing 1 changed file with 159 additions and 0 deletions.
159 changes: 159 additions & 0 deletions src/components/CourseSearchBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import React, { useState, useEffect } from 'react';
import { TextField, Autocomplete, CircularProgress } from '@mui/material';

interface CourseInfo {
id: string;
courseName: string;
courseNumber: string;
preReqs: string;
coReqs: string;
units: string;
}

const CourseSearchBar: React.FC = () => {
const [open, setOpen] = useState(false);
const [searchText, setSearchText] = useState('');
const [options, setOptions] = useState<CourseInfo[]>([]);
const [selectedCourse, setSelectedCourse] = useState<CourseInfo | null>(null);
const [loading, setLoading] = useState(false);

useEffect(() => {
const delayDebounce = setTimeout(() => {
if (searchText === '') {
// setOptions([]);
return;
}

setLoading(true);
fetch(
// change to prod api url when the backend endpoints are updated
// `https://api.cppbroncodirect.me/courses?key=${encodeURIComponent(searchText))}`
`http://localhost:3000/courses?key=${encodeURIComponent(searchText)}`
)
.then(async (response) => await response.json())
.then((data) => {
setOptions(data);
})
.catch((error) => {
console.error(`Error fetching courses:`, error);
})
.finally(() => {
setLoading(false);
});
}, 2000); // 2-second delay

return () => clearTimeout(delayDebounce);
}, [searchText]);

const fetchCourseDetails = async (courseNumber: string): Promise<void> => {
try {
const response = await fetch(
// change to prod api url when the backend endpoints are updated
// `https://api.cppbroncodirect.me/courses/${courseNumber}`
`http://localhost:3000/courses/${courseNumber}`
);
if (!response.ok) {
throw new Error('Network response was not ok');
}
const data: CourseInfo = await response.json();
setSelectedCourse(data);
} catch (error) {
console.error('Error fetching course details:', error);
setSelectedCourse(null);
}
};

return (
<div>
<Autocomplete
id="course-search"
sx={{
width: '100%',
}}
open={open}
onOpen={() => {
setOpen(true);
}}
onClose={() => {
setOpen(false);
}}
getOptionLabel={(option) =>
`${option.courseNumber}: ${option.courseName}`
}
options={options}
loading={loading}
onChange={(event, newValue: CourseInfo | null) => {
if (newValue) {
fetchCourseDetails(newValue.courseNumber).catch((e) => {});
}
}}
renderInput={(params) => (
<TextField
{...params}
onChange={(e) => setSearchText(e.target.value)}
placeholder="Search for a course"
variant="outlined"
InputProps={{
...params.InputProps,
endAdornment: (
<>
{loading && <CircularProgress color="inherit" size={20} />}
{params.InputProps.endAdornment}
</>
),
}}
/>
)}
/>
{selectedCourse && (
<div
style={{
marginTop: 10,
padding: 5,
}}
>
<h2>{selectedCourse.courseName}</h2>
<span style={{ fontSize: 14 }}>
{(
[
{ name: 'Course ID', value: selectedCourse.courseNumber },
{ name: 'Units', value: selectedCourse.units },
{
name: 'Prerequisites',
value: selectedCourse.preReqs,
hidden: !selectedCourse.preReqs,
},
{
name: 'Corequisites',
value: selectedCourse.coReqs,
hidden: !selectedCourse.coReqs,
},
] as CourseFieldProps[]
).map((field, index) => (
<CourseField key={index} {...field} />
))}
</span>
</div>
)}
</div>
);
};

export default CourseSearchBar;

interface CourseFieldProps {
name: string;
value: string;
hidden?: boolean;
}
const CourseField: React.FC<CourseFieldProps> = ({
name,
value,
hidden = false,
}) => {
return hidden ? null : (
<p>
<strong>{name}:</strong> {value}
</p>
);
};

0 comments on commit 8416199

Please sign in to comment.