Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
… into fix/68
  • Loading branch information
sinamong0620 committed Sep 23, 2024
2 parents 8b4778a + 4e00eb3 commit 9e5f1db
Show file tree
Hide file tree
Showing 10 changed files with 1,130 additions and 157 deletions.
1 change: 1 addition & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ const router = (isLoggedIn: boolean) =>
</Route>
<Route path="/challenge" element={<ChallengeCommunityPage />} />
<Route path="/challenge/create" element={<CreateChallengePage />} />
<Route path="/challenge/create/:id" element={<CreateChallengePage />} />
<Route path="/challenge/:id" element={<ChallengeDetailPage />} />
</Route>
)
Expand Down
100 changes: 100 additions & 0 deletions src/api/ChallengeApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { Challenge, ChallengeResponse } from '../types/ChallengeType';
import { axiosInstance } from '../utils/apiConfig';

// * 챌린지 create
export const createChallenge = async (data: FormData): Promise<void | string> => {
try {
const response = await axiosInstance.post('/challenges', data, {
headers: { 'Content-Type': 'multipart/form-data' },
});
console.log(response.data);
return '';
} catch (error) {
console.error('Error fetching data:', error);
// return null;
}
};

// * 챌린지 update
export const patchChallenge = async (id: string, data: FormData): Promise<void> => {
try {
const response = await axiosInstance.patch(`/challenges/${id}`, data, {
headers: { 'Content-Type': 'multipart/form-data' },
});
// console.log(response.data);
return response.data.data.challengeId;
} catch (error) {
console.error('Error fetching data:', error);
// return null;
}
};

// * 챌린지 get
export const getSearchChallenge = async (
keyword: string | null,
category: string | null,
page: number,
size: number
): Promise<ChallengeResponse | null> => {
try {
console.log(keyword, category, '로 검색할게요');
const response = await axiosInstance.get(`/challenges/search`, {
params: {
category: category || '', // 빈 문자열은 전체 검색
keyword: keyword || '',
page: page,
size: size,
},
});
console.log('챌린지 전체 데이터', response.data);
return response.data.data;
} catch (error) {
console.error('Error fetching data:', error);
return null;
}
};

// * 챌린지 상세보기 get
export const getChallengeDetail = async (challengeId: string): Promise<Challenge | null> => {
try {
const response = await axiosInstance.get(`/challenges/${challengeId}`);
// console.log(response.data.data);

return response.data.data;
} catch (error) {
console.error('Error fetching data:', error);
return null;
}
};

// * 챌린지 삭제
export const deleteChallenge = async (id: string): Promise<void> => {
try {
const response = await axiosInstance.delete(`/challenges/${id}`);

console.log(response);
} catch (error) {
console.log('error');
}
};

// * 챌린지 참여
export const joinChallenge = async (challengeId: string, dashboardId: string): Promise<void> => {
try {
const response = await axiosInstance.post(`/challenges/${challengeId}/${dashboardId}`);
console.log(response.data);
} catch (error) {
console.error('Error fetching data:', error);
}
};

// * 챌린지 탈퇴
export const withdrawChallenge = async (id: string): Promise<void> => {
try {
const response = await axiosInstance.delete(`/challenges/${id}/withdraw`);

console.log(response);
} catch (error) {
console.log('error');
}
};
54 changes: 49 additions & 5 deletions src/components/ChallengeCard.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,58 @@
import { useNavigate } from 'react-router-dom';
import * as S from '../styles/ChallengeStyled';
import {
Challenge,
ChallengeCycle,
ChallengeCycleDetail_Monthly,
ChallengeCycleDetail_Weekly,
} from '../types/ChallengeType';
import { useState, useEffect } from 'react';
import defaultImg from '../img/default.png';

const ChallengeCard = () => {
const ChallengeCard = ({ challenge }: { challenge: Challenge }) => {
const navigate = useNavigate();
const [imageSrc, setImageSrc] = useState<string | undefined>(undefined);

useEffect(() => {
if (challenge.representImage instanceof File) {
// File 타입일 경우 URL로 변환
const objectUrl = URL.createObjectURL(challenge.representImage);
setImageSrc(objectUrl);

// 메모리 누수를 방지하기 위해 컴포넌트가 언마운트될 때 URL 해제
return () => URL.revokeObjectURL(objectUrl);
} else {
// string 타입일 경우 그대로 사용
setImageSrc(challenge.representImage);
}
}, [challenge.representImage]);

return (
<S.ChallengeComponent onClick={() => navigate(1)}>
<S.ChallengeImg></S.ChallengeImg>
<S.ChallengeName>챌린지명</S.ChallengeName>
<S.ChallengeHeadCount>참여 인원수</S.ChallengeHeadCount>
<S.ChallengeComponent onClick={() => navigate(`${challenge.challengeId}`)}>
{imageSrc ? <S.ChallengeImg src={imageSrc} /> : <S.ChallengeImg src={defaultImg} />}
<S.ChallengeName>{challenge.title}</S.ChallengeName>
<S.ChallengeHeadCount>
{challenge.cycle
? ChallengeCycle[challenge.cycle as keyof typeof ChallengeCycle]
: '알 수 없는 주기'}{' '}
{challenge.cycle === 'WEEKLY' &&
Array.isArray(challenge.cycleDetails) &&
challenge.cycleDetails
.map(
(detail: string) =>
ChallengeCycleDetail_Weekly[detail as keyof typeof ChallengeCycleDetail_Weekly]
)
.join(', ')}
{challenge.cycle === 'MONTHLY' &&
Array.isArray(challenge.cycleDetails) &&
challenge.cycleDetails
.map(
(detail: string) =>
ChallengeCycleDetail_Monthly[detail as keyof typeof ChallengeCycleDetail_Monthly] +
'일'
)
.join(', ')}
</S.ChallengeHeadCount>
</S.ChallengeComponent>
);
};
Expand Down
98 changes: 98 additions & 0 deletions src/components/JoinChallengeModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import React, { useEffect, useState } from 'react';

import ErrorIcon from '../img/error.png';
import Flex from './Flex';
import {
StyledModal,
customStyles,
ErrorImg,
SubTitle,
Title,
BtnYes,
BtnNo,
} from '../styles/ModalStyled';
import * as S from '../styles/ChallengeStyled';
import { searchPersonalDashBoard } from '../api/BoardApi';
import { useQuery } from '@tanstack/react-query';
import { DashboardItem } from '../types/PersonalDashBoard';
import { joinChallenge } from '../api/ChallengeApi';
import { useNavigate } from 'react-router-dom';

export interface CustomModalProps {
challengeId: string;
onNoClick: () => void;
fetchedData: () => void;
}

const JoinChallengeModal = ({ challengeId, onNoClick, fetchedData }: CustomModalProps) => {
// 데이터 가져오기
const { data } = useQuery({
queryKey: ['personalDashboard'],
queryFn: searchPersonalDashBoard,
});
const [selectDashboard, setSelectDashboard] = useState<string>('');

// * 데이터가 로드되면 첫 번째 대시보드 ID를 기본값으로 설정
useEffect(() => {
if (
data?.data.personalDashboardListResDto &&
data.data.personalDashboardListResDto.length > 0
) {
const firstDashboardId = data.data.personalDashboardListResDto[0].dashboardId;
if (firstDashboardId !== null && firstDashboardId !== undefined) {
setSelectDashboard(String(firstDashboardId)); // 첫 번째 값을 기본 선택값으로 설정
}
}
}, [data]);

// * 선택된 개인 대시보드 (챌린지 추가할 대시보드)
const handleSelectChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
setSelectDashboard(event.target.value);
};

// * 챌린지 참여 api
const submitJoin = async () => {
try {
await joinChallenge(challengeId, selectDashboard); // 챌린지 참여
fetchedData(); // 참여 후 데이터 다시 불러오기
onNoClick(); // 모달 닫기
} catch (error) {
console.error('Error joining challenge:', error);
}
};

return (
<StyledModal isOpen={true} shouldFocusAfterRender={false} style={customStyles}>
{/* <ErrorImg src={ErrorIcon} alt="error_icon" /> */}
<Flex flexDirection="column">
<Title>챌린지 참여하기</Title>
<SubTitle
style={{
marginTop: '0.7rem',
}}
>
챌린지를 추가할 대시보드를 선택해주세요.
</SubTitle>
</Flex>
<S.JoinSelect onChange={handleSelectChange}>
{data?.data.personalDashboardListResDto?.map((item: DashboardItem) => (
<option
key={item.dashboardId}
value={
item.dashboardId !== null && item.dashboardId !== undefined ? item.dashboardId : ''
}
>
{item.title}
</option>
))}
</S.JoinSelect>

<Flex>
<BtnYes onClick={onNoClick}>취소</BtnYes>
<BtnNo onClick={submitJoin}>참여</BtnNo>
</Flex>
</StyledModal>
);
};

export default JoinChallengeModal;
Binary file added src/img/default.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 9e5f1db

Please sign in to comment.