Skip to content

Commit

Permalink
Merge pull request #522 from bounswe/FE-profile-page-api-integration
Browse files Browse the repository at this point in the history
Integrate Profile Page with API
  • Loading branch information
rukiyeaslan authored Dec 16, 2024
2 parents 1bd30ea + 53d2182 commit 969b0f5
Show file tree
Hide file tree
Showing 10 changed files with 654 additions and 154 deletions.
2 changes: 1 addition & 1 deletion frontend/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ function App() {

<Route path="news" element={<NewsPage />} />
<Route path="portfolio" element={<PortfolioPage />} />
<Route path="profile" element={<ProfilePage />} />
<Route path="profile/:userId" element={<ProfilePage />} />

<Route path="/post/:postId" element={<PostView />} />
<Route path="/" element={<Navigate to="/home" />} />
Expand Down
9 changes: 7 additions & 2 deletions frontend/src/components/Dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,12 @@ const Dashboard = () => {
};

const handleProfile = () => {
navigate("/profile");
if (UserService.isLoggedIn()) {
navigate(`/profile/${UserService.getUserId()}`);
} else {
toast.warn("Please sign in to view your profile.");
navigate("/login");
}
};

const toggleDarkMode = () => {
Expand Down Expand Up @@ -91,7 +96,7 @@ const Dashboard = () => {
{isLoggedIn ? (
<div className="user-profile">
<FaUserCircle className="user-icon" onClick={handleProfile} />
<span className="user-name">{userName}</span>
<span className="user-name" onClick={handleProfile}> {userName} </span>
<button onClick={handleSignOut} className="auth-button">
Sign Out
</button>
Expand Down
35 changes: 2 additions & 33 deletions frontend/src/components/community/CommunityPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import PostCard from "./PostCard";
import "../../styles/Page.css";
import { useNavigate } from "react-router-dom";
import { apiClient } from "../../service/apiClient";
import { transformPost } from "../../service/postService";

const CommunityPage = () => {
const [searchTerm, setSearchTerm] = useState("");
Expand All @@ -26,39 +27,7 @@ const CommunityPage = () => {
setUsers(usersById);

const transformedPosts = await Promise.all(
response.data.map(async (post) => {
try {
const commentsResponse = await apiClient.get(
`/comments/post-comments/${post.id}`
);

return {
"post-id": post.id,
user: usersById[post.author] || "Unknown",
title: post.title,
content: [{ type: "plain-text", "plain-text": post.content }],
comments: commentsResponse.data,
likes: post.liked_by?.length || 0,
tags: post.tags || [],
"publication-date": new Date(post.created_at),
};
} catch (error) {
console.error(
`Error fetching comments for post ${post.id}:`,
error
);
return {
"post-id": post.id,
user: usersById[post.author] || "Unknown",
title: post.title,
content: [{ type: "plain-text", "plain-text": post.content }],
comments: 0,
likes: post.liked_by?.length || 0,
tags: post.tags || [],
"publication-date": new Date(post.created_at),
};
}
})
response.data.map(async (post) => transformPost(post))
);
setPosts(transformedPosts);
} catch (error) {
Expand Down
6 changes: 5 additions & 1 deletion frontend/src/components/community/PostCard.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ const PostCard = ({ post }) => {
by <span>{post["user"]}</span>
</p>
</div>
<p>{post.content[0]["plain-text"]}</p>
<p>{
(post.content[0]["plain-text"] ? post.content[0]["plain-text"].substring(0, 200) : "")
+ (post.content[0]["plain-text"] && post.content[0]["plain-text"].length > 200 ? "..." : "")

}</p>
<div className="post-info">
<div className="post-stats">
<span className="likes">
Expand Down
237 changes: 181 additions & 56 deletions frontend/src/components/profile/ProfilePage.js
Original file line number Diff line number Diff line change
@@ -1,71 +1,196 @@
import React from 'react';
import React, { useEffect, useState } from 'react';
import "../../styles/Profile.css";
import "../../styles/Page.css";
import UserService from '../../service/userService';
import { useNavigate, useParams } from 'react-router-dom';
import ProfileService from '../../service/profileService';
import CircleAnimation from '../CircleAnimation';
import { toast } from "react-toastify";
import log from '../../utils/logger';
import UserCard from './UserCard';
import PostCard from '../community/PostCard';

// Sample data
const userProfile = {
name: 'Deniz Coşkuner',
username: '@danzio',
followers: 151,
following: 32,
posts: 15,
portfolios: 1,
comments: 12,
badges: [
{ label: 'Highliked', icon: '🏅' },
{ label: 'Creatager', icon: '🏅' }
]
// Sample data fallback
const userProfilex = {
name: 'Unknown',
image: null,
username: 'unknown',
followers: [],
following: [],
posts: [],
comments: [],
badges: []
};

const ProfilePage = () => {
const { userId } = useParams();
const navigate = useNavigate();
const [userProfile, setUserProfile] = useState(null);
const [loading, setLoading] = useState(true);
const [currentTab, setCurrentTab] = useState('Posts');
const isCurrentUser = userId === UserService.getUserId();
const [isFollowing, setIsFollowing] = useState(false);

useEffect(() => {
if (!UserService.isLoggedIn()) {
navigate('/login');
}

log.debug('Fetching profile for user ID:', userId);
setLoading(true);

ProfileService.fetchProfileById(userId)
.then((profile) => {
setUserProfile(profile);

updateIsFollowing(userProfile).then(() => {
setLoading(false);
log.debug('Profile loaded:', profile);
}).catch((error) => {
console.error('Error updating isFollowing:', error);
setLoading(false);
});
})
.catch((error) => {
console.error('Error fetching user profile:', error);
toast.error('Error fetching user profile');
setUserProfile(userProfilex);
setLoading(false);
});
}, [navigate, userId]);


useEffect(() => {
ProfileService.fetchProfileById(userId)
.then((profile) => {
setUserProfile(profile);
})
.catch((error) => {
console.error('Error fetching user profile:', error);
});
}, [isFollowing]);

const updateIsFollowing = async (user) => {
if (!isCurrentUser) {
const result = await ProfileService.isFollowing(UserService.getUserId(), userId)
setIsFollowing(result);
}
};

const renderListContent = () => {
if (!userProfile) return null;

const contentMap = {
Posts: userProfile.posts,
Comments: userProfile.comments,
Followers: userProfile.followers,
Following: userProfile.following
};

console.log("contentMap", contentMap);

const content = contentMap[currentTab];
if (!content || content.length === 0) {
return <p>No {currentTab.toLowerCase()} available.</p>;
}

return (
<ul className="list-content">
{content.map((item, index) => (
<li key={index}>
{currentTab === 'Following' || currentTab === 'Followers' ? (
<UserCard userId={item} />
) : currentTab === 'Posts' ? (
<PostCard post={item} />
) : (
<span>Comment</span>
// <CommentCard comment={item} />
)}
</li>
))}
</ul>
);
};

const handleFollowToggle = (user) => {
const action = isFollowing ? 'Unfollow' : 'Follow';

ProfileService.handleFollowToggle(user.username)
.then(async (message) => {
toast.success(`${message} ${user.username}`);
await updateIsFollowing();
})
.catch((error) => {
console.error(`Failed to ${action.toLowerCase()} user:`, error);
toast.error(`Failed to ${action.toLowerCase()} user.`);
});


};

if (loading) {
return <CircleAnimation />;
}

return (
<div className="profile-page">
<div className="profile-container">
<div className="profile-avatar">
<span>IMG</span>
</div>
<div className="profile-right-container">
<div className="profile-stats">
<div className="stat-item">
<p>{userProfile.followers} Followers</p>
</div>
<div className="stat-item">
<p>{userProfile.following} Following</p>
</div>
<div className="stat-item">
<p>{userProfile.posts} Posts</p>
</div>
<div className="stat-item">
<p>{userProfile.portfolios} Portfolios</p>
</div>
<div className="stat-item">
<p>{userProfile.comments} Comments</p>
</div>
<div className='page'>
<div className="profile-page">
<div className="profile-container">
<div className="profile-avatar">
{userProfile.image ? (
<img src={userProfile.image} alt="Profile Avatar" />
) : (
<span className="profile-avatar-placeholder">
{userProfile.username.charAt(0).toUpperCase()}
</span>
)}
</div>
<div className="profile-info">
<div className="profile-details">
<h1>{userProfile.name}</h1>
<p>{userProfile.username}</p>
<div className="profile-right-container">
<div className="profile-stats">
<div className="stat-item"><p>{userProfile.followersCnt} Followers</p></div>
<div className="stat-item"><p>{userProfile.followingCnt} Following</p></div>
<div className="stat-item"><p>{userProfile.postsCnt} Posts</p></div>
<div className="stat-item"><p>{userProfile.commentsCnt} Comments</p></div>
</div>
<div className="profile-badges">
{userProfile.badges.map((badge, index) => (
<div key={index} className="badge">
<span className="badge-icon">{badge.icon}</span>
<span className="badge-label">{badge.label}</span>
</div>
))}
<div className="profile-info">
<div className="profile-details">
<h1>{userProfile.username}</h1>
{!isCurrentUser && (
<button className= {`follow-button ${isFollowing ? 'following' : ''}`}
onClick={() => handleFollowToggle(userProfile)}
>
{isFollowing ? 'Unfollow' : 'Follow'}
</button>
)}
</div>
<div className="profile-badges">
{userProfile.badges.map((badge, index) => (
<div key={index} className="badge">
<span className="badge-icon">{badge.icon}</span>
<span className="badge-label">{badge.label}</span>
</div>
))}
</div>
</div>
</div>
</div>
</div>

<nav className="profile-selector">
<button className="selector-item">Posts</button>
<button className="selector-item">Portfolios</button>
<button className="selector-item">Comments</button>
<button className="selector-item">Followers</button>
<button className="selector-item">Following</button>
<button className="selector-item">Settings</button>
</nav>
<nav className="profile-selector">
{['Posts', 'Comments', 'Followers', 'Following'].map((tab) => (
<button
key={tab}
className={`selector-item ${currentTab === tab ? 'active' : ''}`}
onClick={() => setCurrentTab(tab)}
>
{tab}
</button>
))}
</nav>

<div className="profile-content">
{renderListContent()}
</div>
</div>
</div>
);
};
Expand Down
Loading

0 comments on commit 969b0f5

Please sign in to comment.