initial commit
This commit is contained in:
83
src/App.js
Normal file
83
src/App.js
Normal file
@@ -0,0 +1,83 @@
|
||||
import React, { useState } from "react";
|
||||
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 "./index.css";
|
||||
|
||||
function App() {
|
||||
const [selectedStock, setSelectedStock] = useState("NSE:RELIANCE");
|
||||
const [signal, setSignal] = useState(null);
|
||||
const [view, setView] = useState("scanner");
|
||||
|
||||
return (
|
||||
<div className="app-container">
|
||||
<h1>📈 Stock Scanner Pro</h1>
|
||||
|
||||
<div className="nav-container">
|
||||
<button
|
||||
className={`nav-btn ${view === "scanner" ? "active" : ""}`}
|
||||
onClick={() => setView("scanner")}
|
||||
>
|
||||
📊 Scanner
|
||||
</button>
|
||||
|
||||
<button
|
||||
className={`nav-btn ${view === "ribbon" ? "active" : ""}`}
|
||||
onClick={() => setView("ribbon")}
|
||||
>
|
||||
📈 Ribbon Strategy
|
||||
</button>
|
||||
|
||||
<button
|
||||
className={`nav-btn ${view === "smartmoney" ? "active" : ""}`}
|
||||
onClick={() => setView("smartmoney")}
|
||||
>
|
||||
🏦 Smart Money
|
||||
</button>
|
||||
|
||||
<button
|
||||
className={`nav-btn ${view === "trend" ? "active" : ""}`}
|
||||
onClick={() => setView("trend")}
|
||||
>
|
||||
⚡ Trend Analysis
|
||||
</button>
|
||||
|
||||
<button
|
||||
className={`nav-btn ${view === "marketintel" ? "active" : ""}`}
|
||||
onClick={() => setView("marketintel")}
|
||||
>
|
||||
📰 Market Intel
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="animate-fade-in">
|
||||
{view === "scanner" && (
|
||||
<>
|
||||
<AboutScanner />
|
||||
<Scanner onSelectStock={setSelectedStock} onSignal={setSignal} />
|
||||
<div className="glass-card" style={{ marginTop: "20px" }}>
|
||||
<h2>TradingView Chart</h2>
|
||||
<Chart symbol={selectedStock} />
|
||||
</div>
|
||||
{signal && <Popup signal={signal} />}
|
||||
</>
|
||||
)}
|
||||
|
||||
{view === "ribbon" && <Ribbon />}
|
||||
|
||||
{view === "smartmoney" && <SmartMoney />}
|
||||
|
||||
{view === "marketintel" && <MarketIntel />}
|
||||
|
||||
{view === "trend" && <TrendScanner />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
71
src/components/AboutScanner.js
Normal file
71
src/components/AboutScanner.js
Normal file
@@ -0,0 +1,71 @@
|
||||
import React from "react";
|
||||
|
||||
function AboutScanner() {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
background: "#111827",
|
||||
padding: "20px",
|
||||
borderRadius: "10px",
|
||||
marginBottom: "20px",
|
||||
color: "white"
|
||||
}}
|
||||
>
|
||||
<h2>🧠 How The Scanner Works</h2>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<b>📈 Trend:</b> Price above EMA20 indicates bullish trend.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<b>⚡ Momentum:</b> RSI measures strength of buying momentum.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<b>🚀 Breakout:</b> Detects stocks breaking recent resistance levels.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<b>🔥 Volume Ratio:</b> Compares current volume with average volume.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<b>💪 Relative Strength (RS):</b> Measures stock performance vs NIFTY.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<b>🏦 Smart Money Score:</b> Estimates institutional accumulation probability.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<b>🤖 AI Score:</b> Combined score using trend, RSI, breakout, volume and RS.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<b>✅ Confidence:</b> Probability of a quality setup based on AI Score.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<b>🎯 Fund Candidate:</b> High-volume breakout stocks showing potential accumulation.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<b>🏆 Trade Of The Day:</b> Highest ranked stock across all sectors.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>🏅 Score Guide</h3>
|
||||
|
||||
<ul>
|
||||
<li>A+ (90-100) = Exceptional</li>
|
||||
<li>A (80-89) = Strong</li>
|
||||
<li>B (70-79) = Good</li>
|
||||
<li>C (60-69) = Average</li>
|
||||
<li>D (<60) = Avoid</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default AboutScanner;
|
||||
163
src/components/Backtest.js
Normal file
163
src/components/Backtest.js
Normal file
@@ -0,0 +1,163 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import axios from "axios";
|
||||
|
||||
function Backtest() {
|
||||
const [data, setData] = useState({});
|
||||
|
||||
// ✅ Fetch backtest data
|
||||
const loadBacktest = async () => {
|
||||
try {
|
||||
const res = await axios.get("http://127.0.0.1:8000/backtest");
|
||||
setData(res.data);
|
||||
} catch (err) {
|
||||
console.error("Backtest Error:", err);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
loadBacktest();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
background: "#0d1117",
|
||||
color: "#c9d1d9",
|
||||
padding: 20,
|
||||
minHeight: "100vh",
|
||||
}}
|
||||
>
|
||||
<h1 style={{ color: "#58a6ff" }}>
|
||||
📊 Backtest Dashboard (Strategy Performance)
|
||||
</h1>
|
||||
|
||||
{/* ✅ EXPLANATION PANEL */}
|
||||
<div
|
||||
style={{
|
||||
background: "#161b22",
|
||||
border: "1px solid #30363d",
|
||||
padding: 15,
|
||||
borderRadius: 8,
|
||||
marginBottom: 20,
|
||||
}}
|
||||
>
|
||||
<h2 style={{ color: "#58a6ff" }}>📘 How to Read Backtest</h2>
|
||||
|
||||
<p>
|
||||
✅ <b>Trades</b> = Total opportunities generated
|
||||
</p>
|
||||
<p>
|
||||
✅ <b>Wins</b> = Profitable trades
|
||||
</p>
|
||||
<p>
|
||||
✅ <b>Loss</b> = Losing trades
|
||||
</p>
|
||||
<p>
|
||||
✅ <b>Win Rate</b> = Success % of strategy
|
||||
</p>
|
||||
<p>
|
||||
✅ <b>Profit</b> = Net gain (simulated)
|
||||
</p>
|
||||
|
||||
<br />
|
||||
|
||||
<p>
|
||||
💡 <b>Best Strategy Rules:</b>
|
||||
</p>
|
||||
<ul>
|
||||
<li>Win Rate ≥ 60% ✅</li>
|
||||
<li>Trades ≥ 10 ✅</li>
|
||||
<li>Profit positive ✅</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* ✅ SECTORS */}
|
||||
{Object.entries(data).map(([sector, stocks]) => (
|
||||
<div
|
||||
key={sector}
|
||||
style={{
|
||||
background: "#161b22",
|
||||
border: "1px solid #30363d",
|
||||
padding: 15,
|
||||
marginBottom: 20,
|
||||
borderRadius: 10,
|
||||
}}
|
||||
>
|
||||
<h2 style={{ color: "#58a6ff" }}>
|
||||
{sector}
|
||||
</h2>
|
||||
|
||||
<table
|
||||
width="100%"
|
||||
style={{
|
||||
borderCollapse: "collapse",
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
<thead>
|
||||
<tr style={{ borderBottom: "1px solid #30363d" }}>
|
||||
<th>Stock</th>
|
||||
<th>Trades</th>
|
||||
<th>Wins</th>
|
||||
<th>Loss</th>
|
||||
<th>Win %</th>
|
||||
<th>Profit</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{stocks.map((s, i) => (
|
||||
<tr
|
||||
key={i}
|
||||
style={{
|
||||
borderBottom: "1px solid #30363d",
|
||||
}}
|
||||
>
|
||||
<td>{s.symbol}</td>
|
||||
|
||||
<td>{s.trades}</td>
|
||||
|
||||
<td style={{ color: "#3fb950" }}>
|
||||
{s.wins}
|
||||
</td>
|
||||
|
||||
<td style={{ color: "#f85149" }}>
|
||||
{s.loss}
|
||||
</td>
|
||||
|
||||
{/* ✅ WIN RATE COLOR */}
|
||||
<td
|
||||
style={{
|
||||
color:
|
||||
s.win_rate >= 70
|
||||
? "#3fb950"
|
||||
: s.win_rate >= 60
|
||||
? "#d29922"
|
||||
: "#f85149",
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
>
|
||||
{s.win_rate}%
|
||||
</td>
|
||||
|
||||
{/* ✅ PROFIT COLOR */}
|
||||
<td
|
||||
style={{
|
||||
color:
|
||||
s.profit > 0 ? "#3fb950" : "#f85149",
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
>
|
||||
{s.profit}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Backtest;
|
||||
149
src/components/Chart.js
vendored
Normal file
149
src/components/Chart.js
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import { createChart, CandlestickSeries, LineSeries } from "lightweight-charts";
|
||||
import axios from "axios";
|
||||
|
||||
function Chart({ symbol }) {
|
||||
const ref = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!ref.current || !symbol) return;
|
||||
|
||||
ref.current.innerHTML = "";
|
||||
|
||||
try {
|
||||
const chart = createChart(ref.current, {
|
||||
width: 800,
|
||||
height: 500,
|
||||
layout: {
|
||||
background: { color: "#111" },
|
||||
textColor: "#DDD",
|
||||
},
|
||||
grid: {
|
||||
vertLines: { color: "#333" },
|
||||
horzLines: { color: "#333" },
|
||||
},
|
||||
});
|
||||
|
||||
// ✅ MAIN CANDLE SERIES
|
||||
const candleSeries = chart.addSeries(CandlestickSeries);
|
||||
|
||||
// ✅ EMA LINE
|
||||
const emaSeries = chart.addSeries(LineSeries, {
|
||||
color: "yellow",
|
||||
lineWidth: 2,
|
||||
});
|
||||
|
||||
// ✅ RSI LINE (drawn on same chart for simplicity)
|
||||
const rsiSeries = chart.addSeries(LineSeries, {
|
||||
color: "cyan",
|
||||
lineWidth: 1,
|
||||
});
|
||||
|
||||
// ✅ EMA CALCULATION
|
||||
const calculateEMA = (data, period = 20) => {
|
||||
const k = 2 / (period + 1);
|
||||
let ema = data[0].close;
|
||||
return data.map((d) => {
|
||||
ema = d.close * k + ema * (1 - k);
|
||||
return { time: d.time, value: ema };
|
||||
});
|
||||
};
|
||||
|
||||
// ✅ RSI CALCULATION
|
||||
const calculateRSI = (data, period = 14) => {
|
||||
let gains = 0;
|
||||
let losses = 0;
|
||||
|
||||
const result = [];
|
||||
|
||||
for (let i = 1; i < data.length; i++) {
|
||||
const diff = data[i].close - data[i - 1].close;
|
||||
|
||||
if (diff > 0) gains += diff;
|
||||
else losses -= diff;
|
||||
|
||||
if (i >= period) {
|
||||
const rs = gains / (losses || 1);
|
||||
const rsi = 100 - 100 / (1 + rs);
|
||||
|
||||
result.push({ time: data[i].time, value: rsi });
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
// ✅ LOAD DATA FUNCTION
|
||||
const loadData = () => {
|
||||
axios
|
||||
.get(`http://localhost:8000/history?symbol=${symbol}`)
|
||||
.then((res) => {
|
||||
if (!res.data || res.data.length === 0) return;
|
||||
|
||||
const formatted = res.data.map((d) => ({
|
||||
time: Math.floor(d.time),
|
||||
open: Number(d.open),
|
||||
high: Number(d.high),
|
||||
low: Number(d.low),
|
||||
close: Number(d.close),
|
||||
}));
|
||||
|
||||
// ✅ SET CANDLES
|
||||
candleSeries.setData(formatted);
|
||||
|
||||
// ✅ EMA
|
||||
const emaData = calculateEMA(formatted);
|
||||
emaSeries.setData(emaData);
|
||||
|
||||
// ✅ RSI
|
||||
const rsiData = calculateRSI(formatted);
|
||||
rsiSeries.setData(rsiData);
|
||||
|
||||
// ✅ ENTRY / SL / TARGET
|
||||
const lastPrice = formatted[formatted.length - 1].close;
|
||||
|
||||
candleSeries.createPriceLine({
|
||||
price: lastPrice,
|
||||
color: "blue",
|
||||
lineWidth: 2,
|
||||
title: "Entry",
|
||||
});
|
||||
|
||||
candleSeries.createPriceLine({
|
||||
price: lastPrice * 0.97,
|
||||
color: "red",
|
||||
title: "SL",
|
||||
});
|
||||
|
||||
candleSeries.createPriceLine({
|
||||
price: lastPrice * 1.05,
|
||||
color: "green",
|
||||
title: "Target",
|
||||
});
|
||||
|
||||
chart.timeScale().fitContent();
|
||||
})
|
||||
.catch((err) => console.error(err));
|
||||
};
|
||||
|
||||
// ✅ INITIAL LOAD
|
||||
loadData();
|
||||
|
||||
// ✅ LIVE UPDATE (every 1 min)
|
||||
const interval = setInterval(loadData, 60000);
|
||||
|
||||
return () => {
|
||||
clearInterval(interval);
|
||||
chart.remove();
|
||||
};
|
||||
|
||||
} catch (err) {
|
||||
console.error("Chart crash:", err);
|
||||
}
|
||||
|
||||
}, [symbol]);
|
||||
|
||||
return <div ref={ref}></div>;
|
||||
}
|
||||
|
||||
export default Chart;
|
||||
21
src/components/ChartPage.js
Normal file
21
src/components/ChartPage.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import React from "react";
|
||||
|
||||
function ChartPage({ symbol }) {
|
||||
const tvSymbol = symbol || "NSE:RELIANCE";
|
||||
|
||||
const url = `https://www.tradingview.com/chart/?symbol=${tvSymbol}`;
|
||||
|
||||
return (
|
||||
<div style={{ width: "100%", height: "600px", marginTop: "20px" }}>
|
||||
<iframe
|
||||
title="TradingView Chart"
|
||||
src={url}
|
||||
width="100%"
|
||||
height="100%"
|
||||
frameBorder="0"
|
||||
></iframe>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ChartPage;
|
||||
114
src/components/LongTerm.js
Normal file
114
src/components/LongTerm.js
Normal file
@@ -0,0 +1,114 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import axios from "axios";
|
||||
|
||||
function LongTerm() {
|
||||
const [data, setData] = useState({});
|
||||
|
||||
const loadData = async () => {
|
||||
try {
|
||||
const res = await axios.get("http://127.0.0.1:8000/longterm");
|
||||
setData(res.data);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
loadData();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
background: "#0d1117",
|
||||
color: "#c9d1d9",
|
||||
padding: 20,
|
||||
minHeight: "100vh"
|
||||
}}>
|
||||
|
||||
<h1 style={{ color: "#58a6ff" }}>
|
||||
📈 Long-Term Investment Dashboard
|
||||
</h1>
|
||||
|
||||
{/* ✅ EXPLANATION */}
|
||||
<div style={{
|
||||
background: "#161b22",
|
||||
padding: 15,
|
||||
borderRadius: 10,
|
||||
marginBottom: 20
|
||||
}}>
|
||||
<h3>📘 Strategy</h3>
|
||||
|
||||
<ul>
|
||||
<li>✅ Golden Cross = 50 EMA > 200 EMA</li>
|
||||
<li>✅ Support = Price above 200 EMA</li>
|
||||
<li>✅ Momentum = Breakout + Volume</li>
|
||||
</ul>
|
||||
|
||||
<p>💡 Only strong trend reversal stocks are shown</p>
|
||||
</div>
|
||||
|
||||
{Object.entries(data).map(([sector, info]) => {
|
||||
|
||||
|
||||
const filtered = info.stocks.filter(
|
||||
s => s.golden_cross || s.support_strength
|
||||
);
|
||||
|
||||
|
||||
if (filtered.length === 0) return null;
|
||||
|
||||
return (
|
||||
<div key={sector} style={{
|
||||
background: "#161b22",
|
||||
padding: 15,
|
||||
marginBottom: 20,
|
||||
borderRadius: 10
|
||||
}}>
|
||||
|
||||
<h2 style={{ color: "#3fb950" }}>
|
||||
{sector} ✅ Long-Term Strong
|
||||
</h2>
|
||||
|
||||
<table width="100%" style={{ textAlign: "center" }}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Stock</th>
|
||||
<th>Score</th>
|
||||
<th>RSI</th>
|
||||
<th>Golden Cross</th>
|
||||
<th>Support</th>
|
||||
<th>Signal</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{filtered.map((s, i) => (
|
||||
<tr key={i}>
|
||||
<td>{s.symbol}</td>
|
||||
<td>{s.score}</td>
|
||||
<td>{s.rsi}</td>
|
||||
|
||||
<td style={{ color: "#3fb950" }}>
|
||||
✅
|
||||
</td>
|
||||
|
||||
<td style={{ color: "#3fb950" }}>
|
||||
✅
|
||||
</td>
|
||||
|
||||
<td style={{ color: "#3fb950" }}>
|
||||
{s.signal}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default LongTerm;
|
||||
112
src/components/MarketIntel.js
Normal file
112
src/components/MarketIntel.js
Normal file
@@ -0,0 +1,112 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import axios from "axios";
|
||||
|
||||
function MarketIntel() {
|
||||
const [data, setData] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
loadData();
|
||||
}, []);
|
||||
|
||||
const loadData = async () => {
|
||||
try {
|
||||
const res = await axios.get("http://127.0.0.1:8000/marketintel");
|
||||
setData(res.data);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
};
|
||||
|
||||
if (!data) {
|
||||
return <div className="glass-card" style={{ textAlign: "center" }}>Loading Market Intelligence...</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="animate-fade-in">
|
||||
<h2 className="text-blue" style={{ fontSize: "2rem", marginBottom: "20px" }}>📰 Market Intelligence (Live)</h2>
|
||||
|
||||
<div style={{ display: "grid", gridTemplateColumns: "1fr", gap: "20px", marginBottom: "30px" }}>
|
||||
{/* Top News */}
|
||||
<div className="glass-card">
|
||||
<h2 className="text-orange">🔥 Latest Financial News</h2>
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: "12px" }}>
|
||||
{(data.top_news || []).length > 0 ? (
|
||||
(data.top_news || []).map((n, i) => (
|
||||
<div key={i} style={{ padding: "12px", background: "rgba(255,255,255,0.03)", borderRadius: "8px", borderLeft: "3px solid var(--accent-blue)" }}>
|
||||
<a href={n.link} target="_blank" rel="noreferrer" style={{ color: "var(--text-primary)", textDecoration: "none", fontWeight: 600, fontSize: "1.05rem" }}>
|
||||
{n.title}
|
||||
</a>
|
||||
<div style={{ fontSize: "0.85rem", color: "var(--text-secondary)", marginTop: "6px" }}>
|
||||
{n.provider} • {new Date(n.pubDate).toLocaleString()}
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<p className="text-muted">No news available at the moment.</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(300px, 1fr))", gap: "20px" }}>
|
||||
{/* Block Deals */}
|
||||
<div className="glass-card">
|
||||
<h2 className="text-green">💰 Block Deals</h2>
|
||||
<div className="data-table-container">
|
||||
<table className="data-table">
|
||||
<thead><tr><th>Stock</th><th>Buyer</th><th>Value</th></tr></thead>
|
||||
<tbody>
|
||||
{(data.block_deals || []).map((d, i) => (
|
||||
<tr key={i}><td>{d.stock}</td><td>{d.buyer}</td><td>{d.value}</td></tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bulk Deals */}
|
||||
<div className="glass-card">
|
||||
<h2 className="text-green">📦 Bulk Deals</h2>
|
||||
<div className="data-table-container">
|
||||
<table className="data-table">
|
||||
<thead><tr><th>Stock</th><th>Buyer</th><th>Shares</th></tr></thead>
|
||||
<tbody>
|
||||
{(data.bulk_deals || []).map((d, i) => (
|
||||
<tr key={i}><td>{d.stock}</td><td>{d.buyer}</td><td>{d.shares}</td></tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* FII / DII */}
|
||||
<div className="glass-card">
|
||||
<h2 className="text-blue">🏦 Institutional Activity</h2>
|
||||
<div className="data-table-container">
|
||||
<table className="data-table">
|
||||
<thead><tr><th>Institution</th><th>Stock</th><th>Activity</th></tr></thead>
|
||||
<tbody>
|
||||
{(data.fii_activity || []).map((d, i) => (
|
||||
<tr key={`fii-${i}`}>
|
||||
<td><span className="badge badge-neutral">FII</span></td>
|
||||
<td>{d.stock}</td>
|
||||
<td><span className={`badge ${d.activity.includes('Buy') ? 'badge-success' : 'badge-danger'}`}>{d.activity}</span></td>
|
||||
</tr>
|
||||
))}
|
||||
{(data.dii_activity || []).map((d, i) => (
|
||||
<tr key={`dii-${i}`}>
|
||||
<td><span className="badge badge-neutral">DII</span></td>
|
||||
<td>{d.stock}</td>
|
||||
<td><span className={`badge ${d.activity.includes('Buy') ? 'badge-success' : 'badge-danger'}`}>{d.activity}</span></td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default MarketIntel;
|
||||
22
src/components/Popup.js
Normal file
22
src/components/Popup.js
Normal file
@@ -0,0 +1,22 @@
|
||||
import React, { useEffect } from "react";
|
||||
|
||||
function Popup({ signal }) {
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
const popup = document.getElementById("popup");
|
||||
if (popup) popup.style.display = "none";
|
||||
}, 4000);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div id="popup" className="popup">
|
||||
<h3>🚨 Trade Signal</h3>
|
||||
<p>Stock: {signal.name}</p>
|
||||
<p>Code: {signal.code}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Popup;
|
||||
81
src/components/Ribbon.js
Normal file
81
src/components/Ribbon.js
Normal file
@@ -0,0 +1,81 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import axios from "axios";
|
||||
|
||||
function Ribbon() {
|
||||
const [data, setData] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
const loadData = async () => {
|
||||
try {
|
||||
const res = await axios.get("http://127.0.0.1:8000/ribbon");
|
||||
setData(res.data || []);
|
||||
setLoading(false);
|
||||
} catch (err) {
|
||||
console.error("Ribbon error:", err);
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
loadData();
|
||||
const interval = setInterval(loadData, 100000);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
const openChart = (symbol) => {
|
||||
window.open(`https://www.tradingview.com/chart/?symbol=NSE:${symbol}`, "_blank");
|
||||
};
|
||||
|
||||
if (loading) return <div className="glass-card" style={{ textAlign: "center" }}>Loading Ribbon...</div>;
|
||||
|
||||
return (
|
||||
<div className="glass-card animate-fade-in">
|
||||
<h2 className="text-blue">📈 EMA Ribbon Strategy</h2>
|
||||
<div className="data-table-container">
|
||||
<table className="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Stock</th>
|
||||
<th>Price</th>
|
||||
<th>EMA High 5</th>
|
||||
<th>EMA Low 5</th>
|
||||
<th>EMA High 100</th>
|
||||
<th>EMA Low 100</th>
|
||||
<th>EMA Close 100</th>
|
||||
<th>Signal</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{(data || []).map((s, i) => (
|
||||
<tr key={i}>
|
||||
<td style={{ fontWeight: 600 }}>{s.symbol}</td>
|
||||
<td>₹{s.price}</td>
|
||||
<td>{s.ema_high_5}</td>
|
||||
<td>{s.ema_low_5}</td>
|
||||
<td>{s.ema_high_100}</td>
|
||||
<td>{s.ema_low_100}</td>
|
||||
<td className="text-blue">{s.ema_close_100}</td>
|
||||
<td>
|
||||
<span className={`badge ${
|
||||
s.signal.includes("BUY") ? "badge-success" :
|
||||
s.signal.includes("SELL") ? "badge-danger" : "badge-warning"
|
||||
}`}>
|
||||
{s.signal}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<button className="action-btn" onClick={() => openChart(s.symbol)}>
|
||||
Chart
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Ribbon;
|
||||
144
src/components/Scanner.js
Normal file
144
src/components/Scanner.js
Normal file
@@ -0,0 +1,144 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import axios from "axios";
|
||||
|
||||
function Scanner({ onSelectStock, onSignal }) {
|
||||
const [data, setData] = useState(null);
|
||||
const [lastBest, setLastBest] = useState(null);
|
||||
|
||||
const loadData = async () => {
|
||||
try {
|
||||
const res = await axios.get("http://127.0.0.1:8000/scan");
|
||||
const newData = res.data;
|
||||
|
||||
const best = newData.best_5 && newData.best_5.length > 0 ? newData.best_5[0] : null;
|
||||
|
||||
if (best && best.symbol !== lastBest) {
|
||||
if (onSignal) onSignal(`New Best Trade: ${best.symbol}`);
|
||||
setLastBest(best.symbol);
|
||||
}
|
||||
|
||||
setData(newData);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
loadData();
|
||||
const interval = setInterval(loadData, 100000);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
const openChart = (symbol) => {
|
||||
window.open(`https://www.tradingview.com/chart/?symbol=NSE:${symbol}`, "_blank");
|
||||
};
|
||||
|
||||
if (!data) return <div className="glass-card" style={{ textAlign: "center" }}>Loading Scanner Data...</div>;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* Top 5 Trades */}
|
||||
{(data.best_5 || []).length > 0 && (
|
||||
<div className="glass-card animate-fade-in" style={{ marginBottom: "30px", borderLeft: "4px solid var(--accent-green)" }}>
|
||||
<h2 className="text-green">🔥 Top 5 Momentum Picks</h2>
|
||||
<div className="data-table-container">
|
||||
<table className="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Stock</th>
|
||||
<th>Price</th>
|
||||
<th>RSI</th>
|
||||
<th>MACD</th>
|
||||
<th>Signal</th>
|
||||
<th>AI Score</th>
|
||||
<th>Grade</th>
|
||||
<th>Institutional</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{(data.best_5 || []).filter(s => s).map((s, i) => (
|
||||
<tr key={i}>
|
||||
<td style={{ fontWeight: 600 }}>{s?.symbol}</td>
|
||||
<td>₹{s?.price}</td>
|
||||
<td><span className={`badge ${s?.rsi > 60 ? 'badge-success' : 'badge-neutral'}`}>{s?.rsi}</span></td>
|
||||
<td><span className={`badge ${s?.macd_hist > 0 ? 'badge-success' : 'badge-danger'}`}>{s?.macd_hist}</span></td>
|
||||
<td>
|
||||
<span className={`badge ${s?.signal?.includes("BUY") ? 'badge-success' : 'badge-danger'}`}>
|
||||
{s?.signal}
|
||||
</span>
|
||||
</td>
|
||||
<td>{s.ai_score} / 100</td>
|
||||
<td>{s.grade}</td>
|
||||
<td>{s.institutional ? <span className="text-orange">🔥 Yes</span> : <span className="text-muted">No</span>}</td>
|
||||
<td>
|
||||
<button className="action-btn" onClick={() => { openChart(s?.symbol); onSelectStock(s?.symbol); }}>
|
||||
View Chart
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Sector Tables */}
|
||||
{Object.entries(data.sectors || {}).map(([sector, info]) => {
|
||||
const best = info.top_stock;
|
||||
return (
|
||||
<div key={sector} className="glass-card animate-fade-in" style={{ marginBottom: "20px" }}>
|
||||
<h2 className="text-blue">{sector} ({info.stocks.length} Stocks)</h2>
|
||||
{best && (
|
||||
<p className="text-orange" style={{ marginBottom: "20px" }}>
|
||||
<strong>Top Pick:</strong> {best.symbol} (Score: {best.score})
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div className="data-table-container">
|
||||
<table className="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Stock</th>
|
||||
<th>Price</th>
|
||||
<th>MACD</th>
|
||||
<th>Signal</th>
|
||||
<th>Volume</th>
|
||||
<th>Target</th>
|
||||
<th>AI Score</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{(info.stocks || []).map((s, i) => (
|
||||
<tr key={i}>
|
||||
<td style={{ fontWeight: 500 }}>{s?.symbol}</td>
|
||||
<td>₹{s?.price}</td>
|
||||
<td><span className={`badge ${s?.macd_hist > 0 ? 'badge-success' : 'badge-danger'}`}>{s?.macd_hist}</span></td>
|
||||
<td>
|
||||
<span className={`badge ${s?.signal?.includes("BUY") ? 'badge-success' : 'badge-danger'}`}>
|
||||
{s?.signal}
|
||||
</span>
|
||||
</td>
|
||||
<td>{s?.volume?.toLocaleString()}</td>
|
||||
<td className="text-green">₹{s?.target}</td>
|
||||
<td>{s?.ai_score}</td>
|
||||
<td>
|
||||
<button className="action-btn" onClick={() => { openChart(s?.symbol); onSelectStock(s?.symbol); }}>
|
||||
Chart
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Scanner;
|
||||
95
src/components/SmartMoney.js
Normal file
95
src/components/SmartMoney.js
Normal file
@@ -0,0 +1,95 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import axios from "axios";
|
||||
|
||||
function SmartMoney() {
|
||||
const [data, setData] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
loadData();
|
||||
const interval = setInterval(loadData, 100000);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
const loadData = async () => {
|
||||
try {
|
||||
const res = await axios.get("http://127.0.0.1:8000/smartmoney");
|
||||
setData(res.data);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
};
|
||||
|
||||
const openChart = (symbol) => {
|
||||
window.open(`https://www.tradingview.com/chart/?symbol=NSE:${symbol}`, "_blank");
|
||||
};
|
||||
|
||||
if (!data) return <div className="glass-card" style={{ textAlign: "center" }}>Loading Smart Money Data...</div>;
|
||||
|
||||
return (
|
||||
<div className="animate-fade-in">
|
||||
<h2 className="text-blue" style={{ fontSize: "2rem", marginBottom: "20px" }}>🏦 Smart Money Tracker</h2>
|
||||
|
||||
{/* Trade of the Day */}
|
||||
{data.trade_of_day && (
|
||||
<div className="glass-card" style={{ marginBottom: "20px", borderLeft: "4px solid var(--accent-orange)" }}>
|
||||
<h2 className="text-orange">🔥 Institutional Trade of the Day: {data.trade_of_day.symbol}</h2>
|
||||
<p className="text-muted" style={{ marginBottom: "12px" }}>
|
||||
Massive volume spike combined with strong momentum indicating potential smart money buying.
|
||||
</p>
|
||||
<div style={{ display: "flex", gap: "15px", flexWrap: "wrap", marginBottom: "15px" }}>
|
||||
<span className="badge badge-neutral">Volume Ratio: {data.trade_of_day.volume_ratio}x</span>
|
||||
<span className="badge badge-neutral">RSI: {data.trade_of_day.rsi}</span>
|
||||
<span className="badge badge-success">{data.trade_of_day.reason}</span>
|
||||
</div>
|
||||
<button className="nav-btn active" onClick={() => openChart(data.trade_of_day.symbol)}>
|
||||
View Deep Chart
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Top 10 Institutional Buys */}
|
||||
<div className="glass-card">
|
||||
<h2 className="text-green">Top 10 Institutional Breakouts</h2>
|
||||
<div className="data-table-container">
|
||||
<table className="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Stock</th>
|
||||
<th>Inst Label</th>
|
||||
<th>Inst Score</th>
|
||||
<th>Volume Ratio</th>
|
||||
<th>Breakout</th>
|
||||
<th>Reason</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{(data.top_10 || []).map((s, i) => (
|
||||
<tr key={i}>
|
||||
<td style={{ fontWeight: 600 }}>{s.symbol}</td>
|
||||
<td>
|
||||
<span className={`badge ${
|
||||
s.institutional_label.includes("Very Strong") ? 'badge-success' :
|
||||
s.institutional_label.includes("Strong") ? 'badge-success' : 'badge-neutral'
|
||||
}`}>
|
||||
{s.institutional_label}
|
||||
</span>
|
||||
</td>
|
||||
<td>{s.institutional_score}</td>
|
||||
<td><span className="text-orange">{s.volume_ratio}x</span></td>
|
||||
<td>{s.breakout ? "🔥 YES" : "NO"}</td>
|
||||
<td>{s.reason}</td>
|
||||
<td>
|
||||
<button className="action-btn" onClick={() => openChart(s.symbol)}>Chart</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default SmartMoney;
|
||||
78
src/components/TrendScanner.js
Normal file
78
src/components/TrendScanner.js
Normal file
@@ -0,0 +1,78 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import axios from "axios";
|
||||
|
||||
function TrendScanner() {
|
||||
const [data, setData] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
loadData();
|
||||
const interval = setInterval(loadData, 100000);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
const loadData = async () => {
|
||||
try {
|
||||
const res = await axios.get("http://127.0.0.1:8000/trend");
|
||||
setData(res.data);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
};
|
||||
|
||||
const openChart = (symbol) => {
|
||||
window.open(`https://www.tradingview.com/chart/?symbol=NSE:${symbol}`, "_blank");
|
||||
};
|
||||
|
||||
if (!data) return <div className="glass-card" style={{ textAlign: "center" }}>Loading Trend Data...</div>;
|
||||
|
||||
return (
|
||||
<div className="animate-fade-in">
|
||||
<h2 className="text-blue" style={{ fontSize: "2rem", marginBottom: "20px" }}>⚡ Top Trending Stocks</h2>
|
||||
|
||||
<div className="glass-card">
|
||||
<h2 className="text-green">Top 20 Trend Analysis</h2>
|
||||
<div className="data-table-container">
|
||||
<table className="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Stock</th>
|
||||
<th>Trend Signal</th>
|
||||
<th>Trend Score</th>
|
||||
<th>Relative Strength</th>
|
||||
<th>Trend Reason</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{(data.top_20 || []).map((s, i) => (
|
||||
<tr key={i}>
|
||||
<td style={{ fontWeight: 600 }}>{s.symbol}</td>
|
||||
<td>
|
||||
<span className={`badge ${
|
||||
s.trend_signal.includes("BUY") ? 'badge-success' :
|
||||
s.trend_signal.includes("WATCH") ? 'badge-warning' : 'badge-danger'
|
||||
}`}>
|
||||
{s.trend_signal}
|
||||
</span>
|
||||
</td>
|
||||
<td>{s.trend_score}</td>
|
||||
<td>
|
||||
<span className={s.relative_strength > 0 ? "text-green" : "text-red"}>
|
||||
{s.relative_strength}%
|
||||
</span>
|
||||
</td>
|
||||
<td>{s.trend_reason}</td>
|
||||
<td>
|
||||
<button className="action-btn" onClick={() => openChart(s.symbol)}>Chart</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default TrendScanner;
|
||||
216
src/index.css
Normal file
216
src/index.css
Normal file
@@ -0,0 +1,216 @@
|
||||
:root {
|
||||
/* Color Palette - Premium Dark Theme */
|
||||
--bg-primary: #0b1220;
|
||||
--bg-secondary: #111827;
|
||||
--bg-tertiary: #1f2937;
|
||||
|
||||
--text-primary: #f8fafc;
|
||||
--text-secondary: #94a3b8;
|
||||
--text-muted: #64748b;
|
||||
|
||||
--accent-blue: #3b82f6;
|
||||
--accent-blue-hover: #2563eb;
|
||||
|
||||
--accent-green: #10b981;
|
||||
--accent-green-bg: rgba(16, 185, 129, 0.1);
|
||||
|
||||
--accent-red: #ef4444;
|
||||
--accent-red-bg: rgba(239, 68, 68, 0.1);
|
||||
|
||||
--accent-orange: #f59e0b;
|
||||
--accent-orange-bg: rgba(245, 158, 11, 0.1);
|
||||
|
||||
/* Borders & Shadows */
|
||||
--border-color: rgba(255, 255, 255, 0.05);
|
||||
--glass-bg: rgba(17, 24, 39, 0.7);
|
||||
--shadow-sm: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||
--shadow-glow: 0 0 15px rgba(59, 130, 246, 0.3);
|
||||
}
|
||||
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap');
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
background-color: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Glassmorphism Card */
|
||||
.glass-card {
|
||||
background: var(--glass-bg);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 16px;
|
||||
padding: 24px;
|
||||
box-shadow: var(--shadow-sm);
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.glass-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--shadow-glow);
|
||||
}
|
||||
|
||||
/* Typography */
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 800;
|
||||
background: linear-gradient(135deg, #fff, #94a3b8);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.nav-btn {
|
||||
background: var(--bg-tertiary);
|
||||
color: var(--text-primary);
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 10px 20px;
|
||||
border-radius: 8px;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
margin-right: 12px;
|
||||
margin-bottom: 12px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn:hover {
|
||||
background: var(--bg-secondary);
|
||||
border-color: var(--accent-blue);
|
||||
box-shadow: 0 0 10px rgba(59, 130, 246, 0.2);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: var(--accent-blue);
|
||||
border-color: var(--accent-blue-hover);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
color: var(--text-primary);
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 6px 12px;
|
||||
border-radius: 6px;
|
||||
font-size: 0.85rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.action-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-color: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
/* Modern Tables */
|
||||
.data-table-container {
|
||||
overflow-x: auto;
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--border-color);
|
||||
background: var(--bg-secondary);
|
||||
}
|
||||
|
||||
.data-table {
|
||||
width: 100%;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.data-table th {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
color: var(--text-secondary);
|
||||
font-weight: 600;
|
||||
font-size: 0.85rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
padding: 16px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.data-table td {
|
||||
padding: 16px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.02);
|
||||
color: var(--text-primary);
|
||||
font-size: 0.95rem;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.data-table tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.data-table tr:hover td {
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
}
|
||||
|
||||
/* Status Badges */
|
||||
.badge {
|
||||
padding: 4px 10px;
|
||||
border-radius: 20px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.badge-success { background: var(--accent-green-bg); color: var(--accent-green); border: 1px solid rgba(16, 185, 129, 0.2); }
|
||||
.badge-danger { background: var(--accent-red-bg); color: var(--accent-red); border: 1px solid rgba(239, 68, 68, 0.2); }
|
||||
.badge-warning { background: var(--accent-orange-bg); color: var(--accent-orange); border: 1px solid rgba(245, 158, 11, 0.2); }
|
||||
.badge-neutral { background: var(--bg-tertiary); color: var(--text-secondary); border: 1px solid var(--border-color); }
|
||||
|
||||
/* Layouts */
|
||||
.app-container {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 40px 20px;
|
||||
}
|
||||
|
||||
.nav-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
margin-bottom: 40px;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
/* Animations */
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
.animate-fade-in {
|
||||
animation: fadeIn 0.4s ease-out forwards;
|
||||
}
|
||||
|
||||
/* Utilities */
|
||||
.text-green { color: var(--accent-green); }
|
||||
.text-red { color: var(--accent-red); }
|
||||
.text-orange { color: var(--accent-orange); }
|
||||
.text-blue { color: var(--accent-blue); }
|
||||
.text-muted { color: var(--text-muted); }
|
||||
10
src/index.js
Normal file
10
src/index.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import App from "./App";
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById("root"));
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
33
src/styles.css
Normal file
33
src/styles.css
Normal file
@@ -0,0 +1,33 @@
|
||||
.table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-top: 20px;
|
||||
background: #1c1c1c;
|
||||
}
|
||||
|
||||
.table th, .table td {
|
||||
border: 1px solid #333;
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.table th {
|
||||
background: #222;
|
||||
}
|
||||
|
||||
.table tr:hover {
|
||||
background: #2a2a2a;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
td:nth-child(3) {
|
||||
color: #00e676;
|
||||
}
|
||||
|
||||
td:nth-child(5) {
|
||||
color: red;
|
||||
}
|
||||
|
||||
td:nth-child(6) {
|
||||
color: #00c853;
|
||||
}
|
||||
Reference in New Issue
Block a user