Skip to content

Commit 87eafb1

Browse files
committed
Merge branch 'main' of github.com:jkluge/Find-My-Next-Course into reviews
2 parents 5362740 + ee2188c commit 87eafb1

8 files changed

Lines changed: 2041 additions & 346 deletions

File tree

my-app/package-lock.json

Lines changed: 1760 additions & 138 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: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"pdfjs-dist": "^5.1.91",
2020
"react": "^19.0.0",
2121
"react-dom": "^19.0.0",
22+
"react-infinite-scroll-component": "^6.1.0",
2223
"react-router-dom": "^7.4.0",
2324
"reactflow": "^11.11.4",
2425
"tailwindcss": "^4.0.17"

my-app/src/presenters/ListViewPresenter.jsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const ListViewPresenter = observer(({ model }) => {
1010
const addFavourite = (course) => {
1111
model.addFavourite(course);
1212
}
13-
const removeFavourite = (course) => {
13+
const removeFavourite = (course) => {
1414
model.removeFavourite(course);
1515
}
1616
const handleFavouriteClick = (course) => {
@@ -30,6 +30,7 @@ const ListViewPresenter = observer(({ model }) => {
3030
favouriteCourses={model.favourites}
3131
addFavourite={addFavourite}
3232
removeFavourite={removeFavourite}
33+
handleFavouriteClick={handleFavouriteClick}
3334
isOpen={isPopupOpen} onClose={() => setIsPopupOpen(false)}
3435
course={selectedCourse}
3536
prerequisiteTree={preP}
@@ -46,7 +47,7 @@ const ListViewPresenter = observer(({ model }) => {
4647
isPopupOpen={isPopupOpen}
4748
setIsPopupOpen={setIsPopupOpen}
4849
setSelectedCourse={setSelectedCourse}
49-
popUp={popup}
50+
popup={popup}
5051
handleFavouriteClick={handleFavouriteClick}
5152

5253
/>;

my-app/src/presenters/SearchbarPresenter.jsx

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,68 @@
11
import React from 'react';
22
import { observer } from "mobx-react-lite";
3+
import { useState } from 'react';
4+
import CoursePagePopup from '../views/Components/CoursePagePopup.jsx';
5+
import PrerequisitePresenter from './PrerequisitePresenter.jsx';
36
import SearchbarView from "../views/SearchbarView.jsx";
47

58
const SearchbarPresenter = observer(({ model }) => {
69
const searchCourses = (query) => {
710
const searchResults = model.courses.filter(course =>
8-
course.code.toLowerCase() === query.toLowerCase() ||
11+
course.code.toLowerCase().includes(query.toLowerCase()) ||
912
course.name.toLowerCase().includes(query.toLowerCase()) ||
1013
course.description.toLowerCase().includes(query.toLowerCase())
1114
);
1215
model.setCurrentSearch(searchResults);
1316
}
1417

18+
const addFavourite = (course) => {
19+
model.addFavourite(course);
20+
}
1521
const removeFavourite = (course) => {
1622
model.removeFavourite(course);
1723
}
24+
const handleFavouriteClick = (course) => {
25+
if (model.favourites.some(fav => fav.code === course.code)) {
26+
model.removeFavourite(course);
27+
} else {
28+
model.addFavourite(course);
29+
}
30+
};
31+
1832

19-
function removeAllFavourites(){
33+
function removeAllFavourites() {
2034
model.setFavourite([]);
2135
}
2236

37+
const [isPopupOpen, setIsPopupOpen] = useState(false);
38+
const [selectedCourse, setSelectedCourse] = useState(null);
39+
const preP = <PrerequisitePresenter model={model} selectedCourse={selectedCourse} />
40+
41+
const popup = <CoursePagePopup
42+
favouriteCourses={model.favourites}
43+
addFavourite={addFavourite}
44+
removeFavourite={removeFavourite}
45+
handleFavouriteClick={handleFavouriteClick}
46+
isOpen={isPopupOpen} onClose={() => setIsPopupOpen(false)}
47+
course={selectedCourse}
48+
prerequisiteTree={preP} />
49+
50+
51+
52+
2353
return (
2454
<SearchbarView
2555
model={model}
2656
searchCourses={searchCourses}
27-
favouriteCourses = {model.favourites}
28-
removeFavourite={removeFavourite}
57+
favouriteCourses={model.favourites}
2958
removeAllFavourites={removeAllFavourites}
59+
addFavourite={addFavourite}
60+
removeFavourite={removeFavourite}
61+
isPopupOpen={isPopupOpen}
62+
setIsPopupOpen={setIsPopupOpen}
63+
setSelectedCourse={setSelectedCourse}
64+
popup={popup}
65+
handleFavouriteClick={handleFavouriteClick}
3066
/>
3167
);
3268
});
Lines changed: 102 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -1,122 +1,110 @@
1-
import React, { useEffect, useRef } from "react";
1+
import React, { useEffect, useRef } from 'react';
22

33
function CoursePagePopup({
4-
favouriteCourses,
5-
addFavourite,
6-
removeFavourite,
7-
isOpen,
8-
onClose,
9-
course,
10-
prerequisiteTree,
11-
reviewPresenter,
12-
}) {
13-
const treeRef = useRef(null);
14-
useEffect(() => {
15-
const handleKeyDown = (event) => {
16-
if (event.key === "Escape") {
17-
onClose();
18-
}
19-
};
4+
favouriteCourses,
5+
handleFavouriteClick,
6+
isOpen,
7+
onClose,
8+
course,
9+
prerequisiteTree,
10+
reviewPresenter,
11+
}) {
12+
const treeRef = useRef(null);
2013

21-
if (isOpen) {
22-
window.addEventListener("keydown", handleKeyDown);
23-
}
14+
useEffect(() => {
15+
const handleKeyDown = (event) => {
16+
if (event.key === 'Escape') {
17+
onClose();
18+
}
19+
};
20+
if (isOpen) {
21+
window.addEventListener('keydown', handleKeyDown);
22+
}
23+
return () => {
24+
window.removeEventListener('keydown', handleKeyDown);
25+
};
26+
}, [isOpen, onClose]);
2427

25-
return () => {
26-
window.removeEventListener("keydown", handleKeyDown);
27-
};
28-
}, [isOpen, onClose]);
28+
const handleTreeClick = () => {
29+
if (treeRef.current) {
30+
treeRef.current.focus();
31+
}
32+
};
2933

30-
const handleFavouriteClick = (course) => {
31-
if (favouriteCourses.some((fav) => fav.code === course.code)) {
32-
removeFavourite(course);
33-
} else {
34-
addFavourite(course);
35-
}
36-
};
34+
if (!isOpen || !course) return null;
3735

38-
const handleTreeClick = () => {
39-
if (treeRef.current) {
40-
treeRef.current.focus(); // gives it focus
41-
}
42-
};
43-
44-
if (!isOpen || !course) return null; // Don't render if not open or course not selected
45-
return (
46-
<div
47-
className="fixed backdrop-blur-sm inset-0 bg-transparent flex justify-end z-50"
48-
onClick={onClose}
49-
>
50-
<div
51-
className="bg-indigo-300/75 backdrop-blur-lg h-full w-3/4 flex flex-col overflow-auto"
52-
onClick={(e) => e.stopPropagation()}
53-
>
54-
<div className="flex-1">
55-
<div className="px-10 py-10 md:px-20 md:py-16 text-slate-900 space-y-12 font-sans">
56-
{/* Course Title Section */}
57-
<div>
58-
<h2 className="text-5xl font-extrabold text-[#2e2e4f] ">
59-
<span className="text-violet-700">{course.code}</span> -{" "}
60-
{course.name}
61-
<span className="ml-4 text-lg text-violet-700 whitespace-nowrap">
62-
({course.credits} Credits)
63-
</span>
64-
</h2>
65-
<div className="my-6 h-1.5 w-full bg-violet-500"></div>
66-
</div>
67-
<div>
68-
<button
69-
className="text-yellow-500 bg-yellow-400 cursor-pointer"
70-
onClick={(e) => {
71-
e.stopPropagation(); // prevent popup from opening
72-
handleFavouriteClick(course.code);
73-
}}
74-
>
75-
{favouriteCourses.includes(course.code)
76-
? "Remove from Favourites"
77-
: "Add to Favourites"}
78-
</button>
79-
</div>
80-
81-
{/* Description Section */}
82-
<div>
83-
<h3 className="text-2xl font-bold text-[#2e2e4f] mb-0.5">
84-
Course Description
85-
</h3>
86-
<div className="mb-3 h-0.5 w-full bg-violet-500"></div>
87-
<div
88-
className="text-lg leading-8 text-[#2e2e4f] font-semibold tracking-wide prose prose-slate max-w-full"
89-
dangerouslySetInnerHTML={{ __html: course.description }}
90-
/>
91-
</div>
92-
93-
{/* Prerequisite Graph Tree Section */}
94-
<div>
95-
<h3 className="text-2xl font-semibold text-[#2e2e4f] mb-0.5">
96-
Prerequisite Graph Tree
97-
</h3>
98-
<div className="mb-4 h-0.5 w-full bg-violet-500"></div>
99-
<div
100-
className="bg-indigo-300/50 outline-none focus:outline-none focus:ring-2 focus:ring-violet-600 rounded-lg transition-shadow"
101-
ref={treeRef}
102-
onClick={handleTreeClick}
103-
tabIndex={0} // allows the div to receive focus
104-
>
105-
{prerequisiteTree}
106-
</div>
107-
</div>
108-
{reviewPresenter}
109-
</div>
110-
</div>
111-
<button
112-
onClick={onClose}
113-
className="px-4 py-2 bg-violet-500 text-white"
114-
>
115-
Close
116-
</button>
117-
</div>
118-
</div>
119-
);
36+
return (
37+
<div
38+
className="fixed backdrop-blur-sm inset-0 bg-transparent flex justify-end z-50"
39+
onClick={onClose}
40+
>
41+
<div
42+
className="bg-indigo-300/75 backdrop-blur-lg h-full w-3/4 flex flex-col overflow-auto"
43+
onClick={(e) => e.stopPropagation()}
44+
>
45+
<div className="flex-1">
46+
<div className="px-10 py-10 md:px-20 md:py-16 text-slate-900 space-y-12 font-sans">
47+
{/* Course Title Section */}
48+
<div>
49+
<h2 className="text-5xl font-extrabold text-[#2e2e4f]">
50+
<span className="text-violet-700">{course.code}</span> - {course.name}
51+
<span className="ml-4 text-lg text-violet-700 whitespace-nowrap">
52+
({course.credits} Credits)
53+
</span>
54+
</h2>
55+
<div className="my-6 h-1.5 w-full bg-violet-500"></div>
56+
</div>
57+
<div>
58+
<button
59+
className="text-yellow-100 bg-yellow-400 cursor-pointer"
60+
onClick={(e) => {
61+
e.stopPropagation();
62+
handleFavouriteClick(course);
63+
}}
64+
>
65+
{favouriteCourses.some((fav) => fav.code === course.code)
66+
? 'Remove from Favourites'
67+
: 'Add to Favourites'}
68+
</button>
69+
</div>
70+
{/* Description Section */}
71+
<div>
72+
<h3 className="text-2xl font-bold text-[#2e2e4f] mb-0.5">Course Description</h3>
73+
<div className="mb-3 h-0.5 w-full bg-violet-500"></div>
74+
<div
75+
className="text-lg leading-8 text-[#2e2e4f] font-semibold tracking-wide prose prose-slate max-w-full"
76+
dangerouslySetInnerHTML={{ __html: course.description }}
77+
/>
78+
</div>
79+
{/* Prerequisite Graph Tree Section */}
80+
<div>
81+
<h3 className="text-2xl font-semibold text-[#2e2e4f] mb-0.5">Prerequisite Graph Tree</h3>
82+
<div className="mb-4 h-0.5 w-full bg-violet-500"></div>
83+
<div
84+
className="bg-indigo-300/50 outline-none focus:outline-none focus:ring-2 focus:ring-violet-600 rounded-lg transition-shadow"
85+
ref={treeRef}
86+
onClick={handleTreeClick}
87+
tabIndex={0}
88+
>
89+
{prerequisiteTree}
90+
</div>
91+
</div>
92+
{/* Reviews Section (optional) */}
93+
{reviewPresenter && (
94+
<div>
95+
<h3 className="text-2xl font-semibold text-[#2e2e4f] mb-0.5">Reviews</h3>
96+
<div className="mb-4 h-0.5 w-full bg-violet-500"></div>
97+
{reviewPresenter}
98+
</div>
99+
)}
100+
</div>
101+
</div>
102+
<button onClick={onClose} className="px-4 py-2 bg-violet-500 text-white">
103+
Close
104+
</button>
105+
</div>
106+
</div>
107+
);
120108
}
121109

122-
export default CoursePagePopup;
110+
export default CoursePagePopup;

0 commit comments

Comments
 (0)