-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #97 from softeerbootcamp4th/feature/32-admincomment
[feat] 어드민 기대평 페이지 일부 구현
- Loading branch information
Showing
9 changed files
with
310 additions
and
70 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 ( | ||
<div className="mt-3 flex flex-col gap-1"> | ||
{data.map((comment) => ( | ||
<div | ||
key={comment.id} | ||
className="py-1 grid grid-cols-[1fr_5fr_15fr] bg-neutral-50" | ||
> | ||
<input | ||
type="checkbox" | ||
checked={checkedComments.has(comment.id)} | ||
onChange={(e) => onChangeCheckbox(e, comment.id)} | ||
className="w-4 h-4 place-self-center" | ||
/> | ||
|
||
<span className="text-body-s place-self-center"> | ||
{comment.createdAt} | ||
</span> | ||
|
||
<span className="text-body-s">{comment.content}</span> | ||
</div> | ||
))} | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import Spinner from "@common/components/Spinner"; | ||
|
||
export default function Loading() { | ||
return ( | ||
<div className="flex justify-center items-center w-full h-60 bg-slate-50"> | ||
<Spinner /> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 ( | ||
<div className="flex flex-col w-full"> | ||
<button | ||
onClick={deleteComments} | ||
className="self-end px-5 py-1 bg-red-300 text-white hover:bg-red-500 rounded-lg" | ||
> | ||
삭제 | ||
</button> | ||
|
||
<form onSubmit={searchComment} className="mt-5 w-full relative"> | ||
<input | ||
type="text" | ||
name="searchText" | ||
placeholder="검색 단어 입력" | ||
className="bg-neutral-50 focus:bg-white w-full px-4 py-2 rounded-lg" | ||
/> | ||
|
||
<img | ||
src="/icons/search.png" | ||
alt="검색" | ||
className="absolute top-1/2 -translate-y-1/2 right-4" | ||
/> | ||
</form> | ||
|
||
<div className="mt-3 py-2 grid grid-cols-[1fr_5fr_15fr] bg-blue-50 place-items-center"> | ||
<span>선택</span> | ||
<span>작성 시간</span> | ||
<span>기대평 내용</span> | ||
</div> | ||
|
||
<Suspense fallback={<Loading />}> | ||
<Comments | ||
eventId={eventId} | ||
checkedComments={checkedComments} | ||
setCheckedComments={setCheckedComments} | ||
/> | ||
</Suspense> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
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([]); | ||
|
||
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 <= 6) { | ||
setFormString("HD_" + filteredString); | ||
} else if (filteredString.length <= 9) { | ||
setFormString( | ||
"HD_" + filteredString.slice(0, 6) + "_" + filteredString.slice(6), | ||
); | ||
} else return; | ||
|
||
if (filteredString.length >= 6) { | ||
autoCorrect(); | ||
} else setIsSpread(false); | ||
} | ||
|
||
function searchEvent(e, eventId) { | ||
e.preventDefault(); | ||
|
||
const eventIDRegex = /^HD_\d{6}_\d{3}$/; | ||
const searchID = eventId ? eventId : formString; | ||
|
||
if (eventIDRegex.test(searchID)) { | ||
navigate(`/comments/${searchID}`); | ||
} | ||
} | ||
|
||
return ( | ||
<form onSubmit={searchEvent} className="relative flex"> | ||
<input | ||
type="text" | ||
inputMode="numeric" | ||
onChange={onChangeForm} | ||
value={formString} | ||
placeholder="ID (숫자 9자리)" | ||
className={`z-10 bg-neutral-50 focus:bg-white px-4 py-2 w-full ${isSpread ? "rounded-t-md" : "rounded-md"}`} | ||
/> | ||
|
||
<div | ||
className={`absolute max-h-40 top-full border overflow-y-auto w-full rounded-b-md px-3 py-2 flex flex-col gap-2 ${!isSpread && "hidden"}`} | ||
> | ||
{searchList.map((evt) => ( | ||
<li | ||
key={evt.eventId} | ||
onClick={(e) => searchEvent(e, evt.eventId)} | ||
className={`cursor-pointer list-none w-full rounded px-1 flex hover:bg-blue-200`} | ||
> | ||
<span className="w-40">{evt.eventId}</span> | ||
<span>{evt.name}</span> | ||
</li> | ||
))} | ||
|
||
<span | ||
className={`${searchList.length && "hidden"} text-neutral-300 text-body-s`} | ||
> | ||
일치하는 검색 결과가 없습니다. | ||
</span> | ||
</div> | ||
|
||
<img | ||
onClick={searchEvent} | ||
src="/icons/search.png" | ||
alt="검색" | ||
className="z-10 absolute top-1/2 -translate-y-1/2 right-4 cursor-pointer" | ||
/> | ||
</form> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import { http, HttpResponse } from "msw"; | ||
|
||
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(getSampleEventList()), | ||
), | ||
http.get("/api/v1/admin/comments", () => | ||
HttpResponse.json(getSampleCommentList()), | ||
), | ||
http.delete("/api/v1/admin/comments", () => HttpResponse.json(true)), | ||
]; | ||
|
||
export default handlers; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 ( | ||
<Container> | ||
<div className="flex flex-col w-full p-20"> | ||
<span className="text-title-l pb-10">기대평</span> | ||
|
||
<AdminCommentID eventId={eventId} /> | ||
</div> | ||
</Container> | ||
); | ||
} |
Oops, something went wrong.