import { useState, useEffect, useRef } from 'react'
import anime from 'animejs'
import RecommendationHero from './components/RecommendationHero'
import SignalCards from './components/SignalCards'
import AgentStream from './components/AgentStream'
import RiskFlags from './components/RiskFlags'
import PortfolioView from './components/PortfolioView'
import PriceChart from './components/PriceChart'
const POPULAR = ['AAPL', 'NVDA', 'TSLA', 'MSFT', 'AMZN', 'GOOGL', 'META', 'SPY']
// ── Portfolio page ───────────────────────────────────────────────────────────
function PortfolioPage({ onAnalyse, onBack }) {
return (
)
}
// ── Landing ──────────────────────────────────────────────────────────────────
function Landing({ onSearch, onPortfolio }) {
const [input, setInput] = useState('')
const cardRef = useRef(null)
useEffect(() => {
// Animate each letter of the title
anime({
targets: '.hero-letter',
translateY: [40, 0],
opacity: [0, 1],
delay: anime.stagger(45),
easing: 'easeOutExpo',
duration: 750,
})
// Animate the search card up
anime({
targets: cardRef.current,
translateY: [30, 0],
opacity: [0, 1],
delay: 500,
easing: 'easeOutExpo',
duration: 800,
})
}, [])
const go = (ticker) => {
const t = ticker.trim().toUpperCase()
if (t) onSearch(t)
}
return (
🤖 Powered by AI Agents
{'Should you buy it?'.split('').map((ch, i) => (
{ch === ' ' ? '\u00a0' : ch}
))}
Type any stock ticker and get a plain-English verdict powered by
machine learning, technical analysis, and real-time news.
📊 Technical Charts
🏢 Company Financials
🤖 ML Prediction
📰 News Sentiment
📂 View My Portfolio
)
}
// ── Loading ───────────────────────────────────────────────────────────────────
function LoadingScreen({ ticker }) {
useEffect(() => {
anime({
targets: '.loading-dot',
translateY: [-10, 0],
delay: anime.stagger(160),
loop: true,
direction: 'alternate',
easing: 'easeInOutSine',
duration: 450,
})
}, [])
return (
Analysing {ticker} …
Fetching price data, financials, ML signals & news
)
}
// ── Error ─────────────────────────────────────────────────────────────────────
function ErrorScreen({ error, onBack }) {
return (
⚠️
Couldn't find that ticker
{error}
Try another ticker
)
}
// ── Dashboard ─────────────────────────────────────────────────────────────────
function Dashboard({ ticker, onBack }) {
const [data, setData] = useState(null)
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
const [deepMode, setDeepMode] = useState(false)
useEffect(() => {
setLoading(true)
setError(null)
setData(null)
setDeepMode(false)
fetch(`/quick/${ticker}`)
.then((r) => {
if (!r.ok) throw new Error(`Ticker "${ticker}" not found or API error`)
return r.json()
})
.then((d) => {
setData(d)
setLoading(false)
})
.catch((e) => {
setError(e.message)
setLoading(false)
})
}, [ticker])
if (loading) return
if (error) return
return (
{/* Header */}
← Back
{ticker}
{data.current_price > 0 && (
${data.current_price.toFixed(2)}
)}
{/* Content */}
{/* Forecast */}
{data.forecast_upside !== undefined && data.forecast_upside !== 0 && (
📈 30-Day Price Forecast
0 ? 'up' : 'down'}`}
>
{data.forecast_upside > 0 ? '↑' : '↓'}{' '}
{Math.abs(data.forecast_upside).toFixed(1)}%
{data.forecast_upside > 0
? `Our AI forecasts the price could rise roughly ${data.forecast_upside.toFixed(1)}% over the next 30 days — though forecasts are never guaranteed.`
: `Our AI forecasts the price could dip roughly ${Math.abs(data.forecast_upside).toFixed(1)}% over the next 30 days — though forecasts can be wrong.`}
)}
{/* Risk flags */}
{data.risk_flags?.length > 0 &&
}
{/* Deep analysis */}
{!deepMode ? (
🔍 Want the full picture?
Run 6 specialised AI agents to get a detailed investment report
with step-by-step reasoning.
⏱ Takes about 2–3 minutes
setDeepMode(true)}>
Run Deep Analysis
) : (
)}
)
}
// ── Root App ──────────────────────────────────────────────────────────────────
export default function App() {
const [ticker, setTicker] = useState(null)
const [showPortfolio, setPortfolio] = useState(false)
if (showPortfolio)
return { setPortfolio(false); setTicker(t) }}
onBack={() => setPortfolio(false)}
/>
return ticker ? (
setTicker(null)} />
) : (
setPortfolio(true)} />
)
}