feat: Refactor simulation components and enhance product management with new features
This commit is contained in:
parent
d53ef429c8
commit
85515c6cd3
|
@ -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"} />
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
|
@ -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 />
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 = '';
|
||||
});
|
||||
},
|
||||
}))
|
||||
);
|
Loading…
Reference in New Issue