diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 77175a37..c1eab330 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -23,6 +23,8 @@ import ResetPassword from "./Pages/ResetPassword.jsx"; import ForgotPassword from "./Pages/ForgotPassword.jsx"; import RecipeSuggestions from "./Pages/RecipeSuggestions.jsx"; import EmailVerification from "./Pages/EmailVerification.jsx" +import UserProfile from "./Pages/Profile.jsx"; + function App() { const [showScroll, setShowScroll] = useState(false); @@ -81,6 +83,7 @@ function App() { element={} /> } /> + } /> } /> } /> } /> diff --git a/frontend/src/Components/Testimonial.jsx b/frontend/src/Components/Testimonial.jsx index 620fa69f..79c9b2b4 100644 --- a/frontend/src/Components/Testimonial.jsx +++ b/frontend/src/Components/Testimonial.jsx @@ -1,14 +1,16 @@ import React, { useState, useEffect, useCallback } from 'react'; import { ChevronLeft, ChevronRight, Star } from 'lucide-react'; import { useMediaQuery } from 'react-responsive'; - +import { Link } from 'react-router-dom'; const ReviewCard = ({ review }) => (
{review.userId.firstName}
+

{review.userId.firstName} {review.userId.lastName}

+

{review.role}

diff --git a/frontend/src/Pages/Profile.jsx b/frontend/src/Pages/Profile.jsx new file mode 100644 index 00000000..b930e4b4 --- /dev/null +++ b/frontend/src/Pages/Profile.jsx @@ -0,0 +1,211 @@ +import { useState, useEffect } from 'react' +import { useParams } from 'react-router-dom' +import axios from 'axios' + +export default function UserProfile() { + const { id } = useParams() // Get the user ID from the URL params + const backendURL = import.meta.env.VITE_BACKEND_URL + const token = JSON.parse(localStorage.getItem("tastytoken")) // Token still comes from local storage for authentication + + const [username, setUsername] = useState("ChefJulia") + const [bio, setBio] = useState("Passionate about creating delicious, healthy recipes that anyone can make!") + const [imagePreview, setImagePreview] = useState("/placeholder.svg?height=128&width=128") + const [recipes, setRecipes] = useState([]) + const [likedRecipes, setLikedRecipes] = useState([]) // Added state for liked recipes + const [following, setFollowing] = useState(false) + const [followingCount, setFollowingCount] = useState(false) + const [followers, setFollowers] = useState(0) + const [activeTab, setActiveTab] = useState('recipes') + const [loading, setLoading] = useState(false) + const [error, setError] = useState(null) + + useEffect(() => { + // Fetch user information + const fetchUserImage = () => { + axios + .post(`${backendURL}/api/user/fetch`, { id }, { + headers: { + Authorization: `Bearer ${token}`, + }, + }) + .then((res) => { + setUsername(res.data.username) + setBio(res.data.bio) + setImagePreview(res.data.profile) + setFollowingCount(res.data.following.length) + setFollowers(res.data.followers.length) + }) + .catch((err) => { + console.error("Error fetching user data", err) + }) + } + + // Fetch recipes + const fetchRecipes = () => { + setLoading(true) + setError(null) + axios + .post(`${backendURL}/api/recipe/readall`, { id: id }, { + headers: { + Authorization: `Bearer ${token}`, + }, + }) + .then((res) => { + setRecipes(res.data.recipes) + }) + .catch((err) => { + console.error("Error fetching recipes:", err) + setError("Failed to fetch recipes. Please try again.") + }) + .finally(() => setLoading(false)) + } + + // Fetch liked recipes + const fetchLikedRecipes = () => { + axios + .post( + `${backendURL}/api/recipe/liked_recipes`, + { userId: id }, // Use the user ID from URL params + { + headers: { Authorization: `Bearer ${token}` }, + } + ) + .then((res) => { + setLikedRecipes(res.data.likedRecipes) + }) + .catch((err) => { + console.error("Error fetching liked recipes:", err) + setError("Failed to fetch liked recipes. Please try again.") + }) + } + + fetchUserImage() + fetchRecipes() + fetchLikedRecipes() // Call the fetchLikedRecipes function to get liked recipes + }, [id, backendURL, token]) // Use id, backendURL, and token in dependencies + + const handleFollow = () => { + setFollowing(!following) + setFollowers(followers + (following ? -1 : 1)) + } + + return ( +
+ {/* Banner and Profile Picture */} +
+
+ {username} +
+
+ + {/* User Info */} +
+
+
+

{username}

+

{bio}

+
+ +
+ + {/* User Stats */} +
+ {recipes.length} Recipes + {followers} Followers + {followingCount} Following +
+
+ + {/* Tabs for Recipes and Liked Recipes */} +
+
+ + +
+ + {activeTab === 'recipes' && ( +
+ {loading ? ( +

Loading recipes...

+ ) : error ? ( +

{error}

+ ) : recipes.length > 0 ? ( + recipes.map((recipe) => ( +
+ {recipe.name} +
+

{recipe.name}

+

{recipe.description}

+
+ ⭐ {recipe.rating} + 👍 {recipe.likes} +
+
+
+ )) + ) : ( +

No recipes posted

+ )} +
+ )} + + {activeTab === 'liked' && ( +
+ {loading ? ( +

Loading liked recipes...

+ ) : error ? ( +

{error}

+ ) : likedRecipes.length > 0 ? ( + likedRecipes.map((recipe) => ( +
+ {recipe.name} +
+

{recipe.name}

+

{recipe.description}

+
+ ⭐ {recipe.rating} + 👍 {recipe.likes} +
+
+
+ )) + ) : ( +

No liked recipes

+ )} +
+ )} +
+
+ ) +}