Skip to content

Commit

Permalink
Merge pull request #249 from Anifriends/develop
Browse files Browse the repository at this point in the history
release: v0.1.3
  • Loading branch information
DongjaJ authored Nov 30, 2023
2 parents 6b7f643 + df3b1b8 commit d0c7fe6
Show file tree
Hide file tree
Showing 39 changed files with 849 additions and 261 deletions.
8 changes: 7 additions & 1 deletion apps/shelter/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ import theme from 'shared/theme';

import { router } from '@/routes';

const queryClient = new QueryClient();
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 50000,
},
},
});

export default function App() {
return (
Expand Down
1 change: 1 addition & 0 deletions apps/shelter/src/apis/shelter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export const getShelterReviewList = (pageParams: PageParams) =>
volunteerTemperature: number;
volunteerReviewCount: number;
volunteerImageUrl: string;
volunteerId: number;
}[];
}>(`/shelters/me/reviews`, {
params: pageParams,
Expand Down
7 changes: 1 addition & 6 deletions apps/shelter/src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import ReactDOM from 'react-dom/client';

import App from './App.tsx';
Expand All @@ -16,9 +15,5 @@ async function deferRender() {
}

deferRender().then(() => {
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>,
);
ReactDOM.createRoot(document.getElementById('root')!).render(<App />);
});
6 changes: 4 additions & 2 deletions apps/shelter/src/mocks/browser.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { setupWorker } from 'msw/browser';

import { handlers as authHandlers } from './handlers/auth';
import { handlers as imageHandlers } from './handlers/image';
import { handlers as manageHandlers } from './handlers/manage';
import { handlers as recruitmentHandler } from './handlers/recruitment';
import { handlers as recruitmentDetailHandler } from './handlers/recruitmentDetail';
Expand All @@ -9,9 +10,10 @@ import { handlers as volunteerHandlers } from './handlers/volunteers';

export const worker = setupWorker(
...authHandlers,
...shelterHandlers,
...imageHandlers,
...manageHandlers,
...recruitmentHandler,
...recruitmentDetailHandler,
...manageHandlers,
...shelterHandlers,
...volunteerHandlers,
);
14 changes: 14 additions & 0 deletions apps/shelter/src/mocks/handlers/image.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { delay, http, HttpResponse } from 'msw';

export const handlers = [
http.post('/images', async () => {
await delay(200);

return HttpResponse.json(
{
imageUrls: ['https://source.unsplash.com/random'],
},
{ status: 200 },
);
}),
];
39 changes: 20 additions & 19 deletions apps/shelter/src/mocks/handlers/recruitment.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
import { delay, http, HttpResponse } from 'msw';

const DUMMY_RECRUITMENT = {
recruitmentId: 1,
recruitmentTitle: '봉사자를 모집합니다',
recruitmentStartTime: '2021-11-08T11:44:30.327959',
recruitmentEndTime: '2021-11-08T11:44:30.327959',
recruitmentDeadline: '2023-11-20T11:44:30.327959',
recruitmentIsClosed: false,
recruitmentApplicantCount: 15,
recruitmentCapacity: 15,
};
const randomDate = (start: Date, end: Date) =>
new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime()));

// eslint-disable-next-line
// @ts-ignore
const DUMMY_RECRUITMENT_LIST = Array.from(
{ length: 4 },
() => DUMMY_RECRUITMENT,
);
const currentDate = new Date();
const startDate = new Date(currentDate);
const endDate = new Date(currentDate);
startDate.setDate(currentDate.getDate() - 30);
endDate.setDate(endDate.getDate() + 30);

const getRandomDummyRecruitment = () => ({
recruitmentId: Number(String(Math.random()).slice(2)),
shelterId: Number(String(Math.random()).slice(2)),
recruitmentTitle: '봉사자를 모집합니다',
recruitmentStartTime: randomDate(startDate, endDate),
recruitmentEndTime: randomDate(startDate, endDate),
recruitmentDeadline: randomDate(startDate, endDate),
recruitmentIsClosed: Boolean(Math.floor(Math.random() * 2)),
recruitmentApplicantCount: Math.floor(Math.random() * 15),
recruitmentCapacity: 20,
});

export const DUMMY_APPLICANT = {
applicantId: 10,
Expand Down Expand Up @@ -44,9 +47,7 @@ export const handlers = [
hasNext: true,
},
recruitments: Array.from({ length: 4 }, () => ({
...DUMMY_RECRUITMENT,
recruitmentId: Math.random(),
shelterId: Math.random(),
...getRandomDummyRecruitment(),
})),
},
{ status: 200 },
Expand Down
2 changes: 1 addition & 1 deletion apps/shelter/src/mocks/handlers/shelter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export const handlers = [
return HttpResponse.json({ status: 200 });
}),
http.get('/shelters/me/reviews', async () => {
await delay(200);
await delay(3000);
return HttpResponse.json(
{
pageInfo: {
Expand Down
48 changes: 27 additions & 21 deletions apps/shelter/src/pages/my/_hooks/useMyPage.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useQuery } from '@tanstack/react-query';
import { useState } from 'react';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { ChangeEvent, useEffect, useState } from 'react';

import { getShelterInfoAPI, updateAddressStatusAPI } from '@/apis/shelter';
import { ShelterInfo } from '@/types/apis/shetler';
Expand Down Expand Up @@ -33,34 +33,40 @@ const createProfile = (response: ShelterInfo): ShelterProfile => {
};

export const useMyPage = () => {
const [isAddressPublic, setIsAddressPublic] = useState(false);
const queryClient = useQueryClient();

const updateAddressStatus = async () => {
try {
await updateAddressStatusAPI(!isAddressPublic);
setIsAddressPublic(!isAddressPublic);
} catch (error) {
console.error(error);
}
};
const { mutate: updateAddressStatus } = useMutation({
mutationFn: async (event: ChangeEvent<HTMLInputElement>) => {
updateAddressStatusAPI(!event.target.checked);
},
onSuccess: () => {
const isOpenedAddress = !isAddressPublic;
setIsAddressPublic(isOpenedAddress);
queryClient.setQueryData(['shelterProfile'], (data: ShelterProfile) => ({
...data,
isAddressPublic: isOpenedAddress,
}));
},
});

const { data } = useQuery({
queryKey: ['shelterProfile'],
queryFn: async () => {
const response = (await getShelterInfoAPI()).data;
setIsAddressPublic(response.shelterIsOpenedAddress);

return createProfile(response);
},
initialData: {
shelterName: '',
email: '',
phoneNumber: '',
sparePhoneNumber: '',
shelterAddress: '',
isAddressPublic: false,
},
});

const [isAddressPublic, setIsAddressPublic] = useState(
data?.isAddressPublic ?? false,
);

useEffect(() => {
if (data) {
const { isAddressPublic } = data;
setIsAddressPublic(isAddressPublic);
}
}, [data]);

return { shelterProfile: data, isAddressPublic, updateAddressStatus };
};
5 changes: 5 additions & 0 deletions apps/shelter/src/pages/my/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ export default function MyPage() {
const navigate = useNavigate();
const { shelterProfile, isAddressPublic, updateAddressStatus } = useMyPage();

if (!shelterProfile) {
return null;
}

const { shelterName, email, phoneNumber, sparePhoneNumber, shelterAddress } =
shelterProfile;

Expand All @@ -33,6 +37,7 @@ export default function MyPage() {
<InfoItem title="상세주소 공개">
<Switch
size="sm"
colorScheme="orange"
isChecked={isAddressPublic}
onChange={updateAddressStatus}
/>
Expand Down
4 changes: 3 additions & 1 deletion apps/shelter/src/pages/my/reviews/VolunteerProfile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ type VolunteerProfileprops = {
volunteerReviewCount: number;
volunteerImageUrl: string;
reviewCreatedAt: string;
onClickNextButton: VoidFunction;
};

export default function VolunteerProfile({
Expand All @@ -17,6 +18,7 @@ export default function VolunteerProfile({
volunteerReviewCount,
volunteerImageUrl,
reviewCreatedAt,
onClickNextButton,
}: VolunteerProfileprops) {
return (
<HStack spacing={2.5}>
Expand All @@ -26,7 +28,7 @@ export default function VolunteerProfile({
<Text fontWeight="semibold" lineHeight="tall">
{volunteerName}
</Text>
<Box as="button" boxSize={6} minW={6}>
<Box as="button" boxSize={6} minW={6} onClick={onClickNextButton}>
<Image src={NextIcon} w="full" h="full" />
</Box>
<InfoSubtext title="리뷰" content={volunteerReviewCount.toString()} />
Expand Down
13 changes: 10 additions & 3 deletions apps/shelter/src/pages/my/reviews/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Box, Heading, VStack } from '@chakra-ui/react';
import { Suspense } from 'react';
import { useNavigate } from 'react-router-dom';
import ReviewItem from 'shared/components/ReviewItem';
import ReviewItemSkeletonList from 'shared/components/ReviewItemSkeletonList';
import useIntersect from 'shared/hooks/useIntersection';
import { createFormattedTime } from 'shared/utils/date';

Expand All @@ -10,7 +12,7 @@ import VolunteerProfile from './VolunteerProfile';
const PAGE_SIZE = 10;

function Reviews() {
//TODO 봉사자 옆에 화살표 버튼 클릭시 봉사자 프로필 페이지로 가는 기능추가
const navigate = useNavigate();

const {
data: { pages },
Expand All @@ -28,6 +30,10 @@ function Reviews() {
}
});

const goVolunteerProfile = (id: number) => {
navigate(`/volunteers/profile/${id}`);
};

return (
<Box px={4}>
<Heading
Expand All @@ -54,6 +60,7 @@ function Reviews() {
volunteerTempature={review.volunteerTemperature}
volunteerReviewCount={review.volunteerReviewCount}
volunteerImageUrl={review.volunteerImageUrl}
onClickNextButton={() => goVolunteerProfile(review.volunteerId)}
reviewCreatedAt={createFormattedTime(
new Date(review.reviewCreatedAt),
'YY.MM.DD.',
Expand All @@ -62,14 +69,14 @@ function Reviews() {
</ReviewItem>
))}
</VStack>
<Box ref={ref} />
{isFetchingNextPage ? <ReviewItemSkeletonList /> : <Box ref={ref} />}
</Box>
);
}

export default function MyReviewsPage() {
return (
<Suspense fallback={<p>글목록 로딩중...</p>}>
<Suspense fallback={<ReviewItemSkeletonList showTitle />}>
<Reviews />
</Suspense>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import { useDisclosure } from '@chakra-ui/react';
import { useMutation } from '@tanstack/react-query';
import {
InfiniteData,
useMutation,
useQueryClient,
} from '@tanstack/react-query';
import { AxiosResponse } from 'axios';
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';

import {
closeShelterRecruitment,
deleteShelterRecruitment,
} from '@/apis/recruitment';
import { RecruitementsResponse, Recruitment } from '@/types/apis/recruitment';

export const useVolunteerRecruitItem = () => {
const navigate = useNavigate();
Expand All @@ -19,18 +25,64 @@ export const useVolunteerRecruitItem = () => {
onClick: () => {},
});

const queryClient = useQueryClient();

const getNewPages = (
data: InfiniteData<AxiosResponse<RecruitementsResponse>>,
processRecruitments: (recruitments: Recruitment[]) => Recruitment[],
) =>
data.pages.map((page) => ({
...page,
data: {
pageInfo: page.data.pageInfo,
recruitments: processRecruitments(page.data.recruitments),
},
}));

const closeRecruitment = useMutation({
mutationFn: async (recruitmentId: number) => {
await closeShelterRecruitment(recruitmentId);
onClose();
},
onSuccess: (_, recruitmentId) => {
queryClient.setQueryData(
['recruitments'],
(data: InfiniteData<AxiosResponse<RecruitementsResponse>>) => {
return {
pages: getNewPages(data, (recruitments) =>
recruitments.map((recruitment) =>
recruitment.recruitmentId === recruitmentId
? { ...recruitment, recruitmentIsClosed: true }
: recruitment,
),
),
pageParams: data.pageParams,
};
},
);
},
});

const deleteRecruitment = useMutation({
mutationFn: async (recruitmentId: number) => {
await deleteShelterRecruitment(recruitmentId);
onClose();
},
onSuccess: (_, recruitmentId) => {
queryClient.setQueryData(
['recruitments'],
(data: InfiniteData<AxiosResponse<RecruitementsResponse>>) => {
return {
pages: getNewPages(data, (recruitments) =>
recruitments.filter(
(recruitment) => recruitment.recruitmentId !== recruitmentId,
),
),
pageParams: data.pageParams,
};
},
);
},
});

const goVolunteersDetail = (recruitmentId: number) => {
Expand Down
Loading

1 comment on commit d0c7fe6

@vercel
Copy link

@vercel vercel bot commented on d0c7fe6 Nov 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

anifriends-frontend-volunteer – ./apps/volunteer

anifriends-frontend-volunteer-dongja.vercel.app
anifriends-frontend-volunteer-git-main-dongja.vercel.app
anifriends-frontend-volunteer.vercel.app

Please sign in to comment.