From 077f47580f6055399c5aa6e9f28a6ab9bfc35660 Mon Sep 17 00:00:00 2001 From: Javier Garcia Date: Mon, 7 Aug 2023 10:33:26 -0700 Subject: [PATCH] added real time price to stock pages --- backend/ranking.js | 4 ++ frontend/src/components/Ranking/Ranking.jsx | 32 ++++++++--- .../StockCarousel/StockCarousel.jsx | 11 +++- .../src/components/StockData/StockData.jsx | 54 +++++++++++-------- .../src/components/StockNews/StockNews.jsx | 10 +++- 5 files changed, 78 insertions(+), 33 deletions(-) diff --git a/backend/ranking.js b/backend/ranking.js index 858f2bf..7e724d5 100644 --- a/backend/ranking.js +++ b/backend/ranking.js @@ -538,6 +538,10 @@ export async function GetRanking(user, page) { const response = await RankingAlgorithmV4(user); + if (response.status === 422) { + return { status: 422, error: response.error }; + } + if (response.status === 500) { return { status: 500, error: response.error }; } diff --git a/frontend/src/components/Ranking/Ranking.jsx b/frontend/src/components/Ranking/Ranking.jsx index e0eea0e..47d0440 100644 --- a/frontend/src/components/Ranking/Ranking.jsx +++ b/frontend/src/components/Ranking/Ranking.jsx @@ -1,12 +1,13 @@ import "./Ranking.css"; import { useState, useEffect } from "react"; import axios from "axios"; -import { Link } from "react-router-dom"; +import { Link, useNavigate } from "react-router-dom"; import Card from "react-bootstrap/Card"; import Button from "react-bootstrap/Button"; import { useSelector, useDispatch } from "react-redux"; import { setLoading } from "../../redux/loading"; import { NetworkError, ServerError, ResponseError } from "../../utils"; +import Swal from "sweetalert2"; const DEFAULT_RANKING_PAGE = 1; const MAX_PAGE_SIZE = 10; @@ -17,6 +18,8 @@ export default function Ranking() { const [showLoadMore, setShowLoadMore] = useState(true); const dispatch = useDispatch(); const loading = useSelector((state) => state.loading); + const [hasRendered, setHasRendered] = useState(false); + const navigate = useNavigate(); useEffect(() => { const fetchRanking = async () => { @@ -42,7 +45,19 @@ export default function Ranking() { } } - if (response.status === 422 || response.status === 401) { + if (response.status === 422) { + Swal.fire({ + icon: "info", + title: "Get Started with Ranking", + text: "Explore at least 10 stocks to get started." + }).then((result) => { + if (result.isConfirmed) { + navigate("/search"); + } + }); + } + + if (response.status === 401) { ResponseError(response.data.error); } @@ -56,10 +71,15 @@ export default function Ranking() { NetworkError(error); } }; - fetchRanking(); - }, [page]); - return ( + if (hasRendered) { + fetchRanking(); + } else { + setHasRendered(true); + } + }, [page, hasRendered]); + + return stocksRanking.length > 0 ? (

Popular Stocks

@@ -103,5 +123,5 @@ export default function Ranking() { ) : null}

- ); + ) : null; } diff --git a/frontend/src/components/StockCarousel/StockCarousel.jsx b/frontend/src/components/StockCarousel/StockCarousel.jsx index 3286b75..bfa8c69 100644 --- a/frontend/src/components/StockCarousel/StockCarousel.jsx +++ b/frontend/src/components/StockCarousel/StockCarousel.jsx @@ -11,6 +11,7 @@ export default function StockCarousel() { const [stocks, setStocks] = useState([]); const [stocksNotFound, setStocksNotFound] = useState(false); const dispatch = useDispatch(); + const [hasRendered, setHasRendered] = useState(false); useEffect(() => { const fetchStocks = async () => { @@ -38,8 +39,14 @@ export default function StockCarousel() { NetworkError(error); } }; - fetchStocks(); - }, []); + + + if (hasRendered) { + fetchStocks(); + } else { + setHasRendered(true); + } + }, [hasRendered]); return ( <> diff --git a/frontend/src/components/StockData/StockData.jsx b/frontend/src/components/StockData/StockData.jsx index 9d5847e..d7c96dd 100644 --- a/frontend/src/components/StockData/StockData.jsx +++ b/frontend/src/components/StockData/StockData.jsx @@ -30,11 +30,33 @@ export default function StockData() { const [category, setCategory] = useState("chart"); const [stockInDatabase, setStockInDatabase] = useState(false); const navigate = useNavigate(); + const [stockPrice, setStockPrice] = useState(null); useEffect(() => { const fetchStockData = async () => { try { dispatch(setLoading(true)); + + const stockPriceResponse = await axios.get( + getStockPriceUrl(ticker) + ); + + if (stockPriceResponse.data.c === 0) { + dispatch(setLoading(false)); + Swal.fire({ + icon: "error", + title: "Stock Not Found", + text: "The stock ticker you entered does not correspond to any existing company." + }).then((result) => { + if (result.isConfirmed) { + navigate("/search"); + } + }); + return; + } + + setStockPrice(stockPriceResponse.data.c); + const databaseResponse = await axios.get( `${import.meta.env.VITE_HOST}/stock/${ticker}`, { withCredentials: true, validateStatus: () => true } @@ -52,40 +74,26 @@ export default function StockData() { } const stockOverviewUrl = getStockOverviewUrl(ticker); - const stockPriceUrl = getStockPriceUrl(ticker); const stockLogoUrl = getStockLogoUrl(ticker); - const [overviewResponse, priceResponse, logoResponse] = - await Promise.all([ - axios.get(stockOverviewUrl), - axios.get(stockPriceUrl), - axios.get(stockLogoUrl) - ]); + const [overviewResponse, logoResponse] = await Promise.all([ + axios.get(stockOverviewUrl), + axios.get(stockLogoUrl) + ]); const overviewData = overviewResponse.data; - const priceData = priceResponse.data; const logoData = logoResponse.data; - if (Object.keys(overviewData).length === 0) { + if (overviewData.Note != null) { dispatch(setLoading(false)); Swal.fire({ icon: "error", - title: "Stock Not Found", - text: "The stock ticker you entered does not correspond to any existing company." - }).then((result) => { - if (result.isConfirmed) { - navigate("/search"); - } + title: "API Limit Reached", + text: `${overviewData.Note}` }); return; } - if (overviewData.Note != null) { - dispatch(setLoading(false)); - ResponseError(overviewData.Note); - return; - } - const stockSector = capitalize(overviewData.Sector); const combinedStockData = { @@ -93,7 +101,7 @@ export default function StockData() { name: overviewData.Name, description: overviewData.Description, sector: stockSector, - price: priceData.c, + price: stockPriceResponse.data.c, logo: logoData.logo }; @@ -142,7 +150,7 @@ export default function StockData() {

- ${stockData.price?.toFixed(2)} + ${stockPrice?.toFixed(2)}

{ const fetchStockNews = async () => { @@ -63,8 +64,13 @@ export default function StockNews({ stocks }) { NetworkError(); } }; - fetchStockNews(); - }, [currentStock]); + + if (hasRendered) { + fetchStockNews(); + } else { + setHasRendered(true); + } + }, [currentStock, hasRendered]); return (