feat: Implement block and element removal functionality in DashboardEditor
- Added deleteDashBoardBlocksApi service to handle block deletion from the dashboard. - Integrated block removal functionality in DashboardEditor, allowing users to delete blocks. - Updated BlockEditor and ElementEditor components to include remove block and element handlers. - Enhanced Zustand store to manage block and element states effectively. - Updated exported types to accommodate new data structures for blocks and elements.
This commit is contained in:
@@ -18,6 +18,7 @@ import { handleBlockDragStart } from "./functions/block/handleBlockDragStart";
|
|||||||
|
|
||||||
import { getDashBoardBlocksApi } from "../../services/visulization/dashBoard/getDashBoardBlocks";
|
import { getDashBoardBlocksApi } from "../../services/visulization/dashBoard/getDashBoardBlocks";
|
||||||
import { upsetDashBoardBlocksApi } from "../../services/visulization/dashBoard/upsertDashBoardBlocks";
|
import { upsetDashBoardBlocksApi } from "../../services/visulization/dashBoard/upsertDashBoardBlocks";
|
||||||
|
import { deleteDashBoardBlocksApi } from "../../services/visulization/dashBoard/deleteDashBoardBlocks";
|
||||||
|
|
||||||
const DashboardEditor: React.FC = () => {
|
const DashboardEditor: React.FC = () => {
|
||||||
const { simulationDashBoardStore, versionStore } = useSceneContext();
|
const { simulationDashBoardStore, versionStore } = useSceneContext();
|
||||||
@@ -43,6 +44,9 @@ const DashboardEditor: React.FC = () => {
|
|||||||
updateGraphTitle,
|
updateGraphTitle,
|
||||||
updateGraphType,
|
updateGraphType,
|
||||||
swapElements,
|
swapElements,
|
||||||
|
removeBlock,
|
||||||
|
removeElement,
|
||||||
|
getBlockById,
|
||||||
subscribe,
|
subscribe,
|
||||||
} = simulationDashBoardStore();
|
} = simulationDashBoardStore();
|
||||||
|
|
||||||
@@ -83,6 +87,7 @@ const DashboardEditor: React.FC = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!projectId || !selectedVersion) return;
|
if (!projectId || !selectedVersion) return;
|
||||||
getDashBoardBlocksApi(projectId, selectedVersion.versionId).then((data) => {
|
getDashBoardBlocksApi(projectId, selectedVersion.versionId).then((data) => {
|
||||||
|
console.log("data: ", data);
|
||||||
if (data.data?.blocks) {
|
if (data.data?.blocks) {
|
||||||
setBlocks(data.data.blocks);
|
setBlocks(data.data.blocks);
|
||||||
}
|
}
|
||||||
@@ -102,6 +107,7 @@ const DashboardEditor: React.FC = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unsubscribe = subscribe(() => {
|
const unsubscribe = subscribe(() => {
|
||||||
if (!projectId || !selectedVersion) return;
|
if (!projectId || !selectedVersion) return;
|
||||||
|
console.log("blocks: ", blocks);
|
||||||
updateBackend(blocks);
|
updateBackend(blocks);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -374,6 +380,19 @@ const DashboardEditor: React.FC = () => {
|
|||||||
updateBlockPosition={(blockId, position) => updateBlockPosition(blockId, position)}
|
updateBlockPosition={(blockId, position) => updateBlockPosition(blockId, position)}
|
||||||
updateBlockPositionType={(blockId, positionType) => updateBlockPositionType(blockId, positionType)}
|
updateBlockPositionType={(blockId, positionType) => updateBlockPositionType(blockId, positionType)}
|
||||||
updateBlockZIndex={(blockId, zIndex) => updateBlockZIndex(blockId, zIndex)}
|
updateBlockZIndex={(blockId, zIndex) => updateBlockZIndex(blockId, zIndex)}
|
||||||
|
handleRemoveBlock={(blockId) => {
|
||||||
|
const block = getBlockById(blockId);
|
||||||
|
if (!block) return;
|
||||||
|
deleteDashBoardBlocksApi({ projectId: projectId!, versionId: selectedVersion!.versionId, blocks: [block] }).then((data) => {
|
||||||
|
if (data.blocks.length>0) {
|
||||||
|
data.blocks.forEach((updatedBlock: any) => {
|
||||||
|
if (updatedBlock.message === "Block deleted successfully") {
|
||||||
|
removeBlock(updatedBlock.blockUuid);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{selectedElement && editMode && selectedBlock && currentElement && (
|
{selectedElement && editMode && selectedBlock && currentElement && (
|
||||||
@@ -391,6 +410,7 @@ const DashboardEditor: React.FC = () => {
|
|||||||
updateGraphData={(blockId, elementId, newData) => updateGraphData(blockId, elementId, newData)}
|
updateGraphData={(blockId, elementId, newData) => updateGraphData(blockId, elementId, newData)}
|
||||||
updateGraphTitle={(blockId, elementId, title) => updateGraphTitle(blockId, elementId, title)}
|
updateGraphTitle={(blockId, elementId, title) => updateGraphTitle(blockId, elementId, title)}
|
||||||
updateGraphType={(blockId, elementId, type) => updateGraphType(blockId, elementId, type)}
|
updateGraphType={(blockId, elementId, type) => updateGraphType(blockId, elementId, type)}
|
||||||
|
handleRemoveElement={(blockId, elementId) => removeElement(blockId, elementId)}
|
||||||
setSwapSource={setSwapSource}
|
setSwapSource={setSwapSource}
|
||||||
setShowSwapUI={setShowSwapUI}
|
setShowSwapUI={setShowSwapUI}
|
||||||
dataModelManager={dataModelManager}
|
dataModelManager={dataModelManager}
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ const BlockComponent: React.FC<BlockComponentProps> = ({
|
|||||||
handleBlockDragStart,
|
handleBlockDragStart,
|
||||||
}) => {
|
}) => {
|
||||||
const { simulationDashBoardStore } = useSceneContext();
|
const { simulationDashBoardStore } = useSceneContext();
|
||||||
const { addElement } = simulationDashBoardStore()
|
const { addElement } = simulationDashBoardStore();
|
||||||
|
|
||||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
@@ -122,10 +122,11 @@ const BlockComponent: React.FC<BlockComponentProps> = ({
|
|||||||
))}
|
))}
|
||||||
|
|
||||||
{/* Block resize handle */}
|
{/* Block resize handle */}
|
||||||
{editMode && isSelected &&
|
{editMode && isSelected && (
|
||||||
<div className="resize-handle" onMouseDown={(e) => handleBlockResizeStart(block.blockUuid, e)}>
|
<div className="resize-handle" onMouseDown={(e) => handleBlockResizeStart(block.blockUuid, e)}>
|
||||||
<ResizeIcon />
|
<ResizeIcon />
|
||||||
</div>}
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Drag handle indicator for absolute/fixed blocks */}
|
{/* Drag handle indicator for absolute/fixed blocks */}
|
||||||
{isDraggable && editMode && (
|
{isDraggable && editMode && (
|
||||||
@@ -152,9 +153,6 @@ const BlockComponent: React.FC<BlockComponentProps> = ({
|
|||||||
⤣
|
⤣
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ interface BlockEditorProps {
|
|||||||
updateBlockPosition: (blockId: string, position: { x: number; y: number }) => void;
|
updateBlockPosition: (blockId: string, position: { x: number; y: number }) => void;
|
||||||
updateBlockPositionType: (blockId: string, positionType: "relative" | "absolute" | "fixed") => void;
|
updateBlockPositionType: (blockId: string, positionType: "relative" | "absolute" | "fixed") => void;
|
||||||
updateBlockZIndex: (blockId: string, zIndex: number) => void;
|
updateBlockZIndex: (blockId: string, zIndex: number) => void;
|
||||||
|
handleRemoveBlock: (blockId: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const BlockEditor: React.FC<BlockEditorProps> = ({
|
const BlockEditor: React.FC<BlockEditorProps> = ({
|
||||||
@@ -29,12 +30,18 @@ const BlockEditor: React.FC<BlockEditorProps> = ({
|
|||||||
updateBlockPosition,
|
updateBlockPosition,
|
||||||
updateBlockPositionType,
|
updateBlockPositionType,
|
||||||
updateBlockZIndex,
|
updateBlockZIndex,
|
||||||
|
handleRemoveBlock,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div ref={blockEditorRef} className="panel block-editor-panel">
|
<div ref={blockEditorRef} className="panel block-editor-panel">
|
||||||
<div className="header">
|
<div className="header">
|
||||||
<h4>Block Style</h4>
|
<h4>Block Style</h4>
|
||||||
<div className="delete icon">
|
<div
|
||||||
|
className="delete icon"
|
||||||
|
onClick={() => {
|
||||||
|
handleRemoveBlock(selectedBlock);
|
||||||
|
}}
|
||||||
|
>
|
||||||
<DeleteIcon />
|
<DeleteIcon />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ interface ElementEditorProps {
|
|||||||
updateGraphData: (blockId: string, elementId: string, newData: GraphDataPoint[]) => void;
|
updateGraphData: (blockId: string, elementId: string, newData: GraphDataPoint[]) => void;
|
||||||
updateGraphTitle: (blockId: string, elementId: string, title: string) => void;
|
updateGraphTitle: (blockId: string, elementId: string, title: string) => void;
|
||||||
updateGraphType: (blockId: string, elementId: string, type: GraphTypes) => void;
|
updateGraphType: (blockId: string, elementId: string, type: GraphTypes) => void;
|
||||||
|
handleRemoveElement: (blockId: string, elementId: string) => void;
|
||||||
setSwapSource: (source: string | null) => void;
|
setSwapSource: (source: string | null) => void;
|
||||||
setShowSwapUI: (show: boolean) => void;
|
setShowSwapUI: (show: boolean) => void;
|
||||||
dataModelManager: DataModelManager;
|
dataModelManager: DataModelManager;
|
||||||
@@ -37,6 +38,7 @@ const ElementEditor: React.FC<ElementEditorProps> = ({
|
|||||||
updateGraphData,
|
updateGraphData,
|
||||||
updateGraphTitle,
|
updateGraphTitle,
|
||||||
updateGraphType,
|
updateGraphType,
|
||||||
|
handleRemoveElement,
|
||||||
setSwapSource,
|
setSwapSource,
|
||||||
setShowSwapUI,
|
setShowSwapUI,
|
||||||
dataModelManager,
|
dataModelManager,
|
||||||
@@ -52,10 +54,16 @@ const ElementEditor: React.FC<ElementEditorProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={elementEditorRef} className="panel element-editor-panel">
|
<div ref={elementEditorRef} className="panel element-editor-panel">
|
||||||
|
|
||||||
<div className="header">
|
<div className="header">
|
||||||
<h4>Element Style</h4>
|
<h4>Element Style</h4>
|
||||||
<div className="delete icon"><DeleteIcon /></div>
|
<div
|
||||||
|
className="delete icon"
|
||||||
|
onClick={() => {
|
||||||
|
handleRemoveElement(selectedBlock, selectedElement);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DeleteIcon />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{currentElement.type === "label-value" && (
|
{currentElement.type === "label-value" && (
|
||||||
<>
|
<>
|
||||||
@@ -352,11 +360,7 @@ const ElementEditor: React.FC<ElementEditorProps> = ({
|
|||||||
<>
|
<>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label className="form-label">Chart Type: </label>
|
<label className="form-label">Chart Type: </label>
|
||||||
<select
|
<select value={currentElement.graphType || "line"} onChange={(e) => updateGraphType(selectedBlock, selectedElement, e.target.value as GraphTypes)} className="form-select">
|
||||||
value={currentElement.graphType || "line"}
|
|
||||||
onChange={(e) => updateGraphType(selectedBlock, selectedElement, e.target.value as GraphTypes)}
|
|
||||||
className="form-select"
|
|
||||||
>
|
|
||||||
<option value="line">Line Chart</option>
|
<option value="line">Line Chart</option>
|
||||||
<option value="bar">Bar Chart</option>
|
<option value="bar">Bar Chart</option>
|
||||||
<option value="area">Area Chart</option>
|
<option value="area">Area Chart</option>
|
||||||
|
|||||||
@@ -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 deleteDashBoardBlocksApi = async ({ projectId, versionId, blocks }: { projectId: string; versionId: string; blocks: Block[] }) => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${url_Backend_dwinzo}/api/V1/simulation/blocks`, {
|
||||||
|
method: "PATCH",
|
||||||
|
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 delete dashBoardBlocks");
|
||||||
|
}
|
||||||
|
|
||||||
|
return await response.json();
|
||||||
|
} catch {
|
||||||
|
echo.error("Failed to delete dashBoardBlocks");
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -2,11 +2,7 @@ import { MathUtils } from "three";
|
|||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { immer } from "zustand/middleware/immer";
|
import { immer } from "zustand/middleware/immer";
|
||||||
import { defaultGraphData } from "../../components/SimulationDashboard/data/defaultGraphData";
|
import { defaultGraphData } from "../../components/SimulationDashboard/data/defaultGraphData";
|
||||||
import {
|
import { Block, UIElement, ExtendedCSSProperties } from "../../types/exportedTypes";
|
||||||
Block,
|
|
||||||
UIElement,
|
|
||||||
ExtendedCSSProperties,
|
|
||||||
} from "../../types/exportedTypes";
|
|
||||||
|
|
||||||
interface SimulationDashboardStore {
|
interface SimulationDashboardStore {
|
||||||
blocks: Block[];
|
blocks: Block[];
|
||||||
@@ -29,72 +25,33 @@ interface SimulationDashboardStore {
|
|||||||
updateBlockStyle: (blockId: string, newStyle: React.CSSProperties) => void;
|
updateBlockStyle: (blockId: string, newStyle: React.CSSProperties) => void;
|
||||||
updateBlockSize: (blockId: string, size: Size) => void;
|
updateBlockSize: (blockId: string, size: Size) => void;
|
||||||
updateBlockPosition: (blockId: string, position: Position) => void;
|
updateBlockPosition: (blockId: string, position: Position) => void;
|
||||||
updateBlockPositionType: (
|
updateBlockPositionType: (blockId: string, positionType: "relative" | "absolute" | "fixed") => void;
|
||||||
blockId: string,
|
|
||||||
positionType: "relative" | "absolute" | "fixed"
|
|
||||||
) => void;
|
|
||||||
updateBlockZIndex: (blockId: string, zIndex: number) => void;
|
updateBlockZIndex: (blockId: string, zIndex: number) => void;
|
||||||
|
|
||||||
// Element operations
|
// Element operations
|
||||||
addElement: (blockId: string, type: UIType, graphType?: GraphTypes) => void;
|
addElement: (blockId: string, type: UIType, graphType?: GraphTypes) => void;
|
||||||
removeElement: (blockId: string, elementId: string) => void;
|
removeElement: (blockId: string, elementId: string) => void;
|
||||||
updateElement: (
|
updateElement: (blockId: string, elementId: string, updates: Partial<UIElement>) => void;
|
||||||
blockId: string,
|
|
||||||
elementId: string,
|
|
||||||
updates: Partial<UIElement>
|
|
||||||
) => void;
|
|
||||||
|
|
||||||
// Element styling and positioning
|
// Element styling and positioning
|
||||||
updateElementStyle: (
|
updateElementStyle: (blockId: string, elementId: string, newStyle: ExtendedCSSProperties) => void;
|
||||||
blockId: string,
|
|
||||||
elementId: string,
|
|
||||||
newStyle: ExtendedCSSProperties
|
|
||||||
) => void;
|
|
||||||
updateElementSize: (blockId: string, elementId: string, size: Size) => void;
|
updateElementSize: (blockId: string, elementId: string, size: Size) => void;
|
||||||
updateElementPosition: (
|
updateElementPosition: (blockId: string, elementId: string, position: Position) => void;
|
||||||
blockId: string,
|
updateElementPositionType: (blockId: string, elementId: string, positionType: "relative" | "absolute" | "fixed") => void;
|
||||||
elementId: string,
|
updateElementZIndex: (blockId: string, elementId: string, zIndex: number) => void;
|
||||||
position: Position
|
|
||||||
) => void;
|
|
||||||
updateElementPositionType: (
|
|
||||||
blockId: string,
|
|
||||||
elementId: string,
|
|
||||||
positionType: "relative" | "absolute" | "fixed"
|
|
||||||
) => void;
|
|
||||||
updateElementZIndex: (
|
|
||||||
blockId: string,
|
|
||||||
elementId: string,
|
|
||||||
zIndex: number
|
|
||||||
) => void;
|
|
||||||
|
|
||||||
// Element data operations
|
// Element data operations
|
||||||
updateElementData: (
|
updateElementData: (blockId: string, elementId: string, updates: Partial<DataBinding>) => void;
|
||||||
blockId: string,
|
updateGraphData: (blockId: string, elementId: string, newData: GraphDataPoint[]) => void;
|
||||||
elementId: string,
|
|
||||||
updates: Partial<DataBinding>
|
|
||||||
) => void;
|
|
||||||
updateGraphData: (
|
|
||||||
blockId: string,
|
|
||||||
elementId: string,
|
|
||||||
newData: GraphDataPoint[]
|
|
||||||
) => void;
|
|
||||||
updateGraphTitle: (blockId: string, elementId: string, title: string) => void;
|
updateGraphTitle: (blockId: string, elementId: string, title: string) => void;
|
||||||
updateGraphType: (
|
updateGraphType: (blockId: string, elementId: string, graphType: GraphTypes) => void;
|
||||||
blockId: string,
|
|
||||||
elementId: string,
|
|
||||||
graphType: GraphTypes
|
|
||||||
) => void;
|
|
||||||
|
|
||||||
// Selection and hover management
|
// Selection and hover management
|
||||||
setSelectedBlock: (blockId: string | null) => void;
|
setSelectedBlock: (blockId: string | null) => void;
|
||||||
setSelectedElement: (elementId: string | null) => void;
|
setSelectedElement: (elementId: string | null) => void;
|
||||||
|
|
||||||
// Element swapping
|
// Element swapping
|
||||||
swapElements: (
|
swapElements: (blockId: string, elementId1: string, elementId2: string) => void;
|
||||||
blockId: string,
|
|
||||||
elementId1: string,
|
|
||||||
elementId2: string
|
|
||||||
) => void;
|
|
||||||
|
|
||||||
// Helper functions
|
// Helper functions
|
||||||
getBlockById: (blockId: string) => Block | undefined;
|
getBlockById: (blockId: string) => Block | undefined;
|
||||||
@@ -162,9 +119,7 @@ export const createSimulationDashboardStore = () => {
|
|||||||
|
|
||||||
removeBlock: (blockId) => {
|
removeBlock: (blockId) => {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
state.blocks = state.blocks.filter(
|
state.blocks = state.blocks.filter((block) => block.blockUuid !== blockId);
|
||||||
(block) => block.blockUuid !== blockId
|
|
||||||
);
|
|
||||||
if (state.selectedBlockId === blockId) {
|
if (state.selectedBlockId === blockId) {
|
||||||
state.selectedBlockId = null;
|
state.selectedBlockId = null;
|
||||||
}
|
}
|
||||||
@@ -241,34 +196,35 @@ export const createSimulationDashboardStore = () => {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// Element operations
|
// Element operations
|
||||||
addElement: (blockId, type, graphType) => {
|
addElement: (blockId, type, graphType, dataType?: DataType) => {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
||||||
if (block) {
|
if (!block) return;
|
||||||
const newElement: UIElement = {
|
|
||||||
|
let newElement: UIElement;
|
||||||
|
|
||||||
|
const commonProps = {
|
||||||
elementUuid: MathUtils.generateUUID(),
|
elementUuid: MathUtils.generateUUID(),
|
||||||
type: type,
|
positionType: "relative" as const,
|
||||||
graphType,
|
position: { x: 0, y: 0 },
|
||||||
graphTitle: graphType
|
zIndex: 1,
|
||||||
? `${
|
data: {
|
||||||
graphType.charAt(0).toUpperCase() + graphType.slice(1)
|
key: MathUtils.generateUUID(),
|
||||||
} Chart`
|
dataSource: "static" as const,
|
||||||
: undefined,
|
staticValue: "",
|
||||||
style:
|
label: undefined,
|
||||||
type === "graph"
|
},
|
||||||
? {
|
};
|
||||||
width: "100%",
|
|
||||||
height: "100%",
|
switch (type) {
|
||||||
minHeight: "120px",
|
case "label-value":
|
||||||
color: "#ffffff",
|
newElement = {
|
||||||
fontSize: 14,
|
...commonProps,
|
||||||
textAlign: "left" as const,
|
type: "label-value",
|
||||||
backgroundColor: "rgba(0, 0, 0, 0.2)",
|
title: "Label Value",
|
||||||
borderRadius: "4px",
|
dataSource: "machine-1",
|
||||||
padding: "8px",
|
dataValue: "metric-1",
|
||||||
}
|
style: {
|
||||||
: type === "label-value"
|
|
||||||
? {
|
|
||||||
color: "#ffffff",
|
color: "#ffffff",
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
textAlign: "left" as const,
|
textAlign: "left" as const,
|
||||||
@@ -279,34 +235,92 @@ export const createSimulationDashboardStore = () => {
|
|||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
labelColor: "#ffffff",
|
labelColor: "#ffffff",
|
||||||
valueColor: "#ffffff",
|
valueColor: "#ffffff",
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
...commonProps.data,
|
||||||
|
staticValue: "Value",
|
||||||
|
label: "Label",
|
||||||
|
},
|
||||||
|
size: { width: 200, height: 60 },
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "graph":
|
||||||
|
const baseGraphProps = {
|
||||||
|
...commonProps,
|
||||||
|
type: "graph" as const,
|
||||||
|
graphType: graphType,
|
||||||
|
graphTitle: "Graph Title",
|
||||||
|
style: {
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
minHeight: "120px",
|
||||||
|
color: "#ffffff",
|
||||||
|
fontSize: 14,
|
||||||
|
textAlign: "left" as const,
|
||||||
|
backgroundColor: "rgba(0, 0, 0, 0.2)",
|
||||||
|
borderRadius: "4px",
|
||||||
|
padding: "8px",
|
||||||
|
},
|
||||||
|
graphData: defaultGraphData,
|
||||||
|
size: { width: 400, height: 200 },
|
||||||
|
};
|
||||||
|
|
||||||
|
if (dataType === "multiple-machine") {
|
||||||
|
newElement = {
|
||||||
|
...baseGraphProps,
|
||||||
|
dataType: "multiple-machine" as const,
|
||||||
|
title: "Multi Machine Chart",
|
||||||
|
dataSource: ["machine-1", "machine-2", "machine-3"],
|
||||||
|
commonValue: "metric-1",
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// Default to single-machine
|
||||||
|
newElement = {
|
||||||
|
...baseGraphProps,
|
||||||
|
dataType: "single-machine" as const,
|
||||||
|
title: "Single Machine Chart",
|
||||||
|
dataSource: "machine-1",
|
||||||
|
dataValue: ["metric-1", "metric-2", "metric-3"],
|
||||||
|
};
|
||||||
}
|
}
|
||||||
: {
|
break;
|
||||||
|
|
||||||
|
case "text":
|
||||||
|
newElement = {
|
||||||
|
...commonProps,
|
||||||
|
type: "text",
|
||||||
|
style: {
|
||||||
color: "#ffffff",
|
color: "#ffffff",
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
textAlign: "left" as const,
|
textAlign: "left" as const,
|
||||||
},
|
},
|
||||||
positionType: "relative",
|
|
||||||
position: { x: 0, y: 0 },
|
|
||||||
zIndex: 1,
|
|
||||||
data: {
|
data: {
|
||||||
key: MathUtils.generateUUID(),
|
...commonProps.data,
|
||||||
dataSource: "static",
|
staticValue: "Text",
|
||||||
staticValue:
|
|
||||||
type === "label-value"
|
|
||||||
? "Value"
|
|
||||||
: type === "text"
|
|
||||||
? "Text"
|
|
||||||
: "",
|
|
||||||
label: type === "label-value" ? "Label" : undefined,
|
|
||||||
},
|
},
|
||||||
graphData: type === "graph" ? defaultGraphData : undefined,
|
size: { width: 200, height: 40 },
|
||||||
size:
|
|
||||||
type === "graph"
|
|
||||||
? { width: 400, height: 200 }
|
|
||||||
: { width: 200, height: 60 },
|
|
||||||
};
|
};
|
||||||
block.elements.push(newElement);
|
break;
|
||||||
|
|
||||||
|
case "icon":
|
||||||
|
newElement = {
|
||||||
|
...commonProps,
|
||||||
|
type: "icon",
|
||||||
|
style: {
|
||||||
|
color: "#ffffff",
|
||||||
|
fontSize: 14,
|
||||||
|
textAlign: "center" as const,
|
||||||
|
},
|
||||||
|
size: { width: 40, height: 40 },
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return; // Should not happen
|
||||||
}
|
}
|
||||||
|
|
||||||
|
block.elements.push(newElement);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -314,9 +328,7 @@ export const createSimulationDashboardStore = () => {
|
|||||||
set((state) => {
|
set((state) => {
|
||||||
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
||||||
if (block) {
|
if (block) {
|
||||||
block.elements = block.elements.filter(
|
block.elements = block.elements.filter((el) => el.elementUuid !== elementId);
|
||||||
(el) => el.elementUuid !== elementId
|
|
||||||
);
|
|
||||||
if (state.selectedElementId === elementId) {
|
if (state.selectedElementId === elementId) {
|
||||||
state.selectedElementId = null;
|
state.selectedElementId = null;
|
||||||
}
|
}
|
||||||
@@ -328,9 +340,7 @@ export const createSimulationDashboardStore = () => {
|
|||||||
set((state) => {
|
set((state) => {
|
||||||
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
||||||
if (block) {
|
if (block) {
|
||||||
const element = block.elements.find(
|
const element = block.elements.find((el) => el.elementUuid === elementId);
|
||||||
(el) => el.elementUuid === elementId
|
|
||||||
);
|
|
||||||
if (element) {
|
if (element) {
|
||||||
Object.assign(element, updates);
|
Object.assign(element, updates);
|
||||||
}
|
}
|
||||||
@@ -343,9 +353,7 @@ export const createSimulationDashboardStore = () => {
|
|||||||
set((state) => {
|
set((state) => {
|
||||||
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
||||||
if (block) {
|
if (block) {
|
||||||
const element = block.elements.find(
|
const element = block.elements.find((el) => el.elementUuid === elementId);
|
||||||
(el) => el.elementUuid === elementId
|
|
||||||
);
|
|
||||||
if (element) {
|
if (element) {
|
||||||
element.style = { ...element.style, ...newStyle };
|
element.style = { ...element.style, ...newStyle };
|
||||||
}
|
}
|
||||||
@@ -357,9 +365,7 @@ export const createSimulationDashboardStore = () => {
|
|||||||
set((state) => {
|
set((state) => {
|
||||||
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
||||||
if (block) {
|
if (block) {
|
||||||
const element = block.elements.find(
|
const element = block.elements.find((el) => el.elementUuid === elementId);
|
||||||
(el) => el.elementUuid === elementId
|
|
||||||
);
|
|
||||||
if (element) {
|
if (element) {
|
||||||
element.size = size;
|
element.size = size;
|
||||||
}
|
}
|
||||||
@@ -371,9 +377,7 @@ export const createSimulationDashboardStore = () => {
|
|||||||
set((state) => {
|
set((state) => {
|
||||||
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
||||||
if (block) {
|
if (block) {
|
||||||
const element = block.elements.find(
|
const element = block.elements.find((el) => el.elementUuid === elementId);
|
||||||
(el) => el.elementUuid === elementId
|
|
||||||
);
|
|
||||||
if (element) {
|
if (element) {
|
||||||
element.position = position;
|
element.position = position;
|
||||||
}
|
}
|
||||||
@@ -385,9 +389,7 @@ export const createSimulationDashboardStore = () => {
|
|||||||
set((state) => {
|
set((state) => {
|
||||||
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
||||||
if (block) {
|
if (block) {
|
||||||
const element = block.elements.find(
|
const element = block.elements.find((el) => el.elementUuid === elementId);
|
||||||
(el) => el.elementUuid === elementId
|
|
||||||
);
|
|
||||||
if (element) {
|
if (element) {
|
||||||
element.positionType = positionType;
|
element.positionType = positionType;
|
||||||
}
|
}
|
||||||
@@ -399,9 +401,7 @@ export const createSimulationDashboardStore = () => {
|
|||||||
set((state) => {
|
set((state) => {
|
||||||
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
||||||
if (block) {
|
if (block) {
|
||||||
const element = block.elements.find(
|
const element = block.elements.find((el) => el.elementUuid === elementId);
|
||||||
(el) => el.elementUuid === elementId
|
|
||||||
);
|
|
||||||
if (element) {
|
if (element) {
|
||||||
element.zIndex = zIndex;
|
element.zIndex = zIndex;
|
||||||
}
|
}
|
||||||
@@ -414,9 +414,7 @@ export const createSimulationDashboardStore = () => {
|
|||||||
set((state) => {
|
set((state) => {
|
||||||
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
||||||
if (block) {
|
if (block) {
|
||||||
const element = block.elements.find(
|
const element = block.elements.find((el) => el.elementUuid === elementId);
|
||||||
(el) => el.elementUuid === elementId
|
|
||||||
);
|
|
||||||
if (element?.data) {
|
if (element?.data) {
|
||||||
element.data = { ...element.data, ...updates };
|
element.data = { ...element.data, ...updates };
|
||||||
}
|
}
|
||||||
@@ -428,9 +426,7 @@ export const createSimulationDashboardStore = () => {
|
|||||||
set((state) => {
|
set((state) => {
|
||||||
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
||||||
if (block) {
|
if (block) {
|
||||||
const element = block.elements.find(
|
const element = block.elements.find((el) => el.elementUuid === elementId);
|
||||||
(el) => el.elementUuid === elementId
|
|
||||||
);
|
|
||||||
if (element) {
|
if (element) {
|
||||||
element.graphData = newData;
|
element.graphData = newData;
|
||||||
}
|
}
|
||||||
@@ -442,9 +438,7 @@ export const createSimulationDashboardStore = () => {
|
|||||||
set((state) => {
|
set((state) => {
|
||||||
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
||||||
if (block) {
|
if (block) {
|
||||||
const element = block.elements.find(
|
const element = block.elements.find((el) => el.elementUuid === elementId);
|
||||||
(el) => el.elementUuid === elementId
|
|
||||||
);
|
|
||||||
if (element) {
|
if (element) {
|
||||||
element.graphTitle = title;
|
element.graphTitle = title;
|
||||||
}
|
}
|
||||||
@@ -456,14 +450,10 @@ export const createSimulationDashboardStore = () => {
|
|||||||
set((state) => {
|
set((state) => {
|
||||||
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
const block = state.blocks.find((b) => b.blockUuid === blockId);
|
||||||
if (block) {
|
if (block) {
|
||||||
const element = block.elements.find(
|
const element = block.elements.find((el) => el.elementUuid === elementId);
|
||||||
(el) => el.elementUuid === elementId
|
|
||||||
);
|
|
||||||
if (element && element.type === "graph") {
|
if (element && element.type === "graph") {
|
||||||
element.graphType = graphType;
|
element.graphType = graphType;
|
||||||
element.graphTitle = `${
|
element.graphTitle = `${graphType.charAt(0).toUpperCase() + graphType.slice(1)} Chart`;
|
||||||
graphType.charAt(0).toUpperCase() + graphType.slice(1)
|
|
||||||
} Chart`;
|
|
||||||
|
|
||||||
element.graphData = defaultGraphData;
|
element.graphData = defaultGraphData;
|
||||||
}
|
}
|
||||||
@@ -495,20 +485,12 @@ export const createSimulationDashboardStore = () => {
|
|||||||
if (block) {
|
if (block) {
|
||||||
block.elements = block.elements.map((el) => {
|
block.elements = block.elements.map((el) => {
|
||||||
if (el.elementUuid === elementId1) {
|
if (el.elementUuid === elementId1) {
|
||||||
const targetElement = block.elements.find(
|
const targetElement = block.elements.find((e) => e.elementUuid === elementId2);
|
||||||
(e) => e.elementUuid === elementId2
|
return targetElement ? { ...targetElement, elementUuid: elementId1 } : el;
|
||||||
);
|
|
||||||
return targetElement
|
|
||||||
? { ...targetElement, elementUuid: elementId1 }
|
|
||||||
: el;
|
|
||||||
}
|
}
|
||||||
if (el.elementUuid === elementId2) {
|
if (el.elementUuid === elementId2) {
|
||||||
const sourceElement = block.elements.find(
|
const sourceElement = block.elements.find((e) => e.elementUuid === elementId1);
|
||||||
(e) => e.elementUuid === elementId1
|
return sourceElement ? { ...sourceElement, elementUuid: elementId2 } : el;
|
||||||
);
|
|
||||||
return sourceElement
|
|
||||||
? { ...sourceElement, elementUuid: elementId2 }
|
|
||||||
: el;
|
|
||||||
}
|
}
|
||||||
return el;
|
return el;
|
||||||
});
|
});
|
||||||
@@ -528,9 +510,7 @@ export const createSimulationDashboardStore = () => {
|
|||||||
|
|
||||||
getSelectedBlock: () => {
|
getSelectedBlock: () => {
|
||||||
const { selectedBlockId, blocks } = get();
|
const { selectedBlockId, blocks } = get();
|
||||||
return selectedBlockId
|
return selectedBlockId ? blocks.find((b) => b.blockUuid === selectedBlockId) : undefined;
|
||||||
? blocks.find((b) => b.blockUuid === selectedBlockId)
|
|
||||||
: undefined;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getSelectedElement: () => {
|
getSelectedElement: () => {
|
||||||
@@ -538,9 +518,7 @@ export const createSimulationDashboardStore = () => {
|
|||||||
if (!selectedElementId || !selectedBlockId) return undefined;
|
if (!selectedElementId || !selectedBlockId) return undefined;
|
||||||
|
|
||||||
const block = blocks.find((b) => b.blockUuid === selectedBlockId);
|
const block = blocks.find((b) => b.blockUuid === selectedBlockId);
|
||||||
return block?.elements.find(
|
return block?.elements.find((el) => el.elementUuid === selectedElementId);
|
||||||
(el) => el.elementUuid === selectedElementId
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
hasBlock: (blockId) => {
|
hasBlock: (blockId) => {
|
||||||
@@ -549,14 +527,10 @@ export const createSimulationDashboardStore = () => {
|
|||||||
|
|
||||||
hasElement: (blockId, elementId) => {
|
hasElement: (blockId, elementId) => {
|
||||||
const block = get().blocks.find((b) => b.blockUuid === blockId);
|
const block = get().blocks.find((b) => b.blockUuid === blockId);
|
||||||
return (
|
return block?.elements.some((el) => el.elementUuid === elementId) || false;
|
||||||
block?.elements.some((el) => el.elementUuid === elementId) || false
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SimulationDashboardStoreType = ReturnType<
|
export type SimulationDashboardStoreType = ReturnType<typeof createSimulationDashboardStore>;
|
||||||
typeof createSimulationDashboardStore
|
|
||||||
>;
|
|
||||||
|
|||||||
@@ -27,4 +27,33 @@ export type UIElement = {
|
|||||||
zIndex?: number;
|
zIndex?: number;
|
||||||
graphData?: GraphDataPoint[];
|
graphData?: GraphDataPoint[];
|
||||||
size?: Size;
|
size?: Size;
|
||||||
};
|
} & (
|
||||||
|
| {
|
||||||
|
type: "label-value";
|
||||||
|
title: string;
|
||||||
|
dataSource: string;
|
||||||
|
dataValue: string;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "graph";
|
||||||
|
dataType: "single-machine";
|
||||||
|
title: string;
|
||||||
|
dataSource: string;
|
||||||
|
dataValue: string[];
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "graph";
|
||||||
|
dataType: "multiple-machine";
|
||||||
|
title: string;
|
||||||
|
dataSource: string[];
|
||||||
|
commonValue: string;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: Exclude<UIType, "label-value" | "graph">;
|
||||||
|
title?: never;
|
||||||
|
dataSource?: never;
|
||||||
|
dataValue?: never;
|
||||||
|
commonValue?: never;
|
||||||
|
dataType?: never;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|||||||
2
app/src/types/simulationDashboard.d.ts
vendored
2
app/src/types/simulationDashboard.d.ts
vendored
@@ -17,6 +17,8 @@ type DataModel = Record<string, DataModelValue>;
|
|||||||
|
|
||||||
type UIType = "label-value" | "text" | "graph" | "icon";
|
type UIType = "label-value" | "text" | "graph" | "icon";
|
||||||
|
|
||||||
|
type DataType = "single-machine" | "multiple-machine";
|
||||||
|
|
||||||
type Position = {
|
type Position = {
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
|
|||||||
Reference in New Issue
Block a user