diff --git a/app/src/components/layout/sidebarRight/SideBarRight.tsx b/app/src/components/layout/sidebarRight/SideBarRight.tsx index 277637a..b13944c 100644 --- a/app/src/components/layout/sidebarRight/SideBarRight.tsx +++ b/app/src/components/layout/sidebarRight/SideBarRight.tsx @@ -72,9 +72,8 @@ const SideBarRight: React.FC = () => { <div className="sidebar-actions-container"> {/* {activeModule === "builder" && ( */} <div - className={`sidebar-action-list ${ - subModule === "properties" ? "active" : "" - }`} + className={`sidebar-action-list ${subModule === "properties" ? "active" : "" + }`} onClick={() => setSubModule("properties")} > <PropertiesIcon isActive={subModule === "properties"} /> @@ -83,25 +82,22 @@ const SideBarRight: React.FC = () => { {activeModule === "simulation" && ( <> <div - className={`sidebar-action-list ${ - subModule === "mechanics" ? "active" : "" - }`} + className={`sidebar-action-list ${subModule === "mechanics" ? "active" : "" + }`} onClick={() => setSubModule("mechanics")} > <MechanicsIcon isActive={subModule === "mechanics"} /> </div> <div - className={`sidebar-action-list ${ - subModule === "simulations" ? "active" : "" - }`} + className={`sidebar-action-list ${subModule === "simulations" ? "active" : "" + }`} onClick={() => setSubModule("simulations")} > <SimulationIcon isActive={subModule === "simulations"} /> </div> <div - className={`sidebar-action-list ${ - subModule === "analysis" ? "active" : "" - }`} + className={`sidebar-action-list ${subModule === "analysis" ? "active" : "" + }`} onClick={() => setSubModule("analysis")} > <AnalysisIcon isActive={subModule === "analysis"} /> diff --git a/app/src/components/layout/sidebarRight/simulation/Simulations.tsx b/app/src/components/layout/sidebarRight/simulation/Simulations.tsx index 2a12734..cbb19fa 100644 --- a/app/src/components/layout/sidebarRight/simulation/Simulations.tsx +++ b/app/src/components/layout/sidebarRight/simulation/Simulations.tsx @@ -1,147 +1,172 @@ -import React, { useRef, useState } from "react"; +import React, { useEffect, useRef } from "react"; import { - AddIcon, - ArrowIcon, - RemoveIcon, - ResizeHeightIcon, + AddIcon, + ArrowIcon, + RemoveIcon, + ResizeHeightIcon, } from "../../../icons/ExportCommonIcons"; import RenameInput from "../../../ui/inputs/RenameInput"; import { handleResize } from "../../../../functions/handleResizePannel"; +import { useSelectedProduct } from "../../../../store/simulation/useSimulationStore"; +import { useProductStore } from "../../../../store/simulation/useProductStore"; +import { generateUUID } from "three/src/math/MathUtils"; -interface Path { - pathName: string; // Represents the name of the path - Children: string[]; // Represents the list of child points +interface Event { + pathName: string; } -interface DropListProps { - val: Path; // Use the Path interface for the val prop +interface ListProps { + val: Event; } -const DropList: React.FC<DropListProps> = ({ val }) => { - const [openDrop, setOpenDrop] = useState(false); - return ( - <div className="process-container"> - <div - className="value" - onClick={() => { - setOpenDrop(!openDrop); - }} - > - {val.pathName} - <div className={`arrow-container${openDrop ? " active" : ""}`}> - <ArrowIcon /> - </div> - </div> - {val.Children && openDrop && ( - <div className="children-drop"> - {val.Children.map((child, index) => ( - <div key={index} className="value"> - {child} +const List: React.FC<ListProps> = ({ val }) => { + return ( + <div className="process-container"> + <div className="value"> + {val.pathName} </div> - ))} </div> - )} - </div> - ); + ); }; const Simulations: React.FC = () => { - const productsContainerRef = useRef<HTMLDivElement>(null); - const [productsList, setProductsList] = useState<string[]>([]); - const [selectedItem, setSelectedItem] = useState<string>(); + const productsContainerRef = useRef<HTMLDivElement>(null); + const { products, addProduct, removeProduct, renameProduct } = useProductStore(); + const { selectedProduct, setSelectedProduct } = useSelectedProduct(); - const handleAddAction = () => { - setProductsList([...productsList, `Product ${productsList.length + 1}`]); - }; + useEffect(() => { + if (products.length > 0 && selectedProduct.productId === '' && selectedProduct.productName === '') { + setSelectedProduct(products[0].productId, products[0].productName); + } + }, [products, selectedProduct]); - const handleRemoveAction = (index: number) => { - setProductsList(productsList.filter((_, i) => i !== index)); - if (selectedItem === productsList[index]) { - setSelectedItem(""); - } - }; + const handleAddProduct = () => { + addProduct(`Product ${products.length + 1}`, generateUUID()); + }; - const Value = [ - { pathName: "Path 1", Children: ["Point 1", "Point 2"] }, - { pathName: "Path 2", Children: ["Point 1", "Point 2"] }, - { pathName: "Path 3", Children: ["Point 1", "Point 2"] }, - ]; + const handleRemoveProduct = (productId: string) => { + const currentIndex = products.findIndex(p => p.productId === productId); + const isSelected = selectedProduct.productId === productId; - return ( - <div className="simulations-container"> - <div className="header">Simulations</div> - <div className="add-product-container"> - <div className="actions"> - <div className="header"> - <div className="header-value">Products</div> - <div className="add-button" onClick={handleAddAction}> - <AddIcon /> Add - </div> - </div> - <div - className="lists-main-container" - ref={productsContainerRef} - style={{ height: "120px" }} - > - <div className="list-container"> - {productsList.map((action, index) => ( - <div - key={index} - className={`list-item ${ - selectedItem === action ? "active" : "" - }`} - > - <div - className="value" - onClick={() => setSelectedItem(action)} - > - <input type="radio" name="products" id="products" /> - <RenameInput value={action} /> - </div> - <div - className="remove-button" - onClick={() => handleRemoveAction(index)} - > - <RemoveIcon /> - </div> + const updatedProducts = products.filter(p => p.productId !== productId); + + if (isSelected) { + if (updatedProducts.length > 0) { + let newSelectedIndex = currentIndex; + if (currentIndex >= updatedProducts.length) { + newSelectedIndex = updatedProducts.length - 1; + } + setSelectedProduct( + updatedProducts[newSelectedIndex].productId, + updatedProducts[newSelectedIndex].productName + ); + } else { + setSelectedProduct('', ''); + } + } + + removeProduct(productId); + }; + + const handleRenameProduct = (productId: string, newName: string) => { + renameProduct(productId, newName); + if (selectedProduct.productId === productId) { + setSelectedProduct(productId, newName); + } + }; + + const selectedProductData = products.find( + (product) => product.productId === selectedProduct.productId + ); + + const events: Event[] = selectedProductData?.eventsData.map((event, index) => ({ + pathName: `${event.modelName} - ${event.type} #${index + 1}`, + })) || []; + + return ( + <div className="simulations-container"> + <div className="header">Simulations</div> + <div className="add-product-container"> + <div className="actions"> + <div className="header"> + <div className="header-value">Products</div> + <div className="add-button" onClick={handleAddProduct}> + <AddIcon /> Add + </div> + </div> + <div + className="lists-main-container" + ref={productsContainerRef} + style={{ height: "120px" }} + > + <div className="list-container"> + {products.map((product, index) => ( + <div + key={product.productId} + className={`list-item ${selectedProduct.productId === product.productId ? "active" : ""}`} + > + <div + className="value" + onClick={() => setSelectedProduct(product.productId, product.productName)} + > + <input + type="radio" + name="products" + checked={selectedProduct.productId === product.productId} + readOnly + /> + <RenameInput + value={product.productName} + onRename={(newName) => handleRenameProduct(product.productId, newName)} + /> + </div> + {products.length > 1 && ( + <div + className="remove-button" + onClick={() => handleRemoveProduct(product.productId)} + > + <RemoveIcon /> + </div> + )} + </div> + ))} + </div> + <div + className="resize-icon" + id="action-resize" + onMouseDown={(e) => handleResize(e, productsContainerRef)} + > + <ResizeHeightIcon /> + </div> + </div> + </div> + + <div className="simulation-process"> + <div className="collapse-header-container"> + <div className="header">Events</div> + <div className="arrow-container"> + <ArrowIcon /> + </div> + </div> + {events.map((event, index) => ( + <List key={index} val={event} /> + ))} + </div> + + <div className="compare-simulations-container"> + <div className="compare-simulations-header"> + Need to Compare Layout? + </div> + <div className="content"> + Click <span>'Compare'</span> to review and analyze the layout differences between them. + </div> + <div className="input"> + <input type="button" value={"Compare"} className="submit" /> + </div> </div> - ))} </div> - <div - className="resize-icon" - id="action-resize" - onMouseDown={(e) => handleResize(e, productsContainerRef)} - > - <ResizeHeightIcon /> - </div> - </div> </div> - <div className="simulation-process"> - <div className="collapse-header-container"> - <div className="header">Operations</div> - <div className="arrow-container"> - <ArrowIcon /> - </div> - </div> - {Value.map((val, index) => ( - <DropList key={index} val={val} /> - ))} - </div> - <div className="compare-simulations-container"> - <div className="compare-simulations-header"> - Need to Compare Layout? - </div> - <div className="content"> - Click <span>'Compare'</span> to review and analyze the layout - differences between them. - </div> - <div className="input"> - <input type="button" value={"Compare"} className="submit" /> - </div> - </div> - </div> - </div> - ); + ); }; export default Simulations; diff --git a/app/src/modules/simulation/products/products.tsx b/app/src/modules/simulation/products/products.tsx new file mode 100644 index 0000000..2e9a16e --- /dev/null +++ b/app/src/modules/simulation/products/products.tsx @@ -0,0 +1,20 @@ +import React, { useEffect } from 'react' +import { useProductStore } from '../../../store/simulation/useProductStore' +import * as THREE from 'three'; + +function Products() { + const { products, addProduct } = useProductStore(); + + useEffect(() => { + if (products.length === 0) { + addProduct('Product 1', THREE.MathUtils.generateUUID()); + } + }, [products]) + + return ( + <> + </> + ) +} + +export default Products \ No newline at end of file diff --git a/app/src/modules/simulation/simulation.tsx b/app/src/modules/simulation/simulation.tsx index 572fc70..f02b066 100644 --- a/app/src/modules/simulation/simulation.tsx +++ b/app/src/modules/simulation/simulation.tsx @@ -9,6 +9,7 @@ import Materials from './materials/materials'; import Machine from './machine/machine'; import StorageUnit from './storageUnit/storageUnit'; import Simulator from './simulator/simulator'; +import Products from './products/products'; function Simulation() { const { events } = useEventsStore(); @@ -27,6 +28,8 @@ function Simulation() { <Points /> + <Products /> + <Materials /> <Conveyor /> diff --git a/app/src/store/simulation/useArmBotStore.ts b/app/src/store/simulation/useArmBotStore.ts index 493a068..d907f21 100644 --- a/app/src/store/simulation/useArmBotStore.ts +++ b/app/src/store/simulation/useArmBotStore.ts @@ -17,6 +17,9 @@ interface ArmBotStore { addAction: (modelUuid: string, action: RoboticArmPointSchema['actions'][number]) => void; removeAction: (modelUuid: string, actionUuid: string) => void; + updateStartPoint: (modelUuid: string, actionUuid: string, startPoint: [number, number, number] | null) => void; + updateEndPoint: (modelUuid: string, actionUuid: string, endPoint: [number, number, number] | null) => void; + setArmBotActive: (modelUuid: string, isActive: boolean) => void; incrementActiveTime: (modelUuid: string, incrementBy: number) => void; @@ -106,6 +109,30 @@ export const useArmBotStore = create<ArmBotStore>()( }); }, + updateStartPoint: (modelUuid, actionUuid, startPoint) => { + set((state) => { + const armBot = state.armBots.find(a => a.modelUuid === modelUuid); + if (armBot) { + const action = armBot.point.actions.find(a => a.actionUuid === actionUuid); + if (action) { + action.process.startPoint = startPoint; + } + } + }); + }, + + updateEndPoint: (modelUuid, actionUuid, endPoint) => { + set((state) => { + const armBot = state.armBots.find(a => a.modelUuid === modelUuid); + if (armBot) { + const action = armBot.point.actions.find(a => a.actionUuid === actionUuid); + if (action) { + action.process.endPoint = endPoint; + } + } + }); + }, + setArmBotActive: (modelUuid, isActive) => { set((state) => { const armBot = state.armBots.find(a => a.modelUuid === modelUuid); diff --git a/app/src/store/simulation/useProductStore.ts b/app/src/store/simulation/useProductStore.ts index 41a13f6..81feb7f 100644 --- a/app/src/store/simulation/useProductStore.ts +++ b/app/src/store/simulation/useProductStore.ts @@ -48,6 +48,11 @@ type ProductsStore = { updates: Partial<TriggerSchema> ) => void; + // Renaming functions + renameProduct: (productId: string, newName: string) => void; + renameAction: (actionUuid: string, newName: string) => void; + renameTrigger: (triggerUuid: string, newName: string) => void; + // Helper functions getProductById: (productId: string) => { productName: string; productId: string; eventsData: EventsSchema[] } | undefined; }; @@ -331,6 +336,84 @@ export const useProductStore = create<ProductsStore>()( }); }, + // Renaming functions + renameProduct: (productId, newName) => { + set((state) => { + const product = state.products.find(p => p.productId === productId); + if (product) { + product.productName = newName; + } + }); + }, + + renameAction: (actionUuid, newName) => { + set((state) => { + for (const product of state.products) { + for (const event of product.eventsData) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.action && point.action.actionUuid === actionUuid) { + point.action.actionName = newName; + return; + } + } + } else if ('point' in event) { + const point = (event as any).point; + if ('action' in point && point.action.actionUuid === actionUuid) { + point.action.actionName = newName; + return; + } else if ('actions' in point) { + const action = point.actions.find((a: any) => a.actionUuid === actionUuid); + if (action) { + action.actionName = newName; + return; + } + } + } + } + } + }); + }, + + renameTrigger: (triggerUuid, newName) => { + set((state) => { + for (const product of state.products) { + for (const event of product.eventsData) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.action && 'triggers' in point.action) { + const trigger = point.action.triggers.find(t => t.triggerUuid === triggerUuid); + if (trigger) { + trigger.triggerName = newName; + return; + } + } + } + } else if ('point' in event) { + const point = (event as any).point; + if ('action' in point && 'triggers' in point.action) { + const trigger = point.action.triggers.find((t: any) => t.triggerUuid === triggerUuid); + if (trigger) { + trigger.triggerName = newName; + return; + } + } else if ('actions' in point) { + for (const action of point.actions) { + if ('triggers' in action) { + const trigger = action.triggers.find((t: any) => t.triggerUuid === triggerUuid); + if (trigger) { + trigger.triggerName = newName; + return; + } + } + } + } + } + } + } + }); + }, + // Helper functions getProductById: (productId) => { return get().products.find(p => p.productId === productId); diff --git a/app/src/store/simulation/useSimulationStore.ts b/app/src/store/simulation/useSimulationStore.ts index 2d63802..9c0fc00 100644 --- a/app/src/store/simulation/useSimulationStore.ts +++ b/app/src/store/simulation/useSimulationStore.ts @@ -22,4 +22,28 @@ export const useSelectedEventSphere = create<SelectedEventSphereState>()( }); }, })) +); + +interface SelectedProductState { + selectedProduct: { productId: string; productName: string }; + setSelectedProduct: (productId: string, productName: string) => void; + clearSelectedProduct: () => void; +} + +export const useSelectedProduct = create<SelectedProductState>()( + immer((set) => ({ + selectedProduct: { productId: '', productName: '' }, + setSelectedProduct: (productId, productName) => { + set((state) => { + state.selectedProduct.productId = productId; + state.selectedProduct.productName = productName; + }); + }, + clearSelectedProduct: () => { + set((state) => { + state.selectedProduct.productId = ''; + state.selectedProduct.productName = ''; + }); + }, + })) ); \ No newline at end of file