@@ -23,6 +23,9 @@ export function FinanceHub(): React.ReactElement {
2323 const [ query , setQuery ] = useState ( '' ) ;
2424 const [ watchlist , setWatchlist ] = useState < WatchlistRow [ ] > ( [ ] ) ;
2525 const [ recent , setRecent ] = useState < string [ ] > ( [ ] ) ;
26+ const [ bulk , setBulk ] = useState ( '' ) ;
27+ const [ bulkBusy , setBulkBusy ] = useState ( false ) ;
28+ const [ bulkMsg , setBulkMsg ] = useState < string | null > ( null ) ;
2629
2730 useEffect ( ( ) => {
2831 try {
@@ -33,13 +36,51 @@ export function FinanceHub(): React.ReactElement {
3336 }
3437 } , [ ] ) ;
3538
36- useEffect ( ( ) => {
39+ const loadWatchlist = useCallback ( ( ) => {
3740 fetch ( '/api/finance/watchlist' , { cache : 'no-store' } )
3841 . then ( ( res ) => ( res . ok ? res . json ( ) : { watchlist : [ ] } ) )
3942 . then ( ( body : { watchlist ?: WatchlistRow [ ] } ) => setWatchlist ( body . watchlist ?? [ ] ) )
4043 . catch ( ( ) => undefined ) ;
4144 } , [ ] ) ;
4245
46+ useEffect ( ( ) => {
47+ loadWatchlist ( ) ;
48+ } , [ loadWatchlist ] ) ;
49+
50+ const addBulk = useCallback (
51+ async ( e : React . FormEvent ) => {
52+ e . preventDefault ( ) ;
53+ if ( ! bulk . trim ( ) ) return ;
54+ setBulkBusy ( true ) ;
55+ setBulkMsg ( null ) ;
56+ try {
57+ const res = await fetch ( '/api/finance/watchlist' , {
58+ method : 'POST' ,
59+ headers : { 'content-type' : 'application/json' } ,
60+ body : JSON . stringify ( { symbols : bulk } ) ,
61+ } ) ;
62+ const body = await res . json ( ) . catch ( ( ) => ( { } ) ) ;
63+ if ( ! res . ok ) {
64+ setBulkMsg ( body . error === 'no valid symbols' ? 'No valid tickers found.' : 'Could not add tickers.' ) ;
65+ return ;
66+ }
67+ const added = body . count ?? 0 ;
68+ const invalid : string [ ] = body . invalid ?? [ ] ;
69+ setBulkMsg (
70+ `Added ${ added } ticker${ added === 1 ? '' : 's' } ` +
71+ ( invalid . length ? ` · skipped ${ invalid . length } invalid (${ invalid . slice ( 0 , 5 ) . join ( ', ' ) } )` : '' ) ,
72+ ) ;
73+ setBulk ( '' ) ;
74+ loadWatchlist ( ) ;
75+ } catch {
76+ setBulkMsg ( 'Network error.' ) ;
77+ } finally {
78+ setBulkBusy ( false ) ;
79+ }
80+ } ,
81+ [ bulk , loadWatchlist ] ,
82+ ) ;
83+
4384 const go = useCallback (
4485 ( raw : string ) => {
4586 const symbol = normalizeSymbol ( raw ) ;
@@ -99,6 +140,22 @@ export function FinanceHub(): React.ReactElement {
99140 < h2 className = "mb-3 text-sm font-semibold uppercase tracking-wider text-text-muted" >
100141 Watchlist
101142 </ h2 >
143+
144+ < form onSubmit = { addBulk } className = "mb-4 flex flex-col gap-2 sm:flex-row" >
145+ < input
146+ className = "input flex-1"
147+ placeholder = "Paste tickers to add, e.g. NVDA, AAPL, TSLA, SPY"
148+ value = { bulk }
149+ onChange = { ( e ) => setBulk ( e . target . value ) }
150+ autoCapitalize = "characters"
151+ spellCheck = { false }
152+ />
153+ < button type = "submit" disabled = { bulkBusy || ! bulk . trim ( ) } className = "btn btn-secondary disabled:opacity-60" >
154+ { bulkBusy ? 'Adding…' : 'Add all' }
155+ </ button >
156+ </ form >
157+ { bulkMsg ? < p className = "mb-3 text-xs text-text-muted" > { bulkMsg } </ p > : null }
158+
102159 { watchlist . length === 0 ? (
103160 < p className = "text-sm text-text-muted" >
104161 No tickers yet. Open a ticker and tap “Add to watchlist”.
0 commit comments