Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/#271 스터디원api #291

Merged
merged 18 commits into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions src/app/api/member.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable import/prefer-default-export */
import { fetcher } from '@/app/api/fetcher';

const memberFetcher = fetcher();
Expand All @@ -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 };
22 changes: 18 additions & 4 deletions src/app/api/study.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}`);
Expand Down
12 changes: 11 additions & 1 deletion src/app/team/[teamId]/study/[studyId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,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 documentCardData from '@/mocks/documentCard';
import participantData from '@/mocks/participant';
import { Study } from '@/types';
Expand Down Expand Up @@ -84,7 +85,16 @@ const Page = ({ params }: { params: { studyId: number } }) => {
</Flex>
<Flex direction="column" rowGap={{ base: '6', '2xl': '12' }}>
<Feed />
<Participant participantInfos={participantData} />
<Flex align="right" direction="column" rowGap="3">
{studyData && (
<StudyParticipantMenu
studyId={params.studyId}
teamId={studyData?.teamReference.id}
leaderId={studyData?.studyLeaderId}
/>
)}
<Participant participantInfos={participantData} />
</Flex>
</Flex>
</Grid>
</Flex>
Expand Down
2 changes: 1 addition & 1 deletion src/components/StudyCard/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Study } from '@/types';

export interface StudyCardProps extends Study {
export interface StudyCardProps extends Omit<Study, 'studyLeaderId' | 'teamReference'> {
rank: number;
}
79 changes: 79 additions & 0 deletions src/containers/study/StudyParticipantMenu/index.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<ParticipantMenu
gap="3"
w="fit-content"
ml="auto"
isOpen={isOpen}
setIsOpen={setIsOpen}
leader={leader}
includeMembers={includeMembers}
excludeMembers={excludeMembers}
onAdd={handleAddMember}
onRemove={handleDeleteMember}
onMandateLeader={handleMandateLeader}
>
<IconButton
fontSize="16px"
transform={isOpen ? 'rotate(90deg)' : 'rotate(0deg)'}
transition="all 0.2s"
aria-label=""
icon={<MdOutlineArrowForwardIos />}
isRound
size="icon_sm"
variant="icon_orange"
/>
<Text>관리</Text>
</ParticipantMenu>
);
};

export default StudyParticipantMenu;
5 changes: 5 additions & 0 deletions src/containers/study/StudyParticipantMenu/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface StudyParticipantMenuProps {
studyId: number;
teamId: number;
leaderId: number;
}
4 changes: 2 additions & 2 deletions src/hooks/useFetchWithToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ export function useGetFetchWithToken(fetch: (token: string, ...props: any[]) =>
return result;
}

export function useMutateWithToken(fetch: (token: string, ...props: any[]) => Promise<FetchResult>) {
const user = useGetUser();
export function useMutateWithToken(fetch: (token: string, ...props: any[]) => Promise<FetchResult>, originUser?: any) {
const user = originUser !== undefined ? originUser : useGetUser();

return (...props: any[]) => fetch(user?.token || '', ...props);
}
16 changes: 16 additions & 0 deletions src/mocks/participant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
25 changes: 25 additions & 0 deletions src/mocks/studyMember.ts
Original file line number Diff line number Diff line change
@@ -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: '' },
];
15 changes: 15 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -29,6 +36,8 @@ export interface Study {
cropId: number;
status: string;
studyProgressRatio: number;
studyLeaderId: number;
teamReference: TeamReference;
}

export interface Team {
Expand All @@ -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;
Expand Down