Skip to content

Commit 8e5741d

Browse files
authored
feat: Add info panel item panel to load and display data on demand (parse-community#2622)
1 parent aa73757 commit 8e5741d

File tree

2 files changed

+207
-42
lines changed

2 files changed

+207
-42
lines changed

src/components/AggregationPanel/AggregationPanel.js

+138-36
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import LoaderDots from 'components/LoaderDots/LoaderDots.react';
2-
import React, { useEffect, useMemo } from 'react';
2+
import React, { useCallback, useEffect, useMemo, useState } from 'react';
33
import styles from './AggregationPanel.scss';
4+
import Parse from 'parse';
45
import {
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

src/components/AggregationPanel/AggregationPanel.scss

+69-6
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,11 @@
1111

1212
.segmentItems {
1313
font-size: 14px;
14-
padding-left: 10px;
15-
padding-right: 10px;
16-
padding-top: 6px;
14+
padding: 10px;
1715
display: flex;
1816
flex-direction: column;
19-
border-left: 1px solid #e3e3ea;
2017
gap: 10px;
21-
}
18+
}
2219

2320
.keyValue {
2421
font-size: 14px;
@@ -76,13 +73,14 @@
7673
text-align: center;
7774
border-radius: 5px;
7875
font-size: 14px;
76+
7977
&:hover,
8078
&:focus {
8179
background-color: $darkBlue;
8280
}
8381
}
8482

85-
.loading{
83+
.loading {
8684
height: 100%;
8785
display: flex;
8886
flex-direction: column;
@@ -96,4 +94,69 @@
9694
top: 50%;
9795
left: 50%;
9896
@include transform(translate(-50%, -50%));
97+
}
98+
99+
.nestedPanel {
100+
margin-right: -10px;
101+
transition: all 0.3s ease;
102+
}
103+
104+
.nestedPanelHeader {
105+
display: flex;
106+
align-items: center;
107+
justify-content: space-between;
108+
gap: 8px;
109+
border-left: 1px solid transparent;
110+
background-color: $blue;
111+
color: $white;
112+
cursor: pointer;
113+
padding-right: 4px;
114+
115+
&.expanded {
116+
border-left-color: #e3e3ea;
117+
}
118+
}
119+
120+
.expandButton {
121+
display: inline;
122+
padding: 8px;
123+
font-weight: 500;
124+
125+
&.expanded {
126+
font-weight: 600;
127+
}
128+
}
129+
130+
.refreshButton {
131+
background: none;
132+
border: none;
133+
padding: 4px;
134+
cursor: pointer;
135+
color: #666;
136+
font-size: 16px;
137+
138+
&:hover {
139+
color: #333;
140+
}
141+
142+
&:disabled {
143+
color: #ccc;
144+
cursor: not-allowed;
145+
}
146+
}
147+
148+
.nestedPanelContent {
149+
padding: 0 0 8px 0;
150+
}
151+
152+
.loader {
153+
display: flex;
154+
align-items: center;
155+
justify-content: center;
156+
}
157+
158+
.segmentContainer {
159+
border-left: 1px solid #e3e3ea;
160+
border-bottom: 1px solid #e3e3ea;
161+
margin-bottom: 16px;
99162
}

0 commit comments

Comments
 (0)