Merge remote-tracking branch 'origin/v2-ui' into v2-compare

This commit is contained in:
Jerald-Golden-B 2025-05-28 13:34:04 +05:30
commit 3ed7c531aa
14 changed files with 984 additions and 225 deletions

View File

@ -1,17 +1,12 @@
<!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="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"
/>
<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
@ -29,6 +24,7 @@
-->
<title>Dwinzo (beta)</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
@ -44,4 +40,5 @@
To create a production bundle, use `npm run build` or `yarn build`.
-->
</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">
{selectedChartId ? (
<ChartComponent
type={selectedChartId?.type ?? "bar"}
title={selectedChartId?.title ?? "Chart"}
data={{
labels: selectedChartId?.data?.labels ?? defaultChartData.labels,
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

@ -1,4 +1,4 @@
import React, { useState, useRef, useEffect } from "react";
import React, { useState, useRef, useEffect, Suspense } from "react";
import {
CompareLayoutIcon,
LayoutIcon,
@ -9,6 +9,7 @@ import Search from "../inputs/Search";
import OuterClick from "../../../utils/outerClick";
import RegularDropDown from "../inputs/RegularDropDown";
import { useProductStore } from "../../../store/simulation/useProductStore";
import Scene from "../../../modules/scene/scene";
interface Layout {
id: number;
@ -20,7 +21,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);
@ -90,6 +91,7 @@ const CompareLayOut: React.FC<CompareLayoutProps> = ({ dummyLayouts }) => {
window.removeEventListener("mouseup", handleMouseUp);
document.body.classList.remove("resizing-active");
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isResizing]);
// Maintain proportional width on window resize
@ -120,10 +122,22 @@ const CompareLayOut: React.FC<CompareLayoutProps> = ({ dummyLayouts }) => {
ref={wrapperRef}
style={{ width }}
>
<div className="chooseLayout-container">
<div className="resizer" onMouseDown={handleStartResizing}>
<button
title="resize-canvas"
id="compare-resize-slider-btn"
className="resizer"
onMouseDown={handleStartResizing}
>
<ResizerIcon />
</button>
<div className="chooseLayout-container">
{selectedLayout && (
<div className="compare-layout-canvas-container">
{/*<Suspense fallback={null}>
<Scene />
</Suspense> */}
</div>
)}
{width !== "0px" &&
!selectedLayout && ( // Show only if no layout selected

View File

@ -0,0 +1,133 @@
import React, { useMemo } from "react";
import PerformanceResult from "./result-card/PerformanceResult";
import EnergyUsage from "./result-card/EnergyUsage";
import { Bar } from "react-chartjs-2";
const ComparisonResult = () => {
const defaultData = {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [
{
label: "Dataset",
data: [12, 19, 3, 5, 2, 3],
backgroundColor: ["#6f42c1"],
borderColor: "#b392f0",
borderWidth: 1,
},
],
};
// Memoize Chart Options
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="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-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">
<div className="vertical-chart">
<div className="layout1"></div>
<div className="layout2"></div>
</div>
</div>
</div>
</div>
<div className="overallScrapRate comparisionCard">
<div className="overallScrapRate-header">Overall Scrap Rate</div>
<div className="overallScrapRate-wrapper">
<div className="overallScrapRate-value">
<div className="overallScrapRate-label">Layout 1</div>
<div className="overallScrapRate-key">
Total scrap produced by
</div>
<div className="overallScrapRateKey-value">2.7 ton</div>
</div>
<div className="chart">
<Bar data={defaultData} options={options} />
</div>
</div>
</div>
<PerformanceResult />
</div>
</div>
);
};
export default ComparisonResult;

View File

@ -0,0 +1,108 @@
import React, { useMemo } 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 = 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">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

@ -39,6 +39,8 @@ import RegularDropDown from "../components/ui/inputs/RegularDropDown";
import VersionSaved from "../components/layout/sidebarRight/versionHisory/VersionSaved";
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();
@ -179,6 +181,7 @@ const Project: React.FC = () => {
/>
</div>
<CompareLayOut dummyLayouts={dummyLayouts} />
{true && <ComparisonResult />}
</>
)}
<VersionSaved />

View File

@ -26,9 +26,7 @@
animation: slideInFromRight 0.4s ease-out forwards;
user-select: none;
.selectLayout-wrapper {
position: absolute;
top: 100px;
right: 40px;
@ -38,14 +36,6 @@
}
}
.chooseLayout-container {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
position: relative;
.resizer {
width: 32px;
height: 32px;
@ -62,9 +52,23 @@
cursor: ew-resize;
transition: transform 0.1s ease;
z-index: 10;
}
.chooseLayout-container {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
position: relative;
overflow: hidden;
.compare-layout-canvas-container {
position: absolute;
height: 100vh;
width: 100vw;
top: 0;
right: 0;
}
.chooseLayout-wrapper {
@ -83,10 +87,8 @@
text-align: center;
svg {
width: 100%;
}
}
.value {
@ -108,12 +110,8 @@
transition: all 0.2s ease;
&:hover {
transform: translateY(-1px);
}
}
.displayLayouts-container {
@ -149,7 +147,6 @@
.layouts-container {
.layout {
padding: 6px 0;
}
@ -168,24 +165,236 @@
.layout {
color: var(--text-button-color) !important;
}
svg {
path {
fill: var(--text-button-color) !important;
}
}
}
}
}
}
}
}
}
.layout {
.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) {
.metric-label {
top: -57%;
left: 220%;
}
&::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;
}
&:nth-child(2) {
grid-column-start: 1;
grid-row-start: 2;
.metric-label {
white-space: normal;
width: 50px;
left: 230%;
}
}
&:nth-child(3) {
grid-row: span 2 / span 2;
grid-column-start: 2;
grid-row-start: 1;
margin-top: 40%;
left: -16px;
position: relative;
}
}
.metric-label {
position: absolute;
top: 0px;
left: 0%;
white-space: nowrap;
transform: translate(-50%, -50%);
font-size: 10px;
z-index: 1;
}
.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));
z-index: 0;
}
// Content stays above the shape
> * {
position: relative;
z-index: 1;
}
}
}
}
.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;
}
}
}
@ -203,11 +412,175 @@
}
}
.energy-usage {
position: relative;
// body.compare-layout-open {
// main {
// padding-right: 10px;
.energy-usage-wrapper {
h4 {
font-weight: 600;
}
// transition: padding 0.3s ease;
// }
// }
.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-wrapper {
display: flex;
.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;
.totalDownTime-right {
display: flex;
flex-direction: column;
gap: 6px;
}
.totalDownTime-left {
display: flex;
align-items: center;
gap: 6px;
.value {
font-size: var(--font-size-xlarge);
color: var(--background-color-button);
}
}
}
.chart {
width: 30%;
position: relative;
}
}
}
.overallScrapRate {
.overallScrapRate-wrapper {
display: flex;
.overallScrapRate-value {
width: 50%;
display: flex;
flex-direction: column;
gap: 6px;
margin: 40px 0;
.overallScrapRate-key {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.chart {
width: 50%;
position: relative;
}
}
}

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

@ -121,6 +121,7 @@
.zone-container.visualization-playing {
bottom: 74px;
&.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;