Feat: 멤버관련 작업 중
This commit is contained in:
parent
6ac6ddf987
commit
cd4427fd74
@ -29,17 +29,17 @@ const formSchema = z.object({
|
|||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type MemberManageFormValues = z.infer<typeof formSchema>;
|
export type ProjectMemberManageFormValues = z.infer<typeof formSchema>;
|
||||||
|
|
||||||
interface AdminMemberManageFormProps {
|
interface ProjectMemberManageFormProps {
|
||||||
members: ProjectMemberResponse[];
|
members: ProjectMemberResponse[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function AdminMemberManageForm({ members }: AdminMemberManageFormProps) {
|
export default function ProjectMemberManageForm({ members }: ProjectMemberManageFormProps) {
|
||||||
const { projectId } = useParams<{ projectId: string }>();
|
const { projectId } = useParams<{ projectId: string }>();
|
||||||
const { mutate: updatePrivilege } = useUpdateProjectMemberPrivilegeQuery();
|
const { mutate: updatePrivilege } = useUpdateProjectMemberPrivilegeQuery();
|
||||||
|
|
||||||
const form = useForm<MemberManageFormValues>({
|
const form = useForm<ProjectMemberManageFormValues>({
|
||||||
resolver: zodResolver(formSchema),
|
resolver: zodResolver(formSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
members: members.map((m) => ({
|
members: members.map((m) => ({
|
@ -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<typeof formSchema>;
|
||||||
|
|
||||||
|
interface WorkspaceMemberManageFormProps {
|
||||||
|
members: WorkspaceMemberResponse[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function WorkspaceMemberManageForm({ members }: WorkspaceMemberManageFormProps) {
|
||||||
|
const { workspaceId } = useParams<{ workspaceId: string }>();
|
||||||
|
const { mutate: updatePrivilege } = useUpdateProjectMemberPrivilegeQuery();
|
||||||
|
|
||||||
|
const form = useForm<WorkspaceMemberManageFormValues>({
|
||||||
|
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 (
|
||||||
|
<Form {...form}>
|
||||||
|
<div className="flex w-full flex-col gap-4">
|
||||||
|
{members.map((member, index) => (
|
||||||
|
<div
|
||||||
|
key={member.memberId}
|
||||||
|
className="flex items-center gap-4"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
name={`members.${index}.nickname`}
|
||||||
|
control={form.control}
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex-1">
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
placeholder="닉네임을 입력하세요."
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
name={`members.${index}.role`}
|
||||||
|
control={form.control}
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex-1">
|
||||||
|
<FormControl>
|
||||||
|
<Select
|
||||||
|
onValueChange={(value) => {
|
||||||
|
field.onChange(value);
|
||||||
|
handleRoleChange(member.memberId, value as Role);
|
||||||
|
}}
|
||||||
|
defaultValue={field.value}
|
||||||
|
>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue placeholder="역할을 선택해주세요." />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
{roles.map((role) => (
|
||||||
|
<SelectItem
|
||||||
|
key={role}
|
||||||
|
value={role}
|
||||||
|
>
|
||||||
|
{roleToStr[role]}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
}
|
@ -1,34 +1,44 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import AdminMemberManageForm from './AdminMemberManageForm';
|
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import useProjectMembersQuery from '@/queries/projects/useProjectMembersQuery';
|
|
||||||
import useAuthStore from '@/stores/useAuthStore';
|
import useAuthStore from '@/stores/useAuthStore';
|
||||||
|
import useAddWorkspaceMemberQuery from '@/queries/workspaces/useAddWorkspaceMemberQuery';
|
||||||
import useAddProjectMemberQuery from '@/queries/projects/useAddProjectMemberQuery';
|
import useAddProjectMemberQuery from '@/queries/projects/useAddProjectMemberQuery';
|
||||||
|
import useWorkspaceMembersQuery from '@/queries/workspaces/useWorkspaceMembersQuery';
|
||||||
|
import useProjectMembersQuery from '@/queries/projects/useProjectMembersQuery';
|
||||||
import MemberAddModal from '../MemberAddModal';
|
import MemberAddModal from '../MemberAddModal';
|
||||||
import { MemberAddFormValues } from '../MemberAddModal/MemberAddForm';
|
import { MemberAddFormValues } from '../MemberAddModal/MemberAddForm';
|
||||||
|
import WorkspaceMemberManageForm from './WorkspaceMemberManageForm';
|
||||||
|
import ProjectMemberManageForm from './ProjectMemberManageForm';
|
||||||
|
|
||||||
export default function AdminMemberManage() {
|
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 profile = useAuthStore((state) => state.profile);
|
||||||
const memberId = profile?.id || 0;
|
const memberId = profile?.id || 0;
|
||||||
|
|
||||||
const { data: members = [] } = useProjectMembersQuery(Number(projectId), memberId);
|
|
||||||
const addProjectMember = useAddProjectMemberQuery();
|
|
||||||
|
|
||||||
const [, setInviteModalOpen] = useState(false);
|
const [, setInviteModalOpen] = useState(false);
|
||||||
|
|
||||||
const handleMemberInvite = (data: MemberAddFormValues) => {
|
const handleMemberInvite = (data: MemberAddFormValues) => {
|
||||||
addProjectMember.mutate({
|
if (workspaceId) {
|
||||||
projectId: Number(projectId),
|
const addWorkspaceMember = useAddWorkspaceMemberQuery();
|
||||||
|
addWorkspaceMember.mutate({
|
||||||
|
workspaceId: Number(workspaceId),
|
||||||
memberId: memberId,
|
memberId: memberId,
|
||||||
newMember: {
|
newMember: {
|
||||||
// Todo : 멤버 id로 수정하는 로직 수정해야한다.
|
|
||||||
// memberId: data.email,
|
|
||||||
memberId: 0,
|
memberId: 0,
|
||||||
privilegeType: data.role,
|
privilegeType: data.role,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
console.log('Invited:', data);
|
} else if (projectId) {
|
||||||
|
const addProjectMember = useAddProjectMemberQuery();
|
||||||
|
addProjectMember.mutate({
|
||||||
|
projectId: Number(projectId),
|
||||||
|
memberId: memberId,
|
||||||
|
newMember: {
|
||||||
|
memberId: 0,
|
||||||
|
privilegeType: data.role,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
setInviteModalOpen(false);
|
setInviteModalOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -39,7 +49,10 @@ export default function AdminMemberManage() {
|
|||||||
<MemberAddModal onSubmit={handleMemberInvite} />
|
<MemberAddModal onSubmit={handleMemberInvite} />
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<AdminMemberManageForm members={members} />
|
{workspaceId && <WorkspaceMemberManageForm members={useWorkspaceMembersQuery(Number(workspaceId)).data || []} />}
|
||||||
|
{projectId && (
|
||||||
|
<ProjectMemberManageForm members={useProjectMembersQuery(Number(projectId), memberId).data || []} />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '../ui/form';
|
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '../ui/form';
|
||||||
import { Input } from '../ui/input';
|
|
||||||
import { Button } from '../ui/button';
|
import { Button } from '../ui/button';
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../ui/select';
|
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';
|
type PrivilegeType = 'ADMIN' | 'MANAGER' | 'EDITOR' | 'VIEWER';
|
||||||
|
|
||||||
@ -43,6 +45,9 @@ export default function MemberAddForm({ onSubmit }: { onSubmit: (data: MemberAdd
|
|||||||
defaultValues,
|
defaultValues,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [keyword, setKeyword] = useState('');
|
||||||
|
const { data: members } = useSearchMembersByEmailQuery(keyword);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
@ -56,16 +61,39 @@ export default function MemberAddForm({ onSubmit }: { onSubmit: (data: MemberAdd
|
|||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel className="body-strong">이메일</FormLabel>
|
<FormLabel className="body-strong">이메일</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<SearchInput
|
||||||
placeholder="초대할 멤버의 이메일 주소를 입력해주세요."
|
placeholder="초대할 멤버의 이메일을 검색하세요."
|
||||||
maxLength={40}
|
value={field.value}
|
||||||
{...field}
|
onChange={(e) => {
|
||||||
|
field.onChange(e);
|
||||||
|
setKeyword(e.target.value);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
{members && (
|
||||||
|
<ul className="mt-2">
|
||||||
|
{members.map((member) => (
|
||||||
|
<li
|
||||||
|
key={member.id}
|
||||||
|
className="py-1"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={member.profileImage}
|
||||||
|
alt={member.nickname}
|
||||||
|
className="h-8 w-8 rounded-full"
|
||||||
|
/>
|
||||||
|
<span className="ml-2">
|
||||||
|
{member.nickname} ({member.email})
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
|
||||||
<FormField
|
<FormField
|
||||||
name="role"
|
name="role"
|
||||||
control={form.control}
|
control={form.control}
|
||||||
|
@ -13,6 +13,8 @@ import { Suspense } from 'react';
|
|||||||
import WorkspaceBrowseIndex from '@/pages/WorkspaceBrowseIndex';
|
import WorkspaceBrowseIndex from '@/pages/WorkspaceBrowseIndex';
|
||||||
import AdminIndex from '@/pages/AdminIndex';
|
import AdminIndex from '@/pages/AdminIndex';
|
||||||
import LabelCanvas from '@/pages/LabelCanvas';
|
import LabelCanvas from '@/pages/LabelCanvas';
|
||||||
|
import ReviewDetail from '@/components/ReviewDetail';
|
||||||
|
|
||||||
export const webPath = {
|
export const webPath = {
|
||||||
home: () => '/',
|
home: () => '/',
|
||||||
browse: () => '/browse',
|
browse: () => '/browse',
|
||||||
@ -86,6 +88,10 @@ const router = createBrowserRouter([
|
|||||||
path: 'reviews',
|
path: 'reviews',
|
||||||
element: <ReviewList />,
|
element: <ReviewList />,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'reviews/:projectId/:reviewId',
|
||||||
|
element: <ReviewDetail />,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'members',
|
path: 'members',
|
||||||
element: <AdminMemberManage />,
|
element: <AdminMemberManage />,
|
||||||
|
Loading…
Reference in New Issue
Block a user