diff --git a/app/src/components/SimulationDashboard/DashboardEditor.tsx b/app/src/components/SimulationDashboard/DashboardEditor.tsx
index f7d780c..2971e5c 100644
--- a/app/src/components/SimulationDashboard/DashboardEditor.tsx
+++ b/app/src/components/SimulationDashboard/DashboardEditor.tsx
@@ -3,6 +3,7 @@ import React, { useState, useRef, useEffect } from "react";
import { dataModelManager } from "./data/dataModel";
import ControlPanel from "./ControlPanel";
import SwapModal from "./SwapModal";
+import { Block } from "../../types/exportedTypes";
import DataModelPanel from "./components/models/DataModelPanel";
import { useSceneContext } from "../../modules/scene/sceneContext";
@@ -77,23 +78,35 @@ const DashboardEditor: React.FC = () => {
const currentBlock = blocks.find((b) => b.blockUuid === selectedBlock);
const currentElement = currentBlock?.elements.find((el) => el.elementUuid === selectedElement);
+ useEffect(() => {
+ console.log("blocks: ", blocks);
+ }, [blocks]);
+
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);
- // }
- // });
+ getDashBoardBlocksApi(projectId, selectedVersion.versionId).then((data) => {
+ if (data.data?.blocks) {
+ console.log("data.data.blocks: ", data.data.blocks);
+ setBlocks(data.data.blocks);
+ }
+ });
}, [projectId, selectedVersion]);
+ const updateBackend = (blocks: Block[]) => {
+ if (!projectId || !selectedVersion) return;
+
+ upsetDashBoardBlocksApi({ projectId, versionId: selectedVersion.versionId, blocks }).then((data) => {
+ if (data.data?.blocks) {
+ console.log("data.data.blocks: ", data.data.blocks);
+ setBlocks(data.data.blocks);
+ }
+ });
+ };
+
useEffect(() => {
const unsubscribe = subscribe(() => {
if (!projectId || !selectedVersion) return;
-
- // upsetDashBoardBlocksApi({ projectId, versionId: selectedVersion.versionId, blocks }).then((data) => {
- // console.log("data: ", data);
- // });
+ updateBackend(blocks);
});
return () => {
@@ -103,10 +116,11 @@ const DashboardEditor: React.FC = () => {
useCallBackOnKey(
() => {
- console.log(blocks);
+ if (!projectId || !selectedVersion) return;
+ updateBackend(blocks);
},
"Ctrl+S",
- { dependencies: [blocks], noRepeat: true, allowOnInput: true }
+ { dependencies: [blocks, projectId, selectedVersion], noRepeat: true, allowOnInput: true }
);
// Subscribe to data model changes
@@ -118,27 +132,27 @@ const DashboardEditor: React.FC = () => {
const keys = dataModelManager.getAvailableKeys();
const subscriptions: Array<[string, () => void]> = [];
- keys.forEach((key) => {
+ for (const key of keys) {
const callback = () => handleDataChange();
dataModelManager.subscribe(key, callback);
subscriptions.push([key, callback]);
- });
+ }
const interval = setInterval(() => {
const currentKeys = dataModelManager.getAvailableKeys();
const newKeys = currentKeys.filter((key) => !keys.includes(key));
- newKeys.forEach((key) => {
+ for (const key of newKeys) {
const callback = () => handleDataChange();
dataModelManager.subscribe(key, callback);
subscriptions.push([key, callback]);
- });
+ }
}, 1000);
return () => {
- subscriptions.forEach(([key, callback]) => {
+ for (const [key, callback] of subscriptions) {
dataModelManager.unsubscribe(key, callback);
- });
+ }
clearInterval(interval);
};
}, []);
@@ -183,144 +197,144 @@ const DashboardEditor: React.FC = () => {
}, [selectedBlock, selectedElement]);
// Drag and drop handler
-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;
+ 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;
- // 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`;
+ if (blockElement && elementToDrag) {
+ const blockRect = blockElement.getBoundingClientRect();
+ const newX = e.clientX - blockRect.left - elementDragOffset.x;
+ const newY = e.clientY - blockRect.top - elementDragOffset.y;
+
+ // 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 - 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;
+ // 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;
- // 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 (editorElement && blockToDrag) {
+ const editorRect = editorElement.getBoundingClientRect();
+ const newX = e.clientX - editorRect.left - blockDragOffset.x;
+ const newY = e.clientY - editorRect.top - blockDragOffset.y;
+
+ // 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`;
+ }
}
- }
- // Resizing - direct DOM manipulation
- if ((resizingElement || resizingBlock) && resizeStart) {
+ // 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);
+
+ // 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.blockUuid === resizingBlock);
+ const minSize = currentBlock ? calculateMinBlockSize(currentBlock) : { width: 100, height: 50 };
+
+ const newWidth = Math.max(minSize.width, resizeStart.width + deltaX);
+ const newHeight = Math.max(minSize.height, resizeStart.height + deltaY);
+
+ // Direct DOM manipulation
+ blockToResize.style.width = `${newWidth}px`;
+ blockToResize.style.height = `${newHeight}px`;
+ }
+ }
+ }
+ };
+
+ 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 });
+ }
+ }
+
+ // 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 deltaX = e.clientX - resizeStart.x;
- const deltaY = e.clientY - resizeStart.y;
+ const computedStyle = window.getComputedStyle(elementToResize);
+ const width = parseFloat(computedStyle.width);
+ const height = parseFloat(computedStyle.height);
- const newWidth = Math.max(100, resizeStart.width + deltaX);
- const newHeight = Math.max(50, resizeStart.height + deltaY);
-
- // Direct DOM manipulation
- elementToResize.style.width = `${newWidth}px`;
- elementToResize.style.height = `${newHeight}px`;
+ updateElementSize(selectedBlock, resizingElement, { width, height });
}
- } else if (resizingBlock) {
+ }
+
+ 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 computedStyle = window.getComputedStyle(blockToResize);
+ const width = parseFloat(computedStyle.width);
+ const height = parseFloat(computedStyle.height);
- 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);
-
- // Direct DOM manipulation
- blockToResize.style.width = `${newWidth}px`;
- blockToResize.style.height = `${newHeight}px`;
+ updateBlockSize(resizingBlock, { width, height });
}
}
- }
- };
- 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 });
- }
+ // 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);
}
- // 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]);
+ 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(() => {
diff --git a/app/src/components/SimulationDashboard/functions/block/calculateMinBlockSize.ts b/app/src/components/SimulationDashboard/functions/block/calculateMinBlockSize.ts
index a366c20..fb63e7f 100644
--- a/app/src/components/SimulationDashboard/functions/block/calculateMinBlockSize.ts
+++ b/app/src/components/SimulationDashboard/functions/block/calculateMinBlockSize.ts
@@ -1,8 +1,8 @@
import type { Block } from "../../../../types/exportedTypes";
export const calculateMinBlockSize = (block: Block): Size => {
- let minWidth = 300;
- let minHeight = 200;
+ let minWidth = 100;
+ let minHeight = 50;
block.elements.forEach((element) => {
if (element.positionType === "absolute") {
diff --git a/app/src/components/SimulationDashboard/functions/eventHandlers/index.ts b/app/src/components/SimulationDashboard/functions/eventHandlers/index.ts
index 92f0f2b..427feb4 100644
--- a/app/src/components/SimulationDashboard/functions/eventHandlers/index.ts
+++ b/app/src/components/SimulationDashboard/functions/eventHandlers/index.ts
@@ -30,7 +30,6 @@ export const handleBlockDragStart = (
setDraggingBlock: (blockId: string | null) => void,
setBlockDragOffset: (offset: Position) => void // Change to specific offset
): void => {
- console.log("Block drag start:", blockId);
setDraggingBlock(blockId);
const element = event.currentTarget as HTMLElement;
const rect = element.getBoundingClientRect();
diff --git a/app/src/components/ui/list/OutlineList/AssetOutline.tsx b/app/src/components/ui/list/OutlineList/AssetOutline.tsx
index 8be3a61..2f67787 100644
--- a/app/src/components/ui/list/OutlineList/AssetOutline.tsx
+++ b/app/src/components/ui/list/OutlineList/AssetOutline.tsx
@@ -32,6 +32,7 @@ const TreeNode = ({
onDrop,
onToggleExpand,
onOptionClick,
+ onRename,
}: {
item: AssetGroupChild;
level?: number;
@@ -43,6 +44,7 @@ const TreeNode = ({
onClick: (e: React.MouseEvent, selectedItem: AssetGroupChild) => void;
onToggleExpand: (groupUuid: string, newExpanded: boolean) => void;
onOptionClick: (option: string, item: AssetGroupChild) => void;
+ onRename: (item: AssetGroupChild, newName: string) => void;
}) => {
const { assetGroupStore, assetStore } = useSceneContext();
const { hasSelectedAsset, selectedAssets } = assetStore();
@@ -136,7 +138,13 @@ const TreeNode = ({
{isGroupNode ? : }
- {}} canEdit={true} />
+ {
+ onRename(item, newName);
+ }}
+ canEdit={true}
+ />
@@ -195,6 +204,7 @@ export const AssetOutline = () => {
const {
groupHierarchy,
isGroup,
+ getItemId,
getGroupsContainingAsset,
getFlatGroupChildren,
setGroupExpanded,
@@ -207,6 +217,8 @@ export const AssetOutline = () => {
toggleSelectedGroup,
clearSelectedGroups,
hasSelectedGroup,
+ toggleGroupVisibility,
+ toggleGroupLock,
} = assetGroupStore();
const { projectId } = useParams();
const { push3D } = undoRedo3DStore();
@@ -232,6 +244,77 @@ export const AssetOutline = () => {
return flattened;
}, [groupHierarchy, isGroup]);
+ const handleAssetRenameUpdate = async (asset: Asset | null, newName: string) => {
+ if (!asset) return;
+
+ if (!builderSocket?.connected) {
+ setAssetsApi({
+ modelUuid: asset.modelUuid,
+ modelName: newName,
+ assetId: asset.assetId,
+ position: asset.position,
+ rotation: asset.rotation,
+ scale: asset.scale,
+ isCollidable: asset.isCollidable,
+ opacity: asset.opacity,
+ isLocked: asset.isLocked,
+ isVisible: asset.isVisible,
+ versionId: selectedVersion?.versionId || "",
+ projectId: projectId || "",
+ })
+ .then((data) => {
+ if (!data.message || !data.data) {
+ echo.error(`Error renaming asset: ${asset.modelName}`);
+ return;
+ }
+ if (data.message === "Model updated successfully" && data.data) {
+ const model: Asset = {
+ modelUuid: data.data.modelUuid,
+ modelName: data.data.modelName,
+ assetId: data.data.assetId,
+ position: data.data.position,
+ rotation: data.data.rotation,
+ scale: data.data.scale,
+ isLocked: data.data.isLocked,
+ isVisible: data.data.isVisible,
+ isCollidable: data.data.isCollidable,
+ opacity: data.data.opacity,
+ ...(data.data.eventData ? { eventData: data.data.eventData } : {}),
+ };
+
+ updateAssetInScene(model, () => {
+ echo.info(`Renamed asset to: ${model.modelName}`);
+ });
+ } else {
+ echo.error(`Error renaming asset: ${asset.modelName}`);
+ }
+ })
+ .catch(() => {
+ echo.error(`Error renaming asset: ${asset.modelName}`);
+ });
+ } else {
+ const data = {
+ organization,
+ modelUuid: asset.modelUuid,
+ modelName: newName,
+ assetId: asset.assetId,
+ position: asset.position,
+ rotation: asset.rotation,
+ scale: asset.scale,
+ isCollidable: asset.isCollidable,
+ opacity: asset.opacity,
+ isLocked: asset.isLocked,
+ isVisible: asset.isVisible,
+ socketId: builderSocket?.id,
+ versionId: selectedVersion?.versionId || "",
+ projectId,
+ userId,
+ };
+
+ builderSocket.emit("v1:model-asset:add", data);
+ }
+ };
+
const handleAssetVisibilityUpdate = async (asset: Asset | null) => {
if (!asset) return;
@@ -556,11 +639,6 @@ export const AssetOutline = () => {
if (!scene.current) return;
- // Helper to get item ID
- const getItemId = (i: AssetGroupChild): string => {
- return isGroup(i) ? i.groupUuid : i.modelUuid;
- };
-
const itemId = getItemId(item);
const flattened = getFlattenedHierarchy();
const clickedIndex = flattened.findIndex((flatItem) => getItemId(flatItem) === itemId);
@@ -674,14 +752,57 @@ export const AssetOutline = () => {
]
);
+ const handleRename = useCallback(
+ (item: AssetGroupChild, newName: string) => {
+ if (isGroup(item)) {
+ console.log("Rename group:", item.groupUuid, "to:", newName);
+ } else {
+ const asset = getAssetById(item.modelUuid);
+ if (!asset) return;
+ const oldName = asset.modelName;
+
+ const undoActions: UndoRedo3DAction[] = [];
+ const assetsToUpdate: AssetData[] = [];
+
+ assetsToUpdate.push({
+ type: "Asset",
+ assetData: { ...asset, modelName: oldName },
+ newData: { ...asset, modelName: newName },
+ timeStap: new Date().toISOString(),
+ });
+
+ if (assetsToUpdate.length > 0) {
+ if (assetsToUpdate.length === 1) {
+ undoActions.push({
+ module: "builder",
+ actionType: "Asset-Update",
+ asset: assetsToUpdate[0],
+ });
+ } else {
+ undoActions.push({
+ module: "builder",
+ actionType: "Assets-Update",
+ assets: assetsToUpdate,
+ });
+ }
+
+ push3D({
+ type: "Scene",
+ actions: undoActions,
+ });
+ }
+
+ handleAssetRenameUpdate(asset, newName);
+ }
+ },
+ [selectedVersion, builderSocket, projectId, userId, organization]
+ );
+
const handleOptionClick = useCallback(
(option: string, item: AssetGroupChild) => {
- const getItemId = (i: AssetGroupChild): string => {
- return isGroup(i) ? i.groupUuid : i.modelUuid;
- };
-
if (option === "visibility") {
if (isGroup(item)) {
+ toggleGroupVisibility(item.groupUuid);
} else {
const undoActions: UndoRedo3DAction[] = [];
const assetsToUpdate: AssetData[] = [];
@@ -724,6 +845,7 @@ export const AssetOutline = () => {
}
} else if (option === "lock") {
if (isGroup(item)) {
+ toggleGroupLock(item.groupUuid);
} else {
const undoActions: UndoRedo3DAction[] = [];
const assetsToUpdate: AssetData[] = [];
@@ -790,7 +912,7 @@ export const AssetOutline = () => {
const handleAddGroup = useCallback(() => {}, [assetGroupStore, clearSelectedGroups, addSelectedGroup]);
- const handleExpandAll = useCallback(() => {}, [assetGroupStore, setGroupExpanded]);
+ const handleCollapseAll = useCallback(() => {}, [assetGroupStore, setGroupExpanded]);
// Selection statistics
const selectionStats = useMemo(() => {
@@ -824,7 +946,7 @@ export const AssetOutline = () => {
-