diff --git a/frontend/src/components/appModal/ApplicantEvalInfo/EvaluationForm/index.tsx b/frontend/src/components/appModal/ApplicantEvalInfo/EvaluationForm/index.tsx index 5e7bfb364..50bf52325 100644 --- a/frontend/src/components/appModal/ApplicantEvalInfo/EvaluationForm/index.tsx +++ b/frontend/src/components/appModal/ApplicantEvalInfo/EvaluationForm/index.tsx @@ -8,6 +8,7 @@ import useEvaluationMutation from '@hooks/useEvaluationMutation'; import ValidationError from '@utils/errors/ValidationError'; import { validateEvalContent } from '@domain/validations/evaluation'; +import Spinner from '@components/common/Spinner'; import { EVALUATION_CONTENT_MAX_LENGTH, EVALUATION_SCORE } from '../constants'; import S from './style'; @@ -25,7 +26,11 @@ interface EvaluationFormProps { export default function EvaluationForm({ processId, applicantId, onClose }: EvaluationFormProps) { const [formState, setFormState] = useState({ score: '', content: '' }); const [contentErrorMessage, setContentErrorMessage] = useState(); - const { mutate: submitNewEvaluation } = useEvaluationMutation({ processId, applicantId }); + const { mutate: submitNewEvaluation, isPending } = useEvaluationMutation({ + processId, + applicantId, + closeOnSuccess: onClose, + }); const handleChangeScore = (value: string) => { if (Object.keys(EVALUATION_SCORE).includes(value)) { @@ -56,7 +61,6 @@ export default function EvaluationForm({ processId, applicantId, onClose }: Eval event.preventDefault(); if (Object.keys(EVALUATION_SCORE).includes(formState.score)) { submitNewEvaluation({ processId, applicantId, ...formState }); - onClose(); } }; @@ -90,6 +94,7 @@ export default function EvaluationForm({ processId, applicantId, onClose }: Eval color="white" onClick={onClose} size="sm" + disabled={isPending} > 취소 @@ -99,7 +104,13 @@ export default function EvaluationForm({ processId, applicantId, onClose }: Eval size="sm" disabled={formState.score === '' || !!contentErrorMessage} > - 평가 저장 + {isPending ? ( + + + + ) : ( + '평가 저장' + )} diff --git a/frontend/src/components/appModal/ApplicantEvalInfo/EvaluationForm/style.ts b/frontend/src/components/appModal/ApplicantEvalInfo/EvaluationForm/style.ts index 47f99a713..3dbeeab52 100644 --- a/frontend/src/components/appModal/ApplicantEvalInfo/EvaluationForm/style.ts +++ b/frontend/src/components/appModal/ApplicantEvalInfo/EvaluationForm/style.ts @@ -21,9 +21,20 @@ const FormButtonWrapper = styled.div` } `; +const SpinnerContainer = styled.div` + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + + width: 4.5rem; + height: 1.4rem; +`; + const S = { EvaluationForm, FormButtonWrapper, + SpinnerContainer, }; export default S; diff --git a/frontend/src/contexts/ToastContext.tsx b/frontend/src/contexts/ToastContext.tsx index 679dde896..f4074ad8f 100644 --- a/frontend/src/contexts/ToastContext.tsx +++ b/frontend/src/contexts/ToastContext.tsx @@ -1,5 +1,5 @@ import { ToastModal } from '@components/common/Toast'; -import { createContext, useState, ReactNode, useContext, useMemo, useCallback, useRef } from 'react'; +import { createContext, ReactNode, useCallback, useContext, useMemo, useRef, useState } from 'react'; type ToastContextType = { alert: (message: string) => void; diff --git a/frontend/src/hooks/useApplicant/index.ts b/frontend/src/hooks/useApplicant/index.ts index cf0de0128..2850fed1d 100644 --- a/frontend/src/hooks/useApplicant/index.ts +++ b/frontend/src/hooks/useApplicant/index.ts @@ -1,11 +1,13 @@ import applicantApis from '@api/domain/applicant'; import QUERY_KEYS from '@hooks/queryKeys'; +import { useToast } from '@contexts/ToastContext'; import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useParams } from 'react-router-dom'; export default function useApplicant({ applicantId }: { applicantId?: number }) { const queryClient = useQueryClient(); const { dashboardId, postId } = useParams() as { dashboardId: string; postId: string }; + const toast = useToast(); return useMutation({ mutationFn: ({ processId, applicants }: { processId: number; applicants: number[] }) => @@ -17,6 +19,7 @@ export default function useApplicant({ applicantId }: { applicantId?: number }) if (applicantId) { queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.APPLICANT, applicantId] }); } + toast.success('지원자 단계가 이동되었습니다.'); }, }); } diff --git a/frontend/src/hooks/useApplyManagement/index.tsx b/frontend/src/hooks/useApplyManagement/index.tsx index f64306320..46da5bb84 100644 --- a/frontend/src/hooks/useApplyManagement/index.tsx +++ b/frontend/src/hooks/useApplyManagement/index.tsx @@ -7,6 +7,7 @@ import { DEFAULT_QUESTIONS } from '@constants/constants'; import { useMutation, UseMutationResult, useQueryClient } from '@tanstack/react-query'; import questionApis from '@api/domain/question'; import QUERY_KEYS from '@hooks/queryKeys'; +import { useToast } from '@contexts/ToastContext'; interface UseApplyManagementReturn { isLoading: boolean; @@ -47,6 +48,7 @@ export default function useApplyManagement({ postId }: UseApplyManagementProps): const { data, isLoading } = applyQueries.useGetApplyForm({ postId: postId ?? '' }); const [applyState, setApplyState] = useState(getQuestions(data)); const [uniqueId, setUniqueId] = useState(DEFAULT_QUESTIONS.length); + const toast = useToast(); const queryClient = useQueryClient(); useEffect(() => { @@ -76,10 +78,10 @@ export default function useApplyManagement({ postId }: UseApplyManagementProps): }), onSuccess: async () => { queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.RECRUITMENT_INFO, postId] }); - alert('지원서의 사전 질문 항목 수정에 성공했습니다.'); + toast.success('지원서의 사전 질문 항목 수정에 성공했습니다.'); }, onError: () => { - alert('지원서의 사전 질문 항목 수정에 실패했습니다.'); + toast.success('지원서의 사전 질문 항목 수정에 실패했습니다.'); }, }); diff --git a/frontend/src/hooks/useDashboardCreateForm/index.tsx b/frontend/src/hooks/useDashboardCreateForm/index.tsx index d40b9ca7b..2ad002899 100644 --- a/frontend/src/hooks/useDashboardCreateForm/index.tsx +++ b/frontend/src/hooks/useDashboardCreateForm/index.tsx @@ -66,6 +66,7 @@ export default function useDashboardCreateForm(): UseDashboardCreateFormReturn { }), onSuccess: async (data) => { setStepState('finished'); + // TODO: Suspence 작업 해야함. setFinishResJson(data); }, }); diff --git a/frontend/src/hooks/useEvaluationMutation/index.ts b/frontend/src/hooks/useEvaluationMutation/index.ts index c859689d7..9a7453dec 100644 --- a/frontend/src/hooks/useEvaluationMutation/index.ts +++ b/frontend/src/hooks/useEvaluationMutation/index.ts @@ -6,14 +6,17 @@ import QUERY_KEYS from '@hooks/queryKeys'; interface UseEvaluationMutationParams { processId: number; applicantId: number; + closeOnSuccess: () => void; } -interface MutationParams extends UseEvaluationMutationParams { +interface MutationParams { + processId: number; + applicantId: number; score: string; content: string; } -export default function useEvaluationMutation({ processId, applicantId }: UseEvaluationMutationParams) { +export default function useEvaluationMutation({ processId, applicantId, closeOnSuccess }: UseEvaluationMutationParams) { const queryClient = useQueryClient(); const { dashboardId, postId } = useParams() as { dashboardId: string; postId: string }; @@ -26,6 +29,7 @@ export default function useEvaluationMutation({ processId, applicantId }: UseEva mutationFn: (params: MutationParams) => evaluationApis.create(params), onSuccess: () => { invalidateQueries(); + closeOnSuccess(); }, }); } diff --git a/frontend/src/hooks/useSpecificApplicant/index.ts b/frontend/src/hooks/useSpecificApplicant/index.ts index 4932e88ad..2ea38ca9e 100644 --- a/frontend/src/hooks/useSpecificApplicant/index.ts +++ b/frontend/src/hooks/useSpecificApplicant/index.ts @@ -1,4 +1,5 @@ import applicantApis from '@api/domain/applicant'; +import { useToast } from '@contexts/ToastContext'; import { ApplicantDetail, SpecificApplicant } from '@customTypes/applicant'; import QUERY_KEYS from '@hooks/queryKeys'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; @@ -16,10 +17,12 @@ const specificApplicant = { useRejectApplicant: ({ dashboardId, postId }: { dashboardId: string; postId: string }) => { const queryClient = useQueryClient(); + const toast = useToast(); return useMutation({ mutationFn: ({ applicantId }: { applicantId: number }) => applicantApis.reject({ applicantId }), onSuccess: () => { + toast.success('불합격 처리 되었습니다.'); queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.DASHBOARD, dashboardId, postId] }); }, }); @@ -27,10 +30,12 @@ const specificApplicant = { useUnrejectApplicant: ({ dashboardId, postId }: { dashboardId: string; postId: string }) => { const queryClient = useQueryClient(); + const toast = useToast(); return useMutation({ mutationFn: ({ applicantId }: { applicantId: number }) => applicantApis.unreject({ applicantId }), onSuccess: () => { + toast.success('부활하였습니다. 👻예토전생👻'); queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.DASHBOARD, dashboardId, postId] }); }, }); diff --git a/frontend/src/mocks/handlers/evaluationHandlers.ts b/frontend/src/mocks/handlers/evaluationHandlers.ts index a7968618e..3311dcd59 100644 --- a/frontend/src/mocks/handlers/evaluationHandlers.ts +++ b/frontend/src/mocks/handlers/evaluationHandlers.ts @@ -1,4 +1,4 @@ -/* eslint-disable camelcase */ +/* eslint-disable no-promise-executor-return */ import { http, HttpResponse } from 'msw'; import { EVALUATIONS } from '@api/endPoint'; @@ -23,6 +23,8 @@ const evaluationHandlers = [ }); } + await new Promise((resolve) => setTimeout(resolve, 2000)); + return new Response(null, { status: 201, statusText: 'Created',