Merge remote-tracking branch 'origin/v2-ui' into v2
This commit is contained in:
commit
8b0daa3305
|
@ -3,23 +3,20 @@ import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
|
|||
import Dashboard from "./pages/Dashboard";
|
||||
import Project from "./pages/Project";
|
||||
import UserAuth from "./pages/UserAuth";
|
||||
import ToastProvider from "./components/templates/ToastProvider";
|
||||
import "./styles/main.scss"
|
||||
import "./styles/main.scss";
|
||||
import { LoggerProvider } from "./components/ui/log/LoggerContext";
|
||||
|
||||
const App: React.FC = () => {
|
||||
return (
|
||||
<ToastProvider>
|
||||
<LoggerProvider>
|
||||
<Router>
|
||||
<Routes>
|
||||
<Route
|
||||
path="/"
|
||||
element={<UserAuth />}
|
||||
/>
|
||||
<Route path="/" element={<UserAuth />} />
|
||||
<Route path="/dashboard" element={<Dashboard />} />
|
||||
<Route path="/project" element={<Project />} />
|
||||
</Routes>
|
||||
</Router>
|
||||
</ToastProvider>
|
||||
</LoggerProvider>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
||||
};
|
|
@ -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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -142,109 +142,109 @@ const RealTimeVisulization: React.FC = () => {
|
|||
});
|
||||
}, [selectedZone]);
|
||||
|
||||
const handleDrop = async (event: React.DragEvent<HTMLDivElement>) => {
|
||||
event.preventDefault();
|
||||
try {
|
||||
const email = localStorage.getItem("email") ?? "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
// const handleDrop = async (event: React.DragEvent<HTMLDivElement>) => {
|
||||
// event.preventDefault();
|
||||
// try {
|
||||
// const email = localStorage.getItem("email") ?? "";
|
||||
// const organization = email?.split("@")[1]?.split(".")[0];
|
||||
|
||||
const data = event.dataTransfer.getData("text/plain");
|
||||
if (widgetSubOption === "3D") return;
|
||||
if (!data || selectedZone.zoneName === "") return;
|
||||
// const data = event.dataTransfer.getData("text/plain");
|
||||
// if (widgetSubOption === "3D") return;
|
||||
// if (!data || selectedZone.zoneName === "") return;
|
||||
|
||||
const droppedData = JSON.parse(data);
|
||||
const canvasElement = document.getElementById("real-time-vis-canvas");
|
||||
if (!canvasElement) throw new Error("Canvas element not found");
|
||||
// const droppedData = JSON.parse(data);
|
||||
// const canvasElement = document.getElementById("real-time-vis-canvas");
|
||||
// if (!canvasElement) throw new Error("Canvas element not found");
|
||||
|
||||
const rect = canvasElement.getBoundingClientRect();
|
||||
const relativeX = event.clientX - rect.left;
|
||||
const relativeY = event.clientY - rect.top;
|
||||
// const rect = canvasElement.getBoundingClientRect();
|
||||
// const relativeX = event.clientX - rect.left;
|
||||
// const relativeY = event.clientY - rect.top;
|
||||
|
||||
// Widget dimensions
|
||||
const widgetWidth = droppedData.width ?? 125;
|
||||
const widgetHeight = droppedData.height ?? 100;
|
||||
// // Widget dimensions
|
||||
// const widgetWidth = droppedData.width ?? 125;
|
||||
// const widgetHeight = droppedData.height ?? 100;
|
||||
|
||||
// Center the widget at cursor
|
||||
const centerOffsetX = widgetWidth / 2;
|
||||
const centerOffsetY = widgetHeight / 2;
|
||||
// // Center the widget at cursor
|
||||
// const centerOffsetX = widgetWidth / 2;
|
||||
// const centerOffsetY = widgetHeight / 2;
|
||||
|
||||
const adjustedX = relativeX - centerOffsetX;
|
||||
const adjustedY = relativeY - centerOffsetY;
|
||||
// const adjustedX = relativeX - centerOffsetX;
|
||||
// const adjustedY = relativeY - centerOffsetY;
|
||||
|
||||
const finalPosition = determinePosition(rect, adjustedX, adjustedY);
|
||||
const [activeProp1, activeProp2] = getActiveProperties(finalPosition);
|
||||
// const finalPosition = determinePosition(rect, adjustedX, adjustedY);
|
||||
// const [activeProp1, activeProp2] = getActiveProperties(finalPosition);
|
||||
|
||||
let finalY = 0;
|
||||
let finalX = 0;
|
||||
// let finalY = 0;
|
||||
// let finalX = 0;
|
||||
|
||||
if (activeProp1 === "top") {
|
||||
finalY = adjustedY;
|
||||
} else {
|
||||
finalY = rect.height - (adjustedY + widgetHeight);
|
||||
}
|
||||
// if (activeProp1 === "top") {
|
||||
// finalY = adjustedY;
|
||||
// } else {
|
||||
// finalY = rect.height - (adjustedY + widgetHeight);
|
||||
// }
|
||||
|
||||
if (activeProp2 === "left") {
|
||||
finalX = adjustedX;
|
||||
} else {
|
||||
finalX = rect.width - (adjustedX + widgetWidth);
|
||||
}
|
||||
// if (activeProp2 === "left") {
|
||||
// finalX = adjustedX;
|
||||
// } else {
|
||||
// finalX = rect.width - (adjustedX + widgetWidth);
|
||||
// }
|
||||
|
||||
// Clamp to boundaries
|
||||
finalX = Math.max(0, Math.min(rect.width - widgetWidth, finalX));
|
||||
finalY = Math.max(0, Math.min(rect.height - widgetHeight, finalY));
|
||||
// // Clamp to boundaries
|
||||
// finalX = Math.max(0, Math.min(rect.width - widgetWidth, finalX));
|
||||
// finalY = Math.max(0, Math.min(rect.height - widgetHeight, finalY));
|
||||
|
||||
const boundedPosition = {
|
||||
...finalPosition,
|
||||
[activeProp1]: finalY,
|
||||
[activeProp2]: finalX,
|
||||
[activeProp1 === "top" ? "bottom" : "top"]: "auto",
|
||||
[activeProp2 === "left" ? "right" : "left"]: "auto",
|
||||
};
|
||||
// const boundedPosition = {
|
||||
// ...finalPosition,
|
||||
// [activeProp1]: finalY,
|
||||
// [activeProp2]: finalX,
|
||||
// [activeProp1 === "top" ? "bottom" : "top"]: "auto",
|
||||
// [activeProp2 === "left" ? "right" : "left"]: "auto",
|
||||
// };
|
||||
|
||||
const newObject = {
|
||||
...droppedData,
|
||||
id: generateUniqueId(),
|
||||
position: boundedPosition,
|
||||
};
|
||||
// const newObject = {
|
||||
// ...droppedData,
|
||||
// id: generateUniqueId(),
|
||||
// position: boundedPosition,
|
||||
// };
|
||||
|
||||
const existingZone =
|
||||
useDroppedObjectsStore.getState().zones[selectedZone.zoneName];
|
||||
if (!existingZone) {
|
||||
useDroppedObjectsStore
|
||||
.getState()
|
||||
.setZone(selectedZone.zoneName, selectedZone.zoneId);
|
||||
}
|
||||
// const existingZone =
|
||||
// useDroppedObjectsStore.getState().zones[selectedZone.zoneName];
|
||||
// if (!existingZone) {
|
||||
// useDroppedObjectsStore
|
||||
// .getState()
|
||||
// .setZone(selectedZone.zoneName, selectedZone.zoneId);
|
||||
// }
|
||||
|
||||
const addFloatingWidget = {
|
||||
organization,
|
||||
widget: newObject,
|
||||
zoneId: selectedZone.zoneId,
|
||||
};
|
||||
// const addFloatingWidget = {
|
||||
// organization,
|
||||
// widget: newObject,
|
||||
// zoneId: selectedZone.zoneId,
|
||||
// };
|
||||
|
||||
if (visualizationSocket) {
|
||||
visualizationSocket.emit("v2:viz-float:add", addFloatingWidget);
|
||||
}
|
||||
// if (visualizationSocket) {
|
||||
// visualizationSocket.emit("v2:viz-float:add", addFloatingWidget);
|
||||
// }
|
||||
|
||||
useDroppedObjectsStore
|
||||
.getState()
|
||||
.addObject(selectedZone.zoneName, newObject);
|
||||
// useDroppedObjectsStore
|
||||
// .getState()
|
||||
// .addObject(selectedZone.zoneName, newObject);
|
||||
|
||||
const droppedObjectsStore = useDroppedObjectsStore.getState();
|
||||
const currentZone = droppedObjectsStore.zones[selectedZone.zoneName];
|
||||
// const droppedObjectsStore = useDroppedObjectsStore.getState();
|
||||
// const currentZone = droppedObjectsStore.zones[selectedZone.zoneName];
|
||||
|
||||
if (currentZone && currentZone.zoneId === selectedZone.zoneId) {
|
||||
console.log(
|
||||
`Objects for Zone ${selectedZone.zoneId}:`,
|
||||
currentZone.objects
|
||||
);
|
||||
setFloatingWidget(currentZone.objects);
|
||||
} else {
|
||||
console.warn("Zone not found or zoneId mismatch");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error in handleDrop:", error);
|
||||
}
|
||||
};
|
||||
// if (currentZone && currentZone.zoneId === selectedZone.zoneId) {
|
||||
// console.log(
|
||||
// `Objects for Zone ${selectedZone.zoneId}:`,
|
||||
// currentZone.objects
|
||||
// );
|
||||
// setFloatingWidget(currentZone.objects);
|
||||
// } else {
|
||||
// console.warn("Zone not found or zoneId mismatch");
|
||||
// }
|
||||
// } catch (error) {
|
||||
// console.error("Error in handleDrop:", error);
|
||||
// }
|
||||
// };
|
||||
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
|
@ -301,16 +301,7 @@ const RealTimeVisulization: React.FC = () => {
|
|||
}
|
||||
`}
|
||||
</style>
|
||||
<div
|
||||
ref={containerRef}
|
||||
id="real-time-vis-canvas"
|
||||
className={`realTime-viz canvas ${isPlaying ? "playingFlase" : ""}`}
|
||||
style={{
|
||||
height: isPlaying || activeModule !== "visualization" ? "100vh" : "",
|
||||
width: isPlaying || activeModule !== "visualization" ? "100vw" : "",
|
||||
left: isPlaying || activeModule !== "visualization" ? "0%" : "",
|
||||
}}
|
||||
>
|
||||
<div ref={containerRef} className="realTime-viz">
|
||||
<div className="realTime-viz-wrapper">
|
||||
{openConfirmationPopup && (
|
||||
<RenderOverlay>
|
||||
|
@ -321,20 +312,6 @@ const RealTimeVisulization: React.FC = () => {
|
|||
/>
|
||||
</RenderOverlay>
|
||||
)}
|
||||
<div
|
||||
className="scene-container"
|
||||
style={{
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
borderRadius:
|
||||
isPlaying || activeModule !== "visualization" ? "" : "6px",
|
||||
}}
|
||||
role="application"
|
||||
onDrop={(event) => handleDrop(event)}
|
||||
onDragOver={(event) => event.preventDefault()}
|
||||
>
|
||||
<Scene />
|
||||
</div>
|
||||
{activeModule === "visualization" && selectedZone.zoneName !== "" && (
|
||||
<DroppedObjects />
|
||||
)}
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
import { generateUniqueId } from "../../../functions/generateUniqueId";
|
||||
import { useDroppedObjectsStore } from "../../../store/visualization/useDroppedObjectsStore";
|
||||
import { determinePosition } from "./determinePosition";
|
||||
import { getActiveProperties } from "./getActiveProperties";
|
||||
|
||||
interface HandleDropProps {
|
||||
widgetSubOption: any;
|
||||
visualizationSocket: any;
|
||||
selectedZone: any;
|
||||
setFloatingWidget: (value: any) => void;
|
||||
event: React.DragEvent<HTMLDivElement>
|
||||
}
|
||||
|
||||
export const createHandleDrop = ({
|
||||
widgetSubOption,
|
||||
visualizationSocket,
|
||||
selectedZone,
|
||||
setFloatingWidget,
|
||||
event,
|
||||
}: HandleDropProps) => {
|
||||
event.preventDefault();
|
||||
try {
|
||||
const email = localStorage.getItem("email") ?? "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
|
||||
const data = event.dataTransfer.getData("text/plain");
|
||||
if (widgetSubOption === "3D") return;
|
||||
if (!data || selectedZone.zoneName === "") return;
|
||||
|
||||
const droppedData = JSON.parse(data);
|
||||
const canvasElement = document.getElementById("real-time-vis-canvas");
|
||||
if (!canvasElement) throw new Error("Canvas element not found");
|
||||
|
||||
const rect = canvasElement.getBoundingClientRect();
|
||||
const relativeX = event.clientX - rect.left;
|
||||
const relativeY = event.clientY - rect.top;
|
||||
|
||||
// Widget dimensions
|
||||
const widgetWidth = droppedData.width ?? 125;
|
||||
const widgetHeight = droppedData.height ?? 100;
|
||||
|
||||
// Center the widget at cursor
|
||||
const centerOffsetX = widgetWidth / 2;
|
||||
const centerOffsetY = widgetHeight / 2;
|
||||
|
||||
const adjustedX = relativeX - centerOffsetX;
|
||||
const adjustedY = relativeY - centerOffsetY;
|
||||
|
||||
const finalPosition = determinePosition(rect, adjustedX, adjustedY);
|
||||
const [activeProp1, activeProp2] = getActiveProperties(finalPosition);
|
||||
|
||||
let finalY = 0;
|
||||
let finalX = 0;
|
||||
|
||||
if (activeProp1 === "top") {
|
||||
finalY = adjustedY;
|
||||
} else {
|
||||
finalY = rect.height - (adjustedY + widgetHeight);
|
||||
}
|
||||
|
||||
if (activeProp2 === "left") {
|
||||
finalX = adjustedX;
|
||||
} else {
|
||||
finalX = rect.width - (adjustedX + widgetWidth);
|
||||
}
|
||||
|
||||
// Clamp to boundaries
|
||||
finalX = Math.max(0, Math.min(rect.width - widgetWidth, finalX));
|
||||
finalY = Math.max(0, Math.min(rect.height - widgetHeight, finalY));
|
||||
|
||||
const boundedPosition = {
|
||||
...finalPosition,
|
||||
[activeProp1]: finalY,
|
||||
[activeProp2]: finalX,
|
||||
[activeProp1 === "top" ? "bottom" : "top"]: "auto",
|
||||
[activeProp2 === "left" ? "right" : "left"]: "auto",
|
||||
};
|
||||
|
||||
const newObject = {
|
||||
...droppedData,
|
||||
id: generateUniqueId(),
|
||||
position: boundedPosition,
|
||||
};
|
||||
|
||||
const existingZone =
|
||||
useDroppedObjectsStore.getState().zones[selectedZone.zoneName];
|
||||
if (!existingZone) {
|
||||
useDroppedObjectsStore
|
||||
.getState()
|
||||
.setZone(selectedZone.zoneName, selectedZone.zoneId);
|
||||
}
|
||||
|
||||
const addFloatingWidget = {
|
||||
organization,
|
||||
widget: newObject,
|
||||
zoneId: selectedZone.zoneId,
|
||||
};
|
||||
|
||||
if (visualizationSocket) {
|
||||
visualizationSocket.emit("v2:viz-float:add", addFloatingWidget);
|
||||
}
|
||||
|
||||
useDroppedObjectsStore
|
||||
.getState()
|
||||
.addObject(selectedZone.zoneName, newObject);
|
||||
|
||||
const droppedObjectsStore = useDroppedObjectsStore.getState();
|
||||
const currentZone = droppedObjectsStore.zones[selectedZone.zoneName];
|
||||
|
||||
if (currentZone && currentZone.zoneId === selectedZone.zoneId) {
|
||||
console.log(
|
||||
`Objects for Zone ${selectedZone.zoneId}:`,
|
||||
currentZone.objects
|
||||
);
|
||||
setFloatingWidget(currentZone.objects);
|
||||
} else {
|
||||
console.warn("Zone not found or zoneId mismatch");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error in handleDrop:", error);
|
||||
}
|
||||
};
|
|
@ -13,6 +13,7 @@ import {
|
|||
useWallItems,
|
||||
useZones,
|
||||
useLoadingProgress,
|
||||
useWidgetSubOption,
|
||||
} from "../store/store";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { usePlayButtonStore } from "../store/usePlayButtonStore";
|
||||
|
@ -22,12 +23,19 @@ import SimulationPlayer from "../components/ui/simulation/simulationPlayer";
|
|||
import KeyPressListener from "../utils/shortcutkeys/handleShortcutKeys";
|
||||
import { useSelectedUserStore } from "../store/useCollabStore";
|
||||
import FollowPerson from "../components/templates/FollowPerson";
|
||||
import ProductionCapacity from "../components/ui/analysis/ProductionCapacity";
|
||||
import ThroughputSummary from "../components/ui/analysis/ThroughputSummary";
|
||||
import ROISummary from "../components/ui/analysis/ROISummary";
|
||||
import Scene from "../modules/scene/scene";
|
||||
import { createHandleDrop } from "../modules/visualization/functions/handleUiDrop";
|
||||
import { useSelectedZoneStore } from "../store/visualization/useZoneStore";
|
||||
import { useFloatingWidget } from "../store/visualization/useDroppedObjectsStore";
|
||||
import { useLogger } from "../components/ui/log/LoggerContext";
|
||||
import RenderOverlay from "../components/templates/Overlay";
|
||||
import LogList from "../components/ui/log/LogList";
|
||||
import Footer from "../components/footer/Footer";
|
||||
|
||||
const Project: React.FC = () => {
|
||||
let navigate = useNavigate();
|
||||
const echo = useLogger();
|
||||
|
||||
const { activeModule, setActiveModule } = useModuleStore();
|
||||
const { loadingProgress } = useLoadingProgress();
|
||||
const { setUserName } = useUserName();
|
||||
|
@ -50,42 +58,79 @@ const Project: React.FC = () => {
|
|||
setOrganization(Organization);
|
||||
setUserName(name);
|
||||
}
|
||||
echo.info("Log in success full");
|
||||
} else {
|
||||
navigate("/");
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
// global store
|
||||
const { toggleThreeD } = useThreeDStore();
|
||||
|
||||
// simulation store
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
|
||||
// collaboration store
|
||||
const { selectedUser } = useSelectedUserStore();
|
||||
const { isLogListVisible } = useLogger();
|
||||
|
||||
// real-time visualization store
|
||||
const { widgetSubOption } = useWidgetSubOption();
|
||||
const { visualizationSocket } = useSocketStore();
|
||||
const { selectedZone } = useSelectedZoneStore();
|
||||
const { setFloatingWidget } = useFloatingWidget();
|
||||
|
||||
return (
|
||||
<div className="project-main">
|
||||
{/* <div className="analysis">
|
||||
<div className="analysis-wrapper">
|
||||
<ProductionCapacity />
|
||||
<ThroughputSummary />
|
||||
</div>
|
||||
<ROISummary />
|
||||
</div> */}
|
||||
<KeyPressListener />
|
||||
{loadingProgress > 0 && <LoadingPage progress={loadingProgress} />}
|
||||
{!isPlaying && (
|
||||
{!selectedUser && (
|
||||
<>
|
||||
{toggleThreeD && <ModuleToggle />}
|
||||
<SideBarLeft />
|
||||
<SideBarRight />
|
||||
<KeyPressListener />
|
||||
{loadingProgress > 0 && <LoadingPage progress={loadingProgress} />}
|
||||
{!isPlaying && (
|
||||
<>
|
||||
{toggleThreeD && <ModuleToggle />}
|
||||
<SideBarLeft />
|
||||
<SideBarRight />
|
||||
</>
|
||||
)}
|
||||
<RealTimeVisulization />
|
||||
{activeModule === "market" && <MarketPlace />}
|
||||
{activeModule !== "market" && <Tools />}
|
||||
{isPlaying && activeModule === "simulation" && <SimulationPlayer />}
|
||||
</>
|
||||
)}
|
||||
{/* <RenderOverlay>
|
||||
<MenuBar setOpenMenu={setOpenMenu} />
|
||||
</RenderOverlay> */}
|
||||
{activeModule === "market" && <MarketPlace />}
|
||||
<RealTimeVisulization />
|
||||
{activeModule !== "market" && <Tools />}
|
||||
{isPlaying && activeModule === "simulation" && <SimulationPlayer />}
|
||||
{/* {<SimulationPlayer />} */}
|
||||
<div
|
||||
className="scene-container"
|
||||
id="real-time-vis-canvas"
|
||||
style={{
|
||||
height: isPlaying || activeModule !== "visualization" ? "100vh" : "",
|
||||
width: isPlaying || activeModule !== "visualization" ? "100vw" : "",
|
||||
left: isPlaying || activeModule !== "visualization" ? "0%" : "",
|
||||
borderRadius:
|
||||
isPlaying || activeModule !== "visualization" ? "" : "6px",
|
||||
}}
|
||||
role="application"
|
||||
onDrop={(event) =>
|
||||
createHandleDrop({
|
||||
widgetSubOption,
|
||||
visualizationSocket,
|
||||
selectedZone,
|
||||
setFloatingWidget,
|
||||
event,
|
||||
})
|
||||
}
|
||||
onDragOver={(event) => event.preventDefault()}
|
||||
>
|
||||
<Scene />
|
||||
</div>
|
||||
{selectedUser && <FollowPerson />}
|
||||
{isLogListVisible && (
|
||||
<RenderOverlay>
|
||||
<LogList />
|
||||
</RenderOverlay>
|
||||
)}
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { create } from "zustand";
|
||||
import { addingFloatingWidgets } from "../../services/visulization/zone/addFloatingWidgets";
|
||||
import { useSocketStore } from "../store";
|
||||
import useChartStore from "./useChartStore";
|
||||
|
||||
|
|
|
@ -22,7 +22,11 @@ $text-button-color-dark: #f3f3fd;
|
|||
// background colors
|
||||
// ---------- light mode ----------
|
||||
$background-color: linear-gradient(-45deg, #fcfdfd71 0%, #fcfdfd79 100%);
|
||||
$background-color-solid-gradient: linear-gradient(-45deg, #fcfdfd 0%, #fcfdfd 100%);
|
||||
$background-color-solid-gradient: linear-gradient(
|
||||
-45deg,
|
||||
#fcfdfd 0%,
|
||||
#fcfdfd 100%
|
||||
);
|
||||
$background-color-solid: #fcfdfd;
|
||||
$background-color-secondary: #fcfdfd4d;
|
||||
$background-color-accent: #6f42c1;
|
||||
|
@ -45,7 +49,11 @@ $background-radial-gray-gradient: radial-gradient(
|
|||
|
||||
// ---------- dark mode ----------
|
||||
$background-color-dark: linear-gradient(-45deg, #333333b3 0%, #2d2437b3 100%);
|
||||
$background-color-solid-gradient-dark: linear-gradient(-45deg, #333333 0%, #2d2437 100%);
|
||||
$background-color-solid-gradient-dark: linear-gradient(
|
||||
-45deg,
|
||||
#333333 0%,
|
||||
#2d2437 100%
|
||||
);
|
||||
$background-color-solid-dark: #19191d;
|
||||
$background-color-secondary-dark: #19191d99;
|
||||
$background-color-accent-dark: #6f42c1;
|
||||
|
@ -104,6 +112,21 @@ $color3: #b186ff;
|
|||
$color4: #8752e8;
|
||||
$color5: #c7a8ff;
|
||||
|
||||
// log indication colors
|
||||
// ------------ text -------------
|
||||
$log-default-text-color: #6f42c1;
|
||||
$log-info-text-color: #488ef6;
|
||||
$log-warn-text-color: #f3a50c;
|
||||
$log-error-text-color: #f65648;
|
||||
$log-success-text-color: #43c06d;
|
||||
|
||||
// ------------ background -------------
|
||||
$log-default-backgroung-color: #6e42c133;
|
||||
$log-info-background-color: #488ef633;
|
||||
$log-warn-background-color: #f3a50c33;
|
||||
$log-error-background-color: #f6564833;
|
||||
$log-success-background-color: #43c06d33;
|
||||
|
||||
// old variables
|
||||
$accent-color: #6f42c1;
|
||||
$accent-color-dark: #c4abf1;
|
||||
|
|
|
@ -37,11 +37,9 @@
|
|||
|
||||
// old colors
|
||||
--accent-color: #{$accent-color};
|
||||
--highlight-accent-color: #{$highlight-accent-color};
|
||||
--accent-gradient-color: #{$acent-gradient};
|
||||
--faint-gradient-color: #{$faint-gradient};
|
||||
--background-color-gray: #{$background-color-gray};
|
||||
--border-color: #{$border-color};
|
||||
--shadow-main-light: #{$shadow-color};
|
||||
--box-shadow-light: 0px 2px 4px var(--shadow-main-light);
|
||||
--box-shadow-medium: 0px 4px 8px var(--shadow-main-light);
|
||||
|
@ -75,7 +73,7 @@
|
|||
--background-radial-gray-gradient: #{$background-radial-gray-gradient-dark};
|
||||
|
||||
// border colors
|
||||
--border-color: #{$border-color};
|
||||
--border-color: #{$border-color-dark};
|
||||
--input-border-color: #{$input-border-color-dark};
|
||||
--border-color-accent: #{$border-color-accent-dark};
|
||||
|
||||
|
@ -89,11 +87,9 @@
|
|||
|
||||
// old colors
|
||||
--accent-color: #{$accent-color-dark};
|
||||
--highlight-accent-color: #{$highlight-accent-color-dark};
|
||||
--accent-gradient-color: #{$acent-gradient-dark};
|
||||
--faint-gradient-color: #{$faint-gradient-dark};
|
||||
--background-color-gray: #{$background-color-gray-dark};
|
||||
--border-color: #{$border-color-dark};
|
||||
--shadow-main-dark: #{$shadow-color};
|
||||
--box-shadow-light: 0px 2px 4px var(--shadow-main-dark);
|
||||
--box-shadow-medium: 0px 4px 8px var(--shadow-main-dark);
|
||||
|
|
|
@ -1,11 +1,30 @@
|
|||
@use "../abstracts/variables" as *;
|
||||
@use "../abstracts/mixins" as *;
|
||||
|
||||
section, .section{
|
||||
padding: 4px;
|
||||
outline: 1px solid var(--border-color);
|
||||
outline-offset: -1px;
|
||||
border-radius: #{$border-radius-large};
|
||||
background: var(--background-color);
|
||||
margin: 4px 0;
|
||||
section,
|
||||
.section {
|
||||
padding: 4px;
|
||||
outline: 1px solid var(--border-color);
|
||||
outline-offset: -1px;
|
||||
border-radius: #{$border-radius-large};
|
||||
background: var(--background-color);
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.scene-container {
|
||||
width: calc(100% - (320px + 270px + 90px));
|
||||
height: calc(100% - (250px));
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: calc(270px + 45px);
|
||||
overflow: hidden;
|
||||
z-index: 1;
|
||||
transform: translate(0, -50%);
|
||||
transition: all 0.2s;
|
||||
box-shadow: $box-shadow-medium;
|
||||
background: var(--background-color-solid);
|
||||
canvas {
|
||||
outline: none;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,10 +12,3 @@ input[type="password"]::-webkit-clear-button, /* For Chrome/Safari clear button
|
|||
input[type="password"]::-webkit-inner-spin-button { /* Just in case */
|
||||
display: none;
|
||||
}
|
||||
|
||||
button{
|
||||
border: none;
|
||||
outline: none;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
}
|
|
@ -1,311 +0,0 @@
|
|||
.roiSummary-container {
|
||||
.roiSummary-wrapper {
|
||||
background-color: var(--background-color);
|
||||
|
||||
.product-info {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.playBack {
|
||||
display: flex;
|
||||
background-color: var(--background-color);
|
||||
border-radius: 12px;
|
||||
padding: 6px;
|
||||
|
||||
.info {
|
||||
span {
|
||||
font-size: var(--font-size-xlarge);
|
||||
|
||||
&:first-child {
|
||||
color: #31C756;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
color: var(--text-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.roi-details {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
|
||||
.progress-wrapper {
|
||||
width: 250px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3px;
|
||||
align-items: center;
|
||||
|
||||
.key {
|
||||
font-size: var(--font-size-xlarge);
|
||||
color: var(--accent-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.roi-progress {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.metrics {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
|
||||
.metric-item {
|
||||
width: 100%;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #00FF56;
|
||||
background: #436D51;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 4px 6px;
|
||||
|
||||
&:last-child {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.metric-label {
|
||||
font-size: 10px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.metric-value {
|
||||
text-align: center;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.metric-wrapper {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
|
||||
.metric-item {
|
||||
background-color: var(--background-color);
|
||||
border: 1px solid var(--Grays-Gray-6, #F2F2F7);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cost-breakdown {
|
||||
background-color: var(--background-color);
|
||||
border: 1px solid var(--text-disabled);
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
|
||||
.breakdown-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
|
||||
.section-wrapper {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.section-number {
|
||||
font-size: 20px;
|
||||
color: #00aaff;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: var(--font-size-regular);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.expand-icon {
|
||||
font-size: 16px;
|
||||
color: var(--text-color);
|
||||
cursor: pointer;
|
||||
transform: rotate(90deg);
|
||||
transition: transform 0.2s linear;
|
||||
}
|
||||
|
||||
.expand-icon.open {
|
||||
transform: rotate(0deg);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.breakdown-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
border-radius: 8px;
|
||||
|
||||
th,
|
||||
td {
|
||||
padding: 8px;
|
||||
text-align: left;
|
||||
border-top: 1px solid var(--text-disabled);
|
||||
border-bottom: 1px solid var(--text-disabled);
|
||||
}
|
||||
|
||||
th:first-child,
|
||||
td:first-child {
|
||||
border-left: 1px solid var(--text-disabled);
|
||||
}
|
||||
|
||||
th:last-child,
|
||||
td:last-child {
|
||||
border-right: 1px solid var(--text-disabled);
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: var(--background-color);
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.total-row,
|
||||
.net-profit-row {
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tips-section {
|
||||
background-color: var(--background-color);
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
padding: 12px;
|
||||
|
||||
.tip-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.tip-title {
|
||||
color: var(--text-color);
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.tip-description {
|
||||
span {
|
||||
font-size: var(--font-size-xlarge);
|
||||
color: #34C759;
|
||||
|
||||
&:first-child {
|
||||
color: #34C759;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
color: #488EF6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.get-tips-button {
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
margin-top: 8px;
|
||||
display: inline-block;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
background: none;
|
||||
|
||||
.btn {
|
||||
background-color: var(--accent-color);
|
||||
color: var(--background-color);
|
||||
padding: 4px 6px;
|
||||
border-radius: 5px;
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.semi-circle-wrapper {
|
||||
width: 100%;
|
||||
height: 125px;
|
||||
overflow-y: hidden;
|
||||
position: relative;
|
||||
.semi-circle {
|
||||
width: 100%;
|
||||
height: 250px;
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
transition: background 0.5s ease;
|
||||
}
|
||||
.progress-cover {
|
||||
position: absolute;
|
||||
width: 75%;
|
||||
height: 75%;
|
||||
top: 12.5%;
|
||||
left: 12.5%;
|
||||
background: #000000cc;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.label-wrapper {
|
||||
.label {
|
||||
font-size: var(--font-size-xxxlarge);
|
||||
}
|
||||
|
||||
position: absolute;
|
||||
bottom: 0%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0%);
|
||||
font-weight: bold;
|
||||
font-size: 1.2rem;
|
||||
color: #333;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
// Breakdown Table Open/Close Logic
|
||||
|
||||
.breakdown-table-wrapper {
|
||||
&.closed {
|
||||
max-height: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&.open {
|
||||
max-height: 500px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.breakdown-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
|
||||
th,
|
||||
td {
|
||||
padding: 10px;
|
||||
border: 1px solid #ddd;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -1,279 +0,0 @@
|
|||
.analysis {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: start;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
// pointer-events: none;
|
||||
z-index: 10000;
|
||||
|
||||
.analysis-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.analysis-card {
|
||||
min-width: 333px;
|
||||
background: var(--background-color);
|
||||
border-radius: 20px;
|
||||
|
||||
padding: 8px;
|
||||
|
||||
.analysis-card-wrapper {
|
||||
width: 100%;
|
||||
background: var(--background-color);
|
||||
border-radius: 14px;
|
||||
padding: 16px;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
|
||||
.card-header {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.main-header {
|
||||
line-height: 20px;
|
||||
font-size: var(--font-size-regular);
|
||||
}
|
||||
}
|
||||
|
||||
.process-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.throughput-value {
|
||||
font-size: 1rem;
|
||||
|
||||
.value {
|
||||
font-weight: bold;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.progress-bar-wrapper {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
position: relative;
|
||||
// width: 36px;
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
border-radius: 13px;
|
||||
overflow: hidden;
|
||||
background: #FBEBD7;
|
||||
|
||||
.bar-fill {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background: #FC9D2F;
|
||||
border-radius: 13px;
|
||||
}
|
||||
|
||||
.bar-fill.full {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.bar-fill.partial {
|
||||
width: 0; // inline style will override this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.metrics-section {
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid var(--background-color-gray);
|
||||
|
||||
.metric {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
margin-bottom: 8px;
|
||||
|
||||
.label {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.value {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.throughoutSummary {
|
||||
.throughoutSummary-wrapper {
|
||||
.process-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 16px;
|
||||
width: 100%;
|
||||
|
||||
.throughput-value {
|
||||
font-size: var(--font-size-small);
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.value {
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
/* Let the text take available space */
|
||||
}
|
||||
|
||||
.lineChart {
|
||||
max-width: 200px;
|
||||
height: 100px;
|
||||
position: relative;
|
||||
|
||||
.assetUsage {
|
||||
text-align: right;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
canvas {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
gap: 16px; // Space between cards
|
||||
margin-top: 24px;
|
||||
|
||||
.footer-card {
|
||||
width: 100%;
|
||||
background: var(--background-color-gray);
|
||||
border-radius: 6px;
|
||||
padding: 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
|
||||
&:first-child {
|
||||
width: 85%;
|
||||
}
|
||||
|
||||
.header {
|
||||
font-size: var(--font-size-regular);
|
||||
}
|
||||
|
||||
.value-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: end;
|
||||
gap: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.shiftUtilization {
|
||||
.value-container {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
|
||||
.value {
|
||||
font-size: var(--font-size-xlarge);
|
||||
}
|
||||
|
||||
.progress-wrapper {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
|
||||
.progress {
|
||||
border-radius: 6px;
|
||||
height: 5px;
|
||||
|
||||
&:nth-child(1) {
|
||||
background: #F3C64D;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
background: #67B3F4;
|
||||
}
|
||||
|
||||
&:nth-child(3) {
|
||||
background: #7981F5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.progress-indicator {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
gap: 6px;
|
||||
|
||||
.shift-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
|
||||
/* Align items vertically */
|
||||
&:nth-child(1) {
|
||||
.indicator {
|
||||
|
||||
background: #F3C64D;
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
.indicator {
|
||||
|
||||
background: #67B3F4;
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-child(3) {
|
||||
.indicator {
|
||||
|
||||
background: #7981F5;
|
||||
}
|
||||
}
|
||||
|
||||
label {
|
||||
font-size: var(--font-size-small);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.indicator {
|
||||
display: inline-block;
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
border-radius: 50%;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
@use "../abstracts/variables" as *;
|
||||
@use "../abstracts/mixins" as *;
|
||||
|
||||
.labeled-button-container {
|
||||
@include flex-space-between;
|
||||
padding: 6px 12px;
|
||||
|
||||
button {
|
||||
padding: 2px 32px;
|
||||
border: none;
|
||||
border-radius: #{$border-radius-large};
|
||||
color: var(--text-button-color);
|
||||
background: var(--background-color-button);
|
||||
transition: all 0.2s;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
border: none;
|
||||
outline: none;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
@use "../../abstracts/variables" as *;
|
||||
@use "../../abstracts/mixins" as *;
|
||||
|
||||
.footer-wrapper {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 2px 12px;
|
||||
|
||||
.selection-wrapper {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
|
||||
.selector-wrapper {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
background: var(--background-color);
|
||||
padding: 3px 6px;
|
||||
border-radius: 12px;
|
||||
color: var(--text-color);
|
||||
|
||||
.selector {
|
||||
color: var(--text-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.logs-wrapper {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
|
||||
.logs-detail,
|
||||
.version {
|
||||
border-radius: 12px;
|
||||
background: var(--background-color);
|
||||
padding: 3px 6px;
|
||||
color: var(--text-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.logs-detail {
|
||||
padding: 2px 12px;
|
||||
cursor: pointer;
|
||||
.log-icon {
|
||||
@include flex-center;
|
||||
}
|
||||
.log-message {
|
||||
max-width: 40vw;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
.version {
|
||||
font-size: var(--font-size-tiny);
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
.icon{
|
||||
@include flex-center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -639,21 +639,6 @@ input[type="number"] {
|
|||
}
|
||||
}
|
||||
|
||||
.labeled-button-container {
|
||||
@include flex-space-between;
|
||||
padding: 6px 12px;
|
||||
|
||||
button {
|
||||
padding: 2px 32px;
|
||||
border: none;
|
||||
border-radius: #{$border-radius-large};
|
||||
color: var(--text-button-color);
|
||||
background: var(--background-color-button);
|
||||
transition: all 0.2s;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.value-field-container {
|
||||
margin-bottom: 6px;
|
||||
padding: 6px 12px;
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
@use "../../abstracts/variables" as *;
|
||||
@use "../../abstracts/mixins" as *;
|
||||
|
||||
.log-list-container {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background: var(--background-color-secondary);
|
||||
|
||||
.log-list-wrapper {
|
||||
height: 50%;
|
||||
min-width: 50%;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 5;
|
||||
background: var(--background-color);
|
||||
padding: 14px 12px;
|
||||
border-radius: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
backdrop-filter: blur(50px);
|
||||
outline: 1px solid var(--border-color);
|
||||
|
||||
.log-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.log-header-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.close {
|
||||
@include flex-center;
|
||||
height: 28px;
|
||||
width: 28px;
|
||||
cursor: pointer;
|
||||
svg {
|
||||
scale: 1.6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.log-nav-wrapper {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
|
||||
.log-nav {
|
||||
padding: 8px 16px;
|
||||
border-radius: 19px;
|
||||
}
|
||||
|
||||
.log-nav.active {
|
||||
background-color: var(--background-color-accent);
|
||||
color: var(--text-button-color);
|
||||
}
|
||||
}
|
||||
|
||||
.log-entry-wrapper {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
background: var(--background-color);
|
||||
padding: 18px 10px;
|
||||
border-radius: 16px;
|
||||
outline: 1px solid var(--border-color);
|
||||
outline-offset: -1px;
|
||||
|
||||
.log-entry {
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
font-size: var(--font-size-small);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
|
||||
.log-icon {
|
||||
@include flex-center;
|
||||
}
|
||||
.log-entry-message-container {
|
||||
@include flex-space-between;
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
.message-time {
|
||||
font-size: var(--font-size-tiny);
|
||||
font-weight: 300;
|
||||
opacity: 0.8;
|
||||
text-wrap: nowrap;
|
||||
}
|
||||
.log-entry-message{
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-child(odd) {
|
||||
background: var(--background-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,557 @@
|
|||
@use "../../abstracts/variables" as *;
|
||||
@use "../../abstracts/mixins" as *;
|
||||
|
||||
.analysis {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: start;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
pointer-events: none;
|
||||
padding: 10px;
|
||||
z-index: 2;
|
||||
|
||||
.analysis-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
.analysis-card {
|
||||
min-width: 333px;
|
||||
border-radius: 20px;
|
||||
padding: 8px;
|
||||
pointer-events: all;
|
||||
|
||||
.analysis-card-wrapper {
|
||||
width: 100%;
|
||||
background: var(--background-color);
|
||||
border-radius: 14px;
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
backdrop-filter: blur(10px);
|
||||
outline: 1px solid var(--border-color);
|
||||
outline-offset: -1px;
|
||||
|
||||
.card-header {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.main-header {
|
||||
line-height: 20px;
|
||||
font-size: var(--font-size-regular);
|
||||
}
|
||||
}
|
||||
|
||||
.process-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.throughput-value {
|
||||
font-size: 1rem;
|
||||
|
||||
.value {
|
||||
font-weight: bold;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.progress-bar-wrapper {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
border-radius: 13px;
|
||||
overflow: hidden;
|
||||
background: #fbebd7;
|
||||
|
||||
.bar-fill {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background: #fc9d2f;
|
||||
border-radius: 13px;
|
||||
}
|
||||
|
||||
.bar-fill.full {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.bar-fill.partial {
|
||||
width: 0; // inline style will override this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.metrics-section {
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid var(--background-color-gray);
|
||||
|
||||
.metric {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
margin-bottom: 8px;
|
||||
|
||||
.label {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.value {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.throughoutSummary-wrapper {
|
||||
.process-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 16px;
|
||||
width: 100%;
|
||||
|
||||
.throughput-value {
|
||||
font-size: var(--font-size-small);
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.value {
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
/* Let the text take available space */
|
||||
}
|
||||
|
||||
.lineChart {
|
||||
max-width: 200px;
|
||||
height: 100px;
|
||||
position: relative;
|
||||
|
||||
.assetUsage {
|
||||
text-align: right;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
canvas {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
gap: 16px; // Space between cards
|
||||
margin-top: 24px;
|
||||
|
||||
.footer-card {
|
||||
width: 100%;
|
||||
background: var(--background-color-gray);
|
||||
border-radius: 6px;
|
||||
padding: 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
|
||||
&:first-child {
|
||||
width: 85%;
|
||||
}
|
||||
|
||||
.header {
|
||||
font-size: var(--font-size-regular);
|
||||
}
|
||||
|
||||
.value-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: end;
|
||||
gap: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.shiftUtilization {
|
||||
.value-container {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
|
||||
.value {
|
||||
font-size: var(--font-size-xlarge);
|
||||
}
|
||||
|
||||
.progress-wrapper {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
|
||||
.progress {
|
||||
border-radius: 6px;
|
||||
height: 5px;
|
||||
|
||||
&:nth-child(1) {
|
||||
background: #f3c64d;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
background: #67b3f4;
|
||||
}
|
||||
|
||||
&:nth-child(3) {
|
||||
background: #7981f5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.progress-indicator {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
gap: 6px;
|
||||
|
||||
.shift-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
|
||||
/* Align items vertically */
|
||||
&:nth-child(1) {
|
||||
.indicator {
|
||||
background: #f3c64d;
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
.indicator {
|
||||
background: #67b3f4;
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-child(3) {
|
||||
.indicator {
|
||||
background: #7981f5;
|
||||
}
|
||||
}
|
||||
|
||||
label {
|
||||
font-size: var(--font-size-small);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.indicator {
|
||||
display: inline-block;
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.roiSummary-wrapper {
|
||||
max-width: 470px;
|
||||
background-color: var(--background-color);
|
||||
|
||||
.product-info {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.playBack {
|
||||
display: flex;
|
||||
background-color: var(--background-color);
|
||||
border-radius: 12px;
|
||||
padding: 6px;
|
||||
|
||||
.info {
|
||||
span {
|
||||
font-size: var(--font-size-xlarge);
|
||||
|
||||
&:first-child {
|
||||
color: #31c756;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
color: var(--text-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.roi-details {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
|
||||
.progress-wrapper {
|
||||
width: 250px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3px;
|
||||
align-items: center;
|
||||
|
||||
.key {
|
||||
font-size: var(--font-size-xlarge);
|
||||
color: var(--accent-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.roi-progress {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.metrics {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
|
||||
.metric-item {
|
||||
width: 100%;
|
||||
border-radius: #{$border-radius-xxx};
|
||||
border: 1px solid #00ff56;
|
||||
background: #17eb5e42;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 4px 8px;
|
||||
|
||||
&:last-child {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.metric-label {
|
||||
opacity: 0.8;
|
||||
font-size: 10px;
|
||||
font-weight: 300;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.metric-value {
|
||||
text-align: center;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.metric-wrapper {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
|
||||
.metric-item {
|
||||
border-radius: #{$border-radius-large};
|
||||
background-color: var(--background-color);
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cost-breakdown {
|
||||
background-color: var(--background-color);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: #{$border-radius-extra-large};
|
||||
padding: 16px;
|
||||
|
||||
.breakdown-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
|
||||
.section-wrapper {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.section-number {
|
||||
color: #00aaff;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: var(--font-size-regular);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.expand-icon {
|
||||
font-size: 16px;
|
||||
color: var(--text-color);
|
||||
cursor: pointer;
|
||||
transform: rotate(90deg);
|
||||
transition: transform 0.2s linear;
|
||||
}
|
||||
|
||||
.expand-icon.open {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
}
|
||||
|
||||
.breakdown-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
outline: 1px solid var(--border-color);
|
||||
outline-offset: -1px;
|
||||
margin-top: 12px;
|
||||
|
||||
th,
|
||||
td {
|
||||
color: var(--text-color);
|
||||
padding: 8px;
|
||||
text-align: left;
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
th {
|
||||
background-color: var(--background-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tips-section {
|
||||
background-color: var(--background-color);
|
||||
border-radius: #{$border-radius-large};
|
||||
outline: 1px solid var(--border-color);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
padding: 12px;
|
||||
|
||||
.tip-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.tip-title {
|
||||
color: var(--text-color);
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.tip-description {
|
||||
span {
|
||||
font-size: var(--font-size-xlarge);
|
||||
color: #34c759;
|
||||
|
||||
&:first-child {
|
||||
color: #34c759;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
color: #488ef6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.get-tips-button {
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
margin-top: 8px;
|
||||
display: inline-block;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
background: none;
|
||||
|
||||
.btn {
|
||||
color: var(--text-button-color);
|
||||
background: var(--background-color-button);
|
||||
padding: 4px 12px;
|
||||
border-radius: #{$border-radius-large};
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.semi-circle-wrapper {
|
||||
width: 100%;
|
||||
height: 125px;
|
||||
overflow-y: hidden;
|
||||
position: relative;
|
||||
.semi-circle {
|
||||
width: 100%;
|
||||
height: 250px;
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
}
|
||||
.progress-cover {
|
||||
position: absolute;
|
||||
width: 75%;
|
||||
height: 75%;
|
||||
top: 12.5%;
|
||||
left: 12.5%;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.label-wrapper {
|
||||
.label {
|
||||
font-size: var(--font-size-xxxlarge);
|
||||
}
|
||||
|
||||
position: absolute;
|
||||
bottom: 0%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0%);
|
||||
font-weight: bold;
|
||||
font-size: 1.2rem;
|
||||
color: #333;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
.breakdown-table-wrapper {
|
||||
&.closed {
|
||||
max-height: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&.open {
|
||||
max-height: 500px;
|
||||
}
|
||||
|
||||
.breakdown-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
|
||||
th,
|
||||
td {
|
||||
padding: 10px;
|
||||
border: 1px solid #ddd;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Breakdown Table Open/Close Logic
|
||||
|
|
@ -32,10 +32,17 @@
|
|||
}
|
||||
|
||||
.controls-container {
|
||||
@include flex-center;
|
||||
@include flex-space-between;
|
||||
gap: 12px;
|
||||
justify-content: space-between;
|
||||
|
||||
.header{
|
||||
@include flex-center;
|
||||
gap: 6px;
|
||||
padding: 0 8px;
|
||||
svg{
|
||||
scale: 1.3;
|
||||
}
|
||||
}
|
||||
.production-details,
|
||||
.controls-wrapper {
|
||||
@include flex-center;
|
||||
|
@ -72,7 +79,7 @@
|
|||
|
||||
.expand-icon-container {
|
||||
@include flex-center;
|
||||
padding: 6px 8px;
|
||||
padding: 0 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
@ -307,6 +314,22 @@
|
|||
font-size: var(--font-size-tiny);
|
||||
}
|
||||
|
||||
.timmer {
|
||||
width: auto;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
font-size: var(--font-size-tiny);
|
||||
}
|
||||
|
||||
.start-displayer {
|
||||
left: 8px;
|
||||
}
|
||||
|
||||
.end-displayer {
|
||||
width: auto;
|
||||
right: 8px;
|
||||
}
|
||||
|
||||
.start-displayer {
|
||||
bottom: 4px;
|
||||
left: 16px;
|
||||
|
@ -347,6 +370,12 @@
|
|||
}
|
||||
|
||||
.simulation-player-container.open {
|
||||
|
||||
.start-displayer,
|
||||
.end-displayer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.timmer {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
transition: width 0.2s;
|
||||
background: var(--background-color);
|
||||
backdrop-filter: blur(8px);
|
||||
z-index: #{$z-index-default};
|
||||
z-index: 2;
|
||||
outline: 1px solid var(--border-color);
|
||||
outline-offset: -1px;
|
||||
|
||||
|
@ -124,6 +124,8 @@
|
|||
padding: 4px;
|
||||
border-radius: #{$border-radius-medium};
|
||||
background: var(--background-color);
|
||||
outline: 1px solid var(--border-color);
|
||||
outline-offset: -1px;
|
||||
gap: 5px;
|
||||
position: relative;
|
||||
|
||||
|
|
|
@ -58,12 +58,8 @@
|
|||
fill: var(--icon-default-color-active);
|
||||
}
|
||||
&:hover {
|
||||
rect {
|
||||
stroke: var(--icon-default-color);
|
||||
}
|
||||
circle {
|
||||
fill: var(--icon-default-color);
|
||||
}
|
||||
filter: saturate(0.8);
|
||||
background: var(--background-color-accent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -420,7 +416,7 @@
|
|||
outline: none;
|
||||
path {
|
||||
stroke: var(--text-button-color);
|
||||
stroke-width: 1.3;
|
||||
strokeWidth: 1.3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -686,7 +682,7 @@
|
|||
|
||||
path {
|
||||
stroke: var(--accent-color);
|
||||
stroke-width: 1.5px;
|
||||
strokeWidth: 1.5px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
|
@ -714,10 +710,10 @@
|
|||
|
||||
.add-button {
|
||||
@include flex-center;
|
||||
padding: 2px 4px;
|
||||
padding: 4px 8px;
|
||||
background: var(--background-color-button);
|
||||
color: var(--text-button-color);
|
||||
border-radius: #{$border-radius-small};
|
||||
border-radius: #{$border-radius-large};
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
border: none;
|
||||
|
@ -832,10 +828,10 @@
|
|||
transform: translateX(4px);
|
||||
|
||||
&:hover {
|
||||
background: var(--accent-color);
|
||||
background: var(--background-color-accent);
|
||||
|
||||
path {
|
||||
stroke: var(--primary-color);
|
||||
stroke: var(--text-button-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1003,10 +999,10 @@
|
|||
border-radius: 8px 0 0 8px;
|
||||
|
||||
&:hover {
|
||||
background: var(--accent-color);
|
||||
background: var(--background-color-accent);
|
||||
|
||||
path {
|
||||
stroke: var(--primary-color);
|
||||
stroke: var(--text-button-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1067,7 +1063,13 @@
|
|||
.dropdown-content-container {
|
||||
padding: 6px 12px;
|
||||
}
|
||||
|
||||
.value-field-container {
|
||||
padding: 6px;
|
||||
.dropdown {
|
||||
min-width: 44px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
.input-range-container {
|
||||
.input-container {
|
||||
width: 75%;
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
@use 'components/button';
|
||||
@use 'components/form';
|
||||
@use 'components/input';
|
||||
@use 'components/layouts';
|
||||
@use 'components/lists';
|
||||
@use 'components/moduleToggle';
|
||||
@use 'components/templates';
|
||||
|
@ -22,11 +21,12 @@
|
|||
@use 'components/visualization/ui/styledWidgets';
|
||||
@use 'components/visualization/floating/common';
|
||||
@use 'components/marketPlace/marketPlace';
|
||||
@use 'components/simulation/simulation';
|
||||
@use 'components/menu/menu';
|
||||
@use 'components/confirmationPopUp';
|
||||
@use 'components/analysis/analysis';
|
||||
@use 'components/analysis/ROISummary.scss';
|
||||
@use 'components/simulation/simulation';
|
||||
@use 'components/simulation/analysis';
|
||||
@use 'components/logs/logs';
|
||||
@use 'components/footer/footer.scss';
|
||||
|
||||
// layout
|
||||
@use 'layout/loading';
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
|
||||
// Main Container
|
||||
.realTime-viz {
|
||||
background: #131313;
|
||||
box-shadow: $box-shadow-medium;
|
||||
width: calc(100% - (320px + 270px + 90px));
|
||||
height: calc(100% - (250px));
|
||||
position: absolute;
|
||||
|
@ -12,8 +10,8 @@
|
|||
left: calc(270px + 45px);
|
||||
transform: translate(0, -50%);
|
||||
border-radius: #{$border-radius-medium};
|
||||
transition: all 0.2s;
|
||||
z-index: #{$z-index-default};
|
||||
z-index: 2;
|
||||
pointer-events: none;
|
||||
|
||||
.realTime-viz-wrapper {
|
||||
width: 100%;
|
||||
|
@ -39,10 +37,6 @@
|
|||
z-index: 1;
|
||||
}
|
||||
|
||||
.scene-container {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
@ -74,6 +68,8 @@
|
|||
z-index: 3;
|
||||
transform: translate(-50%, -10%);
|
||||
transition: transform 0.5s linear;
|
||||
pointer-events: all;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
@ -367,6 +363,7 @@
|
|||
border-radius: 2px;
|
||||
transition: transform 0.3s ease;
|
||||
box-shadow: #{$box-shadow-medium};
|
||||
pointer-events: all;
|
||||
|
||||
svg {
|
||||
stroke: var(--icon-default-color) !important;
|
||||
|
@ -426,10 +423,8 @@
|
|||
|
||||
path {
|
||||
stroke: #f65648;
|
||||
stroke-width: 1.3;
|
||||
strokeWidth: 1.3;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -778,17 +773,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
.panel-content {
|
||||
background: var(--background-color);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* RIGHT */
|
||||
.panel-content.right-opening {
|
||||
animation: rightExpand 0.5s ease-in-out forwards;
|
||||
|
@ -913,9 +901,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Add button
|
||||
|
||||
.extra-Bs-addopening {
|
||||
|
@ -926,7 +911,6 @@
|
|||
animation: slideUp 0.3s ease forwards;
|
||||
}
|
||||
|
||||
|
||||
@keyframes slideDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
|
|
Loading…
Reference in New Issue