-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: 보호소의 봉사자 프로필 페이지 api를 포함한 로직 추가 (#216)
* feat(shelter): volunteers api 관련 type 추가 * feat(shelter): volunteers api에 봉사자 프로필 관련 추가 * feat(shelter): volunteers에 봉사자 프로필 관련 handlers 추가 * feat(shelter): volunteers handler를 browser에 추가 * feat(shelter): VolunteersRecords를 VolunteerRecruitments로 변경 및 api를 포함한 로직 추가 * feat(shelter): VolunteerReviews에 api를 포함한 로직 추가 * feat(shelter): VolunteerProfilePage에 api를 포함한 로직 추가
- Loading branch information
Showing
8 changed files
with
300 additions
and
111 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import axiosInstance from 'shared/apis/axiosInstance'; | ||
|
||
import { | ||
VolunteerCompletedsRequestParams, | ||
VolunteerProfileResponseData, | ||
VolunteerReviewsOnVolunteerResponseData, | ||
VoluteerRecruitmentsOnVolunteerResponseData, | ||
} from '@/types/apis/volunteers'; | ||
|
||
export const getVolunteerProfile = (volunteerId: number) => | ||
axiosInstance.get<VolunteerProfileResponseData>( | ||
`/shelters/volunteers/${volunteerId}/profile`, | ||
); | ||
|
||
export const getVolunteerReviewsOnVolunteer = ( | ||
volunteerId: number, | ||
params: VolunteerCompletedsRequestParams, | ||
) => | ||
axiosInstance.get<VolunteerReviewsOnVolunteerResponseData>( | ||
`/shelters/volunteers/${volunteerId}/reviews`, | ||
{ params }, | ||
); | ||
|
||
export const getVolunteerRecruitmentsOnVolunteer = ( | ||
volunteerId: number, | ||
params: VolunteerCompletedsRequestParams, | ||
) => | ||
axiosInstance.get<VoluteerRecruitmentsOnVolunteerResponseData>( | ||
`/shelters/volunteers/${volunteerId}/recruitments/completed`, | ||
{ params }, | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import { delay, http, HttpResponse } from 'msw'; | ||
|
||
const DUMMY_REVIEWS_DATA = { | ||
reviewId: 36, | ||
shelterName: '남양주 보호소', | ||
reviewCreatedAt: '2023-03-16T18:00', | ||
reviewContent: '아이들이 너무 귀여워서 봉사하는 시간이 즐거웠습니다~!', | ||
reviewImageUrls: [ | ||
'https://source.unsplash.com/random', | ||
'https://source.unsplash.com/random', | ||
'https://source.unsplash.com/random', | ||
], | ||
}; | ||
|
||
const DUMMY_RECRUITMENTS = { | ||
recruitmentId: 1, | ||
recruitmentTitle: '봉사자를 모집합니다', | ||
recruitmentStartTime: '2023-03-16T18:00:00', | ||
shelterName: '마석 보호소', | ||
}; | ||
|
||
export const handlers = [ | ||
http.get('/shelters/volunteers/:volunteerId/profile', async () => { | ||
await delay(200); | ||
return HttpResponse.json( | ||
{ | ||
volunteerEmail: '[email protected]', | ||
volunteerName: '홍길동', | ||
volunteerTemperature: 36, | ||
volunteerImageUrl: 'https://source.unsplash.com/random', | ||
volunteerPhoneNumber: '010-8237-1847', | ||
}, | ||
{ status: 200 }, | ||
); | ||
}), | ||
http.get('/shelters/volunteers/:volunteerId/reviews', async () => { | ||
await delay(2000); | ||
return HttpResponse.json( | ||
{ | ||
pageInfo: { | ||
totalElements: 20, | ||
hasNext: true, | ||
}, | ||
reviews: Array.from({ length: 10 }, () => DUMMY_REVIEWS_DATA), | ||
}, | ||
{ status: 200 }, | ||
); | ||
}), | ||
http.get( | ||
'/shelters/volunteers/:volunteerId/recruitments/completed', | ||
async () => { | ||
await delay(2000); | ||
return HttpResponse.json({ | ||
pageInfo: { | ||
totalElements: 20, | ||
hasNext: true, | ||
}, | ||
recruitments: Array.from({ length: 10 }, () => DUMMY_RECRUITMENTS), | ||
}); | ||
}, | ||
), | ||
]; |
58 changes: 0 additions & 58 deletions
58
apps/shelter/src/pages/volunteers/profile/_components/VolunteerRecords.tsx
This file was deleted.
Oops, something went wrong.
81 changes: 81 additions & 0 deletions
81
apps/shelter/src/pages/volunteers/profile/_components/VolunteerRecruitments.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import { Box, Card, CardBody, Heading, Text } from '@chakra-ui/react'; | ||
import { useSuspenseInfiniteQuery } from '@tanstack/react-query'; | ||
import useIntersect from 'shared/hooks/useIntersection'; | ||
import { createFormattedTime } from 'shared/utils/date'; | ||
|
||
import { getVolunteerRecruitmentsOnVolunteer } from '@/apis/volunteers'; | ||
|
||
type VolunteerRecruitmentItemProps = { | ||
recruitmentTitle: string; | ||
recruitmentStartTime: string; | ||
shelterName: string; | ||
}; | ||
|
||
type VolunteerRecruitmentsProps = { | ||
id: number; | ||
}; | ||
|
||
export default function VolunteerRecruitments({ | ||
id, | ||
}: VolunteerRecruitmentsProps) { | ||
const { | ||
data: { pages }, | ||
hasNextPage, | ||
isFetchingNextPage, | ||
fetchNextPage, | ||
} = useSuspenseInfiniteQuery({ | ||
queryKey: ['volunteer', 'profile', 'recruitments', id], | ||
queryFn: ({ pageParam }) => | ||
getVolunteerRecruitmentsOnVolunteer(id, { page: pageParam, size: 10 }), | ||
initialPageParam: 0, | ||
getNextPageParam: ({ data: { pageInfo } }, _, lastPageParam) => | ||
pageInfo.hasNext ? lastPageParam + 1 : null, | ||
}); | ||
|
||
const totalRecruitments = pages[0].data.pageInfo.totalElements; | ||
const recruitments = pages.flatMap( | ||
({ data: { recruitments } }) => recruitments, | ||
); | ||
|
||
const ref = useIntersect((entry, observer) => { | ||
observer.unobserve(entry.target); | ||
|
||
if (hasNextPage && !isFetchingNextPage) { | ||
fetchNextPage(); | ||
} | ||
}); | ||
|
||
return ( | ||
<Box> | ||
<Heading fontWeight={600} fontSize="md" py={4}> | ||
봉사 이력 {totalRecruitments}개 | ||
</Heading> | ||
{recruitments.map((recruitment, index) => ( | ||
<VolunteerRecruitmentItem key={index} {...recruitment} /> | ||
))} | ||
<Box ref={ref} /> | ||
</Box> | ||
); | ||
} | ||
|
||
function VolunteerRecruitmentItem({ | ||
shelterName, | ||
recruitmentTitle, | ||
recruitmentStartTime, | ||
}: VolunteerRecruitmentItemProps) { | ||
return ( | ||
<Card p={4} pb={3.5} mb={2}> | ||
<CardBody pos="relative" p={0}> | ||
<Text pb={2} fontWeight={600}> | ||
{recruitmentTitle} | ||
</Text> | ||
<Text fontSize="sm" color="gray.400"> | ||
{shelterName} | ||
</Text> | ||
<Text fontSize="sm" color="black"> | ||
{`봉사일 | ${createFormattedTime(new Date(recruitmentStartTime))}`} | ||
</Text> | ||
</CardBody> | ||
</Card> | ||
); | ||
} |
89 changes: 54 additions & 35 deletions
89
apps/shelter/src/pages/volunteers/profile/_components/VolunteerReviews.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,53 +1,72 @@ | ||
import { Box, Heading, HStack, Text, VStack } from '@chakra-ui/react'; | ||
import { useSuspenseInfiniteQuery } from '@tanstack/react-query'; | ||
import InfoSubtext from 'shared/components/InfoSubtext'; | ||
import ReviewItem from 'shared/components/ReviewItem'; | ||
import useIntersect from 'shared/hooks/useIntersection'; | ||
import { createFormattedTime } from 'shared/utils/date'; | ||
|
||
const DUMMY_REVIEWS_DATA = { | ||
reviewId: 36, | ||
shelterName: '남양주 보호소', | ||
reviewCreatedAt: '2023-03-16T18:00', | ||
reviewContent: '아이들이 너무 귀여워서 봉사하는 시간이 즐거웠습니다~!', | ||
images: [ | ||
'https://source.unsplash.com/random', | ||
'https://source.unsplash.com/random', | ||
'https://source.unsplash.com/random', | ||
], | ||
}; | ||
import { getVolunteerReviewsOnVolunteer } from '@/apis/volunteers'; | ||
|
||
const DUMMY_DATA = { | ||
pageInfo: { | ||
totalElements: 32, | ||
hasNext: false, | ||
}, | ||
reviews: Array.from({ length: 5 }, () => DUMMY_REVIEWS_DATA), | ||
type VolunteerReviewsProps = { | ||
id: number; | ||
}; | ||
|
||
export default function VolunteerReviews() { | ||
export default function VolunteerReviews({ id }: VolunteerReviewsProps) { | ||
const { | ||
data: { pages }, | ||
hasNextPage, | ||
isFetchingNextPage, | ||
fetchNextPage, | ||
} = useSuspenseInfiniteQuery({ | ||
queryKey: ['volunteer', 'profile', 'reviews', id], | ||
queryFn: ({ pageParam }) => | ||
getVolunteerReviewsOnVolunteer(id, { page: pageParam, size: 10 }), | ||
initialPageParam: 0, | ||
getNextPageParam: ({ data: { pageInfo } }, _, lastPageParam) => | ||
pageInfo.hasNext ? lastPageParam + 1 : null, | ||
}); | ||
|
||
const totalReviews = pages[0].data.pageInfo.totalElements; | ||
const reviews = pages.flatMap(({ data: { reviews } }) => reviews); | ||
|
||
const ref = useIntersect((entry, observer) => { | ||
observer.unobserve(entry.target); | ||
|
||
if (hasNextPage && !isFetchingNextPage) { | ||
fetchNextPage(); | ||
} | ||
}); | ||
|
||
return ( | ||
<Box> | ||
<Heading fontSize="md" py={4}> | ||
봉사 후기 {DUMMY_DATA.reviews.length}개 | ||
봉사 후기 {totalReviews}개 | ||
</Heading> | ||
<VStack spacing={2}> | ||
{DUMMY_DATA.reviews.map( | ||
({ shelterName, reviewContent, reviewCreatedAt, images }, index) => { | ||
return ( | ||
<ReviewItem key={index} content={reviewContent} images={images}> | ||
<Box> | ||
<HStack mb={1}> | ||
<Text fontWeight={600}>{shelterName}</Text> | ||
</HStack> | ||
<InfoSubtext | ||
title="작성일" | ||
content={createFormattedTime(new Date(reviewCreatedAt))} | ||
/> | ||
</Box> | ||
</ReviewItem> | ||
); | ||
}, | ||
{reviews.map( | ||
( | ||
{ shelterName, reviewContent, reviewCreatedAt, reviewImageUrls }, | ||
index, | ||
) => ( | ||
<ReviewItem | ||
key={index} | ||
content={reviewContent} | ||
images={reviewImageUrls} | ||
> | ||
<Box> | ||
<HStack mb={1}> | ||
<Text fontWeight={600}>{shelterName}</Text> | ||
</HStack> | ||
<InfoSubtext | ||
title="작성일" | ||
content={createFormattedTime(new Date(reviewCreatedAt))} | ||
/> | ||
</Box> | ||
</ReviewItem> | ||
), | ||
)} | ||
</VStack> | ||
<Box ref={ref} /> | ||
</Box> | ||
); | ||
} |
Oops, something went wrong.