@@ -13,34 +13,54 @@ import {
1313import { Input } from "@/shared/components/input" ;
1414import { Label } from "@/shared/components/label" ;
1515import { uploadImage } from "@/shared/lib/upload-image" ;
16+ import { useGroupMembers } from "@/domain/members/hooks/use-group-members" ;
17+ import { MemberSelectDialog } from "@/domain/members/components/member-select-dialog" ;
18+ import type { GroupMemberInfo } from "@/shared/apis" ;
1619
17- import { X } from "lucide-react" ;
20+ import { UserPlus , X } from "lucide-react" ;
21+ import { useState } from "react" ;
1822import type { ChangeEvent } from "react" ;
1923import { useController , useFieldArray , useForm } from "react-hook-form" ;
2024
25+ const ROLE_LABEL_MAP : Record < GroupMemberInfo [ "role" ] , string > = {
26+ OWNER : "소유자" ,
27+ HEAD_MANAGER : "총괄 매니저" ,
28+ MANAGER : "매니저" ,
29+ STAFF : "스태프" ,
30+ MEMBER : "일반 회원" ,
31+ } ;
32+
2133export type CardsetUpdateFormField = {
2234 name : string ;
2335 publicVisible ?: boolean ;
2436 category : GroupCategory ;
2537 hashtag : { name : string } [ ] ;
2638 imageRefId ?: number ;
39+ managers : number [ ] ;
2740} ;
2841
2942type Props = {
43+ groupId : number ;
3044 onSubmit : ( form : CardsetUpdateFormField ) => void ;
3145 formId ?: string ;
3246 defaultValues ?: Partial < CardsetUpdateFormField > ;
3347} ;
3448
3549const CardsetUpdateForm = ( {
50+ groupId,
3651 onSubmit,
3752 formId = "cardset-update-form" ,
3853 defaultValues,
3954} : Props ) => {
55+ const [ managerDialogOpen , setManagerDialogOpen ] = useState ( false ) ;
56+
57+ const { data : members = [ ] } = useGroupMembers ( groupId ) ;
58+
4059 const { control, formState, register, setValue, handleSubmit } =
4160 useForm < CardsetUpdateFormField > ( {
4261 defaultValues : {
4362 hashtag : [ ] ,
63+ managers : [ ] ,
4464 ...defaultValues ,
4565 } ,
4666 } ) ;
@@ -60,11 +80,33 @@ const CardsetUpdateForm = ({
6080 control,
6181 } ) ;
6282
83+ const { field : managersField } = useController ( {
84+ name : "managers" ,
85+ control,
86+ } ) ;
87+
6388 const { fields, append, remove } = useFieldArray < CardsetUpdateFormField > ( {
6489 name : "hashtag" ,
6590 control,
6691 } ) ;
6792
93+ const selectedManagerIds : number [ ] = managersField . value ?? [ ] ;
94+ const selectedManagers = members . filter ( ( m ) =>
95+ selectedManagerIds . includes ( m . id )
96+ ) ;
97+ const availableManagers = members
98+ . filter ( ( m ) => ! selectedManagerIds . includes ( m . id ) )
99+ . map ( ( m ) => ( { ...m , subtitle : ROLE_LABEL_MAP [ m . role ] } ) ) ;
100+
101+ const addManager = ( member : GroupMemberInfo ) => {
102+ managersField . onChange ( [ ...selectedManagerIds , member . id ] ) ;
103+ setManagerDialogOpen ( false ) ;
104+ } ;
105+
106+ const removeManager = ( id : number ) => {
107+ managersField . onChange ( selectedManagerIds . filter ( ( mId ) => mId !== id ) ) ;
108+ } ;
109+
68110 const handleChangeImage = async ( e : ChangeEvent < HTMLInputElement > ) => {
69111 const file = e . target . files ?. [ 0 ] ;
70112
@@ -157,6 +199,56 @@ const CardsetUpdateForm = ({
157199 </ Button >
158200 </ div >
159201 </ div >
202+
203+ < div >
204+ < Label className = "mb-1" > 카드셋 관리자</ Label >
205+ < Description > 관리자만 카드셋을 수정할 수 있습니다.</ Description >
206+ { selectedManagers . length > 0 && (
207+ < div className = "flex flex-wrap gap-2 mb-2" >
208+ { selectedManagers . map ( ( m ) => (
209+ < div
210+ key = { m . id }
211+ className = "flex items-center gap-1.5 bg-accent rounded-full pl-1.5 pr-2 py-1 text-sm"
212+ >
213+ < img
214+ src = {
215+ m . profile ||
216+ `https://api.dicebear.com/7.x/avataaars/svg?seed=${ m . name } `
217+ }
218+ alt = { m . name }
219+ className = "size-5 rounded-full object-cover"
220+ />
221+ < span > { m . name } </ span >
222+ < button
223+ type = "button"
224+ onClick = { ( ) => removeManager ( m . id ) }
225+ className = "text-muted-foreground hover:text-foreground transition-colors"
226+ >
227+ < X className = "size-3" />
228+ </ button >
229+ </ div >
230+ ) ) }
231+ </ div >
232+ ) }
233+ < Button
234+ type = "button"
235+ variant = "outline"
236+ size = "sm"
237+ onClick = { ( ) => setManagerDialogOpen ( true ) }
238+ >
239+ < UserPlus className = "size-4 mr-1.5" />
240+ 관리자 추가
241+ </ Button >
242+ </ div >
243+
244+ < MemberSelectDialog
245+ open = { managerDialogOpen }
246+ onOpenChange = { setManagerDialogOpen }
247+ members = { availableManagers }
248+ onSelect = { addManager }
249+ title = "카드셋 관리자 추가"
250+ description = "카드셋을 관리할 멤버를 선택하세요."
251+ />
160252 </ form >
161253 ) ;
162254} ;
0 commit comments