From d8bd7ff2a0e478378804bfd24e99dbc628a446ef Mon Sep 17 00:00:00 2001 From: Kacper Lisik Date: Wed, 9 Apr 2025 11:19:15 +0200 Subject: [PATCH 01/17] Course page merged with main --- my-app/src/pages/App.jsx | 27 ------ .../src/views/Components/CoursePagePopup.jsx | 30 +++++++ my-app/src/views/CourseView.jsx | 84 +++++++++---------- my-app/src/views/ListView.jsx | 17 ++++ 4 files changed, 85 insertions(+), 73 deletions(-) create mode 100644 my-app/src/views/Components/CoursePagePopup.jsx diff --git a/my-app/src/pages/App.jsx b/my-app/src/pages/App.jsx index 4906db50..baf836f2 100644 --- a/my-app/src/pages/App.jsx +++ b/my-app/src/pages/App.jsx @@ -22,33 +22,6 @@ function App() {
- - {isPopupOpen && ( -
setIsPopupOpen(false)} - > -
e.stopPropagation()} - > -
- -
- -
-
- )} ); diff --git a/my-app/src/views/Components/CoursePagePopup.jsx b/my-app/src/views/Components/CoursePagePopup.jsx new file mode 100644 index 00000000..8a5239bf --- /dev/null +++ b/my-app/src/views/Components/CoursePagePopup.jsx @@ -0,0 +1,30 @@ +import React from 'react'; +import CourseView from '../CourseView'; + +function CoursePagePopup({ isOpen, onClose, course }) { + if (!isOpen || !course) return null; // Don't render if not open or course not selected + + return ( +
+
e.stopPropagation()} + > +
+ +
+ +
+
+ ); +} + +export default CoursePagePopup; diff --git a/my-app/src/views/CourseView.jsx b/my-app/src/views/CourseView.jsx index 9debafb5..225f9f86 100644 --- a/my-app/src/views/CourseView.jsx +++ b/my-app/src/views/CourseView.jsx @@ -1,61 +1,53 @@ import React from 'react'; +// import {model} from '/src/model.js'; -export default function CourseView(props) { +export default function CourseView({ course }) { + if (!course) return null; + + return ( -
+
{/* Course Title Section */} -
-

ID1203 - Best course in the Universe

+
+

+ {course.code} - {course.name} + ({course.credits} Credits) {/* Display Credits */} +

+
+
+
{/* Description Section */} -
-

- The course is an introduction to networking, protocols and communication. - - We study how large international networks are constructed from the individual computers, via local area, city and national networks. We use the Internet as or working example of such a network. The aim of the course is to give insights into both the theory and practice of the area. - We study how large international networks are constructed from the individual computers, via local area, city and national networks. We use the Internet as or working example of such a network. The aim of the course is to give insights into both the theory and practice of the area. - We study how large international networks are constructed from the individual computers, via local area, city and national networks. We use the Internet as or working example of such a network. The aim of the course is to give insights into both the theory and practice of the area. - We study how large international networks are constructed from the individual computers, via local area, city and national networks. We use the Internet as or working example of such a network. The aim of the course is to give insights into both the theory and practice of the area. - We study how large international networks are constructed from the individual computers, via local area, city and national networks. We use the Internet as or working example of such a network. The aim of the course is to give insights into both the theory and practice of the area. - We study how large international networks are constructed from the individual computers, via local area, city and national networks. We use the Internet as or working example of such a network. The aim of the course is to give insights into both the theory and practice of the area. - We study how large international networks are constructed from the individual computers, via local area, city and national networks. We use the Internet as or working example of such a network. The aim of the course is to give insights into both the theory and practice of the area. - We study how large international networks are constructed from the individual computers, via local area, city and national networks. We use the Internet as or working example of such a network. The aim of the course is to give insights into both the theory and practice of the area. - We study how large international networks are constructed from the individual computers, via local area, city and national networks. We use the Internet as or working example of such a network. The aim of the course is to give insights into both the theory and practice of the area. - We study how large international networks are constructed from the individual computers, via local area, city and national networks. We use the Internet as or working example of such a network. The aim of the course is to give insights into both the theory and practice of the area. - We study how large international networks are constructed from the individual computers, via local area, city and national networks. We use the Internet as or working example of such a network. The aim of the course is to give insights into both the theory and practice of the area. - We study how large international networks are constructed from the individual computers, via local area, city and national networks. We use the Internet as or working example of such a network. The aim of the course is to give insights into both the theory and practice of the area. - We study how large international networks are constructed from the individual computers, via local area, city and national networks. We use the Internet as or working example of such a network. The aim of the course is to give insights into both the theory and practice of the area. - We study how large international networks are constructed from the individual computers, via local area, city and national networks. We use the Internet as or working example of such a network. The aim of the course is to give insights into both the theory and practice of the area. - We study how large international networks are constructed from the individual computers, via local area, city and national networks. We use the Internet as or working example of such a network. The aim of the course is to give insights into both the theory and practice of the area. - We study how large international networks are constructed from the individual computers, via local area, city and national networks. We use the Internet as or working example of such a network. The aim of the course is to give insights into both the theory and practice of the area. - We study how large international networks are constructed from the individual computers, via local area, city and national networks. We use the Internet as or working example of such a network. The aim of the course is to give insights into both the theory and practice of the area. - We study how large international networks are constructed from the individual computers, via local area, city and national networks. We use the Internet as or working example of such a network. The aim of the course is to give insights into both the theory and practice of the area. - We study how large international networks are constructed from the individual computers, via local area, city and national networks. We use the Internet as or working example of such a network. The aim of the course is to give insights into both the theory and practice of the area. - We study how large international networks are constructed from the individual computers, via local area, city and national networks. We use the Internet as or working example of such a network. The aim of the course is to give insights into both the theory and practice of the area. - We study how large international networks are constructed from the individual computers, via local area, city and national networks. We use the Internet as or working example of such a network. The aim of the course is to give insights into both the theory and practice of the area. - We study how large international networks are constructed from the individual computers, via local area, city and national networks. We use the Internet as or working example of such a network. The aim of the course is to give insights into both the theory and practice of the area. - We study how large international networks are constructed from the individual computers, via local area, city and national networks. We use the Internet as or working example of such a network. The aim of the course is to give insights into both the theory and practice of the area. - We study how large international networks are constructed from the individual computers, via local area, city and national networks. We use the Internet as or working example of such a network. The aim of the course is to give insights into both the theory and practice of the area. - We study how large international networks are constructed from the individual computers, via local area, city and national networks. We use the Internet as or working example of such a network. The aim of the course is to give insights into both the theory and practice of the area. - We study how large international networks are constructed from the individual computers, via local area, city and national networks. We use the Internet as or working example of such a network. The aim of the course is to give insights into both the theory and practice of the area. - -The focus of the course is on the protocols and algorithms used, and we will follow how they are used and implemented into the TCP/IP-stack - the basis of the Internet.

+
+

Course Description

+
+ {/* Prerequisite Graph Tree Section */} +
+

Prerequisite Graph Tree

+

Graph tree or prerequisite info will go here...

+
{/* Reviews Section */} -
-

Reviews

- {/* Placeholder for reviews */} -

Here would be some reviews of the course...

+
+

Reviews

+

Here would be some reviews of the course...

- {/* Prerequisite Graph Tree Section */} -
-

Prerequisite Graph Tree

- {/* Placeholder for graph tree */} -

Graph tree or prerequisite info will go here...

-
); } diff --git a/my-app/src/views/ListView.jsx b/my-app/src/views/ListView.jsx index 5f6512bf..e18fb4ff 100644 --- a/my-app/src/views/ListView.jsx +++ b/my-app/src/views/ListView.jsx @@ -1,10 +1,15 @@ import React, { useState } from 'react'; import { Quantum } from 'ldrs/react'; import 'ldrs/react/Quantum.css'; +import CoursePagePopupComponent from '../views/Components/CoursePagePopup.jsx'; + function ListView(props) { const coursesToDisplay = props.searchResults.length > 0 ? props.searchResults : props.courses; const [readMoreState, setReadMoreState] = useState({}); + const [isPopupOpen, setIsPopupOpen] = useState(false); + const [selectedCourse, setSelectedCourse] = useState(null); + const toggleReadMore = (courseCode) => { setReadMoreState(prevState => ( @@ -19,12 +24,18 @@ function ListView(props) { props.addFavourite(course); } }; + return (
{coursesToDisplay.length > 0 ? ( coursesToDisplay.map((course) => (
{ + console.log('Clicked:', course); // check browser console + setSelectedCourse(course); + setIsPopupOpen(true); + }} key={course.code} className="p-5 hover:bg-blue-100 flex items-center border border-b-black border-solid w-full rounded-lg cursor-pointer" > @@ -74,6 +85,12 @@ function ListView(props) { color="#000061" /> )} + setIsPopupOpen(false)} + + course={selectedCourse} + />
); } From 1959be81a9fe95cca49eb9a1a13f65da7604b06d Mon Sep 17 00:00:00 2001 From: Kacper Lisik Date: Wed, 9 Apr 2025 15:26:59 +0200 Subject: [PATCH 02/17] some styling changes --- my-app/src/views/Components/CoursePagePopup.jsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/my-app/src/views/Components/CoursePagePopup.jsx b/my-app/src/views/Components/CoursePagePopup.jsx index 8ca557f6..ea2b5266 100644 --- a/my-app/src/views/Components/CoursePagePopup.jsx +++ b/my-app/src/views/Components/CoursePagePopup.jsx @@ -2,24 +2,24 @@ import React from 'react'; function CoursePagePopup({ isOpen, onClose, course, prerequisiteTree }) { if (!isOpen || !course) return null; // Don't render if not open or course not selected - return (
e.stopPropagation()} >
{/* Course Title Section */}
-

- {course.code} - {course.name} - ({course.credits} Credits) {/* Display Credits */} +

+ {course.code} - {course.name} + ({course.credits} Credits)

+
-
- - {/* Description Section */} -
-

Course Description

-
-
- -
-======= function CoursePagePopup({ favouriteCourses, handleFavouriteClick, @@ -77,7 +30,6 @@ function CoursePagePopup({ treeRef.current.focus(); } }; ->>>>>>> ee2188cfb5b8df8b8c6fc633c807995b4bf6609d if (!isOpen || !course) return null; From 91a8d598578f0fd500693b3f31ca1c9960c82751 Mon Sep 17 00:00:00 2001 From: Kacper Lisik Date: Thu, 10 Apr 2025 16:46:47 +0200 Subject: [PATCH 04/17] fixed fav item reviews --- my-app/src/pages/App.jsx | 4 +- my-app/src/presenters/SearchbarPresenter.jsx | 3 + .../src/views/Components/CoursePagePopup.jsx | 192 +++++++++--------- .../views/Components/FavouriteDropdown.jsx | 5 +- my-app/src/views/ListView.jsx | 123 ++++++----- 5 files changed, 165 insertions(+), 162 deletions(-) diff --git a/my-app/src/pages/App.jsx b/my-app/src/pages/App.jsx index e1d7e9c2..ac1f93d5 100644 --- a/my-app/src/pages/App.jsx +++ b/my-app/src/pages/App.jsx @@ -6,7 +6,7 @@ import {model} from '/src/model.js'; function App() { return ( -
+
@@ -16,7 +16,7 @@ function App() { {/* bg-gradient-to-t from-[#6246a8] to-[#6747c0] */}
-
+
diff --git a/my-app/src/presenters/SearchbarPresenter.jsx b/my-app/src/presenters/SearchbarPresenter.jsx index f34e9d03..ff94f632 100644 --- a/my-app/src/presenters/SearchbarPresenter.jsx +++ b/my-app/src/presenters/SearchbarPresenter.jsx @@ -3,6 +3,7 @@ import { observer } from "mobx-react-lite"; import { useState } from 'react'; import CoursePagePopup from '../views/Components/CoursePagePopup.jsx'; import PrerequisitePresenter from './PrerequisitePresenter.jsx'; +import {ReviewPresenter} from "../presenters/ReviewPresenter.jsx" import SearchbarView from "../views/SearchbarView.jsx"; const SearchbarPresenter = observer(({ model }) => { @@ -37,6 +38,7 @@ const SearchbarPresenter = observer(({ model }) => { const [isPopupOpen, setIsPopupOpen] = useState(false); const [selectedCourse, setSelectedCourse] = useState(null); const preP = + const reviewPresenter = ; const popup = { handleFavouriteClick={handleFavouriteClick} isOpen={isPopupOpen} onClose={() => setIsPopupOpen(false)} course={selectedCourse} + reviewPresenter={reviewPresenter} prerequisiteTree={preP} /> diff --git a/my-app/src/views/Components/CoursePagePopup.jsx b/my-app/src/views/Components/CoursePagePopup.jsx index f64ee474..901e5357 100644 --- a/my-app/src/views/Components/CoursePagePopup.jsx +++ b/my-app/src/views/Components/CoursePagePopup.jsx @@ -1,110 +1,110 @@ import React, { useEffect, useRef } from 'react'; function CoursePagePopup({ - favouriteCourses, - handleFavouriteClick, - isOpen, - onClose, - course, - prerequisiteTree, - reviewPresenter, - }) { - const treeRef = useRef(null); + favouriteCourses, + handleFavouriteClick, + isOpen, + onClose, + course, + prerequisiteTree, + reviewPresenter, +}) { + const treeRef = useRef(null); - useEffect(() => { - const handleKeyDown = (event) => { - if (event.key === 'Escape') { - onClose(); - } - }; - if (isOpen) { - window.addEventListener('keydown', handleKeyDown); - } - return () => { - window.removeEventListener('keydown', handleKeyDown); - }; - }, [isOpen, onClose]); - - const handleTreeClick = () => { - if (treeRef.current) { - treeRef.current.focus(); - } + useEffect(() => { + const handleKeyDown = (event) => { + if (event.key === 'Escape') { + onClose(); + } + }; + if (isOpen) { + window.addEventListener('keydown', handleKeyDown); + } + return () => { + window.removeEventListener('keydown', handleKeyDown); }; + }, [isOpen, onClose]); + + const handleTreeClick = () => { + if (treeRef.current) { + treeRef.current.focus(); + } + }; - if (!isOpen || !course) return null; + if (!isOpen || !course) return null; - return ( -
-
e.stopPropagation()} - > -
-
- {/* Course Title Section */} -
-

- {course.code} - {course.name} - + return ( +
+
e.stopPropagation()} + > +
+
+ {/* Course Title Section */} +
+

+ {course.code} - {course.name} + ({course.credits} Credits) -

-
-
-
- -
- {/* Description Section */} -
-

Course Description

-
-
-
- {/* Prerequisite Graph Tree Section */} -
-

Prerequisite Graph Tree

-
-
- {prerequisiteTree} -
-
- {/* Reviews Section (optional) */} - {reviewPresenter && ( -
-

Reviews

-
- {reviewPresenter} -
- )} -
-
- +

+
+
+
+ +
+ {/* Description Section */} +
+

Course Description

+
+
+
+ {/* Prerequisite Graph Tree Section */} +
+

Prerequisite Graph Tree

+
+
+ {prerequisiteTree} +
+ {/* Reviews Section (optional) */} + {reviewPresenter && ( +
+

Reviews

+
+ {reviewPresenter} +
+ )} +
- ); + +
+
+ ); } export default CoursePagePopup; \ No newline at end of file diff --git a/my-app/src/views/Components/FavouriteDropdown.jsx b/my-app/src/views/Components/FavouriteDropdown.jsx index 3611620b..60d414cb 100644 --- a/my-app/src/views/Components/FavouriteDropdown.jsx +++ b/my-app/src/views/Components/FavouriteDropdown.jsx @@ -8,16 +8,17 @@ function FavouritesDropdown(props) { {props.favouriteCourses.length > 0 ? ( props.favouriteCourses.map(course => (
{ console.log('Clicked:', course); props.setSelectedCourse(course); props.setIsPopupOpen(true); }} key={course.code} - className="p-2 flex justify-between items-center w-full border border-solid border-black"> + className="p-2 hover:bg-blue-100 cursor-pointer flex justify-between items-center w-full border border-solid border-black">

{course.name}

+ /> + {course.description.length > 150 && ( + { + e.stopPropagation(); + toggleReadMore(course.code); + }} + > + {readMore[course.code] ? ' show less' : ' read more'} + + )} +
+ +
-
- ))} - + ))} + +
)} {props.popup}
From c6e5aac5a6635d01ecd575eb87b93b96a6f742d2 Mon Sep 17 00:00:00 2001 From: Kacper Lisik Date: Fri, 11 Apr 2025 09:36:31 +0200 Subject: [PATCH 05/17] working fav drop-down list --- my-app/src/presenters/SearchbarPresenter.jsx | 5 +- .../views/Components/FavouriteDropdown.jsx | 103 +++++++++++------- my-app/src/views/SearchbarView.jsx | 32 +++--- 3 files changed, 86 insertions(+), 54 deletions(-) diff --git a/my-app/src/presenters/SearchbarPresenter.jsx b/my-app/src/presenters/SearchbarPresenter.jsx index ff94f632..d619d071 100644 --- a/my-app/src/presenters/SearchbarPresenter.jsx +++ b/my-app/src/presenters/SearchbarPresenter.jsx @@ -29,7 +29,9 @@ const SearchbarPresenter = observer(({ model }) => { model.addFavourite(course); } }; - + const creditsSum = (favouriteCourses) => { + return favouriteCourses.reduce((sum, course) => sum + parseFloat(course.credits), 0); + }; function removeAllFavourites() { model.setFavourite([]); @@ -66,6 +68,7 @@ const SearchbarPresenter = observer(({ model }) => { setSelectedCourse={setSelectedCourse} popup={popup} handleFavouriteClick={handleFavouriteClick} + const totalCredits = {creditsSum(model.favourites)} /> ); }); diff --git a/my-app/src/views/Components/FavouriteDropdown.jsx b/my-app/src/views/Components/FavouriteDropdown.jsx index 60d414cb..6ce9d192 100644 --- a/my-app/src/views/Components/FavouriteDropdown.jsx +++ b/my-app/src/views/Components/FavouriteDropdown.jsx @@ -1,48 +1,77 @@ import React from 'react'; function FavouritesDropdown(props) { - console.log('Popup:', props.popup); - console.log('isPopupOpen:', props.isPopupOpen); return ( -
- {props.favouriteCourses.length > 0 ? ( - props.favouriteCourses.map(course => ( -
+
+ {/* Fixed Header */} +
+

Code

+

Name

+

Credits

+
+
+ + {/* Scrollable Course List */} +
+ {props.favouriteCourses.length > 0 ? ( + props.favouriteCourses.map(course => ( +
{ + props.setSelectedCourse(course); + props.setIsPopupOpen(true); + }} + key={course.code} + className="p-3 hover:bg-indigo-400/50 cursor-pointer flex justify-between items-center w-full border-b border-solid border-violet-400"> +

{course.code}

+

{course.name}

+

{course.credits} hp

+ +
+ )) + ) : ( +
+ No favourites +
+ )} +
+ + {/* Fixed Footer */} +
+
+

Total:

+

+

{props.totalCredits} hp

+
+
- onClick={() => { - console.log('Clicked:', course); - props.setSelectedCourse(course); - props.setIsPopupOpen(true); - }} - key={course.code} - className="p-2 hover:bg-blue-100 cursor-pointer flex justify-between items-center w-full border border-solid border-black"> -

{course.name}

- +
+ {props.favouriteCourses.length > 0 && ( + <> + + + + )}
- )) - ) : ( -
- No favourites
- )} -
- {props.isPopupOpen && props.popup}
- {props.favouriteCourses.length > 0 && ( - - )} -
+ {props.isPopupOpen && props.popup} + ); } diff --git a/my-app/src/views/SearchbarView.jsx b/my-app/src/views/SearchbarView.jsx index 1a349154..464ea13d 100644 --- a/my-app/src/views/SearchbarView.jsx +++ b/my-app/src/views/SearchbarView.jsx @@ -35,9 +35,14 @@ function SearchbarView(props) { signOut(auth); }; - return ( + const handleClickOutside = (e) => { + if (!e.target.closest('.favourites-container')) { + setShowFavourites(false); + } + }; -
+ return ( +
-
- {showFavourites && ( e.stopPropagation()} /> )}
From d8e4ccb13e08c55e3be1031c55561acb8ae9f396 Mon Sep 17 00:00:00 2001 From: Kacper Lisik Date: Fri, 11 Apr 2025 09:42:05 +0200 Subject: [PATCH 06/17] small fix --- my-app/src/views/Components/FavouriteDropdown.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/my-app/src/views/Components/FavouriteDropdown.jsx b/my-app/src/views/Components/FavouriteDropdown.jsx index 6ce9d192..698308b6 100644 --- a/my-app/src/views/Components/FavouriteDropdown.jsx +++ b/my-app/src/views/Components/FavouriteDropdown.jsx @@ -57,11 +57,11 @@ function FavouritesDropdown(props) { <> From 53836c4c9ecccaeef036d45265545840eb61a059 Mon Sep 17 00:00:00 2001 From: Kacper Lisik Date: Fri, 11 Apr 2025 10:07:50 +0200 Subject: [PATCH 07/17] small fix: clicking the searchbar does close the fav list --- my-app/src/views/SearchbarView.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/my-app/src/views/SearchbarView.jsx b/my-app/src/views/SearchbarView.jsx index 464ea13d..eae174e4 100644 --- a/my-app/src/views/SearchbarView.jsx +++ b/my-app/src/views/SearchbarView.jsx @@ -55,7 +55,9 @@ function SearchbarView(props) { type="text" placeholder="What course are you looking for?" value={searchQuery} - onChange={(e) => handleSearch(e.target.value)} + onChange={(e) => + handleSearch(e.target.value)} + onClick={(e)=>e.stopPropagation()} //TODO decide if we want to close the fav list after clicking the searchbar className="w-[400px] h-[44px] pl-14 pr-4 bg-white text-black rounded-full" /> From d53dfac78bb27aa3c47bc0a973a50fb2968f1c0d Mon Sep 17 00:00:00 2001 From: Kacper Lisik Date: Fri, 11 Apr 2025 11:05:27 +0200 Subject: [PATCH 08/17] crated fav button --- .../src/views/Components/CoursePagePopup.jsx | 216 ++++++++++-------- my-app/src/views/ListView.jsx | 32 ++- 2 files changed, 148 insertions(+), 100 deletions(-) diff --git a/my-app/src/views/Components/CoursePagePopup.jsx b/my-app/src/views/Components/CoursePagePopup.jsx index 901e5357..1d4dde7f 100644 --- a/my-app/src/views/Components/CoursePagePopup.jsx +++ b/my-app/src/views/Components/CoursePagePopup.jsx @@ -1,110 +1,128 @@ import React, { useEffect, useRef } from 'react'; function CoursePagePopup({ - favouriteCourses, - handleFavouriteClick, - isOpen, - onClose, - course, - prerequisiteTree, - reviewPresenter, + favouriteCourses, + handleFavouriteClick, + isOpen, + onClose, + course, + prerequisiteTree, + reviewPresenter, }) { - const treeRef = useRef(null); + const treeRef = useRef(null); - useEffect(() => { - const handleKeyDown = (event) => { - if (event.key === 'Escape') { - onClose(); - } - }; - if (isOpen) { - window.addEventListener('keydown', handleKeyDown); - } - return () => { - window.removeEventListener('keydown', handleKeyDown); - }; - }, [isOpen, onClose]); + useEffect(() => { + const handleKeyDown = (event) => { + if (event.key === 'Escape') { + onClose(); + } + }; + if (isOpen) { + window.addEventListener('keydown', handleKeyDown); + } + return () => { + window.removeEventListener('keydown', handleKeyDown); + }; + }, [isOpen, onClose]); - const handleTreeClick = () => { - if (treeRef.current) { - treeRef.current.focus(); - } - }; + const handleTreeClick = () => { + if (treeRef.current) { + treeRef.current.focus(); + } + }; - if (!isOpen || !course) return null; + if (!isOpen || !course) return null; - return ( -
-
e.stopPropagation()} - > -
-
- {/* Course Title Section */} -
-

- {course.code} - {course.name} - - ({course.credits} Credits) - -

-
-
-
- -
- {/* Description Section */} -
-

Course Description

-
-
-
- {/* Prerequisite Graph Tree Section */} -
-

Prerequisite Graph Tree

-
-
- {prerequisiteTree} -
-
- {/* Reviews Section (optional) */} - {reviewPresenter && ( -
-

Reviews

-
- {reviewPresenter} -
- )} -
-
- -
-
- ); + return ( +
+
e.stopPropagation()} + > +
+
+ {/* Course Title Section */} +
+

+ {course.code} - {course.name} + + ({course.credits} Credits) + +

+
+
+
+ +
+ {/* Description Section */} +
+

Course Description

+
+
+
+ {/* Prerequisite Graph Tree Section */} +
+

Prerequisite Graph Tree

+
+
+ {prerequisiteTree} +
+
+ {/* Reviews Section (optional) */} + {reviewPresenter && ( +
+

Reviews

+
+ {reviewPresenter} +
+ )} +
+
+ +
+
+ ); } export default CoursePagePopup; \ No newline at end of file diff --git a/my-app/src/views/ListView.jsx b/my-app/src/views/ListView.jsx index 0e868552..3d82d4a4 100644 --- a/my-app/src/views/ListView.jsx +++ b/my-app/src/views/ListView.jsx @@ -94,7 +94,7 @@ function ListView(props) { )}
- */} + {/* The new button seems to not mach the style TODO: change the fav button*/} +
From 4c9a1f744fedab63a5e3ecacbbd1fa4f0e662c1d Mon Sep 17 00:00:00 2001 From: Kacper Lisik Date: Fri, 11 Apr 2025 13:39:39 +0200 Subject: [PATCH 09/17] small fix --- my-app/src/views/Components/FavouriteDropdown.jsx | 2 +- my-app/src/views/SearchbarView.jsx | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/my-app/src/views/Components/FavouriteDropdown.jsx b/my-app/src/views/Components/FavouriteDropdown.jsx index 698308b6..7dc150c4 100644 --- a/my-app/src/views/Components/FavouriteDropdown.jsx +++ b/my-app/src/views/Components/FavouriteDropdown.jsx @@ -3,7 +3,7 @@ import React from 'react'; function FavouritesDropdown(props) { return ( <> -
+
{/* Fixed Header */}

Code

diff --git a/my-app/src/views/SearchbarView.jsx b/my-app/src/views/SearchbarView.jsx index eae174e4..e75ab2bb 100644 --- a/my-app/src/views/SearchbarView.jsx +++ b/my-app/src/views/SearchbarView.jsx @@ -74,12 +74,18 @@ function SearchbarView(props) { e.stopPropagation(); setShowFavourites(!showFavourites); }} - className="w-[120px] h-[44px] bg-[#003399] text-white rounded-full border border-[#000061] cursor-pointer hover:bg-[#001a4d] transition-all duration-200"> - Favourites + className={`w-[120px] h-[44px] ${ + !showFavourites + ? 'bg-[#003399] text-white rounded-full border border-[#000061] cursor-pointer hover:bg-[#001a4d] transition-all duration-200' + : 'bg-[#003399] rounded-full border border-red-600 transition-all duration-200 cursor-pointer text-red-600 hover:bg-red-600 hover:text-white border-solid font-bold' + // cursor-pointer text-red-600 hover:bg-red-600 hover:text-white border-r border-solid border-violet-400 font-semibold transition-colors + + }`}> + {showFavourites? "Close":"Favourites"} {showFavourites && ( e.stopPropagation()} /> )} From 1dade476aa628791f49b1ff62fba784b3c909d72 Mon Sep 17 00:00:00 2001 From: Kacper Lisik Date: Wed, 16 Apr 2025 09:29:53 +0200 Subject: [PATCH 10/17] before merge with main --- my-app/src/assets/share_icon.png | Bin 0 -> 1417 bytes my-app/src/pages/SharedView.jsx | 87 ++++++++++++++++++ .../src/views/Components/RatingComponent.jsx | 22 +++++ my-app/src/views/Components/StarComponent.jsx | 32 +++++++ 4 files changed, 141 insertions(+) create mode 100644 my-app/src/assets/share_icon.png create mode 100644 my-app/src/pages/SharedView.jsx create mode 100644 my-app/src/views/Components/RatingComponent.jsx create mode 100644 my-app/src/views/Components/StarComponent.jsx diff --git a/my-app/src/assets/share_icon.png b/my-app/src/assets/share_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..44ade7450661ac89a621705255cf4182e7da95af GIT binary patch literal 1417 zcmYk6dsNbA7{{@jGc%7-qlTrUi<^0AZc3JyEKRL|YAF_|D=nMG577{d%&Zxxsiisa zQm(VolETc5o0wUGQ$)^A<|TjT1;vv5krzZ0WY}u!kN5qa^E~f)p7Vas`Fu0>V*{<0 zezg<^gINU!`5%PA%!u=6(Lz%tX)DssB&+xDI}`vxkeQj8(P(5cnX$34EEWrg!!SCOLyXY+u$3YRgmHXPR=#8GSwcIXglC6xh4OGRR`(HVSz9{OVB&snoejSjaT@l zc{Mnn&^YmikAoI;i!P|@y-HXm(CJTiegWQwzlwjayP3m8N~xHh&<5+q4lA83dfrp97H`#G&e0hPY#Jl)9mwZ~H}Fq*{J%apyioHeG#A z%m?zyj-(>WS)3_60~8C|?zm9z&9+h(pErLLQ^%R6+4nyUY1SGcWC{p(<&P8jujVG* zwmhDs)FIDm?Q-c3a%yT}Kc$;yYtS~B>)%ETAv8QcZtip*QJ0$`^R_K#QdpZjI=n0s z&|^YNPpCFS@!))zQ@z3O;;dgL{e0gxBv?F}v;CvsN7S$A%>oyF_x++KuvvD$UqM6_k*|*lX}eE? z%y`Wm0S-mnEWrc_V(k1Uq+|6>@tVOhZbS9M4mZ%it{wjZsJw=9xAK?Ccs!-4lL!*=t76uqj(%!2wu*?#`bs{sWC7_doyu literal 0 HcmV?d00001 diff --git a/my-app/src/pages/SharedView.jsx b/my-app/src/pages/SharedView.jsx new file mode 100644 index 00000000..08c17308 --- /dev/null +++ b/my-app/src/pages/SharedView.jsx @@ -0,0 +1,87 @@ +/*import React, { useEffect } from "react"; +import { model } from "../model"; + +/** + * Reads favs from URL hash query string and populates model.favourites + +function SharedView() { + useEffect(() => { + const processFavouritesFromURL = () => { + const hash = window.location.hash; + const queryString = hash.includes("?") ? hash.split("?")[1] : ""; + const params = new URLSearchParams(queryString); + const favCodes = (params.get("favs") || "").split(",").filter(Boolean); + + console.log("Parsed fav codes:", favCodes); + + if (!model.courses || model.courses.length === 0) { + console.warn("Courses not yet loaded, waiting..."); + return; + } + + console.log("Courses loaded. Processing shared favourites."); + + const favCourses = favCodes + .map(code => model.getCourse(code)) + .filter(course => course !== undefined); + + model.favourites = favCourses; + + console.log("Updated model.favourites:", favCourses); + }; + + const interval = setInterval(() => { + if (model.courses && model.courses.length > 0) { + processFavouritesFromURL(); + clearInterval(interval); // Stop polling once done + } + }, 200); // Poll every 200ms until courses are ready + + return () => clearInterval(interval); + }, []); + + return ( +
+

This is a shared view of someone's favourite courses.

+

Click the Favourites button to see the list!

+
+ ); +} + +export default SharedView; +*/ + +import React, { useEffect } from "react"; +import MainAppLayout from "./App.jsx"; // or wherever it's defined + +function SharedView({ model }) { + useEffect(() => { + const processFavouritesFromURL = () => { + const hash = window.location.hash; + const queryString = hash.includes("?") ? hash.split("?")[1] : ""; + const params = new URLSearchParams(queryString); + const favCodes = (params.get("favs") || "").split(",").filter(Boolean); + + if (!model.courses || model.courses.length === 0) return; + + const favCourses = favCodes + .map(code => model.getCourse(code)) + .filter(Boolean); + + model.favourites = favCourses; + }; + + const interval = setInterval(() => { + if (model.courses && model.courses.length > 0) { + processFavouritesFromURL(); + clearInterval(interval); + } + }, 200); + + return () => clearInterval(interval); + }, [model]); + + return ; +} + +export default SharedView; diff --git a/my-app/src/views/Components/RatingComponent.jsx b/my-app/src/views/Components/RatingComponent.jsx new file mode 100644 index 00000000..febc577d --- /dev/null +++ b/my-app/src/views/Components/RatingComponent.jsx @@ -0,0 +1,22 @@ +import React, {useState} from 'react'; +import StarComponent from "./StarComponent.jsx"; + +const RatingComponent = () => { + const [rating, setRating] = useState(0); + + const handleRating = (starIndex, isLeftHalf) => { + const newRating = isLeftHalf ? starIndex + 0.5 : starIndex + 1; + setRating(newRating); + } + + return ( +
+
+ {Array.from({length: 5}, (_, index) => ( + ))} +
+
+ ); +} + +export default RatingComponent; \ No newline at end of file diff --git a/my-app/src/views/Components/StarComponent.jsx b/my-app/src/views/Components/StarComponent.jsx new file mode 100644 index 00000000..b5d45d74 --- /dev/null +++ b/my-app/src/views/Components/StarComponent.jsx @@ -0,0 +1,32 @@ +import React from 'react'; + +export const StarComponent = ({ index, rating, onRatingChange, onHover }) => { + const handleLeftClick = () => onRatingChange(index, false); + const handleRightClick = () => onRatingChange(index, true); + + const isFullStar = rating >= index + 1; + const isHalfStar = rating >= index + 0.5 && rating < index + 1; + const starClass = isFullStar ? "bxs-star" : isHalfStar ? "bxs-star-half" : "bx-star"; + + return ( +
onHover(index + 1)} + onMouseLeave={() => onHover(0)} + > + +
+ ); +}; + +export default StarComponent; \ No newline at end of file From 562c217bbc2c789846d346d4f0777ac39d822539 Mon Sep 17 00:00:00 2001 From: Kacper Lisik Date: Wed, 16 Apr 2025 10:25:09 +0200 Subject: [PATCH 11/17] added Found courses {num of courses} in the listview --- my-app/src/presenters/ListViewPresenter.jsx | 1 + my-app/src/views/ListView.jsx | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/my-app/src/presenters/ListViewPresenter.jsx b/my-app/src/presenters/ListViewPresenter.jsx index defb8a66..0264fd29 100644 --- a/my-app/src/presenters/ListViewPresenter.jsx +++ b/my-app/src/presenters/ListViewPresenter.jsx @@ -49,6 +49,7 @@ const ListViewPresenter = observer(({ model }) => { setSelectedCourse={setSelectedCourse} popup={popup} handleFavouriteClick={handleFavouriteClick} + currentSearchLenght={model.currentSearch.length} />; }); diff --git a/my-app/src/views/ListView.jsx b/my-app/src/views/ListView.jsx index 30916566..896ecd36 100644 --- a/my-app/src/views/ListView.jsx +++ b/my-app/src/views/ListView.jsx @@ -63,6 +63,14 @@ function ListView(props) {
) : (
+

+ Found + + {props.currentSearchLenght} + + courses +

+ Date: Wed, 16 Apr 2025 10:45:05 +0200 Subject: [PATCH 12/17] different "Found courses" --- my-app/src/views/ListView.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/my-app/src/views/ListView.jsx b/my-app/src/views/ListView.jsx index 14d0b0cb..72f4e8f6 100644 --- a/my-app/src/views/ListView.jsx +++ b/my-app/src/views/ListView.jsx @@ -56,14 +56,14 @@ function ListView(props) { } return ( -
+
{isLoading ? (
) : (
-

+

Found {props.currentSearchLenght} From 2a23b3e571f89aaadafd03e6bb0d30f4732fccb0 Mon Sep 17 00:00:00 2001 From: Kacper Lisik Date: Wed, 16 Apr 2025 11:42:24 +0200 Subject: [PATCH 13/17] small fixes: prereq tree edges, now you have to click the tree to interact with it --- .../src/presenters/PrerequisitePresenter.jsx | 4 +- .../src/views/Components/CoursePagePopup.jsx | 38 +++++++++++++------ my-app/src/views/PrerequisiteTreeView.jsx | 4 +- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/my-app/src/presenters/PrerequisitePresenter.jsx b/my-app/src/presenters/PrerequisitePresenter.jsx index 455f0cb1..98db2751 100644 --- a/my-app/src/presenters/PrerequisitePresenter.jsx +++ b/my-app/src/presenters/PrerequisitePresenter.jsx @@ -97,7 +97,7 @@ export const PrerequisitePresenter = observer((props) => { ); return ( -

+
{ onConnect={onConnect} connectionLineType={ConnectionLineType.SmoothStep} fitView - style={{ backgroundColor: '#F7F9FB' }} + style={{ backgroundColor: 'white', borderRadius: '10px'}} nodesDraggable={false} nodesConnectable={false} elementsSelectable={true} diff --git a/my-app/src/views/Components/CoursePagePopup.jsx b/my-app/src/views/Components/CoursePagePopup.jsx index 1d4dde7f..25c2de2e 100644 --- a/my-app/src/views/Components/CoursePagePopup.jsx +++ b/my-app/src/views/Components/CoursePagePopup.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useRef } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; function CoursePagePopup({ favouriteCourses, @@ -10,6 +10,7 @@ function CoursePagePopup({ reviewPresenter, }) { const treeRef = useRef(null); + const [showOverlay, setShowOverlay] = useState(true); useEffect(() => { const handleKeyDown = (event) => { @@ -40,7 +41,10 @@ function CoursePagePopup({ >
e.stopPropagation()} + onClick={(e) => { + e.stopPropagation(); + setShowOverlay(true); + }} >
@@ -97,15 +101,27 @@ function CoursePagePopup({ {/* Prerequisite Graph Tree Section */}

Prerequisite Graph Tree

-
-
- {prerequisiteTree} -
+
+
+ {showOverlay && ( +
{ + e.stopPropagation(); + setShowOverlay(false); + }} + > +
+ )} +
+ {prerequisiteTree} +
+
{/* Reviews Section (optional) */} {reviewPresenter && ( diff --git a/my-app/src/views/PrerequisiteTreeView.jsx b/my-app/src/views/PrerequisiteTreeView.jsx index 849a2ad8..f4d805c2 100644 --- a/my-app/src/views/PrerequisiteTreeView.jsx +++ b/my-app/src/views/PrerequisiteTreeView.jsx @@ -2,9 +2,9 @@ import React from "react"; import ReactFlow, { MiniMap, Controls, Background } from "reactflow"; import "reactflow/dist/style.css"; -function PrerequisiteTreeView(props){ +function PrerequisiteTreeView(props) { return ( -
+
From 929ee93bcc5884c08e504643c5debfb6f1a5617e Mon Sep 17 00:00:00 2001 From: Kacper Lisik Date: Wed, 16 Apr 2025 11:51:29 +0200 Subject: [PATCH 14/17] fixed fav list course popup --- .../views/Components/FavouriteDropdown.jsx | 123 +++++++++--------- my-app/src/views/ListView.jsx | 90 ++++++------- 2 files changed, 107 insertions(+), 106 deletions(-) diff --git a/my-app/src/views/Components/FavouriteDropdown.jsx b/my-app/src/views/Components/FavouriteDropdown.jsx index a58facb8..1d3d3e1e 100644 --- a/my-app/src/views/Components/FavouriteDropdown.jsx +++ b/my-app/src/views/Components/FavouriteDropdown.jsx @@ -7,10 +7,10 @@ const FavouritesDropdown = observer((props) => { function handleShareCourses() { if (!props.favouriteCourses || props.favouriteCourses.length === 0) return; - + const courseCodes = props.favouriteCourses.map(course => course.code).join(","); const url = `${window.location.origin}/#/share?favs=${encodeURIComponent(courseCodes)}`; - + navigator.clipboard.writeText(url) .then(() => { setCopied(true); @@ -27,72 +27,73 @@ const FavouritesDropdown = observer((props) => { } return ( -
- {/* Fixed Header */} -
-

Code

-

Name

-

Credits

-
-
+
+
+ {/* Fixed Header */} +
+

Code

+

Name

+

Credits

+
+
- {/* Course list */} -
- {props.favouriteCourses.length > 0 ? ( - props.favouriteCourses.map(course => ( -
{ - props.setSelectedCourse(course); - props.setIsPopupOpen(true); - }} - key={course.code} - className="p-3 hover:bg-indigo-400/50 cursor-pointer flex justify-between items-center w-full border-b border-solid border-violet-400" - > -

{course.code}

-

{course.name}

-

{course.credits} hp

- +

{course.code}

+

{course.name}

+

{course.credits} hp

+ +
+ )) + ) : ( +
+ No favourites
- )) - ) : ( -
- No favourites -
- )} -
+ )} +
-
- {props.favouriteCourses.length > 0 && ( - <> - - - - - )} -
+
+ {props.favouriteCourses.length > 0 && ( + <> + + + + )} +
+ +
{/* Optional course popup */} -
+
{props.isPopupOpen && props.popup}
diff --git a/my-app/src/views/ListView.jsx b/my-app/src/views/ListView.jsx index 72f4e8f6..1788ec94 100644 --- a/my-app/src/views/ListView.jsx +++ b/my-app/src/views/ListView.jsx @@ -4,56 +4,56 @@ import 'ldrs/react/Quantum.css'; import InfiniteScroll from 'react-infinite-scroll-component'; function ListView(props) { - const coursesToDisplay = props.searchResults; - /*(props.searchResults && props.searchResults.length > 0 - ? props.searchResults - : props.courses) || [];*/ + const coursesToDisplay = props.searchResults; + /*(props.searchResults && props.searchResults.length > 0 + ? props.searchResults + : props.courses) || [];*/ - const [displayedCourses, setDisplayedCourses] = useState([]); - const [hasMore, setHasMore] = useState(true); - const [readMore, setReadMore] = useState({}); - const [isLoading, setIsLoading] = useState(true); + const [displayedCourses, setDisplayedCourses] = useState([]); + const [hasMore, setHasMore] = useState(true); + const [readMore, setReadMore] = useState({}); + const [isLoading, setIsLoading] = useState(true); - const toggleReadMore = (courseCode) => { - setReadMore(prevState => ({ - ...prevState, - [courseCode]: !prevState[courseCode] - })); - }; + const toggleReadMore = (courseCode) => { + setReadMore(prevState => ({ + ...prevState, + [courseCode]: !prevState[courseCode] + })); + }; - const handleFavouriteClick = (course) => { - if (props.favouriteCourses?.some((fav) => fav.code === course.code)) { - props.removeFavourite(course); - } else { - props.addFavourite(course); - } - }; + const handleFavouriteClick = (course) => { + if (props.favouriteCourses?.some((fav) => fav.code === course.code)) { + props.removeFavourite(course); + } else { + props.addFavourite(course); + } + }; - useEffect(() => { - setIsLoading(true); - const initialCourses = coursesToDisplay.slice(0, 10); - setDisplayedCourses(initialCourses); - setHasMore(coursesToDisplay.length > 10); - setIsLoading(false); - }, [props.courses, props.searchResults]); + useEffect(() => { + setIsLoading(true); + const initialCourses = coursesToDisplay.slice(0, 10); + setDisplayedCourses(initialCourses); + setHasMore(coursesToDisplay.length > 10); + setIsLoading(false); + }, [props.courses, props.searchResults]); - const fetchMoreCourses = useCallback(() => { - if (!hasMore) return; + const fetchMoreCourses = useCallback(() => { + if (!hasMore) return; - const nextItems = coursesToDisplay.slice(displayedCourses.length, displayedCourses.length + 10); - setDisplayedCourses(prevCourses => [...prevCourses, ...nextItems]); - setHasMore(displayedCourses.length + nextItems.length < coursesToDisplay.length); - }, [displayedCourses.length, coursesToDisplay, hasMore]); + const nextItems = coursesToDisplay.slice(displayedCourses.length, displayedCourses.length + 10); + setDisplayedCourses(prevCourses => [...prevCourses, ...nextItems]); + setHasMore(displayedCourses.length + nextItems.length < coursesToDisplay.length); + }, [displayedCourses.length, coursesToDisplay, hasMore]); - if (!props.courses) { - return ( -
-
- ⚠️ No course data available. -
-
- ); - } + if (!props.courses) { + return ( +
+
+ ⚠️ No course data available. +
+
+ ); + } return (
@@ -64,13 +64,13 @@ function ListView(props) { ) : (

- Found + Found {props.currentSearchLenght} courses

- + Date: Wed, 16 Apr 2025 14:54:25 +0200 Subject: [PATCH 15/17] quick save --- my-app/src/views/Components/CoursePagePopup.jsx | 2 ++ my-app/src/views/ListView.jsx | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/my-app/src/views/Components/CoursePagePopup.jsx b/my-app/src/views/Components/CoursePagePopup.jsx index 25c2de2e..518f8122 100644 --- a/my-app/src/views/Components/CoursePagePopup.jsx +++ b/my-app/src/views/Components/CoursePagePopup.jsx @@ -34,8 +34,10 @@ function CoursePagePopup({ if (!isOpen || !course) return null; + console.log(course);`` return (
diff --git a/my-app/src/views/ListView.jsx b/my-app/src/views/ListView.jsx index 1788ec94..7d10241b 100644 --- a/my-app/src/views/ListView.jsx +++ b/my-app/src/views/ListView.jsx @@ -101,7 +101,7 @@ function ListView(props) { dangerouslySetInnerHTML={{ __html: readMore[course.code] ? course.description - : course.description.slice(0, 150), + : (course.description.slice(0, 200)+"..."), }} /> {course.description.length > 150 && ( From 682d51009386c29b0a0d5207cf53d3c09c9d1a0a Mon Sep 17 00:00:00 2001 From: Kacper Lisik Date: Wed, 16 Apr 2025 16:45:10 +0200 Subject: [PATCH 16/17] small fixes/features --- .../src/presenters/PrerequisitePresenter.jsx | 3 +- .../src/views/Components/CoursePagePopup.jsx | 100 +++++++++++++----- 2 files changed, 75 insertions(+), 28 deletions(-) diff --git a/my-app/src/presenters/PrerequisitePresenter.jsx b/my-app/src/presenters/PrerequisitePresenter.jsx index c45698f3..01ed015d 100644 --- a/my-app/src/presenters/PrerequisitePresenter.jsx +++ b/my-app/src/presenters/PrerequisitePresenter.jsx @@ -274,7 +274,8 @@ export const PrerequisitePresenter = observer((props) => { function loadTree(courses_taken) { - + // console.log("Hei") + // console.log(JSON.parse(localStorage.getItem("completedCourses"))) console.log(JSON.stringify(props.selectedCourse.prerequisites, null, 4)); if (props.selectedCourse.prerequisites === "null" || props.selectedCourse.prerequisites.length == 0) { let display_node = createNode("No Prerequisites", "No Prerequisites", "default"); diff --git a/my-app/src/views/Components/CoursePagePopup.jsx b/my-app/src/views/Components/CoursePagePopup.jsx index 518f8122..35305b37 100644 --- a/my-app/src/views/Components/CoursePagePopup.jsx +++ b/my-app/src/views/Components/CoursePagePopup.jsx @@ -34,10 +34,10 @@ function CoursePagePopup({ if (!isOpen || !course) return null; - console.log(course);`` + console.log(course); `` return (
@@ -53,7 +53,16 @@ function CoursePagePopup({ {/* Course Title Section */}

- {course.code} - {course.name} + + {course.code} + - {' '} + {course.name} + ({course.credits} Credits) @@ -66,9 +75,9 @@ function CoursePagePopup({ transition-all duration-300 ease-in-out font-semibold text-sm shadow-sm ${favouriteCourses.some((fav) => fav.code === course.code) - ? 'bg-yellow-400 /90 hover:bg-yellow-500/90 border-2 border-yellow-600 hover:border-yellow-700 text-yellow-900' - : 'bg-yellow-200/90 hover:bg-yellow-300 border-2 border-yellow-400 hover:border-yellow-500 text-yellow-600 hover:text-yellow-700' - }`} + ? 'bg-yellow-400 /90 hover:bg-yellow-500/90 border-2 border-yellow-600 hover:border-yellow-700 text-yellow-900' + : 'bg-yellow-200/90 hover:bg-yellow-300 border-2 border-yellow-400 hover:border-yellow-500 text-yellow-600 hover:text-yellow-700' + }`} onClick={(e) => { e.stopPropagation(); handleFavouriteClick(course); @@ -92,38 +101,75 @@ function CoursePagePopup({

{/* Description Section */} + {course.description && + course.description.trim() && + course.description.trim() !== "null" && ( +
+

Course Description

+
+
+
+ )} + + {/* Learning outcomes */}
-

Course Description

+

Learning Outcomes:

-
+ {course.learning_outcomes && course.learning_outcomes.trim() && + course.description.trim() !== "null" ? ( +
+ ) : ( +

+ No learning outcomes information available +

+ )}
{/* Prerequisite Graph Tree Section */}

Prerequisite Graph Tree

-
- {showOverlay && ( -
{ - e.stopPropagation(); - setShowOverlay(false); - }} - > -
- )} +
+ {showOverlay && (
{ + e.stopPropagation(); + setShowOverlay(false); + }} > - {prerequisiteTree}
+ )} +
+ {prerequisiteTree}
+
+
+ {/* Prereq Section */} +
+

Prerequisites:

+
+ {course.prerequisites_text && course.prerequisites_text.trim() && + course.description.trim() !== "null" ? ( +
+ ) : ( +

+ Prerequisites information not available +

+ )}
{/* Reviews Section (optional) */} {reviewPresenter && ( From dc4e519e2299cacb49b4b6e45399f8e735e33247 Mon Sep 17 00:00:00 2001 From: Kacper Lisik Date: Thu, 17 Apr 2025 08:28:36 +0200 Subject: [PATCH 17/17] before merge --- my-app/src/views/Components/CoursePagePopup.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/my-app/src/views/Components/CoursePagePopup.jsx b/my-app/src/views/Components/CoursePagePopup.jsx index 35305b37..39b3e6c4 100644 --- a/my-app/src/views/Components/CoursePagePopup.jsx +++ b/my-app/src/views/Components/CoursePagePopup.jsx @@ -59,8 +59,8 @@ function CoursePagePopup({ rel="noopener noreferrer" className="hover:text-violet-600 transition-colors duration-300" > - {course.code} - - {' '} + {course.code} + {' '}- {' '} {course.name}