Skip to content
Open
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
9 changes: 8 additions & 1 deletion src/apis/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,14 @@ export const updateComment = async (id, data) => {
};

// 과제 !!
export const deleteComment = async (id) => {};
export const deleteComment = async (commentId) => {
const response = await instanceWithToken.delete(`/comment/${commentId}/`);
if (response.status === 204) {
console.log("COMMENT DELETED");
} else {
console.log("[ERROR] error while deleting comment");
}
};

export const getUser = async () => {
const response = await instanceWithToken.get("/account/info/");
Expand Down
139 changes: 84 additions & 55 deletions src/components/Comment/CommentElement.jsx
Original file line number Diff line number Diff line change
@@ -1,59 +1,88 @@
import { useState, useEffect } from "react";

import { getCookie } from "../../utils/cookie";
import { getUser } from "../../apis/api";
const CommentElement = (props) => {
const { comment, handleCommentDelete, postId } = props;
const [content, setContent] = useState(comment.content);
const [isEdit, setIsEdit] = useState(false);

const [onChangeValue, setOnChangeValue] = useState(content); // 수정 취소 시 직전 content 값으로 변경을 위한 state

// comment created_at 전처리
const date = new Date(comment.created_at);
const year = date.getFullYear();
let month = date.getMonth() + 1;
month = month < 10 ? `0${month}` : month;
let day = date.getDate();
day = day < 10 ? `0${day}` : day;

const handleEditComment = () => { // add api call for editing comment
setContent(onChangeValue);
setIsEdit(!isEdit);
console.log({
post: postId,
comment: comment.id,
content: content
});
};

useEffect(() => { // add api call to check if user is the author of the comment
}, []);

return (
<div className="w-full flex flex-row justify-between items-center mb-5">
<div className="w-3/4 flex flex-col gap-1">
{isEdit ? (
<input className="input mb-2" value={onChangeValue} onChange={(e) => setOnChangeValue(e.target.value)} />
) : (
<p className="text-lg">{content}</p>
)}

<span className="text-base text-gray-300">{year}.{month}.{day}</span>
</div>

<div className="flex flex-row items-center gap-3">
{isEdit ? (
<>
<button onClick={() => { setIsEdit(!isEdit); setOnChangeValue(content); }}>취소</button>
<button onClick={handleEditComment}>완료</button>
</>
) : (
<>
<button onClick={() => handleCommentDelete(comment.id)}>삭제</button>
<button onClick={() => setIsEdit(!isEdit)}>수정</button>
</>
)}
</div>
</div>
);
const { comment, handleCommentDelete, postId, handleCommentEdit } = props;
const [content, setContent] = useState(comment.content);
const [isEdit, setIsEdit] = useState(false);
const [user, setUser] = useState();
const [onChangeValue, setOnChangeValue] = useState(content); // 수정 취소 시 직전 content 값으로 변경을 위한 state

// 유저 확인
useEffect(() => {
// access_token이 있으면 유저 정보 가져옴
if (getCookie("access_token")) {
const getUserAPI = async () => {
const user = await getUser();
setUser(user);
};
getUserAPI();
}
}, []);

// comment created_at 전처리
const date = new Date(comment.created_at);
const year = date.getFullYear();
let month = date.getMonth() + 1;
month = month < 10 ? `0${month}` : month;
let day = date.getDate();
day = day < 10 ? `0${day}` : day;

const handleEditClick = async () => {
if (isEdit) {
await handleCommentEdit(comment.id, onChangeValue);
}
setIsEdit(!isEdit);
};

useEffect(() => {
// add api call to check if user is the author of the comment
}, []);

console.log("Current User ID:", user?.id);
console.log("Comment Author ID:", comment?.author.id);

return (
<div className="w-full flex flex-row justify-between items-center mb-5">
<div className="w-3/4 flex flex-col gap-1">
{isEdit ? (
<input
className="input mb-2"
value={onChangeValue}
onChange={(e) => setOnChangeValue(e.target.value)}
/>
) : (
<p className="text-lg">{content}</p>
)}

<span className="text-base text-gray-300">
{year}.{month}.{day}
</span>
</div>

<div className="flex flex-row items-center gap-3">
{isEdit ? (
<>
<button
onClick={() => {
setIsEdit(!isEdit);
setOnChangeValue(content);
}}
>
취소
</button>
<button onClick={handleEditClick}>완료</button>
</>
) : user?.id === comment?.author ? (
<>
<button onClick={() => handleCommentDelete(comment.id)}>
삭제
</button>
<button onClick={() => setIsEdit(true)}>수정</button>
</>
) : null}
</div>
</div>
);
};
export default CommentElement;
111 changes: 66 additions & 45 deletions src/components/Comment/index.jsx
Original file line number Diff line number Diff line change
@@ -1,53 +1,74 @@
import { useState } from "react";
import comments from "../../data/comments"; // dummy data
import { useState, useEffect } from "react";
import CommentElement from "./CommentElement";
import { createComment, getComments } from "../../apis/api";
import { getCookie } from "../../utils/cookie";

const Comment = ({ postId }) => {
const [commentList, setCommentList] = useState(comments); // state for comments
const [newContent, setNewContent] = useState(""); // state for new comment
const Comment = ({
postId,
comments,
handleCommentDelete,
handleCommentEdit,
}) => {
const [commentList, setCommentList] = useState(comments);
const [newContent, setNewContent] = useState("");
const [isLoggedIn, setIsLoggedIn] = useState(false);

const handleCommentSubmit = (e) => {
e.preventDefault();
setCommentList([ // TODO: add api call for creating comment
...commentList,
{
id: commentList.length + 1,
content: newContent,
created_at: new Date().toISOString(),
post: postId,
author: {
id: 1,
username: "user1"
}
}
]);
console.log({
post: postId,
content: newContent
});
setNewContent("");
};
useEffect(() => {
// 로그인 여부 확인
if (getCookie("access_token")) {
setIsLoggedIn(true);
}
}, []);

const handleCommentDelete = (commentId) => {
console.log("comment: ", commentId);
setCommentList(commentList.filter((comment) => comment.id !== commentId)); // TODO: add api call for deleting comment
};
const handleCommentSubmit = async (e) => {
e.preventDefault();
try {
const newComment = {
content: newContent,
post: postId,
};
await createComment(newComment);
setNewContent("");
const updatedComments = await getComments(postId); // 새 댓글 목록을 가져옴
setCommentList(updatedComments); // 상태 업데이트
} catch (error) {
console.error("[ERROR] error while creating comment", error);
}
};

return (
<div className="w-full mt-5 self-start">
<h1 className="text-3xl font-bold my-5">Comments</h1>
{commentList.map((comment) => {
return (
<CommentElement key={comment.id} comment={comment} handleCommentDelete={handleCommentDelete} postId={postId} />
);
})}

<form className="flex flex-row mt-10 gap-3" onSubmit={handleCommentSubmit}>
<input type="text" value={newContent} placeholder="댓글을 입력해주세요" className="input" style={{ width: "calc(100% - 100px)" }} onChange={(e) => setNewContent(e.target.value)} />
<button type="submit" className="button">작성</button>
</form>
</div>
);
return (
<div className="w-full mt-5 self-start">
<h1 className="text-3xl font-bold my-5">Comments</h1>
{commentList.map((comment) => (
<CommentElement
key={comment.id}
comment={comment}
handleCommentDelete={handleCommentDelete}
handleCommentEdit={handleCommentEdit}
postId={postId}
/>
))}

{isLoggedIn && (
<form
className="flex flex-row mt-10 gap-3"
onSubmit={handleCommentSubmit}
>
<input
type="text"
value={newContent}
placeholder="댓글을 입력해주세요"
className="input"
style={{ width: "calc(100% - 100px)" }}
onChange={(e) => setNewContent(e.target.value)}
/>
<button type="submit" className="button">
작성
</button>
</form>
)}
</div>
);
};

export default Comment;
59 changes: 50 additions & 9 deletions src/routes/PostDetailPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,20 @@ import { useState, useEffect } from "react";
import { useParams, Link, useNavigate } from "react-router-dom";
import { BigPost } from "../components/Posts";
import Comment from "../components/Comment";
import { getPost } from "../apis/api";
import posts from "../data/posts";
import {
getPost,
getComments,
deleteComment,
updateComment,
} from "../apis/api";
import { getUser, deletePost } from "../apis/api";
import { getCookie } from "../utils/cookie";

const PostDetailPage = () => {
const { postId } = useParams();
const [post, setPost] = useState(null);

const [user, setUser] = useState();

useEffect(() => {
const post = posts.find((post) => post.id === parseInt(postId));
setPost(post);
}, [postId]);
const [comments, setComments] = useState([]);

useEffect(() => {
const getPostAPI = async () => {
Expand All @@ -38,6 +37,15 @@ const PostDetailPage = () => {
}
}, []);

// 댓글 데이터 가져옴.
useEffect(() => {
const getCommentsAPI = async () => {
const comments = await getComments(postId);
setComments(comments);
};
getCommentsAPI();
}, [postId]);

const navigate = useNavigate();

const onClickDelete = async () => {
Expand All @@ -50,12 +58,44 @@ const PostDetailPage = () => {
}
};

const handleCommentDelete = async (commentId) => {
const confirmDelete = window.confirm("댓글을 삭제하시겠습니까?");
if (!confirmDelete) return;
try {
await deleteComment(commentId); // API 호출로 댓글 삭제
setComments(comments.filter((comment) => comment.id !== commentId)); // 로컬 상태에서 댓글 제거
window.location.reload();
} catch (error) {
console.error("[ERROR] error while deleting comment", error);
}
};

const handleCommentEdit = async (commentId, updatedContent) => {
try {
await updateComment(commentId, { content: updatedContent });
setComments(
comments.map((comment) =>
comment.id === commentId
? { ...comment, content: updatedContent }
: comment
)
); // 로컬 상태에서 댓글 업데이트
} catch (error) {
console.error("[ERROR] error while updating comment", error);
}
};

return (
post && (
<div className="flex flex-col items-center w-[60%] p-8">
<BigPost post={post} />
<Comment
postId={postId}
comments={comments}
handleCommentDelete={handleCommentDelete}
handleCommentEdit={handleCommentEdit}
/>

<Comment postId={postId} />
<div className="flex flex-row gap-3">
{user?.id === post?.author.id ? (
<>
Expand All @@ -70,6 +110,7 @@ const PostDetailPage = () => {
</button>
</>
) : null}

{/* user와 post.author가 동일하면 버튼을 리턴, 아니면 null */}
</div>
</div>
Expand Down