1+ import { useEffect , useState } from 'react' ;
2+ import {
3+ AlertDialog ,
4+ AlertDialogContent ,
5+ AlertDialogDescription ,
6+ AlertDialogHeader ,
7+ AlertDialogTitle ,
8+ AlertDialogFooter ,
9+ AlertDialogAction ,
10+ } from "@/components/ui/alert-dialog" ;
11+ import { AlertTriangle , ExternalLink , RefreshCw } from 'lucide-react' ;
12+ import { Button } from '@/components/ui/button' ;
13+ import { useAlpacaConnectionStore } from '@/hooks/useAlpacaConnection' ;
14+
15+ export function AlpacaConnectionErrorModal ( ) {
16+ const { isConnected, lastError, checkConnection, isLoading } = useAlpacaConnectionStore ( ) ;
17+ const [ isOpen , setIsOpen ] = useState ( false ) ;
18+ const [ hasShownOnce , setHasShownOnce ] = useState ( false ) ;
19+
20+ useEffect ( ( ) => {
21+ // Show modal when connection is lost and we haven't shown it already
22+ if ( ! isConnected && ! hasShownOnce ) {
23+ setIsOpen ( true ) ;
24+ setHasShownOnce ( true ) ;
25+ }
26+
27+ // Reset the "shown once" flag when connection is restored
28+ if ( isConnected && hasShownOnce ) {
29+ setHasShownOnce ( false ) ;
30+ setIsOpen ( false ) ;
31+ }
32+ } , [ isConnected , hasShownOnce ] ) ;
33+
34+ const handleRetry = async ( ) => {
35+ await checkConnection ( ) ;
36+ // Modal will auto-close if connection is restored
37+ } ;
38+
39+ return (
40+ < AlertDialog open = { isOpen } onOpenChange = { setIsOpen } >
41+ < AlertDialogContent >
42+ < AlertDialogHeader >
43+ < AlertDialogTitle className = "flex items-center gap-2" >
44+ < AlertTriangle className = "h-5 w-5 text-yellow-500" />
45+ Trading Platform Connection Error
46+ </ AlertDialogTitle >
47+ < AlertDialogDescription className = "space-y-3" >
48+ < p >
49+ Unable to connect to the Alpaca trading platform and data source.
50+ This may be due to service maintenance or connectivity issues.
51+ </ p >
52+
53+ < p className = "font-medium" >
54+ While the connection is down:
55+ </ p >
56+ < ul className = "list-disc list-inside space-y-1 text-sm" >
57+ < li > Analysis features are temporarily disabled</ li >
58+ < li > Portfolio rebalancing is unavailable</ li >
59+ < li > Real-time market data cannot be fetched</ li >
60+ </ ul >
61+
62+ { lastError && (
63+ < p className = "text-sm text-muted-foreground italic mt-2" >
64+ { lastError }
65+ </ p >
66+ ) }
67+
68+ < div className = "flex items-center gap-2 pt-2" >
69+ < a
70+ href = "https://app.alpaca.markets/dashboard/overview"
71+ target = "_blank"
72+ rel = "noopener noreferrer"
73+ className = "inline-flex items-center gap-1 text-sm text-blue-500 hover:underline"
74+ >
75+ Check Alpaca Status
76+ < ExternalLink className = "h-3 w-3" />
77+ </ a >
78+ </ div >
79+ </ AlertDialogDescription >
80+ </ AlertDialogHeader >
81+ < AlertDialogFooter className = "flex gap-2" >
82+ < Button
83+ variant = "outline"
84+ size = "sm"
85+ onClick = { handleRetry }
86+ disabled = { isLoading }
87+ >
88+ { isLoading ? (
89+ < >
90+ < RefreshCw className = "h-4 w-4 mr-2 animate-spin" />
91+ Checking...
92+ </ >
93+ ) : (
94+ < >
95+ < RefreshCw className = "h-4 w-4 mr-2" />
96+ Retry Connection
97+ </ >
98+ ) }
99+ </ Button >
100+ < AlertDialogAction onClick = { ( ) => setIsOpen ( false ) } >
101+ Close
102+ </ AlertDialogAction >
103+ </ AlertDialogFooter >
104+ </ AlertDialogContent >
105+ </ AlertDialog >
106+ ) ;
107+ }
0 commit comments