Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion ui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ export type Page =
| 'trading-as-git'
| 'settings' | 'dev'
| 'traditional-chat' | 'notifications-legacy' | 'connectors-legacy'
| 'trading-accounts'

/** Track whether we're at a desktop viewport (md+ in Tailwind = ≥768px). */
function useIsDesktop(): boolean {
Expand Down
23 changes: 10 additions & 13 deletions ui/src/components/ActivityBar.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type LucideIcon, MessageSquare, MessagesSquare, Inbox, Bell, LineChart, GitBranch, BarChart3, Newspaper, Zap, Settings, Code2, TerminalSquare, ChevronDown, Plug, Landmark, Info } from 'lucide-react'
import { type LucideIcon, MessageSquare, MessagesSquare, Inbox, Bell, LineChart, GitBranch, BarChart3, Newspaper, Zap, Settings, Code2, TerminalSquare, ChevronDown, Plug, Info } from 'lucide-react'
import { useState } from 'react'
import { type Page } from '../App'
import { useWorkspace } from '../tabs/store'
Expand All @@ -25,7 +25,6 @@ function activitySectionFor(page: Page): ActivitySection {
case 'traditional-chat': return 'traditional-chat'
case 'notifications-legacy': return 'notifications-legacy'
case 'connectors-legacy': return 'connectors-legacy'
case 'trading-accounts': return 'trading-accounts'
}
}

Expand Down Expand Up @@ -101,20 +100,18 @@ const NAV_SECTIONS: NavSection[] = [
{ page: 'news', label: 'News', icon: Newspaper, defaultTab: { kind: 'news', params: {} } },
],
},
// Beta — functional but unstable. Goal: unified abstraction across
// broker accounts (Trading Accounts) + the Trading-as-Git workflow
// + the Portfolio view that surfaces it. Large engineering ahead,
// no fixed timeline — configurable today, but lock-in cost can
// change as the abstraction settles. Default-expanded because the
// items here are actively useful; the Beta label is the right
// amount of caution, not a hide.
// Beta — functional but unstable. The underlying cross-broker
// unification (UTA abstraction, FX/options/futures) is in active
// rearchitecture. Portfolio surfaces that state; Trading-as-Git is
// the operations side (pending broker writes). Broker connection
// CRUD lives under Settings → Trading, not here — it's a config
// surface, not a state/ops one.
{
sectionLabel: 'Beta',
description: 'Goal here is a unified abstraction across broker accounts (deposit/withdraw, options, futures, FX). Large engineering effort, no fixed timeline. Configure and try, but don\'t depend on schema or UX as stable yet.',
description: 'Cross-broker unified state + ops surfaces. The abstraction underneath is still being settled — try them, but don\'t depend on schema or UX as stable yet. Broker connection setup lives in Settings → Trading.',
items: [
{ page: 'trading-accounts', label: 'Trading Accounts', icon: Landmark, defaultTab: { kind: 'settings', params: { category: 'trading' } } },
{ page: 'trading-as-git', label: 'Trading as Git', icon: GitBranch },
{ page: 'portfolio', label: 'Portfolio', icon: LineChart, defaultTab: { kind: 'portfolio', params: {} } },
{ page: 'trading-as-git', label: 'Trading as Git', icon: GitBranch },
{ page: 'portfolio', label: 'Portfolio', icon: LineChart, defaultTab: { kind: 'portfolio', params: {} } },
],
},
{
Expand Down
2 changes: 1 addition & 1 deletion ui/src/components/PortfolioSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export function PortfolioSidebar() {
<p className="px-3 py-1 text-[12px] text-text-muted/60">Loading…</p>
) : utas.length === 0 ? (
<p className="px-3 py-1 text-[12px] text-text-muted/60 leading-snug">
No accounts yet. Add one in Settings → Trading Accounts.
No accounts yet. Add one in Settings → Trading.
</p>
) : (
utas.map((uta) => {
Expand Down
21 changes: 6 additions & 15 deletions ui/src/components/SettingsCategoryList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,16 @@ type SettingsCategory = Extract<ViewSpec, { kind: 'settings' }>['params']['categ
interface CategoryItem {
label: string
category: SettingsCategory
/**
* Other view kinds that count as "active" for this row. Used by
* Trading Accounts: when a uta-detail tab is focused, Trading
* Accounts should still light up.
*/
alsoActiveFor?: ViewSpec['kind'][]
}

const CATEGORIES: CategoryItem[] = [
{ label: 'General', category: 'general' },
{ label: 'AI Provider', category: 'ai-provider' },
// Trading Accounts moved to its own ActivityBar Beta entry — see
// TradingAccountsBetaSidebar. The `settings/trading` ViewSpec is
// still the underlying tab.
{ label: 'General', category: 'general' },
{ label: 'AI Provider', category: 'ai-provider' },
{ label: 'Trading', category: 'trading' },
// Connectors moved to its own ActivityBar Legacy entry — see
// ConnectorsLegacySidebar.
{ label: 'MCP Server', category: 'mcp' },
{ label: 'Market Data', category: 'market-data' },
{ label: 'MCP Server', category: 'mcp' },
{ label: 'Market Data', category: 'market-data' },
{ label: 'News Sources', category: 'news-collector' },
]

Expand All @@ -41,8 +33,7 @@ export function SettingsCategoryList() {
<div className="py-0.5">
{CATEGORIES.map((item) => {
const active =
(focused?.kind === 'settings' && focused.params.category === item.category) ||
(item.alsoActiveFor != null && focused != null && item.alsoActiveFor.includes(focused.kind))
focused?.kind === 'settings' && focused.params.category === item.category
return (
<SidebarRow
key={item.category}
Expand Down
45 changes: 0 additions & 45 deletions ui/src/components/TradingAccountsBetaSidebar.tsx

This file was deleted.

36 changes: 28 additions & 8 deletions ui/src/components/uta/EditUTADialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,21 @@ import { SchemaFormFields } from './SchemaFormFields'

/**
* UTA configuration dialog — edits credentials, guards, enabled state.
* Mounted from both the trading page (legacy entry) and the UTA detail
* page (new entry, accessed via the Edit button in the page header).
* Mounted from Settings → Trading (primary CRUD entry) and from the UTA
* detail page in Portfolio (sibling Edit button).
*
* When opened from Settings → Trading, the parent passes `onViewInPortfolio`
* to render a header link that switches the user over to the Portfolio
* drill-in for this account. When opened from inside Portfolio's detail
* page, that prop is omitted (the user is already in that context).
*/
export function EditUTADialog({ uta, preset, health, onSave, onDelete, onClose }: {
export function EditUTADialog({ uta, preset, health, onSave, onDelete, onViewInPortfolio, onClose }: {
uta: UTAConfig
preset?: BrokerPreset
health?: BrokerHealthInfo
onSave: (a: UTAConfig) => Promise<void>
onDelete: () => Promise<void>
onViewInPortfolio?: () => void
onClose: () => void
}) {
const [draft, setDraft] = useState(uta)
Expand Down Expand Up @@ -74,11 +80,25 @@ export function EditUTADialog({ uta, preset, health, onSave, onDelete, onClose }
<h3 className="text-[14px] font-semibold text-text truncate">{uta.id}</h3>
<HealthBadge health={health} size="md" />
</div>
<button onClick={onClose} className="text-text-muted hover:text-text p-1 transition-colors shrink-0">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
<path d="M18 6L6 18M6 6l12 12" />
</svg>
</button>
<div className="flex items-center gap-3 shrink-0">
{onViewInPortfolio && (
<button
onClick={onViewInPortfolio}
className="text-[11px] text-text-muted hover:text-text inline-flex items-center gap-1 transition-colors"
title="See this account's positions and equity in Portfolio"
>
View in Portfolio
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M7 17L17 7M9 7h8v8" />
</svg>
</button>
)}
<button onClick={onClose} className="text-text-muted hover:text-text p-1 transition-colors">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
<path d="M18 6L6 18M6 6l12 12" />
</svg>
</button>
</div>
</div>

{/* Body */}
Expand Down
28 changes: 27 additions & 1 deletion ui/src/pages/PortfolioPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useState, useEffect, useCallback, useMemo } from 'react'
import { api, type Position, type WalletCommitLog, type EquityCurvePoint, type UTASnapshotSummary } from '../api'
import { useAutoSave } from '../hooks/useAutoSave'
import { useAccountHealth } from '../hooks/useAccountHealth'
import { useWorkspace } from '../tabs/store'
import { PageHeader } from '../components/PageHeader'
import { EmptyState } from '../components/StateViews'
import { EquityCurve } from '../components/EquityCurve'
Expand Down Expand Up @@ -277,7 +278,7 @@ export function PortfolioPage() {

{/* Empty states */}
{data.accounts.length === 0 && !loading && (
<EmptyState title="No trading accounts connected." description="Configure connections in the Trading page." />
<NoAccountsEmpty />
)}
{data.accounts.length > 0 && allPositions.length === 0 && !loading && (
<EmptyState title="No open positions." />
Expand Down Expand Up @@ -334,6 +335,31 @@ async function fetchPortfolioData(): Promise<PortfolioData> {
}
}

// ==================== Empty: no trading accounts ====================

function NoAccountsEmpty() {
const openOrFocus = useWorkspace((s) => s.openOrFocus)
const setSidebar = useWorkspace((s) => s.setSidebar)
const goToTradingSettings = () => {
setSidebar('settings')
openOrFocus({ kind: 'settings', params: { category: 'trading' } })
}
return (
<div className="flex flex-col items-center justify-center py-16 text-center">
<p className="text-sm font-medium text-text-muted">No trading accounts connected.</p>
<p className="text-[12px] text-text-muted/60 mt-1.5 max-w-[320px]">
Portfolio shows live equity, positions and PnL across all your brokers. Add a connection to get started.
</p>
<button
onClick={goToTradingSettings}
className="mt-4 btn-primary text-[12px]"
>
Add broker in Settings → Trading
</button>
</div>
)
}

// ==================== Hero Metrics ====================

function HeroMetrics({ equity, curve }: {
Expand Down
Loading
Loading