diff --git a/app/src/components/SimulationDashboard/DashboardEditor.tsx b/app/src/components/SimulationDashboard/DashboardEditor.tsx index 20fa47d..d6e1ad9 100644 --- a/app/src/components/SimulationDashboard/DashboardEditor.tsx +++ b/app/src/components/SimulationDashboard/DashboardEditor.tsx @@ -18,6 +18,7 @@ import { handleBlockDragStart } from "./functions/block/handleBlockDragStart"; import { getDashBoardBlocksApi } from "../../services/visulization/dashBoard/getDashBoardBlocks"; import { upsetDashBoardBlocksApi } from "../../services/visulization/dashBoard/upsertDashBoardBlocks"; +import { deleteDashBoardBlocksApi } from "../../services/visulization/dashBoard/deleteDashBoardBlocks"; const DashboardEditor: React.FC = () => { const { simulationDashBoardStore, versionStore } = useSceneContext(); @@ -43,6 +44,9 @@ const DashboardEditor: React.FC = () => { updateGraphTitle, updateGraphType, swapElements, + removeBlock, + removeElement, + getBlockById, subscribe, } = simulationDashBoardStore(); @@ -83,6 +87,7 @@ const DashboardEditor: React.FC = () => { useEffect(() => { if (!projectId || !selectedVersion) return; getDashBoardBlocksApi(projectId, selectedVersion.versionId).then((data) => { + console.log("data: ", data); if (data.data?.blocks) { setBlocks(data.data.blocks); } @@ -102,6 +107,7 @@ const DashboardEditor: React.FC = () => { useEffect(() => { const unsubscribe = subscribe(() => { if (!projectId || !selectedVersion) return; + console.log("blocks: ", blocks); updateBackend(blocks); }); @@ -374,6 +380,19 @@ const DashboardEditor: React.FC = () => { updateBlockPosition={(blockId, position) => updateBlockPosition(blockId, position)} updateBlockPositionType={(blockId, positionType) => updateBlockPositionType(blockId, positionType)} 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 && ( @@ -391,6 +410,7 @@ const DashboardEditor: React.FC = () => { updateGraphData={(blockId, elementId, newData) => updateGraphData(blockId, elementId, newData)} updateGraphTitle={(blockId, elementId, title) => updateGraphTitle(blockId, elementId, title)} updateGraphType={(blockId, elementId, type) => updateGraphType(blockId, elementId, type)} + handleRemoveElement={(blockId, elementId) => removeElement(blockId, elementId)} setSwapSource={setSwapSource} setShowSwapUI={setShowSwapUI} dataModelManager={dataModelManager} diff --git a/app/src/components/SimulationDashboard/components/block/BlockComponent.tsx b/app/src/components/SimulationDashboard/components/block/BlockComponent.tsx index 3fe3303..0febed7 100644 --- a/app/src/components/SimulationDashboard/components/block/BlockComponent.tsx +++ b/app/src/components/SimulationDashboard/components/block/BlockComponent.tsx @@ -47,14 +47,14 @@ const BlockComponent: React.FC = ({ handleBlockDragStart, }) => { const { simulationDashBoardStore } = useSceneContext(); - const { addElement } = simulationDashBoardStore() + const { addElement } = simulationDashBoardStore(); const dropdownRef = useRef(null); const minSize = calculateMinBlockSize(block); const isSelected = selectedBlock === block.blockUuid; const isDraggable = editMode && (block.positionType === "absolute" || block.positionType === "fixed"); - + const handleMouseDown = (event: React.MouseEvent) => { if (isDraggable) { handleBlockDragStart(block.blockUuid, event); @@ -122,10 +122,11 @@ const BlockComponent: React.FC = ({ ))} {/* Block resize handle */} - {editMode && isSelected && + {editMode && isSelected && (
handleBlockResizeStart(block.blockUuid, e)}> -
} + + )} {/* Drag handle indicator for absolute/fixed blocks */} {isDraggable && editMode && ( @@ -152,9 +153,6 @@ const BlockComponent: React.FC = ({ ⤣ )} - - - ); }; diff --git a/app/src/components/SimulationDashboard/components/block/BlockEditor.tsx b/app/src/components/SimulationDashboard/components/block/BlockEditor.tsx index b1dfafb..205cff3 100644 --- a/app/src/components/SimulationDashboard/components/block/BlockEditor.tsx +++ b/app/src/components/SimulationDashboard/components/block/BlockEditor.tsx @@ -18,6 +18,7 @@ interface BlockEditorProps { updateBlockPosition: (blockId: string, position: { x: number; y: number }) => void; updateBlockPositionType: (blockId: string, positionType: "relative" | "absolute" | "fixed") => void; updateBlockZIndex: (blockId: string, zIndex: number) => void; + handleRemoveBlock: (blockId: string) => void; } const BlockEditor: React.FC = ({ @@ -29,12 +30,18 @@ const BlockEditor: React.FC = ({ updateBlockPosition, updateBlockPositionType, updateBlockZIndex, + handleRemoveBlock, }) => { return (

Block Style

-
+
{ + handleRemoveBlock(selectedBlock); + }} + >
diff --git a/app/src/components/SimulationDashboard/components/element/ElementEditor.tsx b/app/src/components/SimulationDashboard/components/element/ElementEditor.tsx index 2800288..3a54ac7 100644 --- a/app/src/components/SimulationDashboard/components/element/ElementEditor.tsx +++ b/app/src/components/SimulationDashboard/components/element/ElementEditor.tsx @@ -18,6 +18,7 @@ interface ElementEditorProps { updateGraphData: (blockId: string, elementId: string, newData: GraphDataPoint[]) => void; updateGraphTitle: (blockId: string, elementId: string, title: string) => void; updateGraphType: (blockId: string, elementId: string, type: GraphTypes) => void; + handleRemoveElement: (blockId: string, elementId: string) => void; setSwapSource: (source: string | null) => void; setShowSwapUI: (show: boolean) => void; dataModelManager: DataModelManager; @@ -37,6 +38,7 @@ const ElementEditor: React.FC = ({ updateGraphData, updateGraphTitle, updateGraphType, + handleRemoveElement, setSwapSource, setShowSwapUI, dataModelManager, @@ -52,10 +54,16 @@ const ElementEditor: React.FC = ({ return (
-

Element Style

-
+
{ + handleRemoveElement(selectedBlock, selectedElement); + }} + > + +
{currentElement.type === "label-value" && ( <> @@ -352,11 +360,7 @@ const ElementEditor: React.FC = ({ <>
- updateGraphType(selectedBlock, selectedElement, e.target.value as GraphTypes)} className="form-select"> diff --git a/app/src/services/visulization/dashBoard/deleteDashBoardBlocks.ts b/app/src/services/visulization/dashBoard/deleteDashBoardBlocks.ts new file mode 100644 index 0000000..690c4ac --- /dev/null +++ b/app/src/services/visulization/dashBoard/deleteDashBoardBlocks.ts @@ -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 ", + "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"); + } +}; diff --git a/app/src/store/simulation/useSimulationDashBoardStore.ts b/app/src/store/simulation/useSimulationDashBoardStore.ts index 4e851ff..ab7f89c 100644 --- a/app/src/store/simulation/useSimulationDashBoardStore.ts +++ b/app/src/store/simulation/useSimulationDashBoardStore.ts @@ -2,561 +2,535 @@ import { MathUtils } from "three"; import { create } from "zustand"; import { immer } from "zustand/middleware/immer"; import { defaultGraphData } from "../../components/SimulationDashboard/data/defaultGraphData"; -import { - Block, - UIElement, - ExtendedCSSProperties, -} from "../../types/exportedTypes"; +import { Block, UIElement, ExtendedCSSProperties } from "../../types/exportedTypes"; interface SimulationDashboardStore { - blocks: Block[]; - selectedBlockId: string | null; - selectedElementId: string | null; + blocks: Block[]; + selectedBlockId: string | null; + selectedElementId: string | null; - // Subscription management - subscribe: (callback: () => void) => () => void; - _notifySubscribers: () => void; - saveBlocks: () => void; + // Subscription management + subscribe: (callback: () => void) => () => void; + _notifySubscribers: () => void; + saveBlocks: () => void; - // Block operations - addBlock: () => void; - removeBlock: (blockId: string) => void; - updateBlock: (blockId: string, updates: Partial) => void; - clearBlocks: () => void; - setBlocks: (blocks: Block[]) => void; + // Block operations + addBlock: () => void; + removeBlock: (blockId: string) => void; + updateBlock: (blockId: string, updates: Partial) => void; + clearBlocks: () => void; + setBlocks: (blocks: Block[]) => void; - // Block styling and positioning - updateBlockStyle: (blockId: string, newStyle: React.CSSProperties) => void; - updateBlockSize: (blockId: string, size: Size) => void; - updateBlockPosition: (blockId: string, position: Position) => void; - updateBlockPositionType: ( - blockId: string, - positionType: "relative" | "absolute" | "fixed" - ) => void; - updateBlockZIndex: (blockId: string, zIndex: number) => void; + // Block styling and positioning + updateBlockStyle: (blockId: string, newStyle: React.CSSProperties) => void; + updateBlockSize: (blockId: string, size: Size) => void; + updateBlockPosition: (blockId: string, position: Position) => void; + updateBlockPositionType: (blockId: string, positionType: "relative" | "absolute" | "fixed") => void; + updateBlockZIndex: (blockId: string, zIndex: number) => void; - // Element operations - addElement: (blockId: string, type: UIType, graphType?: GraphTypes) => void; - removeElement: (blockId: string, elementId: string) => void; - updateElement: ( - blockId: string, - elementId: string, - updates: Partial - ) => void; + // Element operations + addElement: (blockId: string, type: UIType, graphType?: GraphTypes) => void; + removeElement: (blockId: string, elementId: string) => void; + updateElement: (blockId: string, elementId: string, updates: Partial) => void; - // Element styling and positioning - updateElementStyle: ( - blockId: string, - elementId: string, - newStyle: ExtendedCSSProperties - ) => void; - updateElementSize: (blockId: string, elementId: string, size: Size) => void; - updateElementPosition: ( - blockId: string, - elementId: string, - position: Position - ) => void; - updateElementPositionType: ( - blockId: string, - elementId: string, - positionType: "relative" | "absolute" | "fixed" - ) => void; - updateElementZIndex: ( - blockId: string, - elementId: string, - zIndex: number - ) => void; + // Element styling and positioning + updateElementStyle: (blockId: string, elementId: string, newStyle: ExtendedCSSProperties) => void; + updateElementSize: (blockId: string, elementId: string, size: Size) => void; + updateElementPosition: (blockId: string, elementId: string, position: Position) => void; + updateElementPositionType: (blockId: string, elementId: string, positionType: "relative" | "absolute" | "fixed") => void; + updateElementZIndex: (blockId: string, elementId: string, zIndex: number) => void; - // Element data operations - updateElementData: ( - blockId: string, - elementId: string, - updates: Partial - ) => 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; + // Element data operations + updateElementData: (blockId: string, elementId: string, updates: Partial) => 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; - setSelectedElement: (elementId: string | null) => void; + // Selection and hover management + setSelectedBlock: (blockId: string | null) => void; + setSelectedElement: (elementId: string | null) => void; - // Element swapping - swapElements: ( - blockId: string, - elementId1: string, - elementId2: string - ) => void; + // Element swapping + swapElements: (blockId: string, elementId1: string, elementId2: string) => void; - // Helper functions - getBlockById: (blockId: string) => Block | undefined; - getElementById: (blockId: string, elementId: string) => UIElement | undefined; - getSelectedBlock: () => Block | undefined; - getSelectedElement: () => UIElement | undefined; - hasBlock: (blockId: string) => boolean; - hasElement: (blockId: string, elementId: string) => boolean; + // Helper functions + getBlockById: (blockId: string) => Block | undefined; + getElementById: (blockId: string, elementId: string) => UIElement | undefined; + getSelectedBlock: () => Block | undefined; + getSelectedElement: () => UIElement | undefined; + hasBlock: (blockId: string) => boolean; + hasElement: (blockId: string, elementId: string) => boolean; } const subscribers = new Set<() => void>(); export const createSimulationDashboardStore = () => { - return create()( - immer((set, get) => ({ - blocks: [], - selectedBlockId: null, - selectedElementId: null, + return create()( + immer((set, get) => ({ + blocks: [], + 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 = { - blockUuid: MathUtils.generateUUID(), - style: { - backgroundColor: "rgba(50, 50, 50, 0.8)", - backdropFilter: "blur(10px)", - padding: 10, - borderRadius: 8, - border: "1px solid rgba(255, 255, 255, 0.1)", - position: "relative", - minHeight: "200px", - minWidth: "300px", + subscribe: (callback: () => void) => { + subscribers.add(callback); + return () => { + subscribers.delete(callback); + }; }, - elements: [], - zIndex: 1, - size: { width: 400, height: 300 }, - position: { x: 0, y: 0 }, - positionType: "relative", - }; - state.blocks.push(newBlock); - }); - }, - removeBlock: (blockId) => { - set((state) => { - state.blocks = state.blocks.filter( - (block) => block.blockUuid !== blockId - ); - if (state.selectedBlockId === blockId) { - state.selectedBlockId = null; - } - }); - }, - - updateBlock: (blockId, updates) => { - set((state) => { - const block = state.blocks.find((b) => b.blockUuid === blockId); - if (block) { - Object.assign(block, updates); - } - }); - }, - - clearBlocks: () => { - set((state) => { - state.blocks = []; - state.selectedBlockId = null; - state.selectedElementId = null; - }); - }, - - setBlocks: (blocks) => { - set((state) => { - state.blocks = blocks; - }); - }, - - // Block styling and positioning - updateBlockStyle: (blockId, newStyle) => { - set((state) => { - const block = state.blocks.find((b) => b.blockUuid === blockId); - if (block) { - block.style = { ...block.style, ...newStyle }; - } - }); - }, - - updateBlockSize: (blockId, size) => { - set((state) => { - const block = state.blocks.find((b) => b.blockUuid === blockId); - if (block) { - block.size = size; - } - }); - }, - - updateBlockPosition: (blockId, position) => { - set((state) => { - const block = state.blocks.find((b) => b.blockUuid === blockId); - if (block) { - block.position = position; - } - }); - }, - - updateBlockPositionType: (blockId, positionType) => { - set((state) => { - const block = state.blocks.find((b) => b.blockUuid === blockId); - if (block) { - block.positionType = positionType; - } - }); - }, - - updateBlockZIndex: (blockId, zIndex) => { - set((state) => { - const block = state.blocks.find((b) => b.blockUuid === blockId); - if (block) { - block.zIndex = zIndex; - } - }); - }, - - // Element operations - addElement: (blockId, type, graphType) => { - set((state) => { - const block = state.blocks.find((b) => b.blockUuid === blockId); - if (block) { - const newElement: UIElement = { - elementUuid: MathUtils.generateUUID(), - type: type, - graphType, - graphTitle: graphType - ? `${ - graphType.charAt(0).toUpperCase() + graphType.slice(1) - } Chart` - : undefined, - style: - type === "graph" - ? { - 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", + _notifySubscribers: () => { + subscribers.forEach((callback) => { + try { + callback(); + } catch (error) { + console.error("Error in store subscriber:", error); } - : type === "label-value" - ? { - color: "#ffffff", - fontSize: 14, - textAlign: "left" as const, - display: "flex", - flexDirection: "column", - gap: "4px", - alignItems: "flex-start", - justifyContent: "center", - labelColor: "#ffffff", - valueColor: "#ffffff", + }); + }, + + saveBlocks: () => { + get()._notifySubscribers(); + }, + + // Block operations + addBlock: () => { + set((state) => { + const newBlock: Block = { + blockUuid: MathUtils.generateUUID(), + style: { + backgroundColor: "rgba(50, 50, 50, 0.8)", + backdropFilter: "blur(10px)", + padding: 10, + borderRadius: 8, + border: "1px solid rgba(255, 255, 255, 0.1)", + position: "relative", + minHeight: "200px", + minWidth: "300px", + }, + elements: [], + zIndex: 1, + size: { width: 400, height: 300 }, + position: { x: 0, y: 0 }, + positionType: "relative", + }; + state.blocks.push(newBlock); + }); + }, + + removeBlock: (blockId) => { + set((state) => { + state.blocks = state.blocks.filter((block) => block.blockUuid !== blockId); + if (state.selectedBlockId === blockId) { + state.selectedBlockId = null; } - : { - color: "#ffffff", - fontSize: 14, - textAlign: "left" as const, - }, - positionType: "relative", - position: { x: 0, y: 0 }, - zIndex: 1, - data: { - key: MathUtils.generateUUID(), - dataSource: "static", - staticValue: - type === "label-value" - ? "Value" - : type === "text" - ? "Text" - : "", - label: type === "label-value" ? "Label" : undefined, - }, - graphData: type === "graph" ? defaultGraphData : undefined, - size: - type === "graph" - ? { width: 400, height: 200 } - : { width: 200, height: 60 }, - }; - block.elements.push(newElement); - } - }); - }, + }); + }, - removeElement: (blockId, elementId) => { - set((state) => { - const block = state.blocks.find((b) => b.blockUuid === blockId); - if (block) { - block.elements = block.elements.filter( - (el) => el.elementUuid !== elementId - ); - if (state.selectedElementId === elementId) { - state.selectedElementId = null; - } - } - }); - }, + updateBlock: (blockId, updates) => { + set((state) => { + const block = state.blocks.find((b) => b.blockUuid === blockId); + if (block) { + Object.assign(block, updates); + } + }); + }, - updateElement: (blockId, elementId, updates) => { - set((state) => { - const block = state.blocks.find((b) => b.blockUuid === blockId); - if (block) { - const element = block.elements.find( - (el) => el.elementUuid === elementId - ); - if (element) { - Object.assign(element, updates); - } - } - }); - }, + clearBlocks: () => { + set((state) => { + state.blocks = []; + state.selectedBlockId = null; + state.selectedElementId = null; + }); + }, - // Element styling and positioning - updateElementStyle: (blockId, elementId, newStyle) => { - 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.style = { ...element.style, ...newStyle }; - } - } - }); - }, + setBlocks: (blocks) => { + set((state) => { + state.blocks = blocks; + }); + }, - updateElementSize: (blockId, elementId, size) => { - 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.size = size; - } - } - }); - }, + // Block styling and positioning + updateBlockStyle: (blockId, newStyle) => { + set((state) => { + const block = state.blocks.find((b) => b.blockUuid === blockId); + if (block) { + block.style = { ...block.style, ...newStyle }; + } + }); + }, - updateElementPosition: (blockId, elementId, position) => { - 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.position = position; - } - } - }); - }, + updateBlockSize: (blockId, size) => { + set((state) => { + const block = state.blocks.find((b) => b.blockUuid === blockId); + if (block) { + block.size = size; + } + }); + }, - updateElementPositionType: (blockId, elementId, positionType) => { - 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.positionType = positionType; - } - } - }); - }, + updateBlockPosition: (blockId, position) => { + set((state) => { + const block = state.blocks.find((b) => b.blockUuid === blockId); + if (block) { + block.position = position; + } + }); + }, - updateElementZIndex: (blockId, elementId, zIndex) => { - 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.zIndex = zIndex; - } - } - }); - }, + updateBlockPositionType: (blockId, positionType) => { + set((state) => { + const block = state.blocks.find((b) => b.blockUuid === blockId); + if (block) { + block.positionType = positionType; + } + }); + }, - // Element data operations - updateElementData: (blockId, elementId, updates) => { - set((state) => { - const block = state.blocks.find((b) => b.blockUuid === blockId); - if (block) { - const element = block.elements.find( - (el) => el.elementUuid === elementId - ); - if (element?.data) { - element.data = { ...element.data, ...updates }; - } - } - }); - }, + updateBlockZIndex: (blockId, zIndex) => { + set((state) => { + const block = state.blocks.find((b) => b.blockUuid === blockId); + if (block) { + block.zIndex = zIndex; + } + }); + }, - updateGraphData: (blockId, elementId, newData) => { - 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.graphData = newData; - } - } - }); - }, + // Element operations + addElement: (blockId, type, graphType, dataType?: DataType) => { + set((state) => { + const block = state.blocks.find((b) => b.blockUuid === blockId); + if (!block) return; - updateGraphTitle: (blockId, elementId, title) => { - 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.graphTitle = title; - } - } - }); - }, + let newElement: UIElement; - 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`; + const commonProps = { + elementUuid: MathUtils.generateUUID(), + positionType: "relative" as const, + position: { x: 0, y: 0 }, + zIndex: 1, + data: { + key: MathUtils.generateUUID(), + dataSource: "static" as const, + staticValue: "", + label: undefined, + }, + }; - element.graphData = defaultGraphData; - } - } - }); - }, + switch (type) { + case "label-value": + newElement = { + ...commonProps, + type: "label-value", + title: "Label Value", + dataSource: "machine-1", + dataValue: "metric-1", + style: { + color: "#ffffff", + fontSize: 14, + textAlign: "left" as const, + display: "flex", + flexDirection: "column", + gap: "4px", + alignItems: "flex-start", + justifyContent: "center", + labelColor: "#ffffff", + valueColor: "#ffffff", + }, + data: { + ...commonProps.data, + staticValue: "Value", + label: "Label", + }, + size: { width: 200, height: 60 }, + }; + break; - // Selection and hover management - setSelectedBlock: (blockId) => { - set((state) => { - state.selectedBlockId = blockId; - // Clear element selection when selecting a block - if (blockId) { - state.selectedElementId = null; - } - }); - }, + 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 }, + }; - setSelectedElement: (elementId) => { - set((state) => { - state.selectedElementId = elementId; - }); - }, + 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; - // Element swapping - swapElements: (blockId, elementId1, elementId2) => { - set((state) => { - const block = state.blocks.find((b) => b.blockUuid === blockId); - if (block) { - block.elements = block.elements.map((el) => { - if (el.elementUuid === elementId1) { - const targetElement = block.elements.find( - (e) => e.elementUuid === elementId2 - ); - return targetElement - ? { ...targetElement, elementUuid: elementId1 } - : el; - } - if (el.elementUuid === elementId2) { - const sourceElement = block.elements.find( - (e) => e.elementUuid === elementId1 - ); - return sourceElement - ? { ...sourceElement, elementUuid: elementId2 } - : el; - } - return el; - }); - } - }); - }, + case "text": + newElement = { + ...commonProps, + type: "text", + style: { + color: "#ffffff", + fontSize: 14, + textAlign: "left" as const, + }, + data: { + ...commonProps.data, + staticValue: "Text", + }, + size: { width: 200, height: 40 }, + }; + break; - // Helper functions - getBlockById: (blockId) => { - return get().blocks.find((b) => b.blockUuid === blockId); - }, + case "icon": + newElement = { + ...commonProps, + type: "icon", + style: { + color: "#ffffff", + fontSize: 14, + textAlign: "center" as const, + }, + size: { width: 40, height: 40 }, + }; + break; - getElementById: (blockId, elementId) => { - const block = get().blocks.find((b) => b.blockUuid === blockId); - return block?.elements.find((el) => el.elementUuid === elementId); - }, + default: + return; // Should not happen + } - getSelectedBlock: () => { - const { selectedBlockId, blocks } = get(); - return selectedBlockId - ? blocks.find((b) => b.blockUuid === selectedBlockId) - : undefined; - }, + block.elements.push(newElement); + }); + }, - getSelectedElement: () => { - const { selectedElementId, selectedBlockId, blocks } = get(); - if (!selectedElementId || !selectedBlockId) return undefined; + removeElement: (blockId, elementId) => { + set((state) => { + const block = state.blocks.find((b) => b.blockUuid === blockId); + if (block) { + block.elements = block.elements.filter((el) => el.elementUuid !== elementId); + if (state.selectedElementId === elementId) { + state.selectedElementId = null; + } + } + }); + }, - const block = blocks.find((b) => b.blockUuid === selectedBlockId); - return block?.elements.find( - (el) => el.elementUuid === selectedElementId - ); - }, + updateElement: (blockId, elementId, updates) => { + set((state) => { + const block = state.blocks.find((b) => b.blockUuid === blockId); + if (block) { + const element = block.elements.find((el) => el.elementUuid === elementId); + if (element) { + Object.assign(element, updates); + } + } + }); + }, - hasBlock: (blockId) => { - return get().blocks.some((b) => b.blockUuid === blockId); - }, + // Element styling and positioning + updateElementStyle: (blockId, elementId, newStyle) => { + 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.style = { ...element.style, ...newStyle }; + } + } + }); + }, - hasElement: (blockId, elementId) => { - const block = get().blocks.find((b) => b.blockUuid === blockId); - return ( - block?.elements.some((el) => el.elementUuid === elementId) || false - ); - }, - })) - ); + updateElementSize: (blockId, elementId, size) => { + 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.size = size; + } + } + }); + }, + + updateElementPosition: (blockId, elementId, position) => { + 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.position = position; + } + } + }); + }, + + updateElementPositionType: (blockId, elementId, positionType) => { + 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.positionType = positionType; + } + } + }); + }, + + updateElementZIndex: (blockId, elementId, zIndex) => { + 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.zIndex = zIndex; + } + } + }); + }, + + // Element data operations + updateElementData: (blockId, elementId, updates) => { + set((state) => { + const block = state.blocks.find((b) => b.blockUuid === blockId); + if (block) { + const element = block.elements.find((el) => el.elementUuid === elementId); + if (element?.data) { + element.data = { ...element.data, ...updates }; + } + } + }); + }, + + updateGraphData: (blockId, elementId, newData) => { + 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.graphData = newData; + } + } + }); + }, + + updateGraphTitle: (blockId, elementId, title) => { + 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.graphTitle = title; + } + } + }); + }, + + 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) => { + state.selectedBlockId = blockId; + // Clear element selection when selecting a block + if (blockId) { + state.selectedElementId = null; + } + }); + }, + + setSelectedElement: (elementId) => { + set((state) => { + state.selectedElementId = elementId; + }); + }, + + // Element swapping + swapElements: (blockId, elementId1, elementId2) => { + set((state) => { + const block = state.blocks.find((b) => b.blockUuid === blockId); + if (block) { + block.elements = block.elements.map((el) => { + if (el.elementUuid === elementId1) { + const targetElement = block.elements.find((e) => e.elementUuid === elementId2); + return targetElement ? { ...targetElement, elementUuid: elementId1 } : el; + } + if (el.elementUuid === elementId2) { + const sourceElement = block.elements.find((e) => e.elementUuid === elementId1); + return sourceElement ? { ...sourceElement, elementUuid: elementId2 } : el; + } + return el; + }); + } + }); + }, + + // Helper functions + getBlockById: (blockId) => { + return get().blocks.find((b) => b.blockUuid === blockId); + }, + + getElementById: (blockId, 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.blockUuid === selectedBlockId) : undefined; + }, + + getSelectedElement: () => { + const { selectedElementId, selectedBlockId, blocks } = get(); + if (!selectedElementId || !selectedBlockId) return undefined; + + const block = blocks.find((b) => b.blockUuid === selectedBlockId); + return block?.elements.find((el) => el.elementUuid === selectedElementId); + }, + + hasBlock: (blockId) => { + return get().blocks.some((b) => b.blockUuid === blockId); + }, + + hasElement: (blockId, elementId) => { + 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; diff --git a/app/src/types/exportedTypes.ts b/app/src/types/exportedTypes.ts index bd06446..0e39175 100644 --- a/app/src/types/exportedTypes.ts +++ b/app/src/types/exportedTypes.ts @@ -27,4 +27,33 @@ export type UIElement = { zIndex?: number; graphData?: GraphDataPoint[]; 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; + title?: never; + dataSource?: never; + dataValue?: never; + commonValue?: never; + dataType?: never; + } +); diff --git a/app/src/types/simulationDashboard.d.ts b/app/src/types/simulationDashboard.d.ts index 859b421..09b14ef 100644 --- a/app/src/types/simulationDashboard.d.ts +++ b/app/src/types/simulationDashboard.d.ts @@ -17,6 +17,8 @@ type DataModel = Record; type UIType = "label-value" | "text" | "graph" | "icon"; +type DataType = "single-machine" | "multiple-machine"; + type Position = { x: number; y: number;