11import LoaderDots from 'components/LoaderDots/LoaderDots.react' ;
2- import React , { useEffect , useMemo } from 'react' ;
2+ import React , { useCallback , useEffect , useMemo , useState } from 'react' ;
33import styles from './AggregationPanel.scss' ;
4+ import Parse from 'parse' ;
45import {
56 AudioElement ,
67 ButtonElement ,
@@ -19,8 +20,14 @@ const AggregationPanel = ({
1920 errorAggregatedData,
2021 showNote,
2122 setSelectedObjectId,
22- selectedObjectId
23+ selectedObjectId,
24+ depth = 0 ,
25+ cloudCodeFunction = null ,
26+ panelTitle = null ,
2327} ) => {
28+ const [ isExpanded , setIsExpanded ] = useState ( false ) ;
29+ const [ nestedData , setNestedData ] = useState ( null ) ;
30+ const [ isLoadingNested , setIsLoadingNested ] = useState ( false ) ;
2431
2532 useEffect ( ( ) => {
2633 if ( Object . keys ( errorAggregatedData ) . length !== 0 ) {
@@ -30,54 +37,149 @@ const AggregationPanel = ({
3037 } , [ errorAggregatedData , setSelectedObjectId , setErrorAggregatedData ] ) ;
3138
3239 const isLoading = useMemo ( ( ) =>
33- selectedObjectId && isLoadingCloudFunction && showAggregatedData ,
34- [ selectedObjectId , isLoadingCloudFunction , showAggregatedData ]
40+ depth === 0 && selectedObjectId && isLoadingCloudFunction && showAggregatedData ,
41+ [ depth , selectedObjectId , isLoadingCloudFunction , showAggregatedData ]
3542 ) ;
3643
3744 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 ]
3949 ) ;
4050
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+
41165 return (
42- < >
166+ < div className = { styles . aggregationPanel } >
43167 { isLoading ? (
44168 < div className = { styles . center } >
45169 < LoaderDots />
46170 </ div >
47171 ) : 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 >
75177 ) : (
76- < div className = { styles . loading } >
77- No object selected.
178+ < div className = { styles . center } >
179+ No object selected.
78180 </ div >
79181 ) }
80- </ >
182+ </ div >
81183 ) ;
82184} ;
83185
0 commit comments