diff --git a/src/app/api/member.ts b/src/app/api/member.ts index 2803f02..bc2cf6f 100644 --- a/src/app/api/member.ts +++ b/src/app/api/member.ts @@ -13,6 +13,14 @@ const getSidebarInfo = (token: string, memberId: number) => }, }); +const patchStudyMandate = (token: string, studyId: number, newStudyLeaderId: number) => + memberFetcher(`/study/${studyId}/mandate/${newStudyLeaderId}`, { + method: 'PATCH', + headers: { + Authorization: `Bearer ${token}`, + }, + }); + const useGetSideBarInfoQuery = () => { const user = useGetUser(); return useQuery({ @@ -21,4 +29,4 @@ const useGetSideBarInfoQuery = () => { }); }; -export { getSidebarInfo, useGetSideBarInfoQuery }; +export { getSidebarInfo, useGetSideBarInfoQuery, patchStudyMandate }; diff --git a/src/app/api/study.ts b/src/app/api/study.ts index 946f86c..41c48b8 100644 --- a/src/app/api/study.ts +++ b/src/app/api/study.ts @@ -53,22 +53,36 @@ const patchStudyStatus = (studyId: number, status: string) => method: 'PATCH', }); -const postStudyMember = (studyId: number, userId: number) => +const postStudyMember = (token: string, studyId: number, userId: number) => studyFetcher(`/studies/${studyId}/members/${userId}`, { method: 'POST', + headers: { + Authorization: `Bearer ${token}`, + }, }); -const deleteStudyMember = (studyId: number, userId: number) => +const deleteStudyMember = (token: string, studyId: number, userId: number) => studyFetcher(`/studies/${studyId}/members/${userId}`, { method: 'DELETE', + headers: { + Authorization: `Bearer ${token}`, + }, }); -const leaveStudy = (studyId: number) => +const leaveStudy = (token: string, studyId: number) => studyFetcher(`/studies/${studyId}/members`, { method: 'DELETE', + headers: { + Authorization: `Bearer ${token}`, + }, }); -const getStudyMembers = (studyId: number) => studyFetcher(`/studies/${studyId}/members`); +const getStudyMembers = (token: string, studyId: number) => + studyFetcher(`/studies/${studyId}/members`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); const getStudies = (studyId: number, page: number, size: number) => studyFetcher(`/teams/${studyId}/studies?page=${page}&size=${size}`); diff --git a/src/app/team/[teamId]/study/[studyId]/page.tsx b/src/app/team/[teamId]/study/[studyId]/page.tsx index 4959643..dcfddca 100644 --- a/src/app/team/[teamId]/study/[studyId]/page.tsx +++ b/src/app/team/[teamId]/study/[studyId]/page.tsx @@ -17,6 +17,7 @@ import TerminateStudyModal from '@/containers/study/Modal/TerminateStudyModal'; import Participant from '@/containers/study/Participant'; import StudyControlPanel from '@/containers/study/StudyControlPanel'; import StudyInfoCard from '@/containers/study/StudyInfoCard'; +import StudyParticipantMenu from '@/containers/study/StudyParticipantMenu'; import participantData from '@/mocks/participant'; import { DocumentList, Study } from '@/types'; @@ -102,7 +103,16 @@ const Page = ({ params }: { params: { teamId: number; studyId: number } }) => { {/* */} - + + {studyData && ( + + )} + + diff --git a/src/components/StudyCard/types.ts b/src/components/StudyCard/types.ts index a8c6b98..99c0096 100644 --- a/src/components/StudyCard/types.ts +++ b/src/components/StudyCard/types.ts @@ -1,5 +1,5 @@ import { Study } from '@/types'; -export interface StudyCardProps extends Study { +export interface StudyCardProps extends Omit { rank: number; } diff --git a/src/containers/study/StudyParticipantMenu/index.tsx b/src/containers/study/StudyParticipantMenu/index.tsx new file mode 100644 index 0000000..4620032 --- /dev/null +++ b/src/containers/study/StudyParticipantMenu/index.tsx @@ -0,0 +1,79 @@ +import { IconButton, Text } from '@chakra-ui/react'; +import { useState } from 'react'; +import { MdOutlineArrowForwardIos } from 'react-icons/md'; + +import { patchStudyMandate } from '@/app/api/member'; +import { deleteStudyMember, getStudyMembers, postStudyMember } from '@/app/api/study'; +import { getTeamMembers } from '@/app/api/team'; +import ParticipantMenu from '@/components/ParticipantMenu'; +import { StudyParticipantMenuProps } from '@/containers/study/StudyParticipantMenu/types'; +import { useGetFetchWithToken, useMutateWithToken } from '@/hooks/useFetchWithToken'; +import useGetUser from '@/hooks/useGetUser'; +import { Member, StudyMember } from '@/types'; + +const StudyParticipantMenu = ({ studyId, teamId, leaderId }: StudyParticipantMenuProps) => { + const user = useGetUser(); + const [isOpen, setIsOpen] = useState(false); + + const studyMembers = useGetFetchWithToken(getStudyMembers, [studyId], user)?.map( + (data: StudyMember) => + ({ + id: data.memberId, + name: data.name, + imageUrl: data.imageUrl, + }) as Member, + ); + const teamMembers = useGetFetchWithToken(getTeamMembers, [teamId], user); + + const leader = studyMembers?.find((member: Member) => member.id === leaderId); + const includeMembers = studyMembers?.filter((member: Member) => member.id !== leaderId); + const excludeMembers = teamMembers?.filter( + (member: Member) => !studyMembers?.find((studyData: Member) => studyData.id === member.id), + ); + + const addMember = useMutateWithToken(postStudyMember, user); + const deleteMember = useMutateWithToken(deleteStudyMember, user); + const mandateLeader = useMutateWithToken(patchStudyMandate, user); + + const handleAddMember = (member: Member) => { + addMember(studyId, member.id); + }; + + const handleDeleteMember = (member: Member) => { + deleteMember(studyId, member.id); + }; + + const handleMandateLeader = (member: Member) => { + mandateLeader(studyId, member.id); + }; + + return ( + + } + isRound + size="icon_sm" + variant="icon_orange" + /> + 관리 + + ); +}; + +export default StudyParticipantMenu; diff --git a/src/containers/study/StudyParticipantMenu/types.ts b/src/containers/study/StudyParticipantMenu/types.ts new file mode 100644 index 0000000..13c4de7 --- /dev/null +++ b/src/containers/study/StudyParticipantMenu/types.ts @@ -0,0 +1,5 @@ +export interface StudyParticipantMenuProps { + studyId: number; + teamId: number; + leaderId: number; +} diff --git a/src/hooks/useFetchWithToken.ts b/src/hooks/useFetchWithToken.ts index 1024e07..3278fd0 100644 --- a/src/hooks/useFetchWithToken.ts +++ b/src/hooks/useFetchWithToken.ts @@ -26,8 +26,8 @@ export function useGetFetchWithToken(fetch: (token: string, ...props: any[]) => return result; } -export function useMutateWithToken(fetch: (token: string, ...props: any[]) => Promise) { - const user = useGetUser(); +export function useMutateWithToken(fetch: (token: string, ...props: any[]) => Promise, originUser?: any) { + const user = originUser !== undefined ? originUser : useGetUser(); return (...props: any[]) => fetch(user?.token || '', ...props); } diff --git a/src/mocks/participant.ts b/src/mocks/participant.ts index 6f443fe..71568d0 100644 --- a/src/mocks/participant.ts +++ b/src/mocks/participant.ts @@ -31,6 +31,22 @@ const participantData: ParticipantType[] = [ { id: 28, name: '황철수', status: '스터디원', profileImg: 'profile6.png', myPageUrl: '/' }, { id: 29, name: '진철수', status: '스터디원', profileImg: 'profile5.png', myPageUrl: '/' }, { id: 30, name: '황철수', status: '스터디장', profileImg: 'profile6.png', myPageUrl: '/' }, + { id: 31, name: '김철수', status: '', profileImg: 'profile1.png', myPageUrl: '/' }, + { id: 32, name: '윤철수', status: '', profileImg: 'profile2.png', myPageUrl: '/' }, + { id: 33, name: '박철수', status: '', profileImg: 'profile3.png', myPageUrl: '/' }, + { id: 34, name: '이철수', status: '', profileImg: 'profile4.png', myPageUrl: '/' }, + { id: 35, name: '진철수', status: '', profileImg: 'profile5.png', myPageUrl: '/' }, + { id: 36, name: '황철수', status: '', profileImg: 'profile6.png', myPageUrl: '/' }, + { id: 37, name: '진철수', status: '', profileImg: 'profile5.png', myPageUrl: '/' }, + { id: 38, name: '황철수', status: '', profileImg: 'profile6.png', myPageUrl: '/' }, + { id: 39, name: '진철수', status: '', profileImg: 'profile5.png', myPageUrl: '/' }, + { id: 40, name: '황철수', status: '', profileImg: 'profile6.png', myPageUrl: '/' }, + { id: 41, name: '진철수', status: '', profileImg: 'profile5.png', myPageUrl: '/' }, + { id: 42, name: '황철수', status: '', profileImg: 'profile6.png', myPageUrl: '/' }, + { id: 43, name: '진철수', status: '', profileImg: 'profile5.png', myPageUrl: '/' }, + { id: 44, name: '황철수', status: '', profileImg: 'profile6.png', myPageUrl: '/' }, + { id: 45, name: '진철수', status: '', profileImg: 'profile5.png', myPageUrl: '/' }, + { id: 46, name: '황철수', status: '', profileImg: 'profile6.png', myPageUrl: '/' }, ]; export default participantData; diff --git a/src/mocks/studyMember.ts b/src/mocks/studyMember.ts new file mode 100644 index 0000000..b7d1479 --- /dev/null +++ b/src/mocks/studyMember.ts @@ -0,0 +1,25 @@ +/* eslint-disable import/prefer-default-export */ +import { Member } from '@/types'; + +export const studyMember: Member[] = [ + { id: 21, name: '김철수21', imageUrl: '' }, + { id: 22, name: '김철수22', imageUrl: '' }, + { id: 23, name: '김철수23', imageUrl: '' }, + { id: 24, name: '김철수24', imageUrl: '' }, + { id: 25, name: '김철수25', imageUrl: '' }, + { id: 26, name: '김철수26', imageUrl: '' }, + { id: 27, name: '김철수27', imageUrl: '' }, + { id: 28, name: '김철수28', imageUrl: '' }, + { id: 29, name: '김철수29', imageUrl: '' }, + { id: 10, name: '김철수10', imageUrl: '' }, + { id: 11, name: '김철수11', imageUrl: '' }, + { id: 12, name: '김철수12', imageUrl: '' }, + { id: 13, name: '김철수13', imageUrl: '' }, + { id: 14, name: '김철수14', imageUrl: '' }, + { id: 15, name: '김철수15', imageUrl: '' }, + { id: 16, name: '김철수16', imageUrl: '' }, + { id: 17, name: '김철수17', imageUrl: '' }, + { id: 18, name: '김철수18', imageUrl: '' }, + { id: 19, name: '김철수19', imageUrl: '' }, + { id: 20, name: '김철수20', imageUrl: '' }, +]; diff --git a/src/types.ts b/src/types.ts index 8fe32e8..241a97c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -20,6 +20,13 @@ export interface CurriculumItemDto { isDeleted: boolean; } +interface TeamReference { + readonly id: number; + name: string; + description: string; + imageUrl: string; +} + export interface Study { readonly id: number; name: string; @@ -29,6 +36,8 @@ export interface Study { cropId: number; status: string; studyProgressRatio: number; + studyLeaderId: number; + teamReference: TeamReference; } export interface Team { @@ -42,6 +51,12 @@ export interface TeamDetail extends Team { attendanceRatio: number; } +export interface StudyMember { + readonly memberId: number; + name: string; + imageUrl: string; +} + export interface Member { readonly id: number; name: string;