Refactor Simulations, RenameTooltip, EditWidgetOption, and RoboticArmAnimator components: streamline imports, enhance UI elements, and improve event handling logic.
This commit is contained in:
@@ -1,15 +1,15 @@
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import React, { useRef, useState } 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 {
|
||||
useSelectedAsset,
|
||||
useSelectedProduct,
|
||||
useSelectedAsset,
|
||||
useSelectedProduct,
|
||||
} from "../../../../store/simulation/useSimulationStore";
|
||||
import { useProductStore } from "../../../../store/simulation/useProductStore";
|
||||
import { generateUUID } from "three/src/math/MathUtils";
|
||||
@@ -22,206 +22,222 @@ import { upsertProductOrEventApi } from "../../../../services/simulation/UpsertP
|
||||
import { deleteProductApi } from "../../../../services/simulation/deleteProductApi";
|
||||
|
||||
interface Event {
|
||||
pathName: string;
|
||||
pathName: string;
|
||||
}
|
||||
|
||||
interface ListProps {
|
||||
val: Event;
|
||||
val: Event;
|
||||
}
|
||||
|
||||
const List: React.FC<ListProps> = ({ val }) => {
|
||||
return (
|
||||
<div className="process-container">
|
||||
<div className="value">{val.pathName}</div>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div className="process-container">
|
||||
<div className="value">{val.pathName}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Simulations: React.FC = () => {
|
||||
const productsContainerRef = useRef<HTMLDivElement>(null);
|
||||
const { products, addProduct, removeProduct, renameProduct, addEvent, removeEvent, } = useProductStore();
|
||||
const { selectedProduct, setSelectedProduct } = useSelectedProduct();
|
||||
const { getEventByModelUuid } = useEventsStore();
|
||||
const { selectedAsset, clearSelectedAsset } = useSelectedAsset();
|
||||
const email = localStorage.getItem('email')
|
||||
const organization = (email!.split("@")[1]).split(".")[0];
|
||||
const [openObjects, setOpenObjects] = useState(true);
|
||||
const productsContainerRef = useRef<HTMLDivElement>(null);
|
||||
const {
|
||||
products,
|
||||
addProduct,
|
||||
removeProduct,
|
||||
renameProduct,
|
||||
addEvent,
|
||||
removeEvent,
|
||||
} = useProductStore();
|
||||
const { selectedProduct, setSelectedProduct } = useSelectedProduct();
|
||||
const { getEventByModelUuid } = useEventsStore();
|
||||
const { selectedAsset, clearSelectedAsset } = useSelectedAsset();
|
||||
const email = localStorage.getItem("email");
|
||||
const organization = email!.split("@")[1].split(".")[0];
|
||||
const [openObjects, setOpenObjects] = useState(true);
|
||||
|
||||
const handleAddProduct = () => {
|
||||
const id = generateUUID();
|
||||
const name = `Product ${products.length + 1}`;
|
||||
addProduct(name, id);
|
||||
upsertProductOrEventApi({ productName: name, productId: id, organization: organization });
|
||||
};
|
||||
const handleAddProduct = () => {
|
||||
const id = generateUUID();
|
||||
const name = `Product ${products.length + 1}`;
|
||||
addProduct(name, id);
|
||||
upsertProductOrEventApi({
|
||||
productName: name,
|
||||
productId: id,
|
||||
organization: organization,
|
||||
});
|
||||
};
|
||||
|
||||
const handleRemoveProduct = (productId: string) => {
|
||||
const currentIndex = products.findIndex((p) => p.productId === productId);
|
||||
const isSelected = selectedProduct.productId === productId;
|
||||
const handleRemoveProduct = (productId: string) => {
|
||||
const currentIndex = products.findIndex((p) => p.productId === productId);
|
||||
const isSelected = selectedProduct.productId === productId;
|
||||
|
||||
const updatedProducts = products.filter((p) => p.productId !== productId);
|
||||
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("", "");
|
||||
}
|
||||
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);
|
||||
deleteProductApi(productId, organization);
|
||||
};
|
||||
removeProduct(productId);
|
||||
deleteProductApi(productId, organization);
|
||||
};
|
||||
|
||||
const handleRenameProduct = (productId: string, newName: string) => {
|
||||
renameProduct(productId, newName);
|
||||
if (selectedProduct.productId === productId) {
|
||||
setSelectedProduct(productId, newName);
|
||||
}
|
||||
};
|
||||
const handleRenameProduct = (productId: string, newName: string) => {
|
||||
renameProduct(productId, newName);
|
||||
if (selectedProduct.productId === productId) {
|
||||
setSelectedProduct(productId, newName);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveEventFromProduct = () => {
|
||||
if (selectedAsset) {
|
||||
const email = localStorage.getItem('email')
|
||||
const organization = (email!.split("@")[1]).split(".")[0];
|
||||
deleteEventDataApi({
|
||||
productId: selectedProduct.productId,
|
||||
modelUuid: selectedAsset.modelUuid,
|
||||
organization: organization
|
||||
});
|
||||
removeEvent(selectedProduct.productId, selectedAsset.modelUuid);
|
||||
clearSelectedAsset();
|
||||
}
|
||||
};
|
||||
const handleRemoveEventFromProduct = () => {
|
||||
if (selectedAsset) {
|
||||
const email = localStorage.getItem("email");
|
||||
const organization = email!.split("@")[1].split(".")[0];
|
||||
deleteEventDataApi({
|
||||
productId: selectedProduct.productId,
|
||||
modelUuid: selectedAsset.modelUuid,
|
||||
organization: organization,
|
||||
});
|
||||
removeEvent(selectedProduct.productId, selectedAsset.modelUuid);
|
||||
clearSelectedAsset();
|
||||
}
|
||||
};
|
||||
|
||||
const selectedProductData = products.find(
|
||||
(product) => product.productId === selectedProduct.productId
|
||||
);
|
||||
const selectedProductData = products.find(
|
||||
(product) => product.productId === selectedProduct.productId
|
||||
);
|
||||
|
||||
const events: Event[] = selectedProductData?.eventDatas.map((event) => ({
|
||||
pathName: event.modelName,
|
||||
const events: Event[] =
|
||||
selectedProductData?.eventDatas.map((event) => ({
|
||||
pathName: event.modelName,
|
||||
})) || [];
|
||||
|
||||
return (
|
||||
<div className="simulations-container">
|
||||
<div className="header">Simulations</div>
|
||||
<div className="add-product-container">
|
||||
<div className="actions section">
|
||||
<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 section">
|
||||
<button
|
||||
className="collapse-header-container"
|
||||
onClick={() => setOpenObjects(!openObjects)}
|
||||
>
|
||||
<div className="header">Events</div>
|
||||
<div className="arrow-container">
|
||||
<ArrowIcon />
|
||||
</div>
|
||||
</button>
|
||||
{openObjects &&
|
||||
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>
|
||||
|
||||
{selectedAsset && (
|
||||
<RenderOverlay>
|
||||
<EditWidgetOption
|
||||
options={["Add to Product", "Remove from Product"]}
|
||||
onClick={(option) => {
|
||||
if (option === "Add to Product") {
|
||||
handleAddEventToProduct({
|
||||
event: getEventByModelUuid(selectedAsset.modelUuid),
|
||||
addEvent,
|
||||
selectedProduct,
|
||||
clearSelectedAsset
|
||||
});
|
||||
} else {
|
||||
handleRemoveEventFromProduct();
|
||||
}
|
||||
}}
|
||||
return (
|
||||
<div className="simulations-container">
|
||||
<div className="header">Simulations</div>
|
||||
<div className="add-product-container">
|
||||
<div className="actions section">
|
||||
<div className="header">
|
||||
<div className="header-value">Products</div>
|
||||
<button className="add-button" onClick={handleAddProduct}>
|
||||
<AddIcon /> Add
|
||||
</button>
|
||||
</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"
|
||||
: ""
|
||||
}`}
|
||||
>
|
||||
{/* eslint-disable-next-line */}
|
||||
<div
|
||||
className="value"
|
||||
onClick={() =>
|
||||
setSelectedProduct(product.productId, product.productName)
|
||||
}
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
name="products"
|
||||
checked={selectedProduct.productId === product.productId}
|
||||
readOnly
|
||||
/>
|
||||
</RenderOverlay>
|
||||
)}
|
||||
<RenameInput
|
||||
value={product.productName}
|
||||
onRename={(newName) =>
|
||||
handleRenameProduct(product.productId, newName)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
{products.length > 1 && (
|
||||
<button
|
||||
className="remove-button"
|
||||
onClick={() => handleRemoveProduct(product.productId)}
|
||||
>
|
||||
<RemoveIcon />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<button
|
||||
className="resize-icon"
|
||||
id="action-resize"
|
||||
onMouseDown={(e: any) => handleResize(e, productsContainerRef)}
|
||||
>
|
||||
<ResizeHeightIcon />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
<div className="simulation-process section">
|
||||
<button
|
||||
className="collapse-header-container"
|
||||
onClick={() => setOpenObjects(!openObjects)}
|
||||
>
|
||||
<div className="header">Events</div>
|
||||
<div className="arrow-container">
|
||||
<ArrowIcon />
|
||||
</div>
|
||||
</button>
|
||||
{openObjects &&
|
||||
events.map((event, index) => (
|
||||
<List key={`${index}-${event.pathName}`} 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>
|
||||
|
||||
{selectedAsset && (
|
||||
<RenderOverlay>
|
||||
<EditWidgetOption
|
||||
options={["Add to Product", "Remove from Product"]}
|
||||
onClick={(option) => {
|
||||
if (option === "Add to Product") {
|
||||
handleAddEventToProduct({
|
||||
event: getEventByModelUuid(selectedAsset.modelUuid),
|
||||
addEvent,
|
||||
selectedProduct,
|
||||
clearSelectedAsset,
|
||||
});
|
||||
} else {
|
||||
handleRemoveEventFromProduct();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</RenderOverlay>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Simulations;
|
||||
|
||||
@@ -25,10 +25,8 @@ const RenameTooltip: React.FC<RenameTooltipProps> = ({ name, onSubmit }) => {
|
||||
<div
|
||||
className="rename-tool-tip"
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: `${top}px`,
|
||||
left: `${left}px`,
|
||||
zIndex: 100,
|
||||
}}
|
||||
>
|
||||
<div className="header">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect } from "react";
|
||||
import React from "react";
|
||||
import {
|
||||
useLeftData,
|
||||
useTopData,
|
||||
@@ -28,13 +28,13 @@ const EditWidgetOption: React.FC<EditWidgetOptionProps> = ({
|
||||
>
|
||||
<div className="context-menu-options">
|
||||
{options.map((option, index) => (
|
||||
<div
|
||||
<button
|
||||
className="option"
|
||||
key={index}
|
||||
key={`${index}-${option}`}
|
||||
onClick={() => onClick(option)}
|
||||
>
|
||||
{option}
|
||||
</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -64,8 +64,6 @@ const SimulationPlayer: React.FC = () => {
|
||||
|
||||
const handleMouseDown = () => {
|
||||
isDragging.current = true;
|
||||
document.addEventListener("mousemove", handleMouseMove);
|
||||
document.addEventListener("mouseup", handleMouseUp);
|
||||
};
|
||||
|
||||
const handleMouseMove = (e: MouseEvent) => {
|
||||
@@ -80,11 +78,11 @@ const SimulationPlayer: React.FC = () => {
|
||||
|
||||
const handleMouseUp = () => {
|
||||
isDragging.current = false;
|
||||
document.removeEventListener("mousemove", handleMouseMove);
|
||||
document.removeEventListener("mouseup", handleMouseUp);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener("mousemove", handleMouseMove);
|
||||
document.addEventListener("mouseup", handleMouseUp);
|
||||
return () => {
|
||||
document.removeEventListener("mousemove", handleMouseMove);
|
||||
document.removeEventListener("mouseup", handleMouseUp);
|
||||
@@ -109,24 +107,6 @@ const SimulationPlayer: React.FC = () => {
|
||||
{ name: "process 9", completed: 90 }, // 90% completed
|
||||
{ name: "process 10", completed: 30 }, // 30% completed
|
||||
];
|
||||
// Move getRandomColor out of render
|
||||
const getRandomColor = () => {
|
||||
const letters = "0123456789ABCDEF";
|
||||
let color = "#";
|
||||
for (let i = 0; i < 6; i++) {
|
||||
color += letters[Math.floor(Math.random() * 16)];
|
||||
}
|
||||
return color;
|
||||
};
|
||||
|
||||
// Store colors for each process item
|
||||
const [_, setProcessColors] = useState<string[]>([]);
|
||||
|
||||
// Generate colors on mount or when process changes
|
||||
useEffect(() => {
|
||||
const generatedColors = process.map(() => getRandomColor());
|
||||
setProcessColors(generatedColors);
|
||||
}, []);
|
||||
|
||||
const intervals = [10, 20, 30, 40, 50, 60]; // in minutes
|
||||
const totalSegments = intervals.length;
|
||||
@@ -218,7 +198,7 @@ const SimulationPlayer: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{subModule === "simulations" && (
|
||||
{subModule !== "analysis" && (
|
||||
<div className="header">
|
||||
<InfoIcon />
|
||||
{playSimulation
|
||||
@@ -281,7 +261,7 @@ const SimulationPlayer: React.FC = () => {
|
||||
const segmentProgress = (index / totalSegments) * 100;
|
||||
const isFilled = progress >= segmentProgress;
|
||||
return (
|
||||
<React.Fragment key={index}>
|
||||
<React.Fragment key={`${index}-${label}`}>
|
||||
<div className="label-dot-wrapper">
|
||||
<div className="label">{label} mins</div>
|
||||
<div
|
||||
@@ -360,6 +340,7 @@ const SimulationPlayer: React.FC = () => {
|
||||
className="process-wrapper"
|
||||
style={{ padding: expand ? "0px" : "5px 35px" }}
|
||||
>
|
||||
{/* eslint-disable-next-line */}
|
||||
<div
|
||||
className="process-container"
|
||||
ref={processWrapperRef}
|
||||
@@ -367,7 +348,7 @@ const SimulationPlayer: React.FC = () => {
|
||||
>
|
||||
{process.map((item, index) => (
|
||||
<div
|
||||
key={index}
|
||||
key={`${index}-${item.name}`}
|
||||
className="process"
|
||||
style={{
|
||||
width: `${item.completed}%`,
|
||||
|
||||
Reference in New Issue
Block a user