From 1ab7cbdded4d96020f0165ceb559a305842eebbf Mon Sep 17 00:00:00 2001 From: Hanna Pitino <100425382+hpitino11@users.noreply.github.com> Date: Tue, 23 Jul 2024 04:17:49 +0000 Subject: [PATCH 1/9] added set for the courseID --- web/src/components/Dashboard.jsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/web/src/components/Dashboard.jsx b/web/src/components/Dashboard.jsx index 6c247468..0ef8a3f2 100644 --- a/web/src/components/Dashboard.jsx +++ b/web/src/components/Dashboard.jsx @@ -18,6 +18,7 @@ const Dashboard = () => { const [professorsData, setProfessorsData] = useState([]); const [headersVisible, setHeadersVisible] = useState(true); const [showSuggestions, setShowSuggestions] = useState(true); + const [courseId, setCourseId] = useState(''); const preventClose = (e) => { e.stopPropagation(); @@ -98,6 +99,7 @@ const Dashboard = () => { const fetchProfessors = async (courseId, year, semester) => { const token = localStorage.getItem('token'); + localStorage.setItem('course_id', courseId); if (token) { try { @@ -106,6 +108,7 @@ const Dashboard = () => { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' } + }); if (response.ok) { @@ -128,6 +131,7 @@ const Dashboard = () => { } }; + const fetchCourses = async (schoolId, year, semester, query) => { const token = localStorage.getItem('token'); @@ -168,7 +172,7 @@ const Dashboard = () => { if (token) { try { - const response = await fetch(`http://localhost:8080/professors/${professorId}/rating?topKPercentage=${topKPercentage}`, { + const response = await fetch(`${API_URL}/professors/${professorId}/rating?topKPercentage=${topKPercentage}`, { headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' @@ -198,7 +202,7 @@ const Dashboard = () => { if (token) { try { - const response = await fetch(`http://localhost:8080/professors/${professorId}/analysis`, { + const response = await fetch(`${API_URL}{professorId}/analysis`, { headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' @@ -381,10 +385,11 @@ const ProfessorTable = ({ professors, fetchProfessorRatings, fetchProfessorAnaly })); }; - const handleAddClick = async (event, professorId, courseId) => { + const handleAddClick = async (event, professorId) => { event.stopPropagation(); // Prevent the event from propagating to the row click const token = localStorage.getItem('token'); const userId = localStorage.getItem('user_id'); + const courseId = localStorage.getItem('course_id') if (token && userId) { try { From a9559fe6e8daa59d7feda4e18679c60b7f584eae Mon Sep 17 00:00:00 2001 From: Hanna Pitino <100425382+hpitino11@users.noreply.github.com> Date: Tue, 23 Jul 2024 05:27:06 +0000 Subject: [PATCH 2/9] updated the find my professor header --- web/public/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/public/index.html b/web/public/index.html index dfa28bd0..1b685ca4 100644 --- a/web/public/index.html +++ b/web/public/index.html @@ -33,7 +33,7 @@ work correctly both with client-side routing and a non-root public URL. Learn how to configure a non-root public URL by running `npm run build`. --> - MDBReact5 Template App + Find My Professors From 2164e7aaaa603bae781325778e8a2034cccd7f2b Mon Sep 17 00:00:00 2001 From: Zachary Cary Date: Tue, 23 Jul 2024 10:10:33 -0400 Subject: [PATCH 3/9] accessibility --- package-lock.json | 6 ++++++ web/src/components/Dashboard.jsx | 6 +++++- web/src/components/DashboardHeader.jsx | 4 +++- web/src/components/Header.jsx | 4 +++- web/src/components/Home.jsx | 2 +- web/src/components/SearchBar.jsx | 6 +++++- 6 files changed, 23 insertions(+), 5 deletions(-) create mode 100644 package-lock.json diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..0436f8ec --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "findmyprofessors", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} diff --git a/web/src/components/Dashboard.jsx b/web/src/components/Dashboard.jsx index aba25f44..80604981 100644 --- a/web/src/components/Dashboard.jsx +++ b/web/src/components/Dashboard.jsx @@ -314,11 +314,15 @@ const Dashboard = () => { labelClass="text-black" style={{ backgroundColor: '#FFFFFF', color: 'black', boxShadow: '3px 3px 12px rgba(0, 0, 0, 0.75)' }} contrast + aria-label="Search Courses" label="Search Courses" value={query} onChange={(e) => setFilters({ ...filters, query: e.target.value })} /> - + diff --git a/web/src/components/DashboardHeader.jsx b/web/src/components/DashboardHeader.jsx index 0f99dc8e..b169adc3 100644 --- a/web/src/components/DashboardHeader.jsx +++ b/web/src/components/DashboardHeader.jsx @@ -139,7 +139,9 @@ const DashboardHeader = ({ onSearch }) => { - Cart + Cart diff --git a/web/src/components/Header.jsx b/web/src/components/Header.jsx index e3518e36..9d4dda3b 100644 --- a/web/src/components/Header.jsx +++ b/web/src/components/Header.jsx @@ -108,13 +108,15 @@ function Header() + href='./Cart' + style={{ color: '#FFFFFF' }}> Cart diff --git a/web/src/components/SearchBar.jsx b/web/src/components/SearchBar.jsx index d34ba143..397bc0dc 100644 --- a/web/src/components/SearchBar.jsx +++ b/web/src/components/SearchBar.jsx @@ -184,11 +184,15 @@ const SearchBar = ({ onSearch, filters, setFilters, preventClose, getYearText, g labelClass="text-black" style={{ backgroundColor: '#3f3f3f', boxShadow: '3px 3px 12px rgba(0, 0, 0, 0.75)' }} contrast + aria-label="Search Courses" label="Search Courses" value={query} onChange={(e) => setFilters({ ...filters, query: e.target.value })}/> - + From 638960898474093e50d89c1745039ab228546f7c Mon Sep 17 00:00:00 2001 From: Hanna Pitino <100425382+hpitino11@users.noreply.github.com> Date: Tue, 23 Jul 2024 14:20:22 +0000 Subject: [PATCH 4/9] added cart and works. just needs delete (next update) --- web/src/components/Dashboard.jsx | 93 ++++++++++++++++++++++++++++---- 1 file changed, 82 insertions(+), 11 deletions(-) diff --git a/web/src/components/Dashboard.jsx b/web/src/components/Dashboard.jsx index aba25f44..7e0a500b 100644 --- a/web/src/components/Dashboard.jsx +++ b/web/src/components/Dashboard.jsx @@ -19,6 +19,8 @@ const Dashboard = () => { const [headersVisible, setHeadersVisible] = useState(true); const [showSuggestions, setShowSuggestions] = useState(true); const [courseId, setCourseId] = useState(''); + const [cartVisible, setCartVisible] = useState(false); + const [cartItems, setCartItems] = useState([]); const preventClose = (e) => { e.stopPropagation(); @@ -252,6 +254,39 @@ const Dashboard = () => { } }; + const handleCartClick = async () => { + setCartVisible(!cartVisible); + if (!cartVisible) { + const userId = localStorage.getItem('user_id'); + if (userId) { + try { + const response = await fetch(`${API_URL}/users/${userId}/cart`, { + headers: { + 'Authorization': `Bearer ${localStorage.getItem('token')}`, + 'Content-Type': 'application/json' + } + }); + + if (response.ok) { + const data = await response.json(); + setCartItems(data.entries.map(entry => ({ + firstName: entry.professor.first_name, + lastName: entry.professor.last_name, + code: entry.course.code, + courseName: entry.course.name, + }))); + } else { + console.error('Failed to fetch cart data:', response.statusText); + } + } catch (error) { + console.error('Error fetching cart data:', error); + } + } else { + console.error('No user ID found'); + } + } + }; + return ( <>
@@ -259,7 +294,6 @@ const Dashboard = () => { -
{headersVisible && ( @@ -293,9 +327,8 @@ const Dashboard = () => { 2024 {year === 2024 && } handleDropdownClick('year', 2025)}> - 2025 {year === 2025 && } - - + 2025 {year === 2025 && } + @@ -335,12 +368,25 @@ const Dashboard = () => {
- {!headersVisible && professorsData.length > 0 && ( + + Cart + + + {cartVisible && (
- +
)} + {!headersVisible && professorsData.length > 0 && ( +
+ +
+ )}
@@ -385,22 +431,22 @@ const ProfessorTable = ({ professors, fetchProfessorRatings, fetchProfessorAnaly })); }; - const handleAddClick = async (event, professorId) => { + const handleAddClick = async (event, professor) => { event.stopPropagation(); // Prevent the event from propagating to the row click const token = localStorage.getItem('token'); const userId = localStorage.getItem('user_id'); - const courseId = localStorage.getItem('course_id') + const courseId = localStorage.getItem('course_id'); if (token && userId) { try { - console.log('Sending request with data:', { professorId, courseId }); // Debugging log + console.log('Sending request with data:', { professorId: professor.id, courseId }); // Debugging log const response = await fetch(`${API_URL}/users/${userId}/cart`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, - body: JSON.stringify({ professor_id: professorId, course_id: courseId}) + body: JSON.stringify({ professor_id: professor.id, course_id: courseId }) }); if (response.ok) { @@ -452,7 +498,7 @@ const ProfessorTable = ({ professors, fetchProfessorRatings, fetchProfessorAnaly {ratingsData[professor.id] ? roundToTenth(ratingsData[professor.id].totalQualityAverage) : '-'} {ratingsData[professor.id]?.ratingAmount || '-'} - handleAddClick(event, professor.id)}>Add + handleAddClick(event, professor)}>Add {selectedProfessor === professor && ( @@ -489,6 +535,31 @@ const ProfessorTable = ({ professors, fetchProfessorRatings, fetchProfessorAnaly ); }; +const CartTable = ({ cartItems }) => { + return ( + + + + First Name + Last Name + Code + Course Name + + + + {cartItems.map((item, index) => ( + + {item.firstName} + {item.lastName} + {item.code} + {item.courseName} + + ))} + + + ); +}; + const ProfessorDetails = ({ professor, analysisData }) => { const lineData = { labels: analysisData ? analysisData.averageRatingValues.map(item => `${item.month} ${item.year}`) : [], From 89606a7d7d6466bd964dc42f34a1b3462713e1bd Mon Sep 17 00:00:00 2001 From: Zachary Cary Date: Tue, 23 Jul 2024 10:20:27 -0400 Subject: [PATCH 5/9] accessibility --- web/src/components/EmailConfirmation.jsx | 4 ---- web/src/components/ForgotPassword.jsx | 4 ---- web/src/components/Home.jsx | 2 +- web/src/components/Login.jsx | 20 ++++---------------- web/src/components/Register.jsx | 6 +----- web/src/components/ResetPassword.jsx | 4 ---- web/src/components/VerifyEmail.jsx | 16 ---------------- 7 files changed, 6 insertions(+), 50 deletions(-) diff --git a/web/src/components/EmailConfirmation.jsx b/web/src/components/EmailConfirmation.jsx index b5105738..707de3ef 100644 --- a/web/src/components/EmailConfirmation.jsx +++ b/web/src/components/EmailConfirmation.jsx @@ -59,10 +59,6 @@ const EmailConfirmation = () => {
Your email has been confirmed. Redirecting to login ...
- diff --git a/web/src/components/ForgotPassword.jsx b/web/src/components/ForgotPassword.jsx index 87a099c8..e5f2cfb0 100644 --- a/web/src/components/ForgotPassword.jsx +++ b/web/src/components/ForgotPassword.jsx @@ -97,10 +97,6 @@ const ForgotPassword = () => { Send Reset Link - diff --git a/web/src/components/Home.jsx b/web/src/components/Home.jsx index b2840fce..3fcc13a8 100644 --- a/web/src/components/Home.jsx +++ b/web/src/components/Home.jsx @@ -56,7 +56,7 @@ const containerStyle = { <>
- + diff --git a/web/src/components/Login.jsx b/web/src/components/Login.jsx index a96f8186..0a938a6e 100644 --- a/web/src/components/Login.jsx +++ b/web/src/components/Login.jsx @@ -77,7 +77,7 @@ const Login = () => { <>
- + @@ -169,24 +169,12 @@ const Login = () => {

- Don't have an account? Register + style={{ color: 'white' }}> + Don't have an account? Register

- + diff --git a/web/src/components/Register.jsx b/web/src/components/Register.jsx index e92c1f8f..d889ec93 100644 --- a/web/src/components/Register.jsx +++ b/web/src/components/Register.jsx @@ -211,14 +211,10 @@ const Register = () => {

+ style={{ color: 'white' }}> Already have an account? Login

- diff --git a/web/src/components/ResetPassword.jsx b/web/src/components/ResetPassword.jsx index 92e4a8bf..855fc64b 100644 --- a/web/src/components/ResetPassword.jsx +++ b/web/src/components/ResetPassword.jsx @@ -140,10 +140,6 @@ const ResetPassword = () => { Reset Password - diff --git a/web/src/components/VerifyEmail.jsx b/web/src/components/VerifyEmail.jsx index 2a0a5a1b..fa32e5e4 100644 --- a/web/src/components/VerifyEmail.jsx +++ b/web/src/components/VerifyEmail.jsx @@ -75,22 +75,6 @@ const VerifyEmail = () => { - From a0aab1e6b7e3d382231906395bc2b0fc0a276bdc Mon Sep 17 00:00:00 2001 From: Hanna Pitino <100425382+hpitino11@users.noreply.github.com> Date: Tue, 23 Jul 2024 14:25:22 +0000 Subject: [PATCH 6/9] dashboard cart with delete working --- web/src/components/Dashboard.jsx | 46 +++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/web/src/components/Dashboard.jsx b/web/src/components/Dashboard.jsx index 5a678a21..cbcfbe65 100644 --- a/web/src/components/Dashboard.jsx +++ b/web/src/components/Dashboard.jsx @@ -274,6 +274,8 @@ const Dashboard = () => { lastName: entry.professor.last_name, code: entry.course.code, courseName: entry.course.name, + professorId: entry.professor.id, + courseId: entry.course.id }))); } else { console.error('Failed to fetch cart data:', response.statusText); @@ -287,6 +289,34 @@ const Dashboard = () => { } }; + const handleDeleteClick = async (professorId, courseId) => { + const userId = localStorage.getItem('user_id'); + const token = localStorage.getItem('token'); + + if (userId && token) { + try { + const response = await fetch(`${API_URL}/users/${userId}/cart`, { + method: 'DELETE', + headers: { + 'Authorization': `Bearer ${token}`, + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ professor_id: professorId, course_id: courseId }) + }); + + if (response.ok) { + setCartItems((prevItems) => prevItems.filter(item => !(item.professorId === professorId && item.courseId === courseId))); + } else { + console.error('Failed to delete item from cart:', response.statusText); + } + } catch (error) { + console.error('Error deleting item from cart:', error); + } + } else { + console.error('No user ID or token found'); + } + }; + return ( <>
@@ -347,15 +377,11 @@ const Dashboard = () => { labelClass="text-black" style={{ backgroundColor: '#FFFFFF', color: 'black', boxShadow: '3px 3px 12px rgba(0, 0, 0, 0.75)' }} contrast - aria-label="Search Courses" label="Search Courses" value={query} onChange={(e) => setFilters({ ...filters, query: e.target.value })} /> - + @@ -378,7 +404,7 @@ const Dashboard = () => { {cartVisible && (
- +
)} @@ -539,7 +565,7 @@ const ProfessorTable = ({ professors, fetchProfessorRatings, fetchProfessorAnaly ); }; -const CartTable = ({ cartItems }) => { +const CartTable = ({ cartItems, handleDeleteClick }) => { return ( @@ -548,6 +574,7 @@ const CartTable = ({ cartItems }) => { Last Name Code Course Name + Delete @@ -557,6 +584,11 @@ const CartTable = ({ cartItems }) => { {item.lastName} {item.code} {item.courseName} + + handleDeleteClick(item.professorId, item.courseId)}> + Delete + + ))} From 8f11682e943e8bfd61c4c415ea10e29277d9b53b Mon Sep 17 00:00:00 2001 From: Zachary Cary Date: Tue, 23 Jul 2024 10:31:29 -0400 Subject: [PATCH 7/9] logout functionality --- web/src/components/Header.jsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/web/src/components/Header.jsx b/web/src/components/Header.jsx index 9d4dda3b..372ed03c 100644 --- a/web/src/components/Header.jsx +++ b/web/src/components/Header.jsx @@ -41,7 +41,11 @@ function Header() const navigate = useNavigate(); + const userId = localStorage.getItem('user_id'); + const handleLogout = () => { + localStorage.removeItem('user_id'); + console.log("userID is now " + userId); toggleOpen(); navigate('/Login'); }; From 3e73ead2eeb1a342d8be4723ed3ce87499bbbad0 Mon Sep 17 00:00:00 2001 From: Hanna Pitino <100425382+hpitino11@users.noreply.github.com> Date: Tue, 23 Jul 2024 14:56:21 +0000 Subject: [PATCH 8/9] added new delete and add popups and text to confirm. cart button now next to search. hides professor table when in cart. --- web/src/components/Dashboard.jsx | 64 +++++++++++++++++++------------- 1 file changed, 39 insertions(+), 25 deletions(-) diff --git a/web/src/components/Dashboard.jsx b/web/src/components/Dashboard.jsx index cbcfbe65..735c7a85 100644 --- a/web/src/components/Dashboard.jsx +++ b/web/src/components/Dashboard.jsx @@ -21,6 +21,7 @@ const Dashboard = () => { const [courseId, setCourseId] = useState(''); const [cartVisible, setCartVisible] = useState(false); const [cartItems, setCartItems] = useState([]); + const [addClassMessage, setAddClassMessage] = useState(''); const preventClose = (e) => { e.stopPropagation(); @@ -290,30 +291,32 @@ const Dashboard = () => { }; const handleDeleteClick = async (professorId, courseId) => { - const userId = localStorage.getItem('user_id'); - const token = localStorage.getItem('token'); + if (window.confirm('Are you sure you want to delete this entry?')) { + const userId = localStorage.getItem('user_id'); + const token = localStorage.getItem('token'); - if (userId && token) { - try { - const response = await fetch(`${API_URL}/users/${userId}/cart`, { - method: 'DELETE', - headers: { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ professor_id: professorId, course_id: courseId }) - }); + if (userId && token) { + try { + const response = await fetch(`${API_URL}/users/${userId}/cart`, { + method: 'DELETE', + headers: { + 'Authorization': `Bearer ${token}`, + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ professor_id: professorId, course_id: courseId }) + }); - if (response.ok) { - setCartItems((prevItems) => prevItems.filter(item => !(item.professorId === professorId && item.courseId === courseId))); - } else { - console.error('Failed to delete item from cart:', response.statusText); + if (response.ok) { + setCartItems((prevItems) => prevItems.filter(item => !(item.professorId === professorId && item.courseId === courseId))); + } else { + console.error('Failed to delete item from cart:', response.statusText); + } + } catch (error) { + console.error('Error deleting item from cart:', error); } - } catch (error) { - console.error('Error deleting item from cart:', error); + } else { + console.error('No user ID or token found'); } - } else { - console.error('No user ID or token found'); } }; @@ -384,6 +387,9 @@ const Dashboard = () => { + + Cart + {showSuggestions && searchResults.length > 0 && ( @@ -398,22 +404,28 @@ const Dashboard = () => { - - Cart - + {addClassMessage && ( +
+ {addClassMessage} +
+ )} {cartVisible && (
+ setCartVisible(false)}> + Go Back to Professors +
)} - {!headersVisible && professorsData.length > 0 && ( + {!headersVisible && professorsData.length > 0 && !cartVisible && (
)} @@ -423,7 +435,7 @@ const Dashboard = () => { ); }; -const ProfessorTable = ({ professors, fetchProfessorRatings, fetchProfessorAnalysis }) => { +const ProfessorTable = ({ professors, fetchProfessorRatings, fetchProfessorAnalysis, setAddClassMessage }) => { const [searchTerm, setSearchTerm] = useState(''); const [currentPage, setCurrentPage] = useState(1); const [selectedProfessor, setSelectedProfessor] = useState(null); @@ -480,6 +492,8 @@ const ProfessorTable = ({ professors, fetchProfessorRatings, fetchProfessorAnaly }); if (response.ok) { + setAddClassMessage(`Class added: ${professor.first_name} ${professor.last_name}`); + setTimeout(() => setAddClassMessage(''), 3000); console.log('Professor added to cart successfully'); } else { console.error('Failed to add professor to cart:', response.statusText); From 676c906936428eccef212f234695b23f198a302f Mon Sep 17 00:00:00 2001 From: Hanna Pitino <100425382+hpitino11@users.noreply.github.com> Date: Tue, 23 Jul 2024 15:03:29 +0000 Subject: [PATCH 9/9] reversed order of data points in line chart --- web/src/components/Dashboard.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/components/Dashboard.jsx b/web/src/components/Dashboard.jsx index 735c7a85..17537e13 100644 --- a/web/src/components/Dashboard.jsx +++ b/web/src/components/Dashboard.jsx @@ -612,11 +612,11 @@ const CartTable = ({ cartItems, handleDeleteClick }) => { const ProfessorDetails = ({ professor, analysisData }) => { const lineData = { - labels: analysisData ? analysisData.averageRatingValues.map(item => `${item.month} ${item.year}`) : [], + labels: analysisData ? analysisData.averageRatingValues.map(item => `${item.month} ${item.year}`).reverse() : [], datasets: [ { label: 'Rating Over Time', - data: analysisData ? analysisData.averageRatingValues.map(item => item.value) : [], + data: analysisData ? analysisData.averageRatingValues.map(item => item.value).reverse() : [], fill: false, backgroundColor: 'rgb(75, 192, 192)', borderColor: 'rgba(75, 192, 192, 0.2)',