diff --git a/frontend/src/components/AdminMemberManage/AdminMemberManageForm.tsx b/frontend/src/components/AdminMemberManage/ProjectMemberManageForm.tsx similarity index 93% rename from frontend/src/components/AdminMemberManage/AdminMemberManageForm.tsx rename to frontend/src/components/AdminMemberManage/ProjectMemberManageForm.tsx index 8fe7c5d..66f46cd 100644 --- a/frontend/src/components/AdminMemberManage/AdminMemberManageForm.tsx +++ b/frontend/src/components/AdminMemberManage/ProjectMemberManageForm.tsx @@ -29,17 +29,17 @@ const formSchema = z.object({ ), }); -export type MemberManageFormValues = z.infer; +export type ProjectMemberManageFormValues = z.infer; -interface AdminMemberManageFormProps { +interface ProjectMemberManageFormProps { members: ProjectMemberResponse[]; } -export default function AdminMemberManageForm({ members }: AdminMemberManageFormProps) { +export default function ProjectMemberManageForm({ members }: ProjectMemberManageFormProps) { const { projectId } = useParams<{ projectId: string }>(); const { mutate: updatePrivilege } = useUpdateProjectMemberPrivilegeQuery(); - const form = useForm({ + const form = useForm({ resolver: zodResolver(formSchema), defaultValues: { members: members.map((m) => ({ diff --git a/frontend/src/components/AdminMemberManage/WorkspaceMemberManageForm.tsx b/frontend/src/components/AdminMemberManage/WorkspaceMemberManageForm.tsx new file mode 100644 index 0000000..5df5b95 --- /dev/null +++ b/frontend/src/components/AdminMemberManage/WorkspaceMemberManageForm.tsx @@ -0,0 +1,125 @@ +import { useParams } from 'react-router-dom'; +import { useForm } from 'react-hook-form'; +import { z } from 'zod'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { Form, FormControl, FormField, FormItem, FormMessage } from '../ui/form'; +import { Input } from '../ui/input'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../ui/select'; +import { WorkspaceMemberResponse } from '@/types'; +import useUpdateProjectMemberPrivilegeQuery from '@/queries/projects/useUpdateProjectMemberPrivilegeQuery'; + +type Role = 'ADMIN' | 'MANAGER' | 'EDITOR' | 'VIEWER'; + +const roles: Role[] = ['ADMIN', 'MANAGER', 'EDITOR', 'VIEWER']; + +const roleToStr: { [key in Role]: string } = { + ADMIN: '관리자', + MANAGER: '매니저', + EDITOR: '에디터', + VIEWER: '뷰어', +}; + +const formSchema = z.object({ + members: z.array( + z.object({ + memberId: z.number(), + nickname: z.string().nonempty('닉네임을 입력하세요.'), + role: z.enum(roles as [Role, ...Role[]], { errorMap: () => ({ message: '역할을 선택해주세요.' }) }), + }) + ), +}); + +export type WorkspaceMemberManageFormValues = z.infer; + +interface WorkspaceMemberManageFormProps { + members: WorkspaceMemberResponse[]; +} + +export default function WorkspaceMemberManageForm({ members }: WorkspaceMemberManageFormProps) { + const { workspaceId } = useParams<{ workspaceId: string }>(); + const { mutate: updatePrivilege } = useUpdateProjectMemberPrivilegeQuery(); + + const form = useForm({ + resolver: zodResolver(formSchema), + defaultValues: { + members: members.map((m) => ({ + memberId: m.memberId, + nickname: m.nickname, + role: m.privilegeType as Role, + })), + }, + }); + + const handleRoleChange = (memberId: number, role: Role) => { + updatePrivilege({ + workspaceId: Number(workspaceId), + memberId, + privilegeData: { + memberId, + privilegeType: role, + }, + }); + }; + + return ( +
+
+ {members.map((member, index) => ( +
+ ( + + + + + + + )} + /> + + ( + + + + + + + )} + /> +
+ ))} +
+
+ ); +} diff --git a/frontend/src/components/AdminMemberManage/index.tsx b/frontend/src/components/AdminMemberManage/index.tsx index 3748aa3..ef1abb3 100644 --- a/frontend/src/components/AdminMemberManage/index.tsx +++ b/frontend/src/components/AdminMemberManage/index.tsx @@ -1,34 +1,44 @@ import { useState } from 'react'; -import AdminMemberManageForm from './AdminMemberManageForm'; import { useParams } from 'react-router-dom'; -import useProjectMembersQuery from '@/queries/projects/useProjectMembersQuery'; import useAuthStore from '@/stores/useAuthStore'; +import useAddWorkspaceMemberQuery from '@/queries/workspaces/useAddWorkspaceMemberQuery'; import useAddProjectMemberQuery from '@/queries/projects/useAddProjectMemberQuery'; +import useWorkspaceMembersQuery from '@/queries/workspaces/useWorkspaceMembersQuery'; +import useProjectMembersQuery from '@/queries/projects/useProjectMembersQuery'; import MemberAddModal from '../MemberAddModal'; import { MemberAddFormValues } from '../MemberAddModal/MemberAddForm'; +import WorkspaceMemberManageForm from './WorkspaceMemberManageForm'; +import ProjectMemberManageForm from './ProjectMemberManageForm'; export default function AdminMemberManage() { - const { projectId } = useParams<{ workspaceId: string; projectId: string }>(); + const { workspaceId, projectId } = useParams<{ workspaceId?: string; projectId?: string }>(); const profile = useAuthStore((state) => state.profile); const memberId = profile?.id || 0; - const { data: members = [] } = useProjectMembersQuery(Number(projectId), memberId); - const addProjectMember = useAddProjectMemberQuery(); - const [, setInviteModalOpen] = useState(false); const handleMemberInvite = (data: MemberAddFormValues) => { - addProjectMember.mutate({ - projectId: Number(projectId), - memberId: memberId, - newMember: { - // Todo : 멤버 id로 수정하는 로직 수정해야한다. - // memberId: data.email, - memberId: 0, - privilegeType: data.role, - }, - }); - console.log('Invited:', data); + if (workspaceId) { + const addWorkspaceMember = useAddWorkspaceMemberQuery(); + addWorkspaceMember.mutate({ + workspaceId: Number(workspaceId), + memberId: memberId, + newMember: { + memberId: 0, + privilegeType: data.role, + }, + }); + } else if (projectId) { + const addProjectMember = useAddProjectMemberQuery(); + addProjectMember.mutate({ + projectId: Number(projectId), + memberId: memberId, + newMember: { + memberId: 0, + privilegeType: data.role, + }, + }); + } setInviteModalOpen(false); }; @@ -39,7 +49,10 @@ export default function AdminMemberManage() { - + {workspaceId && } + {projectId && ( + + )} ); } diff --git a/frontend/src/components/MemberAddModal/MemberAddForm.tsx b/frontend/src/components/MemberAddModal/MemberAddForm.tsx index b23745b..3031f08 100644 --- a/frontend/src/components/MemberAddModal/MemberAddForm.tsx +++ b/frontend/src/components/MemberAddModal/MemberAddForm.tsx @@ -1,10 +1,12 @@ +import { useState } from 'react'; import { useForm } from 'react-hook-form'; import { z } from 'zod'; import { zodResolver } from '@hookform/resolvers/zod'; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '../ui/form'; -import { Input } from '../ui/input'; import { Button } from '../ui/button'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../ui/select'; +import SearchInput from '../ui/search-input'; +import useSearchMembersByEmailQuery from '@/queries/members/useSearchMembersByEmailQuery'; type PrivilegeType = 'ADMIN' | 'MANAGER' | 'EDITOR' | 'VIEWER'; @@ -43,6 +45,9 @@ export default function MemberAddForm({ onSubmit }: { onSubmit: (data: MemberAdd defaultValues, }); + const [keyword, setKeyword] = useState(''); + const { data: members } = useSearchMembersByEmailQuery(keyword); + return (
이메일 - { + field.onChange(e); + setKeyword(e.target.value); + }} /> )} /> + {members && ( +
    + {members.map((member) => ( +
  • + {member.nickname} + + {member.nickname} ({member.email}) + +
  • + ))} +
+ )} + '/', browse: () => '/browse', @@ -86,6 +88,10 @@ const router = createBrowserRouter([ path: 'reviews', element: , }, + { + path: 'reviews/:projectId/:reviewId', + element: , + }, { path: 'members', element: ,