본문 바로가기

Web_Project

생성-수정-결제페이지

import React, { useState } from 'react';
import { HiBell } from 'react-icons/hi';
import { BsPersonCircle } from 'react-icons/bs'; // 로그인
import { FaAngleLeft } from 'react-icons/fa6';
// import { RiLogoutBoxRLine } from "react-icons/ri";
import { IoLogOutSharp } from "react-icons/io5"; // 로그아웃
import { useNavigate } from 'react-router-dom';
import {
    NavbarIconContainer,
    NavbarNotificationIconDiv,
    NavbarIconDiv,
    NavbarBtnDiv,
    NavbarBtn,
    LoginIcon,
    LogoIcon,
    LogoTextIcon,
    LogoDiv,
} from '../pages/Home/HomeStyles';
import { NotificationContainer, NotificationDiv, NotificationItem } from '../pages/Home/NotificationStyles';

const Navbar = ({ isLoggedIn, handleLoginClick, handleLogoutClick }) => {
    const navigate = useNavigate();
    const [isNotificationOpen, setIsNotificationOpen] = useState(false);
    const [notifications, setNotifications] = useState([
        { id: 1, text: '알림1' },
        { id: 32, text: '알림2' },
        { id: 42, text: '알림3' },
        { id: 55, text: '알림4' },
        { id: 53, text: '알림5' },
        { id: 52, text: '알림6' },
        { id: 51, text: '알림7' },
    ]);
    // 선택된 알림 아이디를 저장하는 상태
    const [selectedNotifications, setSelectedNotifications] = useState([]);

    const toggleNotification = () => {
        setIsNotificationOpen(!isNotificationOpen);
    };

    const handleNotificationClick = (notification) => {
        console.log(`알림 클릭: ${notification.id}`);
        navigate(`/fundingdetail/${notification.id}`);
    };

    const handleNavbarIconClick = () => navigate('/');

    // 체크박스의 상태를 변경하는 함수
    const handleCheckboxChange = (notificationId) => {
        setSelectedNotifications((prevSelected) =>
            prevSelected.includes(notificationId)
                ? prevSelected.filter((id) => id !== notificationId)
                : [...prevSelected, notificationId]
        );
    };

    // 선택된 알림 삭제를 처리하는 함수
    const handleDeleteSelected = () => {
        setNotifications((prev) => prev.filter((notification) => !selectedNotifications.includes(notification.id)));
        // 선택한 알림 초기화
        setSelectedNotifications([]);
    };

    // 전체 알림 삭제를 처리하는 함수
    const handleDeleteAll = () => {
        setNotifications([]);
        setSelectedNotifications([]);
        toggleNotification();
    };

    // 상태에 따라 Navbar에 표시될 아이콘 결정
    const navbarContents = isLoggedIn ? (
        <>
            <NavbarIconContainer>
                {/* HiBell 아이콘 클릭 시 알림 리스트 표시 */}
                <NavbarNotificationIconDiv onClick={toggleNotification}>
                    <HiBell size="" color="#FFFFFF"/>
                </NavbarNotificationIconDiv>
                {isNotificationOpen && (
                    <NotificationContainer>
                        <NotificationDiv>
                            <button onClick={toggleNotification}>
                                <FaAngleLeft size="" color='white'/>
                            </button>
                            <div>
                                <button onClick={handleDeleteSelected}>선택삭제</button>
                                <button onClick={handleDeleteAll}>전체삭제</button>
                            </div>
                        </NotificationDiv>
                        {notifications.map((notification) => (
                            <NotificationItem
                                key={notification.id}
                                onClick={() => handleNotificationClick(notification)}
                            >
                                {notification.text}
                                <input
                                    type="checkbox"
                                    checked={selectedNotifications.includes(notification.id)}
                                    onChange={() => handleCheckboxChange(notification.id)}
                                />
                            </NotificationItem>
                        ))}
                    </NotificationContainer>
                )}
                <NavbarIconDiv>
                    <BsPersonCircle color="#FFFFFF"/>
                </NavbarIconDiv>
                <NavbarBtn onClick={handleLogoutClick} fs="13px" fw="600">
                    {/* <RiLogoutBoxRLine /> */}
                    <IoLogOutSharp size="28px" color="#FFFFFF"/>
                </NavbarBtn>
            </NavbarIconContainer>
        </>
    ) : (
        <>
            <NavbarBtn onClick={handleLoginClick} pt="10px" fs="20px" fw="600">
            <BsPersonCircle color="#FFFFFF"/>
            </NavbarBtn>
        </>
    );

    return (
        <>
            <NavbarBtn onClick={handleNavbarIconClick} fs="20px" fw="600" pl="25px" color="white">
                <LogoDiv>
                    <LogoIcon src="/imgs/Icon/Frame 7413.png" />
                    <LogoTextIcon src="/imgs/Logo/Giftipie.png" />           
                    
                </LogoDiv>
            </NavbarBtn>
            <NavbarBtnDiv>{navbarContents}</NavbarBtnDiv>
        </>
    );
};

export default Navbar;
/* 네브바 버튼 */
export const NavbarBtn = styled.button`
  font-size: ${(props) => props.fs};
  font-weight: ${(props) => props.fw};
  padding-top: ${(props) => props.pt};
  padding-left: ${(props) => props.pl};
  padding-right: ${(props) => props.pr};
`;

export const NavbarIconContainer = styled.div`
  display: flex;
  align-items: center;
  gap: 10px;
`;

export const NavbarNotificationIconDiv = styled.div`
  font-size: 24px;
  cursor: pointer;
`;

export const NavbarIconDiv = styled.div`
  font-size: 24px;
  cursor: pointer;
`;

export const LoginIcon = styled.img`
  width: 25px;
  height: 25px;
  margin-top: 20px;
`;

export const LogoDiv = styled.div`
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
`

export const LogoIcon = styled.img`
  height: 35px;
  margin-top: 10px;
`;

export const LogoTextIcon = styled.img`
  height: 25px;
  margin-left: 10px;
  margin-top: 10px;
`;
import React, { useState } from "react";
import { useNavigate } from "react-router-dom";
import { fundingCreate } from "../../../apis/funding"; // 펀딩 생성 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>
      </LeftContainer>

      <RightContainer>
        {/* 추가된 코드 */}
        <NavbarDiv>
          <Navbar
            isLoggedIn={isLoggedIn}
            handleLogoutClick={handleLogoutClick}
          />
        </NavbarDiv>

        <Body>
          <form
            onSubmit={(e) => {
              e.preventDefault();
            }}
          >
            <FundingDiv>
              <P pb="10px" fs="16px" fw="900" color="#FF7C7C">
                펀딩 생성페이지
              </P>
              <P pb="20px" fs="10px" fw="900" color="#E4E4E4">
                펀딩 생성 페이지에 상품명과 이미지가 노출돼요.
              </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" color="#FF7C7C">
                    펀딩 내용
                  </P>
                  <P pb="20px" fs="13px" fw="900" color="#E4E4E4">
                    공개 방식을 설정해주세요.
                  </P>
                  <SponserDiv>
                    <RadioInput
                      value="true"
                      checked={createData.publicFlag === true}
                      onChange={handlePublicFlagChange}
                      type="radio"
                      mb="21px"
                    />
                    <P pb="20px" fs="13px" fw="900" pl="20px" color="#E4E4E4">
                      공개
                    </P>
                    <P pb="20px" fs="10px" fw="900" pl="42px" color="#E4E4E4">
                      누구나 볼 수 있어요
                    </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" color="#E4E4E4">
                      비공개
                    </P>
                    <P pb="20px" fs="10px" fw="900" pl="30px" color="#E4E4E4">
                      링크를 통해서만 방문할 수 있어요
                    </P>
                  </SponserDiv>
                </SponserComment>
              </SponserDiv>
              <P pt="30px" pb="5px" fs="13px" fw="800" color="#E4E4E4">
                보여줄 이름
              </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" color="#E4E4E4">
                제목
              </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" color="#E4E4E4">
                본문
              </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" color="#E4E4E4">
                마감일 설정
              </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" color="#FFE6C1">
                펀딩 금액은 계좌로 전달돼요
              </P>
              <P pl="95px" fs="14px" fw="800" color="#FFE6C1">
                펀딩에 성공하면 카톡으로 알림이 가요
              </P>
            </TogetherDiv>

            <Button
              onClick={handleFundingClick}
              w="442px"
              h="60px"
              mt="10px"
              color="white"
              fs="19px"
              bc="#FF7C7C"
            >
              펀딩 등록하기
            </Button>
          </form>
        </Body>
      </RightContainer>
    </MainContainer>
  );
};

export default FundingCreate;
import styled from "styled-components";

// 전체 컨테이너
export const MainContainer = styled.div`
  display: flex;
  justify-content: center;
  max-width: 1200px;
  min-height: 100vh;
  margin: 0 auto;
  flex-wrap: wrap;
`;

// 왼쪽 컨테이너
export const LeftContainer = styled.div`
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  width: 500px;
  height: 100vh;
  padding: 20px;
  /* border: 1px solid lightgray; */
  border-radius: 8px;
  margin-right: 100px;

  @media (max-width: 1024px) {
    display: none;
  }
`;

export const Logo = styled.h1`
  font-size: 24px;
  font-weight: 800;
`;

export const P = styled.p`
  padding-top: ${(props) => props.pt};
  padding-bottom: ${(props) => props.pb};
  padding-left: ${(props) => props.pl};
  padding-right: ${(props) => props.pr};
  font-size: ${(props) => props.fs};
  font-weight: ${(props) => props.fw};
  color: ${(props) => props.color};
  align-items: center;
`;

export const Button = styled.button`
  justify-content: center;
  align-items: center;
  width: ${(props) => props.w};
  height: ${(props) => props.h};
  padding: 10px;
  background-color: ${(props) => props.bc};
  border-radius: 7px;
  color: ${(props) => props.color};
  font-size: ${(props) => props.fs};
  font-weight: 600;
  margin-top: ${(props) => props.mt};
  margin-bottom: ${(props) => props.mb};
  padding-left: ${(props) => props.pl};
  padding-right: ${(props) => props.pr};
  &:hover {
    color: white;
    background-color: black;
    cursor: pointer;
  }
`;

// 오른쪽 컨테이너
export const RightContainer = styled.div`
  position: relative;
  width: 442px;
  border: 1px solid lightgray;
  border-radius: 20px;
  height: 100vh;
  overflow-y: scroll;

  &::-webkit-scrollbar {
    display: none;
  }

  @media (max-width: 442px) {
    width: 100%;
  }
`;

// 네브바 영역
export const NavbarDiv = styled.div`
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: center;
  height: 70px;
`;

// 바디 영역
export const Body = styled.div`
  font-size: 24px;
  font-weight: 800;
  height: auto;
`;

export const FundingDiv = styled.div`
  justify-content: center;
  width: 100%;
  max-width: 442px;
  height: auto;
  padding: 30px;
`;

export const ProducImgtDiv = styled.div`
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: center;
`;
export const SponserDiv = styled.div`
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: center;
`;

export const SponserComment = styled.div`
  margin-top: ${(props) => props.mt};
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: start;
`;

export const FundingImg = styled.img`
  width: ${(props) => props.w};
  height: ${(props) => props.h};
  padding-left: ${(props) => props.pl};
  border-radius: 4px;
  border: none;
  background-color: #eae7de;
  font-weight: 500;
  font-size: 12px;
  justify-content: start;
  align-items: start;
  &:hover {
    cursor: pointer;
    }
`;

export const SponsorComment = styled.div`
  margin-top: ${(props) => props.mt};
  border: none;
  display: flex;
  flex-direction: column;
  justify-content: start;
  align-items: start;
  &:hover {
    cursor: ${(props) => props.pointer};
  }
`;

export const ImgText = styled.h1`
  /* position: absolute; 핵심코드 */
  top: 47%; // 핵심코드
  right: 25%; //핵심코드 */
  transform: translate( 10%, -140%); // 핵심코드
  color: gray;
`;

export const InputTag = styled.input`
  width: ${(props) => props.w};
  height: ${(props) => props.h};
  background-color: #eae7de;
  border-radius: 4px;
  border: none;
  margin-left: ${(props) => props.ml};
  margin-bottom: ${(props) => props.mb};
  padding-left: ${(props) => props.pl};
  padding-top: ${(props) => props.pt};
  padding-bottom: ${(props) => props.pb};
  font-weight: 500;
  font-size: 12px;
  justify-content: start;
  align-items: start;
`;

export const RadioInput = styled.input`
  margin-bottom: ${(props) => props.mb};
  accent-color: black;
`;

export const FundingNewline = styled.div`
  width: 100%;
  height: 12px;
`;

export const TogetherDiv = styled.div`
  margin-top: 30px;
  width: 442px;
  height: 45px;
  background-color: ${(props) => props.bc};
  color: ${(props) => props.color};
  justify-content: center;
  align-items: center;
`;
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,
  deleteFundingModify,
  FundingModifyGet,
  completeFundingModify,
} from "../../../apis/funding"; // 펀딩 수정 API, 펀딩 상세 정보 API
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>
        {/* 펀딩 시작하기 버튼 */}
      </LeftContainer>

      {/* 오른쪽 컨테이너 */}
      <RightContainer>
        {/* 추가된 코드 */}
        <NavbarDiv>
          <Navbar
            isLoggedIn={isLoggedIn}
            handleLogoutClick={handleLogoutClick}
          />
        </NavbarDiv>
        {/* 펀딩 수정 내용 입력 부분 */}
        <Body>
          {/* 펀딩 상품 정보 입력 및 이미지 변경 */}
          <FundingDiv>
            {/* 펀딩 페이지에 노출되는 상품명 및 이미지 변경 버튼 */}
            <P pb="10px" fs="16px" fw="900" color="#FF7C7C">
              펀딩 수정페이지
            </P>
            <P pb="20px" fs="10px" fw="900" color="#E4E4E4">
              펀딩페이지의 상품명, 가격, 이미지는 변경할 수 없습니다.
            </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" color="#FF7C7C">
                  펀딩 내용
                </P>
                <P pb="20px" fs="13px" fw="900" color="#E4E4E4">
                  공개 방식을 설정해주세요.
                </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" color="#E4E4E4">
                    공개
                  </P>
                  <P pb="20px" fs="10px" fw="900" pl="42px" color="#E4E4E4">
                    누구나 볼 수 있어요
                  </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" color="#E4E4E4">
                    비공개
                  </P>
                  <P pb="20px" fs="10px" fw="900" pl="30px" color="#E4E4E4">
                    링크를 통해서만 방문할 수 있어요
                  </P>
                </SponsorDiv>
              </SponsorComment>
            </SponsorDiv>
            <P pt="30px" pb="5px" fs="13px" fw="800" color="#E4E4E4">
              이름
            </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" color="#E4E4E4">
              제목
            </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" color="#E4E4E4">
              본문
            </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" color="#E4E4E4">
              마감일 설정
            </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" color="#FFE6C1">
              펀딩 금액은 계좌로 전달돼요
            </P>
            <P pl="95px" fs="14px" fw="800" color="#FFE6C1">
              펀딩에 성공하면 카톡으로 알림이 가요
            </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="#5F5F5F"
            fs="19px"
            bc="#FFE6C1"
          >
            펀딩 종료하기
          </Button>
          <Button
            onClick={handledeleteFundingClick}
            w="442px"
            h="60px"
            mt="10px"
            color="white"
            fs="19px"
            bc="#FF7C7C"
          >
            펀딩 삭제하기
          </Button>
        </Body>
      </RightContainer>
    </MainContainer>
  );
};

export default FundingModify;
import styled from "styled-components";

// 전체 컨테이너
export const MainContainer = styled.div`
  display: flex;
  justify-content: center;
  max-width: 1200px;
  min-height: 100vh;
  margin: 0 auto;
  flex-wrap: wrap;
`;

// 왼쪽 컨테이너
export const LeftContainer = styled.div`
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  width: 500px;
  height: 100vh;
  padding: 20px;  
  margin-right: 100px;

  @media (max-width: 1024px) {
    display: none;
  }
`;

export const Logo = styled.h1`
  font-size: 24px;
  font-weight: 800;
`;

export const P = styled.p`
  padding-top: ${(props) => props.pt};
  padding-bottom: ${(props) => props.pb};
  padding-left: ${(props) => props.pl};
  padding-right: ${(props) => props.pr};
  font-size: ${(props) => props.fs};
  font-weight: ${(props) => props.fw};
  color: ${(props) => props.color};
  align-items: center;
`;

export const Button = styled.button`
  justify-content: center;
  align-items: center;
  width: ${(props) => props.w};
  height: ${(props) => props.h};
  padding: ${(props) => props.p};
  background-color: ${(props) => props.bc};
  border-radius: 7px;
  color: ${(props) => props.color};
  font-size: ${(props) => props.fs};
  font-weight: 600;
  margin-top: ${(props) => props.mt};
  margin-bottom: ${(props) => props.mb};
  padding-left: ${(props) => props.pl};
  padding-right: ${(props) => props.pr};
  &:hover {
    color: white;
    background-color: black;
    cursor: pointer;
  }
`;

// 오른쪽 컨테이너
export const RightContainer = styled.div`
  position: relative;
  width: 442px;
  border: 1px solid lightgray;
  border-radius: 20px;
  height: 100vh;
  overflow-y: scroll;

  &::-webkit-scrollbar {
    display: none;
  }

  @media (max-width: 442px) {
    width: 100%;
  }
`;

// 네브바 영역
export const NavbarDiv = styled.div`
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: center;
  height: 70px;
`;

// 바디 영역
export const Body = styled.div`
  font-size: 24px;
  font-weight: 800;
  height: auto;
`;

export const FundingDiv = styled.div`
  justify-content: center;
  width: 100%;
  max-width: 442px;
  height: auto;
  padding: 30px;
`;

export const SponsorDiv = styled.div`
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: center;
`;

export const SponsorComment = styled.div`
  margin-top: ${(props) => props.mt};
  display: flex;
  flex-direction: column;
  justify-content: start;
  align-items: start;
  &:hover {
    cursor: ${(props) => props.pointer};
  }
`;

export const FundingProductImg = styled.img`
  width: 90px;
  height: 90px;
  border-radius: 4px;
  margin-top: 10px;
`;

export const FundingImg = styled.img`
  max-width: 90px;
  max-height: 100px;
  width: 100%;
  height: 100%;
  border-radius: 8px;
  margin-top: 10px;
`;
export const ImgText = styled.h1`
  /* position: absolute; 핵심코드 */
  top: 47%; // 핵심코드
  right: 25%; //핵심코드 */
  transform: translate(15%, -130%); // 핵심코드
  color: gray;
`;
export const InputTag = styled.input`
  width: ${(props) => props.w};
  height: ${(props) => props.h};
  background-color: #eae7de;
  border-radius: 4px;
  border: none;
  margin-left: ${(props) => props.ml};
  margin-bottom: ${(props) => props.mb};
  padding-left: ${(props) => props.pl};
  padding-top: ${(props) => props.pt};
  padding-bottom: ${(props) => props.pb};
  font-weight: 500;
  font-size: 12px;
  justify-content: start;
  align-items: start;
`;
export const RadioInput = styled.input`
  margin-bottom: ${(props) => props.mb};
  accent-color: black;
`;

export const FundingNewline = styled.div`
  width: 100%;
  height: 12px;
`;

export const TogetherDiv = styled.div`
  margin-top: 30px;
  width: 442px;
  height: 45px;
  background-color: ${(props) => props.bc};
  color: ${(props) => props.color};
  justify-content: center;
  align-items: center;
`;
import React, { useState, useEffect } from "react";
import { useNavigate, useParams, useLocation } from "react-router-dom";
import CheckBox from "../FundingPay/CheckBox/CheckBox";
import { warnToast } from "../../../components/toast";
import { useDispatch, useSelector } from "react-redux"; // 추가된 코드
import { userLogout } from "../../../redux/authSlice"; // 추가된 코드
import Navbar from "../../../components/Navbar"; // 추가된 코드
import {
  fundingPayDonationReady,
  getDonationApproval,
  getFundingDonation,
} from "../../../apis/funding";
import {
  MainContainer,
  LeftContainer,
  Logo,
  P,
  Button,
  NavbarDiv,
  RightContainer,
  SponserMoney,
  InputTag,
  Body,
  FundingDiv,
  SponserDiv,
  SponserComment,
  SponsorImg,
  TogetherDiv,
  KakaoButton,
  KakaoPayLogo,
} from "./FundingPayStyles";

const FundingPay = () => {
  const navigate = useNavigate();
  const { id } = useParams();
  const location = useLocation();
  const isLoggedIn = useSelector((state) => state.auth.isLoggedIn); // 추가된 코드
  const dispatch = useDispatch(); // 추가된 코드

  // 후원자 정보 및 펀딩 정보를 관리할 상태 변수들을 설정
  const [sponsorDonation, setSponsorDonation] = useState({
    showName: "",
    donation: "",
    donationRanking: "",
    sponsorNickname: "",
    sponsorComment: "",
  });

  // useEffect를 이용하여 URL 매개변수에서 donation, showName 값을 가져오는 부분 합침
  useEffect(() => {
    const fetchData = async () => {
      try {
        if (!id) {
          return;
        }
        const params = new URLSearchParams(location.search);
        const donation = params.get("donation");
        const showName = params.get("showName");

        // 특정 펀딩의 상세 정보를 가져오기
        const response = await getFundingDonation(id);

        // 후원자 정보 업데이트
        setSponsorDonation((prev) => ({
          ...prev,
          donation: donation ? parseInt(donation) : "",
          showName: showName || prev.showName,
          donationRanking: response.result.donationRanking,
        }));

        console.log("펀딩 랭킹 가져오기:", response);
      } catch (error) {
        console.error("결제 오류:", error);
      }
    };

    // 컴포넌트가 마운트될 때와 id가 변경될 때 API 호출 함수 실행
    fetchData();
  }, [id, location.search]);

  const handleFundingDonationClick = async () => {
    try {
      if (
        sponsorDonation.sponsorNickname === "" ||
        sponsorDonation.sponsorComment === ""
      ) {
        warnToast("내용을 입력해주세요");
        return;
      }

      // 결제 준비 API
      const response = await fundingPayDonationReady({
        id,
        sponsorNickname: sponsorDonation.sponsorNickname,
        sponsorComment: sponsorDonation.sponsorComment,
        donation: sponsorDonation.donation,
      });

      // 리다이렉션을 원하면
      window.location.href = response.result.next_redirect_pc_url;
    } catch (error) {
      console.error("결제 준비 오류:", error);
    }
  };

  // 결제 승인 API
  useEffect(() => {
    const fetchData = async () => {
      try {
        // 여기에서 window.location.href 사용하면서 변경된 부분
        console.log("API 호출 전");
        const response = await getDonationApproval();
        console.log("페이지에서 결제 승인 성공: ", response);
        // 여기에서 상태를 업데이트하거나 다른 작업을 수행할 수 있습니다.
      } catch (error) {
        console.error("페이지에서 결제 승인 오류:", error);
        // 오류가 발생한 경우에 대한 처리를 여기에 추가할 수 있습니다.
      }
    };

    // 여기에서도 window.location.href를 사용하면서 변경된 부분
    if (window.location.href.includes("redirected=true")) {
      console.log("fetchData 호출 전");
      fetchData();
    } else {
      console.error(
        "에러: pgToken이 비어있습니다. 결제 승인이 처리되지 않습니다."
      );
    }
  }, []);

  // 추가된 코드
  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="#2C2C2C"
          bc="#FF7C7C"
        >
          Home
        </Button>
      </LeftContainer>

      <RightContainer>
        {/* 추가된 코드 */}
        <NavbarDiv>
          <Navbar
            isLoggedIn={isLoggedIn}
            handleLogoutClick={handleLogoutClick}
          />
        </NavbarDiv>
        <Body>
          <FundingDiv>
            <SponserMoney>
              {/* <SponsorImg src="/imgs/junjihyun.jpg" alt="logo" /> */}
              <P pt="10px" fs="16px" fw="800" pb="5px" color="#FFFFFF">
                {sponsorDonation.showName} 님에게
              </P>
              <P fs="16px" fw="800" pb="5px" color="#FFFFFF">
                {sponsorDonation.donation}원
              </P>
              <P fs="16px" fw="800" color="#FFFFFF">
                후원하기
              </P>
            </SponserMoney>
            <P pt="20px" pb="20px" fs="16px" fw="900" color="#FFFFFF">
              후원자
            </P>
            <SponserDiv>
              <SponserComment mt="10px">
                <P pl="10px" pb="5px" fs="13px" fw="800" color="#FFFFFF">
                  이름
                </P>
                <InputTag
                  type="text"
                  placeholder="남길 이름을 입력해주세요"
                  value={sponsorDonation.sponsorNickname}
                  onChange={(e) => {
                    setSponsorDonation({
                      ...sponsorDonation,
                      sponsorNickname: e.target.value,
                    });
                  }}
                  h="40px"
                />
                <P pl="10px" fs="10px" fw="800" color="#FFFFFF">
                  주최자에게 이름이 모두 공개되고, 후원자 목록에는 두번째
                  글자부터 *으로 표시됩니다. 예) 김 * *
                </P>
              </SponserComment>
            </SponserDiv>
            <P pt="10px" pl="10px" pb="5px" fs="13px" fw="800" color="#FFFFFF">
              메시지
            </P>
            <InputTag
              type="text"
              placeholder="남길 메시지를 입력해주세요"
              value={sponsorDonation.sponsorComment}
              onChange={(e) => {
                setSponsorDonation({
                  ...sponsorDonation,
                  sponsorComment: e.target.value,
                });
              }}
              pb="50px"
              h="100px"
            />
            <P pl="10px" fs="10px" fw="800" color="#FFFFFF">
              현재는 테스트 기간으로, 실제 결제가 이루어지지 않습니다. 대신
              1명이 참여할 때마다 개설자에게 1,000원이 적립됩니다.
            </P>
          </FundingDiv>
          <CheckBox />
          <TogetherDiv pt="10px">
            <P pl="140px" pt="11px" pb="19px" fs="14px" fw="800" bc="#FF7C7C">
              지금 선물하면 {sponsorDonation.donationRanking}등이에요!
            </P>
          </TogetherDiv>
          <KakaoButton onClick={handleFundingDonationClick}>
            <KakaoPayLogo src="/imgs/Logo/kakaopay.png" alt="image"/>
          </KakaoButton>
        </Body>
      </RightContainer>
    </MainContainer>
  );
};

export default FundingPay;
import styled from "styled-components";

// 전체 컨테이너
export const MainContainer = styled.div`
  display: flex;
  justify-content: center;
  max-width: 1200px;
  min-height: 100vh;
  margin: 0 auto;
  flex-wrap: wrap;
`;

// 왼쪽 컨테이너
export const LeftContainer = styled.div`
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  width: 500px;
  height: 100vh;
  padding: 20px;
  margin-right: 100px;

  @media (max-width: 1024px) {
    display: none;
  }
`;

export const Logo = styled.h1`
  font-size: 24px;
  font-weight: 800;
`;

export const P = styled.p`
  padding-top: ${(props) => props.pt};
  padding-bottom: ${(props) => props.pb};
  padding-left: ${(props) => props.pl};
  padding-right: ${(props) => props.pr};
  font-size: ${(props) => props.fs};
  font-weight: ${(props) => props.fw};
  color: ${(props) => props.color};
  background-color: ${(props) => props.bc};
  align-items: center;
`;

export const Button = styled.button`
  justify-content: center;
  align-items: center;
  width: ${(props) => props.w};
  height: ${(props) => props.h};
  background-color: ${(props) => props.bc};
  border-radius: 7px;
  color: ${(props) => props.color};
  font-size: ${(props) => props.fs};
  font-weight: 600;
  margin-top: ${(props) => props.mt};
  margin-bottom: ${(props) => props.mb};
  padding-left: ${(props) => props.pl};
  padding-right: ${(props) => props.pr};
  &:hover {
    color: white;
    background-color: black;
    cursor: pointer;
  }
`;

// 네브바 영역
export const NavbarDiv = styled.div`
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: center;
  height: 70px;
`;

// 오른쪽 컨테이너
export const RightContainer = styled.div`
  position: relative;
  width: 442px;
  border: 1px solid lightgray;
  border-radius: 20px;
  height: 100vh;
  overflow-y: scroll;

  &::-webkit-scrollbar {
    display: none;
  }

  @media (max-width: 442px) {
    width: 100%;
  }
`;

// 바디 영역
export const Body = styled.div`
  font-size: 24px;
  font-weight: 800;
  height: 2100px;
`;

export const FundingDiv = styled.div`
  justify-content: center;
  width: 100%;
  max-width: 442px;
  height: auto;
  padding: 30px;
`;

export const SponserDiv = styled.div`
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 15px;
`;

export const SponserMoney = styled.div`
  margin-top: ${(props) => props.mt};
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
`;

export const SponserComment = styled.div`
  margin-top: ${(props) => props.mt};
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: start;
`;

export const SponsorImg = styled.img`
  width: 60px;
  height: 60px;
  border-radius: 100px;
  margin-top: 10px;
`;

export const InputTag = styled.input`
  width: 98%;
  height: ${(props) => props.h};
  background-color: #eae7de;
  border-radius: 4px;
  border: none;
  margin-left: 10px;
  margin-bottom: 10px;
  padding-left: 10px;
  padding-bottom: ${(props) => props.pb};
  font-weight: 500;
  font-size: 11px;
  justify-content: start;
  align-items: start;
`;

export const FundingNewline = styled.div`
  width: 100%;
  height: 12px;
`;

export const TogetherDiv = styled.div`
  margin-top: 30px;
  width: 442px;
  height: 45px;
  background-color: ${(props) => props.bc};
  color: ${(props) => props.color};
`;
export const KakaoButton = styled.button`
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 60px;
  background-color: #fae101;
  border-radius: 7px;
  font-size: 19px;
  font-weight: 600;
  margin-top: 14px;
  margin-bottom: ${(props) => props.mb};
  padding-left: ${(props) => props.pl};
  padding-right: ${(props) => props.pr};
  &:hover {
    background-color: #fae102;
    cursor: pointer;
  }
`;

export const KakaoPayLogo = styled.img`
  height: 35px;
  margin-top: 5px;
`;

'Web_Project' 카테고리의 다른 글

전체 데스크탑뷰 적용  (0) 2024.02.19
데스크탑뷰  (0) 2024.02.18
로그인모달창, 회원가입, 로그인  (1) 2024.02.16
메인페이지 webkit-scrollbar 적용  (0) 2024.02.16
카카오페이 API 연동  (0) 2024.02.14