Skip to content

Commit

Permalink
feat: 설문 마감, 전송 api 연결 (#71)
Browse files Browse the repository at this point in the history
* refactor: 모든 수신자가 결과를 받았는지 검증하는 훅 useQuery로 수정

* refactor: 아이디 비밀번호 틀렸을 때 예외처리

* refactor: 취합된 설문 갱신하는 api 요청 타입 재설정

* refactor: useQueryClient훅으로 낙관적 업데이트 구현

* refactor: 수신자별 응답 결과 확인했는지 여부 낙관적 업데이트 적용

* feat: 전송된 설문일 때 전송 버튼 disabled처리

* refactor: 최종리뷰 결과가 저장되지 않은 수신자에 대해서만 리뷰 저장 호출

* feat: 리뷰 전송 시 성공,에러 토스트 처리

* feat: 필수 답변이 아닐 때 예외 처리

* refactor: default 값으로 넘어오는 값 필터링 처리

* refactor: api 주소 수정

* refactor: 수정된 api로 연결

* refactor: api타입 수정

* refactor: 빈문자열의 응답 최종리뷰결과에 반영되는 문제 예외 처리

* refactor: 설문 마감시 모두 응답 안한다면 에러 처리

* refactor: 가져오기 수정

* refactor: 토스트 띄우는 부분 컴포넌트에서 띄우게 분기

* refactor: 불필요 코드 제거

* refactor: 불필요 코드 제거

* refactor: strictmode 시 post 두번 가는 요청 ref로 처리

* refactor: 아코디언 높이 수정

* refactor: tab 스타일 수정
  • Loading branch information
khj0426 authored Nov 28, 2023
1 parent 5838cdc commit 10c293b
Show file tree
Hide file tree
Showing 16 changed files with 183 additions and 137 deletions.
2 changes: 1 addition & 1 deletion src/apis/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export { default as useSaveFinalResult } from './useSaveFinalResult'
export { default as useCheckDuplicatedName } from './useCheckDuplicatedName'
export { default as useCheckDuplicatedEmail } from './useCheckDuplicatedEmail'
export { default as useCheckAllReceiverReceived } from './useCheckAllReceiverReceived'
export { default as useCheckAllRecipientReceived } from './useCheckAllRecipientsReceived'
export { default as useCheckAllRecipientReceived } from './useCheckAllReceiverReceived'
export { default as useEditResponse } from './useEditResponse'
export { default as useEditName } from './useEditName'
export { default as useEditPassword } from './useEditPassword'
Expand Down
4 changes: 2 additions & 2 deletions src/apis/hooks/useCheckAllReceiverReceived.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//NOTE - 모든 수신자가 결과를 받았는지 검증하는 훅
import { useSuspenseQuery } from '@tanstack/react-query'
import { useQuery } from '@tanstack/react-query'
import apiClient from '@/apis/apiClient'

interface Response {
Expand All @@ -16,7 +16,7 @@ const useCheckAllReceiverReceived = ({ id }: { id: string }) => {
return response.data
}

return useSuspenseQuery({
return useQuery({
queryKey: [`/final-results/${id}/status`],
queryFn: getCheckAllReceiverReceived,
})
Expand Down
23 changes: 0 additions & 23 deletions src/apis/hooks/useCheckAllRecipientsReceived.ts

This file was deleted.

2 changes: 1 addition & 1 deletion src/apis/hooks/useCloseReview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const useCloseSurvey = ({ id }: { id: string }) => {
mutationFn: closeSurvey,
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: [`/reviews/${id}`],
queryKey: [`/reviews/${id}/creator`],
})
},
})
Expand Down
2 changes: 1 addition & 1 deletion src/apis/hooks/useGetInvitedReview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const useGetInvitedReview = (reviewId: number) => {
}

return useQuery({
queryKey: ['/invited-surveys/${reviewId}'],
queryKey: [`/invited-surveys/${reviewId}`],
queryFn: getInvitedReview,
})
}
Expand Down
2 changes: 1 addition & 1 deletion src/apis/hooks/useGetResponseByReceiver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ interface Receiver {

interface Reply {
id: string
questionId: string
questionId: number
//TODO - 몇명의 피어가 답변했는지를 이 responser로 판별해야함
//TODO - 모든 replies의 responser를 뽑아와야 함
responser: Receiver
Expand Down
27 changes: 0 additions & 27 deletions src/apis/hooks/useGetResponseByResponser.ts

This file was deleted.

5 changes: 2 additions & 3 deletions src/apis/hooks/useLogin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,8 @@ const login = async (user: loginProps) => {
const useLogin = () => {
return useMutation({
mutationFn: login,
onSuccess: ({ data }) => {
localStorage.setItem(TOKEN_KEY, data.data.accessToken)
},
onSuccess: ({ data }) =>
localStorage.setItem(TOKEN_KEY, data.data.accessToken),
})
}

Expand Down
23 changes: 20 additions & 3 deletions src/apis/hooks/useSaveFinalResult.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { QueryClient, useMutation } from '@tanstack/react-query'
import { useQueryClient, useMutation } from '@tanstack/react-query'
import apiClient from '@/apis/apiClient'

interface ReviewId {
reviewId: string
userId: string
}

const useSaveFinalResult = <T extends ReviewId>(finalResult: T) => {
const queryClient = new QueryClient()
const queryClient = useQueryClient()
const saveFinalResult = async () => {
const response = await apiClient.post('/final-results', finalResult)

Expand All @@ -15,7 +16,23 @@ const useSaveFinalResult = <T extends ReviewId>(finalResult: T) => {

return useMutation({
mutationFn: saveFinalResult,
onSuccess: () => {
onMutate: async () => {
const reviewId = finalResult.reviewId
const userId = finalResult.userId

const prevSnapShot = queryClient.getQueryData<{
success: boolean
data: number[]
}>([`/final-results/${reviewId}/status`])

if (prevSnapShot?.success && prevSnapShot?.data) {
queryClient.setQueryData([`/final-results/${reviewId}/status`], {
success: prevSnapShot?.success,
data: [...new Set([...prevSnapShot.data, Number(userId)])],
})
}
},
onSuccess: async () => {
const reviewId = finalResult.reviewId
queryClient.invalidateQueries({
queryKey: [`/final-results/${reviewId}/status`],
Expand Down
6 changes: 5 additions & 1 deletion src/apis/hooks/useSendReview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import apiClient from '@/apis/apiClient'

interface Response {
success: boolean
errorCode?: string
message?: string
}

//NOTE - 대상자별 조합된 리뷰 결과를 저장
Expand All @@ -11,6 +13,8 @@ const useSendReview = () => {
return await apiClient.post<Response>(`/final-results/${reviewId}`)
}

return useMutation({ mutationFn: sendReview })
return useMutation({
mutationFn: sendReview,
})
}
export default useSendReview
8 changes: 0 additions & 8 deletions src/apis/hooks/useUpdateFinalReviewAnswer.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { useMutation } from '@tanstack/react-query'
import { useToast } from '@/hooks'
import apiClient from '@/apis/apiClient'

interface updatedReviewAnswer {
Expand Down Expand Up @@ -29,15 +28,8 @@ const updateFinalReviewResult = async ({
}

const useUpdateFinalReviewAnswer = () => {
const { addToast } = useToast()

return useMutation({
mutationFn: updateFinalReviewResult,
onSuccess: ({ success }) => {
if (success) {
addToast({ message: '성공적으로 저장되었습니다!', type: 'success' })
}
},
})
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useEffect } from 'react'
import { useEffect, useRef } from 'react'
import { useToast } from '@/hooks'
import {
useGetReviewQuestion,
useGetReviewForCreator,
useGetResponseByReceiver,
useSaveFinalResult,
useUpdateFinalReviewAnswer,
Expand All @@ -13,57 +14,87 @@ interface ReviewDetailAccordionProps {
receiverId: string
receiverName: string
reviewId: string
ResponserList?: number[]
}

const ReceiverReviewDetail = ({
receiverId,
reviewId,
receiverName,
ResponserList,
}: ReviewDetailAccordionProps) => {
//NOTE - 하나라도 응답 실패했을 떄 처리
const { data: getReviewQuestion } = useGetReviewQuestion({
id: reviewId,
}).data

const { addToast } = useToast()
const hasAnswered = useRef(false)
const { data: getReviewQuestion } = useGetReviewForCreator({
id: Number(reviewId),
})

const { data: responseByReceiver } = useGetResponseByReceiver({
receiverId,
reviewId,
}).data

const formatAnswers = (
questionType: Parameters<typeof getAnswer>[0],
questionId: Parameters<typeof getAnswer>[1],
) => {
const answer = getAnswer(questionType, questionId, responseByReceiver)
switch (questionType) {
case 'HEXASTAT':
return answer?.map((value) => {
if ('name' in value)
return {
statName: value?.name,
statScore: value.value,
}
})

case 'SUBJECTIVE': {
const result = answer?.map((value) => value.value)?.join('')

return result === '' ? [] : new Array(result)
}

default:
return answer?.map((value) => value.value)
}
}

const saveFinalReviewResult = {
userId: receiverId,
userName: receiverName,
reviewId,
reviewTitle: getReviewQuestion?.title,
reviewDescription: getReviewQuestion?.description,
replies: getReviewQuestion?.questions?.map((question) => {
return {
questionId: question.id,
questionTitle: question.title,
questionType: question.type,
answers: [
question.type !== 'HEXASTAT'
? getAnswer(question?.type, question?.id, responseByReceiver)?.map(
(value) => value.value,
)
: getAnswer(question?.type, question?.id, responseByReceiver)?.map(
(value) => {
if ('name' in value)
return {
statName: value?.name,
statScore: value.value,
}
},
),
].flat(),
}
}),
replies: getReviewQuestion?.questions
?.map((question) => {
const combinedAnswer = formatAnswers(question.type, Number(question.id))

if (combinedAnswer.length > 0) {
return {
questionId: question.id,
questionTitle: question.title,
questionType: question.type,
answers: combinedAnswer,
}
}
})
?.filter((result) => typeof result !== 'undefined'),
}

const { mutate: saveFinalResult } = useSaveFinalResult(saveFinalReviewResult)

const { mutate: updateFinalReviewAnswer } = useUpdateFinalReviewAnswer()
useEffect(() => {
saveFinalResult()
if (
!ResponserList?.includes(Number(receiverId)) &&
hasAnswered.current.valueOf() === false
) {
saveFinalResult()
hasAnswered.current = true
}
}, [receiverId])

//NOTE - 전체 몇 명이 응답했는지 여부
Expand All @@ -75,20 +106,29 @@ const ReceiverReviewDetail = ({
updatedAnswer: string,
questionId: string,
) => {
updateFinalReviewAnswer({
userId: receiverId,
answer: updatedAnswer,
reviewId,
questionId,
})
updateFinalReviewAnswer(
{
userId: receiverId,
answer: updatedAnswer,
reviewId,
questionId,
},
{
onSuccess: ({ success }) => {
if (success) {
addToast({ message: '성공적으로 저장되었습니다!', type: 'success' })
}
},
},
)
}

return (
<>
<label htmlFor="drawer" className="overlay"></label>
<div className="drawer drawer-bottom m-0 flex h-4/5 w-full flex-col items-center gap-10 overflow-auto bg-main-ivory dark:bg-main-red-100 md:h-[32rem]">
<div className="drawer drawer-bottom m-0 flex h-5/6 w-full flex-col items-center gap-10 overflow-auto bg-main-ivory dark:bg-main-red-100 md:h-[32rem]">
<div className="sticky top-0 z-50 flex h-[30px] w-full shrink-0 flex-col items-center justify-center bg-main-yellow dark:bg-main-red-200 sm:h-[40px]">
<label htmlFor="drawer-bottom">
<label htmlFor="drawer-bottom" className="flex w-full justify-center">
<CloseDropDownIcon className="h-[1rem] w-[1rem] cursor-pointer fill-black stroke-black text-black dark:fill-white dark:stroke-white dark:text-white md:h-[1.25rem] md:w-[1.25rem]" />
</label>
</div>
Expand All @@ -109,7 +149,7 @@ const ReceiverReviewDetail = ({
responseByReceiver,
)}
onClickCleanButton={(newAnswer: string) => {
handleUpdateFinalReviewAnswer(newAnswer, question.id)
handleUpdateFinalReviewAnswer(newAnswer, question.id + '')
}}
/>
))}
Expand Down
Loading

0 comments on commit 10c293b

Please sign in to comment.