Skip to content

Commit 7b0003e

Browse files
committed
Improve trade order data handling and add manual refresh
Adds a manual refresh button to TradeHistoryTable with separate loading state, and refines the logic for fetching and displaying trade order data, especially for rejected orders. For rejected orders, TradeOrderCard now always displays the original saved values to accurately reflect the intended trade at the time of creation. Also improves state management for loading and periodic updates.
1 parent e837f62 commit 7b0003e

File tree

2 files changed

+194
-150
lines changed

2 files changed

+194
-150
lines changed

src/components/TradeHistoryTable.tsx

Lines changed: 120 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ interface RebalanceGroup {
4747

4848
export 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

Comments
 (0)