diff --git a/src/assets/icons/rightArrow.svg b/src/assets/icons/rightArrow.svg new file mode 100644 index 00000000..9256f8d1 --- /dev/null +++ b/src/assets/icons/rightArrow.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/common/chart/FundChartComponent.jsx b/src/components/common/chart/FundChartComponent.jsx index 3b003378..162be035 100644 --- a/src/components/common/chart/FundChartComponent.jsx +++ b/src/components/common/chart/FundChartComponent.jsx @@ -5,18 +5,6 @@ import { Doughnut } from "react-chartjs-2"; ChartJS.register(ArcElement, Legend); export default function FundChartComponent({ type, ratio, className }) { - // const defaultColors = [ - // "#F0F4FF", - // "#E4ECFF", - // "#CCD8FF", - // "#B3C5FF", - // "#98B1FF", - // "#6A8CFF", - // "#375AFF", - // "#B3C5FF", - // "#B3C5FF", - // "#B3C5FF", - // ]; // 기본 색상 배열 const defaultColors = [ "#EBEEFF", "#D8DDFF", @@ -43,7 +31,6 @@ export default function FundChartComponent({ type, ratio, className }) { const backgroundColors = ratio.map( (_, index) => defaultColors[index % defaultColors.length] ); - const data = { labels: ["주식", "채권"], datasets: [ diff --git a/src/components/common/rebalnacing/FundListComponent.jsx b/src/components/common/rebalnacing/FundListComponent.jsx new file mode 100644 index 00000000..585d1af2 --- /dev/null +++ b/src/components/common/rebalnacing/FundListComponent.jsx @@ -0,0 +1,34 @@ +import React from "react"; + +// icons +import arrow from "../../../assets/icons/cheveron-right.svg"; + +export default function FundListComponent({ + index, + weight, + data, + setOpen, + setIsSelected, +}) { + return ( +
{ + setOpen(true); + setIsSelected(data); + }} + > +
+
+ {data?.fund_name} +
+ {data?.company_name} +
+
+ {weight[index]}% + +
+
+ ); +} diff --git a/src/components/common/rebalnacing/NewsInfo.jsx b/src/components/common/rebalnacing/NewsInfo.jsx new file mode 100644 index 00000000..432ffc1d --- /dev/null +++ b/src/components/common/rebalnacing/NewsInfo.jsx @@ -0,0 +1,26 @@ +import React from "react"; + +// icons +import arrow from "../../../assets/icons/cheveron-right.svg"; + +export default function NewsInfo({ data }) { + return ( +
+
+
+ + {data.stock_name} +
+ + {data.bad_news_title} + +
+ + + +
+ ); +} diff --git a/src/components/common/rebalnacing/ReportModal.jsx b/src/components/common/rebalnacing/ReportModal.jsx new file mode 100644 index 00000000..858d23e0 --- /dev/null +++ b/src/components/common/rebalnacing/ReportModal.jsx @@ -0,0 +1,76 @@ +import React from "react"; +import { useEffect } from "react"; +import { useState } from "react"; + +// modal +import { Sheet } from "react-modal-sheet"; + +// components +import NewsInfo from "./NewsInfo"; + +export default function ReportModal({ isOpen, setOpen, data }) { + // bad_news가 null이 아닌 항목들만 필터링 + const filteredStocks = + data.stocks?.filter((stock) => stock.bad_news_title !== "None") || []; + + return ( + setOpen(false)} + detent={"content-height"} + > + + + +
+
+ + {data.fund_name} + +
+
+ 운용사 + {data.company_name} +
+
+ 수익률 +
+ + 3개월 + + + {data.return3m?.toFixed(2)}% + +
+
+
+
+
+

+ ✔︎{" "} + + 총 {filteredStocks.length}개의 위험 판단 주식 + + 을 찾았어요. +

+
+ {/** 뉴스 기사 */} +
+ {filteredStocks.map((elem, index) => ( + + ))} +
+
+ +
+
+
+
+
+ ); +} diff --git a/src/components/common/topBar/TopBarComponents.jsx b/src/components/common/topBar/TopBarComponents.jsx index 49b964ce..9da3f8a9 100644 --- a/src/components/common/topBar/TopBarComponents.jsx +++ b/src/components/common/topBar/TopBarComponents.jsx @@ -13,12 +13,12 @@ export const LogoWithNotification = () => {
navigate("/")} /> navigate("/notification")} />
@@ -31,12 +31,12 @@ export const BackArrowWithNotification = () => {
navigate(-1)} /> navigate("/notification")} />
@@ -49,12 +49,12 @@ export const NotificationWithSetting = () => {
navigate("/notification")} /> navigate("/setting")} />
@@ -67,7 +67,7 @@ export const BackArrow = () => {
navigate(-1)} />
diff --git a/src/components/home/RecommendComponent.jsx b/src/components/home/RecommendComponent.jsx index f4951665..da8b820c 100644 --- a/src/components/home/RecommendComponent.jsx +++ b/src/components/home/RecommendComponent.jsx @@ -76,7 +76,7 @@ const PortfolioRecommendComponent = ({ ) : null}
{ data.funds ? navigate(`/detail/${data.name}`, { state: data.funds }) diff --git a/src/components/home/TabComponent.jsx b/src/components/home/TabComponent.jsx index 4045c5c1..f735e9b1 100644 --- a/src/components/home/TabComponent.jsx +++ b/src/components/home/TabComponent.jsx @@ -4,7 +4,7 @@ const TabComponent = ({ type, isSelected, setSelected }) => { return (
setSelected(type)} diff --git a/src/components/manage/MoreServiceComponent.jsx b/src/components/manage/MoreServiceComponent.jsx index a9cef0ef..dd6cef9a 100644 --- a/src/components/manage/MoreServiceComponent.jsx +++ b/src/components/manage/MoreServiceComponent.jsx @@ -16,7 +16,7 @@ export default function MoreServiceComponent({ title, detail }) {
navigate( `${title === "노후 준비 종합 진단" ? "diagnosis" : "pension"}` diff --git a/src/components/notification/NotificationListComponent.jsx b/src/components/notification/NotificationListComponent.jsx index 4b3da958..3aff0533 100644 --- a/src/components/notification/NotificationListComponent.jsx +++ b/src/components/notification/NotificationListComponent.jsx @@ -1,4 +1,5 @@ import React, { useEffect, useState } from "react"; +import { useNavigate } from "react-router-dom"; import { patchNotification } from "../../lib/apis/notificationApi"; export default function NotificationListComponent({ @@ -8,6 +9,7 @@ export default function NotificationListComponent({ check, }) { const [isRead, setIsRead] = useState(check); + const navigate = useNavigate(); const fetchPatchNotification = async (notificationId) => { try { const response = await patchNotification(notificationId); @@ -24,11 +26,14 @@ export default function NotificationListComponent({ onClick={() => { setIsRead(true); fetchPatchNotification(id); + navigate("/rebalancing-report"); }} >

리밸런싱 리포트

- {content} + + {content} + {date}
diff --git a/src/lib/apis/base.jsx b/src/lib/apis/base.jsx index c4a09382..95c862fd 100644 --- a/src/lib/apis/base.jsx +++ b/src/lib/apis/base.jsx @@ -1,7 +1,12 @@ import axios from "axios"; +const portfolioUrl = import.meta.env.VITE_PORTFOLIO_BASE_URI; +const assetUrl = import.meta.env.VITE_ASSET_BASE_URI; +const userUrl = import.meta.env.VITE_USER_BASE_URI; +const mydataUrl = import.meta.env.VITE_MYDATA_BASE_URI; + const instance = axios.create({ - baseURL: "http://localhost:8081/api", + baseURL: `${userUrl}:8081/api`, }); // const persistedString = localStorage.getItem("persist:user"); @@ -10,14 +15,21 @@ const instance = axios.create({ // : ""; const authInstance = (port) => { - const token = localStorage.getItem("accessToken"); - return axios.create({ - baseURL: import.meta.env.VITE_BASE_URL || `http://localhost:${port}/api`, - headers: { - "Content-Type": "application/json", - Authorization: token ? `Bearer ${token}` : "", - }, - }); + const token = localStorage.getItem("accessToken"); + let url = ""; + if (port == 8081) url = userUrl; + else if (port == 8082) url = mydataUrl; + else if (port == 8083) url = assetUrl; + else if (port == 8084) url = portfolioUrl; + else console.log("port is " + port); + + return axios.create({ + baseURL: import.meta.env.VITE_BASE_URL || `${url}:${port}/api`, + headers: { + "Content-Type": "application/json", + Authorization: token ? `Bearer ${token}` : "", + }, + }); }; export default instance; diff --git a/src/lib/apis/diagnosisApi.jsx b/src/lib/apis/diagnosisApi.jsx index 8019930a..137bbec3 100644 --- a/src/lib/apis/diagnosisApi.jsx +++ b/src/lib/apis/diagnosisApi.jsx @@ -1,22 +1,24 @@ import { authInstance } from "./base"; export async function getDiagnosisResult(survey) { - try { - const response = await authInstance(8081).post( - `/retirements/test/results`, - survey - ); - return response.data; - } catch (error) { - return error.response; - } + try { + const response = await authInstance(8081).post( + `/users/retirements/test/results`, + survey + ); + return response.data; + } catch (error) { + return error.response; + } } export async function getPreviousDiagnosisResult() { - try { - const response = await authInstance(8081).get(`/retirements/test/results`); - return response.data; - } catch (error) { - return error.response; - } + try { + const response = await authInstance(8081).get( + `/users/retirements/test/results` + ); + return response.data; + } catch (error) { + return error.response; + } } diff --git a/src/lib/apis/notificationApi.jsx b/src/lib/apis/notificationApi.jsx index b4b78cc8..b08e31af 100644 --- a/src/lib/apis/notificationApi.jsx +++ b/src/lib/apis/notificationApi.jsx @@ -1,35 +1,35 @@ import { authInstance } from "./base"; const getNotification = async () => { - try { - const response = await authInstance(8081).get(`/users/notifications`); - return response.data; - } catch (error) { - return error.response; - } + try { + const response = await authInstance(8081).get(`/users/notifications`); + return response.data; + } catch (error) { + return error.response; + } }; const postNotification = async (reqBody) => { - try { - const response = await authInstance(8084).post( - `/portfolios/send/message`, - reqBody - ); - return response.data; - } catch (error) { - return error.response; - } + try { + const response = await authInstance(8084).post( + `/portfolios/send/message`, + reqBody + ); + return response.data; + } catch (error) { + return error.response; + } }; const patchNotification = async (notificationId) => { - try { - console.log(notificationId); - const response = await authInstance(8081).patch( - `/users/notifications/${notificationId}` - ); - return response.data; - } catch (error) { - return error.response; - } + try { + console.log(notificationId); + const response = await authInstance(8081).patch( + `/users/notifications/${notificationId}` + ); + return response.data; + } catch (error) { + return error.response; + } }; export { getNotification, postNotification, patchNotification }; diff --git a/src/lib/apis/portfolioApi.jsx b/src/lib/apis/portfolioApi.jsx index 2af0f12e..ee49768f 100644 --- a/src/lib/apis/portfolioApi.jsx +++ b/src/lib/apis/portfolioApi.jsx @@ -1,106 +1,103 @@ import instance, { authInstance } from "./base"; import axios from "axios"; +const baseUrl = import.meta.env.VITE_PORTFOLIO_BASE_URI; +const port = 8084; +const url = `${baseUrl}:${port}`; + export async function getAllPortfolio() { - try { - // const response = await authInstance(8084).get("/portfolios/hitit"); - const response = await axios.get( - "http://localhost:8084/api/portfolios/hitit" - ); - return response.data; - } catch (error) { - return error.response; - } + try { + // const response = await authInstance(8084).get("/portfolios/hitit"); + const response = await axios.get(`${url}/api/portfolios/hitit`); + return response.data; + } catch (error) { + return error.response; + } } export async function getFundList(id) { - try { - const response = await axios.get( - `http://localhost:8084/api/portfolios/hitit/${id}` - ); - return response.data; - } catch (error) { - return error.response; - } + try { + const response = await axios.get(`${url}/api/portfolios/hitit/${id}`); + return response.data; + } catch (error) { + return error.response; + } } export async function getFundDetail(portfolioId, fundId) { - try { - const response = await axios.get( - `http://localhost:8084/api/portfolios/hitit/${portfolioId}/${fundId}` - ); - return response.data; - } catch (error) { - return error.response; - } + try { + const response = await axios.get( + `${url}/api/portfolios/hitit/${portfolioId}/${fundId}` + ); + return response.data; + } catch (error) { + return error.response; + } } export async function getMyDataPortfolio() { - const token = localStorage.getItem("accessToken"); - try { - const response = await axios.get( - `http://localhost:8084/api/portfolios/mydata`, - { - headers: { - Authorization: `Bearer ${token}`, - }, - } - ); - return response.data; - } catch (error) { - return error.response; - } + const token = localStorage.getItem("accessToken"); + try { + const response = await axios.get(`${url}/api/portfolios/mydata`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + return response.data; + } catch (error) { + return error.response; + } } export async function changePortfolio(id) { - try { - const response = await authInstance(8084).post(`/portfolios/change/${id}`); - return response.data; - } catch (error) { - return error.response; - } + try { + const response = await authInstance(8084).post(`/portfolios/change/${id}`); + return response.data; + } catch (error) { + return error.response; + } } export async function getUserPortfolio() { - try { - const response = await authInstance(8084).get(`/portfolios/user`); - return response.data; - } catch (error) { - console.log(error); - return error.response; - } + try { + const response = await authInstance(8084).get(`/portfolios/user`); + return response.data; + } catch (error) { + console.log(error); + return error.response; + } } export async function changeMyDataPortfolio(body) { - try { - const response = await authInstance(8084).post( - `/portfolios/mydata/change`, - body - ); - return response.data; - } catch (error) { - return error.response; - } + try { + const response = await authInstance(8084).post( + `/portfolios/mydata/change`, + body + ); + return response.data; + } catch (error) { + return error.response; + } } export async function getUserPortfolioDetail() { - try { - const response = await authInstance(8084).get(`/portfolios/userfunds`); - return response.data; - } catch (error) { - console.log(error); - return error.response; - } + try { + const response = await authInstance(8084).get(`/portfolios/userfunds`); + return response.data; + } catch (error) { + console.log(error); + return error.response; + } } export async function getUserFundDetail(fundId) { - try { - const response = await authInstance(8084).get( - `/portfolios/userfunds/detail/${fundId}` - ); - return response.data; - } catch (error) { - console.log(error); - return error.response; - } + try { + const response = await authInstance(8084).get( + `/portfolios/userfunds/detail/${fundId}` + ); + return response.data; + } catch (error) { + console.log(error); + return error.response; + } } diff --git a/src/lib/apis/rebalancingApi.jsx b/src/lib/apis/rebalancingApi.jsx new file mode 100644 index 00000000..4dbd4f4b --- /dev/null +++ b/src/lib/apis/rebalancingApi.jsx @@ -0,0 +1,13 @@ +import instance, { authInstance } from "./base"; +import axios from "axios"; + +export async function getReport() { + try { + const response = await authInstance(8084).get( + `/portfolios/rebal/getweight` + ); + return response.data; + } catch (error) { + return error.response; + } +} diff --git a/src/lib/apis/testApi.jsx b/src/lib/apis/testApi.jsx index ed035ee0..6e0f0981 100644 --- a/src/lib/apis/testApi.jsx +++ b/src/lib/apis/testApi.jsx @@ -1,36 +1,40 @@ import instance, { authInstance } from "./base"; async function getQuestion(num) { - try { - const response = await instance.get(`/investment_tests/questions/${num}`); - return response; - } catch (error) { - console.log(error); - return error.response; - } + try { + const response = await instance.get( + `/users/investment_tests/questions/${num}` + ); + return response; + } catch (error) { + console.log(error); + return error.response; + } } const token = localStorage.getItem("accessToken"); async function postTestResult(result) { - try { - const response = await authInstance(8081).post( - `/investment_tests/results`, - result - ); - return response; - } catch (error) { - console.log(error); - return error.response; - } + try { + const response = await authInstance(8081).post( + `/users/investment_tests/results`, + result + ); + return response; + } catch (error) { + console.log(error); + return error.response; + } } async function getTestResult() { - try { - const response = await authInstance(8081).get(`/investment_tests/results`); - return response; - } catch (error) { - return error.response; - } + try { + const response = await authInstance(8081).get( + `/users/investment_tests/results` + ); + return response; + } catch (error) { + return error.response; + } } export { getQuestion, postTestResult, getTestResult }; diff --git a/src/routers/mainRouter.jsx b/src/routers/mainRouter.jsx index 21f3563b..7cc16640 100644 --- a/src/routers/mainRouter.jsx +++ b/src/routers/mainRouter.jsx @@ -33,6 +33,7 @@ import KaKaoLoginPage from "../routes/login/KaKaoLoginPage"; import FundDetailPage from "../routes/home/FundDetailPage"; import InvestTestUserResultPage from "../routes/investTest/InvestTestUserResultPage"; import ManageDiagnosisPage from "../routes/manage/diagnosis/ManageDiagnosisPage"; +import RebalancingReportPage from "../routes/rebalancing/RebalancingReportPage"; import AssetPortfolioDetail from "../routes/asset/AssetPortfolioDetail"; // layouts @@ -245,6 +246,11 @@ export const mainRouter = [ element: , index: true, }, + { + path: "rebalancing-report", + element: , + index: true, + }, { path: "account-create", children: [ diff --git a/src/routes/home/PortfolioDetailPage.jsx b/src/routes/home/PortfolioDetailPage.jsx index 03858d3d..19ac4eb5 100644 --- a/src/routes/home/PortfolioDetailPage.jsx +++ b/src/routes/home/PortfolioDetailPage.jsx @@ -90,7 +90,7 @@ const FundListComponent = ({ index, data, type }) => { } return (
{ type === "all" ? navigate(`${index}`) diff --git a/src/routes/manage/ManagePage.jsx b/src/routes/manage/ManagePage.jsx index ec68e846..c2e775d7 100644 --- a/src/routes/manage/ManagePage.jsx +++ b/src/routes/manage/ManagePage.jsx @@ -18,19 +18,28 @@ export default function ManagePage() { const user = useUserStore((store) => store.user); const [dates, setDates] = useState([]); const [values, setValues] = useState([]); + const [rates, setRates] = useState([]); const getData = async () => { const data = await getRates(); - const newDates = []; - const newValues = []; - data.response.forEach((item) => { - const date = Object.keys(item)[0]; - const value = item[date]; - newDates.push(date?.slice(0, 10)); - newValues.push(value); - }); - setDates(newDates); - setValues(newValues); + console.log(data.response); + if (data.response === "내 포트폴리오가 존재하지 않습니다.") { + setRates([]); + } else { + setRates(data.response); + const newDates = []; + const newValues = []; + if (rates.length > 0) { + data.response.forEach((item) => { + const date = Object.keys(item)[0]; + const value = item[date]; + newDates.push(date?.slice(0, 10)); + newValues.push(value); + }); + setDates(newDates); + setValues(newValues); + } + } }; useEffect(() => { getData(); @@ -77,7 +86,7 @@ export default function ManagePage() { />
- ) : ( + ) : rates?.length > 0 ? ( <> - )} + ) : rates?.length === 0 ? ( +
+ 아직 수익률 정보가 없어요! +
+ ) : null}
📌 부가 서비스 diff --git a/src/routes/manage/diagnosis/DiagnosisResultPage.jsx b/src/routes/manage/diagnosis/DiagnosisResultPage.jsx index 6463fc0e..7747bc90 100644 --- a/src/routes/manage/diagnosis/DiagnosisResultPage.jsx +++ b/src/routes/manage/diagnosis/DiagnosisResultPage.jsx @@ -28,6 +28,7 @@ import RobotAnalyzing from "../../../components/home/RobotAnalyzing"; export default function DiagnosisResultPage() { const [isLoading, setIsLoading] = useState(true); const [result, setResult] = useState([]); + const [noResult, setNoResult] = useState(""); const [isPrevious, setIsPrevious] = useState(false); const navigate = useNavigate(); const location = useLocation(); @@ -37,7 +38,10 @@ export default function DiagnosisResultPage() { }; const getPreviousResultData = async () => { const response = await getPreviousDiagnosisResult(); - setResult(response.response); + if (response.response) setResult(response.response); + else { + setResult(response.data); + } setIsLoading(false); }; useEffect(() => { @@ -67,57 +71,65 @@ export default function DiagnosisResultPage() { ) : null}
-
- -
- - - + - - -
-
- 0 - ? "짧아요." - : "길어요." - } +
+ + + + + +
+
+ 0 + ? "짧아요." + : "길어요." + } ${ result?.life_expectancy - result?.asset_life > 0 ? `월 생활비를 ${result?.optimal_monthly_living_expenses}만원으로 낮추면 자산수명과 기대수명이 일치해요.` : "" } `} - /> + /> +
-
+ ) : ( +
+ 이전 기록이 없어요! +

노후 준비 종합 진단을 진행해주세요!

+
+ )} +