Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Follower and Following List to Profile Page #446

Merged
merged 1 commit into from
Nov 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions backend/Controllers/UserController.js
Original file line number Diff line number Diff line change
Expand Up @@ -465,11 +465,55 @@ const toggleFollowUser = async (req, res) => {




import mongoose from "mongoose";

const getFollowerAndFollowingList = async (req, res) => {
const { id } = req.params; // Get the user ID from route params

// Check if the user ID is valid (mongoose checks for ObjectId format)
if (!mongoose.Types.ObjectId.isValid(id)) {
return res.status(400).json({ message: "Invalid user ID" });
}

try {
// Find the user with the provided ID
const user = await User.findById(id);

if (!user) {
return res.status(404).json({ message: "User not found" });
}

// Fetch the followers' details
const followers = await User.find({ '_id': { $in: user.followers } })
.select('firstName lastName username profile'); // Only select necessary fields

// Fetch the following details
const following = await User.find({ '_id': { $in: user.following } })
.select('firstName lastName username profile'); // Only select necessary fields

// Return the followers and following data
return res.json({
followers,
following,
});
} catch (err) {
console.error("Error fetching followers and following", err);
return res.status(500).json({ message: "Server error" });
}
};

export { getFollowerAndFollowingList };




const UserController = {
Signup,
Login,
getAllUserName,
toggleFollowUser,
getFollowerAndFollowingList,
deleteUserById,
verifyUserByToken,
forgotPassword,
Expand Down
1 change: 1 addition & 0 deletions backend/routes/web.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ router.get("/verify-account", VerificationController.verifyEmail);
router.post("/reverify-account", VerificationController.resendVerificationEmail);

router.post("/login", UserController.Login);
router.get("/user/:id/followers-following", UserController.getFollowerAndFollowingList);
router.post("/submitFeedback", UserController.submitFeedback);
router.post("/follow", UserController.toggleFollowUser);
router.get('/feedback', UserController.getAllFeedback);
Expand Down
67 changes: 67 additions & 0 deletions frontend/src/Components/FollowerModal.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// FollowModal.js
import React from 'react';

export default function FollowModal({ isOpen, onClose, followers, following, activeTab, setActiveTab }) {
console.log(following)
if (!isOpen) return null;

return (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div className="bg-white rounded-lg max-w-md w-full p-4">
<div className="flex justify-between mb-4">
<h2 className="text-xl font-semibold">Followers & Following</h2>
<button onClick={onClose} className="text-gray-500 hover:text-red-500">&times;</button>
</div>

{/* Tab Header */}
<div className="flex border-b border-gray-200 mb-4">
<button
className={`w-1/2 py-2 text-center ${activeTab === 'followers' ? 'border-b-2 border-red-700 text-red-700' : 'text-gray-500'}`}
onClick={() => setActiveTab('followers')}
>
Followers
</button>
<button
className={`w-1/2 py-2 text-center ${activeTab === 'following' ? 'border-b-2 border-red-700 text-red-700' : 'text-gray-500'}`}
onClick={() => setActiveTab('following')}
>
Following
</button>
</div>

{/* Tab Content */}
<div className="h-64 overflow-y-auto">
{activeTab === 'followers' ? (
followers.length > 0 ? (
followers.map((follower) => (
<div key={follower._id} className="flex items-center mb-2">
<img src={follower.profile || "/placeholder.svg"} alt={follower.username} className="w-10 h-10 rounded-full mr-3" />
<div>
<span class="block text-red-700">@{follower.username}</span>
<span class="block text-sm">{follower.firstName} {follower.lastName}</span>
</div>
</div>
))
) : (
<p className="text-gray-500">No followers found.</p>
)
) : (
following.length > 0 ? (
following.map((followedUser) => (
<div key={followedUser._id} className="flex items-center mb-2">
<img src={followedUser.profile || "/placeholder.svg"} alt={followedUser.username} className="w-10 h-10 rounded-full mr-3" />
<div>
<span class="block text-red-700">@{followedUser.username}</span>
<span class="block text-sm">{followedUser.firstName} {followedUser.lastName}</span>
</div>
</div>
))
) : (
<p className="text-gray-500">No following users found.</p>
)
)}
</div>
</div>
</div>
);
}
74 changes: 59 additions & 15 deletions frontend/src/Pages/Profile.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useState, useEffect } from 'react'
import { useParams, useLocation } from 'react-router-dom'

import FollowModal from '../Components/FollowerModal'
import axios from 'axios'

export default function UserProfile() {
Expand All @@ -9,7 +9,8 @@ export default function UserProfile() {
const token = JSON.parse(localStorage.getItem("tastytoken")) // Token still comes from local storage for authentication

const [user, setUser] = useState(null)

const [followingList, setFollowingList] = useState(null)
const [followersList, setFollowersList] = useState(null);
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")
Expand Down Expand Up @@ -48,6 +49,7 @@ export default function UserProfile() {


useEffect(() => {
if (user) {
// Fetch user information
const fetchUserImage = () => {
axios
Expand Down Expand Up @@ -113,8 +115,8 @@ export default function UserProfile() {
fetchUserImage()
fetchRecipes()
fetchLikedRecipes() // Call the fetchLikedRecipes function to get liked recipes

}, [id, backendURL, token, user]) // Use id, backendURL, and token in dependencies
}
}, [user])

const handleFollow = async () => {
try {
Expand All @@ -128,8 +130,34 @@ export default function UserProfile() {
console.error("Error updating follow status", err);
}
};
useEffect(() => {
if(user){
const fetchFollowersFollowing = async () => {
try {
const response = await axios.get(`${backendURL}/api/user/${id}/followers-following`);
setFollowersList(response.data.followers);
setFollowingList(response.data.following);
console.log(response)
setLoading(false);
} catch (err) {
setError("Error fetching data");
setLoading(false);
}
};

fetchFollowersFollowing();
}
}, [user]);

const [isModalOpen, setIsModalOpen] = useState(false);
const [activeModalTab, setActiveModalTab] = useState('followers'); // Tracks which tab to show in modal

const handleOpenModal = (tab) => {
setActiveModalTab(tab);
setIsModalOpen(true);
};

const handleCloseModal = () => setIsModalOpen(false);
return (
<div className="bg-white min-h-screen">
{/* Banner and Profile Picture */}
Expand All @@ -155,23 +183,31 @@ export default function UserProfile() {
<h1 className="text-2xl font-bold">{username}</h1>
<p className="text-gray-600 mt-1">{bio}</p>
</div>
{(user && (user.user._id !== id)) &&
<button
className={`px-4 py-2 rounded-full ${
following
? 'bg-white text-red-700 border border-red-700 hover:bg-red-50'
: 'bg-red-700 text-white hover:bg-red-800'
}`}
onClick={handleFollow}
>
{following ? 'Following' : 'Follow'}
</button>
className={`px-4 py-2 rounded-full ${
following
? 'bg-white text-red-700 border border-red-700 hover:bg-red-50'
: 'bg-red-700 text-white hover:bg-red-800'
}`}
onClick={handleFollow}
>
{following ? 'Following' : 'Follow'}
</button>}

</div>

{/* User Stats */}
<div className="flex gap-4 mt-4 text-sm">
<span><strong>{recipes.length}</strong> Recipes</span>
<span><strong>{followers}</strong> Followers</span>
<span><strong>{followingCount}</strong> Following</span>

<button onClick={() => handleOpenModal('followers')}>
<strong>{followers}</strong> Followers
</button>
<button onClick={() => handleOpenModal('following')}>
<strong>{followingCount}</strong> Following
</button>

</div>
</div>

Expand Down Expand Up @@ -252,6 +288,14 @@ export default function UserProfile() {
</div>
)}
</div>
<FollowModal
isOpen={isModalOpen}
onClose={handleCloseModal}
followers={followersList} // Replace with actual followers data
following={followingList} // Replace with actual following data
activeTab={activeModalTab}
setActiveTab={setActiveModalTab}
/>
</div>
)
}
Loading