diff --git a/frontend/README.md b/frontend/README.md index 58beeac..5cee854 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -1,70 +1,145 @@ -# Getting Started with Create React App +# GenEd Frontend + +This directory contains the React.js frontend application for the CMU-Q General Education course planning platform. It provides a searchable, filterable UI to explore and plan general education and core requirements across different majors. + +--- + +## Getting Started + +### Prerequisites + +* Node.js (v16+ recommended) +* npm (comes with Node.js) + +### Setup + +1. **Navigate to the frontend directory:** + ```bash + cd path/to/GenEd-CMUQ/frontend + ``` + +2. **Install dependencies:** + ```bash + npm install + ``` + +3. **Run the development server:** + ```bash + npm start + ``` -This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). +The app will be accessible at `http://localhost:3000`. + +> ⚠️ Make sure the backend is running and available at the correct API base URL. You can configure this in `.env`: +> ```env +> REACT_APP_API_BASE_URL=http://127.0.0.1:8000 +> ``` -## Available Scripts +--- -In the project directory, you can run: +## Features -### `npm start` +- πŸ” Search for courses by department and course number. +- βœ… Filter by campus offering, prerequisites, GenEd/Core types, and requirement fulfillment. +- πŸ“š View courses fulfilling specific requirements. +- πŸ“Š Paginated results for easy navigation. +- 🧩 Modular components with popup detail views. -Runs the app in the development mode.\ -Open [http://localhost:3000](http://localhost:3000) to view it in your browser. +--- -The page will reload when you make changes.\ -You may also see any lint errors in the console. +## Components Overview -### `npm test` +| Component | Purpose | +|----------|---------| +| `CourseTablePage.js` | Top-level controller for state and layout. Combines filters, search bar, and table. | +| `SearchBar.js` | Handles department selection, search input, and checkbox filters. | +| `CourseTable.js` | Renders the course data in a table format with dynamic requirement columns. | +| `MultiSelectDropdown.js` | Generic dropdown for selecting multiple filters (e.g. requirements, semesters). | +| `MultiSelectDepartment.js` | Specialized dropdown for department filtering. | +| `SelectedFilters.js` | Shows active filters with remove buttons. | +| `PopUp.js` | Modal that shows detailed course or requirement information. | -Launches the test runner in the interactive watch mode.\ -See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. +--- -### `npm run build` +## File Structure -Builds the app for production to the `build` folder.\ -It correctly bundles React in production mode and optimizes the build for the best performance. +``` +frontend/ +β”‚ +β”œβ”€β”€ public/ # Static files +β”‚ β”œβ”€β”€ index.html +β”‚ β”œβ”€β”€ manifest.json # PWA settings +β”‚ └── icons # App icons +β”‚ +β”œβ”€β”€ docs/ # Documentation on all components +β”‚ +β”œβ”€β”€ src/ +β”‚ β”œβ”€β”€ components/ # React components +β”‚ β”‚ β”œβ”€β”€ CourseTablePage.js +β”‚ β”‚ β”œβ”€β”€ CourseTable.js +β”‚ β”‚ β”œβ”€β”€ SearchBar.js +β”‚ β”‚ β”œβ”€β”€ PopUp.js +β”‚ β”‚ β”œβ”€β”€ MultiSelectDropdown.js +β”‚ β”‚ β”œβ”€β”€ MultiSelectDepartment.js +β”‚ β”‚ └── SelectedFilters.js +β”‚ β”œβ”€β”€ styles.css # App styling +β”‚ β”œβ”€β”€ planTabStyles.css # App styling for planning tab +β”‚ └── index.js # Entry point +β”‚ └── App.js +β”‚ +β”œβ”€β”€ .env # API base URL config +β”œβ”€β”€ package.json # Project metadata & dependencies +└── README.md # This file +``` -The build is minified and the filenames include the hashes.\ -Your app is ready to be deployed! +--- -See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. +## Running Tests -### `npm run eject` +The frontend uses **Jest** and **React Testing Library** for testing. Make sure you are in the frontend folder before running tests. -**Note: this is a one-way operation. Once you `eject`, you can't go back!** +### Running All Tests +```bash +npm test +``` -If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. +### Running a Specific Test File +```bash +npm test CourseTable.test.js +``` -Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own. +### Example Test Files +Tests are located in the `frontend/tests/` directory and include: -You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it. +``` +tests/ +β”œβ”€β”€ CourseTable.test.js +β”œβ”€β”€ SearchBar.test.js +β”œβ”€β”€ CourseTablePage.test.js +β”œβ”€β”€ MultiSelectDropdown.test.js +└── ... +``` + +Tests cover component rendering, user interactions, and prop handling. Each test simulates real user behavior using `@testing-library/react`. -## Learn More +> Make sure all required props are mocked correctly in your tests to avoid runtime errors. + +--- -You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). +## Development Notes -To learn React, check out the [React documentation](https://reactjs.org/). +- Data is fetched dynamically via REST API endpoints from the FastAPI backend. +- API errors are handled in `useEffect` hooks with simple `console.error()` logsβ€”consider improving with user notifications. +- The UI is responsive and works across modern browsers. + +--- -### Code Splitting - -This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting) - -### Analyzing the Bundle Size - -This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) - -### Making a Progressive Web App - -This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) - -### Advanced Configuration - -This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) - -### Deployment - -This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) - -### `npm run build` fails to minify - -This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) +## Progressive Web App (PWA) + +The frontend includes a `manifest.json` file to support installation as a PWA. Icons and colors can be customized in `public/manifest.json`. + +--- + +## Authors + +This project was developed by CMU-Q Students, Boushra, Jullia, and Yousuf, for the **67-373 Information Systems Consulting Project**. diff --git a/frontend/docs/AnalyticsPage.md b/frontend/docs/AnalyticsPage.md new file mode 100644 index 0000000..b8e3ede --- /dev/null +++ b/frontend/docs/AnalyticsPage.md @@ -0,0 +1,74 @@ +# Component: `` + +## Purpose + +Serves as the main analytics dashboard in the GenEd platform. Displays visual insights on: +- **Requirement Category Coverage** for a selected major +- **Enrollment Trends** across semesters and student classes + +--- + +## Instructions +1. On the tab located on the top of the page, click on the *Analytics* tab. +Screenshot 2025-04-19 at 1 08 46β€―PM + +--- + +## Internal State + +| State Variable | Description | +|------------------|-------------| +| `selectedMajor` | Currently selected major code (e.g., `"cs"`, `"bio"`) β€” saved in `localStorage` | + +--- + +## Logic Overview + +- Renders two major analytics sections: + - ``: shows how many courses fulfill each requirement category + - ``: visualizes overall enrollment and enrollment by class year +- Uses localStorage to persist the selected major across page reloads + +--- + +## Example Usage + +```jsx + +``` + +Used in the **Analytics** tab of the GenEd app. This component provides an overview of course coverage and participation trends per major. + +--- + +## Related Components + +- `` β€” Major/semester-wise requirement category chart +- `` β€” Enrollment comparison charts + +--- + +## Related Tests + +- `AnalyticsPage.test.js` β€” covers: + - LocalStorage persistence of `selectedMajor` + - Rendering both child components + - UI behavior when switching majors + +--- + +## Styling + +| Class | Description | +|-------|-------------| +| `analytics-container` | Main page layout | +| `title`, `subtitle` | Page header styles | +| `
` | Section separator | + +--- + +## Notes + +- Majors are hardcoded in the component for display purposes, but can be dynamically sourced if backend supports it. +- The selected major is passed to both analytics views for synchronized filtering. + diff --git a/frontend/docs/CategoryCoverage.md b/frontend/docs/CategoryCoverage.md new file mode 100644 index 0000000..9d43ba5 --- /dev/null +++ b/frontend/docs/CategoryCoverage.md @@ -0,0 +1,87 @@ +# Component: `` + +## Purpose + +Displays a bar chart showing how many courses fulfill each requirement category for a selected major and semester. Uses Plotly for charting and dynamically fetches data from the backend via `/analytics/course-coverage`. + +--- +## Instructions +1. On the top of the page, select the *Analytics* tab. +2. Locate the *Category Coverage* section. +3. Click on the *Major* dropdown to select a major. This will display all the Core and Gen-Ed requirements on the graph, listed on the Y-axis.
+ Screenshot 2025-04-19 at 1 11 48β€―PM + +5. Click on the *Semester* dropdown to select a specific semester. This will update the graph to show the total count of how many courses satisfy each requirement category for your chosen major and semester.
+ Screenshot 2025-04-19 at 1 12 00β€―PM + +6. Hovering over each bar will show the exact count of offered courses for the respective requirement.
+Screenshot 2025-04-19 at 1 17 40β€―PM + +Screenshot 2025-04-19 at 1 11 43β€―PM + +--- + +## Props + +| Prop Name | Type | Required | Description | +|-------------------|----------|----------|-------------| +| `selectedMajor` | String | βœ… | The major currently selected (e.g., "CS") | +| `setSelectedMajor`| Function | βœ… | Updates the selected major | +| `majors` | Object | βœ… | Dictionary of major codes to names (e.g., `{ CS: "Computer Science" }`) | + +--- + +## Logic Overview + +- Fetches available semesters with data for the selected major +- Fetches requirement coverage data for the selected major + semester +- Aggregates the number of courses per requirement (based on the last node in path) +- Uses Plotly to render a horizontal bar chart, sorted by course count +- Dynamically switches content based on data availability and loading state + +--- + +## Example Usage + +```jsx + +``` + +Used in the **Analytics** page to let users visually explore course distribution across requirement categories. You can change the major and limit by semester. + +--- + +## Related Tests + +- `CategoryCoverage.test.js` β€” test coverage includes: + - Valid/invalid API responses + - Dropdown behavior + - Chart rendering based on dynamic data + - Conditional β€œNo Data” message + +--- + +## Styling + +| Class | Description | +|-------|-------------| +| `search-container` | Wrapper layout | +| `filter-control-group` | Wrapper for dropdowns | +| `search-dropdown` | Select element styling | +| `loading-spinner` | Visual feedback during API load | +| `filter-tag` | Display fallback β€œno data” status | + +--- + +## Notes + +- Filters out requirement categories with `num_courses === 0` to reduce noise +- Responsive to container size (`useResizeHandler`) +- Resets semester when major is changed +- Requires the backend endpoint: + `GET /analytics/course-coverage?major=CS&semester=F23` + diff --git a/frontend/docs/CourseTable.md b/frontend/docs/CourseTable.md new file mode 100644 index 0000000..7dbf08c --- /dev/null +++ b/frontend/docs/CourseTable.md @@ -0,0 +1,105 @@ +# Component: `` + +## Purpose + +Renders the main table displaying filtered courses. Includes interactive filters per major, prerequisite status, and semester offerings. Each row supports a popup to show detailed course or requirement information. Also adapts behavior for β€œPlan” and β€œView” tabs. + +--- +## Instructions +1. On the top of the page, select the *View* or *Plan* tab. +2. Choose a filter, from the dropdowns or the search bar, or search for a course on the search bar. This should reflect and automatically update the course table accordingly.
+ Screenshot 2025-04-19 at 1 26 11β€―PM + +- When the pre-req list is long, click on the *Show More/Less* button to toggle details. +- When on the *Summarized* or *Compact* view, the semester offerings list will also have a similar toggle. Click on the *Show More/Less* button to toggle details.
Screenshot 2025-04-19 at 1 29 18β€―PM + + Screenshot 2025-04-19 at 1 30 20β€―PM + + +--- + +## Props + +| Prop Name | Type | Required | Description | +|--------------------------|----------|----------|-------------| +| `courses` | Array | βœ… | List of course objects to display | +| `allRequirements` | Object | βœ… | Requirement options grouped by major | +| `selectedFilters` | Object | βœ… | Tracks currently selected requirement filters | +| `handleFilterChange` | Function | βœ… | Updates selected filters | +| `clearFilters` | Function | βœ… | Clears all filters for a given major | +| `offeredOptions` | Array | βœ… | List of semesters used in the offered column | +| `selectedOfferedSemesters` | Array | βœ… | List of semesters currently selected | +| `setSelectedOfferedSemesters` | Function | βœ… | Updates selected offered semesters | +| `coreOnly` | Boolean | ❌ | If true, show only Core requirements | +| `genedOnly` | Boolean | ❌ | If true, show only GenEd requirements | +| `allowRemove` | Boolean | ❌ | If true, shows a delete button per course | +| `handleRemoveCourse` | Function | ❌ | Function to remove a course (only if `allowRemove`) | +| `noPrereqs` | Boolean / null | ❌ | Controls Prerequisite dropdown state | +| `setNoPrereqs` | Function | ❌ | Updates prerequisite filter setting | +| `compactViewMode` | String | ❌ | If `"last1"` or `"last2"`, trims long requirement labels | +| `hideDropdowns` | Boolean | ❌ | If true, hides filter dropdowns in headers | +| `isPlanTab` | Boolean | ❌ | If true, adjusts empty state message for Plan tab | + +--- + +## State & Logic + +- `isPopupOpen`, `popupType`, `popupContent`: control the popup modal +- `fetchCourseDetails`: fetches full details of a course on click +- `filterRequirementObjects`: filters requirements based on `coreOnly` and `genedOnly` +- `compactViewMode`: trims requirement text in cells (`last1` or `last2`) +- `OfferedCell` and `PrereqCell`: expandable cells with β€œShow more” toggles + +--- + +## Example Usage + +```jsx + +``` + +--- + +## Related Tests + +- `CourseTable.test.js` – rendering, dropdown filters, popup behavior, expand/collapse functionality. + +--- + +## Related Components + +- `` +- `` +- `` + +--- + +## Styling + +- `remove-col`: column for delete button +- `header-offered`, `header-prereq`, `header-{major}`: table header styles +- `expand-toggle`: Show more / less toggle +- `cell-offered`, `cell-prereq`, `cell-{major}`: data cell styles + +--- + +## Notes + +- Requirements are dynamically filtered and formatted based on Core/GenEd flags. +- Popup supports both course and requirement views. +- Offered semesters are sorted chronologically (F/S/M + year). +- Handles both full view and compact display modes. diff --git a/frontend/docs/CourseTablePage.md b/frontend/docs/CourseTablePage.md new file mode 100644 index 0000000..5018236 --- /dev/null +++ b/frontend/docs/CourseTablePage.md @@ -0,0 +1,130 @@ +# Component: `` + +## Purpose + +Acts as the main page controller for the CMU-Q GenEd frontend. Manages the UI state, filter logic, search behavior, course retrieval, and rendering of child components such as the search bar, table, and popups. + +--- +## Instructions +### View +Choosing a view from the view dropdown will update the length of the displayed requirements accordingly. This dropdown is located above the table, to the left of the Clear Filters button. +- Full Requirements View: Contains the full requirements string. Ex: *Biological Sciences β†’ Biological Sciences Electives β†’ Biological Sciences Electives β†’ Departmental Electives Group* +- Summarized Requirements View: Contains the last 2 groups of the requirements string. Ex: *Biological Sciences Electives β†’ Departmental Electives Group* +- Compact Requirements View: Contains the last group of the requirements string. Ex: *Departmental Electives Group* +Screenshot 2025-04-19 at 1 27 03β€―PM Screenshot 2025-04-19 at 1 37 28β€―PM + +When opening the website for the first time, the default view is the Full Requirements View. + +### Clear Button +To reset all the chosen dropdown filters, search bar filter, or search queries, click on the *Clear All Filters* button. This is located above the table, next to the Requirements View dropdown select. +- A popup message should appear. Click on the *Clear All Filters* option.
Screenshot 2025-04-19 at 1 40 00β€―PM + +### Add to Plan Button +To add all the courses which are currently on your view tab to the planning tab, click on *Add All To Plan* button. This is located above the table, next to the Clear All Filters button. +- If there are more than 20 courses in view, a popup message should appear. Click on the *Yes, Add All* to confirm your decision.
Screenshot 2025-04-19 at 1 40 08β€―PM
+These courses are now successfully added to the plan tab. Open the plan tab to view your courses. +- This button will be disabled if all the courses in the view are already added to the plan tab. + +### Sort Button +You can sort the table results by their course code or by the number of requirements met (with highest being priority). +- To change the sorting order, click on the *Sort by __* button. This is located above the table, next to the Add to Plan button. +- This will dynamically change based on the current sorting order.
+Screenshot 2025-04-19 at 1 40 32β€―PM Screenshot 2025-04-19 at 1 40 23β€―PM + +### Pagination Controls and Display +You can change the number of courses being displayed on the current page by selecting your desired number on the pagination controls. +- Scroll down to the bottom of the page until you reach the end of the current results. +- Click on the right-most dropdown, which controls how many courses are being displayed per page.
+Screenshot 2025-04-19 at 1 40 57β€―PM +
+Your chosen option, along with the total number of courses, is displayed on the top left of the table. +
Screenshot 2025-04-19 at 2 04 26β€―PM + +
+You can also go to the previous or next page using these controls. Simply use the left and right arrows or click on a page number to go to your desired page.
+Screenshot 2025-04-19 at 1 40 49β€―PM + +### Dropdowns +Instructions on the dropdowns are located in the following files: [SearchBar]([url](https://github.com/bbendou/GenEd-CMUQ/blob/frontend_documentation/frontend/docs/SearchBar.md)), [MultiSelectDropdown]([url](https://github.com/bbendou/GenEd-CMUQ/blob/frontend_documentation/frontend/docs/MultiSelectDropdown.md)), and [SingleSelectDropdown]([url](https://github.com/bbendou/GenEd-CMUQ/blob/frontend_documentation/frontend/docs/SingleSelectDropdown.md)). + +--- + +## Internal State + +- `selectedDepartments`, `searchQuery`, `noPrereqs`, `coreOnly`, `genedOnly`, etc.: Manage user-selected filters +- `courses`, `requirements`: Store data fetched from backend +- `offeredOptions`: Populates semester dropdowns +- `sortMode`: Toggles between sorting by course code or requirements +- `compactViewMode`: Controls the display format of requirement text +- `toast`, `loading`, `showConfirmPopup`, `showClearPopup`: UI feedback and modals + +--- + +## API Calls + +- `/departments`: Fetch list of departments for dropdown +- `/requirements`: Fetch course requirement data +- `/courses/search`: Fetch filtered list of courses +- `/courses/semesters`: Get available semesters + +All filters and search values are sent as query parameters. + +--- + +## Lifecycle Effects + +- `useEffect` saves user preferences to `localStorage` for persistence +- Search query is debounced using a `setTimeout` (350ms delay) +- Courses and requirements are re-fetched when filters change +- Paginated display updates when data length or page changes + +--- + +## Handlers + +| Handler | Purpose | +|---------|---------| +| `handleFilterChange` | Updates selected filters by major | +| `clearFilters` | Clears all filters for a major | +| `removeOfferedSemester` | Removes a selected semester | +| `removePrereqFilter` | Resets the pre-req filter to null | +| `handleRemoveCourse` | Removes a course from the local course list | +| `addCoursesToPlan` | Saves selected courses to localStorage | +| `toggleSortByReqs` | Toggles between sorting modes | +| `clearAllFilters` | Resets all filters to their defaults | + +--- + +## Child Components + +- `` +- `` +- `` +- Toast and popup components for UI interaction + +--- + +## Related Tests + +- `CourseTablePage.test.js` – covers filter state logic, fetch behavior, and UI interaction +- Mocks localStorage and API calls for isolation + +--- + +## Styling + +| Class | Description | +|-------|-------------| +| `table-container` | Main container layout | +| `view-toolbar` | Toolbar with filters, sort and plan buttons | +| `toast-snackbar` | Small floating message box | +| `popup-overlay-2`, `popup-box` | Modal styles for confirmation dialogs | +| `no-results-msg` | Display when no matching courses | + +--- + +## Notes + +- Uses `AbortController` to cancel pending API fetches and avoid race conditions. +- Stores filter state in `localStorage` for persistence across page reloads. +- Modular design supports adding new filters or sorting methods with minimal refactoring. diff --git a/frontend/docs/DataUpload.md b/frontend/docs/DataUpload.md new file mode 100644 index 0000000..71952fc --- /dev/null +++ b/frontend/docs/DataUpload.md @@ -0,0 +1,64 @@ +# Component: `` + +## Purpose + +Provides a user interface to upload datasets for initializing or updating the backend database. Supports uploading course ZIPs, audit ZIPs, enrollment Excel files, and department CSV files. Performs client-side validation for structure and file type before uploading. + +--- +## Instructions +For instructions, please visit the dedicated upload URL. + +--- + +## Internal State + +| State Variable | Description | +|---------------------|-------------| +| `courseZips` | ZIP files containing course data | +| `auditZips` | ZIP files containing audit requirement data | +| `enrollmentFile` | Excel file with course enrollment data | +| `departmentCsv` | CSV file with department information | +| `error` / `success` | Display messages for validation/upload outcomes | +| `loading` | Upload process status | + +--- + +## Example Usage + +```jsx + +``` + +This component can be rendered on an admin or setup page where the backend database needs to be initialized with data from structured files. Files must follow expected formats, and course data is required if audit or enrollment data is present. + +--- + +## Related Tests + +- `DataUpload.test.js` β€” verifies: + - ZIP structure validation (audit vs. course) + - File type checks for Excel/CSV + - Successful form submissions + - Error and success message handling + - Disabled state of the "Upload" button + +--- + +## Styling & UI Libraries + +| Element | Source | +|---------------|--------| +| UI Components | [MUI (Material UI)](https://mui.com) | +| File Parsing | [JSZip](https://stuk.github.io/jszip/) | +| Feedback UI | ``, `` from MUI | +| Hidden Input | Styled with `VisuallyHiddenInput` using `@mui/material/styles` | + +--- + +## Notes + +- Performs client-side file type and content validation before sending to the backend. +- Uses `FormData` to send multiple files with the same field key (e.g., multiple ZIPs). +- Backend endpoint expected: `POST /upload/init-db/` +- All validation and upload combinations are described in a detailed instructional UI inside the component. + diff --git a/frontend/docs/EnrollmentAnalytics.md b/frontend/docs/EnrollmentAnalytics.md new file mode 100644 index 0000000..f510f50 --- /dev/null +++ b/frontend/docs/EnrollmentAnalytics.md @@ -0,0 +1,93 @@ +# Component: `` + +## Purpose + +Displays enrollment trends for CMU-Q courses. Combines two sub-components: +- **AggregatedEnrollmentAnalytics**: shows total enrollment across semesters for multiple selected courses. +- **ClassEnrollmentAnalytics**: displays enrollment per class year (Freshman, Sophomore, etc.) for a single course. + +--- +## Instructions +At the top of the site, select the *Analytics* tab and scroll down to access visual insights on past course enrollment trends. + +### Enrollment Analytics Across Courses +Use this section to compare enrollment patterns across multiple courses over time. +1. Enter a course code (e.g., 15-112, 15-110) into the input field and click β€œAdd Course.” +2. Each selected course will appear as a tag below the input, and its enrollment trend will be plotted on the graph. +3. The X-axis displays semesters (e.g., S20, F22, S25), and the Y-axis shows enrollment counts. +4. Hover over the graph points for exact enrollment values per course and semester. +5. To remove a course from the chart, click the Γ— on its corresponding tag. +
+Screenshot 2025-04-19 at 3 55 23β€―PM + +### Enrollment Analytics Across Classes +Use this section to see how a single course is taken by different student groups over time. +1. Enter the course code (e.g., 67-262) into the input field and click β€œLoad Course.” +2. The chart will display enrollment counts for each student class group (e.g., Sophomores, Juniors, Seniors) across recent semesters. +3. Each group is color-coded and appears in the graph legend. +Use this data to understand which year levels typically take the course and how that changes over time. +
+Screenshot 2025-04-19 at 3 55 16β€―PM + +--- + +## Internal Components + +### 1. `` + +| Feature | Description | +|-----------------|-------------| +| Input | Course code text box (`15122` or `15-122`) | +| Output | Line chart of enrollment count over semesters | +| Logic | Fetches enrollment totals for each course from `/analytics/enrollment-data` | +| UI | Adds/removes course chips, shows loading/error states | + +### 2. `` + +| Feature | Description | +|-----------------|-------------| +| Input | Single course code | +| Output | Line chart per class (1st year – 4th year) | +| Logic | Filters enrollment by `class_` field and aggregates data | +| UI | Allows toggling of view based on selected course code | + +--- + +## Example Usage + +```jsx + +``` + +Place this component inside the `Analytics` tab of your app to let users explore how course enrollments evolve across semesters and cohorts. + +--- + +## Related Tests + +- `EnrollmentAnalytics.test.js` β€” test coverage includes: + - API calls for valid/invalid course codes + - Chart rendering using mock Plotly data + - UI for search, input, loading, and error handling + +--- + +## Styling + +| Class / Style | Description | +|-------------------|-------------| +| `search-container` | Container for each analytics panel | +| `text-input`, `add-all-btn` | Input and action controls | +| `filter-tag` | Course chips with remove buttons | +| `loading-spinner` | Displayed while fetching data | +| Plot styling | Handled by `react-plotly.js` responsive config | + +--- + +## Notes + +- Requires backend support at: + `GET /analytics/enrollment-data?course_code=XX-XXX` +- Automatically deduplicates courses and applies semantic formatting (`15-122`) +- Enrollment traces use `lines+markers` for visual clarity +- Class-specific analytics excludes "Class 0" (unspecified or administrative entries) diff --git a/frontend/docs/MainTabs.md b/frontend/docs/MainTabs.md new file mode 100644 index 0000000..1d05192 --- /dev/null +++ b/frontend/docs/MainTabs.md @@ -0,0 +1,79 @@ +# Component: `` + +## Purpose + +Serves as the entry-point navigation for the GenEd planning interface. Manages the three main tabs: **View**, **Plan**, and **Analytics**, and renders their associated components. Uses `localStorage` to persist the user’s last selected tab. + +--- + +## Instructions +Locate the tab selector at the top of the page. It allows you to switch between the three main views: +- **View tab**: Explore all available courses by searching by course code or department, filtering by campus, prerequisites, and requirement type (Core or GenEd), and using dropdowns to narrow results based on major-specific requirements +- **Plan tab**: Manually plan course offerings by add courses one by one to your planning table +- **Analytics tab**: Visualize enrollment trends and requirement coverage. +To navigate, click the tab name at the top of the interface. +Screenshot 2025-04-19 at 2 20 39β€―PM + +--- + +## Internal State + +| State Variable | Description | +|----------------|-------------| +| `activeTab` | Stores the currently selected tab: `"general"`, `"plan"`, or `"analytics"` | + +--- + +## Logic Overview + +- `useEffect` stores the selected tab in `localStorage` to persist tab selection across page reloads +- Renders one of the three tab components conditionally: + - `"general"` β†’ `` + - `"plan"` β†’ `` + - `"analytics"` β†’ `` +- Applies the `"active"` class to the selected tab for styling + +--- + +## Example Usage + +```jsx + +``` + +Used as the main controller in the app’s homepage or dashboard to switch between course viewing, planning, and analytics. + +--- + +## Related Components + +- `` β€” Tab for searching and filtering courses +- `` β€” Tab for managing a planned list of courses +- `` β€” Tab for displaying data visualizations on course metrics + +--- + +## Related Tests + +- `MainTabs.test.js` β€” test coverage includes: + - Tab switching + - Persistence using localStorage + - Conditional rendering of child components + +--- + +## Styling + +| Class | Description | +|-------|-------------| +| `tab-bar` | Container for the tab toggle buttons | +| `tab` | Style for inactive tabs | +| `tab.active` | Highlighted tab styling | +| `tab-content` | Content area under the tab bar | + +--- + +## Notes + +- `key` props are applied to each tab render to ensure full reset of internal state when switching. +- You can extend this component to accept custom tab content or dynamically load tabs from configuration. diff --git a/frontend/docs/MultiSelectDropdown.md b/frontend/docs/MultiSelectDropdown.md new file mode 100644 index 0000000..d200c1c --- /dev/null +++ b/frontend/docs/MultiSelectDropdown.md @@ -0,0 +1,96 @@ +# Component: `` + +## Purpose + +A reusable dropdown component supporting single or grouped multiselect behavior. Used for filtering by requirements, departments, course types, locations, and semesters in the GenEd UI. + +--- + +## Instructions +The dropdowns under each major (BA, BS, CS, IS) and the Offered column allow you to filter courses based on specific requirements or semester offerings. Selecting a requirement will filter all the course results to show only those that fulfill chosen requirments. + +1. Open the dropdown by clicking the β€œSelect Requirements ▼” button. +2. Scroll through available options. These represent requirement paths (e.g., GenEd β†’ Science and Engineering β†’ Lab Requirement). +3. Check the boxes to select one or multiple filters. Click on *Apply Filters* when you're done choosing to update the table. This *Apply* button is disabled (gray) if there are no filters selected. + - To select all options, click the β€œSELECT ALL” button. This will dynamically update the table. + - To clear all selections, click the β€œCLEAR ALL” button. This will dynamically update the table. This button is disabled (gray) if there are no active filters for the major. +4. Selected filters will appear as colored tags above the table. +5. To remove requirement/offering filters: + - Click on the 'x' button on its respective filter tags. + - Open the dropdown again and scroll to the respective requirement/option, click on it again to deselect it. Then, click on *Apply Filters* to update the table. +Screenshot 2025-04-19 at 1 41 36β€―PM + +The department, location, and course type dropdowns in the search bar also use this component. Instructions on how to use them are located in SearchBar.md. + +--- + +## Props + +| Prop Name | Type | Required | Description | +|-----------------------|----------|----------|-------------| +| `major` | String | βœ… | Identifies the type of filter this dropdown manages | +| `allRequirements` | Array | βœ… | List of available options (can be flat or nested objects) | +| `selectedFilters` | Object | βœ… | Current selected filter values | +| `handleFilterChange` | Function | βœ… | Callback to apply new filter selections | +| `clearFilters` | Function | βœ… | Callback to clear all filters for this major | +| `showSelectedInButton` | Boolean | ❌ | If true, displays current selections in the button label | +| `hideSelectButtons` | Boolean | ❌ | Hides "Select All" and "Clear All" buttons if true | +| `wrapperClassName` | String | ❌ | Optional class name for styling the container | + +--- + +## Logic Overview + +- Internally manages dropdown state (`isOpen`, `tempSelection`, `expandedGroups`) +- Uses `buildNestedGroups()` to structure Core/GenEd requirements into collapsible groups +- Dynamically renders: + - Flat checkbox lists for `department`, `location`, etc. + - Collapsible nested groups for requirement filtering +- Tracks temporary selection before applying via β€œApply Filters” button +- Includes built-in toggles for β€œSelect All”, β€œClear All”, and group-specific selection + +--- + +## Example Usage + +```jsx + +``` + +You can reuse this for filtering by `department`, `offered`, `location`, `courseType`, or any requirement type. For non-requirement filters, it renders a simple list; for requirement filters, it renders a hierarchical group. + +--- + +## Related Tests + +- `MultiSelectDropdown.test.js` β€” tests include: + - Dropdown open/close behavior + - Select/deselect logic + - Nested group toggle and β€œSelect All in group” + - Apply filter validation + +--- + +## Styling + +| Class | Description | +|-------|-------------| +| `dropdown`, `dropdown-content`, `dropdown-btn` | Container and toggle styles | +| `dropdown-group`, `dropdown-subgroup` | Indent groups visually | +| `select-buttons-row`, `drop-apply-btn` | Action buttons within dropdown | +| `dropdown-item`, `checkbox-right` | Styles for checkbox labels | +| `dropdown-offered` | Specialized style for semester filters | + +--- + +## Notes + +- Avoid using dropdowns with hundreds of items β€” group hierarchies can grow deep. +- Dropdown closes on outside click (via `useRef` and `mousedown` event). +- Make sure `handleFilterChange()` handles both string and array inputs depending on context. diff --git a/frontend/docs/PlanCourseTab.md b/frontend/docs/PlanCourseTab.md new file mode 100644 index 0000000..ffc83b6 --- /dev/null +++ b/frontend/docs/PlanCourseTab.md @@ -0,0 +1,105 @@ +# Component: `` + +## Purpose + +Provides an interactive tab where users can search for courses by code, add them to a personal plan, and view them in a dedicated table. Utilizes localStorage for persistence across sessions. + +--- +## Instructions +### Adding Courses +Use the search functionality at the top of the Plan tab to find existing courses by code or department. Once located, click the β€œAdd” button next to a course to add it to your personal course plan.
+Screenshot 2025-04-19 at 3 35 48β€―PM + + +### Planned Course Table +Courses you’ve added will appear in a table that mirrors the main course table structure. Each row represents a course, and each column shows which requirements that course fulfills per major. You can hover or click on the requirement cells to view more details in a popup. + +### Removing Courses +To remove a course from your plan, click the βœ– icon in the first column of the row. The table will update immediately to reflect the change.
+Screenshot 2025-04-19 at 3 51 29β€―PM + +### Clear All Courses +Click the β€œClear All” button, then "OK" on the popup message, to remove all courses from your current plan and reset the planning table. +Screenshot 2025-04-19 at 3 36 07β€―PM + + +--- + +## Internal State + +| State Variable | Description | +|---------------------|-------------| +| `searchQuery` | Input for searching course code | +| `searchResults` | Results returned from course search | +| `addedCourses` | Courses added to the plan (stored in localStorage) | +| `requirements` | Requirements used for formatting the course table | +| `toast` | Snackbar message state for success/warnings | +| `showSearchResults` | Toggles visibility of search result panel | +| `compactViewMode` | Controls how requirement paths are displayed | +| `loading` | Whether requirement data is being fetched | + +--- + +## Logic Overview + +- Uses `localStorage` to persist added courses +- Fetches requirement data from backend to support table rendering +- Enables searching by formatted course code (e.g., `15122` or `15-122`) +- Displays toast messages when adding duplicate or new courses +- Filters out already added courses from re-adding +- Supports compact requirement views (`full`, `last2`, `last1`) +- Provides a search dropdown UI that collapses on outside click + +--- + +## Example Usage + +```jsx + +``` + +Used as a standalone tab or page in the GenEd planning interface. Requires the backend to expose: +- `/courses/search` +- `/courses/{course_code}` +- `/requirements` + +--- + +## Related Components + +- `` β€” reused to render planned courses +- Toast/snackbar +- Animated search results dropdown + +--- + +## Related Tests + +- `PlanCourseTab.test.js` β€” test coverage includes: + - Adding/removing courses + - Search result rendering + - Toast messages + - Persistence with localStorage + - UI toggle for compact views + +--- + +## Styling + +| Class | Description | +|-------|-------------| +| `plan-tab-container` | Main wrapper layout | +| `search-bar-container`, `search-bar-enhanced` | Custom search bar layout | +| `scrollable-results` | Floating container for search matches | +| `course-result-item` | Each item in the result list | +| `toast-snackbar` | Feedback messages | +| `loading-spinner` | Loading indicator | +| `view-toggle`, `clear-all-btn` | Utility controls for view and clearing plan | + +--- + +## Notes + +- Integrates with `CourseTable`, so format consistency and filtering logic are shared. +- Can be easily extended to allow editing/removal or viewing course details. +- This component only adds courses via search; it does not support filtering by requirements or location. diff --git a/frontend/docs/PopUp.md b/frontend/docs/PopUp.md new file mode 100644 index 0000000..11d8374 --- /dev/null +++ b/frontend/docs/PopUp.md @@ -0,0 +1,111 @@ +# Component: `` + +## Purpose + +Displays a modal overlay containing detailed information about either a course or a requirement. Dynamically switches between `course` and `requirement` view modes. Used in the CourseTable to provide interactive, in-depth exploration. + +--- +## Instructions +The popup appears when a course code or requirement cell is clicked in the course table. It provides detailed information without navigating away from the main view. + +### Course Popups +Clicking on a course code opens a popup displaying detailed course information, including the course code, title, unit count, description, prerequisites, semester offerings, and the requirements it fulfills across majors. You can scroll through the list of fulfilled requirements, organized by major.
+Screenshot 2025-04-19 at 12 36 51β€―PM + +### Requirement Popups +Clicking on a requirement cell opens a popup showing all the courses that fulfill that specific requirement. The requirement name is shown at the top, and the list of matching courses appears below. Each course name is clickable and will open a course details popup when selected.
+Screenshot 2025-04-19 at 12 36 59β€―PM + + +### Closing the Popup +Click the βœ– icon in the top-right corner of the popup panel to close it and return to the main view. + +--- +## Props + +| Prop Name | Type | Required | Description | +|-------------|----------|----------|-------------| +| `isOpen` | Boolean | βœ… | Controls visibility of the popup | +| `onClose` | Function | βœ… | Callback for closing the popup | +| `type` | String | βœ… | "course" or "requirement" – controls rendering logic | +| `content` | Object | βœ… | Course or requirement data to display | +| `openPopup` | Function | βœ… | Used for opening nested popups (e.g., clicking a requirement course link) | + +--- + +## Logic Overview + +- `formatRequirement()`: Helper function to clean and format raw requirement strings (especially for GenEd). + - Handles variations like "General Education" or "University Core Requirements". + - Converts `---` separators into arrows (`β†’`). +- Requirements and courses are grouped and displayed per major. +- Deduplicates and sorts semesters using `sortSemesters()`. + +--- + +## Example UI + +### Course View + +Displays: +- Course code, name, units, description +- Prerequisites (cleaned) +- Semesters offered +- Requirements it fulfills (grouped by major) + +### Requirement View + +Displays: +- Formatted requirement name +- List of courses that fulfill it +- Each course is clickable and opens its popup view + +--- + + +--- + +## Example Usage + +```jsx + setIsPopupOpen(false)} + type="course" + content={selectedCourse} + openPopup={(type, content) => { + setPopupType(type); + setPopupContent(content); + setIsPopupOpen(true); + }} +/> +``` + +You can trigger this component on row clicks or requirement clicks within ``. The `type` can be `"course"` or `"requirement"` depending on the context, and the `content` prop should contain either full course data or a requirement object with a list of courses. + + +## Related Tests + +- `Popup.test.js` – test visibility toggling, correct formatting of requirements, rendering of course list + +--- + +## Styling + +| Class | Description | +|-------|-------------| +| `popup-overlay` | Full-screen dimmed background | +| `popup-panel` | Main content container | +| `popup-close-btn` | Closes the popup | +| `popup-title` | Header text | +| `requirement-group`, `requirement-list` | Styling for grouped content | +| `course-link` | Clickable course item within requirement view | + +--- + +## Notes + +- Popup is conditionally rendered – returns `null` if `isOpen` is false. +- Uses `Set` and custom sorting to handle semester display cleanly. +- Uses nested click handlers to allow opening another popup from within a requirement popup. + diff --git a/frontend/docs/SearchBar.md b/frontend/docs/SearchBar.md new file mode 100644 index 0000000..4e1cc04 --- /dev/null +++ b/frontend/docs/SearchBar.md @@ -0,0 +1,119 @@ +# Component: `` + +## Purpose + +Provides the search and filtering interface for the GenEd course list. Supports input by course code, department selection, and dropdown filters for location and course type (Core/GenEd). State is managed via props and updates are pushed to the parent component. + +--- +## Instructions +The search bar is located at the top of the page, above the course table. It allows users to refine their search using a combination of inputs and filters for more accurate results.Screenshot 2025-04-19 at 2 44 29β€―PM + +### Course Code Input +Enter a full or partial course code (e.g., 76-101, 101, or 76 101) to search for specific courses by number. The course code will appear as a removable tag once searched. + +### Department Dropdown +Choose a department to filter courses offered by that department. Select one or multiple departments and click on the *Apply Filters* button. The selected departments will appear as a removable tag once applied.
+Screenshot 2025-04-19 at 2 38 50β€―PM + +### Location Checkboxes +Toggle the "Qatar" and "Pittsburgh" checkboxes and click on the *Apply Filters* button to filter courses based on the campus where they are offered.
+Screenshot 2025-04-19 at 2 38 39β€―PM + +### Course Type Checkboxes: +Use the "Core" and "GenEd" checkboxes and click on the *Apply Filters* button to limit the results based on the type of requirement a course fulfills.
+Screenshot 2025-04-19 at 2 38 30β€―PM + +--- + +## Props + +| Prop Name | Type | Required | Description | +|--------------------|----------|----------|-------------| +| `selectedDepartments` | Array | βœ… | Selected department codes | +| `setSelectedDepartments` | Function | βœ… | Updates department filters | +| `searchQuery` | String | βœ… | Input value for course code search | +| `setSearchQuery` | Function | βœ… | Updates course code query | +| `noPrereqs` | Boolean/null | βœ… | Current prerequisite filter | +| `setNoPrereqs` | Function | βœ… | Updates pre-requisite filter | +| `offeredQatar` | Boolean/null | βœ… | Filter for Qatar campus | +| `setOfferedQatar` | Function | βœ… | Updates Qatar location filter | +| `offeredPitts` | Boolean/null | βœ… | Filter for Pittsburgh campus | +| `setOfferedPitts` | Function | βœ… | Updates Pittsburgh location filter | +| `coreOnly` | Boolean/null | βœ… | Core-only course filter | +| `setCoreOnly` | Function | βœ… | Setter for Core filter | +| `genedOnly` | Boolean/null | βœ… | GenEd-only course filter | +| `setGenedOnly` | Function | βœ… | Setter for GenEd filter | + +--- + +## Logic Overview + +- Uses `useEffect` to fetch department list from the backend. +- Default selection sets Core and GenEd to `true` if unset. +- Formats course codes via `formatCourseCode()` utility. +- Handles user interaction: + - Search by course code (supports 15122 or 15-122) + - Multi-select dropdowns for departments, campus, and course type + - Clear buttons for individual filters + +--- + +## Related Components + +- `` β€” reused for department, location, and course type + +--- + + +--- + +## Example Usage + +```jsx + +``` + +Used at the top of the course listing page to allow users to search and filter courses based on department, code, prerequisites, and location. + + +## Related Tests + +- `SearchBar.test.js` – tests for: + - Department and location dropdown rendering + - Search field updates + - Filter tags and removal logic + +--- + +## Styling + +| Class | Description | +|-------|-------------| +| `search-container` | Wrapper for the entire component | +| `search-header-row`, `search-content-row` | Organize layout into rows | +| `filter-group`, `filter-label` | Style each dropdown group | +| `text-input` | Style for the course code input field | +| `selected-filters`, `filter-tag` | Style for filter display and remove buttons | + +--- + +## Notes + +- The search is not manually triggered β€” it uses state change and `useEffect` in the parent. +- Enter key behavior is managed to blur the input but not submit anything. +- All dropdown filters support clearing and partial selection. diff --git a/frontend/docs/SelectedFilters.md b/frontend/docs/SelectedFilters.md new file mode 100644 index 0000000..84a61be --- /dev/null +++ b/frontend/docs/SelectedFilters.md @@ -0,0 +1,98 @@ +# Component: `` + +## Purpose + +Displays all currently applied filters (semesters, prerequisites, and requirement filters) as removable tags. Dynamically groups requirement tags by fully-selected groups and formats them visually. It provides a quick summary of the current filtering criteria. + +--- +## Instructions +The selected filters section appears just above the course table and displays all active filters applied through the dropdowns or search bar. There are different types: +- Requirement Filters: Each selected requirement appears as a colored tag based on the associated major (e.g., CS, IS, BA, BS). These tags show a shortened version of the requirement path, displaying only the last two levels for clarity. +- Offered Semester Filters: Selected semesters (e.g., F24, S25) are displayed as gray tags. These reflect your choices from the "Offered" dropdown column in the table header. +- Pre-req Filters: If you selected "No-Pre Reqs" or "Only with Pre-Req"s in the pre-req column, these will be displayed as gray tags. + +These filters will appear on the search bar section: +- Department dropdown +- Course code input
+They will appear as tags directly below the search bar. These appear as black and indicate the current department and course being searched. + +
+To remove these filters: click the Γ— button on any tag to remove that specific filter. The course table will update immediately to reflect the change.
+Screenshot 2025-04-19 at 2 38 09β€―PM + + +--- + +## Props + +| Prop Name | Type | Required | Description | +|----------------------------|----------|----------|-------------| +| `selectedFilters` | Object | βœ… | Object of selected requirement filters by major | +| `handleFilterChange` | Function | βœ… | Callback to update requirement filters | +| `selectedOfferedSemesters` | Array | βœ… | List of selected semester filters | +| `removeOfferedSemester` | Function | βœ… | Callback to remove a selected semester | +| `noPrereqs` | Boolean/null | βœ… | Current state of the prerequisite filter | +| `removePrereqFilter` | Function | βœ… | Callback to reset the pre-requisite filter | +| `allRequirements` | Object | βœ… | Full list of all requirement options, grouped by major | + +--- + +## Logic Overview + +- Uses `useMemo()` to build a nested tree of requirement groupings from `allRequirements`. +- Displays: + - Semester tags + - Prerequisite filter tag + - Fully selected requirement groups (e.g., "All Analytical Reasoning Requirements") + - Individual requirement filters +- Group tags can be removed as a batch. Individual filters can be removed independently. + +--- + +## Example Usage + +```jsx + +``` + +Use this component underneath the search bar to show users which filters are active and let them remove individual or grouped filters. + +--- + +## Related Components + +- Used in conjunction with `` and `` + +--- + +## Related Tests + +- `SelectedFilters.test.js` β€” tests: + - Displaying correct tags + - Clicking Γ— removes individual and grouped filters + - Conditional rendering + +--- + +## Styling + +| Class | Description | +|-------|-------------| +| `selected-filters` | Main container for all tags | +| `filter-tag` | Individual tag element | +| `filter-tag button` | Remove (Γ—) button styling | + +--- + +## 🚨 Notes + +- Group tags are automatically detected if all values in that group are selected. +- Ensures consistent formatting by trimming the last two parts of the requirement hierarchy (e.g., `GenEd β†’ Foundations β†’ Scientific Reasoning` β†’ `Foundations β†’ Scientific Reasoning`). diff --git a/frontend/docs/SingleSelectDropdown.md b/frontend/docs/SingleSelectDropdown.md new file mode 100644 index 0000000..c38dff7 --- /dev/null +++ b/frontend/docs/SingleSelectDropdown.md @@ -0,0 +1,84 @@ +# Component: `` + +## Purpose + +Provides a simple dropdown component for selecting a single value from a list of options. Used in the GenEd interface to filter by pre-requisite availability. + +--- +## Instructions +This is used exclusively in the Pre-req column of the course table. It allows you to filter courses based on whether or not they have prerequisites. + +Clicking the dropdown will reveal three options: +- All course - Shows all courses (default) +- Has Pre-reqs – Displays only courses that list prerequisites. +- No Pre-reqs – Displays only courses that do not require any prerequisites. + +Once selected, the course table updates automatically to reflect your choice. Only one option can be selected at a time. +Screenshot 2025-04-19 at 12 37 36β€―PM + + +### Clearing the Filter +To remove the filter, click the dropdown again and select *All Courses*. The full list of courses will reappear. Or, you can also press the X button on the respective filter tag that appears on top of the course table. + +--- + +## Props + +| Prop Name | Type | Required | Description | +|-------------|----------|----------|-------------| +| `options` | Array | βœ… | List of options to display in the dropdown | +| `selected` | String | βœ… | Currently selected value | +| `onChange` | Function | βœ… | Callback triggered when a new value is selected | +| `major` | String | ❌ | Optional identifier used for styling contextually (e.g., for prereq dropdown) | + +--- + +## Logic Overview + +- Internal state tracks whether the dropdown is open +- Uses `useRef` and a `mousedown` listener to close the dropdown when clicking outside +- Renders options with a radio-button-like checkbox UI (only one item can be selected at a time) + +--- + +## Example Usage + +```jsx + setNoPrereqs(value === "with" ? true : value === "without" ? false : null)} + major="prereq" +/> +``` + +Used in the β€œPrerequisites” column of `` to toggle between showing: +- All courses +- Only those with prerequisites +- Only those without prerequisites + +--- + +## Related Tests + +- `SingleSelectDropdown.test.js` – tests for: + - Opening and closing the dropdown + - Selecting an item triggers `onChange` + - Only one item can be selected at a time + +--- + +## Styling + +| Class | Description | +|-------|-------------| +| `dropdown`, `dropdown-btn`, `dropdown-content` | General dropdown styles | +| `dropdown-prereq` | Applied when `major="prereq"` to style differently | +| `dropdown-item` | Individual option style | + +--- + +## Notes + +- The dropdown is not a native `