v2-ui #89

Merged
Vishnu merged 5 commits from v2-ui into main 2025-05-16 08:38:20 +00:00
22 changed files with 1784 additions and 620 deletions

View File

@ -386,3 +386,452 @@ export function StorageCapacityIcon() {
</svg> </svg>
); );
} }
export function CompareLayoutIcon() {
return (
<svg
width="182"
height="118"
viewBox="0 0 182 118"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g filter="url(#filter0_d_3106_8112)">
<rect x="24" y="4" width="134" height="106" rx="6" fill="#FCFDFD" />
<rect
x="24.5"
y="4.5"
width="133"
height="105"
rx="5.5"
stroke="#E5E5EA"
/>
</g>
<rect x="98" y="12" width="2" height="2" fill="#E5E5EA" />
<rect x="98" y="19" width="2" height="2" fill="#E5E5EA" />
<rect x="98" y="25" width="2" height="2" fill="#E5E5EA" />
<rect x="98" y="33" width="2" height="2" fill="#E5E5EA" />
<rect x="98" y="39" width="2" height="2" fill="#E5E5EA" />
<rect x="104" y="12" width="7" height="2" fill="#E5E5EA" />
<rect x="104" y="19" width="13" height="2" fill="#E5E5EA" />
<rect x="111" y="26" width="19" height="2" fill="#E5E5EA" />
<rect x="133" y="26" width="10" height="2" fill="#E5E5EA" />
<rect x="111" y="32" width="10" height="2" fill="#E5E5EA" />
<rect x="116" y="39" width="5" height="2" fill="#E5E5EA" />
<rect x="103" y="46" width="9" height="2" fill="#E5E5EA" />
<rect x="111" y="53" width="11" height="2" fill="#E5E5EA" />
<rect x="104" y="60" width="7" height="2" fill="#E5E5EA" />
<rect x="115" y="60" width="11" height="2" fill="#E5E5EA" />
<rect x="130" y="60" width="22" height="2" fill="#E5E5EA" />
<rect x="124" y="53" width="7" height="2" fill="#E5E5EA" />
<rect x="135" y="53" width="17" height="2" fill="#E5E5EA" />
<rect x="114" y="46" width="4" height="2" fill="#E5E5EA" />
<rect x="121" y="46" width="21" height="2" fill="#E5E5EA" />
<rect x="144" y="46" width="4" height="2" fill="#E5E5EA" />
<rect x="124" y="39" width="11" height="2" fill="#E5E5EA" />
<rect x="143" y="39" width="7" height="2" fill="#E5E5EA" />
<rect x="124" y="32" width="10" height="2" fill="#E5E5EA" />
<rect x="137" y="32" width="10" height="2" fill="#E5E5EA" />
<rect x="151" y="32" width="3" height="2" fill="#E5E5EA" />
<rect x="119" y="19" width="5" height="2" fill="#E5E5EA" />
<rect x="127" y="19" width="8" height="2" fill="#E5E5EA" />
<rect x="113" y="12" width="5" height="2" fill="#E5E5EA" />
<rect x="119" y="12" width="20" height="2" fill="#E5E5EA" />
<g filter="url(#filter1_f_3106_8112)">
<path d="M92 5V109.5" stroke="#B392F0" strokeLinecap="round" />
</g>
<path d="M92 5V109.5" stroke="#6F42C1" strokeLinecap="round" />
<g filter="url(#filter2_f_3106_8112)">
<circle cx="92" cy="57" r="12" fill="#6F42C1" />
</g>
<circle cx="92" cy="57" r="12" fill="#6F42C1" />
<g filter="url(#filter3_f_3106_8112)">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M89.9091 61.9119C89.7089 59.2881 87.6238 57.203 85 57.0028C87.6238 56.8027 89.7089 54.7175 89.9091 52.0938C90.1093 54.7175 92.1944 56.8027 94.8182 57.0028C92.1944 57.203 90.1093 59.2881 89.9091 61.9119Z"
fill="#FCFDFD"
/>
</g>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M89.9091 61.9119C89.7089 59.2881 87.6238 57.203 85 57.0028C87.6238 56.8027 89.7089 54.7175 89.9091 52.0938C90.1093 54.7175 92.1944 56.8027 94.8182 57.0028C92.1944 57.203 90.1093 59.2881 89.9091 61.9119Z"
fill="#FCFDFD"
/>
<g filter="url(#filter4_f_3106_8112)">
<path
d="M96.0444 65.5888C95.9374 64.809 95.4601 63.3226 93.5898 63.1342C95.4601 62.9458 95.9374 61.4595 96.0444 60.6797C96.2328 62.5499 97.7191 63.0272 98.4989 63.1342C97.7191 63.2412 96.2328 63.7185 96.0444 65.5888Z"
fill="#FCFDFD"
/>
</g>
<path
d="M96.0444 65.5888C95.9374 64.809 95.4601 63.3226 93.5898 63.1342C95.4601 62.9458 95.9374 61.4595 96.0444 60.6797C96.2328 62.5499 97.7191 63.0272 98.4989 63.1342C97.7191 63.2412 96.2328 63.7185 96.0444 65.5888Z"
fill="#FCFDFD"
/>
<g filter="url(#filter5_f_3106_8112)">
<path
d="M96.0444 48.4062C96.2328 50.2765 97.7191 50.7538 98.4989 50.8608C97.7191 50.9678 96.2328 51.445 96.0444 53.3153C95.9374 52.5355 95.4601 51.0492 93.5898 50.8608C95.4601 50.6724 95.9374 49.1861 96.0444 48.4062Z"
fill="#FCFDFD"
/>
</g>
<path
d="M96.0444 48.4062C96.2328 50.2765 97.7191 50.7538 98.4989 50.8608C97.7191 50.9678 96.2328 51.445 96.0444 53.3153C95.9374 52.5355 95.4601 51.0492 93.5898 50.8608C95.4601 50.6724 95.9374 49.1861 96.0444 48.4062Z"
fill="#FCFDFD"
/>
<g filter="url(#filter6_d_3106_8112)">
<path
d="M157 26H172C175.314 26 178 28.6863 178 32V82C178 85.3137 175.314 88 172 88H157V26Z"
fill="#FCFDFD"
/>
<path
d="M172 26.5C175.038 26.5 177.5 28.9624 177.5 32V82C177.5 85.0376 175.038 87.5 172 87.5H157.5V26.5H172Z"
stroke="#E5E5EA"
/>
</g>
<g filter="url(#filter7_d_3106_8112)">
<path
d="M25 87H10C6.68629 87 4 84.3137 4 81L4 31C4 27.6863 6.68629 25 10 25H25L25 87Z"
fill="#FCFDFD"
/>
<path
d="M10 86.5C6.96243 86.5 4.5 84.0376 4.5 81L4.5 31C4.5 27.9624 6.96244 25.5 10 25.5H24.5L24.5 86.5H10Z"
stroke="#E5E5EA"
/>
</g>
<rect
x="31.25"
y="18.25"
width="53.5"
height="20.5"
rx="3.75"
fill="#F2F2F7"
stroke="#C7C7CC"
strokeWidth="0.5"
/>
<rect
x="35.25"
y="88.25"
width="35.5"
height="9.5"
rx="3.75"
fill="#F2F2F7"
stroke="#C7C7CC"
strokeWidth="0.5"
/>
<rect
x="31.25"
y="51.25"
width="35.5"
height="12.5"
rx="3.75"
fill="#F2F2F7"
stroke="#C7C7CC"
strokeWidth="0.5"
/>
<rect
x="42.25"
y="70.25"
width="35.5"
height="11.5"
rx="3.75"
fill="#F2F2F7"
stroke="#C7C7CC"
strokeWidth="0.5"
/>
<defs>
<filter
id="filter0_d_3106_8112"
x="20"
y="4"
width="142"
height="114"
filterUnits="userSpaceOnUse"
color-interpolation-filters="sRGB"
>
<feFlood floodOpacity="0" result="BackgroundImageFix" />
<feColorMatrix
in="SourceAlpha"
type="matrix"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
result="hardAlpha"
/>
<feOffset dy="4" />
<feGaussianBlur stdDeviation="2" />
<feComposite in2="hardAlpha" operator="out" />
<feColorMatrix
type="matrix"
values="0 0 0 0 0.370833 0 0 0 0 0.370833 0 0 0 0 0.370833 0 0 0 0.11 0"
/>
<feBlend
mode="normal"
in2="BackgroundImageFix"
result="effect1_dropShadow_3106_8112"
/>
<feBlend
mode="normal"
in="SourceGraphic"
in2="effect1_dropShadow_3106_8112"
result="shape"
/>
</filter>
<filter
id="filter1_f_3106_8112"
x="87.5"
y="0.5"
width="9"
height="113.5"
filterUnits="userSpaceOnUse"
color-interpolation-filters="sRGB"
>
<feFlood floodOpacity="0" result="BackgroundImageFix" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
/>
<feGaussianBlur
stdDeviation="2"
result="effect1_foregroundBlur_3106_8112"
/>
</filter>
<filter
id="filter2_f_3106_8112"
x="76"
y="41"
width="32"
height="32"
filterUnits="userSpaceOnUse"
color-interpolation-filters="sRGB"
>
<feFlood floodOpacity="0" result="BackgroundImageFix" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
/>
<feGaussianBlur
stdDeviation="2"
result="effect1_foregroundBlur_3106_8112"
/>
</filter>
<filter
id="filter3_f_3106_8112"
x="81"
y="48.0938"
width="17.8184"
height="17.8203"
filterUnits="userSpaceOnUse"
color-interpolation-filters="sRGB"
>
<feFlood floodOpacity="0" result="BackgroundImageFix" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
/>
<feGaussianBlur
stdDeviation="2"
result="effect1_foregroundBlur_3106_8112"
/>
</filter>
<filter
id="filter4_f_3106_8112"
x="89.5898"
y="56.6797"
width="12.9082"
height="12.9062"
filterUnits="userSpaceOnUse"
color-interpolation-filters="sRGB"
>
<feFlood floodOpacity="0" result="BackgroundImageFix" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
/>
<feGaussianBlur
stdDeviation="2"
result="effect1_foregroundBlur_3106_8112"
/>
</filter>
<filter
id="filter5_f_3106_8112"
x="89.5898"
y="44.4062"
width="12.9082"
height="12.9062"
filterUnits="userSpaceOnUse"
color-interpolation-filters="sRGB"
>
<feFlood floodOpacity="0" result="BackgroundImageFix" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
/>
<feGaussianBlur
stdDeviation="2"
result="effect1_foregroundBlur_3106_8112"
/>
</filter>
<filter
id="filter6_d_3106_8112"
x="153"
y="26"
width="29"
height="70"
filterUnits="userSpaceOnUse"
color-interpolation-filters="sRGB"
>
<feFlood floodOpacity="0" result="BackgroundImageFix" />
<feColorMatrix
in="SourceAlpha"
type="matrix"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
result="hardAlpha"
/>
<feOffset dy="4" />
<feGaussianBlur stdDeviation="2" />
<feComposite in2="hardAlpha" operator="out" />
<feColorMatrix
type="matrix"
values="0 0 0 0 0.370833 0 0 0 0 0.370833 0 0 0 0 0.370833 0 0 0 0.11 0"
/>
<feBlend
mode="normal"
in2="BackgroundImageFix"
result="effect1_dropShadow_3106_8112"
/>
<feBlend
mode="normal"
in="SourceGraphic"
in2="effect1_dropShadow_3106_8112"
result="shape"
/>
</filter>
<filter
id="filter7_d_3106_8112"
x="0"
y="25"
width="29"
height="70"
filterUnits="userSpaceOnUse"
color-interpolation-filters="sRGB"
>
<feFlood floodOpacity="0" result="BackgroundImageFix" />
<feColorMatrix
in="SourceAlpha"
type="matrix"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
result="hardAlpha"
/>
<feOffset dy="4" />
<feGaussianBlur stdDeviation="2" />
<feComposite in2="hardAlpha" operator="out" />
<feColorMatrix
type="matrix"
values="0 0 0 0 0.370833 0 0 0 0 0.370833 0 0 0 0 0.370833 0 0 0 0.11 0"
/>
<feBlend
mode="normal"
in2="BackgroundImageFix"
result="effect1_dropShadow_3106_8112"
/>
<feBlend
mode="normal"
in="SourceGraphic"
in2="effect1_dropShadow_3106_8112"
result="shape"
/>
</filter>
</defs>
</svg>
);
}
export const ResizerIcon = () => {
return (
<svg
width="22"
height="23"
viewBox="0 0 22 23"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clipPath="url(#clip0_3106_8243)">
<g filter="url(#filter0_f_3106_8243)">
<path
d="M8.02644 3.65625V11.0201H3.89599L5.62315 9.28625L4.67923 8.34233L1.33203 11.6895L4.67923 15.0367L5.62315 14.0928L3.89599 12.359H8.02644V19.7228H9.36532V3.65625H8.02644ZM16.7292 8.34233L15.7853 9.28625L17.5124 11.0201H13.382V3.65625H12.0431V19.7228H13.382V12.359H17.5124L15.7853 14.0928L16.7292 15.0367L20.0764 11.6895L16.7292 8.34233Z"
fill="var(--background-color-button)"
/>
</g>
<path
d="M8.02644 3.65625V11.0201H3.89599L5.62315 9.28625L4.67923 8.34233L1.33203 11.6895L4.67923 15.0367L5.62315 14.0928L3.89599 12.359H8.02644V19.7228H9.36532V3.65625H8.02644ZM16.7292 8.34233L15.7853 9.28625L17.5124 11.0201H13.382V3.65625H12.0431V19.7228H13.382V12.359H17.5124L15.7853 14.0928L16.7292 15.0367L20.0764 11.6895L16.7292 8.34233Z"
fill="var(--background-color-button)"
/>
</g>
<defs>
<filter
id="filter0_f_3106_8243"
x="-2.66797"
y="-0.34375"
width="26.7441"
height="24.0703"
filterUnits="userSpaceOnUse"
color-interpolation-filters="sRGB"
>
<feFlood floodOpacity="0" result="BackgroundImageFix" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
/>
<feGaussianBlur
stdDeviation="2"
result="effect1_foregroundBlur_3106_8243"
/>
</filter>
<clipPath id="clip0_3106_8243">
<rect
width="21.4221"
height="21.4221"
fill="var(--background-color-button)"
transform="translate(-0.00976562 0.984375)"
/>
</clipPath>
</defs>
</svg>
);
};
export const LayoutIcon = () => {
return (
<svg
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M9.99935 4.16406C9.5391 4.16406 9.16602 4.53716 9.16602 4.9974C9.16602 5.45763 9.5391 5.83073 9.99935 5.83073H16.666C17.1263 5.83073 17.4993 5.45763 17.4993 4.9974C17.4993 4.53716 17.1263 4.16406 16.666 4.16406H9.99935Z"
fill="var(--text-color)"
/>
<path
d="M5.83398 9.9974C5.83398 9.53715 6.20708 9.16406 6.66732 9.16406H13.334C13.7942 9.16406 14.1673 9.53715 14.1673 9.9974C14.1673 10.4576 13.7942 10.8307 13.334 10.8307H6.66732C6.20708 10.8307 5.83398 10.4576 5.83398 9.9974Z"
fill="var(--text-color)"
/>
<path
d="M2.5 14.9974C2.5 14.5371 2.8731 14.1641 3.33333 14.1641H10C10.4602 14.1641 10.8333 14.5371 10.8333 14.9974C10.8333 15.4576 10.4602 15.8307 10 15.8307H3.33333C2.8731 15.8307 2.5 15.4576 2.5 14.9974Z"
fill="var(--text-color)"
/>
</svg>
);
};

View File

@ -8,6 +8,7 @@ import useModuleStore from "../../../store/useModuleStore";
import Widgets from "./visualization/widgets/Widgets"; import Widgets from "./visualization/widgets/Widgets";
import Templates from "../../../modules/visualization/template/Templates"; import Templates from "../../../modules/visualization/template/Templates";
import Search from "../../ui/inputs/Search"; import Search from "../../ui/inputs/Search";
import { useSaveVersion } from "../../../store/builder/store";
const SideBarLeft: React.FC = () => { const SideBarLeft: React.FC = () => {
const [activeOption, setActiveOption] = useState("Widgets"); const [activeOption, setActiveOption] = useState("Widgets");
@ -15,6 +16,8 @@ const SideBarLeft: React.FC = () => {
const { toggleUILeft } = useToggleStore(); const { toggleUILeft } = useToggleStore();
const { activeModule } = useModuleStore(); const { activeModule } = useModuleStore();
const { isVersionSaved } = useSaveVersion();
// Reset activeOption whenever activeModule changes // Reset activeOption whenever activeModule changes
useEffect(() => { useEffect(() => {
setActiveOption("Outline"); setActiveOption("Outline");
@ -31,7 +34,13 @@ const SideBarLeft: React.FC = () => {
}; };
return ( return (
<div className={`sidebar-left-wrapper ${toggleUILeft ? "open" : "closed"}`}> <div
className={`sidebar-left-wrapper ${
(toggleUILeft && !isVersionSaved) || activeModule !== "simulation"
? "open"
: "closed"
}`}
>
<Header /> <Header />
{toggleUILeft && ( {toggleUILeft && (
<div className={`sidebar-left-container `}> <div className={`sidebar-left-container `}>
@ -67,6 +76,8 @@ const SideBarLeft: React.FC = () => {
); );
} else { } else {
return ( return (
<>
{!isVersionSaved && (
<> <>
<ToggleHeader <ToggleHeader
options={["Outline"]} options={["Outline"]}
@ -77,6 +88,8 @@ const SideBarLeft: React.FC = () => {
{activeOption === "Outline" ? <Outline /> : <Assets />} {activeOption === "Outline" ? <Outline /> : <Assets />}
</div> </div>
</> </>
)}
</>
); );
} }
})()} })()}

View File

@ -14,6 +14,7 @@ import Visualization from "./visualization/Visualization";
import Analysis from "./analysis/Analysis"; import Analysis from "./analysis/Analysis";
import Simulations from "./simulation/Simulations"; import Simulations from "./simulation/Simulations";
import useVersionHistoryStore, { import useVersionHistoryStore, {
useSaveVersion,
useSelectedFloorItem, useSelectedFloorItem,
} from "../../../store/builder/store"; } from "../../../store/builder/store";
import { import {
@ -34,6 +35,7 @@ const SideBarRight: React.FC = () => {
const { selectedEventData } = useSelectedEventData(); const { selectedEventData } = useSelectedEventData();
const { selectedEventSphere } = useSelectedEventSphere(); const { selectedEventSphere } = useSelectedEventSphere();
const { viewVersionHistory, setVersionHistory } = useVersionHistoryStore(); const { viewVersionHistory, setVersionHistory } = useVersionHistoryStore();
const { isVersionSaved } = useSaveVersion();
// Reset activeList whenever activeModule changes // Reset activeList whenever activeModule changes
useEffect(() => { useEffect(() => {
@ -60,10 +62,14 @@ const SideBarRight: React.FC = () => {
return ( return (
<div <div
className={`sidebar-right-wrapper ${toggleUIRight ? "open" : "closed"}`} className={`sidebar-right-wrapper ${
(toggleUIRight && !isVersionSaved) || activeModule !== "simulation"
? "open"
: "closed"
}`}
> >
<Header /> <Header />
{toggleUIRight && ( {toggleUIRight && !isVersionSaved && (
<div className="sidebar-actions-container"> <div className="sidebar-actions-container">
{activeModule !== "simulation" && ( {activeModule !== "simulation" && (
<button <button
@ -170,6 +176,7 @@ const SideBarRight: React.FC = () => {
)} )}
{/* simulation */} {/* simulation */}
{toggleUIRight && {toggleUIRight &&
!isVersionSaved &&
!viewVersionHistory && !viewVersionHistory &&
activeModule === "simulation" && ( activeModule === "simulation" && (
<> <>

View File

@ -30,13 +30,10 @@ const GlobalProperties: React.FC = () => {
const { elevation, setElevation } = useElevation(); const { elevation, setElevation } = useElevation();
const { azimuth, setAzimuth } = useAzimuth(); const { azimuth, setAzimuth } = useAzimuth();
const { renderDistance, setRenderDistance } = useRenderDistance(); const { renderDistance, setRenderDistance } = useRenderDistance();
const { setPlaneValue, setGridValue, planeValue, gridValue } = const { setPlaneValue, setGridValue, planeValue, gridValue } = useTileDistance();
useTileDistance();
useEffect(() => {}, [gridValue, planeValue]);
const { socket } = useSocketStore(); const { socket } = useSocketStore();
const { limitDistance, setLimitDistance } = useLimitDistance(); const { limitDistance, setLimitDistance } = useLimitDistance();
const [distance, setDistance] = useState<number>(40); const [distance, setDistance] = useState<number>(40);
useEffect(() => {}, [limitDistance]);
const [limitGridDistance, setLimitGridDistance] = useState(false); const [limitGridDistance, setLimitGridDistance] = useState(false);
const [gridDistance, setGridDistance] = useState<number>(3); const [gridDistance, setGridDistance] = useState<number>(3);
@ -57,6 +54,7 @@ const GlobalProperties: React.FC = () => {
setRenderDistance(30); setRenderDistance(30);
setLimitDistance(true); setLimitDistance(true);
}; };
const limitRenderDistance = async () => { const limitRenderDistance = async () => {
const email = localStorage.getItem("email"); const email = localStorage.getItem("email");
const organization = email?.split("@")[1]?.split(".")[0] || "defaultOrg"; const organization = email?.split("@")[1]?.split(".")[0] || "defaultOrg";
@ -205,6 +203,7 @@ const GlobalProperties: React.FC = () => {
setShadows(!shadows); setShadows(!shadows);
}; };
const toggleResetCamera = () => { const toggleResetCamera = () => {
if (!toggleView) { if (!toggleView) {
setResetCamera(true); // Trigger reset camera action setResetCamera(true); // Trigger reset camera action

View File

@ -22,8 +22,13 @@ import { upsertProductOrEventApi } from "../../../../services/simulation/UpsertP
import { deleteProductApi } from "../../../../services/simulation/deleteProductApi"; import { deleteProductApi } from "../../../../services/simulation/deleteProductApi";
import { renameProductApi } from "../../../../services/simulation/renameProductApi"; import { renameProductApi } from "../../../../services/simulation/renameProductApi";
import { determineExecutionMachineSequences } from "../../../../modules/simulation/simulator/functions/determineExecutionMachineSequences"; import { determineExecutionMachineSequences } from "../../../../modules/simulation/simulator/functions/determineExecutionMachineSequences";
import ComparePopUp from "../../../../modules/simulation/compare/compare"; import ComparePopUp from "../../../ui/compare/compare";
import { useCompareStore } from "../../../../store/builder/store"; import {
useCompareStore,
useSaveVersion,
} from "../../../../store/builder/store";
import CompareLayOut from "../../../ui/compare/CompareLayOut";
import useToggleStore from "../../../../store/useUIToggleStore";
interface Event { interface Event {
modelName: string; modelName: string;
@ -60,7 +65,17 @@ const Simulations: React.FC = () => {
const organization = email!.split("@")[1].split(".")[0]; const organization = email!.split("@")[1].split(".")[0];
const [openObjects, setOpenObjects] = useState(true); const [openObjects, setOpenObjects] = useState(true);
const [processes, setProcesses] = useState<Event[][]>(); const [processes, setProcesses] = useState<Event[][]>();
const { setToggleUI } = useToggleStore();
const { comparePopUp, setComparePopUp } = useCompareStore(); const { comparePopUp, setComparePopUp } = useCompareStore();
const { isVersionSaved, setIsVersionSaved } = useSaveVersion();
const handleSaveVersion = () => {
setIsVersionSaved(true);
setComparePopUp(false);
setToggleUI(false, false);
};
const handleAddProduct = () => { const handleAddProduct = () => {
const id = generateUUID(); const id = generateUUID();
const name = `Product ${products.length + 1}`; const name = `Product ${products.length + 1}`;
@ -241,9 +256,9 @@ const Simulations: React.FC = () => {
Click '<span>Compare</span>' to review and analyze the layout Click '<span>Compare</span>' to review and analyze the layout
differences between them. differences between them.
</div> </div>
<div className="input" onClick={() => setComparePopUp(true)}> <button className="input" onClick={() => setComparePopUp(true)}>
<input type="button" value={"Compare"} className="submit" /> <input type="button" value={"Compare"} className="submit" />
</div> </button>
</div> </div>
</div> </div>
@ -269,7 +284,7 @@ const Simulations: React.FC = () => {
{comparePopUp && ( {comparePopUp && (
<RenderOverlay> <RenderOverlay>
<ComparePopUp /> <ComparePopUp onClose={handleSaveVersion} />
</RenderOverlay> </RenderOverlay>
)} )}
</div> </div>

View File

@ -0,0 +1,179 @@
import React, { useState, useRef, useEffect } from "react";
import {
CompareLayoutIcon,
LayoutIcon,
ResizerIcon,
} from "../../icons/SimulationIcons";
import { useSaveVersion } from "../../../store/builder/store";
import Search from "../inputs/Search";
import OuterClick from "../../../utils/outerClick";
import RegularDropDown from "../inputs/RegularDropDown";
interface Layout {
id: number;
name: string;
}
interface CompareLayoutProps {
dummyLayouts: Layout[];
}
const CompareLayOut: React.FC<CompareLayoutProps> = ({ dummyLayouts }) => {
const [width, setWidth] = useState("50vw");
const [isResizing, setIsResizing] = useState(false);
const [showLayoutDropdown, setShowLayoutDropdown] = useState(false);
const [selectedLayout, setSelectedLayout] = useState<string | null>(null); // Track selected layout
const wrapperRef = useRef<HTMLDivElement>(null);
const startWidthRef = useRef<number>(0);
const startXRef = useRef<number>(0);
const { setIsVersionSaved } = useSaveVersion();
OuterClick({
contextClassName: ["displayLayouts-container", "selectLayout"],
setMenuVisible: () => setShowLayoutDropdown(false),
});
const handleStartResizing = (e: React.MouseEvent) => {
e.preventDefault();
setIsResizing(true);
startXRef.current = e.clientX;
if (wrapperRef.current) {
startWidthRef.current = wrapperRef.current.getBoundingClientRect().width;
}
};
const handleMouseMove = (e: MouseEvent) => {
if (!isResizing || !wrapperRef.current) return;
const dx = startXRef.current - e.clientX;
const newWidthPx = startWidthRef.current + dx;
const viewportWidth = window.innerWidth;
const newWidthVw = (newWidthPx / viewportWidth) * 100;
if (newWidthVw <= 10) {
setWidth("0px");
} else if (newWidthVw <= 90) {
setWidth(`${newWidthPx}px`);
}
};
const handleMouseUp = () => {
if (!isResizing) return;
if (wrapperRef.current) {
const finalWidthPx = wrapperRef.current.getBoundingClientRect().width;
const viewportWidth = window.innerWidth;
const finalWidthVw = (finalWidthPx / viewportWidth) * 100;
if (finalWidthVw <= 10) {
setWidth("0px");
setIsVersionSaved(false);
} else {
setWidth(`${finalWidthVw}vw`);
}
}
setIsResizing(false);
};
useEffect(() => {
if (isResizing) {
window.addEventListener("mousemove", handleMouseMove);
window.addEventListener("mouseup", handleMouseUp);
document.body.classList.add("resizing-active");
}
return () => {
window.removeEventListener("mousemove", handleMouseMove);
window.removeEventListener("mouseup", handleMouseUp);
document.body.classList.remove("resizing-active");
};
}, [isResizing]);
// Maintain proportional width on window resize
useEffect(() => {
const handleResize = () => {
if (!wrapperRef.current || isResizing) return;
const currentWidth = wrapperRef.current.style.width;
if (currentWidth === "0px" || currentWidth.endsWith("vw")) return;
const pxWidth = parseFloat(currentWidth);
const vwWidth = (pxWidth / window.innerWidth) * 100;
setWidth(`${vwWidth}vw`);
};
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, [isResizing]);
const handleSelectLayout = (option: string) => {
setSelectedLayout(option); // Set selected layout
console.log("Selected layout:", option);
};
return (
<div
className={`compareLayOut-wrapper ${width === "0px" ? "closed" : ""}`}
ref={wrapperRef}
style={{ width }}
>
<div className="chooseLayout-container">
<div className="resizer" onMouseDown={handleStartResizing}>
<ResizerIcon />
</div>
{width !== "0px" &&
!selectedLayout && ( // Show only if no layout selected
<div className="chooseLayout-wrapper">
<div className="icon">
<CompareLayoutIcon />
</div>
<div className="value">Choose Layout to compare</div>
<button
className="selectLayout"
onClick={() => setShowLayoutDropdown(!showLayoutDropdown)}
>
Select Layout
</button>
{showLayoutDropdown && (
<div className="displayLayouts-container">
<div className="header">Layouts</div>
<Search onChange={() => {}} />
<div className="layouts-container">
{dummyLayouts.map((layout) => (
<button
key={layout.id}
className="layout-wrapper"
onClick={() => {
handleSelectLayout(layout.name);
setShowLayoutDropdown(false);
}}
>
<LayoutIcon />
<div className="layout">{layout.name}</div>
</button>
))}
</div>
</div>
)}
</div>
)}
{/* Always show after layout is selected */}
{width !== "0px" && selectedLayout && (
<div className="selectLayout-wrapper">
<RegularDropDown
header={selectedLayout}
options={dummyLayouts.map((l) => l.name)} // Pass layout names as options
onSelect={handleSelectLayout}
search={false}
/>
</div>
)}
</div>
</div>
);
};
export default CompareLayOut;

View File

@ -1,10 +1,15 @@
import React from "react"; import React, { useState } from "react";
import { InfoIcon } from "../../../components/icons/ShortcutIcons"; import { InfoIcon } from "../../icons/ShortcutIcons";
import { SaveDiskIcon } from "../../../components/icons/ExportCommonIcons"; import { SaveDiskIcon } from "../../icons/ExportCommonIcons";
import { useCompareStore } from "../../../store/builder/store"; import { useCompareStore } from "../../../store/builder/store";
import OuterClick from "../../../utils/outerClick"; import OuterClick from "../../../utils/outerClick";
import useToggleStore from "../../../store/useUIToggleStore";
const ComparePopUp = () => { interface ComparePopUpProps {
onClose: () => void;
}
const ComparePopUp: React.FC<ComparePopUpProps> = ({ onClose }) => {
const { setComparePopUp } = useCompareStore(); const { setComparePopUp } = useCompareStore();
OuterClick({ OuterClick({
@ -47,7 +52,9 @@ const ComparePopUp = () => {
<div className="button-wrapper"> <div className="button-wrapper">
<div className="button-group"> <div className="button-group">
<div className="save btn">Save & Continue</div> <button className="save btn" onClick={onClose}>
Save & Continue
</button>
<div className="replace btn">Replace Existing Version</div> <div className="replace btn">Replace Existing Version</div>
</div> </div>
<button <button

View File

@ -27,6 +27,7 @@ import addAssetModel from "../geomentries/assets/addAssetModel";
import { getFloorAssets } from "../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi"; import { getFloorAssets } from "../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi";
import useModuleStore from "../../../store/useModuleStore"; import useModuleStore from "../../../store/useModuleStore";
import { useEventsStore } from "../../../store/simulation/useEventsStore"; import { useEventsStore } from "../../../store/simulation/useEventsStore";
import { findEnvironment } from "../../../services/factoryBuilder/environment/findEnvironment";
const assetManagerWorker = new Worker( const assetManagerWorker = new Worker(
new URL( new URL(
@ -77,6 +78,11 @@ const FloorItemsGroup = ({
const email = localStorage.getItem("email"); const email = localStorage.getItem("email");
const organization = email!.split("@")[1].split(".")[0]; const organization = email!.split("@")[1].split(".")[0];
findEnvironment(
organization,
localStorage.getItem("userId")!
).then((evnironMentData) => {
let totalAssets = 0; let totalAssets = 0;
let loadedAssets = 0; let loadedAssets = 0;
@ -111,7 +117,7 @@ const FloorItemsGroup = ({
itemsGroup, itemsGroup,
setFloorItems, setFloorItems,
addEvent, addEvent,
renderDistance evnironMentData.renderDistance
); );
updateLoadingProgress(100); updateLoadingProgress(100);
} }
@ -135,13 +141,14 @@ const FloorItemsGroup = ({
itemsGroup, itemsGroup,
setFloorItems, setFloorItems,
addEvent, addEvent,
renderDistance evnironMentData.renderDistance
); );
updateLoadingProgress(100); updateLoadingProgress(100);
} }
}); });
} }
}; };
})
}, []); }, []);
useEffect(() => { useEffect(() => {

View File

@ -6,20 +6,22 @@ import { useSelectedProduct } from "../../../../../store/simulation/useSimulatio
import { useStorageUnitStore } from "../../../../../store/simulation/useStorageUnitStore"; import { useStorageUnitStore } from "../../../../../store/simulation/useStorageUnitStore";
import { useArmBotStore } from "../../../../../store/simulation/useArmBotStore"; import { useArmBotStore } from "../../../../../store/simulation/useArmBotStore";
import { useVehicleStore } from "../../../../../store/simulation/useVehicleStore"; import { useVehicleStore } from "../../../../../store/simulation/useVehicleStore";
import { usePlayButtonStore, usePauseButtonStore, useResetButtonStore } from "../../../../../store/usePlayButtonStore"; import { usePlayButtonStore, usePauseButtonStore, useResetButtonStore, useAnimationPlaySpeed } from "../../../../../store/usePlayButtonStore";
export function useRetrieveHandler() { export function useRetrieveHandler() {
const { addMaterial } = useMaterialStore(); const { addMaterial } = useMaterialStore();
const { getModelUuidByActionUuid, getPointUuidByActionUuid, getEventByModelUuid } = useProductStore(); const { getModelUuidByActionUuid, getPointUuidByActionUuid, getEventByModelUuid, getActionByUuid } = useProductStore();
const { getStorageUnitById, getLastMaterial, updateCurrentLoad, removeLastMaterial } = useStorageUnitStore(); const { getStorageUnitById, getLastMaterial, updateCurrentLoad, removeLastMaterial } = useStorageUnitStore();
const { getVehicleById, incrementVehicleLoad, addCurrentMaterial } = useVehicleStore(); const { getVehicleById, incrementVehicleLoad, addCurrentMaterial } = useVehicleStore();
const { selectedProduct } = useSelectedProduct(); const { selectedProduct } = useSelectedProduct();
const { getArmBotById, addCurrentAction } = useArmBotStore(); const { getArmBotById, addCurrentAction } = useArmBotStore();
const { isPlaying } = usePlayButtonStore(); const { isPlaying } = usePlayButtonStore();
const { speed } = useAnimationPlaySpeed();
const { isPaused } = usePauseButtonStore(); const { isPaused } = usePauseButtonStore();
const { isReset } = useResetButtonStore(); const { isReset } = useResetButtonStore();
const [activeRetrievals, setActiveRetrievals] = useState<Map<string, { action: StorageAction, isProcessing: boolean, lastCheckTime: number }>>(new Map()); const [activeRetrievals, setActiveRetrievals] = useState<Map<string, { action: StorageAction, isProcessing: boolean, lastCheckTime: number }>>(new Map());
const retrievalTimeRef = useRef<Map<string, number>>(new Map());
const [initialDelayComplete, setInitialDelayComplete] = useState(false); const [initialDelayComplete, setInitialDelayComplete] = useState(false);
const delayTimerRef = useRef<NodeJS.Timeout | null>(null); const delayTimerRef = useRef<NodeJS.Timeout | null>(null);
@ -125,7 +127,26 @@ export function useRetrieveHandler() {
const armBot = getArmBotById(triggeredModel.modelUuid); const armBot = getArmBotById(triggeredModel.modelUuid);
isIdle = (armBot && !armBot.isActive && armBot.state === 'idle' && !armBot.currentAction) || false; isIdle = (armBot && !armBot.isActive && armBot.state === 'idle' && !armBot.currentAction) || false;
if (isIdle) { if (!armBot) return;
if (!retrievalTimeRef.current.has(actionUuid) && isIdle) {
retrievalTimeRef.current.set(actionUuid, currentTime);
return;
}
const idleStartTime = retrievalTimeRef.current.get(actionUuid);
const minIdleTimeBeforeFirstRetrieval = 5000 / speed;
const minDelayBetweenRetrievals = 5000 / speed;
const canProceedFirstRetrieval = idleStartTime !== undefined &&
(currentTime - idleStartTime) >= minIdleTimeBeforeFirstRetrieval;
const lastRetrievalTime = retrievalTimeRef.current.get(`${actionUuid}_last`) ?? null;
const canProceedSubsequent = lastRetrievalTime === null ||
(currentTime - lastRetrievalTime) >= minDelayBetweenRetrievals;
const canProceed = lastRetrievalTime === null ? canProceedFirstRetrieval : canProceedSubsequent;
if (isIdle && canProceed) {
setActiveRetrievals(prev => { setActiveRetrievals(prev => {
const newRetrievals = new Map(prev); const newRetrievals = new Map(prev);
newRetrievals.set(actionUuid, { newRetrievals.set(actionUuid, {
@ -138,13 +159,23 @@ export function useRetrieveHandler() {
const lastMaterial = getLastMaterial(storageUnit.modelUuid); const lastMaterial = getLastMaterial(storageUnit.modelUuid);
if (lastMaterial) { if (lastMaterial) {
if (retrieval.action.triggers[0].triggeredAsset.triggeredAction?.actionUuid) {
const action = getActionByUuid(selectedProduct.productId, retrieval.action.triggers[0].triggeredAsset.triggeredAction.actionUuid);
if (action && action.triggers.length > 0 && action.triggers[0].triggeredAsset?.triggeredModel.modelUuid) {
const model = getEventByModelUuid(selectedProduct.productId, action.triggers[0].triggeredAsset.triggeredModel.modelUuid);
if (model) {
if (model.type === 'vehicle') {
const vehicle = getVehicleById(model.modelUuid);
if (vehicle && !vehicle.isActive && vehicle.state === 'idle' && vehicle.isPicking && vehicle.currentLoad < vehicle.point.action.loadCapacity) {
const material = createNewMaterial( const material = createNewMaterial(
lastMaterial.materialId, lastMaterial.materialId,
lastMaterial.materialType, lastMaterial.materialType,
storageUnit.point.action storageUnit.point.action
); );
if (material) { if (material) {
addCurrentAction( addCurrentAction(
triggeredModel.modelUuid, triggeredModel.modelUuid,
retrieval.action.triggers[0].triggeredAsset.triggeredAction?.actionUuid ?? '', retrieval.action.triggers[0].triggeredAsset.triggeredAction?.actionUuid ?? '',
@ -154,26 +185,71 @@ export function useRetrieveHandler() {
retrieveLogStatus(material.materialName, `is being picked by ${armBot?.modelName}`); retrieveLogStatus(material.materialName, `is being picked by ${armBot?.modelName}`);
} }
} }
} else {
const material = createNewMaterial(
lastMaterial.materialId,
lastMaterial.materialType,
storageUnit.point.action
);
if (material) {
addCurrentAction(
triggeredModel.modelUuid,
retrieval.action.triggers[0].triggeredAsset.triggeredAction?.actionUuid ?? '',
material.materialType,
material.materialId
);
retrieveLogStatus(material.materialName, `is being picked by ${armBot?.modelName}`);
}
}
setActiveRetrievals(prev => { setActiveRetrievals(prev => {
const newRetrievals = new Map(prev); const newRetrievals = new Map(prev);
newRetrievals.set(actionUuid, { newRetrievals.set(actionUuid, {
...retrieval, ...retrieval,
isProcessing: false, isProcessing: false,
lastCheckTime: currentTime lastCheckTime: currentTime,
}); });
return newRetrievals; return newRetrievals;
}); });
retrievalTimeRef.current.set(actionUuid, currentTime);
}
}
}
}
} else if (!isIdle) {
retrievalTimeRef.current.delete(actionUuid);
} }
} else if (triggeredModel.type === 'vehicle') { } else if (triggeredModel.type === 'vehicle') {
const vehicle = getVehicleById(triggeredModel.modelUuid); const vehicle = getVehicleById(triggeredModel.modelUuid);
isIdle = (vehicle && !vehicle.isActive && vehicle.state === 'idle' && vehicle.isPicking) || false; isIdle = (vehicle && !vehicle.isActive && vehicle.state === 'idle' && vehicle.isPicking && vehicle.currentLoad < vehicle.point.action.loadCapacity) || false;
if (!vehicle) return; if (!vehicle) return;
// const loadDuration = vehicle.point.action.unLoadDuration;
if (isIdle) { const loadDuration = vehicle.point.action.unLoadDuration;
let minDelayBetweenRetrievals = (loadDuration * 1000) / speed;
const minIdleTimeBeforeFirstRetrieval = 3000 / speed;
if (!retrievalTimeRef.current.has(actionUuid) && isIdle) {
retrievalTimeRef.current.set(actionUuid, currentTime);
return;
}
const idleStartTime = retrievalTimeRef.current.get(actionUuid);
const lastRetrievalTime = retrievalTimeRef.current.get(`${actionUuid}_last`) ?? null;
const canProceedFirstRetrieval = idleStartTime !== undefined &&
(currentTime - idleStartTime) >= minIdleTimeBeforeFirstRetrieval;
const canProceedSubsequent = lastRetrievalTime === null ||
(currentTime - lastRetrievalTime) >= minDelayBetweenRetrievals;
const canProceed = lastRetrievalTime === null ? canProceedFirstRetrieval : canProceedSubsequent;
if (isIdle && canProceed) {
setActiveRetrievals(prev => { setActiveRetrievals(prev => {
const newRetrievals = new Map(prev); const newRetrievals = new Map(prev);
newRetrievals.set(actionUuid, { newRetrievals.set(actionUuid, {
@ -208,12 +284,17 @@ export function useRetrieveHandler() {
newRetrievals.set(actionUuid, { newRetrievals.set(actionUuid, {
...retrieval, ...retrieval,
isProcessing: false, isProcessing: false,
lastCheckTime: currentTime lastCheckTime: currentTime,
}); });
return newRetrievals; return newRetrievals;
}); });
}
retrievalTimeRef.current.set(actionUuid, currentTime);
retrievalTimeRef.current.set(`${actionUuid}_last`, currentTime);
} else if (!isIdle) {
retrievalTimeRef.current.delete(actionUuid);
retrievalTimeRef.current.delete(`${actionUuid}_last`);
}
} }
}); });
@ -234,7 +315,7 @@ export function useRetrieveHandler() {
newRetrievals.set(action.actionUuid, { newRetrievals.set(action.actionUuid, {
action, action,
isProcessing: false, isProcessing: false,
lastCheckTime: performance.now() lastCheckTime: performance.now(),
}); });
return newRetrievals; return newRetrievals;
}); });

View File

@ -1,4 +1,4 @@
import React, { useEffect } from 'react' import { useEffect } from 'react'
import MaterialInstances from './instances/materialInstances' import MaterialInstances from './instances/materialInstances'
import { usePlayButtonStore, useResetButtonStore } from '../../../store/usePlayButtonStore'; import { usePlayButtonStore, useResetButtonStore } from '../../../store/usePlayButtonStore';
import { useMaterialStore } from '../../../store/simulation/useMaterialStore'; import { useMaterialStore } from '../../../store/simulation/useMaterialStore';

View File

@ -228,11 +228,11 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
logStatus(armBot.modelUuid, "Moving armBot from rest point to start position.") logStatus(armBot.modelUuid, "Moving armBot from rest point to start position.")
} }
// Moving to Pick to Drop position // Moving to Pick to Drop position
else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "picking" && armBot.currentAction) { else if (armBot && !armBot.isActive && armBot.state === "running" && currentPhase === "picking" && armBot.currentAction) {
requestAnimationFrame(firstFrame); requestAnimationFrame(firstFrame);
} }
//Moving to drop point to restPosition //Moving to drop point to restPosition
else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "dropping" && armBot.currentAction) { else if (armBot && !armBot.isActive && armBot.state === "running" && currentPhase === "dropping" && armBot.currentAction) {
requestAnimationFrame(firstFrame); requestAnimationFrame(firstFrame);
} }
} else { } else {
@ -270,14 +270,14 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
else if (armBot.isActive && armBot.state == "running" && currentPhase == "rest-to-start") { else if (armBot.isActive && armBot.state == "running" && currentPhase == "rest-to-start") {
logStatus(armBot.modelUuid, "Callback triggered: pick."); logStatus(armBot.modelUuid, "Callback triggered: pick.");
setArmBotActive(armBot.modelUuid, false) setArmBotActive(armBot.modelUuid, false)
setArmBotState(armBot.modelUuid, "idle") setArmBotState(armBot.modelUuid, "running")
setCurrentPhase("picking"); setCurrentPhase("picking");
setPath([]) setPath([])
} }
else if (armBot.isActive && armBot.state == "running" && currentPhase == "start-to-end") { else if (armBot.isActive && armBot.state == "running" && currentPhase == "start-to-end") {
logStatus(armBot.modelUuid, "Callback triggered: drop."); logStatus(armBot.modelUuid, "Callback triggered: drop.");
setArmBotActive(armBot.modelUuid, false) setArmBotActive(armBot.modelUuid, false)
setArmBotState(armBot.modelUuid, "idle") setArmBotState(armBot.modelUuid, "running")
setCurrentPhase("dropping"); setCurrentPhase("dropping");
setPath([]) setPath([])
} }

View File

@ -414,6 +414,10 @@ export function useTriggerHandler() {
handleAction(action, material.materialId); handleAction(action, material.materialId);
} }
} else if (action) {
setNextLocation(material.materialId, null)
handleAction(action, material.materialId);
} }
} }

View File

@ -5,6 +5,8 @@ import { NavMeshQuery } from '@recast-navigation/core';
import { useNavMesh } from '../../../../../store/builder/store'; import { useNavMesh } from '../../../../../store/builder/store';
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore } from '../../../../../store/usePlayButtonStore'; import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore } from '../../../../../store/usePlayButtonStore';
import { useVehicleStore } from '../../../../../store/simulation/useVehicleStore'; import { useVehicleStore } from '../../../../../store/simulation/useVehicleStore';
import { useArmBotStore } from '../../../../../store/simulation/useArmBotStore';
import { useConveyorStore } from '../../../../../store/simulation/useConveyorStore';
import { useStorageUnitStore } from '../../../../../store/simulation/useStorageUnitStore'; import { useStorageUnitStore } from '../../../../../store/simulation/useStorageUnitStore';
import { useMaterialStore } from '../../../../../store/simulation/useMaterialStore'; import { useMaterialStore } from '../../../../../store/simulation/useMaterialStore';
import { useProductStore } from '../../../../../store/simulation/useProductStore'; import { useProductStore } from '../../../../../store/simulation/useProductStore';
@ -20,10 +22,12 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
const { isPlaying } = usePlayButtonStore(); const { isPlaying } = usePlayButtonStore();
const { removeMaterial, setEndTime } = useMaterialStore(); const { removeMaterial, setEndTime } = useMaterialStore();
const { getStorageUnitById } = useStorageUnitStore(); const { getStorageUnitById } = useStorageUnitStore();
const { getArmBotById } = useArmBotStore();
const { getConveyorById } = useConveyorStore();
const { triggerPointActions } = useTriggerHandler(); const { triggerPointActions } = useTriggerHandler();
const { getActionByUuid, getEventByModelUuid, getTriggerByUuid } = useProductStore(); const { getActionByUuid, getEventByModelUuid, getTriggerByUuid } = useProductStore();
const { selectedProduct } = useSelectedProduct(); const { selectedProduct } = useSelectedProduct();
const { vehicles, setVehicleActive, setVehicleState, setVehiclePicking, clearCurrentMaterials, setVehicleLoad, decrementVehicleLoad, removeLastMaterial } = useVehicleStore(); const { vehicles, setVehicleActive, setVehicleState, setVehiclePicking, clearCurrentMaterials, setVehicleLoad, decrementVehicleLoad, removeLastMaterial, getLastMaterial } = useVehicleStore();
const [currentPhase, setCurrentPhase] = useState<string>('stationed'); const [currentPhase, setCurrentPhase] = useState<string>('stationed');
const [path, setPath] = useState<[number, number, number][]>([]); const [path, setPath] = useState<[number, number, number][]>([]);
const pauseTimeRef = useRef<number | null>(null); const pauseTimeRef = useRef<number | null>(null);
@ -147,9 +151,6 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
} }
} }
function startUnloadingProcess() { function startUnloadingProcess() {
if (agvDetail.point.action.triggers.length > 0) { if (agvDetail.point.action.triggers.length > 0) {
const trigger = getTriggerByUuid(selectedProduct.productId, agvDetail.point.action.triggers[0].triggerUuid); const trigger = getTriggerByUuid(selectedProduct.productId, agvDetail.point.action.triggers[0].triggerUuid);
@ -159,19 +160,19 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
if (model.type === 'transfer') { if (model.type === 'transfer') {
const action = getActionByUuid(selectedProduct.productId, agvDetail.point.action.actionUuid); const action = getActionByUuid(selectedProduct.productId, agvDetail.point.action.actionUuid);
if (action) { if (action) {
handleMaterialDropToConveyor(action); handleMaterialDropToConveyor(model);
} }
} else if (model.type === 'machine') { } else if (model.type === 'machine') {
// //
} else if (model.type === 'roboticArm') { } else if (model.type === 'roboticArm') {
const action = getActionByUuid(selectedProduct.productId, agvDetail.point.action.actionUuid); const action = getActionByUuid(selectedProduct.productId, agvDetail.point.action.actionUuid);
if (action) { if (action) {
handleMaterialDropToArmBot(action); handleMaterialDropToArmBot(model);
} }
} else if (model.type === 'storageUnit') { } else if (model.type === 'storageUnit') {
const action = getActionByUuid(selectedProduct.productId, agvDetail.point.action.actionUuid); const action = getActionByUuid(selectedProduct.productId, agvDetail.point.action.actionUuid);
if (action) { if (action) {
handleMaterialDropToStorageUnit(action); handleMaterialDropToStorageUnit(model);
} }
} }
} else { } else {
@ -186,25 +187,22 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
} }
} }
function handleMaterialDropToStorageUnit(action: Action) { function handleMaterialDropToStorageUnit(model: StorageEventSchema) {
if (action.triggers.length > 0 && action.triggers[0].triggeredAsset?.triggeredModel.modelUuid) { if (model) {
const storageUnit = getStorageUnitById(action.triggers[0].triggeredAsset?.triggeredModel.modelUuid); if (model.point.action.actionType === 'store') {
if (storageUnit) { loopMaterialDropToStorage(
if (storageUnit.point.action.actionType === 'store') {
handleMaterialDropToStorage(
agvDetail.modelUuid, agvDetail.modelUuid,
agvDetail.currentLoad, agvDetail.currentLoad,
agvDetail.point.action.unLoadDuration, agvDetail.point.action.unLoadDuration,
storageUnit.modelUuid, model.modelUuid,
storageUnit.point.action.storageCapacity, model.point.action.storageCapacity,
agvDetail.point.action agvDetail.point.action
); );
} }
} }
} }
}
function handleMaterialDropToStorage( function loopMaterialDropToStorage(
vehicleId: string, vehicleId: string,
vehicleCurrentLoad: number, vehicleCurrentLoad: number,
unLoadDuration: number, unLoadDuration: number,
@ -262,22 +260,138 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
} }
} }
function handleMaterialDropToConveyor(action: Action) { function handleMaterialDropToConveyor(model: ConveyorEventSchema) {
if (agvDetail.currentLoad > 1) { const conveyor = getConveyorById(model.modelUuid);
// if (conveyor) {
} else if (agvDetail.currentLoad === 1 && agvDetail.currentMaterials.length === 1) { loopMaterialDropToConveyor(
triggerPointActions(action, agvDetail.currentMaterials[0].materialId); agvDetail.modelUuid,
decrementVehicleLoad(agvDetail.modelUuid, 1); agvDetail.currentLoad,
removeLastMaterial(agvDetail.modelUuid); conveyor.modelUuid,
agvDetail.point.action.unLoadDuration,
agvDetail.point.action
);
} }
} }
function handleMaterialDropToArmBot(action: Action) { function loopMaterialDropToConveyor(
if (agvDetail.currentLoad > 1) { vehicleId: string,
// vehicleCurrentLoad: number,
} else if (agvDetail.currentLoad === 1 && agvDetail.currentMaterials.length === 1) { conveyorId: string,
triggerPointActions(action, agvDetail.currentMaterials[0].materialId); unLoadDuration: number,
action: VehicleAction
) {
startTime = performance.now();
const fixedInterval = unLoadDuration * (1000 / speed);
const dropLoop = () => {
if (isPausedRef.current) {
pauseTimeRef.current ??= performance.now();
requestAnimationFrame(dropLoop);
return;
} }
if (pauseTimeRef.current) {
const pauseDuration = performance.now() - pauseTimeRef.current;
startTime += pauseDuration;
pauseTimeRef.current = null;
}
const elapsedTime = performance.now() - startTime;
const conveyor = getConveyorById(conveyorId);
if (elapsedTime >= fixedInterval) {
if (conveyor && !conveyor.isPaused && vehicleCurrentLoad > 0) {
decrementVehicleLoad(vehicleId, 1);
vehicleCurrentLoad -= 1;
const material = removeLastMaterial(vehicleId);
if (material) {
triggerPointActions(action, material.materialId);
}
if (vehicleCurrentLoad > 0) {
startTime = performance.now();
requestAnimationFrame(dropLoop);
}
} else if (!conveyor?.isActive) {
requestAnimationFrame(dropLoop);
}
} else {
requestAnimationFrame(dropLoop);
}
};
dropLoop();
}
function handleMaterialDropToArmBot(model: RoboticArmEventSchema) {
const armBot = getArmBotById(model.modelUuid);
if (armBot && armBot.state === 'idle' && !armBot.isActive) {
loopMaterialDropToArmBot(
agvDetail.modelUuid,
agvDetail.currentLoad,
agvDetail.point.action.unLoadDuration,
model.modelUuid,
agvDetail.point.action
);
}
}
function loopMaterialDropToArmBot(
vehicleId: string,
vehicleCurrentLoad: number,
unLoadDuration: number,
armBotId: string,
action: VehicleAction
) {
startTime = performance.now();
const armBot = getArmBotById(armBotId);
if (!armBot || armBot.state !== 'idle' || armBot.isActive || vehicleCurrentLoad <= 0) {
return;
}
const checkIdleDuration = () => {
if (isPausedRef.current) {
pauseTimeRef.current ??= performance.now();
requestAnimationFrame(checkIdleDuration);
return;
}
if (pauseTimeRef.current) {
const pauseDuration = performance.now() - pauseTimeRef.current;
startTime += pauseDuration;
pauseTimeRef.current = null;
}
const elapsedTime = performance.now() - startTime;
if (elapsedTime >= unLoadDuration * (1000 / speed)) {
const material = getLastMaterial(vehicleId);
if (material) {
vehicleCurrentLoad -= 1;
triggerPointActions(action, material.materialId);
if (vehicleCurrentLoad > 0) {
setTimeout(() => {
const waitForNextTransfer = () => {
const currentArmBot = getArmBotById(armBotId);
if (currentArmBot && currentArmBot.state === 'idle' && !currentArmBot.isActive) {
startTime = performance.now();
loopMaterialDropToArmBot(vehicleId, vehicleCurrentLoad, unLoadDuration, armBotId, action);
} else {
requestAnimationFrame(waitForNextTransfer);
}
};
waitForNextTransfer();
}, 0)
}
}
} else {
requestAnimationFrame(checkIdleDuration);
}
};
checkIdleDuration();
} }
function handleMaterialDropByDefault(droppedMaterial: number) { function handleMaterialDropByDefault(droppedMaterial: number) {

View File

@ -1,4 +1,4 @@
import React, { useEffect } from "react"; import React, { useEffect, useState } from "react";
import ModuleToggle from "../components/ui/ModuleToggle"; import ModuleToggle from "../components/ui/ModuleToggle";
import SideBarLeft from "../components/layout/sidebarLeft/SideBarLeft"; import SideBarLeft from "../components/layout/sidebarLeft/SideBarLeft";
import SideBarRight from "../components/layout/sidebarRight/SideBarRight"; import SideBarRight from "../components/layout/sidebarRight/SideBarRight";
@ -14,6 +14,7 @@ import {
useZones, useZones,
useLoadingProgress, useLoadingProgress,
useWidgetSubOption, useWidgetSubOption,
useSaveVersion,
} from "../store/builder/store"; } from "../store/builder/store";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { usePlayButtonStore } from "../store/usePlayButtonStore"; import { usePlayButtonStore } from "../store/usePlayButtonStore";
@ -33,10 +34,14 @@ import LogList from "../components/ui/log/LogList";
import Footer from "../components/footer/Footer"; import Footer from "../components/footer/Footer";
import SelectFloorPlan from "../components/temporary/SelectFloorPlan"; import SelectFloorPlan from "../components/temporary/SelectFloorPlan";
import ControlsPlayer from "../components/layout/controls/ControlsPlayer"; import ControlsPlayer from "../components/layout/controls/ControlsPlayer";
import CompareLayOut from "../components/ui/compare/CompareLayOut";
import useToggleStore from "../store/useUIToggleStore";
import RegularDropDown from "../components/ui/inputs/RegularDropDown";
const Project: React.FC = () => { const Project: React.FC = () => {
let navigate = useNavigate(); let navigate = useNavigate();
const echo = useLogger(); const echo = useLogger();
const { setToggleUI } = useToggleStore();
const { activeModule, setActiveModule } = useModuleStore(); const { activeModule, setActiveModule } = useModuleStore();
const { loadingProgress } = useLoadingProgress(); const { loadingProgress } = useLoadingProgress();
@ -45,7 +50,13 @@ const Project: React.FC = () => {
const { setFloorItems } = useFloorItems(); const { setFloorItems } = useFloorItems();
const { setWallItems } = useWallItems(); const { setWallItems } = useWallItems();
const { setZones } = useZones(); const { setZones } = useZones();
const { isVersionSaved } = useSaveVersion();
useEffect(() => {
if (!isVersionSaved) {
setToggleUI(true, true);
}
}, [isVersionSaved]);
useEffect(() => { useEffect(() => {
setFloorItems([]); setFloorItems([]);
setWallItems([]); setWallItems([]);
@ -83,6 +94,19 @@ const Project: React.FC = () => {
const { selectedZone } = useSelectedZoneStore(); const { selectedZone } = useSelectedZoneStore();
const { setFloatingWidget } = useFloatingWidget(); const { setFloatingWidget } = useFloatingWidget();
const [selectedLayout, setSelectedLayout] = useState<string | null>(null); // Track selected layout
const dummyLayouts = [
{ id: 1, name: "Layout 1" },
{ id: 2, name: "Layout 2" },
{ id: 3, name: "Layout 3" },
{ id: 4, name: "Layout 4" },
];
const handleSelectLayout = (option: string) => {
setSelectedLayout(option); // Set selected layout
console.log("Selected layout:", option);
};
return ( return (
<div className="project-main"> <div className="project-main">
{!selectedUser && ( {!selectedUser && (
@ -98,7 +122,9 @@ const Project: React.FC = () => {
)} )}
<RealTimeVisulization /> <RealTimeVisulization />
{activeModule === "market" && <MarketPlace />} {activeModule === "market" && <MarketPlace />}
{activeModule !== "market" && !isPlaying && <Tools />} {activeModule !== "market" && !isPlaying && !isVersionSaved && (
<Tools />
)}
{isPlaying && activeModule === "simulation" && <SimulationPlayer />} {isPlaying && activeModule === "simulation" && <SimulationPlayer />}
{isPlaying && activeModule !== "simulation" && <ControlsPlayer />} {isPlaying && activeModule !== "simulation" && <ControlsPlayer />}
@ -137,6 +163,19 @@ const Project: React.FC = () => {
</RenderOverlay> </RenderOverlay>
)} )}
{activeModule !== "market" && !selectedUser && <Footer />} {activeModule !== "market" && !selectedUser && <Footer />}
{isVersionSaved && activeModule === "simulation" && (
<>
<div className="initial-selectLayout-wrapper">
<RegularDropDown
header={selectedLayout ?? "Layout 1"}
options={dummyLayouts.map((l) => l.name)} // Pass layout names as options
onSelect={handleSelectLayout}
search={false}
/>
</div>
<CompareLayOut dummyLayouts={dummyLayouts} />
</>
)}
</div> </div>
); );
}; };

View File

@ -472,8 +472,20 @@ interface CompareStore {
} }
export const useCompareStore = create<CompareStore>((set) => ({ export const useCompareStore = create<CompareStore>((set) => ({
comparePopUp: false, comparePopUp: true,
setComparePopUp: (value) => set({ comparePopUp: value }), setComparePopUp: (value) => set({ comparePopUp: value }),
toggleComparePopUp: () => toggleComparePopUp: () =>
set((state) => ({ comparePopUp: !state.comparePopUp })), set((state) => ({ comparePopUp: !state.comparePopUp })),
})); }));
// Define the types for the store state
interface VersionStore {
isVersionSaved: boolean;
setIsVersionSaved: (value: boolean) => void;
}
// Create the Zustand store
export const useSaveVersion = create<VersionStore>((set) => ({
isVersionSaved: false, // Default state
setIsVersionSaved: (value: boolean) => set({ isVersionSaved: value }), // Function to update the state
}));

View File

@ -25,6 +25,7 @@ interface VehiclesStore {
addCurrentMaterial: (modelUuid: string, materialType: string, materialId: string) => void; addCurrentMaterial: (modelUuid: string, materialType: string, materialId: string) => void;
setCurrentMaterials: (modelUuid: string, materials: { materialType: string; materialId: string; }[]) => void; setCurrentMaterials: (modelUuid: string, materials: { materialType: string; materialId: string; }[]) => void;
removeLastMaterial: (modelUuid: string) => { materialId: string; materialType: string; } | undefined; removeLastMaterial: (modelUuid: string) => { materialId: string; materialType: string; } | undefined;
getLastMaterial: (modelUuid: string) => { materialId: string; materialType: string; } | undefined;
clearCurrentMaterials: (modelUuid: string) => void; clearCurrentMaterials: (modelUuid: string) => void;
incrementActiveTime: (modelUuid: string, incrementBy: number) => void; incrementActiveTime: (modelUuid: string, incrementBy: number) => void;
incrementIdleTime: (modelUuid: string, incrementBy: number) => void; incrementIdleTime: (modelUuid: string, incrementBy: number) => void;
@ -179,6 +180,22 @@ export const useVehicleStore = create<VehiclesStore>()(
return removedMaterial; return removedMaterial;
}, },
getLastMaterial: (modelUuid) => {
let removedMaterial: { materialId: string; materialType: string; } | undefined;
set((state) => {
const vehicle = state.vehicles.find((v) => v.modelUuid === modelUuid);
if (vehicle) {
if (vehicle.currentMaterials.length > 0) {
removedMaterial = {
materialId: vehicle.currentMaterials[vehicle.currentMaterials.length - 1].materialId,
materialType: vehicle.currentMaterials[vehicle.currentMaterials.length - 1].materialType
};
}
}
});
return removedMaterial;
},
clearCurrentMaterials: (modelUuid) => { clearCurrentMaterials: (modelUuid) => {
set((state) => { set((state) => {
const vehicle = state.vehicles.find((v) => v.modelUuid === modelUuid); const vehicle = state.vehicles.find((v) => v.modelUuid === modelUuid);

View File

@ -0,0 +1,200 @@
@use "../abstracts/variables" as *;
@use "../abstracts/mixins" as *;
.initial-selectLayout-wrapper {
position: fixed;
top: 100px;
left: 40px;
z-index: 10;
}
.compareLayOut-wrapper {
position: fixed;
top: 0;
right: 0;
z-index: 2;
height: 100vh;
background: var(--background-color);
backdrop-filter: blur(20px);
display: flex;
justify-content: center;
align-items: center;
animation: slideInFromRight 0.4s ease-out forwards;
user-select: none;
.selectLayout-wrapper {
position: absolute;
top: 100px;
right: 40px;
}
.chooseLayout-container {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
position: relative;
.resizer {
width: 32px;
height: 32px;
@include flex-center;
padding: 6px;
position: absolute;
top: 50%;
left: 0;
transform: translate(-50%, -50%);
background: var(--background-color);
backdrop-filter: blur(20px);
box-shadow: $box-shadow-heavy;
border-radius: 50%;
cursor: ew-resize;
transition: transform 0.1s ease;
z-index: 10;
}
.chooseLayout-wrapper {
background: var(--background-color);
backdrop-filter: blur(20px);
padding: 20px;
border-radius: 8px;
box-shadow: $box-shadow-medium;
max-width: 80%;
text-align: center;
position: relative;
.icon {
width: 100%;
margin-bottom: 15px;
text-align: center;
svg {
width: 100%;
}
}
.value {
margin-bottom: 15px;
font-size: var(--font-size-small);
font-weight: 500;
color: var(--text-primary);
}
button {
display: block;
margin: 0 auto;
padding: 8px 16px;
background: var(--background-color-button);
color: var(--icon-default-color-active);
border: none;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s ease;
&:hover {
transform: translateY(-1px);
}
}
.displayLayouts-container {
max-width: 170px;
height: auto;
background: var(--background-color);
backdrop-filter: blur(20px);
padding: 6px;
border-radius: 8px;
box-shadow: $box-shadow-medium;
position: absolute;
right: 0;
top: 100%;
transform: translate(50%, -10px);
display: flex;
flex-direction: column;
gap: 6px;
.header {
text-align: left;
padding-top: 6px;
padding-left: 6px;
}
.search-wrapper {
padding: 6px 0;
.search-container {
padding: 0;
border-radius: 6px;
}
}
.layouts-container {
.layout {
padding: 6px 0;
}
.layout-wrapper {
display: flex;
align-items: center;
gap: 6px;
cursor: pointer;
padding: 0 10px;
background: none;
width: 100%;
&:hover {
background-color: var(--highlight-text-color) !important;
border-radius: 4px;
svg {
path {
fill: var(--background-color-accent);
}
}
.layout {
color: var(--background-color-accent);
}
}
}
}
}
}
}
}
@keyframes slideInFromRight {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
// body.compare-layout-open {
// main {
// padding-right: 10px;
// transition: padding 0.3s ease;
// }
// }

View File

@ -43,7 +43,6 @@
.header { .header {
text-align: center; text-align: center;
font-size: var(--font-size-small); font-size: var(--font-size-small);
font-weight: 600;
margin-bottom: 8px; margin-bottom: 8px;
} }
@ -104,7 +103,6 @@
.label-tab { .label-tab {
font-size: var(--font-size-small); font-size: var(--font-size-small);
font-weight: 500;
} }
.status { .status {
@ -160,7 +158,6 @@
.btn { .btn {
padding: 10px 16px; padding: 10px 16px;
font-size: var(--font-size-small); font-size: var(--font-size-small);
font-weight: 600;
border-radius: 30px; border-radius: 30px;
cursor: pointer; cursor: pointer;
transition: all 0.3s ease; transition: all 0.3s ease;
@ -191,7 +188,6 @@
.cancel { .cancel {
color: red; color: red;
font-size: var(--font-size-small); font-size: var(--font-size-small);
font-weight: 500;
cursor: pointer; cursor: pointer;
} }
} }

View File

@ -665,7 +665,7 @@
path { path {
stroke: var(--text-button-color); stroke: var(--text-button-color);
stroke-width: 1.3; strokeWidth: 1.3;
} }
} }
} }
@ -936,7 +936,7 @@
path { path {
stroke: var(--accent-color); stroke: var(--accent-color);
stroke-width: 1.5px; strokeWidth: 1.5px;
} }
&:hover { &:hover {
@ -1253,8 +1253,10 @@
} }
.input { .input {
width: 100%;
display: flex; display: flex;
flex-direction: row-reverse; flex-direction: row-reverse;
justify-content: flex-start;
input { input {
width: fit-content; width: fit-content;
@ -1441,12 +1443,15 @@
} }
} }
} }
.toggle-sidebar-ui-button { .toggle-sidebar-ui-button {
svg { svg {
transform: scaleX(-1); transform: scaleX(-1);
} }
.tooltip { .tooltip {
right: 56px; right: 56px;
&::after { &::after {
left: 100%; left: 100%;
bottom: 50%; bottom: 50%;
@ -1620,11 +1625,13 @@
height: 100%; height: 100%;
gap: 6px; gap: 6px;
padding: 2px; padding: 2px;
.no-asset { .no-asset {
text-align: center; text-align: center;
margin: 12px; margin: 12px;
width: 100%; width: 100%;
} }
.assets { .assets {
width: 122px; width: 122px;
height: 95px; height: 95px;

View File

@ -35,6 +35,8 @@
@use 'layout/toast'; @use 'layout/toast';
@use 'layout/skeleton'; @use 'layout/skeleton';
@use 'layout/compareLayoutPopUp'; @use 'layout/compareLayoutPopUp';
@use 'layout/compareLayout';
// pages // pages
@use 'pages/dashboard'; @use 'pages/dashboard';

View File

@ -178,9 +178,6 @@
} }
} }
&.bottom {
bottom: 210px;
}
} }
} }
@ -432,7 +429,7 @@
path { path {
stroke: var(--text-button-color); stroke: var(--text-button-color);
stroke-width: 2; strokeWidth: 2;
} }
} }
@ -444,7 +441,7 @@
path { path {
stroke: #f65648; stroke: #f65648;
stroke-width: 1.3; strokeWidth: 1.3;
} }
} }
} }

View File

@ -6,12 +6,15 @@ import {
useActiveTool, useActiveTool,
useAddAction, useAddAction,
useDeleteTool, useDeleteTool,
useSaveVersion,
useSelectedWallItem, useSelectedWallItem,
useShortcutStore, useShortcutStore,
useToggleView, useToggleView,
useToolMode, useToolMode,
} from "../../store/builder/store"; } from "../../store/builder/store";
import useCameraModeStore, { usePlayButtonStore } from "../../store/usePlayButtonStore"; import useCameraModeStore, {
usePlayButtonStore,
} from "../../store/usePlayButtonStore";
import { detectModifierKeys } from "./detectModifierKeys"; import { detectModifierKeys } from "./detectModifierKeys";
import { useSelectedZoneStore } from "../../store/visualization/useZoneStore"; import { useSelectedZoneStore } from "../../store/visualization/useZoneStore";
@ -30,6 +33,7 @@ const KeyPressListener: React.FC = () => {
const { clearSelectedZone } = useSelectedZoneStore(); const { clearSelectedZone } = useSelectedZoneStore();
const { showShortcuts, setShowShortcuts } = useShortcutStore(); const { showShortcuts, setShowShortcuts } = useShortcutStore();
const { setWalkMode } = useCameraModeStore(); const { setWalkMode } = useCameraModeStore();
const { setIsVersionSaved } = useSaveVersion();
const isTextInput = (element: Element | null): boolean => const isTextInput = (element: Element | null): boolean =>
element instanceof HTMLInputElement || element instanceof HTMLInputElement ||
@ -109,7 +113,6 @@ const KeyPressListener: React.FC = () => {
} }
}; };
const handleSidebarShortcuts = (key: string) => { const handleSidebarShortcuts = (key: string) => {
if (activeModule === "market") return; if (activeModule === "market") return;
@ -145,12 +148,16 @@ const KeyPressListener: React.FC = () => {
} }
}; };
const handleKeyPress = (event: KeyboardEvent) => { const handleKeyPress = (event: KeyboardEvent) => {
if (isTextInput(document.activeElement)) return; if (isTextInput(document.activeElement)) return;
const keyCombination = detectModifierKeys(event); const keyCombination = detectModifierKeys(event);
if (!keyCombination || ["F5", "F11", "F12"].includes(event.key) || keyCombination === "Ctrl+R") return; if (
!keyCombination ||
["F5", "F11", "F12"].includes(event.key) ||
keyCombination === "Ctrl+R"
)
return;
event.preventDefault(); event.preventDefault();
@ -175,6 +182,7 @@ const KeyPressListener: React.FC = () => {
setIsPlaying(false); setIsPlaying(false);
clearSelectedZone(); clearSelectedZone();
setShowShortcuts(false); setShowShortcuts(false);
setIsVersionSaved(false);
} }
if (keyCombination === "Ctrl+Shift+?") { if (keyCombination === "Ctrl+Shift+?") {
@ -182,7 +190,11 @@ const KeyPressListener: React.FC = () => {
} }
// Placeholder for future implementation // Placeholder for future implementation
if (["Ctrl+Z", "Ctrl+Y", "Ctrl+Shift+Z", "Ctrl+H", "Ctrl+F"].includes(keyCombination)) { if (
["Ctrl+Z", "Ctrl+Y", "Ctrl+Shift+Z", "Ctrl+H", "Ctrl+F"].includes(
keyCombination
)
) {
// Implement undo/redo/help/find/shortcuts // Implement undo/redo/help/find/shortcuts
} }
}; };
@ -191,7 +203,14 @@ const KeyPressListener: React.FC = () => {
window.addEventListener("keydown", handleKeyPress); window.addEventListener("keydown", handleKeyPress);
return () => window.removeEventListener("keydown", handleKeyPress); return () => window.removeEventListener("keydown", handleKeyPress);
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [activeModule, toggleUIRight, toggleUILeft, toggleView, showShortcuts, isPlaying]); }, [
activeModule,
toggleUIRight,
toggleUILeft,
toggleView,
showShortcuts,
isPlaying,
]);
return null; return null;
}; };