diff --git a/capstone-showcase/backend/ProductionServer.js b/capstone-showcase/backend/ProductionServer.js index f102327d..cf2c6f5b 100644 --- a/capstone-showcase/backend/ProductionServer.js +++ b/capstone-showcase/backend/ProductionServer.js @@ -928,13 +928,32 @@ app.get("/api/winners", (req, res) => { WHEN MONTH(submitDate) IN (9, 10, 11) THEN 'Fall' END AS semester FROM survey_entries -WHERE position IS NOT NULL;`; - db.query(sql, (err, results) => { +WHERE position IS NOT NULL`; // Sort winners pages by ascending position +const params = []; +// Adding filters to the SQL query if they are provided + if (semester && semester !== 'all') { + const semMap = { sp: 'Spring', su: 'Summer', fa: 'Fall', wi: 'Winter' }; // Mapping 'sp' to 'Spring', etc., to match CASE statement results + sql += ` AND CASE + WHEN MONTH(submitDate) IN (12, 1, 2) THEN 'Winter' + WHEN MONTH(submitDate) IN (3, 4, 5) THEN 'Spring' + WHEN MONTH(submitDate) IN (6, 7, 8) THEN 'Summer' + WHEN MONTH(submitDate) IN (9, 10, 11) THEN 'Fall' + END = ?`; + params.push(semMap[semester] || semester); + } + if (year && year !== 'all') { + sql += ` AND YEAR(submitDate) = ?`; + params.push(year); + } + sql += ` ORDER BY position ASC;`; // ordering results by ascending position + db.query(sql, params, (err, results) => { if (err) { console.error("Error retrieving winners data:", err); return res.status(500).send("Server error"); } console.log("Query results:", results); + console.log("Executed SQL:", sql); + console.log("With Params:", params); res.json(results); }); }); diff --git a/capstone-showcase/backend/localServer.js b/capstone-showcase/backend/localServer.js index 483dc6a8..f085c728 100644 --- a/capstone-showcase/backend/localServer.js +++ b/capstone-showcase/backend/localServer.js @@ -540,9 +540,10 @@ app.get("/api/winners", async (req, res) => { // Ensuring all below necessary columns exist locally first await ensureColumns("survey_entries", LOCAL_DBSCHEMA.survey_entries_Columns); + const { semester, year } = req.query; // Get filters from the URL query string // The below "SELECT" code needs to match the "survey_entries_Columns" constant above. // Any additions the below code makes needs to be reflected in the "survey_entries_Columns" constant above - const sql = `SELECT + let sql = `SELECT Major AS course, youtubeLink AS video, position AS position, @@ -562,14 +563,34 @@ app.get("/api/winners", async (req, res) => { WHEN MONTH(submitDate) IN (9, 10, 11) THEN 'Fall' END AS semester FROM survey_entries -WHERE position IS NOT NULL -ORDER BY submitDate DESC, position ASC;`; - db.query(sql, (err, results) => { +WHERE position IS NOT NULL`; +//ORDER BY position ASC;`; // Sort winners pages by ascending position +//ORDER BY submitDate DESC, position ASC;`; // this is the old local sort +const params = []; +// Adding filters to the SQL query if they are provided + if (semester && semester !== 'all') { + const semMap = { sp: 'Spring', su: 'Summer', fa: 'Fall', wi: 'Winter' }; // Mapping 'sp' to 'Spring', etc., to match CASE statement results + sql += ` AND CASE + WHEN MONTH(submitDate) IN (12, 1, 2) THEN 'Winter' + WHEN MONTH(submitDate) IN (3, 4, 5) THEN 'Spring' + WHEN MONTH(submitDate) IN (6, 7, 8) THEN 'Summer' + WHEN MONTH(submitDate) IN (9, 10, 11) THEN 'Fall' + END = ?`; + params.push(semMap[semester] || semester); + } + if (year && year !== 'all') { + sql += ` AND YEAR(submitDate) = ?`; + params.push(year); + } + sql += ` ORDER BY position ASC;`; // ordering results by ascending position + db.query(sql, params, (err, results) => { if (err) { console.error("Error retrieving winners data:", err); return res.status(500).send("Server error"); } console.log("Query results:", results); + console.log("Executed SQL:", sql); + console.log("With Params:", params); res.json(results); }); }); diff --git a/capstone-showcase/src/Components/WinnerComponent.tsx b/capstone-showcase/src/Components/WinnerComponent.tsx index 81b446a6..490d46f6 100644 --- a/capstone-showcase/src/Components/WinnerComponent.tsx +++ b/capstone-showcase/src/Components/WinnerComponent.tsx @@ -2,6 +2,18 @@ import "../CSS/WinnerComponent.css"; import { Link } from "react-router-dom"; + +const displayDepartmentMap: Record = { + "computer-science": "Computer Science", + "computer-systems-engineering": "Computer Systems Engineering", + "biomedical-engineering": "Biomedical Engineering", + "mechanical-engineering": "Mechanical Engineering", + "electrical-engineering": "Electrical Engineering", + "industrial-engineering": "Industrial Engineering", + "informatics": "Informatics", + "interdisciplinary": "Interdisciplinary", +}; + type ShowcaseEntry = { course: string; id: number; @@ -104,7 +116,7 @@ export function WinnerComponent({ winners }: { winners: ShowcaseEntry[] }) { {winner.semester} {winner.year} - {winner.department || "Computer Science"} + {displayDepartmentMap[winner.course] || winner.course || "General Engineering"}

diff --git a/capstone-showcase/src/Hooks/useWinners.ts b/capstone-showcase/src/Hooks/useWinners.ts index b78fa4e5..acc10f70 100644 --- a/capstone-showcase/src/Hooks/useWinners.ts +++ b/capstone-showcase/src/Hooks/useWinners.ts @@ -1,4 +1,5 @@ import { useEffect, useState } from "react"; +import { useLocation } from "react-router-dom"; // To detect when URL changes, to force hook fetch data whenever user clicks something in the top nav export type ShowcaseEntry = { course: string; @@ -24,6 +25,7 @@ interface Filters { }; export default function useWinners() { + const location = useLocation(); // To listen to URL changes const [pastWinnersData, setPastWinnersData] = useState([]); const [filteredWinnersData, setFilteredWinnersData] = useState([]); const [hasFiltered, setHasFiltered] = useState(false); @@ -36,16 +38,27 @@ export default function useWinners() { const currentYear = new Date().getFullYear(); const years = Array.from({ length: currentYear - 2000 + 1 }, (_, i) => 2000 + i); - const API_BASE_URL = - import.meta.env.PROD + const API_BASE_URL = import.meta.env.PROD ? "/api" // Relative URL - will use https://showcase.asucapstone.com/api : "http://localhost:3000/api"; + // EFFECT 1: Sync URL with Data + // Rruns whenever URL changes (e.g., clicking "Fall 2024" in Nav) useEffect(() => { - fetch(`${API_BASE_URL}/winners`) + const queryParams = new URLSearchParams(location.search); + const urlSem = queryParams.get("semester") || "all"; + const urlYear = queryParams.get("year") || "all"; + setFilters(prev => ({ ...prev, semester: urlSem, year: urlYear })); // Update the dropdown states to match the URL + + // Fetch from backend using URL params + fetch(`${API_BASE_URL}/winners?semester=${urlSem}&year=${urlYear}`) .then((res) => res.json()) - .then((data) => setPastWinnersData(data)) + .then((data) => { + setPastWinnersData(data); + setHasFiltered(false); // Resets in-page filtering when moving to new year + setSearchValue(""); + }) .catch(() => setPastWinnersData([])); - }, []); + }, [location.search]); // Triggered by URL changes const departmentMap: Record = { "computer-science": "CS/E", @@ -92,11 +105,37 @@ export default function useWinners() { setFilteredWinnersData(searched); }; + // This handles the "In-Page" filter button const handleFilterSubmit = (e?: React.FormEvent) => { e?.preventDefault(); setHasFiltered(true); - const filtered = applySelectFilters(pastWinnersData, filters); - setFilteredWinnersData(filtered); + + let results = [...pastWinnersData]; // To filter the data we already fetched for specific year + + // Apply Department filter + if (filters.department !== "all") { + + const prefix = departmentMap[filters.department]; // e.g., "CS/E" + results = results.filter((entry) => { + const matchesMajor = entry.course === filters.department; // Matches the official Major + const matchesPrefix = entry.ProjectTitle.startsWith(prefix); // OR title starts with the Department Code (CS/E, MEE, etc.) + return matchesMajor || matchesPrefix; + }); + + // old filter + //const deptKey = departmentMap[filters.department]; + //results = results.filter(entry => entry.ProjectTitle.includes(deptKey)); + } + + // Apply Search text + if (searchValue) { + results = applyTextSearch(results, searchValue); + } + setFilteredWinnersData(results); + + // old filter + //const filtered = applySelectFilters(pastWinnersData, filters); + //setFilteredWinnersData(filtered); }; const clearFilters = (e?: React.MouseEvent) => {