@@ -55,6 +55,23 @@ class ChatClient {
5555 } ) ;
5656 }
5757
58+ // 미션 완료 버튼 (등록자)
59+ const completeMissionBtn = document . getElementById ( 'completeMissionBtn' ) ;
60+ if ( completeMissionBtn ) {
61+ completeMissionBtn . addEventListener ( 'click' , ( ) => this . completeMission ( ) ) ;
62+ }
63+
64+ // 수행자 확정 버튼 (등록자)
65+ const confirmPerformerBtn = document . getElementById ( 'confirmPerformerBtn' ) ;
66+ if ( confirmPerformerBtn ) {
67+ confirmPerformerBtn . addEventListener ( 'click' , ( ) => this . confirmPerformer ( ) ) ;
68+ }
69+ // 수행자 거부 버튼 (등록자)
70+ const rejectPerformerBtn = document . getElementById ( 'rejectPerformerBtn' ) ;
71+ if ( rejectPerformerBtn ) {
72+ rejectPerformerBtn . addEventListener ( 'click' , ( ) => this . rejectPerformer ( ) ) ;
73+ }
74+
5875 const moreMenuBtn = document . getElementById ( 'moreMenuBtn' ) ;
5976 const moreDropdown = document . getElementById ( 'moreDropdown' ) ;
6077 if ( moreMenuBtn && moreDropdown ) {
@@ -134,6 +151,106 @@ class ChatClient {
134151 }
135152 }
136153
154+ async confirmPerformer ( ) {
155+ const btn = document . getElementById ( 'confirmPerformerBtn' ) ;
156+ if ( ! btn ) return ;
157+ btn . disabled = true ;
158+ try {
159+ const res = await fetch ( `/api/missions/api/${ this . missionId } /confirm/` , {
160+ method : 'POST' ,
161+ credentials : 'include' ,
162+ headers : this . getAuthHeaders ( ) ,
163+ body : JSON . stringify ( { room_id : this . roomId } ) ,
164+ } ) ;
165+ const data = await res . json ( ) . catch ( ( ) => ( { } ) ) ;
166+ if ( res . ok && data . success ) {
167+ alert ( data . message || '수행자가 확정되었습니다.' ) ;
168+
169+ // 수행자 확정/거부 버튼 제거
170+ if ( btn ) btn . remove ( ) ;
171+ const rejectBtn = document . getElementById ( 'rejectPerformerBtn' ) ;
172+ if ( rejectBtn ) rejectBtn . remove ( ) ;
173+
174+ const chatActions = document . getElementById ( 'chatActions' ) ;
175+ if ( chatActions && ! document . getElementById ( 'completeMissionBtn' ) ) {
176+ const completeBtn = document . createElement ( 'button' ) ;
177+ completeBtn . type = 'button' ;
178+ completeBtn . className = 'btn-action-gray' ;
179+ completeBtn . id = 'completeMissionBtn' ;
180+ completeBtn . textContent = '미션 완료' ;
181+ // 클릭 이벤트 연결
182+ completeBtn . addEventListener ( 'click' , ( ) => this . completeMission ( ) ) ;
183+ chatActions . appendChild ( completeBtn ) ;
184+ }
185+ } else {
186+ alert ( data . error || '확정에 실패했습니다.' ) ;
187+ btn . disabled = false ;
188+ }
189+ } catch ( err ) {
190+ console . error ( err ) ;
191+ alert ( '요청 중 오류가 발생했습니다.' ) ;
192+ btn . disabled = false ;
193+ }
194+ }
195+
196+ async rejectPerformer ( ) {
197+ const btn = document . getElementById ( 'rejectPerformerBtn' ) ;
198+ if ( ! btn ) return ;
199+ if ( ! confirm ( '수행자 수락을 거절하시겠습니까?' ) ) return ;
200+ btn . disabled = true ;
201+ try {
202+ const res = await fetch ( `/api/missions/api/${ this . missionId } /reject/` , {
203+ method : 'POST' ,
204+ credentials : 'include' ,
205+ headers : this . getAuthHeaders ( ) ,
206+ body : JSON . stringify ( { room_id : this . roomId } ) ,
207+ } ) ;
208+ const data = await res . json ( ) . catch ( ( ) => ( { } ) ) ;
209+ if ( res . ok && data . success ) {
210+ alert ( data . message || '수락을 거절했습니다.' ) ;
211+ if ( btn ) btn . remove ( ) ;
212+ const confirmBtn = document . getElementById ( 'confirmPerformerBtn' ) ;
213+ if ( confirmBtn ) confirmBtn . remove ( ) ;
214+ } else {
215+ alert ( data . error || '거절에 실패했습니다.' ) ;
216+ btn . disabled = false ;
217+ }
218+ } catch ( err ) {
219+ console . error ( err ) ;
220+ alert ( '요청 중 오류가 발생했습니다.' ) ;
221+ btn . disabled = false ;
222+ }
223+ }
224+
225+ async completeMission ( ) {
226+ const btn = document . getElementById ( 'completeMissionBtn' ) ;
227+ if ( ! btn ) return ;
228+ if ( ! confirm ( '미션을 완료 처리하시겠습니까?' ) ) return ;
229+ btn . disabled = true ;
230+ try {
231+ const res = await fetch ( `/api/missions/api/${ this . missionId } /complete/` , {
232+ method : 'POST' ,
233+ credentials : 'include' ,
234+ headers : this . getAuthHeaders ( ) ,
235+ body : JSON . stringify ( { room_id : this . roomId } ) ,
236+ } ) ;
237+ const data = await res . json ( ) . catch ( ( ) => ( { } ) ) ;
238+ if ( res . ok && data . success ) {
239+ alert ( data . message || '미션이 완료되었습니다.' ) ;
240+ if ( btn ) btn . remove ( ) ;
241+ // 원하면 여기서 채팅 목록 등 다른 페이지로 이동
242+ // window.location.href = '/api/missions/chat/';
243+ } else {
244+ alert ( data . error || '미션 완료에 실패했습니다.' ) ;
245+ btn . disabled = false ;
246+ }
247+ } catch ( err ) {
248+ console . error ( err ) ;
249+ alert ( '요청 중 오류가 발생했습니다.' ) ;
250+ btn . disabled = false ;
251+ }
252+ }
253+
137254 async blockUser ( targetId , username , btnEl ) {
138255 if ( ! btnEl ) return ;
139256 if ( ! confirm ( `${ username || '해당 유저' } 를 차단하시겠습니까?` ) ) return ;
@@ -285,6 +402,28 @@ class ChatClient {
285402 } ;
286403 }
287404
405+ showConfirmButtons ( ) {
406+ const chatActions = document . getElementById ( 'chatActions' ) ;
407+ if ( ! chatActions ) return ;
408+ if ( document . getElementById ( 'confirmPerformerBtn' ) ) return ;
409+
410+ const confirmBtn = document . createElement ( 'button' ) ;
411+ confirmBtn . type = 'button' ;
412+ confirmBtn . className = 'btn-action-outline' ;
413+ confirmBtn . id = 'confirmPerformerBtn' ;
414+ confirmBtn . innerHTML = '<i class="fa-regular fa-circle-check"></i> 수행자 확정' ;
415+ confirmBtn . addEventListener ( 'click' , ( ) => this . confirmPerformer ( ) ) ;
416+ chatActions . appendChild ( confirmBtn ) ;
417+
418+ const rejectBtn = document . createElement ( 'button' ) ;
419+ rejectBtn . type = 'button' ;
420+ rejectBtn . className = 'btn-action-gray' ;
421+ rejectBtn . id = 'rejectPerformerBtn' ;
422+ rejectBtn . textContent = '수행자 거부' ;
423+ rejectBtn . addEventListener ( 'click' , ( ) => this . rejectPerformer ( ) ) ;
424+ chatActions . appendChild ( rejectBtn ) ;
425+ }
426+
288427 handleMessage ( msg ) {
289428 switch ( msg . type ) {
290429 case 'AUTH_SUCCESS' :
@@ -305,6 +444,9 @@ class ChatClient {
305444
306445 case 'SYSTEM' :
307446 this . displaySystemMessage ( msg . content ) ;
447+ if ( msg . action === 'mission_accepted' && this . isAuthor ) {
448+ this . showConfirmButtons ( ) ;
449+ }
308450 break ;
309451
310452 case 'KICK' :
0 commit comments