import { useEffect, useState } from 'react'; import { useAuth } from '../components/AuthContext'; import { EntryForm } from '../components/EntryForm'; import { Pencil, Trash2, Copy as CopyIcon, Search, Plus, Filter, LayoutGrid, Clock, ShieldCheck, Activity } from 'lucide-react'; import type { BotState } from '../hooks/useWebSocket'; import { DEFAULT_BOT_STATE } from '../hooks/useWebSocket'; import { createManualEntry, deleteManualEntry, fetchManualEntries, type ManualEntryPayload } from '../lib/manualEntriesApi'; interface Entry { stock_instance_id: string; symbol: string; active: boolean; user_id: string; buy_price?: string; sell_price?: string; buy_time?: string; sell_time?: string; quantity?: string; filled_quantity?: string; notes?: string; status: string; is_crypto: boolean; is_real_trade: boolean; label?: string; entry_price?: string; gain_threshold_for_sell?: string; drop_threshold_for_buy?: string; workflow_type?: string; holding_mode?: string; automation_state?: string; } export const filterEntriesByTab = (entries: Entry[], activeTab: string) => entries.filter((entry) => { if (String(entry.workflow_type || '').trim().toLowerCase() === 'simple') { return false; } switch (activeTab) { case 'paperActive': return !entry.is_real_trade && entry.active && entry.status !== 'sellCompleted'; case 'paperCompleted': return !entry.is_real_trade && entry.status === 'sellCompleted'; case 'realActive': return entry.is_real_trade && entry.active && entry.status !== 'sellCompleted'; case 'realCompleted': return entry.is_real_trade && entry.status === 'sellCompleted'; case 'inactive': return !entry.active; default: return false; } }); export const buildClonedEntryPayload = (entry: Entry, generatedId: string) => { const rest = { ...entry } as Record; delete rest.stock_instance_id; delete rest.id; delete rest.created_at; delete rest.updated_at; return { ...rest, stock_instance_id: generatedId, active: false, status: 'active' }; }; interface EntriesTabProps { botState?: BotState; } const ENTRY_STATE_MAP = [ { label: 'LOCKED', test: (entry: Entry) => entry.status?.toLowerCase().includes('lock') || entry.active }, { label: 'BLOCKED', test: (entry: Entry) => ['blocked', 'cooldown', 'waiting'].some(term => entry.status?.toLowerCase().includes(term)) }, { label: 'ORPHAN', test: (entry: Entry) => ['orphan', 'stale', 'reconcile'].some(term => entry.status?.toLowerCase().includes(term)) }, ]; const deriveEntryState = (entry: Entry, botState: BotState): string => { const orderMatch = botState.orders.find(o => o.symbol === entry.symbol); const status = (entry.status || '').toLowerCase(); const orderStatus = (orderMatch?.status || '').toLowerCase(); if (ENTRY_STATE_MAP[0].test(entry)) return 'LOCKED'; if (ENTRY_STATE_MAP[1].test(entry)) return 'BLOCKED'; if (orderStatus.includes('pending') || orderStatus.includes('submitted') || status.includes('submitted')) return 'SUBMITTED'; if (orderStatus.includes('filled') || orderStatus.includes('confirmed') || status.includes('filled') || status.includes('confirmed')) return 'CONFIRMED'; if (ENTRY_STATE_MAP[2].test(entry)) return 'ORPHAN'; return 'Unknown'; }; const NAV_TABS = [ { id: "paperActive", label: "Paper Active", icon: }, { id: "realActive", label: "Real Execution", icon: }, { id: "paperCompleted", label: "Paper Archive", icon: }, { id: "realCompleted", label: "Real Archive", icon: }, { id: "inactive", label: "Suspended", icon: } ] as const; const tabClass = (isActive: boolean) => `px-6 py-3 rounded-[1.5rem] text-[10px] font-black uppercase tracking-widest transition-all flex items-center gap-3 whitespace-nowrap ${isActive ? "bg-white text-black shadow-xl" : "text-gray-500 hover:text-gray-300"}`; export const EntriesTab = ({ botState = DEFAULT_BOT_STATE }: EntriesTabProps) => { const { user } = useAuth(); const [entries, setEntries] = useState([]); const [editingEntry, setEditingEntry] = useState(null); const [activeTab, setActiveTab] = useState("paperActive"); const [isAdding, setIsAdding] = useState(false); // Filter Logic const filteredEntries = filterEntriesByTab(entries, activeTab); const fetchEntries = async () => { if (!user) return; try { const data = await fetchManualEntries(); setEntries(data as Entry[]); } catch (err: unknown) { const message = err instanceof Error ? err.message : String(err); console.error("Error fetching entries:", message); } }; useEffect(() => { fetchEntries(); }, [user]); const handleDelete = async (id: string) => { if (!confirm('⚠️ CRITICAL: Permanently delete this entry from neural watchlist?')) return; await deleteManualEntry(id); fetchEntries(); }; const handleClone = async (entry: Entry) => { await createManualEntry(buildClonedEntryPayload(entry, crypto.randomUUID()) as unknown as ManualEntryPayload); fetchEntries(); }; const entryCards = filteredEntries.map((entry) => { const entryState = deriveEntryState(entry, botState); return (
{/* Card Top */}

{entry.symbol}

{entry.is_real_trade ? 'REAL-UNIT' : 'PAPER-VOID'} {entry.label && ( {entry.label} )}
{/* Metrics Grid */}
Entry Price ${entry.buy_price || '0.00'}
Target Exit ${entry.sell_price || '---'}
Quantity/Lot {entry.quantity} UNITS
Status {entry.active ? 'SCANNING' : 'INACTIVE'}
Entry State {entryState}
{/* Entry Notes/Context */}
Neural Context

{entry.notes || 'No manual notes injection provided for this asset cluster.'}

{/* ID Context */}
ID
{entry.stock_instance_id}
); }); const navTabs = NAV_TABS; return (
{/* 1. HEADER & GLOBAL ACTIONS */}

Watchlist & Entries

Neural Opportunity Cluster

{/* 2. FORM DRAWER (IF ADDING/EDITING) */} {(isAdding || editingEntry) && (

{editingEntry ? `Update Identity: ${editingEntry.symbol}` : 'New Opportunity Initialization'}

{ fetchEntries(); setEditingEntry(null); setIsAdding(false); }} initialData={editingEntry} />
)} {/* 3. NAVIGATION CLUSTERS */}
{navTabs.map((tab) => ( ))}
{/* 4. CARDS GRID */}
{entryCards} {filteredEntries.length === 0 && (

Cluster Context Null

)}
); }; const X = ({ size }: { size: number }) => ( );