CourseInsights / frontend / src / components / SearchableDropdownInstructor.tsx
SearchableDropdownInstructor.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
// Adapted Dropdown from from ChatGPT
const SearchableDropdownInstructor: React.FC<{ id: string; dir: string; selectedOption: string; setSelectedOption: (value:string) => void }> = ({ id, dir, selectedOption, setSelectedOption }) => {
  const [searchTerm, setSearchTerm] = useState("");
  const [show, setShow] = useState(false);
  const [instructors, setInstructors] = useState<string[]>([]);
  // const port = 4321;
  // const SERVER_URL = `http://localhost:${port}`;

  useEffect(() => {
    if (id === "") {
      return;
    }
    const queryInstructorOptions = {
      "WHERE": {
        "GT": {
          [`${id}_year`]: 2010
        }
      },
      "OPTIONS": {
        "COLUMNS": [
          `${id}_instructor`
        ],
        "ORDER": {
          "dir": "DOWN",
          "keys": [
            `${id}_instructor`
          ]
        }
      },
      "TRANSFORMATIONS": {
        "GROUP": [
          `${id}_instructor`
        ],
        "APPLY": []
      }
    }
  

    const requestOptions = {
      method: "POST",
      body: JSON.stringify(queryInstructorOptions),
      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 setInstructors([]);
      }
        //console.log("Received data:", data);
        //alert("Perform query executed successfully!");
        const result = data.result.map((item: any) => item[`${id}_instructor`]);
        setInstructors(result);
    })
    .catch((_error) => {
        //console.error("Error:", error);
        //alert("Failed to perform query. Please try again.");
    });

  }, [id, dir, setInstructors])

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

  const handleSelect = (value: string) => {
    setSelectedOption(value);
    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>
  
  <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(SearchableDropdownInstructor);