From a75b5afcc021a5abb2e64b0b692733bae0e2a79e Mon Sep 17 00:00:00 2001 From: Devrat Date: Tue, 10 Jun 2025 09:32:02 -0500 Subject: [PATCH] feat: Add UTA professor profile integration with API proxy --- src/app/api/uta-professors/route.ts | 45 +++++++++ src/app/components/StatsCard.tsx | 138 +++++++++++++++++++++++++--- 2 files changed, 168 insertions(+), 15 deletions(-) create mode 100644 src/app/api/uta-professors/route.ts diff --git a/src/app/api/uta-professors/route.ts b/src/app/api/uta-professors/route.ts new file mode 100644 index 0000000..6a84177 --- /dev/null +++ b/src/app/api/uta-professors/route.ts @@ -0,0 +1,45 @@ +import { NextRequest, NextResponse } from "next/server"; + +export async function GET(request: NextRequest) { + const { searchParams } = new URL(request.url); + const alpha = searchParams.get("alpha"); + + if (!alpha) { + return NextResponse.json( + { error: "Alpha parameter is required" }, + { status: 400 } + ); + } + + try { + const response = await fetch( + `https://api.web.uta.edu/DM/courses/instructors?alpha=${alpha}&sortby=LName`, + { + headers: { + Accept: "application/json", + "User-Agent": "MAVGrades/1.0", + }, + } + ); + + if (!response.ok) { + throw new Error(`UTA API responded with status: ${response.status}`); + } + + const data = await response.json(); + + return NextResponse.json(data, { + headers: { + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": "GET", + "Access-Control-Allow-Headers": "Content-Type", + }, + }); + } catch (error) { + console.error("Error fetching UTA professor data:", error); + return NextResponse.json( + { error: "Failed to fetch UTA professor data" }, + { status: 500 } + ); + } +} diff --git a/src/app/components/StatsCard.tsx b/src/app/components/StatsCard.tsx index 31fc19d..3ef5a7a 100644 --- a/src/app/components/StatsCard.tsx +++ b/src/app/components/StatsCard.tsx @@ -14,6 +14,15 @@ const tailwindColors: { [key: string]: string } = { "border-t-yellow-400": "#FDE047", }; +// Interface for UTA professor data +interface UTAProfessor { + userName: string; + instructorName: string; + nameProfileLink: string; + email: string; + workingTitle: string; +} + const StatsCard = ({ selectedItems, selectedProfessor, @@ -22,6 +31,9 @@ const StatsCard = ({ selectedProfessor?: string; }) => { const [showInfoBox, setShowInfoBox] = useState(false); + const [utaProfessors, setUtaProfessors] = useState<{ + [key: string]: UTAProfessor; + }>({}); const infoBoxRef = useRef(null); const buttonRef = useRef(null); const aggregatedData = Array.from(selectedItems.values()); @@ -45,6 +57,82 @@ const StatsCard = ({ )}`; }; + // Function to extract last name from full professor name + const extractLastName = (fullName: string): string => { + const parts = fullName.trim().split(" "); + return parts[parts.length - 1]; + }; + + // Function to fetch UTA professor data + const fetchUTAProfessor = async ( + professorName: string + ): Promise => { + try { + const lastName = extractLastName(professorName); + const firstLetter = lastName.charAt(0).toUpperCase(); + + console.log( + `Fetching UTA data for professor: ${professorName}, lastName: ${lastName}, firstLetter: ${firstLetter}` + ); + + const response = await fetch(`/api/uta-professors?alpha=${firstLetter}`); + + if (!response.ok) { + throw new Error("Failed to fetch UTA professor data"); + } + + const professors: UTAProfessor[] = await response.json(); + console.log( + `Found ${professors.length} professors for letter ${firstLetter}` + ); + + // Find the professor by matching the last name + const matchedProfessor = professors.find((prof) => + prof.instructorName.toLowerCase().includes(lastName.toLowerCase()) + ); + + console.log(`Matched professor:`, matchedProfessor); + return matchedProfessor || null; + } catch (error) { + console.error("Error fetching UTA professor data:", error); + return null; + } + }; + + // Effect to fetch UTA professor data for all professors in aggregatedData + useEffect(() => { + const fetchAllProfessors = async () => { + const professorPromises = aggregatedData + .filter((data) => data?.instructor1) + .map(async (data) => { + const professorName = data.instructor1; + if (!utaProfessors[professorName]) { + const utaProf = await fetchUTAProfessor(professorName); + if (utaProf) { + return { [professorName]: utaProf }; + } + } + return null; + }); + + const results = await Promise.all(professorPromises); + const newProfessors = results.reduce((acc, result) => { + if (result) { + return { ...acc, ...result }; + } + return acc; + }, {} as { [key: string]: UTAProfessor }); + + if (newProfessors && Object.keys(newProfessors).length > 0) { + setUtaProfessors((prev) => ({ ...prev, ...newProfessors })); + } + }; + + if (aggregatedData.length > 0) { + fetchAllProfessors(); + } + }, [aggregatedData]); + const InfoBox = ({ label, value, @@ -57,6 +145,7 @@ const StatsCard = ({ isProf?: boolean; }) => { const textColor = tailwindColors[colorClass] || "#FFFFFF"; + const utaProf = isProf ? utaProfessors[value.toString()] : null; return (
{isProf && ( - - RMP - +
+ {utaProf && ( + + UTA Profile + + )} + + RMP + +
)}