From 66ee11632aa6b64aadbe4329684e6b53dc61236a Mon Sep 17 00:00:00 2001 From: gimdogyun Date: Sun, 30 Jun 2024 03:01:43 +0900 Subject: [PATCH 01/10] feat: study member api with token - #271 --- src/app/api/study.ts | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/app/api/study.ts b/src/app/api/study.ts index 29e1fe00..1e544fa7 100644 --- a/src/app/api/study.ts +++ b/src/app/api/study.ts @@ -30,22 +30,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}`, + }, + }); // eslint-disable-next-line @typescript-eslint/no-unused-vars const getCurriculum = (studyId: number): { curriculumItems: Curriculum[] } => { From 4618b6d765dced727121e68f96f89474253e81ce Mon Sep 17 00:00:00 2001 From: gimdogyun Date: Fri, 5 Jul 2024 10:09:51 +0900 Subject: [PATCH 02/10] feat : patch study mandate - #271 --- src/app/api/member.ts | 11 +++++++++-- src/hooks/useFetchWithToken.ts | 4 ++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/app/api/member.ts b/src/app/api/member.ts index d7c296fd..e20e4e67 100644 --- a/src/app/api/member.ts +++ b/src/app/api/member.ts @@ -1,4 +1,3 @@ -/* eslint-disable import/prefer-default-export */ import { fetcher } from '@/app/api/fetcher'; const memberFetcher = fetcher(); @@ -10,4 +9,12 @@ const getSidebarInfo = (token: string, memberId: number) => }, }); -export { getSidebarInfo }; +const patchStudyMandate = (token: string, studyId: number, newStudyLeaderId: number) => + memberFetcher(`/study/${studyId}/mandate/${newStudyLeaderId}`, { + method: 'PATCH', + headers: { + Authorization: `Bearer ${token}`, + }, + }); + +export { getSidebarInfo, patchStudyMandate }; diff --git a/src/hooks/useFetchWithToken.ts b/src/hooks/useFetchWithToken.ts index 1024e07c..3278fd07 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); } From c66630888228fc27d53fb69d64c0b1ce44f1bda0 Mon Sep 17 00:00:00 2001 From: gimdogyun Date: Fri, 5 Jul 2024 10:10:43 +0900 Subject: [PATCH 03/10] =?UTF-8?q?feat=20:=20=EC=8A=A4=ED=84=B0=EB=94=94?= =?UTF-8?q?=EC=9B=90=20=EA=B4=80=EB=A6=AC=20Menu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - #271 --- .../team/[teamId]/study/[studyId]/page.tsx | 6 +- .../ParticipantMenu/ParticipantItem/index.tsx | 93 ++++++++++++++++++ .../study/ParticipantMenu/index.tsx | 96 +++++++++++++++++++ src/containers/study/ParticipantMenu/type.ts | 11 +++ src/mocks/participant.ts | 16 ++++ 5 files changed, 221 insertions(+), 1 deletion(-) create mode 100644 src/containers/study/ParticipantMenu/ParticipantItem/index.tsx create mode 100644 src/containers/study/ParticipantMenu/index.tsx create mode 100644 src/containers/study/ParticipantMenu/type.ts diff --git a/src/app/team/[teamId]/study/[studyId]/page.tsx b/src/app/team/[teamId]/study/[studyId]/page.tsx index 529ee747..23a4eb32 100644 --- a/src/app/team/[teamId]/study/[studyId]/page.tsx +++ b/src/app/team/[teamId]/study/[studyId]/page.tsx @@ -13,6 +13,7 @@ import DeleteStudyModal from '@/containers/study/Modal/DeleteStudyModal'; import StudyModal from '@/containers/study/Modal/StudyModal'; import TerminateStudyModal from '@/containers/study/Modal/TerminateStudyModal'; import Participant from '@/containers/study/Participant'; +import ParticipantMenu from '@/containers/study/ParticipantMenu'; import StudyControlPanel from '@/containers/study/StudyControlPanel'; import StudyInfoCard from '@/containers/study/StudyInfoCard'; import documentCardData from '@/mocks/documentCard'; @@ -74,7 +75,10 @@ const Page = ({ params }: { params: { studyId: number } }) => { - + + {}} /> + + diff --git a/src/containers/study/ParticipantMenu/ParticipantItem/index.tsx b/src/containers/study/ParticipantMenu/ParticipantItem/index.tsx new file mode 100644 index 00000000..dc1dc203 --- /dev/null +++ b/src/containers/study/ParticipantMenu/ParticipantItem/index.tsx @@ -0,0 +1,93 @@ +import { Avatar, Flex, IconButton, Text } from '@chakra-ui/react'; +import { RiAddLine, RiCloseFill, RiVipCrownLine } from 'react-icons/ri'; + +import { patchStudyMandate } from '@/app/api/member'; +import { deleteStudyMember, postStudyMember } from '@/app/api/study'; +import { ParticipantItemProps } from '@/containers/study/ParticipantMenu/type'; +import { useMutateWithToken } from '@/hooks/useFetchWithToken'; +import useGetUser from '@/hooks/useGetUser'; +import colors from '@/theme/foundations/colors'; + +const ParticipantItem = ({ member, studyId }: ParticipantItemProps) => { + const user = useGetUser(); + + const isLeader = member.status === '스터디장'; + const isStudyMember = member.status === '스터디원'; + const isNonStudyMember = member.status === ''; + + const addMember = useMutateWithToken(postStudyMember, user); + const deleteMember = useMutateWithToken(deleteStudyMember, user); + const mandateLeader = useMutateWithToken(patchStudyMandate, user); + + const handleAddMember = () => { + addMember(studyId, member.id); + }; + + const handleDeleteMember = () => { + deleteMember(studyId, member.id); + }; + + const handleMandateLeader = () => { + mandateLeader(studyId, member.id); + }; + + return ( + + + + {member.name} + + {isStudyMember && ( + + } + onClick={handleDeleteMember} + size="icon_sm" + /> + } + onClick={handleMandateLeader} + size="icon_sm" + /> + + )} + {isNonStudyMember && ( + + } + onClick={handleAddMember} + size="icon_sm" + /> + + )} + + ); +}; + +export default ParticipantItem; diff --git a/src/containers/study/ParticipantMenu/index.tsx b/src/containers/study/ParticipantMenu/index.tsx new file mode 100644 index 00000000..9eb86c5d --- /dev/null +++ b/src/containers/study/ParticipantMenu/index.tsx @@ -0,0 +1,96 @@ +'use client'; + +import { Divider, Flex, IconButton, Input, Text } from '@chakra-ui/react'; +import { useEffect, useRef, useState } from 'react'; +import { BiSearch } from 'react-icons/bi'; +import { MdOutlineArrowForwardIos } from 'react-icons/md'; + +import { getStudyMembers } from '@/app/api/study'; +import { getTeamMembers } from '@/app/api/team'; +import ParticipantItem from '@/containers/study/ParticipantMenu/ParticipantItem'; +import { ParticipantMenuProps } from '@/containers/study/ParticipantMenu/type'; +import { useGetFetchWithToken } from '@/hooks/useFetchWithToken'; +import useGetUser from '@/hooks/useGetUser'; +import participantData from '@/mocks/participant'; + +const ParticipantMenu = ({ studyId, teamId }: ParticipantMenuProps) => { + const user = useGetUser(); + const studyMembersa = useGetFetchWithToken(getStudyMembers, [studyId], user); + const teamMembersa = useGetFetchWithToken(getTeamMembers, [teamId], user); + + const members = participantData; + const leader = members.find((member) => member.status === '스터디장')!; + const studyMembers = members.filter((member) => member.status !== '스터디장' && member.status !== ''); + const nonStudyMembers = members.filter((member) => member.status === ''); + + const [isOpen, setIsOpen] = useState(false); + const menuRef = useRef(null); + + const onClose = () => { + setIsOpen(false); + }; + + useEffect(() => { + const handleOutsideClose = (e: MouseEvent) => { + if (isOpen && !menuRef.current?.contains(e.target as Node)) setIsOpen(false); + }; + document.addEventListener('click', handleOutsideClose); + + return () => document.removeEventListener('click', handleOutsideClose); + }, [isOpen]); + + return ( + + { + setIsOpen((prev) => !prev); + }} + > + } + isRound + onClick={onClose} + size="icon_sm" + variant="icon_orange" + /> + 관리 + + + ); +}; + +export default ParticipantMenu; diff --git a/src/containers/study/ParticipantMenu/type.ts b/src/containers/study/ParticipantMenu/type.ts new file mode 100644 index 00000000..7251546a --- /dev/null +++ b/src/containers/study/ParticipantMenu/type.ts @@ -0,0 +1,11 @@ +import { ParticipantType } from '@/types'; + +export interface ParticipantMenuProps { + teamId: number; + studyId: number; +} + +export interface ParticipantItemProps { + studyId: number; + member: ParticipantType; +} diff --git a/src/mocks/participant.ts b/src/mocks/participant.ts index 6f443fe3..71568d0c 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; From e47440d855391283a9a3a5b3b0fd63fdc8784042 Mon Sep 17 00:00:00 2001 From: gimdogyun Date: Thu, 11 Jul 2024 19:26:10 +0900 Subject: [PATCH 04/10] feat : search by name --- .../team/[teamId]/study/[studyId]/page.tsx | 2 +- .../ParticipantMenu/ParticipantItem/index.tsx | 8 ++-- .../study/ParticipantMenu/index.tsx | 48 +++++++++++++------ src/containers/study/ParticipantMenu/type.ts | 6 ++- src/mocks/studyMember.ts | 25 ++++++++++ 5 files changed, 67 insertions(+), 22 deletions(-) create mode 100644 src/mocks/studyMember.ts diff --git a/src/app/team/[teamId]/study/[studyId]/page.tsx b/src/app/team/[teamId]/study/[studyId]/page.tsx index 23a4eb32..31755dc5 100644 --- a/src/app/team/[teamId]/study/[studyId]/page.tsx +++ b/src/app/team/[teamId]/study/[studyId]/page.tsx @@ -76,7 +76,7 @@ const Page = ({ params }: { params: { studyId: number } }) => { - {}} /> + diff --git a/src/containers/study/ParticipantMenu/ParticipantItem/index.tsx b/src/containers/study/ParticipantMenu/ParticipantItem/index.tsx index dc1dc203..f29f363d 100644 --- a/src/containers/study/ParticipantMenu/ParticipantItem/index.tsx +++ b/src/containers/study/ParticipantMenu/ParticipantItem/index.tsx @@ -8,12 +8,12 @@ import { useMutateWithToken } from '@/hooks/useFetchWithToken'; import useGetUser from '@/hooks/useGetUser'; import colors from '@/theme/foundations/colors'; -const ParticipantItem = ({ member, studyId }: ParticipantItemProps) => { +const ParticipantItem = ({ member, studyId, type }: ParticipantItemProps) => { const user = useGetUser(); - const isLeader = member.status === '스터디장'; - const isStudyMember = member.status === '스터디원'; - const isNonStudyMember = member.status === ''; + const isLeader = type === '스터디장'; + const isStudyMember = type === '스터디원'; + const isNonStudyMember = type === '팀원'; const addMember = useMutateWithToken(postStudyMember, user); const deleteMember = useMutateWithToken(deleteStudyMember, user); diff --git a/src/containers/study/ParticipantMenu/index.tsx b/src/containers/study/ParticipantMenu/index.tsx index 9eb86c5d..93c58f42 100644 --- a/src/containers/study/ParticipantMenu/index.tsx +++ b/src/containers/study/ParticipantMenu/index.tsx @@ -11,17 +11,29 @@ import ParticipantItem from '@/containers/study/ParticipantMenu/ParticipantItem' import { ParticipantMenuProps } from '@/containers/study/ParticipantMenu/type'; import { useGetFetchWithToken } from '@/hooks/useFetchWithToken'; import useGetUser from '@/hooks/useGetUser'; -import participantData from '@/mocks/participant'; +import { studyMember } from '@/mocks/studyMember'; +import { teamMember } from '@/mocks/teamMember'; +import { Member } from '@/types'; -const ParticipantMenu = ({ studyId, teamId }: ParticipantMenuProps) => { +const ParticipantMenu = ({ studyId, teamId, leaderId }: ParticipantMenuProps) => { const user = useGetUser(); - const studyMembersa = useGetFetchWithToken(getStudyMembers, [studyId], user); - const teamMembersa = useGetFetchWithToken(getTeamMembers, [teamId], user); + const [search, setSearch] = useState(''); + // const studyMembersData = useGetFetchWithToken(getStudyMembers, [studyId], user); + // const teamMembersData = useGetFetchWithToken(getTeamMembers, [teamId], user); + const studyMembersData = { + data: studyMember, + }; + + const teamMembersData = { + data: teamMember, + }; - const members = participantData; - const leader = members.find((member) => member.status === '스터디장')!; - const studyMembers = members.filter((member) => member.status !== '스터디장' && member.status !== ''); - const nonStudyMembers = members.filter((member) => member.status === ''); + const studyMembers = studyMembersData?.data?.filter((member: Member) => !search || member.name.includes(search)); + const leader = studyMembers?.find((member: Member) => member.id === leaderId); + const nonLeaderStudyMembers = studyMembers?.filter((member: Member) => member.id !== leaderId); + const nonStudyMembers = teamMembersData?.data + ?.filter((member: Member) => !studyMembers?.find((m) => m.id === member.id)) + .filter((member: Member) => !search || member.name.includes(search)); const [isOpen, setIsOpen] = useState(false); const menuRef = useRef(null); @@ -74,17 +86,23 @@ const ParticipantMenu = ({ studyId, teamId }: ParticipantMenuProps) => { borderColor="#6c6c6c" borderRadius="full" > - + setSearch(e.target.value)} + value={search} + /> - - - {studyMembers.map((member) => ( - + + {leader && } + {nonLeaderStudyMembers.map((member: Member) => ( + ))} - {nonStudyMembers.map((member) => ( - + {nonStudyMembers.map((member: Member) => ( + ))} diff --git a/src/containers/study/ParticipantMenu/type.ts b/src/containers/study/ParticipantMenu/type.ts index 7251546a..c01d0738 100644 --- a/src/containers/study/ParticipantMenu/type.ts +++ b/src/containers/study/ParticipantMenu/type.ts @@ -1,11 +1,13 @@ -import { ParticipantType } from '@/types'; +import { Member } from '@/types'; export interface ParticipantMenuProps { teamId: number; studyId: number; + leaderId: number; } export interface ParticipantItemProps { studyId: number; - member: ParticipantType; + member: Member; + type: '스터디장' | '스터디원' | '팀원'; } diff --git a/src/mocks/studyMember.ts b/src/mocks/studyMember.ts new file mode 100644 index 00000000..b7d1479e --- /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: '' }, +]; From 51067a178260bbf0cbc08df0b61873c55f2659c1 Mon Sep 17 00:00:00 2001 From: gimdogyun Date: Mon, 15 Jul 2024 17:36:23 +0900 Subject: [PATCH 05/10] =?UTF-8?q?refactor=20:=20ParticipantMenu=20?= =?UTF-8?q?=EA=B3=B5=ED=86=B5=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - #271 --- .../team/[teamId]/study/[studyId]/page.tsx | 4 +- .../ParticipantMenu/ParticipantItem/index.tsx | 93 -------------- .../study/ParticipantMenu/index.tsx | 114 ------------------ src/containers/study/ParticipantMenu/type.ts | 13 -- .../study/StudyParticipantMenu/index.tsx | 73 +++++++++++ .../study/StudyParticipantMenu/types.ts | 5 + 6 files changed, 80 insertions(+), 222 deletions(-) delete mode 100644 src/containers/study/ParticipantMenu/ParticipantItem/index.tsx delete mode 100644 src/containers/study/ParticipantMenu/index.tsx delete mode 100644 src/containers/study/ParticipantMenu/type.ts create mode 100644 src/containers/study/StudyParticipantMenu/index.tsx create mode 100644 src/containers/study/StudyParticipantMenu/types.ts diff --git a/src/app/team/[teamId]/study/[studyId]/page.tsx b/src/app/team/[teamId]/study/[studyId]/page.tsx index c8eabade..47d4d179 100644 --- a/src/app/team/[teamId]/study/[studyId]/page.tsx +++ b/src/app/team/[teamId]/study/[studyId]/page.tsx @@ -14,9 +14,9 @@ import DeleteStudyModal from '@/containers/study/Modal/DeleteStudyModal'; import StudyModal from '@/containers/study/Modal/StudyModal'; import TerminateStudyModal from '@/containers/study/Modal/TerminateStudyModal'; import Participant from '@/containers/study/Participant'; -import ParticipantMenu from '@/containers/study/ParticipantMenu'; import StudyControlPanel from '@/containers/study/StudyControlPanel'; import StudyInfoCard from '@/containers/study/StudyInfoCard'; +import StudyParticipantMenu from '@/containers/study/StudyParticipantMenu'; import documentCardData from '@/mocks/documentCard'; import participantData from '@/mocks/participant'; import { Study } from '@/types'; @@ -86,7 +86,7 @@ const Page = ({ params }: { params: { studyId: number } }) => { - + diff --git a/src/containers/study/ParticipantMenu/ParticipantItem/index.tsx b/src/containers/study/ParticipantMenu/ParticipantItem/index.tsx deleted file mode 100644 index f29f363d..00000000 --- a/src/containers/study/ParticipantMenu/ParticipantItem/index.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import { Avatar, Flex, IconButton, Text } from '@chakra-ui/react'; -import { RiAddLine, RiCloseFill, RiVipCrownLine } from 'react-icons/ri'; - -import { patchStudyMandate } from '@/app/api/member'; -import { deleteStudyMember, postStudyMember } from '@/app/api/study'; -import { ParticipantItemProps } from '@/containers/study/ParticipantMenu/type'; -import { useMutateWithToken } from '@/hooks/useFetchWithToken'; -import useGetUser from '@/hooks/useGetUser'; -import colors from '@/theme/foundations/colors'; - -const ParticipantItem = ({ member, studyId, type }: ParticipantItemProps) => { - const user = useGetUser(); - - const isLeader = type === '스터디장'; - const isStudyMember = type === '스터디원'; - const isNonStudyMember = type === '팀원'; - - const addMember = useMutateWithToken(postStudyMember, user); - const deleteMember = useMutateWithToken(deleteStudyMember, user); - const mandateLeader = useMutateWithToken(patchStudyMandate, user); - - const handleAddMember = () => { - addMember(studyId, member.id); - }; - - const handleDeleteMember = () => { - deleteMember(studyId, member.id); - }; - - const handleMandateLeader = () => { - mandateLeader(studyId, member.id); - }; - - return ( - - - - {member.name} - - {isStudyMember && ( - - } - onClick={handleDeleteMember} - size="icon_sm" - /> - } - onClick={handleMandateLeader} - size="icon_sm" - /> - - )} - {isNonStudyMember && ( - - } - onClick={handleAddMember} - size="icon_sm" - /> - - )} - - ); -}; - -export default ParticipantItem; diff --git a/src/containers/study/ParticipantMenu/index.tsx b/src/containers/study/ParticipantMenu/index.tsx deleted file mode 100644 index 93c58f42..00000000 --- a/src/containers/study/ParticipantMenu/index.tsx +++ /dev/null @@ -1,114 +0,0 @@ -'use client'; - -import { Divider, Flex, IconButton, Input, Text } from '@chakra-ui/react'; -import { useEffect, useRef, useState } from 'react'; -import { BiSearch } from 'react-icons/bi'; -import { MdOutlineArrowForwardIos } from 'react-icons/md'; - -import { getStudyMembers } from '@/app/api/study'; -import { getTeamMembers } from '@/app/api/team'; -import ParticipantItem from '@/containers/study/ParticipantMenu/ParticipantItem'; -import { ParticipantMenuProps } from '@/containers/study/ParticipantMenu/type'; -import { useGetFetchWithToken } from '@/hooks/useFetchWithToken'; -import useGetUser from '@/hooks/useGetUser'; -import { studyMember } from '@/mocks/studyMember'; -import { teamMember } from '@/mocks/teamMember'; -import { Member } from '@/types'; - -const ParticipantMenu = ({ studyId, teamId, leaderId }: ParticipantMenuProps) => { - const user = useGetUser(); - const [search, setSearch] = useState(''); - // const studyMembersData = useGetFetchWithToken(getStudyMembers, [studyId], user); - // const teamMembersData = useGetFetchWithToken(getTeamMembers, [teamId], user); - const studyMembersData = { - data: studyMember, - }; - - const teamMembersData = { - data: teamMember, - }; - - const studyMembers = studyMembersData?.data?.filter((member: Member) => !search || member.name.includes(search)); - const leader = studyMembers?.find((member: Member) => member.id === leaderId); - const nonLeaderStudyMembers = studyMembers?.filter((member: Member) => member.id !== leaderId); - const nonStudyMembers = teamMembersData?.data - ?.filter((member: Member) => !studyMembers?.find((m) => m.id === member.id)) - .filter((member: Member) => !search || member.name.includes(search)); - - const [isOpen, setIsOpen] = useState(false); - const menuRef = useRef(null); - - const onClose = () => { - setIsOpen(false); - }; - - useEffect(() => { - const handleOutsideClose = (e: MouseEvent) => { - if (isOpen && !menuRef.current?.contains(e.target as Node)) setIsOpen(false); - }; - document.addEventListener('click', handleOutsideClose); - - return () => document.removeEventListener('click', handleOutsideClose); - }, [isOpen]); - - return ( - - { - setIsOpen((prev) => !prev); - }} - > - } - isRound - onClick={onClose} - size="icon_sm" - variant="icon_orange" - /> - 관리 - - - ); -}; - -export default ParticipantMenu; diff --git a/src/containers/study/ParticipantMenu/type.ts b/src/containers/study/ParticipantMenu/type.ts deleted file mode 100644 index c01d0738..00000000 --- a/src/containers/study/ParticipantMenu/type.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Member } from '@/types'; - -export interface ParticipantMenuProps { - teamId: number; - studyId: number; - leaderId: number; -} - -export interface ParticipantItemProps { - studyId: number; - member: Member; - type: '스터디장' | '스터디원' | '팀원'; -} diff --git a/src/containers/study/StudyParticipantMenu/index.tsx b/src/containers/study/StudyParticipantMenu/index.tsx new file mode 100644 index 00000000..6eb87935 --- /dev/null +++ b/src/containers/study/StudyParticipantMenu/index.tsx @@ -0,0 +1,73 @@ +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 } from '@/types'; + +const StudyParticipantMenu = ({ studyId, teamId, leaderId }: StudyParticipantMenuProps) => { + const user = useGetUser(); + const [isOpen, setIsOpen] = useState(false); + + const studyMembersData = useGetFetchWithToken(getStudyMembers, [studyId], user); + const teamMembersData = useGetFetchWithToken(getTeamMembers, [teamId], user); + + const studyMembers = studyMembersData?.data; + const leader = studyMembers?.find((member: Member) => member.id === leaderId); + const includeMembers = studyMembers?.filter((member: Member) => member.id !== leaderId); + const excludeMembers = teamMembersData?.data?.filter( + (member: Member) => !studyMembers?.find((m: Member) => m.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 00000000..13c4de7b --- /dev/null +++ b/src/containers/study/StudyParticipantMenu/types.ts @@ -0,0 +1,5 @@ +export interface StudyParticipantMenuProps { + studyId: number; + teamId: number; + leaderId: number; +} From bf2be00373e1c570d934ab7b994494d6f6005a1e Mon Sep 17 00:00:00 2001 From: gimdogyun Date: Tue, 16 Jul 2024 19:12:22 +0900 Subject: [PATCH 06/10] =?UTF-8?q?feat=20:=20=EC=8B=A4=EC=A0=9C=20=EC=8A=A4?= =?UTF-8?q?=ED=84=B0=EB=94=94=20=EC=A0=95=EB=B3=B4=EC=99=80=20=EC=97=B0?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - #271 --- src/app/team/[teamId]/study/[studyId]/page.tsx | 8 +++++++- src/containers/study/StudyParticipantMenu/index.tsx | 13 ++++++------- src/types.ts | 9 +++++++++ 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/app/team/[teamId]/study/[studyId]/page.tsx b/src/app/team/[teamId]/study/[studyId]/page.tsx index 47d4d179..b6605b39 100644 --- a/src/app/team/[teamId]/study/[studyId]/page.tsx +++ b/src/app/team/[teamId]/study/[studyId]/page.tsx @@ -86,7 +86,13 @@ const Page = ({ params }: { params: { studyId: number } }) => { - + {studyData && ( + + )} diff --git a/src/containers/study/StudyParticipantMenu/index.tsx b/src/containers/study/StudyParticipantMenu/index.tsx index 6eb87935..7814b44e 100644 --- a/src/containers/study/StudyParticipantMenu/index.tsx +++ b/src/containers/study/StudyParticipantMenu/index.tsx @@ -15,14 +15,13 @@ const StudyParticipantMenu = ({ studyId, teamId, leaderId }: StudyParticipantMen const user = useGetUser(); const [isOpen, setIsOpen] = useState(false); - const studyMembersData = useGetFetchWithToken(getStudyMembers, [studyId], user); - const teamMembersData = useGetFetchWithToken(getTeamMembers, [teamId], user); + const studyMembers = useGetFetchWithToken(getStudyMembers, [studyId], user); + const teamMembers = useGetFetchWithToken(getTeamMembers, [teamId], user); - const studyMembers = studyMembersData?.data; - const leader = studyMembers?.find((member: Member) => member.id === leaderId); - const includeMembers = studyMembers?.filter((member: Member) => member.id !== leaderId); - const excludeMembers = teamMembersData?.data?.filter( - (member: Member) => !studyMembers?.find((m: Member) => m.id === member.id), + const leader = studyMembers?.find((data: { member: Member }) => data.member.id === leaderId).member; + const includeMembers = studyMembers?.filter((data: { member: Member }) => data.member.id !== leaderId); + const excludeMembers = teamMembers?.filter( + (member: Member) => !studyMembers?.find((studyData: { member: Member }) => studyData.member.id === member.id), ); const addMember = useMutateWithToken(postStudyMember, user); diff --git a/src/types.ts b/src/types.ts index 85287a67..eecdd98d 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 { From ccef39ca50bbf58b82c96c8b2128642b841f7ff3 Mon Sep 17 00:00:00 2001 From: gimdogyun Date: Thu, 18 Jul 2024 17:17:20 +0900 Subject: [PATCH 07/10] fix : build error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - #271 - omit으로 불필요한 props 제거 --- src/components/StudyCard/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/StudyCard/types.ts b/src/components/StudyCard/types.ts index a8c6b981..99c00963 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; } From 8f8dda77543bdd385aba5ddb5db2452b1104b1ae Mon Sep 17 00:00:00 2001 From: gimdogyun Date: Wed, 24 Jul 2024 19:21:53 +0900 Subject: [PATCH 08/10] fix : StudyMemeber Type - #271 --- .../study/StudyParticipantMenu/index.tsx | 17 ++++++++++++----- src/types.ts | 6 ++++++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/containers/study/StudyParticipantMenu/index.tsx b/src/containers/study/StudyParticipantMenu/index.tsx index 7814b44e..46200323 100644 --- a/src/containers/study/StudyParticipantMenu/index.tsx +++ b/src/containers/study/StudyParticipantMenu/index.tsx @@ -9,19 +9,26 @@ 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 } from '@/types'; +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); + 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((data: { member: Member }) => data.member.id === leaderId).member; - const includeMembers = studyMembers?.filter((data: { member: Member }) => data.member.id !== leaderId); + 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: Member }) => studyData.member.id === member.id), + (member: Member) => !studyMembers?.find((studyData: Member) => studyData.id === member.id), ); const addMember = useMutateWithToken(postStudyMember, user); diff --git a/src/types.ts b/src/types.ts index 149d3272..7e415945 100644 --- a/src/types.ts +++ b/src/types.ts @@ -51,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; From 464eb42da3e091076c6911d73e62e542dd31f382 Mon Sep 17 00:00:00 2001 From: gimdogyun Date: Thu, 8 Aug 2024 18:02:41 +0900 Subject: [PATCH 09/10] fix : build error - #271 --- src/app/team/[teamId]/study/[studyId]/page.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app/team/[teamId]/study/[studyId]/page.tsx b/src/app/team/[teamId]/study/[studyId]/page.tsx index e002a8ff..f20d99cb 100644 --- a/src/app/team/[teamId]/study/[studyId]/page.tsx +++ b/src/app/team/[teamId]/study/[studyId]/page.tsx @@ -18,7 +18,6 @@ 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 documentCardData from '@/mocks/documentCard'; import participantData from '@/mocks/participant'; import { DocumentList, Study } from '@/types'; From 6412d57abbd0adb9d3cfb0ff999d69d2480dda2c Mon Sep 17 00:00:00 2001 From: gimdogyun Date: Thu, 29 Aug 2024 17:26:49 +0900 Subject: [PATCH 10/10] fix : import order error - #271 --- src/app/api/member.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/api/member.ts b/src/app/api/member.ts index fb2ee8c3..bc2cf6fb 100644 --- a/src/app/api/member.ts +++ b/src/app/api/member.ts @@ -1,5 +1,6 @@ /* eslint-disable import/prefer-default-export */ import { useQuery } from '@tanstack/react-query'; + import { fetcher } from '@/app/api/fetcher'; import useGetUser from '@/hooks/useGetUser';