Skip to content

Commit

Permalink
[feat] 어드민 기대평 페이지 일부 구현
Browse files Browse the repository at this point in the history
  • Loading branch information
darkdulgi committed Aug 14, 2024
1 parent 17ff0b2 commit 710eb4e
Show file tree
Hide file tree
Showing 8 changed files with 224 additions and 60 deletions.
3 changes: 2 additions & 1 deletion src/adminPage/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -26,7 +27,7 @@ function App() {
/>
<Route path="/events/:id" element={<div>event 보는 화면</div>} />
<Route path="/events" element={<EventsPage />} />
<Route path="/comments/:id" element={<div>기대평 화면</div>} />
<Route path="/comments/:eventId" element={<CommentsIDPage />} />
<Route path="/comments" element={<CommentsPage />} />
</Route>
<Route path="/login" element={<LoginPage />} />
Expand Down
50 changes: 50 additions & 0 deletions src/adminPage/features/comment/id/Comments.jsx
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>
);
}
9 changes: 9 additions & 0 deletions src/adminPage/features/comment/id/Loading.jsx
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>
);
}
73 changes: 73 additions & 0 deletions src/adminPage/features/comment/id/index.jsx
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>
);
}
46 changes: 19 additions & 27 deletions src/adminPage/features/comment/index.jsx
Original file line number Diff line number Diff line change
@@ -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}`)
Expand Down Expand Up @@ -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 (
<form onSubmit={searchComment} className="relative mt-10 flex">
<form onSubmit={searchEvent} className="relative flex">
<input
type="text"
inputMode="numeric"
onKeyDown={onKeyDownForm}
onChange={onChangeForm}
value={formString}
placeholder="ID (숫자 9자리)"
className={`z-10 outline outline-1 outline-neutral-500 px-4 py-2 w-full ${isSpread ? "rounded-t-md" : "rounded-md"}`}
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 outline outline-1 overflow-y-auto w-full outline-neutral-500 rounded-b-md px-3 py-2 flex flex-col gap-2 ${!isSpread && "hidden"}`}
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, index) => (
{searchList.map((evt) => (
<li
key={evt.id}
onMouseEnter={() => 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`}
>
<span className="w-40">{evt.id}</span>
<span className="w-40">{evt.eventId}</span>
<span>{evt.name}</span>
</li>
))}
Expand All @@ -89,10 +81,10 @@ export default function AdminComment() {
</div>

<img
onClick={searchComment}
onClick={searchEvent}
src="/icons/search.png"
alt="검색"
className="absolute top-1/2 -translate-y-1/2 right-4 cursor-pointer"
className="z-10 absolute top-1/2 -translate-y-1/2 right-4 cursor-pointer"
/>
</form>
);
Expand Down
84 changes: 53 additions & 31 deletions src/adminPage/features/comment/mock.js
Original file line number Diff line number Diff line change
@@ -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;
17 changes: 17 additions & 0 deletions src/adminPage/pages/CommentsIDPage.jsx
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>
);
}
2 changes: 1 addition & 1 deletion src/adminPage/pages/CommentsPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export default function CommentsPage() {
return (
<Container>
<div className="flex flex-col w-full p-20">
<span className="text-title-l">기대평</span>
<span className="text-title-l pb-10">기대평</span>

<AdminComment />
</div>
Expand Down

0 comments on commit 710eb4e

Please sign in to comment.