import { useState, useEffect } from 'react'; import type { FormEvent } from 'react'; import { useAuth } from '../components/AuthContext'; import { tradingRuntime } from '../lib/runtime'; import { createManualEntry, updateManualEntry } from '../lib/manualEntriesApi'; import { getPlatformAccessToken } from '../lib/authSession'; import { createRequestId } from '../../../shared/request-id.js'; import { Button } from './ui/button'; import { Input } from './ui/input'; interface EntryFormProps { onSuccess: () => void; initialData?: any; } const tableInputClass = 'h-9 rounded border-[var(--border)] bg-[var(--input)] px-2 py-1 text-sm'; const numericInputClass = `${tableInputClass} font-mono text-right`; const checkboxLabelClass = 'flex cursor-pointer items-center text-[10px] text-[var(--muted-foreground)] hover:text-[var(--foreground)]'; export function EntryForm({ onSuccess, initialData }: EntryFormProps) { const { user } = useAuth(); const [formData, setFormData] = useState({ symbol: '', label: '', status: 'active', active: true, quantity: '', filled_quantity: '', entry_price: '', buy_price: '', sell_price: '', gain_threshold_for_sell: '', drop_threshold_for_buy: '', buy_time: '', sell_time: '', notes: '', is_real_trade: false, is_crypto: false, execute_order: false, }); useEffect(() => { if (initialData) { setFormData({ ...initialData, quantity: initialData.quantity || '', filled_quantity: initialData.filled_quantity || '', entry_price: initialData.entry_price || '', buy_price: initialData.buy_price || '', sell_price: initialData.sell_price || '', gain_threshold_for_sell: initialData.gain_threshold_for_sell || '', drop_threshold_for_buy: initialData.drop_threshold_for_buy || '', buy_time: initialData.buy_time || '', sell_time: initialData.sell_time || '', // Preserve execute_order as false on edit init execute_order: false, }); } else { // Reset form for new entry setFormData({ symbol: '', label: '', status: 'active', active: true, quantity: '', filled_quantity: '', entry_price: '', buy_price: '', sell_price: '', gain_threshold_for_sell: '', drop_threshold_for_buy: '', buy_time: '', sell_time: '', notes: '', is_real_trade: false, is_crypto: false, execute_order: false, }); } }, [initialData]); const handleChange = (e: React.ChangeEvent) => { const { name, value, type } = e.target; setFormData(prev => ({ ...prev, [name]: type === 'checkbox' ? (e.target as HTMLInputElement).checked : value, })); }; const handleSubmit = async (e: FormEvent) => { e.preventDefault(); if (!user) return; try { const payload = { ...formData, user_id: user.id, // Convert numeric fields quantity: formData.quantity ? parseFloat(formData.quantity) : null, filled_quantity: formData.filled_quantity ? parseFloat(formData.filled_quantity) : null, entry_price: formData.entry_price ? parseFloat(formData.entry_price) : null, buy_price: formData.buy_price ? parseFloat(formData.buy_price) : null, sell_price: formData.sell_price ? parseFloat(formData.sell_price) : null, gain_threshold_for_sell: formData.gain_threshold_for_sell ? parseFloat(formData.gain_threshold_for_sell) : null, drop_threshold_for_buy: formData.drop_threshold_for_buy ? parseFloat(formData.drop_threshold_for_buy) : null, buy_time: formData.buy_time ? formData.buy_time : null, sell_time: formData.sell_time ? formData.sell_time : null, updated_at: new Date().toISOString(), }; // Remove temp fields from DB payload delete (payload as any).execute_order; // --- REAL TRADE EXECUTION --- if (formData.execute_order && !initialData) { if (!Number.isFinite(payload.quantity) || !payload.symbol.trim()) { alert('Symbol and a valid quantity are required to execute a trade.'); return; } // Determine side (Buy if buying, Sell if selling - simplistic for now assuming Entry = Buy) // If closing, we handle differently (via close button usually), but here handle Entry. const apiPayload = { symbol: payload.symbol, side: 'buy', qty: payload.quantity, type: 'market', price: payload.buy_price, sl: payload.drop_threshold_for_buy, tp: payload.gain_threshold_for_sell }; const confirmTrade = window.confirm(`EXECUTE LIVE TRADE (via Bot)?\n(Note: Uses currently configured Bot account)\n\nSymbol: ${payload.symbol}\nSide: BUY\nQty: ${payload.quantity}`); if (!confirmTrade) return; const apiUrl = tradingRuntime.tradingApiUrl; const accessToken = await getPlatformAccessToken(); const response = await fetch(`${apiUrl}/api/trade`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${accessToken}`, 'x-request-id': createRequestId('web-entry') }, body: JSON.stringify(apiPayload) }); const resData = await response.json(); if (!resData.success) { throw new Error(`Execution Failed: ${resData.error}`); } alert(`Trade Executed! Order ID: ${resData.orderId}`); } if (initialData?.stock_instance_id) { await updateManualEntry(initialData.stock_instance_id, payload); } else { await createManualEntry({ ...payload, stock_instance_id: crypto.randomUUID() }); } onSuccess(); // Reset only if not editing if (!initialData) { setFormData(prev => ({ ...prev, symbol: '', label: '', notes: '' })); } } catch (error: any) { alert(`Error saving entry: ${error.message}`); } }; return (
{!initialData && (

New Entry / Watchlist

Add a position or watchlist item.

)}
{/* Symbol */} {/* Label */} {/* Options (Real/Crypto) */} {/* Buy Price */} {/* Qty */} {/* SL */} {/* TP */} {/* Time */} {/* Sell Price (Exit) */} {/* Actions */} {/* Notes Row */}
Symbol Tag Options Buy Price Qty Stop Loss Take Profit Buy Time Exit Price Actions
setFormData(prev => ({ ...prev, buy_time: new Date(e.target.value).toISOString() }))} className="h-9 rounded border-[var(--border)] bg-[var(--input)] px-2 py-1 font-mono text-xs" /> {formData.sell_price && formData.buy_price && (
= 0 ? 'text-green-500' : 'text-red-500' }`}> ${((Number(formData.sell_price) - Number(formData.buy_price)) * Number(formData.quantity || 1)).toFixed(2)}
)}
{initialData && ( )}
); }