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 (