diff --git a/web/src/views/SimpleView.tsx b/web/src/views/SimpleView.tsx index d8a9e0a..25ffa05 100644 --- a/web/src/views/SimpleView.tsx +++ b/web/src/views/SimpleView.tsx @@ -350,6 +350,41 @@ function statusToneClasses(tone: SimpleRuntimeSnapshot['tone']): string { } } +const SIMPLE_TIMELINE_STEPS: Array = [ + 'armed', + 'entry_submitted', + 'filled', + 'exit_submitted', + 'closed', +]; + +function isTimelineStepComplete( + current: SimpleRuntimeSnapshot['stage'] | undefined, + step: SimpleRuntimeSnapshot['stage'], +): boolean { + if (!current) return false; + const currentIndex = SIMPLE_TIMELINE_STEPS.indexOf(current); + const stepIndex = SIMPLE_TIMELINE_STEPS.indexOf(step); + return currentIndex >= 0 && stepIndex >= 0 && stepIndex <= currentIndex; +} + +function formatTimelineStepLabel(step: SimpleRuntimeSnapshot['stage']) { + switch (step) { + case 'armed': + return 'Armed'; + case 'entry_submitted': + return 'Entry sent'; + case 'filled': + return 'Filled'; + case 'exit_submitted': + return 'Exit sent'; + case 'closed': + return 'Closed'; + default: + return step; + } +} + function formatSetupUpdatedAt(entry: ManualEntryPayload): string | null { const raw = String(entry.sell_time || entry.buy_time || '').trim(); if (!raw) return null; @@ -449,6 +484,7 @@ export function SimpleView() { const [submitting, setSubmitting] = useState(false); const [loadingPrice, setLoadingPrice] = useState(false); const [marketPriceSource, setMarketPriceSource] = useState(null); + const [copiedKey, setCopiedKey] = useState(null); const [message, setMessage] = useState(null); const [error, setError] = useState(null); const marketPriceRequestSymbolRef = useRef(''); @@ -572,6 +608,20 @@ export function SimpleView() { setDraft((prev) => ({ ...prev, [key]: value })); } + async function copyIdentifier(kind: 'trade' | 'order', value: string | null | undefined) { + if (!value) return; + try { + await navigator.clipboard.writeText(value); + const key = `${kind}:${value}`; + setCopiedKey(key); + window.setTimeout(() => { + setCopiedKey((prev) => (prev === key ? null : prev)); + }, 1200); + } catch { + setError(`Failed to copy ${kind} ID`); + } + } + async function refreshSetupList() { const [profileRows, entryRows] = await Promise.all([ fetchTradeProfiles(), @@ -1077,14 +1127,26 @@ export function SimpleView() { ) : null} {(runtimeSnapshot?.tradeId || entry.linked_trade_id) ? ( - - Trade {String(runtimeSnapshot?.tradeId || entry.linked_trade_id).slice(0, 18)}… - + ) : null} {runtimeSnapshot?.orderId ? ( - - Order {runtimeSnapshot.orderId.slice(0, 12)}… - + ) : null} {updatedAt ? ( @@ -1092,6 +1154,27 @@ export function SimpleView() { ) : null} + +
+ {SIMPLE_TIMELINE_STEPS.map((step) => { + const complete = isTimelineStepComplete(runtimeSnapshot?.stage, step); + const isCurrent = runtimeSnapshot?.stage === step; + return ( +
+ {formatTimelineStepLabel(step)} +
+ ); + })} +
); })}