Merge remote-tracking branch 'origin/v2-ui' into v2
This commit is contained in:
67
app/src/components/footer/Footer.tsx
Normal file
67
app/src/components/footer/Footer.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
import React from "react";
|
||||
import { HelpIcon } from "../icons/DashboardIcon";
|
||||
import {
|
||||
LogInfoIcon,
|
||||
ErrorIcon,
|
||||
WarningIcon,
|
||||
} from "../icons/ExportCommonIcons"; // Adjust path as needed
|
||||
import { useLogger } from "../ui/log/LoggerContext";
|
||||
|
||||
const getLogIcon = (type: string) => {
|
||||
switch (type) {
|
||||
case "info":
|
||||
return <LogInfoIcon />;
|
||||
case "error":
|
||||
return <ErrorIcon />;
|
||||
case "warning":
|
||||
return <WarningIcon />;
|
||||
case "log":
|
||||
default:
|
||||
return <LogInfoIcon />;
|
||||
}
|
||||
};
|
||||
|
||||
const Footer: React.FC = () => {
|
||||
const { logs, setIsLogListVisible } = useLogger();
|
||||
const lastLog = logs.length > 0 ? logs[logs.length - 1] : null;
|
||||
|
||||
return (
|
||||
<div className="footer-wrapper">
|
||||
<div className="selection-wrapper">
|
||||
<div className="selector-wrapper">
|
||||
<div className="icon"></div>
|
||||
<div className="selector">Selection</div>
|
||||
</div>
|
||||
<div className="selector-wrapper">
|
||||
<div className="icon"></div>
|
||||
<div className="selector">Rotate/Zoom</div>
|
||||
</div>
|
||||
<div className="selector-wrapper">
|
||||
<div className="icon"></div>
|
||||
<div className="selector">Pan/Context Menu</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="logs-wrapper">
|
||||
<div className="logs-detail" onClick={() => setIsLogListVisible(true)}>
|
||||
{lastLog ? (
|
||||
<>
|
||||
<span className="log-icon">{getLogIcon(lastLog.type)}</span>
|
||||
<span className="log-message">{lastLog.message}</span>
|
||||
</>
|
||||
) : (
|
||||
"No logs yet."
|
||||
)}
|
||||
</div>
|
||||
<div className="version">
|
||||
V 0.01
|
||||
<div className="icon">
|
||||
<HelpIcon />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Footer;
|
||||
@@ -458,7 +458,7 @@ export function InfoIcon() {
|
||||
>
|
||||
<path
|
||||
d="M5.46289 7.75441C5.46289 7.68342 5.47691 7.6131 5.50422 7.54758C5.53158 7.48201 5.57156 7.42254 5.62201 7.37257C5.67246 7.3226 5.73231 7.2831 5.79807 7.25636C5.86388 7.22963 5.93425 7.21619 6.00529 7.21681C6.11003 7.21873 6.21188 7.25142 6.29814 7.31089C6.38435 7.37036 6.45121 7.45398 6.49019 7.55118C6.52921 7.64843 6.53862 7.75499 6.5174 7.85757C6.49614 7.96014 6.44511 8.05417 6.37071 8.12795C6.29631 8.20168 6.20185 8.25184 6.09908 8.27219C5.99631 8.29254 5.8898 8.28212 5.79294 8.24224C5.69603 8.2024 5.61308 8.13486 5.55438 8.04808C5.49567 7.96134 5.46385 7.8592 5.46289 7.75441ZM5.63564 6.44401L5.56844 3.93842C5.56206 3.87819 5.56844 3.81729 5.58716 3.75968C5.60583 3.70207 5.63641 3.64902 5.67692 3.604C5.71743 3.55897 5.76697 3.52297 5.82227 3.49832C5.87761 3.47368 5.93751 3.46094 5.99804 3.46094C6.05862 3.46094 6.11852 3.47368 6.17387 3.49832C6.22916 3.52297 6.2787 3.55897 6.31921 3.604C6.35972 3.64902 6.3903 3.70207 6.40897 3.75968C6.42769 3.81729 6.43407 3.87819 6.42769 3.93842L6.36529 6.44401C6.36529 6.54073 6.32689 6.63356 6.25844 6.70196C6.19004 6.77036 6.09721 6.80881 6.00049 6.80881C5.90372 6.80881 5.81094 6.77036 5.74254 6.70196C5.67414 6.63356 5.63564 6.54073 5.63564 6.44401Z"
|
||||
fill="var(--icon-default-color)"
|
||||
fill="var(--text-color)"
|
||||
/>
|
||||
<path
|
||||
d="M6.00006 10.3175C8.45219 10.3175 10.4401 8.32963 10.4401 5.8775C10.4401 3.42536 8.45219 1.4375 6.00006 1.4375C3.54792 1.4375 1.56006 3.42536 1.56006 5.8775C1.56006 8.32963 3.54792 10.3175 6.00006 10.3175Z"
|
||||
@@ -471,7 +471,7 @@ export function InfoIcon() {
|
||||
);
|
||||
}
|
||||
|
||||
export function AI_Icon() {
|
||||
export function AIIcon() {
|
||||
return (
|
||||
<svg
|
||||
width="20"
|
||||
@@ -814,3 +814,136 @@ export const SpeedIcon = () => {
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export const LogListIcon = () => {
|
||||
return (
|
||||
<svg
|
||||
width="24"
|
||||
height="25"
|
||||
viewBox="0 0 24 25"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M14.5 18.25H5.5M18.5 21.7272V19.3568H17.3829C16.8245 19.3568 16.375 18.9072 16.375 18.3489V18.08C16.375 17.5216 16.8246 17.0721 17.3829 17.0721H18.5V13.7318H17.2579C16.6995 13.7318 16.25 13.2822 16.25 12.7239V12.4551C16.25 11.8967 16.6996 11.4472 17.2579 11.4472H18.5V7.98185H17.2579C16.6995 7.98185 16.25 7.5323 16.25 6.97395V6.70515C16.25 6.14675 16.6996 5.69725 17.2579 5.69725H18.5V3.27065M14.5 12.75H5.5M12 7.24995H5.5M20.25 3.25H3.75C3.1977 3.25 2.75 3.6977 2.75 4.25V20.75C2.75 21.3023 3.1977 21.75 3.75 21.75H20.25C20.8023 21.75 21.25 21.3023 21.25 20.75V4.25C21.25 3.6977 20.8023 3.25 20.25 3.25Z"
|
||||
stroke="#2B3344"
|
||||
strokeWidth="1.2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export const LogTickIcon = () => {
|
||||
return (
|
||||
<svg
|
||||
width="10"
|
||||
height="10"
|
||||
viewBox="0 0 10 10"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g clip-path="url(#clip0_3962_4223)">
|
||||
<path
|
||||
d="M5.00065 0.835938C2.70482 0.835938 0.833984 2.70677 0.833984 5.0026C0.833984 7.29844 2.70482 9.16927 5.00065 9.16927C7.29648 9.16927 9.16732 7.29844 9.16732 5.0026C9.16732 2.70677 7.29648 0.835938 5.00065 0.835938ZM6.99232 4.04427L4.62982 6.40677C4.57148 6.4651 4.49232 6.49844 4.40898 6.49844C4.32565 6.49844 4.24648 6.4651 4.18815 6.40677L3.00898 5.2276C2.88815 5.10677 2.88815 4.90677 3.00898 4.78594C3.12982 4.6651 3.32982 4.6651 3.45065 4.78594L4.40898 5.74427L6.55065 3.6026C6.67148 3.48177 6.87148 3.48177 6.99232 3.6026C7.11315 3.72344 7.11315 3.91927 6.99232 4.04427Z"
|
||||
fill="#49B841"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_3962_4223">
|
||||
<rect width="10" height="10" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export const LogInfoIcon = () => {
|
||||
return (
|
||||
<svg
|
||||
width="12"
|
||||
height="12"
|
||||
viewBox="0 0 12 12"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M0.75 2.64438V7.41438C0.75 7.92094 0.951232 8.40675 1.30943 8.76495C1.66762 9.12314 2.15344 9.32438 2.66 9.32438H3.615V10.7544L6.5 9.32438H9.365C9.87156 9.32438 10.3574 9.12314 10.7156 8.76495C11.0738 8.40675 11.275 7.92094 11.275 7.41438V2.64438C11.275 2.13781 11.0738 1.652 10.7156 1.2938C10.3574 0.935607 9.87156 0.734375 9.365 0.734375H2.66C2.15344 0.734375 1.66762 0.935607 1.30943 1.2938C0.951232 1.652 0.75 2.13781 0.75 2.64438Z"
|
||||
fill="#8E8E93"
|
||||
/>
|
||||
<path
|
||||
d="M5.04492 6.94531H6.95492"
|
||||
stroke="white"
|
||||
strokeWidth="0.955"
|
||||
strokeMiterlimit="10"
|
||||
/>
|
||||
<path
|
||||
d="M5.04492 4.07812H5.99992V6.94313"
|
||||
stroke="white"
|
||||
strokeWidth="0.955"
|
||||
strokeMiterlimit="10"
|
||||
/>
|
||||
<path
|
||||
d="M5.52539 2.65625H6.47539"
|
||||
stroke="white"
|
||||
strokeWidth="0.955"
|
||||
strokeMiterlimit="10"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export const WarningIcon = () => {
|
||||
return (
|
||||
<svg
|
||||
width="12"
|
||||
height="12"
|
||||
viewBox="0 0 12 12"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M0.75 2.64438V7.41438C0.75 7.92094 0.951232 8.40675 1.30943 8.76495C1.66762 9.12314 2.15344 9.32438 2.66 9.32438H3.615V10.7544L6.5 9.32438H9.365C9.87156 9.32438 10.3574 9.12314 10.7156 8.76495C11.0738 8.40675 11.275 7.92094 11.275 7.41438V2.64438C11.275 2.13781 11.0738 1.652 10.7156 1.2938C10.3574 0.935607 9.87156 0.734375 9.365 0.734375H2.66C2.15344 0.734375 1.66762 0.935607 1.30943 1.2938C0.951232 1.652 0.75 2.13781 0.75 2.64438Z"
|
||||
fill="#8E8E93"
|
||||
/>
|
||||
<path
|
||||
d="M5.04492 6.94531H6.95492"
|
||||
stroke="white"
|
||||
strokeWidth="0.955"
|
||||
strokeMiterlimit="10"
|
||||
/>
|
||||
<path
|
||||
d="M5.04492 4.07812H5.99992V6.94313"
|
||||
stroke="white"
|
||||
strokeWidth="0.955"
|
||||
strokeMiterlimit="10"
|
||||
/>
|
||||
<path
|
||||
d="M5.52539 2.65625H6.47539"
|
||||
stroke="white"
|
||||
strokeWidth="0.955"
|
||||
strokeMiterlimit="10"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export const ErrorIcon = () => {
|
||||
return (
|
||||
<svg
|
||||
width="12"
|
||||
height="12"
|
||||
viewBox="0 0 12 12"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M11 6C11 8.7614 8.7614 11 6 11C3.23857 11 1 8.7614 1 6C1 3.23857 3.23857 1 6 1C8.7614 1 11 3.23857 11 6ZM4.48482 4.48483C4.63126 4.33838 4.8687 4.33838 5.01515 4.48483L6 5.46965L6.9848 4.48484C7.13125 4.33839 7.3687 4.33839 7.51515 4.48484C7.6616 4.63128 7.6616 4.86872 7.51515 5.01515L6.5303 6L7.51515 6.9848C7.6616 7.13125 7.6616 7.3687 7.51515 7.51515C7.3687 7.6616 7.13125 7.6616 6.9848 7.51515L6 6.53035L5.01515 7.51515C4.86871 7.6616 4.63127 7.6616 4.48483 7.51515C4.33838 7.3687 4.33838 7.13125 4.48483 6.98485L5.46965 6L4.48482 5.01515C4.33837 4.86871 4.33837 4.63127 4.48482 4.48483Z"
|
||||
fill="#ED5342"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -125,21 +125,21 @@ export function ResetIcon() {
|
||||
>
|
||||
<path
|
||||
d="M2.54182 4.09637C3.33333 2.73422 4.80832 1.81836 6.49721 1.81836C9.02194 1.81836 11.0686 3.86506 11.0686 6.38979C11.0686 8.91452 9.02194 10.9612 6.49721 10.9612C3.97248 10.9612 1.92578 8.91452 1.92578 6.38979"
|
||||
stroke="var(--icon-default-color-active)"
|
||||
stroke="var(--text-color)"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M7.64118 6.38895C7.64118 7.02013 7.12951 7.53181 6.49833 7.53181C5.86714 7.53181 5.35547 7.02013 5.35547 6.38895C5.35547 5.75777 5.86714 5.24609 6.49833 5.24609C7.12951 5.24609 7.64118 5.75777 7.64118 6.38895Z"
|
||||
fill="var(--icon-default-color-active)"
|
||||
stroke="var(--icon-default-color-active)"
|
||||
fill="var(--text-color)"
|
||||
stroke="var(--text-color)"
|
||||
strokeWidth="0.571429"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M4.78125 4.10407H2.49554V1.81836"
|
||||
stroke="var(--icon-default-color-active)"
|
||||
stroke="var(--text-color)"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
@@ -158,7 +158,7 @@ export function PlayStopIcon() {
|
||||
>
|
||||
<path
|
||||
d="M8 2.88867V9.88867M11 2.88867V9.88867M2 3.99171V8.78562C2 9.28847 2 9.53987 2.09943 9.65627C2.1857 9.75732 2.31512 9.81092 2.44756 9.80047C2.60019 9.78847 2.77796 9.61072 3.13352 9.25517L5.5305 6.85817C5.69485 6.69382 5.777 6.61167 5.8078 6.51692C5.83485 6.43357 5.83485 6.34377 5.8078 6.26042C5.777 6.16567 5.69485 6.08352 5.5305 5.91917L3.13352 3.52219C2.77796 3.16664 2.60019 2.98886 2.44756 2.97685C2.31512 2.96643 2.1857 3.02004 2.09943 3.12105C2 3.23747 2 3.48888 2 3.99171Z"
|
||||
stroke="var(--icon-default-color-active)"
|
||||
stroke="var(--text-color)"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
@@ -177,7 +177,7 @@ export function ExitIcon() {
|
||||
>
|
||||
<path
|
||||
d="M2.08203 6.34311L5.03647 3.38867M2.08203 6.34311L5.03647 9.29755M2.08203 6.34311H5.9228C7.22276 6.34334 9.34995 7.05241 10.7681 9.88867"
|
||||
stroke="var(--icon-default-color-active)"
|
||||
stroke="var(--text-color)"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
|
||||
@@ -19,7 +19,7 @@ const Header: React.FC = () => {
|
||||
<FileMenu />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
<button
|
||||
className={`toggle-sidebar-ui-button ${!toggleUI ? "active" : ""}`}
|
||||
onClick={() => {
|
||||
if (activeModule !== "market") {
|
||||
@@ -29,7 +29,7 @@ const Header: React.FC = () => {
|
||||
}}
|
||||
>
|
||||
<ToggleSidebarIcon />
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState } from "react";
|
||||
import { AI_Icon } from "../../../icons/ExportCommonIcons";
|
||||
import { AIIcon } from "../../../icons/ExportCommonIcons";
|
||||
import RegularDropDown from "../../../ui/inputs/RegularDropDown";
|
||||
import { AnalysisPresetsType } from "../../../../types/analysis";
|
||||
import RenderAnalysisInputs from "./RenderAnalysisInputs";
|
||||
@@ -13,53 +13,24 @@ const Analysis: React.FC = () => {
|
||||
|
||||
const AnalysisPresets: AnalysisPresetsType = {
|
||||
"Throughput time": [
|
||||
{ type: "default", inputs: { label: "Height", activeOption: "mm" } },
|
||||
{ type: "default", inputs: { label: "Width", activeOption: "mm" } },
|
||||
{ type: "default", inputs: { label: "Length", activeOption: "mtr" } },
|
||||
{ type: "default", inputs: { label: "Thickness", activeOption: "mm" } },
|
||||
{
|
||||
type: "default",
|
||||
inputs: { label: "Raw Material", activeOption: "mm" },
|
||||
},
|
||||
{
|
||||
type: "range",
|
||||
inputs: { label: "Material flow", activeOption: "m/min" },
|
||||
},
|
||||
{ type: "default", inputs: { label: "Cycle time", activeOption: "s" } },
|
||||
{ type: "default", inputs: { label: "machines / lines", activeOption: "item" } },
|
||||
{ type: "default", inputs: { label: "Machine uptime", activeOption: "%" } },
|
||||
],
|
||||
"Production capacity": [
|
||||
{ type: "default", inputs: { label: "Height", activeOption: "mm" } },
|
||||
{ type: "default", inputs: { label: "Width", activeOption: "mm" } },
|
||||
{ type: "default", inputs: { label: "Length", activeOption: "mtr" } },
|
||||
{ type: "default", inputs: { label: "Thickness", activeOption: "mm" } },
|
||||
{
|
||||
type: "default",
|
||||
inputs: { label: "Raw Material", activeOption: "mm" },
|
||||
},
|
||||
{
|
||||
type: "range",
|
||||
inputs: { label: "Material flow", activeOption: "m/min" },
|
||||
},
|
||||
{
|
||||
type: "range",
|
||||
inputs: { label: "Shift 1", activeOption: "hr(s)" },
|
||||
},
|
||||
{
|
||||
type: "range",
|
||||
inputs: { label: "Shift 2", activeOption: "hr(s)" },
|
||||
},
|
||||
{
|
||||
type: "range",
|
||||
inputs: { label: "Over time", activeOption: "hr(s)" },
|
||||
},
|
||||
{ type: "default", inputs: { label: "Shift length", activeOption: "hr" } },
|
||||
{ type: "default", inputs: { label: "Shifts / day", activeOption: "unit" } },
|
||||
{ type: "default", inputs: { label: "Working days / year", activeOption: "days" } },
|
||||
{ type: "default", inputs: { label: "Yield rate", activeOption: "%" } },
|
||||
],
|
||||
ROI: [
|
||||
{
|
||||
type: "default",
|
||||
inputs: { label: "Equipment Cost", activeOption: "INR" },
|
||||
inputs: { label: "Selling price", activeOption: "INR" },
|
||||
},
|
||||
{
|
||||
type: "default",
|
||||
inputs: { label: "Employee Salary", activeOption: "INR" },
|
||||
inputs: { label: "Material cost", activeOption: "INR" },
|
||||
},
|
||||
{
|
||||
type: "default",
|
||||
@@ -67,7 +38,7 @@ const Analysis: React.FC = () => {
|
||||
},
|
||||
{
|
||||
type: "default",
|
||||
inputs: { label: "Cost per unit", activeOption: "INR" },
|
||||
inputs: { label: "Maintenance cost", activeOption: "INR" },
|
||||
},
|
||||
{
|
||||
type: "default",
|
||||
@@ -75,20 +46,19 @@ const Analysis: React.FC = () => {
|
||||
},
|
||||
{
|
||||
type: "default",
|
||||
inputs: { label: "Upkeep Cost", activeOption: "INR" },
|
||||
inputs: { label: "Fixed costs", activeOption: "INR" },
|
||||
},
|
||||
{
|
||||
type: "default",
|
||||
inputs: { label: "Working Hours", activeOption: "Hrs" },
|
||||
inputs: { label: "Salvage value", activeOption: "Hrs" },
|
||||
},
|
||||
{
|
||||
type: "default",
|
||||
inputs: { label: "Power Usage", activeOption: "KWH" },
|
||||
inputs: { label: "Production period", activeOption: "yrs" },
|
||||
},
|
||||
{ type: "default", inputs: { label: "KWH", activeOption: "Mos" } },
|
||||
{
|
||||
type: "default",
|
||||
inputs: { label: "Man Power", activeOption: "Person" },
|
||||
inputs: { label: "Tax rate", activeOption: "%" },
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -98,7 +68,7 @@ const Analysis: React.FC = () => {
|
||||
<div className="analysis-main-container">
|
||||
<div className="header">Object</div>
|
||||
<div className="generate-report-button">
|
||||
<AI_Icon /> Generate Report
|
||||
<AIIcon /> Generate Report
|
||||
</div>
|
||||
<div className="analysis-content-container section">
|
||||
<div className="dropdown-header-container">
|
||||
@@ -121,7 +91,7 @@ const Analysis: React.FC = () => {
|
||||
/>
|
||||
<div className="buttons-container">
|
||||
<input type="button" value={"Clear"} className="cancel" />
|
||||
<input type="button" value={"Calculate"} className="submit" />
|
||||
<input type="button" value={"Update"} className="submit" />
|
||||
</div>
|
||||
<div className="create-custom-analysis-container">
|
||||
<div className="custom-analysis-header">Create Custom Analysis</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import InputRange from "../../../ui/inputs/InputRange";
|
||||
import InputToggle from "../../../ui/inputs/InputToggle";
|
||||
import { AI_Icon } from "../../../icons/ExportCommonIcons";
|
||||
import { AIIcon } from "../../../icons/ExportCommonIcons";
|
||||
import LabeledButton from "../../../ui/inputs/LabledButton";
|
||||
import {
|
||||
useAzimuth,
|
||||
@@ -225,7 +225,7 @@ const GlobalProperties: React.FC = () => {
|
||||
<section>
|
||||
<div className="header">Environment</div>
|
||||
<div className="optimize-button" onClick={optimizeScene}>
|
||||
<AI_Icon />
|
||||
<AIIcon />
|
||||
Optimize
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import {
|
||||
useSelectedEventData,
|
||||
useSelectedEventSphere,
|
||||
useSelectedProduct,
|
||||
useSelectedEventData,
|
||||
useSelectedEventSphere,
|
||||
useSelectedProduct,
|
||||
} from "../../../../../store/simulation/useSimulationStore";
|
||||
import { useProductStore } from "../../../../../store/simulation/useProductStore";
|
||||
import ConveyorMechanics from "./mechanics/conveyorMechanics";
|
||||
@@ -15,114 +15,118 @@ import { handleAddEventToProduct } from "../../../../../modules/simulation/event
|
||||
import { useEventsStore } from "../../../../../store/simulation/useEventsStore";
|
||||
|
||||
const EventProperties: React.FC = () => {
|
||||
const { selectedEventData } = useSelectedEventData();
|
||||
const { getEventByModelUuid } = useProductStore();
|
||||
const { selectedProduct } = useSelectedProduct();
|
||||
const [currentEventData, setCurrentEventData] = useState<EventsSchema | null>(null);
|
||||
const [assetType, setAssetType] = useState<string | null>(null);
|
||||
const { products, addEvent } = useProductStore();
|
||||
const { selectedEventSphere } = useSelectedEventSphere();
|
||||
const { selectedEventData } = useSelectedEventData();
|
||||
const { getEventByModelUuid } = useProductStore();
|
||||
const { selectedProduct } = useSelectedProduct();
|
||||
const [currentEventData, setCurrentEventData] = useState<EventsSchema | null>(
|
||||
null
|
||||
);
|
||||
const [assetType, setAssetType] = useState<string | null>(null);
|
||||
const { products, addEvent } = useProductStore();
|
||||
const { selectedEventSphere } = useSelectedEventSphere();
|
||||
|
||||
useEffect(() => {
|
||||
const event = getCurrentEventData();
|
||||
setCurrentEventData(event);
|
||||
useEffect(() => {
|
||||
const event = getCurrentEventData();
|
||||
setCurrentEventData(event);
|
||||
|
||||
const type = determineAssetType(event);
|
||||
setAssetType(type);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [selectedEventData, selectedProduct]);
|
||||
|
||||
const getCurrentEventData = () => {
|
||||
if (!selectedEventData?.data || !selectedProduct) return null;
|
||||
return (
|
||||
getEventByModelUuid(
|
||||
selectedProduct.productId,
|
||||
selectedEventData.data.modelUuid
|
||||
) ?? null
|
||||
);
|
||||
};
|
||||
|
||||
const determineAssetType = (event: EventsSchema | null) => {
|
||||
if (!event) return null;
|
||||
|
||||
switch (event.type) {
|
||||
case "transfer":
|
||||
return "conveyor";
|
||||
case "vehicle":
|
||||
return "vehicle";
|
||||
case "roboticArm":
|
||||
return "roboticArm";
|
||||
case "machine":
|
||||
return "machine";
|
||||
case "storageUnit":
|
||||
return "storageUnit";
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
const type = determineAssetType(event);
|
||||
setAssetType(type);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [selectedEventData, selectedProduct]);
|
||||
|
||||
const getCurrentEventData = () => {
|
||||
if (!selectedEventData?.data || !selectedProduct) return null;
|
||||
return (
|
||||
<div className="event-proprties-wrapper">
|
||||
{currentEventData && (
|
||||
<>
|
||||
<div className="header">
|
||||
<div className="header-value">
|
||||
{selectedEventData?.data.modelName}
|
||||
</div>
|
||||
</div>
|
||||
{assetType === "conveyor" && <ConveyorMechanics />}
|
||||
{assetType === "vehicle" && <VehicleMechanics />}
|
||||
{assetType === "roboticArm" && <RoboticArmMechanics />}
|
||||
{assetType === "machine" && <MachineMechanics />}
|
||||
{assetType === "storageUnit" && <StorageMechanics />}
|
||||
</>
|
||||
)}
|
||||
{!currentEventData && selectedEventSphere && (
|
||||
<div className="no-event-selected">
|
||||
<p>
|
||||
<strong>Oops!</strong> It looks like this object doesn't have an
|
||||
event assigned yet. To continue, please link it to one of the
|
||||
products below.
|
||||
</p>
|
||||
|
||||
<div className="products-list">
|
||||
<p>
|
||||
<strong>Here are some products you can add it to:</strong>
|
||||
</p>
|
||||
<ul>
|
||||
{products.map((product) => (
|
||||
<li key={product.productId}>
|
||||
<button
|
||||
onClick={() => {
|
||||
if (selectedEventData) {
|
||||
handleAddEventToProduct({
|
||||
event: useEventsStore.getState().getEventByModelUuid(selectedEventData?.data.modelUuid),
|
||||
addEvent,
|
||||
selectedProduct,
|
||||
})
|
||||
}
|
||||
}}
|
||||
>
|
||||
<AddIcon />
|
||||
{product.productName}
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{!selectedEventSphere && (
|
||||
<div className="no-event-selected">
|
||||
<p>
|
||||
<strong>Oops!</strong> It looks like you haven't selected an event
|
||||
point yet. Please select an event to view its properties.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div >
|
||||
getEventByModelUuid(
|
||||
selectedProduct.productId,
|
||||
selectedEventData.data.modelUuid
|
||||
) ?? null
|
||||
);
|
||||
};
|
||||
|
||||
const determineAssetType = (event: EventsSchema | null) => {
|
||||
if (!event) return null;
|
||||
|
||||
switch (event.type) {
|
||||
case "transfer":
|
||||
return "conveyor";
|
||||
case "vehicle":
|
||||
return "vehicle";
|
||||
case "roboticArm":
|
||||
return "roboticArm";
|
||||
case "machine":
|
||||
return "machine";
|
||||
case "storageUnit":
|
||||
return "storageUnit";
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="event-proprties-wrapper">
|
||||
{currentEventData && (
|
||||
<>
|
||||
<div className="header">
|
||||
<div className="header-value">
|
||||
{selectedEventData?.data.modelName}
|
||||
</div>
|
||||
</div>
|
||||
{assetType === "conveyor" && <ConveyorMechanics />}
|
||||
{assetType === "vehicle" && <VehicleMechanics />}
|
||||
{assetType === "roboticArm" && <RoboticArmMechanics />}
|
||||
{assetType === "machine" && <MachineMechanics />}
|
||||
{assetType === "storageUnit" && <StorageMechanics />}
|
||||
</>
|
||||
)}
|
||||
{!currentEventData && selectedEventSphere && (
|
||||
<div className="no-event-selected">
|
||||
<p>
|
||||
<strong>Oops!</strong> It looks like this object doesn't have an
|
||||
event assigned yet. To continue, please link it to one of the
|
||||
products below.
|
||||
</p>
|
||||
|
||||
<div className="products-list">
|
||||
<p>
|
||||
<strong>Here are some products you can add it to:</strong>
|
||||
</p>
|
||||
<div className="product-item">
|
||||
{products.map((product) => (
|
||||
<button
|
||||
key={product.productId}
|
||||
onClick={() => {
|
||||
if (selectedEventData) {
|
||||
handleAddEventToProduct({
|
||||
event: useEventsStore
|
||||
.getState()
|
||||
.getEventByModelUuid(
|
||||
selectedEventData?.data.modelUuid
|
||||
),
|
||||
addEvent,
|
||||
selectedProduct,
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<AddIcon />
|
||||
{product.productName}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!selectedEventSphere && (
|
||||
<div className="no-event-selected">
|
||||
<p>
|
||||
<strong>Oops!</strong> It looks like you haven't selected an event
|
||||
point yet. Please select an event to view its properties.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default EventProperties;
|
||||
|
||||
@@ -36,7 +36,6 @@ import {
|
||||
import useToggleStore from "../../store/useUIToggleStore";
|
||||
import {
|
||||
use3DWidget,
|
||||
useDroppedObjectsStore,
|
||||
useFloatingWidget,
|
||||
} from "../../store/visualization/useDroppedObjectsStore";
|
||||
|
||||
@@ -57,20 +56,18 @@ const Tools: React.FC = () => {
|
||||
|
||||
const { widgets3D } = use3DWidget();
|
||||
|
||||
const zones = useDroppedObjectsStore((state) => state.zones);
|
||||
|
||||
// wall options
|
||||
const { toggleView, setToggleView } = useToggleView();
|
||||
const { setDeleteTool } = useDeleteTool();
|
||||
const { setAddAction } = useAddAction();
|
||||
const { setSelectedWallItem } = useSelectedWallItem();
|
||||
|
||||
const { transformMode, setTransformMode } = useTransformMode();
|
||||
const { deletePointOrLine, setDeletePointOrLine } = useDeletePointOrLine();
|
||||
const { movePoint, setMovePoint } = useMovePoint();
|
||||
const { toolMode, setToolMode } = useToolMode();
|
||||
const { setTransformMode } = useTransformMode();
|
||||
const { setDeletePointOrLine } = useDeletePointOrLine();
|
||||
const { setMovePoint } = useMovePoint();
|
||||
const { setToolMode } = useToolMode();
|
||||
const { activeTool, setActiveTool } = useActiveTool();
|
||||
const { refTextupdate, setRefTextUpdate } = useRefTextUpdate();
|
||||
const { setRefTextUpdate } = useRefTextUpdate();
|
||||
|
||||
// Reset activeTool whenever activeModule changes
|
||||
useEffect(() => {
|
||||
@@ -209,268 +206,266 @@ const Tools: React.FC = () => {
|
||||
return (
|
||||
<>
|
||||
{!isPlaying ? (
|
||||
<>
|
||||
<div className="tools-container">
|
||||
<div className="drop-down-icons">
|
||||
<div className="activeDropicon">
|
||||
{activeSubTool == "cursor" && (
|
||||
<div
|
||||
className={`tool-button ${
|
||||
activeTool === "cursor" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setActiveTool("cursor");
|
||||
}}
|
||||
>
|
||||
<CursorIcon isActive={activeTool === "cursor"} />
|
||||
</div>
|
||||
)}
|
||||
{activeSubTool == "free-hand" && (
|
||||
<div
|
||||
className={`tool-button ${
|
||||
activeTool === "free-hand" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setActiveTool("free-hand");
|
||||
}}
|
||||
>
|
||||
<FreeMoveIcon isActive={activeTool === "free-hand"} />
|
||||
</div>
|
||||
)}
|
||||
{activeSubTool == "delete" && (
|
||||
<div
|
||||
className={`tool-button ${
|
||||
activeTool === "delete" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setActiveTool("delete");
|
||||
}}
|
||||
>
|
||||
<DeleteIcon isActive={activeTool === "delete"} />
|
||||
</div>
|
||||
)}
|
||||
{activeModule !== "visualization" && (
|
||||
<div
|
||||
className="drop-down-option-button"
|
||||
ref={dropdownRef}
|
||||
onClick={() => {
|
||||
setOpenDrop(!openDrop);
|
||||
}}
|
||||
>
|
||||
<ArrowIcon />
|
||||
{openDrop && (
|
||||
<div className="drop-down-container">
|
||||
<div
|
||||
className="option-list"
|
||||
onClick={() => {
|
||||
setOpenDrop(false);
|
||||
setActiveTool("cursor");
|
||||
setActiveSubTool("cursor");
|
||||
}}
|
||||
>
|
||||
<div className="active-option">
|
||||
{activeSubTool === "cursor" && <TickIcon />}
|
||||
</div>
|
||||
<CursorIcon isActive={false} />
|
||||
<div className="option">Cursor</div>
|
||||
</div>
|
||||
<div
|
||||
className="option-list"
|
||||
onClick={() => {
|
||||
setOpenDrop(false);
|
||||
setActiveTool("free-hand");
|
||||
setActiveSubTool("free-hand");
|
||||
}}
|
||||
>
|
||||
<div className="active-option">
|
||||
{activeSubTool === "free-hand" && <TickIcon />}
|
||||
</div>
|
||||
<FreeMoveIcon isActive={false} />
|
||||
<div className="option">Free Hand</div>
|
||||
</div>
|
||||
<div
|
||||
className="option-list"
|
||||
onClick={() => {
|
||||
setOpenDrop(false);
|
||||
setActiveTool("delete");
|
||||
setActiveSubTool("delete");
|
||||
}}
|
||||
>
|
||||
<div className="active-option">
|
||||
{activeSubTool === "delete" && <TickIcon />}
|
||||
</div>
|
||||
<DeleteIcon isActive={false} />
|
||||
<div className="option">Delete</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{!toggleThreeD && activeModule === "builder" && (
|
||||
<>
|
||||
<div className="split"></div>
|
||||
<div className="draw-tools">
|
||||
<div
|
||||
className={`tool-button ${
|
||||
activeTool === "draw-wall" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setActiveTool("draw-wall");
|
||||
}}
|
||||
title="Wall"
|
||||
>
|
||||
<WallIcon isActive={activeTool === "draw-wall"} />
|
||||
</div>
|
||||
<div
|
||||
className={`tool-button ${
|
||||
activeTool === "draw-zone" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setActiveTool("draw-zone");
|
||||
}}
|
||||
title="Zone"
|
||||
>
|
||||
<ZoneIcon isActive={activeTool === "draw-zone"} />
|
||||
</div>
|
||||
<div
|
||||
className={`tool-button ${
|
||||
activeTool === "draw-aisle" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setActiveTool("draw-aisle");
|
||||
}}
|
||||
title="Aisle"
|
||||
>
|
||||
<AsileIcon isActive={activeTool === "draw-aisle"} />
|
||||
</div>
|
||||
<div
|
||||
className={`tool-button ${
|
||||
activeTool === "draw-floor" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setActiveTool("draw-floor");
|
||||
}}
|
||||
title="Floor"
|
||||
>
|
||||
<FloorIcon isActive={activeTool === "draw-floor"} />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{activeModule === "builder" && (
|
||||
<>
|
||||
<div className="split"></div>
|
||||
<div className="draw-tools">
|
||||
<div
|
||||
className={`tool-button ${
|
||||
activeTool === "measure" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setActiveTool("measure");
|
||||
}}
|
||||
title="Measure"
|
||||
>
|
||||
<MeasureToolIcon isActive={activeTool === "measure"} />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{activeModule === "simulation" && (
|
||||
<>
|
||||
<div className="split"></div>
|
||||
<div className="draw-tools">
|
||||
<div
|
||||
className={`tool-button ${
|
||||
activeTool === "pen" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setActiveTool("pen");
|
||||
}}
|
||||
>
|
||||
<PenIcon isActive={activeTool === "pen"} />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{activeModule === "visualization" && (
|
||||
<>
|
||||
<div className="split"></div>
|
||||
<div className="draw-tools">
|
||||
<div
|
||||
className={`tool-button`}
|
||||
onClick={() => {
|
||||
handleSaveTemplate({
|
||||
addTemplate,
|
||||
floatingWidget,
|
||||
widgets3D,
|
||||
selectedZone,
|
||||
templates,
|
||||
visualizationSocket,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<SaveTemplateIcon isActive={false} />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className="split"></div>
|
||||
<div className="general-options">
|
||||
<div
|
||||
className={`tool-button ${
|
||||
activeTool === "comment" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setActiveTool("comment");
|
||||
}}
|
||||
>
|
||||
<CommentIcon isActive={activeTool === "comment"} />
|
||||
</div>
|
||||
{toggleThreeD && (
|
||||
<div className="tools-container">
|
||||
<div className="drop-down-icons">
|
||||
<div className="activeDropicon">
|
||||
{activeSubTool == "cursor" && (
|
||||
<div
|
||||
className={`tool-button ${
|
||||
activeTool === "play" ? "active" : ""
|
||||
activeTool === "cursor" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setIsPlaying(!isPlaying);
|
||||
setActiveTool("cursor");
|
||||
}}
|
||||
>
|
||||
<PlayIcon isActive={activeTool === "play"} />
|
||||
<CursorIcon isActive={activeTool === "cursor"} />
|
||||
</div>
|
||||
)}
|
||||
{activeSubTool == "free-hand" && (
|
||||
<div
|
||||
className={`tool-button ${
|
||||
activeTool === "free-hand" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setActiveTool("free-hand");
|
||||
}}
|
||||
>
|
||||
<FreeMoveIcon isActive={activeTool === "free-hand"} />
|
||||
</div>
|
||||
)}
|
||||
{activeSubTool == "delete" && (
|
||||
<div
|
||||
className={`tool-button ${
|
||||
activeTool === "delete" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setActiveTool("delete");
|
||||
}}
|
||||
>
|
||||
<DeleteIcon isActive={activeTool === "delete"} />
|
||||
</div>
|
||||
)}
|
||||
{activeModule !== "visualization" && (
|
||||
<div
|
||||
className="drop-down-option-button"
|
||||
ref={dropdownRef}
|
||||
onClick={() => {
|
||||
setOpenDrop(!openDrop);
|
||||
}}
|
||||
>
|
||||
<ArrowIcon />
|
||||
{openDrop && (
|
||||
<div className="drop-down-container">
|
||||
<div
|
||||
className="option-list"
|
||||
onClick={() => {
|
||||
setOpenDrop(false);
|
||||
setActiveTool("cursor");
|
||||
setActiveSubTool("cursor");
|
||||
}}
|
||||
>
|
||||
<div className="active-option">
|
||||
{activeSubTool === "cursor" && <TickIcon />}
|
||||
</div>
|
||||
<CursorIcon isActive={false} />
|
||||
<div className="option">Cursor</div>
|
||||
</div>
|
||||
<div
|
||||
className="option-list"
|
||||
onClick={() => {
|
||||
setOpenDrop(false);
|
||||
setActiveTool("free-hand");
|
||||
setActiveSubTool("free-hand");
|
||||
}}
|
||||
>
|
||||
<div className="active-option">
|
||||
{activeSubTool === "free-hand" && <TickIcon />}
|
||||
</div>
|
||||
<FreeMoveIcon isActive={false} />
|
||||
<div className="option">Free Hand</div>
|
||||
</div>
|
||||
<div
|
||||
className="option-list"
|
||||
onClick={() => {
|
||||
setOpenDrop(false);
|
||||
setActiveTool("delete");
|
||||
setActiveSubTool("delete");
|
||||
}}
|
||||
>
|
||||
<div className="active-option">
|
||||
{activeSubTool === "delete" && <TickIcon />}
|
||||
</div>
|
||||
<DeleteIcon isActive={false} />
|
||||
<div className="option">Delete</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{activeModule === "builder" && (
|
||||
<>
|
||||
<div className="split"></div>
|
||||
</div>
|
||||
{!toggleThreeD && activeModule === "builder" && (
|
||||
<>
|
||||
<div className="split"></div>
|
||||
<div className="draw-tools">
|
||||
<div
|
||||
className={`toggle-threed-button${
|
||||
toggleThreeD ? " toggled" : ""
|
||||
className={`tool-button ${
|
||||
activeTool === "draw-wall" ? "active" : ""
|
||||
}`}
|
||||
onClick={toggleSwitch}
|
||||
onClick={() => {
|
||||
setActiveTool("draw-wall");
|
||||
}}
|
||||
title="Wall"
|
||||
>
|
||||
<div
|
||||
className={`toggle-option${!toggleThreeD ? " active" : ""}`}
|
||||
>
|
||||
2d
|
||||
</div>
|
||||
<div
|
||||
className={`toggle-option${toggleThreeD ? " active" : ""}`}
|
||||
>
|
||||
3d
|
||||
</div>
|
||||
<WallIcon isActive={activeTool === "draw-wall"} />
|
||||
</div>
|
||||
</>
|
||||
<div
|
||||
className={`tool-button ${
|
||||
activeTool === "draw-zone" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setActiveTool("draw-zone");
|
||||
}}
|
||||
title="Zone"
|
||||
>
|
||||
<ZoneIcon isActive={activeTool === "draw-zone"} />
|
||||
</div>
|
||||
<div
|
||||
className={`tool-button ${
|
||||
activeTool === "draw-aisle" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setActiveTool("draw-aisle");
|
||||
}}
|
||||
title="Aisle"
|
||||
>
|
||||
<AsileIcon isActive={activeTool === "draw-aisle"} />
|
||||
</div>
|
||||
<div
|
||||
className={`tool-button ${
|
||||
activeTool === "draw-floor" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setActiveTool("draw-floor");
|
||||
}}
|
||||
title="Floor"
|
||||
>
|
||||
<FloorIcon isActive={activeTool === "draw-floor"} />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{activeModule === "builder" && (
|
||||
<>
|
||||
<div className="split"></div>
|
||||
<div className="draw-tools">
|
||||
<div
|
||||
className={`tool-button ${
|
||||
activeTool === "measure" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setActiveTool("measure");
|
||||
}}
|
||||
title="Measure"
|
||||
>
|
||||
<MeasureToolIcon isActive={activeTool === "measure"} />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{activeModule === "simulation" && (
|
||||
<>
|
||||
<div className="split"></div>
|
||||
<div className="draw-tools">
|
||||
<div
|
||||
className={`tool-button ${
|
||||
activeTool === "pen" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setActiveTool("pen");
|
||||
}}
|
||||
>
|
||||
<PenIcon isActive={activeTool === "pen"} />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{activeModule === "visualization" && (
|
||||
<>
|
||||
<div className="split"></div>
|
||||
<div className="draw-tools">
|
||||
<div
|
||||
className={`tool-button`}
|
||||
onClick={() => {
|
||||
handleSaveTemplate({
|
||||
addTemplate,
|
||||
floatingWidget,
|
||||
widgets3D,
|
||||
selectedZone,
|
||||
templates,
|
||||
visualizationSocket,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<SaveTemplateIcon isActive={false} />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className="split"></div>
|
||||
<div className="general-options">
|
||||
<div
|
||||
className={`tool-button ${
|
||||
activeTool === "comment" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setActiveTool("comment");
|
||||
}}
|
||||
>
|
||||
<CommentIcon isActive={activeTool === "comment"} />
|
||||
</div>
|
||||
{toggleThreeD && (
|
||||
<div
|
||||
className={`tool-button ${
|
||||
activeTool === "play" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setIsPlaying(!isPlaying);
|
||||
}}
|
||||
>
|
||||
<PlayIcon isActive={activeTool === "play"} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
{activeModule === "builder" && (
|
||||
<>
|
||||
<div className="split"></div>
|
||||
<div
|
||||
className={`toggle-threed-button${
|
||||
toggleThreeD ? " toggled" : ""
|
||||
}`}
|
||||
onClick={toggleSwitch}
|
||||
>
|
||||
<div
|
||||
className={`toggle-option${!toggleThreeD ? " active" : ""}`}
|
||||
>
|
||||
2d
|
||||
</div>
|
||||
<div
|
||||
className={`toggle-option${toggleThreeD ? " active" : ""}`}
|
||||
>
|
||||
3d
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{activeModule !== "simulation" && (
|
||||
<div className="exitPlay" onClick={() => setIsPlaying(false)}>
|
||||
<button className="exitPlay" onClick={() => setIsPlaying(false)}>
|
||||
X
|
||||
</div>
|
||||
</button>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { useState } from "react";
|
||||
import { ROISummaryIcon } from "../../icons/analysis";
|
||||
import SemiCircleProgress from "./SemiCircleProgress";
|
||||
import { ArrowIcon } from "../../icons/ExportCommonIcons";
|
||||
|
||||
const ROISummary = ({
|
||||
roiSummaryData = {
|
||||
@@ -121,7 +122,7 @@ const ROISummary = ({
|
||||
</div>
|
||||
|
||||
<span className={`expand-icon ${isTableOpen ? "open" : ""}`}>
|
||||
{isTableOpen ? "⌵" : "⌵"}
|
||||
<ArrowIcon />
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
|
||||
95
app/src/components/ui/log/LogList.tsx
Normal file
95
app/src/components/ui/log/LogList.tsx
Normal file
@@ -0,0 +1,95 @@
|
||||
// LogList.tsx
|
||||
import React, { useState } from "react";
|
||||
import {
|
||||
LogListIcon,
|
||||
LogInfoIcon,
|
||||
WarningIcon,
|
||||
ErrorIcon,
|
||||
CloseIcon,
|
||||
} from "../../icons/ExportCommonIcons"; // Adjust path as needed
|
||||
import { useLogger } from "./LoggerContext";
|
||||
|
||||
const LogList: React.FC = () => {
|
||||
const { logs, clear, setIsLogListVisible } = useLogger();
|
||||
const [selectedTab, setSelectedTab] = useState<
|
||||
"all" | "info" | "warning" | "error" | "log"
|
||||
>("all");
|
||||
|
||||
const getLogIcon = (type: string) => {
|
||||
switch (type) {
|
||||
case "info":
|
||||
return <LogInfoIcon />;
|
||||
case "error":
|
||||
return <ErrorIcon />;
|
||||
case "warning":
|
||||
return <WarningIcon />;
|
||||
case "log":
|
||||
default:
|
||||
return <LogInfoIcon />;
|
||||
}
|
||||
};
|
||||
|
||||
const formatTimestamp = (date: Date) => new Date(date).toLocaleTimeString();
|
||||
|
||||
const filteredLogs =
|
||||
selectedTab === "all"
|
||||
? [...logs].reverse()
|
||||
: [...logs].filter((log) => log.type === selectedTab).reverse();
|
||||
|
||||
return (
|
||||
// eslint-disable-next-line
|
||||
<div
|
||||
className="log-list-container"
|
||||
onClick={() => setIsLogListVisible(false)}
|
||||
>
|
||||
<div
|
||||
className="log-list-wrapper"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<div className="log-header">
|
||||
<div className="log-header-wrapper">
|
||||
<div className="icon">
|
||||
<LogListIcon />
|
||||
</div>
|
||||
<div className="head">Log List</div>
|
||||
</div>
|
||||
<button className="close" onClick={() => setIsLogListVisible(false)}>
|
||||
<CloseIcon />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Tabs */}
|
||||
<div className="log-nav-wrapper">
|
||||
{["all", "info", "warning", "error"].map((type) => (
|
||||
<div
|
||||
key={type}
|
||||
className={`log-nav ${selectedTab === type ? "active" : ""}`}
|
||||
onClick={() => setSelectedTab(type as any)}
|
||||
>
|
||||
{`${type.charAt(0).toUpperCase() + type.slice(1)} Logs`}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Log Entries */}
|
||||
<div className="log-entry-wrapper">
|
||||
{filteredLogs.map((log) => (
|
||||
<div key={log.id} className={`log-entry ${log.type}`}>
|
||||
<div className="log-icon">{getLogIcon(log.type)}</div>
|
||||
<div className="log-entry-message-container">
|
||||
<div className="log-entry-message">{log.message}</div>
|
||||
<div className="message-time">
|
||||
{formatTimestamp(log.timestamp)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LogList;
|
||||
77
app/src/components/ui/log/LoggerContext.tsx
Normal file
77
app/src/components/ui/log/LoggerContext.tsx
Normal file
@@ -0,0 +1,77 @@
|
||||
import React, { createContext, useContext, useState, useCallback } from "react";
|
||||
|
||||
export type LogType = "log" | "info" | "warning" | "error";
|
||||
|
||||
export interface LogEntry {
|
||||
id: string;
|
||||
type: LogType;
|
||||
message: string;
|
||||
timestamp: Date;
|
||||
}
|
||||
|
||||
interface LoggerContextValue {
|
||||
logs: LogEntry[];
|
||||
setLogs: React.Dispatch<React.SetStateAction<LogEntry[]>>;
|
||||
isLogListVisible: boolean;
|
||||
setIsLogListVisible: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
log: (message: string) => void;
|
||||
info: (message: string) => void;
|
||||
warn: (message: string) => void;
|
||||
error: (message: string) => void;
|
||||
clear: () => void;
|
||||
}
|
||||
|
||||
const LoggerContext = createContext<LoggerContextValue | undefined>(undefined);
|
||||
|
||||
export const LoggerProvider: React.FC<{ children: React.ReactNode }> = ({
|
||||
children,
|
||||
}) => {
|
||||
const [logs, setLogs] = useState<LogEntry[]>([]);
|
||||
const [isLogListVisible, setIsLogListVisible] = useState<boolean>(false);
|
||||
|
||||
const generateId = useCallback(
|
||||
() => Math.random().toString(36).substring(2, 9),
|
||||
[]
|
||||
);
|
||||
|
||||
const addLog = useCallback(
|
||||
(type: LogType, message: string) => {
|
||||
const newLog: LogEntry = {
|
||||
id: generateId(),
|
||||
type,
|
||||
message,
|
||||
timestamp: new Date(),
|
||||
};
|
||||
setLogs((prevLogs) => [...prevLogs, newLog]);
|
||||
},
|
||||
[generateId]
|
||||
);
|
||||
|
||||
const loggerMethods: LoggerContextValue = {
|
||||
logs,
|
||||
setLogs,
|
||||
isLogListVisible,
|
||||
setIsLogListVisible,
|
||||
log: (message: string) => addLog("log", message),
|
||||
info: (message: string) => addLog("info", message),
|
||||
warn: (message: string) => addLog("warning", message),
|
||||
error: (message: string) => addLog("error", message),
|
||||
clear: () => {
|
||||
setLogs([]);
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<LoggerContext.Provider value={loggerMethods}>
|
||||
{children}
|
||||
</LoggerContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useLogger = () => {
|
||||
const context = useContext(LoggerContext);
|
||||
if (!context) {
|
||||
throw new Error("useLogger must be used within a LoggerProvider");
|
||||
}
|
||||
return context;
|
||||
};
|
||||
71
app/src/components/ui/log/logger.ts
Normal file
71
app/src/components/ui/log/logger.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
type LogType = 'log' | 'info' | 'warning' | 'error';
|
||||
|
||||
interface LogEntry {
|
||||
type: LogType;
|
||||
message: string;
|
||||
timestamp: Date;
|
||||
context?: string;
|
||||
}
|
||||
|
||||
class Logger {
|
||||
private static instance: Logger;
|
||||
private logs: LogEntry[] = [];
|
||||
private subscribers: Array<(log: LogEntry) => void> = [];
|
||||
|
||||
private constructor() {}
|
||||
|
||||
public static getInstance(): Logger {
|
||||
if (!Logger.instance) {
|
||||
Logger.instance = new Logger();
|
||||
}
|
||||
return Logger.instance;
|
||||
}
|
||||
|
||||
private notifySubscribers(log: LogEntry) {
|
||||
this.subscribers.forEach(callback => callback(log));
|
||||
}
|
||||
|
||||
private addLog(type: LogType, message: string, context?: string) {
|
||||
const log: LogEntry = { type, message, timestamp: new Date(), context };
|
||||
this.logs.push(log);
|
||||
this.notifySubscribers(log);
|
||||
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
const logMessage = context ? `[${context}] ${message}` : message;
|
||||
console[type === 'warning' ? 'warn' : type](logMessage);
|
||||
}
|
||||
}
|
||||
|
||||
public log(message: string, context?: string) {
|
||||
this.addLog('log', message, context);
|
||||
}
|
||||
|
||||
public info(message: string, context?: string) {
|
||||
this.addLog('info', message, context);
|
||||
}
|
||||
|
||||
public warning(message: string, context?: string) {
|
||||
this.addLog('warning', message, context);
|
||||
}
|
||||
|
||||
public error(message: string, context?: string) {
|
||||
this.addLog('error', message, context);
|
||||
}
|
||||
|
||||
public getLogs(): LogEntry[] {
|
||||
return [...this.logs];
|
||||
}
|
||||
|
||||
public clear() {
|
||||
this.logs = [];
|
||||
}
|
||||
|
||||
public subscribe(callback: (log: LogEntry) => void) {
|
||||
this.subscribers.push(callback);
|
||||
return () => {
|
||||
this.subscribers = this.subscribers.filter(sub => sub !== callback);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const logger = Logger.getInstance();
|
||||
@@ -12,25 +12,31 @@ import {
|
||||
EndIcon,
|
||||
ExpandIcon,
|
||||
HourlySimulationIcon,
|
||||
InfoIcon,
|
||||
MonthlyROI,
|
||||
SpeedIcon,
|
||||
StartIcon,
|
||||
} from "../../icons/ExportCommonIcons";
|
||||
import { getAvatarColor } from "../../../modules/collaboration/functions/getAvatarColor";
|
||||
import { useSubModuleStore } from "../../../store/useModuleStore";
|
||||
import ProductionCapacity from "../analysis/ProductionCapacity";
|
||||
import ThroughputSummary from "../analysis/ThroughputSummary";
|
||||
import ROISummary from "../analysis/ROISummary";
|
||||
|
||||
const SimulationPlayer: React.FC = () => {
|
||||
const MAX_SPEED = 8; // Maximum speed
|
||||
|
||||
const isDragging = useRef(false);
|
||||
const sliderRef = useRef<HTMLDivElement>(null);
|
||||
const [expand, setExpand] = useState(true);
|
||||
const [playSimulation, setPlaySimulation] = useState(false);
|
||||
|
||||
const { speed, setSpeed } = useAnimationPlaySpeed();
|
||||
const [playSimulation, setPlaySimulation] = useState(false);
|
||||
const { setIsPlaying } = usePlayButtonStore();
|
||||
const sliderRef = useRef<HTMLDivElement>(null);
|
||||
const isDragging = useRef(false);
|
||||
const { setActiveTool } = useActiveTool();
|
||||
const { isPaused, setIsPaused } = usePauseButtonStore();
|
||||
const { isReset, setReset } = useResetButtonStore();
|
||||
const { subModule } = useSubModuleStore();
|
||||
|
||||
// Button functions
|
||||
const handleReset = () => {
|
||||
@@ -114,7 +120,7 @@ const SimulationPlayer: React.FC = () => {
|
||||
};
|
||||
|
||||
// Store colors for each process item
|
||||
const [processColors, setProcessColors] = useState<string[]>([]);
|
||||
const [_, setProcessColors] = useState<string[]>([]);
|
||||
|
||||
// Generate colors on mount or when process changes
|
||||
useEffect(() => {
|
||||
@@ -159,210 +165,236 @@ const SimulationPlayer: React.FC = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="simulation-player-wrapper">
|
||||
<div className={`simulation-player-container ${expand ? "open" : ""}`}>
|
||||
<div className="controls-container">
|
||||
<div className="production-details">
|
||||
{/* hourlySimulation */}
|
||||
<div className="hourly-wrapper production-wrapper">
|
||||
<div className="header">
|
||||
<div className="icon">
|
||||
<HourlySimulationIcon />
|
||||
<>
|
||||
<div className="simulation-player-wrapper">
|
||||
<div className={`simulation-player-container ${expand ? "open" : ""}`}>
|
||||
<div className="controls-container">
|
||||
{subModule === "analysis" && (
|
||||
<div className="production-details">
|
||||
{/* hourlySimulation */}
|
||||
<div className="hourly-wrapper production-wrapper">
|
||||
<div className="header">
|
||||
<div className="icon">
|
||||
<HourlySimulationIcon />
|
||||
</div>
|
||||
<div className="label">Hourly Simulation</div>
|
||||
</div>
|
||||
<div className="progress-wrapper">
|
||||
<div
|
||||
className="progress"
|
||||
style={{ width: hourlySimulation }}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="label">Hourly Simulation</div>
|
||||
</div>
|
||||
<div className="progress-wrapper">
|
||||
<div
|
||||
className="progress"
|
||||
style={{ width: hourlySimulation }}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
{/* dailyProduction */}
|
||||
<div className="dailyProduction-wrapper production-wrapper">
|
||||
<div className="header">
|
||||
<div className="icon">
|
||||
<DailyProductionIcon />
|
||||
{/* dailyProduction */}
|
||||
<div className="dailyProduction-wrapper production-wrapper">
|
||||
<div className="header">
|
||||
<div className="icon">
|
||||
<DailyProductionIcon />
|
||||
</div>
|
||||
<div className="label">Daily Production</div>
|
||||
</div>
|
||||
<div className="progress-wrapper">
|
||||
<div
|
||||
className="progress"
|
||||
style={{ width: dailyProduction }}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="label">Daily Production</div>
|
||||
</div>
|
||||
<div className="progress-wrapper">
|
||||
<div
|
||||
className="progress"
|
||||
style={{ width: dailyProduction }}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
{/* monthlyROI */}
|
||||
<div className="monthlyROI-wrapper production-wrapper">
|
||||
<div className="header">
|
||||
<div className="icon">
|
||||
<MonthlyROI />
|
||||
{/* monthlyROI */}
|
||||
<div className="monthlyROI-wrapper production-wrapper">
|
||||
<div className="header">
|
||||
<div className="icon">
|
||||
<MonthlyROI />
|
||||
</div>
|
||||
<div className="label">Monthly ROI</div>
|
||||
</div>
|
||||
<div className="progress-wrapper">
|
||||
<div
|
||||
className="progress"
|
||||
style={{ width: monthlyROI }}
|
||||
></div>
|
||||
</div>{" "}
|
||||
</div>
|
||||
<div className="label">Monthly ROI</div>
|
||||
</div>
|
||||
<div className="progress-wrapper">
|
||||
<div className="progress" style={{ width: monthlyROI }}></div>
|
||||
</div>{" "}
|
||||
</div>
|
||||
</div>
|
||||
<div className="controls-wrapper">
|
||||
<button
|
||||
className="simulation-button-container"
|
||||
onClick={() => {
|
||||
handleReset();
|
||||
}}
|
||||
>
|
||||
<ResetIcon />
|
||||
Reset
|
||||
</button>
|
||||
<button
|
||||
className="simulation-button-container"
|
||||
onClick={() => {
|
||||
handlePlayStop();
|
||||
}}
|
||||
>
|
||||
<PlayStopIcon />
|
||||
{playSimulation ? "Play" : "Stop"}
|
||||
</button>
|
||||
<button
|
||||
className="simulation-button-container"
|
||||
onClick={() => {
|
||||
handleExit();
|
||||
}}
|
||||
>
|
||||
<ExitIcon />
|
||||
Exit
|
||||
</button>
|
||||
<button
|
||||
className="expand-icon-container"
|
||||
onClick={() => setExpand(!expand)}
|
||||
>
|
||||
<ExpandIcon isActive={!expand} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="progresser-wrapper">
|
||||
<div className="time-displayer">
|
||||
<div className="start-time-wrappper">
|
||||
<div className="icon">
|
||||
<StartIcon />
|
||||
)}
|
||||
{subModule === "simulations" && (
|
||||
<div className="header">
|
||||
<InfoIcon />
|
||||
{playSimulation
|
||||
? "Paused - system idle."
|
||||
: "Running simulation..."}
|
||||
</div>
|
||||
<div className="time-wrapper">
|
||||
<div className="date">23 April ,25</div>
|
||||
<div className="time">04:41 PM</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="time-progresser">
|
||||
<div className="timeline">
|
||||
{intervals.map((label, index) => {
|
||||
const segmentProgress = (index / totalSegments) * 100;
|
||||
const isFilled = progress >= segmentProgress;
|
||||
return (
|
||||
<React.Fragment key={index}>
|
||||
<div className="label-dot-wrapper">
|
||||
<div className="label">{label} mins</div>
|
||||
<div
|
||||
className={`dot ${isFilled ? "filled" : ""}`}
|
||||
></div>
|
||||
</div>
|
||||
{index < intervals.length - 1 && (
|
||||
<div
|
||||
className={`line ${
|
||||
progress >= ((index + 1) / totalSegments) * 100
|
||||
? "filled"
|
||||
: ""
|
||||
}`}
|
||||
></div>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="end-time-wrappper">
|
||||
<div className="time-wrapper">
|
||||
<div className="time">00:10:20</div>
|
||||
</div>
|
||||
<div className="icon">
|
||||
<EndIcon />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="speed-control-container">
|
||||
<div className="min-value">
|
||||
<div className="icon">
|
||||
<SpeedIcon />
|
||||
</div>
|
||||
Speed
|
||||
</div>
|
||||
<div className="slider-container" ref={sliderRef}>
|
||||
<div className="speed-label mix-value">0.5X</div>
|
||||
<div className="marker marker-10"></div>
|
||||
<div className="marker marker-20"></div>
|
||||
<div className="marker marker-30"></div>
|
||||
<div className="marker marker-40"></div>
|
||||
<div className="marker marker-50"></div>
|
||||
<div className="marker marker-60"></div>
|
||||
<div className="marker marker-70"></div>
|
||||
<div className="marker marker-80"></div>
|
||||
<div className="marker marker-90"></div>
|
||||
<div className="custom-slider">
|
||||
)}
|
||||
<div className="controls-wrapper">
|
||||
<button
|
||||
className="simulation-button-container"
|
||||
onClick={() => {
|
||||
handleReset();
|
||||
}}
|
||||
>
|
||||
<ResetIcon />
|
||||
Reset
|
||||
</button>
|
||||
<button
|
||||
className="simulation-button-container"
|
||||
onClick={() => {
|
||||
handlePlayStop();
|
||||
}}
|
||||
>
|
||||
<PlayStopIcon />
|
||||
{playSimulation ? "Play" : "Stop"}
|
||||
</button>
|
||||
<button
|
||||
className="simulation-button-container"
|
||||
onClick={() => {
|
||||
handleExit();
|
||||
}}
|
||||
>
|
||||
<ExitIcon />
|
||||
Exit
|
||||
</button>
|
||||
{subModule === "analysis" && (
|
||||
<button
|
||||
className={`slider-handle ${isDragging ? "dragging" : ""}`}
|
||||
style={{ left: `${calculateHandlePosition()}%` }}
|
||||
onMouseDown={handleMouseDown}
|
||||
className="expand-icon-container"
|
||||
onClick={() => setExpand(!expand)}
|
||||
>
|
||||
{speed.toFixed(1)}x
|
||||
<ExpandIcon isActive={!expand} />
|
||||
</button>
|
||||
<input
|
||||
type="range"
|
||||
min="0.5"
|
||||
max={MAX_SPEED}
|
||||
step="0.1"
|
||||
value={speed}
|
||||
onChange={handleSpeedChange}
|
||||
className="slider-input"
|
||||
/>
|
||||
</div>
|
||||
<div className="speed-label max-value">4x</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="processDisplayer">
|
||||
<div className="start-displayer timmer">00:00</div>
|
||||
<div className="end-displayer timmer">24:00</div>
|
||||
<div
|
||||
className="process-wrapper"
|
||||
style={{ padding: expand ? "0px" : "5px 35px" }}
|
||||
>
|
||||
<div
|
||||
className="process-container"
|
||||
ref={processWrapperRef}
|
||||
onMouseDown={handleProcessMouseDown}
|
||||
>
|
||||
{process.map((item, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="process"
|
||||
style={{
|
||||
width: `${item.completed}%`,
|
||||
backgroundColor: getAvatarColor(index),
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="process-player"
|
||||
ref={processPlayerRef}
|
||||
style={{ left: playerPosition, position: "absolute" }}
|
||||
></div>
|
||||
<div className="progresser-wrapper">
|
||||
<div className="time-displayer">
|
||||
<div className="start-time-wrappper">
|
||||
<div className="icon">
|
||||
<StartIcon />
|
||||
</div>
|
||||
))}
|
||||
<div className="time-wrapper">
|
||||
<div className="date">23 April ,25</div>
|
||||
<div className="time">04:41 PM</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="time-progresser">
|
||||
<div className="timeline">
|
||||
{intervals.map((label, index) => {
|
||||
const segmentProgress = (index / totalSegments) * 100;
|
||||
const isFilled = progress >= segmentProgress;
|
||||
return (
|
||||
<React.Fragment key={index}>
|
||||
<div className="label-dot-wrapper">
|
||||
<div className="label">{label} mins</div>
|
||||
<div
|
||||
className={`dot ${isFilled ? "filled" : ""}`}
|
||||
></div>
|
||||
</div>
|
||||
{index < intervals.length - 1 && (
|
||||
<div
|
||||
className={`line ${
|
||||
progress >= ((index + 1) / totalSegments) * 100
|
||||
? "filled"
|
||||
: ""
|
||||
}`}
|
||||
></div>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="end-time-wrappper">
|
||||
<div className="time-wrapper">
|
||||
<div className="time">00:10:20</div>
|
||||
</div>
|
||||
<div className="icon">
|
||||
<EndIcon />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="speed-control-container">
|
||||
<div className="min-value">
|
||||
<div className="icon">
|
||||
<SpeedIcon />
|
||||
</div>
|
||||
Speed
|
||||
</div>
|
||||
<div className="slider-container" ref={sliderRef}>
|
||||
<div className="speed-label mix-value">0.5X</div>
|
||||
<div className="marker marker-10"></div>
|
||||
<div className="marker marker-20"></div>
|
||||
<div className="marker marker-30"></div>
|
||||
<div className="marker marker-40"></div>
|
||||
<div className="marker marker-50"></div>
|
||||
<div className="marker marker-60"></div>
|
||||
<div className="marker marker-70"></div>
|
||||
<div className="marker marker-80"></div>
|
||||
<div className="marker marker-90"></div>
|
||||
<div className="custom-slider">
|
||||
<button
|
||||
className={`slider-handle ${isDragging ? "dragging" : ""}`}
|
||||
style={{ left: `${calculateHandlePosition()}%` }}
|
||||
onMouseDown={handleMouseDown}
|
||||
>
|
||||
{speed.toFixed(1)}x
|
||||
</button>
|
||||
<input
|
||||
type="range"
|
||||
min="0.5"
|
||||
max={MAX_SPEED}
|
||||
step="0.1"
|
||||
value={speed}
|
||||
onChange={handleSpeedChange}
|
||||
className="slider-input"
|
||||
/>
|
||||
</div>
|
||||
<div className="speed-label max-value">4x</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{subModule === "analysis" && (
|
||||
<div className="processDisplayer">
|
||||
<div className="start-displayer timmer">00:00</div>
|
||||
<div className="end-displayer timmer">24:00</div>
|
||||
<div
|
||||
className="process-wrapper"
|
||||
style={{ padding: expand ? "0px" : "5px 35px" }}
|
||||
>
|
||||
<div
|
||||
className="process-container"
|
||||
ref={processWrapperRef}
|
||||
onMouseDown={handleProcessMouseDown}
|
||||
>
|
||||
{process.map((item, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="process"
|
||||
style={{
|
||||
width: `${item.completed}%`,
|
||||
backgroundColor: getAvatarColor(index),
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="process-player"
|
||||
ref={processPlayerRef}
|
||||
style={{ left: playerPosition, position: "absolute" }}
|
||||
></div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="analysis">
|
||||
<div className="analysis-wrapper">
|
||||
<ProductionCapacity />
|
||||
<ThroughputSummary />
|
||||
</div>
|
||||
<ROISummary />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user