diff --git a/src/apis/admin.ts b/src/apis/admin.ts index 53ec1f3..c5152e7 100644 --- a/src/apis/admin.ts +++ b/src/apis/admin.ts @@ -1,24 +1,39 @@ import client from './client'; -const getReports = async ( - setReports: React.Dispatch>, - queryString: string = '', - callBack?: () => void, -) => { +const postReports = async (postReportRequest: PostReportRequest) => { try { - const res = await client.get(`/api/reports${queryString}`); - setReports(res.data.data); - if (callBack) callBack(); - console.log(res.data.data); + const res = await client.post(`/api/reports`, postReportRequest); + if (res.status === 200) { + return res; + } + } catch (error) { + console.error(error); + } +}; + +const getReports = async (reportQueryString: ReportQueryString) => { + try { + const queryParams = new URLSearchParams(); + if (reportQueryString.reportType !== null) + queryParams.append('reportType', reportQueryString.reportType); + if (reportQueryString.status !== null) queryParams.append('status', reportQueryString.status); + if (reportQueryString.page !== null) queryParams.append('page', reportQueryString.page); + if (reportQueryString.size !== null) queryParams.append('size', reportQueryString.size); + + const queryStrings = queryParams.toString(); + const res = await client.get(`/api/reports?${queryStrings}`); + if (!res) throw new Error('신고 목록 데이터 조회 도중 에러가 발생했습니다.'); + console.log(res); + return res; } catch (error) { console.error(error); } }; -const patchReport = async (reportId: number, reportRequest: ReportRequest) => { +const patchReport = async (reportId: number, patchReportRequest: PatchReportRequest) => { try { - console.log(`/api/reports/${reportId}`, reportRequest); - const res = await client.patch(`/api/reports/${reportId}`, reportRequest); + console.log(`/api/reports/${reportId}`, patchReportRequest); + const res = await client.patch(`/api/reports/${reportId}`, patchReportRequest); console.log(res); } catch (error) { console.error(error); @@ -61,4 +76,4 @@ const patchBadWords = async ( } }; -export { getReports, patchReport, getBadWords, postBadWords, patchBadWords }; +export { postReports, getReports, patchReport, getBadWords, postBadWords, patchBadWords }; diff --git a/src/assets/icons/arrow-down.svg b/src/assets/icons/arrow-down.svg index 1076c2b..e00fa93 100644 --- a/src/assets/icons/arrow-down.svg +++ b/src/assets/icons/arrow-down.svg @@ -1,3 +1,3 @@ - - + + \ No newline at end of file diff --git a/src/assets/icons/index.ts b/src/assets/icons/index.ts index 2d86c84..42da171 100644 --- a/src/assets/icons/index.ts +++ b/src/assets/icons/index.ts @@ -16,6 +16,7 @@ import LikeFilledIcon from './like-filled.svg?react'; import LikeOutlinedIcon from './like-outlined.svg?react'; import NaverIcon from './naver.svg?react'; import NoticeIcon from './notice.svg?react'; +import PencilIcon from './pencil.svg?react'; import PersonIcon from './person.svg?react'; import RestartIcon from './restart.svg'; import SirenFilledIcon from './siren-filled.svg?react'; @@ -52,4 +53,5 @@ export { LikeOutlinedIcon, DeleteIcon, CancelIcon, + PencilIcon, }; diff --git a/src/assets/icons/pencil.svg b/src/assets/icons/pencil.svg new file mode 100644 index 0000000..3e22a81 --- /dev/null +++ b/src/assets/icons/pencil.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/components/ReportModal.tsx b/src/components/ReportModal.tsx index 923ec42..be47d81 100644 --- a/src/components/ReportModal.tsx +++ b/src/components/ReportModal.tsx @@ -1,57 +1,91 @@ -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { twMerge } from 'tailwind-merge'; +import { postReports } from '@/apis/admin'; + import ConfirmModal from './ConfirmModal'; import TextareaField from './TextareaField'; interface ReportModalProps { + reportType: ReportType; + letterId: number | null; onClose: () => void; } -const REPORT_REASON = ['욕설', '비방', '폭언', '성희롱', '기타']; +interface ReportReason { + name: string; + type: Reason; +} +const REPORT_REASON: ReportReason[] = [ + { name: '욕설', type: 'ABUSE' }, + { name: '비방', type: 'DEFAMATION' }, + { name: '폭언', type: 'THREATS' }, + { name: '성희롱', type: 'HARASSMENT' }, + { name: '기타', type: 'ETC' }, +]; -const ReportModal = ({ onClose }: ReportModalProps) => { - const [selected, setSelected] = useState(''); - const [additionalReason, setAdditionalReason] = useState(''); +const ReportModal = ({ reportType, letterId, onClose }: ReportModalProps) => { + const [postReportRequest, setPostReportRequest] = useState({ + reportType: reportType, + reasonType: '', + reason: '', + letterId: letterId, + }); - const handleReasonClick = (reason: string) => { - if (selected === reason) setSelected(''); - else setSelected(reason); + const handleReasonClick = (reason: Reason) => { + if (postReportRequest.reasonType === reason) + setPostReportRequest((cur) => ({ ...cur, reasonType: '' })); + else setPostReportRequest((cur) => ({ ...cur, reasonType: reason })); }; - const handleSubmit = () => { - onClose(); + const handleSubmit = async () => { + const res = await postReports(postReportRequest); + if (res?.status === 200) { + alert('신고 처리되었습니다.'); + onClose(); + } else if (res?.status === 409) { + alert('신고한 이력이 있습니다.'); + onClose(); + } }; + useEffect(() => { + if (!postReportRequest.letterId) { + alert('신고 모달을 여는 과정에서 오류가 발생했습니다. 새로고침을 눌러주세요'); + onClose(); + } + }); + return (
- {REPORT_REASON.map((reason) => ( + {REPORT_REASON.map((reason, idx) => ( ))}
setAdditionalReason(e.target.value)} + value={postReportRequest.reason} + onChange={(e) => setPostReportRequest((cur) => ({ ...cur, reason: e.target.value }))} />
); diff --git a/src/pages/Admin/FilteredLetter.tsx b/src/pages/Admin/FilteredLetter.tsx index 0390d61..fa4abcb 100644 --- a/src/pages/Admin/FilteredLetter.tsx +++ b/src/pages/Admin/FilteredLetter.tsx @@ -1,5 +1,6 @@ import { AlarmIcon } from '@/assets/icons'; +import AdminPageTitle from './components/AdminPageTitle'; import FilteredLetterListItem from './components/FilteredLetterListItem'; import ListHeaderFrame from './components/ListHeaderFrame'; import WrapperFrame from './components/WrapperFrame'; @@ -8,20 +9,23 @@ import WrapperTitle from './components/WrapperTitle'; export default function FilteredLetterManage() { const arr = new Array(10).fill(null); return ( - - -
- -
ID - 제보자 이메일 - 작성자 이메일 - 차단 일자 - 포함된 단어 - - {arr.map((_, idx) => { - return ; - })} - - + <> + 검열 관리 / 차단된 편지 목록 + + +
+ + ID + 제보자 이메일 + 작성자 이메일 + 차단 일자 + 포함된 단어 + + {arr.map((_, idx) => { + return ; + })} +
+
+ ); } diff --git a/src/pages/Admin/Filtering.tsx b/src/pages/Admin/Filtering.tsx index 4967629..38ec96e 100644 --- a/src/pages/Admin/Filtering.tsx +++ b/src/pages/Admin/Filtering.tsx @@ -4,6 +4,7 @@ import { getBadWords } from '@/apis/admin'; import { AddIcon, AlarmIcon, CancelIcon } from '@/assets/icons'; import AddInputButton from './components/AddInputButton'; +import AdminPageTitle from './components/AdminPageTitle'; import WrapperFrame from './components/WrapperFrame'; import WrapperTitle from './components/WrapperTitle'; @@ -15,37 +16,40 @@ export default function FilteringManage() { getBadWords(setBadWords); }, []); return ( - - -
- {badWords.map((badWord, idx) => { - return ( - - {badWord.word} - + + ); + })} + {addInputShow ? ( + + ) : ( + + 추가하기 + - ); - })} - {addInputShow ? ( - - ) : ( - - 추가하기 - - - )} -
-
+ )} + + + ); } diff --git a/src/pages/Admin/Report.tsx b/src/pages/Admin/Report.tsx index bbd6f74..0838c4c 100644 --- a/src/pages/Admin/Report.tsx +++ b/src/pages/Admin/Report.tsx @@ -3,6 +3,7 @@ import { useEffect, useState } from 'react'; import { getReports } from '@/apis/admin'; import { AlarmIcon } from '@/assets/icons'; +import AdminPageTitle from './components/AdminPageTitle'; import ListHeaderFrame from './components/ListHeaderFrame'; import ReportDetailModal from './components/ReportDetailModal'; import ReportHandlingModal from './components/ReportHandlingModal'; @@ -14,46 +15,104 @@ export default function ReportManage() { const [detailModalOpen, setDetailModalOpen] = useState(false); const [handleModalOpen, setHandleModalOpen] = useState(false); const [reports, setReports] = useState([]); + const [reportPages, setReportPages] = useState({ + currentPage: '1', + totalPages: '0', + }); const [selectedReport, setSelectReport] = useState(null); const [selectedReportId, setSelectedReportId] = useState(null); // const [allReports, setAllReports] = useState(); + + const [reportQueryString, setReportQueryString] = useState({ + reportType: null, + status: 'PENDING', + page: '1', + size: '3', + }); + const handleGetReports = async (reportQueryString: ReportQueryString) => { + const res = await getReports(reportQueryString); + if (res?.status === 200) { + console.log(res.data.data.content); + setReports(res.data.data.content); + setReportPages(() => ({ + currentPage: res.data.data.currentPage, + totalPages: res.data.data.totalPages, + })); + } + }; useEffect(() => { - getReports(setReports, '?status=PENDING'); - }, []); + handleGetReports(reportQueryString); + }, [reportQueryString]); return ( - - + <> + 검열 관리 / 신고 편지 목록 + + -
- - ID - 제보자 이메일 - 작성자 이메일 - 제보 일자 - 제보 사유 - - {reports.map((data, idx) => ( - + + ID + 제보자 이메일 + 작성자 이메일 + 제보 일자 + 제보 사유 + + {reports?.map((data, idx) => ( + + ))} +
+
+ + + {reportPages.currentPage}/{reportPages.totalPages} + + +
+
+
+ {detailModalOpen && ( + + )} + {handleModalOpen && ( + - ))} - - {detailModalOpen && ( - - )} - {handleModalOpen && ( - - )} -
+ )} +
+ ); } diff --git a/src/pages/Admin/RollingPaper.tsx b/src/pages/Admin/RollingPaper.tsx new file mode 100644 index 0000000..a855c2b --- /dev/null +++ b/src/pages/Admin/RollingPaper.tsx @@ -0,0 +1,81 @@ +import { useState } from 'react'; + +import { AddIcon, AlarmIcon, DeleteIcon } from '@/assets/icons'; + +import AddRollingPaperModal from './components/AddRollingPaperModal'; +import PageTitle from './components/AdminPageTitle'; +import WrapperFrame from './components/WrapperFrame'; +import WrapperTitle from './components/WrapperTitle'; + +export default function AdminRollingPaper() { + const [activeModal, setActiveModal] = useState(false); + + return ( + <> + {activeModal && setActiveModal(false)} />} + 게시판 관리 / 롤링 페이퍼 설정 + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
ID제목쌓인 편지 수상태
1 + 침수 피해를 복구중인 포스코 임직원 분들에게 응원의 메시지를 보내주세요! + 12 + + 진행 중 + +
2 + 침수 피해를 복구중인 포스코 임직원 분들에게 응원의 메시지를 보내주세요! + 12 + + + +
+
+ + ); +} diff --git a/src/pages/Admin/components/AddRollingPaperModal.tsx b/src/pages/Admin/components/AddRollingPaperModal.tsx new file mode 100644 index 0000000..2df17c3 --- /dev/null +++ b/src/pages/Admin/components/AddRollingPaperModal.tsx @@ -0,0 +1,56 @@ +import { ChangeEvent, FormEvent, useState } from 'react'; + +import ModalOverlay from '@/components/ModalOverlay'; + +interface AddRollingPaperModalProps { + onClose: () => void; +} + +export default function AddRollingPaperModal({ onClose }: AddRollingPaperModalProps) { + const [title, setTitle] = useState(''); + const [error, setError] = useState(''); + + const handleChange = (e: ChangeEvent) => { + setTitle(e.target.value); + }; + + const handleSubmit = (e: FormEvent) => { + e.preventDefault(); + if (title.trim() === '') { + setError('내용을 입력해주세요'); + return; + } + + console.log(title); + }; + + return ( + +
+
+

어떤 롤링페이퍼를 생성하시겠어요?

+