From 3bd6d947fbb7c9792f718d57dd01e5e1d932a7ca Mon Sep 17 00:00:00 2001 From: Kacper Lisik Date: Tue, 6 May 2025 18:35:18 +0200 Subject: [PATCH 01/17] fixed summation after long fight with git :! --- .../views/Components/FavouriteDropdown.jsx | 54 ++++++++++--------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/my-app/src/views/Components/FavouriteDropdown.jsx b/my-app/src/views/Components/FavouriteDropdown.jsx index 1d3d3e1e..55c292ba 100644 --- a/my-app/src/views/Components/FavouriteDropdown.jsx +++ b/my-app/src/views/Components/FavouriteDropdown.jsx @@ -14,18 +14,13 @@ const FavouritesDropdown = observer((props) => { navigator.clipboard.writeText(url) .then(() => { setCopied(true); - setTimeout(() => setCopied(false), 2500); // revert after 2.5 seconds + setTimeout(() => setCopied(false), 2500); }) .catch(err => { console.error("Copy failed:", err); }); } - function handleCopy() { - navigator.clipboard.writeText(shareUrl) - .catch(err => console.error("Copy failed:", err)); - } - return (
@@ -70,28 +65,35 @@ const FavouritesDropdown = observer((props) => { )}
-
- {props.favouriteCourses.length > 0 && ( - <> - - - - - )} + {/* Fixed Footer */} +
+
+

Total:

+

+

{props.totalCredits} hp

+
+
+
+ {props.favouriteCourses.length > 0 && ( + <> + + + + )} +
-
+ {/* Optional course popup */}
{props.isPopupOpen && props.popup} From c6a79a261fa4153906b12d1e0d000d4f8a6ec524 Mon Sep 17 00:00:00 2001 From: Kacper Lisik Date: Wed, 7 May 2025 11:20:47 +0200 Subject: [PATCH 02/17] sorting kinda works - some problems exist --- my-app/src/views/ListView.jsx | 93 +++++++++++++++++++++++++++++------ 1 file changed, 77 insertions(+), 16 deletions(-) diff --git a/my-app/src/views/ListView.jsx b/my-app/src/views/ListView.jsx index 666c15cd..37f5e655 100644 --- a/my-app/src/views/ListView.jsx +++ b/my-app/src/views/ListView.jsx @@ -9,6 +9,8 @@ function ListView(props) { const [hasMore, setHasMore] = useState(true); const [readMore, setReadMore] = useState({}); const [isLoading, setIsLoading] = useState(true); + const [sortBy, setSortBy] = useState('relevance'); + const [sortDirection, setSortDirection] = useState('asc'); const toggleReadMore = (courseCode) => { setReadMore(prevState => ({ @@ -25,20 +27,45 @@ function ListView(props) { } }; + const sortCourses = (courses, sortType) => { + const sortedCourses = [...courses]; + const direction = sortDirection === 'asc' ? 1 : -1; + + switch (sortType) { + case 'name': + return sortedCourses.sort((a, b) => + direction * a.name.localeCompare(b.name)); + case 'credits': + return sortedCourses.sort((a, b) => + direction * (parseFloat(a.credits) - parseFloat(b.credits))); + case 'relevance': + default: + return direction === 1 ? courses : [...courses].reverse(); + } + }; + useEffect(() => { setIsLoading(true); - const initialCourses = coursesToDisplay.slice(0, 10); + const sortedCourses = sortCourses(coursesToDisplay, sortBy); + const initialCourses = sortedCourses.slice(0, 10); setDisplayedCourses(initialCourses); - setHasMore(coursesToDisplay.length > 10); + setHasMore(sortedCourses.length > 10); setIsLoading(false); - }, [props.courses, props.searchResults]); + }, [props.courses, props.searchResults, sortBy, sortDirection]); const fetchMoreCourses = useCallback(() => { if (!hasMore) return; + + // Get the next batch of unsorted courses const nextItems = coursesToDisplay.slice(displayedCourses.length, displayedCourses.length + 50); - setDisplayedCourses(prevCourses => [...prevCourses, ...nextItems]); + + // Sort the combined courses (existing + new) to maintain consistency + const allCourses = [...displayedCourses, ...nextItems]; + const sortedCourses = sortCourses(allCourses, sortBy); + + setDisplayedCourses(sortedCourses); setHasMore(displayedCourses.length + nextItems.length < coursesToDisplay.length); - }, [displayedCourses.length, coursesToDisplay, hasMore]); + }, [displayedCourses.length, coursesToDisplay, hasMore, sortBy, sortDirection]); const [isRestoringScroll, setIsRestoringScroll] = useState(false); useEffect(() => { @@ -67,13 +94,45 @@ function ListView(props) {
) : (
-

- Found - - {props.currentSearchLenght} - - courses -

+
+

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

+ +
+ + + +
+
- {course.description.length > 150 && ( + {course.description && course.description.length > 150 && ( { From 1d02b82e6a36571628eeb3e7217da8a3ea1e79bb Mon Sep 17 00:00:00 2001 From: Kacper Lisik Date: Wed, 7 May 2025 14:05:41 +0200 Subject: [PATCH 03/17] fixed search + filter interaction --- my-app/src/presenters/SearchbarPresenter.jsx | 5 ++++- my-app/src/views/SearchbarView.jsx | 11 +++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/my-app/src/presenters/SearchbarPresenter.jsx b/my-app/src/presenters/SearchbarPresenter.jsx index 1002bf67..1f625be2 100644 --- a/my-app/src/presenters/SearchbarPresenter.jsx +++ b/my-app/src/presenters/SearchbarPresenter.jsx @@ -54,6 +54,7 @@ const SearchbarPresenter = observer(({ model }) => { const [selectedCourse, setSelectedCourse] = useState(null); const preP = ; const reviewPresenter = ; + const [searchQuery, setSearchQuery] = useState(""); const popup = { if(model.filtersCalculated){ - searchCourses(""); + searchCourses(searchQuery); model.filtersCalculated = false; } @@ -84,6 +85,8 @@ const SearchbarPresenter = observer(({ model }) => { setIsPopupOpen={setIsPopupOpen} setSelectedCourse={setSelectedCourse} popup={popup} + setSearchQuery={setSearchQuery} + searchQuery={searchQuery} // Add this line handleFavouriteClick={handleFavouriteClick} totalCredits={creditsSum(model.favourites)} resetScrollPosition={resetScoll} diff --git a/my-app/src/views/SearchbarView.jsx b/my-app/src/views/SearchbarView.jsx index dce612d6..51b0e33a 100644 --- a/my-app/src/views/SearchbarView.jsx +++ b/my-app/src/views/SearchbarView.jsx @@ -5,7 +5,7 @@ import project_logo from "../assets/project_icon.png"; import FavouritesDropdown from "./Components/FavouriteDropdown.jsx"; function SearchbarView(props) { - const [searchQuery, setSearchQuery] = useState(""); +// const [searchQuery, setSearchQuery] = useState(""); const [user, setUser] = useState(null); const [showFavourites, setShowFavourites] = useState(false); @@ -21,7 +21,7 @@ function SearchbarView(props) { const handleSearch = (query) => { props.resetScrollPosition(); - setSearchQuery(query); + props.setSearchQuery(query); props.searchCourses(query); }; @@ -52,10 +52,9 @@ function SearchbarView(props) { - handleSearch(e.target.value)} - onClick={(e)=>e.stopPropagation()} //TODO decide if we want to close the fav list after clicking the searchbar + value={props.searchQuery} // Changed from searchQuery to props.searchQuery + onChange={(e) => handleSearch(e.target.value)} + onClick={(e) => e.stopPropagation()} className="w-[400px] h-[44px] pl-14 pr-4 bg-white text-black rounded-full" /> From 9e6da22f8080f62fd5b1cb30d289d57041f7b85a Mon Sep 17 00:00:00 2001 From: Kacper Lisik Date: Wed, 7 May 2025 15:41:46 +0200 Subject: [PATCH 04/17] sorting is better now / Metallic Materials are the only issue --- my-app/src/presenters/ListViewPresenter.jsx | 40 +++++++++++++- my-app/src/views/ListView.jsx | 61 +++++++-------------- 2 files changed, 57 insertions(+), 44 deletions(-) diff --git a/my-app/src/presenters/ListViewPresenter.jsx b/my-app/src/presenters/ListViewPresenter.jsx index ed6518d5..0ae1dcce 100644 --- a/my-app/src/presenters/ListViewPresenter.jsx +++ b/my-app/src/presenters/ListViewPresenter.jsx @@ -76,6 +76,34 @@ const ListViewPresenter = observer(({ model }) => { const [isPopupOpen, setIsPopupOpen] = useState(false); const [selectedCourse, setSelectedCourse] = useState(null); + const [sortBy, setSortBy] = useState('relevance'); + const [sortDirection, setSortDirection] = useState('asc'); + const [sortedCourses, setSortedCourses] = useState([]); + + const sortCourses = (courses, sortType) => { + if (!courses) return []; + + const sortedCourses = [...courses]; + const direction = sortDirection === 'asc' ? 1 : -1; + + switch (sortType) { + case 'name': + return sortedCourses.sort((a, b) => + direction * a.name.localeCompare(b.name)); + case 'credits': + return sortedCourses.sort((a, b) => + direction * (parseFloat(a.credits) - parseFloat(b.credits))); + case 'relevance': + default: + return direction === 1 ? sortedCourses : sortedCourses.reverse(); + } + }; + + useEffect(() => { + const sorted = sortCourses(model.currentSearch, sortBy); + setSortedCourses(sorted); + }, [model.currentSearch, sortBy, sortDirection]); + const preP = { return { scrollContainerRef={scrollContainerRef} persistantScrolling={persistantScrolling} + sortedCourses={sortedCourses} + sortBy={sortBy} + setSortBy={setSortBy} + sortDirection={sortDirection} + setSortDirection={setSortDirection} + currentSearchLenght={sortedCourses.length} />; }); diff --git a/my-app/src/views/ListView.jsx b/my-app/src/views/ListView.jsx index 888dd3d4..1042e32c 100644 --- a/my-app/src/views/ListView.jsx +++ b/my-app/src/views/ListView.jsx @@ -4,13 +4,10 @@ import 'ldrs/react/Quantum.css'; import InfiniteScroll from 'react-infinite-scroll-component'; function ListView(props) { - const coursesToDisplay = props.searchResults; const [displayedCourses, setDisplayedCourses] = useState([]); const [hasMore, setHasMore] = useState(true); const [readMore, setReadMore] = useState({}); const [isLoading, setIsLoading] = useState(true); - const [sortBy, setSortBy] = useState('relevance'); - const [sortDirection, setSortDirection] = useState('asc'); const toggleReadMore = (courseCode) => { setReadMore(prevState => ({ @@ -26,46 +23,30 @@ function ListView(props) { props.addFavourite(course); } }; - - const sortCourses = (courses, sortType) => { - const sortedCourses = [...courses]; - const direction = sortDirection === 'asc' ? 1 : -1; - - switch (sortType) { - case 'name': - return sortedCourses.sort((a, b) => - direction * a.name.localeCompare(b.name)); - case 'credits': - return sortedCourses.sort((a, b) => - direction * (parseFloat(a.credits) - parseFloat(b.credits))); - case 'relevance': - default: - return direction === 1 ? courses : [...courses].reverse(); - } - }; useEffect(() => { setIsLoading(true); - const sortedCourses = sortCourses(coursesToDisplay, sortBy); - const initialCourses = sortedCourses.slice(0, 10); + const initialCourses = props.sortedCourses.slice(0, 10); setDisplayedCourses(initialCourses); - setHasMore(sortedCourses.length > 10); + setHasMore(props.sortedCourses.length > 10); setIsLoading(false); - }, [props.courses, props.searchResults, sortBy, sortDirection]); + }, [props.sortedCourses]); const fetchMoreCourses = useCallback(() => { if (!hasMore) return; - // Get the next batch of unsorted courses - const nextItems = coursesToDisplay.slice(displayedCourses.length, displayedCourses.length + 50); - - // Sort the combined courses (existing + new) to maintain consistency - const allCourses = [...displayedCourses, ...nextItems]; - const sortedCourses = sortCourses(allCourses, sortBy); + const nextItems = props.sortedCourses.slice( + displayedCourses.length, + displayedCourses.length + 50 + ); - setDisplayedCourses(sortedCourses); - setHasMore(displayedCourses.length + nextItems.length < coursesToDisplay.length); - }, [displayedCourses.length, coursesToDisplay, hasMore, sortBy, sortDirection]); + if (nextItems.length > 0) { + setDisplayedCourses(prev => [...prev, ...nextItems]); + setHasMore(displayedCourses.length + nextItems.length < props.sortedCourses.length); + } else { + setHasMore(false); + } + }, [displayedCourses.length, props.sortedCourses, hasMore]); const [isRestoringScroll, setIsRestoringScroll] = useState(false); useEffect(() => { @@ -82,7 +63,7 @@ function ListView(props) { } }, [props.targetScroll]); - if (!props.courses) { + if (!props.sortedCourses) { return (
@@ -111,10 +92,8 @@ function ListView(props) {