From f33011b219f296ac2b51a667a9ff9b3eaa74bd7b Mon Sep 17 00:00:00 2001 From: Narayanan Madaswamy Date: Sat, 8 Aug 2026 08:03:46 +0530 Subject: [PATCH] Research View --- .../api/__pycache__/portfolio.cpython-313.pyc | Bin 5419 -> 7237 bytes backend/app/api/portfolio.py | 42 +++++++ src/App.js | 108 +++++++++++++++++- src/components/MainDashboard.js | 12 +- src/components/Ribbon.js | 11 +- src/components/Scanner.js | 8 +- src/components/SmartMoney.js | 21 ++-- src/components/TrendScanner.js | 21 ++-- src/components/WatchlistManager.js | 20 ++-- 9 files changed, 208 insertions(+), 35 deletions(-) diff --git a/backend/app/api/__pycache__/portfolio.cpython-313.pyc b/backend/app/api/__pycache__/portfolio.cpython-313.pyc index 4e2d38ee3becea5ba1d420377102fa9cc20378a4..51165e03d25b3f836ba44a88361bed01be13a4a6 100644 GIT binary patch delta 1930 zcmah}OKclO7#{D#vDeSF6E`+_xX#moBqVYAunLt!gQzMZl4Vg)48d{MX(Gql%(_Jl zpam`%acPK9T=fuh3*vwgJ%j^NB^s&6SX)F`<&XoH-W;U|1PJkuopq9`RY&$W-~RJ| zGyi-$v)}h@opT;I92|mnm%p*#t6y<8Qi%Go`>yG&xM!lD44iC&Z`f3g#HkR9^Tt!e zB%F#j39h&^!U&B4w7@Kwh7YBR6 zYAW+{RpzIt>S`(#J)_3Dr^qZd*|SuZr>Lxx@ASgv#kvYwDrhB`;Y3aqsOTg@6??5U zds%_&1usRjU?Y81F4$|_A~jk?rHW5v^42z$!C8yvrY&ZN9RaP{Mb)rSIirf^O zf@%|Og4;+H_&#epI*q10#%LW%gkeehRD%%_JR));!7JLGXoxW;4<0YcZbYk_RH#-Y zz#MAK-d-Uwd&-nTuh6Y1WeV4iEeFTs;K_>-g!_>QCT=9=(}|h1gvkjCQ#vbQEMuKZ z%9+&stdvPgx>d~gN-F!{L^YW}ty}bB{-1)Gzazn8M9KT24eFD=2J|K_)(Alh>z>j06GJcDY`kG$Vy69R}cM|2A`fNU$<4QnkK&Ct3=BxNf}Ex z3S4mj1dlh|LQ=d?$n+ESVhkf>0_8*S?Nd}{I@!Tp-n-`iTJ`78XnZJV-f=bj?&>JH zI&#dO&GqGld}KYk9^Gla_+Y8z9pAQ(m(9rITl25@^8+hQIrE;>7_iFs%8`I z7;CXWe?P*FcG34=Z5a*GTTLe5TOk%O+_HMs`$G_?L5ANZ0G}#UIdJ%0!YT(2um0<8 z=01jJaw(MI)wcToE|#e}Q?%?sl;w-SXMt^dmuBkzBO2Qtnb4xbKI$?J7U(kRijl{p PJD?F2-lu@!W|-xF);O4r delta 99 zcmX?Vv097oGcPX}0}%XXD9ifIJCRR { + if (researchModalData) { + setBuyModalRecs(null); + setBuyModalRecsLoading(true); + axios.get(`/api/portfolio/recommendations/${researchModalData.symbol}`) + .then(res => { + setBuyModalRecs(res.data); + setBuyModalRecsLoading(false); + }) + .catch(err => { + console.error("Failed to fetch recs", err); + setBuyModalRecsLoading(false); + }); + } + }, [researchModalData]); + const handleBuySubmit = async () => { if (!buyModalData || buyQuantity < 1) return; try { @@ -66,6 +85,10 @@ function Dashboard() { setBuyQuantity(1); }; + const onResearchClick = (symbol) => { + setResearchModalData({ symbol }); + }; + return (
{/* HEADER */} @@ -119,6 +142,7 @@ function Dashboard() { onSelectStock={setSelectedStock} onSignal={setSignal} onBuyClick={onBuyClick} + onResearchClick={onResearchClick} />

TradingView Chart

@@ -127,12 +151,12 @@ function Dashboard() { {signal && } )} - {view === "dashboard" && } - {view === "ribbon" && } - {view === "smartmoney" && } - {view === "marketintel" && } - {view === "trend" && } - {view === "watchlists" && } + {view === "dashboard" && } + {view === "ribbon" && } + {view === "smartmoney" && } + {view === "marketintel" && } + {view === "trend" && } + {view === "watchlists" && } {view === "profile" && }
@@ -181,6 +205,78 @@ function Dashboard() {
)} + + {/* Research Modal */} + {researchModalData && ( +
+
+

+ 🔍 Analyst Research +

+

{researchModalData.symbol}

+ + {/* Analyst Recommendations */} +
+
+ Analyst Consensus + {buyModalRecsLoading && Analyzing...} + {buyModalRecs && !buyModalRecs.error && ( + = 70 ? 'badge-success' : buyModalRecs.confidence >= 40 ? 'badge-warning' : 'badge-danger'}`} style={{ fontSize: '0.9rem' }}> + {buyModalRecs.confidence}% Confidence + + )} +
+ + {buyModalRecs && buyModalRecs.error && ( +
+ No sufficient analyst coverage found. +
+ )} + + {buyModalRecs && !buyModalRecs.error && ( +
+ {(buyModalRecs.strongBuy + buyModalRecs.buy) > 0 && ( +
+ Buy
({buyModalRecs.strongBuy + buyModalRecs.buy}) +
+ )} + {buyModalRecs.hold > 0 && ( +
+ Hold
({buyModalRecs.hold}) +
+ )} + {(buyModalRecs.sell + buyModalRecs.strongSell) > 0 && ( +
+ Sell
({buyModalRecs.sell + buyModalRecs.strongSell}) +
+ )} +
+ )} +
+ +
+ + Read Detailed Trendlyne Reports ↗ + + +
+
+
+ )} ); } diff --git a/src/components/MainDashboard.js b/src/components/MainDashboard.js index 54395c2..3dc3903 100644 --- a/src/components/MainDashboard.js +++ b/src/components/MainDashboard.js @@ -1,7 +1,7 @@ import React, { useState, useRef, useEffect } from "react"; import axios from "axios"; -function MainDashboard({ userProfile, onBuyClick }) { +function MainDashboard({ userProfile, onBuyClick, onResearchClick }) { const [searchQuery, setSearchQuery] = useState(""); const [searchResults, setSearchResults] = useState([]); const [searching, setSearching] = useState(false); @@ -164,11 +164,19 @@ function MainDashboard({ userProfile, onBuyClick }) {
+
Chart diff --git a/src/components/Ribbon.js b/src/components/Ribbon.js index e1b1296..c83864e 100644 --- a/src/components/Ribbon.js +++ b/src/components/Ribbon.js @@ -1,7 +1,7 @@ import React, { useEffect, useState } from "react"; import axios from "axios"; -function Ribbon({ onBuyClick }) { +function Ribbon({ onBuyClick, onResearchClick }) { const [data, setData] = useState([]); const [loading, setLoading] = useState(true); @@ -78,12 +78,17 @@ function Ribbon({ onBuyClick }) {
+ diff --git a/src/components/Scanner.js b/src/components/Scanner.js index 623f57d..25d5380 100644 --- a/src/components/Scanner.js +++ b/src/components/Scanner.js @@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react"; import axios from "axios"; import AboutScanner from "./AboutScanner"; -function Scanner({ onSelectStock, onSignal, onBuyClick }) { +function Scanner({ onSelectStock, onSignal, onBuyClick, onResearchClick }) { const [data, setData] = useState(null); const [lastBest, setLastBest] = useState(null); @@ -110,6 +110,12 @@ function Scanner({ onSelectStock, onSignal, onBuyClick }) { > Buy + {s?.sector && ( + + {s.sector && ( + + {s.sector && ( + +
))}