Skip to content

Commit

Permalink
Merge pull request #167 from KNU-HAEDAL/Fix/issue-#161
Browse files Browse the repository at this point in the history
내 챌린지 기록 리스트 보기 페이지 수정
  • Loading branch information
Dobbymin authored Sep 29, 2024
2 parents 872ce51 + 1092d42 commit 3f20b0b
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 16 deletions.
27 changes: 27 additions & 0 deletions src/apis/my-challenge-record/getReview.api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { ChallengeResponse } from './getReview.response';
import { axiosClient } from '@/apis/AxiosClient';
import { useQuery } from '@tanstack/react-query';

const getReviewPath = () => '/api/challengeGroups/reviews';

const ReviewQueryKey = [getReviewPath()];

const getReview = async (
page: number,
size: number
): Promise<ChallengeResponse> => {
const response = await axiosClient.get(getReviewPath(), {
params: {
page,
size,
},
});
return response.data;
};

export const useGetReview = (page: number, size: number) => {
return useQuery<ChallengeResponse, Error>({
queryKey: [ReviewQueryKey, page, size],
queryFn: () => getReview(page, size),
});
};
28 changes: 28 additions & 0 deletions src/apis/my-challenge-record/getReview.response.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import ApiResponse from '@/apis/ApiResponse';

export type ChallengeUser = {
id: number;
nickname: string;
profileImageUrl: string;
tierInfo: {
tier: string;
totalExp: number;
currentExp: number;
};
};

export type ChallengeData = {
challengeId: number;
challengeTitle: string;
user: ChallengeUser;
content: string;
rating: number;
};

export type ChallengeResponseData = {
totalPage: number;
hasNext: boolean;
data: ChallengeData[];
};

export type ChallengeResponse = ApiResponse<ChallengeResponseData>;
34 changes: 24 additions & 10 deletions src/pages/my-challenge-record/components/list-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,31 @@ import ProfileImg from '@/assets/challenge/ZZAN-Green.png';
import { Image, Text } from '@chakra-ui/react';
import styled from '@emotion/styled';

const ListItem = () => {
type Props = {
challengeTitle: string;
userNickname: string;
profileImageUrl?: string | null;
id: number;
onClick: (id: number) => void;
};

const ListItem = ({ challengeTitle, profileImageUrl, onClick, id }: Props) => {
const handleClick = () => {
onClick(id);
};

return (
<ListItemLayout>
<ListItemLayout onClick={handleClick}>
<ProfileContainer>
<Image src={ProfileImg} alt='profile' width='1.5rem' />
<Image
src={profileImageUrl || ProfileImg}
alt='profile'
width='1.5rem'
/>
</ProfileContainer>
<ListText maxWidth='150px' fontWeight='700' fontSize='1.125rem'>
쓰레기 줍기 챌린지
<ListText fontWeight='700' fontSize='1.125rem'>
{challengeTitle}
</ListText>
<Text maxWidth='70px' fontSize='0.8rem'>
2024.08.05
</Text>
</ListItemLayout>
);
};
Expand All @@ -25,10 +38,12 @@ const ListItemLayout = styled.div`
flex-direction: row;
width: 100%;
gap: 20px;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #e0e0e0;
margin: 0.2rem 1rem;
&:last-child {
border-bottom: none;
}
`;

const ProfileContainer = styled.div`
Expand All @@ -47,5 +62,4 @@ const ListText = styled(Text)`
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
/* max-width: 150px; */
`;
80 changes: 74 additions & 6 deletions src/pages/my-challenge-record/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,52 @@
import { useCallback, useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';

import ListItem from './components/list-item';
import { useGetReview } from '@/apis/my-challenge-record/getReview.api';
import { ChallengeData } from '@/apis/my-challenge-record/getReview.response';
import TopBar from '@/components/features/layout/top-bar';
import { Box } from '@chakra-ui/react';
import { Box, Spinner, Text } from '@chakra-ui/react';
import styled from '@emotion/styled';

const MyChallengeRecord = () => {
const [page, setPage] = useState(0);
const [allChallenges, setAllChallenges] = useState<ChallengeData[]>([]);
const { data, isLoading } = useGetReview(page, 20);
const navigate = useNavigate();

const loadMoreChallenges = useCallback(() => {
if (data?.data.hasNext && !isLoading) {
setPage((prevPage) => prevPage + 1);
}
}, [data, isLoading]);

useEffect(() => {
if (data?.data.data) {
setAllChallenges((prevChallenges) => [
...prevChallenges,
...data.data.data,
]);
}
}, [data]);

useEffect(() => {
const handleScroll = () => {
if (
window.innerHeight + document.documentElement.scrollTop >=
document.documentElement.offsetHeight - 200
) {
loadMoreChallenges();
}
};

window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, [loadMoreChallenges]);

const handleNavigate = (id: number) => {
navigate(`/challenge/${id}/review`);
};

return (
<>
<TopBar
Expand All @@ -12,11 +55,30 @@ const MyChallengeRecord = () => {
backgroundColor='var(--color-green-06)'
/>
<MyChallengeRecordLayout>
<Box textAlign='center' marginY={3}>
<Text fontWeight='600'>챌린지 기록을 눌러 리뷰를 작성해보세요!</Text>
</Box>
<ChallengeList>
<ListItem />
<ListItem />
<ListItem />
{allChallenges.length > 0 ? (
allChallenges.map((challenge, index) => (
<ListItem
key={`${challenge.challengeId}-${index}`}
id={challenge.challengeId}
challengeTitle={challenge.challengeTitle}
userNickname={challenge.user.nickname}
profileImageUrl={challenge.user.profileImageUrl}
onClick={() => handleNavigate(challenge.challengeId)}
/>
))
) : (
<Text>챌린지 기록이 존재하지 않습니다.</Text>
)}
</ChallengeList>
{isLoading && (
<SpinnerContainer>
<Spinner size='xl' />
</SpinnerContainer>
)}
</MyChallengeRecordLayout>
</>
);
Expand All @@ -27,8 +89,8 @@ export default MyChallengeRecord;
const MyChallengeRecordLayout = styled.div`
display: flex;
flex-direction: column;
height: 100vh;
background-color: var(--color-green-06);
padding-bottom: 5rem;
`;

const ChallengeList = styled(Box)`
Expand All @@ -37,7 +99,13 @@ const ChallengeList = styled(Box)`
padding: 0.625rem 1.5rem;
flex-direction: column;
align-items: center;
border-radius: 1.25rem;
background-color: #fff;
`;

const SpinnerContainer = styled.div`
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
`;

0 comments on commit 3f20b0b

Please sign in to comment.