feat: new cockpit
This commit is contained in:
52
frontend/src/components/TradeRankList.tsx
Normal file
52
frontend/src/components/TradeRankList.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
import { Link } from 'react-router-dom'
|
||||
import { ArrowUpRight } from 'lucide-react'
|
||||
import clsx from 'clsx'
|
||||
|
||||
export default function TradeRankList({ trades, mode, title, linkTo }: {
|
||||
trades: any[]
|
||||
mode: 'top' | 'worst'
|
||||
title: string
|
||||
linkTo: string
|
||||
}) {
|
||||
const sorted = [...trades]
|
||||
.sort((a, b) => mode === 'top'
|
||||
? (b.pnl_pct ?? -999) - (a.pnl_pct ?? -999)
|
||||
: (a.pnl_pct ?? 999) - (b.pnl_pct ?? 999))
|
||||
.slice(0, 5)
|
||||
|
||||
return (
|
||||
<Link to={linkTo} className="card block hover:border-slate-600/60 transition-all cursor-pointer">
|
||||
<div className="flex items-center justify-between mb-1">
|
||||
<span className="section-title mb-0 text-[10px]">{title}</span>
|
||||
<ArrowUpRight className="w-3 h-3 text-slate-600" />
|
||||
</div>
|
||||
{sorted.length > 0 ? (
|
||||
<div className="space-y-1.5 mt-1">
|
||||
{sorted.map((t: any, i: number) => {
|
||||
const pnl: number | null = t.pnl_pct ?? null
|
||||
const isBear = t.strategy?.toLowerCase().includes('put') || t.strategy?.toLowerCase().includes('bear')
|
||||
const entryDate = t.entry_date ? t.entry_date.slice(0, 10) : null
|
||||
return (
|
||||
<div key={i} className="flex items-center gap-1.5 text-[10px]">
|
||||
<span className="text-[9px] text-slate-700 font-mono w-3 shrink-0">{i + 1}</span>
|
||||
<span className="shrink-0">{isBear ? '🐻' : '🐂'}</span>
|
||||
<span className="font-mono text-slate-200 font-bold shrink-0">{t.underlying ?? '—'}</span>
|
||||
<span className="text-slate-500 truncate flex-1 text-[9px]">{t.strategy ?? ''}</span>
|
||||
{entryDate && <span className="text-[8px] text-slate-700 shrink-0 font-mono">{entryDate}</span>}
|
||||
{pnl !== null ? (
|
||||
<span className={clsx('font-mono font-bold shrink-0', pnl >= 0 ? 'text-emerald-400' : 'text-red-400')}>
|
||||
{pnl >= 0 ? '+' : ''}{pnl.toFixed(1)}%
|
||||
</span>
|
||||
) : (
|
||||
<span className="text-slate-700 shrink-0 text-[9px]">—</span>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-[10px] text-slate-600 mt-1">No trades logged</div>
|
||||
)}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
@@ -59,7 +59,7 @@ export default function Sidebar() {
|
||||
<div className="flex items-center gap-2">
|
||||
<Zap className="w-5 h-5 text-blue-400" />
|
||||
<div>
|
||||
<div className="text-sm font-bold text-white tracking-wide">GeoOptions</div>
|
||||
<div className="text-sm font-bold text-white tracking-wide">OpenFin</div>
|
||||
<div className="text-xs text-slate-500">Intelligence v2.0</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -115,7 +115,7 @@ export default function Sidebar() {
|
||||
|
||||
{/* Footer */}
|
||||
<div className="p-3 border-t border-slate-700/40 text-xs text-slate-600">
|
||||
<div>© 2026 GeoOptions</div>
|
||||
<div>© 2026 OpenFin</div>
|
||||
<div className="text-slate-700 mt-0.5">Local · IBKR ready</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
Reference in New Issue
Block a user