feat: Refactor simulation components and enhance product management with new features

This commit is contained in:
Jerald-Golden-B 2025-04-24 11:07:15 +05:30
parent d53ef429c8
commit 85515c6cd3
7 changed files with 313 additions and 135 deletions

View File

@ -72,8 +72,7 @@ const SideBarRight: React.FC = () => {
<div className="sidebar-actions-container"> <div className="sidebar-actions-container">
{/* {activeModule === "builder" && ( */} {/* {activeModule === "builder" && ( */}
<div <div
className={`sidebar-action-list ${ className={`sidebar-action-list ${subModule === "properties" ? "active" : ""
subModule === "properties" ? "active" : ""
}`} }`}
onClick={() => setSubModule("properties")} onClick={() => setSubModule("properties")}
> >
@ -83,24 +82,21 @@ const SideBarRight: React.FC = () => {
{activeModule === "simulation" && ( {activeModule === "simulation" && (
<> <>
<div <div
className={`sidebar-action-list ${ className={`sidebar-action-list ${subModule === "mechanics" ? "active" : ""
subModule === "mechanics" ? "active" : ""
}`} }`}
onClick={() => setSubModule("mechanics")} onClick={() => setSubModule("mechanics")}
> >
<MechanicsIcon isActive={subModule === "mechanics"} /> <MechanicsIcon isActive={subModule === "mechanics"} />
</div> </div>
<div <div
className={`sidebar-action-list ${ className={`sidebar-action-list ${subModule === "simulations" ? "active" : ""
subModule === "simulations" ? "active" : ""
}`} }`}
onClick={() => setSubModule("simulations")} onClick={() => setSubModule("simulations")}
> >
<SimulationIcon isActive={subModule === "simulations"} /> <SimulationIcon isActive={subModule === "simulations"} />
</div> </div>
<div <div
className={`sidebar-action-list ${ className={`sidebar-action-list ${subModule === "analysis" ? "active" : ""
subModule === "analysis" ? "active" : ""
}`} }`}
onClick={() => setSubModule("analysis")} onClick={() => setSubModule("analysis")}
> >

View File

@ -1,4 +1,4 @@
import React, { useRef, useState } from "react"; import React, { useEffect, useRef } from "react";
import { import {
AddIcon, AddIcon,
ArrowIcon, ArrowIcon,
@ -7,65 +7,81 @@ import {
} from "../../../icons/ExportCommonIcons"; } from "../../../icons/ExportCommonIcons";
import RenameInput from "../../../ui/inputs/RenameInput"; import RenameInput from "../../../ui/inputs/RenameInput";
import { handleResize } from "../../../../functions/handleResizePannel"; 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 { interface Event {
pathName: string; // Represents the name of the path pathName: string;
Children: string[]; // Represents the list of child points
} }
interface DropListProps { interface ListProps {
val: Path; // Use the Path interface for the val prop val: Event;
} }
const DropList: React.FC<DropListProps> = ({ val }) => { const List: React.FC<ListProps> = ({ val }) => {
const [openDrop, setOpenDrop] = useState(false);
return ( return (
<div className="process-container"> <div className="process-container">
<div <div className="value">
className="value"
onClick={() => {
setOpenDrop(!openDrop);
}}
>
{val.pathName} {val.pathName}
<div className={`arrow-container${openDrop ? " active" : ""}`}>
<ArrowIcon />
</div> </div>
</div> </div>
{val.Children && openDrop && (
<div className="children-drop">
{val.Children.map((child, index) => (
<div key={index} className="value">
{child}
</div>
))}
</div>
)}
</div>
); );
}; };
const Simulations: React.FC = () => { const Simulations: React.FC = () => {
const productsContainerRef = useRef<HTMLDivElement>(null); const productsContainerRef = useRef<HTMLDivElement>(null);
const [productsList, setProductsList] = useState<string[]>([]); const { products, addProduct, removeProduct, renameProduct } = useProductStore();
const [selectedItem, setSelectedItem] = useState<string>(); const { selectedProduct, setSelectedProduct } = useSelectedProduct();
const handleAddAction = () => { useEffect(() => {
setProductsList([...productsList, `Product ${productsList.length + 1}`]); if (products.length > 0 && selectedProduct.productId === '' && selectedProduct.productName === '') {
setSelectedProduct(products[0].productId, products[0].productName);
}
}, [products, selectedProduct]);
const handleAddProduct = () => {
addProduct(`Product ${products.length + 1}`, generateUUID());
}; };
const handleRemoveAction = (index: number) => { const handleRemoveProduct = (productId: string) => {
setProductsList(productsList.filter((_, i) => i !== index)); const currentIndex = products.findIndex(p => p.productId === productId);
if (selectedItem === productsList[index]) { const isSelected = selectedProduct.productId === productId;
setSelectedItem("");
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 Value = [ const selectedProductData = products.find(
{ pathName: "Path 1", Children: ["Point 1", "Point 2"] }, (product) => product.productId === selectedProduct.productId
{ pathName: "Path 2", Children: ["Point 1", "Point 2"] }, );
{ pathName: "Path 3", Children: ["Point 1", "Point 2"] },
]; const events: Event[] = selectedProductData?.eventsData.map((event, index) => ({
pathName: `${event.modelName} - ${event.type} #${index + 1}`,
})) || [];
return ( return (
<div className="simulations-container"> <div className="simulations-container">
@ -74,7 +90,7 @@ const Simulations: React.FC = () => {
<div className="actions"> <div className="actions">
<div className="header"> <div className="header">
<div className="header-value">Products</div> <div className="header-value">Products</div>
<div className="add-button" onClick={handleAddAction}> <div className="add-button" onClick={handleAddProduct}>
<AddIcon /> Add <AddIcon /> Add
</div> </div>
</div> </div>
@ -84,26 +100,34 @@ const Simulations: React.FC = () => {
style={{ height: "120px" }} style={{ height: "120px" }}
> >
<div className="list-container"> <div className="list-container">
{productsList.map((action, index) => ( {products.map((product, index) => (
<div <div
key={index} key={product.productId}
className={`list-item ${ className={`list-item ${selectedProduct.productId === product.productId ? "active" : ""}`}
selectedItem === action ? "active" : ""
}`}
> >
<div <div
className="value" className="value"
onClick={() => setSelectedItem(action)} onClick={() => setSelectedProduct(product.productId, product.productName)}
> >
<input type="radio" name="products" id="products" /> <input
<RenameInput value={action} /> type="radio"
name="products"
checked={selectedProduct.productId === product.productId}
readOnly
/>
<RenameInput
value={product.productName}
onRename={(newName) => handleRenameProduct(product.productId, newName)}
/>
</div> </div>
{products.length > 1 && (
<div <div
className="remove-button" className="remove-button"
onClick={() => handleRemoveAction(index)} onClick={() => handleRemoveProduct(product.productId)}
> >
<RemoveIcon /> <RemoveIcon />
</div> </div>
)}
</div> </div>
))} ))}
</div> </div>
@ -116,24 +140,25 @@ const Simulations: React.FC = () => {
</div> </div>
</div> </div>
</div> </div>
<div className="simulation-process"> <div className="simulation-process">
<div className="collapse-header-container"> <div className="collapse-header-container">
<div className="header">Operations</div> <div className="header">Events</div>
<div className="arrow-container"> <div className="arrow-container">
<ArrowIcon /> <ArrowIcon />
</div> </div>
</div> </div>
{Value.map((val, index) => ( {events.map((event, index) => (
<DropList key={index} val={val} /> <List key={index} val={event} />
))} ))}
</div> </div>
<div className="compare-simulations-container"> <div className="compare-simulations-container">
<div className="compare-simulations-header"> <div className="compare-simulations-header">
Need to Compare Layout? Need to Compare Layout?
</div> </div>
<div className="content"> <div className="content">
Click <span>'Compare'</span> to review and analyze the layout Click <span>'Compare'</span> to review and analyze the layout differences between them.
differences between them.
</div> </div>
<div className="input"> <div className="input">
<input type="button" value={"Compare"} className="submit" /> <input type="button" value={"Compare"} className="submit" />

View File

@ -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

View File

@ -9,6 +9,7 @@ import Materials from './materials/materials';
import Machine from './machine/machine'; import Machine from './machine/machine';
import StorageUnit from './storageUnit/storageUnit'; import StorageUnit from './storageUnit/storageUnit';
import Simulator from './simulator/simulator'; import Simulator from './simulator/simulator';
import Products from './products/products';
function Simulation() { function Simulation() {
const { events } = useEventsStore(); const { events } = useEventsStore();
@ -27,6 +28,8 @@ function Simulation() {
<Points /> <Points />
<Products />
<Materials /> <Materials />
<Conveyor /> <Conveyor />

View File

@ -17,6 +17,9 @@ interface ArmBotStore {
addAction: (modelUuid: string, action: RoboticArmPointSchema['actions'][number]) => void; addAction: (modelUuid: string, action: RoboticArmPointSchema['actions'][number]) => void;
removeAction: (modelUuid: string, actionUuid: string) => 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; setArmBotActive: (modelUuid: string, isActive: boolean) => void;
incrementActiveTime: (modelUuid: string, incrementBy: number) => 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) => { setArmBotActive: (modelUuid, isActive) => {
set((state) => { set((state) => {
const armBot = state.armBots.find(a => a.modelUuid === modelUuid); const armBot = state.armBots.find(a => a.modelUuid === modelUuid);

View File

@ -48,6 +48,11 @@ type ProductsStore = {
updates: Partial<TriggerSchema> updates: Partial<TriggerSchema>
) => void; ) => void;
// Renaming functions
renameProduct: (productId: string, newName: string) => void;
renameAction: (actionUuid: string, newName: string) => void;
renameTrigger: (triggerUuid: string, newName: string) => void;
// Helper functions // Helper functions
getProductById: (productId: string) => { productName: string; productId: string; eventsData: EventsSchema[] } | undefined; 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 // Helper functions
getProductById: (productId) => { getProductById: (productId) => {
return get().products.find(p => p.productId === productId); return get().products.find(p => p.productId === productId);

View File

@ -23,3 +23,27 @@ 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 = '';
});
},
}))
);