Files
stock-scanner/src/App.js
2026-08-07 22:38:27 +05:30

200 lines
9.9 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState, useEffect } from "react";
import { BrowserRouter as Router, Routes, Route, useNavigate } from "react-router-dom";
import axios from "axios";
import Chart from "./components/Chart";
import Scanner from "./components/Scanner";
import Ribbon from "./components/Ribbon";
import SmartMoney from "./components/SmartMoney";
import Popup from "./components/Popup";
import AboutScanner from "./components/AboutScanner";
import MarketIntel from "./components/MarketIntel";
import TrendScanner from "./components/TrendScanner";
import WatchlistManager from "./components/WatchlistManager";
import Login from "./components/Login";
import Signup from "./components/Signup";
import ProtectedRoute from "./components/ProtectedRoute";
import Profile from "./components/Profile";
import MainDashboard from "./components/MainDashboard";
import "./index.css";
function Dashboard() {
const [selectedStock, setSelectedStock] = useState("NSE:RELIANCE");
const [signal, setSignal] = useState(null);
const [view, setView] = useState("dashboard");
const [userProfile, setUserProfile] = useState(null);
const [dropdownOpen, setDropdownOpen] = useState(false);
const [scannerLoaded, setScannerLoaded] = useState(false);
const [buyModalData, setBuyModalData] = useState(null);
const [buyQuantity, setBuyQuantity] = useState(1);
const navigate = useNavigate();
const fetchProfile = () => {
axios.get("/api/auth/me")
.then(res => setUserProfile(res.data))
.catch(err => {
if (err.response?.status === 401) handleLogout();
});
};
useEffect(() => {
fetchProfile();
}, []);
const handleLogout = () => {
localStorage.removeItem("token");
navigate("/login");
};
const handleBuySubmit = async () => {
if (!buyModalData || buyQuantity < 1) return;
try {
await axios.post("/api/portfolio/buy", {
symbol: buyModalData.symbol,
quantity: buyQuantity,
buy_price: buyModalData.price
});
setBuyModalData(null);
setBuyQuantity(1);
} catch (err) {
console.error("Error buying stock:", err);
alert("Failed to buy stock");
}
};
const onBuyClick = (symbol, price) => {
setBuyModalData({ symbol, price });
setBuyQuantity(1);
};
return (
<div className="app-container">
{/* HEADER */}
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '2rem', position: 'relative' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
{/* Logo only, no text */}
<span style={{ fontSize: '2.5rem' }}>📊</span>
</div>
{userProfile && (
<div style={{ position: 'relative' }}>
<div
style={{ display: 'flex', alignItems: 'center', gap: '12px', cursor: 'pointer', background: 'var(--glass-bg)', padding: '6px 12px', borderRadius: '30px', border: '1px solid var(--border-color)', backdropFilter: 'blur(10px)' }}
onClick={() => setDropdownOpen(!dropdownOpen)}
>
<div style={{ width: '30px', height: '30px', borderRadius: '50%', background: 'linear-gradient(135deg, var(--accent-blue), var(--accent-green))', display: 'flex', justifyContent: 'center', alignItems: 'center', fontWeight: 'bold', fontSize: '1rem', color: 'white' }}>
{userProfile.display_name ? userProfile.display_name.charAt(0).toUpperCase() : '?'}
</div>
<span style={{ fontWeight: 600, fontSize: '0.9rem' }}>{userProfile.display_name || userProfile.username}</span>
<span style={{ fontSize: '0.8rem', color: 'var(--text-secondary)' }}></span>
</div>
{dropdownOpen && (
<div className="glass-card animate-fade-in" style={{ position: 'absolute', top: '50px', right: '0', width: '200px', zIndex: 100, padding: '8px' }}>
<button className="nav-btn" style={{ width: '100%', marginBottom: '8px', border: 'none', whiteSpace: 'nowrap', padding: '8px' }} onClick={() => { setView('profile'); setDropdownOpen(false); }}>
Profile Settings
</button>
<button className="nav-btn" style={{ width: '100%', border: 'none', background: 'var(--accent-red-bg)', color: 'var(--accent-red)', whiteSpace: 'nowrap', padding: '8px' }} onClick={handleLogout}>
🚪 Logout
</button>
</div>
)}
</div>
)}
</div>
<div className="nav-container" style={{ gap: '8px' }}>
<button className={`nav-btn ${view === "dashboard" ? "active" : ""}`} onClick={() => setView("dashboard")} title="Dashboard" style={{ padding: '6px 12px' }}>🏠</button>
<button className={`nav-btn ${view === "scanner" ? "active" : ""}`} onClick={() => setView("scanner")} style={{ padding: '6px 12px', fontSize: '0.9rem' }}>📊 Scanner</button>
<button className={`nav-btn ${view === "ribbon" ? "active" : ""}`} onClick={() => setView("ribbon")} style={{ padding: '6px 12px', fontSize: '0.9rem' }}>📈 Ribbon</button>
<button className={`nav-btn ${view === "smartmoney" ? "active" : ""}`} onClick={() => setView("smartmoney")} style={{ padding: '6px 12px', fontSize: '0.9rem' }}>🏦 Smart Money</button>
<button className={`nav-btn ${view === "trend" ? "active" : ""}`} onClick={() => setView("trend")} style={{ padding: '6px 12px', fontSize: '0.9rem' }}> Trend</button>
<button className={`nav-btn ${view === "marketintel" ? "active" : ""}`} onClick={() => setView("marketintel")} style={{ padding: '6px 12px', fontSize: '0.9rem' }}>📰 Intel</button>
<button className={`nav-btn ${view === "watchlists" ? "active" : ""}`} onClick={() => setView("watchlists")} style={{ padding: '6px 12px', fontSize: '0.9rem' }}>📋 Watchlists</button>
</div>
<div className="animate-fade-in">
{view === "scanner" && (
<>
<Scanner
onSelectStock={setSelectedStock}
onSignal={setSignal}
onBuyClick={onBuyClick}
/>
<div className="glass-card" style={{ marginTop: "20px" }}>
<h2>TradingView Chart</h2>
<Chart symbol={selectedStock} />
</div>
{signal && <Popup signal={signal} />}
</>
)}
{view === "dashboard" && <MainDashboard userProfile={userProfile} onBuyClick={onBuyClick} />}
{view === "ribbon" && <Ribbon onBuyClick={onBuyClick} />}
{view === "smartmoney" && <SmartMoney onBuyClick={onBuyClick} />}
{view === "marketintel" && <MarketIntel onBuyClick={onBuyClick} />}
{view === "trend" && <TrendScanner onBuyClick={onBuyClick} />}
{view === "watchlists" && <WatchlistManager userProfile={userProfile} onProfileUpdated={fetchProfile} onBuyClick={onBuyClick} />}
{view === "profile" && <Profile userProfile={userProfile} onProfileUpdated={fetchProfile} />}
</div>
{/* Buy Modal */}
{buyModalData && (
<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(350px, 90vw)', textAlign: 'center',
boxShadow: '0 8px 32px rgba(0, 0, 0, 0.3)'
}}>
<h2 style={{ marginBottom: '10px' }}>Mock Buy</h2>
<h3 style={{ color: 'var(--accent-green)', margin: '10px 0', fontSize: '1.8rem' }}>{buyModalData.symbol}</h3>
<p style={{ color: 'var(--text-secondary)' }}>Current Price: <strong style={{ color: 'var(--text-color)' }}>{buyModalData.price}</strong></p>
<div style={{ margin: '25px 0', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '15px' }}>
<label style={{ fontWeight: '500' }}>Quantity: </label>
<input
type="number"
value={buyQuantity}
onChange={(e) => setBuyQuantity(parseInt(e.target.value) || 1)}
min="1"
style={{
background: 'var(--search-bg)', border: '1px solid var(--border-color)',
color: 'var(--text-color)', padding: '10px', borderRadius: '8px', width: '100px',
fontSize: '1.1rem', textAlign: 'center'
}}
/>
</div>
<div style={{ margin: '20px 0', padding: '15px', background: 'rgba(0,0,0,0.2)', borderRadius: '10px' }}>
<span style={{ color: 'var(--text-secondary)', fontSize: '0.9rem' }}>Total Investment</span>
<p style={{ fontWeight: 'bold', fontSize: '1.4rem', margin: '5px 0 0 0', color: 'var(--accent-blue)' }}>
{(buyModalData.price * buyQuantity).toFixed(2)}
</p>
</div>
<div style={{ display: 'flex', justifyContent: 'space-between', gap: '15px', marginTop: '25px' }}>
<button className="nav-btn" style={{ flex: 1, padding: '12px' }} onClick={() => setBuyModalData(null)}>Cancel</button>
<button className="action-btn" onClick={handleBuySubmit} style={{ flex: 1, padding: '12px', background: 'linear-gradient(135deg, var(--accent-blue), var(--accent-green))', color: '#1a1a2e', border: 'none', fontWeight: 'bold' }}>Confirm Buy</button>
</div>
</div>
</div>
)}
</div>
);
}
function App() {
return (
<Router>
<Routes>
<Route path="/login" element={<Login />} />
<Route path="/signup" element={<Signup />} />
<Route path="/" element={<ProtectedRoute><Dashboard /></ProtectedRoute>} />
</Routes>
</Router>
);
}
export default App;