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/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
You need to enable JavaScript to run this app.
diff --git a/web/src/components/Dashboard.jsx b/web/src/components/Dashboard.jsx
index eeeb017e..17537e13 100644
--- a/web/src/components/Dashboard.jsx
+++ b/web/src/components/Dashboard.jsx
@@ -18,6 +18,10 @@ const Dashboard = () => {
const [professorsData, setProfessorsData] = useState([]);
const [headersVisible, setHeadersVisible] = useState(true);
const [showSuggestions, setShowSuggestions] = useState(true);
+ const [courseId, setCourseId] = useState('');
+ const [cartVisible, setCartVisible] = useState(false);
+ const [cartItems, setCartItems] = useState([]);
+ const [addClassMessage, setAddClassMessage] = useState('');
const preventClose = (e) => {
e.stopPropagation();
@@ -98,6 +102,7 @@ const Dashboard = () => {
const fetchProfessors = async (courseId, year, semester) => {
const token = localStorage.getItem('token');
+ localStorage.setItem('course_id', courseId);
if (token) {
try {
@@ -106,6 +111,7 @@ const Dashboard = () => {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
+
});
if (response.ok) {
@@ -128,6 +134,7 @@ const Dashboard = () => {
}
};
+
const fetchCourses = async (schoolId, year, semester, query) => {
const token = localStorage.getItem('token');
@@ -248,6 +255,71 @@ 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,
+ professorId: entry.professor.id,
+ courseId: entry.course.id
+ })));
+ } 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');
+ }
+ }
+ };
+
+ const handleDeleteClick = async (professorId, courseId) => {
+ 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 (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 (
<>
@@ -255,7 +327,6 @@ const Dashboard = () => {
-
{headersVisible && (
@@ -289,9 +360,8 @@ const Dashboard = () => {
2024 {year === 2024 && }
handleDropdownClick('year', 2025)}>
- 2025 {year === 2025 && }
-
-
+ 2025 {year === 2025 && }
+
@@ -317,6 +387,9 @@ const Dashboard = () => {
+
+ Cart
+
{showSuggestions && searchResults.length > 0 && (
@@ -331,19 +404,38 @@ const Dashboard = () => {
- {!headersVisible && professorsData.length > 0 && (
+ {addClassMessage && (
+
+ {addClassMessage}
+
+ )}
+
+ {cartVisible && (
-
+
setCartVisible(false)}>
+ Go Back to Professors
+
+
)}
+ {!headersVisible && professorsData.length > 0 && !cartVisible && (
+
+ )}
>
);
};
-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);
@@ -381,24 +473,27 @@ const ProfessorTable = ({ professors, fetchProfessorRatings, fetchProfessorAnaly
}));
};
- const handleAddClick = async (event, professorId, courseId) => {
+ 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');
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) {
+ 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);
@@ -447,7 +542,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 && (
@@ -484,13 +579,44 @@ const ProfessorTable = ({ professors, fetchProfessorRatings, fetchProfessorAnaly
);
};
+const CartTable = ({ cartItems, handleDeleteClick }) => {
+ return (
+
+
+
+ First Name
+ Last Name
+ Code
+ Course Name
+ Delete
+
+
+
+ {cartItems.map((item, index) => (
+
+ {item.firstName}
+ {item.lastName}
+ {item.code}
+ {item.courseName}
+
+ handleDeleteClick(item.professorId, item.courseId)}>
+ Delete
+
+
+
+ ))}
+
+
+ );
+};
+
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)',
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/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/Header.jsx b/web/src/components/Header.jsx
index e3518e36..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');
};
@@ -108,13 +112,15 @@ function Header()
+ href='./Cart'
+ style={{ color: '#FFFFFF' }}>
Cart
-
+
@@ -118,7 +118,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/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 })}/>
-
+
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 = () => {
-