@@ -47,6 +47,7 @@ interface RebalanceGroup {
4747
4848export default function TradeHistoryTable ( ) {
4949 const [ loading , setLoading ] = useState ( true ) ;
50+ const [ refreshing , setRefreshing ] = useState ( false ) ; // Separate state for refresh button
5051 const [ allTrades , setAllTrades ] = useState < TradeDecision [ ] > ( [ ] ) ;
5152 const [ selectedAnalysisId , setSelectedAnalysisId ] = useState < string | null > ( null ) ;
5253 const [ selectedRebalanceId , setSelectedRebalanceId ] = useState < string | null > ( null ) ;
@@ -62,6 +63,9 @@ export default function TradeHistoryTable() {
6263 const [ selectedDate , setSelectedDate ] = useState < string > ( todayString ) ;
6364 const isViewingToday = selectedDate === todayString ;
6465
66+ // Track if initial data has been loaded
67+ const [ initialLoadComplete , setInitialLoadComplete ] = useState ( false ) ;
68+
6569 // Helper to format date display
6670 const getDateDisplay = ( ) => {
6771 const today = new Date ( ) ;
@@ -112,11 +116,24 @@ export default function TradeHistoryTable() {
112116 setSelectedDate ( todayString ) ;
113117 } ;
114118
119+ // Manual refresh handler
120+ const handleManualRefresh = async ( ) => {
121+ setRefreshing ( true ) ;
122+ await fetchAllTrades ( false ) ;
123+ if ( hasAlpacaConfig ) {
124+ await updateAlpacaOrderStatus ( ) ;
125+ }
126+ setRefreshing ( false ) ;
127+ } ;
128+
115129 // Fetch all trades from trading_actions table
116- const fetchAllTrades = async ( ) => {
130+ const fetchAllTrades = async ( isInitialLoad = false ) => {
117131 if ( ! user ?. id ) return ;
118132
119- setLoading ( true ) ;
133+ // Only show loading state on initial load or when explicitly requested
134+ if ( isInitialLoad ) {
135+ setLoading ( true ) ;
136+ }
120137 try {
121138 // Build date range for the selected date using local date parsing
122139 const [ year , month , day ] = selectedDate . split ( '-' ) . map ( Number ) ;
@@ -171,7 +188,10 @@ export default function TradeHistoryTable() {
171188 variant : "destructive" ,
172189 } ) ;
173190 } finally {
174- setLoading ( false ) ;
191+ if ( isInitialLoad ) {
192+ setLoading ( false ) ;
193+ }
194+ setInitialLoadComplete ( true ) ;
175195 }
176196 } ;
177197
@@ -315,7 +335,7 @@ export default function TradeHistoryTable() {
315335 if ( hasUpdates ) {
316336 console . log ( 'Updates were made, refreshing trades...' ) ;
317337 setTimeout ( ( ) => {
318- fetchAllTrades ( ) ;
338+ fetchAllTrades ( false ) ;
319339 } , 500 ) ;
320340 }
321341 } catch ( err ) {
@@ -330,7 +350,8 @@ export default function TradeHistoryTable() {
330350 return ;
331351 }
332352
333- fetchAllTrades ( ) ;
353+ // Pass true for isInitialLoad only when we haven't loaded data yet
354+ fetchAllTrades ( ! initialLoadComplete ) ;
334355 } , [ user ?. id , selectedDate ] ) ;
335356
336357 useEffect ( ( ) => {
@@ -344,15 +365,17 @@ export default function TradeHistoryTable() {
344365
345366 // Periodically update Alpaca order status
346367 useEffect ( ( ) => {
347- if ( ! user ?. id || ! hasAlpacaConfig || ! isViewingToday ) return ;
368+ if ( ! user ?. id || ! hasAlpacaConfig || ! isViewingToday || ! initialLoadComplete ) return ;
348369
349370 const interval = setInterval ( ( ) => {
350371 console . log ( 'Periodic order status update...' ) ;
351372 updateAlpacaOrderStatus ( ) ;
373+ // Also refresh trades but without loading indicator
374+ fetchAllTrades ( false ) ;
352375 } , 30000 ) ; // Check every 30 seconds
353376
354377 return ( ) => clearInterval ( interval ) ;
355- } , [ user ?. id , hasAlpacaConfig , isViewingToday ] ) ;
378+ } , [ user ?. id , hasAlpacaConfig , isViewingToday , initialLoadComplete ] ) ;
356379
357380 const formatTimestamp = ( timestamp : string ) => {
358381 const date = new Date ( timestamp ) ;
@@ -404,7 +427,7 @@ export default function TradeHistoryTable() {
404427 } ) ;
405428
406429 // Refresh trades
407- fetchAllTrades ( ) ;
430+ fetchAllTrades ( false ) ;
408431 } else {
409432 toast ( {
410433 title : "Order Failed" ,
@@ -442,7 +465,7 @@ export default function TradeHistoryTable() {
442465 } ) ;
443466
444467 // Refresh trades
445- fetchAllTrades ( ) ;
468+ fetchAllTrades ( false ) ;
446469 } catch ( err ) {
447470 console . error ( 'Error rejecting decision:' , err ) ;
448471 toast ( {
@@ -835,112 +858,107 @@ export default function TradeHistoryTable() {
835858 Trade History
836859 </ CardTitle >
837860
838- < div className = "flex items-center gap-1" >
861+ < div className = "flex items-center gap-2" >
862+ { /* Manual refresh button */ }
839863 < Button
840864 variant = "ghost"
841865 size = "sm"
842- onClick = { ( ) => navigateDate ( 'prev' ) }
866+ onClick = { handleManualRefresh }
867+ disabled = { refreshing }
843868 className = "h-8 w-8 p-0 hover:bg-[#fc0]/10 hover:text-[#fc0]"
869+ title = "Refresh"
844870 >
845- < ChevronLeft className = " h-4 w-4" />
871+ < RefreshCw className = { ` h-4 w-4 ${ refreshing ? 'animate-spin' : '' } ` } />
846872 </ Button >
873+
874+ < div className = "w-px h-6 bg-border" />
875+
876+ < div className = "flex items-center gap-1" >
877+ < Button
878+ variant = "ghost"
879+ size = "sm"
880+ onClick = { ( ) => navigateDate ( 'prev' ) }
881+ className = "h-8 w-8 p-0 hover:bg-[#fc0]/10 hover:text-[#fc0]"
882+ >
883+ < ChevronLeft className = "h-4 w-4" />
884+ </ Button >
847885
848- < Popover >
849- < PopoverTrigger asChild >
850- < Button
851- variant = "outline"
852- size = "sm"
853- className = "px-3 min-w-[140px] hover:border-[#fc0] hover:bg-[#fc0]/10 hover:text-[#fc0] transition-all duration-200"
886+ < Popover >
887+ < PopoverTrigger asChild >
888+ < Button
889+ variant = "outline"
890+ size = "sm"
891+ className = "px-3 min-w-[140px] hover:border-[#fc0] hover:bg-[#fc0]/10 hover:text-[#fc0] transition-all duration-200"
892+ >
893+ < CalendarIcon className = "h-4 w-4 mr-2" />
894+ { getDateDisplay ( ) }
895+ </ Button >
896+ </ PopoverTrigger >
897+ < PopoverContent
898+ className = "w-auto p-0 bg-background border-border"
899+ align = "center"
854900 >
855- < CalendarIcon className = "h-4 w-4 mr-2" />
856- { getDateDisplay ( ) }
857- </ Button >
858- </ PopoverTrigger >
859- < PopoverContent
860- className = "w-auto p-0 bg-background border-border"
861- align = "center"
862- >
863- < div className = "space-y-2 p-3" >
864- < div className = "flex gap-2" >
865- < Button
866- size = "sm"
867- variant = "outline"
868- className = "flex-1 text-xs hover:bg-[#fc0]/10 hover:border-[#fc0]/50 hover:text-[#fc0]"
869- onClick = { ( ) => {
870- const yesterday = new Date ( ) ;
871- yesterday . setDate ( yesterday . getDate ( ) - 1 ) ;
872- const year = yesterday . getFullYear ( ) ;
873- const month = String ( yesterday . getMonth ( ) + 1 ) . padStart ( 2 , '0' ) ;
874- const day = String ( yesterday . getDate ( ) ) . padStart ( 2 , '0' ) ;
875- setSelectedDate ( `${ year } -${ month } -${ day } ` ) ;
876- } }
877- >
878- Yesterday
879- </ Button >
880- < Button
881- size = "sm"
882- variant = "outline"
883- className = "flex-1 text-xs hover:bg-[#fc0]/10 hover:border-[#fc0]/50 hover:text-[#fc0]"
884- onClick = { jumpToToday }
885- >
886- Today
887- </ Button >
901+ < div className = "space-y-2 p-3" >
902+ < div className = "flex gap-2" >
903+ < Button
904+ size = "sm"
905+ variant = "outline"
906+ className = "flex-1 text-xs hover:bg-[#fc0]/10 hover:border-[#fc0]/50 hover:text-[#fc0]"
907+ onClick = { ( ) => {
908+ const yesterday = new Date ( ) ;
909+ yesterday . setDate ( yesterday . getDate ( ) - 1 ) ;
910+ const year = yesterday . getFullYear ( ) ;
911+ const month = String ( yesterday . getMonth ( ) + 1 ) . padStart ( 2 , '0' ) ;
912+ const day = String ( yesterday . getDate ( ) ) . padStart ( 2 , '0' ) ;
913+ setSelectedDate ( `${ year } -${ month } -${ day } ` ) ;
914+ } }
915+ >
916+ Yesterday
917+ </ Button >
918+ < Button
919+ size = "sm"
920+ variant = "outline"
921+ className = "flex-1 text-xs hover:bg-[#fc0]/10 hover:border-[#fc0]/50 hover:text-[#fc0]"
922+ onClick = { jumpToToday }
923+ >
924+ Today
925+ </ Button >
926+ </ div >
888927 </ div >
889- </ div >
890- < DatePicker
891- mode = "single"
892- selected = { ( ( ) => {
893- const [ year , month , day ] = selectedDate . split ( '-' ) . map ( Number ) ;
894- return new Date ( year , month - 1 , day ) ;
895- } ) ( ) }
896- onSelect = { ( date ) => {
897- if ( date ) {
898- const year = date . getFullYear ( ) ;
899- const month = String ( date . getMonth ( ) + 1 ) . padStart ( 2 , '0' ) ;
900- const day = String ( date . getDate ( ) ) . padStart ( 2 , '0' ) ;
901- setSelectedDate ( `${ year } -${ month } -${ day } ` ) ;
902- }
903- } }
904- disabled = { ( date ) => {
905- const today = new Date ( ) ;
906- today . setHours ( 23 , 59 , 59 , 999 ) ;
907- return date > today ;
908- } }
909- showOutsideDays = { false }
910- initialFocus
911- className = "rounded-b-lg"
912- />
913- </ PopoverContent >
914- </ Popover >
915-
916- < Button
917- variant = "ghost"
918- size = "sm"
919- onClick = { ( ) => navigateDate ( 'next' ) }
920- disabled = { selectedDate === todayString }
921- className = "h-8 w-8 p-0 hover:bg-[#fc0]/10 hover:text-[#fc0] disabled:opacity-50 disabled:hover:bg-transparent disabled:hover:text-muted-foreground"
922- >
923- < ChevronRight className = "h-4 w-4" />
924- </ Button >
928+ < DatePicker
929+ mode = "single"
930+ selected = { ( ( ) => {
931+ const [ year , month , day ] = selectedDate . split ( '-' ) . map ( Number ) ;
932+ return new Date ( year , month - 1 , day ) ;
933+ } ) ( ) }
934+ onSelect = { ( date ) => {
935+ if ( date ) {
936+ const year = date . getFullYear ( ) ;
937+ const month = String ( date . getMonth ( ) + 1 ) . padStart ( 2 , '0' ) ;
938+ const day = String ( date . getDate ( ) ) . padStart ( 2 , '0' ) ;
939+ setSelectedDate ( `${ year } -${ month } -${ day } ` ) ;
940+ }
941+ } }
942+ disabled = { ( date ) => {
943+ const today = new Date ( ) ;
944+ today . setHours ( 23 , 59 , 59 , 999 ) ;
945+ return date > today ;
946+ } }
947+ showOutsideDays = { false }
948+ initialFocus
949+ className = "rounded-b-lg"
950+ />
951+ </ PopoverContent >
952+ </ Popover >
925953
926- < div className = "ml-2" >
927954 < Button
928955 variant = "ghost"
929- size = "icon"
930- className = "h-8 w-8"
931- onClick = { ( ) => {
932- fetchAllTrades ( ) ;
933- if ( hasAlpacaConfig ) {
934- updateAlpacaOrderStatus ( ) ;
935- }
936- } }
937- disabled = { loading }
956+ size = "sm"
957+ onClick = { ( ) => navigateDate ( 'next' ) }
958+ disabled = { selectedDate === todayString }
959+ className = "h-8 w-8 p-0 hover:bg-[#fc0]/10 hover:text-[#fc0] disabled:hover:bg-transparent disabled:hover:text-muted-foreground"
938960 >
939- { loading ? (
940- < Loader2 className = "h-4 w-4 animate-spin" />
941- ) : (
942- < RefreshCw className = "h-4 w-4" />
943- ) }
961+ < ChevronRight className = "h-4 w-4" />
944962 </ Button >
945963 </ div >
946964 </ div >
0 commit comments