feat: Add dragging and rotating state management to simulation store

- Introduced `useIsDragging` and `useIsRotating` stores to manage dragging and rotating states.
- Each store maintains its own state and provides setter functions.

refactor: Enhance storage unit and vehicle stores

- Added `clearStorageUnits` and `clearvehicles` methods to clear respective stores.
- Prevent duplicate entries in `addStorageUnit` and `addVehicle` methods by checking for existing model UUIDs.

style: Update SCSS variables and improve styling consistency

- Refactored background gradient variables for better readability.
- Introduced log indication colors for better visual feedback.
- Cleaned up and organized styles in various components for improved maintainability.

chore: Remove unused ROISummary styles

- Deleted `ROISummary.scss` as it was no longer needed.

feat: Implement new analysis component styles

- Created `analysis.scss` for the new analysis component layout and styles.
- Added styles for various sections including metrics, throughput values, and progress bars.

fix: Update main styles import

- Adjusted imports in `main.scss` to reflect the new structure and removed obsolete imports.
This commit is contained in:
Nalvazhuthi
2025-05-03 11:20:31 +05:30
45 changed files with 1680 additions and 802 deletions

View File

@@ -137,7 +137,7 @@ function MachineMechanics() {
</div>
</div>
<div className="tirgger">
<Trigger />
<Trigger selectedPointData={selectedPointData as any} type= {'Machine'} />
</div>
</section>
)}

View File

@@ -280,7 +280,7 @@ function RoboticArmMechanics() {
/>
</div>
<div className="tirgger">
<Trigger />
<Trigger selectedPointData={selectedPointData as any} type= {'RoboticArm'} />
</div>
</div>
)}

View File

@@ -146,7 +146,7 @@ function StorageMechanics() {
</div>
</div>
<div className="tirgger">
<Trigger />
<Trigger selectedPointData={selectedPointData as any} type= {'StorageUnit'} />
</div>
</section>
)}

View File

@@ -235,7 +235,7 @@ function VehicleMechanics() {
</div>
</div>
<div className="tirgger">
<Trigger />
<Trigger selectedPointData={selectedPointData as any} type= {'Vehicle'} />
</div>
</section>
</>

View File

@@ -1,4 +1,5 @@
import React, { useEffect, useRef, useState } from "react";
import React, { useEffect, useMemo, useRef, useState } from "react";
import * as THREE from "three";
import {
AddIcon,
RemoveIcon,
@@ -9,6 +10,7 @@ import RenameInput from "../../../../../ui/inputs/RenameInput";
import { handleResize } from "../../../../../../functions/handleResizePannel";
import { useProductStore } from "../../../../../../store/simulation/useProductStore";
import { useSelectedProduct } from "../../../../../../store/simulation/useSimulationStore";
import { upsertProductOrEventApi } from "../../../../../../services/simulation/UpsertProductOrEventApi";
type TriggerProps = {
selectedPointData?: PointsScheme | undefined;
@@ -18,44 +20,223 @@ type TriggerProps = {
const Trigger = ({ selectedPointData, type }: TriggerProps) => {
const [currentAction, setCurrentAction] = useState<string | undefined>();
const { selectedProduct } = useSelectedProduct();
const { getActionByUuid } = useProductStore();
const { getActionByUuid, addTrigger, removeTrigger, updateTrigger, renameTrigger, getProductById } = useProductStore();
const [triggers, setTriggers] = useState<TriggerSchema[]>([]);
const [selectedTrigger, setSelectedTrigger] = useState<TriggerSchema | undefined>();
const [activeOption, setActiveOption] = useState("onComplete");
const [activeOption, setActiveOption] = useState<"onComplete" | "onStart" | "onStop" | "delay" | "onError">("onComplete");
const triggersContainerRef = useRef<HTMLDivElement>(null);
const email = localStorage.getItem('email')
const organization = (email!.split("@")[1]).split(".")[0];
useEffect(() => {
if (!selectedPointData) return;
if (!selectedPointData || !selectedProduct) return;
let actionUuid: string | undefined;
if (type === 'Conveyor' || type === 'Vehicle' || type === 'Machine' || type === 'StorageUnit') {
setCurrentAction((selectedPointData as ConveyorPointSchema).action.actionUuid);
actionUuid = (selectedPointData as ConveyorPointSchema | VehiclePointSchema | MachinePointSchema | StoragePointSchema).action?.actionUuid;
} else if (type === 'RoboticArm') {
actionUuid = (selectedPointData as RoboticArmPointSchema).actions[0]?.actionUuid;
}
}, [selectedPointData]);
setCurrentAction(actionUuid);
}, [selectedPointData, selectedProduct, type]);
const updateBackend = (
productName: string,
productId: string,
organization: string,
eventData: EventsSchema
) => {
upsertProductOrEventApi({
productName: productName,
productId: productId,
organization: organization,
eventDatas: eventData
})
}
useEffect(() => {
if (!currentAction || !selectedProduct) return;
const action = getActionByUuid(selectedProduct.productId, currentAction);
setTriggers(action?.triggers || []);
setSelectedTrigger(action?.triggers[0] || undefined);
const actionTriggers = action?.triggers || [];
setTriggers(actionTriggers);
setSelectedTrigger(actionTriggers[0]);
}, [currentAction, selectedProduct]);
const addTrigger = (): void => {
const handleAddTrigger = () => {
if (!selectedProduct || !currentAction) return;
const newTrigger: TriggerSchema = {
triggerUuid: THREE.MathUtils.generateUUID(),
triggerName: `New Trigger ${triggers.length + 1}`,
triggerType: activeOption,
delay: 0,
triggeredAsset: null
};
addTrigger(selectedProduct.productId, currentAction, newTrigger);
setSelectedTrigger(newTrigger);
};
const removeTrigger = (triggerUuid: string): void => {
const handleRemoveTrigger = (triggerUuid: string) => {
if (!selectedProduct) return;
removeTrigger(selectedProduct.productId, triggerUuid);
if (selectedTrigger?.triggerUuid === triggerUuid) {
const remainingTriggers = triggers.filter(t => t.triggerUuid !== triggerUuid);
setSelectedTrigger(remainingTriggers[0]);
}
};
const handleTriggerRename = (triggerUuid: string, newName: string) => {
if (!selectedProduct) return;
renameTrigger(selectedProduct.productId, triggerUuid, newName);
};
const handleTriggerTypeChange = (option: string) => {
if (!selectedTrigger || !selectedProduct) return;
const validTypes: Array<TriggerSchema['triggerType']> = ["onComplete", "onStart", "onStop", "delay", "onError"];
if (!validTypes.includes(option as TriggerSchema['triggerType'])) return;
setActiveOption(option as TriggerSchema['triggerType']);
updateTrigger(selectedProduct.productId, selectedTrigger.triggerUuid, {
triggerType: option as TriggerSchema['triggerType']
});
};
const triggeredModel = selectedTrigger?.triggeredAsset?.triggeredModel || { modelName: "Select Model", modelUuid: "" };
const triggeredPoint = selectedTrigger?.triggeredAsset?.triggeredPoint || { pointName: "Select Point", pointUuid: "" };
const triggeredAction = selectedTrigger?.triggeredAsset?.triggeredAction || { actionName: "Select Action", actionUuid: "" };
const modelOptions = getProductById(selectedProduct.productId)?.eventDatas || [];
const pointOptions: PointsScheme[] = useMemo(() => {
if (!triggeredModel.modelUuid) return [];
const model = modelOptions.find(m => m.modelUuid === triggeredModel.modelUuid);
if (!model) return [];
if ('points' in model) {
return (model as ConveyorEventSchema).points;
} else if ('point' in model) {
return [(model as VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema).point];
}
return [];
}, [triggeredModel.modelUuid, modelOptions]);
const actionOptions: any = useMemo(() => {
if (!triggeredPoint.pointUuid) return [];
const point = pointOptions.find((p) => p.uuid === triggeredPoint.pointUuid);
if (!point) return [];
if ('action' in point) {
const typedPoint = point as ConveyorPointSchema | VehiclePointSchema | MachinePointSchema | StoragePointSchema;
return typedPoint.action ? [typedPoint.action] : [];
} else if ('actions' in point) {
const typedPoint = point as RoboticArmPointSchema;
return typedPoint.actions;
}
return [];
}, [triggeredPoint.pointUuid, pointOptions]);
const handleModelSelect = (option: string, triggerUuid: string) => {
if (!selectedProduct) return;
const selectedModel = modelOptions.find(m => m.modelName === option);
if (!selectedModel) return;
const event = updateTrigger(selectedProduct.productId, triggerUuid, {
triggeredAsset: {
triggeredModel: {
modelName: selectedModel.modelName,
modelUuid: selectedModel.modelUuid
},
triggeredPoint: null,
triggeredAction: null
}
});
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productId,
organization,
event
);
}
};
const handlePointSelect = (option: string, triggerUuid: string) => {
if (!selectedProduct || !selectedTrigger) return;
const pointUuid = pointOptions.find(p => `Point ${p.uuid.slice(0, 5)}` === option)?.uuid;
if (!pointUuid) return;
if (selectedTrigger.triggeredAsset?.triggeredModel) {
const event = updateTrigger(selectedProduct.productId, triggerUuid, {
triggeredAsset: {
...selectedTrigger.triggeredAsset,
triggeredPoint: {
pointName: option,
pointUuid: pointUuid
},
triggeredAction: null
}
});
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productId,
organization,
event
);
}
}
};
const handleActionSelect = (option: string, triggerUuid: string) => {
if (!selectedProduct || !selectedTrigger) return;
const selectedAction = actionOptions.find((a: any) => a.actionName === option);
if (!selectedAction) return;
if (selectedTrigger.triggeredAsset?.triggeredPoint) {
const event = updateTrigger(selectedProduct.productId, triggerUuid, {
triggeredAsset: {
...selectedTrigger.triggeredAsset,
triggeredAction: {
actionName: option,
actionUuid: selectedAction.actionUuid
}
}
});
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productId,
organization,
event
);
}
}
};
return (
<div className="trigger-wrapper">
<div className="header">
<div className="title">Trigger</div>
<button
className="add-button"
onClick={addTrigger}
onClick={handleAddTrigger}
style={{ cursor: "pointer" }}
disabled={!currentAction}
>
<AddIcon /> Add
</button>
@@ -73,13 +254,19 @@ const Trigger = ({ selectedPointData, type }: TriggerProps) => {
className={`list-item ${selectedTrigger?.triggerUuid === trigger.triggerUuid ? "active" : ""}`}
onClick={() => setSelectedTrigger(trigger)}
>
<button className="value" onClick={() => { }}>
<RenameInput value={trigger.triggerName} onRename={() => { }} />
<button className="value">
<RenameInput
value={trigger.triggerName}
onRename={(newName) => handleTriggerRename(trigger.triggerUuid, newName)}
/>
</button>
{triggers.length > 1 && (
<button
className="remove-button"
onClick={() => removeTrigger(trigger.triggerUuid)}
onClick={(e) => {
e.stopPropagation();
handleRemoveTrigger(trigger.triggerUuid);
}}
>
<RemoveIcon />
</button>
@@ -95,35 +282,39 @@ const Trigger = ({ selectedPointData, type }: TriggerProps) => {
<ResizeHeightIcon />
</button>
</div>
<div className="trigger-item">
<div className="trigger-name">{selectedTrigger?.triggerName}</div>
<LabledDropdown
label="Trigger Type"
defaultOption={activeOption}
options={["onComplete", "onStart", "onStop", "delay"]}
onSelect={(option) => setActiveOption(option)}
/>
<div className="trigger-options">
{selectedTrigger && (
<div className="trigger-item">
<div className="trigger-name">{selectedTrigger.triggerName}</div>
<LabledDropdown
label="Triggered Object"
defaultOption={triggeredModel.modelName}
options={[]}
onSelect={(option) => { }}
/>
<LabledDropdown
label="Triggered Point"
defaultOption={triggeredPoint.pointName}
options={[]}
onSelect={(option) => { }}
/>
<LabledDropdown
label="Triggered Action"
defaultOption={triggeredAction.actionName}
options={[]}
onSelect={(option) => { }}
label="Trigger Type"
defaultOption={selectedTrigger.triggerType}
options={["onComplete", "onStart", "onStop", "delay", "onError"]}
onSelect={handleTriggerTypeChange}
/>
<div className="trigger-options">
<LabledDropdown
label="Triggered Object"
defaultOption={triggeredModel.modelName}
options={[...modelOptions.map((option) => (option.modelName))]}
onSelect={(option) => { handleModelSelect(option, selectedTrigger.triggerUuid) }}
/>
<LabledDropdown
label="Triggered Point"
defaultOption={triggeredPoint.pointName}
options={[...pointOptions.map((option) => (`Point ${option.uuid.slice(0, 5)}`))]}
onSelect={(option) => { handlePointSelect(option, selectedTrigger.triggerUuid) }}
/>
<LabledDropdown
label="Triggered Action"
defaultOption={triggeredAction.actionName}
options={[...actionOptions.map((option: any) => (option.actionName))]}
onSelect={(option) => { handleActionSelect(option, selectedTrigger.triggerUuid) }}
/>
</div>
</div>
</div>
)}
</div>
</div>
);

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

@@ -0,0 +1,50 @@
import React, { useState } from "react";
import { RenameIcon } from "../../icons/ContextMenuIcons";
import {
useLeftData,
useTopData,
} from "../../../store/visualization/useZone3DWidgetStore";
type RenameTooltipProps = {
name: string;
onSubmit: (newName: string) => void;
};
const RenameTooltip: React.FC<RenameTooltipProps> = ({ name, onSubmit }) => {
const [value, setValue] = useState(name);
const { top } = useTopData();
const { left } = useLeftData();
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
onSubmit(value.trim());
};
return (
<div
className="rename-tool-tip"
style={{
top: `${top}px`,
left: `${left}px`,
}}
>
<div className="header">
<div className="icon">
<RenameIcon />
</div>
<div className="name">Name</div>
</div>
<form className="input" onSubmit={handleSubmit}>
<input
type="text"
value={value}
onChange={(e) => setValue(e.target.value)}
autoFocus
/>
</form>
</div>
);
};
export default RenameTooltip;

View File

@@ -27,6 +27,7 @@ interface ZoneItem {
id: string;
name: string;
assets?: Asset[];
active?: boolean;
}
interface ListProps {
@@ -157,7 +158,7 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
{items?.map((item) => (
<React.Fragment key={`zone-${item.id}`}>
<li className="list-container">
<div className="list-item">
<div className={`list-item ${item.active ? "active" : ""}`}>
<div className="zone-header">
<button
className="value"

View File

@@ -1,4 +1,4 @@
import React, { useEffect } from "react";
import React from "react";
import {
useLeftData,
useTopData,
@@ -16,29 +16,25 @@ const EditWidgetOption: React.FC<EditWidgetOptionProps> = ({
const { top } = useTopData();
const { left } = useLeftData();
useEffect(() => {
}, [top, left]);
return (
<div
className="editWidgetOptions-wrapper"
className="context-menu-options-wrapper"
style={{
position: "absolute",
top: `${top}px`,
left: `${left}px`,
zIndex: 10000,
zIndex: 100,
}}
>
<div className="editWidgetOptions">
<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}%`,