From 1dd22482462ca7552ebe2cd77e951e81b165f125 Mon Sep 17 00:00:00 2001 From: Cho SeungYeon <111514472+layout-SY@users.noreply.github.com> Date: Sun, 29 Jun 2025 21:13:07 +0900 Subject: [PATCH 1/8] =?UTF-8?q?feat=20:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EC=8B=A0=EA=B3=A0=20=EA=B2=80=ED=86=A0=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/admin/report.api.ts | 45 +++++ src/api/admin/user.api.ts | 4 +- src/api/report.api.ts | 12 +- .../AdminReportDetail.styled.ts | 144 +++++++++++++++ .../adminUserReport/AdminReportDetail.tsx | 167 ++++++++++++++++++ .../reportsPreview/ReportsPreview.tsx | 20 +-- .../reportCheckBox/ReportCheckBox.styled.ts | 31 ++++ .../common/reportCheckBox/ReportCheckBox.tsx | 37 ++++ .../user/reportComponent/ReportModal.tsx | 19 +- src/constants/admin/adminModal.ts | 1 + src/hooks/admin/useAdminReportDetail.ts | 16 ++ src/hooks/admin/useAdminReports.ts | 29 +++ src/hooks/admin/useGetAllReports.ts | 9 +- src/hooks/admin/useGetAllReportsPreview.ts | 17 ++ src/hooks/admin/useHandleUser.ts | 56 ++++++ src/hooks/admin/useHandleUserApi.ts | 56 ++++++ src/hooks/queries/keys.ts | 2 + src/models/admin/userDetail/reportDetail.ts | 22 +++ src/models/report.ts | 14 +- .../admin/adminReports/AdminReports.styled.ts | 97 ++++++++++ src/pages/admin/adminReports/AdminReports.tsx | 108 ++++++++++- 21 files changed, 855 insertions(+), 51 deletions(-) create mode 100644 src/api/admin/report.api.ts create mode 100644 src/components/admin/adminUserReport/AdminReportDetail.styled.ts create mode 100644 src/components/admin/adminUserReport/AdminReportDetail.tsx create mode 100644 src/components/common/reportCheckBox/ReportCheckBox.styled.ts create mode 100644 src/components/common/reportCheckBox/ReportCheckBox.tsx create mode 100644 src/hooks/admin/useAdminReportDetail.ts create mode 100644 src/hooks/admin/useAdminReports.ts create mode 100644 src/hooks/admin/useGetAllReportsPreview.ts create mode 100644 src/hooks/admin/useHandleUser.ts create mode 100644 src/hooks/admin/useHandleUserApi.ts create mode 100644 src/models/admin/userDetail/reportDetail.ts create mode 100644 src/pages/admin/adminReports/AdminReports.styled.ts diff --git a/src/api/admin/report.api.ts b/src/api/admin/report.api.ts new file mode 100644 index 00000000..c9c311bc --- /dev/null +++ b/src/api/admin/report.api.ts @@ -0,0 +1,45 @@ +import { ApiReportDetail } from '../../models/admin/userDetail/reportDetail'; +import { ApiAllReports } from '../../models/report'; +import { SearchType } from '../../models/search'; +import { httpClient } from '../http.api'; + +export const postDeleteReport = async (id: number) => { + try { + await httpClient.delete(`/report/${id}`); + } catch (e) { + console.error(e); + throw e; + } +}; + +export const getReportDetail = async (reportId: number) => { + try { + const response = await httpClient.get( + `/report/${reportId}` + ); + return response.data.data; + } catch (e) { + console.error(e); + throw e; + } +}; + +export const getAllReports = async (params: SearchType) => { + try { + const response = await httpClient.get(`/report`, { params }); + return response.data.data; + } catch (e) { + console.error(e); + throw e; + } +}; + +export const getAllReportsPreview = async () => { + try { + const response = await httpClient.get(`/report`); + return response.data.data; + } catch (e) { + console.error(e); + throw e; + } +}; diff --git a/src/api/admin/user.api.ts b/src/api/admin/user.api.ts index 0ce6d90c..023a63df 100644 --- a/src/api/admin/user.api.ts +++ b/src/api/admin/user.api.ts @@ -2,7 +2,7 @@ import { ApiUserApplicantsData } from '../../models/admin/userDetail/userDetail. import { ApiUserProjectDataResponse } from '../../models/admin/userDetail/userProjectData'; import { httpClient } from '../http.api'; -export const postBanUser = async (id: string) => { +export const postBanUser = async (id: number) => { try { await httpClient.post(`/ban/${id}`); } catch (e) { @@ -11,7 +11,7 @@ export const postBanUser = async (id: string) => { } }; -export const postWarningUser = async (id: string) => { +export const postWarningUser = async (id: number) => { try { await httpClient.post(`/warning/${id}`); } catch (e) { diff --git a/src/api/report.api.ts b/src/api/report.api.ts index 0b0d24dd..111ef186 100644 --- a/src/api/report.api.ts +++ b/src/api/report.api.ts @@ -1,4 +1,4 @@ -import { type ApiAllReports, type ApiPostContent } from '../models/report'; +import type { ApiPostContent } from '../models/report'; import { httpClient } from './http.api'; export const postReport = async (formData: ApiPostContent) => { @@ -13,13 +13,3 @@ export const postReport = async (formData: ApiPostContent) => { throw error; } }; - -export const getAllReports = async () => { - try { - const response = await httpClient.get(`/reports`); - return response.data.data; - } catch (e) { - console.error(e); - throw e; - } -}; diff --git a/src/components/admin/adminUserReport/AdminReportDetail.styled.ts b/src/components/admin/adminUserReport/AdminReportDetail.styled.ts new file mode 100644 index 00000000..09bb7998 --- /dev/null +++ b/src/components/admin/adminUserReport/AdminReportDetail.styled.ts @@ -0,0 +1,144 @@ +import styled from 'styled-components'; +import { ScrollArea } from '../../common/header/Notification/Notification.styled'; +import { SpinnerContainer } from '../../user/mypage/Spinner.styled'; +import { Link } from 'react-router-dom'; +import Button from '../../common/Button/Button'; + +export const Container = styled.div` + display: flex; + flex-direction: column; + margin: 8rem 0 auto; +`; + +export const Spinner = styled(SpinnerContainer)``; + +export const Wrapper = styled.div` + display: flex; + flex-direction: column; + align-items: center; +`; + +export const HeaderContainer = styled.div` + width: 80%; + height: 100px; + display: flex; + justify-content: space-around; + align-items: center; + border: 1px solid ${({ theme }) => theme.color.border}; + border-radius: ${({ theme }) => theme.borderRadius.primary}; +`; + +export const UserContainer = styled.div` + display: flex; + flex-direction: column; + align-items: center; +`; + +export const NickName = styled.p` + font-size: 0.8rem; + opacity: 40%; +`; + +export const Category = styled.p``; + +export const Arrow = styled.div` + width: 50px; + height: 50px; + position: relative; + margin-bottom: 30px; + + &::before, + &::after { + content: ''; + position: absolute; + } + + &::before { + width: 45%; + height: 45%; + top: 59%; + right: -10rem; + border: 1.5px solid ${({ theme }) => theme.color.primary}; + border-right: 0; + border-bottom: 0; + transform: rotate(130deg); + } + + &::after { + width: 24.3rem; + height: 1px; + top: 78%; + left: -11rem; + background-color: ${({ theme }) => theme.color.primary}; + transform-origin: 0 100%; + transform: rotate(0deg); + + } + } +`; + +export const Date = styled.p` + opacity: 60%; +`; + +export const ContentContainer = styled.div` + width: 80%; + height: 550px; + display: flex; + flex-direction: column; + align-items: center; + border: 1px solid ${({ theme }) => theme.color.border}; + border-radius: ${({ theme }) => theme.borderRadius.primary}; + margin-top: 30px; + padding: 40px; +`; + +export const ContentHeader = styled.div` + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +`; + +export const Divider = styled.hr` + width: 400px; + height: 1px; + margin: 20px 0 20px 0; + opacity: 40%; +`; + +export const Scroll = styled(ScrollArea)` + padding: 20px; +`; + +export const Content = styled.p` + white-space: pre-wrap; +`; + +export const ConfirmContainer = styled.div` + width: 80%; + display: flex; + justify-content: space-between; + margin-top: 30px; +`; + +export const ConfirmArea = styled(Link)` + display: flex; + justify-content: flex-end; + align-items: center; +`; + +export const ButtonArea = styled.div` + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 10px; +`; + +export const WarningButton = styled(Button)` + background-color: ${({ theme }) => theme.color.red}; +`; + +export const BanButton = styled(Button)``; + +export const ConfirmButton = styled(Button)``; diff --git a/src/components/admin/adminUserReport/AdminReportDetail.tsx b/src/components/admin/adminUserReport/AdminReportDetail.tsx new file mode 100644 index 00000000..d20205a4 --- /dev/null +++ b/src/components/admin/adminUserReport/AdminReportDetail.tsx @@ -0,0 +1,167 @@ +import React from 'react'; +import * as S from './AdminReportDetail.styled'; +import AdminTitle from '../../common/admin/title/AdminTitle'; +import Avatar from '../../common/avatar/Avatar'; +import defaultImg from '../../../assets/defaultImg.png'; +import ReportCheckBox from '../../common/reportCheckBox/ReportCheckBox'; +import ScrollPreventor from '../../common/modal/ScrollPreventor'; +import { Link, useParams } from 'react-router-dom'; +import { useGetReportDetail } from '../../../hooks/admin/useAdminReportDetail'; +import { Spinner } from '../../common/loadingSpinner/LoadingSpinner.styled'; +import { formatDate } from '../../../util/formatDate'; +import { useHandleUser } from '../../../hooks/admin/useHandleUser'; +import { useModal } from '../../../hooks/useModal'; +import Modal from '../../common/modal/Modal'; + +export default function AdminReportDetail() { + const { id: reportId } = useParams(); + + const { reportDetailData, isLoading, isFetching } = useGetReportDetail( + Number(reportId) + ); + + const { isOpen, message, handleModalOpen, handleModalClose, handleConfirm } = + useModal(); + + const { handleClickBanButton, handleClickWarningButton } = useHandleUser({ + handleModalOpen, + handleConfirm, + }); + + if (!reportDetailData) { + return 신고 상세 정보를 찾을 수 없습니다.; + } + + if (isLoading || isFetching) { + return ( + + + + ); + } + + const linkUrl = ( + id: number, + location: 'USER' | 'PROJECT' | 'COMMENT' | 'RECOMMENT' + ) => { + if ( + location === 'PROJECT' || + location === 'COMMENT' || + location === 'RECOMMENT' + ) { + return `/project-detail/${id}`; + } else { + return `/user/${id}`; + } + }; + + return ( + + + + + + + + + {reportDetailData.reporter.nickname} + + + + 신고 + + + + + + {reportDetailData.reportedUser.nickname} + + + + + + + + {formatDate(reportDetailData.reportedAt)} + + + + {reportDetailData.reason} + + + + + + 검토 하기 + + + + + handleClickWarningButton( + e, + reportDetailData.reportedUser.userId + ) + } + > + 경고 + + + handleClickBanButton(e, reportDetailData.reportedUser.userId) + } + > + 강퇴 + + + + + + + {message} + + + ); +} diff --git a/src/components/admin/previewComponent/reportsPreview/ReportsPreview.tsx b/src/components/admin/previewComponent/reportsPreview/ReportsPreview.tsx index eda68d8d..05cf9466 100644 --- a/src/components/admin/previewComponent/reportsPreview/ReportsPreview.tsx +++ b/src/components/admin/previewComponent/reportsPreview/ReportsPreview.tsx @@ -1,12 +1,12 @@ import * as S from './ReportsPreview.styled'; -import { useGetAllReports } from '../../../../hooks/admin/useGetAllReports'; import Avatar from '../../../common/avatar/Avatar'; import arrow_right from '../../../../assets/ArrowRight.svg'; import { ADMIN_ROUTE } from '../../../../constants/routes'; import { Spinner } from '../../../common/loadingSpinner/LoadingSpinner.styled'; +import { useGetAllReportsPreview } from '../../../../hooks/admin/useGetAllReportsPreview'; const ReportsPreview = () => { - const { allReportsData, isLoading, isFetching } = useGetAllReports(); + const { allReportsData, isLoading, isFetching } = useGetAllReportsPreview(); if (isLoading || isFetching) { return ( @@ -19,22 +19,22 @@ const ReportsPreview = () => { return ( {allReportsData?.map((report) => ( - - - + + + - {report.reportedCount} 번 + {report.warning} 번 {report.category} - {report.createdAt} + {report.reportedAt} | - - {report.isDone ? '검토 완료' : '검토 미완료'} + + {report.imposed ? '검토 완료' : '검토 미완료'} - + 신고 상세 보기 diff --git a/src/components/common/reportCheckBox/ReportCheckBox.styled.ts b/src/components/common/reportCheckBox/ReportCheckBox.styled.ts new file mode 100644 index 00000000..46d676f8 --- /dev/null +++ b/src/components/common/reportCheckBox/ReportCheckBox.styled.ts @@ -0,0 +1,31 @@ +import styled from 'styled-components'; + +export const Container = styled.div` + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1rem; + margin-bottom: 1.5rem; +`; + +export const CheckRow = styled.div` + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1.5rem; +`; + +export const CheckItem = styled.div``; + +export const CheckInput = styled.input` + accent-color: red; + margin-right: 0.5rem; + cursor: pointer; +`; + +export const CheckContent = styled.label` + font-size: 0.9rem; +`; + +export const ErrorMessage = styled.p` + font-size: 11px; + color: ${({ theme }) => theme.color.red}; +`; diff --git a/src/components/common/reportCheckBox/ReportCheckBox.tsx b/src/components/common/reportCheckBox/ReportCheckBox.tsx new file mode 100644 index 00000000..4ee0f258 --- /dev/null +++ b/src/components/common/reportCheckBox/ReportCheckBox.tsx @@ -0,0 +1,37 @@ +import * as S from './ReportCheckBox.styled'; +import { reasons } from '../../../constants/user/reportConstants'; + +interface ReportCheckBoxProps { + isNotExist?: boolean; + isAdmin?: boolean; + selectedCheckbox?: string[]; +} + +const ReportCheckBox = ({ + isNotExist = false, + isAdmin = false, + selectedCheckbox, +}: ReportCheckBoxProps) => { + return ( + + {reasons.map((reason) => ( + + + {reason} + + ))} + + {isNotExist && ( + 신고 사유를 하나 이상 선택해주세요. + )} + + ); +}; + +export default ReportCheckBox; diff --git a/src/components/user/reportComponent/ReportModal.tsx b/src/components/user/reportComponent/ReportModal.tsx index 537714d8..b91c222e 100644 --- a/src/components/user/reportComponent/ReportModal.tsx +++ b/src/components/user/reportComponent/ReportModal.tsx @@ -1,8 +1,8 @@ import { postReport } from '../../../api/report.api'; -import { reasons } from '../../../constants/user/reportConstants'; import Avatar from '../../common/avatar/Avatar'; import Button from '../../common/Button/Button'; import ScrollPreventor from '../../common/modal/ScrollPreventor'; +import ReportCheckBox from '../../common/reportCheckBox/ReportCheckBox'; import * as S from './ReportModal.styled'; import { useRef, useState } from 'react'; @@ -80,22 +80,7 @@ const ReportModal = ({ 신고 사유 - - {reasons.map((reason) => ( - - - - {reason} - - - ))} - - {isNotExist && ( - - 신고 사유를 하나 이상 선택해주세요. - - )} - + 상세 작성(생략 가능) { + const { + data: reportDetailData, + isLoading, + isFetching, + } = useQuery({ + queryKey: [ReportData.reportDetail], + queryFn: () => getReportDetail(reportId), + }); + + return { reportDetailData, isLoading, isFetching }; +}; diff --git a/src/hooks/admin/useAdminReports.ts b/src/hooks/admin/useAdminReports.ts new file mode 100644 index 00000000..5deb67d1 --- /dev/null +++ b/src/hooks/admin/useAdminReports.ts @@ -0,0 +1,29 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { AxiosError } from 'axios'; +import { ReportData } from '../queries/keys'; +import { ADMIN_MODAL_MESSAGE } from '../../constants/admin/adminModal'; +import { postDeleteReport } from '../../api/admin/report.api'; + +interface useAdminReportsProps { + handleModalOpen: (message: string) => void; +} + +export const useAdminReports = ({ handleModalOpen }: useAdminReportsProps) => { + const queryClient = useQueryClient(); + + const postDelete = useMutation({ + mutationFn: (id: number) => postDeleteReport(id), + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: [ReportData.allReports], + }); + queryClient.invalidateQueries({ + queryKey: [ReportData.allReportsPreview], + }); + handleModalOpen(ADMIN_MODAL_MESSAGE.banSuccess); + }, + onError: () => {}, + }); + + return { postDelete }; +}; diff --git a/src/hooks/admin/useGetAllReports.ts b/src/hooks/admin/useGetAllReports.ts index 951f30b9..93e6e8c9 100644 --- a/src/hooks/admin/useGetAllReports.ts +++ b/src/hooks/admin/useGetAllReports.ts @@ -1,15 +1,16 @@ import { useQuery } from '@tanstack/react-query'; import { ReportData } from '../queries/keys'; -import { getAllReports } from '../../api/report.api'; +import { getAllReports } from '../../api/admin/report.api'; +import { SearchType } from '../../models/search'; -export const useGetAllReports = () => { +export const useGetAllReports = (searchUnit: SearchType) => { const { data: allReportsData, isLoading, isFetching, } = useQuery({ - queryKey: [ReportData.allReports], - queryFn: () => getAllReports(), + queryKey: [ReportData.allReports, searchUnit.keyword, searchUnit.page], + queryFn: () => getAllReports(searchUnit), select: (allReports) => allReports.slice(0, 5), }); diff --git a/src/hooks/admin/useGetAllReportsPreview.ts b/src/hooks/admin/useGetAllReportsPreview.ts new file mode 100644 index 00000000..8756a1ab --- /dev/null +++ b/src/hooks/admin/useGetAllReportsPreview.ts @@ -0,0 +1,17 @@ +import { useQuery } from '@tanstack/react-query'; +import { ReportData } from '../queries/keys'; +import { getAllReportsPreview } from '../../api/admin/report.api'; + +export const useGetAllReportsPreview = () => { + const { + data: allReportsData, + isLoading, + isFetching, + } = useQuery({ + queryKey: [ReportData.allReportsPreview], + queryFn: () => getAllReportsPreview(), + select: (allReports) => allReports.slice(0, 5), + }); + + return { allReportsData, isLoading, isFetching }; +}; diff --git a/src/hooks/admin/useHandleUser.ts b/src/hooks/admin/useHandleUser.ts new file mode 100644 index 00000000..ba3e2dce --- /dev/null +++ b/src/hooks/admin/useHandleUser.ts @@ -0,0 +1,56 @@ +import { useAdminReports } from './useAdminReports'; +import { useHandleUserApi } from './useHandleUserApi'; + +interface useBanUserProps { + handleModalOpen: (message: string) => void; + handleConfirm: () => void; +} + +export const useHandleUser = ({ + handleModalOpen, + handleConfirm, +}: useBanUserProps) => { + const { postBan, postWarning } = useHandleUserApi({ + handleModalOpen, + handleConfirm, + }); + + const { postDelete } = useAdminReports({ handleModalOpen }); + + const handleClickBanButton = ( + e: React.MouseEvent, + userId: number + ) => { + e.preventDefault(); + e.stopPropagation(); + if (confirm('정말 강퇴 하시겠습니까?')) { + postBan.mutate(userId); + } + }; + + const handleClickWarningButton = ( + e: React.MouseEvent, + userId: number + ) => { + e.preventDefault(); + e.stopPropagation(); + if (confirm('정말 경고 하시겠습니까?')) { + postWarning.mutate(userId); + } + }; + + const handleClickDeleteButton = ( + e: React.MouseEvent, + id: number + ) => { + e.preventDefault(); + e.stopPropagation(); + postDelete.mutate(id); + }; + + return { + handleClickBanButton, + handleClickWarningButton, + handleClickDeleteButton, + }; +}; diff --git a/src/hooks/admin/useHandleUserApi.ts b/src/hooks/admin/useHandleUserApi.ts new file mode 100644 index 00000000..bdb647e2 --- /dev/null +++ b/src/hooks/admin/useHandleUserApi.ts @@ -0,0 +1,56 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { postBanUser, postWarningUser } from '../../api/admin/user.api'; +import { ADMIN_MODAL_MESSAGE } from '../../constants/admin/adminModal'; +import { ReportData, UserData } from '../queries/keys'; +import { AxiosError } from 'axios'; + +interface useBanUserProps { + handleModalOpen: (message: string) => void; + handleConfirm: () => void; +} + +export const useHandleUserApi = ({ handleModalOpen }: useBanUserProps) => { + const queryClient = useQueryClient(); + + const postBan = useMutation({ + mutationFn: (id: number) => postBanUser(id), + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: [UserData.allUser], + }); + queryClient.invalidateQueries({ + queryKey: [UserData.allUserPreview], + }); + queryClient.invalidateQueries({ + queryKey: [ReportData.allReports], + }); + queryClient.invalidateQueries({ + queryKey: [ReportData.allReportsPreview], + }); + handleModalOpen(ADMIN_MODAL_MESSAGE.banSuccess); + }, + onError: () => {}, + }); + + const postWarning = useMutation({ + mutationFn: (id: number) => postWarningUser(id), + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: [UserData.allUser], + }); + queryClient.invalidateQueries({ + queryKey: [UserData.allUserPreview], + }); + queryClient.invalidateQueries({ + queryKey: [ReportData.allReports], + }); + queryClient.invalidateQueries({ + queryKey: [ReportData.allReportsPreview], + }); + handleModalOpen(ADMIN_MODAL_MESSAGE.banSuccess); + }, + onError: () => {}, + }); + + return { postBan, postWarning }; +}; diff --git a/src/hooks/queries/keys.ts b/src/hooks/queries/keys.ts index 8ec9d37d..ec42a7aa 100644 --- a/src/hooks/queries/keys.ts +++ b/src/hooks/queries/keys.ts @@ -56,6 +56,7 @@ export const Inquiries = { export const CustomerService = { faq: 'faq', notice: 'notice', + noticePreview: 'noticePreview', noticeDetail: 'noticeDetail', inquiryDetail: 'inquiryDetail', } as const; @@ -63,6 +64,7 @@ export const CustomerService = { export const ReportData = { allReports: ['AllReports'], allReportsPreview: ['AllReportsPreview'], + reportDetail: ['ReportDetail'], } as const; export const UserData = { diff --git a/src/models/admin/userDetail/reportDetail.ts b/src/models/admin/userDetail/reportDetail.ts new file mode 100644 index 00000000..062fe679 --- /dev/null +++ b/src/models/admin/userDetail/reportDetail.ts @@ -0,0 +1,22 @@ +import { ApiCommonType } from '../../apiCommon'; + +export interface UserData { + userId: number; + nickname: string; + img: string; +} + +export interface ReportDetail { + reportId: number; + reporter: UserData; + reportedUser: UserData; + reportedAt: string; + reason: string; + category: 'ALL' | 'ABUSE' | 'SEXUAL' | 'AD' | 'COPYRIGHT' | 'ETC'; + location: 'USER' | 'PROJECT' | 'COMMENT' | 'RECOMMENT'; + locationId: number; +} + +export interface ApiReportDetail extends ApiCommonType { + data: ReportDetail; +} diff --git a/src/models/report.ts b/src/models/report.ts index 783b3fbb..e8fe5d43 100644 --- a/src/models/report.ts +++ b/src/models/report.ts @@ -1,4 +1,4 @@ -import { type ApiCommonType, type User } from './apiCommon'; +import type { ApiCommonType } from './apiCommon'; export interface ApiPostContent { reportTargetId: number; @@ -12,10 +12,12 @@ export interface ApiAllReports extends ApiCommonType { } export interface AllReports { - id: number; - reportedCount: number; + reportId: number; + userId: number; + nickname: string; + profileImg: string; + warning: number; category: string; - user: User; - isDone: boolean; - createdAt: string; + reportedAt: string; + imposed: boolean; } diff --git a/src/pages/admin/adminReports/AdminReports.styled.ts b/src/pages/admin/adminReports/AdminReports.styled.ts new file mode 100644 index 00000000..dd075096 --- /dev/null +++ b/src/pages/admin/adminReports/AdminReports.styled.ts @@ -0,0 +1,97 @@ +import styled from 'styled-components'; +import Button from '../../../components/common/Button/Button'; +import { Link } from 'react-router-dom'; + +export const Container = styled.div` + display: flex; + flex-direction: column; +`; + +export const SpinnerWrapper = styled.div` + display: flex; + justify-content: center; + align-items: center; + height: 100%; +`; + +export const SearchBarWrapper = styled.div` + width: 100%; + margin-top: 120px; +`; + +export const List = styled.div` + width: 90%; + height: 600px; + border: 1px solid ${({ theme }) => theme.color.border}; + border-radius: ${({ theme }) => theme.borderRadius.primary}; + padding: 45px; + margin: 30px auto 0; + overflow-y: auto; + padding-bottom: 30px; + + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-thumb { + background-color: #ccc; + border-radius: 4px; + } +`; + +export const Item = styled(Link)` + display: flex; + align-items: center; + margin-bottom: 24px; +`; + +export const ProfileImg = styled.div` + flex-shrink: 0; + text-align: center; + margin-right: 16px; +`; + +export const NickName = styled.p` + ${({ theme }) => theme.heading.xsSmall};ß + margin-top: 3px; + opacity: 50%; +`; + +export const ContentArea = styled.div` + display: flex; + flex: 1; + flex-direction: column; + justify-content: center; + align-items: flex-start; + margin-left: 35px; + margin-bottom: 30px; +`; + +export const Category = styled.p` + ${({ theme }) => theme.heading.small}; + opacity: 50%; +`; + +export const Content = styled.p` + ${({ theme }) => theme.heading.semiSmall}; + font-weight: 500; +`; + +export const ButtonArea = styled.div` + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 10px; +`; + +export const WarningButton = styled(Button)` + background-color: ${({ theme }) => theme.color.red}; +`; + +export const BanButton = styled(Button)``; + +export const DeleteButton = styled.button` + border: 1px solid ${({ theme }) => theme.color.primary}; + border-radius: ${({ theme }) => theme.borderRadius.large}; + margin-left: 10px; +`; diff --git a/src/pages/admin/adminReports/AdminReports.tsx b/src/pages/admin/adminReports/AdminReports.tsx index 2d5490d9..2073b703 100644 --- a/src/pages/admin/adminReports/AdminReports.tsx +++ b/src/pages/admin/adminReports/AdminReports.tsx @@ -1,3 +1,109 @@ +import SearchBar from '../../../components/common/admin/searchBar/SearchBar'; +import AdminTitle from '../../../components/common/admin/title/AdminTitle'; +import Avatar from '../../../components/common/avatar/Avatar'; +import useSearchBar from '../../../hooks/admin/useSearchBar'; +import * as S from './AdminReports.styled'; +import defaultImg from '../../../assets/defaultImg.png'; +import { XMarkIcon } from '@heroicons/react/24/outline'; +import Pagination from '../../../components/common/pagination/Pagination'; + +import { useModal } from '../../../hooks/useModal'; +import Modal from '../../../components/common/modal/Modal'; + +import { useGetAllReports } from '../../../hooks/admin/useGetAllReports'; +import { Spinner } from '../../../components/common/loadingSpinner/LoadingSpinner.styled'; +import { useHandleUser } from '../../../hooks/admin/useHandleUser'; + export default function AdminReports() { - return
; + const { searchUnit, value, handleChangePagination, handleGetKeyword } = + useSearchBar(); + const { isOpen, message, handleModalOpen, handleModalClose, handleConfirm } = + useModal(); + + const { allReportsData, isLoading, isFetching } = + useGetAllReports(searchUnit); + + const { + handleClickBanButton, + handleClickWarningButton, + handleClickDeleteButton, + } = useHandleUser({ + handleModalOpen, + handleConfirm, + }); + + if (isLoading || isFetching) { + return ( + + + + ); + } + + return ( + <> + + + + + + + {allReportsData?.map((data) => ( + + + + {data.nickname} + + + {data.category} + + + handleClickWarningButton(e, data.userId)} + > + 경고 + + handleClickBanButton(e, data.userId)} + > + 강퇴 + + + handleClickDeleteButton(e, data.reportId)} + > + + + + + ))} + + + + + {message} + + + ); } From 028cb0ae321a1e3748103fc91b0d2b15b16bb301 Mon Sep 17 00:00:00 2001 From: Cho SeungYeon <111514472+layout-SY@users.noreply.github.com> Date: Sun, 29 Jun 2025 23:10:35 +0900 Subject: [PATCH 2/8] =?UTF-8?q?refactor=20:=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EC=82=AC=ED=95=AD=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/admin/report.api.ts | 2 +- .../user/mypage/activityLog/inquiries/Inquiries.tsx | 4 +++- src/constants/admin/adminModal.ts | 1 + src/hooks/admin/useAdminReportDetail.ts | 2 +- src/hooks/admin/useAdminReports.ts | 1 - src/hooks/admin/useGetAllReports.ts | 1 - src/hooks/admin/useHandleUserApi.ts | 10 +++++----- src/models/report.ts | 1 + src/pages/admin/adminReports/AdminReports.styled.ts | 2 +- src/pages/admin/adminReports/AdminReports.tsx | 11 ++++++++--- 10 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/api/admin/report.api.ts b/src/api/admin/report.api.ts index c9c311bc..29f990e8 100644 --- a/src/api/admin/report.api.ts +++ b/src/api/admin/report.api.ts @@ -27,7 +27,7 @@ export const getReportDetail = async (reportId: number) => { export const getAllReports = async (params: SearchType) => { try { const response = await httpClient.get(`/report`, { params }); - return response.data.data; + return response.data; } catch (e) { console.error(e); throw e; diff --git a/src/components/user/mypage/activityLog/inquiries/Inquiries.tsx b/src/components/user/mypage/activityLog/inquiries/Inquiries.tsx index b20c3022..50f39523 100644 --- a/src/components/user/mypage/activityLog/inquiries/Inquiries.tsx +++ b/src/components/user/mypage/activityLog/inquiries/Inquiries.tsx @@ -29,7 +29,9 @@ export default function Inquiries() { ); - const myInquiriesData = userActivityData as MyInquiries[]; + const myInquiriesData = Array.isArray(userActivityData) + ? (userActivityData as MyInquiries[]) + : []; return ( diff --git a/src/constants/admin/adminModal.ts b/src/constants/admin/adminModal.ts index d1df0446..472a5e59 100644 --- a/src/constants/admin/adminModal.ts +++ b/src/constants/admin/adminModal.ts @@ -6,4 +6,5 @@ export const ADMIN_MODAL_MESSAGE = { writeError: '알수없는 에러가 발생했습니다.', NO_RESULT: '결과가 존재하지 않습니다.', banSuccess: '성공적으로 강퇴 했습니다.', + warningSuccess: '성공적으로 경고 했습니다.', }; diff --git a/src/hooks/admin/useAdminReportDetail.ts b/src/hooks/admin/useAdminReportDetail.ts index d3ff1f75..93e16d9f 100644 --- a/src/hooks/admin/useAdminReportDetail.ts +++ b/src/hooks/admin/useAdminReportDetail.ts @@ -8,7 +8,7 @@ export const useGetReportDetail = (reportId: number) => { isLoading, isFetching, } = useQuery({ - queryKey: [ReportData.reportDetail], + queryKey: [ReportData.reportDetail, reportId], queryFn: () => getReportDetail(reportId), }); diff --git a/src/hooks/admin/useAdminReports.ts b/src/hooks/admin/useAdminReports.ts index 5deb67d1..9fac5622 100644 --- a/src/hooks/admin/useAdminReports.ts +++ b/src/hooks/admin/useAdminReports.ts @@ -22,7 +22,6 @@ export const useAdminReports = ({ handleModalOpen }: useAdminReportsProps) => { }); handleModalOpen(ADMIN_MODAL_MESSAGE.banSuccess); }, - onError: () => {}, }); return { postDelete }; diff --git a/src/hooks/admin/useGetAllReports.ts b/src/hooks/admin/useGetAllReports.ts index 93e6e8c9..641832e9 100644 --- a/src/hooks/admin/useGetAllReports.ts +++ b/src/hooks/admin/useGetAllReports.ts @@ -11,7 +11,6 @@ export const useGetAllReports = (searchUnit: SearchType) => { } = useQuery({ queryKey: [ReportData.allReports, searchUnit.keyword, searchUnit.page], queryFn: () => getAllReports(searchUnit), - select: (allReports) => allReports.slice(0, 5), }); return { allReportsData, isLoading, isFetching }; diff --git a/src/hooks/admin/useHandleUserApi.ts b/src/hooks/admin/useHandleUserApi.ts index bdb647e2..538c7c3f 100644 --- a/src/hooks/admin/useHandleUserApi.ts +++ b/src/hooks/admin/useHandleUserApi.ts @@ -4,12 +4,14 @@ import { ADMIN_MODAL_MESSAGE } from '../../constants/admin/adminModal'; import { ReportData, UserData } from '../queries/keys'; import { AxiosError } from 'axios'; -interface useBanUserProps { +interface useHandleUserApiProps { handleModalOpen: (message: string) => void; handleConfirm: () => void; } -export const useHandleUserApi = ({ handleModalOpen }: useBanUserProps) => { +export const useHandleUserApi = ({ + handleModalOpen, +}: useHandleUserApiProps) => { const queryClient = useQueryClient(); const postBan = useMutation({ @@ -27,9 +29,8 @@ export const useHandleUserApi = ({ handleModalOpen }: useBanUserProps) => { queryClient.invalidateQueries({ queryKey: [ReportData.allReportsPreview], }); - handleModalOpen(ADMIN_MODAL_MESSAGE.banSuccess); + handleModalOpen(ADMIN_MODAL_MESSAGE.warningSuccess); }, - onError: () => {}, }); const postWarning = useMutation({ @@ -49,7 +50,6 @@ export const useHandleUserApi = ({ handleModalOpen }: useBanUserProps) => { }); handleModalOpen(ADMIN_MODAL_MESSAGE.banSuccess); }, - onError: () => {}, }); return { postBan, postWarning }; diff --git a/src/models/report.ts b/src/models/report.ts index e8fe5d43..c46f67f2 100644 --- a/src/models/report.ts +++ b/src/models/report.ts @@ -9,6 +9,7 @@ export interface ApiPostContent { export interface ApiAllReports extends ApiCommonType { data: AllReports[]; + totalPage: number; } export interface AllReports { diff --git a/src/pages/admin/adminReports/AdminReports.styled.ts b/src/pages/admin/adminReports/AdminReports.styled.ts index dd075096..b18b8e1d 100644 --- a/src/pages/admin/adminReports/AdminReports.styled.ts +++ b/src/pages/admin/adminReports/AdminReports.styled.ts @@ -52,7 +52,7 @@ export const ProfileImg = styled.div` `; export const NickName = styled.p` - ${({ theme }) => theme.heading.xsSmall};ß + ${({ theme }) => theme.heading.xsSmall}; margin-top: 3px; opacity: 50%; `; diff --git a/src/pages/admin/adminReports/AdminReports.tsx b/src/pages/admin/adminReports/AdminReports.tsx index 2073b703..a802fe79 100644 --- a/src/pages/admin/adminReports/AdminReports.tsx +++ b/src/pages/admin/adminReports/AdminReports.tsx @@ -13,6 +13,7 @@ import Modal from '../../../components/common/modal/Modal'; import { useGetAllReports } from '../../../hooks/admin/useGetAllReports'; import { Spinner } from '../../../components/common/loadingSpinner/LoadingSpinner.styled'; import { useHandleUser } from '../../../hooks/admin/useHandleUser'; +import { ADMIN_MODAL_MESSAGE } from '../../../constants/admin/adminModal'; export default function AdminReports() { const { searchUnit, value, handleChangePagination, handleGetKeyword } = @@ -32,6 +33,10 @@ export default function AdminReports() { handleConfirm, }); + if (!allReportsData) { + return {ADMIN_MODAL_MESSAGE.NO_RESULT}; + } + if (isLoading || isFetching) { return ( @@ -52,10 +57,10 @@ export default function AdminReports() { /> - {allReportsData?.map((data) => ( + {allReportsData?.data.map((data) => ( - + {data.nickname} @@ -93,7 +98,7 @@ export default function AdminReports() { From 3707476b82e6f8db18d508dce502834c9be1425c Mon Sep 17 00:00:00 2001 From: Cho SeungYeon <111514472+layout-SY@users.noreply.github.com> Date: Sun, 29 Jun 2025 23:11:32 +0900 Subject: [PATCH 3/8] =?UTF-8?q?feat=20:=20=ED=94=84=EB=A1=9C=ED=95=84=20?= =?UTF-8?q?=EC=82=AC=EC=A7=84=EC=9D=B4=20=EC=97=86=EB=8A=94=20=EA=B2=BD?= =?UTF-8?q?=EC=9A=B0=20=EA=B8=B0=EB=B3=B8=20=EC=82=AC=EC=A7=84=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/admin/adminReports/AdminReports.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/pages/admin/adminReports/AdminReports.tsx b/src/pages/admin/adminReports/AdminReports.tsx index a802fe79..1892d0b4 100644 --- a/src/pages/admin/adminReports/AdminReports.tsx +++ b/src/pages/admin/adminReports/AdminReports.tsx @@ -60,7 +60,10 @@ export default function AdminReports() { {allReportsData?.data.map((data) => ( - + {data.nickname} From e35773a300465b796cee5fb77654d1271fcc5699 Mon Sep 17 00:00:00 2001 From: Cho SeungYeon <111514472+layout-SY@users.noreply.github.com> Date: Mon, 30 Jun 2025 21:54:40 +0900 Subject: [PATCH 4/8] =?UTF-8?q?feat=20:=20=EC=8B=A0=EA=B3=A0=20=EA=B2=80?= =?UTF-8?q?=ED=86=A0=EC=97=90=EC=84=9C=20=EC=8B=A0=EA=B3=A0=20=EC=B9=B4?= =?UTF-8?q?=ED=85=8C=EA=B3=A0=EB=A6=AC=20API=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../adminUserReport/AdminReportDetail.tsx | 19 ++++++++----------- .../admin/adminReportCategoryList.ts | 13 +++++++++++++ src/models/admin/userDetail/reportDetail.ts | 4 ++-- src/models/report.ts | 2 +- .../admin/adminReports/AdminReports.styled.ts | 16 +++++++++++----- src/pages/admin/adminReports/AdminReports.tsx | 9 ++++++--- 6 files changed, 41 insertions(+), 22 deletions(-) create mode 100644 src/constants/admin/adminReportCategoryList.ts diff --git a/src/components/admin/adminUserReport/AdminReportDetail.tsx b/src/components/admin/adminUserReport/AdminReportDetail.tsx index d20205a4..b20158ef 100644 --- a/src/components/admin/adminUserReport/AdminReportDetail.tsx +++ b/src/components/admin/adminUserReport/AdminReportDetail.tsx @@ -12,6 +12,7 @@ import { formatDate } from '../../../util/formatDate'; import { useHandleUser } from '../../../hooks/admin/useHandleUser'; import { useModal } from '../../../hooks/useModal'; import Modal from '../../common/modal/Modal'; +import { REPORT_CATEGORY_LIST } from '../../../constants/admin/adminReportCategoryList'; export default function AdminReportDetail() { const { id: reportId } = useParams(); @@ -65,8 +66,8 @@ export default function AdminReportDetail() { REPORT_CATEGORY_LIST[num - 1] + )} /> {formatDate(reportDetailData.reportedAt)} diff --git a/src/constants/admin/adminReportCategoryList.ts b/src/constants/admin/adminReportCategoryList.ts new file mode 100644 index 00000000..02696104 --- /dev/null +++ b/src/constants/admin/adminReportCategoryList.ts @@ -0,0 +1,13 @@ +export const REPORT_CATEGORY_LIST = [ + '욕설/비속어', + '성적내용/음란물', + '광고/스팸', + '사기/부정행위', + '도배/스팸', + '혐오/차별발언', + '사생활 침해', + '저작권 침해', + '기타', +] as const; + +export type ReportCategory = (typeof REPORT_CATEGORY_LIST)[number]; diff --git a/src/models/admin/userDetail/reportDetail.ts b/src/models/admin/userDetail/reportDetail.ts index 062fe679..0a065ee0 100644 --- a/src/models/admin/userDetail/reportDetail.ts +++ b/src/models/admin/userDetail/reportDetail.ts @@ -3,7 +3,7 @@ import { ApiCommonType } from '../../apiCommon'; export interface UserData { userId: number; nickname: string; - img: string; + profileImg: string; } export interface ReportDetail { @@ -12,7 +12,7 @@ export interface ReportDetail { reportedUser: UserData; reportedAt: string; reason: string; - category: 'ALL' | 'ABUSE' | 'SEXUAL' | 'AD' | 'COPYRIGHT' | 'ETC'; + category: number[]; location: 'USER' | 'PROJECT' | 'COMMENT' | 'RECOMMENT'; locationId: number; } diff --git a/src/models/report.ts b/src/models/report.ts index c46f67f2..df339e4d 100644 --- a/src/models/report.ts +++ b/src/models/report.ts @@ -18,7 +18,7 @@ export interface AllReports { nickname: string; profileImg: string; warning: number; - category: string; + category: number[]; reportedAt: string; imposed: boolean; } diff --git a/src/pages/admin/adminReports/AdminReports.styled.ts b/src/pages/admin/adminReports/AdminReports.styled.ts index b18b8e1d..cb84af9f 100644 --- a/src/pages/admin/adminReports/AdminReports.styled.ts +++ b/src/pages/admin/adminReports/AdminReports.styled.ts @@ -46,9 +46,12 @@ export const Item = styled(Link)` `; export const ProfileImg = styled.div` - flex-shrink: 0; - text-align: center; + width: 80px; + display: flex; + flex-direction: column; + align-items: center; margin-right: 16px; + flex-shrink: 0; `; export const NickName = styled.p` @@ -60,14 +63,17 @@ export const NickName = styled.p` export const ContentArea = styled.div` display: flex; flex: 1; - flex-direction: column; - justify-content: center; + justify-content: flex-start; align-items: flex-start; margin-left: 35px; - margin-bottom: 30px; + + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; `; export const Category = styled.p` + padding: 5px 10px; ${({ theme }) => theme.heading.small}; opacity: 50%; `; diff --git a/src/pages/admin/adminReports/AdminReports.tsx b/src/pages/admin/adminReports/AdminReports.tsx index 1892d0b4..8b96a77a 100644 --- a/src/pages/admin/adminReports/AdminReports.tsx +++ b/src/pages/admin/adminReports/AdminReports.tsx @@ -6,14 +6,13 @@ import * as S from './AdminReports.styled'; import defaultImg from '../../../assets/defaultImg.png'; import { XMarkIcon } from '@heroicons/react/24/outline'; import Pagination from '../../../components/common/pagination/Pagination'; - import { useModal } from '../../../hooks/useModal'; import Modal from '../../../components/common/modal/Modal'; - import { useGetAllReports } from '../../../hooks/admin/useGetAllReports'; import { Spinner } from '../../../components/common/loadingSpinner/LoadingSpinner.styled'; import { useHandleUser } from '../../../hooks/admin/useHandleUser'; import { ADMIN_MODAL_MESSAGE } from '../../../constants/admin/adminModal'; +import { REPORT_CATEGORY_LIST } from '../../../constants/admin/adminReportCategoryList'; export default function AdminReports() { const { searchUnit, value, handleChangePagination, handleGetKeyword } = @@ -67,7 +66,11 @@ export default function AdminReports() { {data.nickname} - {data.category} + {data.category.map((category) => ( + + "{REPORT_CATEGORY_LIST[category - 1]}" + + ))} Date: Mon, 30 Jun 2025 22:10:58 +0900 Subject: [PATCH 5/8] =?UTF-8?q?refactor=20:=20model=20import=EC=97=90=20ty?= =?UTF-8?q?pe=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/admin/report.api.ts | 6 +++--- src/models/admin/userDetail/reportDetail.ts | 2 +- src/models/alarm.ts | 2 +- src/models/applicant.ts | 4 ++-- src/models/comment.ts | 2 +- src/models/customerService.ts | 2 +- src/models/manageMyProject.ts | 4 ++-- src/models/projectDetail.ts | 2 +- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/api/admin/report.api.ts b/src/api/admin/report.api.ts index 29f990e8..682c7658 100644 --- a/src/api/admin/report.api.ts +++ b/src/api/admin/report.api.ts @@ -1,6 +1,6 @@ -import { ApiReportDetail } from '../../models/admin/userDetail/reportDetail'; -import { ApiAllReports } from '../../models/report'; -import { SearchType } from '../../models/search'; +import type { ApiReportDetail } from '../../models/admin/userDetail/reportDetail'; +import type { ApiAllReports } from '../../models/report'; +import type { SearchType } from '../../models/search'; import { httpClient } from '../http.api'; export const postDeleteReport = async (id: number) => { diff --git a/src/models/admin/userDetail/reportDetail.ts b/src/models/admin/userDetail/reportDetail.ts index 0a065ee0..d9ec8ccc 100644 --- a/src/models/admin/userDetail/reportDetail.ts +++ b/src/models/admin/userDetail/reportDetail.ts @@ -1,4 +1,4 @@ -import { ApiCommonType } from '../../apiCommon'; +import type { ApiCommonType } from '../../apiCommon'; export interface UserData { userId: number; diff --git a/src/models/alarm.ts b/src/models/alarm.ts index e8ea2397..58330006 100644 --- a/src/models/alarm.ts +++ b/src/models/alarm.ts @@ -1,4 +1,4 @@ -import { ApiCommonType } from './apiCommon'; +import type { ApiCommonType } from './apiCommon'; export interface ApiAlarmList extends ApiCommonType { data: Alarm[] | null; diff --git a/src/models/applicant.ts b/src/models/applicant.ts index 633c44c3..ce325fa3 100644 --- a/src/models/applicant.ts +++ b/src/models/applicant.ts @@ -1,5 +1,5 @@ -import { ApiCommonType } from './apiCommon'; -import { SkillTag } from './tags'; +import type { ApiCommonType } from './apiCommon'; +import type { SkillTag } from './tags'; export interface ApplicantInfo { userId: number; diff --git a/src/models/comment.ts b/src/models/comment.ts index ffe0aec5..4c57d0ff 100644 --- a/src/models/comment.ts +++ b/src/models/comment.ts @@ -1,4 +1,4 @@ -import { ApiCommonType, User } from './apiCommon'; +import type { ApiCommonType, User } from './apiCommon'; export interface GetCommentType extends ApiCommonType { data: CommentType[]; diff --git a/src/models/customerService.ts b/src/models/customerService.ts index c7ded677..17bda365 100644 --- a/src/models/customerService.ts +++ b/src/models/customerService.ts @@ -1,4 +1,4 @@ -import { ApiCommonType } from './apiCommon'; +import type { ApiCommonType } from './apiCommon'; export interface FAQ { id: number; diff --git a/src/models/manageMyProject.ts b/src/models/manageMyProject.ts index 249eea34..4e40bf78 100644 --- a/src/models/manageMyProject.ts +++ b/src/models/manageMyProject.ts @@ -1,5 +1,5 @@ -import { ApiCommonType } from './apiCommon'; -import { SkillTag } from './tags'; +import type { ApiCommonType } from './apiCommon'; +import type { SkillTag } from './tags'; export interface ManagedProject { id: number; diff --git a/src/models/projectDetail.ts b/src/models/projectDetail.ts index de1a594a..d3cc56c7 100644 --- a/src/models/projectDetail.ts +++ b/src/models/projectDetail.ts @@ -1,4 +1,4 @@ -import { ApiCommonType, User } from './apiCommon'; +import type { ApiCommonType, User } from './apiCommon'; import type { MethodTag, PositionTag, SkillTag } from './tags'; export interface ProjectSkillTagList { From bdf5e6650653d72a916365ca4d5f0d7e73a66b09 Mon Sep 17 00:00:00 2001 From: Cho SeungYeon <111514472+layout-SY@users.noreply.github.com> Date: Mon, 30 Jun 2025 22:11:29 +0900 Subject: [PATCH 6/8] =?UTF-8?q?feat=20:=20=EB=AC=B8=EC=9D=98=EB=B3=B4?= =?UTF-8?q?=EA=B8=B0=20preview=20=EB=B3=84=EB=8F=84=20=ED=9B=85=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/customerService.api.ts | 11 +++++++++++ .../noticePreview/NoticePreview.tsx | 11 ++++------- src/hooks/admin/useGetAllNoticePreview.ts | 19 +++++++++++++++++++ 3 files changed, 34 insertions(+), 7 deletions(-) create mode 100644 src/hooks/admin/useGetAllNoticePreview.ts diff --git a/src/api/customerService.api.ts b/src/api/customerService.api.ts index 19483fb9..56b1ac1f 100644 --- a/src/api/customerService.api.ts +++ b/src/api/customerService.api.ts @@ -29,6 +29,17 @@ export const getNotice = async (params: NoticeSearch) => { } }; +export const getNoticePreview = async () => { + try { + const response = await httpClient.get(`/notice`); + + return response.data.data; + } catch (e) { + console.error(e); + throw e; + } +}; + export const getNoticeDetail = async (id: string) => { try { const response = await httpClient.get(`/notice/${id}`); diff --git a/src/components/admin/previewComponent/noticePreview/NoticePreview.tsx b/src/components/admin/previewComponent/noticePreview/NoticePreview.tsx index e83fe5fe..fc6ca315 100644 --- a/src/components/admin/previewComponent/noticePreview/NoticePreview.tsx +++ b/src/components/admin/previewComponent/noticePreview/NoticePreview.tsx @@ -1,13 +1,10 @@ import * as S from './NoticePreview.styled'; -import { useGetNotice } from '../../../../hooks/user/useGetNotice'; import line from '../../../../assets/line.svg'; import { Spinner } from '../../../common/loadingSpinner/LoadingSpinner.styled'; +import { useGetNoticePreview } from '../../../../hooks/admin/useGetAllNoticePreview'; const NoticePreview = () => { - const { noticeData, isLoading, isFetching } = useGetNotice({ - keyword: '', - page: 1, - }); + const { noticeData, isLoading, isFetching } = useGetNoticePreview(); if (isLoading || isFetching) { return ( @@ -17,13 +14,13 @@ const NoticePreview = () => { ); } - if (!noticeData?.notices || noticeData.notices.length === 0) { + if (!noticeData) { return 공지사힝이 없습니다.; } return ( - {noticeData.notices.map((notice) => ( + {noticeData.map((notice) => ( {notice.title} diff --git a/src/hooks/admin/useGetAllNoticePreview.ts b/src/hooks/admin/useGetAllNoticePreview.ts new file mode 100644 index 00000000..b4228ea1 --- /dev/null +++ b/src/hooks/admin/useGetAllNoticePreview.ts @@ -0,0 +1,19 @@ +import { useQuery } from '@tanstack/react-query'; +import { CustomerService } from '../queries/keys'; +import { getNoticePreview } from '../../api/customerService.api'; + +export const useGetNoticePreview = () => { + const { + data: noticeData, + isLoading, + isFetching, + } = useQuery({ + queryKey: [CustomerService.noticePreview], + queryFn: () => getNoticePreview(), + select: (notice) => notice.notices.slice(0, 5), + staleTime: Infinity, + gcTime: Infinity, + }); + + return { noticeData, isLoading, isFetching }; +}; From 76e991c2f31ab192bdc067af3e64850f2b95b72f Mon Sep 17 00:00:00 2001 From: Cho SeungYeon <111514472+layout-SY@users.noreply.github.com> Date: Mon, 30 Jun 2025 22:18:04 +0900 Subject: [PATCH 7/8] =?UTF-8?q?refactor=20:=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EC=82=AC=ED=95=AD=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/admin/adminReports/AdminReports.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/pages/admin/adminReports/AdminReports.tsx b/src/pages/admin/adminReports/AdminReports.tsx index 8b96a77a..3d8bc99a 100644 --- a/src/pages/admin/adminReports/AdminReports.tsx +++ b/src/pages/admin/adminReports/AdminReports.tsx @@ -66,9 +66,12 @@ export default function AdminReports() { {data.nickname} - {data.category.map((category) => ( - - "{REPORT_CATEGORY_LIST[category - 1]}" + {data.category.map((category, index) => ( + + " + {REPORT_CATEGORY_LIST[category - 1] || + '알 수 없는 카테고리'} + " ))} From da050aa4f159c86e0c5e3f8d048921e731ebe1a5 Mon Sep 17 00:00:00 2001 From: Cho SeungYeon <111514472+layout-SY@users.noreply.github.com> Date: Tue, 1 Jul 2025 22:16:29 +0900 Subject: [PATCH 8/8] =?UTF-8?q?refactor=20:=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EC=82=AC=ED=95=AD=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/common/reportCheckBox/ReportCheckBox.tsx | 6 +++--- src/constants/user/reportConstants.ts | 2 +- src/hooks/admin/useGetAllReports.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/common/reportCheckBox/ReportCheckBox.tsx b/src/components/common/reportCheckBox/ReportCheckBox.tsx index 4ee0f258..626f5b46 100644 --- a/src/components/common/reportCheckBox/ReportCheckBox.tsx +++ b/src/components/common/reportCheckBox/ReportCheckBox.tsx @@ -1,5 +1,5 @@ import * as S from './ReportCheckBox.styled'; -import { reasons } from '../../../constants/user/reportConstants'; +import { REASON_LIST } from '../../../constants/user/reportConstants'; interface ReportCheckBoxProps { isNotExist?: boolean; @@ -14,8 +14,8 @@ const ReportCheckBox = ({ }: ReportCheckBoxProps) => { return ( - {reasons.map((reason) => ( - + {REASON_LIST.map((reason, index) => ( + { const {