From d30110bbb42417b16dea4ad7c01eaa85caaf0816 Mon Sep 17 00:00:00 2001 From: CZ Date: Sat, 20 May 2023 13:22:21 +0900 Subject: [PATCH 1/3] week9 task --- src/components/Comments/CommentElement.jsx | 85 +++++----- src/components/Comments/index.jsx | 183 +++++++++++---------- src/routes/PostDetailPage.jsx | 2 +- 3 files changed, 139 insertions(+), 131 deletions(-) diff --git a/src/components/Comments/CommentElement.jsx b/src/components/Comments/CommentElement.jsx index 1057c66..ce51251 100644 --- a/src/components/Comments/CommentElement.jsx +++ b/src/components/Comments/CommentElement.jsx @@ -1,10 +1,16 @@ import { useEffect, useState } from "react"; -const CommentElement = ({ comment, deleteComment, editComment}) => { +const CommentElement = ({ + comment, + comments, + deleteComment, + setComments +}) => { // TODO : props 받기 // TODO : 수정하는 input 내용 관리` // comment created_at 전처리 + console.log(comments); const date = new Date(comment.created_at); const year = date.getFullYear(); let month = date.getMonth() + 1; @@ -13,6 +19,7 @@ const CommentElement = ({ comment, deleteComment, editComment}) => { day = day < 10 ? `0${day}` : day; const [isClickEdit, setIsClickEdit] = useState(false); + const [commentContent, setCommentContent] = useState(comment.content); const onClickDelete = () => { console.log("delete"); @@ -20,43 +27,55 @@ const CommentElement = ({ comment, deleteComment, editComment}) => { }; const onClickEdit = () => { + console.log("edit"); setIsClickEdit(true); - }; - - const doneClickEdit = () => { - // setFormData((nowFormData) => ({ - // ...nowFormData, - // content: input.value - // })) - editComment(comment); - setIsClickEdit(false); - }; + } - // useEffect(()=>{ - // if(handleCommentDelete){ + const editingContent = (e) => { + // console.log(e.target.value); + setCommentContent(e.target.value); + } - // } - // }) + const updateComment = (e) => { + e.preventDefault(); + const updatingComments = comments.map((c) => ( + // c.id == comment.id ? ([...comments, c.content= commentContent]) : ([...comments]) + c.id == comment.id ? (c.content= commentContent) : ([...comments]) + )); + // console.log("comments!", comments); + setComments(comments); + // console.log(comments); + setIsClickEdit(false); + } return (
{isClickEdit ? ( -
-
- - - {year}.{month}.{day} - +
+
+
+ + + {year}.{month}.{day} + +
+
+ +
-
- -
-
+ ) : (
-
{comment.content}
+
{commentContent}
{year}.{month}.{day} @@ -70,19 +89,7 @@ const CommentElement = ({ comment, deleteComment, editComment}) => {
)}
-
// 1 - //
- //
- //
{comment.content}
- // - // {year}.{month}.{day} - // - //
- //
- // - // - //
- // )}; +
); }; diff --git a/src/components/Comments/index.jsx b/src/components/Comments/index.jsx index aca7515..8df5f42 100644 --- a/src/components/Comments/index.jsx +++ b/src/components/Comments/index.jsx @@ -4,106 +4,107 @@ import comment from "../../data/comment"; import CommentElement from "./CommentElement"; const Comment = ({ commentt }) => { - // TODO 1: 가짜 comments 불러와서 관리해야겟즤 - const { postId } = useParams(); - - const [comments, setComments] = useState(); - - useEffect(() => { - const comments = comment.filter((comment)=> comment.post == postId); - setComments(comments); - }, [postId]); - - // TODO 2: comment추가하는 input 관리해줘야겟지 - const [isSubmitted, setIsSubmitted] = useState(false); - const [commentInputValue, setCommentInputValue] = useState(""); - - useEffect(() =>{ - const commentInputValue = formData.content; - setCommentInputValue(commentInputValue); - // commentelement로 만들어줘야겠지? -> 이게 아닌가벼? - // 그럼 이걸 그냥 밑에 올리는. 그런... - },[commentInputValue]); - - // TODO 3: comment Form 제출됐을때 실행되는 함수 만들어줘 - - const time = new Date(); - const [created_at, setCreated_at] = useState(time.toJSON()); - // ✅ submit 될 때의 시간을 넣고 싶은데 어떻게 하지??!? - - const [formData, setFormData] = useState({ - id: comment.length + 1, // 이건 data/comment에 접근을 못해서 제대로 된 index가 나올 수 없을 듯... - content: "", - created_at: created_at, - post: postId, // 사실 여기에는 postId가 들어가는 명세로 되어있어요 - author: {id: 2, username: "user2"} - }); - - const handleFormData = (e) => { + // TODO 1: 가짜 comments 불러와서 관리해야겟즤 + const { postId } = useParams(); + + const [comments, setComments] = useState(); + + useEffect(() => { + const comments = comment.filter((comment) => comment.post == postId); + setComments(comments); + }, [postId]); + + // TODO 2: comment추가하는 input 관리해줘야겟지 + const [isSubmitted, setIsSubmitted] = useState(false); + const [commentInputValue, setCommentInputValue] = useState(""); + + useEffect(() => { + const commentInputValue = formData.content; + setCommentInputValue(commentInputValue); + // commentelement로 만들어줘야겠지? -> 이게 아닌가벼? + // 그럼 이걸 그냥 밑에 올리는. 그런... + }, [commentInputValue]); + + // TODO 3: comment Form 제출됐을때 실행되는 함수 만들어줘 + + const time = new Date(); + const [created_at, setCreated_at] = useState(time.toJSON()); + + const [formData, setFormData] = useState({ + id: comment.length + 1, // 이건 data/comment에 접근을 못해서 제대로 된 index가 나올 수 없을 듯... + content: "", + created_at: created_at, + post: postId, // 사실 여기에는 postId가 들어가는 명세로 되어있어요 + author: { id: 2, username: "user2" }, + }); + + const handleFormData = (e) => { const { id, value } = e.target; setFormData({ ...formData, [id]: value }); - console.log(formData); + console.log(formData); }; - const handleCommentSubmit = (e) => { - e.preventDefault(); - comments.push(formData); - console.log(formData.content); - setFormData((nowFormData) => ({ - ...nowFormData, - content: "" - })) - // console.log("input", commentInputValue); - console.log(comments); + const handleCommentSubmit = (e) => { + e.preventDefault(); + comments.push(formData); + console.log(formData.content); + setFormData((nowFormData) => ({ + ...nowFormData, + content: "", + })); + // console.log("input", commentInputValue); + console.log(comments); // alert(`"${formData.content}" 댓글이 작성 완료되었습니다잉`); - // comments.push(formData); + // comments.push(formData); + }; + + // TODO 4: commet Delete 하는 함수 만들어죠 + const deleteComment = (comment) => { + console.log(comment.content); + const deletedComments = comments.filter( + (c) => c.content !== comment.content + ); + setComments(deletedComments); + console.log(deletedComments); }; -// TODO 4: commet Delete 하는 함수 만들어죠 - const deleteComment = (comment) => { - console.log(comment.content); - const deletedComments = comments.filter((c) => c.content !== comment.content) - setComments(deletedComments); - console.log(deletedComments); - }; - - const editComment = (comment) => { - // console.log("comment:", comment); - // console.log("comment.id:", comment.id); - // console.log("감히 제가.. comments에 접근해봅니다", comments[comment.id-1]); - console.log(comment.value); - const editedComment = comments[comment.id-1].content = comment.value; - // console.log(editedComment); - }; - - return ( -
+ return ( +

Comments

- {/* // commentElement */} - {/* // 가 comment마다 반복시켜야즤 */} - - {comments && comments.map((comment)=> ( - - ))} - -
- {/* // TODO 2-3 : comment 추가하는 comment form 만들어주기 */} - - - -
-
- ); + +
+ ); }; -export default Comment; \ No newline at end of file +export default Comment; diff --git a/src/routes/PostDetailPage.jsx b/src/routes/PostDetailPage.jsx index 5813914..3bf6111 100644 --- a/src/routes/PostDetailPage.jsx +++ b/src/routes/PostDetailPage.jsx @@ -34,7 +34,7 @@ useEffect(() => {
{/* post detail component */} - +
From d7019f08125e3e78852863f0cd9f48b1230eb6e2 Mon Sep 17 00:00:00 2001 From: CZ Date: Tue, 30 May 2023 14:29:27 +0900 Subject: [PATCH 2/3] week10 task --- package-lock.json | 87 +++++++++++++ package.json | 2 + src/apis/api.js | 142 +++++++++++++++++++++ src/apis/axios.js | 57 +++++++++ src/components/Comments/CommentElement.jsx | 142 ++++++++++----------- src/components/Comments/index.jsx | 85 ++++++------ src/components/Form/index.jsx | 1 + src/components/Header/index.jsx | 36 +++++- src/components/Posts/index.jsx | 7 +- src/routes/HomePage.jsx | 70 ++++++---- src/routes/PostCreatePage.jsx | 68 ++++------ src/routes/PostDetailPage.jsx | 73 +++++++---- src/routes/PostEditPage.jsx | 101 +++++++-------- src/routes/SignInPage.jsx | 8 +- src/routes/SignUpPage.jsx | 21 +-- src/utils/cookie.js | 22 ++++ 16 files changed, 630 insertions(+), 292 deletions(-) create mode 100644 src/apis/api.js create mode 100644 src/apis/axios.js create mode 100644 src/utils/cookie.js diff --git a/package-lock.json b/package-lock.json index cc88270..3ac2ccb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,9 @@ "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "axios": "^1.4.0", "react": "^18.2.0", + "react-cookie": "^4.1.1", "react-dom": "^18.2.0", "react-router-dom": "^6.11.1", "react-scripts": "5.0.1", @@ -3820,6 +3822,11 @@ "@types/node": "*" } }, + "node_modules/@types/cookie": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.3.3.tgz", + "integrity": "sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow==" + }, "node_modules/@types/eslint": { "version": "8.37.0", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.37.0.tgz", @@ -3873,6 +3880,15 @@ "@types/node": "*" } }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "node_modules/@types/html-minifier-terser": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", @@ -5096,6 +5112,29 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", + "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/axobject-query": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz", @@ -8702,6 +8741,19 @@ "he": "bin/he" } }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, "node_modules/hoopy": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", @@ -14078,6 +14130,11 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", @@ -14222,6 +14279,19 @@ "node": ">=14" } }, + "node_modules/react-cookie": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-4.1.1.tgz", + "integrity": "sha512-ffn7Y7G4bXiFbnE+dKhHhbP+b8I34mH9jqnm8Llhj89zF4nPxPutxHT1suUqMeCEhLDBI7InYwf1tpaSoK5w8A==", + "dependencies": { + "@types/hoist-non-react-statics": "^3.0.1", + "hoist-non-react-statics": "^3.0.0", + "universal-cookie": "^4.0.0" + }, + "peerDependencies": { + "react": ">= 16.3.0" + } + }, "node_modules/react-dev-utils": { "version": "12.0.1", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", @@ -16266,6 +16336,23 @@ "node": ">=8" } }, + "node_modules/universal-cookie": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-4.0.4.tgz", + "integrity": "sha512-lbRVHoOMtItjWbM7TwDLdl8wug7izB0tq3/YVKhT/ahB4VDvWMyvnADfnJI8y6fSvsjh51Ix7lTGC6Tn4rMPhw==", + "dependencies": { + "@types/cookie": "^0.3.3", + "cookie": "^0.4.0" + } + }, + "node_modules/universal-cookie/node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", diff --git a/package.json b/package.json index ac7db0d..d3fe664 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,9 @@ "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "axios": "^1.4.0", "react": "^18.2.0", + "react-cookie": "^4.1.1", "react-dom": "^18.2.0", "react-router-dom": "^6.11.1", "react-scripts": "5.0.1", diff --git a/src/apis/api.js b/src/apis/api.js new file mode 100644 index 0000000..d9e5ff1 --- /dev/null +++ b/src/apis/api.js @@ -0,0 +1,142 @@ +import { instance, instanceWithToken } from "./axios"; + +// src/api/api.js +// Account 관련 API들 +export const signIn = async (data) => { + const response = await instance.post("/account/signin/", data); + if (response.status === 200) { + window.location.href = "/"; + } else { + console.log("Error"); + } +}; + +export const signUp = async (data) => { + const response = await instance.post("/account/signup/", data); + if (response.status === 200) { + window.location.href = "/"; + } + return response; +}; + +export const getUser = async () => { + const response = await instanceWithToken.get("/account/info/"); + if (response.status == 200) { + // console.log("success"); + } else { + console.log("fail"); + } + return response; +}; + +export const getPosts = async () => { + const response = await instance.get("/post/"); + return response.data; +}; + +export const getPost = async (id) => { + const response = await instance.get(`/post/${id}/`); + return response.data; +}; + +export const createPost = async (data, navigate) => { + const response = await instanceWithToken.post("/post/", data); + if (response.status === 201) { + console.log("POST SUCCESS"); + navigate(-1); // 새로고침 X + navigate("/post"); + } else { + console.log("[ERROR] error while creating post"); + } +}; + +export const updatePost = async (id, data, navigate) => { + const response = await instanceWithToken.patch(`/post/${id}/`, data); + if (response.status === 200) { + console.log("POST UPDATE SUCCESS"); + navigate(-1); + } else { + console.log("[ERROR] error while updating post"); + } +}; + +// 과제!! +export const deletePost = async (id, navigate) => { + const response = await instanceWithToken.delete(`/post/${id}/`); + window.confirm("정말 삭제하실건가요...? U._.U"); + if (response.status === 204) { + console.log("POST DELETE SUCCESS"); + window.location.reload(); + navigate(-1); + } else { + console.log("[ERROR] error while deleting post"); + } +}; + +// 과제!! +export const likePost = async (postId) => { + const response = await instanceWithToken.post(`/post/${postId}/like/`); + if (response.status == 200) { + console.log("I like it!"); + window.location.reload(); + } else { + console.log("[Error] error while liking post"); + } +}; + +// src/apis/api.js +// ... +// 추가 + +// Tag 관련 API들 +export const getTags = async () => { + const response = await instance.get("/tag/"); + return response.data; +}; + +export const createTag = async (data) => { + const response = await instanceWithToken.post("/tag/", data); + if (response.status === 201) { + console.log("TAG SUCCESS"); + } else { + console.log("[ERROR] error while creating tag"); + } + return response; // response 받아서 그 다음 처리 +}; + +// Comment 관련 API들 +export const getComments = async (postId) => { + const response = await instance.get(`/comment/?post=${postId}`); + return response.data; +}; + +export const createComment = async (data) => { + const response = await instanceWithToken.post("/comment/", data); + if (response.status === 201) { + console.log("COMMENT SUCCESS"); + window.location.reload(); // 새로운 코멘트 생성시 새로고침으로 반영 + } else { + console.log("[ERROR] error while creating comment"); + } +}; + +export const updateComment = async (id, data) => { + const response = await instanceWithToken.patch(`/comment/${id}/`, data); + if (response.status === 200) { + console.log("COMMENT UPDATE SUCCESS"); + window.location.reload(); + } else { + console.log("[ERROR] error while updating comment"); + } +}; + +// 과제 !! +export const deleteComment = async (id) => { + const response = await instanceWithToken.delete(`/comment/${id}/`); + if (response.status === 204) { + console.log("COMMENT DELETE SUCCESS"); + window.location.reload(); + } else { + console.log("[ERROR] error while deleting comment"); + } +}; diff --git a/src/apis/axios.js b/src/apis/axios.js new file mode 100644 index 0000000..ecf79ce --- /dev/null +++ b/src/apis/axios.js @@ -0,0 +1,57 @@ +// src/apis/axios.js + +import axios from "axios"; +import { getCookie } from "../utils/cookie"; + +// baseURL, credential, 헤더 세팅 +axios.defaults.baseURL = 'http://localhost:8000/api'; +axios.defaults.withCredentials = true; +axios.defaults.headers.post['Content-Type'] = 'application/json'; +axios.defaults.headers.common['X-CSRFToken'] = getCookie('csrftoken'); + + +// 누구나 접근 가능한 API들 +export const instance = axios.create(); + +// Token 있어야 접근 가능한 API들 - 얘는 토큰을 넣어줘야 해요 +export const instanceWithToken = axios.create(); + +// src/apis/axios.js +// ⬇️ 추가 +// instanceWithToken에는 쿠키에서 토큰을 찾고 담아줍시다! +instanceWithToken.interceptors.request.use( + // 요청을 보내기전 수행할 일 + // 사실상 이번 세미나에 사용할 부분은 이거밖에 없어요 + (config) => { + const accessToken = getCookie('access_token'); + + if (!accessToken) { + // token 없으면 리턴 + return; + } else { + // token 있으면 헤더에 담아주기 (Authorization은 장고에서 JWT 토큰을 인식하는 헤더 key) + config.headers["Authorization"] = `Bearer ${accessToken}`; + } + return config; + }, + + // 클라이언트 요청 오류 났을 때 처리 + (error) => { + // 콘솔에 찍어주고, 요청을 보내지 않고 오류를 발생시킴 + console.log("Request Error!!"); + return Promise.reject(error); + } +); + +instanceWithToken.interceptors.response.use( + (response) => { + // 서버 응답 데이터를 프론트에 넘겨주기 전 수행할 일 + console.log("Interceptor Response!!"); + return response; + }, + (error) => { + // 서버가 오류를 응답했을 때 처리 - 콘솔 찍어주고, 프론트에게 보내지 않고 오류를 발생시킴 + console.log("Response Error!!"); + return Promise.reject(error); + } +); \ No newline at end of file diff --git a/src/components/Comments/CommentElement.jsx b/src/components/Comments/CommentElement.jsx index ce51251..90d8305 100644 --- a/src/components/Comments/CommentElement.jsx +++ b/src/components/Comments/CommentElement.jsx @@ -1,16 +1,14 @@ import { useEffect, useState } from "react"; +import { getUser, updateComment, deleteComment } from "../../apis/api"; +import { getCookie } from "../../utils/cookie"; -const CommentElement = ({ - comment, - comments, - deleteComment, - setComments -}) => { - // TODO : props 받기 - // TODO : 수정하는 input 내용 관리` +const CommentElement = (props) => { + const { comment, handleCommentDelete } = props; + const [content, setContent] = useState(comment.content); + const [isEdit, setIsEdit] = useState(false); + + const [user, setUser] = useState(null); - // comment created_at 전처리 - console.log(comments); const date = new Date(comment.created_at); const year = date.getFullYear(); let month = date.getMonth() + 1; @@ -18,79 +16,73 @@ const CommentElement = ({ let day = date.getDate(); day = day < 10 ? `0${day}` : day; - const [isClickEdit, setIsClickEdit] = useState(false); - const [commentContent, setCommentContent] = useState(comment.content); - - const onClickDelete = () => { - console.log("delete"); - deleteComment(comment); + const handleEditComment = () => { + console.log("content: ", content); + updateComment(comment.id, { content: content }); }; - const onClickEdit = () => { - console.log("edit"); - setIsClickEdit(true); - } - - const editingContent = (e) => { - // console.log(e.target.value); - setCommentContent(e.target.value); - } + const handleDeleteComment = () => { + console.log("comment.id: ", comment.id); + deleteComment(comment.id); + }; - const updateComment = (e) => { - e.preventDefault(); - const updatingComments = comments.map((c) => ( - // c.id == comment.id ? ([...comments, c.content= commentContent]) : ([...comments]) - c.id == comment.id ? (c.content= commentContent) : ([...comments]) - )); - // console.log("comments!", comments); - setComments(comments); - // console.log(comments); - setIsClickEdit(false); - } + useEffect(() => { + // access_token이 있으면 유저 정보 가져옴 + if (getCookie("access_token")) { + const getUserAPI = async () => { + const user = await getUser(); + setUser(user); + }; + getUserAPI(); + } + }, []); return ( -
-
- {isClickEdit ? ( -
-
-
- - - {year}.{month}.{day} - -
-
- -
-
-
+
+
+ {isEdit ? ( + setContent(e.target.value)} + /> ) : ( -
-
-
{commentContent}
- - {year}.{month}.{day} - -
-
- - -
-
+

{comment.content}

)} + + {year}.{month}.{day} +
-
+ {user?.data.id === comment.author.id ? ( +
+ {isEdit ? ( + <> + + + + ) : ( + <> + + + + )} +
+ ) : null} +
); }; - export default CommentElement; diff --git a/src/components/Comments/index.jsx b/src/components/Comments/index.jsx index 8df5f42..68cc298 100644 --- a/src/components/Comments/index.jsx +++ b/src/components/Comments/index.jsx @@ -2,17 +2,25 @@ import { createElement, useEffect, useState } from "react"; import { useParams } from "react-router-dom"; import comment from "../../data/comment"; import CommentElement from "./CommentElement"; +import { getComments } from "../../apis/api"; +import { createComment } from "../../apis/api"; -const Comment = ({ commentt }) => { +const Comment = ({ postId }) => { // TODO 1: 가짜 comments 불러와서 관리해야겟즤 - const { postId } = useParams(); - - const [comments, setComments] = useState(); - - useEffect(() => { - const comments = comment.filter((comment) => comment.post == postId); - setComments(comments); + // const { postId } = useParams(); + + const [commentList, setCommentList] = useState([]); // 처음에는 빈 리스트로 설정 + const [newContent, setNewContent] = useState(""); + + //추가 + useEffect(() => { + const getCommentsAPI = async () => { + const comments = await getComments(postId); + setCommentList(comments); + }; + getCommentsAPI(); }, [postId]); +; // TODO 2: comment추가하는 input 관리해줘야겟지 const [isSubmitted, setIsSubmitted] = useState(false); @@ -21,8 +29,6 @@ const Comment = ({ commentt }) => { useEffect(() => { const commentInputValue = formData.content; setCommentInputValue(commentInputValue); - // commentelement로 만들어줘야겠지? -> 이게 아닌가벼? - // 그럼 이걸 그냥 밑에 올리는. 그런... }, [commentInputValue]); // TODO 3: comment Form 제출됐을때 실행되는 함수 만들어줘 @@ -46,59 +52,42 @@ const Comment = ({ commentt }) => { const handleCommentSubmit = (e) => { e.preventDefault(); - comments.push(formData); - console.log(formData.content); - setFormData((nowFormData) => ({ - ...nowFormData, - content: "", - })); - // console.log("input", commentInputValue); - console.log(comments); - // alert(`"${formData.content}" 댓글이 작성 완료되었습니다잉`); - // comments.push(formData); + createComment({ post: postId, content: newContent }); + setNewContent(""); }; // TODO 4: commet Delete 하는 함수 만들어죠 - const deleteComment = (comment) => { - console.log(comment.content); - const deletedComments = comments.filter( - (c) => c.content !== comment.content - ); - setComments(deletedComments); - console.log(deletedComments); - }; + // const handleCommentDelete = () => { + // // deleteComment(comment.id); + // console.log("delete"); + // }; return (

Comments

- {/* // commentElement */} - {/* // 가 comment마다 반복시켜야즤 */} - - {comments && - comments.map((comment) => ( - - ))} - + {commentList.map((comment) => { + return ( +
+ +
+ ); + })} + {/* comment form component */}
- {/* // TODO 2-3 : comment 추가하는 comment form 만들어주기 */} setNewContent(e.target.value)} /> - diff --git a/src/components/Form/index.jsx b/src/components/Form/index.jsx index 9743d2c..a6b485d 100644 --- a/src/components/Form/index.jsx +++ b/src/components/Form/index.jsx @@ -181,6 +181,7 @@ export const PostForm = ({ onSubmit, tags, formData, setFormData }) => { tags: formData.tags.filter((t) => t !== tag), }); }; + return (
); }; diff --git a/src/components/Posts/index.jsx b/src/components/Posts/index.jsx index f8449b6..a0fccfa 100644 --- a/src/components/Posts/index.jsx +++ b/src/components/Posts/index.jsx @@ -1,9 +1,11 @@ import { Link } from "react-router-dom"; +import { likePost } from "../../apis/api"; export const SmallPost = ({ post }) => { const onClickLike = () => { console.log("나도 좋아!"); // add api call for liking post here + likePost(post.id); }; return ( @@ -32,13 +34,14 @@ export const SmallPost = ({ post }) => { export const BigPost = ({ post }) => { const onClickLike = () => { console.log("나도 좋아!"); + likePost(post.id); // add api call for liking post here }; return (
-

{post.title}

+

{post.title}

{post.author.username}
{post.content}
@@ -55,4 +58,4 @@ export const BigPost = ({ post }) => {
); -}; \ No newline at end of file +}; diff --git a/src/routes/HomePage.jsx b/src/routes/HomePage.jsx index aa78d04..0896e1c 100644 --- a/src/routes/HomePage.jsx +++ b/src/routes/HomePage.jsx @@ -2,12 +2,34 @@ import { useEffect, useState } from "react"; import { SmallPost } from "../components/Posts"; import posts from "../data/posts"; import { Link } from "react-router-dom"; +import { getPosts, getUser } from "../apis/api"; +import { getTags } from "../apis/api"; +import { getCookie } from "../utils/cookie"; const HomePage = () => { const [tags, setTags] = useState([]); const [searchTags, setSearchTags] = useState([]); const [searchValue, setSearchValue] = useState(""); - const [postList, setPostList] = useState(posts); + const [postList, setPostList] = useState([]); + + useEffect(() => { + const getPostsAPI = async () => { + const posts = await getPosts(); + setPostList(posts); + }; + getPostsAPI(); + + const getTagsAPI = async () => { + const tags = await getTags(); + const tagContents = tags.map((tag) => { + return tag.content; + }); + setTags(tagContents); + setSearchTags(tagContents); + }; + getTagsAPI(); + }, []); + useEffect(() => { const tagList = posts.reduce((acc, post) => { @@ -27,25 +49,13 @@ const HomePage = () => { }; const handleTagFilter = (e) => { - const tagText = e.target.innerText; - console.log(tagText); - const filteredTagText = tagText.replace("#", ""); - console.log(filteredTagText); - setSearchValue(filteredTagText); - - if (filteredTagText === searchValue) { - console.log("다시"); + const { innerText } = e.target; + if (searchValue === innerText.substring(1)) { setSearchValue(""); - setPostList(posts); - return; + } else { + const activeTag = innerText.substring(1); + setSearchValue(activeTag); } - - console.log("postList", postList); - console.log("posts", posts); - const filteredPosts = posts.filter((post) => - post.tags.some((tag) => tag.content === filteredTagText) - ); - setPostList(filteredPosts); }; return ( @@ -76,15 +86,23 @@ const HomePage = () => {
- {postList.map((post) => ( - - ))} -
-
- - Post - + {postList + .filter((post) => + searchValue + ? post.tags.find((tag) => tag.content === searchValue) + : post + ) + .map((post) => ( + + ))}
+ {getCookie("access_token") ? ( +
+ + Post + +
+ ) : null}
); }; diff --git a/src/routes/PostCreatePage.jsx b/src/routes/PostCreatePage.jsx index b92e1f9..52e05bd 100644 --- a/src/routes/PostCreatePage.jsx +++ b/src/routes/PostCreatePage.jsx @@ -1,66 +1,46 @@ import { useEffect, useState } from "react"; -import { BigPost } from "../components/Posts"; -import posts from "../data/posts"; import { PostForm } from "../components/Form"; +import { useNavigate } from "react-router-dom"; +import { createPost, getTags } from "../apis/api"; const PostCreatePage = () => { - const [isSubmitted, setIsSubmitted] = useState(false); const [formData, setFormData] = useState({ - id: posts.length, title: "", content: "", - author: { id: posts.length, username: "와기" }, tags: [], }); const [tags, setTags] = useState([]); 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]); + const getTagsAPI = async () => { + const tags = await getTags(); + const tagContents = tags.map((tag) => { + return tag.content; + }); + setTags(tagContents); + }; + getTagsAPI(); }, []); + const navigate = useNavigate(); + const onSubmit = (e) => { - //TODO : api connect e.preventDefault(); - const createdPost = { - ...formData, - like_users: [], - tags: formData.tags.map((tag, idx) => { - return { id: idx + 1, content: tag }; - }), - }; - setFormData(createdPost); - setIsSubmitted(true); + createPost(formData, navigate); + window.location.href="/"; }; return ( - <> - {isSubmitted ? ( -
- -
- ) : ( -
-

New Post

- -
- )} - +
+

New Post

+ +
); }; -export default PostCreatePage; +export default PostCreatePage; \ No newline at end of file diff --git a/src/routes/PostDetailPage.jsx b/src/routes/PostDetailPage.jsx index 3bf6111..f170352 100644 --- a/src/routes/PostDetailPage.jsx +++ b/src/routes/PostDetailPage.jsx @@ -1,50 +1,71 @@ import { useEffect, useState } from "react"; -import { useParams } from "react-router-dom"; +import { useNavigate, useParams } from "react-router-dom"; +import Comment from "../components/Comments"; import { BigPost } from "../components/Posts"; -import Comment from "../components/Comments" import { Link } from "react-router-dom"; -import posts from "../data/posts"; -import comments from "../data/comment"; +import { deletePost, getPost, getUser } from "../apis/api"; +import { getCookie } from "../utils/cookie"; const PostDetailPage = () => { -// parameter로 받은 id에 해당하는 post를 찾아서 넣자 -// TODO : api call(get post by id) const { postId } = useParams(); const [post, setPost] = useState(); + const [user, setUser] = useState(); + + console.log(user); + useEffect(() => { - const post = posts.find((post) => post.id === parseInt(postId)); - setPost(post); + const getPostAPI = async () => { + const post = await getPost(postId); + setPost(post); + }; + getPostAPI(); }, [postId]); + useEffect(() => { + // access_token이 있으면 유저 정보 가져옴 + if (getCookie("access_token")) { + const getUserAPI = async () => { + const user = await getUser(); + setUser(user); + }; + getUserAPI(); + } + }, []); -const onClickDelete = () => { - console.log("delete"); - // add api call for deleting post here - // add redirect to home page - }; -const [comment, setComment] = useState(); -useEffect(() => { - const comment = comments.find((comment) => comment.post === parseInt(postId)); - setComment(comment); -}, [postId]); + const navigate = useNavigate(); + + const onClickDelete = () => { + deletePost(post.id, navigate); + }; return ( post && (
{/* post detail component */} - -
- - - - -
+ {/* comments component */} + + +
+ {user?.data.id === post?.author.id ? ( + <> + + + + + + ) : null} +
) ); }; -export default PostDetailPage; \ No newline at end of file +export default PostDetailPage; diff --git a/src/routes/PostEditPage.jsx b/src/routes/PostEditPage.jsx index aec8ded..8c214b9 100644 --- a/src/routes/PostEditPage.jsx +++ b/src/routes/PostEditPage.jsx @@ -1,74 +1,65 @@ import { useEffect, useState } from "react"; -import { BigPost } from "../components/Posts"; -import posts from "../data/posts"; +import { useNavigate, useParams } from "react-router-dom"; import { PostForm } from "../components/Form"; -import { useParams } from "react-router-dom"; +import posts from "../data/posts"; import { Link } from "react-router-dom"; +import { BigPost } from "../components/Posts"; +import { getPost, getTags, updatePost } from "../apis/api"; const PostEditPage = () => { - const [isSubmitted, setIsSubmitted] = useState(false); - const { postId } = useParams(); - const [formData, setFormData] = useState({}); - useEffect(() => { - const post = posts.find((post) => post.id === parseInt(postId)); - const postFormData = { ...post, tags: post.tags.map((tag) => tag.content) }; - setFormData(postFormData); - }, [postId]); + const { postId } = useParams(); + const [formData, setFormData] = useState({ + title: "", + content: "", + tags: [], + }); + const navigate = useNavigate(); + useEffect(() => { + const getPostAPI = async () => { + const post = await getPost(postId); + const postFormData = { + ...post, + tags: post.tags.map((tag) => tag.content), + }; + setFormData(postFormData); + }; + getPostAPI(); + }, [postId]); const [tags, setTags] = useState([]); 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]); + const getTagsAPI = async () => { + const tags = await getTags(); + const tagContents = tags.map((tag) => { + return tag.content; + }); + setTags(tagContents); + }; + getTagsAPI(); }, []); const onSubmit = (e) => { - //TODO : api connect e.preventDefault(); - const createdPost = { - ...formData, - like_users: [], - tags: formData.tags.map((tag, idx) => { - return { id: idx + 1, content: tag }; - }), - }; - setFormData(createdPost); - setIsSubmitted(true); + updatePost(postId, formData, navigate); }; return ( - <> - {isSubmitted ? ( -
- -
- ) : ( -
-
- - - -

Edit Post

-
- -
- )} - +
+
+ + + +

Edit Post

+
+ +
); }; -export default PostEditPage; +export default PostEditPage; \ No newline at end of file diff --git a/src/routes/SignInPage.jsx b/src/routes/SignInPage.jsx index 3cd17c3..9632b35 100644 --- a/src/routes/SignInPage.jsx +++ b/src/routes/SignInPage.jsx @@ -1,5 +1,6 @@ import { useState } from "react"; import { SignInForm } from "../components/Form"; +import { signIn } from "../apis/api"; const SignInPage = () => { const [formData, setFormData] = useState({ @@ -7,10 +8,9 @@ const SignInPage = () => { password: "", }); - const handleSignInSubmit = () => { - console.log(formData); - alert("로그인 완-료!"); - // add api call for sign in here + const handleSignInSubmit = (e) => { + e.preventDefault(); + signIn(formData); }; return ( diff --git a/src/routes/SignUpPage.jsx b/src/routes/SignUpPage.jsx index 92b990b..5303e52 100644 --- a/src/routes/SignUpPage.jsx +++ b/src/routes/SignUpPage.jsx @@ -1,5 +1,8 @@ import { useState } from "react"; import { SignUpForm } from "../components/Form"; +import axios from "axios"; +import { getCookie } from "../utils/cookie"; +import { signUp } from "../apis/api"; const SignUpPage = () => { const [formData, setFormData] = useState({ @@ -11,20 +14,20 @@ const SignUpPage = () => { major: "", }); - const handleSignUpSubmit = () => { // API 호출하는 함수 - console.log(formData); - alert(`${formData.email}로 회원가입 해 줘`); - // add api call for sign up here + const handleSignUpSubmit = async (e) => { + // API 호출하는 함수 + e.preventDefault(); // form 보낼 때 새로고침 안 되도록 해주세용 + signUp(formData); }; return (

Sign Up

- +
); }; diff --git a/src/utils/cookie.js b/src/utils/cookie.js new file mode 100644 index 0000000..9be8dea --- /dev/null +++ b/src/utils/cookie.js @@ -0,0 +1,22 @@ +// src/utils/cookie.js + +import { Cookies } from "react-cookie"; + +const cookies = new Cookies(); + +// 쿠키 설정하는 함수 +// 궁금하실까봐 만들긴 했는데, 우리는 안 쓸거에요!! (쿠키에 토큰 넣어주는 건 서버에서 해주니까요) +export const setCookie = (name, value, option) => { + return cookies.set(name, value, { ...option }); +}; + +// 쿠키 정보 가져오는 함수 +export const getCookie = (name) => { + const cookieValue = cookies.get(name); + return cookies.get(name); +}; + +// 쿠키 정보 삭제하는 함수 +export const removeCookie = (name) => { + cookies.remove(name); +}; From 53a062803aca6ffb29a87f085614050f037b9365 Mon Sep 17 00:00:00 2001 From: CZ Date: Fri, 2 Jun 2023 05:19:42 +0900 Subject: [PATCH 3/3] week 11 task --- src/App.js | 3 + src/apis/api.js | 50 +++++- src/apis/axios.js | 16 +- src/components/Header/index.jsx | 11 +- src/index.css | 8 + src/routes/MyInfoPage.jsx | 275 ++++++++++++++++++++++++++++++++ 6 files changed, 353 insertions(+), 10 deletions(-) create mode 100644 src/routes/MyInfoPage.jsx diff --git a/src/App.js b/src/App.js index c1f77e7..785bd66 100644 --- a/src/App.js +++ b/src/App.js @@ -9,6 +9,7 @@ import PostEditPage from "./routes/PostEditPage"; import SignUpPage from "./routes/SignUpPage"; import PostDetailPage from "./routes/PostDetailPage"; import SignInPage from "./routes/SignInPage"; +import MyInfoPage from "./routes/MyInfoPage" function App() { @@ -29,6 +30,8 @@ function App() { } /> {/* sign up */} } /> + {/* my info */} + } />