updated layout comparsion

This commit is contained in:
2025-09-09 16:07:07 +05:30
parent 395c5e5112
commit a47ad60813
13 changed files with 556 additions and 643 deletions

View File

@@ -9,7 +9,8 @@ import ComparisonResult from "../../ui/compareVersion/ComparisonResult";
import RegularDropDown from "../../ui/inputs/RegularDropDown";
import { useSimulationManager } from "../../../store/rough/useSimulationManagerStore";
import { useParams } from "react-router-dom";
import { validateSimulationDataApi } from "../../../services/simulation/comparison/validateSimulationDataApi";
import { calculateSimulationData } from "./functions/calculateSimulationData";
type AssetData = {
activeTime: number;
idleTime: number;
@@ -20,23 +21,6 @@ type AssetData = {
export interface CompareProduct {
productUuid: string;
productName: string;
// simulationData: {
// // costPerUnit: number;
// // workingDaysPerYear: number;
// // shiftLength: number;
// // shiftsPerDay: number;
// roiPercentage: number;
// // paybackPeriod: number;
// // totalCost: number;
// // revenueGenerated: number;
// netProfit: number;
// productionCapacity: number;
// paybackPeriod: number;
// // netLoss: number;
// machineIdleTime: number;
// machineActiveTime: number;
// throughputData: number;
// };
simulationData: {
roiPercentage: number;
netProfit: number;
@@ -48,67 +32,14 @@ export interface CompareProduct {
simulationTime?: number;
simulationCost?: number;
efficiencyScore?: number;
// totalCost: number;
// revenueGenerated: number;
// costPerUnit: number;
//workingDaysPerYear: number;
//shiftLength: number;
//shiftsPerDay: number;
};
}
const calculateSimulationData = (assets: AssetData[]) => {
let totalActiveTime = 0;
let totalIdleTime = 0;
let throughput = 0;
let productionCapacity = 0;
let simulationCost = 0;
// Cost weight per type (example values, adjust as needed)
const costWeight: Record<string, number> = {
roboticArm: 50,
machine: 100,
human: 30,
transfer: 20,
storageUnit: 10,
};
const energyUsage = assets.filter((a) => a.type === "human").reduce((sum, a) => sum + a.activeTime * 1, 0);
assets.forEach((asset) => {
totalActiveTime += asset.activeTime;
totalIdleTime += asset.idleTime;
if (asset.activeTime > 0) throughput += 1;
productionCapacity += asset.activeTime;
simulationCost += asset.activeTime * (costWeight[asset.type] || 10);
});
const machineActiveTime = assets.filter((a) => a.type === "machine").reduce((acc, a) => acc + a.activeTime, 0);
const machineIdleTime = assets.filter((a) => a.type === "machine").reduce((acc, a) => acc + a.idleTime, 0);
const simulationTime = totalActiveTime + totalIdleTime;
// --- Efficiency Score ---
// Weighted formula: lower cost + lower time => higher score
// Example formula (normalize to 0100):
const efficiencyScore = Math.max(
0,
100 -
(simulationTime / 1000) * 0.5 - // weight 0.5 for time
(simulationCost / 1000) * 0.5 // weight 0.5 for cost
);
return {
throughputData: throughput,
machineActiveTime,
machineIdleTime,
productionCapacity,
netProfit: 0, // placeholder
roiPercentage: 0, // placeholder
paybackPeriod: 0, // placeholder
simulationTime,
simulationCost,
efficiencyScore,
energyUsage,
};
};
export const createCompareProduct = (productUuid: string, productName: string, assets: AssetData[]): CompareProduct => ({
productUuid,
productName,
@@ -128,11 +59,30 @@ function ComparisonScene() {
const { projectId } = useParams();
const { setCompareProductsData } = useCompareProductDataStore();
const [shouldShowComparisonResult, setShouldShowComparisonResult] = useState(false);
const { addSimulationRecord } = useSimulationManager();
useEffect(() => {});
const handleSelectVersion = (option: string) => {
const version = versionHistory.find((version) => version.versionName === option);
if (version) {
setSelectedVersion(version);
const singleData = {
projectId: projectId,
versionId: selectedVersion?.versionId || "",
productUuid: selectedProduct?.productUuid || "",
};
validateSimulationDataApi(singleData).then((getData) => {
echo.log(getData.message);
const getSimulate = getData?.data?.existingSimulatedData;
if (!getSimulate) return;
if (!selectedVersion?.versionId || !projectId || getSimulate === undefined || !selectedProduct.productUuid) {
echo.warn("No prebacked Data found");
alert("Please run the simulation before comparing.");
return;
}
addSimulationRecord(projectId, selectedVersion?.versionId || "", selectedProduct.productUuid || "", getSimulate.data);
});
}
};
@@ -145,39 +95,33 @@ function ComparisonScene() {
versionUuid: selectedVersion.versionId,
versionName: selectedVersion.versionName,
};
const singleData = {
projectId: projectId,
versionId: selectedVersion.versionId,
productUuid: product.productUuid,
};
validateSimulationDataApi(singleData).then((getData) => {
echo.warn(getData.message);
const getSimulate = getData?.data?.existingSimulatedData;
if (!getSimulate) return;
addSimulationRecord(projectId, selectedVersion?.versionId || "", product.productUuid || "", getSimulate.data);
});
setComparisonState(data);
}
};
// useEffect(() => {
// if (mainProduct && comparisonProduct && compareProductsData.length > 1) {
// const hasMain = compareProductsData.some((val) => val.productUuid === mainProduct.productUuid);
// const hasComparison = compareProductsData.some((val) => val.productUuid === comparisonProduct.productUuid);
// if (hasMain && hasComparison && mainProduct.productUuid !== comparisonProduct.productUuid) {
// setShouldShowComparisonResult(true);
// } else {
// setShouldShowComparisonResult(false);
// }
// } else {
// setShouldShowComparisonResult(false);
// }
// }, [compareProductsData, mainProduct, comparisonProduct]);
useEffect(() => {
// const selectedProductData = getProductById(selectedProduct.productUuid);
if (mainScene && comparisonScene && selectedVersion) {
const product1 = useSimulationManager.getState().getProductById(projectId, mainScene.version.versionUuid, mainScene.product.productUuid);
console.log("product1: ", product1);
const mainVersion = useSimulationManager.getState().getProductById(projectId, mainScene.version.versionUuid, mainScene.product.productUuid);
const product2 = useSimulationManager.getState().getProductById(projectId, comparisonScene.version.versionUuid, comparisonScene.product.productUuid);
console.log("product2: ", product2);
const compareVersion = useSimulationManager.getState().getProductById(projectId, comparisonScene.version.versionUuid, comparisonScene.product.productUuid);
const compareProduct1 = createCompareProduct(product1?.productId ?? "", mainScene.product.productName, product1?.simulateData || []);
const compareProduct2 = createCompareProduct(product2?.productId ?? "", comparisonScene.product.productName, product2?.simulateData || []);
const mainVompareversion = createCompareProduct(mainVersion?.productId ?? "", mainScene.product.productName, mainVersion?.simulateData || []);
const compareProduct2 = createCompareProduct(compareVersion?.productId ?? "", comparisonScene.product.productName, compareVersion?.simulateData || []);
const comparedArray = [compareProduct1, compareProduct2];
const comparedArray = [mainVompareversion, compareProduct2];
if (product1 === undefined || product2 === undefined) {
if (mainVersion === undefined || compareVersion === undefined) {
setShouldShowComparisonResult(false);
} else if (comparedArray.length === 2) {
setCompareProductsData(comparedArray);
@@ -186,7 +130,7 @@ function ComparisonScene() {
} else {
setShouldShowComparisonResult(false);
}
}, [mainScene, comparisonScene, selectedVersion, projectId, setCompareProductsData]);
}, [mainScene, comparisonScene, selectedVersion, projectId, setCompareProductsData, simulationRecords]);
return (
<>
@@ -210,7 +154,6 @@ function ComparisonScene() {
</div>
)}
<CompareLayOut />
{shouldShowComparisonResult && !loadingProgress && <ComparisonResult />}
</>
)}

View File

@@ -0,0 +1,99 @@
type AssetData = {
activeTime: number;
idleTime: number;
type: string;
assetId: string;
};
export interface CompareProduct {
productUuid: string;
productName: string;
// simulationData: {
// // costPerUnit: number;
// // workingDaysPerYear: number;
// // shiftLength: number;
// // shiftsPerDay: number;
// roiPercentage: number;
// // paybackPeriod: number;
// // totalCost: number;
// // revenueGenerated: number;
// netProfit: number;
// productionCapacity: number;
// paybackPeriod: number;
// // netLoss: number;
// machineIdleTime: number;
// machineActiveTime: number;
// throughputData: number;
// };
simulationData: {
roiPercentage: number;
netProfit: number;
productionCapacity: number;
paybackPeriod: number;
machineIdleTime: number;
machineActiveTime: number;
throughputData: number;
simulationTime?: number;
simulationCost?: number;
efficiencyScore?: number;
};
}
export const calculateSimulationData = (assets: AssetData[]) => {
let totalActiveTime = 0;
let totalIdleTime = 0;
let throughput = 0;
let productionCapacity = 0;
let simulationCost = 0;
// Cost weight per type (example values, adjust as needed)
const costWeight: Record<string, number> = {
roboticArm: 50,
machine: 100,
human: 30,
transfer: 20,
storageUnit: 10,
};
const energyUsage = assets.filter((a) => a.type === "human").reduce((sum, a) => sum + a.activeTime * 1, 0);
assets.forEach((asset) => {
totalActiveTime += asset.activeTime;
totalIdleTime += asset.idleTime;
if (asset.activeTime > 0) throughput += 1;
productionCapacity += asset.activeTime;
simulationCost += asset.activeTime * (costWeight[asset.type] || 10);
});
const machineActiveTime = assets.filter((a) => a.type === "human").reduce((acc, a) => acc + a.activeTime, 0);
const machineIdleTime = assets.filter((a) => a.type === "machine").reduce((acc, a) => acc + a.idleTime, 0);
const simulationTime = totalActiveTime + totalIdleTime;
// --- Efficiency Score ---
// Weighted formula: lower cost + lower time => higher score
// Example formula (normalize to 0100):
const efficiencyScore = Math.max(
0,
100 -
(simulationTime / 1000) * 0.5 - // weight 0.5 for time
(simulationCost / 1000) * 0.5 // weight 0.5 for cost
);
return {
throughputData: throughput,
machineActiveTime,
machineIdleTime,
productionCapacity,
netProfit: 0, // placeholder
roiPercentage: 0, // placeholder
paybackPeriod: 0, // placeholder
simulationTime,
simulationCost,
efficiencyScore,
energyUsage,
};
};

View File

@@ -1,5 +1,5 @@
import React, { useEffect, useRef, useState } from "react";
import { AddIcon, ArrowIcon, RemoveIcon, ResizeHeightIcon } from "../../../icons/ExportCommonIcons";
import React, { useEffect, useRef, useState } from "react";
import RenameInput from "../../../ui/inputs/RenameInput";
import { handleResize } from "../../../../functions/handleResizePannel";
import { useSimulationState, useSelectedAsset } from "../../../../store/simulation/useSimulationStore";
@@ -17,7 +17,6 @@ import { useCompareStore, useIsComparing } from "../../../../store/builder/store
import { useToggleStore } from "../../../../store/ui/useUIToggleStore";
import { useParams } from "react-router-dom";
import { useSceneContext } from "../../../../modules/scene/sceneContext";
import { useSimulationManager } from "../../../../store/rough/useSimulationManagerStore";
import { validateSimulationDataApi } from "../../../../services/simulation/comparison/validateSimulationDataApi";
interface Event {
@@ -39,10 +38,9 @@ const Simulations: React.FC = () => {
const { selectedVersion } = versionStore();
const { comparePopUp, setComparePopUp } = useCompareStore();
const { setIsComparing } = useIsComparing();
const { addSimulationRecord } = useSimulationManager();
const handleSaveVersion = () => {
setIsComparing(true);
// setIsComparing(true);
setComparePopUp(false);
setToggleUI(false, false);
const singleData = {
@@ -52,9 +50,15 @@ const Simulations: React.FC = () => {
};
validateSimulationDataApi(singleData).then((getData) => {
echo.log(getData.message);
const getSimulate = getData?.data;
if (!selectedVersion?.versionId && !projectId && getSimulate === undefined && !selectedProduct.productUuid) return;
addSimulationRecord(projectId, selectedVersion?.versionId || "", selectedProduct.productUuid || "", getSimulate.data);
const getSimulate = getData?.data?.existingSimulatedData;
if (!selectedVersion?.versionId || !projectId || getSimulate === undefined || !selectedProduct.productUuid) {
echo.warn("No prebacked Data found");
alert("Please run the simulation before comparing.");
return;
} else if (getSimulate) {
setToggleUI(true, true);
setIsComparing(true);
}
});
};
@@ -149,11 +153,13 @@ const Simulations: React.FC = () => {
setProcesses(processes);
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedProduct.productUuid, products]);
useEffect(() => {
if (comparePopUp || selectedProduct.productUuid) {
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [comparePopUp]);
return (

View File

@@ -12,12 +12,11 @@ import Scene from "../../../modules/scene/scene";
import useRestStates from "../../../hooks/useResetStates";
import { getVersionHistoryApi } from "../../../services/factoryBuilder/versionControl/getVersionHistoryApi";
import { useSimulationManager } from "../../../store/rough/useSimulationManagerStore";
import { validateSimulationDataApi } from "../../../services/simulation/comparison/validateSimulationDataApi";
const CompareLayOut = () => {
const { clearComparisonState, comparisonScene, setComparisonState } = useSimulationState();
const { versionStore, productStore } = useSceneContext();
const { versionStore } = useSceneContext();
const { versionHistory, selectedVersion, setSelectedVersion, clearSelectedVersion, setVersions } = versionStore();
const { setLoadingProgress } = useLoadingProgress();
const [width, setWidth] = useState("50vw");
@@ -31,8 +30,6 @@ const CompareLayOut = () => {
const { setIsPlaying } = usePlayButtonStore();
const { projectId } = useParams();
const { resetStates } = useRestStates();
const { products } = productStore();
const { addSimulationRecord } = useSimulationManager();
useEffect(() => {
return () => {
resetStates();
@@ -58,9 +55,7 @@ const CompareLayOut = () => {
});
setVersions(versions);
})
.catch(() => {
console.error("Error fetching version history");
});
.catch(() => {});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [projectId]);
@@ -167,14 +162,16 @@ const CompareLayOut = () => {
setLoadingProgress(1);
const singleData = {
projectId: projectId,
versionId: version.versionId, // use version directly, not selectedVersion
productUuid: data[0].productUuid, // productUuid is already a string
versionId: version.versionId,
productUuid: data[0].productUuid,
};
console.log("products", products);
validateSimulationDataApi(singleData).then((getData) => {
echo.log(getData.message);
const getSimulate = getData?.data;
addSimulationRecord(projectId, selectedVersion?.versionId || "", data[0].productUuid || "", getSimulate.data);
const getSimulate = getData?.data?.existingSimulatedData;
if (!getSimulate) return;
// addSimulationRecord(projectId, version?.versionId || "", data[0].productUuid || "", getSimulate.data);
});
}
});

View File

@@ -28,14 +28,7 @@ const ComparisonResult = () => {
useEffect(() => {
if (compareProductsData.length > 0 && comparisonScene && mainScene) {
const mainProductData = compareProductsData.find((product) => product.productUuid === mainScene.product.productUuid);
const comparisonProductData = compareProductsData.find((product) => product.productUuid === comparisonScene.product.productUuid);
if (mainProductData && comparisonProductData) {
setComparedProducts([mainProductData, comparisonProductData]);
} else {
setComparedProducts([]);
}
setComparedProducts([compareProductsData[0], compareProductsData[1]]);
} else {
setComparedProducts([]);
}
@@ -122,18 +115,87 @@ const ComparisonResult = () => {
],
};
const highestProductivityProduct = (comparedProducts[0]?.simulationData?.productionCapacity ?? 0) > (comparedProducts[1]?.simulationData?.productionCapacity ?? 0) ? comparedProducts[0] : comparedProducts[1];
const highestProductivityProduct =
(comparedProducts[0]?.simulationData?.productionCapacity ?? 0) > (comparedProducts[1]?.simulationData?.productionCapacity ?? 0) ? comparedProducts[0] : comparedProducts[1];
const product1CyclePercentage = ((comparedProducts[0]?.simulationData?.machineActiveTime ?? 0) / ((compareProductsData[0]?.simulationData?.machineActiveTime ?? 0) + (compareProductsData[0]?.simulationData?.machineIdleTime ?? 0))) * 100;
const product2CyclePercentage = ((comparedProducts[1]?.simulationData?.machineActiveTime ?? 0) / ((compareProductsData[1]?.simulationData?.machineActiveTime ?? 0) + (compareProductsData[1]?.simulationData?.machineIdleTime ?? 0))) * 100;
const product1CyclePercentage =
((comparedProducts[0]?.simulationData?.machineActiveTime ?? 0) /
((compareProductsData[0]?.simulationData?.machineActiveTime ?? 0) + (compareProductsData[0]?.simulationData?.machineIdleTime ?? 0))) *
100;
const product2CyclePercentage =
((comparedProducts[1]?.simulationData?.machineActiveTime ?? 0) /
((compareProductsData[1]?.simulationData?.machineActiveTime ?? 0) + (compareProductsData[1]?.simulationData?.machineIdleTime ?? 0))) *
100;
const product1IdlePercentage = ((comparedProducts[0]?.simulationData?.machineIdleTime ?? 0) / ((compareProductsData[0]?.simulationData?.machineActiveTime ?? 0) + (compareProductsData[0]?.simulationData?.machineIdleTime ?? 0))) * 100;
const product2IdlePercentage = ((comparedProducts[1]?.simulationData?.machineIdleTime ?? 0) / ((compareProductsData[1]?.simulationData?.machineActiveTime ?? 0) + (compareProductsData[1]?.simulationData?.machineIdleTime ?? 0))) * 100;
const product1IdlePercentage =
((comparedProducts[0]?.simulationData?.machineIdleTime ?? 0) /
((compareProductsData[0]?.simulationData?.machineActiveTime ?? 0) + (compareProductsData[0]?.simulationData?.machineIdleTime ?? 0))) *
100;
const product2IdlePercentage =
((comparedProducts[1]?.simulationData?.machineIdleTime ?? 0) /
((compareProductsData[1]?.simulationData?.machineActiveTime ?? 0) + (compareProductsData[1]?.simulationData?.machineIdleTime ?? 0))) *
100;
return (
<div className="compare-result-container">
<div className="header">Performance Comparison</div>
<div className="compare-result-wrapper">
<div className="cycle-time-container comparisionCard">
<div className="cycle-main">
<h4 className="cycle-header">Human Active Time</h4>
{/* <h4 className="cycle-header">Cycle Time</h4> */}
<div className="layers-wrapper">
<div className="layers">
<div className="layer-name">{comparedProducts[0]?.productName}</div>
<div className="layer-time">{compareProductsData[0]?.simulationData.machineActiveTime} Sec</div>
<div className={`layer-change ${product1CyclePercentage >= 50 ? "profit" : "loss"}`}>
<span>{product1CyclePercentage >= 50 ? "↑" : "↓"}</span>
{(100 - product1CyclePercentage).toFixed(2)}%
</div>
</div>
<div className="layers">
<div className="layer-name">{comparedProducts[1]?.productName}</div>
<div className="layer-time">{compareProductsData[1]?.simulationData.machineActiveTime} Sec</div>
<div className={`layer-change ${product2CyclePercentage >= 50 ? "profit" : "loss"}`}>
<span>{product2CyclePercentage >= 50 ? "↑" : "↓"}</span>
{(100 - product2CyclePercentage).toFixed(2)}%
</div>
</div>
</div>
</div>
<div className="chart">
<Pie data={cycleTimePieData} options={options} />
</div>
</div>
{/* <EnergyUsage comparedProducts={comparedProducts} /> */}
<div className="cycle-time-container comparisionCard">
<div className="cycle-main">
<div className="cycle-header">Human Idle Time</div>
{/* <div className="cycle-header">Overall Downtime</div> */}
<div className="layers-wrapper">
<div className="layers">
<div className="layer-name">{comparedProducts[0]?.productName}</div>
<div className="layer-time">{compareProductsData[0]?.simulationData.machineIdleTime} Sec</div>
<div className={`layer-profit ${product1IdlePercentage >= 50 ? "profit" : "loss"}`}>
<span>{product1IdlePercentage >= 50 ? "↑" : "↓"}</span>
{(100 - product1IdlePercentage).toFixed(2)}%
</div>
</div>
<div className="layers">
<div className="layer-name">{comparedProducts[1]?.productName}</div>
<div className="layer-time">{compareProductsData[1]?.simulationData.machineIdleTime} Sec</div>
<div className={`layer-profit ${product2IdlePercentage >= 50 ? "profit" : "loss"}`}>
<span>{product2IdlePercentage >= 50 ? "↑" : "↓"}</span>
{(100 - product2IdlePercentage).toFixed(2)}%
</div>
</div>
</div>
</div>
<div className="chart">
<Pie data={downtimeData} options={options} />
</div>
</div>
<div className="throughPutCard-container comparisionCard">
<h4>Throughput (units/hr)</h4>
<div className="layers-wrapper">
@@ -151,78 +213,24 @@ const ComparisonResult = () => {
</div>
</div>
<div className="cycle-time-container comparisionCard">
<div className="cycle-main">
<h4 className="cycle-header">Cycle Time</h4>
<div className="layers-wrapper">
<div className="layers">
<div className="layer-name">{comparedProducts[0]?.productName}</div>
<div className="layer-time">{compareProductsData[0]?.simulationData.machineActiveTime} Sec</div>
<div className="layer-profit">
<span></span>
{(100 - product1CyclePercentage).toFixed(2)}%
</div>
<div className="overallDowntime-container comparisionCard">
<h4 className="overallDowntime-header">Overall Downtime</h4>
<div className="totalDownTime-wrapper">
<div className="totalDownTime">
<div className="totalDownTime-right">
<div className="totalDownTime-label">Total down time</div>
<div className="totalDownTime-subLabel">(Simulation 1)</div>
</div>
<div className="layers">
<div className="layer-name">{comparedProducts[1]?.productName}</div>
<div className="layer-time">{compareProductsData[1]?.simulationData.machineActiveTime} Sec</div>
<div className="layer-profit">
<span></span>
{(100 - product2CyclePercentage).toFixed(2)}%
</div>
<div className="totalDownTime-left">
<div className="value">17</div>
<div className="key">mins</div>
</div>
</div>
</div>
<div className="chart">
<Pie data={cycleTimePieData} options={options} />
</div>
</div>
{/* <EnergyUsage comparedProducts={comparedProducts} /> */}
<div className="cycle-time-container comparisionCard">
<div className="cycle-main">
<div className="cycle-header">Overall Downtime</div>
<div className="layers-wrapper">
<div className="layers">
<div className="layer-name">{comparedProducts[0]?.productName}</div>
<div className="layer-time">{compareProductsData[0]?.simulationData.machineIdleTime} Sec</div>
<div className="layer-profit">
<span></span>
{(100 - product1IdlePercentage).toFixed(2)}%
</div>
</div>
<div className="layers">
<div className="layer-name">{comparedProducts[1]?.productName}</div>
<div className="layer-time">{compareProductsData[1]?.simulationData.machineIdleTime} Sec</div>
<div className="layer-profit">
<span></span>
{(100 - product2IdlePercentage).toFixed(2)}%
</div>
</div>
<div className="chart">
<Bar data={downtimeData} options={options} />
</div>
</div>
<div className="chart">
<Pie data={downtimeData} options={options} />
</div>
</div>
{/*
<div className="overallDowntime-container comparisionCard">
<h4 className="overallDowntime-header">Overall Downtime</h4>
<div className="totalDownTime-wrapper">
<div className="totalDownTime">
<div className="totalDownTime-right">
<div className="totalDownTime-label">Total down time</div>
<div className="totalDownTime-subLabel">(Simulation 1)</div>
</div>
<div className="totalDownTime-left">
<div className="value">17</div>
<div className="key">mins</div>
</div>
</div>
<div className="chart">
<Bar data={downtimeData} options={options} />
</div>
</div>
</div> */}
<div className="overallScrapRate comparisionCard">
<h4 className="overallScrapRate-header">Production Capacity</h4>

View File

@@ -4,114 +4,34 @@ import { Chart as ChartJS, LineElement, PointElement, CategoryScale, LinearScale
ChartJS.register(LineElement, PointElement, CategoryScale, LinearScale, Tooltip, Legend);
// const EnergyUsage = ({comparedProducts}:any) => {
// const data = useMemo(() => {
// const randomizeData = () =>
// Array.from({ length: 5 }, () => Math.floor(Math.random() * (2000 - 300 + 1)) + 300);
// return {
// labels: ["Mon", "Tue", "Wed", "Thu", "Fri"],
// datasets: [
// {
// label: "Simulation 1",
// data: randomizeData(),
// borderColor: "#6a0dad",
// fill: false,
// tension: 0.5, // More curved line
// pointRadius: 0, // Remove point indicators
// },
// {
// label: "Simulation 2",
// data: randomizeData(),
// borderColor: "#b19cd9",
// fill: false,
// tension: 0.5,
// pointRadius: 0,
// },
// ],
// };
// }, []);
// const options = useMemo(
// () => ({
// responsive: true,
// maintainAspectRatio: false,
// plugins: {
// title: {
// display: true,
// },
// legend: {
// display: false,
// },
// },
// scales: {
// x: {
// display: false, // Hide x-axis
// grid: {
// display: false,
// },
// },
// y: {
// display: false, // Hide y-axis
// grid: {
// display: false,
// },
// },
// },
// }),
// []
// );
// return (
// <div className="comparisionCard energy-usage">
// <div className="energy-usage-wrapper">
// <h4>Energy Usage</h4>
// <p className="value">
// 2500 <span>kWh</span>
// </p>
// </div>
// <div className="simulation-details">
// <div className="simulation-wrapper sim-1">
// <div className="icon"></div>
// <div className="simulation-details-wrapper">
// <div className="label">{comparedProducts[0]?.productName}</div>
// <div className="value">98%</div>
// </div>
// </div>
// <div className="simulation-wrapper sim-2">
// <div className="icon"></div>
// <div className="simulation-details-wrapper">
// <div className="label">{comparedProducts[1]?.productName}</div>
// <div className="value">97%</div>
// </div>
// </div>
// </div>
// <div className="chart">
// <Line data={data} options={options} />
// </div>
// </div>
// );
// };
// export default EnergyUsage;
const EnergyUsage = ({ comparedProducts }: any) => {
const data = useMemo(() => {
const randomizeData = () => Array.from({ length: 5 }, () => Math.floor(Math.random() * (2000 - 300 + 1)) + 300);
console.log("randomizeData: ", randomizeData());
return {
labels: ["Mon", "Tue", "Wed", "Thu", "Fri"],
datasets: comparedProducts.map((product: any, idx: number) => ({
label: product.productName,
// use actual energyUsage instead of random data
data: Array(5).fill(product.simulationData.energyUsage),
borderColor: idx === 0 ? "#6a0dad" : "#b19cd9",
fill: false,
tension: 0.5,
pointRadius: 0,
})),
datasets: [
{
label: "Simulation 1",
data: randomizeData(),
// data: randomizeData(),
borderColor: "#6a0dad",
fill: false,
tension: 0.5, // More curved line
pointRadius: 0, // Remove point indicators
},
{
label: "Simulation 2",
data: randomizeData(),
borderColor: "#b19cd9",
fill: false,
tension: 0.5,
pointRadius: 0,
},
],
};
}, [comparedProducts]);
}, []);
const options = useMemo(
() => ({
@@ -127,13 +47,13 @@ const EnergyUsage = ({ comparedProducts }: any) => {
},
scales: {
x: {
display: false,
display: false, // Hide x-axis
grid: {
display: false,
},
},
y: {
display: false,
display: false, // Hide y-axis
grid: {
display: false,
},
@@ -148,20 +68,25 @@ const EnergyUsage = ({ comparedProducts }: any) => {
<div className="energy-usage-wrapper">
<h4>Energy Usage</h4>
<p className="value">
{comparedProducts.reduce((acc: number, p: any) => acc + (p.simulationData.energyUsage || 0), 0)} <span>kWh</span>
2500 <span>kWh</span>
</p>
</div>
<div className="simulation-details">
{comparedProducts.map((product: any, idx: number) => (
<div key={product.productUuid} className={`simulation-wrapper sim-${idx + 1}`}>
<div className="icon"></div>
<div className="simulation-details-wrapper">
<div className="label">{product.productName}</div>
<div className="value">{product.simulationData.energyUsage} kWh</div>
</div>
<div className="simulation-wrapper sim-1">
<div className="icon"></div>
<div className="simulation-details-wrapper">
<div className="label">{comparedProducts[0]?.productName}</div>
<div className="value">98%</div>
</div>
))}
</div>
<div className="simulation-wrapper sim-2">
<div className="icon"></div>
<div className="simulation-details-wrapper">
<div className="label">{comparedProducts[1]?.productName}</div>
<div className="value">97%</div>
</div>
</div>
</div>
<div className="chart">
@@ -172,3 +97,79 @@ const EnergyUsage = ({ comparedProducts }: any) => {
};
export default EnergyUsage;
// const EnergyUsage = ({ comparedProducts }: any) => {
// const data = useMemo(() => {
// return {
// labels: ["Mon", "Tue", "Wed", "Thu", "Fri"],
// datasets: comparedProducts.map((product: any, idx: number) => ({
// label: product.productName,
// // use actual energyUsage instead of random data
// data: Array(5).fill(product.simulationData.energyUsage),
// borderColor: idx === 0 ? "#6a0dad" : "#b19cd9",
// fill: false,
// tension: 0.5,
// pointRadius: 0,
// })),
// };
// }, [comparedProducts]);
// const options = useMemo(
// () => ({
// responsive: true,
// maintainAspectRatio: false,
// plugins: {
// title: {
// display: true,
// },
// legend: {
// display: false,
// },
// },
// scales: {
// x: {
// display: false,
// grid: {
// display: false,
// },
// },
// y: {
// display: false,
// grid: {
// display: false,
// },
// },
// },
// }),
// []
// );
// return (
// <div className="comparisionCard energy-usage">
// <div className="energy-usage-wrapper">
// <h4>Energy Usage</h4>
// <p className="value">
// {comparedProducts.reduce((acc: number, p: any) => acc + (p.simulationData.energyUsage || 0), 0)} <span>kWh</span>
// </p>
// </div>
// <div className="simulation-details">
// {comparedProducts.map((product: any, idx: number) => (
// <div key={product.productUuid} className={`simulation-wrapper sim-${idx + 1}`}>
// <div className="icon"></div>
// <div className="simulation-details-wrapper">
// <div className="label">{product.productName}</div>
// <div className="value">{product.simulationData.energyUsage} kWh</div>
// </div>
// </div>
// ))}
// </div>
// <div className="chart">
// <Line data={data} options={options} />
// </div>
// </div>
// );
// };
// export default EnergyUsage;

View File

@@ -1,232 +0,0 @@
import React, { useEffect, useState } from "react";
import { useSceneContext } from "../../scene/sceneContext";
import { determineExecutionMachineSequences } from "./functions/determineExecutionMachineSequences";
import { usePlayButtonStore } from "../../../store/ui/usePlayButtonStore";
import { useSimulationManager } from "../../../store/rough/useSimulationManagerStore";
import { useParams } from "react-router-dom";
import { saveSimulationDataApi } from "../../../services/simulation/comparison/saveSimulationDataApi";
interface SimulationUsageRecord {
activeTime: number;
isActive: boolean;
idleTime: number;
type: "roboticArm" | "vehicle" | "transfer" | "storageUnit" | "crane" | "human" | "machine";
assetId: string;
}
// Product → holds multiple usage records
interface ProductSimulation {
productId: string;
data: SimulationUsageRecord[];
}
// Version → holds multiple products
interface VersionSimulation {
versionId: string;
products: ProductSimulation[];
}
const SimulationHandler = () => {
const { materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, productStore, craneStore, humanStore, versionStore } = useSceneContext();
const { armBots, getArmBotById } = armBotStore();
const { vehicles, getVehicleById } = vehicleStore();
const { getConveyorById } = conveyorStore();
const { materialHistory, materials } = materialStore();
const { getProductById, selectedProduct } = productStore();
const { machines, getMachineById } = machineStore();
const { getHumanById } = humanStore();
const { getCraneById } = craneStore();
const { getStorageUnitById } = storageUnitStore();
const { isPlaying, setIsPlaying } = usePlayButtonStore();
const { resetProductRecords, addSimulationRecord } = useSimulationManager();
const { projectId } = useParams();
const COST_RATES: Record<SimulationUsageRecord["type"], number> = {
roboticArm: 5,
vehicle: 2,
transfer: 1,
storageUnit: 1,
crane: 6,
human: 4,
machine: 3,
};
const { selectedVersion } = versionStore();
// Calculate totals for one product
function calculateProductMetrics(product: ProductSimulation) {
let totalActiveTime = 0;
let totalIdleTime = 0;
let totalCost = 0;
product.data.forEach((record) => {
const resourceTime = record.activeTime + record.idleTime;
const costRate = COST_RATES[record.type] || 0;
totalActiveTime += record.activeTime;
totalIdleTime += record.idleTime;
totalCost += resourceTime * costRate;
});
return {
totalTime: totalActiveTime + totalIdleTime,
totalActiveTime,
totalIdleTime,
totalCost,
};
}
// Calculate totals for a version
function calculateVersionMetrics(version: VersionSimulation) {
return version.products.reduce(
(acc, product) => {
const metrics = calculateProductMetrics(product);
acc.totalTime += metrics.totalTime;
acc.totalActiveTime += metrics.totalActiveTime;
acc.totalIdleTime += metrics.totalIdleTime;
acc.totalCost += metrics.totalCost;
return acc;
},
{ totalTime: 0, totalActiveTime: 0, totalIdleTime: 0, totalCost: 0 }
);
}
// Efficiency score (compare across versions)
function calculateEfficiencyScores(versions: VersionSimulation[]) {
const versionMetrics = versions.map((v) => ({
versionId: v.versionId,
...calculateVersionMetrics(v),
}));
const minTime = Math.min(...versionMetrics.map((m) => m.totalTime));
const minCost = Math.min(...versionMetrics.map((m) => m.totalCost));
return versionMetrics.map((m) => {
const timeFactor = minTime / m.totalTime;
const costFactor = minCost / m.totalCost;
const efficiencyScore = 0.5 * timeFactor + 0.5 * costFactor;
return { ...m, efficiencyScore };
});
}
useEffect(() => {
let checkTimer: ReturnType<typeof setTimeout>;
if (!projectId) return;
async function checkActiveMachines() {
const currentProduct = getProductById(selectedProduct.productUuid);
let hasActiveEntity = false;
if (currentProduct) {
const executionSequences = await determineExecutionMachineSequences([currentProduct]);
if (executionSequences?.length > 0) {
executionSequences.forEach((sequence) => {
sequence.forEach((entity) => {
if (entity.type === "roboticArm") {
const roboticArm = getArmBotById(entity.modelUuid);
if (roboticArm?.isActive) {
hasActiveEntity = true;
}
}
if (entity.type === "vehicle") {
const vehicle = getVehicleById(entity.modelUuid);
if (vehicle?.isActive) {
hasActiveEntity = true;
}
}
if (entity.type === "machine") {
const machine = getMachineById(entity.modelUuid);
if (machine?.isActive) {
hasActiveEntity = true;
}
}
if (entity.type === "human") {
const human = getHumanById(entity.modelUuid);
if (human?.isActive) {
hasActiveEntity = true;
}
}
if (entity.type === "crane") {
const crane = getCraneById(entity.modelUuid);
if (crane?.isActive) {
hasActiveEntity = true;
}
}
if (entity.type === "storageUnit") {
const storageUnit = getStorageUnitById(entity.modelUuid);
if (storageUnit?.isActive) {
hasActiveEntity = true;
}
}
if (entity.type === "transfer") {
const storageUnit = getConveyorById(entity.modelUuid);
if (storageUnit?.isActive) {
hasActiveEntity = true;
}
}
});
});
}
if (materials.length === 0 && materialHistory.length >= 0 && !hasActiveEntity) {
if (!selectedVersion) return;
resetProductRecords(projectId, selectedVersion?.versionId, selectedProduct?.productUuid);
if (executionSequences?.length > 0) {
executionSequences.forEach((sequence) => {
sequence.forEach((entity) => {
const typeToGetter: Record<string, (id: string) => any> = {
roboticArm: getArmBotById,
vehicle: getVehicleById,
machine: getMachineById,
human: getHumanById,
crane: getCraneById,
storageUnit: getStorageUnitById,
transfer: getConveyorById,
};
const getter = typeToGetter[entity.type];
if (!getter) return; // skip unknown entity types
const obj = getter(entity.modelUuid);
if (!obj) return; // skip if not found
addSimulationRecord(projectId, selectedVersion?.versionId || "", selectedProduct?.productUuid, {
activeTime: obj.activeTime ?? 0,
isActive: obj.isActive ?? false,
idleTime: obj.idleTime ?? 0,
type: entity.type as "roboticArm" | "vehicle" | "machine" | "human" | "crane" | "storageUnit" | "transfer",
assetId: entity.modelUuid,
});
});
});
}
const data = {
projectId: projectId,
versionId: selectedVersion?.versionId,
productUuid: selectedProduct.productUuid,
simulateData: useSimulationManager.getState().getProductById(projectId || "", selectedVersion?.versionId || "", selectedProduct?.productUuid || "")?.simulateData,
};
const simulations = await saveSimulationDataApi(data);
echo.log("Simulation data saved successfully");
setIsPlaying(false);
}
}
}
if (isPlaying) {
checkTimer = setTimeout(() => {
checkActiveMachines();
}, 1500);
}
return () => {
if (checkTimer) clearTimeout(checkTimer);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [materials, materialHistory, selectedVersion, selectedProduct?.productUuid, isPlaying, armBots, vehicles, machines]);
return null;
};
export default SimulationHandler;

View File

@@ -0,0 +1,118 @@
import React, { useEffect, useState } from "react";
import { useSceneContext } from "../../scene/sceneContext";
import { determineExecutionMachineSequences } from "./functions/determineExecutionMachineSequences";
import { usePlayButtonStore } from "../../../store/ui/usePlayButtonStore";
import { useSimulationManager } from "../../../store/rough/useSimulationManagerStore";
import { useParams } from "react-router-dom";
import { saveSimulationDataApi } from "../../../services/simulation/comparison/saveSimulationDataApi";
const SimulationHandler = () => {
const { materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, productStore, craneStore, humanStore, versionStore } = useSceneContext();
const { armBots, getArmBotById } = armBotStore();
const { vehicles, getVehicleById } = vehicleStore();
const { getConveyorById } = conveyorStore();
const { materialHistory, materials } = materialStore();
const { getProductById, selectedProduct } = productStore();
const { machines, getMachineById } = machineStore();
const { getHumanById } = humanStore();
const { getCraneById } = craneStore();
const { getStorageUnitById } = storageUnitStore();
const { isPlaying, setIsPlaying } = usePlayButtonStore();
const { resetProductRecords, addSimulationRecord } = useSimulationManager();
const { selectedVersion } = versionStore();
const { projectId } = useParams();
useEffect(() => {
if (!projectId) return;
let checkTimer: ReturnType<typeof setTimeout>;
const typeToGetter: Record<string, (id: string) => any> = {
roboticArm: getArmBotById,
vehicle: getVehicleById,
machine: getMachineById,
human: getHumanById,
crane: getCraneById,
storageUnit: getStorageUnitById,
transfer: getConveyorById,
};
const isEntityActive = (entity: any): boolean => {
const getter = typeToGetter[entity.type];
const obj = getter?.(entity.modelUuid);
return obj?.isActive ?? false;
};
const recordEntity = (entity: any) => {
const getter = typeToGetter[entity.type];
const obj = getter?.(entity.modelUuid);
if (!obj) return;
addSimulationRecord(projectId, selectedVersion?.versionId || "", selectedProduct?.productUuid, {
activeTime: obj.activeTime ?? 0,
isActive: obj.isActive ?? false,
idleTime: obj.idleTime ?? 0,
type: entity.type as "roboticArm" | "vehicle" | "machine" | "human" | "crane" | "storageUnit" | "transfer",
assetId: entity.modelUuid,
});
};
function checkActiveMachines() {
const currentProduct = getProductById(selectedProduct.productUuid);
if (!currentProduct) return;
determineExecutionMachineSequences([currentProduct]).then((executionSequences) => {
// --- Check if any entity is active ---
let hasActiveEntity = false;
executionSequences?.forEach((sequence) =>
sequence.forEach((entity) => {
if (isEntityActive(entity)) {
hasActiveEntity = true;
}
})
);
// --- Save simulation data if idle ---
const noMaterials = materials.length === 0;
const hasHistory = materialHistory.length >= 0;
if (noMaterials && hasHistory && !hasActiveEntity) {
if (!selectedVersion) return;
resetProductRecords(projectId, selectedVersion.versionId, selectedProduct.productUuid);
executionSequences?.forEach((sequence) => sequence.forEach((entity) => recordEntity(entity)));
const data = {
projectId,
versionId: selectedVersion.versionId,
productUuid: selectedProduct.productUuid,
simulateData: useSimulationManager.getState().getProductById(projectId, selectedVersion.versionId, selectedProduct.productUuid)?.simulateData,
};
saveSimulationDataApi(data)
.then(() => {
echo.log("Simulation data saved successfully");
setIsPlaying(false);
})
.catch((err) => {
console.error("Failed to save simulation data:", err);
});
}
});
}
if (isPlaying) {
checkTimer = setTimeout(checkActiveMachines, 1500);
}
return () => {
if (checkTimer) clearTimeout(checkTimer);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [materials, materialHistory, selectedVersion, selectedProduct?.productUuid, isPlaying, armBots, vehicles, machines]);
return null;
};
export default SimulationHandler;

View File

@@ -3,10 +3,12 @@ import { useActionHandler } from "../actions/useActionHandler";
import { usePlayButtonStore, useResetButtonStore } from "../../../store/ui/usePlayButtonStore";
import { determineExecutionOrder } from "./functions/determineExecutionOrder";
import { useSceneContext } from "../../scene/sceneContext";
import SimulationHandler from "./SimulationHandler1";
import SimulationHandler from "./simulationHandler";
import { useParams } from "react-router-dom";
import { useSimulationManager } from "../../../store/rough/useSimulationManagerStore";
import { getSimulationDataApi } from "../../../services/simulation/comparison/getSimulationDataApi";
import { getProductApi } from "../../../services/simulation/products/getProductApi";
import { useIsComparing } from "../../../store/builder/store";
function Simulator() {
const { productStore, versionStore } = useSceneContext();
@@ -16,7 +18,8 @@ function Simulator() {
const { isReset } = useResetButtonStore();
const { projectId } = useParams();
const { selectedVersion } = versionStore();
const { addSimulationRecord } = useSimulationManager();
const { setIsComparing } = useIsComparing();
const { addSimulationRecords } = useSimulationManager();
useEffect(() => {
if (!isPlaying || isReset || !selectedProduct.productUuid) return;
@@ -34,28 +37,26 @@ function Simulator() {
useEffect(() => {
if (!projectId || !selectedVersion || !selectedProduct?.productUuid) return;
const fetchSimulateData = async () => {
const getData = await getSimulationDataApi(projectId, selectedVersion.versionId, selectedProduct?.productUuid);
getSimulationDataApi(projectId, selectedVersion.versionId, selectedProduct?.productUuid).then((getData) => {
getProductApi(selectedProduct.productUuid, projectId, selectedVersion.versionId).then((product) => {
if (!product) return;
const getSimulate = getData?.data;
const product = getProductById(selectedProduct.productUuid);
if (!product) return;
const products: ProductsSchema = [product];
const getSimulate = getData?.data;
if (getData.message !== "Simulated data not found" && getSimulate && getSimulate.productTimestamp === products[0]?.timestamp) {
addSimulationRecord(projectId, selectedVersion?.versionId || "", selectedProduct?.productUuid || "", getSimulate.data);
echo.log("Simulation data is up to date");
return;
} else {
}
};
fetchSimulateData();
if (getData.message !== "Simulated data not found" && getSimulate && getSimulate.productTimestamp === product?.timestamp) {
addSimulationRecords(projectId, selectedVersion?.versionId || "", selectedProduct?.productUuid || "", getSimulate.data);
echo.warn("Simulation data is up to date");
return;
} else {
setIsComparing(false);
echo.warn("Please run the simulation before comparing.");
}
});
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
}, [selectedProduct, projectId]);
return (
<>
{/* <simulationHandler/> */}
<SimulationHandler />
</>
);

View File

@@ -12,8 +12,6 @@ export const getSimulationDataApi = async (projectId: string, versionId: string,
},
});
console.log("response: ", response);
const newAccessToken = response.headers.get("x-access-token");
if (newAccessToken) {
localStorage.setItem("token", newAccessToken);

View File

@@ -2,12 +2,13 @@ let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_UR
export const getProductApi = async (
productUuid: string,
projectId: string
projectId: string,
versionId: string,
) => {
try {
const response = await fetch(
`${url_Backend_dwinzo}/api/V1/EventsByProduct?productUuid=${productUuid}&projectId=${projectId}`,
`${url_Backend_dwinzo}/api/V1/EventsByProduct?productUuid=${productUuid}&projectId=${projectId}&versionId=${versionId}`,
{
method: "GET",
headers: {

View File

@@ -25,7 +25,6 @@ interface ProjectSimulation {
projectId: string | undefined;
versions: VersionSimulation[];
}
// or same file
interface SimulationManagerStore {
simulationRecords: ProjectSimulation[];
@@ -41,52 +40,6 @@ interface SimulationManagerStore {
export const useSimulationManager = create<SimulationManagerStore>((set, get) => ({
simulationRecords: [],
// addSimulationRecord: (projectId, versionId, productId, record) =>
// set((state) => {
// const projects = state.simulationRecords.map((project) => {
// if (project.projectId !== projectId) return project;
// return {
// ...project,
// versions: project.versions.map((version) => {
// if (version.versionId !== versionId) return version;
// return {
// ...version,
// products: version.products.map((product) => (product.productId === productId ? { ...product, simulateData: [...product.simulateData, record] } : product)),
// };
// }),
// };
// });
// // If project doesn't exist, create it
// if (!state.simulationRecords.find((p) => p.projectId === projectId)) {
// projects.push({
// projectId,
// versions: [
// {
// versionId,
// products: [{ productId, simulateData: [record] }],
// },
// ],
// });
// } else {
// const project = projects.find((p) => p.projectId === projectId)!;
// if (!project.versions.find((v) => v.versionId === versionId)) {
// project.versions.push({
// versionId,
// products: [{ productId, simulateData: [record] }],
// });
// } else {
// const version = project.versions.find((v) => v.versionId === versionId)!;
// if (!version.products.find((p) => p.productId === productId)) {
// version.products.push({ productId, simulateData: [record] });
// }
// }
// }
// return { simulationRecords: projects };
// }),
addSimulationRecord: (projectId, versionId, productId, record) =>
set((state) => {
const projects = state.simulationRecords.map((project) => {

View File

@@ -305,19 +305,14 @@
background-color: #b7b7c6;
// Custom polygon shape (adjust if needed)
clip-path: polygon(96% 52%,
96% 54%,
45% 53%,
3% 100%,
0 100%,
42% 52%);
clip-path: polygon(96% 52%, 96% 54%, 45% 53%, 3% 100%, 0 100%, 42% 52%);
z-index: 0; // Behind any inner content
}
}
// Optional: content above the shape
>* {
> * {
position: relative;
z-index: 1;
}
@@ -372,19 +367,14 @@
height: 100%;
background: var(--background-color, wheat);
clipPath: polygon(25% 0%,
75% 0%,
100% 50%,
75% 100%,
25% 100%,
0% 50%);
clippath: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%);
filter: drop-shadow(0 4px 6px rgba(0, 0, 0, 0.25));
z-index: 0;
}
// Content stays above the shape
>* {
> * {
position: relative;
z-index: 1;
}
@@ -422,8 +412,6 @@
position: relative;
.energy-usage-wrapper {
.value {
padding-top: 25px;
font-size: var(--font-size-xxxlarge);
@@ -518,14 +506,46 @@
.layer-time {
font-size: var(--font-size-large);
}
.layer-profit {
color: #14ca44;
text-align: end;
span {
.layer-change {
&.profit {
color: #14ca44;
span {
color: #14ca44;
}
}
&.loss {
color: #ff0000;
span {
color: #ff0000;
}
}
text-align: end;
}
// .layer-profit {
// color: #14ca44;
// text-align: end;
// span {
// color: #14ca44;
// }
// }
.layer-profit {
&.profit {
color: #14ca44;
span {
color: #14ca44;
}
}
&.loss {
color: #ff0000;
span {
color: #ff0000;
}
}
text-align: end;
}
}
}
@@ -543,19 +563,19 @@
.overallDowntime-container {
.totalDownTime-wrapper {
display: flex;
align-items: flex-start;
gap: 4px;
.totalDownTime {
width: 70%;
background: var(--background-color-secondary);
backdrop-filter: blur(20px);
border-radius: 12px;
display: flex;
justify-content: space-between;
align-items: center;
// gap: 20px;
padding: 8px 10px;
margin: 44px 0;
margin-top: 16px;
.totalDownTime-right {
display: flex;