From 43aee9e5b7a5b6dab2b3d1fd15e94d086d76be01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9E=A5=EC=84=B8=EC=A0=95?= Date: Sun, 9 Jun 2024 16:50:29 +0900 Subject: [PATCH] =?UTF-8?q?[refactor]=20=EC=BB=A4=EB=AE=A4=EB=8B=88?= =?UTF-8?q?=ED=8B=B0=20css=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- front-end/package-lock.json | 115 +++++ front-end/package.json | 1 + .../components/community/CommentListItem.jsx | 165 ++++--- .../src/pages/community/detail/index.jsx | 437 ++++++++++-------- 4 files changed, 452 insertions(+), 266 deletions(-) diff --git a/front-end/package-lock.json b/front-end/package-lock.json index d63fd61f..bfa7655a 100644 --- a/front-end/package-lock.json +++ b/front-end/package-lock.json @@ -29,6 +29,7 @@ "react-image-crop": "^11.0.5", "react-intersection-observer": "^9.5.3", "react-modal": "^3.16.1", + "react-quill": "^2.0.0", "react-redux": "^8.1.3", "react-router-dom": "^6.21.3", "react-scripts": "5.0.1", @@ -5062,6 +5063,14 @@ "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.11.tgz", "integrity": "sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ==" }, + "node_modules/@types/quill": { + "version": "1.3.10", + "resolved": "https://registry.npmjs.org/@types/quill/-/quill-1.3.10.tgz", + "integrity": "sha512-IhW3fPW+bkt9MLNlycw8u8fWb7oO7W5URC9MfZYHBlA24rex9rs23D5DETChu1zvgVdc5ka64ICjJOgQMr6Shw==", + "dependencies": { + "parchment": "^1.1.2" + } + }, "node_modules/@types/range-parser": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", @@ -6879,6 +6888,14 @@ "wrap-ansi": "^7.0.0" } }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "engines": { + "node": ">=0.8" + } + }, "node_modules/clsx": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz", @@ -9236,11 +9253,21 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "node_modules/fast-diff": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz", + "integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==" + }, "node_modules/fast-equals": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.0.1.tgz", @@ -15729,6 +15756,11 @@ "tslib": "^2.0.3" } }, + "node_modules/parchment": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/parchment/-/parchment-1.1.4.tgz", + "integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg==" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -17433,6 +17465,75 @@ } ] }, + "node_modules/quill": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/quill/-/quill-1.3.7.tgz", + "integrity": "sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g==", + "dependencies": { + "clone": "^2.1.1", + "deep-equal": "^1.0.1", + "eventemitter3": "^2.0.3", + "extend": "^3.0.2", + "parchment": "^1.1.4", + "quill-delta": "^3.6.2" + } + }, + "node_modules/quill-delta": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-3.6.3.tgz", + "integrity": "sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==", + "dependencies": { + "deep-equal": "^1.0.1", + "extend": "^3.0.2", + "fast-diff": "1.1.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/quill-delta/node_modules/deep-equal": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.2.tgz", + "integrity": "sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==", + "dependencies": { + "is-arguments": "^1.1.1", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.5.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/quill/node_modules/deep-equal": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.2.tgz", + "integrity": "sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==", + "dependencies": { + "is-arguments": "^1.1.1", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.5.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/quill/node_modules/eventemitter3": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz", + "integrity": "sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg==" + }, "node_modules/raf": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", @@ -17695,6 +17796,20 @@ "react-dom": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18" } }, + "node_modules/react-quill": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/react-quill/-/react-quill-2.0.0.tgz", + "integrity": "sha512-4qQtv1FtCfLgoD3PXAur5RyxuUbPXQGOHgTlFie3jtxp43mXDtzCKaOgQ3mLyZfi1PUlyjycfivKelFhy13QUg==", + "dependencies": { + "@types/quill": "^1.3.10", + "lodash": "^4.17.4", + "quill": "^1.3.7" + }, + "peerDependencies": { + "react": "^16 || ^17 || ^18", + "react-dom": "^16 || ^17 || ^18" + } + }, "node_modules/react-redux": { "version": "8.1.3", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.3.tgz", diff --git a/front-end/package.json b/front-end/package.json index 4f91dcff..8ee25812 100644 --- a/front-end/package.json +++ b/front-end/package.json @@ -24,6 +24,7 @@ "react-image-crop": "^11.0.5", "react-intersection-observer": "^9.5.3", "react-modal": "^3.16.1", + "react-quill": "^2.0.0", "react-redux": "^8.1.3", "react-router-dom": "^6.21.3", "react-scripts": "5.0.1", diff --git a/front-end/src/components/community/CommentListItem.jsx b/front-end/src/components/community/CommentListItem.jsx index ae1bf9f0..70e08f68 100644 --- a/front-end/src/components/community/CommentListItem.jsx +++ b/front-end/src/components/community/CommentListItem.jsx @@ -8,100 +8,121 @@ import swal from 'sweetalert'; export default function CommentListItem(props) { // 코드에 아무것도 없는 null은 객체라서 오류나니깐 .. - const [code,setCode] = useState(props.commentItem.code || '') + const [code, setCode] = useState(props.commentItem.code || '') const userId = useSelector(state => state.auth.userId) const commentId = props.commentItem.writerId // 댓글작성자와 로그인한유저 검사하려고 const commentNo = props.commentItem.commentId // 댓글삭제하기위해 댓글commentId가져옴 - const [comment,setComment] = useState(props.commentItem.comment) + const [comment, setComment] = useState(props.commentItem.comment) const articleNo = props.commentItem.articleNo const navigate = useNavigate() + const [bgcolor, setBgcolor] = useState('#F4ECE4'); + const [showCodeMirror, setShowCodeMirror] = useState(false) - const commentDelete = () =>{ + + const colorChange = () => { + if (bgcolor === '#F4ECE4') { + setBgcolor('#ffffff') + setShowCodeMirror(true) + } else { + setBgcolor('#F4ECE4') + setShowCodeMirror(false) + } + } + + const commentDelete = () => { axios({ - url : `https://codearena.shop/api/comment/delete?commentId=${commentNo}`, - method : 'delete' - }) - .then((res)=>{ - console.log(res) - props.onDelete(commentNo) - swal("댓글삭제완료","","success") - }) - .catch((err)=>{ - console.log(err) + url: `https://codearena.shop/api/comment/delete?commentId=${commentNo}`, + method: 'delete' }) + .then((res) => { + console.log(res) + props.onDelete(commentNo) + swal("댓글삭제완료", "", "success") + }) + .catch((err) => { + console.log(err) + }) } const onChangeCode = useCallback((code, viewUpdate) => { setCode(code); }, []); - - const updateComment = () =>{ + + const updateComment = () => { axios({ - url : 'https://codearena.shop/api/comment/update', - method : 'put', - data : { - commentId : commentNo, - comment : comment, - code : code, + url: 'https://codearena.shop/api/comment/update', + method: 'put', + data: { + commentId: commentNo, + comment: comment, + code: code, } }) - .then((res)=>{ - console.log(res) - swal("댓글수정완료","","success") - navigate(`/community/${articleNo}/detail`) - }) - .catch((err)=>{ - console.log(err) - }) + .then((res) => { + console.log(res) + swal("댓글수정완료", "", "success") + navigate(`/community/${articleNo}/detail`) + }) + .catch((err) => { + console.log(err) + }) } - - return( + return (
-
-
-
-
작성자 : {props.commentItem.writerNickname}
-
-
-
- -
-
- {code && ( -
-
-
- +
+
+
{props.commentItem.writerNickname}
-
-
- )} - { userId === commentId && ( -
-
수정
- -
- +
+
- )} -
-
+ {code && ( +
+
+ +
+
+ )} + {showCodeMirror && ( +
+
+ +
+
+ )} + {userId === commentId && ( +
+
+ 수정 +
+ +
+ )} +
+
- + ) } diff --git a/front-end/src/pages/community/detail/index.jsx b/front-end/src/pages/community/detail/index.jsx index 2308be20..6a563a8b 100644 --- a/front-end/src/pages/community/detail/index.jsx +++ b/front-end/src/pages/community/detail/index.jsx @@ -1,5 +1,5 @@ import { useNavigate, useParams } from "react-router-dom" -import { useState,useEffect,useCallback,useRef} from "react" +import { useState, useEffect, useCallback, useRef } from "react" import axios from "axios" import "../../css/problemdetail.css" import Editor from "@monaco-editor/react" @@ -11,128 +11,140 @@ import Pencil from '../../../images/common/pencil.png' -export default function CommunityDetail(){ +export default function CommunityDetail() { const monaco = useMonaco() const accessToken = useSelector(state => state.access.accessToken) - const [bgcolor,setBgcolor] = useState('#F4F2CA'); - const [showCodeMirror, setShowCodeMirror] = useState(true) + const [bgcolor, setBgcolor] = useState('#F4F2CA'); + const [showCodeMirror, setShowCodeMirror] = useState(false) + const [bgcolor2, setBgcolor2] = useState('#F4F2CA'); + const [showCodeMirrorComment, setShowCodeMirrorComment] = useState(false) const params = useParams(); const boardId = params.communityId const userId = useSelector(state => state.auth.userId) - const [articleId,setArticleId] = useState('') - const [board,setBoard] = useState({}) + const [articleId, setArticleId] = useState('') + const [board, setBoard] = useState({}) const navigate = useNavigate() - const [cate,setCate] = useState('') - const catedic = {'1':'질문','2':'시간복잡도','3':'공간복잡도','4':'반례 요청','5':'반례',} - const [comment,setComment] = useState('') - const [commentcode,setCommentCode] = useState('') - const [commentlist,setCommentList] = useState([]) - const [articleNickname,setArticleNickname] = useState('') + const [cate, setCate] = useState('') + const catedic = { '1': '질문', '2': '시간복잡도', '3': '공간복잡도', '4': '반례 요청', '5': '반례', } + const [comment, setComment] = useState('') + const [commentcode, setCommentCode] = useState('') + const [commentlist, setCommentList] = useState([]) + const [articleNickname, setArticleNickname] = useState('') - const colorChange = () =>{ - if (bgcolor === '#F4F2CA'){ + const colorChange = () => { + if (bgcolor === '#F4F2CA') { setBgcolor('#ffffff') setShowCodeMirror(true) - }else { + } else { setBgcolor('#F4F2CA') setShowCodeMirror(false) } } + const colorChange2 = () => { + if (bgcolor2 === '#F4F2CA') { + setBgcolor2('#ffffff') + setShowCodeMirrorComment(true) + } else { + setBgcolor2('#F4F2CA') + setShowCodeMirrorComment(false) + } + } + // 게시글상세조회 - useEffect(()=>{ + useEffect(() => { axios({ - url : `https://codearena.shop/api/board/detail/${boardId}`, - method : 'get', - headers : { - Authorization : accessToken + url: `https://codearena.shop/api/board/detail/${boardId}`, + method: 'get', + headers: { + Authorization: accessToken } }) - .then((res)=>{ - console.log(res.data.data) - console.log(res.data.data.spoiler) - console.log(res.data.data.isSolved) - - if (res.data.data.spoiler === 1) { - setShowCodeMirror(false) - if (res.data.data.isSolved === '1') { - setShowCodeMirror(true) - } else { + .then((res) => { + console.log(res.data.data) + console.log(res.data.data.spoiler) + console.log(res.data.data.isSolved) + + if (res.data.data.spoiler === 1) { setShowCodeMirror(false) + if (res.data.data.isSolved === '1') { + setShowCodeMirror(true) + } else { + setShowCodeMirror(false) + } } - } - - setBoard(res.data.data) - setCate(catedic[res.data.data.type]) - setArticleId(res.data.data.userId) - setArticleNickname(res.data.data.nickName) - }) - .catch((err)=>{ - console.log(err) - }) - - },[]) + + setBoard(res.data.data) + setCate(catedic[res.data.data.type]) + setArticleId(res.data.data.userId) + setArticleNickname(res.data.data.nickName) + }) + .catch((err) => { + console.log(err) + }) + + }, []) // 게시글삭제 - const boardDelete = ()=>{ + const boardDelete = () => { axios({ - url : `https://codearena.shop/api/board/delete/${boardId}`, - method : 'delete' - }) - .then((res)=>{ - console.log(res) - swal("게시글이 삭제완료","","success") - navigate('/community') - }) - .catch((err)=>{ - console.log(err) + url: `https://codearena.shop/api/board/delete/${boardId}`, + method: 'delete' }) + .then((res) => { + console.log(res) + swal("게시글이 삭제완료", "", "success") + navigate('/community') + }) + .catch((err) => { + console.log(err) + }) } //댓글작성 (댓글작성 후 새로고침되야함) - const createComment = () =>{ + const createComment = () => { axios({ - url : 'https://codearena.shop/api/comment/write', - method : 'post', - data : { - articleNo : boardId, - userId : userId, - comment : comment, - code : commentcode + url: 'https://codearena.shop/api/comment/write', + method: 'post', + data: { + articleNo: boardId, + userId: userId, + comment: comment, + code: commentcode } }) - .then((res)=>{ - console.log(res) - window.location.reload(); - }) - .catch((err)=>{ - console.log(err) - }) + .then((res) => { + console.log(res) + window.location.reload(); + }) + .catch((err) => { + console.log(err) + }) } //댓글 목록조회 - useEffect(()=>{ + useEffect(() => { axios({ - method : 'get', - url : `https://codearena.shop/api/comment/list?articleNo=${boardId}`, - }) - .then((res)=> { - console.log(res) - // console.log(res.data.data); - setCommentList(res.data.data) + method: 'get', + url: `https://codearena.shop/api/comment/list?articleNo=${boardId}`, }) - .catch((err)=> { - console.log(err); - }) - },[]) - + .then((res) => { + console.log(res) + // console.log(res.data.data); + setCommentList(res.data.data) + }) + .catch((err) => { + console.log(err); + }) + }, []) + const onDelete = (id) => { - const tmpArr = commentlist.filter((el)=>el.commentId !== id) + const tmpArr = commentlist.filter((el) => el.commentId !== id) setCommentList(tmpArr) // tmpArr에는 각요소들의 commentId와 id가 다른 요소들로만 이루어진 배열이됨 } - const goEdit = () =>{ + const goEdit = () => { navigate((`/community/${boardId}/edit`)); window.scrollTo(0, 0); } @@ -140,11 +152,11 @@ export default function CommunityDetail(){ const onChangeCode = useCallback((code, viewUpdate) => { setCommentCode(code); }, []); - - const goUp = () =>{ + + const goUp = () => { window.scrollTo({ - top : 0, - behavior : "smooth" + top: 0, + behavior: "smooth" }) } @@ -158,148 +170,185 @@ export default function CommunityDetail(){ }); } }, [monaco, bgcolor]); // monaco와 bgcolor가 변경될 때마다 테마를 다시 설정합니다. - + return (
- -

맨위로

-
- -
-
- + +

+ 맨위로 +

+
+ -
- -
+ + {/* 제목 */} +
+
+ + {/* 제목 input */} +
+ +
+
-
-
- - +
+
+ +
- - + +
- - + +
-
-
- -
-
+ {/* 내용 */} +
+ +
- -
-
-
-
- - - - {/* 댓글 모달창 */} - -
-
- -
+
+ + + {/* 댓글 모달창 */} + +
+
+ +
+
+ +
+ {showCodeMirrorComment && (
-
-
-
-
-
-
-
- -
-
+ >댓글작성
+ +
+ +
- -
-
+
+
- { showCodeMirror && ( -
-
-
- +
+
-
)} - { articleId === userId &&( -
- - -
-
+ {articleId === userId && ( +
+ + +
)} -
- {/* 댓글 */} - +
+ {/* 댓글 */} + {commentlist.length > 0 && ( - commentlist.map((comment,index)=>{ - return( - - )}) + commentlist.map((comment, index) => { + return ( + + ) + }) )} {commentlist.length === 0 && ( -
+

댓글이 없습니다

@@ -307,7 +356,7 @@ export default function CommunityDetail(){
) - + }