Skip to content
9 changes: 8 additions & 1 deletion packages/dashboard/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ export function App() {
paperAccount, paperPositions, paperOrders,
requestChain, sendRaw, login, register, logout,
saveAgentConfig, requestAgentConfig, sendTestAlert,
requestWatchlist, saveWatchlist, deleteWatchlistItem, syncTastytradeWatchlists,
requestWatchlist, saveWatchlist, deleteWatchlistItem,
createWatchlist, deleteWatchlist, renameWatchlist,
syncTastytradeWatchlists,
searchSymbols, sendChatMessage, clearChat,
saveScheduleConfig, requestScheduleConfig, requestBudgetStatus,
requestReports, runAnalysisNow,
Expand Down Expand Up @@ -153,6 +155,9 @@ export function App() {
onRequestWatchlist={requestWatchlist}
onSave={saveWatchlist}
onDelete={deleteWatchlistItem}
onCreate={createWatchlist}
onDeleteWatchlist={deleteWatchlist}
onRenameWatchlist={renameWatchlist}
onSync={syncTastytradeWatchlists}
onSearch={searchSymbols}
/>
Expand All @@ -176,6 +181,7 @@ export function App() {
<ReportsPanel
reports={reports}
budgetStatus={budgetStatus}
agentStatus={agentStatus}
onRequestReports={requestReports}
onRunNow={runAnalysisNow}
onRequestBudget={requestBudgetStatus}
Expand All @@ -188,6 +194,7 @@ export function App() {
onSave={saveAgentConfig}
onRequest={requestAgentConfig}
onTestAlert={sendTestAlert}
env={env}
scheduleConfig={scheduleConfig}
budgetStatus={budgetStatus}
onSaveSchedule={saveScheduleConfig}
Expand Down
9 changes: 5 additions & 4 deletions packages/dashboard/src/components/AgentSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ interface AgentSettingsProps {
onSave: (config: { provider: string; apiKey?: string; model?: string; maxBudgetUsd?: number; externalUrl?: string }) => void
onRequest: () => void
onTestAlert: (ticker: string) => void
env: 'sandbox' | 'production'
scheduleConfig: ScheduleConfigResponse | null
budgetStatus: BudgetStatus | null
onSaveSchedule: (cfg: Partial<ScheduleConfig>) => void
Expand All @@ -19,7 +20,7 @@ const MODELS = [
'claude-haiku-3-20250422',
]

export function AgentSettings({ config, onSave, onRequest, onTestAlert, scheduleConfig, budgetStatus, onSaveSchedule, onRequestSchedule, onRequestBudget }: AgentSettingsProps) {
export function AgentSettings({ config, onSave, onRequest, onTestAlert, env, scheduleConfig, budgetStatus, onSaveSchedule, onRequestSchedule, onRequestBudget }: AgentSettingsProps) {
const [provider, setProvider] = useState<string>(config?.provider ?? 'none')
const [apiKey, setApiKey] = useState('')
const [model, setModel] = useState(config?.model ?? 'claude-sonnet-4-20250514')
Expand Down Expand Up @@ -214,7 +215,7 @@ export function AgentSettings({ config, onSave, onRequest, onTestAlert, schedule
{saved ? 'Saved!' : 'Save Configuration'}
</button>

{provider !== 'none' && (
{provider !== 'none' && env === 'sandbox' && (
<button
type="button"
disabled={provider === 'claude-sdk' && !hasKey && !apiKey}
Expand All @@ -228,10 +229,10 @@ export function AgentSettings({ config, onSave, onRequest, onTestAlert, schedule
? 'border-green-600 text-green-400 bg-green-900/20'
: provider === 'claude-sdk' && !hasKey && !apiKey
? 'border-gray-800 text-gray-700 bg-transparent cursor-not-allowed'
: 'border-gray-700 text-gray-400 hover:border-amber-500/50 hover:text-amber-400 bg-transparent'
: 'border-amber-800/50 text-amber-400/70 hover:border-amber-500/50 hover:text-amber-400 bg-amber-900/10'
}`}
>
{testFired ? 'Alert Sent — check Analysis tab' : 'Fire Test Alert'}
{testFired ? 'Alert Sent — check Analysis tab' : 'TEST — Fire Synthetic Alert'}
</button>
)}
</div>
Expand Down
46 changes: 40 additions & 6 deletions packages/dashboard/src/components/ReportsPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { useState, useEffect } from 'react'
import type { AnalysisReport, BudgetStatus } from '@tastytrade-monitor/shared'
import { useState, useEffect, useRef } from 'react'
import type { AnalysisReport, BudgetStatus, AgentStatus } from '@tastytrade-monitor/shared'

interface Props {
reports: AnalysisReport[]
budgetStatus: BudgetStatus | null
agentStatus: AgentStatus | null
onRequestReports: (date?: string) => void
onRunNow: () => void
onRequestBudget: () => void
Expand All @@ -18,20 +19,30 @@ const RUN_TYPE_LABELS: Record<string, { label: string; color: string }> = {
manual: { label: 'Manual', color: 'text-gray-400 bg-gray-800' },
}

export function ReportsPanel({ reports, budgetStatus, onRequestReports, onRunNow, onRequestBudget }: Props) {
export function ReportsPanel({ reports, budgetStatus, agentStatus, onRequestReports, onRunNow, onRequestBudget }: Props) {
const [selectedDate, setSelectedDate] = useState(() => new Date().toISOString().slice(0, 10))
const [expandedId, setExpandedId] = useState<string | null>(null)
const [running, setRunning] = useState(false)
const [runRequested, setRunRequested] = useState(false)
const prevReportCount = useRef(reports.length)

const isProcessing = agentStatus?.state === 'processing'
const running = runRequested || isProcessing

useEffect(() => {
onRequestReports(selectedDate)
onRequestBudget()
}, [selectedDate, onRequestReports, onRequestBudget])

useEffect(() => {
if (reports.length > prevReportCount.current && runRequested) {
setRunRequested(false)
}
prevReportCount.current = reports.length
}, [reports.length, runRequested])

const handleRunNow = () => {
setRunning(true)
setRunRequested(true)
onRunNow()
setTimeout(() => setRunning(false), 10000)
}

return (
Expand Down Expand Up @@ -60,6 +71,29 @@ export function ReportsPanel({ reports, budgetStatus, onRequestReports, onRunNow
</div>
</div>

{/* Agent progress banner */}
{running && (
<div className="border border-amber-800/50 bg-amber-900/20 rounded-lg px-4 py-3 flex items-center gap-3">
<div className="w-4 h-4 border-2 border-amber-400 border-t-transparent rounded-full animate-spin" />
<div className="flex-1">
<span className="text-amber-400 text-sm font-mono">
{isProcessing && agentStatus?.currentTicker
? `Analyzing ${agentStatus.currentTicker}...`
: 'Agent processing...'}
</span>
{agentStatus && (
<div className="flex items-center gap-3 mt-1 text-[10px] font-mono text-gray-500">
<span>Model: {agentStatus.model}</span>
{(agentStatus.queueDepth ?? 0) > 0 && <span>Queue: {agentStatus.queueDepth}</span>}
</div>
)}
</div>
<div className="w-24 h-1.5 bg-gray-800 rounded-full overflow-hidden">
<div className="h-full bg-amber-400 rounded-full animate-pulse" style={{ width: '60%' }} />
</div>
</div>
)}

{/* Budget bar */}
{budgetStatus && (
<div className="border border-gray-800 rounded p-3 bg-[#0a0a0a]">
Expand Down
Loading
Loading