diff --git a/src/components/AggregationPanel/AggregationPanel.js b/src/components/AggregationPanel/AggregationPanel.js index 5cd5a0440..3fbca8882 100644 --- a/src/components/AggregationPanel/AggregationPanel.js +++ b/src/components/AggregationPanel/AggregationPanel.js @@ -1,6 +1,7 @@ import LoaderDots from 'components/LoaderDots/LoaderDots.react'; -import React, { useEffect, useMemo } from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import styles from './AggregationPanel.scss'; +import Parse from 'parse'; import { AudioElement, ButtonElement, @@ -19,8 +20,14 @@ const AggregationPanel = ({ errorAggregatedData, showNote, setSelectedObjectId, - selectedObjectId + selectedObjectId, + depth = 0, + cloudCodeFunction = null, + panelTitle = null, }) => { + const [isExpanded, setIsExpanded] = useState(false); + const [nestedData, setNestedData] = useState(null); + const [isLoadingNested, setIsLoadingNested] = useState(false); useEffect(() => { if (Object.keys(errorAggregatedData).length !== 0) { @@ -30,54 +37,149 @@ const AggregationPanel = ({ }, [errorAggregatedData, setSelectedObjectId, setErrorAggregatedData]); const isLoading = useMemo(() => - selectedObjectId && isLoadingCloudFunction && showAggregatedData, - [selectedObjectId, isLoadingCloudFunction, showAggregatedData] + depth === 0 && selectedObjectId && isLoadingCloudFunction && showAggregatedData, + [depth, selectedObjectId, isLoadingCloudFunction, showAggregatedData] ); const shouldShowAggregatedData = useMemo(() => - selectedObjectId && showAggregatedData && Object.keys(data).length !== 0 && Object.keys(errorAggregatedData).length === 0, [selectedObjectId, showAggregatedData, data, errorAggregatedData] + depth === 0 + ? (selectedObjectId && showAggregatedData && Object.keys(data).length !== 0 && Object.keys(errorAggregatedData).length === 0) + : true, + [depth, selectedObjectId, showAggregatedData, data, errorAggregatedData] ); + const fetchNestedData = useCallback(async () => { + setIsLoadingNested(true); + try { + const params = { objectId: selectedObjectId }; + const result = await Parse.Cloud.run(cloudCodeFunction, params); + if (result?.panel?.segments) { + setNestedData(result); + } else { + const errorMsg = 'Improper JSON format'; + showNote(errorMsg, true); + } + } catch (error) { + const errorMsg = error.message; + showNote(errorMsg, true); + } finally { + setIsLoadingNested(false); + } + }, [cloudCodeFunction, selectedObjectId, showNote]); + + const handleToggle = useCallback(async () => { + if (!isExpanded && !nestedData && cloudCodeFunction) { + fetchNestedData(); + } + setIsExpanded(prev => !prev); + }, [isExpanded, nestedData, cloudCodeFunction, fetchNestedData]); + + const handleRefresh = useCallback(() => { + setNestedData(null); + setIsExpanded(false); + fetchNestedData(); + }, [fetchNestedData]); + + const renderSegmentContent = (segment, index) => ( +
+

{segment.title}

+
+ {segment.items.map((item, idx) => { + switch (item.type) { + case 'text': + return ; + case 'keyValue': + return ; + case 'table': + return ; + case 'image': + return ; + case 'video': + return ; + case 'audio': + return ; + case 'button': + return ; + case 'panel': + return ( +
+ +
+ ); + default: + return null; + } + })} +
+
+ ); + + if (depth > 0) { + return ( +
+
+ {panelTitle} +
+ {isExpanded && ( + + + )} + {isExpanded ? '▼' : '▲'} +
+
+ {isExpanded && ( +
+ {isLoadingNested ? ( +
+ +
+ ) : ( + nestedData && nestedData.panel.segments.map((segment, index) => + renderSegmentContent(segment, index) + ) + )} +
+ )} +
+ ); + } + return ( - <> +
{isLoading ? (
) : shouldShowAggregatedData ? ( - data.panel.segments.map((segment, index) => ( -
-

{segment.title}

-
- {segment.items.map((item, idx) => { - switch (item.type) { - case 'text': - return ; - case 'keyValue': - return ; - case 'table': - return ; - case 'image': - return ; - case 'video': - return ; - case 'audio': - return ; - case 'button': - return ; - default: - return null; - } - })} -
-
- )) +
+ {data.panel.segments.map((segment, index) => + renderSegmentContent(segment, index) + )} +
) : ( -
- No object selected. +
+ No object selected.
)} - +
); }; diff --git a/src/components/AggregationPanel/AggregationPanel.scss b/src/components/AggregationPanel/AggregationPanel.scss index 2bfda1414..58edd2a41 100644 --- a/src/components/AggregationPanel/AggregationPanel.scss +++ b/src/components/AggregationPanel/AggregationPanel.scss @@ -11,14 +11,11 @@ .segmentItems { font-size: 14px; - padding-left: 10px; - padding-right: 10px; - padding-top: 6px; + padding: 10px; display: flex; flex-direction: column; - border-left: 1px solid #e3e3ea; gap: 10px; -} + } .keyValue { font-size: 14px; @@ -76,13 +73,14 @@ text-align: center; border-radius: 5px; font-size: 14px; + &:hover, &:focus { background-color: $darkBlue; } } -.loading{ +.loading { height: 100%; display: flex; flex-direction: column; @@ -96,4 +94,69 @@ top: 50%; left: 50%; @include transform(translate(-50%, -50%)); +} + +.nestedPanel { + margin-right: -10px; + transition: all 0.3s ease; +} + +.nestedPanelHeader { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + border-left: 1px solid transparent; + background-color: $blue; + color: $white; + cursor: pointer; + padding-right: 4px; + + &.expanded { + border-left-color: #e3e3ea; + } + } + +.expandButton { + display: inline; + padding: 8px; + font-weight: 500; + + &.expanded { + font-weight: 600; + } +} + +.refreshButton { + background: none; + border: none; + padding: 4px; + cursor: pointer; + color: #666; + font-size: 16px; + + &:hover { + color: #333; + } + + &:disabled { + color: #ccc; + cursor: not-allowed; + } +} + +.nestedPanelContent { + padding: 0 0 8px 0; +} + +.loader { + display: flex; + align-items: center; + justify-content: center; +} + +.segmentContainer { + border-left: 1px solid #e3e3ea; + border-bottom: 1px solid #e3e3ea; + margin-bottom: 16px; } \ No newline at end of file