From b33afc6c8cc39aac87a17eb147ed526a50f8930e Mon Sep 17 00:00:00 2001 From: root Date: Wed, 6 May 2026 00:22:07 +0000 Subject: [PATCH] fix(web): require active profile for simple trades --- web/src/views/SimpleView.tsx | 75 +++++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 2 deletions(-) diff --git a/web/src/views/SimpleView.tsx b/web/src/views/SimpleView.tsx index 2e8a899..ad1e6d3 100644 --- a/web/src/views/SimpleView.tsx +++ b/web/src/views/SimpleView.tsx @@ -5,6 +5,7 @@ import { fetchChartBars } from '../lib/marketApi'; import { getPlatformAccessToken } from '../lib/authSession'; import { tradingRuntime } from '../lib/runtime'; import { createRequestId } from '../../../shared/request-id.js'; +import { fetchTradeProfiles, type TradeProfilePayload } from '../lib/profileApi'; type SimpleSide = 'buy' | 'sell'; type SimpleOrderType = 'market' | 'limit'; @@ -22,6 +23,7 @@ type SimpleTradeDraft = { }; type SimpleTradePayload = { + profile_id?: string; symbol: string; side: SimpleSide; qty: number; @@ -144,6 +146,8 @@ function buildExecutionPreview(payload: SimpleTradePayload | null) { export function SimpleView() { const { botState } = useAppContext(); + const [profiles, setProfiles] = useState([]); + const [selectedProfileId, setSelectedProfileId] = useState(''); const [draft, setDraft] = useState(DEFAULT_DRAFT); const [submitting, setSubmitting] = useState(false); const [loadingPrice, setLoadingPrice] = useState(false); @@ -154,6 +158,43 @@ export function SimpleView() { const livePrice = normalizedSymbol ? Number(botState.symbols?.[normalizedSymbol]?.price || 0) : 0; const resolvedMarketPrice = parsePositiveNumber(draft.currentMarketPrice); const marketPriceSource = livePrice > 0 ? 'live' : (resolvedMarketPrice !== null ? 'recent_close' : null); + const activeProfiles = useMemo( + () => profiles.filter((profile) => Boolean(profile.is_active)), + [profiles], + ); + + useEffect(() => { + let cancelled = false; + + async function loadProfiles() { + try { + const rows = await fetchTradeProfiles(); + if (cancelled) return; + setProfiles(rows); + } catch (err: any) { + if (cancelled) return; + setError(err?.message ?? 'Failed to load trade profiles'); + } + } + + void loadProfiles(); + return () => { + cancelled = true; + }; + }, []); + + useEffect(() => { + if (!activeProfiles.length) { + setSelectedProfileId(''); + return; + } + + if (selectedProfileId && activeProfiles.some((profile) => profile.id === selectedProfileId)) { + return; + } + + setSelectedProfileId(String(activeProfiles[0]?.id || '')); + }, [activeProfiles, selectedProfileId]); useEffect(() => { if (!livePrice) return; @@ -221,7 +262,14 @@ export function SimpleView() { setMessage(null); try { + if (!activeProfiles.length) { + throw new Error('No active trade profile available. Activate a profile first.'); + } + const payload = buildSimpleTradePayload(draft); + if (selectedProfileId) { + payload.profile_id = selectedProfileId; + } const accessToken = await getPlatformAccessToken(); const response = await fetch(`${tradingRuntime.tradingApiUrl}/api/trade`, { method: 'POST', @@ -315,6 +363,23 @@ export function SimpleView() { + +