Refactor Simulations, RenameTooltip, EditWidgetOption, and RoboticArmAnimator components: streamline imports, enhance UI elements, and improve event handling logic.

This commit is contained in:
2025-05-03 11:17:14 +05:30
parent 6a79ef563c
commit b4e4bf7fb3
5 changed files with 452 additions and 416 deletions

View File

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

View File

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

View File

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

View File

@@ -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}%`,