Skip to content

Commit f7e2c8e

Browse files
committed
Enhanced Rebalance Detail Modal UI
1 parent 94c69bc commit f7e2c8e

File tree

5 files changed

+529
-117
lines changed

5 files changed

+529
-117
lines changed

src/components/RecentTrades.tsx

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ function RecentTrades() {
8282
totalValue: item.dollar_amount ? Number(item.dollar_amount) : Number(item.shares) * Number(item.price),
8383
status: item.status,
8484
executedAt: item.executed_at ? formatTimestamp(item.executed_at) : null,
85-
sourceType: item.source_type,
85+
sourceType: item.source_type == 'rebalance' ? item.source_type : 'analysis',
8686
analysisId: item.analysis_id,
8787
rebalanceRequestId: item.rebalance_request_id,
8888
alpacaOrderId: item.metadata?.alpaca_order?.id,
@@ -141,7 +141,7 @@ function RecentTrades() {
141141
// Extract all Alpaca order IDs
142142
const alpacaOrderIds = ordersWithAlpacaIds.map(o => o.metadata.alpaca_order.id);
143143
console.log(`Fetching status for ${alpacaOrderIds.length} Alpaca orders:`, alpacaOrderIds);
144-
144+
145145
// Fetch all orders from Alpaca using batch API
146146
const session = await getCachedSession();
147147
if (!session?.access_token) {
@@ -182,33 +182,33 @@ function RecentTrades() {
182182

183183
if (alpacaOrder) {
184184
console.log(`Found Alpaca order ${alpacaOrderId} with status: ${alpacaOrder.status}`);
185-
185+
186186
// Check if status has changed or if there's new fill information
187187
const currentAlpacaStatus = order.metadata?.alpaca_order?.status;
188188
const currentFilledQty = order.metadata?.alpaca_order?.filled_qty;
189189
const hasStatusChanged = currentAlpacaStatus !== alpacaOrder.status;
190190
const hasNewFillData = alpacaOrder.filled_qty && alpacaOrder.filled_qty !== currentFilledQty;
191-
191+
192192
// Always update if we don't have a status yet, or if something changed
193193
if (!currentAlpacaStatus || hasStatusChanged || hasNewFillData) {
194194
console.log(`Order ${alpacaOrderId} updating: current status "${currentAlpacaStatus}" -> new status "${alpacaOrder.status}"`);
195195
hasUpdates = true;
196-
196+
197197
// Build the alpaca_order object, only including defined values
198198
const alpacaOrderUpdate: any = {
199199
...(order.metadata?.alpaca_order || {}),
200200
status: alpacaOrder.status,
201201
updated_at: new Date().toISOString()
202202
};
203-
203+
204204
// Only add filled_qty and filled_avg_price if they exist
205205
if (alpacaOrder.filled_qty) {
206206
alpacaOrderUpdate.filled_qty = parseFloat(alpacaOrder.filled_qty);
207207
}
208208
if (alpacaOrder.filled_avg_price) {
209209
alpacaOrderUpdate.filled_avg_price = parseFloat(alpacaOrder.filled_avg_price);
210210
}
211-
211+
212212
// Update metadata with latest Alpaca order info
213213
const updatedMetadata = {
214214
...(order.metadata || {}),
@@ -236,7 +236,7 @@ function RecentTrades() {
236236
.update(updates)
237237
.eq('id', order.id)
238238
.select();
239-
239+
240240
if (updateError) {
241241
console.error(`Failed to update order ${order.id}:`, updateError);
242242
console.error('Update payload was:', updates);
@@ -291,7 +291,7 @@ function RecentTrades() {
291291
// Add a small delay on initial mount to ensure session is settled
292292
const timeoutId = setTimeout(() => {
293293
fetchAllTrades();
294-
294+
295295
// Also update Alpaca order status if credentials exist
296296
if (hasAlpacaConfig) {
297297
console.log('Alpaca credentials detected, updating order status...');
@@ -587,12 +587,6 @@ function RecentTrades() {
587587
</div>
588588
)}
589589

590-
{/* Show filled details if available */}
591-
{decision.alpacaFilledQty && decision.alpacaFilledPrice ? (
592-
<div className="text-xs text-muted-foreground text-center">
593-
{Number(decision.alpacaFilledQty).toFixed(2)} @ ${Number(decision.alpacaFilledPrice || 0).toFixed(2)}
594-
</div>
595-
) : null}
596590
</>
597591
)}
598592

src/components/TradeHistoryTable.tsx

Lines changed: 35 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -86,13 +86,13 @@ export default function TradeHistoryTable() {
8686
const navigateDate = (direction: 'prev' | 'next') => {
8787
const [year, month, day] = selectedDate.split('-').map(Number);
8888
const currentDate = new Date(year, month - 1, day);
89-
89+
9090
if (direction === 'prev') {
9191
currentDate.setDate(currentDate.getDate() - 1);
9292
} else {
9393
currentDate.setDate(currentDate.getDate() + 1);
9494
}
95-
95+
9696
const newDateString = `${currentDate.getFullYear()}-${String(currentDate.getMonth() + 1).padStart(2, '0')}-${String(currentDate.getDate()).padStart(2, '0')}`;
9797
setSelectedDate(newDateString);
9898
};
@@ -140,7 +140,7 @@ export default function TradeHistoryTable() {
140140
totalValue: item.dollar_amount ? Number(item.dollar_amount) : Number(item.shares) * Number(item.price),
141141
status: item.status,
142142
executedAt: item.executed_at ? formatTimestamp(item.executed_at) : null,
143-
sourceType: item.source_type,
143+
sourceType: item.source_type == 'rebalance' ? item.source_type : 'analysis',
144144
analysisId: item.analysis_id,
145145
rebalanceRequestId: item.rebalance_request_id,
146146
alpacaOrderId: item.metadata?.alpaca_order?.id,
@@ -194,7 +194,7 @@ export default function TradeHistoryTable() {
194194
// Extract all Alpaca order IDs
195195
const alpacaOrderIds = ordersWithAlpacaIds.map(o => o.metadata.alpaca_order.id);
196196
console.log(`Fetching status for ${alpacaOrderIds.length} Alpaca orders:`, alpacaOrderIds);
197-
197+
198198
// Fetch all orders from Alpaca using batch API
199199
const session = await getCachedSession();
200200
if (!session?.access_token) {
@@ -233,33 +233,33 @@ export default function TradeHistoryTable() {
233233

234234
if (alpacaOrder) {
235235
console.log(`Found Alpaca order ${alpacaOrderId} with status: ${alpacaOrder.status}`);
236-
236+
237237
// Check if status has changed or if there's new fill information
238238
const currentAlpacaStatus = order.metadata?.alpaca_order?.status;
239239
const currentFilledQty = order.metadata?.alpaca_order?.filled_qty;
240240
const hasStatusChanged = currentAlpacaStatus !== alpacaOrder.status;
241241
const hasNewFillData = alpacaOrder.filled_qty && alpacaOrder.filled_qty !== currentFilledQty;
242-
242+
243243
// Always update if we don't have a status yet, or if something changed
244244
if (!currentAlpacaStatus || hasStatusChanged || hasNewFillData) {
245245
console.log(`Order ${alpacaOrderId} updating: current status "${currentAlpacaStatus}" -> new status "${alpacaOrder.status}"`);
246246
hasUpdates = true;
247-
247+
248248
// Build the alpaca_order object, only including defined values
249249
const alpacaOrderUpdate: any = {
250250
...(order.metadata?.alpaca_order || {}),
251251
status: alpacaOrder.status,
252252
updated_at: new Date().toISOString()
253253
};
254-
254+
255255
// Only add filled_qty and filled_avg_price if they exist
256256
if (alpacaOrder.filled_qty) {
257257
alpacaOrderUpdate.filled_qty = parseFloat(alpacaOrder.filled_qty);
258258
}
259259
if (alpacaOrder.filled_avg_price) {
260260
alpacaOrderUpdate.filled_avg_price = parseFloat(alpacaOrder.filled_avg_price);
261261
}
262-
262+
263263
// Update metadata with latest Alpaca order info
264264
const updatedMetadata = {
265265
...(order.metadata || {}),
@@ -287,7 +287,7 @@ export default function TradeHistoryTable() {
287287
.update(updates)
288288
.eq('id', order.id)
289289
.select();
290-
290+
291291
if (updateError) {
292292
console.error(`Failed to update order ${order.id}:`, updateError);
293293
console.error('Update payload was:', updates);
@@ -441,15 +441,15 @@ export default function TradeHistoryTable() {
441441
// Filter trades by status
442442
const getFilteredTrades = (status?: string) => {
443443
if (!status || status === 'all') return allTrades;
444-
444+
445445
// Special handling for executed - look at Alpaca order status
446446
if (status === 'executed') {
447-
return allTrades.filter(trade =>
448-
trade.alpacaOrderStatus === 'filled' ||
447+
return allTrades.filter(trade =>
448+
trade.alpacaOrderStatus === 'filled' ||
449449
trade.alpacaOrderStatus === 'partially_filled'
450450
);
451451
}
452-
452+
453453
return allTrades.filter(trade => trade.status === status);
454454
};
455455

@@ -475,16 +475,16 @@ export default function TradeHistoryTable() {
475475
});
476476

477477
// Convert to array and sort chronologically (mixing both types)
478-
478+
479479
// Sort trades within each rebalance group
480480
Array.from(rebalanceGroups.values()).forEach(group => {
481481
group.trades.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
482482
});
483-
483+
484484
// Combine rebalance groups and standalone trades, then sort by creation time
485485
const rebalanceGroupsArray = Array.from(rebalanceGroups.values());
486486
const allItems: (TradeDecision | RebalanceGroup)[] = [...rebalanceGroupsArray, ...standaloneTradesList];
487-
487+
488488
// Sort all items by their creation time (newest first)
489489
allItems.sort((a, b) => {
490490
const timeA = new Date(a.createdAt).getTime();
@@ -527,9 +527,8 @@ export default function TradeHistoryTable() {
527527
return (
528528
<div
529529
key={decision.id}
530-
className={`p-3 rounded-lg border transition-colors flex flex-col gap-3 ${
531-
isInGroup ? 'border-l-2 border-l-border ml-4' : ''
532-
} ${getCardClasses()}`}
530+
className={`p-3 rounded-lg border transition-colors flex flex-col gap-3 ${isInGroup ? 'border-l-2 border-l-border ml-4' : ''
531+
} ${getCardClasses()}`}
533532
>
534533
<div className="flex items-start justify-between gap-3">
535534
<div className="flex gap-3 flex-1">
@@ -544,8 +543,8 @@ export default function TradeHistoryTable() {
544543
<div className="space-y-1 flex-1">
545544
<div className="flex items-center gap-2 flex-wrap">
546545
<span className="font-semibold text-sm">{decision.symbol}</span>
547-
<Badge
548-
variant={decision.action === 'BUY' ? 'buy' : decision.action === 'SELL' ? 'sell' : 'hold'}
546+
<Badge
547+
variant={decision.action === 'BUY' ? 'buy' : decision.action === 'SELL' ? 'sell' : 'hold'}
549548
className="text-xs"
550549
>
551550
{decision.action}
@@ -717,7 +716,7 @@ export default function TradeHistoryTable() {
717716
)}
718717
</div>
719718
</div>
720-
719+
721720
{/* Metadata - at bottom of card */}
722721
<div className="flex items-center gap-2 text-xs text-muted-foreground border-t border-slate-800 pt-2">
723722
{decision.agent && !decision.agent.toLowerCase().includes('portfolio') && (
@@ -729,10 +728,10 @@ export default function TradeHistoryTable() {
729728
{decision.sourceType && (
730729
<>
731730
<span className="capitalize">{decision.sourceType.replace('_', ' ')}</span>
732-
<span></span>
731+
{!isApproved && <span></span>}
733732
</>
734733
)}
735-
<span>{decision.timestamp}</span>
734+
{!isApproved && <span>{decision.timestamp}</span>}
736735
{decision.executedAt && (
737736
<>
738737
<span></span>
@@ -795,7 +794,7 @@ export default function TradeHistoryTable() {
795794
{formatFullDate(group.createdAt)}
796795
</div>
797796
</div>
798-
797+
799798
{/* Rebalance Trades */}
800799
<div className="space-y-3">
801800
{group.trades.map(trade => renderTradeCard(trade, true))}
@@ -820,7 +819,7 @@ export default function TradeHistoryTable() {
820819
<TrendingUp className="h-5 w-5 text-primary" />
821820
Trade History
822821
</CardTitle>
823-
822+
824823
<div className="flex items-center gap-1">
825824
<Button
826825
variant="ghost"
@@ -830,7 +829,7 @@ export default function TradeHistoryTable() {
830829
>
831830
<ChevronLeft className="h-4 w-4" />
832831
</Button>
833-
832+
834833
<Button
835834
variant="outline"
836835
size="sm"
@@ -840,7 +839,7 @@ export default function TradeHistoryTable() {
840839
<CalendarIcon className="h-4 w-4 mr-2" />
841840
{getDateDisplay()}
842841
</Button>
843-
842+
844843
<Button
845844
variant="ghost"
846845
size="sm"
@@ -850,7 +849,7 @@ export default function TradeHistoryTable() {
850849
>
851850
<ChevronRight className="h-4 w-4" />
852851
</Button>
853-
852+
854853
<div className="ml-2">
855854
<Button
856855
variant="ghost"
@@ -874,7 +873,7 @@ export default function TradeHistoryTable() {
874873
</div>
875874
</div>
876875
</CardHeader>
877-
876+
878877
<CardContent>
879878
<Tabs value={activeTab} onValueChange={setActiveTab} className="w-full">
880879
<TabsList className="grid w-full grid-cols-5">
@@ -899,23 +898,23 @@ export default function TradeHistoryTable() {
899898
<span className="hidden sm:inline">Rejected ({getFilteredTrades('rejected').length})</span>
900899
</TabsTrigger>
901900
</TabsList>
902-
901+
903902
<TabsContent value="all" className="mt-6">
904903
{renderContent(getFilteredTrades('all'))}
905904
</TabsContent>
906-
905+
907906
<TabsContent value="pending" className="mt-6">
908907
{renderContent(getFilteredTrades('pending'))}
909908
</TabsContent>
910-
909+
911910
<TabsContent value="approved" className="mt-6">
912911
{renderContent(getFilteredTrades('approved'))}
913912
</TabsContent>
914-
913+
915914
<TabsContent value="executed" className="mt-6">
916915
{renderContent(getFilteredTrades('executed'))}
917916
</TabsContent>
918-
917+
919918
<TabsContent value="rejected" className="mt-6">
920919
{renderContent(getFilteredTrades('rejected'))}
921920
</TabsContent>

0 commit comments

Comments
 (0)