1+ import { useCallback , useState } from 'react' ;
12import { ChevronLeftIcon , ChevronRightIcon } from 'lucide-react' ;
23import {
34 Table ,
@@ -12,6 +13,10 @@ import { Checkbox } from '@/components/ui/checkbox';
1213import { Item , ItemTableProps , ItemTypeText } from '@/types/items' ;
1314import Image from 'next/image' ;
1415import { PageChangeAction } from '@/types/paginationType' ;
16+ import { useMutation } from '@tanstack/react-query' ;
17+ import { updateItems } from '@/services/items' ;
18+ import toast from 'react-hot-toast' ;
19+ import Sidebar from '../ItemSidebar/index' ;
1520
1621export default function ItemTable ( {
1722 items = [ ] ,
@@ -25,17 +30,31 @@ export default function ItemTable({
2530 console . log ( pageAction ) ;
2631 } ,
2732} : ItemTableProps ) {
28- // 선택된 항목을 다루는 함수
33+ const [ formData , setFormData ] = useState ( {
34+ itemId : selected ,
35+ selectedImage : null as File | null ,
36+ itemName : '' ,
37+ isConsumable : false ,
38+ quantity : '' as number | '' ,
39+ } ) ;
40+
41+ const mutation = useMutation ( {
42+ mutationFn : ( data : { itemId : number ; formData : FormData } ) =>
43+ updateItems ( data . formData , data . itemId ) ,
44+ onSuccess : ( ) => {
45+ toast . success ( '변경사항이 저장되었습니다.' ) ;
46+ } ,
47+ onError : ( ) => {
48+ toast . error ( '변경사항 저장에 실패했습니다.' ) ;
49+ } ,
50+ } ) ;
51+
2952 const handleSelect = ( id : number ) => {
30- setSelected ( id ) ; // 단일 선택으로 변경
53+ setSelected ( id ) ;
3154 } ;
3255
3356 const handleSelectAll = ( ) => {
34- if ( selected === items [ 0 ] ?. itemId ) {
35- setSelected ( 0 ) ; // 전체 선택 해제
36- } else {
37- setSelected ( items [ 0 ] ?. itemId ) ; // 첫 번째 항목을 선택(전체 선택)
38- }
57+ setSelected ( selected === items [ 0 ] ?. itemId ? 0 : items [ 0 ] ?. itemId ) ; // Toggle selection of first item
3958 } ;
4059
4160 const handlePageChangeBtnClick = (
@@ -46,6 +65,49 @@ export default function ItemTable({
4665 onPageChange ( pageChangeAction ) ;
4766 } ;
4867
68+ const handleUpdateItem = useCallback (
69+ ( itemId : number ) => {
70+ const { itemName, quantity, selectedImage, isConsumable } = formData ;
71+
72+ if ( ! itemName || quantity === '' || quantity <= 0 ) {
73+ toast . error ( '모든 정보를 입력하세요.' ) ;
74+ return ;
75+ }
76+
77+ const newFormData = new FormData ( ) ;
78+ if ( selectedImage ) newFormData . append ( 'image' , selectedImage ) ;
79+
80+ const editData = {
81+ name : itemName ,
82+ type : isConsumable ? 'CONSUMPTION' : 'RENTAL' ,
83+ count : Number ( quantity ) ,
84+ } ;
85+
86+ newFormData . append (
87+ 'itemRequest' ,
88+ new Blob ( [ JSON . stringify ( editData ) ] , { type : 'application/json' } ) ,
89+ ) ;
90+
91+ mutation . mutate ( { itemId, formData : newFormData } ) ;
92+
93+ setFormData ( {
94+ itemId : 0 ,
95+ selectedImage : null ,
96+ itemName : '' ,
97+ isConsumable : false ,
98+ quantity : '' ,
99+ } ) ;
100+ } ,
101+ [ formData , mutation ] ,
102+ ) ;
103+
104+ const handleImageChange = ( event : React . ChangeEvent < HTMLInputElement > ) => {
105+ const file = event . target . files ?. [ 0 ] ;
106+ if ( file ) {
107+ setFormData ( ( prev ) => ( { ...prev , selectedImage : file } ) ) ;
108+ }
109+ } ;
110+
49111 return (
50112 < div className = "flex w-full flex-col p-10" >
51113 < Table >
@@ -54,7 +116,7 @@ export default function ItemTable({
54116 { showCheckboxes && (
55117 < TableHead className = "w-10 text-center" >
56118 < Checkbox
57- checked = { selected === items [ 0 ] ?. itemId } // 선택된 첫 번째 항목이 전체 항목과 일치하는지 확인
119+ checked = { selected === items [ 0 ] ?. itemId }
58120 onCheckedChange = { handleSelectAll }
59121 />
60122 </ TableHead >
@@ -73,7 +135,7 @@ export default function ItemTable({
73135 { showCheckboxes && (
74136 < TableCell className = "w-10 text-center" >
75137 < Checkbox
76- checked = { selected === item . itemId } // selected 상태가 현재 itemId와 일치하면 체크
138+ checked = { selected === item . itemId }
77139 onCheckedChange = { ( ) => handleSelect ( item . itemId ) }
78140 />
79141 </ TableCell >
@@ -97,6 +159,90 @@ export default function ItemTable({
97159 < TableCell className = "w-30 text-center" >
98160 { item . renterCount }
99161 </ TableCell >
162+ < TableCell className = "w-30 text-center" >
163+ < Sidebar triggerText = "수정하기" title = "물품 수정하기" >
164+ < div className = "mt-4 flex flex-col gap-2" >
165+ { /* eslint-disable-next-line jsx-a11y/label-has-associated-control */ }
166+ < label className = "text-sm font-semibold" >
167+ 복지물품명
168+ </ label >
169+ < input
170+ type = "text"
171+ value = { formData . itemName }
172+ onChange = { ( e ) =>
173+ setFormData ( { ...formData , itemName : e . target . value } )
174+ }
175+ className = "rounded-md border px-4 py-2"
176+ />
177+ { /* eslint-disable-next-line jsx-a11y/label-has-associated-control */ }
178+ < label className = "text-sm font-semibold" >
179+ 소모품 여부
180+ </ label >
181+ < div className = "flex gap-2" >
182+ < button
183+ type = "button"
184+ className = { `rounded-md border px-4 py-2 ${ ! formData . isConsumable ? 'bg-blue-500 text-white' : 'text-blue-500' } ` }
185+ onClick = { ( ) =>
186+ setFormData ( { ...formData , isConsumable : false } )
187+ }
188+ >
189+ 대여물품
190+ </ button >
191+ < button
192+ type = "button"
193+ className = { `rounded-md border px-4 py-2 ${ formData . isConsumable ? 'bg-blue-500 text-white' : 'text-blue-500' } ` }
194+ onClick = { ( ) =>
195+ setFormData ( { ...formData , isConsumable : true } )
196+ }
197+ >
198+ 소모물품
199+ </ button >
200+ </ div >
201+ { /* eslint-disable-next-line jsx-a11y/label-has-associated-control */ }
202+ < label className = "text-sm font-semibold" > 수량</ label >
203+ < input
204+ type = "number"
205+ value = { formData . quantity }
206+ onChange = { ( e ) =>
207+ setFormData ( {
208+ ...formData ,
209+ quantity : Number ( e . target . value ) ,
210+ } )
211+ }
212+ className = "rounded-md border px-4 py-2"
213+ />
214+ { /* eslint-disable-next-line jsx-a11y/label-has-associated-control */ }
215+ < label className = "text-sm font-semibold" >
216+ 이미지 업로드
217+ </ label >
218+ < p > 이미지 변경이 없을 경우 업로드하지 않고 저장합니다.</ p >
219+ < input
220+ type = "file"
221+ accept = "image/*"
222+ onChange = { handleImageChange }
223+ />
224+ { formData . selectedImage && (
225+ < Image
226+ src = { URL . createObjectURL ( formData . selectedImage ) }
227+ width = { 24 }
228+ height = { 24 }
229+ alt = "미리보기"
230+ className = "mt-2 h-32 w-32 rounded-md object-cover"
231+ />
232+ ) }
233+ </ div >
234+ < div className = "flex justify-center" >
235+ < Button
236+ size = "lg"
237+ variant = "primary"
238+ onClick = { ( ) => handleUpdateItem ( item . itemId ) }
239+ className = "mt-4 w-full"
240+ >
241+ 저장
242+ </ Button >
243+ </ div >
244+ </ Sidebar >
245+ </ TableCell >
100246 </ TableRow >
101247 ) )
102248 ) : (
0 commit comments