1
1
import LoaderDots from 'components/LoaderDots/LoaderDots.react' ;
2
- import React , { useEffect , useMemo } from 'react' ;
2
+ import React , { useCallback , useEffect , useMemo , useState } from 'react' ;
3
3
import styles from './AggregationPanel.scss' ;
4
+ import Parse from 'parse' ;
4
5
import {
5
6
AudioElement ,
6
7
ButtonElement ,
@@ -19,8 +20,14 @@ const AggregationPanel = ({
19
20
errorAggregatedData,
20
21
showNote,
21
22
setSelectedObjectId,
22
- selectedObjectId
23
+ selectedObjectId,
24
+ depth = 0 ,
25
+ cloudCodeFunction = null ,
26
+ panelTitle = null ,
23
27
} ) => {
28
+ const [ isExpanded , setIsExpanded ] = useState ( false ) ;
29
+ const [ nestedData , setNestedData ] = useState ( null ) ;
30
+ const [ isLoadingNested , setIsLoadingNested ] = useState ( false ) ;
24
31
25
32
useEffect ( ( ) => {
26
33
if ( Object . keys ( errorAggregatedData ) . length !== 0 ) {
@@ -30,54 +37,149 @@ const AggregationPanel = ({
30
37
} , [ errorAggregatedData , setSelectedObjectId , setErrorAggregatedData ] ) ;
31
38
32
39
const isLoading = useMemo ( ( ) =>
33
- selectedObjectId && isLoadingCloudFunction && showAggregatedData ,
34
- [ selectedObjectId , isLoadingCloudFunction , showAggregatedData ]
40
+ depth === 0 && selectedObjectId && isLoadingCloudFunction && showAggregatedData ,
41
+ [ depth , selectedObjectId , isLoadingCloudFunction , showAggregatedData ]
35
42
) ;
36
43
37
44
const shouldShowAggregatedData = useMemo ( ( ) =>
38
- selectedObjectId && showAggregatedData && Object . keys ( data ) . length !== 0 && Object . keys ( errorAggregatedData ) . length === 0 , [ selectedObjectId , showAggregatedData , data , errorAggregatedData ]
45
+ depth === 0
46
+ ? ( selectedObjectId && showAggregatedData && Object . keys ( data ) . length !== 0 && Object . keys ( errorAggregatedData ) . length === 0 )
47
+ : true ,
48
+ [ depth , selectedObjectId , showAggregatedData , data , errorAggregatedData ]
39
49
) ;
40
50
51
+ const fetchNestedData = useCallback ( async ( ) => {
52
+ setIsLoadingNested ( true ) ;
53
+ try {
54
+ const params = { objectId : selectedObjectId } ;
55
+ const result = await Parse . Cloud . run ( cloudCodeFunction , params ) ;
56
+ if ( result ?. panel ?. segments ) {
57
+ setNestedData ( result ) ;
58
+ } else {
59
+ const errorMsg = 'Improper JSON format' ;
60
+ showNote ( errorMsg , true ) ;
61
+ }
62
+ } catch ( error ) {
63
+ const errorMsg = error . message ;
64
+ showNote ( errorMsg , true ) ;
65
+ } finally {
66
+ setIsLoadingNested ( false ) ;
67
+ }
68
+ } , [ cloudCodeFunction , selectedObjectId , showNote ] ) ;
69
+
70
+ const handleToggle = useCallback ( async ( ) => {
71
+ if ( ! isExpanded && ! nestedData && cloudCodeFunction ) {
72
+ fetchNestedData ( ) ;
73
+ }
74
+ setIsExpanded ( prev => ! prev ) ;
75
+ } , [ isExpanded , nestedData , cloudCodeFunction , fetchNestedData ] ) ;
76
+
77
+ const handleRefresh = useCallback ( ( ) => {
78
+ setNestedData ( null ) ;
79
+ setIsExpanded ( false ) ;
80
+ fetchNestedData ( ) ;
81
+ } , [ fetchNestedData ] ) ;
82
+
83
+ const renderSegmentContent = ( segment , index ) => (
84
+ < div key = { index } className = { styles . segmentContainer } >
85
+ < h2 className = { styles . heading } > { segment . title } </ h2 >
86
+ < div className = { styles . segmentItems } >
87
+ { segment . items . map ( ( item , idx ) => {
88
+ switch ( item . type ) {
89
+ case 'text' :
90
+ return < TextElement key = { idx } text = { item . text } /> ;
91
+ case 'keyValue' :
92
+ return < KeyValueElement key = { idx } item = { item } /> ;
93
+ case 'table' :
94
+ return < TableElement key = { idx } columns = { item . columns } rows = { item . rows } /> ;
95
+ case 'image' :
96
+ return < ImageElement key = { idx } url = { item . url } /> ;
97
+ case 'video' :
98
+ return < VideoElement key = { idx } url = { item . url } /> ;
99
+ case 'audio' :
100
+ return < AudioElement key = { idx } url = { item . url } /> ;
101
+ case 'button' :
102
+ return < ButtonElement key = { idx } item = { item } showNote = { showNote } /> ;
103
+ case 'panel' :
104
+ return (
105
+ < div key = { idx } className = { styles . nestedPanelContainer } >
106
+ < AggregationPanel
107
+ data = { { } }
108
+ isLoadingCloudFunction = { false }
109
+ showAggregatedData = { true }
110
+ setErrorAggregatedData = { setErrorAggregatedData }
111
+ errorAggregatedData = { errorAggregatedData }
112
+ showNote = { showNote }
113
+ setSelectedObjectId = { setSelectedObjectId }
114
+ selectedObjectId = { selectedObjectId }
115
+ depth = { depth + 1 }
116
+ cloudCodeFunction = { item . cloudCodeFunction }
117
+ panelTitle = { item . title }
118
+ />
119
+ </ div >
120
+ ) ;
121
+ default :
122
+ return null ;
123
+ }
124
+ } ) }
125
+ </ div >
126
+ </ div >
127
+ ) ;
128
+
129
+ if ( depth > 0 ) {
130
+ return (
131
+ < div className = { styles . nestedPanel } >
132
+ < div className = { `${ styles . nestedPanelHeader } ${ isExpanded ? styles . expanded : '' } ` } onClick = { handleToggle } >
133
+ < span className = { `${ styles . expandButton } ${ isExpanded ? styles . expanded : '' } ` } > { panelTitle } </ span >
134
+ < div >
135
+ { isExpanded && (
136
+ < button
137
+ onClick = { handleRefresh }
138
+ className = { styles . refreshButton }
139
+ disabled = { isLoadingNested }
140
+ >
141
+ < span > ↻</ span >
142
+ </ button >
143
+
144
+ ) }
145
+ < span > { isExpanded ? '▼' : '▲' } </ span >
146
+ </ div >
147
+ </ div >
148
+ { isExpanded && (
149
+ < div className = { styles . nestedPanelContent } >
150
+ { isLoadingNested ? (
151
+ < div className = { styles . loader } >
152
+ < LoaderDots />
153
+ </ div >
154
+ ) : (
155
+ nestedData && nestedData . panel . segments . map ( ( segment , index ) =>
156
+ renderSegmentContent ( segment , index )
157
+ )
158
+ ) }
159
+ </ div >
160
+ ) }
161
+ </ div >
162
+ ) ;
163
+ }
164
+
41
165
return (
42
- < >
166
+ < div className = { styles . aggregationPanel } >
43
167
{ isLoading ? (
44
168
< div className = { styles . center } >
45
169
< LoaderDots />
46
170
</ div >
47
171
) : shouldShowAggregatedData ? (
48
- data . panel . segments . map ( ( segment , index ) => (
49
- < div key = { index } >
50
- < h2 className = { styles . heading } > { segment . title } </ h2 >
51
- < div className = { styles . segmentItems } >
52
- { segment . items . map ( ( item , idx ) => {
53
- switch ( item . type ) {
54
- case 'text' :
55
- return < TextElement key = { idx } text = { item . text } /> ;
56
- case 'keyValue' :
57
- return < KeyValueElement key = { idx } item = { item } /> ;
58
- case 'table' :
59
- return < TableElement key = { idx } columns = { item . columns } rows = { item . rows } /> ;
60
- case 'image' :
61
- return < ImageElement key = { idx } url = { item . url } /> ;
62
- case 'video' :
63
- return < VideoElement key = { idx } url = { item . url } /> ;
64
- case 'audio' :
65
- return < AudioElement key = { idx } url = { item . url } /> ;
66
- case 'button' :
67
- return < ButtonElement key = { idx } item = { item } showNote = { showNote } /> ;
68
- default :
69
- return null ;
70
- }
71
- } ) }
72
- </ div >
73
- </ div >
74
- ) )
172
+ < div className = { styles . mainContent } >
173
+ { data . panel . segments . map ( ( segment , index ) =>
174
+ renderSegmentContent ( segment , index )
175
+ ) }
176
+ </ div >
75
177
) : (
76
- < div className = { styles . loading } >
77
- No object selected.
178
+ < div className = { styles . center } >
179
+ No object selected.
78
180
</ div >
79
181
) }
80
- </ >
182
+ </ div >
81
183
) ;
82
184
} ;
83
185
0 commit comments