Skip to content

Commit 874fa25

Browse files
Responsive filtering (#79)
* 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 * wrote departments filter option * added check to make sure to not recommend courses the client has taken before * initial state good * departments go to model * trying to add filtering out courses with NULL fields as an implementable filter option, merging with main start as per teams request * merging into brain? * some ?. properties check, because skill issue * more course?. things * finished the noNull feature, essentially fixed typos * null course checkbox * null check linked * Null field toggle works --------- Co-authored-by: benedekboldizsar <bb.boldizsarbenedek@gmail.com>
1 parent 14872ac commit 874fa25

File tree

10 files changed

+110
-30
lines changed

10 files changed

+110
-30
lines changed

my-app/package-lock.json

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

my-app/package.json

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@
1010
"preview": "vite preview"
1111
},
1212
"dependencies": {
13-
13+
"@dagrejs/dagre": "^1.1.4",
1414
"@headlessui/react": "^2.2.1",
1515
"@heroicons/react": "^2.2.0",
16-
"@dagrejs/dagre": "^1.1.4",
16+
"@react-buddy/ide-toolbox": "^2.4.0",
1717
"@tailwindcss/vite": "^4.0.17",
1818
"@xyflow/react": "^12.5.5",
1919
"autoprefixer": "^10.4.21",
@@ -28,8 +28,7 @@
2828
"react-infinite-scroll-component": "^6.1.0",
2929
"react-router-dom": "^7.4.0",
3030
"reactflow": "^11.11.4",
31-
"tailwindcss": "^4.0.17",
32-
"@react-buddy/ide-toolbox": "^2.4.0"
31+
"tailwindcss": "^4.0.17"
3332
},
3433
"devDependencies": {
3534
"@eslint/js": "^9.21.0",

my-app/src/model.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,11 @@ export const model = {
131131
this.filtersCalculated = true;
132132
},
133133

134+
setApplyRemoveNullCourses() {
135+
this.filterOptions.applyRemoveNullCourses = !this.filterOptions.applyRemoveNullCourses;
136+
this.setFiltersChange();
137+
},
138+
134139
updateLevelFilter(level) {
135140
this.filterOptions.level = level;
136141
},
@@ -149,7 +154,6 @@ export const model = {
149154
},
150155

151156
updateDepartmentFilter(department) {
152-
console.log(department);
153157
this.filterOptions.department = department;
154158
},
155159

my-app/src/presenters/FilterPresenter.jsx

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ const FilterPresenter = observer(({ model }) => {
2929
//console.log(course?.prerequisites);
3030
if (storedFinishedCourses.includes(course?.code))
3131
return;
32-
if (course?.prerequisites && (course.prerequisites !== "null"))
32+
if (course?.prerequisites && (course?.prerequisites !== "null"))
3333
var resultEligibility = eligibility(storedFinishedCourses, course?.prerequisites);
3434
else { // {strong: , zero: , moderate: , weak: }
3535
zerocourses.push(course);
@@ -89,9 +89,9 @@ const FilterPresenter = observer(({ model }) => {
8989

9090
localFilteredCourses = localFilteredCourses.filter(function (course) {
9191
try {
92-
return ((course.credits >= min) && (course.credits <= max));
92+
return ((course?.credits >= min) && (course?.credits <= max));
9393
} catch (error) {
94-
console.log("for some reason course.credits is: ", course?.credits, error);
94+
console.log("for some reason course?.credits is: ", course?.credits, error);
9595
return false;
9696
}
9797

@@ -108,9 +108,9 @@ const FilterPresenter = observer(({ model }) => {
108108

109109
bestCourses = localFilteredCourses.filter(function (course) {
110110
try {
111-
return (locations.includes(course.location));
111+
return (locations.includes(course?.location));
112112
} catch (error) {
113-
console.log("for some reason course.location is: ", course?.location, error);
113+
console.log("for some reason course?.location is: ", course?.location, error);
114114
return false;
115115
}
116116

@@ -140,8 +140,8 @@ const FilterPresenter = observer(({ model }) => {
140140
let worstCourses = [];
141141

142142
//in the database a course can have
143-
//course.language.english (true/false/"null")
144-
//course.language.swedish (true/false/"null")
143+
//course?.language.english (true/false/"null")
144+
//course?.language.swedish (true/false/"null")
145145

146146
//console.log(data);
147147

@@ -278,9 +278,9 @@ const FilterPresenter = observer(({ model }) => {
278278

279279
bestCourses = localFilteredCourses.filter(function (course) {
280280
try {
281-
return (deparments.includes(course.deparment));
281+
return (deparments.includes(course?.deparment));
282282
} catch (error) {
283-
console.log("for some reason course.department is: ", course?.department, error);
283+
console.log("for some reason course?.department is: ", course?.department, error);
284284
return false;
285285
}
286286

@@ -305,19 +305,19 @@ const FilterPresenter = observer(({ model }) => {
305305

306306
if(model.filterOptions.applyTranscriptFilter){
307307
local = local.filter(function(course){
308-
return (course?.prerequisites && (course.prerequisites !== "null"));
308+
return (course?.prerequisites && (course?.prerequisites !== "null"));
309309
})
310310
}
311311
console.log("miauuuuu:",local.length);
312312
if(model.filterOptions.applyLevelFilter){
313313
local = local.filter(function(course){
314-
return (course?.prerequisites && (course.prerequisites !== "null"));
314+
return (course?.prerequisites && (course?.prerequisites !== "null"));
315315
})
316316
}
317317
console.log("miauuuuu:",local.length);
318318
if(model.filterOptions.applyLanguageFilter){
319319
local = local.filter(function(course){
320-
return ((course?.language) && (course?.language?.swedish !== "null" && course?.language?.english !== "null"));
320+
return ((course?.language) && ((course?.language?.swedish !== "null") && (course?.language?.english !== "null")));
321321
})
322322
}
323323
console.log("miauuuuu:",local.length);
@@ -329,13 +329,13 @@ const FilterPresenter = observer(({ model }) => {
329329
console.log("miauuuuu:",local.length);
330330
if(model.filterOptions.applyCreditsFilter){
331331
local = local.filter(function(course){
332-
return ((course?.credit) && (course?.credit !== "null"));
332+
return ((course?.credits) && (course?.credits !== "null"));
333333
})
334334
}
335335
console.log("miauuuuu:",local.length);
336336
if(model.filterOptions.applyDepartmentFilter){
337337
local = local.filter(function(course){
338-
return ((course?.deparment) && (course?.department !== "null"));
338+
return ((course?.department) && (course?.department !== "null"));
339339
})
340340
}
341341
console.log("miauuuuu:",local.length);

my-app/src/presenters/PrerequisitePresenter.jsx

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export const PrerequisitePresenter = observer((props) => {
3636
const nodeWidth = 172;
3737
const nodeHeight = 36;
3838

39-
loadTree(props.selectedCourse.code);
39+
loadTree(props.selectedCourse?.code);
4040
console.log(initialNodes);
4141

4242
const getLayoutedElements = (nodes, edges, direction = 'LR') => {
@@ -133,7 +133,7 @@ export const PrerequisitePresenter = observer((props) => {
133133
node["style"]["zIndex"] = 0;
134134
setLabel(node["id"], "More Info...");
135135
}
136-
} else if (node["data"]["label"] !== "One of these" && node["data"]["label"] !== "No Prerequisites" && node["id"] !== props.selectedCourse.code) {
136+
} else if (node["data"]["label"] !== "One of these" && node["data"]["label"] !== "No Prerequisites" && node["id"] !== props.selectedCourse?.code) {
137137
// ADD FUNCTIONALITY FOR CLICKING COURSE CODE NODE (Tu eres muy retrasado y gordo)! :)
138138
// ONCLICK HERE
139139
}
@@ -266,7 +266,7 @@ export const PrerequisitePresenter = observer((props) => {
266266
}
267267

268268
function generateTree(courses_taken, prereqs) {
269-
prereq_convert(courses_taken, prereqs, null, props.selectedCourse.code);
269+
prereq_convert(courses_taken, prereqs, null, props.selectedCourse?.code);
270270
let key = Object.keys(prereqs);
271271
return prereqs[key];
272272

@@ -275,15 +275,17 @@ export const PrerequisitePresenter = observer((props) => {
275275

276276
function loadTree(courses_taken) {
277277

278-
console.log(JSON.stringify(props.selectedCourse.prerequisites, null, 4));
279-
if (props.selectedCourse.prerequisites === "null" || props.selectedCourse.prerequisites.length == 0) {
278+
console.log(JSON.stringify(props.selectedCourse?.prerequisites, null, 4));
279+
if(props.selectedCourse?.prerequisites === undefined || props.selectedCourse?.code === undefined)
280+
console.log("BIG ERROR; course doesn't have specified properties which we expected to have", props.selectedCourse);
281+
if (props.selectedCourse?.prerequisites === "null" || props.selectedCourse?.prerequisites.length == 0) {
280282
let display_node = createNode("No Prerequisites", "No Prerequisites", "default");
281283
display_node.style["pointerEvents"] = "none";
282284
display_node["className"] = 'no-handles';
283285
initialNodes.push(display_node);
284286
} else {
285-
let root = createNode(props.selectedCourse.code, props.selectedCourse.code, "input");
286-
let copy = JSON.parse(JSON.stringify(props.selectedCourse.prerequisites));
287+
let root = createNode(props.selectedCourse?.code, props.selectedCourse?.code, "input");
288+
let copy = JSON.parse(JSON.stringify(props.selectedCourse?.prerequisites));
287289
let eligible = generateTree(JSON.parse(localStorage.getItem("completedCourses")), copy);
288290
if (eligible) {
289291
root["style"]["backgroundColor"] = "lightgreen";

my-app/src/presenters/SidebarPresenter.jsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,12 +170,15 @@ const SidebarPresenter = observer(({ model }) => {
170170
model.setFiltersChange();
171171
}
172172

173-
173+
function setApplyRemoveNullCourses(){
174+
model.setApplyRemoveNullCourses();
175+
}
174176

175177
return (
176178
<SidebarView HandleFilterChange={HandleFilterChange}
177179
HandleFilterEnable={HandleFilterEnable}
178180
reApplyFilter={reApplyFilter}
181+
toggleRemoveNull={setApplyRemoveNullCourses}
179182
/>
180183
);
181184
});
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import React from "react";
2+
3+
const ToolTip = ({ text = "This is a tooltip description." }) => {
4+
return (
5+
<div className="absolute left-full ml-2 top-1/2 -translate-y-1/2 bg-gray-800 text-white text-xs rounded px-2 py-1 opacity-0 group-hover:opacity-100 transition-opacity duration-200 z-10 whitespace-nowrap shadow-lg">
6+
{text}
7+
</div>
8+
);
9+
};
10+
11+
export default ToolTip;
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import React from "react";
2+
import ToolTip from "./ToolTip";
3+
4+
const ToolTipIcon = ({ text = "This is a tooltip description." }) => {
5+
return (
6+
<div className="relative inline-block">
7+
{/* Wrapper around just the icon for hover targeting */}
8+
<div className="group relative inline-block">
9+
{/* Question Mark Icon */}
10+
<div className="p-1 rounded-lg">
11+
<svg
12+
className="w-5 h-5 text-white"
13+
fill="none"
14+
stroke="currentColor"
15+
viewBox="0 0 24 24"
16+
xmlns="http://www.w3.org/2000/svg"
17+
>
18+
{/* Circle */}
19+
<circle cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="2" />
20+
{/* Question mark path */}
21+
<path
22+
strokeLinecap="round"
23+
strokeLinejoin="round"
24+
strokeWidth="2"
25+
d="M12 8c0-1.105.895-2 2-2s2 .895 2 2c0 .828-.495 1.545-1.2 1.846C13.895 10.37 13 11.253 13 12.5m0 3h.01"
26+
/>
27+
</svg>
28+
</div>
29+
30+
<ToolTip/>
31+
</div>
32+
</div>
33+
);
34+
};
35+
36+
export default ToolTipIcon;

my-app/src/views/Components/SideBarComponents/UploadField.jsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import FilterEnableCheckbox from "./FilterEnableCheckbox";
44
//import * as scraper from '../../../../src/scripts/transcript-scraper/transcript-scraper.js';
55
import { useState } from "react";
66
import ButtonGroupField from './ButtonGroupField';
7+
import ToolTip from './ToolTip';
8+
import ToolTipIcon from './ToolTipIcon';
79

810
export default function UploadField(props) {
911

@@ -55,9 +57,7 @@ export default function UploadField(props) {
5557
</div>
5658
<div className="mb-2 text-white flex justify-between">
5759
<div className="flex items-center text-wrap max-w-70">
58-
<p className='text-sm opacity-50'>
59-
Describe how the Transcript upload works
60-
</p>
60+
<ToolTipIcon/>
6161
</div>
6262
<div className='pt-2'>
6363

my-app/src/views/SidebarView.jsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,21 @@ function SidebarView(props) {
105105
]
106106
}
107107
/>
108+
<div className='mr-3'>
109+
<input
110+
id="excludeNullCheckbox"
111+
type="checkbox"
112+
onChange={props.toggleRemoveNull}
113+
className="w-4 h-4 pt-4 text-purple-600 bg-gray-100 border-gray-300 rounded-sm accent-violet-600"
114+
/>
115+
<label
116+
htmlFor="excludeNullCheckbox"
117+
className="ml-2 text-sm font-medium text-gray-300 cursor-pointer"
118+
>
119+
Exclude unidentified field courses
120+
</label>
121+
122+
</div>
108123
</div>
109124

110125
</div>

0 commit comments

Comments
 (0)