diff --git a/src/Component/Button/DeleteButton.tsx b/src/Component/Button/DeleteButton.tsx index 0f20711..55de04a 100644 --- a/src/Component/Button/DeleteButton.tsx +++ b/src/Component/Button/DeleteButton.tsx @@ -2,24 +2,25 @@ import React from "react"; import "./DeleteButtonStyle.css"; interface AnimatedButtonProps { - onClick: () => void; + onClick: () => void; } const DeleteButton: React.FC = ({ onClick }) => { - return ( -
-
-
-
- ); + return ( +
+
+
+
+ ); }; export default DeleteButton; diff --git a/src/Component/Carousel/EmblaCarousel.tsx b/src/Component/Carousel/EmblaCarousel.tsx index d7142c6..67ec0cb 100644 --- a/src/Component/Carousel/EmblaCarousel.tsx +++ b/src/Component/Carousel/EmblaCarousel.tsx @@ -183,6 +183,7 @@ const EmblaCarousel: React.FC = ({ style={{ fontSize: "20px", color: "black", + wordWrap: "break-word", }} > ₩ {formatPrice(item.budget)} diff --git a/src/Component/Carousel/embla.css b/src/Component/Carousel/embla.css index 7293788..4d5ef7f 100644 --- a/src/Component/Carousel/embla.css +++ b/src/Component/Carousel/embla.css @@ -40,6 +40,7 @@ margin-top: 0.7rem; margin-bottom: 1rem; min-height: 75px; + word-wrap: break-word; } @media screen and (max-width: 768px) { @@ -48,6 +49,7 @@ margin-top: 0.7rem; margin-bottom: 1rem; min-height: 40px; + word-wrap: break-word; } } diff --git a/src/Component/List/Data/stockDataList.tsx b/src/Component/List/Data/stockDataList.tsx index 2da20a3..b4e5c1b 100644 --- a/src/Component/List/Data/stockDataList.tsx +++ b/src/Component/List/Data/stockDataList.tsx @@ -17,7 +17,7 @@ const ListWrapper = styled.div` const StockDataList: React.FC = ({ data }) => { return ( -
+
{data.map((item) => ( diff --git a/src/Component/List/MyStockItem.tsx b/src/Component/List/MyStockItem.tsx index 7affd3d..3a3325e 100644 --- a/src/Component/List/MyStockItem.tsx +++ b/src/Component/List/MyStockItem.tsx @@ -11,290 +11,284 @@ import { useNavigate } from "react-router-dom"; import { usePortfolioStore } from "../../store/usePortfolioStore"; const MyStockItem: React.FC = ({ - code, - name, - quantity, - average, - ror, - portfolioId, - logo, + code, + name, + quantity, + average, + ror, + portfolioId, + logo, }) => { - const [isFormOpen, setIsFormOpen] = useState(false); - const [isPlusOpen, setIsPlusOpen] = useState(false); - const [isDeleteOpen, setIsDeleteOpen] = useState(false); - const [selectedStock, setSelectedStock] = useState(null); + const [isFormOpen, setIsFormOpen] = useState(false); + const [isPlusOpen, setIsPlusOpen] = useState(false); + const [isDeleteOpen, setIsDeleteOpen] = useState(false); + const [selectedStock, setSelectedStock] = useState(null); - const [modalAction, setModalAction] = useState< - "edit" | "delete" | "plus" | null - >(null); - const navigate = useNavigate(); + const [modalAction, setModalAction] = useState< + "edit" | "delete" | "plus" | null + >(null); + const navigate = useNavigate(); - const addStockToStore = usePortfolioStore((state) => state.addStock); - const updateStockInStore = usePortfolioStore((state) => state.updateStock); - const deleteStockFromStore = usePortfolioStore( - (state) => state.deleteStock - ); - const calculateROR = usePortfolioStore((state) => state.calculateROR); - const setChange = usePortfolioStore((state) => state.setChange); - const change = usePortfolioStore((state) => state.change); + const addStockToStore = usePortfolioStore((state) => state.addStock); + const updateStockInStore = usePortfolioStore((state) => state.updateStock); + const deleteStockFromStore = usePortfolioStore((state) => state.deleteStock); + const calculateROR = usePortfolioStore((state) => state.calculateROR); + const setChange = usePortfolioStore((state) => state.setChange); + const change = usePortfolioStore((state) => state.change); - const userStocks: PlusProps[] = [ - { - code, - name, - quantity, - average, - portfolioId, - logo: logo || "default_logo", - }, - ]; - - const handleConfirm = (newQuantity: number, newPrice: number) => { - if (modalAction === "plus") { - axios - .patch( - `${process.env.REACT_APP_API_URL}/v1/portfolio/${portfolioId}/holding/${code}`, - { quantity: newQuantity, price: newPrice }, - { withCredentials: true } - ) - .then((res) => { - if (res.status === 200) { - const updatedStock: StockProps = { - code, - name, - quantity: newQuantity, - average: newPrice, - ror, - portfolioId, - logo, - }; - addStockToStore(updatedStock); - calculateROR(); - setChange(!change); - swal({ - title: "추가 등록완료!", - icon: "success", - }); - setIsPlusOpen(false); - setSelectedStock(null); - setModalAction(null); - } - }) - .catch((error) => { - swal({ - title: "등록에 실패하셨습니다.", - text: "다시 시도해주세요!", - icon: "error", - }); - setSelectedStock(null); - setModalAction(null); - }); - } else if (modalAction === "edit") { - axios - .put( - `${process.env.REACT_APP_API_URL}/v1/portfolio/${portfolioId}/holding/${code}`, - { quantity: newQuantity, price: newPrice }, - { withCredentials: true } - ) - .then((res) => { - if (res.status === 200) { - const oldStockValue = quantity * average; - const newStockValue = newQuantity * newPrice; - const valueDifference = newStockValue - oldStockValue; + const userStocks: PlusProps[] = [ + { + code, + name, + quantity, + average, + portfolioId, + logo: logo || "default_logo", + }, + ]; - const updatedStock: StockProps = { - code, - name, - quantity: newQuantity, - average: newPrice, - ror, - portfolioId, - logo, - }; - updateStockInStore(updatedStock); - calculateROR(); - setChange(!change); - swal({ - title: "수정 완료!", - icon: "success", - }); - setIsFormOpen(false); - } else if (res.status === 401) { - swal({ - title: "수정 실패하셨습니다.", - text: "다시 시도해주세요!", - icon: "error", - }).then(() => { - navigate("/login"); - }); - } - }) - .catch((error) => { - if (error.response && error.response.status === 401) { - swal({ - title: "Unauthorized", - text: "로그인을 다시 시도해주세요.", - icon: "warning", - }).then(() => { - navigate("/login"); - }); - } else { - swal({ - title: "Edit failed", - text: "다시 시도해주세요", - icon: "error", - }); - setSelectedStock(null); - setModalAction(null); - } - }); - } - }; + const handleConfirm = (newQuantity: number, newPrice: number) => { + if (modalAction === "plus") { + axios + .patch( + `${process.env.REACT_APP_API_URL}/v1/portfolio/${portfolioId}/holding/${code}`, + { quantity: newQuantity, price: newPrice }, + { withCredentials: true } + ) + .then((res) => { + if (res.status === 200) { + const updatedStock: StockProps = { + code, + name, + quantity: newQuantity, + average: newPrice, + ror, + portfolioId, + logo, + }; + addStockToStore(updatedStock); + calculateROR(); + setChange(!change); + swal({ + title: "추가 등록완료!", + icon: "success", + }); + setIsPlusOpen(false); + setSelectedStock(null); + setModalAction(null); + } + }) + .catch((error) => { + swal({ + title: "등록에 실패하셨습니다.", + text: "다시 시도해주세요!", + icon: "error", + }); + setSelectedStock(null); + setModalAction(null); + }); + } else if (modalAction === "edit") { + axios + .put( + `${process.env.REACT_APP_API_URL}/v1/portfolio/${portfolioId}/holding/${code}`, + { quantity: newQuantity, price: newPrice }, + { withCredentials: true } + ) + .then((res) => { + if (res.status === 200) { + const oldStockValue = quantity * average; + const newStockValue = newQuantity * newPrice; + const valueDifference = newStockValue - oldStockValue; - const deleteHandle = () => { - axios - .delete( - `${process.env.REACT_APP_API_URL}/v1/portfolio/${portfolioId}/holding/${code}`, - { - withCredentials: true, - } - ) - .then((res) => { - if (res.status === 200) { - const deletedStockValue = quantity * average; + const updatedStock: StockProps = { + code, + name, + quantity: newQuantity, + average: newPrice, + ror, + portfolioId, + logo, + }; + updateStockInStore(updatedStock); + calculateROR(); + setChange(!change); + swal({ + title: "수정 완료!", + icon: "success", + }); + setIsFormOpen(false); + } else if (res.status === 401) { + swal({ + title: "수정 실패하셨습니다.", + text: "다시 시도해주세요!", + icon: "error", + }).then(() => { + navigate("/login"); + }); + } + }) + .catch((error) => { + if (error.response && error.response.status === 401) { + swal({ + title: "Unauthorized", + text: "로그인을 다시 시도해주세요.", + icon: "warning", + }).then(() => { + navigate("/login"); + }); + } else { + swal({ + title: "Edit failed", + text: "다시 시도해주세요", + icon: "error", + }); + setSelectedStock(null); + setModalAction(null); + } + }); + } + }; - deleteStockFromStore(code, deletedStockValue); + const deleteHandle = () => { + axios + .delete( + `${process.env.REACT_APP_API_URL}/v1/portfolio/${portfolioId}/holding/${code}`, + { + withCredentials: true, + } + ) + .then((res) => { + if (res.status === 200) { + const deletedStockValue = quantity * average; - calculateROR(); - setChange(!change); + deleteStockFromStore(code, deletedStockValue); - swal({ - title: "삭제 완료!", - icon: "success", - }); - setIsDeleteOpen(false); - setSelectedStock(null); - setModalAction(null); - } - }) - .catch((error) => { - swal({ - title: "삭제에 실패하셨습니다.", - text: "다시 시도해주세요!", - icon: "error", - }); - setSelectedStock(null); - setModalAction(null); - }); - }; + calculateROR(); + setChange(!change); - const openModal = (action: "edit" | "delete" | "plus") => { - setModalAction(action); - setSelectedStock({ - portfolioId, - code, - name, - quantity, - average, - ror, - logo: selectedStock?.logo || logo, - }); - if (action === "delete") { - setIsDeleteOpen(true); - } else if (action === "plus") { - setIsPlusOpen(true); - } else { - setIsFormOpen(true); + swal({ + title: "삭제 완료!", + icon: "success", + }); + setIsDeleteOpen(false); + setSelectedStock(null); + setModalAction(null); } - }; + }) + .catch((error) => { + swal({ + title: "삭제에 실패하셨습니다.", + text: "다시 시도해주세요!", + icon: "error", + }); + setSelectedStock(null); + setModalAction(null); + }); + }; - return ( -
-
- - - -
-
- {logo ? ( - {`${name} - ) : ( -
- {name.charAt(0)} -
- )} -
-

{name}

-

{code}

-
-
- {`${formatROR(ror).value} %`} -
-
-

수량 {formatPrice(quantity)}

-
{formatPrice(average)}원
-

- {formatPrice( - quantity * average * (1 + ror / 100) - - quantity * average - )} - 원 -

-
-
- {isFormOpen && selectedStock && ( - setIsFormOpen(false)} - onConfirm={handleConfirm} - action={modalAction === "edit" ? "edit" : undefined} - selectedStock={selectedStock} - /> - )} + const openModal = (action: "edit" | "delete" | "plus") => { + setModalAction(action); + setSelectedStock({ + portfolioId, + code, + name, + quantity, + average, + ror, + logo: selectedStock?.logo || logo, + }); + if (action === "delete") { + setIsDeleteOpen(true); + } else if (action === "plus") { + setIsPlusOpen(true); + } else { + setIsFormOpen(true); + } + }; - {isPlusOpen && ( - setIsPlusOpen(false)} - onConfirm={handleConfirm} - userStocks={userStocks} - /> - )} - - {isDeleteOpen && ( - setIsDeleteOpen(false)} - showCancelButton={true} - /> + return ( +
+
+ + + +
+
+ {logo ? ( + {`${name} + ) : ( +
+ {name.charAt(0)} +
+ )} +
+

{name}

+

{code}

+
+
+ {`${formatROR(ror).value} %`} +
+
+

수량 {formatPrice(quantity)}

+
{formatPrice(average)}원
+

+ {formatPrice( + quantity * average * (1 + ror / 100) - quantity * average )} + 원 +

- ); +
+ {isFormOpen && selectedStock && ( + setIsFormOpen(false)} + onConfirm={handleConfirm} + action={modalAction === "edit" ? "edit" : undefined} + selectedStock={selectedStock} + /> + )} + + {isPlusOpen && ( + setIsPlusOpen(false)} + onConfirm={handleConfirm} + userStocks={userStocks} + /> + )} + + {isDeleteOpen && ( + setIsDeleteOpen(false)} + showCancelButton={true} + /> + )} +
+ ); }; export default MyStockItem; diff --git a/src/Component/Modal/AddPortfolio.tsx b/src/Component/Modal/AddPortfolio.tsx index 7105490..364b5da 100644 --- a/src/Component/Modal/AddPortfolio.tsx +++ b/src/Component/Modal/AddPortfolio.tsx @@ -77,7 +77,7 @@ const AddPortfolioModal: React.FC = ({
} - placeholder="포트폴리오 이름을 입력해주세요 (최대 15글자)" + placeholder="이름을 입력해주세요 (최대 15글자)" size="medium" colorType="strokeType" value={portfolioName} diff --git a/src/Component/Modal/modal.tsx b/src/Component/Modal/modal.tsx index a83dbc1..88ca6cc 100644 --- a/src/Component/Modal/modal.tsx +++ b/src/Component/Modal/modal.tsx @@ -1,8 +1,34 @@ -import React from "react"; +import React, { useEffect, useState } from "react"; import Button from "../Button/button"; import { ButtonStyle, ModalBody, ModalHeader, ModalStyles } from "./modalStyle"; import { ModalProps } from "../../constants/interface"; -import ReactModal from "react-modal"; +import ReactModal, { Styles } from "react-modal"; + +const getModalStyles = (isSmallScreen: boolean): Styles => { + return { + overlay: { + backgroundColor: "rgb(255 255 255 / 60%)", + width: "90%", + height: "100%", + zIndex: 100, + top: "0", + left: "0", + }, + content: { + width: isSmallScreen ? "85%" : "400px", + height: isSmallScreen ? "auto" : "450px", + zIndex: "11", + position: "fixed" as const, + scrollbarWidth: "none", + marginLeft: "-15px", + top: "50%", + left: "50%", + transform: "translate(-50%, -50%)", + overflow: "auto", + padding: isSmallScreen ? "20px" : "40px", + }, + }; +}; const ModalOpen: React.FC = ({ title, @@ -18,6 +44,35 @@ const ModalOpen: React.FC = ({ children, icon, }) => { + const [isSmallScreen, setIsSamllScreen] = useState( + window.matchMedia("(max-width: 768px)").matches + ); + + useEffect(() => { + const handleResize = () => { + setIsSamllScreen(window.matchMedia("(max-width: 768px)").matches); + }; + + window.addEventListener("resize", handleResize); + + return () => { + window.removeEventListener("resize", handleResize); + }; + }, []); + + useEffect(() => { + if (isOpen) { + document.body.style.overflow = "hidden"; + } else { + document.body.style.overflow = "auto"; + } + return () => { + document.body.style.overflow = "auto"; + }; + }, [isOpen]); + + const modalStyles = getModalStyles(isSmallScreen); + return ( = ({ ariaHideApp={false} contentLabel={title || "모달창"} shouldCloseOnOverlayClick={false} - style={ModalStyles} + style={modalStyles} > {icon ? ( @@ -66,7 +121,7 @@ const ModalOpen: React.FC = ({ style={{ display: "flex", justifyContent: "center", - marginTop: "50px", + marginTop: isSmallScreen ? "20px" : "50px", }} > {showCancelButton && ( @@ -98,28 +153,3 @@ const ModalOpen: React.FC = ({ }; export default ModalOpen; - -// 모달 사용 예시 -{ - /* setIsOpen(false)} - showOneConfirmBtn={true} - text="자산 추가" - onConfirm={handleConfirm} - > -
-
-

APS

-

05462

-
-
-
수량
- -
단가
- -
-
-
*/ -} diff --git a/src/Component/Modal/modalStyle.ts b/src/Component/Modal/modalStyle.ts index a54499f..157e8cd 100644 --- a/src/Component/Modal/modalStyle.ts +++ b/src/Component/Modal/modalStyle.ts @@ -15,6 +15,7 @@ export const ModalStyles: ReactModal.Styles = { height: "450px", zIndex: "11", position: "fixed", + scrollbarWidth: "none", top: "50%", left: "50%", transform: "translate(-50%, -50%)", @@ -28,6 +29,10 @@ export const ModalHeader = styled.div` align-items: center; padding-bottom: 10px; margin-bottom: 20px; + ::-webkit-scrollbar { + width: 0px; + height: 0px; + } h2 { margin: 0; diff --git a/src/Component/Portfolio/PfCard.tsx b/src/Component/Portfolio/PfCard.tsx index 8998685..7cfbfe2 100644 --- a/src/Component/Portfolio/PfCard.tsx +++ b/src/Component/Portfolio/PfCard.tsx @@ -4,35 +4,31 @@ import { usePortfolioStore } from "../../store/usePortfolioStore"; import { getGrowthColor, formatROR } from "../../util/util"; const PfCard: React.FC = () => { - const budget = usePortfolioStore((state) => state.budget); - const principal = usePortfolioStore((state) => state.principal); - const profitLoss = usePortfolioStore((state) => state.ret); - const ror = usePortfolioStore((state) => state.ror); - const { value: formattedROR, color } = formatROR(ror); + const budget = usePortfolioStore((state) => state.budget); + const principal = usePortfolioStore((state) => state.principal); + const profitLoss = usePortfolioStore((state) => state.ret); + const ror = usePortfolioStore((state) => state.ror); + const { value: formattedROR, color } = formatROR(ror); - return ( -
-

총 자산

-

₩ {budget.toLocaleString()}

-
-
-
투자 총 금액
-
- ₩ {principal.toLocaleString()} -
-
-
-
평가 손익
-
-

{`₩ ${profitLoss.toLocaleString()}`}

-

{`(${formattedROR}%)`}

-
-
-
+ return ( +
+

총 자산

+

₩ {budget.toLocaleString()}

+
+
+
투자 총 금액
+
₩ {principal.toLocaleString()}
- ); +
+
평가 손익
+
+

{`₩ ${profitLoss.toLocaleString()}`}

+

{`(${formattedROR}%)`}

+
+
+
+
+ ); }; export default PfCard; diff --git a/src/Component/Portfolio/PfCardStyle.css b/src/Component/Portfolio/PfCardStyle.css index 80e6b87..e463796 100644 --- a/src/Component/Portfolio/PfCardStyle.css +++ b/src/Component/Portfolio/PfCardStyle.css @@ -1,63 +1,69 @@ .PfCard { - width: 388px; - height: 260px; - border-radius: 20px; - background-color: #615efc; - padding: 37px 17px; - box-shadow: 2px 5px 5px rgba(0, 0, 0, 0.35); + width: 90%; + height: 260px; + border-radius: 20px; + background-color: #615efc; + padding: 37px 17px; + box-shadow: 2px 5px 5px rgba(0, 0, 0, 0.35); +} + +@media screen and (max-width: 768px) { + .PfCard { + width: 100%; + } } .PfCard h3 { - font-size: 20px; - color: #e0e0e0; - margin-bottom: 14px; + font-size: 20px; + color: #e0e0e0; + margin-bottom: 14px; } .PfCard p { - font-size: 25px; - color: #ffffff; - font-weight: 700; + font-size: 25px; + color: #ffffff; + font-weight: 700; } .detail-section { - display: flex; - flex-direction: column; - justify-content: space-between; - align-items: center; - gap: 14px; - margin-top: 50px; - width: 100%; + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: center; + gap: 14px; + margin-top: 50px; + width: 100%; } .total-value, .gain-and-loss { - display: flex; - flex-direction: row; - width: 100%; + display: flex; + flex-direction: row; + width: 100%; } .title-area { - flex: 1; - color: #cbcbcb; - font-size: 15px; + flex: 1; + color: #cbcbcb; + font-size: 15px; } .value-area { - font-size: 18px; - display: flex; - flex-direction: column; - align-items: flex-end; - gap: 5px; + font-size: 18px; + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 5px; } .total-value .value-area { - color: #ffffff; + color: #ffffff; } .gain-and-loss .value-area { - color: red; + color: red; } .value-area > p { - font-size: 19px; + font-size: 19px; } diff --git a/src/Component/Portfolio/PortfolioDetail.tsx b/src/Component/Portfolio/PortfolioDetail.tsx index 0e8ac2a..d60a29f 100644 --- a/src/Component/Portfolio/PortfolioDetail.tsx +++ b/src/Component/Portfolio/PortfolioDetail.tsx @@ -12,124 +12,122 @@ import DeleteButton2 from "../Button/DeleteButton2"; import styled from "styled-components"; const TitleDiv = styled.div` - width: 60%; - display: flex; - align-items: center; - position: relative; - justify-content: space-between; - @media (max-width: 768px) { - width: 100%; - padding-right: 1rem; - } + width: 95%; + display: flex; + align-items: center; + position: relative; + margin-left: 1rem; + justify-content: space-between; + @media (max-width: 768px) { + width: 100%; + padding-right: 1rem; + } `; const PortfolioDetail = () => { - const location = useLocation(); - const id = Number(location.pathname.split("/")[2]); - const nav = useNavigate(); - - const setPortfolio = usePortfolioStore((state) => state.setPortfolio); - const setFinancialData = usePortfolioStore( - (state) => state.setFinancialData - ); - const pfName = usePortfolioStore((state) => state.pfName); - const data = usePortfolioStore((state) => state.data); - const stockData = usePortfolioStore((state) => state.stockData); - const budget = usePortfolioStore((state) => state.budget); - const principal = usePortfolioStore((state) => state.principal); - const ret = usePortfolioStore((state) => state.ret); - const ror = usePortfolioStore((state) => state.ror); - const changeStatus = usePortfolioStore((state) => state.change); - const [isDeleteOpen, setIsDeleteOpen] = useState(false); + const location = useLocation(); + const id = Number(location.pathname.split("/")[2]); + const nav = useNavigate(); - const change = usePortfolioStore((state) => state.change); - const setChange = usePortfolioStore((state) => state.setChange); + const setPortfolio = usePortfolioStore((state) => state.setPortfolio); + const setFinancialData = usePortfolioStore((state) => state.setFinancialData); + const pfName = usePortfolioStore((state) => state.pfName); + const data = usePortfolioStore((state) => state.data); + const stockData = usePortfolioStore((state) => state.stockData); + const budget = usePortfolioStore((state) => state.budget); + const principal = usePortfolioStore((state) => state.principal); + const ret = usePortfolioStore((state) => state.ret); + const ror = usePortfolioStore((state) => state.ror); + const changeStatus = usePortfolioStore((state) => state.change); + const [isDeleteOpen, setIsDeleteOpen] = useState(false); - // const changeCheck = usePortfolioStore((state) => state.setChange); - const deletePortfolio = async (id: number) => { - try { - const res = await axios.delete( - `${process.env.REACT_APP_API_URL}/v1/portfolio/${id}`, - { - withCredentials: true, - } - ); + const change = usePortfolioStore((state) => state.change); + const setChange = usePortfolioStore((state) => state.setChange); - if (res.status === 200) { - setChange(!change); - swal({ - title: "삭제 완료!", - icon: "success", - }).then(() => { - nav("/portfolio"); - }); - setIsDeleteOpen(false); - } - } catch (error: any) { - if (error.response && error.response.status === 404) { - nav("/portfolio"); - } + // const changeCheck = usePortfolioStore((state) => state.setChange); + const deletePortfolio = async (id: number) => { + try { + const res = await axios.delete( + `${process.env.REACT_APP_API_URL}/v1/portfolio/${id}`, + { + withCredentials: true, } - }; - - // 포트폴리오 상세 조회 - useEffect(() => { - axios - .get(`${process.env.REACT_APP_API_URL}/v1/portfolio/${id}`, { - withCredentials: true, - headers: { - "Content-Type": "application/json", - }, - }) - .then((res) => { - if (res.status === 200) { - setPortfolio(res.data.name, res.data, res.data.stocks); - setFinancialData( - res.data.budget, - res.data.principal, - res.data.ret, - res.data.ror - ); - } else if (res.status === 401) { - alert("401"); - } - }) - .catch((e) => { - // alert(e); - }); - }, [setPortfolio, setFinancialData, changeStatus]); + ); - if (!data) { - return
포트폴리오를 찾을 수 없습니다.
; + if (res.status === 200) { + setChange(!change); + swal({ + title: "삭제 완료!", + icon: "success", + }).then(() => { + nav("/portfolio"); + }); + setIsDeleteOpen(false); + } + } catch (error: any) { + if (error.response && error.response.status === 404) { + nav("/portfolio"); + } } + }; + + // 포트폴리오 상세 조회 + useEffect(() => { + axios + .get(`${process.env.REACT_APP_API_URL}/v1/portfolio/${id}`, { + withCredentials: true, + headers: { + "Content-Type": "application/json", + }, + }) + .then((res) => { + if (res.status === 200) { + setPortfolio(res.data.name, res.data, res.data.stocks); + setFinancialData( + res.data.budget, + res.data.principal, + res.data.ret, + res.data.ror + ); + } else if (res.status === 401) { + alert("401"); + } + }) + .catch((e) => { + // alert(e); + }); + }, [setPortfolio, setFinancialData, changeStatus]); + + if (!data) { + return
포트폴리오를 찾을 수 없습니다.
; + } - const openDeleteModal = () => { - setIsDeleteOpen(true); - }; - return ( -
- -

{pfName}

- -
- - - - deletePortfolio(id)} // 삭제 함수 호출 - onRequestClose={() => setIsDeleteOpen(false)} // 모달 닫기 - showCancelButton={true} - /> -
- ); + return ( +
+ +

{pfName}

+ setIsDeleteOpen(true)} /> + {isDeleteOpen && ( + deletePortfolio(id)} // 삭제 함수 호출 + onRequestClose={() => setIsDeleteOpen(false)} // 모달 닫기 + showCancelButton={true} + /> + )} +
+ + + +
+ ); }; export default PortfolioDetail; diff --git a/src/Component/Portfolio/portfolio.tsx b/src/Component/Portfolio/portfolio.tsx index 0b54860..25d3004 100644 --- a/src/Component/Portfolio/portfolio.tsx +++ b/src/Component/Portfolio/portfolio.tsx @@ -133,7 +133,7 @@ const Portfolio = () => {
diff --git a/src/Component/Swipe/Swipe.tsx b/src/Component/Swipe/Swipe.tsx index 82fe2bf..4f4d2e5 100644 --- a/src/Component/Swipe/Swipe.tsx +++ b/src/Component/Swipe/Swipe.tsx @@ -71,7 +71,7 @@ const Swipe: React.FC = ({ portfolioId }) => { }; return ( -
+
{ - if (growth > 0) { - return "#FF5759"; - } else if (growth < 0) { - return "#21BF73"; - } else { - return "black"; - } + if (growth > 0) { + return "#FF5759"; + } else if (growth < 0) { + return "#21BF73"; + } else { + return "black"; + } }; export const formatPrice = (price: number | undefined | null): string => { - if (price === undefined || price === null) { - return "0"; // price가 undefined나 null일 경우 기본값 반환 - } - return `${price.toLocaleString()}`; + if (price === undefined || price === null) { + return "0"; // price가 undefined나 null일 경우 기본값 반환 + } + return `${price.toLocaleString()}`; }; export const formatRate = (rate: number | undefined | null): string => { - if (rate === undefined || rate === null) { - return "0.00"; // Return "0.00" if rate is undefined or null - } - return rate.toLocaleString(undefined, { - minimumFractionDigits: 2, - maximumFractionDigits: 2, - }); + if (rate === undefined || rate === null) { + return "0.00"; // Return "0.00" if rate is undefined or null + } + return rate.toLocaleString(undefined, { + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }); }; // export const formatROR = (ror: number): string => { @@ -31,27 +31,27 @@ export const formatRate = (rate: number | undefined | null): string => { // } type FormattedROR = { - value: string; - color: string; + value: string; + color: string; }; export const formatROR = (ror: number | undefined): FormattedROR => { - // 기본값 설정 - if (ror === undefined || ror === null) { - return { value: "0.00", color: "blue" }; - } + // 기본값 설정 + if (ror === undefined || ror === null) { + return { value: "0.00", color: "blue" }; + } - // 양수, 음수, 0 판단 - const isPositive = ror > 0; - const isZero = ror === 0; + // 양수, 음수, 0 판단 + const isPositive = ror > 0; + const isZero = ror === 0; - // + 붙이기, 2자리 소수점으로 표시, 0일 때는 기호 없이 표시 - const formattedValue = isZero - ? ror.toFixed(2) - : `${isPositive ? "+" : ""}${ror.toFixed(2)}`; + // + 붙이기, 2자리 소수점으로 표시, 0일 때는 기호 없이 표시 + const formattedValue = isZero + ? ror.toFixed(2) + : `${isPositive ? "+" : ""}${ror.toFixed(2)}`; - // 색상 설정, 0일 때는 회색 - const color = isZero ? "#5B5B5B" : isPositive ? "#FF4949" : "#001AFF"; + // 색상 설정, 0일 때는 회색 + const color = isZero ? "#5B5B5B" : isPositive ? "#FF4949" : "#001AFF"; - return { value: formattedValue, color: color }; + return { value: formattedValue, color: color }; };