-
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 #98 from softeerbootcamp4th/feature/26-admin-events
[feat] 어드민 페이지 이벤트 목록 구현
- Loading branch information
Showing
43 changed files
with
831 additions
and
99 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
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,22 @@ | ||
import Button from "@common/components/Button.jsx"; | ||
|
||
function EventEditContainer({ title, children }) { | ||
return ( | ||
<section className="flex flex-col gap-8"> | ||
<div className="flex w-full justify-between"> | ||
<div> | ||
<h2 className="text-title-m font-bold">{title}</h2> | ||
<p className="text-detail-l">*는 필수 입력</p> | ||
</div> | ||
<div> | ||
<Button>임시저장 불러오기</Button> | ||
<Button>임시저장</Button> | ||
<Button>등록</Button> | ||
</div> | ||
</div> | ||
{children} | ||
</section> | ||
); | ||
} | ||
|
||
export default EventEditContainer; |
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,25 @@ | ||
import Input from "@common/components/Input.jsx"; | ||
|
||
function EventEditor() { | ||
return ( | ||
<div> | ||
<label> | ||
<span> | ||
이벤트 명<sup>*</sup> | ||
</span> | ||
<Input /> | ||
</label> | ||
<label> | ||
<span>이벤트 ID</span> | ||
<span>(대충 이벤트 id 들어감)</span> | ||
</label> | ||
<div> | ||
<span> | ||
이벤트 기간<sup>*</sup> | ||
</span> | ||
</div> | ||
</div> | ||
); | ||
} | ||
|
||
export default EventEditor; |
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,55 @@ | ||
import { fetchServer } from "@common/dataFetch/fetchServer.js"; | ||
import { useMutation } from "@common/dataFetch/getQuery.js"; | ||
import ConfirmModal from "@admin/modals/ConfirmModal.jsx"; | ||
import AlertModal from "@admin/modals/AlertModal.jsx"; | ||
import Button from "@common/components/Button.jsx"; | ||
import openModal from "@common/modal/openModal.js"; | ||
|
||
function DeleteButton({ selected, reset }) { | ||
const mutate = useMutation( | ||
"admin-event-list", | ||
() => | ||
fetchServer("/api/v1/admin/events", { | ||
method: "delete", | ||
body: { | ||
eventIds: [...selected], | ||
}, | ||
}), | ||
{ | ||
onSuccess: () => { | ||
openModal( | ||
<AlertModal title="삭제" description="기대평이 삭제되었습니다." />, | ||
); | ||
reset(); | ||
}, | ||
}, | ||
); | ||
const deleteConfirmModal = ( | ||
<ConfirmModal | ||
title="삭제" | ||
description={ | ||
<> | ||
<span>이 동작은 다시 돌이킬 수 없습니다.</span> | ||
<br /> | ||
<span> | ||
{selected.keys().next().value} | ||
{selected.size > 1 && ` 외 ${selected.size - 1} 개의`} 이벤트를 | ||
삭제하시겠습니까? | ||
</span> | ||
</> | ||
} | ||
onConfirm={mutate} | ||
/> | ||
); | ||
|
||
function onClick() { | ||
openModal(deleteConfirmModal); | ||
} | ||
return ( | ||
<Button onClick={onClick} disabled={selected.size === 0}> | ||
삭제 | ||
</Button> | ||
); | ||
} | ||
|
||
export default DeleteButton; |
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,24 @@ | ||
function checkReducer(state, action) { | ||
switch (action.type) { | ||
case "reset": | ||
return new Set(); | ||
case "check_key": { | ||
const newSet = new Set(state); | ||
if (action.value === true) newSet.add(action.key); | ||
else if (action.value === false) newSet.delete(action.key); | ||
else if (state.has(action.key)) newSet.delete(action.key); | ||
else newSet.add(action.key); | ||
return newSet; | ||
} | ||
case "toggle_keys": { | ||
const newSet = new Set(state); | ||
let allFalse = action.keys.every((key) => state.has(key) === false); | ||
if (allFalse) action.keys.forEach((key) => newSet.add(key)); | ||
else action.keys.forEach((key) => newSet.delete(key)); | ||
return newSet; | ||
} | ||
} | ||
throw Error("unknown action."); | ||
} | ||
|
||
export default checkReducer; |
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,105 @@ | ||
import { http, HttpResponse } from "msw"; | ||
import { makeLorem } from "@common/mock/utils.js"; | ||
|
||
function getEventsMock() { | ||
return Array.from({ length: 100 }, (_, i) => { | ||
const startTime = new Date( | ||
Date.now() - | ||
86400 * 30 * 1000 + | ||
Math.floor(Math.random() * 86400 * 60 * 1000), | ||
); | ||
const endTime = new Date( | ||
startTime.getTime() + Math.floor(Math.random() * 86400 * 120) * 1000, | ||
); | ||
|
||
return { | ||
name: makeLorem(3, 7), | ||
eventType: Math.random() > 0.5 ? "fcfs" : "draw", | ||
startTime, | ||
endTime, | ||
eventId: `HD_240808_${i.toString().padStart(3, "0")}`, | ||
}; | ||
}); | ||
} | ||
|
||
const dummyData = getEventsMock(); | ||
|
||
function filterData(filterParam) { | ||
const filterKey = filterParam.split(","); | ||
return function (data) { | ||
if (filterKey.length === 0) return true; | ||
for (let key of filterKey) { | ||
if (key === "fcfs" && data.eventType === "fcfs") return true; | ||
if (key === "draw" && data.eventType === "draw") return true; | ||
} | ||
return false; | ||
}; | ||
} | ||
|
||
function compareString(a, b) { | ||
if (a < b) return -1; | ||
if (a > b) return 1; | ||
return 0; | ||
} | ||
|
||
function sortData(sortParam) { | ||
const sortKey = sortParam.split(",").map((keyValue) => keyValue.split(":")); | ||
return function (a, b) { | ||
for (let [key, sorter] of sortKey) { | ||
const pm = sorter === "desc" ? -1 : 1; | ||
if (key === "eventId") { | ||
const compared = compareString(a.eventId, b.eventId) * pm; | ||
if (compared !== 0) return compared; | ||
} | ||
if (key === "name") { | ||
const compared = compareString(a.name, b.name) * pm; | ||
if (compared !== 0) return compared; | ||
} | ||
if (key === "startTime") { | ||
const compared = (a.startTime - b.startTime) * pm; | ||
if (compared !== 0) return compared; | ||
} | ||
if (key === "endTime") { | ||
const compared = (a.endTime - b.endTime) * pm; | ||
if (compared !== 0) return compared; | ||
} | ||
if (key === "eventType") { | ||
const compared = compareString(a.eventType, b.eventType) * pm; | ||
if (compared !== 0) return compared; | ||
} | ||
} | ||
return 0; | ||
}; | ||
} | ||
|
||
const handlers = [ | ||
http.get("/api/v1/admin/events", async ({ request }) => { | ||
const url = new URL(request.url); | ||
const search = url.searchParams.get("search"); | ||
const filter = url.searchParams.get("filter"); | ||
const sort = url.searchParams.get("sort"); | ||
const page = +url.searchParams.get("page") ?? 1; | ||
const size = +url.searchParams.get("size") ?? 5; | ||
|
||
const result = dummyData | ||
.filter(({ name }) => (search === null ? true : name.includes(search))) | ||
.filter(filterData(filter)) | ||
.sort(sortData(sort)) | ||
.slice((page - 1) * size, page * size); | ||
|
||
return HttpResponse.json(result); | ||
}), | ||
http.delete("/api/v1/admin/events", async ({ request }) => { | ||
const { eventIds } = await request.json(); | ||
|
||
for (let id of eventIds) { | ||
const index = dummyData.findIndex(({ eventId }) => eventId === id); | ||
if (index === -1) continue; | ||
dummyData.splice(index, 1); | ||
} | ||
|
||
return HttpResponse.json(true); | ||
}), | ||
]; | ||
|
||
export default handlers; |
Oops, something went wrong.