200 lines
9.9 KiB
JavaScript
200 lines
9.9 KiB
JavaScript
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; |