refactor(web): normalize secondary ux surfaces

This commit is contained in:
root 2026-05-06 03:27:52 +00:00
parent 266b367322
commit 76d326c793
7 changed files with 365 additions and 570 deletions

View File

@ -2,6 +2,7 @@ import { Link, Routes, Route, useLocation } from 'react-router-dom';
import { Sidebar } from './Sidebar'; import { Sidebar } from './Sidebar';
import { Header } from './Header'; import { Header } from './Header';
import { RightPanel } from './RightPanel'; import { RightPanel } from './RightPanel';
import { Button } from '../ui/button';
import { HomeView } from '../../views/HomeView'; import { HomeView } from '../../views/HomeView';
import { PortfolioView } from '../../views/PortfolioView'; import { PortfolioView } from '../../views/PortfolioView';
import { ResearchView } from '../../views/ResearchView'; import { ResearchView } from '../../views/ResearchView';
@ -21,40 +22,28 @@ function NotFoundView() {
style={{ style={{
minHeight: 420, minHeight: 420,
borderRadius: 24, borderRadius: 24,
border: '1px solid #E5E7EB', border: '1px solid var(--border)',
background: 'linear-gradient(135deg, #FFF7ED 0%, #EFF6FF 100%)', background: 'var(--hero-gradient)',
padding: '56px 32px', padding: '56px 32px',
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
textAlign: 'center', textAlign: 'center',
color: '#111827', color: 'var(--foreground)',
}} }}
> >
<div style={{ fontSize: 12, fontWeight: 900, letterSpacing: '0.16em', color: '#2563EB', textTransform: 'uppercase' }}> <div style={{ fontSize: 12, fontWeight: 900, letterSpacing: '0.16em', color: 'var(--accent)', textTransform: 'uppercase' }}>
404 404
</div> </div>
<h1 id="not-found-title" style={{ margin: '10px 0 8px', fontSize: 34, fontWeight: 900 }}> <h1 id="not-found-title" style={{ margin: '10px 0 8px', fontSize: 34, fontWeight: 900 }}>
Route not found Route not found
</h1> </h1>
<p style={{ margin: 0, maxWidth: 520, color: '#4B5563', fontSize: 14, lineHeight: 1.6 }}> <p style={{ margin: 0, maxWidth: 520, color: 'var(--muted-foreground)', fontSize: 14, lineHeight: 1.6 }}>
No trading workspace exists at <code style={{ fontWeight: 800 }}>{location.pathname}</code>. The app is still running normally. No trading workspace exists at <code style={{ fontWeight: 800 }}>{location.pathname}</code>. The app is still running normally.
</p> </p>
<Link <Link to="/" style={{ marginTop: 24, textDecoration: 'none' }}>
to="/" <Button>Return home</Button>
style={{
marginTop: 24,
borderRadius: 999,
background: '#111827',
color: '#fff',
padding: '10px 18px',
fontSize: 13,
fontWeight: 800,
textDecoration: 'none',
}}
>
Return home
</Link> </Link>
</section> </section>
); );

View File

@ -3,6 +3,7 @@ import { ArrowRight } from 'lucide-react';
import { useAppContext } from '../../context/AppContext'; import { useAppContext } from '../../context/AppContext';
import { fetchNews, type NewsArticle as MarketNewsArticle } from '../../lib/marketApi'; import { fetchNews, type NewsArticle as MarketNewsArticle } from '../../lib/marketApi';
import { SkeletonBlock, SkeletonText } from '../Skeleton'; import { SkeletonBlock, SkeletonText } from '../Skeleton';
import { Button } from '../ui/button';
// ─── Portfolio Summary ──────────────────────────────────────────────────────── // ─── Portfolio Summary ────────────────────────────────────────────────────────
@ -25,15 +26,15 @@ function PortfolioSummary() {
<div style={{ padding: '16px 16px 12px' }}> <div style={{ padding: '16px 16px 12px' }}>
{/* Header */} {/* Header */}
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }}> <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }}>
<span style={{ fontSize: 13, fontWeight: 700, color: '#111827' }}>Portfolio</span> <span style={{ fontSize: 13, fontWeight: 700, color: 'var(--foreground)' }}>Portfolio</span>
<span style={{ fontSize: 12, color: '#2563EB', cursor: 'pointer', fontWeight: 500 }}> <Button variant="ghost" size="sm" className="h-8 px-2 text-xs">
View All <ArrowRight size={12} style={{ display: 'inline', verticalAlign: 'middle' }} /> View All <ArrowRight size={12} />
</span> </Button>
</div> </div>
{/* Total value */} {/* Total value */}
<div style={{ marginBottom: 12 }}> <div style={{ marginBottom: 12 }}>
<div style={{ fontSize: 20, fontWeight: 800, color: '#111827', letterSpacing: '-0.5px' }}> <div style={{ fontSize: 20, fontWeight: 800, color: 'var(--foreground)', letterSpacing: '-0.5px' }}>
{fmt$(totalValue)} {fmt$(totalValue)}
</div> </div>
<div style={{ <div style={{
@ -49,10 +50,10 @@ function PortfolioSummary() {
display: 'grid', display: 'grid',
gridTemplateColumns: '2fr 1.2fr 1fr 1.2fr', gridTemplateColumns: '2fr 1.2fr 1fr 1.2fr',
gap: 4, paddingBottom: 6, gap: 4, paddingBottom: 6,
borderBottom: '1px solid #F3F4F6', marginBottom: 4, borderBottom: '1px solid var(--border)', marginBottom: 4,
}}> }}>
{['Symbol','Price','Change','Value'].map(h => ( {['Symbol','Price','Change','Value'].map(h => (
<span key={h} style={{ fontSize: 10, color: '#9CA3AF', fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.05em' }}> <span key={h} style={{ fontSize: 10, color: 'var(--muted-foreground)', fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.05em' }}>
{h} {h}
</span> </span>
))} ))}
@ -60,7 +61,7 @@ function PortfolioSummary() {
{/* Rows */} {/* Rows */}
{positions.length === 0 ? ( {positions.length === 0 ? (
<div style={{ fontSize: 12, color: '#9CA3AF', padding: '12px 0', textAlign: 'center' }}> <div style={{ fontSize: 12, color: 'var(--muted-foreground)', padding: '12px 0', textAlign: 'center' }}>
No open positions No open positions
</div> </div>
) : ( ) : (
@ -73,15 +74,15 @@ function PortfolioSummary() {
display: 'grid', display: 'grid',
gridTemplateColumns: '2fr 1.2fr 1fr 1.2fr', gridTemplateColumns: '2fr 1.2fr 1fr 1.2fr',
gap: 4, padding: '5px 0', gap: 4, padding: '5px 0',
borderBottom: '1px solid #F9FAFB', borderBottom: '1px solid var(--border)',
alignItems: 'center', alignItems: 'center',
}}> }}>
<span style={{ fontSize: 12, fontWeight: 700, color: '#111827' }}>{pos.symbol}</span> <span style={{ fontSize: 12, fontWeight: 700, color: 'var(--foreground)' }}>{pos.symbol}</span>
<span style={{ fontSize: 12, color: '#374151' }}>{pos.currentPrice?.toFixed(2) ?? '—'}</span> <span style={{ fontSize: 12, color: 'var(--foreground)' }}>{pos.currentPrice?.toFixed(2) ?? '—'}</span>
<span style={{ fontSize: 12, fontWeight: 600, color: pos_ ? '#16A34A' : '#DC2626' }}> <span style={{ fontSize: 12, fontWeight: 600, color: pos_ ? '#16A34A' : '#DC2626' }}>
{pos_ ? '+' : ''}{pct.toFixed(2)}% {pos_ ? '+' : ''}{pct.toFixed(2)}%
</span> </span>
<span style={{ fontSize: 12, color: '#374151' }}>{fmt$(pos.marketValue ?? 0)}</span> <span style={{ fontSize: 12, color: 'var(--foreground)' }}>{fmt$(pos.marketValue ?? 0)}</span>
</div> </div>
); );
})} })}
@ -115,10 +116,10 @@ function NewsCard({ article }: { article: NewsArticle }) {
display: 'flex', gap: 10, display: 'flex', gap: 10,
padding: '10px 16px', padding: '10px 16px',
textDecoration: 'none', textDecoration: 'none',
borderBottom: '1px solid #F3F4F6', borderBottom: '1px solid var(--border)',
transition: 'background 0.1s', transition: 'background 0.1s',
}} }}
onMouseEnter={e => (e.currentTarget.style.background = '#F9FAFB')} onMouseEnter={e => (e.currentTarget.style.background = 'var(--muted)')}
onMouseLeave={e => (e.currentTarget.style.background = 'transparent')} onMouseLeave={e => (e.currentTarget.style.background = 'transparent')}
> >
{img && ( {img && (
@ -130,7 +131,7 @@ function NewsCard({ article }: { article: NewsArticle }) {
)} )}
<div style={{ flex: 1, minWidth: 0 }}> <div style={{ flex: 1, minWidth: 0 }}>
<div style={{ <div style={{
fontSize: 12, fontWeight: 600, color: '#111827', fontSize: 12, fontWeight: 600, color: 'var(--foreground)',
lineHeight: 1.4, lineHeight: 1.4,
display: '-webkit-box', display: '-webkit-box',
WebkitLineClamp: 2, WebkitLineClamp: 2,
@ -140,7 +141,7 @@ function NewsCard({ article }: { article: NewsArticle }) {
}}> }}>
{article.headline} {article.headline}
</div> </div>
<div style={{ fontSize: 10, color: '#9CA3AF' }}> <div style={{ fontSize: 10, color: 'var(--muted-foreground)' }}>
{article.source} · {timeAgo(article.created_at)} {article.source} · {timeAgo(article.created_at)}
</div> </div>
</div> </div>
@ -158,7 +159,7 @@ function NewsFeedSkeleton() {
display: 'flex', display: 'flex',
gap: 10, gap: 10,
padding: '10px 16px', padding: '10px 16px',
borderBottom: i < 2 ? '1px solid #F9FAFB' : 'none', borderBottom: i < 2 ? '1px solid var(--border)' : 'none',
}} }}
> >
<SkeletonBlock width={48} height={48} radius={10} style={{ flexShrink: 0 }} /> <SkeletonBlock width={48} height={48} radius={10} style={{ flexShrink: 0 }} />
@ -200,10 +201,10 @@ function NewsFeed() {
display: 'flex', justifyContent: 'space-between', alignItems: 'center', display: 'flex', justifyContent: 'space-between', alignItems: 'center',
padding: '12px 16px 8px', padding: '12px 16px 8px',
}}> }}>
<span style={{ fontSize: 13, fontWeight: 700, color: '#111827' }}>Latest News</span> <span style={{ fontSize: 13, fontWeight: 700, color: 'var(--foreground)' }}>Latest News</span>
<span style={{ fontSize: 12, color: '#2563EB', fontWeight: 500, cursor: 'pointer' }}> <Button variant="ghost" size="sm" className="h-8 px-2 text-xs">
View All <ArrowRight size={12} style={{ display: 'inline', verticalAlign: 'middle' }} /> View All <ArrowRight size={12} />
</span> </Button>
</div> </div>
{loading && <NewsFeedSkeleton />} {loading && <NewsFeedSkeleton />}
@ -213,7 +214,7 @@ function NewsFeed() {
)} )}
{!loading && !error && news.length === 0 && ( {!loading && !error && news.length === 0 && (
<div style={{ fontSize: 12, color: '#9CA3AF', padding: '16px', textAlign: 'center' }}> <div style={{ fontSize: 12, color: 'var(--muted-foreground)', padding: '16px', textAlign: 'center' }}>
{activeSymbol ? `No news found for ${activeSymbol}` : 'Search a ticker to see news'} {activeSymbol ? `No news found for ${activeSymbol}` : 'Search a ticker to see news'}
</div> </div>
)} )}
@ -228,14 +229,14 @@ function NewsFeed() {
export function RightPanel() { export function RightPanel() {
return ( return (
<aside className="dashboard-right-panel" style={{ <aside className="dashboard-right-panel" style={{
background: '#ffffff', background: 'var(--card)',
borderLeft: '1px solid #E5E7EB', borderLeft: '1px solid var(--border)',
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
overflowY: 'auto', overflowY: 'auto',
flexShrink: 0, flexShrink: 0,
}}> }}>
<div style={{ borderBottom: '1px solid #E5E7EB' }}> <div style={{ borderBottom: '1px solid var(--border)' }}>
<PortfolioSummary /> <PortfolioSummary />
</div> </div>
<NewsFeed /> <NewsFeed />

View File

@ -218,6 +218,50 @@ body {
box-shadow: var(--card-shadow); box-shadow: var(--card-shadow);
} }
.section-title {
font-size: 15px;
font-weight: 700;
color: var(--foreground);
margin-bottom: 4px;
}
.section-description {
font-size: 13px;
color: var(--muted-foreground);
}
.pill-group {
display: inline-flex;
gap: 6px;
padding: 4px;
width: fit-content;
border-radius: 999px;
border: 1px solid var(--border);
background: var(--card);
box-shadow: var(--card-shadow);
}
.stat-chip {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 6px 12px;
border-radius: 999px;
border: 1px solid var(--border);
background: var(--card-elevated);
color: var(--muted-foreground);
font-size: 11px;
font-weight: 700;
}
.stat-chip[data-tone="success"] {
color: #16a34a;
}
.stat-chip[data-tone="danger"] {
color: #dc2626;
}
.trading-sidebar-nav { .trading-sidebar-nav {
flex: 1; flex: 1;
display: flex; display: flex;

View File

@ -4,6 +4,10 @@ import { BacktestRunnerPanel } from '../backtest/components/BacktestRunnerPanel'
import { BacktestComparePanel } from '../backtest/components/BacktestComparePanel'; import { BacktestComparePanel } from '../backtest/components/BacktestComparePanel';
import { useBacktestFeatureGate } from '../backtest/useBacktestFeatureGate'; import { useBacktestFeatureGate } from '../backtest/useBacktestFeatureGate';
import { fetchTradeProfiles } from '../lib/profileApi'; import { fetchTradeProfiles } from '../lib/profileApi';
import { PageHeader } from '../components/ui/page-header';
import { Card, CardContent } from '../components/ui/card';
import { Button } from '../components/ui/button';
import { Select } from '../components/ui/select';
interface BacktestTabProps { interface BacktestTabProps {
previewAsCustomer?: boolean; previewAsCustomer?: boolean;
@ -71,36 +75,25 @@ export const BacktestTab: React.FC<BacktestTabProps> = ({ previewAsCustomer = fa
return ( return (
<div style={{ maxWidth: '1200px', margin: '0 auto', padding: '0 20px 60px 20px' }}> <div style={{ maxWidth: '1200px', margin: '0 auto', padding: '0 20px 60px 20px' }}>
<div style={{ marginBottom: '24px' }}> <PageHeader
<h2 style={{ color: 'white', fontWeight: 900, letterSpacing: '-0.02em', marginBottom: '6px' }}> title="Backtesting"
Backtesting description="Deterministic historical simulation. No real or paper orders are placed."
</h2> />
<p style={{ color: '#8b8b95', fontSize: '13px', margin: 0 }}>
Deterministic historical simulation. No real or paper orders are placed.
</p>
</div>
{showAccessPanel && ( {showAccessPanel && (
<div <Card style={{ marginBottom: 16 }}>
style={{ <CardContent style={{ padding: '14px 16px' }}>
marginBottom: '16px',
padding: '14px 16px',
borderRadius: '14px',
background: 'rgba(17,24,39,0.45)',
border: '1px solid rgba(148,163,184,0.22)'
}}
>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '8px', marginBottom: '10px' }}> <div style={{ display: 'flex', flexWrap: 'wrap', gap: '8px', marginBottom: '10px' }}>
<span style={{ fontSize: '11px', color: '#d1d5db', fontWeight: 700 }}> <span className="stat-chip">
Role: {isAdminAccount ? 'admin' : 'customer'} Role: {isAdminAccount ? 'admin' : 'customer'}
</span> </span>
<span style={{ fontSize: '11px', color: '#d1d5db', fontWeight: 700 }}> <span className="stat-chip">
Preview Mode: {previewAsCustomer ? 'customer view' : 'off'} Preview Mode: {previewAsCustomer ? 'customer view' : 'off'}
</span> </span>
<span style={{ fontSize: '11px', color: runtimeFlags.enableBacktest ? '#34d399' : '#f87171', fontWeight: 700 }}> <span className="stat-chip" data-tone={runtimeFlags.enableBacktest ? 'success' : 'danger'}>
ENABLE_BACKTEST: {String(runtimeFlags.enableBacktest)} ENABLE_BACKTEST: {String(runtimeFlags.enableBacktest)}
</span> </span>
<span style={{ fontSize: '11px', color: runtimeFlags.customerEnabled ? '#34d399' : '#f87171', fontWeight: 700 }}> <span className="stat-chip" data-tone={runtimeFlags.customerEnabled ? 'success' : 'danger'}>
BACKTEST_CUSTOMER_ENABLED: {String(runtimeFlags.customerEnabled)} BACKTEST_CUSTOMER_ENABLED: {String(runtimeFlags.customerEnabled)}
</span> </span>
</div> </div>
@ -114,132 +107,45 @@ export const BacktestTab: React.FC<BacktestTabProps> = ({ previewAsCustomer = fa
Backtest access is enabled for this view. Backtest access is enabled for this view.
</p> </p>
)} )}
</div> </CardContent>
</Card>
)} )}
{backtestGateLoading ? ( {backtestGateLoading ? (
<div <Card><CardContent style={{ padding: 18, color: 'var(--muted-foreground)', fontSize: 12 }}>Resolving backtest access...</CardContent></Card>
style={{
padding: '18px',
borderRadius: '14px',
background: 'rgba(24,24,27,0.55)',
border: '1px solid rgba(113,113,122,0.25)',
color: '#a1a1aa',
fontSize: '12px'
}}
>
Resolving backtest access...
</div>
) : !backtestEnabled ? ( ) : !backtestEnabled ? (
<div <Card><CardContent style={{ padding: 18, color: '#DC2626', fontSize: 12 }}>Backtest is disabled for this account view.</CardContent></Card>
style={{
padding: '18px',
borderRadius: '14px',
background: 'rgba(127,29,29,0.22)',
border: '1px solid rgba(248,113,113,0.3)',
color: '#fecaca',
fontSize: '12px'
}}
>
Backtest is disabled for this account view.
</div>
) : loadingProfiles ? ( ) : loadingProfiles ? (
<div <Card><CardContent style={{ padding: 18, color: 'var(--muted-foreground)', fontSize: 12 }}>Loading strategy profiles...</CardContent></Card>
style={{
padding: '18px',
borderRadius: '14px',
background: 'rgba(24,24,27,0.55)',
border: '1px solid rgba(113,113,122,0.25)',
color: '#a1a1aa',
fontSize: '12px'
}}
>
Loading strategy profiles...
</div>
) : profiles.length === 0 ? ( ) : profiles.length === 0 ? (
<div <Card><CardContent style={{ padding: 18, color: 'var(--muted-foreground)', fontSize: 12 }}>Create a strategy profile first, then run backtests from this tab.</CardContent></Card>
style={{
padding: '18px',
borderRadius: '14px',
background: 'rgba(24,24,27,0.55)',
border: '1px solid rgba(113,113,122,0.25)',
color: '#a1a1aa',
fontSize: '12px'
}}
>
Create a strategy profile first, then run backtests from this tab.
</div>
) : ( ) : (
<div style={{ display: 'grid', gap: '12px' }}> <div style={{ display: 'grid', gap: '12px' }}>
<div <div className="pill-group">
style={{ <Button type="button" variant={mode === 'single' ? 'default' : 'ghost'} size="sm" onClick={() => setMode('single')}>
display: 'inline-flex',
gap: '6px',
background: 'rgba(24,24,27,0.85)',
border: '1px solid rgba(148,163,184,0.25)',
borderRadius: '999px',
padding: '4px',
width: 'fit-content'
}}
>
<button
type="button"
onClick={() => setMode('single')}
style={{
border: 'none',
borderRadius: '999px',
padding: '8px 12px',
fontSize: '11px',
fontWeight: 800,
cursor: 'pointer',
background: mode === 'single' ? '#22d3ee' : 'transparent',
color: mode === 'single' ? '#06141b' : '#cbd5e1'
}}
>
Single Run Single Run
</button> </Button>
<button <Button type="button" variant={mode === 'compare' ? 'default' : 'ghost'} size="sm" onClick={() => setMode('compare')}>
type="button"
onClick={() => setMode('compare')}
style={{
border: 'none',
borderRadius: '999px',
padding: '8px 12px',
fontSize: '11px',
fontWeight: 800,
cursor: 'pointer',
background: mode === 'compare' ? '#818cf8' : 'transparent',
color: mode === 'compare' ? '#0f1022' : '#cbd5e1'
}}
>
Compare Strategies Compare Strategies
</button> </Button>
</div> </div>
{mode === 'single' ? ( {mode === 'single' ? (
<> <>
<label style={{ display: 'grid', gap: '6px' }}> <label style={{ display: 'grid', gap: '6px' }}>
<span style={{ fontSize: '11px', color: '#9ca3af', fontWeight: 800, textTransform: 'uppercase' }}> <span style={{ fontSize: '11px', color: 'var(--muted-foreground)', fontWeight: 800, textTransform: 'uppercase' }}>
Strategy Profile Strategy Profile
</span> </span>
<select <Select
value={selectedProfileId} value={selectedProfileId}
onChange={(event) => setSelectedProfileId(event.target.value)} onChange={(event) => setSelectedProfileId(event.target.value)}
style={{
padding: '10px 12px',
borderRadius: '10px',
border: '1px solid rgba(148,163,184,0.25)',
background: 'rgba(24,24,27,0.85)',
color: '#fff',
fontSize: '12px'
}}
> >
{profiles.map((item) => ( {profiles.map((item) => (
<option key={item.id} value={item.id}> <option key={item.id} value={item.id}>
{item.name} ({item.symbols}) {item.name} ({item.symbols})
</option> </option>
))} ))}
</select> </Select>
</label> </label>
{selectedProfile && ( {selectedProfile && (

View File

@ -1,8 +1,10 @@
import React from 'react'; import React from 'react';
import { PresetMarketplace } from '../components/PresetMarketplace'; import { PresetMarketplace } from '../components/PresetMarketplace';
import type { StrategyPreset } from '../lib/PresetRegistry'; import type { StrategyPreset } from '../lib/PresetRegistry';
import type { BotState } from '../hooks/useWebSocket'; import type { BotState } from '../hooks/useWebSocket';
import { TrendingUp, Brain } from 'lucide-react'; import { TrendingUp, Brain } from 'lucide-react';
import { PageHeader } from '../components/ui/page-header';
import { Card, CardContent } from '../components/ui/card';
interface MarketplaceTabProps { interface MarketplaceTabProps {
onClone: (preset: StrategyPreset) => void; onClone: (preset: StrategyPreset) => void;
@ -21,12 +23,16 @@ export const MarketplaceTab: React.FC<MarketplaceTabProps> = ({ onClone, botStat
.sort((a, b) => Math.abs(botState.symbols[b].change24h || 0) - Math.abs(botState.symbols[a].change24h || 0)) .sort((a, b) => Math.abs(botState.symbols[b].change24h || 0) - Math.abs(botState.symbols[a].change24h || 0))
.slice(0, 6); .slice(0, 6);
return ( return (
<div style={{ animation: 'fadeIn 0.5s ease-out' }}> <div style={{ animation: 'fadeIn 0.5s ease-out' }}>
<PageHeader
{/* Contextual Intelligence Row */} title="Marketplace"
<div style={{ description="Explore curated strategy presets and compare them against current market context."
display: 'grid', />
{/* Contextual Intelligence Row */}
<div style={{
display: 'grid',
gridTemplateColumns: '1fr 1fr', gridTemplateColumns: '1fr 1fr',
gap: '20px', gap: '20px',
maxWidth: '1400px', maxWidth: '1400px',
@ -34,74 +40,68 @@ export const MarketplaceTab: React.FC<MarketplaceTabProps> = ({ onClone, botStat
padding: '0 20px' padding: '0 20px'
}}> }}>
{/* AI Best Setups Panel */} {/* AI Best Setups Panel */}
<div style={{ <Card className="hero-surface">
background: 'linear-gradient(135deg, rgba(0, 255, 136, 0.04), rgba(0,0,0,0))', <CardContent style={{ padding: '24px 28px' }}>
border: '1px solid rgba(0, 255, 136, 0.1)', <div style={{ display: 'flex', alignItems: 'center', gap: '10px', marginBottom: '20px' }}>
borderRadius: '24px', <div style={{ width: '32px', height: '32px', borderRadius: '10px', background: 'var(--accent-soft)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
padding: '24px 28px' <Brain size={16} color="var(--accent)" />
}}> </div>
<div style={{ display: 'flex', alignItems: 'center', gap: '10px', marginBottom: '20px' }}> <div>
<div style={{ width: '32px', height: '32px', borderRadius: '10px', background: 'rgba(0,255,136,0.1)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}> <div className="section-title">Best AI Setups</div>
<Brain size={16} color="#00ff88" /> <div style={{ fontSize: '10px', color: 'var(--muted-foreground)', fontWeight: 700, marginTop: '1px' }}>High-confidence signals find the right strategy below</div>
</div> </div>
<div> </div>
<div style={{ fontSize: '11px', fontWeight: 900, color: '#00ff88', textTransform: 'uppercase', letterSpacing: '2px' }}>🧠 Best AI Setups</div> <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
<div style={{ fontSize: '10px', color: '#444', fontWeight: 700, marginTop: '1px' }}>High-confidence signals find the right strategy below</div>
</div>
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
{aiSetups.map(s => { {aiSetups.map(s => {
const conf = botState.symbols[s]?.rules['AIAnalysisRule']?.metadata?.confidence || 0; const conf = botState.symbols[s]?.rules['AIAnalysisRule']?.metadata?.confidence || 0;
return ( return (
<div key={s} style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '10px 14px', background: 'rgba(0,0,0,0.2)', borderRadius: '12px', border: '1px solid rgba(255,255,255,0.03)' }}> <div key={s} style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '10px 14px', background: 'var(--card-elevated)', borderRadius: '12px', border: '1px solid var(--border)' }}>
<span style={{ fontWeight: 800, fontSize: '13px', color: 'white' }}>{s}</span> <span style={{ fontWeight: 800, fontSize: '13px', color: 'var(--foreground)' }}>{s}</span>
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}> <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
<div style={{ width: `${Math.min(conf, 60)}px`, height: '3px', background: `hsl(${conf * 1.2}, 100%, 55%)`, borderRadius: '99px' }} /> <div style={{ width: `${Math.min(conf, 60)}px`, height: '3px', background: `hsl(${conf * 1.2}, 100%, 55%)`, borderRadius: '99px' }} />
<span style={{ fontSize: '12px', fontWeight: 900, color: '#00ff88', fontFamily: 'monospace' }}>{conf}%</span> <span style={{ fontSize: '12px', fontWeight: 900, color: 'var(--accent)', fontFamily: 'monospace' }}>{conf}%</span>
</div> </div>
</div> </div>
); );
})} })}
{aiSetups.length === 0 && ( {aiSetups.length === 0 && (
<div style={{ textAlign: 'center', padding: '20px', color: '#333', fontSize: '12px', fontStyle: 'italic' }}>AI analysis scanning markets...</div> <div style={{ textAlign: 'center', padding: '20px', color: 'var(--muted-foreground)', fontSize: '12px', fontStyle: 'italic' }}>AI analysis scanning markets...</div>
)} )}
</div> </div>
</div> </CardContent>
</Card>
{/* Top Volatile Panel */} {/* Top Volatile Panel */}
<div style={{ <Card className="hero-surface">
background: 'linear-gradient(135deg, rgba(255, 170, 0, 0.03), rgba(0,0,0,0))', <CardContent style={{ padding: '24px 28px' }}>
border: '1px solid rgba(255, 170, 0, 0.08)', <div style={{ display: 'flex', alignItems: 'center', gap: '10px', marginBottom: '20px' }}>
borderRadius: '24px', <div style={{ width: '32px', height: '32px', borderRadius: '10px', background: 'rgba(255,170,0,0.1)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
padding: '24px 28px' <TrendingUp size={16} color="#ffaa00" />
}}> </div>
<div style={{ display: 'flex', alignItems: 'center', gap: '10px', marginBottom: '20px' }}> <div>
<div style={{ width: '32px', height: '32px', borderRadius: '10px', background: 'rgba(255,170,0,0.1)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}> <div className="section-title">Top Volatile (24h)</div>
<TrendingUp size={16} color="#ffaa00" /> <div style={{ fontSize: '10px', color: 'var(--muted-foreground)', fontWeight: 700, marginTop: '1px' }}>Most active markets match with strategies below</div>
</div> </div>
<div> </div>
<div style={{ fontSize: '11px', fontWeight: 900, color: '#ffaa00', textTransform: 'uppercase', letterSpacing: '2px' }}>🔥 Top Volatile (24h)</div>
<div style={{ fontSize: '10px', color: '#444', fontWeight: 700, marginTop: '1px' }}>Most active markets match with strategies below</div>
</div>
</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '8px' }}> <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '8px' }}>
{topVolatile.map(s => { {topVolatile.map(s => {
const change = botState.symbols[s]?.change24h || 0; const change = botState.symbols[s]?.change24h || 0;
return ( return (
<div key={s} style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '10px 14px', background: 'rgba(0,0,0,0.2)', borderRadius: '12px', border: '1px solid rgba(255,255,255,0.03)' }}> <div key={s} style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '10px 14px', background: 'var(--card-elevated)', borderRadius: '12px', border: '1px solid var(--border)' }}>
<span style={{ fontWeight: 800, fontSize: '12px', color: '#bbb' }}>{s.split('/')[0]}</span> <span style={{ fontWeight: 800, fontSize: '12px', color: 'var(--foreground)' }}>{s.split('/')[0]}</span>
<span style={{ fontSize: '12px', fontWeight: 900, fontFamily: 'monospace', color: change >= 0 ? '#00ff88' : '#ff3366' }}> <span style={{ fontSize: '12px', fontWeight: 900, fontFamily: 'monospace', color: change >= 0 ? '#00ff88' : '#ff3366' }}>
{change >= 0 ? '+' : ''}{change.toFixed(2)}% {change >= 0 ? '+' : ''}{change.toFixed(2)}%
</span> </span>
</div> </div>
); );
})} })}
{topVolatile.length === 0 && ( {topVolatile.length === 0 && (
<div style={{ gridColumn: '1/-1', textAlign: 'center', padding: '20px', color: '#333', fontSize: '12px', fontStyle: 'italic' }}>Scanning markets...</div> <div style={{ gridColumn: '1/-1', textAlign: 'center', padding: '20px', color: 'var(--muted-foreground)', fontSize: '12px', fontStyle: 'italic' }}>Scanning markets...</div>
)} )}
</div> </div>
</div> </CardContent>
</div> </Card>
</div>
{/* Strategy Marketplace Grid */} {/* Strategy Marketplace Grid */}
<div style={{ maxWidth: '1400px', margin: '0 auto', padding: '0 0 40px 0' }}> <div style={{ maxWidth: '1400px', margin: '0 auto', padding: '0 0 40px 0' }}>

View File

@ -1,316 +1,183 @@
import React from 'react'; import React from 'react';
import { import {
Crown, Crown,
Zap, Zap,
Lock, Lock,
CheckCircle2, CheckCircle2,
Sparkles, Sparkles,
Dna, Fingerprint,
ArrowUpRight, Cpu,
Fingerprint, Target,
Cpu, Activity,
Target, ArrowUpRight
Activity } from 'lucide-react';
} from 'lucide-react'; import { TIER_POLICIES } from '../lib/TierPolicy';
import { TIER_POLICIES } from '../lib/TierPolicy'; import { PageHeader } from '../components/ui/page-header';
import { Card, CardContent } from '../components/ui/card';
const PlanCard: React.FC<{ import { Button } from '../components/ui/button';
id: string;
policy: any; const PlanCard: React.FC<{
}> = ({ id, policy }) => { id: string;
const isElite = id === 'elite'; policy: any;
const isPro = id === 'pro'; }> = ({ id, policy }) => {
const themeColor = isElite ? '#00ff88' : isPro ? '#3498db' : '#929292'; const isElite = id === 'elite';
const isPro = id === 'pro';
const price = id === 'free' ? '$0' : id === 'pro' ? '$49' : '$199'; const themeColor = isElite ? '#00ff88' : isPro ? '#3498db' : '#929292';
const description = id === 'free' ? 'Perfect for learning the fundamentals of automated trading with zero risk.' :
id === 'pro' ? 'For serious traders looking to scale their operations with more bots and flexibility.' : const price = id === 'free' ? '$0' : id === 'pro' ? '$49' : '$199';
'The full Bytelyst ecosystem with zero restrictions and maximum performance.'; const description = id === 'free'
? 'Perfect for learning the fundamentals of automated trading with zero risk.'
const specs = [ : id === 'pro'
{ label: 'Latency', value: id === 'free' ? 'Standard' : id === 'pro' ? 'Priority' : 'Real-time', icon: <Cpu size={14} /> }, ? 'For serious traders looking to scale with more bots and more flexibility.'
{ label: 'Capacity', value: id === 'free' ? '1 Node' : id === 'pro' ? '5 Nodes' : 'Unlimited', icon: <Target size={14} /> }, : 'The full Bytelyst ecosystem with zero restrictions and maximum performance.';
{ label: 'Access', value: id === 'free' ? 'Basic' : id === 'pro' ? 'Full' : 'Exclusive', icon: <Fingerprint size={14} /> },
{ label: 'Performance', value: id === 'free' ? 'Capped' : id === 'pro' ? 'Advanced' : 'Unlimited', icon: <Activity size={14} /> } const specs = [
]; { label: 'Latency', value: id === 'free' ? 'Standard' : id === 'pro' ? 'Priority' : 'Real-time', icon: <Cpu size={14} /> },
{ label: 'Capacity', value: id === 'free' ? '1 Node' : id === 'pro' ? '5 Nodes' : 'Unlimited', icon: <Target size={14} /> },
return ( { label: 'Access', value: id === 'free' ? 'Basic' : id === 'pro' ? 'Full' : 'Exclusive', icon: <Fingerprint size={14} /> },
<div style={{ { label: 'Performance', value: id === 'free' ? 'Capped' : id === 'pro' ? 'Advanced' : 'Unlimited', icon: <Activity size={14} /> }
background: '#14151a', ];
borderRadius: '28px',
border: '1px solid rgba(255, 255, 255, 0.05)', return (
padding: '32px', <Card className="h-full rounded-[28px]">
display: 'flex', <CardContent className="flex min-h-[620px] flex-col p-8">
flexDirection: 'column', <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 28 }}>
position: 'relative', <div style={{ display: 'flex', gap: 14, alignItems: 'center' }}>
overflow: 'hidden', <div style={{
transition: 'all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275)', width: 44,
cursor: 'default', height: 44,
height: '100%', borderRadius: 14,
minHeight: '620px' background: 'var(--card-elevated)',
}} className="membership-card-hover"> display: 'flex',
alignItems: 'center',
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '28px' }}> justifyContent: 'center',
<div style={{ display: 'flex', gap: '14px', alignItems: 'center' }}> border: '1px solid var(--border)',
<div style={{ color: themeColor
width: '44px', }}>
height: '44px', {isElite ? <Crown size={22} /> : isPro ? <Zap size={22} fill="currentColor" /> : <Lock size={20} />}
borderRadius: '14px', </div>
background: 'rgba(255,255,255,0.03)', <div style={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
display: 'flex', <span style={{ fontSize: 10, fontWeight: 900, color: 'var(--muted-foreground)', textTransform: 'uppercase', letterSpacing: '2px' }}>
alignItems: 'center', Tier level
justifyContent: 'center', </span>
border: '1px solid rgba(255, 255, 255, 0.05)', <span style={{ fontSize: 13, fontWeight: 800, color: 'var(--foreground)', textTransform: 'uppercase', letterSpacing: '0.5px' }}>
color: themeColor {policy.label}
}}> </span>
{isElite ? <Crown size={22} /> : isPro ? <Zap size={22} fill="currentColor" /> : <Lock size={20} />} </div>
</div> </div>
<div style={{ display: 'flex', flexDirection: 'column', gap: '2px' }}> {isElite && (
<span style={{ fontSize: '10px', fontWeight: 900, color: '#444', textTransform: 'uppercase', letterSpacing: '2px' }}>Tier Level</span> <span className="stat-chip" data-tone="success">
<span style={{ fontSize: '13px', fontWeight: 800, color: 'white', textTransform: 'uppercase', letterSpacing: '0.5px' }}>{policy.label}</span> Peak
</div> </span>
</div> )}
{isElite && ( </div>
<div style={{
background: 'rgba(0, 255, 136, 0.1)', <div style={{ marginBottom: 24, display: 'flex', flexDirection: 'column', alignItems: 'flex-start', gap: 12 }}>
border: '1px solid rgba(0, 255, 136, 0.2)', <div style={{ display: 'flex', alignItems: 'baseline', gap: 4 }}>
padding: '6px 12px', <h3 style={{ fontSize: 42, fontWeight: 950, color: 'var(--foreground)', letterSpacing: '-0.04em', margin: 0 }}>{price}</h3>
borderRadius: '10px', <span style={{ fontSize: 14, color: 'var(--muted-foreground)', fontWeight: 900, textTransform: 'uppercase' }}>/mo</span>
fontSize: '10px', </div>
fontWeight: 900, <div className="stat-chip">
color: '#00ff88', <Sparkles size={14} style={{ color: '#fbbf24' }} />
textTransform: 'uppercase' Professional grade DNA
}}> </div>
Peak </div>
</div>
)} <p style={{
</div> fontSize: 14,
color: 'var(--muted-foreground)',
<div style={{ marginBottom: '24px', display: 'flex', flexDirection: 'column', alignItems: 'flex-start', gap: '12px' }}> lineHeight: '1.6',
<div style={{ display: 'flex', alignItems: 'baseline', gap: '4px' }}> marginBottom: 28,
<h3 style={{ fontSize: '42px', fontWeight: 950, color: 'white', letterSpacing: '-0.04em', margin: 0 }}>{price}</h3> textAlign: 'left',
<span style={{ fontSize: '14px', color: '#444', fontWeight: 900, textTransform: 'uppercase' }}>/mo</span> flex: 1
</div> }}>
<div style={{ {description}
display: 'inline-flex', </p>
alignItems: 'center',
gap: '8px', <div style={{
background: 'rgba(255, 255, 255, 0.03)', display: 'grid',
padding: '6px 14px', gridTemplateColumns: '1fr 1fr',
borderRadius: '8px', gap: 14,
fontSize: '11px', marginBottom: 28
color: '#929292', }}>
fontWeight: 900, {specs.map((spec, i) => (
textTransform: 'uppercase', <div key={i} style={{
border: '1px solid rgba(255, 255, 255, 0.05)', background: 'var(--card-elevated)',
letterSpacing: '0.5px' border: '1px solid var(--border)',
}}> padding: 16,
<Sparkles size={14} style={{ color: '#fbbf24' }} /> Professional Grade DNA borderRadius: 20,
</div> display: 'flex',
</div> flexDirection: 'column',
gap: 6,
<p style={{ alignItems: 'flex-start'
fontSize: '14px', }}>
color: '#888', <div style={{ display: 'flex', alignItems: 'center', gap: 8, color: 'var(--muted-foreground)', fontSize: 10, fontWeight: 900, textTransform: 'uppercase', letterSpacing: '1px' }}>
lineHeight: '1.6', {spec.icon} {spec.label}
marginBottom: '28px', </div>
textAlign: 'left', <div style={{ color: 'var(--foreground)', fontWeight: 900, fontSize: 15 }}>{spec.value}</div>
flex: 1 </div>
}}> ))}
{description} Enhanced with Bytelyst's core strategy engine and isolated security nodes. </div>
</p>
<div style={{ display: 'flex', flexDirection: 'column', gap: 10, marginBottom: 32 }}>
<div style={{ <div style={{ display: 'flex', alignItems: 'center', gap: 12, fontSize: 12, color: 'var(--muted-foreground)', fontWeight: 700 }}>
display: 'grid', <CheckCircle2 size={16} style={{ color: themeColor }} />
gridTemplateColumns: '1fr 1fr', {id === 'free' ? 'Safe style only' : id === 'pro' ? 'Safe and balanced' : 'All risk styles unlocked'}
gap: '14px', </div>
marginBottom: '28px' <div style={{ display: 'flex', alignItems: 'center', gap: 12, fontSize: 12, color: 'var(--muted-foreground)', fontWeight: 700 }}>
}}> <CheckCircle2 size={16} style={{ color: themeColor }} />
{specs.map((spec, i) => ( {id === 'free' ? '$100 profit limit' : id === 'pro' ? '$2,000 profit limit' : 'Zero profit restrictions'}
<div key={i} style={{ </div>
background: 'rgba(0,0,0,0.2)', </div>
border: '1px solid rgba(255,255,255,0.03)',
padding: '16px', <div style={{ display: 'flex', gap: 12, marginTop: 'auto' }}>
borderRadius: '20px', <Button
display: 'flex', disabled={id !== 'elite'}
flexDirection: 'column', variant={isElite ? 'default' : 'outline'}
gap: '6px', size="lg"
alignItems: 'flex-start' className="flex-1 rounded-[18px] text-[12px] font-black tracking-[1.5px]"
}}> >
<div style={{ display: 'flex', alignItems: 'center', gap: '8px', color: '#555', fontSize: '10px', fontWeight: 900, textTransform: 'uppercase', letterSpacing: '1px' }}> {isElite ? 'ACTIVATE PREVIEW' : 'COMING SOON'}
{spec.icon} {spec.label} {isElite && <ArrowUpRight size={18} strokeWidth={3} />}
</div> </Button>
<div style={{ color: 'white', fontWeight: 900, fontSize: '15px' }}>{spec.value}</div> </div>
</div>
))} <div style={{
</div> marginTop: 24,
height: 4,
<div style={{ display: 'flex', flexDirection: 'column', gap: '10px', marginBottom: '32px' }}> borderRadius: 999,
<div style={{ display: 'flex', alignItems: 'center', gap: '12px', fontSize: '12px', color: 'rgba(255,255,255,0.4)', fontWeight: 700 }}> background: `linear-gradient(90deg, transparent, ${themeColor}, transparent)`,
<CheckCircle2 size={16} style={{ color: themeColor }} /> opacity: 0.2
{id === 'free' ? 'Safe Style Only' : id === 'pro' ? 'Safe & Balanced' : 'All Risk Styles Unlocked'} }} />
</div> </CardContent>
<div style={{ display: 'flex', alignItems: 'center', gap: '12px', fontSize: '12px', color: 'rgba(255,255,255,0.4)', fontWeight: 700 }}> </Card>
<CheckCircle2 size={16} style={{ color: themeColor }} /> );
{id === 'free' ? '$100 Profit Limit' : id === 'pro' ? '$2,000 Profit Limit' : 'Zero Profit Restrictions'} };
</div>
</div> export const MembershipTab: React.FC = () => {
return (
<div style={{ display: 'flex', gap: '12px', marginTop: 'auto' }}> <div style={{ maxWidth: 1400, margin: '0 auto', padding: '0 20px 100px 20px' }}>
<button <PageHeader
disabled={id !== 'elite'} title="Membership Plans"
style={{ description="Upgrade your algorithmic edge with advanced strategies, higher capacity, and priority execution."
flex: 1, />
height: '56px',
background: isElite ? '#00ff88' : 'rgba(255,255,255,0.02)', <div style={{ display: 'flex', gap: 12, flexWrap: 'wrap', marginBottom: 28 }}>
color: isElite ? 'black' : '#444', <span className="stat-chip">3 tiers</span>
borderRadius: '18px', <span className="stat-chip">Preview pricing</span>
border: isElite ? 'none' : '1px solid rgba(255,255,255,0.05)', <span className="stat-chip">Product direction only</span>
fontWeight: 900, </div>
fontSize: '12px',
textTransform: 'uppercase', <div style={{
cursor: isElite ? 'pointer' : 'not-allowed', display: 'grid',
display: 'flex', gridTemplateColumns: 'repeat(auto-fill, minmax(320px, 1fr))',
alignItems: 'center', gap: 32,
justifyContent: 'center', width: '100%'
gap: '10px', }}>
boxShadow: isElite ? '0 12px 36px -12px rgba(0, 255, 136, 0.4)' : 'none', {Object.entries(TIER_POLICIES).map(([id, policy]) => (
transition: 'all 0.2s', <PlanCard key={id} id={id} policy={policy} />
letterSpacing: '1.5px' ))}
}} </div>
className={isElite ? "clone-btn" : ""} </div>
> );
{isElite ? 'ACTIVATE PREVIEW' : 'COMING SOON'} {isElite && <ArrowUpRight size={18} strokeWidth={3} />} };
</button>
</div>
<div style={{
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
height: '4px',
background: `linear-gradient(90deg, transparent, ${themeColor}, transparent)`,
opacity: 0.2
}} />
<style>{`
.membership-card-hover:hover {
border-color: ${themeColor}40 !important;
transform: translateY(-8px);
box-shadow: 0 40px 80px -20px rgba(0,0,0,0.8) !important;
background: #1a1b21 !important;
}
.clone-btn:hover {
filter: brightness(1.1);
transform: scale(1.02);
}
`}</style>
</div>
);
};
export const MembershipTab: React.FC = () => {
return (
<div style={{
maxWidth: '1400px',
margin: '0 auto',
padding: '0 20px 100px 20px',
animation: 'fadeIn 0.7s ease-out'
}}>
<div style={{
display: 'flex',
flexDirection: 'column',
marginBottom: '60px',
padding: '60px 0',
borderBottom: '1px solid rgba(255, 255, 255, 0.05)',
position: 'relative',
alignItems: 'flex-start'
}}>
<div style={{
position: 'absolute',
top: 0,
right: 0,
opacity: 0.03,
pointerEvents: 'none',
transform: 'translate(40px, -20px)'
}}>
<Dna size={320} strokeWidth={1} />
</div>
<div style={{
display: 'inline-flex',
alignItems: 'center',
gap: '12px',
color: '#00ff88',
fontSize: '11px',
fontWeight: 900,
textTransform: 'uppercase',
letterSpacing: '4px',
marginBottom: '24px'
}}>
PRICING PHILOSOPHY
<div style={{ width: '30px', height: '1px', background: '#00ff88', opacity: 0.2 }} />
</div>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end', width: '100%', flexWrap: 'wrap', gap: '32px' }}>
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start' }}>
<h2 style={{
fontSize: '84px',
fontWeight: 950,
color: 'white',
letterSpacing: '-0.04em',
lineHeight: '0.9',
margin: 0,
textTransform: 'uppercase'
}}>
Membership<br />
<span style={{ color: '#00ff88' }}>Plans</span>
</h2>
<p style={{ fontSize: '20px', color: '#666', marginTop: '24px', maxWidth: '600px', fontWeight: 500, margin: '24px 0 0 0', textAlign: 'left' }}>
Upgrade your algorithmic edge with advanced strategies and priority execution.
</p>
</div>
<div style={{ display: 'flex', gap: '16px', alignItems: 'center' }}>
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-end', padding: '0 24px', borderLeft: '1px solid rgba(255, 255, 255, 0.08)' }}>
<span style={{ color: '#444', fontSize: '11px', fontWeight: 900, textTransform: 'uppercase', letterSpacing: '2px' }}>Tiers</span>
<span style={{ color: 'white', fontSize: '32px', fontWeight: 950, lineHeight: '1' }}>3</span>
</div>
<div style={{
background: 'rgba(251, 191, 36, 0.05)',
border: '1px solid rgba(251, 191, 36, 0.1)',
padding: '12px 24px',
borderRadius: '16px',
color: '#fbbf24',
fontSize: '11px',
fontWeight: 900,
textTransform: 'uppercase',
letterSpacing: '1px'
}}>
Global Preview Mode Active
</div>
</div>
</div>
</div>
<div style={{
display: 'grid',
gridTemplateColumns: 'repeat(auto-fill, minmax(320px, 1fr))',
gap: '40px',
width: '100%'
}}>
{Object.entries(TIER_POLICIES).map(([id, policy]) => (
<PlanCard key={id} id={id} policy={policy} />
))}
</div>
<style>{`
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
`}</style>
</div>
);
};

View File

@ -7,6 +7,7 @@ import { VisualRuleBuilder, type VisualRule } from '../components/strategy/Visua
import { createTradeProfile } from '../lib/profileApi'; import { createTradeProfile } from '../lib/profileApi';
import { BacktestRunnerPanel } from '../backtest/components/BacktestRunnerPanel'; import { BacktestRunnerPanel } from '../backtest/components/BacktestRunnerPanel';
import { PageHeader } from '../components/ui/page-header'; import { PageHeader } from '../components/ui/page-header';
import { Card, CardContent } from '../components/ui/card';
type ResearchTab = 'Strategies' | 'Visual Builder' | 'Code Editor' | 'Signals' | 'Backtesting'; type ResearchTab = 'Strategies' | 'Visual Builder' | 'Code Editor' | 'Signals' | 'Backtesting';
@ -105,10 +106,10 @@ export function ResearchView() {
{tab === 'Visual Builder' && ( {tab === 'Visual Builder' && (
<div> <div>
<div style={{ marginBottom: 16 }}> <div style={{ marginBottom: 16 }}>
<div style={{ fontSize: 15, fontWeight: 700, color: '#111827', marginBottom: 4 }}> <div className="section-title">
Visual Rule Builder Visual Rule Builder
</div> </div>
<div style={{ fontSize: 13, color: '#6B7280' }}> <div className="section-description">
Build a trading strategy by composing IF/THEN rules. Drag rows to reorder. Click "Save Strategy" to store it. Build a trading strategy by composing IF/THEN rules. Drag rows to reorder. Click "Save Strategy" to store it.
</div> </div>
</div> </div>
@ -134,10 +135,10 @@ export function ResearchView() {
{tab === 'Code Editor' && ( {tab === 'Code Editor' && (
<div> <div>
<div style={{ marginBottom: 16 }}> <div style={{ marginBottom: 16 }}>
<div style={{ fontSize: 15, fontWeight: 700, color: '#111827', marginBottom: 4 }}> <div className="section-title">
Code Strategy Editor Code Strategy Editor
</div> </div>
<div style={{ fontSize: 13, color: '#6B7280' }}> <div className="section-description">
Write a custom strategy function in JavaScript. Click "Run Backtest" to test it against historical data. Write a custom strategy function in JavaScript. Click "Run Backtest" to test it against historical data.
</div> </div>
</div> </div>
@ -164,23 +165,10 @@ export function ResearchView() {
function CodeStrategyEditorFallback() { function CodeStrategyEditorFallback() {
return ( return (
<div <Card role="status" aria-label="Loading code strategy editor" className="hero-surface min-h-[420px]">
role="status" <CardContent className="flex min-h-[420px] items-center justify-center text-sm font-semibold text-[var(--muted-foreground)]">
aria-label="Loading code strategy editor" Loading code strategy editor
style={{ </CardContent>
minHeight: 420, </Card>
border: '1px solid #E5E7EB',
borderRadius: 12,
background: 'linear-gradient(135deg, #F8FAFC, #EEF2FF)',
color: '#4B5563',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: 13,
fontWeight: 700,
}}
>
Loading code strategy editor
</div>
); );
} }