11import { Link , useNavigate } from "react-router-dom" ;
2- import { useState , useEffect } from "react" ;
2+ import { useState , useEffect , useMemo } from "react" ;
33import { Button } from "@/components/ui/button" ;
44import {
55 TrendingUp ,
@@ -15,11 +15,11 @@ import {
1515 Menu ,
1616 Loader2
1717} from "lucide-react" ;
18- import { useAuth , hasRequiredApiKeys , isSessionValid } from "@/lib/auth" ;
18+ import { useAuth , hasRequiredApiKeys , hasAlpacaCredentials , isSessionValid } from "@/lib/auth" ;
1919import { RoleBadge , RoleGate } from "@/components/RoleBasedAccess" ;
2020import { useRBAC } from "@/hooks/useRBAC" ;
2121import { supabase } from "@/lib/supabase" ;
22- import {
22+ import {
2323 ANALYSIS_STATUS ,
2424 convertLegacyAnalysisStatus ,
2525 isAnalysisActive ,
@@ -41,7 +41,20 @@ export default function Header() {
4141 const [ runningAnalyses , setRunningAnalyses ] = useState ( 0 ) ;
4242 const [ runningRebalances , setRunningRebalances ] = useState ( 0 ) ;
4343
44- const hasApiKeys = hasRequiredApiKeys ( apiSettings ) ;
44+ const hasAiConfig = hasRequiredApiKeys ( apiSettings ) ;
45+ const hasAlpacaConfig = hasAlpacaCredentials ( apiSettings ) ;
46+ const systemStatus = useMemo ( ( ) => {
47+ if ( hasAiConfig && hasAlpacaConfig ) {
48+ return { dotClass : 'bg-buy' , message : 'All Agents Ready' } ;
49+ }
50+ if ( hasAiConfig && ! hasAlpacaConfig ) {
51+ return { dotClass : 'bg-yellow-500' , message : 'Alpaca Config Required' } ;
52+ }
53+ if ( ! hasAiConfig && hasAlpacaConfig ) {
54+ return { dotClass : 'bg-yellow-500' , message : 'AI Config Required' } ;
55+ }
56+ return { dotClass : 'bg-red-500' , message : 'Require API Configurations' } ;
57+ } , [ hasAiConfig , hasAlpacaConfig ] ) ;
4558 const primaryRole = getPrimaryRole ( ) ;
4659
4760 // Check for running analyses and rebalances
@@ -59,7 +72,7 @@ export default function Header() {
5972 // Check running analyses - only fetch from last 24 hours
6073 const twentyFourHoursAgo = new Date ( ) ;
6174 twentyFourHoursAgo . setHours ( twentyFourHoursAgo . getHours ( ) - 24 ) ;
62-
75+
6376 const { data : analysisData } = await supabase
6477 . from ( 'analysis_history' )
6578 . select ( 'id, analysis_status, is_canceled' )
@@ -68,14 +81,14 @@ export default function Header() {
6881
6982 if ( analysisData ) {
7083 const runningCount = analysisData . filter ( item => {
71- const currentStatus = typeof item . analysis_status === 'number'
84+ const currentStatus = typeof item . analysis_status === 'number'
7285 ? convertLegacyAnalysisStatus ( item . analysis_status )
7386 : item . analysis_status ;
74-
87+
7588 if ( item . is_canceled || currentStatus === ANALYSIS_STATUS . CANCELLED ) {
7689 return false ;
7790 }
78-
91+
7992 return isAnalysisActive ( currentStatus ) ;
8093 } ) . length ;
8194 setRunningAnalyses ( runningCount ) ;
@@ -88,7 +101,7 @@ export default function Header() {
88101 . eq ( 'user_id' , user . id ) ;
89102
90103 if ( rebalanceData ) {
91- const runningCount = rebalanceData . filter ( item =>
104+ const runningCount = rebalanceData . filter ( item =>
92105 isRebalanceActive ( item . status )
93106 ) . length ;
94107 setRunningRebalances ( runningCount ) ;
@@ -157,24 +170,23 @@ export default function Header() {
157170 < DropdownMenu >
158171 < DropdownMenuTrigger asChild className = "hidden md:flex" >
159172 { isRoleLoading ? (
160- < Button
161- variant = "ghost"
162- size = "sm"
173+ < Button
174+ variant = "ghost"
175+ size = "sm"
163176 className = "max-w-[150px] sm:max-w-none"
164177 disabled
165178 >
166179 < Loader2 className = "h-4 w-4 mr-2 animate-spin" />
167180 < span className = "truncate" > Loading...</ span >
168181 </ Button >
169182 ) : (
170- < Button
171- variant = "ghost"
172- size = "sm"
173- className = { `max-w-[150px] sm:max-w-none transition-all duration-200 ${
174- primaryRole ?. color
175- ? `border border-opacity-30 hover:bg-opacity-20`
176- : 'border border-border hover:bg-accent'
177- } `}
183+ < Button
184+ variant = "ghost"
185+ size = "sm"
186+ className = { `max-w-[150px] sm:max-w-none transition-all duration-200 ${ primaryRole ?. color
187+ ? `border border-opacity-30 hover:bg-opacity-20`
188+ : 'border border-border hover:bg-accent'
189+ } `}
178190 style = { primaryRole ?. color ? {
179191 borderColor : `${ primaryRole . color } 4D` , // 30% opacity
180192 backgroundColor : `${ primaryRole . color } 1A` , // 10% opacity
@@ -192,8 +204,8 @@ export default function Header() {
192204 } }
193205 >
194206 { primaryRole ?. icon_url ? (
195- < img
196- src = { primaryRole . icon_url }
207+ < img
208+ src = { primaryRole . icon_url }
197209 alt = { primaryRole . display_name }
198210 className = "h-4 w-4 mr-2 object-contain"
199211 />
@@ -333,9 +345,9 @@ export default function Header() {
333345 < div className = "text-right hidden sm:block" >
334346 < p className = "text-sm font-medium text-foreground" > System Status</ p >
335347 < div className = "flex items-center gap-2" >
336- < div className = { `h-2 w-2 rounded-full ${ hasApiKeys ? 'bg-buy' : 'bg-yellow-500' } animate-pulse` } > </ div >
348+ < div className = { `h-2 w-2 rounded-full ${ systemStatus . dotClass } animate-pulse` } > </ div >
337349 < span className = "text-xs text-muted-foreground" >
338- { hasApiKeys ? 'All Agents Ready' : 'API Keys Required' }
350+ { systemStatus . message }
339351 </ span >
340352 </ div >
341353 </ div >
@@ -364,7 +376,7 @@ export default function Header() {
364376 </ div >
365377 </ div >
366378 </ header >
367-
379+
368380 { /* Running tasks banner */ }
369381 { isAuthenticated && ( runningAnalyses > 0 || runningRebalances > 0 ) && (
370382 < div className = "bg-primary/10 border-b border-primary/20 backdrop-blur-sm" >
@@ -392,4 +404,4 @@ export default function Header() {
392404 ) }
393405 </ div >
394406 ) ;
395- }
407+ }
0 commit comments