@@ -9,14 +9,18 @@ import {
9
9
useFunnelDetails ,
10
10
useFunnelsList ,
11
11
} from 'hooks/TracesFunnels/useFunnels' ;
12
- import { ArrowLeft , Plus , Search } from 'lucide-react' ;
12
+ import { isEqual } from 'lodash-es' ;
13
+ import { ArrowLeft , Check , Plus , Search } from 'lucide-react' ;
13
14
import FunnelConfiguration from 'pages/TracesFunnelDetails/components/FunnelConfiguration/FunnelConfiguration' ;
14
15
import { TracesFunnelsContentRenderer } from 'pages/TracesFunnels' ;
15
16
import CreateFunnel from 'pages/TracesFunnels/components/CreateFunnel/CreateFunnel' ;
16
17
import { FunnelListItem } from 'pages/TracesFunnels/components/FunnelsList/FunnelsList' ;
17
- import { FunnelProvider } from 'pages/TracesFunnels/FunnelContext' ;
18
+ import {
19
+ FunnelProvider ,
20
+ useFunnelContext ,
21
+ } from 'pages/TracesFunnels/FunnelContext' ;
18
22
import { filterFunnelsByQuery } from 'pages/TracesFunnels/utils' ;
19
- import { ChangeEvent , useMemo , useState } from 'react' ;
23
+ import { ChangeEvent , useEffect , useMemo , useState } from 'react' ;
20
24
import { Span } from 'types/api/trace/getTraceV2' ;
21
25
import { FunnelData } from 'types/api/traceFunnels' ;
22
26
@@ -28,21 +32,54 @@ enum ModalView {
28
32
function FunnelDetailsView ( {
29
33
funnel,
30
34
span,
35
+ triggerAutoSave,
36
+ showNotifications,
37
+ onChangesDetected,
38
+ triggerDiscard,
31
39
} : {
32
40
funnel : FunnelData ;
33
41
span : Span ;
42
+ triggerAutoSave : boolean ;
43
+ showNotifications : boolean ;
44
+ onChangesDetected : ( hasChanges : boolean ) => void ;
45
+ triggerDiscard : boolean ;
34
46
} ) : JSX . Element {
47
+ const { handleRestoreSteps, steps } = useFunnelContext ( ) ;
48
+
49
+ // Track changes between current steps and original steps
50
+ useEffect ( ( ) => {
51
+ const hasChanges = ! isEqual ( steps , funnel . steps ) ;
52
+ if ( onChangesDetected ) {
53
+ onChangesDetected ( hasChanges ) ;
54
+ }
55
+ } , [ steps , funnel . steps , onChangesDetected ] ) ;
56
+
57
+ // Handle discard when triggered from parent
58
+ useEffect ( ( ) => {
59
+ if ( triggerDiscard && funnel . steps ) {
60
+ handleRestoreSteps ( funnel . steps ) ;
61
+ }
62
+ } , [ triggerDiscard , funnel . steps , handleRestoreSteps ] ) ;
63
+
35
64
return (
36
65
< div className = "add-span-to-funnel-modal__details" >
37
66
< FunnelListItem
38
67
funnel = { funnel }
39
68
shouldRedirectToTracesListOnDeleteSuccess = { false }
40
69
isSpanDetailsPage
41
70
/>
42
- < FunnelConfiguration funnel = { funnel } isTraceDetailsPage span = { span } />
71
+ < FunnelConfiguration
72
+ funnel = { funnel }
73
+ isTraceDetailsPage
74
+ span = { span }
75
+ disableAutoSave
76
+ triggerAutoSave = { triggerAutoSave }
77
+ showNotifications = { showNotifications }
78
+ />
43
79
</ div >
44
80
) ;
45
81
}
82
+
46
83
interface AddSpanToFunnelModalProps {
47
84
isOpen : boolean ;
48
85
onClose : ( ) => void ;
@@ -60,6 +97,9 @@ function AddSpanToFunnelModal({
60
97
undefined ,
61
98
) ;
62
99
const [ isCreateModalOpen , setIsCreateModalOpen ] = useState < boolean > ( false ) ;
100
+ const [ triggerSave , setTriggerSave ] = useState < boolean > ( false ) ;
101
+ const [ isUnsavedChanges , setIsUnsavedChanges ] = useState < boolean > ( false ) ;
102
+ const [ triggerDiscard , setTriggerDiscard ] = useState < boolean > ( false ) ;
63
103
64
104
const handleSearch = ( e : ChangeEvent < HTMLInputElement > ) : void => {
65
105
setSearchQuery ( e . target . value ) ;
@@ -92,12 +132,26 @@ function AddSpanToFunnelModal({
92
132
const handleBack = ( ) : void => {
93
133
setActiveView ( ModalView . LIST ) ;
94
134
setSelectedFunnelId ( undefined ) ;
135
+ setIsUnsavedChanges ( false ) ;
136
+ setTriggerSave ( false ) ;
95
137
} ;
96
138
97
139
const handleCreateNewClick = ( ) : void => {
98
140
setIsCreateModalOpen ( true ) ;
99
141
} ;
100
142
143
+ const handleSaveFunnel = ( ) : void => {
144
+ setTriggerSave ( true ) ;
145
+ // Reset trigger after a brief moment to allow the save to be processed
146
+ setTimeout ( ( ) => setTriggerSave ( false ) , 100 ) ;
147
+ } ;
148
+
149
+ const handleDiscard = ( ) : void => {
150
+ setTriggerDiscard ( true ) ;
151
+ // Reset trigger after a brief moment
152
+ setTimeout ( ( ) => setTriggerDiscard ( false ) , 100 ) ;
153
+ } ;
154
+
101
155
const renderListView = ( ) : JSX . Element => (
102
156
< div className = "add-span-to-funnel-modal" >
103
157
{ ! ! filteredData ?. length && (
@@ -156,7 +210,14 @@ function AddSpanToFunnelModal({
156
210
< div className = "traces-funnel-details__steps-config" >
157
211
{ selectedFunnelId && funnelDetails ?. payload && (
158
212
< FunnelProvider funnelId = { selectedFunnelId } >
159
- < FunnelDetailsView funnel = { funnelDetails . payload } span = { span } />
213
+ < FunnelDetailsView
214
+ funnel = { funnelDetails . payload }
215
+ span = { span }
216
+ triggerAutoSave = { triggerSave }
217
+ showNotifications
218
+ onChangesDetected = { setIsUnsavedChanges }
219
+ triggerDiscard = { triggerDiscard }
220
+ />
160
221
</ FunnelProvider >
161
222
) }
162
223
</ div >
@@ -175,18 +236,43 @@ function AddSpanToFunnelModal({
175
236
'add-span-to-funnel-modal-container--details' :
176
237
activeView === ModalView . DETAILS ,
177
238
} ) }
178
- okText = "Save Funnel"
179
239
footer = {
180
- activeView === ModalView . LIST && ! ! filteredData ?. length ? (
181
- < Button
182
- type = "default"
183
- className = "add-span-to-funnel-modal__create-button"
184
- onClick = { handleCreateNewClick }
185
- icon = { < Plus size = { 14 } /> }
186
- >
187
- Create new funnel
188
- </ Button >
189
- ) : null
240
+ activeView === ModalView . DETAILS
241
+ ? [
242
+ < Button key = "close" onClick = { onClose } >
243
+ Close
244
+ </ Button > ,
245
+ < Button
246
+ type = "default"
247
+ key = "discard"
248
+ onClick = { handleDiscard }
249
+ className = "add-span-to-funnel-modal__discard-button"
250
+ disabled = { ! isUnsavedChanges }
251
+ >
252
+ Discard
253
+ </ Button > ,
254
+ < Button
255
+ key = "save"
256
+ type = "primary"
257
+ className = "add-span-to-funnel-modal__save-button"
258
+ onClick = { handleSaveFunnel }
259
+ disabled = { ! isUnsavedChanges }
260
+ icon = { < Check size = { 14 } color = "var(--bg-vanilla-100)" /> }
261
+ >
262
+ Save Funnel
263
+ </ Button > ,
264
+ ]
265
+ : [
266
+ < Button
267
+ key = "create"
268
+ type = "default"
269
+ className = "add-span-to-funnel-modal__create-button"
270
+ onClick = { handleCreateNewClick }
271
+ icon = { < Plus size = { 14 } /> }
272
+ >
273
+ Create new funnel
274
+ </ Button > ,
275
+ ]
190
276
}
191
277
>
192
278
{ activeView === ModalView . LIST
0 commit comments