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
87 changes: 87 additions & 0 deletions src/components/Comment/CommentElement.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { useState, useEffect } from "react";

const CommentElement = ({ comment, handleCommentDelete, handleCommentEdit }) => {

/* TODO: props 받기
Hint: src/components/Comment/index.jsx에서 어떠한 props를 넘겨주는지 확인해보세요! */

/* TODO: 댓글을 수정하는 input의 value를 관리하기 위한 state 작성
Hint: 댓글의 내용을 저장하는 state와 수정 중인지 여부를 저장하는 state를 따로 만드는 게 좋겠죠? */
const [isEditing, setIsEditing] = useState(false);
const [editedComment, setEditedComment] = useState(comment.content);

// 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;

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">
{/* // TODO: 수정 중일 때와 아닐 때를 나눠서 보여줘야 해요! */}

{isEditing ? (
<input
className="input"
onChange={(e) => setEditedComment(e.target.value)}
value={editedComment}
></input>
) : (
<p>{comment.content}</p>
)}
{/* // 날짜 */}
<span className="text-base text-gray-300">
{year}.{month}.{day}
</span>
</div>

<div className="flex flex-row items-center gap-3">
{isEditing ? (
<>
<button
onClick={() => {
setIsEditing(false);
}}
>
취소
</button>
<button
onClick={() => {
setIsEditing(false);
handleCommentEdit(comment.id, editedComment);
}}
>
완료
</button>
</>
) : (
<>
<button
onClick={() => {
setIsEditing(true);
}}
>
수정
</button>
<button
onClick={() => {
setEditedComment([]);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

빈 리스트로 만들어주는 이유가 궁금해요

handleCommentDelete(comment.id);
}}
>
삭제
</button>
</>
)}
</div>
</div>
);
};
export default CommentElement;
68 changes: 68 additions & 0 deletions src/components/Comment/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { useState } from "react";
import comments from "../../data/comments"; // dummy data
import CommentElement from "./CommentElement";

const Comment = ({ postId }) => {
// TODO: comments를 저장하기 위한 state를 만들어주세요
const [commentList, setCommentList] = useState(comments);
// TODO: 새로운 댓글을 추가하기 위한 state를 만들어주세요
const [newComment, setNewComment] = useState([]);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

새로운 댓글은 아무래도 string 형태의 데이터일 것이니, 초기화 시 빈 배열보다는 빈 문자열 ("")로 하는 것이 더 적합하지 않을까 생각해요!


const handleCommentSubmit = (e) => {
e.preventDefault();
const newCommentObject = {
id: commentList.length + 1,
content: newComment,
created_at: Date(),
};
setCommentList([...commentList, newCommentObject]);
setNewComment("");
alert("댓글 작성"); // add api call for creating comment
};

const handleCommentDelete = (commentId) => {
setCommentList(commentList.filter((comment) => comment.id !== commentId));
alert("댓글 삭제"); // add api call for deleting comment
};

const handleCommentEdit = (commentId, editedComment) => {
setCommentList((commentList) => {
return commentList.map((comment) => {
if (comment.id === commentId) {
return { ...comment, content: editedComment };
}
return comment;
});
});
alert("댓글 수정");
};

return (
<div className="w-full mt-5 self-start">
<h1 className="text-3xl font-bold my-5">Comments</h1>
{commentList.map((comment) => (
<CommentElement
comment={comment}
handleCommentDelete={handleCommentDelete}
handleCommentEdit={handleCommentEdit}
/>
))}
<form
onSubmit={handleCommentSubmit}
className="flex flex-row items-center justify-center mt-10 gap-2"
>
<input
required
className="input"
value={newComment}
onChange={(e) => setNewComment(e.target.value)}
></input>
<button type="submit" className="button w-24 h-12 py-10">
작성
</button>
</form>
</div>
);
};

export default Comment;
35 changes: 35 additions & 0 deletions src/data/comments.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// dummy data
const comments = [
{
"id": 1,
"content": "새해 복 많이 받으세요^^",
"created_at": "2024-01-01T15:09:43Z",
"post": 1,
"author": {
"id": 2,
"username": "user2"
}
},
{
"id": 2,
"content": "축구 2대 0;;;",
"created_at": "2024-02-07T15:09:43Z",
"post": 1,
"author": {
"id": 3,
"username": "user3"
}
},
{
"id": 3,
"content": "망할 개강이야...",
"created_at": "2024-03-02T15:09:43Z",
"post": 1,
"author": {
"id": 4,
"username": "user4"
}
},
]

export default comments;
2 changes: 2 additions & 0 deletions src/routes/PostDetailPage.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useState, useEffect } from "react";
import { useParams, Link, useNavigate } from "react-router-dom";
import { BigPost } from "../components/Posts";
import Comment from "../components/Comment";
import posts from "../data/posts";

const PostDetailPage = () => {
Expand All @@ -21,6 +22,7 @@ const PostDetailPage = () => {
post && (
<div className="flex flex-col items-center w-[60%] p-8">
<BigPost post={post} />
<Comment postId={postId} />
<div className="flex flex-row gap-3">
<Link to={`/${post.id}/edit`}>
<button className="button mt-10 py-2 px-10">수정</button>
Expand Down
85 changes: 83 additions & 2 deletions src/routes/PostEditPage.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,34 @@
import { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import posts from "../data/posts";
import { BigPost } from "../components/Posts";

const PostEditPage = () => {
const { postId } = useParams();
const [post, setPost] = useState({});
const [isSubmitted, setIsSubmitted] = useState(false);
const [tagInputValue, setTagInputValue] = useState("");
const [autoCompletes, setAutoCompletes] = useState([]);
const [tags, setTags] = useState([]);
const [post, setPost] = useState({
id: posts.length,
title: "",
content: "",
author: { id: posts.length, username: "아기사자" },
tags: [],
like_users: [],
created_at: "2024-02-04T07:42:50.658501Z",
});

useEffect(() => {
const duplicatedTagList = posts.reduce((acc, post) => {
for (let tag of post.tags) {
acc.add(tag.content);
}
return acc;
}, new Set());
const tagList = [...duplicatedTagList];
setTags([...tagList]);
}, []);

// 기존 게시글 불러오기
useEffect(() => {
Expand All @@ -13,12 +37,69 @@ const PostEditPage = () => {
setPost(originalPost);
}, [postId]);

const handleChange = (e) => {
setPost({ ...post, [e.target.id]: e.target.value });
};

const handleTag = (e) => {
setTagInputValue(e.target.value);
if (e.target.value) {
const autoCompleteData = tags.filter((tag) =>
tag.includes(e.target.value)
);
setAutoCompletes(autoCompleteData);
}
};

const handleAutoCompletes = (autoComplete) => {
const selectedTag = tags.find((tag) => tag === autoComplete);
if (post.tags.includes(selectedTag)) return;
setPost({
...post,
tags: [...post.tags, selectedTag],
});
setTagInputValue("");
setAutoCompletes([]);
};

const addTag = (e) => {
e.preventDefault();
if (post.tags.find((tag) => tag === tagInputValue)) return;
setPost({
...post,
tags: [...post.tags, tagInputValue],
});
setTagInputValue("");
setAutoCompletes([]);
};

const deleteTag = (tag) => {
setPost({
...post,
tags: post.tags.filter((t) => t !== tag),
});
};

const onSubmit = (e) => {
e.preventDefault();
const editedPost = {
...post,
like_users: [],
tags: post.tags.map((tag, idx) => {
return { id: idx + 1, content: tag };
}),
};
setPost(editedPost);
setIsSubmitted(true);
alert("게시글을 수정합니다.");
// TODO : api connect(edit post)
};

return (
return isSubmitted ? (
<div className="flex flex-col items-center w-[60%] p-8">
<BigPost post={post} />
</div>
) : (
<div className="flex flex-col items-center w-3/5">
<h3 className="font-bold text-4xl">게시글 수정</h3>
<form className="form" onSubmit={onSubmit}>
Expand Down