Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

완료한 챌린지 api 호출 기능 수정 #195

Merged
merged 17 commits into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ https://zzansuni-fe-vercel.vercel.app/
사용자들에게 챌린지에 대해 공유 할 수 있으며, 랭킹을 통해 서로 경쟁할 수
있습니다.

### 개발자 소개

| 백엔드 | 백엔드 | 프론트엔드 | 프론트엔드 | 백엔드 |
| :----------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------: |
| [<img src="https://github.com/momnpa333.png" width="100px">](https://github.com/momnpa333) | [<img src="https://github.com/kwonssshyeon.png" width="100px">](https://github.com/kwonssshyeon) | [<img src="https://github.com/Dobbymin.png" width="100px">](https://github.com/Dobbymin) | [<img src="https://github.com/joojjang.png" width="100px">](https://github.com/joojjang) | [<img src="https://github.com/bayy1216.png" width="100px">](https://github.com/bayy1216) |
| 권다운 | 권수현 | 김강민 | 김민주 | 손홍석 |

### 개발 동기

기획 단계에서 팀원들과 다양한 아이디어에 대해 생각을 해 보았고, 공통적으로
Expand All @@ -27,3 +34,20 @@ https://zzansuni-fe-vercel.vercel.app/
그 결과 다양한 주제의 챌린지를 진행할 수 있는 '짠순이' 라는 서비스를 기획하게
되었습니다.

## 기술 스택

- Frontend: `React`, `vite`, `typescript`, `@emotion/styled`, `@emotion/react`
- Backend: `Spring`, `JPA`, `MySQL`, `S3`

## 아키텍쳐
![image](https://github.com/user-attachments/assets/e0bfee6d-4de3-448b-ae20-f78936a7074e)


## ERD

![image](https://github.com/user-attachments/assets/8cd2aa03-9bd1-4ee0-a511-451711da40bb)


## CLASS DIAGRAM

![image](https://github.com/user-attachments/assets/60e5ea66-eb87-4f9f-84bd-94eb2e70963e)
27 changes: 27 additions & 0 deletions src/apis/challenge-completes/challenge-completes.api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { axiosClient } from '../AxiosClient';
import { ChallengeCompletesResponse } from './challenge-completes.response';
import { useQuery } from '@tanstack/react-query';

export const challengeCompletesPath = () => '/api/user/challenges/completes';

export const ChallengeCompletesQueryKey = [challengeCompletesPath()];

export const getChallengeCompletes = async (
page: number,
size: number
): Promise<ChallengeCompletesResponse> => {
const response = await axiosClient.get(challengeCompletesPath(), {
params: {
page,
size,
},
});
return response.data;
};

export const useGetChallengeCompletes = (page: number, size: number) => {
return useQuery<ChallengeCompletesResponse, Error>({
queryKey: [ChallengeCompletesQueryKey, page, size],
queryFn: () => getChallengeCompletes(page, size),
});
};
18 changes: 18 additions & 0 deletions src/apis/challenge-completes/challenge-completes.response.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import ApiResponse from '../ApiResponse';

export type ChallengeData = {
id: number;
challengeGroupId: number;
title: string;
successDate: string;
category: 'HEALTH' | 'ECHO' | 'SHARE' | 'VOLUNTEER';
reviewWritten: boolean;
};

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

export type ChallengeCompletesResponse = ApiResponse<ChallengeCompletes>;
15 changes: 12 additions & 3 deletions src/components/common/star-rating/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,16 @@ interface StarRatingProps {
export const StarRating = ({ rating, size = 24, onClick }: StarRatingProps) => {
const [ratingToPercent, setRatingToPercent] = useState<number>(0);

// rating 새로 전달받을 때마다 퍼센테이지 계산
useEffect(() => {
if (rating !== undefined) {
setRatingToPercent((rating / 5) * 100);
}
}, [rating]);

const handleClick = (rating: number) => {
// 별점 클릭 핸들러
const handleClickStar = (rating: number, e: React.MouseEvent) => {
e.preventDefault();
if (onClick) {
onClick(rating + 1); // 클릭한 별점 값 전달 (1부터 시작)
}
Expand All @@ -27,14 +30,20 @@ export const StarRating = ({ rating, size = 24, onClick }: StarRatingProps) => {
<Wrapper size={size}>
<FilledStars ratingToPercent={ratingToPercent}>
{[...Array(5)].map((_, index) => (
<Star key={`fill-${index}`} onClick={() => handleClick(index)}>
<Star
key={`fill-${index}`}
onClick={(e) => handleClickStar(index, e)}
>
</Star>
))}
</FilledStars>
<BaseStars>
{[...Array(5)].map((_, index) => (
<Star key={`base-${index}`} onClick={() => handleClick(index)}>
<Star
key={`base-${index}`}
onClick={(e) => handleClickStar(index, e)}
>
</Star>
))}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ const TooltipBox = styled.div<{ direction: string }>`
font-size: 12px;
font-weight: 500;
white-space: nowrap;
z-index: 10;
z-index: 5;
display: flex;
align-items: center;

Expand Down
2 changes: 1 addition & 1 deletion src/components/features/layout/nav-bar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const NavBar = () => {
return (
<Wrapper as='nav'>
{navBarData.map((item) => (
<Tab key={item.title} onClick={() => handleNav(item.path)}>
<Tab as='a' key={item.title} onClick={() => handleNav(item.path)}>
<IconImage src={item.icon} alt={item.title} />
</Tab>
))}
Expand Down
2 changes: 1 addition & 1 deletion src/components/features/layout/top-bar/page-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ const PageBarLayout = styled(Box)<{
padding: 0.5rem;
gap: 1rem;
background-color: ${(props) => props.backgroundColor};
z-index: 1;
z-index: 10;
position: sticky;
top: ${({ show }) => (show ? '0' : '-100px')};
transition: top 0.3s;
Expand Down
2 changes: 1 addition & 1 deletion src/pages/challenge-record/records/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
ChallengeRecordData,
ChallengeRecordDetailData,
} from '@/apis/challenge-record/challenge.record.response';
import Tooltip from '@/components/common/form/textarea/tooltip';
import Tooltip from '@/components/common/tooltip';
import { formatDate } from '@/utils/formatters';
import { Text } from '@chakra-ui/react';
import styled from '@emotion/styled';
Expand Down
12 changes: 3 additions & 9 deletions src/pages/my-challenge-record/components/list-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,10 @@ import styled from '@emotion/styled';
type Props = {
challengeId: number;
challengeTitle: string;
userNickname: string;
profileImageUrl?: string | null;
};

const ListItem = ({ challengeId, challengeTitle, profileImageUrl }: Props) => {
sessionStorage.setItem('activeTab', '0'); // 선택 탭 초기화
const ListItem = ({ challengeId, challengeTitle }: Props) => {
sessionStorage.setItem('activeTab', '0');

const navigate = useNavigate();

Expand Down Expand Up @@ -51,11 +49,7 @@ const ListItem = ({ challengeId, challengeTitle, profileImageUrl }: Props) => {
return (
<ListItemLayout>
<ProfileContainer>
<Image
src={profileImageUrl || ProfileImg}
alt='profile'
width='1.5rem'
/>
<Image src={ProfileImg} alt='profile' width='1.5rem' />
</ProfileContainer>
<ChallengeTitle
onClick={() => handleChallengeClick(challengeId, challengeTitle)}
Expand Down
14 changes: 6 additions & 8 deletions src/pages/my-challenge-record/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { useCallback, useEffect, useState } from 'react';

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 { useGetChallengeCompletes } from '@/apis/challenge-completes/challenge-completes.api';
import { ChallengeData } from '@/apis/challenge-completes/challenge-completes.response';
import EmptyState from '@/components/common/empty-state';
import TopBar, { HEADER_HEIGHT } from '@/components/features/layout/top-bar';
import { Box, Spinner } from '@chakra-ui/react';
Expand All @@ -11,7 +11,7 @@ import styled from '@emotion/styled';
const MyChallengeRecord = () => {
const [page, setPage] = useState(0);
const [allChallenges, setAllChallenges] = useState<ChallengeData[]>([]);
const { data, isLoading } = useGetReview(page, 20);
const { data, isLoading } = useGetChallengeCompletes(page, 20);

const loadMoreChallenges = useCallback(() => {
if (data?.data.hasNext && !isLoading) {
Expand Down Expand Up @@ -54,11 +54,9 @@ const MyChallengeRecord = () => {
{allChallenges.length > 0 ? (
allChallenges.map((challenge, index) => (
<ListItem
key={`${challenge.challengeId}-${index}`}
challengeId={challenge.challengeId}
challengeTitle={challenge.challengeTitle}
userNickname={challenge.user.nickname}
profileImageUrl={challenge.user.profileImageUrl}
key={`${challenge.id}-${index}`}
challengeId={challenge.challengeGroupId}
challengeTitle={challenge.title}
/>
))
) : (
Expand Down
2 changes: 1 addition & 1 deletion src/pages/rank/components/all/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const AllRank = () => {

useEffect(() => {
const fetchUserRanking = async () => {
const response: UserRankingResponse = await getUserRanking(1, 10);
const response: UserRankingResponse = await getUserRanking(0, 10);
const pageData = response.data;
const allUserData: User[] = pageData.data.map((user: UserData) => ({
...user,
Expand Down
6 changes: 6 additions & 0 deletions src/pages/review-write/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';

import { postReview } from '@/apis/review/review.api';
import ChallengeTitle from '@/components/common/challenge-title';
import CTA, { CTAContainer } from '@/components/common/cta';
import Textarea from '@/components/common/form/textarea';
import { StarRating } from '@/components/common/star-rating';
import TopBar, { HEADER_HEIGHT } from '@/components/features/layout/top-bar';
import { RouterPath } from '@/routes/path';
import {
formatRating,
formatDifficulty,
Expand Down Expand Up @@ -35,6 +37,7 @@ const ReviewWrite = () => {
const [content, setContent] = useState('');
const [isContentValid, setIsContentValid] = useState(true);
const [isButtonDisabled, setIsButtonDisabled] = useState(true);
const navigate = useNavigate();

const handleDifficultyClick = (difficulty: number) => {
setSelectedDifficulty(difficulty);
Expand Down Expand Up @@ -83,6 +86,7 @@ const ReviewWrite = () => {
})
.then(() => {
alert('리뷰가 등록되었습니다!');
navigate(`/${RouterPath.challenge}/${RouterPath.myRecord}`);
})
.catch((error) => {
// API에서 받은 오류 객체일 경우
Expand Down Expand Up @@ -242,6 +246,8 @@ const Chip = styled.button<{ isSelected: boolean }>`
font-size: var(--font-size-sm);
font-weight: 600;
text-align: center;
cursor: pointer;
outline: none;
${({ isSelected }) =>
isSelected &&
`
Expand Down