1- import { useState , useEffect , useRef } from "react" ;
1+ import React , { useState , useEffect , useRef } from "react" ;
22import {
33 Dialog ,
4- DialogContent ,
54 DialogDescription ,
65 DialogHeader ,
76 DialogTitle ,
87} from "@/components/ui/dialog" ;
8+ import * as DialogPrimitive from "@radix-ui/react-dialog" ;
9+ import { cn } from "@/lib/utils" ;
910import { Badge } from "@/components/ui/badge" ;
11+ import { Button } from "@/components/ui/button" ;
1012import { Tabs , TabsContent , TabsList , TabsTrigger } from "@/components/ui/tabs" ;
1113import {
1214 RefreshCw ,
@@ -17,7 +19,10 @@ import {
1719 Target ,
1820 Brain ,
1921 Activity ,
20- XCircle
22+ XCircle ,
23+ X ,
24+ ChevronUp ,
25+ ChevronDown
2126} from "lucide-react" ;
2227import { formatDistanceToNow } from "date-fns" ;
2328import { supabase } from "@/lib/supabase" ;
@@ -66,7 +71,26 @@ interface RebalancePosition {
6671 tradeActionId ?: string ;
6772}
6873
69-
74+ // Custom DialogContent without the default close button
75+ const DialogContentNoClose = React . forwardRef <
76+ React . ElementRef < typeof DialogPrimitive . Content > ,
77+ React . ComponentPropsWithoutRef < typeof DialogPrimitive . Content >
78+ > ( ( { className, children, ...props } , ref ) => (
79+ < DialogPrimitive . Portal >
80+ < DialogPrimitive . Overlay className = "fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0" />
81+ < DialogPrimitive . Content
82+ ref = { ref }
83+ className = { cn (
84+ "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg" ,
85+ className
86+ ) }
87+ { ...props }
88+ >
89+ { children }
90+ </ DialogPrimitive . Content >
91+ </ DialogPrimitive . Portal >
92+ ) ) ;
93+ DialogContentNoClose . displayName = "DialogContentNoClose" ;
7094
7195export default function RebalanceDetailModal ( { rebalanceId, isOpen, onClose, rebalanceDate } : RebalanceDetailModalProps ) {
7296 const { user } = useAuth ( ) ;
@@ -76,6 +100,7 @@ export default function RebalanceDetailModal({ rebalanceId, isOpen, onClose, reb
76100 const [ loading , setLoading ] = useState ( true ) ;
77101 const [ error , setError ] = useState < string | null > ( null ) ;
78102 const [ isLiveRebalance , setIsLiveRebalance ] = useState ( false ) ;
103+ const [ collapsedCards , setCollapsedCards ] = useState < Set < string > > ( new Set ( ) ) ;
79104 const intervalRef = useRef < NodeJS . Timeout | undefined > ( ) ;
80105
81106 const [ selectedAnalysis , setSelectedAnalysis ] = useState < {
@@ -682,7 +707,7 @@ export default function RebalanceDetailModal({ rebalanceId, isOpen, onClose, reb
682707 return (
683708 < >
684709 < Dialog open = { isOpen } onOpenChange = { ( ) => onClose ( ) } >
685- < DialogContent className = "max-w-7xl max-h-[90vh] p-0" >
710+ < DialogContentNoClose className = "max-w-7xl max-h-[90vh] p-0" >
686711 < DialogHeader className = "px-6 pt-6 pb-4 border-b" >
687712 < div className = "flex items-center justify-between" >
688713 < div className = "flex items-center gap-3" >
@@ -715,6 +740,16 @@ export default function RebalanceDetailModal({ rebalanceId, isOpen, onClose, reb
715740 </ Badge >
716741 ) }
717742 </ div >
743+
744+ { /* Close button */ }
745+ < Button
746+ size = "sm"
747+ variant = "outline"
748+ className = "border border-slate-700"
749+ onClick = { ( ) => onClose ( ) }
750+ >
751+ < X className = "h-4 w-4" />
752+ </ Button >
718753 </ div >
719754 < DialogDescription className = "mt-2 flex justify-between items-center" >
720755 < span >
@@ -734,20 +769,82 @@ export default function RebalanceDetailModal({ rebalanceId, isOpen, onClose, reb
734769
735770 < Tabs value = { activeTab } onValueChange = { setActiveTab } className = "flex-1" >
736771 < div className = "px-6 pt-4 pb-4" >
737- < TabsList className = "grid w-full grid-cols-3 max-w-3xl mx-auto" >
738- < TabsTrigger value = "actions" className = "flex items-center gap-2" >
739- < Target className = "w-4 h-4" />
740- Actions
741- </ TabsTrigger >
742- < TabsTrigger value = "workflow" className = "flex items-center gap-2" >
743- < Activity className = "w-4 h-4" />
744- Workflow
745- </ TabsTrigger >
746- < TabsTrigger value = "insights" className = "flex items-center gap-2" >
747- < Brain className = "w-4 h-4" />
748- Insights
749- </ TabsTrigger >
750- </ TabsList >
772+ < div className = "relative flex items-center justify-center" >
773+ < TabsList className = "grid w-full grid-cols-3 max-w-3xl" >
774+ < TabsTrigger value = "actions" className = "flex items-center gap-2" >
775+ < Target className = "w-4 h-4" />
776+ Actions
777+ </ TabsTrigger >
778+ < TabsTrigger value = "workflow" className = "flex items-center gap-2" >
779+ < Activity className = "w-4 h-4" />
780+ Workflow
781+ </ TabsTrigger >
782+ < TabsTrigger value = "insights" className = "flex items-center gap-2" >
783+ < Brain className = "w-4 h-4" />
784+ Insights
785+ </ TabsTrigger >
786+ </ TabsList >
787+ { activeTab === "insights" && (
788+ < Button
789+ variant = "outline"
790+ size = "sm"
791+ onClick = { ( ) => {
792+ if ( collapsedCards . size === 0 ) {
793+ // Collapse all - need to get all card keys
794+ const allCardKeys = new Set < string > ( ) ;
795+
796+ // Add threshold card if it exists
797+ if ( ! rebalanceData ?. skipThresholdCheck ) {
798+ const thresholdStep = rebalanceData ?. workflowSteps ?. find ( ( s : any ) => s . id === 'threshold' ) ;
799+ if ( thresholdStep ?. insights ) {
800+ allCardKeys . add ( 'threshold' ) ;
801+ }
802+ }
803+
804+ // Add opportunity card if it exists
805+ if ( ! rebalanceData ?. skipOpportunityAgent ) {
806+ const opportunityStep = rebalanceData ?. workflowSteps ?. find ( ( s : any ) => s . id === 'opportunity' ) ;
807+ if ( opportunityStep ?. insights || opportunityStep ?. data ) {
808+ allCardKeys . add ( 'opportunity' ) ;
809+ }
810+ }
811+
812+ // Add portfolio manager card if it exists
813+ const portfolioInsights =
814+ rebalanceData ?. rebalance_plan ?. portfolioManagerAnalysis ||
815+ rebalanceData ?. rebalance_plan ?. portfolioManagerInsights ||
816+ rebalanceData ?. rebalance_plan ?. rebalance_agent_insight ||
817+ rebalanceData ?. rebalance_plan ?. agentInsights ?. portfolioManager ||
818+ rebalanceData ?. rebalance_plan ?. agentInsights ?. rebalanceAgent ||
819+ rebalanceData ?. agentInsights ?. portfolioManager ||
820+ rebalanceData ?. agentInsights ?. rebalanceAgent ;
821+
822+ if ( portfolioInsights ) {
823+ allCardKeys . add ( 'portfolioManager' ) ;
824+ }
825+
826+ setCollapsedCards ( allCardKeys ) ;
827+ } else {
828+ // Expand all
829+ setCollapsedCards ( new Set ( ) ) ;
830+ }
831+ } }
832+ className = "text-xs absolute right-0"
833+ >
834+ { collapsedCards . size === 0 ? (
835+ < >
836+ < ChevronUp className = "h-3 w-3 mr-1" />
837+ Collapse All
838+ </ >
839+ ) : (
840+ < >
841+ < ChevronDown className = "h-3 w-3 mr-1" />
842+ Expand All
843+ </ >
844+ ) }
845+ </ Button >
846+ ) }
847+ </ div >
751848 </ div >
752849
753850 { error ? (
@@ -790,11 +887,13 @@ export default function RebalanceDetailModal({ rebalanceId, isOpen, onClose, reb
790887 rebalanceData = { rebalanceData }
791888 selectedAnalysis = { selectedAnalysis }
792889 setSelectedAnalysis = { setSelectedAnalysis }
890+ collapsedCards = { collapsedCards }
891+ setCollapsedCards = { setCollapsedCards }
793892 />
794893 </ >
795894 ) }
796895 </ Tabs >
797- </ DialogContent >
896+ </ DialogContentNoClose >
798897 </ Dialog >
799898
800899 { /* Analysis Detail Modal */ }
0 commit comments