Skip to content

Commit 914cc71

Browse files
authored
feat: Add data panel to display object related data fetched via Cloud Function (#2584)
1 parent fcfc757 commit 914cc71

18 files changed

+872
-75
lines changed

package-lock.json

+38
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,11 @@
6565
"react-dnd": "10.0.2",
6666
"react-dnd-html5-backend": "10.0.2",
6767
"react-dom": "16.14.0",
68+
"react-draggable": "4.4.6",
6869
"react-helmet": "6.1.0",
6970
"react-json-view": "1.21.3",
7071
"react-popper-tooltip": "4.4.2",
72+
"react-resizable": "3.0.5",
7173
"react-router-dom": "6.4.1",
7274
"regenerator-runtime": "0.13.11"
7375
},
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import LoaderDots from 'components/LoaderDots/LoaderDots.react';
2+
import React, { useEffect, useMemo } from 'react';
3+
import styles from './AggregationPanel.scss';
4+
import {
5+
AudioElement,
6+
ButtonElement,
7+
ImageElement,
8+
KeyValueElement,
9+
TableElement,
10+
TextElement,
11+
VideoElement,
12+
} from './AggregationPanelComponents';
13+
14+
const AggregationPanel = ({
15+
data,
16+
isLoadingCloudFunction,
17+
showAggregatedData,
18+
setErrorAggregatedData,
19+
errorAggregatedData,
20+
showNote,
21+
setSelectedObjectId,
22+
selectedObjectId
23+
}) => {
24+
25+
useEffect(() => {
26+
if (Object.keys(errorAggregatedData).length !== 0) {
27+
setSelectedObjectId(null);
28+
setErrorAggregatedData({});
29+
}
30+
}, [errorAggregatedData, setSelectedObjectId, setErrorAggregatedData]);
31+
32+
const isLoading = useMemo(() =>
33+
selectedObjectId && isLoadingCloudFunction && showAggregatedData,
34+
[selectedObjectId, isLoadingCloudFunction, showAggregatedData]
35+
);
36+
37+
const shouldShowAggregatedData = useMemo(() =>
38+
selectedObjectId && showAggregatedData && Object.keys(data).length !== 0 && Object.keys(errorAggregatedData).length === 0, [selectedObjectId, showAggregatedData, data, errorAggregatedData]
39+
);
40+
41+
return (
42+
<>
43+
{isLoading ? (
44+
<div className={styles.center}>
45+
<LoaderDots />
46+
</div>
47+
) : 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+
))
75+
) : (
76+
<div className={styles.loading}>
77+
No object selected.
78+
</div>
79+
)}
80+
</>
81+
);
82+
};
83+
84+
export default AggregationPanel;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
@import 'stylesheets/globals.scss';
2+
3+
.heading {
4+
font-size: 14px;
5+
margin-top: 0;
6+
padding: 8px;
7+
padding-left: 10px;
8+
background-color: $blue;
9+
color: $white;
10+
}
11+
12+
.segmentItems {
13+
font-size: 14px;
14+
padding-left: 10px;
15+
padding-right: 10px;
16+
padding-top: 6px;
17+
display: flex;
18+
flex-direction: column;
19+
border-left: 1px solid #e3e3ea;
20+
gap: 10px;
21+
}
22+
23+
.keyValue {
24+
font-size: 14px;
25+
display: flex;
26+
gap: 10px;
27+
}
28+
29+
.video {
30+
width: 100%;
31+
height: 100%;
32+
}
33+
34+
.image {
35+
width: 100%;
36+
height: 100%;
37+
}
38+
39+
.audio {
40+
width: 100%;
41+
}
42+
43+
.segmentItems table,
44+
.segmentItems th,
45+
.segmentItems td {
46+
font-size: 14px;
47+
border: 1px solid #ddd;
48+
}
49+
50+
.segmentItems th,
51+
.segmentItems td {
52+
padding: 4px;
53+
text-align: left;
54+
}
55+
56+
.buttonContainer {
57+
display: flex;
58+
justify-content: center;
59+
align-items: center;
60+
}
61+
62+
.button {
63+
width: auto;
64+
max-width: 100%;
65+
overflow: hidden;
66+
text-overflow: ellipsis;
67+
cursor: pointer;
68+
margin-bottom: 15px;
69+
background-color: $blue;
70+
padding: 3px 13px;
71+
border: none;
72+
color: $white;
73+
line-height: 28px;
74+
outline: 0;
75+
text-decoration: none;
76+
text-align: center;
77+
border-radius: 5px;
78+
font-size: 14px;
79+
&:hover,
80+
&:focus {
81+
background-color: $darkBlue;
82+
}
83+
}
84+
85+
.loading{
86+
height: 100%;
87+
display: flex;
88+
flex-direction: column;
89+
justify-content: center;
90+
text-align: center;
91+
}
92+
93+
.center {
94+
position: absolute;
95+
text-align: center;
96+
top: 50%;
97+
left: 50%;
98+
@include transform(translate(-50%, -50%));
99+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import React from 'react';
2+
import styles from './AggregationPanel.scss';
3+
4+
// Text Element Component
5+
export const TextElement = ({ text }) => (
6+
<div className="text-element">
7+
<p>{text}</p>
8+
</div>
9+
);
10+
11+
// Key-Value Element Component
12+
export const KeyValueElement = ({ item }) => (
13+
<div className={styles.keyValue}>
14+
{item.key}:
15+
{item.url ? <a href={item.url} target="_blank">{item.value}</a> : <span>{item.value}</span>}
16+
</div>
17+
);
18+
19+
// Table Element Component
20+
export const TableElement = ({ columns, rows }) => (
21+
<div className="table-element">
22+
<table>
23+
<thead>
24+
<tr>
25+
{columns.map((column, idx) => (
26+
<th key={idx}>{column.name}</th>
27+
))}
28+
</tr>
29+
</thead>
30+
<tbody>
31+
{rows.map((row, idx) => (
32+
<tr key={idx}>
33+
{columns.map((column, colIdx) => (
34+
<td key={colIdx}>{row[column.name]}</td>
35+
))}
36+
</tr>
37+
))}
38+
</tbody>
39+
</table>
40+
</div>
41+
);
42+
43+
// Image Element Component
44+
export const ImageElement = ({ url }) => (
45+
<div className="image-element">
46+
<a href={url} target="_blank" rel="noopener noreferrer">
47+
<img src={url} alt="Image" className={styles.image} />
48+
</a>
49+
</div>
50+
);
51+
52+
// Video Element Component
53+
export const VideoElement = ({ url }) => (
54+
<div className="video-element">
55+
<video controls className={styles.video}>
56+
<source src={url} type="video/mp4" />
57+
Your browser does not support the video tag.
58+
</video>
59+
</div>
60+
);
61+
62+
// Audio Element Component
63+
export const AudioElement = ({ url }) => (
64+
<div className="audio-element">
65+
<audio controls className={styles.audio}>
66+
<source src={url} type="audio/mpeg" />
67+
Your browser does not support the audio element.
68+
</audio>
69+
</div>
70+
);
71+
72+
// Button Element Component
73+
export const ButtonElement = ({ item, showNote }) => {
74+
const handleClick = () => {
75+
fetch(item.action.url, {
76+
method: item.action.method,
77+
headers: item.action.headers,
78+
body: JSON.stringify(item.action.body),
79+
})
80+
.then(response => response.json())
81+
.then(data => {
82+
const formattedData = JSON.stringify(data, null, 2);
83+
showNote(`${formattedData}`,false)
84+
})
85+
.catch(error => {
86+
showNote(`${error}`,true)
87+
});
88+
};
89+
90+
return (
91+
<div className={styles.buttonContainer}>
92+
<button onClick={handleClick} className={styles.button}>
93+
{item.text}
94+
</button>
95+
</div>
96+
);
97+
};

0 commit comments

Comments
 (0)