Skip to content

Commit

Permalink
feat: 봉사모집 상세페이지 보호소프로필 이동, 봉사 신청 msw 추가 (#245)
Browse files Browse the repository at this point in the history
* refactor(volunteer): 봉사모집상세조회, 보호소 간단정보조회 fetch hook 분리

* refactor(volunteer): 봉사모집상세조회 데이터 받아오기

* feat(volunteer): 봉사신청 mock api 추가

* fix(volunteer): 봉사신청 api 수정

* feat(volunteer): 봉사신청 msw 연결

* fix(volunteer): 채팅하기 버튼 제거

* feat(volunteer): 보호소 정보 클릭하면 보호소 프로필 페이지로 이동

* feat(volunteer): 봉사신청 자동마감 된 경우 추가
  • Loading branch information
Eosdia authored Nov 30, 2023
1 parent 816d9ec commit df3b1b8
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 146 deletions.
4 changes: 2 additions & 2 deletions apps/volunteer/src/apis/recruitment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import {
RecruitmentsRequest,
} from '@/types/apis/recruitment';

export const ApplyRecruitments = (recruitmentId: string) =>
axiosInstance.post(`/recruitments/${recruitmentId}/apply`);
export const applyRecruitments = (recruitmentId: number) =>
axiosInstance.post(`/volunteers/recruitments/${recruitmentId}/apply`);

export const getRecruitments = (request: Partial<RecruitmentsRequest>) =>
axiosInstance.get<RecruitementsResponse, RecruitmentsRequest>(
Expand Down
5 changes: 5 additions & 0 deletions apps/volunteer/src/mocks/handlers/recruitment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,9 @@ export const handlers = [
{ status: 200 },
);
}),
http.post('/volunteers/recruitments/:recruitmentId/apply', async () => {
return new HttpResponse(null, {
status: 204,
});
}),
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { useSuspenseQuery } from '@tanstack/react-query';
import { getRecruitmentDetail } from 'shared/apis/common/Recruitments';

const useFetchRecruitmentDetail = (recruitmentId: number) =>
useSuspenseQuery({
queryKey: ['recruitment', recruitmentId],
queryFn: async () => (await getRecruitmentDetail(recruitmentId)).data,
});
export default useFetchRecruitmentDetail;
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { useSuspenseQuery } from '@tanstack/react-query';

import { getSimpleShelterProfile } from '@/apis/shelter';

const useFetchSimpleShelterInfo = (shelterId: number) =>
useSuspenseQuery({
queryKey: ['shelter', 'simpleProfile', shelterId],
queryFn: async () => (await getSimpleShelterProfile(shelterId)).data,
});

export default useFetchSimpleShelterInfo;

This file was deleted.

181 changes: 111 additions & 70 deletions apps/volunteer/src/pages/volunteers/detail/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,120 +5,160 @@ import {
HStack,
Text,
useDisclosure,
useToast,
VStack,
} from '@chakra-ui/react';
import { useEffect, useState } from 'react';
import { useMutation } from '@tanstack/react-query';
import { useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import AlertModal from 'shared/components/AlertModal';
import ImageCarousel from 'shared/components/ImageCarousel';
import InfoTextList from 'shared/components/InfoTextList';
import { LabelProps } from 'shared/components/Label';
import Label from 'shared/components/Label';
import LabelText from 'shared/components/LabelText';
import ProfileInfo from 'shared/components/ProfileInfo';
import { getDDay } from 'shared/utils/date';
import {
createFormattedTime,
createWeekDayLocalString,
getDDay,
} from 'shared/utils/date';

import { applyRecruitments } from '@/apis/recruitment';

import useFetchVolunteerDetail from './_hooks/useFetchVolunteerDetail';
import useFetchVolunteerDetail from './_hooks/useFetchRecruitmentDetail';
import useFetchSimpleShelterInfo from './_hooks/useFetchSimpleShelterInfo';

export default function VolunteersDetailPage() {
const toast = useToast();
const navigate = useNavigate();
const { id } = useParams();

const { id } = useParams<{ id: string }>();
const recruitmentId = Number(id);

const { isOpen, onOpen, onClose } = useDisclosure();
const [label, setLabel] = useState<LabelProps>({
labelTitle: '모집중',
type: 'GREEN',

const { data } = useFetchVolunteerDetail(recruitmentId);

const [isRecruitmentClosed, setIsRecruitmentClosed] = useState(
data.recruitmentIsClosed,
);

const volunteerDay = new Date(data.recruitmentStartTime);
const deadline = new Date(data.recruitmentDeadline);
const createdAt = new Date(data.recruitmentCreatedAt);
const updatedAt = new Date(data.recruitmentUpdatedAt);

const { data: shelter } = useFetchSimpleShelterInfo(data.shelterId);

const { mutate: applyRecruitment } = useMutation({
mutationFn: async () => await applyRecruitments(recruitmentId),
onSuccess: () => {
toast({
position: 'top',
description: '봉사 신청이 완료되었습니다.',
status: 'success',
duration: 1500,
});
},
onError: (error) => {
if (error.response?.status === 409) {
toast({
position: 'top',
description: '봉사 모집이 마감되었습니다.',
status: 'error',
duration: 1500,
});
setIsRecruitmentClosed(!isRecruitmentClosed);
}
},
});

const { data } = useFetchVolunteerDetail(3);

const {
imageUrls,
title,
content,
applicant,
capacity,
volunteerDay,
recruitmentDeadline,
volunteerStartTime,
volunteerEndTime,
recruitmentCreatedAt,
recruitmentIsClosed,
shelterInfo,
} = data;
const { shelterName, shelterImageUrl, shelterAddress, shelterEmail } =
shelterInfo;

useEffect(() => {
if (recruitmentIsClosed) {
setLabel({ labelTitle: '마감완료', type: 'GRAY' });
}
}, [recruitmentIsClosed]);

const goChatting = () => {
//TODO 채팅방 생성 API
navigate(`/chattings/${recruitmentId}`);
const goShelterProfilePage = () => {
navigate(`/shelters/profile/${data.shelterId}`);
};

const onApplyRecruitment = () => {
onClose();
//TODO 봉사신청 API
//TODO 봉사신청완료 toast
applyRecruitment();
};

return (
<Box pb={118}>
<ImageCarousel imageUrls={imageUrls} />
<ImageCarousel imageUrls={data.recruitmentImageUrls} />
<VStack spacing="5px" align="flex-start" p={4}>
<LabelText
labelTitle={label.labelTitle}
type={label.type}
content={`D-${getDDay(recruitmentDeadline)}`}
/>
{isRecruitmentClosed ? (
<Label labelTitle="마감완료" type="GRAY" />
) : (
<LabelText
labelTitle="모집중"
content={`D-${getDDay(data.recruitmentDeadline)}`}
/>
)}
<Text fontSize="xl" fontWeight="semibold">
{title}
{data.recruitmentTitle}
</Text>
<Text fontSize="sm" fontWeight="normal" color="gray.500">
작성일 | {recruitmentCreatedAt}(수정됨)
작성일 |{' '}
{updatedAt
? `${createFormattedTime(updatedAt)} (수정됨)`
: createFormattedTime(createdAt)}
</Text>
</VStack>

<InfoTextList
infoTextItems={[
{ title: '모집 인원', content: `${applicant}명 / ${capacity}명` },
{ title: '봉사일', content: volunteerDay },
{
title: '모집 인원',
content: `${data.recruitmentApplicantCount}명 / ${data.recruitmentCapacity}명`,
},
{
title: '봉사일',
content:
createFormattedTime(volunteerDay, 'YY.MM.DD') +
`(${createWeekDayLocalString(volunteerDay)})`,
},
{
title: '봉사 시간',
content: `${volunteerStartTime}~${volunteerEndTime}`,
content: `${createFormattedTime(
volunteerDay,
'hh:mm',
)} ~ ${createFormattedTime(
new Date(data.recruitmentEndTime),
'hh:mm',
)}`,
},
{
title: '마감일',
content:
createFormattedTime(deadline) +
`(${createWeekDayLocalString(deadline)})` +
createFormattedTime(deadline, 'hh:mm'),
},
{ title: '마감일', content: recruitmentDeadline },
]}
/>

<Text fontWeight="medium" px={4} py={6} wordBreak="keep-all">
{content}
{data.recruitmentContent}
</Text>
<Divider />
<ProfileInfo
infoImage={shelterImageUrl}
infoTitle={shelterName}
infoTexts={[shelterEmail, shelterAddress]}
/>
<Box cursor="pointer" onClick={goShelterProfilePage}>
<ProfileInfo
infoImage={shelter.shelterImageUrl}
infoTitle={shelter.shelterName}
infoTexts={[shelter.shelterEmail, shelter.shelterAddress]}
/>
</Box>
<Divider />

<HStack px={4} w="100%" pos="absolute" bottom="10px" left={0} spacing={5}>
<Button
onClick={goChatting}
size="md"
color="orange.400"
bgColor="white"
border="1.5px solid"
borderColor="orange.400"
w="100%"
_active={{ bg: undefined }}
_hover={{ bg: undefined }}
>
채팅하기
</Button>
<HStack
px={4}
w="100%"
pos="absolute"
bottom="10px"
left={0}
spacing={5}
backgroundColor="white"
>
<Button
onClick={onOpen}
size="md"
Expand All @@ -127,6 +167,7 @@ export default function VolunteersDetailPage() {
w="100%"
_active={{ bg: undefined }}
_hover={{ bg: undefined }}
isDisabled={isRecruitmentClosed}
>
신청하기
</Button>
Expand Down

0 comments on commit df3b1b8

Please sign in to comment.