React
수정, 삭제, 완료 API 연결
WEB_CREASTORY
2024. 2. 13. 03:14
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { fundingCreate } from '../../../api/api'; // 펀딩 생성 API import
import { useParams } from 'react-router-dom';
import CreateModal from './Modal/CreateModal';
import Navbar from '../../../components/Navbar'; // 추가된 코드
import { useDispatch, useSelector } from 'react-redux'; // 추가된 코드
import { userLogout } from '../../../redux/authSlice'; // 추가된 코드
import {
MainContainer,
LeftContainer,
Logo,
P,
Button,
RightContainer,
NavbarDiv,
ProducImgtDiv,
InputTag,
FundingImg,
Body,
FundingDiv,
SponserDiv,
RadioInput,
SponserComment,
TogetherDiv,
SponsorComment,
ImgText,
} from './FundingCreateStyles';
// 펀딩 생성 페이지 컴포넌트
const FundingCreate = () => {
const navigate = useNavigate(); // React Router의 네비게이션 기능을 사용하기 위한 hook
const { id } = useParams(); // URL 매개변수(id)를 가져옴
const [itemImage, setItemImage] = useState(false);
const [isFundingModalOpen, setIsFundingModalOpen] = useState(false); // 모달 창의 열림 여부 상태 변수
const isLoggedIn = useSelector((state) => state.auth.isLoggedIn); // 추가된 코드
const dispatch = useDispatch(); // 추가된 코드
// 펀딩 생성 페이지에서 사용될 상태 변수 초기화
const [createData, setCreateData] = useState({
itemName: '',
targetAmount: '',
publicFlag: true,
showName: '',
title: '',
content: '',
endDate: '',
});
// 펀딩 이미지를 클릭했을 때 모달을 열고 이미지를 설정하는 함수
const handleFundingModalClick = (e) => {
setIsFundingModalOpen(true);
};
// 모달을 닫는 함수
const closeModal = () => {
setIsFundingModalOpen(false);
};
// 모달 내에서 이미지를 선택하고 설정하는 함수
const handleImageSelection = (itemImage) => {
setItemImage(itemImage);
setIsFundingModalOpen(false); // 이미지 선택 후 모달을 닫습니다.
};
// 각 입력값에 대한 상태 업데이트 핸들러
const handleItemNameChange = (e) => {
setCreateData({ ...createData, itemName: e.target.value });
};
const handleTargetAmountChange = (e) => {
setCreateData({ ...createData, targetAmount: e.target.value });
};
const handleShowNameChange = (e) => {
setCreateData({ ...createData, showName: e.target.value });
};
const handleTitleChange = (e) => {
setCreateData({ ...createData, title: e.target.value });
};
const handleContentChange = (e) => {
setCreateData({ ...createData, content: e.target.value });
};
const handleEndDateChange = (e) => {
setCreateData({ ...createData, endDate: e.target.value });
};
const handlePublicFlagChange = (e) => {
// 업데이트: 한 번에 하나의 옵션만 선택했는지 확인하세요.
const value = e.target.value === 'true' ? true : false;
setCreateData({ ...createData, publicFlag: value });
};
// 펀딩 생성 요청 처리 함수
const handleFundingClick = async () => {
try {
if (
itemImage === '' ||
createData.itemName === '' ||
createData.targetAmount === '' ||
createData.publicFlag === '' ||
createData.showName === '' ||
createData.title === '' ||
createData.content === '' ||
createData.endDate === ''
) {
alert('내용을 입력해주세요');
return;
}
// 펀딩 생성 API 호출 및 데이터 전송
const response = await fundingCreate({
id,
itemImage,
itemName: createData.itemName,
targetAmount: createData.targetAmount,
publicFlag: createData.publicFlag,
showName: createData.showName,
title: createData.title,
content: createData.content,
endDate: createData.endDate,
});
console.log('펀딩 생성 성공:', response);
navigate(`/fundingdetail/${response.id}`);
} catch (error) {
if (error.response) {
const statusCode = error.response.status;
const errorMessage = error.response.data.message;
if (statusCode === 400) {
// alert(errorMessage);
alert('펀딩 생성 실패 :', errorMessage);
}
}
}
};
// 추가된 코드
const handleLogoutClick = () => {
dispatch(userLogout()); // 로그아웃 액션 디스패치
navigate('/');
};
return (
<MainContainer>
<LeftContainer>
<Logo>😉 Giftipie</Logo>
<P pt="25px" fs="16px" fw="800" pb="5px">
기프티파이에서
</P>
<P fs="16px" fw="800" pb="5px">
정말 원하는 선물을
</P>
<P fs="16px" fw="800">
주고 받아요
</P>
<Button onClick={() => navigate('/')} mt="20px" w="180px" h="50px" fs="16px" color="white" bc="orange">
펀딩 시작하기
</Button>
</LeftContainer>
<RightContainer>
{/* 추가된 코드 */}
<NavbarDiv>
<Navbar isLoggedIn={isLoggedIn} handleLogoutClick={handleLogoutClick} />
</NavbarDiv>
<Body>
<form
onSubmit={(e) => {
e.preventDefault();
}}
>
<FundingDiv>
<P pb="10px" fs="16px" fw="900">
펀딩 생성페이지
</P>
<P pb="20px" fs="10px" fw="900">
펀딩 생성 페이지에 상품명과 이미지가 노출돼요.
</P>
<ProducImgtDiv>
<SponsorComment mt="10px" pointer="pointer" onClick={handleFundingModalClick}>
<FundingImg src={itemImage} h="90px" w="80px" />
<ImgText>상품 링크 URL</ImgText>
</SponsorComment>
<div>
<InputTag
type="text"
value={createData.itemName}
onChange={handleItemNameChange}
placeholder="상품명을 입력해주세요"
h="40px"
w="97%"
ml="10px"
mb="10px"
pl="10px"
/>
<InputTag
type="text"
value={createData.targetAmount}
onChange={handleTargetAmountChange}
placeholder="가격을 입력해주세요"
h="40px"
w="97%"
ml="10px"
pl="10px"
/>
</div>
</ProducImgtDiv>
{/* 모달 컴포넌트 표시 여부 확인 후 표시 */}
{isFundingModalOpen && (
<CreateModal closeModal={closeModal} handleImageSelection={handleImageSelection} />
)}
{/* 펀딩 내용 및 공개 여부 입력 폼 */}
<SponserDiv>
<SponserComment mt="50px">
<P pb="10px" fs="16px" fw="900">
펀딩 내용
</P>
<P pb="20px" fs="13px" fw="900">
공개 방식
</P>
<SponserDiv>
<RadioInput
value="true"
checked={createData.publicFlag === true}
onChange={handlePublicFlagChange}
type="radio"
mb="21px"
/>
<P pb="20px" fs="13px" fw="900" pl="20px">
공개
</P>
<P pb="20px" fs="10px" fw="900" pl="42px">
누구나 볼 수 있어요
</P>
</SponserDiv>
{/* 여기까지*/}
<SponserDiv>
<RadioInput
value="false"
checked={createData.publicFlag === false}
onChange={handlePublicFlagChange}
type="radio"
mb="21px"
/>
<P pb="20px" fs="13px" fw="900" pl="20px">
비공개
</P>
<P pb="20px" fs="10px" fw="900" pl="30px">
링크를 통해서만 방문할 수 있어요
</P>
</SponserDiv>
</SponserComment>
</SponserDiv>
<P pt="30px" pb="5px" fs="13px" fw="800">
보여줄 이름
</P>
<InputTag
type="text"
value={createData.showName}
onChange={handleShowNameChange}
placeholder="이름을 입력해주세요"
h="40px"
w="97%"
mb="10px"
pl="10px"
/>
<P pt="10px" pb="5px" fs="13px" fw="800">
제목
</P>
<InputTag
type="text"
value={createData.title}
onChange={handleTitleChange}
placeholder="제목을 입력해주세요"
h="40px"
w="97%"
mb="10px"
pl="10px"
/>
<P pt="10px" pb="5px" fs="13px" fw="800">
본문
</P>
<InputTag
type="text"
value={createData.content}
onChange={handleContentChange}
placeholder="본문을 입력해주세요"
h="90px"
w="97%"
mb="10px"
pl="10px"
pb="50px"
/>
<P pt="10px" pb="5px" fs="13px" fw="800">
마감일 설정
</P>
<InputTag
type="date"
value={createData.endDate}
onChange={handleEndDateChange}
h="40px"
w="97%"
pl="10px"
pt="10px"
/>
</FundingDiv>
<TogetherDiv>
<P pl="130px" fs="14px" fw="800">
펀딩 금액은 계좌로 전달돼요
</P>
<P pl="95px" fs="14px" fw="800">
펀딩에 성공하면 카톡으로 알림이 가요
</P>
</TogetherDiv>
<Button
onClick={handleFundingClick}
w="442px"
h="60px"
mt="10px"
color="white"
fs="19px"
bc="gray"
>
펀딩 등록하기
</Button>
</form>
</Body>
</RightContainer>
</MainContainer>
);
};
export default FundingCreate;
import axios from "axios";
export const instance = axios.create({
baseURL: process.env.REACT_APP_API_URL,
withCredentials: true, // Cookies에 브라우저가 자동으로 쿠키를 넣어줌
headers: {
"Access-Control-Allow-Origin": `${process.env.REACT_APP_API_URL}`,
},
});
// 회원가입 API
export const signup = async (userData) => {
try {
const response = await instance.post("/api/signup", userData);
if (response.status === 201) {
const { code, message } = response.data;
if (code === 2000) {
alert(message);
console.log("가입 성공! 환영합니다.");
} else {
console.error("올바르지 않은 응답 형식 또는 값");
throw new Error("회원가입 처리 중 오류가 발생했습니다.");
}
}
return response.data;
} catch (error) {
console.error("회원가입 오류:", error);
if (error.response && error.response.status === 400) {
const { code, message } = error.response.data;
if (code === 4000) {
alert(message);
} else {
console.error("올바르지 않은 응답 형식 또는 값");
alert("회원가입 처리 중 오류가 발생했습니다.");
}
}
throw error;
}
};
// 로그인 API
export const login = async (credentials) => {
try {
const response = await instance.post("/api/login", credentials);
if (response.status === 200) {
const { code, message, result } = response.data;
if (code === 2000 && result) {
alert(message);
} else {
console.error("올바르지 않은 응답 형식 또는 값");
throw new Error("로그인 처리 중 오류가 발생했습니다.");
}
}
return response.data;
} catch (error) {
console.error("로그인 오류:", error);
if (error.response && error.response.status === 401) {
const { code, message } = error.response.data;
if (code === 4000) {
alert(message);
} else {
console.error("올바르지 않은 응답 형식 또는 값");
alert("로그인 처리 중 오류가 발생했습니다.");
}
}
throw error;
}
};
// 펀딩 생성페이지 API
export const fundingCreate = async (fundingData) => {
try {
const response = await instance.post("/api/funding/create", fundingData); // 펀딩 생성 요청
console.log("펀딩 생성페이지 API", response);
return response.data; // 응답 데이터 반환
} catch (error) {
throw error; // 실패 시 예외 처리
}
};
// 펀딩 생성페이지 모달창(ItemLink) API
export const modalItemLink = async (LinkData) => {
try {
const response = await instance.post("/api/funding/addLink", LinkData); // 모달창(ItemLink) API 호출
return response.data; // 응답 데이터 반환
} catch (error) {
throw error; // 실패 시 예외 처리
}
};
// 펀딩 상세페이지 API
export const fetchFundingDetail = async (id) => {
try {
const response = await instance.get(`/api/funding/${id}`); // 펀딩 상세페이지 요청
console.log("펀딩 상세페이지 API", response);
return response.data; // 응답 데이터 반환
} catch (error) {
console.error("펀딩 상세페이지 API 호출 오류:", error); // 오류 로깅
throw error; // 에러 다시 throw 또는 다른 적절한 처리를 수행
}
};
// 펀딩 후원자 상세페이지 API
export const fetchSponsorDetail = async (id) => {
try {
const response = await instance.get(`/api/fundingsponsordetail/${id}`); // 펀딩 후원자 상세페이지 요청
if (response.status === 200) {
alert("후원자 상세페이지입니다.");
return response.data; // 응답 데이터 반환
}
} catch (error) {
console.error("펀딩 상세페이지 API 호출 오류:", error); // 오류 로깅
throw error; // 에러 다시 throw 또는 다른 적절한 처리를 수행
}
};
// 펀딩 수정페이지 불러오기 API - get
export const FundingModifyGet = async (id, data) => {
try {
const response = await instance.get(`/api/funding/${id}`, data); // 펀딩 수정페이지 요청
console.log("펀딩 불러오기 API", response);
if (response.status === 200) {
return response.data; // 응답 데이터 반환
}
} catch (error) {
console.error("API 호출 중 에러 발생:", error); // 오류 로깅
throw error; // 에러 다시 throw
}
};
// 펀딩 수정페이지 API - 변경버튼 - patch
export const updateFundingModify = async (id, data) => {
try {
const response = await instance.patch(`/api/funding/${id}/update`, data); // 펀딩 수정페이지 요청
console.log("펀딩 수정 API", response);
if (response.status === 200) {
return response.data; // 응답 데이터 반환
}
} catch (error) {
throw error; // 실패 시 예외 처리
}
};
// 펀딩 수정페이지 - 상품링크 변경 모달창(ItemLink) API
export const modalLinkModify = async (linkModifyData) => {
try {
const response = await instance.post(
"/api/funding/modifyLink",
linkModifyData
); // 모달창(ItemLink) API 호출
if (response.status === 200) {
return response.data; // 응답 데이터 반환
}
} catch (error) {
if (error.response) {
const statusCode = error.response.status;
const errorMessage = error.response.data.message;
if (statusCode === 400) {
alert(errorMessage);
}
}
}
};
// 펀딩 수정페이지 - 삭제하기 버튼 API - delete
export const deleteFundingModify = async (id, data) => {
try {
const response = await instance.delete(`/api/funding/${id}`, data);
console.log("펀딩 삭제 API", response);
if (response.status === 200) {
return response.data; // 응답 데이터 반환
}
} catch (error) {
if (error.response) {
const statusCode = error.response.status;
const errorMessage = error.response.data.message;
if (statusCode === 400) {
alert(errorMessage);
}
}
}
};
// 펀딩 수정페이지 API - 종료버튼 API - patch
export const completeFundingModify = async (id, data) => {
try {
const response = await instance.patch(`/api/funding/${id}/finish`, data); // 펀딩 수정페이지 요청
console.log("펀딩 종료 API", response);
if (response.status === 200) {
return response.data; // 응답 데이터 반환
}
} catch (error) {
throw error; // 실패 시 예외 처리
}
};
import React, { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { useParams } from "react-router-dom";
import { fetchFundingDetail } from "../../../api/api"; // 펀딩 상세 정보를 가져오는 API 함수 import
import Navbar from "../../../components/Navbar"; // 추가된 코드
import { useDispatch, useSelector } from "react-redux"; // 추가된 코드
import { userLogout } from "../../../redux/authSlice"; // 추가된 코드
import {
MainContainer,
LeftContainer,
Logo,
P,
Button,
RightContainer,
NavbarDiv,
NavbarBtn,
NavigateBtn,
Body,
BannerImg,
FundingDiv,
SponserDiv,
SponserComment,
SponsorImg,
ProgressBar,
Progress,
BetweenDiv,
TogetherDiv,
} from './FundingDetailStyles';
// 펀딩 상세 페이지 컴포넌트
const FundingDetail = () => {
const navigate = useNavigate(); // React Router의 네비게이션 기능을 사용하기 위한 hook
const { id } = useParams(); // URL 매개변수(id)를 가져옴
const isLoggedIn = useSelector((state) => state.auth.isLoggedIn); // 추가된 코드
const dispatch = useDispatch(); // 추가된 코드
// 펀딩 상세 정보를 담는 상태 변수 초기화
const [detailData, setDetailData] = useState({
// 초기 상태를 명세서에 따라 설정
// FundingCreate에서 받아올 Data 초기값
itemImage: '',
itemName: '',
targetAmount: 0,
publicFlag: false, // 공개, 비공개 여부
showName: '',
title: '',
content: '',
endDate: '',
// FundignDetail에 출력되는 Data 초기값
itemLink: '',
currentAmount: 0,
dday: '',
status: false,
achievementRate: 0,
ownerFlag: false, // true면 수정 페이지 버튼 보여지게
modifiedAt: '', // 수정 날짜
// 후원자 이름 추가
// 후원자 댓글 추가
});
useEffect(() => {
// API를 호출하여 펀딩 상세 정보를 가져오는 함수 정의
const fetchData = async () => {
try {
if (!id) {
// 유효한 id가 없으면 데이터를 요청하지 않음
return;
}
// 펀딩 ID를 설정하여 특정 펀딩의 상세 정보 가져오기
// const fundingid = 1; // 예: 펀딩 ID가 1인 경우
const data = await fetchFundingDetail(id);
setDetailData(data); // 가져온 데이터를 상태 변수에 설정
} catch (error) {
if (error.response) {
const statusCode = error.response.status;
const errorMessage = error.response.data.message;
if (statusCode === 400) {
alert(errorMessage);
}
}
}
};
// 컴포넌트가 마운트될 때 API 호출 함수 실행
fetchData();
}, [id]); // 빈 배열을 전달하여 한 번만 실행하도록 설정
// 추가된 코드
const handleLogoutClick = () => {
dispatch(userLogout()); // 로그아웃 액션 디스패치
navigate("/");
};
return (
<MainContainer>
<LeftContainer>
<Logo>😉 Giftipie</Logo>
<P pt="25px" fs="16px" fw="800" pb="5px">
기프티파이에서
</P>
<P fs="16px" fw="800" pb="5px">
정말 원하는 선물을
</P>
<P fs="16px" fw="800">
주고 받아요
</P>
<Button
onClick={() => navigate("/")}
mt="20px"
w="180px"
h="50px"
fs="16px"
color="white"
bc="orange"
>
펀딩 시작하기
</Button>
</LeftContainer>
<RightContainer>
{/* 추가된 코드 */}
<NavbarDiv>
<Navbar
isLoggedIn={isLoggedIn}
handleLogoutClick={handleLogoutClick}
/>
</NavbarDiv>
<Body>
<NavigateBtn onClick={() => navigate(`/fundingModify/${id}`)} pl="360px" fs="13px" fw="600">
🖍 수정하기
</NavigateBtn>
<BannerImg src={detailData.itemImage} alt="image" />
<FundingDiv>
<P pt="20px" fs="13px" fw="800">
{detailData.status}
</P>
<P pt="10px" fs="20px" fw="900">
{detailData.title}
</P>
<P pt="10px" fs="15px" fw="900">
{detailData.itemName}
</P>
<BetweenDiv>
<P pt="10px" fs="15px" fw="800">
{detailData.dday}
</P>
<P pt="10px" fs="15px" fw="800">
{detailData.endDate}
</P>
</BetweenDiv>
<P pt="10px" fs="15px" fw="800">
{detailData.showName}
</P>
<ProgressBar>
<Progress width={(65 / 100) * 100} />
</ProgressBar>
<BetweenDiv>
<P pt="8px" fs="15px" fw="800">
{detailData.achievementRate}%
</P>
<P pt="8px" pl="90px" fs="15px" fw="800">
현재 {detailData.currentAmount}원
</P>
<P pt="8px" pl="90px" fs="15px" fw="800">
{detailData.targetAmount}원
</P>
</BetweenDiv>
</FundingDiv>
<TogetherDiv bc="orange">
<P pt="30px" pl="30px" fs="14px" fw="800">
{detailData.content}
</P>
</TogetherDiv>
<FundingDiv>
<P pt="20px" fs="16px" fw="900">
후원자
</P>
{/* <Sponsor /> */}
<SponserDiv>
<SponsorImg src="/imgs/iu.jpg" alt="image" />
<SponserComment mt="10px">
<P pl="5px" fs="13px" fw="800">
아**
</P>
<Button
mt="5px"
w="300px"
h="40px"
pr="90px"
fs="13px"
bc="violet"
>
줄이어폰 그만써~ 생일축하해!!
</Button>
</SponserComment>
</SponserDiv>
<SponserDiv>
<SponsorImg src="/imgs/songjoongy.jpg" alt="logo" />
<SponserComment mt="10px">
<P pl="5px" fs="13px" fw="800">
{detailData.showName}
</P>
<Button
mt="5px"
w="300px"
h="40px"
pr="90px"
fs="13px"
bc="violet"
>
{detailData.content}
</Button>
</SponserComment>
</SponserDiv>
<SponserDiv>
<SponsorImg src="/imgs/junjihyun.jpg" alt="img" />
<SponserComment mt="10px">
<P pl="5px" fs="13px" fw="800">
{detailData.showName}
</P>
<Button
mt="5px"
w="300px"
h="40px"
pr="90px"
fs="13px"
bc="violet"
>
{detailData.content}
</Button>
</SponserComment>
</SponserDiv>
<P
onClick={() => navigate("/fundingsponsordetail")}
pt="40px"
pl="160px"
fs="14px"
fw="800"
>
전체보기 ▶
</P>
</FundingDiv>
<FundingDiv>
<P pt="10px" fs="16px" fw="900">
펀딩 참여하기
</P>
<Button onClick={() => navigate("/fundingpay")} mt="30px" w="375px" h="60px" bc="orange">
<BetweenDiv>
<P pt="2px" pl="20px" fs="15px" fw="800" color="black">
커피 한잔 선물하기
</P>
<P pt="2px" pr="20px" fs="15px" fw="700" color="black">
5,000원
</P>
</BetweenDiv>
</Button>
<Button onClick={() => navigate("/fundingpay")} mt="10px" w="375px" h="60px" bc="orange">
<BetweenDiv>
<P pt="2px" pl="20px" fs="15px" fw="800" color="black">
파인트 아이스크림 선물하기
</P>
<P pt="2px" pr="20px" fs="15px" fw="700" color="black">
10,000원
</P>
</BetweenDiv>
</Button>
<Button onClick={() => navigate("/fundingpay")}
mt="10px"
w="375px"
h="60px"
bc="orange"
>
<BetweenDiv>
<P pt="2px" pl="20px" fs="15px" fw="800" color="black">
원하는만큼 선물하기
</P>
<P pt="2px" pr="20px" fs="15px" fw="700" color="black">
직접입력
</P>
</BetweenDiv>
</Button>
<Button onClick={() => navigate("/fundingpay")} mt="10px" w="375px" h="60px" bc="orange">
<BetweenDiv>
<P pt="2px" pl="20px" fs="15px" fw="800" color="black">
이 펀딩을 끝내러 왔다
</P>
<P pt="2px" pr="20px" fs="15px" fw="700" color="black">
{detailData.currentAmount}원
</P>
</BetweenDiv>
</Button>
</FundingDiv>
<TogetherDiv bc="violet">
<P pt="30px" pl="30px" fs="16px" fw="800">
Giftipie에서 함께 하는 기쁨
</P>
<BetweenDiv>
<P pt="40px" pl="30px" fs="13px" fw="800">
펀딩에 참여한 사람
</P>
<P pt="40px" pr="30px" fs="13px" fw="800">
11명
</P>
</BetweenDiv>
<BetweenDiv>
<P pt="20px" pl="30px" fs="13px" fw="800">
선물을 받은 사람
</P>
<P pt="20px" pr="30px" fs="13px" fw="800">
11명
</P>
</BetweenDiv>
<BetweenDiv>
<P pt="20px" pl="30px" fs="13px" fw="800">
모인 펀딩 금액
</P>
<P pt="20px" pr="30px" fs="13px" fw="800">
{detailData.currentAmount}원
</P>
</BetweenDiv>
</TogetherDiv>
<Button
onClick={() => navigate("/fundingpay")}
mt="30px"
w="442px"
h="60px"
color="black"
fs="19px"
bc="orange"
>
선물하기
</Button>
</Body>
</RightContainer>
</MainContainer>
);
};
export default FundingDetail;
import React, { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom'; // React Router에서 네비게이션 기능을 사용하기 위한 hook
import { useParams } from 'react-router-dom'; // React Router에서 URL 매개변수를 가져오기 위한 hook
import Navbar from '../../../components/Navbar'; // 추가된 코드
import { useDispatch, useSelector } from 'react-redux'; // 추가된 코드
import { userLogout } from '../../../redux/authSlice'; // 추가된 코드
import { updateFundingModify } from '../../../api/api'; // 펀딩 수정 API를 호출하기 위한 함수 import
import { deleteFundingModify } from '../../../api/api'; // 펀딩 수정 API를 호출하기 위한 함수 import
import { FundingModifyGet } from '../../../api/api'; // 펀딩 상세 정보를 가져오기 위한 함수 import
import { completeFundingModify } from '../../../api/api'; // 펀딩 상세 정보를 가져오기 위한 함수 import
// import ModifyModal from './ModifyModal'; // 이미지 선택 모달 컴포넌트 import
import {
MainContainer,
LeftContainer,
Logo,
P,
Button,
NavbarDiv,
RightContainer,
InputTag,
Body,
FundingDiv,
SponsorDiv,
RadioInput,
SponsorComment,
FundingImg,
// ImgText,
TogetherDiv,
} from './FundingModifyStyles'; // 스타일 컴포넌트 import
// 펀딩 수정 페이지 컴포넌트
const FundingModify = () => {
const navigate = useNavigate(); // React Router의 네비게이션 기능을 사용하기 위한 hook
const { id } = useParams(); // URL 매개변수(id)를 가져옴
const isLoggedIn = useSelector((state) => state.auth.isLoggedIn); // 추가된 코드
const dispatch = useDispatch(); // 추가된 코드
// 펀딩 데이터 상태와 상태 설정 함수 초기화
const [fundingData, setFundingData] = useState({
itemName: '',
showName: '',
title: '',
content: '',
targetAmount: 0,
publicFlag: '',
endDate: '',
itemImage: '',
});
// 추가된 코드
const handleLogoutClick = () => {
dispatch(userLogout()); // 로그아웃 액션 디스패치
navigate('/');
};
// const [isFundingModalOpen, setIsFundingModalOpen] = useState(false); // 모달 창의 열림 여부 상태 변수
// 펀딩 이미지를 클릭하여 모달을 열고 이미지를 설정하는 함수
// const handleFundingModalClick = (e) => {
// setIsFundingModalOpen(true);
// };
// 모달을 닫는 함수
// const closeModal = () => {
// setIsFundingModalOpen(false);
// // setItemImage(''); // 이미지 상태를 초기화하여 이미지를 숨김
// };
// 모달 내에서 이미지를 선택하고 설정하는 함수
// const handleImageSelection = (itemImage) => {
// setFundingData(itemImage);
// setIsFundingModalOpen(false); // 이미지 선택 후 모달 닫기
// };
useEffect(() => {
// API를 호출하여 펀딩 상세 정보를 가져오는 함수 정의
const fetchData = async () => {
try {
if (!id) {
// 유효한 id가 없으면 데이터를 요청하지 않음
return;
}
const data = await FundingModifyGet(id); // 펀딩 상세 정보 가져오기
setFundingData(data); // 가져온 데이터를 상태 변수에 설정
console.log('펀딩상세 가져오기 성공', data);
} catch (error) {
console.error('펀딩상세 가져오기 오류:', error);
}
};
// 컴포넌트가 마운트될 때 API 호출 함수 실행
fetchData();
}, [id]); // 빈 배열을 전달하여 한 번만 실행하도록 설정
// 펀딩 수정 요청 함수
const handlefundingModifyClick = async () => {
try {
if (
fundingData.publicFlag === '' ||
fundingData.showName === '' ||
fundingData.title === '' ||
fundingData.content === ''
) {
alert('내용을 입력해주세요');
return;
}
const data = await updateFundingModify(id, fundingData); // 펀딩 수정 API 호출
// setFundingData(fundingData.map((data) => {
// if (data.id === id) {
// return { ...data, fundingData };
// } else {
// return data;
// }
// }))
console.log('펀딩 수정 성공:', data);
navigate(`/fundingdetail/${data.id}`); // 펀딩 상세 페이지로 이동
} catch (error) {
if (error.response) {
const statusCode = error.response.status;
const errorMessage = error.response.data.message;
if (statusCode === 400) {
alert('펀딩 수정 실패 :', errorMessage);
}
}
}
};
const handledeleteFundingClick = async () => {
try {
const confirmDelete = window.confirm('정말 삭제하시겠습니까?');
if (!confirmDelete) return;
await deleteFundingModify(id, fundingData);
console.log('펀딩 삭제 성공:', id);
navigate(`/`);
// if (id) {
// alert('정말 삭제하시겠습니까?');
// return;
// }
// const data = await deleteFundingModify(id, fundingData);
// const data = await deleteFundingModify(id);
// setFundingData(
// fundingData.filter((data) => {
// return data.id !== id;
// })
// );
// console.log('펀딩 삭제 성공:', data);
// navigate(`/`);
} catch (error) {
console.error('펀딩 삭제 실패:', error);
// 에러 핸들링
}
};
const handlecompleteFundingClick = async () => {
try {
if (!id) {
// 유효한 id가 없으면 데이터를 요청하지 않음
return;
}
const data = await completeFundingModify(id); // 펀딩 상세 정보 가져오기
setFundingData(data); // 가져온 데이터를 상태 변수에 설정
console.log('펀딩 종료 성공', data);
navigate(`/`);
} catch (error) {
console.error('펀딩 종료 오류:', error);
}
};
// UI 렌더링
return (
<MainContainer>
{/* 왼쪽 컨테이너 */}
<LeftContainer>
{/* 로고 및 간단한 설명 */}
<Logo>😉 Giftipie</Logo>
<P pt="25px" fs="16px" fw="800" pb="5px">
기프티파이에서
</P>
<P fs="16px" fw="800" pb="5px">
정말 원하는 선물을
</P>
<P fs="16px" fw="800">
주고 받아요
</P>
{/* 펀딩 시작하기 버튼 */}
<Button onClick={() => navigate('/')} mt="20px" w="180px" h="50px" fs="16px" color="white" bc="orange">
펀딩 시작하기
</Button>
</LeftContainer>
{/* 오른쪽 컨테이너 */}
<RightContainer>
{/* 추가된 코드 */}
<NavbarDiv>
<Navbar isLoggedIn={isLoggedIn} handleLogoutClick={handleLogoutClick} />
</NavbarDiv>
{/* 펀딩 수정 내용 입력 부분 */}
<Body>
{/* 펀딩 상품 정보 입력 및 이미지 변경 */}
<FundingDiv>
{/* 펀딩 페이지에 노출되는 상품명 및 이미지 변경 버튼 */}
<P pb="10px" fs="16px" fw="900">
펀딩 수정페이지
</P>
<P pb="20px" fs="10px" fw="900">
펀딩페이지의 상품명, 가격, 이미지는 변경할 수 없습니다.
</P>
<SponsorDiv>
{/* <SponsorComment pointer="pointer" onClick={handleFundingModalClick}> */}
{/* <FundingImg onClick={handleFundingModalClick} src={fundingData.itemImage} alt="logo"/> */}
<FundingImg src={fundingData.itemImage} alt="logo" />
{/* <ImgText>이미지 변경</ImgText> */}
{/* </SponsorComment> */}
<SponsorComment mt="10px">
<div>
<InputTag
type="text"
placeholder="상품명을 입력해주세요"
value={fundingData.itemName}
// onChange={(e) => {
// setFundingData({ ...fundingData, itemName: e.target.value });
// }}
h="40px"
w="92%"
ml="10px"
mb="10px"
pl="10px"
/>
<InputTag
type="text"
placeholder="가격을 입력해주세요"
value={fundingData.targetAmount}
// onChange={(e) => {
// setFundingData({ ...fundingData, targetAmount: e.target.value });
// }}
h="40px"
w="92%"
ml="10px"
pl="10px"
/>
</div>
</SponsorComment>
{/* 모달 컴포넌트 표시 여부 확인 후 표시 */}
{/* {isFundingModalOpen && (
<ModifyModal closeModal={closeModal} handleImageSelection={handleImageSelection} />
)} */}
</SponsorDiv>
{/* 펀딩 내용 및 공개 여부 입력 부분 */}
<SponsorDiv>
<SponsorComment mt="50px">
<P pb="10px" fs="16px" fw="900">
펀딩 내용
</P>
<P pb="20px" fs="13px" fw="900">
공개 방식
</P>
<SponsorDiv>
<RadioInput
value="true"
checked={fundingData.publicFlag === "true"}
onChange={(e) => {
setFundingData({ ...fundingData, publicFlag: e.target.value });
}}
type="radio"
mb="21px"
/>
<P pb="20px" fs="13px" fw="900" pl="20px">
공개
</P>
<P pb="20px" fs="10px" fw="900" pl="42px">
누구나 볼 수 있어요
</P>
</SponsorDiv>
<SponsorDiv>
<RadioInput
value="false"
checked={fundingData.publicFlag === "false"}
onChange={(e) => {
setFundingData({ ...fundingData, publicFlag: e.target.value });
}}
type="radio"
mb="21px"
/>
<P pb="20px" fs="13px" fw="900" pl="20px">
비공개
</P>
<P pb="20px" fs="10px" fw="900" pl="30px">
링크를 통해서만 방문할 수 있어요
</P>
</SponsorDiv>
</SponsorComment>
</SponsorDiv>
<P pt="30px" pb="5px" fs="13px" fw="800">
이름
</P>
<InputTag
type="text"
placeholder="이름을 입력해주세요"
value={fundingData.showName}
onChange={(e) => {
setFundingData({ ...fundingData, showName: e.target.value });
}}
h="40px"
w="97%"
mb="10px"
pl="10px"
/>
<P pt="10px" pb="5px" fs="13px" fw="800">
제목
</P>
<InputTag
type="text"
placeholder="제목을 입력해주세요"
value={fundingData.title}
onChange={(e) => {
setFundingData({ ...fundingData, title: e.target.value });
}}
h="40px"
w="97%"
mb="10px"
pl="10px"
/>
<P pt="10px" pb="5px" fs="13px" fw="800">
본문
</P>
<InputTag
type="text"
placeholder="본문을 입력해주세요"
value={fundingData.content}
onChange={(e) => {
setFundingData({ ...fundingData, content: e.target.value });
}}
h="90px"
w="97%"
mb="10px"
pl="10px"
pb="50px"
/>
<P pt="10px" pb="5px" fs="13px" fw="800">
마감일 설정
</P>
<InputTag
type="date"
value={fundingData.endDate}
// onChange={(e) => {
// setFundingData({ ...fundingData, endDate: e.target.value });
// }}
h="40px"
w="97%"
pl="10px"
pt="10px"
/>
</FundingDiv>
{/* 펀딩 안내 문구 */}
<TogetherDiv>
<P pl="130px" fs="14px" fw="800">
펀딩 금액은 계좌로 전달돼요
</P>
<P pl="95px" fs="14px" fw="800">
펀딩에 성공하면 카톡으로 알림이 가요
</P>
</TogetherDiv>
{/* 펀딩 변경하기 및 펀딩 종료하기 버튼 */}
<Button
onClick={handlefundingModifyClick}
w="442px"
h="60px"
mt="10px"
color="white"
fs="19px"
bc="gray"
>
펀딩 변경하기
</Button>
<Button
onClick={handlecompleteFundingClick}
w="442px"
h="60px"
mt="10px"
color="white"
fs="19px"
bc="orange"
>
펀딩 종료하기
</Button>
<Button
onClick={handledeleteFundingClick}
w="442px"
h="60px"
mt="10px"
color="white"
fs="19px"
bc="red"
>
펀딩 삭제하기
</Button>
</Body>
</RightContainer>
</MainContainer>
);
};
export default FundingModify;
import React from 'react';
import { useNavigate } from 'react-router-dom';
import {
MainContainer,
LeftContainer,
Logo,
P,
Button,
RightContainer,
SponserMoney,
InputTag,
Body,
FundingDiv,
SponserDiv,
SponserComment,
SponsorImg,
TogetherDiv,
} from './FundingPayStyles';
import CheckBox from '../FundingPay/CheckBox/CheckBox';
import KakaoPay from './KakaoPay/KakaoPay';
const FundingPay = () => {
const navigate = useNavigate();
return (
<MainContainer>
<LeftContainer>
<Logo>😉 Giftipie</Logo>
<P pt="25px" fs="16px" fw="800" pb="5px">
기프티파이에서
</P>
<P fs="16px" fw="800" pb="5px">
정말 원하는 선물을
</P>
<P fs="16px" fw="800">
주고 받아요
</P>
<Button onClick={() => navigate('/')} mt="20px" w="180px" h="50px" fs="16px" color="white" bc="orange">
펀딩 시작하기
</Button>
</LeftContainer>
<RightContainer>
<Body>
<FundingDiv>
<SponserMoney>
<SponsorImg src="/imgs/junjihyun.jpg" alt="logo" />
<P pt="10px" fs="16px" fw="800" pb="5px">
윤다인 님에게
</P>
<P fs="16px" fw="800" pb="5px">
5,000원
</P>
<P fs="16px" fw="800">
후원하기
</P>
</SponserMoney>
<P pt="20px" pb="20px" fs="16px" fw="900">
후원자
</P>
<SponserDiv>
<SponserComment mt="10px">
<P pl="10px" pb="5px" fs="13px" fw="800">
이름
</P>
<InputTag type="text" placeholder="남길 이름을 입력해주세요" h="40px" />
<P pl="10px" fs="10px" fw="800">
주최자에게 이름이 모두 공개되고, 후원자 목록에는 두번째 글자부터 *으로 표시됩니다.
예) 김 * *
</P>
</SponserComment>
</SponserDiv>
<P pt="10px" pl="10px" pb="5px" fs="13px" fw="800">
후원금
</P>
<InputTag type="text" placeholder="남길 이름을 입력해주세요" h="40px" />
<P pt="10px" pl="10px" pb="5px" fs="13px" fw="800">
메시지
</P>
<InputTag type="text" placeholder="남길 메시지를 입력해주세요" pb="50px" h="100px" />
<P pl="10px" fs="10px" fw="800">
현재는 테스트 기간으로, 실제 결제가 이루어지지 않습니다. 대신 1명이 참여할 때마다 개설자에게
1,000원이 적립됩니다.
</P>
</FundingDiv>
<CheckBox />
<TogetherDiv pt="10px" bc="orange">
<P pl="140px" fs="14px" fw="800">
<br />
지금 선물하면 3등이에요!
<br />
</P>
</TogetherDiv>
<KakaoPay />
</Body>
</RightContainer>
</MainContainer>
);
};
export default FundingPay;
import React, { useEffect } from "react"; // React와 useEffect 훅을 React 라이브러리에서 불러옵니다.
import axios from "axios"; // HTTP 요청을 보내기 위해 axios를 가져옵니다.
import { Button, KakaoPayLogo } from "./KakaoPayStyles";
const KakaoPay = () => {
// KakaoPay라는 함수형 컴포넌트를 정의합니다.
const sendAmountToServer = async (amount) => {
// 서버로 금액을 전송하는 비동기 함수를 정의합니다.
const accessToken = localStorage.getItem("accessToken"); // localStorage에서 accessToken을 가져옵니다.
try {
const response = await axios.post(
"REACT_APP_API_URL",
{
// 서버로 POST 요청을 보냅니다.
point: amount, // 요청 본문에 금액을 담습니다.
},
{
headers: {
Authorization: accessToken, // 요청 헤더에 accessToken을 포함시킵니다.
},
}
);
if (response.status === 200) {
// 요청이 성공했는지 확인합니다.
alert("서버로 amount 전송 완료"); // 서버로의 전송이 성공한 경우 알림창을 띄웁니다.
} else {
alert("서버로 amount 전송 실패"); // 서버로의 전송이 실패한 경우 알림창을 띄웁니다.
}
} catch (error) {
console.error("서버로 amount 전송 중 오류:", error); // 전송 중 오류가 발생한 경우 콘솔에 오류를 기록합니다.
alert("서버로 amount 전송 중 오류 발생"); // 전송 중 오류가 발생한 경우 알림창을 띄웁니다.
}
};
const requestPay = () => {
// 결제 요청을 처리하는 함수를 정의합니다.
const { IMP } = window; // window 객체에서 IMP 객체를 가져옵니다.
IMP.init("imp82385247"); // IMP 객체를 초기화합니다.
IMP.request_pay(
{
// IMP 객체를 사용하여 결제 요청을 보냅니다.
pg: "kakaopay.TC0ONETIME", // 결제 수단을 설정합니다.
pay_method: "card", // 결제 방법을 설정합니다.
merchant_uid: new Date().getTime(), // 고유한 상점 ID를 생성합니다.
name: "Giftipie후원금", // 상품 이름을 설정합니다.
amount: 10000, // 결제 금액을 설정합니다.
buyer_email: "test@naver.com", // 구매자 이메일을 설정합니다.
buyer_name: "코드쿡", // 구매자 이름을 설정합니다.
buyer_tel: "010-1234-5678", // 구매자 전화번호를 설정합니다.
// buyer_addr: '서울특별시', // 구매자 주소를 설정합니다.
// buyer_postcode: '123-456', // 구매자 우편번호를 설정합니다.
},
async (rsp) => {
// 결제 요청 후 콜백 함수를 처리합니다.
try {
const { data } = await axios.post("REACT_APP_API_URL" + rsp.imp_uid); // 서버에서 결제 검증을 수행합니다.
if (rsp.paid_amount === data.response.amount) {
// 결제 금액이 서버 응답과 일치하는지 확인합니다.
alert("결제 성공"); // 결제가 성공한 경우 알림창을 띄웁니다.
sendAmountToServer(rsp.paid_amount); // 결제된 금액을 서버로 전송합니다.
} else {
alert("결제 실패"); // 결제가 실패한 경우 알림창을 띄웁니다.
}
} catch (error) {
console.error("결제 검증 중 오류:", error); // 결제 검증 중 오류가 발생한 경우 콘솔에 오류를 기록합니다.
alert("결제 실패"); // 결제 검증 중 오류가 발생한 경우 알림창을 띄웁니다.
}
}
);
};
useEffect(() => {
// 외부 스크립트를 로드하기 위해 useEffect 훅을 사용합니다.
const jquery = document.createElement("script"); // jQuery 스크립트 엘리먼트를 생성합니다.
jquery.src = "http://code.jquery.com/jquery-1.12.4.min.js"; // jQuery 스크립트의 소스를 설정합니다.
const iamport = document.createElement("script"); // Iamport 스크립트 엘리먼트를 생성합니다.
iamport.src = "http://cdn.iamport.kr/js/iamport.payment-1.1.7.js"; // Iamport 스크립트의 소스를 설정합니다.
document.head.appendChild(jquery); // jQuery 스크립트를 문서 헤드에 추가합니다.
document.head.appendChild(iamport); // Iamport 스크립트를 문서 헤드에 추가합니다.
return () => {
// 컴포넌트가 언마운트될 때 추가한 스크립트를 제거하기 위한 클린업 함수를 반환합니다.
document.head.removeChild(jquery); // 문서 헤드에서 jQuery 스크립트를 제거합니다.
document.head.removeChild(iamport); // 문서 헤드에서 Iamport 스크립트를 제거합니다.
};
}, []); // useEffect가 초기 렌더링 후 한 번만 실행되도록 빈 의존성 배열을 전달합니다.
return (
<div>
{/* <button onClick={requestPay}>결제하기</button> */}
<Button onClick={requestPay}>
<KakaoPayLogo src="/imgs/kakaopay.png" alt="image" />
</Button>
</div>
);
};
export default KakaoPay; // Payment 컴포넌트를 내보냅니다.