Skip to content

Commit

Permalink
feat: 비로그인 사용자 또는 신청한 봉사인 경우 봉사신청 버튼 disabled (#258)
Browse files Browse the repository at this point in the history
* feat(volunteer): 봉사모집신청여부 조회 api 추가

* feat(volunteer): 봉사모집상세조회 fetch hook useQueries 변경, 신청 여부 fetch 추가

* feat(volunteer): 신청여부에 따라 봉사신청버튼 비활성화, 비로그인유저 신청버튼 클릭 시 로그인하기 모달

* feat(volunteer): 봉사신청 여부 조회 mock api 추가

* fix(volunteer): 모집마감일이 지나면 모집마감
  • Loading branch information
Eosdia authored Dec 1, 2023
1 parent df3b1b8 commit 97f4169
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 29 deletions.
9 changes: 9 additions & 0 deletions apps/volunteer/src/apis/recruitment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,12 @@ export const getRecruitments = (request: Partial<RecruitmentsRequest>) =>
'/recruitments',
{ params: request },
);

type IsAppliedRecruitmentResponse = {
isAppliedRecruitment: boolean;
};

export const getIsAppliedRecruitment = (recruitmentId: number) =>
axiosInstance.get<IsAppliedRecruitmentResponse>(
`/volunteers/recruitments/${recruitmentId}/apply`,
);
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 @@ -59,4 +59,9 @@ export const handlers = [
status: 204,
});
}),
http.get('/volunteers/recruitments/:recruitmentId/apply', async () => {
return HttpResponse.json({
isAppliedRecruitment: false,
});
}),
];
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
import { useSuspenseQuery } from '@tanstack/react-query';
import { useSuspenseQueries } from '@tanstack/react-query';
import { getRecruitmentDetail } from 'shared/apis/common/Recruitments';

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

const useFetchRecruitmentDetail = (recruitmentId: number) =>
useSuspenseQuery({
queryKey: ['recruitment', recruitmentId],
queryFn: async () => (await getRecruitmentDetail(recruitmentId)).data,
useSuspenseQueries({
queries: [
{
queryKey: ['recruitment', recruitmentId],
queryFn: async () => (await getRecruitmentDetail(recruitmentId)).data,
},
{
queryKey: ['recruitment', recruitmentId, 'isApplied'],
queryFn: async () =>
(await getIsAppliedRecruitment(recruitmentId)).data,
},
],
});
export default useFetchRecruitmentDetail;
97 changes: 72 additions & 25 deletions apps/volunteer/src/pages/volunteers/detail/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@ import {
useToast,
VStack,
} from '@chakra-ui/react';
import { useMutation } from '@tanstack/react-query';
import { useState } from 'react';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { Suspense, useEffect, 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 Label from 'shared/components/Label';
import LabelText from 'shared/components/LabelText';
import ProfileInfo from 'shared/components/ProfileInfo';
import useAuthStore from 'shared/store/authStore';
import {
createFormattedTime,
createWeekDayLocalString,
Expand All @@ -28,31 +29,70 @@ import { applyRecruitments } from '@/apis/recruitment';
import useFetchVolunteerDetail from './_hooks/useFetchRecruitmentDetail';
import useFetchSimpleShelterInfo from './_hooks/useFetchSimpleShelterInfo';

export default function VolunteersDetailPage() {
const toast = useToast();
function VolunteersDetail() {
const navigate = useNavigate();
const { user } = useAuthStore();

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

const { isOpen, onOpen, onClose } = useDisclosure();
const toast = useToast();

const { data } = useFetchVolunteerDetail(recruitmentId);
const [alertModalState, setAlertModalState] = useState({
modalTitle: '',
modalContent: '',
btnTitle: '',
onClick: () => {},
});

const [isRecruitmentClosed, setIsRecruitmentClosed] = useState(
data.recruitmentIsClosed,
);
useEffect(() => {
if (!user) {
setAlertModalState({
modalTitle: '로그인 하기',
modalContent: '로그인 하시겠습니까?',
btnTitle: '로그인 하기',
onClick: goLogInPage,
});
} else {
setAlertModalState({
modalTitle: '봉사 신청',
modalContent: '봉사를 신청하시겠습니까?',
btnTitle: '신청하기',
onClick: onApplyRecruitment,
});
}
}, [user]);

const [
{ data },
{
data: { isAppliedRecruitment },
},
] = useFetchVolunteerDetail(recruitmentId);

const [isApplied, setIsApplied] = useState(isAppliedRecruitment);

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 volunteerDateDay = getDDay(data.recruitmentDeadline);

const [isRecruitmentClosed, setIsRecruitmentClosed] = useState(
data.recruitmentIsClosed || volunteerDateDay < 0,
);

const queryClient = useQueryClient();

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

const { mutate: applyRecruitment } = useMutation({
mutationFn: async () => await applyRecruitments(recruitmentId),
onSuccess: () => {
queryClient.setQueryData(['recruitment', recruitmentId, 'isApplied'], {
isAppliedRecruitment: true,
});
setIsApplied(true);
toast({
position: 'top',
description: '봉사 신청이 완료되었습니다.',
Expand All @@ -77,6 +117,10 @@ export default function VolunteersDetailPage() {
navigate(`/shelters/profile/${data.shelterId}`);
};

const goLogInPage = () => {
navigate('/signin');
};

const onApplyRecruitment = () => {
onClose();
applyRecruitment();
Expand All @@ -91,17 +135,15 @@ export default function VolunteersDetailPage() {
) : (
<LabelText
labelTitle="모집중"
content={`D-${getDDay(data.recruitmentDeadline)}`}
content={`D-${volunteerDateDay === 0 ? 'Day' : volunteerDateDay}`}
/>
)}
<Text fontSize="xl" fontWeight="semibold">
{data.recruitmentTitle}
</Text>
<Text fontSize="sm" fontWeight="normal" color="gray.500">
작성일 |{' '}
{updatedAt
? `${createFormattedTime(updatedAt)} (수정됨)`
: createFormattedTime(createdAt)}
작성일 | {createFormattedTime(createdAt)}
{data.recruitmentUpdatedAt && ' (수정됨)'}
</Text>
</VStack>

Expand Down Expand Up @@ -131,7 +173,7 @@ export default function VolunteersDetailPage() {
title: '마감일',
content:
createFormattedTime(deadline) +
`(${createWeekDayLocalString(deadline)})` +
`(${createWeekDayLocalString(deadline)}) ` +
createFormattedTime(deadline, 'hh:mm'),
},
]}
Expand Down Expand Up @@ -167,19 +209,24 @@ export default function VolunteersDetailPage() {
w="100%"
_active={{ bg: undefined }}
_hover={{ bg: undefined }}
isDisabled={isRecruitmentClosed}
isDisabled={isRecruitmentClosed || isApplied}
>
신청하기
{isRecruitmentClosed
? '모집마감'
: isApplied
? '신청완료'
: '신청하기'}
</Button>
</HStack>
<AlertModal
modalTitle="봉사 신청"
modalContent="봉사를 신청하시겠습니까?"
btnTitle="신청하기"
isOpen={isOpen}
onClose={onClose}
onClick={onApplyRecruitment}
/>
<AlertModal {...alertModalState} isOpen={isOpen} onClose={onClose} />
</Box>
);
}

export default function VolunteersDetailPage() {
return (
<Suspense>
<VolunteersDetail />
</Suspense>
);
}

0 comments on commit 97f4169

Please sign in to comment.