// Watchlist.jsx const WatchlistPage = () => { const { useState, useEffect } = React; const STORAGE_KEY = 'tj_watchlist_v1'; const load = () => { try { return JSON.parse(localStorage.getItem(STORAGE_KEY)) || []; } catch { return []; } }; const [items, setItems] = useState(load); const [form, setForm] = useState({ symbol: '', notes: '', sector: '', priceTarget: '', alertPrice: '', priority: 'medium' }); const [editId, setEditId] = useState(null); const [filter, setFilter] = useState('all'); useEffect(() => { try { localStorage.setItem(STORAGE_KEY, JSON.stringify(items)); } catch {} }, [items]); const PRIORITY_COLORS = { high: '#f04060', medium: '#ffb83f', low: '#5588ff' }; const SECTORS = ['Tech','AI','Index','Energy','Financials','Healthcare','Consumer','Industrials','Other']; const save = () => { if (!form.symbol.trim()) return; const entry = { ...form, symbol: form.symbol.toUpperCase().trim(), id: editId || genId(), addedAt: editId ? items.find(i=>i.id===editId)?.addedAt : new Date().toISOString().slice(0,10) }; if (editId) { setItems(its => its.map(i => i.id === editId ? entry : i)); setEditId(null); } else setItems(its => [entry, ...its]); setForm({ symbol: '', notes: '', sector: '', priceTarget: '', alertPrice: '', priority: 'medium' }); }; const del = (id) => setItems(its => its.filter(i => i.id !== id)); const startEdit = (item) => { setEditId(item.id); setForm({ ...item }); }; const filtered = filter === 'all' ? items : items.filter(i => i.priority === filter); const inp = (extra={}) => ({ background: 'var(--surface3)', border: '1px solid var(--border)', borderRadius: '6px', color: 'var(--text)', padding: '8px 12px', fontSize: '13px', outline: 'none', fontFamily: 'Space Grotesk, sans-serif', ...extra }); return React.createElement('div', { style: { padding: '28px 32px', maxWidth: '1100px' } }, [ // Header React.createElement('div', { key: 'hdr', style: { display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: '24px' } }, [ React.createElement('div', { key: 'l' }, [ React.createElement('h1', { key: 'h', style: { fontSize: '22px', fontWeight: 700, color: 'var(--text)', margin: 0 } }, 'Watchlist'), React.createElement('p', { key: 's', style: { fontSize: '13px', color: 'var(--muted)', margin: '4px 0 0' } }, `${items.length} symbols tracked`) ]), React.createElement('div', { key: 'filters', style: { display: 'flex', gap: '4px', background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: '8px', padding: '4px' } }, [['all','All'],['high','High'],['medium','Med'],['low','Low']].map(([id, lbl]) => React.createElement('button', { key: id, onClick: () => setFilter(id), style: { padding: '5px 12px', fontSize: '12px', fontWeight: filter===id ? 600 : 400, borderRadius: '6px', border: 'none', cursor: 'pointer', fontFamily: 'Space Grotesk, sans-serif', background: filter===id ? (id==='all'?'var(--border)': PRIORITY_COLORS[id]) : 'transparent', color: filter===id ? (id==='all'?'var(--text)':'#fff') : 'var(--muted)', transition: 'all 0.15s' } }, lbl) ) ) ]), // Add / Edit form React.createElement('div', { key: 'form', style: { background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: '10px', padding: '18px 22px', marginBottom: '20px' } }, [ React.createElement('div', { key: 'title', style: { fontSize: '12px', color: 'var(--muted)', fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.07em', marginBottom: '14px' } }, editId ? 'Edit Symbol' : 'Add to Watchlist'), React.createElement('div', { key: 'row1', style: { display: 'grid', gridTemplateColumns: '120px 140px 140px 140px 1fr', gap: '10px', marginBottom: '10px', alignItems: 'end' } }, [ React.createElement('div', { key: 'sym' }, [ React.createElement('label', { key: 'l', style: { fontSize: '10px', color: 'var(--muted)', display: 'block', marginBottom: '4px', textTransform: 'uppercase', letterSpacing: '0.07em' } }, 'Symbol *'), React.createElement('input', { value: form.symbol, onChange: e => setForm(f=>({...f, symbol: e.target.value.toUpperCase()})), placeholder: 'AAPL', style: { ...inp(), width: '100%', boxSizing: 'border-box', fontFamily: 'JetBrains Mono, monospace', letterSpacing: '0.04em' } }) ]), React.createElement('div', { key: 'sec' }, [ React.createElement('label', { key: 'l', style: { fontSize: '10px', color: 'var(--muted)', display: 'block', marginBottom: '4px', textTransform: 'uppercase', letterSpacing: '0.07em' } }, 'Sector'), React.createElement('select', { value: form.sector, onChange: e => setForm(f=>({...f,sector:e.target.value})), style: { ...inp(), width: '100%', boxSizing: 'border-box' } }, [ React.createElement('option', { key: '', value: '' }, '—'), ...SECTORS.map(s => React.createElement('option', { key: s, value: s }, s)) ]) ]), React.createElement('div', { key: 'pt' }, [ React.createElement('label', { key: 'l', style: { fontSize: '10px', color: 'var(--muted)', display: 'block', marginBottom: '4px', textTransform: 'uppercase', letterSpacing: '0.07em' } }, 'Price Target'), React.createElement('input', { type: 'number', step: '0.01', value: form.priceTarget, onChange: e => setForm(f=>({...f,priceTarget:e.target.value})), placeholder: '200.00', style: { ...inp(), width: '100%', boxSizing: 'border-box' } }) ]), React.createElement('div', { key: 'pri' }, [ React.createElement('label', { key: 'l', style: { fontSize: '10px', color: 'var(--muted)', display: 'block', marginBottom: '4px', textTransform: 'uppercase', letterSpacing: '0.07em' } }, 'Priority'), React.createElement('div', { key: 'btns', style: { display: 'flex', background: 'var(--surface3)', border: '1px solid var(--border)', borderRadius: '6px', padding: '3px', gap: '3px' } }, ['high','medium','low'].map(p => React.createElement('button', { key: p, onClick: () => setForm(f=>({...f,priority:p})), type: 'button', style: { flex: 1, padding: '5px 4px', fontSize: '11px', border: 'none', borderRadius: '4px', cursor: 'pointer', fontFamily: 'Space Grotesk, sans-serif', background: form.priority===p ? PRIORITY_COLORS[p] : 'transparent', color: form.priority===p ? '#fff' : 'var(--muted)', fontWeight: form.priority===p ? 600 : 400, textTransform: 'capitalize' } }, p)) ) ]), React.createElement('div', { key: 'notes' }, [ React.createElement('label', { key: 'l', style: { fontSize: '10px', color: 'var(--muted)', display: 'block', marginBottom: '4px', textTransform: 'uppercase', letterSpacing: '0.07em' } }, 'Setup Notes'), React.createElement('input', { value: form.notes, onChange: e => setForm(f=>({...f,notes:e.target.value})), placeholder: 'Why watching this? Entry conditions…', style: { ...inp(), width: '100%', boxSizing: 'border-box' } }) ]), ]), React.createElement('div', { key: 'actions', style: { display: 'flex', gap: '8px' } }, [ React.createElement('button', { onClick: save, style: { background: '#5588ff', border: 'none', borderRadius: '7px', color: '#fff', fontSize: '13px', fontWeight: 600, cursor: 'pointer', padding: '8px 20px', fontFamily: 'Space Grotesk, sans-serif' } }, editId ? 'Save Changes' : '+ Add'), editId && React.createElement('button', { key: 'cancel', onClick: () => { setEditId(null); setForm({ symbol: '', notes: '', sector: '', priceTarget: '', alertPrice: '', priority: 'medium' }); }, style: { background: 'var(--surface2)', border: '1px solid var(--border)', borderRadius: '7px', color: 'var(--text2)', fontSize: '13px', cursor: 'pointer', padding: '8px 16px', fontFamily: 'Space Grotesk, sans-serif' } }, 'Cancel') ]) ]), // List filtered.length === 0 ? React.createElement('div', { key: 'empty', style: { textAlign: 'center', padding: '60px', color: 'var(--muted)' } }, 'No symbols yet — add your first one above') : React.createElement('div', { key: 'list', style: { display: 'flex', flexDirection: 'column', gap: '8px' } }, filtered.map(item => React.createElement('div', { key: item.id, style: { background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: '10px', padding: '14px 18px', display: 'flex', alignItems: 'center', gap: '16px', transition: 'border-color 0.15s' }, onMouseEnter: e => e.currentTarget.style.borderColor = '#283060', onMouseLeave: e => e.currentTarget.style.borderColor = 'var(--border)' }, [ // Priority dot React.createElement('div', { key: 'dot', style: { width: '8px', height: '8px', borderRadius: '50%', background: PRIORITY_COLORS[item.priority] || 'var(--muted)', flexShrink: 0, boxShadow: `0 0 6px ${PRIORITY_COLORS[item.priority] || 'var(--muted)'}55` } }), // Symbol React.createElement('span', { key: 'sym', style: { fontFamily: 'JetBrains Mono, monospace', fontWeight: 700, fontSize: '15px', color: 'var(--text)', letterSpacing: '0.04em', width: '80px' } }, item.symbol), // Sector item.sector && React.createElement(Badge, { key: 'sec' }, item.sector), // Notes React.createElement('span', { key: 'notes', style: { flex: 1, fontSize: '13px', color: 'var(--muted)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, item.notes || 'No notes'), // Price target item.priceTarget && React.createElement('div', { key: 'pt', style: { display: 'flex', alignItems: 'center', gap: '6px', flexShrink: 0 } }, [ React.createElement('span', { key: 'l', style: { fontSize: '11px', color: 'var(--muted)' } }, 'Target'), React.createElement('span', { key: 'v', style: { fontFamily: 'JetBrains Mono, monospace', fontSize: '13px', color: '#00d27a' } }, `$${parseFloat(item.priceTarget).toFixed(2)}`) ]), // Date added React.createElement('span', { key: 'date', style: { fontSize: '11px', color: 'var(--dim)', flexShrink: 0 } }, item.addedAt), // Actions React.createElement('div', { key: 'acts', style: { display: 'flex', gap: '6px', flexShrink: 0 } }, [ React.createElement('button', { key: 'e', onClick: () => startEdit(item), style: { background: 'var(--surface2)', border: '1px solid var(--border)', borderRadius: '5px', color: 'var(--text2)', fontSize: '12px', cursor: 'pointer', padding: '4px 10px' } }, 'Edit'), React.createElement('button', { key: 'd', onClick: () => del(item.id), style: { background: 'rgba(240,64,96,0.08)', border: '1px solid rgba(240,64,96,0.15)', borderRadius: '5px', color: '#f04060', fontSize: '12px', cursor: 'pointer', padding: '4px 8px' } }, '×') ]) ]) ) ) ]); }; Object.assign(window, { WatchlistPage });