From 5d9790731186fd8ed0a82be3b033b2123ba411ac Mon Sep 17 00:00:00 2001 From: darkdulgi Date: Tue, 13 Aug 2024 22:14:55 +0900 Subject: [PATCH 1/4] =?UTF-8?q?[file]=20=EA=B8=B0=EB=8C=80=ED=8F=89=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/adminPage/features/comment/index.jsx | 72 ++++++++++++++++++++++++ src/adminPage/pages/CommentsPage.jsx | 69 +---------------------- 2 files changed, 74 insertions(+), 67 deletions(-) create mode 100644 src/adminPage/features/comment/index.jsx diff --git a/src/adminPage/features/comment/index.jsx b/src/adminPage/features/comment/index.jsx new file mode 100644 index 00000000..5fa28125 --- /dev/null +++ b/src/adminPage/features/comment/index.jsx @@ -0,0 +1,72 @@ +import { useState } from "react"; + +export default function AdminComment() { + const [formString, setFormString] = useState(""); + const [isSpread, setIsSpread] = useState(false); + const searchList = [ + { + id: "HD_2409_01", + name: "이벤트", + }, + { + id: "HD_2409_02", + name: "이벤트", + }, + { + id: "HD_2409_03", + name: "이벤트", + }, + ]; + + function onChangeForm(e) { + const inputString = e.target.value; + const numberRegex = /^\d*$/; + + if (inputString.length <= 6 && numberRegex.test(inputString)) { + setFormString(inputString); + } + } + + function searchComment(e, eventId) { + e.preventDefault(); + + if (eventId) console.log(eventId + " 검색"); + else console.log(formString + " 검색"); + } + + return ( +
+ setIsSpread(true)} + onBlur={() => setIsSpread(false)} + value={formString} + placeholder="ID (숫자 6자리)" + className={`outline outline-1 outline-neutral-500 px-4 py-2 w-full ${isSpread ? "rounded-t-md" : "rounded-md"}`} + /> + +
+ {searchList.map((evt) => ( +
  • + {evt.id} + {evt.name} +
  • + ))} +
    + + 검색 +
    + ); +} diff --git a/src/adminPage/pages/CommentsPage.jsx b/src/adminPage/pages/CommentsPage.jsx index ecb4dd3b..f9056717 100644 --- a/src/adminPage/pages/CommentsPage.jsx +++ b/src/adminPage/pages/CommentsPage.jsx @@ -1,78 +1,13 @@ import Container from "@admin/components/Container.jsx"; -import { useState } from "react"; +import AdminComment from "../features/comment"; export default function CommentsPage() { - const [formString, setFormString] = useState(""); - const [isSpread, setIsSpread] = useState(false); - const searchList = [ - { - id: "HD_2409_01", - name: "이벤트", - }, - { - id: "HD_2409_02", - name: "이벤트", - }, - { - id: "HD_2409_03", - name: "이벤트", - }, - ]; - - function onChangeForm(e) { - const inputString = e.target.value; - const numberRegex = /^\d*$/; - - if (inputString.length <= 6 && numberRegex.test(inputString)) { - setFormString(inputString); - } - } - - function searchComment(e, eventId) { - e.preventDefault(); - - if (eventId) console.log(eventId + " 검색"); - else console.log(formString + " 검색"); - } - return (
    기대평 -
    - setIsSpread(true)} - onBlur={() => setIsSpread(false)} - value={formString} - placeholder="ID (숫자 6자리)" - className={`outline outline-1 outline-neutral-500 px-4 py-2 w-full ${isSpread ? "rounded-t-md" : "rounded-md"}`} - /> - -
    - {searchList.map((evt) => ( -
  • - {evt.id} - {evt.name} -
  • - ))} -
    - - 검색 -
    +
    ); From c5a9c7a5195bc28becdaa79e2f1546ce5ed4bc37 Mon Sep 17 00:00:00 2001 From: darkdulgi Date: Wed, 14 Aug 2024 10:44:07 +0900 Subject: [PATCH 2/4] =?UTF-8?q?[feat]=20=EC=9D=B8=ED=92=8B=EC=B0=BD=20?= =?UTF-8?q?=ED=85=8D=EC=8A=A4=ED=8A=B8=20=ED=98=95=EC=8B=9D=20=EC=9E=90?= =?UTF-8?q?=EB=8F=99=EC=99=84=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/adminPage/features/comment/index.jsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/adminPage/features/comment/index.jsx b/src/adminPage/features/comment/index.jsx index 5fa28125..e1473d6d 100644 --- a/src/adminPage/features/comment/index.jsx +++ b/src/adminPage/features/comment/index.jsx @@ -19,11 +19,16 @@ export default function AdminComment() { ]; function onChangeForm(e) { - const inputString = e.target.value; - const numberRegex = /^\d*$/; + const filteredString = e.target.value.replace(/[^0-9]/g, ""); - if (inputString.length <= 6 && numberRegex.test(inputString)) { - setFormString(inputString); + if (!filteredString) { + setFormString(""); + } else if (filteredString.length <= 4) { + setFormString("HD_" + filteredString); + } else if (filteredString.length <= 6) { + setFormString( + "HD_" + filteredString.slice(0, 4) + "_" + filteredString.slice(4), + ); } } @@ -53,6 +58,7 @@ export default function AdminComment() { {searchList.map((evt) => (
  • setFormString(evt.id)} className="list-none w-full hover:bg-blue-200 rounded px-1 flex" > {evt.id} From 17ff0b29b5cdad0d2fcb198fd0b609c80bba28b1 Mon Sep 17 00:00:00 2001 From: darkdulgi Date: Wed, 14 Aug 2024 12:28:03 +0900 Subject: [PATCH 3/4] =?UTF-8?q?[dev]=20mock=20api=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/adminPage/features/comment/index.jsx | 75 +++++++++++++++--------- src/adminPage/features/comment/mock.js | 40 +++++++++++++ src/adminPage/mock.js | 3 +- src/adminPage/pages/CommentsPage.jsx | 2 +- 4 files changed, 91 insertions(+), 29 deletions(-) create mode 100644 src/adminPage/features/comment/mock.js diff --git a/src/adminPage/features/comment/index.jsx b/src/adminPage/features/comment/index.jsx index e1473d6d..315f0599 100644 --- a/src/adminPage/features/comment/index.jsx +++ b/src/adminPage/features/comment/index.jsx @@ -1,42 +1,57 @@ import { useState } from "react"; +import { fetchServer } from "@common/dataFetch/fetchServer"; export default function AdminComment() { const [formString, setFormString] = useState(""); const [isSpread, setIsSpread] = useState(false); - const searchList = [ - { - id: "HD_2409_01", - name: "이벤트", - }, - { - id: "HD_2409_02", - name: "이벤트", - }, - { - id: "HD_2409_03", - name: "이벤트", - }, - ]; + const [searchList, setSearchList] = useState([]); + const [selectedIndex, setSelectedIndex] = useState(-1); + + function autoCorrect() { + fetchServer(`/api/v1/admin/events/hints?search=${formString}`) + .then((res) => { + setSearchList(res); + setIsSpread(true); + }) + .catch((e) => { + console.log(e); + }); + } function onChangeForm(e) { const filteredString = e.target.value.replace(/[^0-9]/g, ""); if (!filteredString) { setFormString(""); - } else if (filteredString.length <= 4) { - setFormString("HD_" + filteredString); } else if (filteredString.length <= 6) { + setFormString("HD_" + filteredString); + } else if (filteredString.length <= 9) { setFormString( - "HD_" + filteredString.slice(0, 4) + "_" + filteredString.slice(4), + "HD_" + filteredString.slice(0, 6) + "_" + filteredString.slice(6), ); + } else return; + + if (filteredString.length >= 6) { + autoCorrect(); + } else setIsSpread(false); + } + + function onKeyDownForm(e) { + if (!isSpread) return; + switch (e.key) { + case "ArrowUp": + setSelectedIndex((idx) => idx - 1); + break; + case "ArrowDown": + setSelectedIndex((idx) => idx + 1); + break; } } - function searchComment(e, eventId) { + function searchComment(e) { e.preventDefault(); - if (eventId) console.log(eventId + " 검색"); - else console.log(formString + " 검색"); + console.log(formString + " 검색"); } return ( @@ -44,27 +59,33 @@ export default function AdminComment() { setIsSpread(true)} - onBlur={() => setIsSpread(false)} value={formString} - placeholder="ID (숫자 6자리)" - className={`outline outline-1 outline-neutral-500 px-4 py-2 w-full ${isSpread ? "rounded-t-md" : "rounded-md"}`} + placeholder="ID (숫자 9자리)" + className={`z-10 outline outline-1 outline-neutral-500 px-4 py-2 w-full ${isSpread ? "rounded-t-md" : "rounded-md"}`} />
    - {searchList.map((evt) => ( + {searchList.map((evt, index) => (
  • setSelectedIndex(index)} onMouseDown={() => setFormString(evt.id)} - className="list-none w-full hover:bg-blue-200 rounded px-1 flex" + className={`list-none w-full rounded px-1 flex ${selectedIndex === index && "bg-blue-200"}`} > {evt.id} {evt.name}
  • ))} + + + 일치하는 검색 결과가 없습니다. + + HttpResponse.json(sampleEventList), + ), +]; + +export default handlers; diff --git a/src/adminPage/mock.js b/src/adminPage/mock.js index 4f1446f8..f68decce 100644 --- a/src/adminPage/mock.js +++ b/src/adminPage/mock.js @@ -1,7 +1,8 @@ import { setupWorker } from "msw/browser"; import authHandler from "@admin/auth/mock.js"; +import commentHandler from "./features/comment/mock.js"; // mocking은 기본적으로 각 feature 폴더 내의 mock.js로 정의합니다. // 새로운 feature의 mocking을 추가하셨으면, mock.js의 setupWorker 내부 함수에 인자를 spread 연산자를 이용해 추가해주세요. // 예시 : export default setupWorker(...authHandler, ...questionHandler, ...articleHandler); -export default setupWorker(...authHandler); +export default setupWorker(...authHandler, ...commentHandler); diff --git a/src/adminPage/pages/CommentsPage.jsx b/src/adminPage/pages/CommentsPage.jsx index f9056717..4f78a440 100644 --- a/src/adminPage/pages/CommentsPage.jsx +++ b/src/adminPage/pages/CommentsPage.jsx @@ -4,7 +4,7 @@ import AdminComment from "../features/comment"; export default function CommentsPage() { return ( -
    +
    기대평 From 710eb4e693f9ea73896083f5f3458bc9aa81a950 Mon Sep 17 00:00:00 2001 From: darkdulgi Date: Wed, 14 Aug 2024 18:49:30 +0900 Subject: [PATCH 4/4] =?UTF-8?q?[feat]=20=EC=96=B4=EB=93=9C=EB=AF=BC=20?= =?UTF-8?q?=EA=B8=B0=EB=8C=80=ED=8F=89=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EC=9D=BC=EB=B6=80=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/adminPage/App.jsx | 3 +- .../features/comment/id/Comments.jsx | 50 +++++++++++ src/adminPage/features/comment/id/Loading.jsx | 9 ++ src/adminPage/features/comment/id/index.jsx | 73 ++++++++++++++++ src/adminPage/features/comment/index.jsx | 46 +++++----- src/adminPage/features/comment/mock.js | 84 ++++++++++++------- src/adminPage/pages/CommentsIDPage.jsx | 17 ++++ src/adminPage/pages/CommentsPage.jsx | 2 +- 8 files changed, 224 insertions(+), 60 deletions(-) create mode 100644 src/adminPage/features/comment/id/Comments.jsx create mode 100644 src/adminPage/features/comment/id/Loading.jsx create mode 100644 src/adminPage/features/comment/id/index.jsx create mode 100644 src/adminPage/pages/CommentsIDPage.jsx diff --git a/src/adminPage/App.jsx b/src/adminPage/App.jsx index bc6e5e1d..1ca2d56e 100644 --- a/src/adminPage/App.jsx +++ b/src/adminPage/App.jsx @@ -5,6 +5,7 @@ import EventsPage from "./pages/EventsPage.jsx"; import ProtectedRoute from "./pages/ProtectedRoute.jsx"; import RootRoute from "./pages/RootRoute.jsx"; import CommentsPage from "./pages/CommentsPage.jsx"; +import CommentsIDPage from "./pages/CommentsIDPage.jsx"; import { initLoginState, logout } from "@admin/auth/store.js"; import useLogoutMiddleware from "@common/dataFetch/initLogoutMiddleware"; @@ -26,7 +27,7 @@ function App() { /> event 보는 화면
    } /> } /> - 기대평 화면
    } /> + } /> } /> } /> diff --git a/src/adminPage/features/comment/id/Comments.jsx b/src/adminPage/features/comment/id/Comments.jsx new file mode 100644 index 00000000..804b48ca --- /dev/null +++ b/src/adminPage/features/comment/id/Comments.jsx @@ -0,0 +1,50 @@ +import { useQuery } from "@common/dataFetch/getQuery.js"; +import { fetchServer } from "@common/dataFetch/fetchServer.js"; + +export default function Comments({ + eventId, + checkedComments, + setCheckedComments, +}) { + const data = useQuery(eventId, () => + fetchServer( + `/api/v1/admin/comments?eventId=${eventId}&page=${0}&size=15`, + ).then((res) => res.comments), + ); + + function onChangeCheckbox(e, id) { + if (e.target.checked) { + setCheckedComments((oldSet) => new Set([...oldSet, id])); + } else { + setCheckedComments((oldSet) => { + const newSet = new Set(oldSet); + newSet.delete(id); + return newSet; + }); + } + } + + return ( +
    + {data.map((comment) => ( +
    + onChangeCheckbox(e, comment.id)} + className="w-4 h-4 place-self-center" + /> + + + {comment.createdAt} + + + {comment.content} +
    + ))} +
    + ); +} diff --git a/src/adminPage/features/comment/id/Loading.jsx b/src/adminPage/features/comment/id/Loading.jsx new file mode 100644 index 00000000..05bcd500 --- /dev/null +++ b/src/adminPage/features/comment/id/Loading.jsx @@ -0,0 +1,9 @@ +import Spinner from "@common/components/Spinner"; + +export default function Loading() { + return ( +
    + +
    + ); +} diff --git a/src/adminPage/features/comment/id/index.jsx b/src/adminPage/features/comment/id/index.jsx new file mode 100644 index 00000000..4607a8ab --- /dev/null +++ b/src/adminPage/features/comment/id/index.jsx @@ -0,0 +1,73 @@ +import Suspense from "@common/components/Suspense"; +import Loading from "./Loading.jsx"; +import Comments from "./Comments.jsx"; +import { useState } from "react"; +import { fetchServer } from "@common/dataFetch/fetchServer.js"; + +export default function AdminCommentID({ eventId }) { + const [checkedComments, setCheckedComments] = useState(new Set()); + + function deleteComments() { + const num = checkedComments.size; + if (!num) return; + + fetchServer("/api/v1/admin/comments", { + method: "DELETE", + body: { + commentIds: [...checkedComments], + }, + }) + .then(() => { + alert(num + "개의 기대평 삭제 완료."); + setCheckedComments(new Set()); + }) + .catch((e) => { + console.log(e); + }); + } + + function searchComment(e) { + e.preventDefault(); + console.log(e.target.searchText.value + "검색"); + } + + return ( +
    + + +
    + + + 검색 +
    + +
    + 선택 + 작성 시간 + 기대평 내용 +
    + + }> + + +
    + ); +} diff --git a/src/adminPage/features/comment/index.jsx b/src/adminPage/features/comment/index.jsx index 315f0599..c8282b47 100644 --- a/src/adminPage/features/comment/index.jsx +++ b/src/adminPage/features/comment/index.jsx @@ -1,11 +1,12 @@ import { useState } from "react"; import { fetchServer } from "@common/dataFetch/fetchServer"; +import { useNavigate } from "react-router-dom"; export default function AdminComment() { + const navigate = useNavigate(); const [formString, setFormString] = useState(""); const [isSpread, setIsSpread] = useState(false); const [searchList, setSearchList] = useState([]); - const [selectedIndex, setSelectedIndex] = useState(-1); function autoCorrect() { fetchServer(`/api/v1/admin/events/hints?search=${formString}`) @@ -36,47 +37,38 @@ export default function AdminComment() { } else setIsSpread(false); } - function onKeyDownForm(e) { - if (!isSpread) return; - switch (e.key) { - case "ArrowUp": - setSelectedIndex((idx) => idx - 1); - break; - case "ArrowDown": - setSelectedIndex((idx) => idx + 1); - break; - } - } - - function searchComment(e) { + function searchEvent(e, eventId) { e.preventDefault(); - console.log(formString + " 검색"); + const eventIDRegex = /^HD_\d{6}_\d{3}$/; + const searchID = eventId ? eventId : formString; + + if (eventIDRegex.test(searchID)) { + navigate(`/comments/${searchID}`); + } } return ( -
    +
    - {searchList.map((evt, index) => ( + {searchList.map((evt) => (
  • setSelectedIndex(index)} - onMouseDown={() => setFormString(evt.id)} - className={`list-none w-full rounded px-1 flex ${selectedIndex === index && "bg-blue-200"}`} + key={evt.eventId} + onClick={(e) => searchEvent(e, evt.eventId)} + className={`cursor-pointer list-none w-full rounded px-1 flex hover:bg-blue-200`} > - {evt.id} + {evt.eventId} {evt.name}
  • ))} @@ -89,10 +81,10 @@ export default function AdminComment() {
    검색
    ); diff --git a/src/adminPage/features/comment/mock.js b/src/adminPage/features/comment/mock.js index 167ab4cd..b70955f0 100644 --- a/src/adminPage/features/comment/mock.js +++ b/src/adminPage/features/comment/mock.js @@ -1,40 +1,62 @@ import { http, HttpResponse } from "msw"; -const sampleEventList = [ - { - id: "HD_240909_001", - name: "이벤트1", - }, - { - id: "HD_240909_002", - name: "이벤트2", - }, - { - id: "HD_240909_003", - name: "이벤트3", - }, - { - id: "HD_240909_004", - name: "이벤트4", - }, - { - id: "HD_240909_005", - name: "이벤트5", - }, - { - id: "HD_240909_006", - name: "이벤트6", - }, - { - id: "HD_240909_007", - name: "이벤트7", - }, -]; +function getRandomString(len) { + // const startCode = 0xac00; + // const endCode = 0xd7a3; + + const startCode = 0x0750; + const endCode = 0x077f; + + let str = ""; + for (let i = 0; i < len; i++) { + const randomCode = + Math.floor(Math.random() * (endCode - startCode + 1)) + startCode; + str += String.fromCharCode(randomCode); + } + + return str; +} + +function getSampleEventList() { + const len = 10; + let eventList = []; + for (let i = 0; i < len; i++) { + eventList = [ + ...eventList, + { + eventId: "HD_240909_00" + i, + name: getRandomString(10), + }, + ]; + } + return eventList; +} + +function getSampleCommentList() { + const len = 15; + let commentList = []; + for (let i = 0; i < len; i++) { + commentList = [ + ...commentList, + { + id: i, + content: getRandomString(50), + userName: getRandomString(5), + createdAt: "2024-08-14T07:11:27.244Z", + }, + ]; + } + return { comments: commentList }; +} const handlers = [ http.get("/api/v1/admin/events/hints", () => - HttpResponse.json(sampleEventList), + HttpResponse.json(getSampleEventList()), + ), + http.get("/api/v1/admin/comments", () => + HttpResponse.json(getSampleCommentList()), ), + http.delete("/api/v1/admin/comments", () => HttpResponse.json(true)), ]; export default handlers; diff --git a/src/adminPage/pages/CommentsIDPage.jsx b/src/adminPage/pages/CommentsIDPage.jsx new file mode 100644 index 00000000..b3a9c69d --- /dev/null +++ b/src/adminPage/pages/CommentsIDPage.jsx @@ -0,0 +1,17 @@ +import Container from "@admin/components/Container.jsx"; +import AdminCommentID from "../features/comment/id"; +import { useParams } from "react-router-dom"; + +export default function CommentsPage() { + const { eventId } = useParams(); + + return ( + +
    + 기대평 + + +
    +
    + ); +} diff --git a/src/adminPage/pages/CommentsPage.jsx b/src/adminPage/pages/CommentsPage.jsx index 4f78a440..28f980db 100644 --- a/src/adminPage/pages/CommentsPage.jsx +++ b/src/adminPage/pages/CommentsPage.jsx @@ -5,7 +5,7 @@ export default function CommentsPage() { return (
    - 기대평 + 기대평