Repository: icssc/AntAlmanac
Pull request #1222: feat: remove dept label
Author: login: KevinWu098
Created: 2025-04-01T20:15:08Z | Merged: 2025-04-14T21:52:15Z

## Summary
Refactors and simplifies our logic for `deptLabel` and `deptValue` by completing removing `deptLabel` (it doesn't exist in our codebase anymore!)

For context, `deptLabel` is the human-friendly text (e.g. Computer Science) versus `deptValue` (e.g. COMPSCI). In a lot of our codebase, these two values are extremely coupled and are basically not used without each other. This increased complexity and introduced a long-running UI bug:

1. In the video below, I visit https://antalmanac.com/?deptValue=COMPSCI. 
2. It sends me to the `added` page because I'm signed in
    - this should not happen — if params are present, we should be given the `search` page (implemented in this PR)
4. When I visit the `search` page, the results are already populated because `deptValue` is a "required" param
5. When I click the back arrow to return to the search form, you can see that the Department Search Bar appears to be `ALL`
    - this should not happen — this occurs because although `deptValue` is correctly set to `COMPSCI` (more on that below), `deptLabel` was not being set
6. Clicking search again searches for COMPSCI, rather than "ALL"
7. Clicking back and opening the search bar shows that `COMPSCI` is _actually_ the selected value

https://github.com/user-attachments/assets/bc0c2d76-8861-4bea-8301-ee433ea677e1

To fix this problem, we now only use `deptValue` in our codebase for handling departments and only use department labels when rendering the department search bar.

## Test Plan

1. Loading in a URL without params should function as expected (e.g. `added` tab, if signed in)
2. Loading in a URL with params should direct to the search page, with values correctly filled
8. Selecting a department should correctly mark it as a recently searched
9. Using the dept search bar should still work as before

## Issues
Closes #

<!-- [Optional]
## Future Followup
-->



Diff:
diff --git a/apps/antalmanac/src/components/RightPane/CoursePane/CoursePaneRoot.tsx b/apps/antalmanac/src/components/RightPane/CoursePane/CoursePaneRoot.tsx
index 468f5951a1..ab8f16c114 100644
--- a/apps/antalmanac/src/components/RightPane/CoursePane/CoursePaneRoot.tsx
+++ b/apps/antalmanac/src/components/RightPane/CoursePane/CoursePaneRoot.tsx
@@ -14,7 +14,7 @@ import { WebSOC } from '$lib/websoc';
 import { useCoursePaneStore } from '$stores/CoursePaneStore';
 
 export function CoursePaneRoot() {
-    const { key, forceUpdate, searchIsDisplayed, displaySearch, displaySections } = useCoursePaneStore();
+    const { key, forceUpdate, searchFormIsDisplayed, displaySearch, displaySections } = useCoursePaneStore();
 
     const handleSearch = useCallback(() => {
         if (RightPaneStore.formDataIsValid()) {
@@ -56,11 +56,15 @@ export function CoursePaneRoot() {
     return (
         <Box height={'0px'} flexGrow={1}>
             <CoursePaneButtonRow
-                showSearch={!searchIsDisplayed}
+                showSearch={!searchFormIsDisplayed}
                 onDismissSearchResults={displaySearch}
                 onRefreshSearch={refreshSearch}
             />
-            {searchIsDisplayed ? <SearchForm toggleSearch={handleSearch} /> : <CourseRenderPane key={key} id={key} />}
+            {searchFormIsDisplayed ? (
+                <SearchForm toggleSearch={handleSearch} />
+            ) : (
+                <CourseRenderPane key={key} id={key} />
+            )}
         </Box>
     );
 }
diff --git a/apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/AdvancedSearch/AdvancedSearch.tsx b/apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/AdvancedSearch/AdvancedSearch.tsx
index f614724dc4..28d147bf43 100644
--- a/apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/AdvancedSearch/AdvancedSearch.tsx
+++ b/apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/AdvancedSearch/AdvancedSearch.tsx
@@ -43,10 +43,6 @@ class AdvancedSearch extends PureComponent<AdvancedSearchProps, AdvancedSearchSt
         const formData = RightPaneStore.getFormData();
         const defaultFormData = RightPaneStore.getDefaultFormData();
         for (const [key, value] of Object.entries(formData)) {
-            if (key === 'deptLabel' || key === 'deptValue') {
-                continue;
-            }
-
             if (defaultFormData[key] != value) {
                 advanced = true;
                 break;
@@ -69,10 +65,6 @@ class AdvancedSearch extends PureComponent<AdvancedSearchProps, AdvancedSearchSt
 
         const formData = RightPaneStore.getFormData();
         for (const key of Object.keys(formData)) {
-            if (key === 'deptLabel' || key === 'deptValue') {
-                continue;
-            }
-
             urlParam.delete(key);
         }
 
diff --git a/apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/DepartmentSearchBar/DepartmentSearchBar.tsx b/apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/DepartmentSearchBar/DepartmentSearchBar.tsx
new file mode 100644
index 0000000000..42a05c84f1
--- /dev/null
+++ b/apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/DepartmentSearchBar/DepartmentSearchBar.tsx
@@ -0,0 +1,98 @@
+import { Autocomplete, Box, TextField } from '@mui/material';
+import { useCallback, useEffect, useState } from 'react';
+
+import { DEPARTMENT_MAP } from '$components/RightPane/CoursePane/SearchForm/DepartmentSearchBar/constants';
+import RightPaneStore from '$components/RightPane/RightPaneStore';
+import { getLocalStorageRecentlySearched, setLocalStorageRecentlySearched } from '$lib/localStorage';
+
+const options = Object.keys(DEPARTMENT_MAP);
+
+const parseLocalStorageRecentlySearched = (): string[] => {
+    try {
+        const data = JSON.parse(getLocalStorageRecentlySearched() ?? '[]');
+
+        if (!Array.isArray(data)) {
+            return [];
+        }
+
+        if (data.every((x) => typeof x === 'string')) {
+            return data;
+        }
+
+        return [];
+    } catch (e) {
+        console.error('An error occurred:', e);
+        return [];
+    }
+};
+
+export function DepartmentSearchBar() {
+    const [value, setValue] = useState(() => RightPaneStore.getFormData().deptValue);
+    const [recentSearches, setRecentSearches] = useState<typeof options>(() => parseLocalStorageRecentlySearched());
+
+    const resetField = useCallback(() => {
+        setValue(() => RightPaneStore.getFormData().deptValue);
+    }, []);
+
+    const handleChange = useCallback(
+        (_: unknown, option: string | null) => {
+            const newValue = option ?? options[0]; // options[0] corresponds to `ALL`
+
+            setValue(newValue);
+            RightPaneStore.updateFormValue('deptValue', newValue);
+
+            const stateObj = { url: 'url' };
+            const url = new URL(window.location.href);
+            const urlParam = new URLSearchParams(url.search);
+
+            urlParam.delete('deptValue');
+
+            if (newValue != 'ALL') {
+                urlParam.append('deptValue', newValue);
+            }
+            const param = urlParam.toString();
+            const new_url = `${param.trim() ? '?' : ''}${param}`;
+            history.replaceState(stateObj, 'url', '/' + new_url);
+
+            if (newValue === 'ALL') return;
+
+            recentSearches.includes(newValue)
+                ? setRecentSearches((prev) =>
+                      prev.sort((a, b) => {
+                          return a === newValue ? -1 : b === newValue ? 1 : 0;
+                      })
+                  )
+                : setRecentSearches((prev) => [newValue, ...prev].slice(0, 5));
+        },
+        [recentSearches]
+    );
+
+    useEffect(() => {
+        RightPaneStore.on('formReset', resetField);
+
+        return () => {
+            RightPaneStore.off('formReset', resetField);
+        };
+    }, [resetField]);
+
+    useEffect(() => {
+        setLocalStorageRecentlySearched(JSON.stringify(recentSearches));
+    }, [recentSearches]);
+
+    return (
+        <Box sx={{ flexGrow: 1, width: '50%' }}>
+            <Autocomplete
+                value={value}
+                options={Array.from(new Set<string>([...recentSearches, ...options]))}
+                autoHighlight={true}
+                openOnFocus={true}
+                getOptionLabel={(option) => DEPARTMENT_MAP[option.toUpperCase() as keyof typeof DEPARTMENT_MAP]}
+                onChange={handleChange}
+                includeInputInList={true}
+                noOptionsText="No departments match the search"
+                groupBy={(option) => (recentSearches.includes(option) ? 'Recently Searched' : 'Departments')}
+                renderInput={(params) => <TextField {...params} label="Department" variant="standard" />}
+            />
+        </Box>
+    );
+}
diff --git a/apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/DepartmentSearchBar/constants.ts b/apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/DepartmentSearchBar/constants.ts
new file mode 100644
index 0000000000..fe91e7c718
--- /dev/null
+++ b/apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/DepartmentSearchBar/constants.ts
@@ -0,0 +1,144 @@
+export const DEPARTMENT_MAP = {
+    ALL: 'ALL: Include All Departments',
+    'AC ENG': 'AC ENG: Academic English and ESL',
+    AFAM: 'AFAM: African American Studies',
+    ANATOMY: 'ANATOMY: Anatomy and Neurobiology',
+    ANESTH: 'ANESTH: Anesthesiology',
+    ANTHRO: 'ANTHRO: Anthropology',
+    ARABIC: 'ARABIC: Arabic',
+    ARMN: 'ARMN: Armenian',
+    ART: 'ART: Art',
+    'ART HIS': 'ART HIS: Art History',
+    ARTS: 'ARTS: Arts',
+    ARTSHUM: 'ARTSHUM: Arts and Humanities',
+    ASIANAM: 'ASIANAM: Asian American Studies',
+    BANA: 'BANA: Business Analytics',
+    BATS: 'BATS: Biomedical and Translational Science',
+    'BIO SCI': 'BIO SCI: Biological Sciences',
+    BIOCHEM: 'BIOCHEM: Biological Chemistry',
+    BME: 'BME: Biomedical Engineering',
+    CAMPREC: 'CAMPREC: Campus Recreation',
+    CBE: 'CBE: Chemical and Biomolecular Engineering',
+    CEM: 'CEM: Community and Environmental Medicine',
+    'CHC/LAT': 'CHC/LAT: Chicano Latino',
+    CHEM: 'CHEM: Chemistry',
+    CHINESE: 'CHINESE: Chinese',
+    CLASSIC: 'CLASSIC: Classics',
+    'CLT&THY': 'CLT&THY: Culture & Theory',
+    COGS: 'COGS: Cognitive Sciences',
+    'COM LIT': 'COM LIT: Comparative Literature',
+    COMPSCI: 'COMPSCI: Computer Science',
+    CRITISM: 'CRITISM: Criticism',
+    'CRM/LAW': 'CRM/LAW: Criminology, Law and Society',
+    CSE: 'CSE: Computer Science and Engineering',
+    DANCE: 'DANCE: Dance',
+    DERM: 'DERM: Dermatology',
+    'DEV BIO': 'DEV BIO: Developmental and Cell Biology',
+    DRAMA: 'DRAMA: Drama',
+    EARTHSS: 'EARTHSS: Earth System Science',
+    EAS: 'EAS: East Asian Studies',
+    'ECO EVO': 'ECO EVO: Ecology and Evolutionary Biology',
+    ECON: 'ECON: Economics',
+    ECPS: 'ECPS: Embedded and Cyber-Physical Systems',
+    'ED AFF': 'ED AFF: Educational Affairs (Sch of Med)',
+    EDUC: 'EDUC: Education',
+    EECS: 'EECS: Electrical Engineering & Computer Science',
+    EHS: 'EHS: Environmental Health Sciences',
+    ENGLISH: 'ENGLISH: English',
+    ENGR: 'ENGR: Engineering',
+    ENGRCEE: 'ENGRCEE: Engineering, Civil and Environmental',
+    ENGRMAE: 'ENGRMAE: Engineering, Mechanical and Aerospace',
+    EPIDEM: 'EPIDEM: Epidemiology',
+    'ER MED': 'ER MED: Emergency Medicine',
+    'EURO ST': 'EURO ST: European Studies',
+    'FAM MED': 'FAM MED: Family Medicine',
+    FIN: 'FIN: Finance',
+    'FLM&MDA': 'FLM&MDA: Film and Media Studies',
+    FRENCH: 'FRENCH: French',
+    GDIM: 'GDIM: Game Design and Interactive Media',
+    'GEN&SEX': 'GEN&SEX: Gender and Sexuality Studies',
+    GERMAN: 'GERMAN: German',
+    'GLBL ME': 'GLBL ME: Global Middle East Studies',
+    GLBLCLT: 'GLBLCLT: Global Cultures',
+    GREEK: 'GREEK: Greek',
+    HEBREW: 'HEBREW: Hebrew',
+    HINDI: 'HINDI: Hindi',
+    HISTORY: 'HISTORY: History',
+    HUMAN: 'HUMAN: Humanities',
+    HUMARTS: 'HUMARTS: Humanities and Arts',
+    'I&C SCI': 'I&C SCI: Information and Computer Science',
+    IN4MATX: 'IN4MATX: Informatics',
+    INNO: 'INNO: Masters of Innovation and Entrepreneurship',
+    'INT MED': 'INT MED: Internal Medicine',
+    'INTL ST': 'INTL ST: International Studies',
+    IRAN: 'IRAN: Iranian',
+    ITALIAN: 'ITALIAN: Italian',
+    JAPANSE: 'JAPANSE: Japanese',
+    KOREAN: 'KOREAN: Korean',
+    LATIN: 'LATIN: Latin',
+    LAW: 'LAW: Law',
+    'LIT JRN': 'LIT JRN: Literary Journalism',
+    LPS: 'LPS: Logic and Philosophy of Science',
+    LSCI: 'LSCI: Language Science',
+    'M&MG': 'M&MG: Microbiology and Molecular Genetics',
+    MATH: 'MATH: Mathematics',
+    MED: 'MED: Medicine',
+    'MED ED': 'MED ED: Medical Education',
+    'MED HUM': 'MED HUM: Medical Humanities',
+    MGMT: 'MGMT: Management',
+    'MGMT EP': 'MGMT EP: Executive MBA',
+    'MGMT FE': 'MGMT FE: Fully Employed MBA',
+    'MGMT HC': 'MGMT HC: Health Care MBA',
+    MGMTMBA: 'MGMTMBA: Management MBA',
+    MGMTPHD: 'MGMTPHD: Management PhD',
+    'MIC BIO': 'MIC BIO: Microbiology',
+    'MOL BIO': 'MOL BIO: Molecular Biology and Biochemistry',
+    MPAC: 'MPAC: Accounting',
+    MSE: 'MSE: Materials Science and Engineering',
+    MUSIC: 'MUSIC: Music',
+    'NET SYS': 'NET SYS: Networked Systems',
+    NEURBIO: 'NEURBIO: Neurobiology and Behavior',
+    NEUROL: 'NEUROL: Neurology',
+    'NUR SCI': 'NUR SCI: Nursing Science',
+    'OB/GYN': 'OB/GYN: Obstetrics and Gynecology',
+    OPHTHAL: 'OPHTHAL: Ophthalmology',
+    PATH: 'PATH: Pathology and Laboratory Medicine',
+    'PED GEN': 'PED GEN: Pediatrics Genetics',
+    PEDS: 'PEDS: Pediatrics',
+    PERSIAN: 'PERSIAN: Persian',
+    PHARM: 'PHARM: Medical Pharmacology',
+    PHILOS: 'PHILOS: Philosophy',
+    PHRMSCI: 'PHRMSCI: Pharmaceutical Sciences',
+    'PHY SCI': 'PHY SCI: Physical Science',
+    PHYSICS: 'PHYSICS: Physics',
+    PHYSIO: 'PHYSIO: Physiology and Biophysics',
+    PLASTIC: 'PLASTIC: Plastic Surgery',
+    'PM&R': 'PM&R: Physical Medicine and Rehabilitation',
+    'POL SCI': 'POL SCI: Political Science',
+    PORTUG: 'PORTUG: Portuguese',
+    PSCI: 'PSCI: Psychological Science',
+    PSYCH: 'PSYCH: Psychology',
+    'PUB POL': 'PUB POL: Public Policy',
+    PUBHLTH: 'PUBHLTH: Public Health',
+    RADIO: 'RADIO: Radiology',
+    'REL STD': 'REL STD: Religious Studies',
+    ROTC: "ROTC: Reserve Officers' Training Corps",
+    RUSSIAN: 'RUSSIAN: Russian',
+    'SOC SCI': 'SOC SCI: Social Science',
+    SOCECOL: 'SOCECOL: Social Ecology',
+    SOCIOL: 'SOCIOL: Sociology',
+    SPANISH: 'SPANISH: Spanish',
+    SPPS: 'SPPS: Social Policy & Public Service',
+    STATS: 'STATS: Statistics',
+    SURGERY: 'SURGERY: Surgery',
+    SWE: 'SWE: Software Engineering',
+    TAGALOG: 'TAGALOG: Tagalog',
+    TOX: 'TOX: Toxicology',
+    UCDC: 'UCDC: UC Washington DC',
+    'UNI AFF': 'UNI AFF: University Affairs',
+    'UNI STU': 'UNI STU: University Studies',
+    UPPP: 'UPPP: Urban Planning and Public Policy',
+    VIETMSE: 'VIETMSE: Vietnamese',
+    'VIS STD': 'VIS STD: Visual Studies',
+    WRITING: 'WRITING: Writing',
+} as const;
diff --git a/apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/DeptSearchBar/DeptSearchBar.tsx b/apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/DeptSearchBar/DeptSearchBar.tsx
deleted file mode 100644
index 4ad2087ac9..0000000000
--- a/apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/DeptSearchBar/DeptSearchBar.tsx
+++ /dev/null
@@ -1,175 +0,0 @@
-import { TextField } from '@material-ui/core';
-import { withStyles } from '@material-ui/core/styles';
-import { ClassNameMap } from '@material-ui/core/styles/withStyles';
-import { Autocomplete } from '@material-ui/lab';
-import { ChangeEvent, PureComponent } from 'react';
-
-import RightPaneStore from '../../../RightPaneStore';
-
-import depts from './depts';
-
-import { getLocalStorageFavorites, setLocalStorageFavorites } from '$lib/localStorage';
-
-const style = {
-    formControl: {
-        flexGrow: 1,
-        width: '50%',
-    },
-};
-
-const options = depts.map((dept) => {
-    return {
-        ...dept,
-        isFavorite: false,
-    };
-});
-
-interface DeptSearchBarProps {
-    classes: ClassNameMap;
-}
-
-interface Department {
-    deptLabel: string;
-    deptValue: string;
-    isFavorite: boolean;
-}
-
-interface DeptSearchBarState {
-    value: Department;
-    favorites: Department[];
-}
-
-class DeptSearchBar extends PureComponent<DeptSearchBarProps, DeptSearchBarState> {
-    updatedeptLabelAndGetFormData() {
-        RightPaneStore.updateFormValue('deptLabel', RightPaneStore.getUrlDeptLabel());
-        RightPaneStore.updateFormValue('deptValue', RightPaneStore.getUrlDeptValue());
-        return RightPaneStore.getFormData().deptLabel;
-    }
-
-    updatedeptValueAndGetFormData() {
-        RightPaneStore.updateFormValue('deptValue', RightPaneStore.getUrlDeptValue());
-        return RightPaneStore.getFormData().deptValue;
-    }
-
-    getDeptValue() {
-        return RightPaneStore.getUrlDeptValue().trim()
-            ? this.updatedeptValueAndGetFormData()
-            : RightPaneStore.getFormData().deptValue;
-    }
-
-    getDeptLabel() {
-        return RightPaneStore.getUrlDeptLabel().trim()
-            ? this.updatedeptLabelAndGetFormData()
-            : RightPaneStore.getFormData().deptLabel;
-    }
-
-    constructor(props: DeptSearchBarProps) {
-        super(props);
-
-        let favorites: Department[] = [];
-        if (typeof Storage !== 'undefined') {
-            const locallyStoredFavorites = getLocalStorageFavorites();
-            favorites = locallyStoredFavorites != null ? JSON.parse(locallyStoredFavorites) : [];
-        }
-        this.state = {
-            value: {
-                deptValue: this.getDeptValue(),
-                deptLabel: this.getDeptLabel(),
-                isFavorite: false,
-            },
-            favorites: favorites,
-        };
-    }
-
-    componentDidMount() {
-        RightPaneStore.on('formReset', this.resetField);
-    }
-
-    componentWillUnmount() {
-        RightPaneStore.removeListener('formReset', this.resetField);
-    }
-
-    resetField = () => {
-        const formData = RightPaneStore.getFormData();
-
-        this.setState({
-            value: {
-                deptValue: formData.deptValue,
-                deptLabel: formData.deptLabel,
-                isFavorite: false,
-            },
-        });
-    };
-
-    compareValues = (option: Department, value: Department) => {
-        return option.deptValue === value.deptValue;
-    };
-
-    handleSetDept = (event: ChangeEvent<unknown>, newDept: Department | null) => {
-        const setDeptValue = newDept === null ? options[0] : newDept;
-
-        this.setState({ value: setDeptValue });
-        RightPaneStore.updateFormValue('deptValue', setDeptValue.deptValue);
-        RightPaneStore.updateFormValue('deptLabel', setDeptValue.deptLabel);
-
-        const stateObj = { url: 'url' };
-        const url = new URL(window.location.href);
-        const urlParam = new URLSearchParams(url.search);
-        urlParam.delete('deptLabel');
-        urlParam.delete('deptValue');
-        if (
-            setDeptValue.deptValue &&
-            setDeptValue.deptValue != 'ALL' &&
-            setDeptValue.deptLabel &&
-            setDeptValue.deptLabel != 'ALL: Include All Departments'
-        ) {
-            urlParam.append('deptLabel', setDeptValue.deptLabel);
-            urlParam.append('deptValue', setDeptValue.deptValue);
-        }
-        const param = urlParam.toString();
-        const new_url = `${param.trim() ? '?' : ''}${param}`;
-        history.replaceState(stateObj, 'url', '/' + new_url);
-
-        if (newDept == null || newDept.deptValue === 'ALL') return;
-
-        const favorites = this.state.favorites;
-        let updatedFavorites = [...favorites];
-
-        if (favorites.filter((favorite) => newDept.deptValue === favorite.deptValue).length > 0) {
-            updatedFavorites.sort((a, b) => {
-                return a.deptValue === newDept.deptValue ? -1 : b.deptValue === newDept.deptValue ? 1 : 0;
-            });
-        } else {
-            updatedFavorites = [{ ...newDept, isFavorite: true }].concat(favorites);
-            if (updatedFavorites.length > 5) updatedFavorites.pop();
-        }
-        this.setState({ favorites: updatedFavorites });
-        setLocalStorageFavorites(JSON.stringify(updatedFavorites));
-    };
-
-    render() {
-        const { classes } = this.props;
-
-        return (
-            <div className={classes.formControl}>
-                <Autocomplete
-                    value={this.state.value}
-                    options={this.state.favorites.concat(options)}
-                    autoHighlight={true}
-                    openOnFocus={true}
-                    getOptionSelected={this.compareValues}
-                    getOptionLabel={(option) => option.deptLabel}
-                    onChange={this.handleSetDept}
-                    includeInputInList={true}
-                    noOptionsText="No departments match the search"
-                    groupBy={(dept) => (dept.isFavorite ? 'Recent Departments' : 'Departments')}
-                    renderInput={(params) => (
-                        <TextField {...params} label="Department" type="search" InputLabelProps={{ shrink: true }} />
-                    )}
-                />
-            </div>
-        );
-    }
-}
-
-export default withStyles(style)(DeptSearchBar);
diff --git a/apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/DeptSearchBar/MobileDeptSelector.tsx b/apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/DeptSearchBar/MobileDeptSelector.tsx
deleted file mode 100644
index bda0ebc36e..0000000000
--- a/apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/DeptSearchBar/MobileDeptSelector.tsx
+++ /dev/null
@@ -1,59 +0,0 @@
-import FormControl from '@material-ui/core/FormControl';
-import InputLabel from '@material-ui/core/InputLabel';
-import MenuItem from '@material-ui/core/MenuItem';
-import Select from '@material-ui/core/Select';
-import { withStyles } from '@material-ui/core/styles';
-import { ClassNameMap } from '@material-ui/core/styles/withStyles';
-import { ChangeEvent, PureComponent } from 'react';
-
-import RightPaneStore from '../../../RightPaneStore';
-
-import depts from './depts';
-
-const style = {
-    formControl: {
-        flexGrow: 1,
-        marginRight: 15,
-        width: '50%',
-    },
-};
-
-interface MobileDeptSelectorProps {
-    classes: ClassNameMap;
-}
-
-interface MobileDeptSelectorState {
-    deptLabel: string | unknown;
-}
-
-class MobileDeptSelector extends PureComponent<MobileDeptSelectorProps, MobileDeptSelectorState> {
-    state = {
-        deptLabel: RightPaneStore.getFormData().deptLabel,
-    };
-
-    handleChange = (event: ChangeEvent<{ name?: string | undefined; value: unknown }>) => {
-        this.setState({ deptLabel: event.target.value });
-        RightPaneStore.updateFormValue('deptLabel', event.target.value as string);
-    };
-
-    render() {
-        const { classes } = this.props;
-
-        return (
-            <FormControl className={classes.formControl}>
-                <InputLabel>Department</InputLabel>
-                <Select value={this.state.deptLabel} onChange={this.handleChange} fullWidth>
-                    {depts.map((dept) => {
-                        return (
-                            <MenuItem key={dept.deptValue} value={dept.deptValue}>
-                                {dept.deptLabel}
-                            </MenuItem>
-                        );
-                    })}
-                </Select>
-            </FormControl>
-        );
-    }
-}
-
-export default withStyles(style)(MobileDeptSelector);
diff --git a/apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/DeptSearchBar/depts.ts b/apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/DeptSearchBar/depts.ts
deleted file mode 100644
index 9b7739f552..0000000000
--- a/apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/DeptSearchBar/depts.ts
+++ /dev/null
@@ -1,144 +0,0 @@
-export default [
-    { deptLabel: 'ALL: Include All Departments', deptValue: 'ALL' },
-    { deptLabel: 'AC ENG: Academic English and ESL', deptValue: 'AC ENG' },
-    { deptLabel: 'AFAM: African American Studies', deptValue: 'AFAM' },
-    { deptLabel: 'ANATOMY: Anatomy and Neurobiology', deptValue: 'ANATOMY' },
-    { deptLabel: 'ANESTH: Anesthesiology', deptValue: 'ANESTH' },
-    { deptLabel: 'ANTHRO: Anthropology', deptValue: 'ANTHRO' },
-    { deptLabel: 'ARABIC: Arabic', deptValue: 'ARABIC' },
-    { deptLabel: 'ARMN: Armenian', deptValue: 'ARMN' },
-    { deptLabel: 'ART: Art', deptValue: 'ART' },
-    { deptLabel: 'ART HIS: Art History', deptValue: 'ART HIS' },
-    { deptLabel: 'ARTS: Arts', deptValue: 'ARTS' },
-    { deptLabel: 'ARTSHUM: Arts and Humanities', deptValue: 'ARTSHUM' },
-    { deptLabel: 'ASIANAM: Asian American Studies', deptValue: 'ASIANAM' },
-    { deptLabel: 'BANA: Business Analytics', deptValue: 'BANA' },
-    { deptLabel: 'BATS: Biomedical and Translational Science', deptValue: 'BATS' },
-    { deptLabel: 'BIO SCI: Biological Sciences', deptValue: 'BIO SCI' },
-    { deptLabel: 'BIOCHEM: Biological Chemistry', deptValue: 'BIOCHEM' },
-    { deptLabel: 'BME: Biomedical Engineering', deptValue: 'BME' },
-    { deptLabel: 'CAMPREC: Campus Recreation', deptValue: 'CAMPREC' },
-    { deptLabel: 'CBE: Chemical and Biomolecular Engineering', deptValue: 'CBE' },
-    { deptLabel: 'CEM: Community and Environmental Medicine', deptValue: 'CEM' },
-    { deptLabel: 'CHC/LAT: Chicano Latino', deptValue: 'CHC/LAT' },
-    { deptLabel: 'CHEM: Chemistry', deptValue: 'CHEM' },
-    { deptLabel: 'CHINESE: Chinese', deptValue: 'CHINESE' },
-    { deptLabel: 'CLASSIC: Classics', deptValue: 'CLASSIC' },
-    { deptLabel: 'CLT&THY: Culture & Theory', deptValue: 'CLT&THY' },
-    { deptLabel: 'COGS: Cognitive Sciences', deptValue: 'COGS' },
-    { deptLabel: 'COM LIT: Comparative Literature', deptValue: 'COM LIT' },
-    { deptLabel: 'COMPSCI: Computer Science', deptValue: 'COMPSCI' },
-    { deptLabel: 'CRITISM: Criticism', deptValue: 'CRITISM' },
-    { deptLabel: 'CRM/LAW: Criminology, Law and Society', deptValue: 'CRM/LAW' },
-    { deptLabel: 'CSE: Computer Science and Engineering', deptValue: 'CSE' },
-    { deptLabel: 'DANCE: Dance', deptValue: 'DANCE' },
-    { deptLabel: 'DERM: Dermatology', deptValue: 'DERM' },
-    { deptLabel: 'DEV BIO: Developmental and Cell Biology', deptValue: 'DEV BIO' },
-    { deptLabel: 'DRAMA: Drama', deptValue: 'DRAMA' },
-    { deptLabel: 'EARTHSS: Earth System Science', deptValue: 'EARTHSS' },
-    { deptLabel: 'EAS: East Asian Studies', deptValue: 'EAS' },
-    { deptLabel: 'ECO EVO: Ecology and Evolutionary Biology', deptValue: 'ECO EVO' },
-    { deptLabel: 'ECON: Economics', deptValue: 'ECON' },
-    { deptLabel: 'ECPS: Embedded and Cyber-Physical Systems', deptValue: 'ECPS' },
-    { deptLabel: 'ED AFF: Educational Affairs (Sch of Med)', deptValue: 'ED AFF' },
-    { deptLabel: 'EDUC: Education', deptValue: 'EDUC' },
-    { deptLabel: 'EECS: Electrical Engineering & Computer Science', deptValue: 'EECS' },
-    { deptLabel: 'EHS: Environmental Health Sciences', deptValue: 'EHS' },
-    { deptLabel: 'ENGLISH: English', deptValue: 'ENGLISH' },
-    { deptLabel: 'ENGR: Engineering', deptValue: 'ENGR' },
-    { deptLabel: 'ENGRCEE: Engineering, Civil and Environmental', deptValue: 'ENGRCEE' },
-    { deptLabel: 'ENGRMAE: Engineering, Mechanical and Aerospace', deptValue: 'ENGRMAE' },
-    { deptLabel: 'EPIDEM: Epidemiology', deptValue: 'EPIDEM' },
-    { deptLabel: 'ER MED: Emergency Medicine', deptValue: 'ER MED' },
-    { deptLabel: 'EURO ST: European Studies', deptValue: 'EURO ST' },
-    { deptLabel: 'FAM MED: Family Medicine', deptValue: 'FAM MED' },
-    { deptLabel: 'FIN: Finance', deptValue: 'FIN' },
-    { deptLabel: 'FLM&MDA: Film and Media Studies', deptValue: 'FLM&MDA' },
-    { deptLabel: 'FRENCH: French', deptValue: 'FRENCH' },
-    { deptLabel: 'GDIM: Game Design and Interactive Media', deptValue: 'GDIM' },
-    { deptLabel: 'GEN&SEX: Gender and Sexuality Studies', deptValue: 'GEN&SEX' },
-    { deptLabel: 'GERMAN: German', deptValue: 'GERMAN' },
-    { deptLabel: 'GLBL ME: Global Middle East Studies', deptValue: 'GLBL ME' },
-    { deptLabel: 'GLBLCLT: Global Cultures', deptValue: 'GLBLCLT' },
-    { deptLabel: 'GREEK: Greek', deptValue: 'GREEK' },
-    { deptLabel: 'HEBREW: Hebrew', deptValue: 'HEBREW' },
-    { deptLabel: 'HINDI: Hindi', deptValue: 'HINDI' },
-    { deptLabel: 'HISTORY: History', deptValue: 'HISTORY' },
-    { deptLabel: 'HUMAN: Humanities', deptValue: 'HUMAN' },
-    { deptLabel: 'HUMARTS: Humanities and Arts', deptValue: 'HUMARTS' },
-    { deptLabel: 'I&C SCI: Information and Computer Science', deptValue: 'I&C SCI' },
-    { deptLabel: 'IN4MATX: Informatics', deptValue: 'IN4MATX' },
-    { deptLabel: 'INNO: Masters of Innovation and Entrepreneurship', deptValue: 'INNO' },
-    { deptLabel: 'INT MED: Internal Medicine', deptValue: 'INT MED' },
-    { deptLabel: 'INTL ST: International Studies', deptValue: 'INTL ST' },
-    { deptLabel: 'IRAN: Iranian', deptValue: 'IRAN' },
-    { deptLabel: 'ITALIAN: Italian', deptValue: 'ITALIAN' },
-    { deptLabel: 'JAPANSE: Japanese', deptValue: 'JAPANSE' },
-    { deptLabel: 'KOREAN: Korean', deptValue: 'KOREAN' },
-    { deptLabel: 'LATIN: Latin', deptValue: 'LATIN' },
-    { deptLabel: 'LAW: Law', deptValue: 'LAW' },
-    { deptLabel: 'LIT JRN: Literary Journalism', deptValue: 'LIT JRN' },
-    { deptLabel: 'LPS: Logic and Philosophy of Science', deptValue: 'LPS' },
-    { deptLabel: 'LSCI: Language Science', deptValue: 'LSCI' },
-    { deptLabel: 'M&MG: Microbiology and Molecular Genetics', deptValue: 'M&MG' },
-    { deptLabel: 'MATH: Mathematics', deptValue: 'MATH' },
-    { deptLabel: 'MED: Medicine', deptValue: 'MED' },
-    { deptLabel: 'MED ED: Medical Education', deptValue: 'MED ED' },
-    { deptLabel: 'MED HUM: Medical Humanities', deptValue: 'MED HUM' },
-    { deptLabel: 'MGMT: Management', deptValue: 'MGMT' },
-    { deptLabel: 'MGMT EP: Executive MBA', deptValue: 'MGMT EP' },
-    { deptLabel: 'MGMT FE: Fully Employed MBA', deptValue: 'MGMT FE' },
-    { deptLabel: 'MGMT HC: Health Care MBA', deptValue: 'MGMT HC' },
-    { deptLabel: 'MGMTMBA: Management MBA', deptValue: 'MGMTMBA' },
-    { deptLabel: 'MGMTPHD: Management PhD', deptValue: 'MGMTPHD' },
-    { deptLabel: 'MIC BIO: Microbiology', deptValue: 'MIC BIO' },
-    { deptLabel: 'MOL BIO: Molecular Biology and Biochemistry', deptValue: 'MOL BIO' },
-    { deptLabel: 'MPAC: Accounting', deptValue: 'MPAC' },
-    { deptLabel: 'MSE: Materials Science and Engineering', deptValue: 'MSE' },
-    { deptLabel: 'MUSIC: Music', deptValue: 'MUSIC' },
-    { deptLabel: 'NET SYS: Networked Systems', deptValue: 'NET SYS' },
-    { deptLabel: 'NEURBIO: Neurobiology and Behavior', deptValue: 'NEURBIO' },
-    { deptLabel: 'NEUROL: Neurology', deptValue: 'NEUROL' },
-    { deptLabel: 'NUR SCI: Nursing Science', deptValue: 'NUR SCI' },
-    { deptLabel: 'OB/GYN: Obstetrics and Gynecology', deptValue: 'OB/GYN' },
-    { deptLabel: 'OPHTHAL: Ophthalmology', deptValue: 'OPHTHAL' },
-    { deptLabel: 'PATH: Pathology and Laboratory Medicine', deptValue: 'PATH' },
-    { deptLabel: 'PED GEN: Pediatrics Genetics', deptValue: 'PED GEN' },
-    { deptLabel: 'PEDS: Pediatrics', deptValue: 'PEDS' },
-    { deptLabel: 'PERSIAN: Persian', deptValue: 'PERSIAN' },
-    { deptLabel: 'PHARM: Medical Pharmacology', deptValue: 'PHARM' },
-    { deptLabel: 'PHILOS: Philosophy', deptValue: 'PHILOS' },
-    { deptLabel: 'PHRMSCI: Pharmaceutical Sciences', deptValue: 'PHRMSCI' },
-    { deptLabel: 'PHY SCI: Physical Science', deptValue: 'PHY SCI' },
-    { deptLabel: 'PHYSICS: Physics', deptValue: 'PHYSICS' },
-    { deptLabel: 'PHYSIO: Physiology and Biophysics', deptValue: 'PHYSIO' },
-    { deptLabel: 'PLASTIC: Plastic Surgery', deptValue: 'PLASTIC' },
-    { deptLabel: 'PM&R: Physical Medicine and Rehabilitation', deptValue: 'PM&R' },
-    { deptLabel: 'POL SCI: Political Science', deptValue: 'POL SCI' },
-    { deptLabel: 'PORTUG: Portuguese', deptValue: 'PORTUG' },
-    { deptLabel: 'PSCI: Psychological Science', deptValue: 'PSCI' },
-    { deptLabel: 'PSYCH: Psychology', deptValue: 'PSYCH' },
-    { deptLabel: 'PUB POL: Public Policy', deptValue: 'PUB POL' },
-    { deptLabel: 'PUBHLTH: Public Health', deptValue: 'PUBHLTH' },
-    { deptLabel: 'RADIO: Radiology', deptValue: 'RADIO' },
-    { deptLabel: 'REL STD: Religious Studies', deptValue: 'REL STD' },
-    { deptLabel: "ROTC: Reserve Officers' Training Corps", deptValue: 'ROTC' },
-    { deptLabel: 'RUSSIAN: Russian', deptValue: 'RUSSIAN' },
-    { deptLabel: 'SOC SCI: Social Science', deptValue: 'SOC SCI' },
-    { deptLabel: 'SOCECOL: Social Ecology', deptValue: 'SOCECOL' },
-    { deptLabel: 'SOCIOL: Sociology', deptValue: 'SOCIOL' },
-    { deptLabel: 'SPANISH: Spanish', deptValue: 'SPANISH' },
-    { deptLabel: 'SPPS: Social Policy & Public Service', deptValue: 'SPPS' },
-    { deptLabel: 'STATS: Statistics', deptValue: 'STATS' },
-    { deptLabel: 'SURGERY: Surgery', deptValue: 'SURGERY' },
-    { deptLabel: 'SWE: Software Engineering', deptValue: 'SWE' },
-    { deptLabel: 'TAGALOG: Tagalog', deptValue: 'TAGALOG' },
-    { deptLabel: 'TOX: Toxicology', deptValue: 'TOX' },
-    { deptLabel: 'UCDC: UC Washington DC', deptValue: 'UCDC' },
-    { deptLabel: 'UNI AFF: University Affairs', deptValue: 'UNI AFF' },
-    { deptLabel: 'UNI STU: University Studies', deptValue: 'UNI STU' },
-    { deptLabel: 'UPPP: Urban Planning and Public Policy', deptValue: 'UPPP' },
-    { deptLabel: 'VIETMSE: Vietnamese', deptValue: 'VIETMSE' },
-    { deptLabel: 'VIS STD: Visual Studies', deptValue: 'VIS STD' },
-    { deptLabel: 'WRITING: Writing', deptValue: 'WRITING' },
-];
diff --git a/apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/FuzzySearch.tsx b/apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/FuzzySearch.tsx
index 195c65c79b..2c2627aa28 100644
--- a/apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/FuzzySearch.tsx
+++ b/apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/FuzzySearch.tsx
@@ -70,12 +70,10 @@ class FuzzySearch extends PureComponent<FuzzySearchProps, FuzzySearchState> {
                 break;
             case emojiMap.DEPARTMENT:
                 RightPaneStore.updateFormValue('deptValue', ident[0]);
-                RightPaneStore.updateFormValue('deptLabel', ident.join(':'));
                 break;
             case emojiMap.COURSE: {
                 const deptValue = ident[0].split(' ').slice(0, -1).join(' ');
                 RightPaneStore.updateFormValue('deptValue', deptValue);
-                RightPaneStore.updateFormValue('deptLabel', deptValue);
                 RightPaneStore.updateFormValue('courseNumber', ident[0].split(' ').slice(-1)[0]);
                 break;
             }
diff --git a/apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/LegacySearch.tsx b/apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/LegacySearch.tsx
index a189da75db..5c8813753b 100644
--- a/apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/LegacySearch.tsx
+++ b/apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/LegacySearch.tsx
@@ -1,10 +1,10 @@
 import { Box, Button } from '@mui/material';
 
-import AdvancedSearch from './AdvancedSearch/AdvancedSearch';
-import CourseNumberSearchBar from './CourseNumberSearchBar';
-import DeptSearchBar from './DeptSearchBar/DeptSearchBar';
-import GESelector from './GESelector';
-import SectionCodeSearchBar from './SectionCodeSearchBar';
+import AdvancedSearch from '$components/RightPane/CoursePane/SearchForm/AdvancedSearch/AdvancedSearch';
+import CourseNumberSearchBar from '$components/RightPane/CoursePane/SearchForm/CourseNumberSearchBar';
+import { DepartmentSearchBar } from '$components/RightPane/CoursePane/SearchForm/DepartmentSearchBar/DepartmentSearchBar';
+import GESelector from '$components/RightPane/CoursePane/SearchForm/GESelector';
+import SectionCodeSearchBar from '$components/RightPane/CoursePane/SearchForm/SectionCodeSearchBar';
 
 interface LegacySearchProps {
     onSubmit: VoidFunction;
@@ -16,7 +16,7 @@ export function LegacySearch({ onSubmit, onReset }: LegacySearchProps) {
         <Box sx={{ display: 'flex', flexDirection: 'column', gap: 0.5 }}>
             <Box>
                 <Box sx={{ display: 'flex', width: '100%', gap: 2 }}>
-                    <DeptSearchBar />
+                    <DepartmentSearchBar />
                     <CourseNumberSearchBar />
                 </Box>
                 <Box sx={{ display: 'flex', width: '100%', gap: 2 }}>
diff --git a/apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/TermSelector.tsx b/apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/TermSelector.tsx
index bc44aaf6a9..8cf9a834dc 100644
--- a/apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/TermSelector.tsx
+++ b/apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/TermSelector.tsx
@@ -51,7 +51,7 @@ function TermSelector(props: TermSelectorProps) {
     return (
         <FormControl fullWidth>
             <InputLabel>Term</InputLabel>
-            <Select value={term} onChange={handleChange}>
+            <Select value={term} onChange={handleChange} fullWidth>
                 {termData.map((term, index) => (
                     <MenuItem key={index} value={term.shortName}>
                         {term.longName}
diff --git a/apps/antalmanac/src/components/RightPane/RightPaneStore.ts b/apps/antalmanac/src/components/RightPane/RightPaneStore.ts
index 9c89088b26..1c7dbe7f82 100644
--- a/apps/antalmanac/src/components/RightPane/RightPaneStore.ts
+++ b/apps/antalmanac/src/components/RightPane/RightPaneStore.ts
@@ -4,7 +4,6 @@ import { getDefaultTerm } from '$lib/termData';
 
 const defaultFormValues: Record<string, string> = {
     deptValue: 'ALL',
-    deptLabel: 'ALL: Include All Departments',
     ge: 'ANY',
     term: getDefaultTerm().shortName,
     courseNumber: '',
@@ -33,7 +32,6 @@ class RightPaneStore extends EventEmitter {
     private urlTermValue: string;
     private urlGEValue: string;
     private urlCourseNumValue: string;
-    private urlDeptLabel: string;
     private urlDeptValue: string;
 
     constructor() {
@@ -47,7 +45,6 @@ class RightPaneStore extends EventEmitter {
         this.urlTermValue = search.get('term') || '';
         this.urlGEValue = search.get('ge') || '';
         this.urlCourseNumValue = search.get('courseNumber') || '';
-        this.urlDeptLabel = search.get('deptLabel') || '';
         this.urlDeptValue = search.get('deptValue') || '';
 
         this.updateFormDataFromURL(search);
@@ -82,7 +79,6 @@ class RightPaneStore extends EventEmitter {
     getUrlTermValue = () => this.urlTermValue;
     getUrlGEValue = () => this.urlGEValue;
     getUrlCourseNumValue = () => this.urlCourseNumValue;
-    getUrlDeptLabel = () => this.urlDeptLabel;
     getUrlDeptValue = () => this.urlDeptValue;
 
     updateFormValue = (field: string, value: string) => {
@@ -101,7 +97,9 @@ class RightPaneStore extends EventEmitter {
 
     formDataIsValid = () => {
         const { ge, deptValue, sectionCode, instructor } = this.formData;
-        return ge !== 'ANY' || deptValue !== 'ALL' || sectionCode !== '' || instructor !== '';
+        return (
+            ge.toUpperCase() !== 'ANY' || deptValue.toUpperCase() !== 'ALL' || sectionCode !== '' || instructor !== ''
+        );
     };
 }
 
diff --git a/apps/antalmanac/src/components/RightPane/SectionTable/SectionTableBody/SectionTableBodyCells/ActionCell.tsx b/apps/antalmanac/src/components/RightPane/SectionTable/SectionTableBody/SectionTableBodyCells/ActionCell.tsx
index 04da31e61f..ee2dd6bc88 100644
--- a/apps/antalmanac/src/components/RightPane/SectionTable/SectionTableBody/SectionTableBodyCells/ActionCell.tsx
+++ b/apps/antalmanac/src/components/RightPane/SectionTable/SectionTableBody/SectionTableBodyCells/ActionCell.tsx
@@ -88,7 +88,7 @@ export function ColorAndDelete(props: ActionProps) {
  * Copying a specific class's link will only copy its course code.
  * If there is random value let in the url, it will interfere with the generated url.
  */
-const fieldsToReset = ['courseCode', 'courseNumber', 'deptLabel', 'deptValue', 'ge', 'term'];
+const fieldsToReset = ['courseCode', 'courseNumber', 'deptValue', 'ge', 'term'];
 
 /**
  * Sections that have not been added to a schedule can be added to a schedule.
diff --git a/apps/antalmanac/src/components/ScheduleManagement/ScheduleManagement.tsx b/apps/antalmanac/src/components/ScheduleManagement/ScheduleManagement.tsx
index 4ea395045e..b3ab30cce7 100644
--- a/apps/antalmanac/src/components/ScheduleManagement/ScheduleManagement.tsx
+++ b/apps/antalmanac/src/components/ScheduleManagement/ScheduleManagement.tsx
@@ -5,6 +5,7 @@ import { useParams } from 'react-router-dom';
 import { ScheduleManagementContent } from '$components/ScheduleManagement/ScheduleManagementContent';
 import { ScheduleManagementTabs } from '$components/ScheduleManagement/ScheduleManagementTabs';
 import { getLocalStorageUserId } from '$lib/localStorage';
+import { paramsAreInURL } from '$stores/CoursePaneStore';
 import { useTabStore } from '$stores/TabStore';
 
 /**
@@ -34,7 +35,6 @@ export function ScheduleManagement() {
         });
     };
 
-    // Change the tab to the "added classes" tab if the user was previously logged in.
     useEffect(() => {
         if (tab) {
             switch (tab) {
@@ -51,7 +51,7 @@ export function ScheduleManagement() {
 
         const userId = getLocalStorageUserId();
 
-        if (userId === null) {
+        if (userId === null || paramsAreInURL()) {
             setActiveTab('search');
         } else if (isMobile) {
             setActiveTab('calendar');
diff --git a/apps/antalmanac/src/lib/localStorage.ts b/apps/antalmanac/src/lib/localStorage.ts
index fb37676724..5897ae4788 100644
--- a/apps/antalmanac/src/lib/localStorage.ts
+++ b/apps/antalmanac/src/lib/localStorage.ts
@@ -5,7 +5,7 @@ enum LocalStorageKeys {
     phoneNumber = 'phoneNumber',
     recruitmentDismissalTime = 'recruitmentDismissalTime',
     advanced = 'advanced',
-    favorites = 'favorites',
+    recentlySearched = 'recentlySearched',
     tourHasRun = 'tourHasRun',
     theme = 'theme',
     show24HourTime = 'show24HourTime',
@@ -84,17 +84,17 @@ export function removeLocalStorageAdvanced() {
     window.localStorage.removeItem(LSK.advanced);
 }
 
-// Helper functions for favorites
-export function setLocalStorageFavorites(value: string) {
-    window.localStorage.setItem(LSK.favorites, value);
+// Helper functions for recently searched
+export function setLocalStorageRecentlySearched(value: string) {
+    window.localStorage.setItem(LSK.recentlySearched, value);
 }
 
-export function getLocalStorageFavorites() {
-    return window.localStorage.getItem(LSK.favorites);
+export function getLocalStorageRecentlySearched() {
+    return window.localStorage.getItem(LSK.recentlySearched);
 }
 
-export function removeLocalStorageFavorites() {
-    window.localStorage.removeItem(LSK.favorites);
+export function removeLocalStorageRecentlySearched() {
+    window.localStorage.removeItem(LSK.recentlySearched);
 }
 
 // Helper functions for tourHasRun
diff --git a/apps/antalmanac/src/stores/CoursePaneStore.ts b/apps/antalmanac/src/stores/CoursePaneStore.ts
index be1a48f693..adeca483f3 100644
--- a/apps/antalmanac/src/stores/CoursePaneStore.ts
+++ b/apps/antalmanac/src/stores/CoursePaneStore.ts
@@ -1,8 +1,9 @@
 import { create } from 'zustand';
 
+import RightPaneStore from '$components/RightPane/RightPaneStore';
 interface CoursePaneStore {
     /** Whether the search form is displayed (or the classes view) */
-    searchIsDisplayed: boolean;
+    searchFormIsDisplayed: boolean;
     /** Switch to the course search form */
     displaySearch: () => void;
     /** Switch to the classes view */
@@ -17,14 +18,13 @@ interface CoursePaneStore {
     forceUpdate: () => void;
 }
 
-function paramsAreInURL() {
+export function paramsAreInURL() {
     const search = new URLSearchParams(window.location.search);
 
     // TODO: This should be standardized
     const searchParams = [
         'courseCode',
         'courseNumber',
-        'deptLabel',
         'ge',
         'deptValue',
         'term',
@@ -52,12 +52,12 @@ function requiredParamsAreInURL() {
 
 export const useCoursePaneStore = create<CoursePaneStore>((set) => {
     return {
-        searchIsDisplayed: requiredParamsAreInURL() ? false : true,
+        searchFormIsDisplayed: !requiredParamsAreInURL() || !RightPaneStore.formDataIsValid(),
         displaySearch: () => {
-            set({ searchIsDisplayed: true });
+            set({ searchFormIsDisplayed: true });
         },
         displaySections: () => {
-            set({ searchIsDisplayed: false });
+            set({ searchFormIsDisplayed: false });
         },
 
         manualSearchEnabled: paramsAreInURL(),


Reviews:
user: login: KevinWu098
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-02T00:33:33Z
user: login: KevinWu098
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-02T00:39:26Z
user: login: jotalis
body: Overall solid PR and nice reimplementation of DeptSearchBar with a few nits and small comments
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-13T00:50:05Z
user: login: KevinWu098
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-13T00:54:35Z
user: login: KevinWu098
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-13T00:56:16Z
user: login: KevinWu098
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-13T00:58:49Z
user: login: KevinWu098
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-13T00:58:57Z
user: login: KevinWu098
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-13T01:01:45Z
user: login: KevinWu098
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-13T01:05:26Z
user: login: jotalis
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-13T01:10:41Z
user: login: jotalis
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-14T21:42:42Z
user: login: jotalis
body: l g t m  🐮 
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-14T21:51:45Z

Review comments:
path: apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/DeptSearchBar/MobileDeptSelector.tsx
user: login: KevinWu098
body: unused file
created_at: 2025-04-02T00:33:33Z
author_association: MEMBER
path: apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/DeptSearchBar/constants.ts
user: login: KevinWu098
body: This file takes the deleted `depts.ts` and set the key to `deptValue` and value to `deptLabel`
created_at: 2025-04-02T00:39:26Z
author_association: MEMBER
diff_hunk: @@ -69,7 +69,7 @@ class AdvancedSearch extends PureComponent<AdvancedSearchProps, AdvancedSearchSt
 
         const formData = RightPaneStore.getFormData();
         for (const key of Object.keys(formData)) {
-            if (key === 'deptLabel' || key === 'deptValue') {
+            if (key === 'deptValue') {
                 continue;
path: apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/AdvancedSearch/AdvancedSearch.tsx
user: login: jotalis
body: question: why are we skipping if the key equals `deptValue`?
created_at: 2025-04-12T00:40:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -43,7 +43,7 @@ class AdvancedSearch extends PureComponent<AdvancedSearchProps, AdvancedSearchSt
         const formData = RightPaneStore.getFormData();
         const defaultFormData = RightPaneStore.getDefaultFormData();
         for (const [key, value] of Object.entries(formData)) {
-            if (key === 'deptLabel' || key === 'deptValue') {
+            if (key === 'deptValue') {
                 continue;
path: apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/AdvancedSearch/AdvancedSearch.tsx
user: login: jotalis
body: question: why are we skipping if the key equals deptValue?
created_at: 2025-04-12T00:50:01Z
author_association: CONTRIBUTOR
diff_hunk: @@ -42,7 +41,7 @@ function paramsAreInURL() {
     return searchParams.some((param) => search.get(param) !== null);
 }
 
-function requiredParamsAreInURL() {
+export function requiredParamsAreInURL() {
path: apps/antalmanac/src/stores/CoursePaneStore.ts
user: login: jotalis
body: nit: if this isn't imported anywhere and only used a toggle in `useCoursePaneStore`, do we need to export it? 
created_at: 2025-04-12T00:58:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -51,7 +51,9 @@ export function ScheduleManagement() {
 
         const userId = getLocalStorageUserId();
 
-        if (userId === null) {
+        // Change the tab to the "added classes" tab if the user was previously logged in
+        // or if a search parameter is in the URL
+        if (userId === null || paramsAreInURL()) {
             setActiveTab('search');
         } else if (isMobile) {
             setActiveTab('calendar');
path: apps/antalmanac/src/components/ScheduleManagement/ScheduleManagement.tsx
user: login: jotalis
body: issue (non-blocking): comment is inaccurate and confusing
created_at: 2025-04-12T01:08:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,175 +1,101 @@
-import { TextField } from '@material-ui/core';
-import { withStyles } from '@material-ui/core/styles';
-import { ClassNameMap } from '@material-ui/core/styles/withStyles';
-import { Autocomplete } from '@material-ui/lab';
-import { ChangeEvent, PureComponent } from 'react';
-
-import RightPaneStore from '../../../RightPaneStore';
-
-import depts from './depts';
+import { Autocomplete, Box, TextField } from '@mui/material';
+import { useCallback, useEffect, useState } from 'react';
 
+import { DEPARTMENT_MAP } from '$components/RightPane/CoursePane/SearchForm/DeptSearchBar/constants';
+import RightPaneStore from '$components/RightPane/RightPaneStore';
 import { getLocalStorageFavorites, setLocalStorageFavorites } from '$lib/localStorage';
path: apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/DeptSearchBar/DeptSearchBar.tsx
user: login: jotalis
body: nit: can we refactor everywhere 'favorites' is mentioned to something more clear? e.g., `getLocalStorageRecentSearches` or `getLocalStorageSearchHistory`
created_at: 2025-04-12T01:38:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,175 +1,101 @@
-import { TextField } from '@material-ui/core';
-import { withStyles } from '@material-ui/core/styles';
-import { ClassNameMap } from '@material-ui/core/styles/withStyles';
-import { Autocomplete } from '@material-ui/lab';
-import { ChangeEvent, PureComponent } from 'react';
-
-import RightPaneStore from '../../../RightPaneStore';
-
-import depts from './depts';
+import { Autocomplete, Box, TextField } from '@mui/material';
+import { useCallback, useEffect, useState } from 'react';
 
+import { DEPARTMENT_MAP } from '$components/RightPane/CoursePane/SearchForm/DeptSearchBar/constants';
+import RightPaneStore from '$components/RightPane/RightPaneStore';
 import { getLocalStorageFavorites, setLocalStorageFavorites } from '$lib/localStorage';
 
-const style = {
-    formControl: {
-        flexGrow: 1,
-        width: '50%',
-    },
-};
+const options = Object.keys(DEPARTMENT_MAP);
 
-const options = depts.map((dept) => {
-    return {
-        ...dept,
-        isFavorite: false,
-    };
-});
+// This helper handles parsing the current and prior formats of localStorageFavorites
+const parseLocalStorageFavorites = (): string[] => {
+    try {
+        const data = JSON.parse(getLocalStorageFavorites() ?? '[]');
 
-interface DeptSearchBarProps {
-    classes: ClassNameMap;
-}
+        if (!Array.isArray(data)) {
+            return [];
+        }
 
-interface Department {
-    deptLabel: string;
-    deptValue: string;
-    isFavorite: boolean;
-}
+        if (data.every((x) => typeof x === 'string')) {
+            return data;
+        }
 
-interface DeptSearchBarState {
-    value: Department;
-    favorites: Department[];
-}
+        if (data.every((x) => typeof x === 'object' && x !== null && 'deptValue' in x)) {
+            return data.map((x) => x.deptValue);
+        }
path: apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/DeptSearchBar/DeptSearchBar.tsx
user: login: jotalis
body: nice 😎 
created_at: 2025-04-12T23:37:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,175 +1,101 @@
-import { TextField } from '@material-ui/core';
-import { withStyles } from '@material-ui/core/styles';
-import { ClassNameMap } from '@material-ui/core/styles/withStyles';
-import { Autocomplete } from '@material-ui/lab';
-import { ChangeEvent, PureComponent } from 'react';
-
-import RightPaneStore from '../../../RightPaneStore';
-
-import depts from './depts';
+import { Autocomplete, Box, TextField } from '@mui/material';
+import { useCallback, useEffect, useState } from 'react';
 
+import { DEPARTMENT_MAP } from '$components/RightPane/CoursePane/SearchForm/DeptSearchBar/constants';
+import RightPaneStore from '$components/RightPane/RightPaneStore';
 import { getLocalStorageFavorites, setLocalStorageFavorites } from '$lib/localStorage';
 
-const style = {
-    formControl: {
-        flexGrow: 1,
-        width: '50%',
-    },
-};
+const options = Object.keys(DEPARTMENT_MAP);
 
-const options = depts.map((dept) => {
-    return {
-        ...dept,
-        isFavorite: false,
-    };
-});
+// This helper handles parsing the current and prior formats of localStorageFavorites
+const parseLocalStorageFavorites = (): string[] => {
+    try {
+        const data = JSON.parse(getLocalStorageFavorites() ?? '[]');
 
-interface DeptSearchBarProps {
-    classes: ClassNameMap;
-}
+        if (!Array.isArray(data)) {
+            return [];
+        }
 
-interface Department {
-    deptLabel: string;
-    deptValue: string;
-    isFavorite: boolean;
-}
+        if (data.every((x) => typeof x === 'string')) {
+            return data;
+        }
 
-interface DeptSearchBarState {
-    value: Department;
-    favorites: Department[];
-}
+        if (data.every((x) => typeof x === 'object' && x !== null && 'deptValue' in x)) {
+            return data.map((x) => x.deptValue);
+        }
 
-class DeptSearchBar extends PureComponent<DeptSearchBarProps, DeptSearchBarState> {
-    updatedeptLabelAndGetFormData() {
-        RightPaneStore.updateFormValue('deptLabel', RightPaneStore.getUrlDeptLabel());
-        RightPaneStore.updateFormValue('deptValue', RightPaneStore.getUrlDeptValue());
-        return RightPaneStore.getFormData().deptLabel;
+        return [];
+    } catch (e) {
+        console.error('An error occurred:', e);
+        return [];
     }
+};
 
-    updatedeptValueAndGetFormData() {
-        RightPaneStore.updateFormValue('deptValue', RightPaneStore.getUrlDeptValue());
-        return RightPaneStore.getFormData().deptValue;
-    }
+export function DepartmentSearchBar() {
+    const [value, setValue] = useState(() => RightPaneStore.getFormData().deptValue);
+    const [favorites, setFavorites] = useState<typeof options>(() => parseLocalStorageFavorites());
 
-    getDeptValue() {
-        return RightPaneStore.getUrlDeptValue().trim()
-            ? this.updatedeptValueAndGetFormData()
-            : RightPaneStore.getFormData().deptValue;
-    }
+    const resetField = useCallback(() => {
+        setValue(() => RightPaneStore.getFormData().deptValue);
+    }, []);
 
-    getDeptLabel() {
-        return RightPaneStore.getUrlDeptLabel().trim()
-            ? this.updatedeptLabelAndGetFormData()
-            : RightPaneStore.getFormData().deptLabel;
-    }
+    const handleChange = useCallback(
+        (_: unknown, option: string | null) => {
+            const newValue = option ?? options[0]; // options[0] corresponds to `ALL`
 
-    constructor(props: DeptSearchBarProps) {
-        super(props);
+            setValue(newValue);
+            RightPaneStore.updateFormValue('deptValue', newValue);
 
-        let favorites: Department[] = [];
-        if (typeof Storage !== 'undefined') {
-            const locallyStoredFavorites = getLocalStorageFavorites();
-            favorites = locallyStoredFavorites != null ? JSON.parse(locallyStoredFavorites) : [];
-        }
-        this.state = {
-            value: {
-                deptValue: this.getDeptValue(),
-                deptLabel: this.getDeptLabel(),
-                isFavorite: false,
-            },
-            favorites: favorites,
-        };
-    }
+            const stateObj = { url: 'url' };
+            const url = new URL(window.location.href);
+            const urlParam = new URLSearchParams(url.search);
 
-    componentDidMount() {
-        RightPaneStore.on('formReset', this.resetField);
-    }
+            urlParam.delete('deptValue');
 
-    componentWillUnmount() {
-        RightPaneStore.removeListener('formReset', this.resetField);
-    }
+            if (newValue && newValue != 'ALL') {
+                urlParam.append('deptValue', newValue);
+            }
+            const param = urlParam.toString();
+            const new_url = `${param.trim() ? '?' : ''}${param}`;
+            history.replaceState(stateObj, 'url', '/' + new_url);
 
-    resetField = () => {
-        const formData = RightPaneStore.getFormData();
-
-        this.setState({
-            value: {
-                deptValue: formData.deptValue,
-                deptLabel: formData.deptLabel,
-                isFavorite: false,
-            },
-        });
-    };
-
-    compareValues = (option: Department, value: Department) => {
-        return option.deptValue === value.deptValue;
-    };
-
-    handleSetDept = (event: ChangeEvent<unknown>, newDept: Department | null) => {
-        const setDeptValue = newDept === null ? options[0] : newDept;
-
-        this.setState({ value: setDeptValue });
-        RightPaneStore.updateFormValue('deptValue', setDeptValue.deptValue);
-        RightPaneStore.updateFormValue('deptLabel', setDeptValue.deptLabel);
-
-        const stateObj = { url: 'url' };
-        const url = new URL(window.location.href);
-        const urlParam = new URLSearchParams(url.search);
-        urlParam.delete('deptLabel');
-        urlParam.delete('deptValue');
-        if (
-            setDeptValue.deptValue &&
-            setDeptValue.deptValue != 'ALL' &&
-            setDeptValue.deptLabel &&
-            setDeptValue.deptLabel != 'ALL: Include All Departments'
-        ) {
-            urlParam.append('deptLabel', setDeptValue.deptLabel);
-            urlParam.append('deptValue', setDeptValue.deptValue);
-        }
-        const param = urlParam.toString();
-        const new_url = `${param.trim() ? '?' : ''}${param}`;
-        history.replaceState(stateObj, 'url', '/' + new_url);
-
-        if (newDept == null || newDept.deptValue === 'ALL') return;
-
-        const favorites = this.state.favorites;
-        let updatedFavorites = [...favorites];
-
-        if (favorites.filter((favorite) => newDept.deptValue === favorite.deptValue).length > 0) {
-            updatedFavorites.sort((a, b) => {
-                return a.deptValue === newDept.deptValue ? -1 : b.deptValue === newDept.deptValue ? 1 : 0;
-            });
-        } else {
-            updatedFavorites = [{ ...newDept, isFavorite: true }].concat(favorites);
-            if (updatedFavorites.length > 5) updatedFavorites.pop();
-        }
-        this.setState({ favorites: updatedFavorites });
-        setLocalStorageFavorites(JSON.stringify(updatedFavorites));
-    };
-
-    render() {
-        const { classes } = this.props;
-
-        return (
-            <div className={classes.formControl}>
-                <Autocomplete
-                    value={this.state.value}
-                    options={this.state.favorites.concat(options)}
-                    autoHighlight={true}
-                    openOnFocus={true}
-                    getOptionSelected={this.compareValues}
-                    getOptionLabel={(option) => option.deptLabel}
-                    onChange={this.handleSetDept}
-                    includeInputInList={true}
-                    noOptionsText="No departments match the search"
-                    groupBy={(dept) => (dept.isFavorite ? 'Recent Departments' : 'Departments')}
-                    renderInput={(params) => (
-                        <TextField {...params} label="Department" type="search" InputLabelProps={{ shrink: true }} />
-                    )}
-                />
-            </div>
-        );
-    }
-}
+            if (newValue == null || newValue === 'ALL') return;
path: apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/DeptSearchBar/DeptSearchBar.tsx
user: login: jotalis
body: nit: redundant to check for null
```suggestion
            if (newValue === 'ALL') return;
```
created_at: 2025-04-13T00:02:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,175 +1,101 @@
-import { TextField } from '@material-ui/core';
-import { withStyles } from '@material-ui/core/styles';
-import { ClassNameMap } from '@material-ui/core/styles/withStyles';
-import { Autocomplete } from '@material-ui/lab';
-import { ChangeEvent, PureComponent } from 'react';
-
-import RightPaneStore from '../../../RightPaneStore';
-
-import depts from './depts';
+import { Autocomplete, Box, TextField } from '@mui/material';
+import { useCallback, useEffect, useState } from 'react';
 
+import { DEPARTMENT_MAP } from '$components/RightPane/CoursePane/SearchForm/DeptSearchBar/constants';
+import RightPaneStore from '$components/RightPane/RightPaneStore';
 import { getLocalStorageFavorites, setLocalStorageFavorites } from '$lib/localStorage';
 
-const style = {
-    formControl: {
-        flexGrow: 1,
-        width: '50%',
-    },
-};
+const options = Object.keys(DEPARTMENT_MAP);
 
-const options = depts.map((dept) => {
-    return {
-        ...dept,
-        isFavorite: false,
-    };
-});
+// This helper handles parsing the current and prior formats of localStorageFavorites
+const parseLocalStorageFavorites = (): string[] => {
+    try {
+        const data = JSON.parse(getLocalStorageFavorites() ?? '[]');
 
-interface DeptSearchBarProps {
-    classes: ClassNameMap;
-}
+        if (!Array.isArray(data)) {
+            return [];
+        }
 
-interface Department {
-    deptLabel: string;
-    deptValue: string;
-    isFavorite: boolean;
-}
+        if (data.every((x) => typeof x === 'string')) {
+            return data;
+        }
 
-interface DeptSearchBarState {
-    value: Department;
-    favorites: Department[];
-}
+        if (data.every((x) => typeof x === 'object' && x !== null && 'deptValue' in x)) {
+            return data.map((x) => x.deptValue);
+        }
 
-class DeptSearchBar extends PureComponent<DeptSearchBarProps, DeptSearchBarState> {
-    updatedeptLabelAndGetFormData() {
-        RightPaneStore.updateFormValue('deptLabel', RightPaneStore.getUrlDeptLabel());
-        RightPaneStore.updateFormValue('deptValue', RightPaneStore.getUrlDeptValue());
-        return RightPaneStore.getFormData().deptLabel;
+        return [];
+    } catch (e) {
+        console.error('An error occurred:', e);
+        return [];
     }
+};
 
-    updatedeptValueAndGetFormData() {
-        RightPaneStore.updateFormValue('deptValue', RightPaneStore.getUrlDeptValue());
-        return RightPaneStore.getFormData().deptValue;
-    }
+export function DepartmentSearchBar() {
+    const [value, setValue] = useState(() => RightPaneStore.getFormData().deptValue);
+    const [favorites, setFavorites] = useState<typeof options>(() => parseLocalStorageFavorites());
 
-    getDeptValue() {
-        return RightPaneStore.getUrlDeptValue().trim()
-            ? this.updatedeptValueAndGetFormData()
-            : RightPaneStore.getFormData().deptValue;
-    }
+    const resetField = useCallback(() => {
+        setValue(() => RightPaneStore.getFormData().deptValue);
+    }, []);
 
-    getDeptLabel() {
-        return RightPaneStore.getUrlDeptLabel().trim()
-            ? this.updatedeptLabelAndGetFormData()
-            : RightPaneStore.getFormData().deptLabel;
-    }
+    const handleChange = useCallback(
+        (_: unknown, option: string | null) => {
+            const newValue = option ?? options[0]; // options[0] corresponds to `ALL`
 
-    constructor(props: DeptSearchBarProps) {
-        super(props);
+            setValue(newValue);
+            RightPaneStore.updateFormValue('deptValue', newValue);
 
-        let favorites: Department[] = [];
-        if (typeof Storage !== 'undefined') {
-            const locallyStoredFavorites = getLocalStorageFavorites();
-            favorites = locallyStoredFavorites != null ? JSON.parse(locallyStoredFavorites) : [];
-        }
-        this.state = {
-            value: {
-                deptValue: this.getDeptValue(),
-                deptLabel: this.getDeptLabel(),
-                isFavorite: false,
-            },
-            favorites: favorites,
-        };
-    }
+            const stateObj = { url: 'url' };
+            const url = new URL(window.location.href);
+            const urlParam = new URLSearchParams(url.search);
 
-    componentDidMount() {
-        RightPaneStore.on('formReset', this.resetField);
-    }
+            urlParam.delete('deptValue');
 
-    componentWillUnmount() {
-        RightPaneStore.removeListener('formReset', this.resetField);
-    }
+            if (newValue && newValue != 'ALL') {
path: apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/DeptSearchBar/DeptSearchBar.tsx
user: login: jotalis
body: nit: redundant to check for truthyness
```suggestion
            if (newValue != 'ALL') {
```
created_at: 2025-04-13T00:03:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,175 +1,101 @@
-import { TextField } from '@material-ui/core';
-import { withStyles } from '@material-ui/core/styles';
-import { ClassNameMap } from '@material-ui/core/styles/withStyles';
-import { Autocomplete } from '@material-ui/lab';
-import { ChangeEvent, PureComponent } from 'react';
-
-import RightPaneStore from '../../../RightPaneStore';
-
-import depts from './depts';
+import { Autocomplete, Box, TextField } from '@mui/material';
+import { useCallback, useEffect, useState } from 'react';
 
+import { DEPARTMENT_MAP } from '$components/RightPane/CoursePane/SearchForm/DeptSearchBar/constants';
+import RightPaneStore from '$components/RightPane/RightPaneStore';
 import { getLocalStorageFavorites, setLocalStorageFavorites } from '$lib/localStorage';
 
-const style = {
-    formControl: {
-        flexGrow: 1,
-        width: '50%',
-    },
-};
+const options = Object.keys(DEPARTMENT_MAP);
 
-const options = depts.map((dept) => {
-    return {
-        ...dept,
-        isFavorite: false,
-    };
-});
+// This helper handles parsing the current and prior formats of localStorageFavorites
+const parseLocalStorageFavorites = (): string[] => {
+    try {
+        const data = JSON.parse(getLocalStorageFavorites() ?? '[]');
 
-interface DeptSearchBarProps {
-    classes: ClassNameMap;
-}
+        if (!Array.isArray(data)) {
+            return [];
+        }
 
-interface Department {
-    deptLabel: string;
-    deptValue: string;
-    isFavorite: boolean;
-}
+        if (data.every((x) => typeof x === 'string')) {
+            return data;
+        }
 
-interface DeptSearchBarState {
-    value: Department;
-    favorites: Department[];
-}
+        if (data.every((x) => typeof x === 'object' && x !== null && 'deptValue' in x)) {
+            return data.map((x) => x.deptValue);
+        }
 
-class DeptSearchBar extends PureComponent<DeptSearchBarProps, DeptSearchBarState> {
-    updatedeptLabelAndGetFormData() {
-        RightPaneStore.updateFormValue('deptLabel', RightPaneStore.getUrlDeptLabel());
-        RightPaneStore.updateFormValue('deptValue', RightPaneStore.getUrlDeptValue());
-        return RightPaneStore.getFormData().deptLabel;
+        return [];
+    } catch (e) {
+        console.error('An error occurred:', e);
+        return [];
     }
+};
 
-    updatedeptValueAndGetFormData() {
-        RightPaneStore.updateFormValue('deptValue', RightPaneStore.getUrlDeptValue());
-        return RightPaneStore.getFormData().deptValue;
-    }
+export function DepartmentSearchBar() {
+    const [value, setValue] = useState(() => RightPaneStore.getFormData().deptValue);
+    const [favorites, setFavorites] = useState<typeof options>(() => parseLocalStorageFavorites());
 
-    getDeptValue() {
-        return RightPaneStore.getUrlDeptValue().trim()
-            ? this.updatedeptValueAndGetFormData()
-            : RightPaneStore.getFormData().deptValue;
-    }
+    const resetField = useCallback(() => {
+        setValue(() => RightPaneStore.getFormData().deptValue);
+    }, []);
 
-    getDeptLabel() {
-        return RightPaneStore.getUrlDeptLabel().trim()
-            ? this.updatedeptLabelAndGetFormData()
-            : RightPaneStore.getFormData().deptLabel;
-    }
+    const handleChange = useCallback(
+        (_: unknown, option: string | null) => {
+            const newValue = option ?? options[0]; // options[0] corresponds to `ALL`
 
-    constructor(props: DeptSearchBarProps) {
-        super(props);
+            setValue(newValue);
+            RightPaneStore.updateFormValue('deptValue', newValue);
 
-        let favorites: Department[] = [];
-        if (typeof Storage !== 'undefined') {
-            const locallyStoredFavorites = getLocalStorageFavorites();
-            favorites = locallyStoredFavorites != null ? JSON.parse(locallyStoredFavorites) : [];
-        }
-        this.state = {
-            value: {
-                deptValue: this.getDeptValue(),
-                deptLabel: this.getDeptLabel(),
-                isFavorite: false,
-            },
-            favorites: favorites,
-        };
-    }
+            const stateObj = { url: 'url' };
+            const url = new URL(window.location.href);
+            const urlParam = new URLSearchParams(url.search);
 
-    componentDidMount() {
-        RightPaneStore.on('formReset', this.resetField);
-    }
+            urlParam.delete('deptValue');
 
-    componentWillUnmount() {
-        RightPaneStore.removeListener('formReset', this.resetField);
-    }
+            if (newValue && newValue != 'ALL') {
+                urlParam.append('deptValue', newValue);
+            }
+            const param = urlParam.toString();
+            const new_url = `${param.trim() ? '?' : ''}${param}`;
+            history.replaceState(stateObj, 'url', '/' + new_url);
 
-    resetField = () => {
-        const formData = RightPaneStore.getFormData();
-
-        this.setState({
-            value: {
-                deptValue: formData.deptValue,
-                deptLabel: formData.deptLabel,
-                isFavorite: false,
-            },
-        });
-    };
-
-    compareValues = (option: Department, value: Department) => {
-        return option.deptValue === value.deptValue;
-    };
-
-    handleSetDept = (event: ChangeEvent<unknown>, newDept: Department | null) => {
-        const setDeptValue = newDept === null ? options[0] : newDept;
-
-        this.setState({ value: setDeptValue });
-        RightPaneStore.updateFormValue('deptValue', setDeptValue.deptValue);
-        RightPaneStore.updateFormValue('deptLabel', setDeptValue.deptLabel);
-
-        const stateObj = { url: 'url' };
-        const url = new URL(window.location.href);
-        const urlParam = new URLSearchParams(url.search);
-        urlParam.delete('deptLabel');
-        urlParam.delete('deptValue');
-        if (
-            setDeptValue.deptValue &&
-            setDeptValue.deptValue != 'ALL' &&
-            setDeptValue.deptLabel &&
-            setDeptValue.deptLabel != 'ALL: Include All Departments'
-        ) {
-            urlParam.append('deptLabel', setDeptValue.deptLabel);
-            urlParam.append('deptValue', setDeptValue.deptValue);
-        }
-        const param = urlParam.toString();
-        const new_url = `${param.trim() ? '?' : ''}${param}`;
-        history.replaceState(stateObj, 'url', '/' + new_url);
-
-        if (newDept == null || newDept.deptValue === 'ALL') return;
-
-        const favorites = this.state.favorites;
-        let updatedFavorites = [...favorites];
-
-        if (favorites.filter((favorite) => newDept.deptValue === favorite.deptValue).length > 0) {
-            updatedFavorites.sort((a, b) => {
-                return a.deptValue === newDept.deptValue ? -1 : b.deptValue === newDept.deptValue ? 1 : 0;
-            });
-        } else {
-            updatedFavorites = [{ ...newDept, isFavorite: true }].concat(favorites);
-            if (updatedFavorites.length > 5) updatedFavorites.pop();
-        }
-        this.setState({ favorites: updatedFavorites });
-        setLocalStorageFavorites(JSON.stringify(updatedFavorites));
-    };
-
-    render() {
-        const { classes } = this.props;
-
-        return (
-            <div className={classes.formControl}>
-                <Autocomplete
-                    value={this.state.value}
-                    options={this.state.favorites.concat(options)}
-                    autoHighlight={true}
-                    openOnFocus={true}
-                    getOptionSelected={this.compareValues}
-                    getOptionLabel={(option) => option.deptLabel}
-                    onChange={this.handleSetDept}
-                    includeInputInList={true}
-                    noOptionsText="No departments match the search"
-                    groupBy={(dept) => (dept.isFavorite ? 'Recent Departments' : 'Departments')}
-                    renderInput={(params) => (
-                        <TextField {...params} label="Department" type="search" InputLabelProps={{ shrink: true }} />
-                    )}
-                />
-            </div>
-        );
-    }
-}
+            if (newValue == null || newValue === 'ALL') return;
+
+            favorites.includes(newValue)
+                ? setFavorites((prev) =>
+                      prev.sort((a, b) => {
+                          return a === newValue ? -1 : b === newValue ? 1 : 0;
+                      })
+                  )
+                : setFavorites((prev) => [newValue, ...prev].slice(0, 5));
+
+            setLocalStorageFavorites(JSON.stringify(favorites));
+        },
path: apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/DeptSearchBar/DeptSearchBar.tsx
user: login: jotalis
body: issue: there's a race condition with the `favorites` state here

`setLocalStorageFavorites` is often called with the current favorites value before the asynchronous `setFavorites` state update completes, causing localStorage to save stale data instead of the newly updated favorites list.

consider moving `setLocalStorageFavorites(JSON.stringify(favorites));` outside and call it in a useEffect below e.g.,
``` tsx
    useEffect(() => {
        setLocalStorageFavorites(JSON.stringify(favorites));
    }, [favorites]);
```

 
created_at: 2025-04-13T00:47:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -43,7 +43,7 @@ class AdvancedSearch extends PureComponent<AdvancedSearchProps, AdvancedSearchSt
         const formData = RightPaneStore.getFormData();
         const defaultFormData = RightPaneStore.getDefaultFormData();
         for (const [key, value] of Object.entries(formData)) {
-            if (key === 'deptLabel' || key === 'deptValue') {
+            if (key === 'deptValue') {
                 continue;
path: apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/AdvancedSearch/AdvancedSearch.tsx
user: login: KevinWu098
body: Ah, that's because the presence of deptValue doesn't imply that advanced settings have been set (which is what is checked/handled in lines 50-53)
created_at: 2025-04-13T00:54:34Z
author_association: MEMBER
diff_hunk: @@ -43,7 +43,7 @@ class AdvancedSearch extends PureComponent<AdvancedSearchProps, AdvancedSearchSt
         const formData = RightPaneStore.getFormData();
         const defaultFormData = RightPaneStore.getDefaultFormData();
         for (const [key, value] of Object.entries(formData)) {
-            if (key === 'deptLabel' || key === 'deptValue') {
+            if (key === 'deptValue') {
                 continue;
path: apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/AdvancedSearch/AdvancedSearch.tsx
user: login: KevinWu098
body: wait no, I'm trolling
created_at: 2025-04-13T00:56:15Z
author_association: MEMBER
diff_hunk: @@ -69,7 +69,7 @@ class AdvancedSearch extends PureComponent<AdvancedSearchProps, AdvancedSearchSt
 
         const formData = RightPaneStore.getFormData();
         for (const key of Object.keys(formData)) {
-            if (key === 'deptLabel' || key === 'deptValue') {
+            if (key === 'deptValue') {
                 continue;
path: apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/AdvancedSearch/AdvancedSearch.tsx
user: login: KevinWu098
body: Good question, I imagine it was a legacy thing — I can't see anything breaking as a result of removing this on my local branch
created_at: 2025-04-13T00:58:48Z
author_association: MEMBER
diff_hunk: @@ -69,7 +69,7 @@ class AdvancedSearch extends PureComponent<AdvancedSearchProps, AdvancedSearchSt
 
         const formData = RightPaneStore.getFormData();
         for (const key of Object.keys(formData)) {
-            if (key === 'deptLabel' || key === 'deptValue') {
+            if (key === 'deptValue') {
                 continue;
path: apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/AdvancedSearch/AdvancedSearch.tsx
user: login: KevinWu098
body: (removed)
created_at: 2025-04-13T00:58:56Z
author_association: MEMBER
diff_hunk: @@ -51,7 +51,9 @@ export function ScheduleManagement() {
 
         const userId = getLocalStorageUserId();
 
-        if (userId === null) {
+        // Change the tab to the "added classes" tab if the user was previously logged in
+        // or if a search parameter is in the URL
+        if (userId === null || paramsAreInURL()) {
             setActiveTab('search');
         } else if (isMobile) {
             setActiveTab('calendar');
path: apps/antalmanac/src/components/ScheduleManagement/ScheduleManagement.tsx
user: login: KevinWu098
body: fixed comment
created_at: 2025-04-13T01:01:44Z
author_association: MEMBER
diff_hunk: @@ -1,175 +1,101 @@
-import { TextField } from '@material-ui/core';
-import { withStyles } from '@material-ui/core/styles';
-import { ClassNameMap } from '@material-ui/core/styles/withStyles';
-import { Autocomplete } from '@material-ui/lab';
-import { ChangeEvent, PureComponent } from 'react';
-
-import RightPaneStore from '../../../RightPaneStore';
-
-import depts from './depts';
+import { Autocomplete, Box, TextField } from '@mui/material';
+import { useCallback, useEffect, useState } from 'react';
 
+import { DEPARTMENT_MAP } from '$components/RightPane/CoursePane/SearchForm/DeptSearchBar/constants';
+import RightPaneStore from '$components/RightPane/RightPaneStore';
 import { getLocalStorageFavorites, setLocalStorageFavorites } from '$lib/localStorage';
path: apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/DeptSearchBar/DeptSearchBar.tsx
user: login: KevinWu098
body: I've chosen to not rename the localStorage functions, but I have updated the component to use `recentSearches` over favorites. Main reason being that I'd like to keep our helper functions as close to the actual key they're modifying as possible.

Thoughts?
created_at: 2025-04-13T01:05:25Z
author_association: MEMBER
diff_hunk: @@ -1,175 +1,101 @@
-import { TextField } from '@material-ui/core';
-import { withStyles } from '@material-ui/core/styles';
-import { ClassNameMap } from '@material-ui/core/styles/withStyles';
-import { Autocomplete } from '@material-ui/lab';
-import { ChangeEvent, PureComponent } from 'react';
-
-import RightPaneStore from '../../../RightPaneStore';
-
-import depts from './depts';
+import { Autocomplete, Box, TextField } from '@mui/material';
+import { useCallback, useEffect, useState } from 'react';
 
+import { DEPARTMENT_MAP } from '$components/RightPane/CoursePane/SearchForm/DeptSearchBar/constants';
+import RightPaneStore from '$components/RightPane/RightPaneStore';
 import { getLocalStorageFavorites, setLocalStorageFavorites } from '$lib/localStorage';
path: apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/DeptSearchBar/DeptSearchBar.tsx
user: login: jotalis
body: you should just refactor the localStorage as well, since those functions and `favorites` are only being used for this purpose. for clarity, it would make sense to refactor the localStorage names to `recentDepartmentSearches` as opposed to my previous suggestions, since localStorage covers a broader scope
created_at: 2025-04-13T01:10:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,103 @@
+import { Autocomplete, Box, TextField } from '@mui/material';
+import { useCallback, useEffect, useState } from 'react';
+
+import { DEPARTMENT_MAP } from '$components/RightPane/CoursePane/SearchForm/DepartmentSearchBar/constants';
+import RightPaneStore from '$components/RightPane/RightPaneStore';
+import { getLocalStorageFavorites, setLocalStorageFavorites } from '$lib/localStorage';
+
+const options = Object.keys(DEPARTMENT_MAP);
+
+// This helper handles parsing the current and prior formats of localStorageFavorites
+const parseLocalStorageFavorites = (): string[] => {
+    try {
+        const data = JSON.parse(getLocalStorageFavorites() ?? '[]');
+
+        if (!Array.isArray(data)) {
+            return [];
+        }
+
+        if (data.every((x) => typeof x === 'string')) {
+            return data;
+        }
+
+        if (data.every((x) => typeof x === 'object' && x !== null && 'deptValue' in x)) {
+            return data.map((x) => x.deptValue);
+        }
+
+        return [];
+    } catch (e) {
+        console.error('An error occurred:', e);
+        return [];
+    }
+};
+
+export function DepartmentSearchBar() {
+    const [value, setValue] = useState(() => RightPaneStore.getFormData().deptValue);
+    const [recentSearches, setRecentSearches] = useState<typeof options>(() => parseLocalStorageFavorites());
+
+    const resetField = useCallback(() => {
+        setValue(() => RightPaneStore.getFormData().deptValue);
+    }, []);
+
+    const handleChange = useCallback(
+        (_: unknown, option: string | null) => {
+            const newValue = option ?? options[0]; // options[0] corresponds to `ALL`
+
+            setValue(newValue);
+            RightPaneStore.updateFormValue('deptValue', newValue);
+
+            const stateObj = { url: 'url' };
+            const url = new URL(window.location.href);
+            const urlParam = new URLSearchParams(url.search);
+
+            urlParam.delete('deptValue');
+
+            if (newValue != 'ALL') {
+                urlParam.append('deptValue', newValue);
+            }
+            const param = urlParam.toString();
+            const new_url = `${param.trim() ? '?' : ''}${param}`;
+            history.replaceState(stateObj, 'url', '/' + new_url);
+
+            if (newValue === 'ALL') return;
+
+            recentSearches.includes(newValue)
+                ? setRecentSearches((prev) =>
+                      prev.sort((a, b) => {
+                          return a === newValue ? -1 : b === newValue ? 1 : 0;
+                      })
+                  )
+                : setRecentSearches((prev) => [newValue, ...prev].slice(0, 5));
+        },
+        [recentSearches]
+    );
+
+    useEffect(() => {
+        RightPaneStore.on('formReset', resetField);
+
+        return () => {
+            RightPaneStore.off('formReset', resetField);
+        };
+    }, [resetField]);
+
+    useEffect(() => {
+        setLocalStorageFavorites(JSON.stringify(recentSearches));
+    }, [recentSearches]);
+
+    return (
+        <Box sx={{ flexGrow: 1, width: '50%' }}>
+            <Autocomplete
+                value={value}
+                options={Array.from(new Set<string>([...recentSearches, ...options]))}
+                autoHighlight={true}
+                openOnFocus={true}
+                getOptionLabel={(option) => DEPARTMENT_MAP[option.toUpperCase() as keyof typeof DEPARTMENT_MAP]}
path: apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/DepartmentSearchBar/DepartmentSearchBar.tsx
user: login: jotalis
body: question(non-blocking): why is `toUpperCase()` needed if we're populating autocomplete with DEPARTMENT_MAP options? even if they enter invalid text, it either forces the first available function or shows `noOptionsText`
created_at: 2025-04-14T21:26:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -101,7 +97,9 @@ class RightPaneStore extends EventEmitter {
 
     formDataIsValid = () => {
         const { ge, deptValue, sectionCode, instructor } = this.formData;
-        return ge !== 'ANY' || deptValue !== 'ALL' || sectionCode !== '' || instructor !== '';
+        return (
+            ge.toUpperCase() !== 'ANY' || deptValue.toUpperCase() !== 'ALL' || sectionCode !== '' || instructor !== ''
+        );
path: apps/antalmanac/src/components/RightPane/RightPaneStore.ts
user: login: jotalis
body: issue: you should perform the uppercase validation when we parse the URL 
created_at: 2025-04-14T21:40:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -51,7 +51,9 @@ export function ScheduleManagement() {
 
         const userId = getLocalStorageUserId();
 
-        if (userId === null) {
+        // Change the tab to the search tab if the user is not logged in
+        // or if a search parameter is in the URL
+        if (userId === null || paramsAreInURL()) {
path: apps/antalmanac/src/components/ScheduleManagement/ScheduleManagement.tsx
user: login: jotalis
body: nit: make this comment read better or delete it.
created_at: 2025-04-14T21:42:22Z
author_association: CONTRIBUTOR

PR comments:
user: login: jotalis
created_at: 2025-04-13T00:50:54Z
body: note: trying `deptValue=All` in the url leads to a TRPC error and infinite search screen, rather than erroring like `deptValue = {random_invalid_string}` does. 

not sure if this is expected behavior (it happens on prod too), but it would be better to catch it and throw a warning / cancel the search
author_association: CONTRIBUTOR
user: login: KevinWu098
created_at: 2025-04-13T01:43:41Z
body: > note: trying `deptValue=All` in the url leads to a TRPC error and infinite search screen, rather than erroring like `deptValue = {random_invalid_string}` does.
> 
> not sure if this is expected behavior (it happens on prod too), but it would be better to catch it and throw a warning / cancel the search

Hmm, this is super bizarre — looking into it
author_association: MEMBER
user: login: KevinWu098
created_at: 2025-04-14T18:21:30Z
body: Fix was found @jotalis — okay so, technically, "all" is a valid input to the API now. The request is so large, however, that it times out our lambda function so TRPC (which batches calls) will also fail our user loading (among other things).

The reason this was possible was because we were checking for the existence of a required parameter (e.g. `deptValue`) but not that the parameter was valid `RightPaneStore.formDataIsValid()`.
author_association: MEMBER