diff --git a/src/api/services/group/group.api.ts b/src/api/services/group/group.api.ts index ae6e7da3..b2a09924 100644 --- a/src/api/services/group/group.api.ts +++ b/src/api/services/group/group.api.ts @@ -141,21 +141,6 @@ export const useGroupRanking = ({ groupId }: { groupId: number }) => { }) } -export const getGroupQuestions = async ( - groupId: string, - status: 'READY' | 'APPROVED' | 'REJECTED', - page: number, - size: number -) => { - const response = await authorizationInstance.get( - `/api/group/${groupId}/question`, - { - params: { status, page, size }, - } - ) - return response.data -} - export const approveGroupQuestion = async ( groupId: string, questionId: number, diff --git a/src/api/services/question/group.api.ts b/src/api/services/question/group.api.ts index 206059a0..f7eeed21 100644 --- a/src/api/services/question/group.api.ts +++ b/src/api/services/question/group.api.ts @@ -1,8 +1,21 @@ -import { useSuspenseQuery } from '@tanstack/react-query' +import { useQuery, useSuspenseInfiniteQuery } from '@tanstack/react-query' import { authorizationInstance } from '@/api/instance' +import { appendParamsToUrl } from '@/api/utils/common/appendParamsToUrl' +import { PagingRequestParams } from '@/types' + +interface Question { + questionId: number + questionContent: string + status: 'READY' | 'APPROVED' | 'REJECTED' +} export type GroupQuestionResponse = { + page: number + totalPages: number + content: Question[] + size: number + totalElements: number questions: { questionId: number content: string @@ -15,20 +28,6 @@ export type GroupQuestionResponse = { }[] } -const getGroupQuestion = async (groupId: number) => { - const response = await authorizationInstance.get( - `/api/group/${groupId}/question/random` - ) - - return response.data -} - -export const useGroupQuestion = (groupId: number) => { - return useSuspenseQuery({ - queryKey: ['group', groupId, 'question', 'random'], - queryFn: () => getGroupQuestion(groupId), - }) -} export type CreateGroupQuestionPayload = { groupId: number content: string @@ -39,4 +38,71 @@ export const createGroupQuestion = async ( ): Promise => { const response = await authorizationInstance.post('/api/group/question', data) return response.data.message -} \ No newline at end of file +} + +const getGroupQuestions = async ( + groupId: string, + status: 'READY' | 'APPROVED' | 'REJECTED', + page: number, + size: number +) => { + const response = await authorizationInstance.get( + `/api/group/${groupId}/question`, + { + params: { status, page, size }, + } + ) + return response.data +} + +type GroupQuestionParams = { + groupId: string + status: 'READY' | 'APPROVED' | 'REJECTED' +} & PagingRequestParams + +const getQuestionPaging = async ( + groupId: number, + params: GroupQuestionParams +) => { + const response = await authorizationInstance.get( + appendParamsToUrl(`/api/group/${groupId}/question`, params) + ) + const { data } = response + return { + data, + nextPageToken: + data.page !== data.totalPages - 1 + ? (data.page + 1).toString() + : undefined, + } +} + +interface GroupQuestionPagingProps extends PagingRequestParams { + initPageToken?: string + groupId: string + status: 'READY' | 'APPROVED' | 'REJECTED' +} +export const useQuestionPaging = ({ + size = 10, + sort, + groupId, + status, + initPageToken, +}: GroupQuestionPagingProps) => { + return useSuspenseInfiniteQuery({ + queryKey: ['question', initPageToken], + queryFn: ({ pageParam = initPageToken }) => + getQuestionPaging(Number(pageParam), { size, sort, groupId, status }), + initialPageParam: initPageToken, + getNextPageParam: (lastPage) => lastPage.nextPageToken, + }) +} + +export const useGroupQuestion = ({ groupId, status }: GroupQuestionParams) => { + return useQuery({ + queryKey: ['groupQuestions', groupId, status], + queryFn: () => getGroupQuestions(groupId, status, 0, 10), + enabled: !!groupId, + staleTime: 0, + }) +} diff --git a/src/components/RankingGraph/index.tsx b/src/components/RankingGraph/index.tsx index 07ec7ef6..00f0639f 100644 --- a/src/components/RankingGraph/index.tsx +++ b/src/components/RankingGraph/index.tsx @@ -38,12 +38,12 @@ export const RankingGraph = ({ rank }: RankingGraphProps) => { {/* 왼쪽 그래프 */} - + {sortedRank.map((item) => { const percentage = (item.count / maxAmount) * 100 @@ -111,9 +111,8 @@ export const RankingGraph = ({ rank }: RankingGraphProps) => { /> )} {!item.imageSrc &&
} - + {' '} - {/* 이미지 높이에 맞춰 텍스트 위아래 정렬 */} {item.title} diff --git a/src/pages/GroupPage/Management/Questions/QuestionList/index.tsx b/src/pages/GroupPage/Management/Questions/QuestionList/index.tsx new file mode 100644 index 00000000..980ece34 --- /dev/null +++ b/src/pages/GroupPage/Management/Questions/QuestionList/index.tsx @@ -0,0 +1,61 @@ +import { Box, Radio, RadioGroup, Stack, Text } from '@chakra-ui/react' + +import { Loading } from '@/components/Loading' + +export interface Question { + questionId: number + questionContent: string + status: 'READY' | 'APPROVED' | 'REJECTED' +} + +interface QuestionListProps { + questions: Question[] + isLoading: boolean + isError: boolean + onStatusChange: (questionId: number, approve: boolean) => void +} + +export const QuestionList = ({ + isLoading, + isError, + questions, + onStatusChange, +}: QuestionListProps) => { + if (isLoading) return + if (isError) return 질문을 불러오는 데 실패했습니다. + if (questions.length === 0) return 질문이 없습니다. + + return ( + + + {questions.map((question) => ( + + + {question.questionContent} + + { + const approve = value === 'APPROVED' + onStatusChange(question.questionId, approve) + }} + value={question.status} + > + + + + + + + ))} + + + ) +} diff --git a/src/pages/GroupPage/Management/Questions/StatusButton/index.tsx b/src/pages/GroupPage/Management/Questions/StatusButton/index.tsx new file mode 100644 index 00000000..7347e3c6 --- /dev/null +++ b/src/pages/GroupPage/Management/Questions/StatusButton/index.tsx @@ -0,0 +1,49 @@ +import { Box, Button, Flex, Stack, Text } from '@chakra-ui/react' + +interface StatusButtonsProps { + status: 'READY' | 'APPROVED' | 'REJECTED' + setStatus: (status: 'READY' | 'APPROVED' | 'REJECTED') => void +} + +export const StatusButtons = ({ status, setStatus }: StatusButtonsProps) => { + return ( + + + + + + + + + 승인 거절 + + + + ) +} diff --git a/src/pages/GroupPage/Management/Questions/index.tsx b/src/pages/GroupPage/Management/Questions/index.tsx index 75b20954..62dec7be 100644 --- a/src/pages/GroupPage/Management/Questions/index.tsx +++ b/src/pages/GroupPage/Management/Questions/index.tsx @@ -1,52 +1,31 @@ -import { Suspense, useCallback, useEffect, useState } from 'react' -import { useLocation, useParams } from 'react-router-dom' +import { Suspense, useCallback, useState } from 'react' +import { useParams } from 'react-router-dom' -import { Box, Button, Radio, RadioGroup, Stack, Text } from '@chakra-ui/react' -import { useMutation, useQuery } from '@tanstack/react-query' +import { Box, Text } from '@chakra-ui/react' +import { useMutation } from '@tanstack/react-query' -import { - approveGroupQuestion, - getGroupQuestions, -} from '@/api/services/group/group.api' +import { approveGroupQuestion } from '@/api/services/group/group.api' +import { useGroupQuestion } from '@/api/services/question/group.api' import { Loading } from '@/components/Loading' -import ErrorPage from '@/pages/ErrorPage' import Navigate from '../Navigate' +import { QuestionList } from './QuestionList' +import { StatusButtons } from './StatusButton' -interface Question { - questionId: number - questionContent: string - status: 'READY' | 'APPROVED' | 'REJECTED' -} - -interface GroupQuestionsResponse { - content: Question[] - page: number - size: number - totalElements: number - totalPages: number -} - -export default function QuestionManagement() { - const location = useLocation() +export const QuestionManagement = () => { const { groupId } = useParams<{ groupId: string }>() - const role = location.state?.role - const [status, setStatus] = useState<'READY' | 'APPROVED' | 'REJECTED'>( 'READY' ) - const [content, setContent] = useState(null) const { - data: groupQuestionsResponse = { content: [] }, + data: groupQuestions, isLoading, isError, refetch, - } = useQuery({ - queryKey: ['groupQuestions', groupId, status], - queryFn: () => getGroupQuestions(String(groupId), status, 0, 10), - enabled: !!groupId, - staleTime: 0, + } = useGroupQuestion({ + groupId: groupId || '', + status, }) const { mutate } = useMutation({ @@ -66,82 +45,6 @@ export default function QuestionManagement() { [mutate] ) - if (!role || role === 'MEMBER') return - - const statusButtons = ( - - - - - - - - ) - - useEffect(() => { - if (isLoading) { - setContent() - } else if (isError) { - setContent(질문을 불러오는 데 실패했습니다.) - } else if (groupQuestionsResponse.content.length > 0) { - setContent( - - {groupQuestionsResponse.content.map((question: Question) => ( - - - {question.questionContent} - - - { - const approve = value === 'APPROVED' - handleStatusChange(question.questionId, approve) - }} - value={question.status} - > - - - - - - - ))} - - ) - } else { - setContent(질문이 없습니다.) - } - }, [isLoading, isError, groupQuestionsResponse, handleStatusChange]) - return ( }> @@ -154,10 +57,13 @@ export default function QuestionManagement() { 질문 관리 - {statusButtons} - - {content} - + + ) diff --git a/src/pages/MyPage/Profile/index.tsx b/src/pages/MyPage/Profile/index.tsx index f32e044a..1d9698cd 100644 --- a/src/pages/MyPage/Profile/index.tsx +++ b/src/pages/MyPage/Profile/index.tsx @@ -57,10 +57,7 @@ export default function Profile({ queryClient.invalidateQueries({ queryKey: ['uploadImage'] }) window.location.reload() }, - onError: () => { - setErrorMessage('이미지 파일은 20MB를 초과할 수 없습니다') - errorAlert.onOpen() - }, + onError: () => {}, }) const { mutate: modifyDescription } = useMutation({ @@ -86,6 +83,7 @@ export default function Profile({ const selectedFile = event.target.files?.[0] if (selectedFile) { const validTypes = ['image/jpeg', 'image/jpg', 'image/png'] + const maxFileSize = 10 * 1024 * 1024 if (!validTypes.includes(selectedFile.type)) { toast({ title: 'Only JPEG, JPG, or PNG files are allowed.', @@ -95,6 +93,11 @@ export default function Profile({ }) return } + if (selectedFile.size > maxFileSize) { + setErrorMessage('이미지 파일은 10MB를 초과할 수 없습니다') + errorAlert.onOpen() + return + } setFile(selectedFile) console.log(file) uploadImage({ image: selectedFile }) @@ -192,7 +195,7 @@ export default function Profile({ /> ) : ( - {profile.description} + {profileDescription} )} {isMyPage && ( @@ -246,7 +249,6 @@ export default function Profile({ isOpen={successAlert.isOpen} onClose={() => { successAlert.onClose() - window.location.reload() }} icon={} title="한 줄 소개를 수정하였습니다" diff --git a/src/routes/index.tsx b/src/routes/index.tsx index ebea3856..c361c988 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -5,7 +5,7 @@ import CreateGroupPage from '@/pages/CreateGroupPage' import ErrorPage from '@/pages/ErrorPage' import GroupMembersPage from '@/pages/GroupMembersPage' import GroupPage from '@/pages/GroupPage' -import QuestionManagement from '@/pages/GroupPage/Management/Questions' +import { QuestionManagement } from '@/pages/GroupPage/Management/Questions' import InvitePage from '@/pages/InvitePage' import { CardLayout } from '@/pages/Layout/CardLayout' import { GroupMemberLayout } from '@/pages/Layout/GroupMemberLayout'