CourseInsights / frontend / src / components / SectionsBarChart.tsx
SectionsBarChart.tsx
Raw
import React, { useEffect, useRef, useState } from 'react';
import { Bar } from 'react-chartjs-2';
import { Chart as ChartJS, Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale } from 'chart.js';
import jsPDF from 'jspdf';
import { Container } from "react-bootstrap";
import { SERVER_URL } from '../App';

// Register necessary chart components
ChartJS.register(Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale);

interface Props {
  dir: string;
  chartId: string;
  queryDept: string;
  queryId: string;
  queryYear: string;
}

const SectionsBarChart: React.FC<Props> = ({ dir, chartId, queryDept, queryId, queryYear }) => {
  const chartRef = useRef<any>(null);
  const [courses, setCourses] = useState<any[]>([]);
  // const port = 4321;
  // const SERVER_URL = `http://localhost:${port}`;

  useEffect(() => {

    const querySections = {
      "WHERE": {
        "AND": [
          { "EQ": { [`${chartId}_year`]: Number(queryYear === "Year" ? -1 : queryYear)} },
          { "IS": { [`${chartId}_dept`]: queryDept } },
          { "IS": { [`${chartId}_id`]: queryId } }
        ]
      },
      "OPTIONS": {
        "COLUMNS": [
          `${chartId}_uuid`,
          `${chartId}_instructor`,
          `${chartId}_avg`,
          `${chartId}_pass`,
          `${chartId}_fail`
        ],
        "ORDER": { "dir": dir, "keys": [`${chartId}_avg`] }
      }
    };

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

    fetch(`${SERVER_URL}/query`, requestOptions)
      .then((res) => {
        if (!res.ok) {
          return "";
          throw new Error(`Failed to query: ${res.statusText}`);
        }
        return res.json();
      })
      .then((data) => {
        if (data === "") {
          return setCourses([]);
        }
        //console.log("Received data: SectionsBarChart", data);
        setCourses(data.result);
      })
      .catch((_error) => {
        //console.error("Error:", error);
      });
  }, [chartId, dir, queryDept, queryId, queryYear]);

  let labels = courses.map(course => `${course[`${chartId}_uuid`]} (${course[`${chartId}_instructor`]})`);
  let averages = courses.map(course => course[`${chartId}_avg`]);
  let passes = courses.map(course => course[`${chartId}_pass`]);
  let failures = courses.map(course => course[`${chartId}_fail`]);

  let combined = labels.map((id, index) => ({
    label: id,
    average: averages[index],
    pass: passes[index],
    fail: failures[index]
  }));

  combined = combined.sort((a, b) => a.average - b.average).slice(-10);

  labels = combined.map(item => item.label);
  averages = combined.map(item => item.average);
  passes = combined.map(item => item.pass);
  failures = combined.map(item => item.fail);

  const data = {
    labels,
    datasets: [
      {
        label: 'Average Score',
        data: averages,
        backgroundColor: 'rgba(54, 162, 235, 0.6)',
        borderColor: 'rgba(54, 162, 235, 1)',
        borderWidth: 2,
      },
      {
        label: 'Pass Count',
        data: passes,
        backgroundColor: 'rgba(75, 192, 192, 0.6)',
        borderColor: 'rgba(75, 192, 192, 1)',
        borderWidth: 2,
      },
      {
        label: 'Fail Count',
        data: failures,
        backgroundColor: 'rgba(255, 0, 0, 0.6)',
        borderColor: 'rgba(75, 7, 192, 1)',
        borderWidth: 2,
      }
    ]
  };

  const options = {
    responsive: true,
    plugins: {
      tooltip: {
        callbacks: {
          label: (tooltipItem: any) => `Value: ${tooltipItem.raw}`
        },
      },
      legend: { display: true },
    }
  };

  const downloadPDF = () => {
    if (!chartRef.current) return;

    const chartInstance = chartRef.current;
    const chartImage = chartInstance.toBase64Image();

    const pdf = new jsPDF('landscape');
    pdf.addImage(chartImage, 'PNG', 15, 20, 250, 120);
    pdf.save('SectionsBarChart.pdf');
  };

  return (
    <Container className="mt-4 container-xxl">
      <h3 className="text-center">Sections</h3>
      <Bar ref={chartRef} data={data} options={options} />
      <button className="btn btn-primary mt-3" onClick={downloadPDF}>Download PDF</button>
    </Container>
  );
};

export default SectionsBarChart;