Refactor simulation styles and update API services
- Refactored SCSS styles for simulation player and dashboard components to improve readability and maintainability. - Changed property names in Block and UIElement types for clarity. - Implemented getDashBoardBlocksApi and upsertDashBoardBlocksApi services for fetching and updating dashboard blocks. - Removed unnecessary transition property in simulation dashboard styles.
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { useParams } from "react-router-dom";
|
||||
import React, { useState, useRef, useEffect } from "react";
|
||||
import { dataModelManager } from "./data/dataModel";
|
||||
import ControlPanel from "./ControlPanel";
|
||||
@@ -5,16 +6,24 @@ import SwapModal from "./SwapModal";
|
||||
import DataModelPanel from "./components/models/DataModelPanel";
|
||||
|
||||
import { useSceneContext } from "../../modules/scene/sceneContext";
|
||||
import useModuleStore from "../../store/ui/useModuleStore";
|
||||
import { calculateMinBlockSize } from "./functions/block/calculateMinBlockSize";
|
||||
import { handleElementDragStart, handleElementResizeStart, handleBlockResizeStart, handleSwapStart, handleSwapTarget, handleBlockClick, handleElementClick } from "./functions/eventHandlers";
|
||||
import BlockGrid from "./components/block/BlockGrid";
|
||||
import ElementDropdown from "./components/element/ElementDropdown";
|
||||
import BlockEditor from "./components/block/BlockEditor";
|
||||
import ElementEditor from "./components/element/ElementEditor";
|
||||
import useCallBackOnKey from "../../utils/hooks/useCallBackOnKey";
|
||||
import { handleBlockDragStart } from "./functions/block/handleBlockDragStart";
|
||||
|
||||
import { getDashBoardBlocksApi } from "../../services/visulization/dashBoard/getDashBoardBlocks";
|
||||
import { upsetDashBoardBlocksApi } from "../../services/visulization/dashBoard/upsertDashBoardBlocks";
|
||||
|
||||
const DashboardEditor: React.FC = () => {
|
||||
const { simulationDashBoardStore } = useSceneContext();
|
||||
const { simulationDashBoardStore, versionStore } = useSceneContext();
|
||||
const { selectedVersion } = versionStore();
|
||||
const { projectId } = useParams();
|
||||
const { activeModule } = useModuleStore();
|
||||
const {
|
||||
blocks,
|
||||
setBlocks,
|
||||
@@ -33,7 +42,9 @@ const DashboardEditor: React.FC = () => {
|
||||
updateElementData,
|
||||
updateGraphData,
|
||||
updateGraphTitle,
|
||||
updateGraphType,
|
||||
swapElements,
|
||||
subscribe,
|
||||
} = simulationDashBoardStore();
|
||||
const [selectedBlock, setSelectedBlock] = useState<string | null>(null);
|
||||
const [selectedElement, setSelectedElement] = useState<string | null>(null);
|
||||
@@ -63,8 +74,40 @@ const DashboardEditor: React.FC = () => {
|
||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||
const blockRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const currentBlock = blocks.find((b) => b.id === selectedBlock);
|
||||
const currentElement = currentBlock?.elements.find((el) => el.id === selectedElement);
|
||||
const currentBlock = blocks.find((b) => b.blockUuid === selectedBlock);
|
||||
const currentElement = currentBlock?.elements.find((el) => el.elementUuid === selectedElement);
|
||||
|
||||
useEffect(() => {
|
||||
if (!projectId || !selectedVersion) return;
|
||||
// getDashBoardBlocksApi(projectId, selectedVersion.versionId).then((data) => {
|
||||
// if (data.data && data.data.blocks) {
|
||||
// console.log("data: ", data);
|
||||
// setBlocks(data.data.blocks);
|
||||
// }
|
||||
// });
|
||||
}, [projectId, selectedVersion]);
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribe = subscribe(() => {
|
||||
if (!projectId || !selectedVersion) return;
|
||||
|
||||
// upsetDashBoardBlocksApi({ projectId, versionId: selectedVersion.versionId, blocks }).then((data) => {
|
||||
// console.log("data: ", data);
|
||||
// });
|
||||
});
|
||||
|
||||
return () => {
|
||||
unsubscribe();
|
||||
};
|
||||
}, [blocks, projectId, selectedVersion]);
|
||||
|
||||
useCallBackOnKey(
|
||||
() => {
|
||||
console.log(blocks);
|
||||
},
|
||||
"Ctrl+S",
|
||||
{ dependencies: [blocks], noRepeat: true, allowOnInput: true }
|
||||
);
|
||||
|
||||
// Subscribe to data model changes
|
||||
useEffect(() => {
|
||||
@@ -140,86 +183,144 @@ const DashboardEditor: React.FC = () => {
|
||||
}, [selectedBlock, selectedElement]);
|
||||
|
||||
// Drag and drop handler
|
||||
useEffect(() => {
|
||||
const handleMouseMove = (e: MouseEvent): void => {
|
||||
// Element dragging - use elementDragOffset
|
||||
if (draggingElement && selectedBlock && currentElement?.positionType === "absolute") {
|
||||
const blockElement = document.querySelector(`[data-block-id="${selectedBlock}"]`) as HTMLElement;
|
||||
if (blockElement) {
|
||||
const blockRect = blockElement.getBoundingClientRect();
|
||||
const newX = e.clientX - blockRect.left - elementDragOffset.x; // Use elementDragOffset
|
||||
const newY = e.clientY - blockRect.top - elementDragOffset.y; // Use elementDragOffset
|
||||
useEffect(() => {
|
||||
const handleMouseMove = (e: MouseEvent): void => {
|
||||
// Element dragging - direct DOM manipulation
|
||||
if (draggingElement && selectedBlock && currentElement?.positionType === "absolute") {
|
||||
const blockElement = document.querySelector(`[data-block-id="${selectedBlock}"]`) as HTMLElement;
|
||||
const elementToDrag = document.querySelector(`[data-element-id="${draggingElement}"]`) as HTMLElement;
|
||||
|
||||
if (blockElement && elementToDrag) {
|
||||
const blockRect = blockElement.getBoundingClientRect();
|
||||
const newX = e.clientX - blockRect.left - elementDragOffset.x;
|
||||
const newY = e.clientY - blockRect.top - elementDragOffset.y;
|
||||
|
||||
updateElementPosition(selectedBlock, draggingElement, {
|
||||
x: Math.max(0, Math.min(blockRect.width - 50, newX)),
|
||||
y: Math.max(0, Math.min(blockRect.height - 30, newY)),
|
||||
});
|
||||
}
|
||||
// Direct DOM manipulation
|
||||
elementToDrag.style.left = `${Math.max(0, Math.min(blockRect.width - 50, newX))}px`;
|
||||
elementToDrag.style.top = `${Math.max(0, Math.min(blockRect.height - 30, newY))}px`;
|
||||
}
|
||||
}
|
||||
|
||||
// Block dragging - use blockDragOffset
|
||||
if (draggingBlock && currentBlock?.positionType && (currentBlock.positionType === "absolute" || currentBlock.positionType === "fixed")) {
|
||||
const editorElement = editorRef.current;
|
||||
if (editorElement) {
|
||||
const editorRect = editorElement.getBoundingClientRect();
|
||||
const newX = e.clientX - editorRect.left - blockDragOffset.x; // Use blockDragOffset
|
||||
const newY = e.clientY - editorRect.top - blockDragOffset.y; // Use blockDragOffset
|
||||
// Block dragging - direct DOM manipulation
|
||||
if (draggingBlock && currentBlock?.positionType && (currentBlock.positionType === "absolute" || currentBlock.positionType === "fixed")) {
|
||||
const editorElement = editorRef.current;
|
||||
const blockToDrag = document.querySelector(`[data-block-id="${draggingBlock}"]`) as HTMLElement;
|
||||
|
||||
if (editorElement && blockToDrag) {
|
||||
const editorRect = editorElement.getBoundingClientRect();
|
||||
const newX = e.clientX - editorRect.left - blockDragOffset.x;
|
||||
const newY = e.clientY - editorRect.top - blockDragOffset.y;
|
||||
|
||||
updateBlockPosition(draggingBlock, {
|
||||
x: Math.max(0, Math.min(editorRect.width - (currentBlock.size?.width || 400), newX)),
|
||||
y: Math.max(0, Math.min(editorRect.height - (currentBlock.size?.height || 300), newY)),
|
||||
});
|
||||
}
|
||||
// Direct DOM manipulation
|
||||
blockToDrag.style.left = `${Math.max(0, Math.min(editorRect.width - (currentBlock.size?.width || 400), newX))}px`;
|
||||
blockToDrag.style.top = `${Math.max(0, Math.min(editorRect.height - (currentBlock.size?.height || 300), newY))}px`;
|
||||
}
|
||||
}
|
||||
|
||||
if ((resizingElement || resizingBlock) && resizeStart) {
|
||||
if (resizingElement && selectedBlock) {
|
||||
// Resizing - direct DOM manipulation
|
||||
if ((resizingElement || resizingBlock) && resizeStart) {
|
||||
if (resizingElement && selectedBlock) {
|
||||
const elementToResize = document.querySelector(`[data-element-id="${resizingElement}"]`) as HTMLElement;
|
||||
if (elementToResize) {
|
||||
const deltaX = e.clientX - resizeStart.x;
|
||||
const deltaY = e.clientY - resizeStart.y;
|
||||
|
||||
const newWidth = Math.max(100, resizeStart.width + deltaX);
|
||||
const newHeight = Math.max(50, resizeStart.height + deltaY);
|
||||
|
||||
updateElementSize(selectedBlock, resizingElement, {
|
||||
width: newWidth,
|
||||
height: newHeight,
|
||||
});
|
||||
} else if (resizingBlock) {
|
||||
// Direct DOM manipulation
|
||||
elementToResize.style.width = `${newWidth}px`;
|
||||
elementToResize.style.height = `${newHeight}px`;
|
||||
}
|
||||
} else if (resizingBlock) {
|
||||
const blockToResize = document.querySelector(`[data-block-id="${resizingBlock}"]`) as HTMLElement;
|
||||
if (blockToResize) {
|
||||
const deltaX = e.clientX - resizeStart.x;
|
||||
const deltaY = e.clientY - resizeStart.y;
|
||||
|
||||
const currentBlock = blocks.find((b) => b.id === resizingBlock);
|
||||
const currentBlock = blocks.find((b) => b.blockUuid === resizingBlock);
|
||||
const minSize = currentBlock ? calculateMinBlockSize(currentBlock) : { width: 300, height: 200 };
|
||||
|
||||
const newWidth = Math.max(minSize.width, resizeStart.width + deltaX);
|
||||
const newHeight = Math.max(minSize.height, resizeStart.height + deltaY);
|
||||
|
||||
updateBlockSize(resizingBlock, {
|
||||
width: newWidth,
|
||||
height: newHeight,
|
||||
});
|
||||
// Direct DOM manipulation
|
||||
blockToResize.style.width = `${newWidth}px`;
|
||||
blockToResize.style.height = `${newHeight}px`;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const handleMouseUp = (): void => {
|
||||
setDraggingElement(null);
|
||||
setResizingElement(null);
|
||||
setDraggingBlock(null);
|
||||
setResizingBlock(null);
|
||||
setResizeStart(null);
|
||||
};
|
||||
|
||||
if (draggingElement || draggingBlock || resizingElement || resizingBlock) {
|
||||
document.addEventListener("mousemove", handleMouseMove);
|
||||
document.addEventListener("mouseup", handleMouseUp);
|
||||
const handleMouseUp = (): void => {
|
||||
// Update state only on mouse up for elements
|
||||
if (draggingElement && selectedBlock && currentElement?.positionType === "absolute") {
|
||||
const blockElement = document.querySelector(`[data-block-id="${selectedBlock}"]`) as HTMLElement;
|
||||
const elementToDrag = document.querySelector(`[data-element-id="${draggingElement}"]`) as HTMLElement;
|
||||
|
||||
if (blockElement && elementToDrag) {
|
||||
const computedStyle = window.getComputedStyle(elementToDrag);
|
||||
const x = parseFloat(computedStyle.left);
|
||||
const y = parseFloat(computedStyle.top);
|
||||
|
||||
updateElementPosition(selectedBlock, draggingElement, { x, y });
|
||||
}
|
||||
}
|
||||
|
||||
return () => {
|
||||
document.removeEventListener("mousemove", handleMouseMove);
|
||||
document.removeEventListener("mouseup", handleMouseUp);
|
||||
};
|
||||
}, [draggingElement, resizingElement, draggingBlock, resizingBlock, elementDragOffset, blockDragOffset, selectedBlock, currentElement, resizeStart, currentBlock, blocks]);
|
||||
// Update state only on mouse up for blocks
|
||||
if (draggingBlock && currentBlock?.positionType && (currentBlock.positionType === "absolute" || currentBlock.positionType === "fixed")) {
|
||||
const blockToDrag = document.querySelector(`[data-block-id="${draggingBlock}"]`) as HTMLElement;
|
||||
|
||||
if (blockToDrag) {
|
||||
const computedStyle = window.getComputedStyle(blockToDrag);
|
||||
const x = parseFloat(computedStyle.left);
|
||||
const y = parseFloat(computedStyle.top);
|
||||
|
||||
updateBlockPosition(draggingBlock, { x, y });
|
||||
}
|
||||
}
|
||||
|
||||
// Update state only on mouse up for resizing
|
||||
if (resizingElement && selectedBlock) {
|
||||
const elementToResize = document.querySelector(`[data-element-id="${resizingElement}"]`) as HTMLElement;
|
||||
if (elementToResize) {
|
||||
const computedStyle = window.getComputedStyle(elementToResize);
|
||||
const width = parseFloat(computedStyle.width);
|
||||
const height = parseFloat(computedStyle.height);
|
||||
|
||||
updateElementSize(selectedBlock, resizingElement, { width, height });
|
||||
}
|
||||
}
|
||||
|
||||
if (resizingBlock) {
|
||||
const blockToResize = document.querySelector(`[data-block-id="${resizingBlock}"]`) as HTMLElement;
|
||||
if (blockToResize) {
|
||||
const computedStyle = window.getComputedStyle(blockToResize);
|
||||
const width = parseFloat(computedStyle.width);
|
||||
const height = parseFloat(computedStyle.height);
|
||||
|
||||
updateBlockSize(resizingBlock, { width, height });
|
||||
}
|
||||
}
|
||||
|
||||
// Reset all dragging states
|
||||
setDraggingElement(null);
|
||||
setResizingElement(null);
|
||||
setDraggingBlock(null);
|
||||
setResizingBlock(null);
|
||||
setResizeStart(null);
|
||||
};
|
||||
|
||||
if (draggingElement || draggingBlock || resizingElement || resizingBlock) {
|
||||
document.addEventListener("mousemove", handleMouseMove);
|
||||
document.addEventListener("mouseup", handleMouseUp);
|
||||
}
|
||||
|
||||
return () => {
|
||||
document.removeEventListener("mousemove", handleMouseMove);
|
||||
document.removeEventListener("mouseup", handleMouseUp);
|
||||
};
|
||||
}, [draggingElement, resizingElement, draggingBlock, resizingBlock, elementDragOffset, blockDragOffset, selectedBlock, currentElement, resizeStart, currentBlock, blocks]);
|
||||
|
||||
// Update dropdown position when showElementDropdown changes
|
||||
useEffect(() => {
|
||||
@@ -234,7 +335,9 @@ const DashboardEditor: React.FC = () => {
|
||||
|
||||
return (
|
||||
<div ref={editorRef} className="dashboard-editor">
|
||||
<ControlPanel editMode={editMode} setEditMode={setEditMode} addBlock={() => addBlock()} showDataModelPanel={showDataModelPanel} setShowDataModelPanel={setShowDataModelPanel} />
|
||||
{activeModule === "visualization" && (
|
||||
<ControlPanel editMode={editMode} setEditMode={setEditMode} addBlock={() => addBlock()} showDataModelPanel={showDataModelPanel} setShowDataModelPanel={setShowDataModelPanel} />
|
||||
)}
|
||||
|
||||
<BlockGrid
|
||||
blocks={blocks}
|
||||
@@ -285,8 +388,6 @@ const DashboardEditor: React.FC = () => {
|
||||
currentElement={currentElement}
|
||||
selectedBlock={selectedBlock}
|
||||
selectedElement={selectedElement}
|
||||
blocks={blocks}
|
||||
setBlocks={setBlocks}
|
||||
updateElementStyle={(blockId, elementId, style) => updateElementStyle(blockId, elementId, style)}
|
||||
updateElementSize={(blockId, elementId, size) => updateElementSize(blockId, elementId, size)}
|
||||
updateElementPosition={(blockId, elementId, position) => updateElementPosition(blockId, elementId, position)}
|
||||
@@ -295,6 +396,7 @@ const DashboardEditor: React.FC = () => {
|
||||
updateElementData={(blockId, elementId, updates) => updateElementData(blockId, elementId, updates)}
|
||||
updateGraphData={(blockId, elementId, newData) => updateGraphData(blockId, elementId, newData)}
|
||||
updateGraphTitle={(blockId, elementId, title) => updateGraphTitle(blockId, elementId, title)}
|
||||
updateGraphType={(blockId, elementId, type) => updateGraphType(blockId, elementId, type)}
|
||||
setSwapSource={setSwapSource}
|
||||
setShowSwapUI={setShowSwapUI}
|
||||
dataModelManager={dataModelManager}
|
||||
|
||||
@@ -44,36 +44,27 @@ const BlockComponent: React.FC<BlockComponentProps> = ({
|
||||
handleBlockDragStart,
|
||||
}) => {
|
||||
const minSize = calculateMinBlockSize(block);
|
||||
const isSelected = selectedBlock === block.id;
|
||||
const isDraggable =
|
||||
editMode && (block.positionType === "absolute" || block.positionType === "fixed");
|
||||
const isSelected = selectedBlock === block.blockUuid;
|
||||
const isDraggable = editMode && (block.positionType === "absolute" || block.positionType === "fixed");
|
||||
|
||||
const handleMouseDown = (event: React.MouseEvent) => {
|
||||
if (isDraggable) {
|
||||
handleBlockDragStart(block.id, event);
|
||||
handleBlockDragStart(block.blockUuid, event);
|
||||
}
|
||||
handleBlockClick(block.id, event);
|
||||
handleBlockClick(block.blockUuid, event);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
key={block.id}
|
||||
data-block-id={block.id}
|
||||
key={block.blockUuid}
|
||||
data-block-id={block.blockUuid}
|
||||
ref={isSelected ? blockRef : null}
|
||||
className={`block ${isSelected ? "selected" : ""} ${editMode ? "edit-mode" : ""} ${
|
||||
isDraggable ? "draggable" : ""
|
||||
}`}
|
||||
className={`block ${isSelected ? "selected" : ""} ${editMode ? "edit-mode" : ""} ${isDraggable ? "draggable" : ""}`}
|
||||
style={{
|
||||
...block.style,
|
||||
position: block.positionType || "relative",
|
||||
left:
|
||||
block.positionType === "absolute" || block.positionType === "fixed"
|
||||
? `${block.position?.x || 0}px`
|
||||
: "auto",
|
||||
top:
|
||||
block.positionType === "absolute" || block.positionType === "fixed"
|
||||
? `${block.position?.y || 0}px`
|
||||
: "auto",
|
||||
left: block.positionType === "absolute" || block.positionType === "fixed" ? `${block.position?.x || 0}px` : "auto",
|
||||
top: block.positionType === "absolute" || block.positionType === "fixed" ? `${block.position?.y || 0}px` : "auto",
|
||||
width: block.size?.width || 400,
|
||||
height: block.size?.height || 300,
|
||||
minWidth: minSize.width,
|
||||
@@ -88,7 +79,7 @@ const BlockComponent: React.FC<BlockComponentProps> = ({
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setShowElementDropdown(showElementDropdown === block.id ? null : block.id);
|
||||
setShowElementDropdown(showElementDropdown === block.blockUuid ? null : block.blockUuid);
|
||||
}}
|
||||
className="add-element-button"
|
||||
>
|
||||
@@ -99,9 +90,9 @@ const BlockComponent: React.FC<BlockComponentProps> = ({
|
||||
{/* Elements */}
|
||||
{block.elements.map((el) => (
|
||||
<ElementComponent
|
||||
key={el.id}
|
||||
key={el.elementUuid}
|
||||
element={el}
|
||||
blockId={block.id}
|
||||
blockId={block.blockUuid}
|
||||
editMode={editMode}
|
||||
selectedElement={selectedElement}
|
||||
showSwapUI={showSwapUI}
|
||||
@@ -115,12 +106,7 @@ const BlockComponent: React.FC<BlockComponentProps> = ({
|
||||
))}
|
||||
|
||||
{/* Block resize handle */}
|
||||
{editMode && isSelected && (
|
||||
<div
|
||||
className="resize-handle"
|
||||
onMouseDown={(e) => handleBlockResizeStart(block.id, e)}
|
||||
/>
|
||||
)}
|
||||
{editMode && isSelected && <div className="resize-handle" onMouseDown={(e) => handleBlockResizeStart(block.blockUuid, e)} />}
|
||||
|
||||
{/* Drag handle indicator for absolute/fixed blocks */}
|
||||
{isDraggable && editMode && (
|
||||
|
||||
@@ -50,7 +50,7 @@ const BlockGrid: React.FC<BlockGridProps> = ({
|
||||
<div className="block-grid">
|
||||
{blocks.map((block) => (
|
||||
<BlockComponent
|
||||
key={block.id}
|
||||
key={block.blockUuid}
|
||||
block={block}
|
||||
editMode={editMode}
|
||||
selectedBlock={selectedBlock}
|
||||
@@ -68,7 +68,7 @@ const BlockGrid: React.FC<BlockGridProps> = ({
|
||||
handleBlockDragStart={handleBlockDragStart}
|
||||
setShowElementDropdown={setShowElementDropdown}
|
||||
showElementDropdown={showElementDropdown}
|
||||
blockRef={selectedBlock === block.id ? blockRef : noopRef}
|
||||
blockRef={selectedBlock === block.blockUuid ? blockRef : noopRef}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -30,8 +30,8 @@ const ElementComponent: React.FC<ElementComponentProps> = ({
|
||||
handleSwapStart,
|
||||
handleSwapTarget,
|
||||
}) => {
|
||||
const isSelected = selectedElement === element.id;
|
||||
const isSwapSource = swapSource === element.id;
|
||||
const isSelected = selectedElement === element.elementUuid;
|
||||
const isSwapSource = swapSource === element.elementUuid;
|
||||
|
||||
const elementClasses = [
|
||||
"element",
|
||||
@@ -47,14 +47,13 @@ const ElementComponent: React.FC<ElementComponentProps> = ({
|
||||
|
||||
return (
|
||||
<div
|
||||
key={element.id}
|
||||
data-element-id={element.id}
|
||||
key={element.elementUuid}
|
||||
data-element-id={element.elementUuid}
|
||||
className={elementClasses}
|
||||
style={{
|
||||
...element.style,
|
||||
position: element.positionType || "relative",
|
||||
left:
|
||||
element.positionType === "absolute" ? `${element.position?.x || 0}px` : "auto",
|
||||
left: element.positionType === "absolute" ? `${element.position?.x || 0}px` : "auto",
|
||||
top: element.positionType === "absolute" ? `${element.position?.y || 0}px` : "auto",
|
||||
width: element.size?.width || "auto",
|
||||
height: element.size?.height || "auto",
|
||||
@@ -63,30 +62,24 @@ const ElementComponent: React.FC<ElementComponentProps> = ({
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (showSwapUI && swapSource !== element.id) {
|
||||
handleSwapTarget(element.id, e);
|
||||
if (showSwapUI && swapSource !== element.elementUuid) {
|
||||
handleSwapTarget(element.elementUuid, e);
|
||||
} else {
|
||||
handleElementClick(blockId, element.id, e);
|
||||
handleElementClick(blockId, element.elementUuid, e);
|
||||
}
|
||||
}}
|
||||
onMouseDown={(e) => handleElementDragStart(element.id, e)}
|
||||
onMouseDown={(e) => handleElementDragStart(element.elementUuid, e)}
|
||||
>
|
||||
<ElementContent element={element} resolvedData={resolveElementValue(element)} />
|
||||
|
||||
{editMode && (
|
||||
<>
|
||||
{element.positionType === "relative" && (
|
||||
<button
|
||||
onClick={(e) => handleSwapStart(element.id, e)}
|
||||
className="swap-button"
|
||||
>
|
||||
<button onClick={(e) => handleSwapStart(element.elementUuid, e)} className="swap-button">
|
||||
Swap
|
||||
</button>
|
||||
)}
|
||||
<div
|
||||
className="resize-handle"
|
||||
onMouseDown={(e) => handleElementResizeStart(element.id, e)}
|
||||
/>
|
||||
<div className="resize-handle" onMouseDown={(e) => handleElementResizeStart(element.elementUuid, e)} />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { RefObject } from "react";
|
||||
import { ExtendedCSSProperties, UIElement, Block } from "../../../../types/exportedTypes";
|
||||
import { ExtendedCSSProperties, UIElement } from "../../../../types/exportedTypes";
|
||||
import { getCurrentElementStyleValue } from "../../functions/helpers/getCurrentElementStyleValue";
|
||||
import type { DataModelManager } from "../../data/dataModel";
|
||||
|
||||
@@ -8,8 +8,6 @@ interface ElementEditorProps {
|
||||
currentElement: UIElement;
|
||||
selectedBlock: string;
|
||||
selectedElement: string;
|
||||
blocks: Block[];
|
||||
setBlocks: (blocks: Block[]) => void;
|
||||
updateElementStyle: (blockId: string, elementId: string, style: ExtendedCSSProperties) => void;
|
||||
updateElementSize: (blockId: string, elementId: string, size: { width: number; height: number }) => void;
|
||||
updateElementPosition: (blockId: string, elementId: string, position: { x: number; y: number }) => void;
|
||||
@@ -18,6 +16,7 @@ interface ElementEditorProps {
|
||||
updateElementData: (blockId: string, elementId: string, updates: Partial<DataBinding>) => void;
|
||||
updateGraphData: (blockId: string, elementId: string, newData: GraphDataPoint[]) => void;
|
||||
updateGraphTitle: (blockId: string, elementId: string, title: string) => void;
|
||||
updateGraphType: (blockId: string, elementId: string, type: GraphTypes) => void;
|
||||
setSwapSource: (source: string | null) => void;
|
||||
setShowSwapUI: (show: boolean) => void;
|
||||
dataModelManager: DataModelManager;
|
||||
@@ -28,8 +27,6 @@ const ElementEditor: React.FC<ElementEditorProps> = ({
|
||||
currentElement,
|
||||
selectedBlock,
|
||||
selectedElement,
|
||||
blocks,
|
||||
setBlocks,
|
||||
updateElementStyle,
|
||||
updateElementSize,
|
||||
updateElementPosition,
|
||||
@@ -38,6 +35,7 @@ const ElementEditor: React.FC<ElementEditorProps> = ({
|
||||
updateElementData,
|
||||
updateGraphData,
|
||||
updateGraphTitle,
|
||||
updateGraphType,
|
||||
setSwapSource,
|
||||
setShowSwapUI,
|
||||
dataModelManager,
|
||||
@@ -352,26 +350,7 @@ const ElementEditor: React.FC<ElementEditorProps> = ({
|
||||
<label className="form-label">Chart Type: </label>
|
||||
<select
|
||||
value={currentElement.graphType || "line"}
|
||||
onChange={(e) => {
|
||||
const newGraphType = e.target.value as "line" | "bar" | "area" | "pie" | "radar";
|
||||
setBlocks(
|
||||
blocks.map((b) =>
|
||||
b.id === selectedBlock
|
||||
? {
|
||||
...b,
|
||||
elements: b.elements.map((el) =>
|
||||
el.id === selectedElement
|
||||
? {
|
||||
...el,
|
||||
graphType: newGraphType,
|
||||
}
|
||||
: el
|
||||
),
|
||||
}
|
||||
: b
|
||||
)
|
||||
);
|
||||
}}
|
||||
onChange={(e) => updateGraphType(selectedBlock, selectedElement, e.target.value as GraphTypes)}
|
||||
className="form-select"
|
||||
>
|
||||
<option value="line">Line Chart</option>
|
||||
|
||||
@@ -27,13 +27,13 @@ import RenameTooltip from "../../ui/features/RenameTooltip";
|
||||
import VersionSaved from "../sidebarRight/versionHisory/VersionSaved";
|
||||
import Footer from "../../footer/Footer";
|
||||
import ThreadChat from "../../ui/collaboration/threads/ThreadChat";
|
||||
import DashboardEditor from "../../SimulationDashboard/DashboardEditor";
|
||||
import Scene from "../../../modules/scene/scene";
|
||||
|
||||
import { recentlyViewedApi } from "../../../services/dashboard/recentlyViewedApi";
|
||||
import { setAssetsApi } from "../../../services/builder/asset/floorAsset/setAssetsApi";
|
||||
import { getVersionHistoryApi } from "../../../services/builder/versionControl/getVersionHistoryApi";
|
||||
import { getUserData } from "../../../functions/getUserData";
|
||||
import DashboardEditor from "../../SimulationDashboard/DashboardEditor";
|
||||
|
||||
function MainScene() {
|
||||
const { setMainState, clearComparisonState } = useSimulationState();
|
||||
@@ -205,7 +205,7 @@ function MainScene() {
|
||||
{activeModule !== "market" && !isPlaying && !isComparing && <Tools />}
|
||||
{isPlaying && activeModule === "simulation" && loadingProgress === 0 && <SimulationPlayer />}
|
||||
{isPlaying && activeModule !== "simulation" && <ControlsPlayer />}
|
||||
{activeModule === "visualization" && <DashboardEditor />}
|
||||
{(activeModule === "visualization" || (isPlaying && activeModule === "simulation")) && <DashboardEditor />}
|
||||
|
||||
{isRenameMode && selectedAssets.length === 1 && <RenameTooltip name={selectedAssets[0].userData.modelName} onSubmit={handleObjectRename} />}
|
||||
|
||||
|
||||
@@ -1,31 +1,12 @@
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
AsileIcon,
|
||||
CommentIcon,
|
||||
CursorIcon,
|
||||
DeleteIcon,
|
||||
FloorIcon,
|
||||
FreeMoveIcon,
|
||||
MeasureToolIcon,
|
||||
PenIcon,
|
||||
PlayIcon,
|
||||
SaveTemplateIcon,
|
||||
WallIcon,
|
||||
ZoneIcon,
|
||||
} from "../icons/ExportToolsIcons";
|
||||
import { AsileIcon, CommentIcon, CursorIcon, DeleteIcon, FloorIcon, FreeMoveIcon, MeasureToolIcon, PenIcon, PlayIcon, SaveTemplateIcon, WallIcon, ZoneIcon } from "../icons/ExportToolsIcons";
|
||||
import { ArrowIcon, TickIcon } from "../icons/ExportCommonIcons";
|
||||
import useModuleStore from "../../store/ui/useModuleStore";
|
||||
import { handleSaveTemplate } from "../../modules/visualization/functions/handleSaveTemplate";
|
||||
import { usePlayButtonStore } from "../../store/ui/usePlayButtonStore";
|
||||
import useTemplateStore from "../../store/ui/useTemplateStore";
|
||||
import { useSelectedZoneStore } from "../../store/visualization/old/useZoneStore";
|
||||
import {
|
||||
useActiveTool,
|
||||
useToggleView,
|
||||
useToolMode,
|
||||
useActiveSubTool,
|
||||
useShortcutStore,
|
||||
} from "../../store/builder/store";
|
||||
import { useActiveTool, useToggleView, useToolMode, useActiveSubTool, useShortcutStore } from "../../store/builder/store";
|
||||
import { useSocketStore } from "../../store/socket/useSocketStore";
|
||||
import { useToggleStore } from "../../store/ui/useUIToggleStore";
|
||||
import { use3DWidget, useFloatingWidget } from "../../store/visualization/old/useDroppedObjectsStore";
|
||||
@@ -66,7 +47,8 @@ const Tools: React.FC = () => {
|
||||
const { widgets3D } = use3DWidget();
|
||||
|
||||
const { visualizationSocket } = useSocketStore();
|
||||
const { versionStore } = useSceneContext();
|
||||
const { versionStore, simulationDashBoardStore } = useSceneContext();
|
||||
const { saveBlocks } = simulationDashBoardStore();
|
||||
const { selectedVersion } = versionStore();
|
||||
const { projectId } = useParams();
|
||||
|
||||
@@ -75,10 +57,7 @@ const Tools: React.FC = () => {
|
||||
|
||||
// 1. Set UI toggles on initial render
|
||||
useEffect(() => {
|
||||
setToggleUI(
|
||||
localStorage.getItem("navBarUiLeft") !== "false",
|
||||
localStorage.getItem("navBarUiRight") !== "false"
|
||||
);
|
||||
setToggleUI(localStorage.getItem("navBarUiLeft") !== "false", localStorage.getItem("navBarUiRight") !== "false");
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
@@ -172,67 +151,28 @@ const Tools: React.FC = () => {
|
||||
}
|
||||
setActiveTool("cursor");
|
||||
setActiveSubTool("cursor");
|
||||
setToggleUI(
|
||||
localStorage.getItem("navBarUiLeft") !== "false",
|
||||
localStorage.getItem("navBarUiRight") !== "false"
|
||||
);
|
||||
setToggleUI(localStorage.getItem("navBarUiLeft") !== "false", localStorage.getItem("navBarUiRight") !== "false");
|
||||
};
|
||||
|
||||
const renderBuilderTools = () => (
|
||||
<>
|
||||
{toggleView && (
|
||||
<div className="draw-tools">
|
||||
<ToolButton
|
||||
toolId="drawWall"
|
||||
icon={WallIcon}
|
||||
tooltip="draw wall (q)"
|
||||
active={activeTool === "draw-wall"}
|
||||
onClick={() => setActiveTool("draw-wall")}
|
||||
/>
|
||||
<ToolButton
|
||||
toolId="drawZone"
|
||||
icon={ZoneIcon}
|
||||
tooltip="draw zone (e)"
|
||||
active={activeTool === "draw-zone"}
|
||||
onClick={() => setActiveTool("draw-zone")}
|
||||
/>
|
||||
<ToolButton
|
||||
toolId="drawAisle"
|
||||
icon={AsileIcon}
|
||||
tooltip="draw aisle (r)"
|
||||
active={activeTool === "draw-aisle"}
|
||||
onClick={() => setActiveTool("draw-aisle")}
|
||||
/>
|
||||
<ToolButton
|
||||
toolId="drawFloor"
|
||||
icon={FloorIcon}
|
||||
tooltip="draw floor (t)"
|
||||
active={activeTool === "draw-floor"}
|
||||
onClick={() => setActiveTool("draw-floor")}
|
||||
/>
|
||||
<ToolButton toolId="drawWall" icon={WallIcon} tooltip="draw wall (q)" active={activeTool === "draw-wall"} onClick={() => setActiveTool("draw-wall")} />
|
||||
<ToolButton toolId="drawZone" icon={ZoneIcon} tooltip="draw zone (e)" active={activeTool === "draw-zone"} onClick={() => setActiveTool("draw-zone")} />
|
||||
<ToolButton toolId="drawAisle" icon={AsileIcon} tooltip="draw aisle (r)" active={activeTool === "draw-aisle"} onClick={() => setActiveTool("draw-aisle")} />
|
||||
<ToolButton toolId="drawFloor" icon={FloorIcon} tooltip="draw floor (t)" active={activeTool === "draw-floor"} onClick={() => setActiveTool("draw-floor")} />
|
||||
</div>
|
||||
)}
|
||||
<div className="draw-tools">
|
||||
<ToolButton
|
||||
toolId="measureScale"
|
||||
icon={MeasureToolIcon}
|
||||
tooltip="measure scale (m)"
|
||||
active={activeTool === "measure"}
|
||||
onClick={() => setActiveTool("measure")}
|
||||
/>
|
||||
<ToolButton toolId="measureScale" icon={MeasureToolIcon} tooltip="measure scale (m)" active={activeTool === "measure"} onClick={() => setActiveTool("measure")} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
const renderSimulationTools = () => (
|
||||
<div className="draw-tools">
|
||||
<ToolButton
|
||||
toolId="pen"
|
||||
icon={PenIcon}
|
||||
tooltip="pen"
|
||||
active={activeTool === "pen"}
|
||||
onClick={() => setActiveTool("pen")}
|
||||
/>
|
||||
<ToolButton toolId="pen" icon={PenIcon} tooltip="pen" active={activeTool === "pen"} onClick={() => setActiveTool("pen")} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -243,7 +183,8 @@ const Tools: React.FC = () => {
|
||||
icon={SaveTemplateIcon}
|
||||
tooltip="save template"
|
||||
active={false}
|
||||
onClick={() =>
|
||||
onClick={() => {
|
||||
saveBlocks();
|
||||
handleSaveTemplate({
|
||||
addTemplate,
|
||||
floatingWidget,
|
||||
@@ -253,18 +194,14 @@ const Tools: React.FC = () => {
|
||||
visualizationSocket,
|
||||
projectId,
|
||||
versionId: selectedVersion?.versionId || "",
|
||||
})
|
||||
}
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
const renderModeSwitcher = () => (
|
||||
<button
|
||||
id="toggle-threed-button"
|
||||
className={`toggle-threed-button${!toggleView ? " toggled" : ""}`}
|
||||
onClick={toggle2D3D}
|
||||
>
|
||||
<button id="toggle-threed-button" className={`toggle-threed-button${!toggleView ? " toggled" : ""}`} onClick={toggle2D3D}>
|
||||
<div className="tooltip">toggle view (tab)</div>
|
||||
<div className={`toggle-option${toggleView ? " active" : ""}`}>2d</div>
|
||||
<div className={`toggle-option${!toggleView ? " active" : ""}`}>3d</div>
|
||||
@@ -337,13 +274,7 @@ const Tools: React.FC = () => {
|
||||
)}
|
||||
{/* Dropdown Menu */}
|
||||
{activeModule !== "visualization" && (
|
||||
<button
|
||||
id="drop-down-button"
|
||||
title="drop-down"
|
||||
className="drop-down-option-button"
|
||||
ref={dropdownRef}
|
||||
onClick={() => setOpenDrop(!openDrop)}
|
||||
>
|
||||
<button id="drop-down-button" title="drop-down" className="drop-down-option-button" ref={dropdownRef} onClick={() => setOpenDrop(!openDrop)}>
|
||||
<ArrowIcon />
|
||||
{openDrop && (
|
||||
<div className="drop-down-container">
|
||||
@@ -358,9 +289,7 @@ const Tools: React.FC = () => {
|
||||
setOpenDrop(false);
|
||||
}}
|
||||
>
|
||||
<div className="active-option">
|
||||
{activeSubTool === option && <TickIcon />}
|
||||
</div>
|
||||
<div className="active-option">{activeSubTool === option && <TickIcon />}</div>
|
||||
{getIconComponent(option)}
|
||||
<div className="option">{option}</div>
|
||||
</button>
|
||||
@@ -376,14 +305,7 @@ const Tools: React.FC = () => {
|
||||
<div className="split"></div>
|
||||
<div className="transform-tools">
|
||||
{["move", "rotate"].map((tool) => (
|
||||
<ToolButton
|
||||
key={tool}
|
||||
toolId={tool}
|
||||
icon={getIconByTool(tool)}
|
||||
tooltip={`${tool}`}
|
||||
active={activeTool === tool}
|
||||
onClick={() => setActiveTool(tool)}
|
||||
/>
|
||||
<ToolButton key={tool} toolId={tool} icon={getIconByTool(tool)} tooltip={`${tool}`} active={activeTool === tool} onClick={() => setActiveTool(tool)} />
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
@@ -396,13 +318,7 @@ const Tools: React.FC = () => {
|
||||
|
||||
<div className="split"></div>
|
||||
<div className="general-options">
|
||||
<ToolButton
|
||||
toolId="comment"
|
||||
icon={CommentIcon}
|
||||
tooltip="comment"
|
||||
active={activeTool === "comment"}
|
||||
onClick={() => setActiveTool("comment")}
|
||||
/>
|
||||
<ToolButton toolId="comment" icon={CommentIcon} tooltip="comment" active={activeTool === "comment"} onClick={() => setActiveTool("comment")} />
|
||||
{!toggleView && (
|
||||
<ToolButton
|
||||
toolId="play"
|
||||
|
||||
@@ -142,11 +142,9 @@ const TreeNode = ({
|
||||
<button className="control-button" title={isVisible ? "Visible" : "Hidden"} onClick={(e) => handleOptionClick(e, "visibility")}>
|
||||
<EyeIcon isClosed={!isVisible} />
|
||||
</button>
|
||||
{isGroupNode && item.children && item.children.length > 0 && (
|
||||
<button className="control-button" title="Focus" onClick={(e) => handleOptionClick(e, "focus")}>
|
||||
<FocusIcon />
|
||||
</button>
|
||||
)}
|
||||
<button className="control-button" title="Focus" onClick={(e) => handleOptionClick(e, "focus")}>
|
||||
<FocusIcon />
|
||||
</button>
|
||||
<button className="control-button" title={isLocked ? "Locked" : "Unlocked"} onClick={(e) => handleOptionClick(e, "lock")}>
|
||||
<LockIcon isLocked={isLocked} />
|
||||
</button>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Box3, Object3D, Vector3, PerspectiveCamera } from "three";
|
||||
import { Box3, Object3D, Vector3, PerspectiveCamera, Sphere } from "three";
|
||||
import { useCallback } from "react";
|
||||
import { useSceneContext } from "../../scene/sceneContext";
|
||||
|
||||
@@ -15,9 +15,10 @@ export function useZoomMesh() {
|
||||
if (models.length === 0) return;
|
||||
|
||||
const box = new Box3();
|
||||
models.forEach((model) => {
|
||||
|
||||
for (const model of models) {
|
||||
box.expandByObject(model);
|
||||
});
|
||||
}
|
||||
|
||||
const cam = camera.current;
|
||||
if (!(cam instanceof PerspectiveCamera)) {
|
||||
@@ -25,50 +26,29 @@ export function useZoomMesh() {
|
||||
return;
|
||||
}
|
||||
|
||||
const camPos = new Vector3();
|
||||
cam.getWorldPosition(camPos);
|
||||
const camDir = new Vector3(0, 0, -1).transformDirection(cam.matrixWorld);
|
||||
const boxCenter = box.getCenter(new Vector3());
|
||||
const boxSize = box.getSize(new Vector3());
|
||||
const boundingSphere = new Sphere();
|
||||
box.getBoundingSphere(boundingSphere);
|
||||
|
||||
const radius = boundingSphere.radius;
|
||||
const fov = (cam.fov * Math.PI) / 180;
|
||||
const aspect = cam.aspect;
|
||||
const halfFovY = fov / 2;
|
||||
const halfFovX = Math.atan(Math.tan(halfFovY) * aspect);
|
||||
const camRight = new Vector3(1, 0, 0).transformDirection(cam.matrixWorld);
|
||||
const camUp = new Vector3(0, 1, 0).transformDirection(cam.matrixWorld);
|
||||
const distance = radius / Math.sin(fov / 2);
|
||||
const distanceWithMargin = distance * 0.75;
|
||||
const camDirection = new Vector3();
|
||||
cam.getWorldDirection(camDirection);
|
||||
|
||||
const corners = [];
|
||||
for (let i = 0; i < 8; i++) {
|
||||
const corner = new Vector3();
|
||||
corner.x = i & 1 ? box.max.x : box.min.x;
|
||||
corner.y = i & 2 ? box.max.y : box.min.y;
|
||||
corner.z = i & 4 ? box.max.z : box.min.z;
|
||||
corners.push(corner);
|
||||
const newCameraPosition = new Vector3().copy(boxCenter).sub(camDirection.multiplyScalar(distanceWithMargin));
|
||||
|
||||
const minDistance = Math.max(radius * 0.5, boxSize.length() * 0.1);
|
||||
const currentDistance = newCameraPosition.distanceTo(boxCenter);
|
||||
|
||||
if (currentDistance < minDistance) {
|
||||
const direction = new Vector3().subVectors(newCameraPosition, boxCenter).normalize();
|
||||
newCameraPosition.copy(boxCenter).add(direction.multiplyScalar(minDistance));
|
||||
}
|
||||
|
||||
let maxRight = 0;
|
||||
let maxUp = 0;
|
||||
for (const corner of corners) {
|
||||
const toCorner = corner.clone().sub(boxCenter);
|
||||
const projRight = Math.abs(toCorner.dot(camRight));
|
||||
const projUp = Math.abs(toCorner.dot(camUp));
|
||||
maxRight = Math.max(maxRight, projRight);
|
||||
maxUp = Math.max(maxUp, projUp);
|
||||
}
|
||||
|
||||
const distX = maxRight / Math.sin(halfFovX);
|
||||
const distY = maxUp / Math.sin(halfFovY);
|
||||
const requiredDist = Math.max(distX, distY);
|
||||
const toBox = boxCenter.clone().sub(camPos);
|
||||
const currentDistAlongView = toBox.dot(camDir);
|
||||
|
||||
if (currentDistAlongView <= 0) {
|
||||
const newPos = boxCenter.clone().add(camDir.clone().multiplyScalar(requiredDist));
|
||||
controls.current.setLookAt(newPos.x, newPos.y, newPos.z, boxCenter.x, boxCenter.y, boxCenter.z, true);
|
||||
return;
|
||||
}
|
||||
|
||||
const targetCamPos = boxCenter.clone().sub(camDir.clone().multiplyScalar(requiredDist));
|
||||
controls.current.setLookAt(targetCamPos.x, targetCamPos.y, targetCamPos.z, boxCenter.x, boxCenter.y, boxCenter.z, true);
|
||||
controls.current.setLookAt(newCameraPosition.x, newCameraPosition.y, newCameraPosition.z, boxCenter.x, boxCenter.y, boxCenter.z, true);
|
||||
},
|
||||
[scene, camera, controls]
|
||||
);
|
||||
|
||||
@@ -54,3 +54,13 @@ export function getAvatarColor(index: number, userId?: string): string {
|
||||
// Fallback: Assign a color using the index if no userId or local storage is unavailable
|
||||
return avatarColors[index % avatarColors.length];
|
||||
}
|
||||
|
||||
export function getAvatarColorUsingUserID(userId: string): string {
|
||||
// Simple deterministic color based on userId hash
|
||||
let hash = 0;
|
||||
for (let i = 0; i < userId.length; i++) {
|
||||
hash = userId.charCodeAt(i) + ((hash << 5) - hash);
|
||||
}
|
||||
hash = Math.abs(hash);
|
||||
return avatarColors[hash % avatarColors.length];
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
|
||||
|
||||
export const getDashBoardBlocksApi = async (projectId: string, versionId: string) => {
|
||||
try {
|
||||
const response = await fetch(`${url_Backend_dwinzo}/api/V1/simulation/blocks/${projectId}/${versionId}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: "Bearer <access_token>",
|
||||
"Content-Type": "application/json",
|
||||
token: localStorage.getItem("token") || "",
|
||||
refresh_token: localStorage.getItem("refreshToken") || "",
|
||||
},
|
||||
});
|
||||
const newAccessToken = response.headers.get("x-access-token");
|
||||
if (newAccessToken) {
|
||||
localStorage.setItem("token", newAccessToken);
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
echo.error("Failed to fetch dashBoardBlocks");
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
} catch {
|
||||
echo.error("Failed to fetch dashBoardBlocks");
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
import { Block } from "../../../types/exportedTypes";
|
||||
|
||||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
|
||||
|
||||
export const upsetDashBoardBlocksApi = async ({ projectId, versionId, blocks }: { projectId: string; versionId: string; blocks: Block[] }) => {
|
||||
try {
|
||||
const response = await fetch(`${url_Backend_dwinzo}/api/V1/simulation/blocks/upsert`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: "Bearer <access_token>",
|
||||
"Content-Type": "application/json",
|
||||
token: localStorage.getItem("token") || "",
|
||||
refresh_token: localStorage.getItem("refreshToken") || "",
|
||||
},
|
||||
body: JSON.stringify({ projectId, versionId, blocks }),
|
||||
});
|
||||
const newAccessToken = response.headers.get("x-access-token");
|
||||
if (newAccessToken) {
|
||||
localStorage.setItem("token", newAccessToken);
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
echo.error("Failed to upsert dashBoardBlocks");
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
} catch {
|
||||
echo.error("Failed to upsert dashBoardBlocks");
|
||||
}
|
||||
};
|
||||
@@ -9,6 +9,11 @@ interface SimulationDashboardStore {
|
||||
selectedBlockId: string | null;
|
||||
selectedElementId: string | null;
|
||||
|
||||
// Subscription management
|
||||
subscribe: (callback: () => void) => () => void;
|
||||
_notifySubscribers: () => void;
|
||||
saveBlocks: () => void;
|
||||
|
||||
// Block operations
|
||||
addBlock: () => void;
|
||||
removeBlock: (blockId: string) => void;
|
||||
@@ -39,6 +44,7 @@ interface SimulationDashboardStore {
|
||||
updateElementData: (blockId: string, elementId: string, updates: Partial<DataBinding>) => void;
|
||||
updateGraphData: (blockId: string, elementId: string, newData: GraphDataPoint[]) => void;
|
||||
updateGraphTitle: (blockId: string, elementId: string, title: string) => void;
|
||||
updateGraphType: (blockId: string, elementId: string, graphType: GraphTypes) => void;
|
||||
|
||||
// Selection and hover management
|
||||
setSelectedBlock: (blockId: string | null) => void;
|
||||
@@ -56,6 +62,8 @@ interface SimulationDashboardStore {
|
||||
hasElement: (blockId: string, elementId: string) => boolean;
|
||||
}
|
||||
|
||||
const subscribers = new Set<() => void>();
|
||||
|
||||
export const createSimulationDashboardStore = () => {
|
||||
return create<SimulationDashboardStore>()(
|
||||
immer((set, get) => ({
|
||||
@@ -63,11 +71,32 @@ export const createSimulationDashboardStore = () => {
|
||||
selectedBlockId: null,
|
||||
selectedElementId: null,
|
||||
|
||||
subscribe: (callback: () => void) => {
|
||||
subscribers.add(callback);
|
||||
return () => {
|
||||
subscribers.delete(callback);
|
||||
};
|
||||
},
|
||||
|
||||
_notifySubscribers: () => {
|
||||
subscribers.forEach((callback) => {
|
||||
try {
|
||||
callback();
|
||||
} catch (error) {
|
||||
console.error("Error in store subscriber:", error);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
saveBlocks: () => {
|
||||
get()._notifySubscribers();
|
||||
},
|
||||
|
||||
// Block operations
|
||||
addBlock: () => {
|
||||
set((state) => {
|
||||
const newBlock: Block = {
|
||||
id: MathUtils.generateUUID(),
|
||||
blockUuid: MathUtils.generateUUID(),
|
||||
style: {
|
||||
backgroundColor: "rgba(50, 50, 50, 0.8)",
|
||||
backdropFilter: "blur(10px)",
|
||||
@@ -90,7 +119,7 @@ export const createSimulationDashboardStore = () => {
|
||||
|
||||
removeBlock: (blockId) => {
|
||||
set((state) => {
|
||||
state.blocks = state.blocks.filter((block) => block.id !== blockId);
|
||||
state.blocks = state.blocks.filter((block) => block.blockUuid !== blockId);
|
||||
if (state.selectedBlockId === blockId) {
|
||||
state.selectedBlockId = null;
|
||||
}
|
||||
@@ -99,7 +128,7 @@ export const createSimulationDashboardStore = () => {
|
||||
|
||||
updateBlock: (blockId, updates) => {
|
||||
set((state) => {
|
||||
const block = state.blocks.find((b) => b.id === blockId);
|
||||
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
||||
if (block) {
|
||||
Object.assign(block, updates);
|
||||
}
|
||||
@@ -123,7 +152,7 @@ export const createSimulationDashboardStore = () => {
|
||||
// Block styling and positioning
|
||||
updateBlockStyle: (blockId, newStyle) => {
|
||||
set((state) => {
|
||||
const block = state.blocks.find((b) => b.id === blockId);
|
||||
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
||||
if (block) {
|
||||
block.style = { ...block.style, ...newStyle };
|
||||
}
|
||||
@@ -132,7 +161,7 @@ export const createSimulationDashboardStore = () => {
|
||||
|
||||
updateBlockSize: (blockId, size) => {
|
||||
set((state) => {
|
||||
const block = state.blocks.find((b) => b.id === blockId);
|
||||
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
||||
if (block) {
|
||||
block.size = size;
|
||||
}
|
||||
@@ -141,7 +170,7 @@ export const createSimulationDashboardStore = () => {
|
||||
|
||||
updateBlockPosition: (blockId, position) => {
|
||||
set((state) => {
|
||||
const block = state.blocks.find((b) => b.id === blockId);
|
||||
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
||||
if (block) {
|
||||
block.position = position;
|
||||
}
|
||||
@@ -150,7 +179,7 @@ export const createSimulationDashboardStore = () => {
|
||||
|
||||
updateBlockPositionType: (blockId, positionType) => {
|
||||
set((state) => {
|
||||
const block = state.blocks.find((b) => b.id === blockId);
|
||||
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
||||
if (block) {
|
||||
block.positionType = positionType;
|
||||
}
|
||||
@@ -159,7 +188,7 @@ export const createSimulationDashboardStore = () => {
|
||||
|
||||
updateBlockZIndex: (blockId, zIndex) => {
|
||||
set((state) => {
|
||||
const block = state.blocks.find((b) => b.id === blockId);
|
||||
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
||||
if (block) {
|
||||
block.zIndex = zIndex;
|
||||
}
|
||||
@@ -169,10 +198,10 @@ export const createSimulationDashboardStore = () => {
|
||||
// Element operations
|
||||
addElement: (blockId, type, graphType) => {
|
||||
set((state) => {
|
||||
const block = state.blocks.find((b) => b.id === blockId);
|
||||
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
||||
if (block) {
|
||||
const newElement: UIElement = {
|
||||
id: MathUtils.generateUUID(),
|
||||
elementUuid: MathUtils.generateUUID(),
|
||||
type: type,
|
||||
graphType,
|
||||
graphTitle: graphType ? `${graphType.charAt(0).toUpperCase() + graphType.slice(1)} Chart` : undefined,
|
||||
@@ -226,9 +255,9 @@ export const createSimulationDashboardStore = () => {
|
||||
|
||||
removeElement: (blockId, elementId) => {
|
||||
set((state) => {
|
||||
const block = state.blocks.find((b) => b.id === blockId);
|
||||
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
||||
if (block) {
|
||||
block.elements = block.elements.filter((el) => el.id !== elementId);
|
||||
block.elements = block.elements.filter((el) => el.elementUuid !== elementId);
|
||||
if (state.selectedElementId === elementId) {
|
||||
state.selectedElementId = null;
|
||||
}
|
||||
@@ -238,9 +267,9 @@ export const createSimulationDashboardStore = () => {
|
||||
|
||||
updateElement: (blockId, elementId, updates) => {
|
||||
set((state) => {
|
||||
const block = state.blocks.find((b) => b.id === blockId);
|
||||
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
||||
if (block) {
|
||||
const element = block.elements.find((el) => el.id === elementId);
|
||||
const element = block.elements.find((el) => el.elementUuid === elementId);
|
||||
if (element) {
|
||||
Object.assign(element, updates);
|
||||
}
|
||||
@@ -251,9 +280,9 @@ export const createSimulationDashboardStore = () => {
|
||||
// Element styling and positioning
|
||||
updateElementStyle: (blockId, elementId, newStyle) => {
|
||||
set((state) => {
|
||||
const block = state.blocks.find((b) => b.id === blockId);
|
||||
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
||||
if (block) {
|
||||
const element = block.elements.find((el) => el.id === elementId);
|
||||
const element = block.elements.find((el) => el.elementUuid === elementId);
|
||||
if (element) {
|
||||
element.style = { ...element.style, ...newStyle };
|
||||
}
|
||||
@@ -263,9 +292,9 @@ export const createSimulationDashboardStore = () => {
|
||||
|
||||
updateElementSize: (blockId, elementId, size) => {
|
||||
set((state) => {
|
||||
const block = state.blocks.find((b) => b.id === blockId);
|
||||
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
||||
if (block) {
|
||||
const element = block.elements.find((el) => el.id === elementId);
|
||||
const element = block.elements.find((el) => el.elementUuid === elementId);
|
||||
if (element) {
|
||||
element.size = size;
|
||||
}
|
||||
@@ -275,9 +304,9 @@ export const createSimulationDashboardStore = () => {
|
||||
|
||||
updateElementPosition: (blockId, elementId, position) => {
|
||||
set((state) => {
|
||||
const block = state.blocks.find((b) => b.id === blockId);
|
||||
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
||||
if (block) {
|
||||
const element = block.elements.find((el) => el.id === elementId);
|
||||
const element = block.elements.find((el) => el.elementUuid === elementId);
|
||||
if (element) {
|
||||
element.position = position;
|
||||
}
|
||||
@@ -287,9 +316,9 @@ export const createSimulationDashboardStore = () => {
|
||||
|
||||
updateElementPositionType: (blockId, elementId, positionType) => {
|
||||
set((state) => {
|
||||
const block = state.blocks.find((b) => b.id === blockId);
|
||||
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
||||
if (block) {
|
||||
const element = block.elements.find((el) => el.id === elementId);
|
||||
const element = block.elements.find((el) => el.elementUuid === elementId);
|
||||
if (element) {
|
||||
element.positionType = positionType;
|
||||
}
|
||||
@@ -299,9 +328,9 @@ export const createSimulationDashboardStore = () => {
|
||||
|
||||
updateElementZIndex: (blockId, elementId, zIndex) => {
|
||||
set((state) => {
|
||||
const block = state.blocks.find((b) => b.id === blockId);
|
||||
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
||||
if (block) {
|
||||
const element = block.elements.find((el) => el.id === elementId);
|
||||
const element = block.elements.find((el) => el.elementUuid === elementId);
|
||||
if (element) {
|
||||
element.zIndex = zIndex;
|
||||
}
|
||||
@@ -312,9 +341,9 @@ export const createSimulationDashboardStore = () => {
|
||||
// Element data operations
|
||||
updateElementData: (blockId, elementId, updates) => {
|
||||
set((state) => {
|
||||
const block = state.blocks.find((b) => b.id === blockId);
|
||||
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
||||
if (block) {
|
||||
const element = block.elements.find((el) => el.id === elementId);
|
||||
const element = block.elements.find((el) => el.elementUuid === elementId);
|
||||
if (element?.data) {
|
||||
element.data = { ...element.data, ...updates };
|
||||
}
|
||||
@@ -324,9 +353,9 @@ export const createSimulationDashboardStore = () => {
|
||||
|
||||
updateGraphData: (blockId, elementId, newData) => {
|
||||
set((state) => {
|
||||
const block = state.blocks.find((b) => b.id === blockId);
|
||||
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
||||
if (block) {
|
||||
const element = block.elements.find((el) => el.id === elementId);
|
||||
const element = block.elements.find((el) => el.elementUuid === elementId);
|
||||
if (element) {
|
||||
element.graphData = newData;
|
||||
}
|
||||
@@ -336,9 +365,9 @@ export const createSimulationDashboardStore = () => {
|
||||
|
||||
updateGraphTitle: (blockId, elementId, title) => {
|
||||
set((state) => {
|
||||
const block = state.blocks.find((b) => b.id === blockId);
|
||||
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
||||
if (block) {
|
||||
const element = block.elements.find((el) => el.id === elementId);
|
||||
const element = block.elements.find((el) => el.elementUuid === elementId);
|
||||
if (element) {
|
||||
element.graphTitle = title;
|
||||
}
|
||||
@@ -346,6 +375,21 @@ export const createSimulationDashboardStore = () => {
|
||||
});
|
||||
},
|
||||
|
||||
updateGraphType: (blockId, elementId, graphType) => {
|
||||
set((state) => {
|
||||
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
||||
if (block) {
|
||||
const element = block.elements.find((el) => el.elementUuid === elementId);
|
||||
if (element && element.type === "graph") {
|
||||
element.graphType = graphType;
|
||||
element.graphTitle = `${graphType.charAt(0).toUpperCase() + graphType.slice(1)} Chart`;
|
||||
|
||||
element.graphData = defaultGraphData;
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// Selection and hover management
|
||||
setSelectedBlock: (blockId) => {
|
||||
set((state) => {
|
||||
@@ -366,16 +410,16 @@ export const createSimulationDashboardStore = () => {
|
||||
// Element swapping
|
||||
swapElements: (blockId, elementId1, elementId2) => {
|
||||
set((state) => {
|
||||
const block = state.blocks.find((b) => b.id === blockId);
|
||||
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
||||
if (block) {
|
||||
block.elements = block.elements.map((el) => {
|
||||
if (el.id === elementId1) {
|
||||
const targetElement = block.elements.find((e) => e.id === elementId2);
|
||||
return targetElement ? { ...targetElement, id: elementId1 } : el;
|
||||
if (el.elementUuid === elementId1) {
|
||||
const targetElement = block.elements.find((e) => e.elementUuid === elementId2);
|
||||
return targetElement ? { ...targetElement, elementUuid: elementId1 } : el;
|
||||
}
|
||||
if (el.id === elementId2) {
|
||||
const sourceElement = block.elements.find((e) => e.id === elementId1);
|
||||
return sourceElement ? { ...sourceElement, id: elementId2 } : el;
|
||||
if (el.elementUuid === elementId2) {
|
||||
const sourceElement = block.elements.find((e) => e.elementUuid === elementId1);
|
||||
return sourceElement ? { ...sourceElement, elementUuid: elementId2 } : el;
|
||||
}
|
||||
return el;
|
||||
});
|
||||
@@ -385,37 +429,37 @@ export const createSimulationDashboardStore = () => {
|
||||
|
||||
// Helper functions
|
||||
getBlockById: (blockId) => {
|
||||
return get().blocks.find((b) => b.id === blockId);
|
||||
return get().blocks.find((b) => b.blockUuid === blockId);
|
||||
},
|
||||
|
||||
getElementById: (blockId, elementId) => {
|
||||
const block = get().blocks.find((b) => b.id === blockId);
|
||||
return block?.elements.find((el) => el.id === elementId);
|
||||
const block = get().blocks.find((b) => b.blockUuid === blockId);
|
||||
return block?.elements.find((el) => el.elementUuid === elementId);
|
||||
},
|
||||
|
||||
getSelectedBlock: () => {
|
||||
const { selectedBlockId, blocks } = get();
|
||||
return selectedBlockId ? blocks.find((b) => b.id === selectedBlockId) : undefined;
|
||||
return selectedBlockId ? blocks.find((b) => b.blockUuid === selectedBlockId) : undefined;
|
||||
},
|
||||
|
||||
getSelectedElement: () => {
|
||||
const { selectedElementId, selectedBlockId, blocks } = get();
|
||||
if (!selectedElementId || !selectedBlockId) return undefined;
|
||||
|
||||
const block = blocks.find((b) => b.id === selectedBlockId);
|
||||
return block?.elements.find((el) => el.id === selectedElementId);
|
||||
const block = blocks.find((b) => b.blockUuid === selectedBlockId);
|
||||
return block?.elements.find((el) => el.elementUuid === selectedElementId);
|
||||
},
|
||||
|
||||
hasBlock: (blockId) => {
|
||||
return get().blocks.some((b) => b.id === blockId);
|
||||
return get().blocks.some((b) => b.blockUuid === blockId);
|
||||
},
|
||||
|
||||
hasElement: (blockId, elementId) => {
|
||||
const block = get().blocks.find((b) => b.id === blockId);
|
||||
return block?.elements.some((el) => el.id === elementId) || false;
|
||||
const block = get().blocks.find((b) => b.blockUuid === blockId);
|
||||
return block?.elements.some((el) => el.elementUuid === elementId) || false;
|
||||
},
|
||||
}))
|
||||
);
|
||||
};
|
||||
|
||||
export type SimulationDashboardStoreType = ReturnType<typeof createSimulationDashboardStore>;
|
||||
export type SimulationDashboardStoreType = ReturnType<typeof createSimulationDashboardStore>;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -62,7 +62,7 @@
|
||||
cursor: pointer;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
||||
transition: all 0.3s ease;
|
||||
// transition: all 0.3s ease;
|
||||
z-index: 1;
|
||||
resize: none;
|
||||
overflow: hidden;
|
||||
|
||||
@@ -6,7 +6,7 @@ export interface ExtendedCSSProperties extends CSSProperties {
|
||||
}
|
||||
|
||||
export type Block = {
|
||||
id: string;
|
||||
blockUuid: string;
|
||||
style: CSSProperties;
|
||||
elements: UIElement[];
|
||||
zIndex?: number;
|
||||
@@ -16,7 +16,7 @@ export type Block = {
|
||||
};
|
||||
|
||||
export type UIElement = {
|
||||
id: string;
|
||||
elementUuid: string;
|
||||
type: UIType;
|
||||
graphType?: GraphTypes;
|
||||
graphTitle?: string;
|
||||
|
||||
Reference in New Issue
Block a user