feat: add ComparisonResult and EnergyUsage components with performance metrics and energy usage charts

This commit is contained in:
Nalvazhuthi 2025-05-27 18:16:11 +05:30
parent 640505a7f7
commit d3048d7ef1
14 changed files with 733 additions and 44 deletions

View File

@ -1,24 +1,19 @@
<!DOCTYPE html>
<html lang="en" data-theme="light">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, user-scalable=no"
/>
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Web site created using create-react-app" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
@ -27,13 +22,14 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>Dwinzo (beta)</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<div id="root-over"></div>
<!--
<title>Dwinzo (beta)</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<div id="root-over"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
@ -43,5 +39,6 @@
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
</body>
</html>

View File

@ -1279,3 +1279,59 @@ export const FinishEditIcon = () => {
</svg>
);
};
export const PerformanceIcon = () => {
return (
<svg
width="22"
height="18"
viewBox="0 0 22 18"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M11.1484 1.16797C16.2895 1.16797 20.4921 5.3714 20.4922 10.5117C20.4922 12.7821 19.6669 14.8349 18.3467 16.4316L18.1807 16.2656C19.3378 14.8012 20.1179 12.9631 20.2236 10.9746L20.2354 10.5107C20.2364 9.46665 20.0573 8.43143 19.707 7.4502L19.5469 7.03223C19.1476 6.06682 18.5848 5.17876 17.8848 4.40625L17.5771 4.08301C16.733 3.23884 15.7302 2.56951 14.627 2.11328C13.524 1.65716 12.342 1.42257 11.1484 1.42383C10.1041 1.42277 9.06837 1.60175 8.08691 1.95215L7.66992 2.11328C6.70451 2.51257 5.81644 3.07536 5.04395 3.77539L4.71973 4.08398C3.98113 4.8228 3.37584 5.68237 2.93066 6.625L2.75 7.03418C2.3509 7.99977 2.12214 9.02598 2.07227 10.0674L2.06152 10.5127C2.06152 12.7338 2.80096 14.7019 4.10059 16.2803L3.93652 16.4443C2.58335 14.8587 1.80469 12.8058 1.80469 10.5117C1.80479 5.37146 6.00742 1.16807 11.1484 1.16797ZM10.8008 11.2383L10.3887 10.8076L13.8027 7.81543L10.8008 11.2383Z"
stroke="url(#paint0_linear_1736_988)"
stroke-width="1.80917"
stroke-linecap="round"
/>
<defs>
<linearGradient
id="paint0_linear_1736_988"
x1="11.1486"
y1="0.263672"
x2="11.1486"
y2="17.742"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="#6F42C1" />
<stop offset="1" stop-color="#B392F0" />
</linearGradient>
</defs>
</svg>
);
};
export const GreenTickIcon = () => {
return (
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7.8265 15.1051C11.9138 15.1051 15.2272 11.7998 15.2272 7.72249C15.2272 3.64517 11.9138 0.339844 7.8265 0.339844C3.73919 0.339844 0.425781 3.64517 0.425781 7.72249C0.425781 11.7998 3.73919 15.1051 7.8265 15.1051Z"
fill="#14CA44"
/>
<path
d="M4.85742 7.20505L6.91318 9.25578L10.3394 5.83789"
stroke="white"
stroke-width="2.45534"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
);
};

View File

@ -115,16 +115,21 @@ const Design = () => {
<div className="element-container">
<div className="display-element">
<ChartComponent
type={selectedChartId?.type ?? "bar"}
title={selectedChartId?.title ?? "Chart"}
data={{
labels: selectedChartId?.data?.labels ?? defaultChartData.labels,
datasets: selectedChartId?.data?.datasets?.length
? selectedChartId.data.datasets
: defaultChartData.datasets,
}}
/>
{selectedChartId ? (
<ChartComponent
type={selectedChartId?.type ?? "bar"}
title={selectedChartId?.title ?? "Chart"}
data={{
labels:
selectedChartId?.data?.labels ?? defaultChartData.labels,
datasets: selectedChartId?.data?.datasets?.length
? selectedChartId.data.datasets
: defaultChartData.datasets,
}}
/>
) : (
"No Preview"
)}
</div>
<div className="name-wrapper">

View File

@ -70,6 +70,7 @@ const ComparePopUp: React.FC<ComparePopUpProps> = ({ onClose }) => {
<InfoIcon /> Save this version and proceed.
</div>
</div>
</div>
);
};

View File

@ -20,7 +20,7 @@ interface CompareLayoutProps {
const CompareLayOut: React.FC<CompareLayoutProps> = ({ dummyLayouts }) => {
const { products } = useProductStore();
console.log('products: ', products);
console.log("products: ", products);
const [width, setWidth] = useState("50vw");
const [isResizing, setIsResizing] = useState(false);
const [showLayoutDropdown, setShowLayoutDropdown] = useState(false);

View File

@ -0,0 +1,74 @@
import React from "react";
import PerformanceResult from "./result-card/PerformanceResult";
import EnergyUsage from "./result-card/EnergyUsage";
const ComparisonResult = () => {
return (
<div className="compare-result-container">
<div className="header">Performance Comparison</div>
<div className="compare-result-wrapper">
<EnergyUsage />
<div className="throughPutCard-container comparisionCard">
<h4>Throughput (units/hr)</h4>
<div className="layers-wrapper">
<div className="layer-wrapper">
<div className="key">Layout 1</div>
<div className="value">500/ hr</div>
</div>
<div className="layer-wrapper">
<div className="key">Layout 2</div>
<div className="value">550/ hr</div>
</div>
</div>
<div className="chart"></div>
</div>
<div className="cycle-time-container comparisionCard">
<div className="cycle-main">
<div className="cycle-header">Cycle Time</div>
<div className="layers-wrapper">
<div className="layers">
<div className="layer-name">Layout 1</div>
<div className="layer-time">120 Sec</div>
<div className="layer-profit">
<span></span>19.6%
</div>
</div>
<div className="layers">
<div className="layer-name">Layout 2</div>
<div className="layer-time">110 Sec</div>
<div className="layer-profit">
<span></span>19.6%1.6%
</div>
</div>
</div>
</div>
</div>
<div className="overallDowntime-container comparisionCard">
<div className="overallDowntime-header">Overall Downtime</div>
<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>
<div className="overallScrapRate comparisionCard">
<div className="overallScrapRate-header">Overall Scrap Rate</div>
<div className="overallScrapRate-value">
<div className="overallScrapRate-label">Layout 1</div>
<div className="overallScrapRate-key">Total scrap produced by</div>
<div className="overallScrapRate-value">2.7 ton</div>
</div>
</div>
<PerformanceResult />
</div>
</div>
);
};
export default ComparisonResult;

View File

@ -0,0 +1,104 @@
import React from "react";
import { Line } from "react-chartjs-2";
import {
Chart as ChartJS,
LineElement,
PointElement,
CategoryScale,
LinearScale,
Tooltip,
Legend,
} from "chart.js";
ChartJS.register(
LineElement,
PointElement,
CategoryScale,
LinearScale,
Tooltip,
Legend
);
const EnergyUsage = () => {
const data = {
labels: ["Mon", "Tue", "Wed", "Thu", "Fri"],
datasets: [
{
label: "Simulation 1",
data: [400, 600, 450, 1000, 1000],
borderColor: "#6a0dad",
fill: false,
tension: 0.5, // More curved line
pointRadius: 0, // Remove point indicators
},
{
label: "Simulation 2",
data: [300, 500, 700, 950, 1100],
borderColor: "#b19cd9",
fill: false,
tension: 0.5,
pointRadius: 0,
},
],
};
const options = {
responsive: true,
plugins: {
legend: {
display: false, // Hide legend
},
tooltip: {
enabled: false, // Hide tooltips
},
},
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">Simulation 1</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">Simulation 2</div>
<div className="value">97%</div>
</div>
</div>
</div>
<div className="chart">
<Line data={data} options={options} />
</div>
</div>
);
};
export default EnergyUsage;

View File

@ -0,0 +1,63 @@
import React from "react";
import {
GreenTickIcon,
PerformanceIcon,
TickIcon,
} from "../../../icons/ExportCommonIcons";
const PerformanceResult = () => {
return (
<div className="performanceResult-wrapper comparisionCard">
<div className="header">
<div className="icon">
<PerformanceIcon />
</div>
<div className="head">Performance result</div>
</div>
<div className="metrics-container">
<div className="metrics-left">
<div className="metric">
<div className="metric-label">
Success rate{" "}
<span>
<GreenTickIcon />
</span>
</div>
<div className="metric-value">98%</div>
</div>
<div className="label">Environmental impact</div>
</div>
<div className="metrics-right">
<div className="metric-wrapper">
<div className="metric-label">Waste generation</div>
<div className="metric">
<div className="metric-icon">I</div>
<div className="metric-value">0.5%</div>
</div>
</div>
<div className="metric-wrapper">
<div className="metric-label">Risk management</div>
<div className="metric">
<div className="metric-icon">I</div>
<div className="metric-value">0.1%</div>
</div>
</div>
<div className="metric-wrapper">
<div className="metric">
<div className="metric-icon">I</div>
<div className="metric-value">0.5%</div>
</div>
</div>
</div>
</div>
<div className="simulation-tag">Simulation 1</div>
</div>
);
};
export default PerformanceResult;

View File

@ -195,10 +195,9 @@ const ProductionCapacity: React.FC<ProductionCapacityProps> = ({
}
}, [chartMeasurements, chartDuration, widgetName]);
useEffect(() => { }, [rotation]);
useEffect(() => {}, [rotation]);
return (
<Html
// data
position={position}
@ -222,8 +221,9 @@ const ProductionCapacity: React.FC<ProductionCapacityProps> = ({
}}
>
<div
className={`productionCapacity-wrapper card ${selectedChartId?.id === id ? "activeChart" : ""
}`}
className={`productionCapacity-wrapper card ${
selectedChartId?.id === id ? "activeChart" : ""
}`}
onClick={() => setSelectedChartId({ id: id, type: type })}
onContextMenu={onContextMenu}
style={{

View File

@ -40,6 +40,7 @@ import VersionSaved from "../components/layout/sidebarRight/versionHisory/Versio
import SimulationPlayer from "../components/ui/simulation/simulationPlayer";
import { useProductStore } from "../store/simulation/useProductStore";
import ThreadChat from "../components/ui/collaboration/ThreadChat";
import ComparisonResult from "../components/ui/compareVersion/ComparisonResult";
const Project: React.FC = () => {
let navigate = useNavigate();
@ -180,6 +181,7 @@ const Project: React.FC = () => {
/>
</div>
<CompareLayOut dummyLayouts={dummyLayouts} />
{true && <ComparisonResult />}
</>
)}
<VersionSaved />

View File

@ -189,8 +189,242 @@
}
}
}
}
.compare-result-container {
display: flex;
flex-direction: column;
gap: 6px;
position: fixed;
bottom: 40px;
width: 100%;
min-height: 200px;
z-index: 10;
background: var(--background-color-secondary);
backdrop-filter: blur(20px);
padding: 18px 8px;
.header {
width: fit-content;
background-color: var(--background-color-solid);
color: var(--background-color-accent);
padding: 6px 10px;
border-radius: 6px;
}
.compare-result-wrapper {
display: flex;
gap: 12px;
.comparisionCard {
position: relative;
flex: 1;
width: auto;
max-height: 200px;
background: var(--background-color);
outline: 1px solid var(--border-color);
outline-offset: -1px;
border-radius: 12px;
padding: 8px 12px;
overflow: hidden;
}
.performanceResult-wrapper {
min-width: 328px;
flex: 0;
position: relative;
padding-right: 65px;
.header {
display: flex;
gap: 12px;
align-items: center;
}
.metrics-container {
display: flex;
gap: 12px;
height: 100%;
.metrics-left {
display: flex;
flex-direction: column;
justify-content: space-around;
height: 100%;
.metric {
.metric-label {
display: flex;
align-items: center;
gap: 6px;
span {
display: flex;
}
}
.metric-value {
padding-top: 6px;
font-size: var(--font-size-xlarge);
color: var(--background-color-accent);
font-weight: 600;
}
}
.label {
padding-bottom: 68px;
}
}
.metrics-right {
height: fit-content;
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-template-rows: repeat(2, 1fr);
gap: 2px;
overflow: visible;
margin: auto 0;
.metric-wrapper {
position: relative;
width: 64px;
height: 50px;
overflow: visible; // allow content like labels to overflow
&:nth-child(1) {
&::after {
content: "";
position: absolute;
top: -100%;
left: 50%;
width: 100%; // Required for visible shape
height: 40px;
background-color: #B7B7C6;
// Custom polygon shape (adjust if needed)
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;
}
}
.metric-label {
position: absolute;
top: 0px;
left: 0%;
white-space: nowrap;
transform: translate(-50%, -50%);
font-size: 10px;
z-index: 1;
}
.metric-wrapper {
&:nth-child(1) {
.metric-label {
top: -57%;
left: 220%;
}
}
&:nth-child(2) {
.metric-label {
white-space: normal;
width: 50px;
// top: -50%;
left: 230%;
}
}
}
.metric {
width: 100%;
height: 100%;
position: relative;
display: flex;
justify-content: center;
align-items: center;
&::after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: var(--background-color, wheat);
clip-path: 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));
// backdrop-filter: blur(20px);
// filter: drop-shadow(30px 10px 4px #4444dd);
z-index: 0;
}
// Content stays above the shape
>* {
position: relative;
z-index: 1;
}
}
.metric-wrapper:nth-child(2) {
grid-column-start: 1;
grid-row-start: 2;
}
.metric-wrapper:nth-child(3) {
grid-row: span 2 / span 2;
grid-column-start: 2;
grid-row-start: 1;
margin-top: 40%;
left: -16px;
position: relative;
}
}
}
.simulation-tag {
background: var(--background-color-button);
color: var(--icon-default-color-active);
position: absolute;
bottom: 0;
right: 0;
padding: 10px 5px;
border-radius: 12px 0 0 0;
}
}
}
}
@keyframes slideInFromRight {
from {
transform: translateX(100%);
@ -210,4 +444,151 @@
// transition: padding 0.3s ease;
// }
// }
// }
.energy-usage {
position: relative;
.energy-usage-wrapper {
h4 {
font-weight: 600;
}
.value {
padding-top: 25px;
font-size: var(--font-size-xxxlarge);
color: var(--background-color-accent);
}
}
.simulation-details {
position: absolute;
bottom: 12px;
right: 12px;
.simulation-wrapper {
display: flex;
align-items: center;
gap: 6px;
.icon {
width: 20px;
height: 20px;
border-radius: 50%;
background-color: var(--background-color-accent);
}
}
}
.chart {
width: 90%;
position: absolute;
top: 10px;
left: 0;
}
}
.throughPutCard-container {
.layers-wrapper {
padding: 20px 10px;
height: 100%;
width: 100%;
display: flex;
justify-content: space-between;
.layer-wrapper {
display: flex;
flex-direction: column;
&:last-child {
justify-content: end;
}
}
}
.chart {
height: 90%;
position: absolute;
bottom: 0;
left: 0;
}
}
.cycle-time-container {
.cycle-main {
display: flex;
justify-content: space-between;
height: 100%;
.layers-wrapper {
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
.layers {
display: flex;
flex-direction: column;
gap: 4px;
.layer-name {
color: var(--background-color-accent);
}
.layer-time {
font-size: var(--font-size-large);
}
.layer-profit {
color: #14CA44;
text-align: end;
span {
color: #14CA44;
}
}
}
}
}
}
.overallDowntime-container {
.totalDownTime {
background: var(--background-color-secondary);
backdrop-filter: blur(20px);
border-radius: 12px;
width: fit-content;
display: flex;
justify-content: space-between;
align-items: center;
gap: 20px;
padding: 8px 10px;
margin: 44px 0;
.totalDownTime-right {
display: flex;
flex-direction: column;
gap: 6px;
}
.totalDownTime-left {
display: flex;
gap: 6px;
.value {
font-size: var(--font-size-xlarge);
color: var(--background-color-button);
}
}
}
}
.overallScrapRate {
.overallScrapRate-value {
display: flex;
flex-direction: column;
gap: 6px;
margin: 40px 0;
}
}

View File

@ -202,6 +202,8 @@
gap: 6px;
}
}
}

View File

@ -221,7 +221,7 @@
padding: 13px 5px;
background: var(--background-color-secondary);
border-radius: #{$border-radius-medium};
box-shadow:var(--box-shadow-light);
display: flex;
justify-content: space-between;
@ -922,6 +922,7 @@
.display-element {
width: 100%;
height: 150px;
@include flex-center;
background: var(--background-color);
backdrop-filter: blur(20px);
border-radius: 5px;

View File

@ -69,7 +69,7 @@
pointer-events: all;
transition: all 0.3s linear;
&.bottom{
&.bottom {
bottom: var(--bottomWidth);
}
@ -121,7 +121,8 @@
.zone-container.visualization-playing {
bottom: 74px;
&.bottom{
&.bottom {
bottom: var(--bottomWidth);
}
}
@ -612,7 +613,9 @@
top: 18px;
right: 5px;
transform: translate(0px, 0);
overflow: hidden;
background: var(--background-color);
backdrop-filter: blur(20px);
z-index: 10;
display: flex;