-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[FE] refactor,fix : 리뷰 작성 페이지 디자인 버그 수정 및 상태 관리 리팩토링 (#369)
* chore: 깃허브 로고 주석 처리 * fix: Checkbox 의 onChange 오류 수정 - 오류 : handleChange로 props명이 변경되면서 구조분해할당으로 input에 onChange 이벤트가 들어가지 않게 됨 - 오류 수정; onChange에 handleChange를 직접 넣어주는 방식으로 수정 * chore: 서술형 질문에 글자 수 안내 문구 삭제, 선택 문항에 '(선택)' 문구 추 * refactor: multipleGuidline을 useMultipleChoice로 이동 * refactor: useMutateReview의 onSuccess에서 리뷰 생성 후 기능 실행하도록 수정 - mutate 성공 시, 실행할 executeAfterMutateSuccess를 useMutateReview의 props로 추가 - 리뷰 제출 성공 시, 모달 닫고 페이지 이동하는 코드를 onSuccess 에서 실행하도록 수정 * refactor: useMutateReview의 onSuccess에서 리뷰 생성 후 기능 실행하도록 수정 - mutate 성공 시, 실행할 executeAfterMutateSuccess를 useMutateReview의 props로 추가 - 리뷰 제출 성공 시, 모달 닫고 페이지 이동하는 코드를 onSuccess 에서 실행하도록 수정 * fix: Checkbox props명 오류 수정 - isDisabled -> disabled * refactor : 리뷰 작성 질문들을 recoil로 관리하도록 리팩토링 - 컴포넌트간 props drilling을 줄이기 위해 recoil 상태관리를 사용 - 카테고리 선택 결과에 따라 리뷰 작성 질문지(questionList)가 동적으로 변할 수 있도록 atom, selector를 사용 * refactor: answerMap, answerValidationMap을 atom으로 변경 * refactor: useReviewAnswer를 3개의 훅으로 분리 <hook> - 분리되어 생성된 훅 :useUpdateReviewerAnswer, useUpdateDefaultAnswers, useCheckStepAvailability <components> - QnABox: props 변경 및 useUpdateReviewerAnswer 사용 - RevieWritingCard : props 변경 및 useCheckNextStepAvailability 사용 * feat: 리뷰 작성 폼 관련 recoil 상태 초기화하는 훅 생성 및 적용 * fix: 리뷰 작성 페이지 목 데이터에서 optionId 중복 수정 * chore: 코드 설명에 대한 주석 추가 * refactor: useUpdateDefaultAnswers 에서 객관식/서술형 기본값 상수 처리 * fix: 서술형에서 길에 한문장으로 작성 시 맥에서 가로 스크롤이 생기는 오류 수정 * fix : ReviewWritingFrom의 formId 타입 변경 (string-> number) * chore: REVIEW_WRITING_FORM_CARD_DATA 변경 * refactor: 훅 , selector 네이밍 변경 - questionList -> cardSectionList * refactor: useMultipleChoice 리팩토링 - useAboveSelectionLimit , useUpdateMultipleChoiceAnswer로 분리 * feat :기존에 답변한 카테고리를 카테고리 질문에서 해제할 때에 대한 대응 추가 - useCancelAnsweredCategory 추가 - 카테고리 질문에서 이미 답변을 작성한 카테고리 선택을 해제하는 지 판단 - 해제하려는 경우, answerMap, answerValidationMap의 상태 변경을 하지 않고 모달을 띄워줌 - 모달에서 확인 버튼 클릭하면, 해제되고 취소되면 기존의 선택이 유지 * refactor: ReviewWritingCardForm하위 컴포넌트 파일들을 components 폴더로 이동 * feat: QnABox에 답변한 카테고리 해제 시, 관련 기능 적용 - 리뷰 작성 페이지의 다른 모달들과 같은 컴포넌트에 위치하면 좋겠으나, 확인 버튼 클릭 시 사용자가 해제하려했던 카테고리 선택이 해제되어야해서 컴포넌트 구조상 QnABox에 관련 모달이 있어야한다고 판단함 * refactor: QnABox에서 MultipleChoiceQuestion 분리 * fix: 개수 제한 가이드 라인 표시 이전에 선택 오류 수정 개수 제한 가이드 라인 표시 이전에 최대 개수를 넘는 선택 시, 체크박스 선택되는 오류 수정 * chore: 필요없는 코드 삭제 * fix: 답변이 없는 카테고리 선택 취소 시, 모달 띄워지는 오류 수정 * fix: handleModalOpen 파라미터 오류 수정 * fix: 답변이 없는 카테고리 선택 취소 시 답변 기본값에 의한 오류 수정 - 답변의 기본값이 빈문자열 이나 빈 배열이여서 답변을 하지 않았음에도 모달이 띄워지는 오류가 있었음 * refactor: useTextAnswer에서 useUpdateReviewerAnswer사용 * design: 프로젝트 이름, 리뷰이의 이름이 길어지는 경우를 대비한 디자인 수정 * feat: 작성 내용 확인 버튼에 비활성화 기능 추가 - 모든 답변이 제출 가능한 상태여야 리뷰 작성 확인 버튼도 비활성화 됨 - 버튼의 text를 작성 내용 확인 버튼으로 수정 * fix: Checkbox 의 onChange 오류 수정 - 오류 : handleChange로 props명이 변경되면서 구조분해할당으로 input에 onChange 이벤트가 들어가지 않게 됨 - 오류 수정; onChange에 handleChange를 직접 넣어주는 방식으로 수정 * chore: 서술형 질문에 글자 수 안내 문구 삭제, 선택 문항에 '(선택)' 문구 추 * fix: Checkbox 의 onChange 오류 수정 - 오류 : handleChange로 props명이 변경되면서 구조분해할당으로 input에 onChange 이벤트가 들어가지 않게 됨 - 오류 수정; onChange에 handleChange를 직접 넣어주는 방식으로 수정 * fix: 서술형에서 길에 한문장으로 작성 시 맥에서 가로 스크롤이 생기는 오류 수정 * design: 프로젝트 이름, 리뷰이의 이름이 길어지는 경우를 대비한 디자인 수정 * chore: Checkbox 변경으로 인한 코드 수정
- Loading branch information
1 parent
ae79025
commit 0ed89fd
Showing
36 changed files
with
868 additions
and
495 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,11 @@ | ||
export { default as useReviewerAnswer } from './useReviewerAnswer'; | ||
export { default as useSlideWidthAndHeight } from './useSlideWidthAndHeight'; | ||
export { default as useCheckNextStepAvailability } from './useCheckNextStepAvailability'; | ||
export { default as useCurrentCardIndex } from './useCurrentCardIndex'; | ||
export { default as useMultipleChoice } from './useMultipleChoice'; | ||
export { default as useTextAnswer } from './useTextAnswer'; | ||
export { default as useQuestionList } from './useQuestionList'; | ||
export { default as useGetDataToWrite } from './useGetDataToWrite'; | ||
export { default as useMutateReview } from './useMutateReview'; | ||
export { default as useMultipleChoice } from './multiplceChoice/useMultipleChoice'; | ||
export { default as useCardSectionList } from './useCardSectionList'; | ||
export { default as useResetFormRecoil } from './useResetFormRecoil'; | ||
export { default as useUpdateReviewerAnswer } from './useUpdateReviewerAnswer'; | ||
export { default as useSlideWidthAndHeight } from './useSlideWidthAndHeight'; | ||
export { default as useTextAnswer } from './useTextAnswer'; | ||
export { default as useUpdateDefaultAnswers } from './useUpdateDefaultAnswers'; |
42 changes: 42 additions & 0 deletions
42
frontend/src/hooks/review/writingCardForm/multiplceChoice/useAboveSelectionLimit.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { useState } from 'react'; | ||
|
||
import { ReviewWritingCardQuestion } from '@/types'; | ||
|
||
interface UseAboveSelectionLimit { | ||
question: ReviewWritingCardQuestion; | ||
selectedOptionList: number[]; | ||
} | ||
const useAboveSelectionLimit = ({ question, selectedOptionList }: UseAboveSelectionLimit) => { | ||
const [isOpenLimitGuide, setIsOpenLimitGuide] = useState(false); | ||
|
||
const isMaxCheckedNumber = () => { | ||
if (!question.optionGroup) return false; | ||
return selectedOptionList.length >= question.optionGroup.maxCount; | ||
}; | ||
|
||
const isSelectedCheckbox = (optionId: number) => { | ||
return selectedOptionList.includes(optionId); | ||
}; | ||
|
||
/** | ||
* 선택 가능한 문항 수를 넘어서 문항을 선택하려 하는지 여부 | ||
*/ | ||
const isAboveSelectionLimit = (optionId: number) => !!(isMaxCheckedNumber() && !isSelectedCheckbox(optionId)); | ||
|
||
/** | ||
* 최대 문항 수를 넘어서 선택하려는지, 그럴 경우에 대한 핸들링 | ||
* @param id : 객관식 문항의 optionId | ||
*/ | ||
const handleLimitGuideOpen = (isOpen: boolean) => { | ||
setIsOpenLimitGuide(isOpen); | ||
}; | ||
|
||
return { | ||
isOpenLimitGuide, | ||
isSelectedCheckbox, | ||
isAboveSelectionLimit, | ||
handleLimitGuideOpen, | ||
}; | ||
}; | ||
|
||
export default useAboveSelectionLimit; |
58 changes: 58 additions & 0 deletions
58
frontend/src/hooks/review/writingCardForm/multiplceChoice/useCancelAnsweredCategory.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import { useRecoilValue } from 'recoil'; | ||
|
||
import { answerMapAtom, cardSectionListSelector } from '@/recoil'; | ||
import { ReviewWritingCardQuestion } from '@/types'; | ||
|
||
interface UseCancelAnsweredCategoryProps { | ||
question: ReviewWritingCardQuestion; | ||
} | ||
const useCancelAnsweredCategory = ({ question }: UseCancelAnsweredCategoryProps) => { | ||
const cardSectionList = useRecoilValue(cardSectionListSelector); | ||
const answerMap = useRecoilValue(answerMapAtom); | ||
const isCategoryQuestion = () => { | ||
return question.questionId === cardSectionList[0].questions[0].questionId; | ||
}; | ||
// 이미 답변을 작성한 카테고리를 해제하는 경우 | ||
/** | ||
* 카테고리 항목 선택일때, optionId에 해당하는 카테고리 찾기 | ||
*/ | ||
const getCategoryByOptionId = (categoryOptionId: number) => { | ||
return cardSectionList.filter((section) => section.onSelectedOptionId === categoryOptionId)[0]; | ||
}; | ||
|
||
/** | ||
* categoryOptionId로 찾은 카테고리에 답변이 달려있는 지 여부 | ||
* 답이 있기만 하다면 true | ||
*/ | ||
const isSelectedCategoryAnswer = (categoryOptionId: number) => { | ||
if (!answerMap) return false; | ||
// 선택한 객관식에 해당하는 카테고리의 질문들 가져오기 | ||
const targetCategoryQuestionList = getCategoryByOptionId(categoryOptionId); | ||
|
||
if (!targetCategoryQuestionList) return false; | ||
//카테고리에 유효한 답변이 있는 지 판단 | ||
const questionIdList = targetCategoryQuestionList.questions.map((question) => question.questionId); | ||
const questionId = questionIdList.find((id) => answerMap.has(id)); | ||
|
||
if (!questionId) return; | ||
|
||
const answer = answerMap.get(questionId); | ||
|
||
return !!answer?.selectedOptionIds?.length || !!answer?.text?.length; | ||
}; | ||
|
||
/** | ||
* 해제하기 위해 카테고리 문항을 선택한 경우, 이미 이에 대해 답변을 했는 지 여부 | ||
* @param optionId : 문항의 optionId | ||
*/ | ||
const isAnsweredCategoryChanged = (optionId: number) => { | ||
if (!isCategoryQuestion) return false; | ||
return isSelectedCategoryAnswer(optionId); | ||
}; | ||
|
||
return { | ||
isAnsweredCategoryChanged, | ||
}; | ||
}; | ||
|
||
export default useCancelAnsweredCategory; |
57 changes: 57 additions & 0 deletions
57
frontend/src/hooks/review/writingCardForm/multiplceChoice/useMultipleChoice.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import { useState } from 'react'; | ||
|
||
import { ReviewWritingCardQuestion } from '@/types'; | ||
|
||
import useAboveSelectionLimit from './useAboveSelectionLimit'; | ||
import useCancelAnsweredCategory from './useCancelAnsweredCategory'; | ||
import useUpdateMultipleChoiceAnswer from './useUpdateMultipleChoiceAnswer'; | ||
|
||
interface UseMultipleChoiceProps { | ||
question: ReviewWritingCardQuestion; | ||
handleModalOpen: (isOpen: boolean) => void; | ||
} | ||
/** | ||
* 하나의 객관식 질문에서 선택된 문항, 문항 선택 관리(최대를 넘는 문항 선택 시, 안내 문구 표시)등을 하는 훅 | ||
*/ | ||
const useMultipleChoice = ({ question, handleModalOpen }: UseMultipleChoiceProps) => { | ||
const [unCheckTargetOptionId, setUnCheckTargetOptionId] = useState<number | null>(null); | ||
|
||
const { isAnsweredCategoryChanged } = useCancelAnsweredCategory({ question }); | ||
|
||
const { selectedOptionList, updateAnswerState } = useUpdateMultipleChoiceAnswer({ question }); | ||
|
||
const { isOpenLimitGuide, isSelectedCheckbox, isAboveSelectionLimit, handleLimitGuideOpen } = useAboveSelectionLimit({ | ||
question, | ||
selectedOptionList, | ||
}); | ||
|
||
const handleCheckboxChange = (event: React.ChangeEvent<HTMLInputElement>) => { | ||
const { id, checked } = event.currentTarget; | ||
const optionId = Number(id); | ||
if (isAboveSelectionLimit(optionId)) { | ||
return handleLimitGuideOpen(true); | ||
} | ||
handleLimitGuideOpen(false); | ||
// 답변이 달린 카테고리를 해제하려는 경우 | ||
const isUnCheckCategory = isAnsweredCategoryChanged(optionId); | ||
setUnCheckTargetOptionId(isUnCheckCategory ? optionId : null); | ||
handleModalOpen(!!isUnCheckCategory); | ||
|
||
if (!isUnCheckCategory) { | ||
updateAnswerState({ optionId, checked }); | ||
} | ||
}; | ||
|
||
const unCheckTargetOption = () => { | ||
if (unCheckTargetOptionId) { | ||
updateAnswerState({ optionId: unCheckTargetOptionId, checked: false }); | ||
} | ||
}; | ||
return { | ||
isOpenLimitGuide, | ||
handleCheckboxChange, | ||
isSelectedCheckbox, | ||
unCheckTargetOption, | ||
}; | ||
}; | ||
export default useMultipleChoice; |
64 changes: 64 additions & 0 deletions
64
frontend/src/hooks/review/writingCardForm/multiplceChoice/useUpdateMultipleChoiceAnswer.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import { useState } from 'react'; | ||
|
||
import { ReviewWritingAnswer, ReviewWritingCardQuestion } from '@/types'; | ||
|
||
import useUpdateReviewerAnswer from '../useUpdateReviewerAnswer'; | ||
|
||
interface UseUpdateMultipleChoiceAnswerProps { | ||
question: ReviewWritingCardQuestion; | ||
} | ||
|
||
const useUpdateMultipleChoiceAnswer = ({ question }: UseUpdateMultipleChoiceAnswerProps) => { | ||
const [selectedOptionList, setSelectedOptionList] = useState<number[]>([]); | ||
|
||
const { updateAnswerMap, updateAnswerValidationMap } = useUpdateReviewerAnswer(); | ||
|
||
interface MakeNewSelectedOptionList { | ||
optionId: number; | ||
checked: boolean; | ||
} | ||
/** | ||
* checkbox의 change 이벤트에 따라 새로운 selectedOptionList를 반환하는 함수 | ||
*/ | ||
const makeNewSelectedOptionList = ({ optionId, checked }: MakeNewSelectedOptionList) => { | ||
if (checked) { | ||
return selectedOptionList.concat(optionId); | ||
} | ||
|
||
return selectedOptionList.filter((option) => option !== optionId); | ||
}; | ||
|
||
const isValidatedChoice = (newSelectedOptionList: number[]) => { | ||
if (!question.optionGroup) return false; | ||
|
||
const { minCount, maxCount } = question.optionGroup; | ||
const { length } = newSelectedOptionList; | ||
|
||
return length >= minCount && length <= maxCount; | ||
}; | ||
|
||
const updateAnswerState = ({ optionId, checked }: MakeNewSelectedOptionList) => { | ||
const newSelectedOptionList = makeNewSelectedOptionList({ optionId, checked }); | ||
setSelectedOptionList(newSelectedOptionList); | ||
|
||
// 유효한 선택(=객관식 문항의 최소,최대 개수를 지켰을 경우)인지에 따라 answer 변경 | ||
const isValidatedAnswer = isValidatedChoice(newSelectedOptionList); | ||
const isNotRequiredEmptyAnswer = !question.required && newSelectedOptionList.length === 0; | ||
|
||
const newAnswer: ReviewWritingAnswer = { | ||
questionId: question.questionId, | ||
selectedOptionIds: isValidatedAnswer ? newSelectedOptionList : [], | ||
text: null, | ||
}; | ||
|
||
updateAnswerMap(newAnswer); | ||
updateAnswerValidationMap(newAnswer, isValidatedAnswer || isNotRequiredEmptyAnswer); | ||
}; | ||
|
||
return { | ||
selectedOptionList, | ||
updateAnswerState, | ||
}; | ||
}; | ||
|
||
export default useUpdateMultipleChoiceAnswer; |
29 changes: 29 additions & 0 deletions
29
frontend/src/hooks/review/writingCardForm/useCardSectionList.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { useEffect } from 'react'; | ||
import { useRecoilValue, useSetRecoilState } from 'recoil'; | ||
|
||
import { cardSectionListSelector, reviewWritingFormSectionListAtom } from '@/recoil'; | ||
import { ReviewWritingCardSection } from '@/types'; | ||
|
||
interface UseCardSectionListProps { | ||
cardSectionListData: ReviewWritingCardSection[]; | ||
} | ||
/** | ||
* 서버에서 받아온 데이터를 바탕으로 리뷰 작성 폼에서 사용할 질문지(상태)를 변경하는 훅 | ||
* @param {ReviewWritingCardSection[]} cardSectionListData 서버에서 받아온 질문 데이터 | ||
* @returns | ||
*/ | ||
const useCardSectionList = ({ cardSectionListData }: UseCardSectionListProps) => { | ||
const setReviewWritingFormSectionList = useSetRecoilState(reviewWritingFormSectionListAtom); | ||
|
||
const cardSectionList = useRecoilValue(cardSectionListSelector); | ||
|
||
useEffect(() => { | ||
setReviewWritingFormSectionList(cardSectionListData); | ||
}, [cardSectionListData]); | ||
|
||
return { | ||
cardSectionList, | ||
}; | ||
}; | ||
|
||
export default useCardSectionList; |
38 changes: 38 additions & 0 deletions
38
frontend/src/hooks/review/writingCardForm/useCheckNextStepAvailability.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { useEffect, useState } from 'react'; | ||
import { useRecoilValue } from 'recoil'; | ||
|
||
import { cardSectionListSelector, answerValidationMapAtom, answerMapAtom } from '@/recoil'; | ||
|
||
interface UseCheckNextStepAvailability { | ||
currentCardIndex: number; | ||
} | ||
const useCheckNextStepAvailability = ({ currentCardIndex }: UseCheckNextStepAvailability) => { | ||
const cardSectionList = useRecoilValue(cardSectionListSelector); | ||
const answerValidationMap = useRecoilValue(answerValidationMapAtom); | ||
const answerMap = useRecoilValue(answerMapAtom); | ||
|
||
const [isAbleNextStep, setIsAbleNextStep] = useState(false); | ||
|
||
const isValidateAnswerList = () => { | ||
if (!cardSectionList.length) return false; | ||
|
||
return cardSectionList[currentCardIndex].questions.every((question) => { | ||
const { questionId, required } = question; | ||
const answerValidation = answerValidationMap?.get(questionId); | ||
|
||
if (!required && answerValidation) return true; | ||
return !!answerValidation; | ||
}); | ||
}; | ||
|
||
useEffect(() => { | ||
const answerListValidation = isValidateAnswerList(); | ||
setIsAbleNextStep(answerListValidation); | ||
}, [answerMap, currentCardIndex]); | ||
|
||
return { | ||
isAbleNextStep, | ||
}; | ||
}; | ||
|
||
export default useCheckNextStepAvailability; |
78 changes: 0 additions & 78 deletions
78
frontend/src/hooks/review/writingCardForm/useMultipleChoice.ts
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.