diff --git a/components/Layout/Layout.tsx b/components/Layout/Layout.tsx
index 59cbb3596..3b22a05c8 100644
--- a/components/Layout/Layout.tsx
+++ b/components/Layout/Layout.tsx
@@ -68,7 +68,8 @@ export default function AppLayout({ children }: AppLayoutProps) {
{presentPath !== '/match' &&
presentPath !== '/manual' &&
- presentPath !== '/store' && (
+ presentPath !== '/store' &&
+ !presentPath.startsWith('/party') && (
diff --git a/components/admin/SideNav.tsx b/components/admin/SideNav.tsx
index 3d01b48fb..1628c994d 100644
--- a/components/admin/SideNav.tsx
+++ b/components/admin/SideNav.tsx
@@ -9,7 +9,13 @@ import {
} from 'react-icons/gr';
import { IoGameControllerOutline, IoReceiptOutline } from 'react-icons/io5';
import { MdOutlineMessage } from 'react-icons/md';
-import { TbCalendarTime, TbCoin, TbPaperBag, TbTrophy } from 'react-icons/tb';
+import {
+ TbCalendarTime,
+ TbCoin,
+ TbPaperBag,
+ TbTrophy,
+ TbNote,
+} from 'react-icons/tb';
import SideNavContent from 'components/admin/SideNavContent';
import styles from 'styles/admin/SideNav.module.scss';
@@ -121,6 +127,14 @@ export default function SideNav() {
>
+
+
+
+
);
}
diff --git a/components/admin/party/AdminCommentReport.tsx b/components/admin/party/AdminCommentReport.tsx
new file mode 100644
index 000000000..797af5e55
--- /dev/null
+++ b/components/admin/party/AdminCommentReport.tsx
@@ -0,0 +1,108 @@
+import { useEffect, useState } from 'react';
+import { useSetRecoilState } from 'recoil';
+import {
+ Paper,
+ Table,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableRow,
+} from '@mui/material';
+import { PartyCommentReport, PartyCommentReportTable } from 'types/partyTypes';
+import { instanceInPartyManage } from 'utils/axios';
+import { toastState } from 'utils/recoil/toast';
+import { tableFormat } from 'constants/admin/table';
+import {
+ AdminEmptyItem,
+ AdminTableHead,
+} from 'components/admin/common/AdminTable';
+import PageNation from 'components/Pagination';
+import styles from 'styles/admin/Party/AdminPartyCommon.module.scss';
+
+const tableTitle: { [key: string]: string } = {
+ id: '번호',
+ reporterIntraId: '신고자 이름',
+ commentsId: '댓글 번호',
+ roomId: '방',
+ message: '메세지',
+ createdAt: '시간',
+};
+
+export default function AdminCommentReport() {
+ const [commentInfo, setCommentInfo] = useState
({
+ commentReportList: [],
+ totalPage: 0,
+ currentPage: 0,
+ });
+ const [currentPage, setCurrentPage] = useState(1);
+ const setSnackBar = useSetRecoilState(toastState);
+
+ useEffect(() => {
+ instanceInPartyManage
+ .get(`/reports/comments?page=${currentPage}&size=10`)
+ .then((res) => {
+ setCommentInfo({
+ commentReportList: res.data.commentReportList,
+ totalPage: res.data.totalPage,
+ currentPage: currentPage,
+ });
+ })
+ .catch((error) => {
+ setSnackBar({
+ toastName: 'GET request',
+ message: '댓글신고를 가져오는데 실패했습니다.',
+ severity: 'error',
+ clicked: true,
+ });
+ });
+ }, [currentPage]);
+
+ return (
+
+
+ 댓글 신고리스트
+
+
+
+
+
+ {commentInfo.commentReportList &&
+ commentInfo.commentReportList.length > 0 ? (
+ commentInfo.commentReportList.map(
+ (report: PartyCommentReport, index: number) => (
+
+ {tableFormat['partyCommentReport'].columns.map(
+ (columnName) => {
+ return (
+
+ {report[
+ columnName as keyof PartyCommentReport
+ ]?.toString()}
+
+ );
+ }
+ )}
+
+ )
+ )
+ ) : (
+
+ )}
+
+
+
+
+
{
+ setCurrentPage(pageNumber);
+ }}
+ />
+
+
+ );
+}
diff --git a/components/admin/party/AdminPartyNoShow.tsx b/components/admin/party/AdminPartyNoShow.tsx
new file mode 100644
index 000000000..474a2a940
--- /dev/null
+++ b/components/admin/party/AdminPartyNoShow.tsx
@@ -0,0 +1,108 @@
+import { useEffect, useState } from 'react';
+import { useSetRecoilState } from 'recoil';
+import {
+ Paper,
+ Table,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableRow,
+} from '@mui/material';
+import { PartyNoshowReport, PartyNoshowReportTable } from 'types/partyTypes';
+import { instanceInPartyManage } from 'utils/axios';
+import { toastState } from 'utils/recoil/toast';
+import { tableFormat } from 'constants/admin/table';
+import {
+ AdminEmptyItem,
+ AdminTableHead,
+} from 'components/admin/common/AdminTable';
+import PageNation from 'components/Pagination';
+import styles from 'styles/admin/Party/AdminPartyCommon.module.scss';
+
+const tableTitle: { [key: string]: string } = {
+ id: '번호',
+ reporterIntraId: '신고자 이름',
+ reporteeIntraId: '피신고자 이름',
+ roomId: '방',
+ message: '메세지',
+ createdAt: '시간',
+};
+
+export default function AdminPartyNoShow() {
+ const [noShowInfo, setNoShowInfo] = useState({
+ noShowReportList: [],
+ totalPages: 0,
+ currentPage: 0,
+ });
+ const [currentPage, setCurrentPage] = useState(1);
+ const setSnackBar = useSetRecoilState(toastState);
+
+ useEffect(() => {
+ instanceInPartyManage
+ .get(`/reports/users?page=${currentPage}&size=10`)
+ .then((res) => {
+ setNoShowInfo({
+ noShowReportList: res.data.noShowReportList,
+ totalPages: res.data.totalPage,
+ currentPage: currentPage,
+ });
+ })
+ .catch((error) => {
+ setSnackBar({
+ toastName: 'GET request',
+ message: '댓글신고를 가져오는데 실패했습니다.',
+ severity: 'error',
+ clicked: true,
+ });
+ });
+ }, [currentPage]);
+
+ return (
+
+
+ 노쇼 신고리스트
+
+
+
+
+
+ {noShowInfo.noShowReportList &&
+ noShowInfo.noShowReportList.length > 0 ? (
+ noShowInfo.noShowReportList.map(
+ (report: PartyNoshowReport, index: number) => (
+
+ {tableFormat['partyNoshowReport'].columns.map(
+ (columnName) => {
+ return (
+
+ {report[
+ columnName as keyof PartyNoshowReport
+ ]?.toString()}
+
+ );
+ }
+ )}
+
+ )
+ )
+ ) : (
+
+ )}
+
+
+
+
+
{
+ setCurrentPage(pageNumber);
+ }}
+ />
+
+
+ );
+}
diff --git a/components/admin/party/AdminPartyPenalty.tsx b/components/admin/party/AdminPartyPenalty.tsx
new file mode 100644
index 000000000..e19fb588b
--- /dev/null
+++ b/components/admin/party/AdminPartyPenalty.tsx
@@ -0,0 +1,133 @@
+import { useCallback, useEffect, useState } from 'react';
+import { useSetRecoilState } from 'recoil';
+import {
+ Paper,
+ Table,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableRow,
+} from '@mui/material';
+import { PartyPenaltyAdmin, PartyPenaltyTable } from 'types/partyTypes';
+import { instanceInPartyManage } from 'utils/axios';
+import { modalState } from 'utils/recoil/modal';
+import { toastState } from 'utils/recoil/toast';
+import { tableFormat } from 'constants/admin/table';
+import {
+ AdminEmptyItem,
+ AdminTableHead,
+} from 'components/admin/common/AdminTable';
+import PageNation from 'components/Pagination';
+import styles from 'styles/admin/Party/AdminPartyCommon.module.scss';
+
+const tableTitle: { [key: string]: string } = {
+ id: '번호',
+ userIntraId: '유저',
+ penaltyType: '패널티 타입',
+ message: '내용',
+ startTime: '시작 시간',
+ penaltyTime: '패널티 시간',
+ edit: '수정',
+};
+
+export default function AdminCommentReport() {
+ const [penaltyInfo, setPenaltyInfo] = useState({
+ penaltyList: [],
+ totalPage: 0,
+ currentPage: 0,
+ });
+ const [currentPage, setCurrentPage] = useState(1);
+ const setModal = useSetRecoilState(modalState);
+ const setSnackBar = useSetRecoilState(toastState);
+
+ useEffect(() => {
+ instanceInPartyManage
+ .get(`/penalties?page=${currentPage}&size=10`)
+ .then((res) => {
+ setPenaltyInfo({
+ penaltyList: res.data.penaltyList,
+ totalPage: res.data.totalPage,
+ currentPage: currentPage,
+ });
+ })
+ .catch((error) => {
+ setSnackBar({
+ toastName: 'GET request',
+ message: '댓글신고를 가져오는데 실패했습니다.',
+ severity: 'error',
+ clicked: true,
+ });
+ });
+ }, [currentPage]);
+
+ const handleAddpenalty = () => {
+ setModal({ modalName: 'ADMIN-PARTY_ADMIN_PENALTY' });
+ };
+
+ const handleEditpenalty = (partyPenalty?: PartyPenaltyAdmin) => {
+ setModal({ modalName: 'ADMIN-PARTY_ADMIN_PENALTY', partyPenalty });
+ };
+
+ return (
+
+
+ 패널티 리스트
+
+
+
+
+
+
+ {penaltyInfo.penaltyList && penaltyInfo.penaltyList.length > 0 ? (
+ penaltyInfo.penaltyList.map(
+ (penalty: PartyPenaltyAdmin, index: number) => (
+
+ {tableFormat['partyPenaltyAdmin'].columns.map(
+ (columnName) => {
+ return (
+
+ {columnName === 'edit' ? (
+
+ ) : (
+ penalty[
+ columnName as keyof PartyPenaltyAdmin
+ ]?.toString()
+ )}
+
+ );
+ }
+ )}
+
+ )
+ )
+ ) : (
+
+ )}
+
+
+
+
+
{
+ setCurrentPage(pageNumber);
+ }}
+ />
+
+
+ );
+}
diff --git a/components/admin/party/AdminPartyRoomReport.tsx b/components/admin/party/AdminPartyRoomReport.tsx
new file mode 100644
index 000000000..067b6281b
--- /dev/null
+++ b/components/admin/party/AdminPartyRoomReport.tsx
@@ -0,0 +1,103 @@
+import { useEffect, useState } from 'react';
+import { useSetRecoilState } from 'recoil';
+import {
+ Paper,
+ Table,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableRow,
+} from '@mui/material';
+import { PartyRoomReport, PartyRoomReportTable } from 'types/partyTypes';
+import { instanceInPartyManage } from 'utils/axios';
+import { toastState } from 'utils/recoil/toast';
+import { tableFormat } from 'constants/admin/table';
+import {
+ AdminEmptyItem,
+ AdminTableHead,
+} from 'components/admin/common/AdminTable';
+import PageNation from 'components/Pagination';
+import styles from 'styles/admin/Party/AdminPartyCommon.module.scss';
+
+const tableTitle: { [key: string]: string } = {
+ id: '번호',
+ reporterIntraId: '신고자 이름',
+ reporteeIntraId: '피신고자 이름',
+ roomId: '방',
+ message: '메세지',
+ createdAt: '시간',
+};
+
+export default function AdminPartyRoomReport() {
+ const [roomInfo, setRoomInfo] = useState({
+ roomReportList: [],
+ totalPage: 0,
+ currentPage: 0,
+ });
+ const [currentPage, setCurrentPage] = useState(1);
+
+ const setSnackBar = useSetRecoilState(toastState);
+
+ useEffect(() => {
+ instanceInPartyManage
+ .get(`/reports/rooms?page=${currentPage}&size=10`)
+ .then((res) => {
+ setRoomInfo({
+ roomReportList: res.data.roomReportList,
+ totalPage: res.data.totalPage,
+ currentPage: currentPage,
+ });
+ })
+ .catch((error) => {
+ setSnackBar({
+ toastName: 'GET request',
+ message: '댓글신고를 가져오는데 실패했습니다.',
+ severity: 'error',
+ clicked: true,
+ });
+ });
+ }, [currentPage]);
+
+ return (
+
+
+ 방 신고리스트
+
+
+
+
+
+ {roomInfo.roomReportList && roomInfo.roomReportList.length > 0 ? (
+ roomInfo.roomReportList.map(
+ (report: PartyRoomReport, index: number) => (
+
+ {tableFormat['partyRoomReport'].columns.map(
+ (columnName) => (
+
+ {report[
+ columnName as keyof PartyRoomReport
+ ]?.toString()}
+
+ )
+ )}
+
+ )
+ )
+ ) : (
+
+ )}
+
+
+
+
+
{
+ setCurrentPage(pageNumber);
+ }}
+ />
+
+
+ );
+}
diff --git a/components/admin/party/PartyCategory.tsx b/components/admin/party/PartyCategory.tsx
new file mode 100644
index 000000000..f11bd9cd7
--- /dev/null
+++ b/components/admin/party/PartyCategory.tsx
@@ -0,0 +1,83 @@
+import { useState } from 'react';
+import {
+ Paper,
+ Table,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableRow,
+} from '@mui/material';
+import { PartyCategory } from 'types/partyTypes';
+import { tableFormat } from 'constants/admin/table';
+import { AdminTableHead } from 'components/admin/common/AdminTable';
+import usePartyCategory from 'hooks/party/usePartyCategory';
+import styles from 'styles/admin/Party/AdminPartyCommon.module.scss';
+
+const tableTitle: { [key: string]: string } = {
+ categoryId: '카테고리번호',
+ categoryName: '카테고리',
+ delete: '삭제',
+};
+
+export default function PartyCategories() {
+ const { categories, createCategory, deleteCategory } = usePartyCategory();
+ const [newCategoryName, setNewCategoryName] = useState('');
+
+ const handleConfirm = () => {
+ if (newCategoryName.trim() !== '') {
+ createCategory(newCategoryName);
+ setNewCategoryName('');
+ }
+ };
+
+ return (
+
+
+
카테고리 관리
+
+ setNewCategoryName(e.target.value)}
+ />
+
+
+
+
+
+
+
+ {categories.map((c) => (
+
+ {tableFormat['partyCategory'].columns.map((columnName) => {
+ return (
+
+ {columnName === 'delete' ? (
+
+ ) : (
+ c[columnName as keyof PartyCategory]?.toString()
+ )}
+
+ );
+ })}
+
+ ))}
+
+
+
+
+ );
+}
diff --git a/components/admin/party/PartyNav.tsx b/components/admin/party/PartyNav.tsx
new file mode 100644
index 000000000..3ed0a36b8
--- /dev/null
+++ b/components/admin/party/PartyNav.tsx
@@ -0,0 +1,38 @@
+import { useState } from 'react';
+import styles from 'styles/party/PartyNav.module.scss';
+import PartyPenalty from './AdminPartyPenalty';
+import PartyCategory from './PartyCategory';
+import PartyReportNav from './PartyReportNav';
+import PartyTemplate from './PartyTemplate';
+
+export default function PartyNav() {
+ const [navValue, setNavValue] = useState('penalty');
+ return (
+ <>
+
+
+ - setNavValue('penalty')}>
+ 패널티
+
+ - setNavValue('report')}>
+ 신고 관리
+
+ - setNavValue('room')}>
+ 방 관리
+
+ - setNavValue('template')}>
+ 템플릿 관리
+
+ - setNavValue('category')}>
+ 카테고리 관리
+
+
+
+ {navValue === 'report' &&
}
+ {navValue === 'template' &&
}
+ {navValue === 'category' &&
}
+ {navValue === 'penalty' &&
}
+
+ >
+ );
+}
diff --git a/components/admin/party/PartyReportNav.tsx b/components/admin/party/PartyReportNav.tsx
new file mode 100644
index 000000000..de6392674
--- /dev/null
+++ b/components/admin/party/PartyReportNav.tsx
@@ -0,0 +1,29 @@
+import { useState } from 'react';
+import styles from 'styles/party/PartyNav.module.scss';
+import AdminCommentReport from './AdminCommentReport';
+import AdminPartyNoShowReport from './AdminPartyNoShow';
+import AdminPartyRoomReport from './AdminPartyRoomReport';
+
+export default function PartyReportNav() {
+ const [navValue, setNavValue] = useState('noshow');
+ return (
+ <>
+
+
+ - setNavValue('noshow')}>
+ 노쇼 관리
+
+ - setNavValue('comment')}>
+ 댓글 관리
+
+ - setNavValue('room')}>
+ 방 관리
+
+
+ {navValue === 'noshow' &&
}
+ {navValue === 'comment' &&
}
+ {navValue === 'room' &&
}
+
+ >
+ );
+}
diff --git a/components/admin/party/PartyTemplate.tsx b/components/admin/party/PartyTemplate.tsx
new file mode 100644
index 000000000..86f791db2
--- /dev/null
+++ b/components/admin/party/PartyTemplate.tsx
@@ -0,0 +1,106 @@
+import React, { useEffect } from 'react';
+import { useSetRecoilState } from 'recoil';
+import {
+ Paper,
+ Table,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableRow,
+} from '@mui/material';
+import { PartyGameTemplate } from 'types/partyTypes';
+import { modalState } from 'utils/recoil/modal';
+import { tableFormat } from 'constants/admin/table';
+import { AdminTableHead } from 'components/admin/common/AdminTable';
+import { usePartyTemplate } from 'hooks/party/usePartyTemplate';
+import styles from 'styles/admin/Party/AdminPartyCommon.module.scss';
+
+const tableTitle: { [key: string]: string } = {
+ gameTemplateId: '템플릿번호',
+ categoryId: '카테고리',
+ gameName: '템플릿이름',
+ maxGamePeople: '최대인원',
+ minGamePeople: '최소인원',
+ maxGameTime: '최대게임시간',
+ minGameTime: '최소게임시간',
+ genre: '장르',
+ difficulty: '난이도',
+ summary: '요약',
+ change: '수정',
+ delete: '삭제',
+};
+
+export default function PartyTemplate() {
+ const { templates, deleteTemplate } = usePartyTemplate();
+ const setModal = useSetRecoilState(modalState);
+
+ useEffect(() => {
+ console.log('Render');
+ }, [templates]);
+
+ const handleEditTemplate = (template?: PartyGameTemplate) => {
+ setModal({ modalName: 'ADMIN-PARTY_TEMPLATE', template });
+ };
+
+ const handleAddTemplate = () => {
+ setModal({ modalName: 'ADMIN-PARTY_TEMPLATE' });
+ };
+
+ const deleteHandler = (gameTemplateId: number) => {
+ deleteTemplate({ gameTemplateId });
+ };
+
+ return (
+
+
+
+ 템플릿 관리
+
+
+
+
+
+
+ {templates.map((t) => (
+
+ {tableFormat['partyTemplate'].columns.map((columnName) => {
+ return (
+
+ {columnName === 'change' && (
+
+ )}
+ {columnName === 'delete' ? (
+
+ ) : (
+ t[columnName as keyof PartyGameTemplate]?.toString()
+ )}
+
+ );
+ })}
+
+ ))}
+
+
+
+
+
+ );
+}
diff --git a/components/main/PartyPreview.tsx b/components/main/PartyPreview.tsx
new file mode 100644
index 000000000..d9f8beb17
--- /dev/null
+++ b/components/main/PartyPreview.tsx
@@ -0,0 +1,22 @@
+import usePartyRoomList from 'hooks/party/usePartyRoomList';
+import styles from 'styles/main/PartyPreview.module.scss';
+export default function PartyPreview() {
+ const { partyRooms } = usePartyRoomList();
+ const limitedRooms = partyRooms.slice(0, 3);
+ return (
+
+ );
+}
diff --git a/components/main/Section.tsx b/components/main/Section.tsx
index 1c4019eca..67e561f73 100644
--- a/components/main/Section.tsx
+++ b/components/main/Section.tsx
@@ -5,6 +5,7 @@ import GameResult from 'components/game/GameResult';
import TournamentPreview from 'components/main/TournamentPreview';
import RankListMain from 'components/rank/topRank/RankListMain';
import styles from 'styles/main/Section.module.scss';
+import PartyPreview from './PartyPreview';
type SectionProps = {
sectionTitle: string;
@@ -18,6 +19,7 @@ export default function Section({ sectionTitle, path }: SectionProps) {
const pathCheck: pathType = {
game: ,
rank: ,
+ party: ,
tournament: ,
};
diff --git a/components/modal/ModalProvider.tsx b/components/modal/ModalProvider.tsx
index abbf1aa7f..cd1f462ae 100644
--- a/components/modal/ModalProvider.tsx
+++ b/components/modal/ModalProvider.tsx
@@ -6,6 +6,7 @@ import AdminModal from 'components/modal/modalType/AdminModal';
import NormalModal from 'components/modal/modalType/NormalModal';
import StoreModal from 'components/modal/modalType/StoreModal';
import styles from 'styles/modal/Modal.module.scss';
+import PartyModal from './modalType/PartyModal';
import TournamentModal from './modalType/TournamentModal';
export default function ModalProvider() {
@@ -46,6 +47,8 @@ export default function ModalProvider() {
) : modalType === 'TOURNAMENT' ? (
+ ) : modalType === 'PARTY' ? (
+
) : null}
)
diff --git a/components/modal/Party/PartyReportModal.tsx b/components/modal/Party/PartyReportModal.tsx
new file mode 100644
index 000000000..228fc3e29
--- /dev/null
+++ b/components/modal/Party/PartyReportModal.tsx
@@ -0,0 +1,83 @@
+import { useState } from 'react';
+import { useSetRecoilState } from 'recoil';
+import { Modal, PartyReportModalData } from 'types/modalTypes';
+import { instance } from 'utils/axios';
+import { modalState } from 'utils/recoil/modal';
+import styles from 'styles/modal/menu/ReportModal.module.scss';
+import { ModalButton, ModalButtonContainer } from '../ModalButton';
+
+export function PartyReportModal({ report }: { report: PartyReportModalData }) {
+ const [isLoading, setIsLoading] = useState(false);
+ const [content, setContent] = useState('');
+ const setModal = useSetRecoilState