1- import { useMemo , useState } from "react" ;
1+ import { useEffect , useMemo , useState } from "react" ;
22import CardBox from "./components/CardBox" ;
33import RoundTabButton from "./components/RoundTabButton" ;
44import refreshIcon from "../../assets/refresh.svg" ;
@@ -8,25 +8,29 @@ import on from "../../assets/on.svg";
88import onIcon from "../../assets/toggleOn.svg" ; // 켜짐 상태 이미지
99import offIcon from "../../assets/toggleOFF.svg" ;
1010import { useWindowWidth } from "../../hooks/useWindowWidth" ;
11- type WaitingStatus = "대기 중" | "호출 중" | "입장 완료" | "대기 취소" ;
11+ import { useUpdateReservationStatus } from "../../hooks/useUpdateReservationStatus" ;
12+ type WaitingStatus =
13+ | "WAITING"
14+ | "CALLING"
15+ | "CONFIRMED"
16+ | "CANCELLED"
17+ | "NO_SHOW" ;
1218
1319interface Reservation {
1420 id : number ;
15- number : number ;
1621 time : string ;
22+ requestedAt : string ;
1723 waitMinutes : number ;
1824 peopleCount : number ;
1925 name : string ;
2026 phone : string ;
2127 status : WaitingStatus ;
28+ calledAt ?: string ;
2229}
2330
2431const AdminHome = ( ) => {
25- const handleCall = ( ) => alert ( "🔔 고객 호출" ) ;
26- const handleEnter = ( ) => alert ( "🏢 고객 입장 처리" ) ;
27- const handleClose = ( ) => alert ( "❌ 카드 닫기" ) ;
28-
2932 const width = useWindowWidth ( ) ;
33+ const { mutate : updateStatus } = useUpdateReservationStatus ( ) ;
3034
3135 console . log ( width ) ;
3236
@@ -35,6 +39,8 @@ const AdminHome = () => {
3539 const [ activeTab , setActiveTab ] = useState ( "전체 보기" ) ;
3640 const storeId = 1 ; //현재는 임시로 mockdata씀
3741 const [ isOn , setIsOn ] = useState ( false ) ;
42+ const [ reservations , setReservations ] = useState < Reservation [ ] > ( [ ] ) ;
43+ const { data, isLoading, isError } = useGetReservationList ( storeId ) ;
3844
3945 const toggle = ( ) => setIsOn ( ( prev ) => ! prev ) ;
4046 const statusMap = {
@@ -43,28 +49,119 @@ const AdminHome = () => {
4349 CONFIRMED : "입장 완료" ,
4450 CANCELLED : "대기 취소" ,
4551 } ;
46- const { data, isLoading, isError } = useGetReservationList ( storeId ) ;
47- console . log ( data , "ReservationList" ) ;
48-
49- // 전체 목록 예약순 예약 번호 부여
50- const numberedReservations = useMemo ( ( ) => {
51- if ( ! data ) return [ ] ;
52- return data . reservationList . map ( ( res , idx ) => ( {
53- ...res ,
54- number : idx + 1 ,
55- } ) ) ;
56- } , [ data ] ) ;
5752
5853 const filteredReservations = useMemo ( ( ) => {
59- if ( activeTab === "전체 보기" ) return numberedReservations ;
54+ if ( activeTab === "전체 보기" ) return reservations ;
6055
6156 const targetStatus = Object . entries ( statusMap ) . find (
6257 ( [ , label ] ) => label === activeTab
6358 ) ?. [ 0 ] ;
6459
6560 if ( ! targetStatus ) return [ ] ;
66- return numberedReservations . filter ( ( res ) => res . status === targetStatus ) ;
67- } , [ numberedReservations , activeTab ] ) ;
61+
62+ return reservations . filter ( ( res ) => res . status === targetStatus ) ;
63+ } , [ reservations , activeTab ] ) ;
64+
65+ // 호출 버튼 클릭 이벤트
66+ const handleCall = ( id : number ) => {
67+ // 상태 변화 api 호출 --> 성공시 --> reservation status 변경(호출 시간 calledAt추가해야 됨)
68+ updateStatus (
69+ { reservationId : id , status : "CALLING" } ,
70+ {
71+ onSuccess : ( ) => {
72+ setReservations ( ( prev ) =>
73+ prev . map ( ( res ) =>
74+ res . id === id
75+ ? {
76+ ...res ,
77+ status : "CALLING" ,
78+ calledAt : new Date ( ) . toISOString ( ) ,
79+ }
80+ : res
81+ )
82+ ) ;
83+ } ,
84+ onError : ( ) => {
85+ alert ( "호출 상태 변경 실패" ) ;
86+ } ,
87+ }
88+ ) ;
89+ } ;
90+
91+ const handleEnter = ( id : number ) => {
92+ updateStatus (
93+ { reservationId : id , status : "CONFIRMED" } ,
94+ {
95+ onSuccess : ( ) => {
96+ setReservations ( ( prev ) =>
97+ prev . map ( ( res ) =>
98+ res . id === id ? { ...res , status : "CONFIRMED" } : res
99+ )
100+ ) ;
101+ } ,
102+ }
103+ ) ;
104+ } ;
105+
106+ const handleClose = ( id : number ) => {
107+ updateStatus (
108+ { reservationId : id , status : "CANCELLED" } ,
109+ {
110+ onSuccess : ( ) => {
111+ setReservations ( ( prev ) =>
112+ prev . map ( ( res ) =>
113+ res . id === id ? { ...res , status : "CANCELLED" } : res
114+ )
115+ ) ;
116+ } ,
117+ }
118+ ) ;
119+ } ;
120+
121+ const handleNoShow = ( id : number ) => {
122+ const target = reservations . find ( ( res ) => res . id === id ) ;
123+ if ( target ?. status === "NO_SHOW" ) return ;
124+ updateStatus (
125+ { reservationId : id , status : "NO_SHOW" } ,
126+ {
127+ onSuccess : ( ) => {
128+ setReservations ( ( prev ) =>
129+ prev . map ( ( res ) =>
130+ res . id === id ? { ...res , status : "NO_SHOW" } : res
131+ )
132+ ) ;
133+ } ,
134+ }
135+ ) ;
136+ } ;
137+
138+ useEffect ( ( ) => {
139+ if ( ! data ?. reservationList ) return ;
140+
141+ const now = Date . now ( ) ;
142+
143+ setReservations (
144+ data . reservationList . map ( ( res , idx ) => {
145+ const requested = new Date ( res . requestedAt ) ;
146+ return {
147+ id : res . id ,
148+ requestedAt : res . requestedAt , //서버 데이터 문자열 그대로 사용("2025-06-24T12:33:26")
149+ time : requested . toLocaleTimeString ( "ko-KR" , {
150+ hour : "2-digit" ,
151+ minute : "2-digit" ,
152+ hour12 : true ,
153+ } ) ,
154+ waitMinutes : Math . floor ( ( now - requested . getTime ( ) ) / 60000 ) ,
155+ peopleCount : res . partySize ,
156+ name : res . userName ,
157+ phone : "010-****-****" ,
158+ status : res . status ,
159+ calledAt :
160+ res . status === "CALLING" ? requested . toISOString ( ) : undefined ,
161+ } ;
162+ } )
163+ ) ;
164+ } , [ data ] ) ;
68165
69166 return (
70167 < div
@@ -100,7 +197,7 @@ const AdminHome = () => {
100197 < section id = "대기자 목록" className = "flex flex-col w-full" >
101198 < h1 className = "title-20-bold mb-5" > 대기자 목록</ h1 >
102199 < div className = "flex justify-between items-center" >
103- < div className = "flex flex-wrap gap-2" >
200+ < div className = "flex flex-wrap gap-2 overflow-x-auto scrollbar-hide [@media(max-width:431px)]:flex-nowrap " >
104201 { [ "전체 보기" , "대기 중" , "호출 중" , "입장 완료" , "대기 취소" ] . map (
105202 ( label ) => (
106203 < RoundTabButton
@@ -122,23 +219,33 @@ const AdminHome = () => {
122219 </ section >
123220
124221 < div className = "w-full grid grid-cols-1 gap-[10px] md:grid-cols-2 [@media(max-width:431px)]:place-items-center" >
125- { filteredReservations . map ( ( res ) => (
126- < WaitingCard
127- key = { res . id }
128- number = { res . number }
129- time = { new Date ( res . requestedAt ) . toLocaleTimeString ( "ko-KR" , {
130- hour : "2-digit" ,
131- minute : "2-digit" ,
132- } ) }
133- waitMinutes = { 3 }
134- peopleCount = { res . partySize }
135- name = { res . userName }
136- phone = "010-****-****"
137- onCall = { handleCall }
138- onEnter = { handleEnter }
139- onClose = { handleClose }
140- />
141- ) ) }
222+ { filteredReservations . map ( ( res ) => {
223+ const requested = new Date ( res . requestedAt ) ;
224+
225+ return (
226+ < WaitingCard
227+ key = { res . id }
228+ number = { res . id }
229+ time = { requested . toLocaleTimeString ( "ko-KR" , {
230+ hour : "2-digit" ,
231+ minute : "2-digit" ,
232+ hour12 : true ,
233+ } ) }
234+ waitMinutes = { Math . floor (
235+ ( Date . now ( ) - requested . getTime ( ) ) / 60000
236+ ) }
237+ peopleCount = { res . peopleCount }
238+ name = { res . name }
239+ phone = "010-****-****"
240+ status = { res . status }
241+ calledAt = { res . calledAt }
242+ onCall = { ( ) => handleCall ( res . id ) }
243+ onEnter = { ( ) => handleEnter ( res . id ) }
244+ onClose = { ( ) => handleClose ( res . id ) }
245+ onNoShow = { ( ) => handleNoShow ( res . id ) }
246+ />
247+ ) ;
248+ } ) }
142249 </ div >
143250 </ div >
144251 ) ;
0 commit comments