@@ -190,7 +190,8 @@ const initialTeamMembers: TeamMember[] = [
190190export function TeamPanel ( { onInvite, onOpenChannel } : TeamPanelProps ) {
191191 const [ members , setMembers ] = useState < TeamMember [ ] > ( initialTeamMembers ) ;
192192 const [ activeRoomId , setActiveRoomId ] = useState ( teamRooms [ 1 ] . id ) ;
193- const [ notice , setNotice ] = useState ( "팀원 역할 수정, 삭제, 채팅방 이동이 가능합니다." ) ;
193+ const [ notice , setNotice ] = useState ( "팀원 역할 수정 및 삭제가 가능합니다." ) ;
194+ const [ confirmTarget , setConfirmTarget ] = useState < TeamMember | null > ( null ) ;
194195 const activeRoom = teamRooms . find ( ( room ) => room . id === activeRoomId ) ?? teamRooms [ 0 ] ;
195196 const onlineCount = members . filter ( ( member ) => member . online ) . length ;
196197 const activityItems = useMemo ( ( ) => {
@@ -219,15 +220,19 @@ export function TeamPanel({ onInvite, onOpenChannel }: TeamPanelProps) {
219220 setNotice ( `${ target ?. name ?? "팀원" } 역할을 ${ nextRole } (으)로 변경했습니다.` ) ;
220221 } ;
221222
222- const handleDeleteMember = ( memberId : string ) => {
223- const target = members . find ( ( member ) => member . id === memberId ) ;
224- if ( ! target || target . protected ) {
223+ const handleDeleteClick = ( member : TeamMember ) => {
224+ if ( member . protected ) {
225225 setNotice ( "팀 리드는 현재 화면에서 삭제할 수 없습니다." ) ;
226226 return ;
227227 }
228+ setConfirmTarget ( member ) ;
229+ } ;
228230
229- setMembers ( ( prev ) => prev . filter ( ( member ) => member . id !== memberId ) ) ;
230- setNotice ( `${ target . name } 팀원을 삭제했습니다.` ) ;
231+ const handleConfirmDelete = ( ) => {
232+ if ( ! confirmTarget ) return ;
233+ setMembers ( ( prev ) => prev . filter ( ( member ) => member . id !== confirmTarget . id ) ) ;
234+ setNotice ( `${ confirmTarget . name } 팀원을 추방했습니다.` ) ;
235+ setConfirmTarget ( null ) ;
231236 } ;
232237
233238 return (
@@ -247,7 +252,7 @@ export function TeamPanel({ onInvite, onOpenChannel }: TeamPanelProps) {
247252 팀
248253 </ h2 >
249254 < p className = "m-0 mt-2 tracking-tight" style = { { color : "var(--muted)" , fontSize : 14 , fontWeight : 800 } } >
250- { members . length } 명 · { onlineCount } 명 접속 중 · 채팅방 { teamRooms . length } 개
255+ { members . length } 명 · { onlineCount } 명 접속 중
251256 </ p >
252257 </ div >
253258
@@ -282,142 +287,6 @@ export function TeamPanel({ onInvite, onOpenChannel }: TeamPanelProps) {
282287 { notice }
283288 </ div >
284289
285- < section
286- className = "mb-5 overflow-hidden rounded-[26px]"
287- style = { {
288- background : "rgba(8, 17, 31, 0.74)" ,
289- border : "1px solid rgba(32, 227, 255, 0.18)" ,
290- boxShadow : "0 20px 56px rgba(0, 0, 0, 0.30), inset 0 1px 0 rgba(255,255,255,0.06)" ,
291- backdropFilter : "blur(18px) saturate(160%)"
292- } }
293- >
294- < div className = "flex flex-wrap items-center justify-between gap-3 px-5 py-4" style = { { borderBottom : "1px solid rgba(32, 227, 255, 0.12)" } } >
295- < div >
296- < h3 className = "m-0 tracking-tight" style = { { color : "var(--white)" , fontSize : 18 , fontWeight : 950 } } >
297- 팀 채팅방
298- </ h3 >
299- < p className = "m-0 mt-1 tracking-tight" style = { { color : "var(--muted)" , fontSize : 13 , fontWeight : 800 } } >
300- 팀 탭에서도 바로 채팅방을 확인하고 이동할 수 있습니다.
301- </ p >
302- </ div >
303- < span className = "inline-flex items-center gap-2 rounded-full px-3 py-1.5" style = { {
304- background : "rgba(57, 255, 136, 0.10)" ,
305- border : "1px solid rgba(57, 255, 136, 0.20)" ,
306- color : "var(--matrix-green)" ,
307- fontSize : 12 ,
308- fontWeight : 950
309- } } >
310- < Users size = { 14 } />
311- { onlineCount } 명 온라인
312- </ span >
313- </ div >
314-
315- < div className = "grid gap-0 xl:grid-cols-[minmax(0,1fr)_360px]" >
316- < div className = "grid gap-3 p-5 md:grid-cols-2" >
317- { teamRooms . map ( ( room ) => {
318- const Icon = room . icon ;
319- const isActive = room . id === activeRoomId ;
320-
321- return (
322- < button
323- key = { room . id }
324- type = "button"
325- onClick = { ( ) => handleOpenRoom ( room ) }
326- className = "group rounded-2xl p-4 text-left transition-all hover:translate-y-[-2px]"
327- style = { {
328- background : isActive ? `${ room . accent } 18` : "rgba(234, 247, 255, 0.045)" ,
329- border : isActive ? `1px solid ${ room . accent } 66` : "1px solid rgba(234, 247, 255, 0.10)" ,
330- boxShadow : isActive ? `0 0 26px ${ room . accent } 1f` : "none" ,
331- color : "var(--white)" ,
332- cursor : "pointer"
333- } }
334- >
335- < div className = "mb-4 flex items-start justify-between gap-3" >
336- < div className = "flex items-center gap-3" >
337- < span className = "flex h-11 w-11 items-center justify-center rounded-2xl" style = { {
338- background : `${ room . accent } 18` ,
339- border : `1px solid ${ room . accent } 44` ,
340- color : room . accent
341- } } >
342- < Icon size = { 20 } />
343- </ span >
344- < div className = "min-w-0" >
345- < p className = "m-0 truncate tracking-tight" style = { { color : "var(--white)" , fontSize : 15 , fontWeight : 950 } } >
346- { room . name }
347- </ p >
348- < p className = "m-0 mt-1 truncate tracking-tight" style = { { color : "var(--muted)" , fontSize : 12 , fontWeight : 800 } } >
349- { room . description }
350- </ p >
351- </ div >
352- </ div >
353- { room . unread > 0 && (
354- < span className = "rounded-full px-2 py-0.5" style = { {
355- background : "var(--neon-cyan)" ,
356- color : "#021014" ,
357- fontSize : 11 ,
358- fontWeight : 950
359- } } >
360- { room . unread }
361- </ span >
362- ) }
363- </ div >
364-
365- < p className = "m-0 line-clamp-2 tracking-tight" style = { { color : "var(--soft-mint)" , fontSize : 13 , fontWeight : 850 , lineHeight : 1.55 } } >
366- { room . lastMessage }
367- </ p >
368- < div className = "mt-4 flex items-center justify-between gap-3" >
369- < span className = "inline-flex items-center gap-1.5" style = { { color : "var(--muted)" , fontSize : 12 , fontWeight : 800 } } >
370- < Clock3 size = { 13 } />
371- { room . updatedAt }
372- </ span >
373- < span className = "inline-flex items-center gap-1.5" style = { { color : room . accent , fontSize : 12 , fontWeight : 950 } } >
374- 입장
375- < ArrowRight size = { 14 } className = "transition-transform group-hover:translate-x-1" />
376- </ span >
377- </ div >
378- </ button >
379- ) ;
380- } ) }
381- </ div >
382-
383- < aside className = "p-5 xl:border-l" style = { { borderColor : "rgba(32, 227, 255, 0.12)" } } >
384- < div className = "rounded-2xl px-4 py-4" style = { {
385- background : "rgba(5, 11, 20, 0.46)" ,
386- border : "1px solid rgba(32, 227, 255, 0.14)"
387- } } >
388- < div className = "mb-4 flex items-center justify-between gap-3" >
389- < div >
390- < p className = "m-0 tracking-tight" style = { { color : "var(--white)" , fontSize : 15 , fontWeight : 950 } } >
391- { activeRoom . name } 미리보기
392- </ p >
393- < p className = "m-0 mt-1 tracking-tight" style = { { color : "var(--muted)" , fontSize : 12 , fontWeight : 800 } } >
394- { activeRoom . online } 명 대화 중
395- </ p >
396- </ div >
397- < span className = "h-3 w-3 rounded-full" style = { { background : activeRoom . accent , boxShadow : `0 0 16px ${ activeRoom . accent } ` } } />
398- </ div >
399-
400- < div className = "grid gap-3" >
401- { roomPreviewMessages . map ( ( message ) => (
402- < div key = { `${ message . author } -${ message . time } ` } className = "rounded-2xl px-3 py-3" style = { {
403- background : "rgba(234, 247, 255, 0.055)" ,
404- border : "1px solid rgba(234, 247, 255, 0.08)"
405- } } >
406- < div className = "mb-1 flex items-center justify-between gap-2" >
407- < span style = { { color : "var(--white)" , fontSize : 12 , fontWeight : 950 } } > { message . author } </ span >
408- < span className = "font-mono" style = { { color : "var(--muted)" , fontSize : 10 , fontWeight : 800 } } > { message . time } </ span >
409- </ div >
410- < p className = "m-0 tracking-tight" style = { { color : "var(--soft-mint)" , fontSize : 12 , fontWeight : 800 , lineHeight : 1.55 } } >
411- { message . text }
412- </ p >
413- </ div >
414- ) ) }
415- </ div >
416- </ div >
417- </ aside >
418- </ div >
419- </ section >
420-
421290 < div className = "grid grid-cols-1 gap-4 lg:grid-cols-3" >
422291 { members . map ( ( member ) => (
423292 < article
@@ -469,7 +338,7 @@ export function TeamPanel({ onInvite, onOpenChannel }: TeamPanelProps) {
469338 </ span >
470339 < button
471340 type = "button"
472- onClick = { ( ) => handleDeleteMember ( member . id ) }
341+ onClick = { ( ) => handleDeleteClick ( member ) }
473342 disabled = { member . protected }
474343 className = "flex h-8 w-8 items-center justify-center rounded-lg transition-all hover:scale-105"
475344 style = { {
@@ -568,6 +437,73 @@ export function TeamPanel({ onInvite, onOpenChannel }: TeamPanelProps) {
568437 } ) }
569438 </ div >
570439 </ section >
440+
441+ { confirmTarget && (
442+ < div
443+ className = "fixed inset-0 z-50 flex items-center justify-center px-4"
444+ style = { { background : "rgba(0, 0, 0, 0.65)" , backdropFilter : "blur(8px)" } }
445+ onClick = { ( ) => setConfirmTarget ( null ) }
446+ >
447+ < div
448+ className = "w-full max-w-[420px] rounded-[24px] px-8 py-8"
449+ style = { {
450+ background : "linear-gradient(135deg, rgba(11, 22, 40, 0.98), rgba(5, 11, 20, 0.98))" ,
451+ border : "1px solid rgba(255, 107, 107, 0.35)" ,
452+ boxShadow : "0 28px 80px rgba(0, 0, 0, 0.55), 0 0 40px rgba(255, 107, 107, 0.10)" ,
453+ } }
454+ onClick = { ( e ) => e . stopPropagation ( ) }
455+ >
456+ < div className = "mb-5 flex h-14 w-14 items-center justify-center rounded-2xl" style = { {
457+ background : "rgba(255, 107, 107, 0.12)" ,
458+ border : "1px solid rgba(255, 107, 107, 0.30)"
459+ } } >
460+ < Trash2 size = { 26 } style = { { color : "#FF6B6B" } } />
461+ </ div >
462+
463+ < h3 className = "m-0 mb-2 tracking-tight" style = { { color : "var(--white)" , fontSize : 20 , fontWeight : 950 } } >
464+ 정말 추방하시겠어요?
465+ </ h3 >
466+ < p className = "m-0 mb-7 tracking-tight" style = { { color : "var(--muted)" , fontSize : 14 , fontWeight : 800 , lineHeight : 1.6 } } >
467+ < span style = { { color : "var(--white)" , fontWeight : 950 } } > { confirmTarget . name } </ span > 팀원을 워크스페이스에서
468+ 추방합니다. 이 작업은 되돌릴 수 없습니다.
469+ </ p >
470+
471+ < div className = "flex gap-3" >
472+ < button
473+ type = "button"
474+ onClick = { ( ) => setConfirmTarget ( null ) }
475+ className = "flex-1 rounded-xl border-0 py-3 tracking-tight transition-all hover:scale-[1.02]"
476+ style = { {
477+ background : "rgba(234, 247, 255, 0.07)" ,
478+ border : "1px solid rgba(234, 247, 255, 0.14)" ,
479+ color : "var(--muted)" ,
480+ cursor : "pointer" ,
481+ fontSize : 15 ,
482+ fontWeight : 900
483+ } }
484+ >
485+ 취소
486+ </ button >
487+ < button
488+ type = "button"
489+ onClick = { handleConfirmDelete }
490+ className = "flex-1 rounded-xl border-0 py-3 tracking-tight transition-all hover:scale-[1.02]"
491+ style = { {
492+ background : "linear-gradient(135deg, #FF6B6B, #cc3333)" ,
493+ border : "none" ,
494+ color : "#fff" ,
495+ cursor : "pointer" ,
496+ fontSize : 15 ,
497+ fontWeight : 950 ,
498+ boxShadow : "0 0 24px rgba(255, 107, 107, 0.30)"
499+ } }
500+ >
501+ 추방하기
502+ </ button >
503+ </ div >
504+ </ div >
505+ </ div >
506+ ) }
571507 </ div >
572508 ) ;
573509}
0 commit comments