CourseInsights / frontend / src / components / SearchableDropdownDept.tsx
SearchableDropdownDept.tsx
Raw
import React, { useEffect, useState } from "react";
import { Dropdown, FormControl } from "react-bootstrap";
import { SERVER_URL } from '../App';

// Inspired from UBCGrades search dropdown https://ubcgrades.com
const SearchableDropdownDept: React.FC<{ id: string; dir: string; selectedOption: string; setSelectedOption: (value:string) => void; setId: (value:string) => void }> = ({ id, dir, selectedOption, setSelectedOption, setId }) => {
  const [searchTerm, setSearchTerm] = useState("");
  const [show, setShow] = useState(false);
  const [depts, setDepts] = useState<string[]>([]);
  // const port = 4321;
  // const SERVER_URL = `http://localhost:${port}`;

  useEffect(() => {
    if (id === "") {
      return;
    }
    const queryDeptOptions = {
        "WHERE": {
        },
        "OPTIONS": {
          "COLUMNS": [
            `${id}_dept`
          ],
          "ORDER": {
            "dir": "UP",
            "keys": [
              `${id}_dept`
            ]
          }
        },
        "TRANSFORMATIONS": {
          "GROUP": [
            `${id}_dept`
          ],
          "APPLY": [
          ]
        }
      }

    const requestOptions = {
      method: "POST",
      body: JSON.stringify(queryDeptOptions),
      headers: {'Content-Type': 'application/json'},
  }

    const ENDPOINT_URL = `/query`;

    fetch(`${SERVER_URL}${ENDPOINT_URL}`, requestOptions)
    .then((res) => {
        if (!res.ok) {
          return "";
            throw new Error(`Failed to query: ${res.json()}`);
        }
        return res.json();
    })
    .then((data) => {
      if (data === "") {
        return setDepts([]);
      }
        //console.log("Received data SearchableDropdownDept:", data);
        const result = data.result.map((item: any) => item[`${id}_dept`]);
        setDepts(result);
    })
    .catch((_error) => {
        //console.error("Error:", error);
    });

  }, [id, dir, setDepts])

  const filteredOptions = depts.filter(option =>
    option?.toLowerCase().includes(searchTerm.toLowerCase())
  );

  const handleSelect = (value: string) => {
    setSelectedOption(value);
    setId("Course#");
    setShow(false);
  };

  return (
    <Dropdown show={show} onToggle={(isOpen) => setShow(isOpen)}>
  <Dropdown.Toggle 
    style={{
      minWidth: '200px',
      border: '1px solid #ccc',
      backgroundColor: '#f8f9fa',
      color: '#6c757d',
      padding: '8px 12px',
      boxShadow: 'none',
      display: 'flex',
      justifyContent: 'space-between',
      alignItems: 'center',
      margin: '0 auto',
    }} 
    variant="primary"
  >
    <span>{selectedOption}</span> 
    
  </Dropdown.Toggle>
  {/* Fix resposiveness */}
  <Dropdown.Menu 
    style={{
      maxHeight: '300px', 
      overflowY: 'auto', 
      minWidth: '150px',
      maxWidth: '200px', 
    }}
  >

    <FormControl
      autoFocus
      placeholder="Search..."
      onChange={(e) => setSearchTerm(e.target.value)}
      onClick={(e) => e.stopPropagation()} // Prevents dropdown from closing on input click
      style={{ border: 'none', paddingLeft: '8px' }}
    />
    

    {filteredOptions.length > 0 ? (
      filteredOptions.map((option, index) => (
        <Dropdown.Item key={index} onClick={() => handleSelect(option)}>
          {option}
        </Dropdown.Item>
      ))
    ) : (
      <Dropdown.Item disabled>No results found</Dropdown.Item>
    )}
  </Dropdown.Menu>
</Dropdown>);
};

export default React.memo(SearchableDropdownDept);