@@ -3,11 +3,17 @@ import { useEffect, useState } from "react";
33import { Controller } from "react-hook-form" ;
44import { useLocation , useNavigate , useSearchParams } from "react-router-dom" ;
55
6+ import { applyApi , type JobFamily } from "@/apis/apply" ;
67import { APPLY_DIALOG , APPLY_MESSAGE } from "@/constants/applyMessages.tsx" ;
7- import { APPLY_TITLE } from "@/constants/applyPageData" ;
8+ import { findJobFamilyOption , APPLY_TITLE } from "@/constants/applyPageData" ;
89import { PATH } from "@/constants/path" ;
910import { ApplyStepLayout } from "@/features/shared/components" ;
10- import { useCheckApplyStatusMutation , usePinLoginMutation } from "@/hooks/apply" ;
11+ import {
12+ useCheckApplyStatusMutation ,
13+ useDeleteDraftMutation ,
14+ useMemberProfileMutation ,
15+ usePinLoginMutation ,
16+ } from "@/hooks/apply" ;
1117import { useApplyEmailForm } from "@/hooks/useApplyEmailForm" ;
1218import { useApplyPinForm } from "@/hooks/useApplyPinForm" ;
1319import type { ContinueWritingFunnelSteps } from "@/types/funnel" ;
@@ -33,14 +39,21 @@ interface IdentityVerificationStepProps {
3339 ) => void ;
3440}
3541
36- export function IdentityVerificationStep ( {
37- context,
38- dispatch,
39- } : IdentityVerificationStepProps ) {
42+ interface JobFamilyMismatchDialog {
43+ isOpen : boolean ;
44+ savedJobFamily : JobFamily | null ;
45+ }
46+
47+ export function IdentityVerificationStep ( { context, dispatch } : IdentityVerificationStepProps ) {
4048 const location = useLocation ( ) ;
4149 const navigate = useNavigate ( ) ;
4250 const [ searchParams , setSearchParams ] = useSearchParams ( ) ;
4351 const [ isSubmittedDialogOpen , setIsSubmittedDialogOpen ] = useState ( false ) ;
52+ const [ mismatchDialog , setMismatchDialog ] = useState < JobFamilyMismatchDialog > ( {
53+ isOpen : false ,
54+ savedJobFamily : null ,
55+ } ) ;
56+ const [ verifiedEmail , setVerifiedEmail ] = useState < string > ( "" ) ;
4457
4558 //PIN 재설정 후 돌아왔을 때 파라미터
4659 const isPinResetSuccess = searchParams . get ( "pinReset" ) === "success" ;
@@ -72,20 +85,28 @@ export function IdentityVerificationStep({
7285 toastController . basic ( "PIN 재설정 완료" , "새로운 PIN을 입력해 본인 확인을 다시 진행해주세요." ) ;
7386
7487 // 3. URL 파라미터 정리
75- setSearchParams ( prev => {
76- prev . delete ( "pinReset" ) ;
77- prev . delete ( "email" ) ;
78- return prev ;
79- } , { replace : true } ) ;
88+ setSearchParams (
89+ prev => {
90+ prev . delete ( "pinReset" ) ;
91+ prev . delete ( "email" ) ;
92+ return prev ;
93+ } ,
94+ { replace : true } ,
95+ ) ;
8096 } , [ isPinResetSuccess , prefillEmail , setEmailValue , setSearchParams ] ) ;
8197
8298 const email = watchEmail ( "email" ) ;
8399 const isFormValid = emailFormState . isValid && pinFormState . isValid ;
84100
85- const { mutate : checkApplyStatusMutate , isPending : isCheckingStatus } = useCheckApplyStatusMutation ( ) ;
101+ const [ isChangingJobFamily , setIsChangingJobFamily ] = useState ( false ) ;
102+
103+ const { mutate : checkApplyStatusMutate , isPending : isCheckingStatus } =
104+ useCheckApplyStatusMutation ( ) ;
105+ const { mutateAsync : deleteDraftAsync } = useDeleteDraftMutation ( ) ;
106+ const { mutateAsync : updateProfileAsync } = useMemberProfileMutation ( ) ;
86107
87108 const handleCheckApplyStatus = ( userEmail : string ) => {
88- checkApplyStatusMutate ( userEmail , {
109+ checkApplyStatusMutate ( undefined , {
89110 onSuccess : data => {
90111 if ( data . result === "PROFILE_NOT_REGISTERED" ) {
91112 dispatch ( "goToProfile" , userEmail ) ;
@@ -97,9 +118,30 @@ export function IdentityVerificationStep({
97118 return ;
98119 }
99120
100- // CONTINUE (TEMP_SAVED 또는 JOINED)
101- toastController . positive ( APPLY_MESSAGE . success . continueWriting ) ;
102- dispatch ( "goToApply" , userEmail ) ;
121+ // CONTINUE (TEMP_SAVED 또는 JOINED) → draft 확인
122+ void applyApi
123+ . getDraft ( )
124+ . then ( draft => {
125+ // 파트 불일치 체크: draft에 저장된 jobFamily와 현재 접근한 jobFamily가 다른 경우
126+ if ( draft . jobFamily != null && draft . jobFamily !== context . jobFamily ) {
127+ setVerifiedEmail ( userEmail ) ;
128+ setMismatchDialog ( {
129+ isOpen : true ,
130+ savedJobFamily : draft . jobFamily ,
131+ } ) ;
132+ return ;
133+ }
134+
135+ // 같은 파트 또는 draft 없음 → 이어서 작성
136+ toastController . positive ( APPLY_MESSAGE . success . continueWriting ) ;
137+ dispatch ( "goToApply" , userEmail ) ;
138+ } )
139+ . catch ( ( error : unknown ) => {
140+ // draft 조회 실패 시에도 이어서 작성 가능 (빈 폼으로 시작)
141+ handleError ( error , "임시저장 데이터 조회 실패" ) ;
142+ toastController . positive ( APPLY_MESSAGE . success . continueWriting ) ;
143+ dispatch ( "goToApply" , userEmail ) ;
144+ } ) ;
103145 } ,
104146 onError : error => {
105147 handleError ( error , "지원 상태 확인 실패" ) ;
@@ -132,6 +174,56 @@ export function IdentityVerificationStep({
132174 void navigate ( `${ PATH . resetPin } ?returnTo=${ encodeURIComponent ( returnTo ) } ` ) ;
133175 } ;
134176
177+ // 파트 불일치 다이얼로그: "기존 파트 지원서 이어서 작성하기" 선택
178+ const handleContinueSavedDraft = ( ) => {
179+ if ( mismatchDialog . savedJobFamily ) {
180+ void navigate ( `${ PATH . applyContinue } /${ mismatchDialog . savedJobFamily } ` ) ;
181+ }
182+ setMismatchDialog ( { isOpen : false , savedJobFamily : null } ) ;
183+ } ;
184+
185+ // 파트 불일치 다이얼로그: "새로운 파트로 지원하기" 선택
186+ const handleStartNewApplication = async ( ) => {
187+ setIsChangingJobFamily ( true ) ;
188+ setMismatchDialog ( { isOpen : false , savedJobFamily : null } ) ;
189+
190+ try {
191+ // 1. 현재 프로필 백업
192+ const profile = await applyApi . getMe ( ) ;
193+
194+ // 2. 기존 draft + 프로필 삭제
195+ await deleteDraftAsync ( ) ;
196+
197+ // 3. 프로필 복원 (새로운 jobFamily로)
198+ await updateProfileAsync ( {
199+ name : profile . name ,
200+ phoneNumber : "01012345678" , // TODO: getMe 응답에 phoneNumber가 없어서 임시 처리
201+ careerDetails : profile . careerDetails ,
202+ region : profile . region ,
203+ experiencePeriod : profile . experiencePeriod ,
204+ interestedDomains : profile . interestedDomains ,
205+ jobFamily : context . jobFamily ,
206+ } ) ;
207+
208+ // 4. 지원서 작성으로 이동
209+ toastController . positive ( APPLY_MESSAGE . success . continueWriting ) ;
210+ dispatch ( "goToApply" , verifiedEmail ) ;
211+ } catch ( error ) {
212+ handleError ( error , "파트 변경 실패" ) ;
213+ toastController . destructive ( "파트 변경에 실패했습니다. 다시 시도해주세요." ) ;
214+ } finally {
215+ setIsChangingJobFamily ( false ) ;
216+ }
217+ } ;
218+
219+ // 다이얼로그 메시지 생성
220+ const mismatchDialogContent = mismatchDialog . savedJobFamily
221+ ? APPLY_DIALOG . jobFamilyMismatch (
222+ findJobFamilyOption ( mismatchDialog . savedJobFamily ) . korean ,
223+ findJobFamilyOption ( context . jobFamily ) . korean ,
224+ )
225+ : null ;
226+
135227 return (
136228 < ApplyStepLayout
137229 variant = 'auth'
@@ -213,6 +305,29 @@ export function IdentityVerificationStep({
213305 onClick : ( ) => setIsSubmittedDialogOpen ( false ) ,
214306 } }
215307 />
308+
309+ { mismatchDialogContent && (
310+ < Dialog
311+ open = { mismatchDialog . isOpen || isChangingJobFamily }
312+ onOpenChange = { open =>
313+ ! open &&
314+ ! isChangingJobFamily &&
315+ setMismatchDialog ( { isOpen : false , savedJobFamily : null } )
316+ }
317+ header = { mismatchDialogContent . header }
318+ body = { mismatchDialogContent . body }
319+ primaryAction = { {
320+ children : mismatchDialogContent . primaryAction ,
321+ onClick : handleContinueSavedDraft ,
322+ disabled : isChangingJobFamily ,
323+ } }
324+ secondaryAction = { {
325+ children : isChangingJobFamily ? "변경 중..." : mismatchDialogContent . secondaryAction ,
326+ onClick : ( ) => void handleStartNewApplication ( ) ,
327+ disabled : isChangingJobFamily ,
328+ } }
329+ />
330+ ) }
216331 </ ApplyStepLayout >
217332 ) ;
218333}
0 commit comments