diff --git a/frontend/src/components/community/CommunityPage.js b/frontend/src/components/community/CommunityPage.js index 11f0a7bc..5bb10e9d 100644 --- a/frontend/src/components/community/CommunityPage.js +++ b/frontend/src/components/community/CommunityPage.js @@ -5,14 +5,13 @@ import "../../styles/community/CommunityPage.css"; import PostCard from "./PostCard"; import "../../styles/Page.css"; import { useNavigate } from "react-router-dom"; -import {apiClient} from "../../service/apiClient"; +import { apiClient } from "../../service/apiClient"; const CommunityPage = () => { const [searchTerm, setSearchTerm] = useState(""); const [sortOrder, setSortOrder] = useState("dsc"); const [searchActive, setSearchActive] = useState(false); const [posts, setPosts] = useState([]); - const [tags, setTags] = useState({}); const [users, setUsers] = useState({}); const navigate = useNavigate(); @@ -20,34 +19,48 @@ const CommunityPage = () => { const fetchPosts = async () => { try { const response = await apiClient.get("/posts"); - const tagsResponse = await apiClient.get("/tags"); - if (!tagsResponse.data) throw new Error("Failed to fetch tags"); - const tagsById = tagsResponse.data.reduce((acc, tag) => { - acc[tag.id] = tag.name; - return acc; - }, {}); - setTags(tagsById); - const usersResponse = await apiClient.get("/users"); - console.log(usersResponse); const usersById = usersResponse.data.reduce((acc, user) => { acc[user.id] = user.username; - console.log(acc); return acc; }, {}); setUsers(usersById); - console.log(usersById); - const transformedPosts = response.data.map((post) => ({ - "post-id": post.id, - user: usersById[post.author] || "Unknown", - title: post.title, - content: [{ type: "plain-text", "plain-text": post.content }], - comments: [], - likes: post.liked_by.length, - tags: post.tags.map((tagId) => tagsById[tagId] || "Unknown"), - "publication-date": new Date(post.created_at).toLocaleDateString(), - })); + 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), + }; + } + }) + ); setPosts(transformedPosts); } catch (error) { console.error("Error fetching posts:", error); diff --git a/frontend/src/components/community/CreatePostPage.js b/frontend/src/components/community/CreatePostPage.js index c33c389e..b077b17d 100644 --- a/frontend/src/components/community/CreatePostPage.js +++ b/frontend/src/components/community/CreatePostPage.js @@ -1,7 +1,8 @@ import React, { useState, useEffect, useRef } from "react"; import { useNavigate } from "react-router-dom"; import "../../styles/community/CreatePostPage.css"; -import {apiClient} from "../../service/apiClient"; +import { apiClient } from "../../service/apiClient"; +import userService from "../../service/userService"; const CreatePostPage = () => { const navigate = useNavigate(); @@ -29,23 +30,12 @@ const CreatePostPage = () => { }, [navigate]); useEffect(() => { - const mockTags = [ - { id: 1, name: "Stock Analysis" }, - { id: 2, name: "BIST30" }, - { id: 3, name: "S&P" }, - { id: 4, name: "NASDAQ" }, - { id: 5, name: "Dow Jones" }, - { id: 6, name: "USA" }, - { id: 7, name: "Türkiye" }, - ]; - const fetchTags = async () => { try { const response = await apiClient.get("/tags"); setAvailableTags(response.data); } catch (error) { - console.error("Failed to fetch tags, using mock data", error); - setAvailableTags(mockTags); + console.error("Failed to fetch tags!", error); } }; @@ -100,10 +90,11 @@ const CreatePostPage = () => { } const tagIds = selectedTags.map((tag) => tag.id); + const userID = userService.getUserId(); const postData = { title, content: description, - author: 2, + author: userID, liked_by: [], tags: tagIds, portfolios: [], @@ -147,14 +138,17 @@ const CreatePostPage = () => {
- setShowTagSuggestions(true)} - /> +
+ setShowTagSuggestions(true)} + /> + +
{showTagSuggestions && (
{filteredTags.map((tag, index) => ( diff --git a/frontend/src/components/community/PostCard.js b/frontend/src/components/community/PostCard.js index 4c412083..9b352796 100644 --- a/frontend/src/components/community/PostCard.js +++ b/frontend/src/components/community/PostCard.js @@ -1,62 +1,62 @@ -import React from 'react'; -import { FaHeart, FaComment } from 'react-icons/fa'; -import { useNavigate } from 'react-router-dom'; -import '../../styles/community/PostCard.css'; +import React from "react"; +import { FaHeart, FaComment } from "react-icons/fa"; +import { useNavigate } from "react-router-dom"; +import "../../styles/community/PostCard.css"; const getColorForTag = (tag) => { - const asciiValue = tag.charCodeAt(0); - const colors = [ - '#3498db', - '#e74c3c', - '#2ecc71', - '#f1c40f', - '#9b59b6' - ]; - return colors[asciiValue % 5]; + const tagName = typeof tag === "string" ? tag : tag.name; + const asciiValue = tagName.charCodeAt(0); + const colors = ["#3498db", "#e74c3c", "#2ecc71", "#f1c40f", "#9b59b6"]; + return colors[asciiValue % 5]; }; const PostCard = ({ post }) => { - const navigate = useNavigate(); + const navigate = useNavigate(); - const navigateToPost = (postId) => { - navigate(`/post/${postId}`); - }; + const navigateToPost = (postId) => { + navigate(`/post/${postId}`); + }; - return ( -
-
-

{post.title}

-

- Published on: {new Date(post['publication-date']).toLocaleDateString()} by {post['user']} -

-
-

{post.content[0]["plain-text"]}

-
-
- - {post.likes} - - - { post.comments.length } - -
- -
-
- {post.tags.map((tag, index) => ( - - {tag} - - ))} -
+ return ( +
+
+

{post.title}

+

+ Published on:{" "} + {new Date(post["publication-date"]).toLocaleDateString()}{" "} + by {post["user"]} +

+
+

{post.content[0]["plain-text"]}

+
+
+ + {post.likes} + + + {post.comments.length} +
- ); + +
+
+ {post.tags.map((tag, index) => ( + + {tag} + + ))} +
+
+ ); }; export default PostCard; diff --git a/frontend/src/components/community/PostView.js b/frontend/src/components/community/PostView.js index f7324c5a..8736c4e1 100644 --- a/frontend/src/components/community/PostView.js +++ b/frontend/src/components/community/PostView.js @@ -3,9 +3,10 @@ import { useParams } from "react-router-dom"; import mockPosts from "../../data/mockPosts"; import FinancialGraph from "./FinancialGraph"; import "../../styles/community/PostView.css"; -import {apiClient} from "../../service/apiClient"; +import { apiClient } from "../../service/apiClient"; import CircleAnimation from "../CircleAnimation"; import NotFound from "../notfound/NotFound"; +import UserService from "../../service/userService"; import { FaNewspaper, FaImage, @@ -22,68 +23,65 @@ const PostView = () => { const [post, setPost] = useState(null); const [commentText, setCommentText] = useState(""); const [tags, setTags] = useState([]); - const [users, setUsers] = useState({}); - const [isUsersLoaded, setIsUsersLoaded] = useState(false); - - const fetchTags = async (tags) => { + const [isLikedByUser, setIsLikedByUser] = useState(false); + const getUserName = async (userID) => { try { - const tagPromises = tags.map((tag) => apiClient.get(`/tags/${tag.id}/`)); - const responses = await Promise.all(tagPromises); - const tagNames = responses.map((response) => response.data.name); - - setTags(tagNames); + const userData = await apiClient.get(`/users/${userID}`); + const userName = userData.data.username; + return userName; } catch (error) { - console.error("Error fetching tags:", error); - setTags([]); - } - }; - - const fetchUsers = async () => { - try { - const response = await apiClient.get("/users"); - const usersById = response.data.reduce((acc, user) => { - acc[user.id] = user.username; - return acc; - }, {}); - setUsers(usersById); - setIsUsersLoaded(true); - } catch (error) { - console.error("Error fetching users:", error); + console.error("Error fetching user name:", error); + return "Unknown"; } }; const getColorForTag = (tag) => { - const asciiValue = tag.charCodeAt(0); + const tagName = typeof tag === "string" ? tag : tag.name; + const asciiValue = tagName.charCodeAt(0); const colors = ["#3498db", "#e74c3c", "#2ecc71", "#f1c40f", "#9b59b6"]; return colors[asciiValue % 5]; }; - useEffect(() => { - if (!isUsersLoaded) { - fetchUsers(); - } - }, [isUsersLoaded]); - useEffect(() => { const fetchBackendPost = async () => { try { const response = await apiClient.get(`/posts/${postId}/`); const backendPost = response.data; + const commentsResponse = await apiClient.get( + `/comments/post-comments/${postId}` + ); + + const commentsData = commentsResponse.data; + + const backendComments = await Promise.all( + commentsData.map(async (comment) => { + const username = await getUserName(comment.user_id); + return { + "comment-id": comment.id, + user: username, + comment: comment.content, + }; + }) + ); + + console.log("backedn, comment", backendComments); + const loggedInUser = parseInt(UserService.getUserId(), 10); + const userHasLiked = backendPost.liked_by.includes(loggedInUser); + const normalizedPost = { "post-id": backendPost.id, - user: users[backendPost.author] || "Unknown", + user: await getUserName(backendPost.author), title: backendPost.title, content: [{ type: "plain-text", "plain-text": backendPost.content }], - comments: [], + comments: backendComments, likes: backendPost.liked_by.length, tags: backendPost.tags, "publication-date": new Date( backendPost.created_at ).toLocaleDateString(), }; - + setIsLikedByUser(userHasLiked); setPost(normalizedPost); - fetchTags(backendPost.tags); } catch (error) { console.error("Error fetching post:", error); setPost(null); @@ -92,7 +90,7 @@ const PostView = () => { } }; - if (isUsersLoaded) { + if (postId) { const fetchData = async () => { const mockPost = mockPosts.find( (post) => post["post-id"] === parseInt(postId) @@ -100,28 +98,62 @@ const PostView = () => { if (mockPost) { setPost(mockPost); setTags(mockPost.tags); + setLoading(false); } else { await fetchBackendPost(); } - setLoading(false); }; + fetchData(); } - }, [isUsersLoaded, postId, users]); + }, [postId]); const handleCommentChange = (e) => setCommentText(e.target.value); - const handleSubmitComment = () => { + const handleSubmitComment = async () => { if (commentText.trim()) { - const newComment = { - "comment-id": Date.now(), - user: "Current User", - comment: commentText, - }; + try { + const username = UserService.getUsername(); + + const payload = { + post_id: postId, + content: commentText.trim(), + }; + + await apiClient.post("/comments/", payload); + + setPost((prevPost) => ({ + ...prevPost, + comments: [ + ...prevPost.comments, + { + "comment-id": Date.now(), // Temporary ID until refreshed + user: username, + comment: commentText.trim(), + }, + ], + })); + setCommentText(""); + } catch (error) { + console.error("Error submitting comment:", error); + } + } + }; + + const handleToggleLike = async () => { + try { + const endpoint = isLikedByUser ? `/like` : `/like`; // Reuse the same endpoint for toggling + const payload = { post_id: postId }; + + await apiClient.post(endpoint, payload); + setPost((prevPost) => ({ ...prevPost, - comments: [...prevPost.comments, newComment], + likes: isLikedByUser ? prevPost.likes - 1 : prevPost.likes + 1, })); - setCommentText(""); + + setIsLikedByUser((prevLiked) => !prevLiked); + } catch (error) { + console.error(`Error toggling like state: ${error.message}`); } }; @@ -152,13 +184,16 @@ const PostView = () => { {tag} )) ) : ( -

Loading tags...

+

)}
@@ -227,8 +262,11 @@ const PostView = () => {
-