From da166c7f7f09767a5307cec9a0089814ee856e9f Mon Sep 17 00:00:00 2001 From: benedekboldizsar Date: Wed, 16 Apr 2025 09:29:46 +0200 Subject: [PATCH 01/49] applied filters into the search function using flags in the model, will have to refine more in the filters, but this is great for the demo --- my-app/src/model.js | 5 +++++ my-app/src/pages/App.jsx | 2 +- my-app/src/presenters/FilterPresenter.jsx | 5 +++-- my-app/src/presenters/SearchbarPresenter.jsx | 7 ++++++- my-app/src/views/ListView.jsx | 6 +++--- 5 files changed, 18 insertions(+), 7 deletions(-) diff --git a/my-app/src/model.js b/my-app/src/model.js index 6fc835fa..7e323ed9 100644 --- a/my-app/src/model.js +++ b/my-app/src/model.js @@ -10,6 +10,7 @@ export const model = { favourites: [], isReady: false, filtersChange: false, + filtersCalculated: false, filteredCourses: [], filterOptions: { applyTranscriptFilter: true, @@ -108,6 +109,10 @@ export const model = { setFiltersChange() { this.filtersChange = true; }, + + setFiltersCalculated() { + this.filtersCalculated = true; + }, updateLevelFilter(level) { this.filterOptions.level = level; diff --git a/my-app/src/pages/App.jsx b/my-app/src/pages/App.jsx index 206dedce..ac7e426e 100644 --- a/my-app/src/pages/App.jsx +++ b/my-app/src/pages/App.jsx @@ -9,7 +9,7 @@ import { model } from '/src/model.js'; function MainAppLayout({ model }) { return ( -
+
diff --git a/my-app/src/presenters/FilterPresenter.jsx b/my-app/src/presenters/FilterPresenter.jsx index 2e356084..007445f7 100644 --- a/my-app/src/presenters/FilterPresenter.jsx +++ b/my-app/src/presenters/FilterPresenter.jsx @@ -28,8 +28,8 @@ const FilterPresenter = observer(({ model }) => { //console.log(course?.prerequisites); if(course?.prerequisites && (course.prerequisites !== "null")) var resultEligibility = eligibility(storedFinishedCourses, course?.prerequisites); - else{ - //zerocourses.push(course); + else{ // {strong: , zero: , moderate: , weak: } + zerocourses.push(course); return; } if(resultEligibility.strong){ @@ -292,6 +292,7 @@ const FilterPresenter = observer(({ model }) => { model.filteredCourses = [...localFilteredCourses]; model.filtersChange = false; + model.setFiltersCalculated(); console.log("filtered objects number of elements: ", model.filteredCourses.length); } }); diff --git a/my-app/src/presenters/SearchbarPresenter.jsx b/my-app/src/presenters/SearchbarPresenter.jsx index 1e127dca..c1a86f77 100644 --- a/my-app/src/presenters/SearchbarPresenter.jsx +++ b/my-app/src/presenters/SearchbarPresenter.jsx @@ -11,7 +11,7 @@ const SearchbarPresenter = observer(({ model }) => { //model.filteredCourses is essentially a smaller subset of model.courses, if theres no filters, it should be the same console.log("---------------search recalculated"); console.log("filtered courses length: ", model.filteredCourses.length); - const searchResults = model.courses.filter(course => + const searchResults = model.filteredCourses.filter(course => course.code.toLowerCase().includes(query.toLowerCase()) || course.name.toLowerCase().includes(query.toLowerCase()) || course.description.toLowerCase().includes(query.toLowerCase()) @@ -61,6 +61,11 @@ const SearchbarPresenter = observer(({ model }) => { prerequisiteTree={preP} />; + if(model.filtersCalculated){ + searchCourses(""); + model.filtersCalculated = false; + } + return ( 0 + const coursesToDisplay = props.searchResults; + /*(props.searchResults && props.searchResults.length > 0 ? props.searchResults - : props.courses) || []; + : props.courses) || [];*/ const [displayedCourses, setDisplayedCourses] = useState([]); const [hasMore, setHasMore] = useState(true); From 190a822d8643a64c2b9622c09de2c01d6f57baa9 Mon Sep 17 00:00:00 2001 From: benedekboldizsar Date: Wed, 16 Apr 2025 10:18:01 +0200 Subject: [PATCH 02/49] wrote departments filter option --- my-app/src/model.js | 3 ++- my-app/src/presenters/FilterPresenter.jsx | 31 +++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/my-app/src/model.js b/my-app/src/model.js index 7e323ed9..5e8d3c08 100644 --- a/my-app/src/model.js +++ b/my-app/src/model.js @@ -24,7 +24,8 @@ export const model = { applyCreditsFilter:true, creditMin: 0, creditMax: 45, - //applyDepartmentFilter:false, + applyDepartmentFilter:false, + department: [] }, setUser(user) { diff --git a/my-app/src/presenters/FilterPresenter.jsx b/my-app/src/presenters/FilterPresenter.jsx index 007445f7..46c71b84 100644 --- a/my-app/src/presenters/FilterPresenter.jsx +++ b/my-app/src/presenters/FilterPresenter.jsx @@ -267,6 +267,33 @@ const FilterPresenter = observer(({ model }) => { } localFilteredCourses = [...stayingCourses];*/ } + + function updateDepartments(){ + const deparments = model.filterOptions.deparment; + let bestCourses = []; + let worstCourses = []; + + bestCourses = localFilteredCourses.filter(function(course) { + try { + return (deparments.includes(course.deparment)); + } catch (error) { + console.log("for some reason course.department is: ", course?.department, error); + return false; + } + + }); + worstCourses = localFilteredCourses.filter(function(course) { + try { + return ((course?.department === undefined) || (course?.deparment === "null")); + } catch (error) { + console.log("BIG ERROR", error); + return false; + } + + }); + + localFilteredCourses = [...bestCourses, ...worstCourses]; + } if (model.filtersChange) { localFilteredCourses = [...model.courses]; @@ -289,6 +316,10 @@ const FilterPresenter = observer(({ model }) => { if (model.filterOptions.applyTranscriptFilter) { applyTranscriptEligibility(); } + if (model.filterOptions.applyDepartments) { + //console.log("going to apply location on:",localFilteredCourses.length); + //updateDepartments(); + } model.filteredCourses = [...localFilteredCourses]; model.filtersChange = false; From 7ca07a11877222994152e4e33cc11b883d8b13a5 Mon Sep 17 00:00:00 2001 From: benedekboldizsar Date: Wed, 16 Apr 2025 11:26:04 +0200 Subject: [PATCH 03/49] added check to make sure to not recommend courses the client has taken before --- my-app/src/presenters/FilterPresenter.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/my-app/src/presenters/FilterPresenter.jsx b/my-app/src/presenters/FilterPresenter.jsx index 46c71b84..70f45f5a 100644 --- a/my-app/src/presenters/FilterPresenter.jsx +++ b/my-app/src/presenters/FilterPresenter.jsx @@ -26,6 +26,8 @@ const FilterPresenter = observer(({ model }) => { localFilteredCourses.forEach(course => { //console.log(storedFinishedCourses); //console.log(course?.prerequisites); + if(storedFinishedCourses.includes(course?.code)) + return; if(course?.prerequisites && (course.prerequisites !== "null")) var resultEligibility = eligibility(storedFinishedCourses, course?.prerequisites); else{ // {strong: , zero: , moderate: , weak: } From df2872b446c4a44eb4e26cf45208f0a338f81a8d Mon Sep 17 00:00:00 2001 From: kexana Date: Wed, 16 Apr 2025 14:04:19 +0200 Subject: [PATCH 04/49] initial state good --- my-app/src/model.js | 6 +- my-app/src/pages/App.jsx | 2 +- my-app/src/presenters/FilterPresenter.jsx | 181 ++++++++++-------- my-app/src/presenters/SearchbarPresenter.jsx | 3 +- my-app/src/presenters/SidebarPresenter.jsx | 20 +- .../presenters/UploadTranscriptPresenter.jsx | 5 +- .../CollapsibleCheckboxes.jsx | 8 +- .../CourseTranscriptList.jsx | 5 +- .../SideBarComponents/DropDownField.jsx | 9 +- .../SideBarComponents/UploadField.jsx | 5 +- my-app/src/views/ListView.jsx | 9 +- my-app/src/views/SidebarView.jsx | 1 + 12 files changed, 158 insertions(+), 96 deletions(-) diff --git a/my-app/src/model.js b/my-app/src/model.js index 5e8d3c08..e118a497 100644 --- a/my-app/src/model.js +++ b/my-app/src/model.js @@ -16,7 +16,7 @@ export const model = { applyTranscriptFilter: true, eligibility: "weak", //the possible values for the string are: "weak"/"moderate"/"strong" applyLevelFilter: true, - level: [], //the possible values for the array are: "PREPARATORY", "BASIC", "ADVANCED", "RESEARCH" + level: ["PREPARATORY", "BASIC", "ADVANCED", "RESEARCH"], //the possible values for the array are: "PREPARATORY", "BASIC", "ADVANCED", "RESEARCH" applyLanguageFilter: true, language: "none", //the possible values for the string are: "none"/"english"/"swedish"/"both" applyLocationFilter:true, @@ -24,7 +24,7 @@ export const model = { applyCreditsFilter:true, creditMin: 0, creditMax: 45, - applyDepartmentFilter:false, + applyDepartmentFilter: true, department: [] }, @@ -138,6 +138,8 @@ export const model = { }, setApplyLevelFilter(levelFilterState) { this.filterOptions.applyLevelFilter = levelFilterState; + + console.log("model -",this.filterOptions.level); }, setApplyLanguageFilter(languageFilterState) { this.filterOptions.applyLanguageFilter = languageFilterState; diff --git a/my-app/src/pages/App.jsx b/my-app/src/pages/App.jsx index ac7e426e..0beae15d 100644 --- a/my-app/src/pages/App.jsx +++ b/my-app/src/pages/App.jsx @@ -10,7 +10,6 @@ import { model } from '/src/model.js'; function MainAppLayout({ model }) { return (
-
@@ -21,6 +20,7 @@ function MainAppLayout({ model }) {
+
); diff --git a/my-app/src/presenters/FilterPresenter.jsx b/my-app/src/presenters/FilterPresenter.jsx index 70f45f5a..a673d1ab 100644 --- a/my-app/src/presenters/FilterPresenter.jsx +++ b/my-app/src/presenters/FilterPresenter.jsx @@ -1,13 +1,14 @@ import React from 'react'; import { observer } from "mobx-react-lite"; import eligibility from "../scripts/eligibility_refined.js"; +import { SearchbarPresenter } from './SearchbarPresenter.jsx'; const FilterPresenter = observer(({ model }) => { var localFilteredCourses = []; //might need to declare out of scope. idk js function applyTranscriptEligibility() { - if(localFilteredCourses.length == 0) + if (localFilteredCourses.length == 0) return; /* this elias thing */ const eligibilitytype = model.filterOptions.eligibility; @@ -22,38 +23,38 @@ const FilterPresenter = observer(({ model }) => { if (localStorage.getItem("completedCourses")) storedFinishedCourses = JSON.parse(localStorage.getItem("completedCourses")); - + localFilteredCourses.forEach(course => { //console.log(storedFinishedCourses); //console.log(course?.prerequisites); - if(storedFinishedCourses.includes(course?.code)) + if (storedFinishedCourses.includes(course?.code)) return; - if(course?.prerequisites && (course.prerequisites !== "null")) + if (course?.prerequisites && (course.prerequisites !== "null")) var resultEligibility = eligibility(storedFinishedCourses, course?.prerequisites); - else{ // {strong: , zero: , moderate: , weak: } + else { // {strong: , zero: , moderate: , weak: } zerocourses.push(course); return; } - if(resultEligibility.strong){ + if (resultEligibility.strong) { strongcourses.push(course); return; - }else if(resultEligibility.zero){ + } else if (resultEligibility.zero) { zerocourses.push(course); return; - }else if(resultEligibility.moderate){ + } else if (resultEligibility.moderate) { moderatecourses.push(course); return; - }else if(resultEligibility.weak){ + } else if (resultEligibility.weak) { weakcourses.push(course); return; - }else{ + } else { //it's not eligible at all return; } - + }); - switch(eligibilitytype){ + switch (eligibilitytype) { case "strong": { localFilteredCourses = [...strongcourses, ...zerocourses]; @@ -67,7 +68,7 @@ const FilterPresenter = observer(({ model }) => { case "weak": { localFilteredCourses = [...strongcourses, ...moderatecourses, ...weakcourses, ...zerocourses]; - break; + break; } default: { @@ -76,24 +77,24 @@ const FilterPresenter = observer(({ model }) => { break; } } - + } function updateCredits() { - if(localFilteredCourses.length == 0) + if (localFilteredCourses.length == 0) return; const min = model.filterOptions.creditMin; const max = model.filterOptions.creditMax; - localFilteredCourses = localFilteredCourses.filter(function(course){ + localFilteredCourses = localFilteredCourses.filter(function (course) { try { - return ((course.credits >= min)&&(course.credits <= max)); + return ((course.credits >= min) && (course.credits <= max)); } catch (error) { console.log("for some reason course.credits is: ", course?.credits, error); return false; } - + }); } @@ -105,31 +106,31 @@ const FilterPresenter = observer(({ model }) => { let bestCourses = []; let worstCourses = []; - bestCourses = localFilteredCourses.filter(function(course) { + bestCourses = localFilteredCourses.filter(function (course) { try { return (locations.includes(course.location)); } catch (error) { console.log("for some reason course.location is: ", course?.location, error); return false; } - + }); - worstCourses = localFilteredCourses.filter(function(course) { + worstCourses = localFilteredCourses.filter(function (course) { try { return ((course?.location === undefined) || (course?.location === "null")); } catch (error) { console.log("BIG ERROR", error); return false; } - + }); localFilteredCourses = [...bestCourses, ...worstCourses]; - + } function updateLanguages() { - if(localFilteredCourses.length == 0) + if (localFilteredCourses.length == 0) return; //possible model.filterOptions.languages values: "none"/"english"/"swedish"/"both" const languages = model.filterOptions.language; @@ -143,7 +144,7 @@ const FilterPresenter = observer(({ model }) => { //course.language.swedish (true/false/"null") //console.log(data); - + switch (languages) { case "none": { @@ -160,18 +161,18 @@ const FilterPresenter = observer(({ model }) => { console.log("BIG ERROR", error); return false; } - + } ); - worstCourses = data.filter(function(course) { + worstCourses = data.filter(function (course) { try { - return ((course?.language === undefined ) || course?.language?.english === "null"); + return ((course?.language === undefined) || course?.language?.english === "null"); } catch (error) { console.log(course); console.log("BIG ERROR"); return false; } - + }); break; } @@ -185,18 +186,18 @@ const FilterPresenter = observer(({ model }) => { console.log("BIG ERROR"); return false; } - + } ); - worstCourses = data.filter(function(course) { + worstCourses = data.filter(function (course) { try { - return ((course?.language === undefined ) || course?.language?.swedish === "null"); + return ((course?.language === undefined) || course?.language?.swedish === "null"); } catch (error) { console.log(course); console.log("BIG ERROR"); return false; } - + }); break; } @@ -210,30 +211,30 @@ const FilterPresenter = observer(({ model }) => { console.log("BIG ERROR"); return false; } - + } ); middleCourses = data.filter(function (course) { try { return (((course?.language?.english === true) && (course?.language?.swedish === false)) - || ((course?.language?.english === false) && (course?.language?.swedish === true))); + || ((course?.language?.english === false) && (course?.language?.swedish === true))); } catch (error) { console.log(course); console.log("BIG ERROR"); return false; } - - } + + } ); - worstCourses = data.filter(function(course) { + worstCourses = data.filter(function (course) { try { - return ((course?.language === undefined ) || course?.language?.english === "null"); + return ((course?.language === undefined) || course?.language?.english === "null"); } catch (error) { console.log(course); console.log("BIG ERROR"); return false; } - + }); break; } @@ -243,16 +244,16 @@ const FilterPresenter = observer(({ model }) => { } function updateLevels() { - if(localFilteredCourses.length == 0) + if (localFilteredCourses.length == 0) return; //the possible values are: "PREPARATORY", "BASIC", "ADVANCED", "RESEARCH" //model.filterOptions.level is an array. it can have [] const levels = model.filterOptions.level; - - + + localFilteredCourses = localFilteredCourses.filter(course => levels.includes(course.academicLevel)); - + /* let levels = model.filterOptions.level; let stayingCourses = []; @@ -270,64 +271,90 @@ const FilterPresenter = observer(({ model }) => { localFilteredCourses = [...stayingCourses];*/ } - function updateDepartments(){ + function updateDepartments() { const deparments = model.filterOptions.deparment; let bestCourses = []; let worstCourses = []; - bestCourses = localFilteredCourses.filter(function(course) { + bestCourses = localFilteredCourses.filter(function (course) { try { return (deparments.includes(course.deparment)); } catch (error) { console.log("for some reason course.department is: ", course?.department, error); return false; } - + }); - worstCourses = localFilteredCourses.filter(function(course) { + worstCourses = localFilteredCourses.filter(function (course) { try { return ((course?.department === undefined) || (course?.deparment === "null")); } catch (error) { console.log("BIG ERROR", error); return false; } - + }); localFilteredCourses = [...bestCourses, ...worstCourses]; } - - if (model.filtersChange) { - localFilteredCourses = [...model.courses]; + async function run() { + if (model.courses.length == 0) { + return; + } + if (model.filtersChange) { + localFilteredCourses = [...model.courses]; - if (model.filterOptions.applyLocationFilter) { - //after deo finishes locations, until then dont + if (model.filterOptions.applyLocationFilter) { + //after deo finishes locations, until then dont - //console.log("going to apply location on:",localFilteredCourses.length); - //updateLocations(); - } - if (model.filterOptions.applyLevelFilter) { - updateLevels(); - } - if (model.filterOptions.applyLanguageFilter) { - updateLanguages(); - } - if (model.filterOptions.applyCreditsFilter) { - updateCredits(); - } - if (model.filterOptions.applyTranscriptFilter) { - applyTranscriptEligibility(); - } - if (model.filterOptions.applyDepartments) { - //console.log("going to apply location on:",localFilteredCourses.length); - //updateDepartments(); - } + //console.log("going to apply location on:",localFilteredCourses.length); + //updateLocations(); + } + if (model.filterOptions.applyLevelFilter) { + updateLevels(); + } + if (model.filterOptions.applyLanguageFilter) { + updateLanguages(); + } + if (model.filterOptions.applyCreditsFilter) { + updateCredits(); + } + if (model.filtersChange) { + localFilteredCourses = [...model.courses]; + + if (model.filterOptions.applyLocationFilter) { + //after deo finishes locations, until then dont - model.filteredCourses = [...localFilteredCourses]; - model.filtersChange = false; - model.setFiltersCalculated(); - console.log("filtered objects number of elements: ", model.filteredCourses.length); + //console.log("going to apply location on:",localFilteredCourses.length); + //updateLocations(); + } + if (model.filterOptions.applyLevelFilter) { + updateLevels(); + } + if (model.filterOptions.applyLanguageFilter) { + updateLanguages(); + } + if (model.filterOptions.applyCreditsFilter) { + updateCredits(); + } + if (model.filterOptions.applyTranscriptFilter) { + applyTranscriptEligibility(); + } + if (model.filterOptions.applyDepartments) { + //console.log("going to apply location on:",localFilteredCourses.length); + //updateDepartments(); + } + + model.filteredCourses = [...localFilteredCourses]; + model.filtersChange = false; + model.setFiltersCalculated(); + console.log("filtered objects number of elements: ", model.filteredCourses.length); + } + } } + + run(); + }); export { FilterPresenter }; \ No newline at end of file diff --git a/my-app/src/presenters/SearchbarPresenter.jsx b/my-app/src/presenters/SearchbarPresenter.jsx index c1a86f77..2e9ecb2b 100644 --- a/my-app/src/presenters/SearchbarPresenter.jsx +++ b/my-app/src/presenters/SearchbarPresenter.jsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import { observer } from "mobx-react-lite"; import { useState } from 'react'; import CoursePagePopup from '../views/Components/CoursePagePopup.jsx'; @@ -7,6 +7,7 @@ import { ReviewPresenter } from "../presenters/ReviewPresenter.jsx"; import SearchbarView from "../views/SearchbarView.jsx"; const SearchbarPresenter = observer(({ model }) => { + const searchCourses = (query) => { //model.filteredCourses is essentially a smaller subset of model.courses, if theres no filters, it should be the same console.log("---------------search recalculated"); diff --git a/my-app/src/presenters/SidebarPresenter.jsx b/my-app/src/presenters/SidebarPresenter.jsx index 42bab7f2..039670e3 100644 --- a/my-app/src/presenters/SidebarPresenter.jsx +++ b/my-app/src/presenters/SidebarPresenter.jsx @@ -1,12 +1,16 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import { observer } from "mobx-react-lite"; import SidebarView from "../views/SidebarView.jsx"; const SidebarPresenter = observer(({ model }) => { + useEffect(() => { + model.setFiltersChange(); + }) + let currentLanguageSet = 'none'; - let currentLevelSet = []; + let currentLevelSet = ["PREPARATORY", "BASIC", "ADVANCED", "RESEARCH"]; function handleLanguageFilterChange(param) { if (param === "English") { switch (currentLanguageSet) { @@ -117,6 +121,8 @@ const SidebarPresenter = observer(({ model }) => { break; case "level": console.log("level filter set to: " + param[1]); + + console.log("model -",model.filterOptions.level); model.setApplyLevelFilter(param[1]); break; case "location": @@ -136,9 +142,17 @@ const SidebarPresenter = observer(({ model }) => { } model.setFiltersChange(); } + function reApplyFilter() { + model.setFiltersChange(); + } + + + return ( + HandleFilterEnable={HandleFilterEnable} + reApplyFilter = {reApplyFilter} + /> ); }); diff --git a/my-app/src/presenters/UploadTranscriptPresenter.jsx b/my-app/src/presenters/UploadTranscriptPresenter.jsx index c473d157..7bd85005 100644 --- a/my-app/src/presenters/UploadTranscriptPresenter.jsx +++ b/my-app/src/presenters/UploadTranscriptPresenter.jsx @@ -167,6 +167,8 @@ const UploadTranscriptPresenter = observer((props) => { } writeLocalStorage_completedCourses(scrapedCodes); //console.log(localStorage.getItem("completedCourses")); + + props.reApplyFilter(); } const handleFileChange = (event) => { @@ -185,7 +187,8 @@ const UploadTranscriptPresenter = observer((props) => { fileInputValue={fileInputValue} HandleFilterEnable={props.HandleFilterEnable} HandleFilterChange={props.HandleFilterChange} - filterName= {props.filterName} + filterName={props.filterName} + reApplyFilter = {props.reApplyFilter} />); }); diff --git a/my-app/src/views/Components/SideBarComponents/CollapsibleCheckboxes.jsx b/my-app/src/views/Components/SideBarComponents/CollapsibleCheckboxes.jsx index 8d445a05..2ec89c70 100644 --- a/my-app/src/views/Components/SideBarComponents/CollapsibleCheckboxes.jsx +++ b/my-app/src/views/Components/SideBarComponents/CollapsibleCheckboxes.jsx @@ -7,7 +7,7 @@ const CollapsibleCheckboxes = (props) => { const [checkedSubItems, setCheckedSubItems] = useState({}); const [stupidLines, setStupidLines] = useState(0); - const strokeWidth = 2; + const strokeWidth = 5; const toggleExpand = (id, subItems) => { setExpanded((prev) => ({ @@ -107,13 +107,13 @@ const CollapsibleCheckboxes = (props) => { { + setSelectedItems(items); + }, [items]); + const toggleDropdown = () => setIsOpen(!isOpen); const handleCheckboxChange = (item) => { setSelectedItems((prev) => prev.includes(item) ? prev.filter((i) => i !== item) : [...prev, item] ); + console.log(item); props.HandleFilterChange([paramFieldType, props.filterName, item]); }; @@ -52,7 +57,7 @@ export default function DropDownField(props) {
-
+
{/* Dropdown Button */}
); diff --git a/my-app/src/views/ListView.jsx b/my-app/src/views/ListView.jsx index 72f4e8f6..3ef9a523 100644 --- a/my-app/src/views/ListView.jsx +++ b/my-app/src/views/ListView.jsx @@ -3,6 +3,8 @@ import { DotPulse, Quantum } from 'ldrs/react'; import 'ldrs/react/Quantum.css'; import InfiniteScroll from 'react-infinite-scroll-component'; +var startupFlag = true; + function ListView(props) { const coursesToDisplay = props.searchResults; /*(props.searchResults && props.searchResults.length > 0 @@ -21,6 +23,12 @@ function ListView(props) { })); }; + if (startupFlag) { + startupFlag = false; + console.log("first presenter"); + //model.setFiltersCalculated(); +} + const handleFavouriteClick = (course) => { if (props.favouriteCourses?.some((fav) => fav.code === course.code)) { props.removeFavourite(course); @@ -54,7 +62,6 @@ function ListView(props) {
); } - return (
{isLoading ? ( diff --git a/my-app/src/views/SidebarView.jsx b/my-app/src/views/SidebarView.jsx index d9afe5ed..f7b92def 100644 --- a/my-app/src/views/SidebarView.jsx +++ b/my-app/src/views/SidebarView.jsx @@ -24,6 +24,7 @@ function SidebarView(props) { HandleFilterChange={props.HandleFilterChange} filterName = "transcript" HandleFilterEnable={props.HandleFilterEnable} + reApplyFilter = {props.reApplyFilter} />
From d8f15435d0a6d75d31a41ebb66fc1d0396b56458 Mon Sep 17 00:00:00 2001 From: kexana Date: Wed, 16 Apr 2025 15:29:47 +0200 Subject: [PATCH 05/49] departments go to model --- my-app/src/model.js | 18 ++++-- my-app/src/presenters/SidebarPresenter.jsx | 32 ++++++++-- .../CollapsibleCheckboxes.jsx | 37 ++---------- my-app/src/views/SidebarView.jsx | 59 ++++++++++++++++--- 4 files changed, 98 insertions(+), 48 deletions(-) diff --git a/my-app/src/model.js b/my-app/src/model.js index e118a497..15f3275b 100644 --- a/my-app/src/model.js +++ b/my-app/src/model.js @@ -25,7 +25,10 @@ export const model = { creditMin: 0, creditMax: 45, applyDepartmentFilter: true, - department: [] + department: ["EECS/Computational Science and Technology", "EECS/Theoretical Computer Science", "EECS/Electric Power and Energy Systems", "EECS/Network and Systems Engineering", + "ITM/Learning in Engineering Sciences", "ITM/Industrial Economics and Management", "ITM/Energy Systems", "ITM/Integrated Product Development and Design", "ITM/SKD GRU", + "SCI/Mathematics", "SCI/Applied Physics", "SCI/Mechanics", "SCI/Aeronautical and Vehicle Engineering", + "ABE/Sustainability and Environmental Engineering", "ABE/Concrete Structures", "ABE/Structural Design & Bridges", "ABE/History of Science, Technology and Environment", ] }, setUser(user) { @@ -132,14 +135,17 @@ export const model = { this.filterOptions.eligibility = eligibility; }, + updateDepartmentFilter(department) { + console.log(department); + this.filterOptions.department = department; + }, + //setters for the filter options setApplyTranscriptFilter(transcriptFilterState) { this.filterOptions.applyTranscriptFilter = transcriptFilterState; }, setApplyLevelFilter(levelFilterState) { this.filterOptions.applyLevelFilter = levelFilterState; - - console.log("model -",this.filterOptions.level); }, setApplyLanguageFilter(languageFilterState) { this.filterOptions.applyLanguageFilter = languageFilterState; @@ -150,9 +156,9 @@ export const model = { setApplyCreditsFilter(creditsFilterState) { this.filterOptions.applyCreditsFilter = creditsFilterState; }, - // setApplyDepartmentFilter(departmentFilterState) { - // this.filterOptions.applyDepartmentFilter = departmentFilterState; - // }, + setApplyDepartmentFilter(departmentFilterState) { + this.filterOptions.applyDepartmentFilter = departmentFilterState; + }, diff --git a/my-app/src/presenters/SidebarPresenter.jsx b/my-app/src/presenters/SidebarPresenter.jsx index 039670e3..ed6f4afe 100644 --- a/my-app/src/presenters/SidebarPresenter.jsx +++ b/my-app/src/presenters/SidebarPresenter.jsx @@ -11,6 +11,12 @@ const SidebarPresenter = observer(({ model }) => { let currentLanguageSet = 'none'; let currentLevelSet = ["PREPARATORY", "BASIC", "ADVANCED", "RESEARCH"]; + let currentDepartmentSet = [ + "EECS/Computational Science and Technology", "EECS/Theoretical Computer Science", "EECS/Electric Power and Energy Systems", "EECS/Network and Systems Engineering", + "ITM/Learning in Engineering Sciences", "ITM/Industrial Economics and Management", "ITM/Energy Systems", "ITM/Integrated Product Development and Design", "ITM/SKD GRU", + "SCI/Mathematics", "SCI/Applied Physics", "SCI/Mechanics", "SCI/Aeronautical and Vehicle Engineering", + "ABE/Sustainability and Environmental Engineering", "ABE/Concrete Structures", "ABE/Structural Design & Bridges", "ABE/History of Science, Technology and Environment", + ] function handleLanguageFilterChange(param) { if (param === "English") { switch (currentLanguageSet) { @@ -77,6 +83,19 @@ const SidebarPresenter = observer(({ model }) => { model.updateLevelFilter(currentLevelSet); } + function handleDepartmentFilterChange(param) { + if (currentDepartmentSet.includes(param)) { + const index = currentDepartmentSet.indexOf(param); + if (index > -1) { + currentDepartmentSet.splice(index, 1); + } + } else { + currentDepartmentSet.push(param); + } + model.updateDepartmentFilter(currentDepartmentSet); + model.setFiltersChange(); + } + /*HandleFilterChange param is structured as such [ type of the field: (toggle, slider, dropdown, buttongroup) @@ -101,6 +120,9 @@ const SidebarPresenter = observer(({ model }) => { case "eligibility": model.updateTranscriptElegibilityFilter(param[2].toLowerCase()); break; + case "department": + handleDepartmentFilterChange(param[2]); + break; default: console.log("Invalid filter type"); } @@ -121,8 +143,6 @@ const SidebarPresenter = observer(({ model }) => { break; case "level": console.log("level filter set to: " + param[1]); - - console.log("model -",model.filterOptions.level); model.setApplyLevelFilter(param[1]); break; case "location": @@ -137,6 +157,10 @@ const SidebarPresenter = observer(({ model }) => { console.log("transcript filter set to: " + param[1]); model.setApplyTranscriptFilter(param[1]); break; + case "department": + console.log("department filter set to: " + param[1]); + model.setApplyDepartmentFilter(param[1]); + break; default: console.log("Invalid filter type"); } @@ -146,12 +170,12 @@ const SidebarPresenter = observer(({ model }) => { model.setFiltersChange(); } - + return ( ); }); diff --git a/my-app/src/views/Components/SideBarComponents/CollapsibleCheckboxes.jsx b/my-app/src/views/Components/SideBarComponents/CollapsibleCheckboxes.jsx index 2ec89c70..be644fde 100644 --- a/my-app/src/views/Components/SideBarComponents/CollapsibleCheckboxes.jsx +++ b/my-app/src/views/Components/SideBarComponents/CollapsibleCheckboxes.jsx @@ -9,6 +9,10 @@ const CollapsibleCheckboxes = (props) => { const strokeWidth = 5; + let paramFieldType = "checkboxhierarchy"; + + const rows = props.fields; + const toggleExpand = (id, subItems) => { setExpanded((prev) => ({ ...prev, @@ -33,38 +37,9 @@ const CollapsibleCheckboxes = (props) => { ...prev, [key]: !prev[key], })); + props.HandleFilterChange([paramFieldType, props.filterName, rows.find(item => item.id === mainId).label+"/"+rows.find(item => item.id === mainId).subItems[index]]); }; - const rows = [ - { - id: 1, - label: "Category 1", - subItems: ["Sub-item 1.1", "Sub-item 1.2", "Sub-item 1.3"], - }, - { - id: 2, - label: "Category 2", - subItems: ["Sub-item 2.1", "Sub-item 2.2"], - }, - { - id: 3, - label: "Category 2", - subItems: ["Sub-item 2.1", "Sub-item 2.2"], - }, - { - id: 4, - label: "Category 3", - subItems: [ - "Sub-item 3.1", - "Sub-item 3.2", - "Sub-item 3.3", - "Sub-item 3.4", - "Sub-item 3.5", - "Sub-item 3.6", - ], - }, - ]; - return (
@@ -190,7 +165,7 @@ const CollapsibleCheckboxes = (props) => { onChange={() => toggleSubCheckbox(row.id, index)} />
); diff --git a/my-app/src/views/SidebarView.jsx b/my-app/src/views/SidebarView.jsx index f7b92def..04c016a0 100644 --- a/my-app/src/views/SidebarView.jsx +++ b/my-app/src/views/SidebarView.jsx @@ -18,27 +18,27 @@ function SidebarView(props) { }} >
- Filters -
+ Filters +
- + @@ -59,6 +59,51 @@ function SidebarView(props) { HandleFilterChange={props.HandleFilterChange} filterName="department" HandleFilterEnable={props.HandleFilterEnable} + fields={ + [ + { + id: 1, + label: "EECS", + subItems: [ + "Computational Science and Technology", + "Theoretical Computer Science", + "Electric Power and Energy Systems", + "Network and Systems Engineering", + ], + }, + { + id: 2, + label: "ITM", + subItems: [ + "Learning in Engineering Sciences", + "Industrial Economics and Management", + "Energy Systems", + "Integrated Product Development and Design", + "SKD GRU", + ], + }, + { + id: 3, + label: "SCI", + subItems: [ + "Mathematics", + "Applied Physics", + "Mechanics", + "Aeronautical and Vehicle Engineering", + ], + }, + { + id: 4, + label: "ABE", + subItems: [ + "Sustainability and Environmental Engineering", + "Concrete Structures", + "Structural Design & Bridges", + "History of Science, Technology and Environment", + ], + }, + ] + } />
From b162882a5accaf5be341240df8c51041e0b87527 Mon Sep 17 00:00:00 2001 From: benedekboldizsar Date: Wed, 16 Apr 2025 15:31:36 +0200 Subject: [PATCH 06/49] trying to add filtering out courses with NULL fields as an implementable filter option, merging with main start as per teams request --- my-app/src/model.js | 3 +- my-app/src/presenters/FilterPresenter.jsx | 92 +++++++++++++++-------- my-app/src/views/ListView.jsx | 2 +- 3 files changed, 64 insertions(+), 33 deletions(-) diff --git a/my-app/src/model.js b/my-app/src/model.js index d259614d..12ed3897 100644 --- a/my-app/src/model.js +++ b/my-app/src/model.js @@ -27,7 +27,8 @@ export const model = { creditMin: 0, creditMax: 45, applyDepartmentFilter: true, - department: [] + department: [], + applyRemoveNullCourses: false }, setUser(user) { diff --git a/my-app/src/presenters/FilterPresenter.jsx b/my-app/src/presenters/FilterPresenter.jsx index a673d1ab..0a7df957 100644 --- a/my-app/src/presenters/FilterPresenter.jsx +++ b/my-app/src/presenters/FilterPresenter.jsx @@ -252,7 +252,7 @@ const FilterPresenter = observer(({ model }) => { const levels = model.filterOptions.level; - localFilteredCourses = localFilteredCourses.filter(course => levels.includes(course.academicLevel)); + localFilteredCourses = localFilteredCourses.filter(course => levels.includes(course?.academicLevel)); /* let levels = model.filterOptions.level; @@ -297,6 +297,52 @@ const FilterPresenter = observer(({ model }) => { localFilteredCourses = [...bestCourses, ...worstCourses]; } + + function updateNoNullcourses(){ + let local = [...localFilteredCourses]; + + console.log("miauuuuu:",local.length); + + if(model.filterOptions.applyTranscriptFilter){ + local = local.filter(function(course){ + return (course?.prerequisites && (course.prerequisites !== "null")); + }) + } + console.log("miauuuuu:",local.length); + if(model.filterOptions.applyLevelFilter){ + local = local.filter(function(course){ + return (course?.prerequisites && (course.prerequisites !== "null")); + }) + } + console.log("miauuuuu:",local.length); + if(model.filterOptions.applyLanguageFilter){ + local = local.filter(function(course){ + return ((course?.language) && (course?.language?.swedish !== "null" && course?.language?.english !== "null")); + }) + } + console.log("miauuuuu:",local.length); + /*if(model.filterOptions.applyLocationFilter){ + local = local.filter(function(course){ + return ((course?.location) && (course?.location !== "null")); + }) + }*/ + console.log("miauuuuu:",local.length); + if(model.filterOptions.applyCreditsFilter){ + local = local.filter(function(course){ + return ((course?.credit) && (course?.credit !== "null")); + }) + } + console.log("miauuuuu:",local.length); + if(model.filterOptions.applyDepartmentFilter){ + local = local.filter(function(course){ + return ((course?.deparment) && (course?.department !== "null")); + }) + } + console.log("miauuuuu:",local.length); + + localFilteredCourses = [...local]; + } + async function run() { if (model.courses.length == 0) { return; @@ -304,6 +350,9 @@ const FilterPresenter = observer(({ model }) => { if (model.filtersChange) { localFilteredCourses = [...model.courses]; + if (model.filterOptions.applyRemoveNullCourses) { + updateNoNullcourses(); + } if (model.filterOptions.applyLocationFilter) { //after deo finishes locations, until then dont @@ -319,37 +368,18 @@ const FilterPresenter = observer(({ model }) => { if (model.filterOptions.applyCreditsFilter) { updateCredits(); } - if (model.filtersChange) { - localFilteredCourses = [...model.courses]; - - if (model.filterOptions.applyLocationFilter) { - //after deo finishes locations, until then dont - - //console.log("going to apply location on:",localFilteredCourses.length); - //updateLocations(); - } - if (model.filterOptions.applyLevelFilter) { - updateLevels(); - } - if (model.filterOptions.applyLanguageFilter) { - updateLanguages(); - } - if (model.filterOptions.applyCreditsFilter) { - updateCredits(); - } - if (model.filterOptions.applyTranscriptFilter) { - applyTranscriptEligibility(); - } - if (model.filterOptions.applyDepartments) { - //console.log("going to apply location on:",localFilteredCourses.length); - //updateDepartments(); - } - - model.filteredCourses = [...localFilteredCourses]; - model.filtersChange = false; - model.setFiltersCalculated(); - console.log("filtered objects number of elements: ", model.filteredCourses.length); + if (model.filterOptions.applyTranscriptFilter) { + applyTranscriptEligibility(); } + if (model.filterOptions.applyDepartments) { + //console.log("going to apply location on:",localFilteredCourses.length); + //updateDepartments(); + } + + model.filteredCourses = [...localFilteredCourses]; + model.filtersChange = false; + model.setFiltersCalculated(); + console.log("filtered objects number of elements: ", model.filteredCourses.length); } } diff --git a/my-app/src/views/ListView.jsx b/my-app/src/views/ListView.jsx index 8a09b640..9b565ddb 100644 --- a/my-app/src/views/ListView.jsx +++ b/my-app/src/views/ListView.jsx @@ -6,7 +6,7 @@ import InfiniteScroll from 'react-infinite-scroll-component'; var startupFlag = true; function ListView(props) { - const coursesToDisplay = props.searchResults.length > 0 ? props.searchResults : props.courses; + const coursesToDisplay = props.searchResults; const [displayedCourses, setDisplayedCourses] = useState([]); const [hasMore, setHasMore] = useState(true); const [readMore, setReadMore] = useState({}); From 170e1b48c8992062befa6d7c247b89f09fccf5e4 Mon Sep 17 00:00:00 2001 From: benedekboldizsar Date: Wed, 16 Apr 2025 15:38:08 +0200 Subject: [PATCH 07/49] merging into brain? --- my-app/src/views/ListView.jsx | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/my-app/src/views/ListView.jsx b/my-app/src/views/ListView.jsx index 9b565ddb..211a27f2 100644 --- a/my-app/src/views/ListView.jsx +++ b/my-app/src/views/ListView.jsx @@ -3,8 +3,6 @@ import { DotPulse, Quantum } from 'ldrs/react'; import 'ldrs/react/Quantum.css'; import InfiniteScroll from 'react-infinite-scroll-component'; -var startupFlag = true; - function ListView(props) { const coursesToDisplay = props.searchResults; const [displayedCourses, setDisplayedCourses] = useState([]); @@ -42,15 +40,25 @@ function ListView(props) { setHasMore(displayedCourses.length + nextItems.length < coursesToDisplay.length); }, [displayedCourses.length, coursesToDisplay, hasMore]); - if (!props.courses) { - return ( -
-
- ⚠️ No course data available. -
-
- ); - } + const [isRestoringScroll, setIsRestoringScroll] = useState(false); + useEffect(() => { + if (props.targetScroll > 0 && !isRestoringScroll) { + setIsRestoringScroll(true); + props.persistantScrolling(fetchMoreCourses, hasMore); + setIsRestoringScroll(false); + } + }, [props.targetScroll, hasMore, displayedCourses.length]); + + if (!props.courses) { + return ( +
+
+ ⚠️ No course data available. +
+
+ ); + } + return (
{isLoading ? ( From 855f4ab18abd5136cc76be5af1cb732969ac0eb2 Mon Sep 17 00:00:00 2001 From: benedekboldizsar Date: Wed, 16 Apr 2025 16:10:07 +0200 Subject: [PATCH 08/49] some ?. properties check, because skill issue --- my-app/src/presenters/PrerequisitePresenter.jsx | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/my-app/src/presenters/PrerequisitePresenter.jsx b/my-app/src/presenters/PrerequisitePresenter.jsx index c45698f3..f7170b6b 100644 --- a/my-app/src/presenters/PrerequisitePresenter.jsx +++ b/my-app/src/presenters/PrerequisitePresenter.jsx @@ -36,7 +36,7 @@ export const PrerequisitePresenter = observer((props) => { const nodeWidth = 172; const nodeHeight = 36; - loadTree(props.selectedCourse.code); + loadTree(props.selectedCourse?.code); console.log(initialNodes); const getLayoutedElements = (nodes, edges, direction = 'LR') => { @@ -133,7 +133,7 @@ export const PrerequisitePresenter = observer((props) => { node["style"]["zIndex"] = 0; setLabel(node["id"], "More Info..."); } - } else if (node["data"]["label"] !== "One of these" && node["data"]["label"] !== "No Prerequisites" && node["id"] !== props.selectedCourse.code) { + } else if (node["data"]["label"] !== "One of these" && node["data"]["label"] !== "No Prerequisites" && node["id"] !== props.selectedCourse?.code) { // ADD FUNCTIONALITY FOR CLICKING COURSE CODE NODE (Tu eres muy retrasado y gordo)! :) // ONCLICK HERE } @@ -266,7 +266,7 @@ export const PrerequisitePresenter = observer((props) => { } function generateTree(courses_taken, prereqs) { - prereq_convert(courses_taken, prereqs, null, props.selectedCourse.code); + prereq_convert(courses_taken, prereqs, null, props.selectedCourse?.code); let key = Object.keys(prereqs); return prereqs[key]; @@ -275,15 +275,17 @@ export const PrerequisitePresenter = observer((props) => { function loadTree(courses_taken) { - console.log(JSON.stringify(props.selectedCourse.prerequisites, null, 4)); - if (props.selectedCourse.prerequisites === "null" || props.selectedCourse.prerequisites.length == 0) { + console.log(JSON.stringify(props.selectedCourse?.prerequisites, null, 4)); + if(props.selectedCourse?.prerequisites === undefined || props.selectedCourse?.code === undefined) + console.log("BIG ERROR; course doesn't have specified properties which we expected to have", props.selectedCourse); + if (props.selectedCourse?.prerequisites === "null" || props.selectedCourse?.prerequisites.length == 0) { let display_node = createNode("No Prerequisites", "No Prerequisites", "default"); display_node.style["pointerEvents"] = "none"; display_node["className"] = 'no-handles'; initialNodes.push(display_node); } else { - let root = createNode(props.selectedCourse.code, props.selectedCourse.code, "input"); - let copy = JSON.parse(JSON.stringify(props.selectedCourse.prerequisites)); + let root = createNode(props.selectedCourse?.code, props.selectedCourse?.code, "input"); + let copy = JSON.parse(JSON.stringify(props.selectedCourse?.prerequisites)); let eligible = generateTree(JSON.parse(localStorage.getItem("completedCourses")), copy); if (eligible) { root["style"]["backgroundColor"] = "lightgreen"; From fb0554b586094fe2ca67fd27678adedd1673afcf Mon Sep 17 00:00:00 2001 From: benedekboldizsar Date: Wed, 16 Apr 2025 16:15:38 +0200 Subject: [PATCH 09/49] more course?. things --- my-app/src/presenters/FilterPresenter.jsx | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/my-app/src/presenters/FilterPresenter.jsx b/my-app/src/presenters/FilterPresenter.jsx index 0a7df957..b7d94dbb 100644 --- a/my-app/src/presenters/FilterPresenter.jsx +++ b/my-app/src/presenters/FilterPresenter.jsx @@ -29,7 +29,7 @@ const FilterPresenter = observer(({ model }) => { //console.log(course?.prerequisites); if (storedFinishedCourses.includes(course?.code)) return; - if (course?.prerequisites && (course.prerequisites !== "null")) + if (course?.prerequisites && (course?.prerequisites !== "null")) var resultEligibility = eligibility(storedFinishedCourses, course?.prerequisites); else { // {strong: , zero: , moderate: , weak: } zerocourses.push(course); @@ -89,9 +89,9 @@ const FilterPresenter = observer(({ model }) => { localFilteredCourses = localFilteredCourses.filter(function (course) { try { - return ((course.credits >= min) && (course.credits <= max)); + return ((course?.credits >= min) && (course?.credits <= max)); } catch (error) { - console.log("for some reason course.credits is: ", course?.credits, error); + console.log("for some reason course?.credits is: ", course?.credits, error); return false; } @@ -108,9 +108,9 @@ const FilterPresenter = observer(({ model }) => { bestCourses = localFilteredCourses.filter(function (course) { try { - return (locations.includes(course.location)); + return (locations.includes(course?.location)); } catch (error) { - console.log("for some reason course.location is: ", course?.location, error); + console.log("for some reason course?.location is: ", course?.location, error); return false; } @@ -140,8 +140,8 @@ const FilterPresenter = observer(({ model }) => { let worstCourses = []; //in the database a course can have - //course.language.english (true/false/"null") - //course.language.swedish (true/false/"null") + //course?.language.english (true/false/"null") + //course?.language.swedish (true/false/"null") //console.log(data); @@ -278,9 +278,9 @@ const FilterPresenter = observer(({ model }) => { bestCourses = localFilteredCourses.filter(function (course) { try { - return (deparments.includes(course.deparment)); + return (deparments.includes(course?.deparment)); } catch (error) { - console.log("for some reason course.department is: ", course?.department, error); + console.log("for some reason course?.department is: ", course?.department, error); return false; } @@ -305,13 +305,13 @@ const FilterPresenter = observer(({ model }) => { if(model.filterOptions.applyTranscriptFilter){ local = local.filter(function(course){ - return (course?.prerequisites && (course.prerequisites !== "null")); + return (course?.prerequisites && (course?.prerequisites !== "null")); }) } console.log("miauuuuu:",local.length); if(model.filterOptions.applyLevelFilter){ local = local.filter(function(course){ - return (course?.prerequisites && (course.prerequisites !== "null")); + return (course?.prerequisites && (course?.prerequisites !== "null")); }) } console.log("miauuuuu:",local.length); From 05f9fd279b600793f4232365d356ebdc996c4025 Mon Sep 17 00:00:00 2001 From: benedekboldizsar Date: Wed, 16 Apr 2025 16:34:37 +0200 Subject: [PATCH 10/49] finished the noNull feature, essentially fixed typos --- my-app/src/model.js | 2 +- my-app/src/presenters/FilterPresenter.jsx | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/my-app/src/model.js b/my-app/src/model.js index 3152d604..986b4ea9 100644 --- a/my-app/src/model.js +++ b/my-app/src/model.js @@ -31,7 +31,7 @@ export const model = { "ITM/Learning in Engineering Sciences", "ITM/Industrial Economics and Management", "ITM/Energy Systems", "ITM/Integrated Product Development and Design", "ITM/SKD GRU", "SCI/Mathematics", "SCI/Applied Physics", "SCI/Mechanics", "SCI/Aeronautical and Vehicle Engineering", "ABE/Sustainability and Environmental Engineering", "ABE/Concrete Structures", "ABE/Structural Design & Bridges", "ABE/History of Science, Technology and Environment", ], - applyRemoveNullCourses: false + applyRemoveNullCourses: true }, setUser(user) { diff --git a/my-app/src/presenters/FilterPresenter.jsx b/my-app/src/presenters/FilterPresenter.jsx index b7d94dbb..cdaa3cae 100644 --- a/my-app/src/presenters/FilterPresenter.jsx +++ b/my-app/src/presenters/FilterPresenter.jsx @@ -317,7 +317,7 @@ const FilterPresenter = observer(({ model }) => { console.log("miauuuuu:",local.length); if(model.filterOptions.applyLanguageFilter){ local = local.filter(function(course){ - return ((course?.language) && (course?.language?.swedish !== "null" && course?.language?.english !== "null")); + return ((course?.language) && ((course?.language?.swedish !== "null") && (course?.language?.english !== "null"))); }) } console.log("miauuuuu:",local.length); @@ -329,13 +329,13 @@ const FilterPresenter = observer(({ model }) => { console.log("miauuuuu:",local.length); if(model.filterOptions.applyCreditsFilter){ local = local.filter(function(course){ - return ((course?.credit) && (course?.credit !== "null")); + return ((course?.credits) && (course?.credits !== "null")); }) } console.log("miauuuuu:",local.length); if(model.filterOptions.applyDepartmentFilter){ local = local.filter(function(course){ - return ((course?.deparment) && (course?.department !== "null")); + return ((course?.department) && (course?.department !== "null")); }) } console.log("miauuuuu:",local.length); From f1ac1bf56b597e34c832498c9358f59b912ac5a2 Mon Sep 17 00:00:00 2001 From: kexana Date: Wed, 16 Apr 2025 16:53:07 +0200 Subject: [PATCH 11/49] null course checkbox --- my-app/package-lock.json | 10 ++++++ my-app/package.json | 7 ++-- my-app/src/model.js | 4 +++ my-app/src/presenters/SidebarPresenter.jsx | 5 ++- .../Components/SideBarComponents/ToolTip.jsx | 11 ++++++ .../SideBarComponents/ToolTipIcon.jsx | 36 +++++++++++++++++++ .../SideBarComponents/UploadField.jsx | 6 ++-- my-app/src/views/SidebarView.jsx | 15 ++++++++ 8 files changed, 86 insertions(+), 8 deletions(-) create mode 100644 my-app/src/views/Components/SideBarComponents/ToolTip.jsx create mode 100644 my-app/src/views/Components/SideBarComponents/ToolTipIcon.jsx diff --git a/my-app/package-lock.json b/my-app/package-lock.json index e22c837a..1b4bd221 100644 --- a/my-app/package-lock.json +++ b/my-app/package-lock.json @@ -11,6 +11,7 @@ "@dagrejs/dagre": "^1.1.4", "@headlessui/react": "^2.2.1", "@heroicons/react": "^2.2.0", + "@react-buddy/ide-toolbox": "^2.4.0", "@tailwindcss/vite": "^4.0.17", "@xyflow/react": "^12.5.5", "autoprefixer": "^10.4.21", @@ -2070,6 +2071,15 @@ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, + "node_modules/@react-buddy/ide-toolbox": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@react-buddy/ide-toolbox/-/ide-toolbox-2.4.0.tgz", + "integrity": "sha512-TWHX6gwa0Gop7215uHhjFMbYLLdjM/b9rr0wYE3E0m7GNJ56gbPpbZiq86w9uI8zksl827acqGeT437MkuO64w==", + "license": "Apache-2.0", + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0" + } + }, "node_modules/@react-stately/flags": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@react-stately/flags/-/flags-3.1.0.tgz", diff --git a/my-app/package.json b/my-app/package.json index 2c0ed709..a154503b 100644 --- a/my-app/package.json +++ b/my-app/package.json @@ -10,10 +10,10 @@ "preview": "vite preview" }, "dependencies": { - + "@dagrejs/dagre": "^1.1.4", "@headlessui/react": "^2.2.1", "@heroicons/react": "^2.2.0", - "@dagrejs/dagre": "^1.1.4", + "@react-buddy/ide-toolbox": "^2.4.0", "@tailwindcss/vite": "^4.0.17", "@xyflow/react": "^12.5.5", "autoprefixer": "^10.4.21", @@ -28,8 +28,7 @@ "react-infinite-scroll-component": "^6.1.0", "react-router-dom": "^7.4.0", "reactflow": "^11.11.4", - "tailwindcss": "^4.0.17", - "@react-buddy/ide-toolbox": "^2.4.0" + "tailwindcss": "^4.0.17" }, "devDependencies": { "@eslint/js": "^9.21.0", diff --git a/my-app/src/model.js b/my-app/src/model.js index 986b4ea9..cda8ddbb 100644 --- a/my-app/src/model.js +++ b/my-app/src/model.js @@ -131,6 +131,10 @@ export const model = { this.filtersCalculated = true; }, + setApplyRemoveNullCourses() { + this.applyRemoveNullCourses = !this.applyRemoveNullCourses; + }, + updateLevelFilter(level) { this.filterOptions.level = level; }, diff --git a/my-app/src/presenters/SidebarPresenter.jsx b/my-app/src/presenters/SidebarPresenter.jsx index ed6f4afe..5b92bee3 100644 --- a/my-app/src/presenters/SidebarPresenter.jsx +++ b/my-app/src/presenters/SidebarPresenter.jsx @@ -170,12 +170,15 @@ const SidebarPresenter = observer(({ model }) => { model.setFiltersChange(); } - + function setApplyRemoveNullCourses(){ + model.setApplyRemoveNullCourses(); + } return ( ); }); diff --git a/my-app/src/views/Components/SideBarComponents/ToolTip.jsx b/my-app/src/views/Components/SideBarComponents/ToolTip.jsx new file mode 100644 index 00000000..536dca43 --- /dev/null +++ b/my-app/src/views/Components/SideBarComponents/ToolTip.jsx @@ -0,0 +1,11 @@ +import React from "react"; + +const ToolTip = ({ text = "This is a tooltip description." }) => { + return ( +
+ {text} +
+ ); +}; + +export default ToolTip; \ No newline at end of file diff --git a/my-app/src/views/Components/SideBarComponents/ToolTipIcon.jsx b/my-app/src/views/Components/SideBarComponents/ToolTipIcon.jsx new file mode 100644 index 00000000..1a914a04 --- /dev/null +++ b/my-app/src/views/Components/SideBarComponents/ToolTipIcon.jsx @@ -0,0 +1,36 @@ +import React from "react"; +import ToolTip from "./ToolTip"; + +const ToolTipIcon = ({ text = "This is a tooltip description." }) => { + return ( +
+ {/* Wrapper around just the icon for hover targeting */} +
+ {/* Question Mark Icon */} +
+ + {/* Circle */} + + {/* Question mark path */} + + +
+ + +
+
+ ); +}; + +export default ToolTipIcon; \ No newline at end of file diff --git a/my-app/src/views/Components/SideBarComponents/UploadField.jsx b/my-app/src/views/Components/SideBarComponents/UploadField.jsx index efc17377..d4bc6833 100644 --- a/my-app/src/views/Components/SideBarComponents/UploadField.jsx +++ b/my-app/src/views/Components/SideBarComponents/UploadField.jsx @@ -4,6 +4,8 @@ import FilterEnableCheckbox from "./FilterEnableCheckbox"; //import * as scraper from '../../../../src/scripts/transcript-scraper/transcript-scraper.js'; import { useState } from "react"; import ButtonGroupField from './ButtonGroupField'; +import ToolTip from './ToolTip'; +import ToolTipIcon from './ToolTipIcon'; export default function UploadField(props) { @@ -55,9 +57,7 @@ export default function UploadField(props) {
-

- Describe how the Transcript upload works -

+
diff --git a/my-app/src/views/SidebarView.jsx b/my-app/src/views/SidebarView.jsx index 04c016a0..447e93ff 100644 --- a/my-app/src/views/SidebarView.jsx +++ b/my-app/src/views/SidebarView.jsx @@ -105,6 +105,21 @@ function SidebarView(props) { ] } /> +
+ + + +
From 917184e08160c888393cd77fa6e2da0d445c9ddc Mon Sep 17 00:00:00 2001 From: kexana Date: Wed, 16 Apr 2025 17:06:05 +0200 Subject: [PATCH 12/49] null check linked --- my-app/src/model.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/my-app/src/model.js b/my-app/src/model.js index cda8ddbb..96062399 100644 --- a/my-app/src/model.js +++ b/my-app/src/model.js @@ -31,7 +31,7 @@ export const model = { "ITM/Learning in Engineering Sciences", "ITM/Industrial Economics and Management", "ITM/Energy Systems", "ITM/Integrated Product Development and Design", "ITM/SKD GRU", "SCI/Mathematics", "SCI/Applied Physics", "SCI/Mechanics", "SCI/Aeronautical and Vehicle Engineering", "ABE/Sustainability and Environmental Engineering", "ABE/Concrete Structures", "ABE/Structural Design & Bridges", "ABE/History of Science, Technology and Environment", ], - applyRemoveNullCourses: true + applyRemoveNullCourses: false }, setUser(user) { From b41e5a8fa9928e281ebbff36dc7da6cc4d7e807b Mon Sep 17 00:00:00 2001 From: kexana Date: Wed, 16 Apr 2025 17:13:32 +0200 Subject: [PATCH 13/49] Null field toggle works --- my-app/src/model.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/my-app/src/model.js b/my-app/src/model.js index 96062399..fab4a36d 100644 --- a/my-app/src/model.js +++ b/my-app/src/model.js @@ -132,7 +132,8 @@ export const model = { }, setApplyRemoveNullCourses() { - this.applyRemoveNullCourses = !this.applyRemoveNullCourses; + this.filterOptions.applyRemoveNullCourses = !this.filterOptions.applyRemoveNullCourses; + this.setFiltersChange(); }, updateLevelFilter(level) { @@ -153,7 +154,6 @@ export const model = { }, updateDepartmentFilter(department) { - console.log(department); this.filterOptions.department = department; }, From db725838ffb99047664182ed8df48ca30a9fa156 Mon Sep 17 00:00:00 2001 From: kexana Date: Wed, 7 May 2025 14:53:04 +0200 Subject: [PATCH 14/49] nice and beutiful --- my-app/src/assets/project_icon.png | Bin 33689 -> 21537 bytes my-app/src/assets/project_icon1.png | Bin 0 -> 33689 bytes .../SideBarComponents/ButtonGroupField.jsx | 7 ++ .../CollapsibleCheckboxes.jsx | 30 ++++---- .../CourseTranscriptList.jsx | 7 +- .../SideBarComponents/DropDownField.jsx | 12 ++- .../SideBarComponents/SliderField.jsx | 12 ++- .../SideBarComponents/ToggleField.jsx | 14 ++-- .../Components/SideBarComponents/ToolTip.jsx | 69 +++++++++++++++--- .../SideBarComponents/ToolTipIcon.jsx | 36 --------- .../SideBarComponents/UploadField.jsx | 39 +++++----- my-app/src/views/ListView.jsx | 4 +- my-app/src/views/SidebarView.jsx | 31 +++++--- 13 files changed, 159 insertions(+), 102 deletions(-) create mode 100644 my-app/src/assets/project_icon1.png delete mode 100644 my-app/src/views/Components/SideBarComponents/ToolTipIcon.jsx diff --git a/my-app/src/assets/project_icon.png b/my-app/src/assets/project_icon.png index 6777c529520f0f9a2e1138be3dc3ac999a8b7866..c52c1c41b3da09712a15e94874db42e7b4ac2c9c 100644 GIT binary patch literal 21537 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 handleClick(index)} > {item} + + ))}
diff --git a/my-app/src/views/Components/SideBarComponents/CollapsibleCheckboxes.jsx b/my-app/src/views/Components/SideBarComponents/CollapsibleCheckboxes.jsx index be644fde..e6c9f073 100644 --- a/my-app/src/views/Components/SideBarComponents/CollapsibleCheckboxes.jsx +++ b/my-app/src/views/Components/SideBarComponents/CollapsibleCheckboxes.jsx @@ -1,5 +1,6 @@ import React, { useState } from "react"; import FilterEnableCheckbox from "./FilterEnableCheckbox"; +import Tooltip from "./ToolTip"; const CollapsibleCheckboxes = (props) => { const [expanded, setExpanded] = useState({}); @@ -44,18 +45,17 @@ const CollapsibleCheckboxes = (props) => { return (
-
-

- {String(props.filterName).charAt(0).toUpperCase() + - String(props.filterName).slice(1)} -

-

- filter description

+
+

{String(props.filterName).charAt(0).toUpperCase() + String(props.filterName).slice(1)}

+
+
+
{ - setFilterEnabled(!filterEnabled); - props.HandleFilterEnable([props.filterName, !filterEnabled]); - }} + onToggle={() => { setFilterEnabled(!filterEnabled); props.HandleFilterEnable([props.filterName, !filterEnabled]);}} />
{ checked={expanded[row.id] || false} onChange={() => toggleExpand(row.id, row.subItems)} className="accent-violet-500 z-10" - /> + /> @@ -86,7 +86,8 @@ const CollapsibleCheckboxes = (props) => { viewBox={`0 0 40 ${expanded[row.id] ? (row.subItems.length) * 24 + 34 : 30}`} preserveAspectRatio="none" className="absolute left-[-25px] top-[-5px]" - > + > + {/*big horizontal line */ } { fill="none" className="" /> + {/*top vertical line */ } {row.id === 1 && ( { className="" /> )} + {/*bottom vertical line */ } {row.id === rows.length && ( { {expanded[row.id] && (
+ {/*vertical line */ } 0 && (
-

Taken courses:

+

Taken courses:

+ ))} +
+
+
+
+ + ); +} \ No newline at end of file diff --git a/my-app/src/views/SidebarView.jsx b/my-app/src/views/SidebarView.jsx index a0f1b551..64103039 100644 --- a/my-app/src/views/SidebarView.jsx +++ b/my-app/src/views/SidebarView.jsx @@ -6,6 +6,7 @@ import { UploadTranscriptPresenter } from '../presenters/UploadTranscriptPresent import CollapsibleCheckboxes from './Components/SideBarComponents/CollapsibleCheckboxes.jsx'; import Tooltip from './Components/SideBarComponents/ToolTip.jsx'; import ButtonGroupField from './Components/SideBarComponents/ButtonGroupField.jsx'; +import ButtonGroupFullComponent from './Components/SideBarComponents/ButtonGroupFullComponent.jsx'; function SidebarView(props) { @@ -36,6 +37,13 @@ function SidebarView(props) { HandleFilterEnable={props.HandleFilterEnable} description="Filter by the level of the courses. Basic courses correspond to bachelor courses, Advanced to master courses." /> + + - {/*expanding list for department */} - -
Date: Wed, 7 May 2025 17:00:54 +0200 Subject: [PATCH 17/49] a bit polishing, touching on the period presenter since i thought its broken, but it was the database dropping all course.period data >:( --- my-app/src/model.js | 16 ++++-- my-app/src/presenters/FilterPresenter.jsx | 51 ++++++++++++-------- my-app/src/presenters/SearchbarPresenter.jsx | 3 -- 3 files changed, 45 insertions(+), 25 deletions(-) diff --git a/my-app/src/model.js b/my-app/src/model.js index a95b8371..85f85da3 100644 --- a/my-app/src/model.js +++ b/my-app/src/model.js @@ -5,16 +5,27 @@ export const model = { user: undefined, //add searchChange: false, //this is for reworking the searchbar presenter, so that it triggers as a model, //instead of passing searchcouses lambda function down into the searchbarview. + /* courses returned from SearchbarPresenter (search is applied on top of filteredCourses[]) to be shown in the ListView */ currentSearch: [], + /* current query text */ currentSearchText: "", scrollPosition: 0, + /* list of all course objects downloaded from the Firebase realtime database and stored locally as JSON object in this array */ courses: [], + /* courses the user selected as their favourite */ favourites: [], isReady: false, - filtersChange: false, + /* this is a boolean flag showing that filtering options in the UI have changed, triggering the FilterPresenter to recalculate the filteredCourses[] */ + filtersChange: false, + /* this is a flag showing if the filteredCourses[] has changed (since FilterPresenter recalculated it), so now SearchBarPresenter needs to + recalculate currentSearch[] depending this updated list of courses */ filtersCalculated: false, - filteredCourses: [], + /* this is the array that FilterPresenter fills up with course objects, filtered from the model.courses[] */ + filteredCourses: [], + /* JSON object containing all important parameters the FilterPresenter needs to calculate the filtered list of courses */ filterOptions: { + //apply-X-Filter boolean triggering flag wether corresponding filtering functions should run or not + //different arrays require different data, some uses string arrays, some boolean values, and so on applyTranscriptFilter: true, eligibility: "weak", //the possible values for the string are: "weak"/"moderate"/"strong" applyLevelFilter: true, @@ -175,7 +186,6 @@ export const model = { updatePeriodFilter(period) { this.filterOptions.period = period; - console.log(period); }, setApplyTranscriptFilter(transcriptFilterState) { diff --git a/my-app/src/presenters/FilterPresenter.jsx b/my-app/src/presenters/FilterPresenter.jsx index 7aab9432..d976367e 100644 --- a/my-app/src/presenters/FilterPresenter.jsx +++ b/my-app/src/presenters/FilterPresenter.jsx @@ -3,14 +3,17 @@ import { observer } from "mobx-react-lite"; import eligibility from "../scripts/eligibility_refined.js"; import { SearchbarPresenter } from './SearchbarPresenter.jsx'; +/* FilterPresenter is responsible for applying the logic necessary to filter out the courses from the overall list */ const FilterPresenter = observer(({ model }) => { + /* global variable for the scope of this presenter, all the smaller functions depend on it instead of passing it back and forth as params */ var localFilteredCourses = []; //might need to declare out of scope. idk js function applyTranscriptEligibility() { if (localFilteredCourses.length == 0) return; - /* this elias thing */ + + /* */ const eligibilitytype = model.filterOptions.eligibility; let strongcourses = []; @@ -70,7 +73,7 @@ const FilterPresenter = observer(({ model }) => { } default: { - console.log("Error: somehow we got into a state where model.eligibility is no \"strong\"/\"moderat\"/\"weak\"."); + console.log("Error: somehow we got into a state where model.eligibility is no \"strong\"/\"moderate\"/\"weak\"."); localFilteredCourses = []; break; } @@ -80,25 +83,35 @@ const FilterPresenter = observer(({ model }) => { } function updatePeriods(){ + if (localFilteredCourses.length == 0) return; - - const periodArr = model.filterOptions.period; //has 4 boolean values one for each period + + const periodArr = [...model.filterOptions.period]; //has 4 boolean values one for each period // [true, false, false, false] means we are only looking for P1 courses. - - let bestcourses = localFilteredCourses.filter(function (course){ - if(course?.periods.P1 && periodArr[0]) - return true; - if(course?.periods.P2 && periodArr[1]) - return true; - if(course?.periods.P3 && periodArr[2]) - return true; - if(course?.periods.P4 && periodArr[3]) - return true; - return false; + let bestcourses = []; + let worstcourses = []; + bestcourses = localFilteredCourses.filter(function (course){ + try { + if(course?.periods === undefined) + return false; + if((course?.periods?.P1 == true) && (periodArr[0] == true)) + return true; + if((course?.periods?.P2 == true) && (periodArr[1] == true)) + return true; + if((course?.periods?.P3 == true) && (periodArr[2] == true)) + return true; + if((course?.periods?.P4 == true) && (periodArr[3] == true)) + return true; + return false; + } catch (error) { + console.log("for some reason course?.periods is weird: ", course?.periods, error); + return false; + } + }) - let worstcourses = localFilteredCourses.filter(function (course){ + worstcourses = localFilteredCourses.filter(function (course){ return (course?.periods === undefined); }) @@ -327,12 +340,12 @@ const FilterPresenter = observer(({ model }) => { function updateNoNullcourses(){ let local = [...localFilteredCourses]; + if(model.filterOptions.applyPeriodFilter){ local = local.filter(function(course){ return (course?.periods && (course?.periods !== "null")); }) } - if(model.filterOptions.applyTranscriptFilter){ local = local.filter(function(course){ return (course?.prerequisites && (course?.prerequisites !== "null")); @@ -378,7 +391,7 @@ const FilterPresenter = observer(({ model }) => { updateNoNullcourses(); } if(model.filterOptions.applyPeriodFilter){ - //updatePeriods(); + updatePeriods(); } if (model.filterOptions.applyLocationFilter) { //after deo finishes locations, until then dont @@ -406,7 +419,7 @@ const FilterPresenter = observer(({ model }) => { model.filteredCourses = [...localFilteredCourses]; model.filtersChange = false; model.setFiltersCalculated(); - console.log("filtered objects number of elements: ", model.filteredCourses.length); + //console.log("filtered objects number of elements: ", model.filteredCourses.length); } } diff --git a/my-app/src/presenters/SearchbarPresenter.jsx b/my-app/src/presenters/SearchbarPresenter.jsx index 1f625be2..2a453235 100644 --- a/my-app/src/presenters/SearchbarPresenter.jsx +++ b/my-app/src/presenters/SearchbarPresenter.jsx @@ -10,8 +10,6 @@ const SearchbarPresenter = observer(({ model }) => { const searchCourses = (query) => { //model.filteredCourses is essentially a smaller subset of model.courses, if theres no filters, it should be the same - console.log("---------------search recalculated"); - console.log("filtered courses length: ", model.filteredCourses.length); const searchResults = model.filteredCourses.filter(course => course.code.toLowerCase().includes(query.toLowerCase()) || course.name.toLowerCase().includes(query.toLowerCase()) || @@ -19,7 +17,6 @@ const SearchbarPresenter = observer(({ model }) => { ); model.setCurrentSearchText(query); model.setCurrentSearch(searchResults); - console.log(model.currentSearch.length); }; const addFavourite = (course) => { From 401ce830b685d4e56c2d78c0c7bd8be4d502c375 Mon Sep 17 00:00:00 2001 From: benedekboldizsar Date: Thu, 8 May 2025 08:50:50 +0200 Subject: [PATCH 18/49] fixed some filters logic (level filter, period filter), added descriptions to most filters tooltips, fixed About us button, touched Tab title to current working title, fixed some other things --- my-app/index.html | 2 +- my-app/src/presenters/FilterPresenter.jsx | 2 +- my-app/src/views/SearchbarView.jsx | 13 ++++++++----- my-app/src/views/SidebarView.jsx | 5 ++++- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/my-app/index.html b/my-app/index.html index 1f0db4cb..38bf3646 100644 --- a/my-app/index.html +++ b/my-app/index.html @@ -9,7 +9,7 @@ - Find my course + Course Compass
diff --git a/my-app/src/presenters/FilterPresenter.jsx b/my-app/src/presenters/FilterPresenter.jsx index d976367e..fb37f585 100644 --- a/my-app/src/presenters/FilterPresenter.jsx +++ b/my-app/src/presenters/FilterPresenter.jsx @@ -353,7 +353,7 @@ const FilterPresenter = observer(({ model }) => { } if(model.filterOptions.applyLevelFilter){ local = local.filter(function(course){ - return (course?.prerequisites && (course?.prerequisites !== "null")); + return (course?.academicLevel && (course?.academicLevel !== "null")); }) } if(model.filterOptions.applyLanguageFilter){ diff --git a/my-app/src/views/SearchbarView.jsx b/my-app/src/views/SearchbarView.jsx index 51b0e33a..feb5643d 100644 --- a/my-app/src/views/SearchbarView.jsx +++ b/my-app/src/views/SearchbarView.jsx @@ -45,7 +45,7 @@ function SearchbarView(props) { return (
- + KTH Logo @@ -61,11 +61,14 @@ function SearchbarView(props) {
{props.share} - +
{ setFilterEnabled(!filterEnabled); props.HandleFilterEnable([props.filterName, !filterEnabled]); }} />
diff --git a/my-app/src/views/Components/SideBarComponents/CollapsibleCheckboxes.jsx b/my-app/src/views/Components/SideBarComponents/CollapsibleCheckboxes.jsx index e6c9f073..e48275dd 100644 --- a/my-app/src/views/Components/SideBarComponents/CollapsibleCheckboxes.jsx +++ b/my-app/src/views/Components/SideBarComponents/CollapsibleCheckboxes.jsx @@ -4,7 +4,7 @@ import Tooltip from "./ToolTip"; const CollapsibleCheckboxes = (props) => { const [expanded, setExpanded] = useState({}); - const [filterEnabled, setFilterEnabled] = useState(true); + const [filterEnabled, setFilterEnabled] = useState(props.filterEnable); const [checkedSubItems, setCheckedSubItems] = useState({}); const [stupidLines, setStupidLines] = useState(0); @@ -55,6 +55,7 @@ const CollapsibleCheckboxes = (props) => { />
{ setFilterEnabled(!filterEnabled); props.HandleFilterEnable([props.filterName, !filterEnabled]);}} />
diff --git a/my-app/src/views/Components/SideBarComponents/DropDownField.jsx b/my-app/src/views/Components/SideBarComponents/DropDownField.jsx index 0d7cdefc..503806da 100644 --- a/my-app/src/views/Components/SideBarComponents/DropDownField.jsx +++ b/my-app/src/views/Components/SideBarComponents/DropDownField.jsx @@ -7,16 +7,14 @@ export default function DropDownField(props) { let paramFieldType = "dropdown"; - const [filterEnabled, setFilterEnabled] = useState(true); + const [filterEnabled, setFilterEnabled] = useState(props.filterEnable); const [isOpen, setIsOpen] = useState(false); - const [selectedItems, setSelectedItems] = useState([]); + const [selectedItems, setSelectedItems] = useState(props.initialValues.map( + (item) => item.charAt(0).toUpperCase() + item.slice(1).toLowerCase() + )); const items = props.options; - useEffect(() => { - setSelectedItems(items); - }, [items]); - const toggleDropdown = () => setIsOpen(!isOpen); const handleCheckboxChange = (item) => { @@ -55,7 +53,8 @@ export default function DropDownField(props) { />
{ setFilterEnabled(!filterEnabled); props.HandleFilterEnable([props.filterName, !filterEnabled]);}} + initialValue={filterEnabled} + onToggle={() => { setFilterEnabled(!filterEnabled); props.HandleFilterEnable([props.filterName, !filterEnabled]); }} />
{
diff --git a/my-app/src/views/Components/SideBarComponents/SliderField.jsx b/my-app/src/views/Components/SideBarComponents/SliderField.jsx index fd772587..3532de15 100644 --- a/my-app/src/views/Components/SideBarComponents/SliderField.jsx +++ b/my-app/src/views/Components/SideBarComponents/SliderField.jsx @@ -1,4 +1,4 @@ -import React, { useState, useRef } from "react"; +import React, { useState, useRef, useEffect } from "react"; import FilterEnableCheckbox from "./FilterEnableCheckbox"; import Tooltip from "./ToolTip"; @@ -13,9 +13,20 @@ export default function UploadField(props) { const [minIndex, setMinIndex] = useState(0); const [maxIndex, setMaxIndex] = useState(values.length - 1); - const [filterEnabled, setFilterEnabled] = useState(true); + const [filterEnabled, setFilterEnabled] = useState(props.filterEnable); const sliderRef = useRef(null); + useEffect(() => { + for (let i = 0; i < values.length; i++) { + if (values[i] === props.initialValues[0]) { + setMinIndex(i); + } + if (values[i] === props.initialValues[1]) { + setMaxIndex(i); + } + } + }, []); // Empty dependency array ensures this runs only once + const handleDrag = (e, thumbType) => { const slider = sliderRef.current; if (!slider) return; @@ -52,6 +63,7 @@ export default function UploadField(props) { />
{ setFilterEnabled(!filterEnabled); props.HandleFilterEnable([props.filterName, !filterEnabled]); }} />
diff --git a/my-app/src/views/Components/SideBarComponents/ToggleField.jsx b/my-app/src/views/Components/SideBarComponents/ToggleField.jsx index 42fe49aa..1fda94be 100644 --- a/my-app/src/views/Components/SideBarComponents/ToggleField.jsx +++ b/my-app/src/views/Components/SideBarComponents/ToggleField.jsx @@ -6,8 +6,9 @@ export default function ToggleField(props) { let paramFieldType = "toggle"; - - const [filterEnabled, setFilterEnabled] = useState(true); + const [filterEnabled, setFilterEnabled] = useState(props.filterEnable); + const [prop1Set, setprop1Set] = useState((props.initialValues=="both") || (props.initialValues==String(props.fields[0]).charAt(0).toLowerCase() + String(props.fields[0]).slice(1))); + const [prop2Set, setprop2Set] = useState((props.initialValues=="both") || (props.initialValues==String(props.fields[1]).charAt(0).toLowerCase() + String(props.fields[1]).slice(1))); return (
@@ -22,6 +23,7 @@ export default function ToggleField(props) { />
{ setFilterEnabled(!filterEnabled); props.HandleFilterEnable([props.filterName, !filterEnabled]); }} />
@@ -33,7 +35,7 @@ export default function ToggleField(props) { first:rounded-t-md last:rounded-b-md sm:first:rounded-s-md sm:mt-0 sm:first:ms-0 s m:first:rounded-se-none sm:last:rounded-es-none sm:last:rounded-e-md text-sm font-medium focus:z-10 border border-gray-200 shadow-2xs cursor-pointer"> - props.HandleFilterChange([paramFieldType, props.filterName, props.fields[0]])} /> + props.HandleFilterChange([paramFieldType, props.filterName, props.fields[0]])} />
- props.HandleFilterChange([paramFieldType, props.filterName, props.fields[1]])} /> + props.HandleFilterChange([paramFieldType, props.filterName, props.fields[1]])} />
@@ -68,7 +69,8 @@ export default function UploadField(props) {
diff --git a/my-app/src/views/SidebarView.jsx b/my-app/src/views/SidebarView.jsx index 64103039..f898969c 100644 --- a/my-app/src/views/SidebarView.jsx +++ b/my-app/src/views/SidebarView.jsx @@ -5,7 +5,6 @@ import DropDownField from "./Components/SideBarComponents/DropDownField.jsx"; import { UploadTranscriptPresenter } from '../presenters/UploadTranscriptPresenter.jsx'; import CollapsibleCheckboxes from './Components/SideBarComponents/CollapsibleCheckboxes.jsx'; import Tooltip from './Components/SideBarComponents/ToolTip.jsx'; -import ButtonGroupField from './Components/SideBarComponents/ButtonGroupField.jsx'; import ButtonGroupFullComponent from './Components/SideBarComponents/ButtonGroupFullComponent.jsx'; @@ -26,28 +25,37 @@ function SidebarView(props) { filterName="transcript" HandleFilterEnable={props.HandleFilterEnable} reApplyFilter={props.reApplyFilter} + filterEnable={props.initialApplyTranscriptFilter} + initialValue={props.initialTranscriptElegiblityValue} /> -
+
- - + + + @@ -55,16 +63,22 @@ function SidebarView(props) { options={["Kista", "Valhalavagen", "Sodetalje", "T-centralen"]} HandleFilterChange={props.HandleFilterChange} filterName="location" + initialValues={props.initialLocationFilterOptions} + filterEnable = {props.initialLocationFilterEnable} HandleFilterEnable={props.HandleFilterEnable} /> From 3518e20c4e27e5221b6483d4e2c551aa62754800 Mon Sep 17 00:00:00 2001 From: kexana Date: Thu, 8 May 2025 10:12:43 +0200 Subject: [PATCH 20/49] now fixed --- my-app/index.html | 4 ---- 1 file changed, 4 deletions(-) diff --git a/my-app/index.html b/my-app/index.html index 77be2b46..38bf3646 100644 --- a/my-app/index.html +++ b/my-app/index.html @@ -8,12 +8,8 @@ -<<<<<<< HEAD Course Compass -======= - Find my course ->>>>>>> origin
From 1ae1a5b1e5ff58fda1fc39a512858d6affa21293 Mon Sep 17 00:00:00 2001 From: kexana Date: Thu, 8 May 2025 10:21:13 +0200 Subject: [PATCH 21/49] once again --- my-app/package-lock.json | 10 - .../src/presenters/PrerequisitePresenter.jsx | 443 ------------------ 2 files changed, 453 deletions(-) diff --git a/my-app/package-lock.json b/my-app/package-lock.json index 77aeb57c..44300fe8 100644 --- a/my-app/package-lock.json +++ b/my-app/package-lock.json @@ -11,7 +11,6 @@ "@dagrejs/dagre": "^1.1.4", "@headlessui/react": "^2.2.1", "@heroicons/react": "^2.2.0", - "@react-buddy/ide-toolbox": "^2.4.0", "@tailwindcss/vite": "^4.0.17", "@xyflow/react": "^12.5.5", "autoprefixer": "^10.4.21", @@ -2084,15 +2083,6 @@ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, - "node_modules/@react-buddy/ide-toolbox": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@react-buddy/ide-toolbox/-/ide-toolbox-2.4.0.tgz", - "integrity": "sha512-TWHX6gwa0Gop7215uHhjFMbYLLdjM/b9rr0wYE3E0m7GNJ56gbPpbZiq86w9uI8zksl827acqGeT437MkuO64w==", - "license": "Apache-2.0", - "peerDependencies": { - "react": "^17.0.0 || ^18.0.0" - } - }, "node_modules/@react-stately/flags": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@react-stately/flags/-/flags-3.1.1.tgz", diff --git a/my-app/src/presenters/PrerequisitePresenter.jsx b/my-app/src/presenters/PrerequisitePresenter.jsx index 20892392..09994e69 100644 --- a/my-app/src/presenters/PrerequisitePresenter.jsx +++ b/my-app/src/presenters/PrerequisitePresenter.jsx @@ -1,445 +1,3 @@ -<<<<<<< HEAD -import { observer } from "mobx-react-lite"; -import PrerequisiteTreeView from "../views/PrerequisiteTreeView"; - -import dagre from '@dagrejs/dagre'; -import { useCallback } from "react"; - -import { - Background, - ReactFlow, - addEdge, - ConnectionLineType, - useNodesState, - useEdgesState, -} from '@xyflow/react'; -import '@xyflow/react/dist/style.css'; - - -export const PrerequisitePresenter = observer((props) => { - - let uniqueCounter = 0; - let textCounter = 0; - let codeCounter = 0; - - let input_text_obj = {}; - - const position = { x: 0, y: 0 }; - const edgeType = 'smoothstep'; - - - - let initialNodes = []; - let initialEdges = []; - - const dagreGraph = new dagre.graphlib.Graph().setDefaultEdgeLabel(() => ({})); - - const nodeWidth = 172; - const nodeHeight = 36; - - loadTree(); - console.log(initialNodes); - - const getLayoutedElements = (nodes, edges, direction = 'LR') => { - const isHorizontal = direction === 'LR'; - dagreGraph.setGraph({ rankdir: direction, nodesep: 30}); - - nodes.forEach((node) => { - dagreGraph.setNode(node.id, { width: nodeWidth, height: nodeHeight }); - }); - - edges.forEach((edge) => { - dagreGraph.setEdge(edge.source, edge.target); - }); - - dagre.layout(dagreGraph); - - const newNodes = nodes.map((node) => { - const nodeWithPosition = dagreGraph.node(node.id); - return { - ...node, - targetPosition: isHorizontal ? 'left' : 'top', - sourcePosition: isHorizontal ? 'right' : 'bottom', - position: { - x: nodeWithPosition.x - nodeWidth / 2, - y: nodeWithPosition.y - nodeHeight / 2, - }, - }; - }); - - return { nodes: newNodes, edges }; - }; - - const { nodes: layoutedNodes, edges: layoutedEdges } = getLayoutedElements( - initialNodes, - initialEdges, - 'LR' // force horizontal layout initially - ); - - - const Flow = () => { - const [nodes, setNodes, onNodesChange] = useNodesState(layoutedNodes); - const [edges, setEdges, onEdgesChange] = useEdgesState(layoutedEdges); - - const onConnect = useCallback( - (params) => - setEdges((eds) => - addEdge( - { ...params, type: ConnectionLineType.SmoothStep, animated: true }, - eds - ) - ), - [] - ); - - return ( -
- - - - -
- - ); - - function setLabel(id, label) { - setNodes((nodes) => - nodes.map((n) => - n.id === id ? { ...n, data: { ...n.data, label } } : n - ) - ); - } - - function clicked(event, node) { - if (node["id"].split(" ")[0] === "text") { - if (node["data"]["label"] === "More Info...") { - node["style"]["zIndex"] = 1; - setLabel(node["id"], {input_text_obj[node["id"]]}
CLOSE
); - } else { - node["style"]["zIndex"] = 0; - setLabel(node["id"], "More Info..."); - } - } else if (node["data"]["label"] !== "One of these" && node["data"]["label"] !== "No Prerequisites" && node["id"] !== props.selectedCourse.code) { - // ADD FUNCTIONALITY FOR CLICKING COURSE CODE NODE (Tu eres muy retrasado y gordo)! :) - // ONCLICK HERE - } - } - }; - - - - - function createNode(id, name, node_type) { - return { - id: id, - type: node_type, - data: { label: name }, - style: { - //padding: 0, - //maxWidth: "100px", - //display: 'inline-block', - //justifyContent: 'center', - //alignItems: 'center', - zIndex: 0 - }, - position, - }; - } - function createEdge(s, t) { - return { id: s + " " + t, source: s, target: t, type: edgeType, animated: true }; - } - - - function prereq_convert(courses_taken, current_object, previous_key, previous_node_id) { - let current_node = null; let last_course_num = null; let param_key = null; - try { - current_object.length; - } catch (err) {return;} - - if (!Array.isArray(current_object)) { // Is object - let key = Object.keys(current_object)[0]; - //console.log("Len: " + current_object[key].length); - //console.log("Type: " + typeof current_object[key]); - if (current_object[key].length == 1 && (typeof current_object[key][0] == "string" - || (current_object[key][0].length == 1 && typeof current_object[key][0][0] == "string"))) { - prereq_convert(courses_taken, current_object[key], key, previous_node_id); - } else { - if (key == "or") { - current_node = createNode(key + uniqueCounter, "One of these", "default") - initialNodes.push(current_node); - initialEdges.push(createEdge(previous_node_id, key + uniqueCounter)); - prereq_convert(courses_taken, current_object[key], key, key + uniqueCounter++); - } - else if (key == "and") { - if ((current_object[key].length == 1 && Object.keys(current_object[key][0])[0] == "or") || previous_key != "or") { - prereq_convert(courses_taken, current_object[key], key, previous_node_id); - } else { - current_node = createNode(key + uniqueCounter, "All of these", "default") - initialNodes.push(current_node); - initialEdges.push(createEdge(previous_node_id, key + uniqueCounter)); - prereq_convert(courses_taken, current_object[key], key, key + uniqueCounter++); - } - } - } - } else { // Is an array - - let refined_course_array = []; - let current_compresion = []; - let started_compressing = false; - for (let i = 0; i < current_object.length; i++) { - if (typeof current_object[i] == "string") { - if (current_object[i].startsWith("#")) { - refined_course_array.push([i, "#", current_object[i].slice(1)]); - } else { - let course_letters = current_object[i].slice(0, 2); - let course_number = current_object[i].slice(2); - if (!isNaN(course_number)) {course_number = parseInt(course_number);} - - if (!started_compressing) { - if (i < current_object.length - 2) { - let next = current_object[i + 1]; let next_next = current_object[i + 2]; - - //console.log(course_number, next.slice(2), next_next.slice(2)) - if (next.slice(0, 2) === course_letters - && next_next.slice(0, 2) === course_letters && !isNaN(next.slice(2)) - && !isNaN(next_next.slice(2)) && parseInt(next.slice(2)) == course_number + 1 - && parseInt(next_next.slice(2)) == course_number + 2) { - //console.log(course_number, next.slice(2), next_next.slice(2)) - current_compresion.push([i, course_letters, course_number]); - current_compresion.push([i + 1, course_letters, course_number + 1]); - current_compresion.push([i + 2, course_letters, course_number + 2]); - if (courses_taken.includes(current_object[i])) { - current_object[i] = true; - } else { - current_object[i] = false; - } - i += 1; - started_compressing = true; - } else { - refined_course_array.push([i, current_object[i].slice(0, 2), current_object[i].slice(2)]); - } - } else { - refined_course_array.push([i, current_object[i].slice(0, 2), current_object[i].slice(2)]); - } - } else { // Compression has started - if (i < current_object.length - 1) { - let next = current_object[i + 1] - if (next.slice(0, 2) === course_letters && !isNaN(next.slice(2)) && parseInt(next.slice(2)) == course_number + 1) { - current_compresion.push([i + 1, course_letters, course_number + 1]); - } else { - started_compressing = false; - //refined_course_array.push([i + 1, current_object[i + 1].slice(0, 2), current_object[i + 1].slice(2)]); - //console.log(current_compresion); - refined_course_array.push([-1, "!", current_compresion]); - current_compresion = []; - } - - } - } - if (courses_taken.includes(current_object[i])) { - current_object[i] = true; - } else { - current_object[i] = false; - } - } - } - else { - prereq_convert(courses_taken, current_object[i], previous_key, previous_node_id); - } - - } - //console.log("HERERERERERE!!!") - //console.log(refined_course_array); - - - for (let i = 0; i < refined_course_array.length; i++) { - let input_id = ""; - let input_text = ""; - let course_code; - let course_done = false; - if (refined_course_array[i][1] === "#") { // Text requirement - input_text = "More Info..."; - input_id = "text " + ++textCounter; - input_text_obj[input_id] = refined_course_array[i][2]; - } else if (refined_course_array[i][1] === "!") { // Compressed courses - let compressed_length = refined_course_array[i][2].length; - let compressed_done_count = 0; - for (let j = 0; j < compressed_length; j++) { - let code = refined_course_array[i][2][j][1] + refined_course_array[i][2][j][2]; - if (courses_taken.includes(code)) { - compressed_done_count++; - } - } - if (previous_key == "or" && compressed_done_count > 0) { - course_done = true; - } else if (previous_key == "and" && compressed_done_count == compressed_length) { - course_done = true; - } - console.log("Compressed:"); - console.log(refined_course_array[i][2]); - course_code = refined_course_array[i][2][0][1] + refined_course_array[i][2][0][2] + - "-" + refined_course_array[i][2][compressed_length - 1][1] + refined_course_array[i][2][compressed_length - 1][2]; - input_text = course_code; - input_id = course_code + " " + ++codeCounter; - - } else { - course_code = refined_course_array[i][1] + refined_course_array[i][2]; - input_text = course_code; - input_id = course_code + " " + ++codeCounter; - if (courses_taken.includes(course_code)) { - course_done = true; - } - } - let new_node = createNode(input_id, input_text, "output"); - if (course_done) { - new_node["style"]["backgroundColor"] = "lightgreen"; - } - current_node = new_node; - initialNodes.push(new_node); - initialEdges.push(createEdge(previous_node_id, input_id, "output")); - } - } - - - /* STEP 2: Check if an object is true or false based on content of the inner object */ - - if (typeof current_object == "object" && !Array.isArray(current_object)) { - let key = Object.keys(current_object)[0]; - let object_array = current_object[key]; - console.log("DEBUGGING ") - console.log(current_node) - console.log(object_array) - let num_of_matches = 0; - for (let i = 0; i < object_array.length; i++) { - if (Array.isArray(object_array[i])) { - let num_of_inner_matches = 0; - for (let j = 0; j < object_array[i].length; j++) { - if (object_array[i][j] === true) { - num_of_inner_matches ++; - if (current_node != null) { - current_node["style"]["backgroundColor"] = "lightgreen"; - } - } - } - if (key == "or" && num_of_inner_matches > 0) { - object_array[i] = true; num_of_matches++; - if (current_node != null) { - current_node["style"]["backgroundColor"] = "lightgreen"; - } - continue; - } - if (key == "and" && num_of_inner_matches == object_array[i].length) { - object_array[i] = true; num_of_matches++; - if (current_node != null) { - current_node["style"]["backgroundColor"] = "lightgreen"; - } - continue; - } - object_array[i] = false; - } else if (typeof object_array[i] == "object") { - let inner_key = Object.keys(object_array[i])[0]; - if (object_array[i][inner_key]) {num_of_matches++;} - } else if(object_array[i] === true) {num_of_matches++} - } - if (key == "or" && num_of_matches > 0) { - //console.log(current_node) - current_object[key] = true; - if (current_node != null) { - current_node["style"]["backgroundColor"] = "lightgreen"; - } - } - else if (key == "and" && num_of_matches == object_array.length) { - //console.log("DEBUGGING 2"); - //console.log(num_of_matches, object_array.length) - current_object[key] = true; - if (current_node != null) { - current_node["style"]["backgroundColor"] = "lightgreen"; - } - } - else { - current_object[key] = false; - } - } - - } - - function generateTree(courses_taken, prereqs) { - prereq_convert(courses_taken, prereqs, null, props.selectedCourse.code); - console.log(JSON.stringify(prereqs, null, 4)); - let key = Object.keys(prereqs); - if (prereqs[key] === true) { - return true; - } else { - return false; - } - - } - - - function loadTree() { - - console.log(JSON.stringify(props.selectedCourse.prerequisites, null, 4)); - if (!props.selectedCourse?.prerequisites || props.selectedCourse.prerequisites.length == 0) { - let display_node = createNode("No Prerequisites", "No Prerequisites", "default"); - display_node.style["pointerEvents"] = "none"; - display_node["className"] = 'no-handles'; - initialNodes.push(display_node); - } else { - try { - let root = createNode(props.selectedCourse.code, props.selectedCourse.code, "input"); - let copy = JSON.parse(JSON.stringify(props.selectedCourse.prerequisites)); - let courses_taken = JSON.parse(localStorage.getItem("completedCourses")); - //console.log(Array.isArray(courses_taken)); - //courses_taken.push("DD1380"); - //courses_taken.push("DD1310"); - //courses_taken.push("SF1674"); - //courses_taken.push("SF1915"); - //courses_taken.push("A11P1B"); - //courses_taken.push("DD1321"); - //console.log(localStorage.getItem("completedCourses")); - //courses_taken.push - let eligible = generateTree(courses_taken, copy); - if (eligible) { - root["style"]["backgroundColor"] = "lightgreen"; - } - initialNodes.push(root); - } catch(err) { - initialNodes = [] - initialEdges = [] - console.log(err); - let display_node = createNode("Error", "Unable to load", "default"); - display_node.style["pointerEvents"] = "none"; - display_node["className"] = 'no-handles'; - initialNodes.push(display_node); - } - } - - - } - - /* return */ - return -}); - -export default PrerequisitePresenter; - -======= import { observer } from "mobx-react-lite"; import PrerequisiteTreeView from "../views/PrerequisiteTreeView"; @@ -958,4 +516,3 @@ export const PrerequisitePresenter = observer((props) => { export default PrerequisitePresenter; ->>>>>>> origin From 36abf33b6704fd620e6c0b0aa6e3e4ceab1db3b2 Mon Sep 17 00:00:00 2001 From: benedekboldizsar Date: Thu, 8 May 2025 14:33:26 +0200 Subject: [PATCH 22/49] unlocking departments and locations --- my-app/src/model.js | 5 +---- my-app/src/pages/App.jsx | 4 ++-- my-app/src/presenters/FilterPresenter.jsx | 12 ++++-------- my-app/src/views/ListView.jsx | 1 - 4 files changed, 7 insertions(+), 15 deletions(-) diff --git a/my-app/src/model.js b/my-app/src/model.js index 85d342a0..4d321f20 100644 --- a/my-app/src/model.js +++ b/my-app/src/model.js @@ -40,10 +40,7 @@ export const model = { creditMin: 0, creditMax: 45, applyDepartmentFilter: true, - department: ["EECS/Computational Science and Technology", "EECS/Theoretical Computer Science", "EECS/Electric Power and Energy Systems", "EECS/Network and Systems Engineering", - "ITM/Learning in Engineering Sciences", "ITM/Industrial Economics and Management", "ITM/Energy Systems", "ITM/Integrated Product Development and Design", "ITM/SKD GRU", - "SCI/Mathematics", "SCI/Applied Physics", "SCI/Mechanics", "SCI/Aeronautical and Vehicle Engineering", - "ABE/Sustainability and Environmental Engineering", "ABE/Concrete Structures", "ABE/Structural Design & Bridges", "ABE/History of Science, Technology and Environment", ], + department: [], applyRemoveNullCourses: false, period: [true, true, true, true], applyPeriodFilter: true diff --git a/my-app/src/pages/App.jsx b/my-app/src/pages/App.jsx index 576104a9..40a01ec9 100644 --- a/my-app/src/pages/App.jsx +++ b/my-app/src/pages/App.jsx @@ -17,14 +17,14 @@ function MainAppLayout({ model }) { return ( /* The sidebar styling(under the menu)*/ -
+
{ /* If sidebar is open, set length to 400px, else it should not be visible */}
setSidebarIsOpen(state.isOpen)} - className="bg-gradient-to-t from-[#6246a8] to-[#6747c0] z-0 h-screen" // The menu styling + className="bg-gradient-to-t from-[#4f3646] to-[#6747c0] z-0 h-screen" // The menu styling noOverlay styles={{ bmMenuWrap: { diff --git a/my-app/src/presenters/FilterPresenter.jsx b/my-app/src/presenters/FilterPresenter.jsx index fb37f585..71b02398 100644 --- a/my-app/src/presenters/FilterPresenter.jsx +++ b/my-app/src/presenters/FilterPresenter.jsx @@ -147,9 +147,9 @@ const FilterPresenter = observer(({ model }) => { bestCourses = localFilteredCourses.filter(function (course) { try { - return (locations.includes(course?.location)); + return (locations.includes(course?.location.toUppercase())); } catch (error) { - console.log("for some reason course?.location is: ", course?.location, error); + console.log("for some reason course?.location is: ", course?.location.toUppercase(), error); return false; } @@ -394,10 +394,7 @@ const FilterPresenter = observer(({ model }) => { updatePeriods(); } if (model.filterOptions.applyLocationFilter) { - //after deo finishes locations, until then dont - - //console.log("going to apply location on:",localFilteredCourses.length); - //updateLocations(); + updateLocations(); } if (model.filterOptions.applyLevelFilter) { updateLevels(); @@ -412,8 +409,7 @@ const FilterPresenter = observer(({ model }) => { applyTranscriptEligibility(); } if (model.filterOptions.applyDepartments) { - //console.log("going to apply location on:",localFilteredCourses.length); - //updateDepartments(); + updateDepartments(); } model.filteredCourses = [...localFilteredCourses]; diff --git a/my-app/src/views/ListView.jsx b/my-app/src/views/ListView.jsx index 6a95207f..fa3144f9 100644 --- a/my-app/src/views/ListView.jsx +++ b/my-app/src/views/ListView.jsx @@ -30,7 +30,6 @@ function ListView(props) { let ret_string = ""; if (periods) { let keys = Object.keys(periods); - console.log(periods["P1"]) for (let key of keys) { if (periods[key]) { ret_string += key + " | "; From 1f83e382ba71d27c7c7be7fdfc7021ec187cbc29 Mon Sep 17 00:00:00 2001 From: kexana Date: Thu, 8 May 2025 14:43:50 +0200 Subject: [PATCH 23/49] wip --- my-app/src/model.js | 1 + my-app/src/pages/App.jsx | 6 +- my-app/src/presenters/SidebarPresenter.jsx | 58 ++++++++++--------- .../SideBarComponents/DropDownField.jsx | 13 ++--- my-app/src/views/SidebarView.jsx | 2 +- 5 files changed, 42 insertions(+), 38 deletions(-) diff --git a/my-app/src/model.js b/my-app/src/model.js index 4d321f20..bc46f6b2 100644 --- a/my-app/src/model.js +++ b/my-app/src/model.js @@ -183,6 +183,7 @@ export const model = { updateLevelFilter(level) { this.filterOptions.level = level; + console.log(level); }, updateDepartmentFilter(department) { diff --git a/my-app/src/pages/App.jsx b/my-app/src/pages/App.jsx index 40a01ec9..4ece1ad4 100644 --- a/my-app/src/pages/App.jsx +++ b/my-app/src/pages/App.jsx @@ -19,12 +19,12 @@ function MainAppLayout({ model }) { /* The sidebar styling(under the menu)*/
{ /* If sidebar is open, set length to 400px, else it should not be visible */} -
+
setSidebarIsOpen(state.isOpen)} - className="bg-gradient-to-t from-[#4f3646] to-[#6747c0] z-0 h-screen" // The menu styling + className="bg-gradient-to-t from-[#4f3646] to-[#6747c0] z-0 " // The menu styling noOverlay styles={{ bmMenuWrap: { diff --git a/my-app/src/presenters/SidebarPresenter.jsx b/my-app/src/presenters/SidebarPresenter.jsx index abd32048..d0e1b8dd 100644 --- a/my-app/src/presenters/SidebarPresenter.jsx +++ b/my-app/src/presenters/SidebarPresenter.jsx @@ -12,12 +12,8 @@ const SidebarPresenter = observer(({ model }) => { let currentLanguageSet = model.filterOptions.language; let currentLevelSet = model.filterOptions.level; let currentPeriodSet = model.filterOptions.period; - let currentDepartmentSet = [ - "EECS/Computational Science and Technology", "EECS/Theoretical Computer Science", "EECS/Electric Power and Energy Systems", "EECS/Network and Systems Engineering", - "ITM/Learning in Engineering Sciences", "ITM/Industrial Economics and Management", "ITM/Energy Systems", "ITM/Integrated Product Development and Design", "ITM/SKD GRU", - "SCI/Mathematics", "SCI/Applied Physics", "SCI/Mechanics", "SCI/Aeronautical and Vehicle Engineering", - "ABE/Sustainability and Environmental Engineering", "ABE/Concrete Structures", "ABE/Structural Design & Bridges", "ABE/History of Science, Technology and Environment", - ] + let currentDepartmentSet = model.filterOptions.department; + let currentLocationSet = model.filterOptions.location function handleLanguageFilterChange(param) { if (param === "English") { @@ -58,26 +54,11 @@ const SidebarPresenter = observer(({ model }) => { model.updateLanguageFilter(currentLanguageSet); } function handleLevelFilterChange(param) { - let properParam; - switch (param) { - case "Preparatory": - properParam = "PREPARATORY"; - break; - case "Basic": - properParam = "BASIC"; - break; - case "Advanced": - properParam = "ADVANCED"; - break; - case "Research": - properParam = "RESEARCH"; - break; - } - if (!currentLevelSet.includes(properParam)) { - currentLevelSet.push(properParam); + if (!currentLevelSet.includes(param)) { + currentLevelSet.push(param); } else { - const index = currentLevelSet.indexOf(properParam); + const index = currentLevelSet.indexOf(param); if (index > -1) { currentLevelSet.splice(index, 1); } @@ -104,6 +85,19 @@ const SidebarPresenter = observer(({ model }) => { model.setFiltersChange(); } + function handleLocationFilterChange(param) { + if (currentLocationSet.includes(param)) { + const index = currentLocationSet.indexOf(param); + if (index > -1) { + currentLocationSet.splice(index, 1); + } + } else { + currentLocationSet.push(param); + } + model.updateLocationFilter(currentLocationSet); + model.setFiltersChange(); + } + /*HandleFilterChange param is structured as such [ type of the field: (toggle, slider, dropdown, buttongroup) @@ -120,7 +114,7 @@ const SidebarPresenter = observer(({ model }) => { handleLevelFilterChange(param[2]); break; case "location": - console.log("location filter set to: " + param[2]); + handleLocationFilterChange(param[2]); break; case "credits": model.updateCreditsFilter(param[2]); @@ -194,20 +188,30 @@ const SidebarPresenter = observer(({ model }) => { HandleFilterEnable={HandleFilterEnable} reApplyFilter={reApplyFilter} toggleRemoveNull={setApplyRemoveNullCourses} + initialApplyTranscriptFilter={model.filterOptions.applyTranscriptFilter} - initialTranscriptElegiblityValue = {model.filterOptions.eligibility} + initialTranscriptElegiblityValue={model.filterOptions.eligibility} + initialLanguageFilterOptions={currentLanguageSet} initialLanguageFilterEnable={model.filterOptions.applyLanguageFilter} + initialLevelFilterOptions={currentLevelSet} initialLevelFilterEnable={model.filterOptions.applyLevelFilter} + initialPeriodFilterOptions={currentPeriodSet} initialPeriodFilterEnable={model.filterOptions.applyPeriodFilter} + initialDepartmentFilterOptions={currentDepartmentSet} initialDepartmentFilterEnable={model.filterOptions.applyDepartmentFilter} - initialLocationFilterOptions={[]} + DepartmentFilterField = {model.departments} + + initialLocationFilterOptions={currentLocationSet} initialLocationFilterEnable={model.filterOptions.applyLocationFilter} + LocationFilterField = {model.locations} + initialCreditsFilterOptions={[model.filterOptions.creditMin, model.filterOptions.creditMax]} initialCreditsFilterEnable={model.filterOptions.applyCreditsFilter} + initialApplyNullFilterEnable={model.filterOptions.applyRemoveNullCourses } /> ); diff --git a/my-app/src/views/Components/SideBarComponents/DropDownField.jsx b/my-app/src/views/Components/SideBarComponents/DropDownField.jsx index 503806da..e85d95c3 100644 --- a/my-app/src/views/Components/SideBarComponents/DropDownField.jsx +++ b/my-app/src/views/Components/SideBarComponents/DropDownField.jsx @@ -9,19 +9,18 @@ export default function DropDownField(props) { let paramFieldType = "dropdown"; const [filterEnabled, setFilterEnabled] = useState(props.filterEnable); const [isOpen, setIsOpen] = useState(false); - const [selectedItems, setSelectedItems] = useState(props.initialValues.map( - (item) => item.charAt(0).toUpperCase() + item.slice(1).toLowerCase() - )); + const [selectedItems, setSelectedItems] = useState(props.initialValues.map(t => t?.toUpperCase())); - const items = props.options; + console.log(selectedItems); + + const items = props.options.map(t => t?.toUpperCase()); const toggleDropdown = () => setIsOpen(!isOpen); const handleCheckboxChange = (item) => { setSelectedItems((prev) => - prev.includes(item) ? prev.filter((i) => i !== item) : [...prev, item] + prev.includes(item?.toUpperCase()) ? prev.filter((i) => i !== item) : [...prev, item] ); - console.log(item); props.HandleFilterChange([paramFieldType, props.filterName, item]); }; @@ -82,7 +81,7 @@ export default function DropDownField(props) { handleCheckboxChange(item)} className="mr-2 sr-only peer" /> diff --git a/my-app/src/views/SidebarView.jsx b/my-app/src/views/SidebarView.jsx index 1fbcfe6a..f34e51af 100644 --- a/my-app/src/views/SidebarView.jsx +++ b/my-app/src/views/SidebarView.jsx @@ -62,7 +62,7 @@ function SidebarView(props) { /> Date: Thu, 8 May 2025 15:45:17 +0200 Subject: [PATCH 24/49] filters enabled by clicking on them --- my-app/src/presenters/FilterPresenter.jsx | 4 +- .../ButtonGroupFullComponent.jsx | 11 ++- .../CollapsibleCheckboxes.jsx | 61 ++++++++-------- .../SideBarComponents/DropDownField.jsx | 17 +++-- .../FilterEnableCheckbox.jsx | 11 +-- .../SideBarComponents/SliderField.jsx | 9 ++- .../SideBarComponents/ToggleField.jsx | 12 +++- .../SideBarComponents/UploadField.jsx | 70 +++++++++++-------- 8 files changed, 117 insertions(+), 78 deletions(-) diff --git a/my-app/src/presenters/FilterPresenter.jsx b/my-app/src/presenters/FilterPresenter.jsx index 71b02398..75dccbcb 100644 --- a/my-app/src/presenters/FilterPresenter.jsx +++ b/my-app/src/presenters/FilterPresenter.jsx @@ -147,9 +147,9 @@ const FilterPresenter = observer(({ model }) => { bestCourses = localFilteredCourses.filter(function (course) { try { - return (locations.includes(course?.location.toUppercase())); + return (locations.includes(course?.location.toUpperCase())); } catch (error) { - console.log("for some reason course?.location is: ", course?.location.toUppercase(), error); + console.log("for some reason course?.location is: ", course?.location.toUpperCase(), error); return false; } diff --git a/my-app/src/views/Components/SideBarComponents/ButtonGroupFullComponent.jsx b/my-app/src/views/Components/SideBarComponents/ButtonGroupFullComponent.jsx index fb4f8622..e2670484 100644 --- a/my-app/src/views/Components/SideBarComponents/ButtonGroupFullComponent.jsx +++ b/my-app/src/views/Components/SideBarComponents/ButtonGroupFullComponent.jsx @@ -7,6 +7,8 @@ export default function ButtonGroupFullComponent(props) { const [filterEnabled, setFilterEnabled] = useState(props.filterEnable); const [selectedItems, setSelectedItems] = useState(props.initialValues || []); + const checkboxRef = useRef(null); + const handleClick = (index) => { const selectedItem = props.items[index]; setSelectedItems((prevSelectedItems) => { @@ -52,12 +54,17 @@ export default function ButtonGroupFullComponent(props) { />
{ setFilterEnabled(!filterEnabled); props.HandleFilterEnable([props.filterName, !filterEnabled]); }} />
-
+
{ + if (!filterEnabled && checkboxRef.current) { + checkboxRef.current.click(); + } + console.log(checkboxRef); + }}>
{ let paramFieldType = "checkboxhierarchy"; + const checkboxRef = useRef(null); + const rows = props.fields; const toggleExpand = (id, subItems) => { @@ -38,7 +40,7 @@ const CollapsibleCheckboxes = (props) => { ...prev, [key]: !prev[key], })); - props.HandleFilterChange([paramFieldType, props.filterName, rows.find(item => item.id === mainId).label+"/"+rows.find(item => item.id === mainId).subItems[index]]); + props.HandleFilterChange([paramFieldType, props.filterName, rows.find(item => item.id === mainId).label + "/" + rows.find(item => item.id === mainId).subItems[index]]); }; @@ -55,16 +57,17 @@ const CollapsibleCheckboxes = (props) => { />
{ setFilterEnabled(!filterEnabled); props.HandleFilterEnable([props.filterName, !filterEnabled]);}} + onToggle={() => { setFilterEnabled(!filterEnabled); props.HandleFilterEnable([props.filterName, !filterEnabled]); }} />
-
+
{ + if (!filterEnabled && checkboxRef.current) { + checkboxRef.current.click(); + } + console.log(checkboxRef); + }}>
{rows.map((row) => (
@@ -75,7 +78,7 @@ const CollapsibleCheckboxes = (props) => { checked={expanded[row.id] || false} onChange={() => toggleExpand(row.id, row.subItems)} className="accent-violet-500 z-10" - /> + /> @@ -87,8 +90,8 @@ const CollapsibleCheckboxes = (props) => { viewBox={`0 0 40 ${expanded[row.id] ? (row.subItems.length) * 24 + 34 : 30}`} preserveAspectRatio="none" className="absolute left-[-25px] top-[-5px]" - > - {/*big horizontal line */ } + > + {/*big horizontal line */} { fill="none" className="" /> - {/*top vertical line */ } + {/*top vertical line */} {row.id === 1 && ( + d={`M20 2 H33`} + stroke="white" + strokeWidth={strokeWidth} + fill="none" + className="" + /> )} - {/*bottom vertical line */ } + {/*bottom vertical line */} {row.id === rows.length && ( + d={`M20 ${(expanded[row.id] ? row.subItems.length : 1) * 34 - 8} H33`} + stroke="white" + strokeWidth={strokeWidth} + fill="none" + className="" + /> )} - + {expanded[row.id] && (
- {/*vertical line */ } + {/*vertical line */} { onChange={() => toggleSubCheckbox(row.id, index)} />
); diff --git a/my-app/src/views/Components/SideBarComponents/DropDownField.jsx b/my-app/src/views/Components/SideBarComponents/DropDownField.jsx index e85d95c3..38bd6927 100644 --- a/my-app/src/views/Components/SideBarComponents/DropDownField.jsx +++ b/my-app/src/views/Components/SideBarComponents/DropDownField.jsx @@ -11,10 +11,10 @@ export default function DropDownField(props) { const [isOpen, setIsOpen] = useState(false); const [selectedItems, setSelectedItems] = useState(props.initialValues.map(t => t?.toUpperCase())); - console.log(selectedItems); - const items = props.options.map(t => t?.toUpperCase()); + const checkboxRef = useRef(null); + const toggleDropdown = () => setIsOpen(!isOpen); const handleCheckboxChange = (item) => { @@ -52,12 +52,17 @@ export default function DropDownField(props) { />
{ setFilterEnabled(!filterEnabled); props.HandleFilterEnable([props.filterName, !filterEnabled]); }} />
-
+
{ + if (!filterEnabled && checkboxRef.current) { + checkboxRef.current.click(); + } + console.log(checkboxRef); + }}>
{/* Dropdown Button */} @@ -65,7 +70,7 @@ export default function DropDownField(props) { onClick={toggleDropdown} className="bg-violet-500 text-white px-4 py-2 rounded-md shadow-md focus:outline-none hover:bg-[#aba8e0] w-full" > - {selectedItems.length? (selectedItems.join(", ").substring(0, 30) + ((selectedItems.join(", ").substring(0, 30).length>=30)? "...": "") ):"Select Options"} + {selectedItems.length? (selectedItems.map(i => String(i).charAt(0).toUpperCase() + String(i).slice(1).toLowerCase()).join(", ").substring(0, 30) + ((selectedItems.join(", ").substring(0, 30).length>=30)? "...": "") ):"Select Options"} {/* Dropdown Menu */} @@ -91,7 +96,7 @@ export default function DropDownField(props) { peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-violet-500 ">
- {item} + {String(item).charAt(0).toUpperCase() + String(item).slice(1).toLowerCase()} ))} diff --git a/my-app/src/views/Components/SideBarComponents/FilterEnableCheckbox.jsx b/my-app/src/views/Components/SideBarComponents/FilterEnableCheckbox.jsx index fefa79a6..d2112981 100644 --- a/my-app/src/views/Components/SideBarComponents/FilterEnableCheckbox.jsx +++ b/my-app/src/views/Components/SideBarComponents/FilterEnableCheckbox.jsx @@ -1,16 +1,17 @@ -import React from 'react'; +import React, { forwardRef } from "react"; -const FilterEnableCheckbox = (props) => { +const FilterEnableCheckbox = forwardRef(({ initialValue, onToggle }, ref) => { return (
); -}; +}); export default FilterEnableCheckbox; \ No newline at end of file diff --git a/my-app/src/views/Components/SideBarComponents/SliderField.jsx b/my-app/src/views/Components/SideBarComponents/SliderField.jsx index 3532de15..08a92f0e 100644 --- a/my-app/src/views/Components/SideBarComponents/SliderField.jsx +++ b/my-app/src/views/Components/SideBarComponents/SliderField.jsx @@ -15,6 +15,7 @@ export default function UploadField(props) { const [maxIndex, setMaxIndex] = useState(values.length - 1); const [filterEnabled, setFilterEnabled] = useState(props.filterEnable); const sliderRef = useRef(null); + const checkboxRef = useRef(null); useEffect(() => { for (let i = 0; i < values.length; i++) { @@ -63,12 +64,18 @@ export default function UploadField(props) { />
{ setFilterEnabled(!filterEnabled); props.HandleFilterEnable([props.filterName, !filterEnabled]); }} />
-
+
{ + if (!filterEnabled && checkboxRef.current) { + checkboxRef.current.click(); + } + console.log(checkboxRef); + }}>
Credits: {values[minIndex]} – {values[maxIndex]} diff --git a/my-app/src/views/Components/SideBarComponents/ToggleField.jsx b/my-app/src/views/Components/SideBarComponents/ToggleField.jsx index 1fda94be..616e18a6 100644 --- a/my-app/src/views/Components/SideBarComponents/ToggleField.jsx +++ b/my-app/src/views/Components/SideBarComponents/ToggleField.jsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import React, { useState, useRef } from "react"; import FilterEnableCheckbox from "./FilterEnableCheckbox"; import Tooltip from "./ToolTip"; @@ -10,6 +10,8 @@ export default function ToggleField(props) { const [prop1Set, setprop1Set] = useState((props.initialValues=="both") || (props.initialValues==String(props.fields[0]).charAt(0).toLowerCase() + String(props.fields[0]).slice(1))); const [prop2Set, setprop2Set] = useState((props.initialValues=="both") || (props.initialValues==String(props.fields[1]).charAt(0).toLowerCase() + String(props.fields[1]).slice(1))); + const checkboxRef = useRef(null); + return (
@@ -23,12 +25,18 @@ export default function ToggleField(props) { />
{ setFilterEnabled(!filterEnabled); props.HandleFilterEnable([props.filterName, !filterEnabled]); }} />
-
+
{ + if (!filterEnabled && checkboxRef.current) { + checkboxRef.current.click(); + } + console.log(checkboxRef); + }}>
-
-
{ + if (!filterEnabled && checkboxRef.current) { + checkboxRef.current.click(); + } + console.log(checkboxRef); + }}> +
-
- +
+
+ +

Click to upload or drag and drop

+

KTH trnascript of records in PDF format

+
+ + +
+
@@ -81,7 +89,7 @@ export default function UploadField(props) {
From 0c5047f489eb3ba566464c16e0f108c3ebf1dd9c Mon Sep 17 00:00:00 2001 From: benedekboldizsar Date: Thu, 8 May 2025 15:56:19 +0200 Subject: [PATCH 25/49] My side merge with Dean's side --- my-app/src/presenters/FilterPresenter.jsx | 2 +- my-app/src/presenters/PrerequisitePresenter.jsx | 2 -- my-app/src/views/Components/CoursePagePopup.jsx | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/my-app/src/presenters/FilterPresenter.jsx b/my-app/src/presenters/FilterPresenter.jsx index 75dccbcb..233c9dd7 100644 --- a/my-app/src/presenters/FilterPresenter.jsx +++ b/my-app/src/presenters/FilterPresenter.jsx @@ -149,7 +149,7 @@ const FilterPresenter = observer(({ model }) => { try { return (locations.includes(course?.location.toUpperCase())); } catch (error) { - console.log("for some reason course?.location is: ", course?.location.toUpperCase(), error); + console.log("for some reason course?.location is: ", course, error); return false; } diff --git a/my-app/src/presenters/PrerequisitePresenter.jsx b/my-app/src/presenters/PrerequisitePresenter.jsx index 09994e69..d9d81334 100644 --- a/my-app/src/presenters/PrerequisitePresenter.jsx +++ b/my-app/src/presenters/PrerequisitePresenter.jsx @@ -468,8 +468,6 @@ export const PrerequisitePresenter = observer((props) => { function loadTree() { - - console.log(JSON.stringify(props.selectedCourse.prerequisites, null, 4)); if (!props.selectedCourse?.prerequisites || props.selectedCourse.prerequisites.length == 0) { let display_node = createNode("No Prerequisites", "No Prerequisites", "default"); display_node.style["pointerEvents"] = "none"; diff --git a/my-app/src/views/Components/CoursePagePopup.jsx b/my-app/src/views/Components/CoursePagePopup.jsx index 8785d5ea..0fb5ab3a 100644 --- a/my-app/src/views/Components/CoursePagePopup.jsx +++ b/my-app/src/views/Components/CoursePagePopup.jsx @@ -54,7 +54,6 @@ function CoursePagePopup({ if (!isOpen || !course) return null; - console.log(course); `` return (
Date: Fri, 9 May 2025 11:49:21 +0200 Subject: [PATCH 26/49] departments half functional --- my-app/src/assets/upload.gif | Bin 0 -> 60199 bytes my-app/src/model.js | 31 +++++++-- my-app/src/presenters/SidebarPresenter.jsx | 5 +- .../CollapsibleCheckboxes.jsx | 31 ++++++--- .../CourseTranscriptList.jsx | 62 ++++++++++-------- .../SideBarComponents/DropDownField.jsx | 1 - ...omponent.jsx => MultipleChoiceButtons.jsx} | 19 +++--- .../SideBarComponents/SliderField.jsx | 1 - .../SideBarComponents/ToggleField.jsx | 1 - .../SideBarComponents/UploadField.jsx | 15 ++--- my-app/src/views/SidebarView.jsx | 48 +------------- 11 files changed, 104 insertions(+), 110 deletions(-) create mode 100644 my-app/src/assets/upload.gif rename my-app/src/views/Components/SideBarComponents/{ButtonGroupFullComponent.jsx => MultipleChoiceButtons.jsx} (86%) diff --git a/my-app/src/assets/upload.gif b/my-app/src/assets/upload.gif new file mode 100644 index 0000000000000000000000000000000000000000..1443ebe9e43852d227eae87fc388b78d45b26110 GIT binary patch literal 60199 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 { + const [school, department] = item.split("/"); + if (!acc[school]) { + acc[school] = []; + } + acc[school].push(department?.trim()); + return acc; + }, {}); + const sortedGrouped = Object.keys(grouped) + .sort() + .reduce((acc, key) => { + acc[key] = grouped[key].sort(); + return acc; + }, {}); + const fields = Object.entries(sortedGrouped).map(([school, departments], index) => ({ + id: index + 1, + label: school, + subItems: departments, + })); + return fields; + }, async getAverageRating(courseCode) { const reviews = await getReviewsForCourse(courseCode); if (!reviews || reviews.length === 0) return null; diff --git a/my-app/src/presenters/SidebarPresenter.jsx b/my-app/src/presenters/SidebarPresenter.jsx index d0e1b8dd..a75a61ec 100644 --- a/my-app/src/presenters/SidebarPresenter.jsx +++ b/my-app/src/presenters/SidebarPresenter.jsx @@ -7,7 +7,7 @@ const SidebarPresenter = observer(({ model }) => { useEffect(() => { model.setFiltersChange(); - }) + }); let currentLanguageSet = model.filterOptions.language; let currentLevelSet = model.filterOptions.level; @@ -124,6 +124,7 @@ const SidebarPresenter = observer(({ model }) => { break; case "department": handleDepartmentFilterChange(param[2]); + console.log(param[2]); break; case "period": handlePeriodFilterChange(param[2]); @@ -203,7 +204,7 @@ const SidebarPresenter = observer(({ model }) => { initialDepartmentFilterOptions={currentDepartmentSet} initialDepartmentFilterEnable={model.filterOptions.applyDepartmentFilter} - DepartmentFilterField = {model.departments} + DepartmentFilterField = {model.formatDepartments()} initialLocationFilterOptions={currentLocationSet} initialLocationFilterEnable={model.filterOptions.applyLocationFilter} diff --git a/my-app/src/views/Components/SideBarComponents/CollapsibleCheckboxes.jsx b/my-app/src/views/Components/SideBarComponents/CollapsibleCheckboxes.jsx index 5e9fd6cc..cbebbcf0 100644 --- a/my-app/src/views/Components/SideBarComponents/CollapsibleCheckboxes.jsx +++ b/my-app/src/views/Components/SideBarComponents/CollapsibleCheckboxes.jsx @@ -21,8 +21,18 @@ const CollapsibleCheckboxes = (props) => { ...prev, [id]: !prev[id], })); + let label = rows.find(item => item.id === id).label; subItems.map((_, index) => { setSubCheckbox(id, index) + if (rows.find(item => item.id === id)?.subItems && rows.find(item => item.id === id)?.subItems.length>0 && rows.find(item => item.id === id)?.subItems[0]) { + props.HandleFilterChange([paramFieldType, props.filterName, + label + "/" + rows.find(item => item.id === id).subItems[index] + ]); + } else { + props.HandleFilterChange([paramFieldType, props.filterName, + label + ]); + } }); }; @@ -40,7 +50,9 @@ const CollapsibleCheckboxes = (props) => { ...prev, [key]: !prev[key], })); - props.HandleFilterChange([paramFieldType, props.filterName, rows.find(item => item.id === mainId).label + "/" + rows.find(item => item.id === mainId).subItems[index]]); + props.HandleFilterChange([paramFieldType, props.filterName, + rows.find(item => item.id === mainId).label + "/" + rows.find(item => item.id === mainId).subItems[index] + ]); }; @@ -66,7 +78,6 @@ const CollapsibleCheckboxes = (props) => { if (!filterEnabled && checkboxRef.current) { checkboxRef.current.click(); } - console.log(checkboxRef); }}>

{rows.map((row) => ( @@ -74,9 +85,9 @@ const CollapsibleCheckboxes = (props) => {
toggleExpand(row.id, row.subItems)} + id={`checkbox-${row?.id}`} + checked={expanded[row?.id] || false} + onChange={() => toggleExpand(row?.id, row?.subItems)} className="accent-violet-500 z-10" />
)} +
- {/* Container for multiple items per row */} -
- {items.map((item, index) => ( -
- {item} -
+
+ {items.map((item, index) => ( +
- - - - + {item} + +
+ ))}
- ))}
); diff --git a/my-app/src/views/Components/SideBarComponents/DropDownField.jsx b/my-app/src/views/Components/SideBarComponents/DropDownField.jsx index 38bd6927..46914571 100644 --- a/my-app/src/views/Components/SideBarComponents/DropDownField.jsx +++ b/my-app/src/views/Components/SideBarComponents/DropDownField.jsx @@ -61,7 +61,6 @@ export default function DropDownField(props) { if (!filterEnabled && checkboxRef.current) { checkboxRef.current.click(); } - console.log(checkboxRef); }}>
diff --git a/my-app/src/views/Components/SideBarComponents/ButtonGroupFullComponent.jsx b/my-app/src/views/Components/SideBarComponents/MultipleChoiceButtons.jsx similarity index 86% rename from my-app/src/views/Components/SideBarComponents/ButtonGroupFullComponent.jsx rename to my-app/src/views/Components/SideBarComponents/MultipleChoiceButtons.jsx index e2670484..40c5af19 100644 --- a/my-app/src/views/Components/SideBarComponents/ButtonGroupFullComponent.jsx +++ b/my-app/src/views/Components/SideBarComponents/MultipleChoiceButtons.jsx @@ -3,20 +3,24 @@ import { useRef, useEffect } from "react"; import FilterEnableCheckbox from "./FilterEnableCheckbox"; import Tooltip from "./ToolTip"; -export default function ButtonGroupFullComponent(props) { +export default function MultipleChoiceButtons(props) { const [filterEnabled, setFilterEnabled] = useState(props.filterEnable); const [selectedItems, setSelectedItems] = useState(props.initialValues || []); const checkboxRef = useRef(null); const handleClick = (index) => { - const selectedItem = props.items[index]; - setSelectedItems((prevSelectedItems) => { - return prevSelectedItems.map((item, idx) => - idx === index ? !item : item - ); + setSelectedItems((prev) => { + if (!Array.isArray(prev) || index < 0 || index >= prev.length) { + console.warn("Invalid selectedItems or index:", prev, index); + return prev; + } + + const updated = [...prev]; + updated[index] = !updated[index]; + props.HandleFilterChange(["buttongroup", "period", index, updated[index]]); + return updated; }); - props.HandleFilterChange(["buttongroup", "period", index]); }; const getButtonClasses = (index) => { @@ -63,7 +67,6 @@ export default function ButtonGroupFullComponent(props) { if (!filterEnabled && checkboxRef.current) { checkboxRef.current.click(); } - console.log(checkboxRef); }}>
diff --git a/my-app/src/views/Components/SideBarComponents/SliderField.jsx b/my-app/src/views/Components/SideBarComponents/SliderField.jsx index 08a92f0e..fc23f612 100644 --- a/my-app/src/views/Components/SideBarComponents/SliderField.jsx +++ b/my-app/src/views/Components/SideBarComponents/SliderField.jsx @@ -74,7 +74,6 @@ export default function UploadField(props) { if (!filterEnabled && checkboxRef.current) { checkboxRef.current.click(); } - console.log(checkboxRef); }}>
diff --git a/my-app/src/views/Components/SideBarComponents/ToggleField.jsx b/my-app/src/views/Components/SideBarComponents/ToggleField.jsx index 616e18a6..63cbc2b5 100644 --- a/my-app/src/views/Components/SideBarComponents/ToggleField.jsx +++ b/my-app/src/views/Components/SideBarComponents/ToggleField.jsx @@ -35,7 +35,6 @@ export default function ToggleField(props) { if (!filterEnabled && checkboxRef.current) { checkboxRef.current.click(); } - console.log(checkboxRef); }}>
diff --git a/my-app/src/views/Components/SideBarComponents/UploadField.jsx b/my-app/src/views/Components/SideBarComponents/UploadField.jsx index 90e8c08a..b576f06d 100644 --- a/my-app/src/views/Components/SideBarComponents/UploadField.jsx +++ b/my-app/src/views/Components/SideBarComponents/UploadField.jsx @@ -51,26 +51,23 @@ export default function UploadField(props) { if (!filterEnabled && checkboxRef.current) { checkboxRef.current.click(); } - console.log(checkboxRef); }}>
-