Research View
This commit is contained in:
Binary file not shown.
@@ -100,3 +100,45 @@ def delete_portfolio_item(item_id: int, db: Session = Depends(get_db), current_u
|
|||||||
db.delete(item)
|
db.delete(item)
|
||||||
db.commit()
|
db.commit()
|
||||||
return {"message": "Stock sold/removed successfully"}
|
return {"message": "Stock sold/removed successfully"}
|
||||||
|
|
||||||
|
@router.get("/recommendations/{symbol}")
|
||||||
|
def get_recommendations(symbol: str, current_user: User = Depends(get_current_user)):
|
||||||
|
try:
|
||||||
|
ticker = yf.Ticker(symbol)
|
||||||
|
recs = ticker.recommendations
|
||||||
|
|
||||||
|
if recs is None or recs.empty:
|
||||||
|
return {"error": "No recommendation data available", "confidence": 0}
|
||||||
|
|
||||||
|
# Get the most recent month's data (period '0m' is usually index 0)
|
||||||
|
latest = recs.iloc[0]
|
||||||
|
|
||||||
|
strong_buy = int(latest.get("strongBuy", 0))
|
||||||
|
buy = int(latest.get("buy", 0))
|
||||||
|
hold = int(latest.get("hold", 0))
|
||||||
|
sell = int(latest.get("sell", 0))
|
||||||
|
strong_sell = int(latest.get("strongSell", 0))
|
||||||
|
|
||||||
|
total = strong_buy + buy + hold + sell + strong_sell
|
||||||
|
|
||||||
|
if total == 0:
|
||||||
|
return {"error": "No recommendations found", "confidence": 0}
|
||||||
|
|
||||||
|
# Calculate confidence score
|
||||||
|
# Strong Buy = 100, Buy = 75, Hold = 50, Sell = 25, Strong Sell = 0
|
||||||
|
score = (strong_buy * 100 + buy * 75 + hold * 50 + sell * 25) / total
|
||||||
|
|
||||||
|
return {
|
||||||
|
"symbol": symbol,
|
||||||
|
"period": str(latest.get("period", "0m")),
|
||||||
|
"strongBuy": strong_buy,
|
||||||
|
"buy": buy,
|
||||||
|
"hold": hold,
|
||||||
|
"sell": sell,
|
||||||
|
"strongSell": strong_sell,
|
||||||
|
"total": total,
|
||||||
|
"confidence": round(score, 1)
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error fetching recommendations for {symbol}: {e}")
|
||||||
|
return {"error": str(e), "confidence": 0}
|
||||||
|
|||||||
108
src/App.js
108
src/App.js
@@ -25,6 +25,9 @@ function Dashboard() {
|
|||||||
const [dropdownOpen, setDropdownOpen] = useState(false);
|
const [dropdownOpen, setDropdownOpen] = useState(false);
|
||||||
const [scannerLoaded, setScannerLoaded] = useState(false);
|
const [scannerLoaded, setScannerLoaded] = useState(false);
|
||||||
const [buyModalData, setBuyModalData] = useState(null);
|
const [buyModalData, setBuyModalData] = useState(null);
|
||||||
|
const [researchModalData, setResearchModalData] = useState(null);
|
||||||
|
const [buyModalRecs, setBuyModalRecs] = useState(null);
|
||||||
|
const [buyModalRecsLoading, setBuyModalRecsLoading] = useState(false);
|
||||||
const [buyQuantity, setBuyQuantity] = useState(1);
|
const [buyQuantity, setBuyQuantity] = useState(1);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
@@ -45,6 +48,22 @@ function Dashboard() {
|
|||||||
navigate("/login");
|
navigate("/login");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
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 () => {
|
const handleBuySubmit = async () => {
|
||||||
if (!buyModalData || buyQuantity < 1) return;
|
if (!buyModalData || buyQuantity < 1) return;
|
||||||
try {
|
try {
|
||||||
@@ -66,6 +85,10 @@ function Dashboard() {
|
|||||||
setBuyQuantity(1);
|
setBuyQuantity(1);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onResearchClick = (symbol) => {
|
||||||
|
setResearchModalData({ symbol });
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="app-container">
|
<div className="app-container">
|
||||||
{/* HEADER */}
|
{/* HEADER */}
|
||||||
@@ -119,6 +142,7 @@ function Dashboard() {
|
|||||||
onSelectStock={setSelectedStock}
|
onSelectStock={setSelectedStock}
|
||||||
onSignal={setSignal}
|
onSignal={setSignal}
|
||||||
onBuyClick={onBuyClick}
|
onBuyClick={onBuyClick}
|
||||||
|
onResearchClick={onResearchClick}
|
||||||
/>
|
/>
|
||||||
<div className="glass-card" style={{ marginTop: "20px" }}>
|
<div className="glass-card" style={{ marginTop: "20px" }}>
|
||||||
<h2>TradingView Chart</h2>
|
<h2>TradingView Chart</h2>
|
||||||
@@ -127,12 +151,12 @@ function Dashboard() {
|
|||||||
{signal && <Popup signal={signal} />}
|
{signal && <Popup signal={signal} />}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{view === "dashboard" && <MainDashboard userProfile={userProfile} onBuyClick={onBuyClick} />}
|
{view === "dashboard" && <MainDashboard userProfile={userProfile} onBuyClick={onBuyClick} onResearchClick={onResearchClick} />}
|
||||||
{view === "ribbon" && <Ribbon onBuyClick={onBuyClick} />}
|
{view === "ribbon" && <Ribbon onBuyClick={onBuyClick} onResearchClick={onResearchClick} />}
|
||||||
{view === "smartmoney" && <SmartMoney onBuyClick={onBuyClick} />}
|
{view === "smartmoney" && <SmartMoney onBuyClick={onBuyClick} onResearchClick={onResearchClick} />}
|
||||||
{view === "marketintel" && <MarketIntel onBuyClick={onBuyClick} />}
|
{view === "marketintel" && <MarketIntel onBuyClick={onBuyClick} onResearchClick={onResearchClick} />}
|
||||||
{view === "trend" && <TrendScanner onBuyClick={onBuyClick} />}
|
{view === "trend" && <TrendScanner onBuyClick={onBuyClick} onResearchClick={onResearchClick} />}
|
||||||
{view === "watchlists" && <WatchlistManager userProfile={userProfile} onProfileUpdated={fetchProfile} onBuyClick={onBuyClick} />}
|
{view === "watchlists" && <WatchlistManager userProfile={userProfile} onProfileUpdated={fetchProfile} onBuyClick={onBuyClick} onResearchClick={onResearchClick} />}
|
||||||
{view === "profile" && <Profile userProfile={userProfile} onProfileUpdated={fetchProfile} />}
|
{view === "profile" && <Profile userProfile={userProfile} onProfileUpdated={fetchProfile} />}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -181,6 +205,78 @@ function Dashboard() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Research Modal */}
|
||||||
|
{researchModalData && (
|
||||||
|
<div style={{
|
||||||
|
position: 'fixed', top: 0, left: 0, width: '100%', height: '100%',
|
||||||
|
backgroundColor: 'rgba(0,0,0,0.7)', backdropFilter: 'blur(5px)',
|
||||||
|
display: 'flex', justifyContent: 'center', alignItems: 'center', zIndex: 9999
|
||||||
|
}}>
|
||||||
|
<div style={{
|
||||||
|
background: 'var(--glass-bg)', padding: '30px', borderRadius: '15px',
|
||||||
|
border: '1px solid var(--border-color)', width: 'min(400px, 90vw)', textAlign: 'center',
|
||||||
|
boxShadow: '0 8px 32px rgba(0, 0, 0, 0.3)'
|
||||||
|
}}>
|
||||||
|
<h2 style={{ marginBottom: '10px', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '8px' }}>
|
||||||
|
<span>🔍</span> Analyst Research
|
||||||
|
</h2>
|
||||||
|
<h3 style={{ color: 'var(--accent-blue)', margin: '10px 0', fontSize: '1.8rem' }}>{researchModalData.symbol}</h3>
|
||||||
|
|
||||||
|
{/* Analyst Recommendations */}
|
||||||
|
<div style={{ margin: '20px 0', padding: '15px', background: 'var(--bg-secondary)', borderRadius: '10px', border: '1px solid var(--border-color)', textAlign: 'left' }}>
|
||||||
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '15px' }}>
|
||||||
|
<span style={{ fontSize: '0.95rem', fontWeight: 'bold', color: 'var(--text-primary)' }}>Analyst Consensus</span>
|
||||||
|
{buyModalRecsLoading && <span style={{ fontSize: '0.8rem', color: 'var(--text-secondary)' }}>Analyzing...</span>}
|
||||||
|
{buyModalRecs && !buyModalRecs.error && (
|
||||||
|
<span className={`badge ${buyModalRecs.confidence >= 70 ? 'badge-success' : buyModalRecs.confidence >= 40 ? 'badge-warning' : 'badge-danger'}`} style={{ fontSize: '0.9rem' }}>
|
||||||
|
{buyModalRecs.confidence}% Confidence
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{buyModalRecs && buyModalRecs.error && (
|
||||||
|
<div style={{ fontSize: '0.9rem', color: 'var(--text-secondary)', textAlign: 'center', padding: '10px 0' }}>
|
||||||
|
No sufficient analyst coverage found.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{buyModalRecs && !buyModalRecs.error && (
|
||||||
|
<div style={{ display: 'flex', gap: '8px', fontSize: '0.85rem', textAlign: 'center' }}>
|
||||||
|
{(buyModalRecs.strongBuy + buyModalRecs.buy) > 0 && (
|
||||||
|
<div style={{ flex: buyModalRecs.strongBuy + buyModalRecs.buy, background: 'var(--accent-green-bg)', color: 'var(--accent-green)', padding: '8px 4px', borderRadius: '6px', fontWeight: 'bold' }}>
|
||||||
|
Buy<br/>({buyModalRecs.strongBuy + buyModalRecs.buy})
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{buyModalRecs.hold > 0 && (
|
||||||
|
<div style={{ flex: buyModalRecs.hold, background: 'var(--accent-orange-bg)', color: 'var(--accent-orange)', padding: '8px 4px', borderRadius: '6px', fontWeight: 'bold' }}>
|
||||||
|
Hold<br/>({buyModalRecs.hold})
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{(buyModalRecs.sell + buyModalRecs.strongSell) > 0 && (
|
||||||
|
<div style={{ flex: buyModalRecs.sell + buyModalRecs.strongSell, background: 'var(--accent-red-bg)', color: 'var(--accent-red)', padding: '8px 4px', borderRadius: '6px', fontWeight: 'bold' }}>
|
||||||
|
Sell<br/>({buyModalRecs.sell + buyModalRecs.strongSell})
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '15px', marginTop: '25px' }}>
|
||||||
|
<a
|
||||||
|
href={`https://trendlyne.com/research-reports/stock/${researchModalData.symbol.replace('.NS', '')}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
className="action-btn"
|
||||||
|
style={{ padding: '12px', background: 'var(--bg-tertiary)', color: 'var(--text-primary)', border: '1px solid var(--border-color)', fontWeight: 'bold', textDecoration: 'none' }}
|
||||||
|
>
|
||||||
|
Read Detailed Trendlyne Reports ↗
|
||||||
|
</a>
|
||||||
|
<button className="nav-btn" style={{ padding: '12px', margin: 0, justifyContent: 'center' }} onClick={() => setResearchModalData(null)}>Close</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { useState, useRef, useEffect } from "react";
|
import React, { useState, useRef, useEffect } from "react";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
function MainDashboard({ userProfile, onBuyClick }) {
|
function MainDashboard({ userProfile, onBuyClick, onResearchClick }) {
|
||||||
const [searchQuery, setSearchQuery] = useState("");
|
const [searchQuery, setSearchQuery] = useState("");
|
||||||
const [searchResults, setSearchResults] = useState([]);
|
const [searchResults, setSearchResults] = useState([]);
|
||||||
const [searching, setSearching] = useState(false);
|
const [searching, setSearching] = useState(false);
|
||||||
@@ -164,11 +164,19 @@ function MainDashboard({ userProfile, onBuyClick }) {
|
|||||||
</div>
|
</div>
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: '15px' }}>
|
<div style={{ display: 'flex', alignItems: 'center', gap: '15px' }}>
|
||||||
<button
|
<button
|
||||||
|
className="action-btn"
|
||||||
|
style={{ background: 'var(--accent-green)', color: '#1a1a2e', padding: '4px 10px', fontSize: '0.8rem', fontWeight: 'bold', borderRadius: '8px', border: 'none', cursor: 'pointer' }}
|
||||||
onClick={(e) => { e.stopPropagation(); onBuyClick(result.symbol, result.currentPrice); }}
|
onClick={(e) => { e.stopPropagation(); onBuyClick(result.symbol, result.currentPrice); }}
|
||||||
style={{ background: 'linear-gradient(135deg, var(--accent-blue), var(--accent-green))', color: '#1a1a2e', padding: '6px 12px', borderRadius: '8px', border: 'none', fontWeight: 'bold', cursor: 'pointer' }}
|
|
||||||
>
|
>
|
||||||
Buy
|
Buy
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
className="action-btn"
|
||||||
|
style={{ background: 'transparent', color: 'var(--accent-blue)', border: '1px solid var(--accent-blue)', padding: '4px 10px', fontSize: '0.8rem', fontWeight: 'bold', borderRadius: '8px', cursor: 'pointer' }}
|
||||||
|
onClick={(e) => { e.stopPropagation(); onResearchClick(result.symbol); }}
|
||||||
|
>
|
||||||
|
Research
|
||||||
|
</button>
|
||||||
<div style={{ color: 'var(--accent-green)', display: 'flex', alignItems: 'center', gap: '5px' }}>
|
<div style={{ color: 'var(--accent-green)', display: 'flex', alignItems: 'center', gap: '5px' }}>
|
||||||
<span>Chart</span>
|
<span>Chart</span>
|
||||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
function Ribbon({ onBuyClick }) {
|
function Ribbon({ onBuyClick, onResearchClick }) {
|
||||||
const [data, setData] = useState([]);
|
const [data, setData] = useState([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
@@ -78,12 +78,17 @@ function Ribbon({ onBuyClick }) {
|
|||||||
<td>
|
<td>
|
||||||
<div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
|
<div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
|
||||||
<button
|
<button
|
||||||
className="action-btn"
|
|
||||||
style={{ background: 'var(--accent-green)', color: '#1a1a2e', padding: '4px 8px', fontSize: '0.8rem' }}
|
|
||||||
onClick={() => onBuyClick(s.symbol, s.price)}
|
onClick={() => onBuyClick(s.symbol, s.price)}
|
||||||
|
style={{ background: 'var(--accent-green)', color: '#1a1a2e', border: 'none', borderRadius: '4px', cursor: 'pointer', padding: '6px 12px', fontSize: '0.85rem', fontWeight: 'bold' }}
|
||||||
>
|
>
|
||||||
Buy
|
Buy
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => onResearchClick(s.symbol)}
|
||||||
|
style={{ background: 'transparent', color: 'var(--accent-blue)', border: '1px solid var(--accent-blue)', borderRadius: '4px', cursor: 'pointer', padding: '6px 12px', fontSize: '0.85rem', fontWeight: 'bold' }}
|
||||||
|
>
|
||||||
|
Research
|
||||||
|
</button>
|
||||||
<button className="action-btn" onClick={() => openChart(s.symbol)}>
|
<button className="action-btn" onClick={() => openChart(s.symbol)}>
|
||||||
Chart
|
Chart
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react";
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import AboutScanner from "./AboutScanner";
|
import AboutScanner from "./AboutScanner";
|
||||||
|
|
||||||
function Scanner({ onSelectStock, onSignal, onBuyClick }) {
|
function Scanner({ onSelectStock, onSignal, onBuyClick, onResearchClick }) {
|
||||||
const [data, setData] = useState(null);
|
const [data, setData] = useState(null);
|
||||||
const [lastBest, setLastBest] = useState(null);
|
const [lastBest, setLastBest] = useState(null);
|
||||||
|
|
||||||
@@ -110,6 +110,12 @@ function Scanner({ onSelectStock, onSignal, onBuyClick }) {
|
|||||||
>
|
>
|
||||||
Buy
|
Buy
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => onResearchClick(s?.symbol)}
|
||||||
|
style={{ background: 'transparent', color: 'var(--accent-blue)', border: '1px solid var(--accent-blue)', borderRadius: '4px', cursor: 'pointer', padding: '6px 12px', fontSize: '0.85rem', fontWeight: 'bold' }}
|
||||||
|
>
|
||||||
|
Research
|
||||||
|
</button>
|
||||||
{s?.sector && (
|
{s?.sector && (
|
||||||
<button
|
<button
|
||||||
onClick={() => removeStockFromSector(s?.symbol, s.sector)}
|
onClick={() => removeStockFromSector(s?.symbol, s.sector)}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
function SmartMoney({ onBuyClick }) {
|
function SmartMoney({ onBuyClick, onResearchClick }) {
|
||||||
const [data, setData] = useState(null);
|
const [data, setData] = useState(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -105,12 +105,17 @@ function SmartMoney({ onBuyClick }) {
|
|||||||
<td>
|
<td>
|
||||||
<div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
|
<div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
|
||||||
<button
|
<button
|
||||||
className="action-btn"
|
|
||||||
style={{ background: 'var(--accent-green)', color: '#1a1a2e', padding: '4px 8px', fontSize: '0.8rem' }}
|
|
||||||
onClick={() => onBuyClick(s.symbol, s.price || s.current_price || 0)}
|
onClick={() => onBuyClick(s.symbol, s.price || s.current_price || 0)}
|
||||||
|
style={{ background: 'var(--accent-green)', color: '#1a1a2e', border: 'none', borderRadius: '4px', cursor: 'pointer', padding: '6px 12px', fontSize: '0.85rem', fontWeight: 'bold' }}
|
||||||
>
|
>
|
||||||
Buy
|
Buy
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => onResearchClick(s.symbol)}
|
||||||
|
style={{ background: 'transparent', color: 'var(--accent-blue)', border: '1px solid var(--accent-blue)', borderRadius: '4px', cursor: 'pointer', padding: '6px 12px', fontSize: '0.85rem', fontWeight: 'bold' }}
|
||||||
|
>
|
||||||
|
Research
|
||||||
|
</button>
|
||||||
<button className="action-btn" onClick={() => openChart(s.symbol)}>Chart</button>
|
<button className="action-btn" onClick={() => openChart(s.symbol)}>Chart</button>
|
||||||
{s.sector && (
|
{s.sector && (
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
function TrendScanner({ onBuyClick }) {
|
function TrendScanner({ onBuyClick, onResearchClick }) {
|
||||||
const [data, setData] = useState(null);
|
const [data, setData] = useState(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -82,12 +82,17 @@ function TrendScanner({ onBuyClick }) {
|
|||||||
<td>
|
<td>
|
||||||
<div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
|
<div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
|
||||||
<button
|
<button
|
||||||
className="action-btn"
|
|
||||||
style={{ background: 'var(--accent-green)', color: '#1a1a2e', padding: '4px 8px', fontSize: '0.8rem' }}
|
|
||||||
onClick={() => onBuyClick(s.symbol, s.price || s.current_price || 0)}
|
onClick={() => onBuyClick(s.symbol, s.price || s.current_price || 0)}
|
||||||
|
style={{ background: 'var(--accent-green)', color: '#1a1a2e', border: 'none', borderRadius: '4px', cursor: 'pointer', padding: '6px 12px', fontSize: '0.85rem', fontWeight: 'bold' }}
|
||||||
>
|
>
|
||||||
Buy
|
Buy
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => onResearchClick(s.symbol)}
|
||||||
|
style={{ background: 'transparent', color: 'var(--accent-blue)', border: '1px solid var(--accent-blue)', borderRadius: '4px', cursor: 'pointer', padding: '6px 12px', fontSize: '0.85rem', fontWeight: 'bold' }}
|
||||||
|
>
|
||||||
|
Research
|
||||||
|
</button>
|
||||||
<button className="action-btn" onClick={() => openChart(s.symbol)}>Chart</button>
|
<button className="action-btn" onClick={() => openChart(s.symbol)}>Chart</button>
|
||||||
{s.sector && (
|
{s.sector && (
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { useState, useEffect, useRef } from "react";
|
import React, { useState, useEffect, useRef } from "react";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
function WatchlistManager({ userProfile, onProfileUpdated, onBuyClick }) {
|
function WatchlistManager({ userProfile, onProfileUpdated, onBuyClick, onResearchClick }) {
|
||||||
const [sectorMap, setSectorMap] = useState({});
|
const [sectorMap, setSectorMap] = useState({});
|
||||||
const [newSectorName, setNewSectorName] = useState("");
|
const [newSectorName, setNewSectorName] = useState("");
|
||||||
const [searchQuery, setSearchQuery] = useState("");
|
const [searchQuery, setSearchQuery] = useState("");
|
||||||
@@ -269,10 +269,16 @@ function WatchlistManager({ userProfile, onProfileUpdated, onBuyClick }) {
|
|||||||
)}
|
)}
|
||||||
<button
|
<button
|
||||||
onClick={() => onBuyClick(symbol, 0)}
|
onClick={() => onBuyClick(symbol, 0)}
|
||||||
style={{ flex: 1, background: 'var(--accent-green)', color: '#1a1a2e', border: 'none', borderRadius: '4px', cursor: 'pointer', padding: '6px', fontSize: '0.85rem', fontWeight: 'bold' }}
|
style={{ background: 'var(--accent-green)', color: '#1a1a2e', border: 'none', borderRadius: '4px', cursor: 'pointer', padding: '6px 12px', fontSize: '0.85rem', fontWeight: 'bold' }}
|
||||||
>
|
>
|
||||||
Buy
|
Buy
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => onResearchClick(symbol)}
|
||||||
|
style={{ background: 'transparent', color: 'var(--accent-blue)', border: '1px solid var(--accent-blue)', borderRadius: '4px', cursor: 'pointer', padding: '6px 12px', fontSize: '0.85rem', fontWeight: 'bold' }}
|
||||||
|
>
|
||||||
|
Research
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
Reference in New Issue
Block a user