added ui for changing position of vehicles pickup-point and unloadPoint

This commit is contained in:
Poovizhi99 2025-04-30 09:15:21 +05:30
commit 9574d70b56
58 changed files with 3491 additions and 2435 deletions

Binary file not shown.

Binary file not shown.

View File

@ -42,44 +42,46 @@ export function FlipXAxisIcon() {
}
export function FlipYAxisIcon() {
<svg
width="12"
height="12"
viewBox="0 0 12 12"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M9.0568 11H2.94321C2.09213 11 1.6666 11 1.52703 10.7288C1.38746 10.4576 1.6348 10.1113 2.12947 9.41877L2.6942 8.62814C2.91415 8.32021 3.02413 8.16625 3.18565 8.08313C3.34718 8 3.53639 8 3.9148 8H8.08525C8.46365 8 8.65285 8 8.8144 8.08313C8.9759 8.16625 9.0859 8.32021 9.30585 8.62814L9.87055 9.41876C10.3653 10.1113 10.6126 10.4576 10.473 10.7288C10.3335 11 9.9079 11 9.0568 11Z"
fill="var(--text-color)"
/>
<path
d="M2.99935 3.15388L2.99936 3.1539C3.11318 3.31326 3.18452 3.41252 3.2461 3.48409C3.30271 3.54989 3.33407 3.57152 3.3572 3.58344L2.99935 3.15388ZM2.99935 3.15388L2.43462 2.36329C2.43462 2.36329 2.43462 2.36328 2.43462 2.36328C2.17987 2.00663 2.01464 1.7736 1.92406 1.59704C1.88093 1.51299 1.86801 1.46687 1.86465 1.44495C1.88444 1.43495 1.92948 1.41866 2.02293 1.4049C2.21925 1.37599 2.50491 1.375 2.94321 1.375L9.0568 1.375C9.49511 1.375 9.78078 1.37599 9.9771 1.4049C10.0705 1.41866 10.1156 1.43494 10.1354 1.44495C10.132 1.46687 10.1191 1.51299 10.076 1.59705C9.98539 1.7736 9.82016 2.00663 9.5654 2.36328L9.5654 2.36329L9.0007 3.15389L9.00069 3.15389M2.99935 3.15388L9.00069 3.15389M9.00069 3.15389C8.88688 3.31325 8.81553 3.41252 8.75395 3.48409C8.69737 3.54985 8.666 3.5715 8.64287 3.58343C8.61972 3.59534 8.5839 3.60831 8.49735 3.61614C8.40331 3.62465 8.28108 3.625 8.08525 3.625L3.9148 3.625C3.71896 3.625 3.59672 3.62465 3.50269 3.61614M9.00069 3.15389L3.50269 3.61614M3.50269 3.61614C3.41623 3.60832 3.38041 3.59537 3.35729 3.58348L3.50269 3.61614ZM1.86435 1.42845C1.86468 1.42867 1.86476 1.43108 1.86363 1.43501C1.86345 1.4302 1.86402 1.42823 1.86435 1.42845ZM1.85597 1.4499C1.85343 1.45311 1.85142 1.45444 1.85105 1.4543C1.85068 1.45416 1.85195 1.45255 1.85597 1.4499ZM10.149 1.45429C10.1486 1.45443 10.1466 1.4531 10.1441 1.44989C10.1481 1.45254 10.1493 1.45415 10.149 1.45429ZM10.1364 1.435C10.1353 1.43107 10.1353 1.42867 10.1357 1.42845C10.136 1.42823 10.1366 1.4302 10.1364 1.435Z"
stroke="var(--text-color)"
strokeWidth="0.75"
/>
<path
opacity="0.5"
d="M7 6H5"
stroke="var(--text-color)"
strokeWidth="0.75"
strokeLinecap="round"
/>
<path
opacity="0.5"
d="M3 6H1"
stroke="var(--text-color)"
strokeWidth="0.75"
strokeLinecap="round"
/>
<path
opacity="0.5"
d="M11 6H9"
stroke="var(--text-color)"
strokeWidth="0.75"
strokeLinecap="round"
/>
</svg>;
return (
<svg
width="12"
height="12"
viewBox="0 0 12 12"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M9.0568 11H2.94321C2.09213 11 1.6666 11 1.52703 10.7288C1.38746 10.4576 1.6348 10.1113 2.12947 9.41877L2.6942 8.62814C2.91415 8.32021 3.02413 8.16625 3.18565 8.08313C3.34718 8 3.53639 8 3.9148 8H8.08525C8.46365 8 8.65285 8 8.8144 8.08313C8.9759 8.16625 9.0859 8.32021 9.30585 8.62814L9.87055 9.41876C10.3653 10.1113 10.6126 10.4576 10.473 10.7288C10.3335 11 9.9079 11 9.0568 11Z"
fill="var(--text-color)"
/>
<path
d="M2.99935 3.15388L2.99936 3.1539C3.11318 3.31326 3.18452 3.41252 3.2461 3.48409C3.30271 3.54989 3.33407 3.57152 3.3572 3.58344L2.99935 3.15388ZM2.99935 3.15388L2.43462 2.36329C2.43462 2.36329 2.43462 2.36328 2.43462 2.36328C2.17987 2.00663 2.01464 1.7736 1.92406 1.59704C1.88093 1.51299 1.86801 1.46687 1.86465 1.44495C1.88444 1.43495 1.92948 1.41866 2.02293 1.4049C2.21925 1.37599 2.50491 1.375 2.94321 1.375L9.0568 1.375C9.49511 1.375 9.78078 1.37599 9.9771 1.4049C10.0705 1.41866 10.1156 1.43494 10.1354 1.44495C10.132 1.46687 10.1191 1.51299 10.076 1.59705C9.98539 1.7736 9.82016 2.00663 9.5654 2.36328L9.5654 2.36329L9.0007 3.15389L9.00069 3.15389M2.99935 3.15388L9.00069 3.15389M9.00069 3.15389C8.88688 3.31325 8.81553 3.41252 8.75395 3.48409C8.69737 3.54985 8.666 3.5715 8.64287 3.58343C8.61972 3.59534 8.5839 3.60831 8.49735 3.61614C8.40331 3.62465 8.28108 3.625 8.08525 3.625L3.9148 3.625C3.71896 3.625 3.59672 3.62465 3.50269 3.61614M9.00069 3.15389L3.50269 3.61614M3.50269 3.61614C3.41623 3.60832 3.38041 3.59537 3.35729 3.58348L3.50269 3.61614ZM1.86435 1.42845C1.86468 1.42867 1.86476 1.43108 1.86363 1.43501C1.86345 1.4302 1.86402 1.42823 1.86435 1.42845ZM1.85597 1.4499C1.85343 1.45311 1.85142 1.45444 1.85105 1.4543C1.85068 1.45416 1.85195 1.45255 1.85597 1.4499ZM10.149 1.45429C10.1486 1.45443 10.1466 1.4531 10.1441 1.44989C10.1481 1.45254 10.1493 1.45415 10.149 1.45429ZM10.1364 1.435C10.1353 1.43107 10.1353 1.42867 10.1357 1.42845C10.136 1.42823 10.1366 1.4302 10.1364 1.435Z"
stroke="var(--text-color)"
strokeWidth="0.75"
/>
<path
opacity="0.5"
d="M7 6H5"
stroke="var(--text-color)"
strokeWidth="0.75"
strokeLinecap="round"
/>
<path
opacity="0.5"
d="M3 6H1"
stroke="var(--text-color)"
strokeWidth="0.75"
strokeLinecap="round"
/>
<path
opacity="0.5"
d="M11 6H9"
stroke="var(--text-color)"
strokeWidth="0.75"
strokeLinecap="round"
/>
</svg>
);
}
export function FlipZAxisIcon() {
return (

View File

@ -168,20 +168,6 @@ export function AddIcon() {
);
}
export function RmoveIcon() {
return (
<svg
width="12"
height="12"
viewBox="0 0 12 12"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M3 6.5H9" stroke="var(--text-color)" strokeLinecap="round" />
</svg>
);
}
export function CloseIcon() {
return (
<svg

View File

@ -124,7 +124,6 @@ export function LogoIconLarge() {
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="45" cy="45" r="45" fill="#FCFDFD" />
<circle
cx="45.1957"
cy="45.1957"

View File

@ -0,0 +1,93 @@
export function ThroughputSummaryIcon() {
return (
<svg
width="26"
height="27"
viewBox="0 0 26 27"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="13.3457" cy="13.498" r="12.6543" fill="#FC9D2F" />
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M13.9063 12.9046L14.2265 13.86L14.4378 14.5073C14.9906 16.2239 15.2594 17.2662 15.2594 17.7299C15.2594 18.8219 14.3742 19.7072 13.2822 19.7072C12.1902 19.7072 11.305 18.8219 11.305 17.7299C11.305 17.2106 11.6422 15.9654 12.3379 13.86L12.658 12.9046C12.8604 12.3082 13.704 12.3082 13.9063 12.9046ZM13.2822 7.84375C16.9222 7.84375 19.873 10.7945 19.873 14.4345C19.873 15.7565 19.4823 17.0219 18.7621 18.0974C18.5596 18.3999 18.1502 18.4809 17.8478 18.2784C17.5453 18.0758 17.4643 17.6665 17.6668 17.364C18.2428 16.5038 18.5548 15.4933 18.5548 14.4345C18.5548 11.5225 16.1942 9.16191 13.2822 9.16191C10.3702 9.16191 8.00956 11.5225 8.00956 14.4345C8.00956 15.4933 8.32153 16.5038 8.89752 17.364C9.10005 17.6665 9.01904 18.0758 8.71659 18.2784C8.41414 18.4809 8.00477 18.3999 7.80224 18.0974C7.08206 17.0219 6.69141 15.7565 6.69141 14.4345C6.69141 10.7945 9.6422 7.84375 13.2822 7.84375ZM13.2822 15.2247L13.0657 15.9238L12.9161 16.4319C12.7219 17.111 12.6231 17.5509 12.6231 17.7299C12.6231 18.0939 12.9182 18.389 13.2822 18.389C13.6462 18.389 13.9413 18.0939 13.9413 17.7299C13.9413 17.511 13.7936 16.9022 13.5044 15.9428L13.2822 15.2247Z"
fill="white"
/>
</svg>
);
}
export function ProductionCapacityIcon() {
return (
<svg
width="26"
height="27"
viewBox="0 0 26 27"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="13.3457" cy="13.498" r="12.6543" fill="#FC9D2F" />
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M13.9063 12.9046L14.2265 13.86L14.4378 14.5073C14.9906 16.2239 15.2594 17.2662 15.2594 17.7299C15.2594 18.8219 14.3742 19.7072 13.2822 19.7072C12.1902 19.7072 11.305 18.8219 11.305 17.7299C11.305 17.2106 11.6422 15.9654 12.3379 13.86L12.658 12.9046C12.8604 12.3082 13.704 12.3082 13.9063 12.9046ZM13.2822 7.84375C16.9222 7.84375 19.873 10.7945 19.873 14.4345C19.873 15.7565 19.4823 17.0219 18.7621 18.0974C18.5596 18.3999 18.1502 18.4809 17.8478 18.2784C17.5453 18.0758 17.4643 17.6665 17.6668 17.364C18.2428 16.5038 18.5548 15.4933 18.5548 14.4345C18.5548 11.5225 16.1942 9.16191 13.2822 9.16191C10.3702 9.16191 8.00956 11.5225 8.00956 14.4345C8.00956 15.4933 8.32153 16.5038 8.89752 17.364C9.10005 17.6665 9.01904 18.0758 8.71659 18.2784C8.41414 18.4809 8.00477 18.3999 7.80224 18.0974C7.08206 17.0219 6.69141 15.7565 6.69141 14.4345C6.69141 10.7945 9.6422 7.84375 13.2822 7.84375ZM13.2822 15.2247L13.0657 15.9238L12.9161 16.4319C12.7219 17.111 12.6231 17.5509 12.6231 17.7299C12.6231 18.0939 12.9182 18.389 13.2822 18.389C13.6462 18.389 13.9413 18.0939 13.9413 17.7299C13.9413 17.511 13.7936 16.9022 13.5044 15.9428L13.2822 15.2247Z"
fill="white"
/>
</svg>
);
}
export function ROISummaryIcon() {
return (
<svg
width="25"
height="26"
viewBox="0 0 25 26"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect y="0.515625" width="25" height="25" rx="12.5" fill="#28B9F3" />
<path
d="M6.00015 7.51562V19.0974H19.0002"
stroke="white"
stroke-linecap="round"
/>
<path
d="M6.50037 15.0553L10.3102 11.847C10.6984 11.52 11.2701 11.5358 11.6397 11.8837L15.0095 15.0553L19.5004 11.2734"
stroke="white"
stroke-linecap="round"
/>
</svg>
);
}
export function PowerIcon() {
return (
<svg
width="21"
height="21"
viewBox="0 0 21 21"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clip-path="url(#clip0_4107_3144)">
<path
d="M12.1277 1.76564L10.7174 9.17535L15.8265 9.19254L8.87213 19.2375L10.2824 11.0856L5.17369 11.0678L12.1277 1.76564ZM12.1287 0.515664C12.0949 0.515664 12.0612 0.516895 12.0281 0.519375C11.8075 0.537207 11.6612 0.610957 11.4878 0.72752C11.3901 0.792624 11.3021 0.871096 11.2262 0.960645C11.2034 0.987526 11.1819 1.01547 11.1618 1.04439L4.15775 10.3141C3.88119 10.6931 3.84056 11.1935 4.05306 11.6116C4.26525 12.0297 4.69431 12.2947 5.16463 12.2982L8.77275 12.3244L7.63838 19.0079C7.53056 19.5822 7.83681 20.1547 8.37588 20.3854C8.53254 20.4527 8.70128 20.4873 8.87179 20.4872C9.26461 20.4872 9.58742 20.3035 9.82963 19.9716L16.8424 9.92658C17.119 9.5475 17.1593 9.04656 16.9471 8.62906C16.7349 8.21094 16.3059 7.94592 15.8356 7.9425L12.2274 7.93625L13.3496 2.05969C13.3734 1.96348 13.3854 1.86473 13.3853 1.76562C13.3853 1.08938 12.8468 0.538125 12.1731 0.51625C12.1581 0.515625 12.1434 0.515625 12.1287 0.515625L12.1287 0.515664Z"
fill="#F3C64D"
/>
<path
d="M4.875 11.125L12.375 1.125L11.125 8.625H16.125L8.625 19.875L9.875 11.125H4.875Z"
fill="#F3C64D"
/>
</g>
<defs>
<clipPath id="clip0_4107_3144">
<rect
width="20"
height="20"
fill="white"
transform="translate(0.5 0.5)"
/>
</clipPath>
</defs>
</svg>
);
}

View File

@ -23,16 +23,16 @@ const MarketPlaceBanner = () => {
<path
d="M167.189 2C154.638 36.335 104.466 106.204 4.18872 111"
stroke="white"
stroke-width="3"
stroke-linecap="round"
stroke-linejoin="round"
strokeWidth="3"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M10.662 118.326L1.59439 111.524L9.47334 103.374"
stroke="white"
stroke-width="3"
stroke-linecap="round"
stroke-linejoin="round"
strokeWidth="3"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</div>

View File

@ -11,7 +11,7 @@ import {
import { SettingsIcon, TrashIcon } from "../../icons/ExportCommonIcons";
const SidePannel: React.FC = () => {
const userName = localStorage.getItem("userName") || "Anonymous";
const userName = localStorage.getItem("userName") ?? "Anonymous";
return (
<div className="side-pannel-container">
<div className="side-pannel-header">

View File

@ -17,12 +17,12 @@ const ConfirmationPopup: React.FC<ConfirmationPopupProps> = ({
<div className="confirmation-modal">
<p className="message">{message}</p>
<div className="buttton-wrapper">
<div className="confirmation-button" onClick={onConfirm}>
<button className="confirmation-button" onClick={onConfirm}>
OK
</div>
<div className="confirmation-button" onClick={onCancel}>
</button>
<button className="confirmation-button" onClick={onCancel}>
Cancel
</div>
</button>
</div>
</div>
</div>

View File

@ -1,6 +1,5 @@
import React, { useEffect, useRef, useMemo } from "react";
import { Chart } from "chart.js/auto";
// import { useThemeStore } from "../../../../../store/useThemeStore";
// Define Props Interface
interface ChartComponentProps {
@ -29,7 +28,6 @@ const ChartComponent = ({
data: propsData,
}: ChartComponentProps) => {
const canvasRef = useRef<HTMLCanvasElement>(null);
// const { themeColor } = useThemeStore();
// Memoize Theme Colors to Prevent Unnecessary Recalculations
// const buttonActionColor = useMemo(
@ -66,7 +64,7 @@ const ChartComponent = ({
// Memoize Chart Font Style
const chartFontStyle = useMemo(
() => ({
family: fontFamily || "Arial",
family: fontFamily ?? "Arial",
size: fontSizeValue,
weight: fontWeightValue,
color: "#2B3344",

View File

@ -1,4 +1,3 @@
import { useState } from "react";
import ToggleHeader from "../../../../ui/inputs/ToggleHeader";
import Widgets2D from "./Widgets2D";
import Widgets3D from "./Widgets3D";
@ -6,7 +5,6 @@ import WidgetsFloating from "./WidgetsFloating";
import { useWidgetSubOption } from "../../../../../store/store";
const Widgets = () => {
const [activeOption, setActiveOption] = useState("2D");
const { widgetSubOption, setWidgetSubOption } = useWidgetSubOption();
const handleToggleClick = (option: string) => {

View File

@ -7,43 +7,12 @@ import {
} from "../../../../icons/3dChartIcons";
import SimpleCard from "../../../../../modules/visualization/widgets/floating/cards/SimpleCard";
import WarehouseThroughput from "../../../../../modules/visualization/widgets/floating/cards/WarehouseThroughput";
import ProductivityDashboard from "../../../../../modules/visualization/widgets/floating/cards/ProductivityDashboard";
import FleetEfficiency from "../../../../../modules/visualization/widgets/floating/cards/FleetEfficiency";
interface Widget {
id: string;
name: string;
}
import WarehouseThroughput from "../../../../../modules//visualization/widgets/floating/cards/WarehouseThroughput";
import FleetEfficiency from "../../../../../modules//visualization/widgets/floating/cards/FleetEfficiency";
const WidgetsFloating = () => {
// const [widgets, setWidgets] = useState<Widget[]>([
// { id: "1", name: "Working State Widget" },
// { id: "2", name: "Floating Widget 2" },
// { id: "3", name: "Floating Widget 3" },
// { id: "4", name: "Floating Widget 4" },
// ]);
// Function to handle drag start
const handleDragStart = (
e: React.DragEvent<HTMLDivElement>,
widget: Widget
) => {
e.dataTransfer.setData("application/json", JSON.stringify(widget));
};
return (
<div className="floatingWidgets-wrapper widgets-wrapper">
{/* {widgets.map((widget) => (
<div
key={widget.id}
className="floating"
draggable
onDragStart={(e) => handleDragStart(e, widget)}
>
{widget.name}
</div>
))} */}
{/* Floating 1 */}
<SimpleCard
header={"Todays Earnings"}

View File

@ -1,148 +1,161 @@
import React, { useEffect } from "react";
import Header from "./Header";
import useModuleStore, {
useSubModuleStore,
useSubModuleStore,
} from "../../../store/useModuleStore";
import {
AnalysisIcon,
MechanicsIcon,
PropertiesIcon,
SimulationIcon,
AnalysisIcon,
MechanicsIcon,
PropertiesIcon,
SimulationIcon,
} from "../../icons/SimulationIcons";
import useToggleStore from "../../../store/useUIToggleStore";
import Visualization from "./visualization/Visualization";
import Analysis from "./analysis/Analysis";
import Simulations from "./simulation/Simulations";
import { useSelectedFloorItem } from "../../../store/store";
import { useSelectedEventData, useSelectedEventSphere } from "../../../store/simulation/useSimulationStore";
import {
useSelectedEventData,
useSelectedEventSphere,
} from "../../../store/simulation/useSimulationStore";
import GlobalProperties from "./properties/GlobalProperties";
import AsstePropertiies from "./properties/AssetProperties";
import ZoneProperties from "./properties/ZoneProperties";
import EventProperties from "./properties/eventProperties/EventProperties";
const SideBarRight: React.FC = () => {
const { activeModule } = useModuleStore();
const { toggleUI } = useToggleStore();
const { subModule, setSubModule } = useSubModuleStore();
const { selectedFloorItem } = useSelectedFloorItem();
const { selectedEventData } = useSelectedEventData();
const { selectedEventSphere } = useSelectedEventSphere();
const { activeModule } = useModuleStore();
const { toggleUI } = useToggleStore();
const { subModule, setSubModule } = useSubModuleStore();
const { selectedFloorItem } = useSelectedFloorItem();
const { selectedEventData } = useSelectedEventData();
const { selectedEventSphere } = useSelectedEventSphere();
// Reset activeList whenever activeModule changes
useEffect(() => {
if (activeModule !== "simulation") setSubModule("properties");
if (activeModule === "simulation") setSubModule("simulations");
}, [activeModule]);
// Reset activeList whenever activeModule changes
useEffect(() => {
if (activeModule !== "simulation") setSubModule("properties");
if (activeModule === "simulation") setSubModule("simulations");
}, [activeModule, setSubModule]);
useEffect(() => {
if (activeModule !== "mechanics" && selectedEventData && selectedEventSphere) {
setSubModule("mechanics");
} else if (!selectedEventData && !selectedEventSphere) {
if (activeModule === 'simulation') {
setSubModule("simulations");
}
};
}, [activeModule, selectedEventData, selectedEventSphere])
useEffect(() => {
if (
activeModule !== "mechanics" &&
selectedEventData &&
selectedEventSphere
) {
setSubModule("mechanics");
} else if (!selectedEventData && !selectedEventSphere) {
if (activeModule === "simulation") {
setSubModule("simulations");
}
}
}, [activeModule, selectedEventData, selectedEventSphere, setSubModule]);
return (
<div className="sidebar-right-wrapper">
<Header />
{toggleUI && (
<div className="sidebar-actions-container">
<div
className={`sidebar-action-list ${subModule === "properties" ? "active" : ""
}`}
onClick={() => setSubModule("properties")}
>
<PropertiesIcon isActive={subModule === "properties"} />
</div>
{activeModule === "simulation" && (
<>
<div
className={`sidebar-action-list ${subModule === "mechanics" ? "active" : ""
}`}
onClick={() => setSubModule("mechanics")}
>
<MechanicsIcon isActive={subModule === "mechanics"} />
</div>
<div
className={`sidebar-action-list ${subModule === "simulations" ? "active" : ""
}`}
onClick={() => setSubModule("simulations")}
>
<SimulationIcon isActive={subModule === "simulations"} />
</div>
<div
className={`sidebar-action-list ${subModule === "analysis" ? "active" : ""
}`}
onClick={() => setSubModule("analysis")}
>
<AnalysisIcon isActive={subModule === "analysis"} />
</div>
</>
)}
</div>
)}
{/* process builder */}
{toggleUI &&
subModule === "properties" &&
activeModule !== "visualization" &&
!selectedFloorItem && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<GlobalProperties />
</div>
</div>
)}
{toggleUI &&
subModule === "properties" &&
activeModule !== "visualization" &&
selectedFloorItem && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<AsstePropertiies />
</div>
</div>
)}
{toggleUI &&
subModule === "zoneProperties" &&
(activeModule === "builder" || activeModule === "simulation") && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<ZoneProperties />
</div>
</div>
)}
{/* simulation */}
{toggleUI && activeModule === "simulation" && (
<>
{subModule === "simulations" && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<Simulations />
</div>
</div>
)}
{subModule === "mechanics" && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<EventProperties />
</div>
</div>
)}
{subModule === "analysis" && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<Analysis />
</div>
</div>
)}
</>
)}
{/* realtime visualization */}
{toggleUI && activeModule === "visualization" && <Visualization />}
return (
<div className="sidebar-right-wrapper">
<Header />
{toggleUI && (
<div className="sidebar-actions-container">
{activeModule !== "simulation" && (
<button
className={`sidebar-action-list ${
subModule === "properties" ? "active" : ""
}`}
onClick={() => setSubModule("properties")}
>
<PropertiesIcon isActive={subModule === "properties"} />
</button>
)}
{activeModule === "simulation" && (
<>
<button
className={`sidebar-action-list ${
subModule === "simulations" ? "active" : ""
}`}
onClick={() => setSubModule("simulations")}
>
<SimulationIcon isActive={subModule === "simulations"} />
</button>
<button
className={`sidebar-action-list ${
subModule === "mechanics" ? "active" : ""
}`}
onClick={() => setSubModule("mechanics")}
>
<MechanicsIcon isActive={subModule === "mechanics"} />
</button>
<button
className={`sidebar-action-list ${
subModule === "analysis" ? "active" : ""
}`}
onClick={() => setSubModule("analysis")}
>
<AnalysisIcon isActive={subModule === "analysis"} />
</button>
</>
)}
</div>
);
)}
{/* process builder */}
{toggleUI &&
subModule === "properties" &&
activeModule !== "visualization" &&
!selectedFloorItem && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<GlobalProperties />
</div>
</div>
)}
{toggleUI &&
subModule === "properties" &&
activeModule !== "visualization" &&
selectedFloorItem && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<AsstePropertiies />
</div>
</div>
)}
{toggleUI &&
subModule === "zoneProperties" &&
(activeModule === "builder" || activeModule === "simulation") && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<ZoneProperties />
</div>
</div>
)}
{/* simulation */}
{toggleUI && activeModule === "simulation" && (
<>
{subModule === "simulations" && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<Simulations />
</div>
</div>
)}
{subModule === "mechanics" && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<EventProperties />
</div>
</div>
)}
{subModule === "analysis" && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<Analysis />
</div>
</div>
)}
</>
)}
{/* realtime visualization */}
{toggleUI && activeModule === "visualization" && <Visualization />}
</div>
);
};
export default SideBarRight;
export default SideBarRight;

View File

@ -1,64 +1,132 @@
import React, { useEffect, useRef, useState } from "react";
import { useSelectedEventData, useSelectedProduct } from "../../../../../store/simulation/useSimulationStore";
import React, { useEffect, useState } from "react";
import {
useSelectedAsset,
useSelectedEventData,
useSelectedEventSphere,
useSelectedProduct,
} from "../../../../../store/simulation/useSimulationStore";
import { useProductStore } from "../../../../../store/simulation/useProductStore";
import ConveyorMechanics from "./mechanics/conveyorMechanics";
import VehicleMechanics from "./mechanics/vehicleMechanics";
import RoboticArmMechanics from "./mechanics/roboticArmMechanics";
import MachineMechanics from "./mechanics/machineMechanics";
import StorageMechanics from "./mechanics/storageMechanics";
import { AddIcon } from "../../../../icons/ExportCommonIcons";
import { handleAddEventToProduct } from "../../../../../modules/simulation/events/points/functions/handleAddEventToProduct";
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 { 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);
const { selectedAsset, clearSelectedAsset } = useSelectedAsset();
useEffect(() => {
const event = getCurrentEventData();
setCurrentEventData(event);
const type = determineAssetType(event);
setAssetType(type);
}, [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 />}
</>
}
</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>
<ul>
{products.map((product) => (
<li key={product.productId}>
<button
onClick={() =>
handleAddEventToProduct({
selectedAsset,
addEvent,
selectedProduct: {
productId: product.productId,
productName: product.productName,
},
clearSelectedAsset,
})
}
>
<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>
);
};
export default EventProperties;
export default EventProperties;

View File

@ -2,29 +2,33 @@ import React from "react";
import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown";
interface DelayActionProps {
value: string;
defaultValue: string;
min: number;
max: number;
onChange: (value: string) => void;
value: string;
defaultValue: string;
min: number;
max: number;
onChange: (value: string) => void;
}
const DelayAction: React.FC<DelayActionProps> = ({ value, defaultValue, min, max, onChange }) => {
return (
<>
<InputWithDropDown
label="Delay"
value={value}
min={min}
step={0.1}
defaultValue={defaultValue}
max={max}
activeOption="s"
onClick={() => { }}
onChange={onChange}
/>
</>
);
const DelayAction: React.FC<DelayActionProps> = ({
value,
defaultValue,
min,
max,
onChange,
}) => {
return (
<InputWithDropDown
label="Delay"
value={value}
min={min}
step={0.1}
defaultValue={defaultValue}
max={max}
activeOption="s"
onClick={() => {}}
onChange={onChange}
/>
);
};
export default DelayAction;

View File

@ -1,5 +1,4 @@
import React from "react";
import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown";
const DespawnAction: React.FC = () => {
return (

View File

@ -1,27 +1,25 @@
import React from "react";
import PreviewSelectionWithUpload from "../../../../../ui/inputs/PreviewSelectionWithUpload";
import LabledDropdown from "../../../../../ui/inputs/LabledDropdown";
interface SwapActionProps {
onSelect: (option: string) => void;
defaultOption: string;
options: string[];
onSelect: (option: string) => void;
defaultOption: string;
options: string[];
}
const SwapAction: React.FC<SwapActionProps> = ({ onSelect, defaultOption, options }) => {
return (
<>
{/* <PreviewSelectionWithUpload /> */}
<LabledDropdown
label="Presets"
defaultOption={defaultOption}
options={options}
onSelect={onSelect}
/>
</>
);
const SwapAction: React.FC<SwapActionProps> = ({
onSelect,
defaultOption,
options,
}) => {
return (
<PreviewSelectionWithUpload
label="Presets"
defaultOption={defaultOption}
options={options}
onSelect={onSelect}
/>
);
};
export default SwapAction;

View File

@ -0,0 +1,202 @@
import React, { useRef } from "react";
import {
AddIcon,
RemoveIcon,
ResizeHeightIcon,
} from "../../../../../icons/ExportCommonIcons";
import RenameInput from "../../../../../ui/inputs/RenameInput";
import { handleResize } from "../../../../../../functions/handleResizePannel";
import {
useSelectedAction,
useSelectedEventData,
useSelectedProduct,
} from "../../../../../../store/simulation/useSimulationStore";
import { MathUtils } from "three";
import { useProductStore } from "../../../../../../store/simulation/useProductStore";
interface ActionsListProps {
setSelectedPointData: (data: any) => void; // You can replace `any` with a more specific type if you have one
selectedPointData: any; // You can replace `any` with a more specific type if you have one
// ui control props
multipleAction?: boolean;
}
const ActionsList: React.FC<ActionsListProps> = ({
setSelectedPointData,
selectedPointData,
multipleAction = false,
}) => {
const actionsContainerRef = useRef<HTMLDivElement>(null);
// store
const { selectedEventData } = useSelectedEventData();
const { updateAction, addAction, removeAction } = useProductStore();
const { selectedProduct } = useSelectedProduct();
const { selectedAction, setSelectedAction, clearSelectedAction } =
useSelectedAction();
const handleAddAction = () => {
if (!selectedEventData || !selectedPointData) return;
const newAction = {
actionUuid: MathUtils.generateUUID(),
actionName: `Action ${selectedPointData.actions.length + 1}`,
actionType: "pickAndPlace" as const,
process: {
startPoint: null,
endPoint: null,
},
triggers: [] as TriggerSchema[],
};
addAction(
selectedProduct.productId,
selectedEventData.data.modelUuid,
selectedEventData.selectedPoint,
newAction
);
const updatedPoint = {
...selectedPointData,
actions: [...selectedPointData.actions, newAction],
};
setSelectedPointData(updatedPoint);
setSelectedAction(newAction.actionUuid, newAction.actionName);
};
const handleDeleteAction = (actionUuid: string) => {
if (!selectedPointData) return;
removeAction(actionUuid);
const newActions = selectedPointData.actions.filter(
(a: any) => a.actionUuid !== actionUuid
);
const updatedPoint = {
...selectedPointData,
actions: newActions,
};
setSelectedPointData(updatedPoint);
if (selectedAction.actionId === actionUuid) {
if (newActions.length > 0) {
setSelectedAction(newActions[0].actionUuid, newActions[0].actionName);
} else {
clearSelectedAction();
}
}
};
const handleRenameAction = (newName: string) => {
if (!selectedAction.actionId) return;
updateAction(selectedAction.actionId, { actionName: newName });
if (selectedPointData?.actions) {
const updatedActions = selectedPointData.actions.map((action: any) =>
action.actionUuid === selectedAction.actionId
? { ...action, actionName: newName }
: action
);
setSelectedPointData({
...selectedPointData,
actions: updatedActions,
});
} else {
// write logic for single action
return;
}
};
const handleActionSelect = (actionUuid: string, actionName: string) => {
setSelectedAction(actionUuid, actionName);
};
return (
<div className="actions-list-container">
<div className="actions">
<div className="header">
<div className="header-value">Actions</div>
<button
className="add-button"
onClick={() => handleAddAction()}
disabled={!multipleAction}
>
<AddIcon /> Add
</button>
</div>
<div
className="lists-main-container"
ref={actionsContainerRef}
style={{ height: "120px" }}
>
<div className="list-container">
{multipleAction &&
selectedPointData.actions.map((action: any) => (
<div
key={action.actionUuid}
className={`list-item ${
selectedAction.actionId === action.actionUuid
? "active"
: ""
}`}
>
<button
className="value"
onClick={() =>
handleActionSelect(action.actionUuid, action.actionName)
}
>
<RenameInput
value={action.actionName}
onRename={handleRenameAction}
/>
</button>
{selectedPointData.actions.length > 1 && (
<button
className="remove-button"
onClick={() => handleDeleteAction(action.actionUuid)}
>
<RemoveIcon />
</button>
)}
</div>
))}
{!multipleAction && selectedPointData && (
<div
key={selectedPointData.action.actionUuid}
className={`list-item active`}
>
<button
className="value"
onClick={() =>
handleActionSelect(
selectedPointData.action.actionUuid,
selectedPointData.action.actionName
)
}
>
<RenameInput
value={selectedPointData.action.actionName}
onRename={handleRenameAction}
/>
</button>
</div>
)}
</div>
{multipleAction && (
<button
className="resize-icon"
id="action-resize"
onMouseDown={(e: any) => handleResize(e, actionsContainerRef)}
>
<ResizeHeightIcon />
</button>
)}
</div>
</div>
</div>
);
};
export default ActionsList;

View File

@ -1,212 +1,224 @@
import { useEffect, useState } from 'react'
import InputWithDropDown from '../../../../../ui/inputs/InputWithDropDown'
import DelayAction from '../actions/DelayAction'
import RenameInput from '../../../../../ui/inputs/RenameInput'
import LabledDropdown from '../../../../../ui/inputs/LabledDropdown'
import DespawnAction from '../actions/DespawnAction'
import SwapAction from '../actions/SwapAction'
import SpawnAction from '../actions/SpawnAction'
import DefaultAction from '../actions/DefaultAction'
import Trigger from '../trigger/Trigger'
import { useSelectedEventData, useSelectedProduct } from "../../../../../../store/simulation/useSimulationStore";
import { useEffect, useState } from "react";
import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown";
import DelayAction from "../actions/DelayAction";
import RenameInput from "../../../../../ui/inputs/RenameInput";
import LabledDropdown from "../../../../../ui/inputs/LabledDropdown";
import DespawnAction from "../actions/DespawnAction";
import SwapAction from "../actions/SwapAction";
import SpawnAction from "../actions/SpawnAction";
import DefaultAction from "../actions/DefaultAction";
import Trigger from "../trigger/Trigger";
import {
useSelectedEventData,
useSelectedProduct,
} from "../../../../../../store/simulation/useSimulationStore";
import { useProductStore } from "../../../../../../store/simulation/useProductStore";
import ActionsList from "../components/ActionsList";
function ConveyorMechanics() {
const [activeOption, setActiveOption] = useState<"default" | "spawn" | "swap" | "delay" | "despawn">("default");
const [selectedPointData, setSelectedPointData] = useState<ConveyorPointSchema | undefined>();
const { selectedEventData } = useSelectedEventData();
const { getPointByUuid, updateEvent, updateAction } = useProductStore();
const { selectedProduct } = useSelectedProduct();
const [activeOption, setActiveOption] = useState<
"default" | "spawn" | "swap" | "delay" | "despawn"
>("default");
const [selectedPointData, setSelectedPointData] = useState<
ConveyorPointSchema | undefined
>();
const { selectedEventData } = useSelectedEventData();
const { getPointByUuid, updateEvent, updateAction } = useProductStore();
const { selectedProduct } = useSelectedProduct();
useEffect(() => {
if (selectedEventData) {
const point = getPointByUuid(
selectedProduct.productId,
selectedEventData?.data.modelUuid,
selectedEventData?.selectedPoint
) as ConveyorPointSchema | undefined;
if (point && 'action' in point) {
setSelectedPointData(point);
setActiveOption(point.action.actionType as "default" | "spawn" | "swap" | "delay" | "despawn");
}
}
}, [selectedProduct, selectedEventData])
const handleSpeedChange = (value: string) => {
if (!selectedEventData) return;
updateEvent(
selectedProduct.productId,
selectedEventData.data.modelUuid,
{ speed: parseFloat(value) }
useEffect(() => {
if (selectedEventData) {
const point = getPointByUuid(
selectedProduct.productId,
selectedEventData?.data.modelUuid,
selectedEventData?.selectedPoint
) as ConveyorPointSchema | undefined;
if (point && "action" in point) {
setSelectedPointData(point);
setActiveOption(
point.action.actionType as
| "default"
| "spawn"
| "swap"
| "delay"
| "despawn"
);
};
}
}
}, [selectedProduct, selectedEventData, getPointByUuid]);
const handleActionTypeChange = (option: string) => {
if (!selectedEventData || !selectedPointData) return;
const validOption = option as "default" | "spawn" | "swap" | "delay" | "despawn";
setActiveOption(validOption);
const handleSpeedChange = (value: string) => {
if (!selectedEventData) return;
updateEvent(selectedProduct.productId, selectedEventData.data.modelUuid, {
speed: parseFloat(value),
});
};
updateAction(
selectedPointData.action.actionUuid,
{ actionType: validOption }
);
};
const handleActionTypeChange = (option: string) => {
if (!selectedEventData || !selectedPointData) return;
const validOption = option as
| "default"
| "spawn"
| "swap"
| "delay"
| "despawn";
setActiveOption(validOption);
const handleRenameAction = (newName: string) => {
if (!selectedPointData) return;
updateAction(
selectedPointData.action.actionUuid,
{ actionName: newName }
);
};
updateAction(selectedPointData.action.actionUuid, {
actionType: validOption,
});
};
const handleSpawnCountChange = (value: string) => {
if (!selectedPointData) return;
updateAction(
selectedPointData.action.actionUuid,
{ spawnCount: value === "inherit" ? "inherit" : parseFloat(value) }
);
};
const handleRenameAction = (newName: string) => {
if (!selectedPointData) return;
updateAction(selectedPointData.action.actionUuid, { actionName: newName });
};
const handleSpawnIntervalChange = (value: string) => {
if (!selectedPointData) return;
updateAction(
selectedPointData.action.actionUuid,
{ spawnInterval: value === "inherit" ? "inherit" : parseFloat(value) }
);
};
const handleSpawnCountChange = (value: string) => {
if (!selectedPointData) return;
updateAction(selectedPointData.action.actionUuid, {
spawnCount: value === "inherit" ? "inherit" : parseFloat(value),
});
};
const handleMaterialSelect = (material: string) => {
if (!selectedPointData) return;
updateAction(
selectedPointData.action.actionUuid,
{ material }
);
};
const handleSpawnIntervalChange = (value: string) => {
if (!selectedPointData) return;
updateAction(selectedPointData.action.actionUuid, {
spawnInterval: value === "inherit" ? "inherit" : parseFloat(value),
});
};
const handleDelayChange = (value: string) => {
if (!selectedPointData) return;
updateAction(
selectedPointData.action.actionUuid,
{ delay: value === "inherit" ? "inherit" : parseFloat(value) }
);
};
const handleMaterialSelect = (material: string) => {
if (!selectedPointData) return;
updateAction(selectedPointData.action.actionUuid, { material });
};
const availableActions = {
defaultOption: "default",
options: ["default", "spawn", "swap", "delay", "despawn"],
};
const handleDelayChange = (value: string) => {
if (!selectedPointData) return;
updateAction(selectedPointData.action.actionUuid, {
delay: value === "inherit" ? "inherit" : parseFloat(value),
});
};
// Get current values from store
const currentSpeed = selectedEventData?.data.type === "transfer"
? selectedEventData.data.speed.toString()
: "0.5";
const availableActions = {
defaultOption: "default",
options: ["default", "spawn", "swap", "delay", "despawn"],
};
const currentActionName = selectedPointData
? selectedPointData.action.actionName
: "Action Name";
// Get current values from store
const currentSpeed =
selectedEventData?.data.type === "transfer"
? selectedEventData.data.speed.toString()
: "0.5";
const currentMaterial = selectedPointData
? selectedPointData.action.material
: "Default material";
const currentActionName = selectedPointData
? selectedPointData.action.actionName
: "Action Name";
const currentSpawnCount = selectedPointData
? selectedPointData.action.spawnCount?.toString() || "1"
: "1";
const currentMaterial = selectedPointData
? selectedPointData.action.material
: "Default material";
const currentSpawnInterval = selectedPointData
? selectedPointData.action.spawnInterval?.toString() || "1"
: "1";
const currentSpawnCount = selectedPointData
? selectedPointData.action.spawnCount?.toString() || "1"
: "1";
const currentDelay = selectedPointData
? selectedPointData.action.delay?.toString() || "0"
: "0";
const currentSpawnInterval = selectedPointData
? selectedPointData.action.spawnInterval?.toString() || "1"
: "1";
return (
const currentDelay = selectedPointData
? selectedPointData.action.delay?.toString() || "0"
: "0";
return (
<>
{selectedEventData && (
<>
{selectedEventData &&
<>
<div key={selectedPointData?.uuid} className="global-props">
<div className="property-list-container">
<div className="property-item">
<InputWithDropDown
label="Speed"
value={currentSpeed}
min={0}
step={0.1}
defaultValue={"0.5"}
max={10}
activeOption="m/s"
onClick={() => { }}
onChange={handleSpeedChange}
/>
</div>
</div>
</div>
<div key={selectedPointData?.uuid} className="global-props">
<div className="property-list-container">
<div className="property-item">
<InputWithDropDown
label="Speed"
value={currentSpeed}
min={0}
step={0.1}
defaultValue={"0.5"}
max={10}
activeOption="m/s"
onClick={() => {}}
onChange={handleSpeedChange}
/>
</div>
</div>
</div>
<div className="selected-actions-details">
<div className="selected-actions-header">
<RenameInput
value={currentActionName}
onRename={handleRenameAction}
/>
</div>
<div className="selected-actions-list">
<LabledDropdown
defaultOption={selectedPointData
? selectedPointData.action.actionType
: "default"}
options={availableActions.options}
onSelect={handleActionTypeChange}
/>
{activeOption === "default" &&
<DefaultAction />
}
{activeOption === "spawn" &&
<SpawnAction
onChangeCount={handleSpawnCountChange}
options={["Default material", "Material 1", "Material 2"]}
defaultOption={currentMaterial}
onSelect={handleMaterialSelect}
onChangeInterval={handleSpawnIntervalChange}
intervalValue={currentSpawnInterval}
countValue={currentSpawnCount}
intervalMin={1}
intervalMax={60}
intervalDefaultValue="1"
countMin={1}
countMax={100}
countDefaultValue="1"
/>
}
{activeOption === "swap" &&
<SwapAction
options={["Default material", "Material 1", "Material 2"]}
defaultOption={currentMaterial}
onSelect={handleMaterialSelect}
/>
}
{activeOption === "despawn" &&
<DespawnAction />
}
{activeOption === "delay" &&
<DelayAction
value={currentDelay}
defaultValue="0"
min={0}
max={60}
onChange={handleDelayChange}
/>
}
</div>
</div>
<div className="tirgger">
<Trigger />
</div>
</>
}
<ActionsList
setSelectedPointData={setSelectedPointData}
selectedPointData={selectedPointData}
/>
<div className="selected-actions-details">
<div className="selected-actions-header">
<RenameInput
value={currentActionName}
onRename={handleRenameAction}
/>
</div>
<div className="selected-actions-list">
<LabledDropdown
defaultOption={
selectedPointData
? selectedPointData.action.actionType
: "default"
}
options={availableActions.options}
onSelect={handleActionTypeChange}
/>
{activeOption === "default" && <DefaultAction />}
{activeOption === "spawn" && (
<SpawnAction
onChangeCount={handleSpawnCountChange}
options={["Default material", "Material 1", "Material 2"]}
defaultOption={currentMaterial}
onSelect={handleMaterialSelect}
onChangeInterval={handleSpawnIntervalChange}
intervalValue={currentSpawnInterval}
countValue={currentSpawnCount}
intervalMin={1}
intervalMax={60}
intervalDefaultValue="1"
countMin={1}
countMax={100}
countDefaultValue="1"
/>
)}
{activeOption === "swap" && (
<SwapAction
options={["Default material", "Material 1", "Material 2"]}
defaultOption={currentMaterial}
onSelect={handleMaterialSelect}
/>
)}
{activeOption === "despawn" && <DespawnAction />}
{activeOption === "delay" && (
<DelayAction
value={currentDelay}
defaultValue="0"
min={0}
max={60}
onChange={handleDelayChange}
/>
)}
</div>
</div>
<div className="tirgger">
<Trigger />
</div>
</>
)
)}
</>
);
}
export default ConveyorMechanics
export default ConveyorMechanics;

View File

@ -1,123 +1,129 @@
import { useEffect, useState } from 'react'
import RenameInput from '../../../../../ui/inputs/RenameInput'
import LabledDropdown from '../../../../../ui/inputs/LabledDropdown'
import Trigger from '../trigger/Trigger'
import { useSelectedEventData, useSelectedProduct } from "../../../../../../store/simulation/useSimulationStore";
import { useEffect, useState } from "react";
import RenameInput from "../../../../../ui/inputs/RenameInput";
import LabledDropdown from "../../../../../ui/inputs/LabledDropdown";
import Trigger from "../trigger/Trigger";
import {
useSelectedEventData,
useSelectedProduct,
} from "../../../../../../store/simulation/useSimulationStore";
import { useProductStore } from "../../../../../../store/simulation/useProductStore";
import ProcessAction from '../actions/ProcessAction'
import ProcessAction from "../actions/ProcessAction";
import ActionsList from "../components/ActionsList";
function MachineMechanics() {
const [activeOption, setActiveOption] = useState<"default" | "process">("default");
const [selectedPointData, setSelectedPointData] = useState<MachinePointSchema | undefined>();
const { selectedEventData } = useSelectedEventData();
const { getPointByUuid, updateAction } = useProductStore();
const { selectedProduct } = useSelectedProduct();
const [activeOption, setActiveOption] = useState<"default" | "process">(
"default"
);
const [selectedPointData, setSelectedPointData] = useState<
MachinePointSchema | undefined
>();
const { selectedEventData } = useSelectedEventData();
const { getPointByUuid, updateAction } = useProductStore();
const { selectedProduct } = useSelectedProduct();
useEffect(() => {
if (selectedEventData) {
const point = getPointByUuid(
selectedProduct.productId,
selectedEventData?.data.modelUuid,
selectedEventData?.selectedPoint
) as MachinePointSchema | undefined;
if (point && 'action' in point) {
setSelectedPointData(point);
setActiveOption(point.action.actionType as "process");
}
}
}, [selectedProduct, selectedEventData])
useEffect(() => {
if (selectedEventData) {
const point = getPointByUuid(
selectedProduct.productId,
selectedEventData?.data.modelUuid,
selectedEventData?.selectedPoint
) as MachinePointSchema | undefined;
if (point && "action" in point) {
setSelectedPointData(point);
setActiveOption(point.action.actionType as "process");
}
}
}, [selectedProduct, selectedEventData, getPointByUuid]);
const handleActionTypeChange = (option: string) => {
if (!selectedEventData || !selectedPointData) return;
const validOption = option as "process";
setActiveOption(validOption);
const handleActionTypeChange = (option: string) => {
if (!selectedEventData || !selectedPointData) return;
const validOption = option as "process";
setActiveOption(validOption);
updateAction(
selectedPointData.action.actionUuid,
{ actionType: validOption }
);
};
updateAction(selectedPointData.action.actionUuid, {
actionType: validOption,
});
};
const handleRenameAction = (newName: string) => {
if (!selectedPointData) return;
updateAction(
selectedPointData.action.actionUuid,
{ actionName: newName }
);
};
const handleRenameAction = (newName: string) => {
if (!selectedPointData) return;
updateAction(selectedPointData.action.actionUuid, { actionName: newName });
};
const handleProcessTimeChange = (value: string) => {
if (!selectedPointData) return;
updateAction(
selectedPointData.action.actionUuid,
{ processTime: parseFloat(value) }
);
};
const handleProcessTimeChange = (value: string) => {
if (!selectedPointData) return;
updateAction(selectedPointData.action.actionUuid, {
processTime: parseFloat(value),
});
};
const handleMaterialSelect = (material: string) => {
if (!selectedPointData) return;
updateAction(
selectedPointData.action.actionUuid,
{ swapMaterial: material }
);
};
const handleMaterialSelect = (material: string) => {
if (!selectedPointData) return;
updateAction(selectedPointData.action.actionUuid, {
swapMaterial: material,
});
};
// Get current values from store
const currentActionName = selectedPointData
? selectedPointData.action.actionName
: "Action Name";
// Get current values from store
const currentActionName = selectedPointData
? selectedPointData.action.actionName
: "Action Name";
const currentProcessTime = selectedPointData
? selectedPointData.action.processTime.toString()
: "1";
const currentProcessTime = selectedPointData
? selectedPointData.action.processTime.toString()
: "1";
const currentMaterial = selectedPointData
? selectedPointData.action.swapMaterial
: "Default material";
const currentMaterial = selectedPointData
? selectedPointData.action.swapMaterial
: "Default material";
const availableActions = {
defaultOption: "process",
options: ["process"],
};
const availableActions = {
defaultOption: "process",
options: ["process"],
};
return (
return (
<>
{selectedEventData && (
<>
{selectedEventData &&
<>
<div className="selected-actions-details">
<div className="selected-actions-header">
<RenameInput
value={currentActionName}
onRename={handleRenameAction}
/>
</div>
<div className="selected-actions-list">
<LabledDropdown
defaultOption="process"
options={availableActions.options}
onSelect={handleActionTypeChange}
/>
{activeOption === "process" &&
<ProcessAction
value={currentProcessTime}
min={0.1}
max={60}
defaultValue="1"
onChange={handleProcessTimeChange}
swapOptions={["Default material", "Material 1", "Material 2"]}
swapDefaultOption={currentMaterial}
onSwapSelect={handleMaterialSelect}
/>
}
</div>
</div>
<div className="tirgger">
<Trigger />
</div>
</>
}
<div className="selected-actions-details">
<div className="selected-actions-header">
<RenameInput
value={currentActionName}
onRename={handleRenameAction}
/>
</div>
<ActionsList
setSelectedPointData={setSelectedPointData}
selectedPointData={selectedPointData}
/>
<div className="selected-actions-list">
<LabledDropdown
defaultOption="process"
options={availableActions.options}
onSelect={handleActionTypeChange}
/>
{activeOption === "process" && (
<ProcessAction
value={currentProcessTime}
min={0.1}
max={60}
defaultValue="1"
onChange={handleProcessTimeChange}
swapOptions={["Default material", "Material 1", "Material 2"]}
swapDefaultOption={currentMaterial}
onSwapSelect={handleMaterialSelect}
/>
)}
</div>
</div>
<div className="tirgger">
<Trigger />
</div>
</>
)
)}
</>
);
}
export default MachineMechanics
export default MachineMechanics;

View File

@ -1,276 +1,194 @@
import { useEffect, useRef, useState } from 'react'
import * as THREE from 'three';
import InputWithDropDown from '../../../../../ui/inputs/InputWithDropDown'
import RenameInput from '../../../../../ui/inputs/RenameInput'
import LabledDropdown from '../../../../../ui/inputs/LabledDropdown'
import Trigger from '../trigger/Trigger'
import { useSelectedEventData, useSelectedProduct, useSelectedAction } from "../../../../../../store/simulation/useSimulationStore";
import { useEffect, useState } from "react";
import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown";
import RenameInput from "../../../../../ui/inputs/RenameInput";
import LabledDropdown from "../../../../../ui/inputs/LabledDropdown";
import Trigger from "../trigger/Trigger";
import {
useSelectedEventData,
useSelectedProduct,
useSelectedAction,
} from "../../../../../../store/simulation/useSimulationStore";
import { useProductStore } from "../../../../../../store/simulation/useProductStore";
import { AddIcon, RemoveIcon, ResizeHeightIcon } from '../../../../../icons/ExportCommonIcons'
import { handleResize } from '../../../../../../functions/handleResizePannel'
import PickAndPlaceAction from '../actions/PickAndPlaceAction'
import PickAndPlaceAction from "../actions/PickAndPlaceAction";
import ActionsList from "../components/ActionsList";
function RoboticArmMechanics() {
const actionsContainerRef = useRef<HTMLDivElement>(null);
const [activeOption, setActiveOption] = useState<"default" | "pickAndPlace">("default");
const [selectedPointData, setSelectedPointData] = useState<RoboticArmPointSchema | undefined>();
const { selectedEventData } = useSelectedEventData();
const { getPointByUuid, updateEvent, updateAction, addAction, removeAction } = useProductStore();
const { selectedProduct } = useSelectedProduct();
const { selectedAction, setSelectedAction, clearSelectedAction } = useSelectedAction();
const [activeOption, setActiveOption] = useState<"default" | "pickAndPlace">(
"default"
);
const [selectedPointData, setSelectedPointData] = useState<
RoboticArmPointSchema | undefined
>();
const { selectedEventData } = useSelectedEventData();
const { getPointByUuid, updateEvent, updateAction } = useProductStore();
const { selectedProduct } = useSelectedProduct();
const { selectedAction, setSelectedAction, clearSelectedAction } =
useSelectedAction();
useEffect(() => {
if (selectedEventData) {
const point = getPointByUuid(
selectedProduct.productId,
selectedEventData.data.modelUuid,
selectedEventData.selectedPoint
) as RoboticArmPointSchema | undefined;
if (point) {
setSelectedPointData(point);
setActiveOption(point.actions[0].actionType as "default" | "pickAndPlace");
if (point.actions.length > 0 && !selectedAction.actionId) {
setSelectedAction(point.actions[0].actionUuid, point.actions[0].actionName);
}
}
} else {
clearSelectedAction();
useEffect(() => {
if (selectedEventData) {
const point = getPointByUuid(
selectedProduct.productId,
selectedEventData.data.modelUuid,
selectedEventData.selectedPoint
) as RoboticArmPointSchema | undefined;
if (point?.actions) {
setSelectedPointData(point);
setActiveOption(
point.actions[0].actionType as "default" | "pickAndPlace"
);
if (point.actions.length > 0 && !selectedAction.actionId) {
setSelectedAction(
point.actions[0].actionUuid,
point.actions[0].actionName
);
}
}, [selectedEventData, selectedProduct]);
}
} else {
clearSelectedAction();
}
}, [
clearSelectedAction,
getPointByUuid,
selectedAction.actionId,
selectedEventData,
selectedProduct,
setSelectedAction,
]);
const handleActionSelect = (actionUuid: string, actionName: string) => {
setSelectedAction(actionUuid, actionName);
};
const handleRenameAction = (newName: string) => {
if (!selectedAction.actionId) return;
updateAction(selectedAction.actionId, { actionName: newName });
const handleAddAction = () => {
if (!selectedEventData || !selectedPointData) return;
if (selectedPointData) {
const updatedActions = selectedPointData.actions.map((action) =>
action.actionUuid === selectedAction.actionId
? { ...action, actionName: newName }
: action
);
setSelectedPointData({
...selectedPointData,
actions: updatedActions,
});
}
};
const newAction = {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: `Action ${selectedPointData.actions.length + 1}`,
actionType: "pickAndPlace" as "pickAndPlace",
process: {
startPoint: null,
endPoint: null
},
triggers: [] as TriggerSchema[]
};
const handleSpeedChange = (value: string) => {
if (!selectedEventData) return;
updateEvent(selectedProduct.productId, selectedEventData.data.modelUuid, {
speed: parseFloat(value),
});
};
addAction(
selectedProduct.productId,
selectedEventData.data.modelUuid,
selectedEventData.selectedPoint,
newAction
);
const handlePickPointChange = (value: string) => {
if (!selectedAction.actionId || !selectedPointData) return;
const [x, y, z] = value.split(",").map(Number);
const updatedPoint = {
...selectedPointData,
actions: [...selectedPointData.actions, newAction]
};
setSelectedPointData(updatedPoint);
setSelectedAction(newAction.actionUuid, newAction.actionName);
};
updateAction(selectedAction.actionId, {
process: {
startPoint: [x, y, z] as [number, number, number],
endPoint:
selectedPointData.actions.find(
(a) => a.actionUuid === selectedAction.actionId
)?.process.endPoint || null,
},
});
};
const handleDeleteAction = (actionUuid: string) => {
if (!selectedPointData) return;
const handlePlacePointChange = (value: string) => {
if (!selectedAction.actionId || !selectedPointData) return;
const [x, y, z] = value.split(",").map(Number);
removeAction(actionUuid);
const newActions = selectedPointData.actions.filter(a => a.actionUuid !== actionUuid);
updateAction(selectedAction.actionId, {
process: {
startPoint:
selectedPointData.actions.find(
(a) => a.actionUuid === selectedAction.actionId
)?.process.startPoint || null,
endPoint: [x, y, z] as [number, number, number],
},
});
};
const updatedPoint = {
...selectedPointData,
actions: newActions
};
setSelectedPointData(updatedPoint);
const availableActions = {
defaultOption: "pickAndPlace",
options: ["pickAndPlace"],
};
if (selectedAction.actionId === actionUuid) {
if (newActions.length > 0) {
setSelectedAction(newActions[0].actionUuid, newActions[0].actionName);
} else {
clearSelectedAction();
}
}
};
const currentSpeed =
selectedEventData?.data.type === "roboticArm"
? selectedEventData.data.speed.toString()
: "0.5";
const handleRenameAction = (newName: string) => {
if (!selectedAction.actionId) return;
updateAction(
selectedAction.actionId,
{ actionName: newName }
);
const currentAction = selectedPointData?.actions.find(
(a) => a.actionUuid === selectedAction.actionId
);
const currentPickPoint = currentAction?.process.startPoint
? `${currentAction.process.startPoint[0]},${currentAction.process.startPoint[1]},${currentAction.process.startPoint[2]}`
: "";
const currentPlacePoint = currentAction?.process.endPoint
? `${currentAction.process.endPoint[0]},${currentAction.process.endPoint[1]},${currentAction.process.endPoint[2]}`
: "";
if (selectedPointData) {
const updatedActions = selectedPointData.actions.map(action =>
action.actionUuid === selectedAction.actionId
? { ...action, actionName: newName }
: action
);
setSelectedPointData({
...selectedPointData,
actions: updatedActions
});
}
};
const handleSpeedChange = (value: string) => {
if (!selectedEventData) return;
updateEvent(
selectedProduct.productId,
selectedEventData.data.modelUuid,
{ speed: parseFloat(value) }
);
};
const handlePickPointChange = (value: string) => {
if (!selectedAction.actionId || !selectedPointData) return;
const [x, y, z] = value.split(',').map(Number);
updateAction(
selectedAction.actionId,
{
process: {
startPoint: [x, y, z] as [number, number, number],
endPoint: selectedPointData.actions.find(a => a.actionUuid === selectedAction.actionId)?.process.endPoint || null
}
}
);
};
const handlePlacePointChange = (value: string) => {
if (!selectedAction.actionId || !selectedPointData) return;
const [x, y, z] = value.split(',').map(Number);
updateAction(
selectedAction.actionId,
{
process: {
startPoint: selectedPointData.actions.find(a => a.actionUuid === selectedAction.actionId)?.process.startPoint || null,
endPoint: [x, y, z] as [number, number, number]
}
}
);
};
const availableActions = {
defaultOption: "pickAndPlace",
options: ["pickAndPlace"],
};
const currentSpeed = selectedEventData?.data.type === "roboticArm"
? selectedEventData.data.speed.toString()
: "0.5";
const currentAction = selectedPointData?.actions.find(a => a.actionUuid === selectedAction.actionId);
const currentPickPoint = currentAction?.process.startPoint
? `${currentAction.process.startPoint[0]},${currentAction.process.startPoint[1]},${currentAction.process.startPoint[2]}`
: "";
const currentPlacePoint = currentAction?.process.endPoint
? `${currentAction.process.endPoint[0]},${currentAction.process.endPoint[1]},${currentAction.process.endPoint[2]}`
: "";
return (
return (
<>
{selectedEventData && selectedPointData && (
<>
{selectedEventData && selectedPointData && (
<>
<div className="global-props">
<div className="property-list-container">
<div className="property-item">
<InputWithDropDown
label="Speed"
value={currentSpeed}
min={0}
step={0.1}
defaultValue={"0.5"}
max={10}
activeOption="m/s"
onClick={() => { }}
onChange={handleSpeedChange}
/>
</div>
</div>
</div>
<div className="global-props">
<div className="property-list-container">
<div className="property-item">
<InputWithDropDown
label="Speed"
value={currentSpeed}
min={0}
step={0.1}
defaultValue={"0.5"}
max={10}
activeOption="m/s"
onClick={() => {}}
onChange={handleSpeedChange}
/>
</div>
</div>
</div>
<div className="actions-list-container">
<div className="actions">
<div className="header">
<div className="header-value">Actions</div>
<div className="add-button" onClick={handleAddAction}>
<AddIcon /> Add
</div>
</div>
<div
className="lists-main-container"
ref={actionsContainerRef}
style={{ height: "120px" }}
>
<div className="list-container">
{selectedPointData.actions.map((action) => (
<div
key={action.actionUuid}
className={`list-item ${selectedAction.actionId === action.actionUuid ? "active" : ""}`}
>
<div
className="value"
onClick={() => handleActionSelect(action.actionUuid, action.actionName)}
>
<RenameInput
value={action.actionName}
onRename={handleRenameAction}
/>
</div>
{selectedPointData.actions.length > 1 && (
<div
className="remove-button"
onClick={() => handleDeleteAction(action.actionUuid)}
>
<RemoveIcon />
</div>
)}
</div>
))}
</div>
<div
className="resize-icon"
id="action-resize"
onMouseDown={(e) => handleResize(e, actionsContainerRef)}
>
<ResizeHeightIcon />
</div>
</div>
</div>
</div>
<ActionsList
setSelectedPointData={setSelectedPointData}
selectedPointData={selectedPointData}
multipleAction
/>
{selectedAction.actionId && currentAction && (
<div className="selected-actions-details">
<div className="selected-actions-header">
<RenameInput
value={selectedAction.actionName}
onRename={handleRenameAction}
/>
</div>
<div className="selected-actions-list">
<LabledDropdown
defaultOption={activeOption}
options={availableActions.options}
onSelect={() => { }}
disabled={true}
/>
<PickAndPlaceAction
pickPointValue={currentPickPoint}
pickPointOnChange={handlePickPointChange}
placePointValue={currentPlacePoint}
placePointOnChange={handlePlacePointChange}
/>
</div>
<div className="tirgger">
<Trigger />
</div>
</div>
)}
</>
)}
{selectedAction.actionId && currentAction && (
<div className="selected-actions-details">
<div className="selected-actions-header">
<RenameInput
value={selectedAction.actionName}
onRename={handleRenameAction}
/>
</div>
<div className="selected-actions-list">
<LabledDropdown
defaultOption={activeOption}
options={availableActions.options}
onSelect={() => {}}
disabled={true}
/>
<PickAndPlaceAction
pickPointValue={currentPickPoint}
pickPointOnChange={handlePickPointChange}
placePointValue={currentPlacePoint}
placePointOnChange={handlePlacePointChange}
/>
</div>
<div className="tirgger">
<Trigger />
</div>
</div>
)}
</>
)
)}
</>
);
}
export default RoboticArmMechanics
export default RoboticArmMechanics;

View File

@ -1,113 +1,120 @@
import { useEffect, useState } from 'react'
import RenameInput from '../../../../../ui/inputs/RenameInput'
import LabledDropdown from '../../../../../ui/inputs/LabledDropdown'
import Trigger from '../trigger/Trigger'
import { useSelectedEventData, useSelectedProduct } from "../../../../../../store/simulation/useSimulationStore";
import { useEffect, useState } from "react";
import RenameInput from "../../../../../ui/inputs/RenameInput";
import LabledDropdown from "../../../../../ui/inputs/LabledDropdown";
import Trigger from "../trigger/Trigger";
import {
useSelectedEventData,
useSelectedProduct,
} from "../../../../../../store/simulation/useSimulationStore";
import { useProductStore } from "../../../../../../store/simulation/useProductStore";
import StorageAction from '../actions/StorageAction';
import StorageAction from "../actions/StorageAction";
import ActionsList from "../components/ActionsList";
function StorageMechanics() {
const [activeOption, setActiveOption] = useState<"default" | "store" | "spawn">("default");
const [selectedPointData, setSelectedPointData] = useState<StoragePointSchema | undefined>();
const { selectedEventData } = useSelectedEventData();
const { getPointByUuid, updateAction } = useProductStore();
const { selectedProduct } = useSelectedProduct();
const [activeOption, setActiveOption] = useState<
"default" | "store" | "spawn"
>("default");
const [selectedPointData, setSelectedPointData] = useState<
StoragePointSchema | undefined
>();
const { selectedEventData } = useSelectedEventData();
const { getPointByUuid, updateAction } = useProductStore();
const { selectedProduct } = useSelectedProduct();
useEffect(() => {
if (selectedEventData) {
const point = getPointByUuid(
selectedProduct.productId,
selectedEventData?.data.modelUuid,
selectedEventData?.selectedPoint
) as StoragePointSchema | undefined;
if (point && 'action' in point) {
setSelectedPointData(point);
setActiveOption(point.action.actionType as "store" | "spawn");
}
}
}, [selectedProduct, selectedEventData])
useEffect(() => {
if (selectedEventData) {
const point = getPointByUuid(
selectedProduct.productId,
selectedEventData?.data.modelUuid,
selectedEventData?.selectedPoint
) as StoragePointSchema | undefined;
if (point && "action" in point) {
setSelectedPointData(point);
setActiveOption(point.action.actionType as "store" | "spawn");
}
}
}, [selectedProduct, selectedEventData, getPointByUuid]);
const handleActionTypeChange = (option: string) => {
if (!selectedEventData || !selectedPointData) return;
const validOption = option as "store" | "spawn";
setActiveOption(validOption);
const handleActionTypeChange = (option: string) => {
if (!selectedEventData || !selectedPointData) return;
const validOption = option as "store" | "spawn";
setActiveOption(validOption);
updateAction(
selectedPointData.action.actionUuid,
{ actionType: validOption }
);
};
updateAction(selectedPointData.action.actionUuid, {
actionType: validOption,
});
};
const handleRenameAction = (newName: string) => {
if (!selectedPointData) return;
updateAction(
selectedPointData.action.actionUuid,
{ actionName: newName }
);
};
const handleRenameAction = (newName: string) => {
if (!selectedPointData) return;
updateAction(selectedPointData.action.actionUuid, { actionName: newName });
};
const handleCapacityChange = (value: string) => {
if (!selectedPointData) return;
updateAction(
selectedPointData.action.actionUuid,
{ storageCapacity: parseInt(value) }
);
};
const handleCapacityChange = (value: string) => {
if (!selectedPointData) return;
updateAction(selectedPointData.action.actionUuid, {
storageCapacity: parseInt(value),
});
};
// Get current values from store
const currentActionName = selectedPointData
? selectedPointData.action.actionName
: "Action Name";
// Get current values from store
const currentActionName = selectedPointData
? selectedPointData.action.actionName
: "Action Name";
const currentCapacity = selectedPointData
? selectedPointData.action.storageCapacity.toString()
: "0";
const currentCapacity = selectedPointData
? selectedPointData.action.storageCapacity.toString()
: "0";
const availableActions = {
defaultOption: "store",
options: ["store", "spawn"],
};
const availableActions = {
defaultOption: "store",
options: ["store", "spawn"],
};
return (
return (
<>
{selectedEventData && (
<>
{selectedEventData &&
<>
<div className="selected-actions-details">
<div className="selected-actions-header">
<RenameInput
value={currentActionName}
onRename={handleRenameAction}
/>
</div>
<div className="selected-actions-list">
<LabledDropdown
defaultOption={activeOption}
options={availableActions.options}
onSelect={handleActionTypeChange}
/>
{activeOption === "store" &&
<StorageAction
value={currentCapacity}
defaultValue="0"
min={0}
max={20}
onChange={handleCapacityChange}
/>
}
{activeOption === "spawn" && (
<div className="spawn-options">
<p>Spawn configuration options would go here</p>
</div>
)}
</div>
</div>
<div className="tirgger">
<Trigger />
</div>
</>
}
<ActionsList
setSelectedPointData={setSelectedPointData}
selectedPointData={selectedPointData}
/>
<div className="selected-actions-details">
<div className="selected-actions-header">
<RenameInput
value={currentActionName}
onRename={handleRenameAction}
/>
</div>
<div className="selected-actions-list">
<LabledDropdown
defaultOption={activeOption}
options={availableActions.options}
onSelect={handleActionTypeChange}
/>
{activeOption === "store" && (
<StorageAction
value={currentCapacity}
defaultValue="0"
min={0}
max={20}
onChange={handleCapacityChange}
/>
)}
{activeOption === "spawn" && (
<div className="spawn-options">
<p>Spawn configuration options would go here</p>
</div>
)}
</div>
</div>
<div className="tirgger">
<Trigger />
</div>
</>
)
)}
</>
);
}
export default StorageMechanics
export default StorageMechanics;

View File

@ -1,14 +1,20 @@
import { useEffect, useState } from 'react'
import InputWithDropDown from '../../../../../ui/inputs/InputWithDropDown'
import RenameInput from '../../../../../ui/inputs/RenameInput'
import LabledDropdown from '../../../../../ui/inputs/LabledDropdown'
import Trigger from '../trigger/Trigger'
import { useSelectedEventData, useSelectedProduct } from "../../../../../../store/simulation/useSimulationStore";
import { useEffect, useState } from "react";
import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown";
import RenameInput from "../../../../../ui/inputs/RenameInput";
import LabledDropdown from "../../../../../ui/inputs/LabledDropdown";
import Trigger from "../trigger/Trigger";
import {
useSelectedEventData,
useSelectedProduct,
} from "../../../../../../store/simulation/useSimulationStore";
import { useProductStore } from "../../../../../../store/simulation/useProductStore";
import TravelAction from '../actions/TravelAction'
import TravelAction from "../actions/TravelAction";
import ActionsList from "../components/ActionsList";
function VehicleMechanics() {
const [activeOption, setActiveOption] = useState<"default" | "travel">("default");
const [activeOption, setActiveOption] = useState<"default" | "travel">(
"default"
);
const [selectedPointData, setSelectedPointData] = useState<VehiclePointSchema | undefined>();
const { selectedEventData } = useSelectedEventData();
const { getPointByUuid, updateEvent, updateAction } = useProductStore();
@ -27,15 +33,13 @@ function VehicleMechanics() {
setActiveOption(point.action.actionType as "travel");
}
}
}, [selectedProduct, selectedEventData])
}, [selectedProduct, selectedEventData, getPointByUuid]);
const handleSpeedChange = (value: string) => {
if (!selectedEventData) return;
updateEvent(
selectedProduct.productId,
selectedEventData.data.modelUuid,
{ speed: parseFloat(value) }
);
updateEvent(selectedProduct.productId, selectedEventData.data.modelUuid, {
speed: parseFloat(value),
});
};
const handleActionTypeChange = (option: string) => {
@ -43,58 +47,43 @@ function VehicleMechanics() {
const validOption = option as "travel";
setActiveOption(validOption);
updateAction(
selectedPointData.action.actionUuid,
{ actionType: validOption }
);
updateAction(selectedPointData.action.actionUuid, {
actionType: validOption,
});
};
const handleRenameAction = (newName: string) => {
if (!selectedPointData) return;
updateAction(
selectedPointData.action.actionUuid,
{ actionName: newName }
);
updateAction(selectedPointData.action.actionUuid, { actionName: newName });
};
const handleLoadCapacityChange = (value: string) => {
if (!selectedPointData) return;
updateAction(
selectedPointData.action.actionUuid,
{ loadCapacity: parseFloat(value) }
);
updateAction(selectedPointData.action.actionUuid, {
loadCapacity: parseFloat(value),
});
};
const handleUnloadDurationChange = (value: string) => {
if (!selectedPointData) return;
updateAction(
selectedPointData.action.actionUuid,
{ unLoadDuration: parseFloat(value) }
);
updateAction(selectedPointData.action.actionUuid, {
unLoadDuration: parseFloat(value),
});
};
const handlePickPointChange = (value: string) => {
if (!selectedPointData) return;
const [x, y, z] = value.split(',').map(Number);
updateAction(
selectedPointData.action.actionUuid,
{ pickUpPoint: { x, y, z } }
);
};
const handleUnloadPointChange = (value: string) => {
if (!selectedPointData) return;
const [x, y, z] = value.split(',').map(Number);
updateAction(
selectedPointData.action.actionUuid,
{ unLoadPoint: { x, y, z } }
);
};
// Get current values from store
const currentSpeed = selectedEventData?.data.type === "vehicle"
? selectedEventData.data.speed.toString()
: "0.5";
const currentSpeed =
selectedEventData?.data.type === "vehicle"
? selectedEventData.data.speed.toString()
: "0.5";
const currentActionName = selectedPointData
? selectedPointData.action.actionName
@ -108,13 +97,9 @@ function VehicleMechanics() {
? selectedPointData.action.unLoadDuration.toString()
: "1";
const currentPickPoint = selectedPointData?.action.pickUpPoint
? `${selectedPointData.action.pickUpPoint.x},${selectedPointData.action.pickUpPoint.y},${selectedPointData.action.pickUpPoint.z}`
: "";
const currentPickPoint = selectedPointData?.action.pickUpPoint;
const currentUnloadPoint = selectedPointData?.action.unLoadPoint
? `${selectedPointData.action.unLoadPoint.x},${selectedPointData.action.unLoadPoint.y},${selectedPointData.action.unLoadPoint.z}`
: "";
const currentUnloadPoint = selectedPointData?.action.unLoadPoint;
const availableActions = {
defaultOption: "travel",
@ -123,7 +108,7 @@ function VehicleMechanics() {
return (
<>
{selectedEventData &&
{selectedEventData && (
<>
<div className="global-props">
<div className="property-list-container">
@ -142,7 +127,10 @@ function VehicleMechanics() {
</div>
</div>
</div>
<ActionsList
setSelectedPointData={setSelectedPointData}
selectedPointData={selectedPointData}
/>
<div className="selected-actions-details">
<div className="selected-actions-header">
<RenameInput
@ -157,7 +145,7 @@ function VehicleMechanics() {
onSelect={handleActionTypeChange}
/>
{activeOption === 'travel' &&
{activeOption === "travel" && (
<TravelAction
loadCapacity={{
value: currentLoadCapacity,
@ -182,16 +170,16 @@ function VehicleMechanics() {
// onChange: handleUnloadPointChange,
// }}
/>
}
)}
</div>
</div>
<div className="tirgger">
<Trigger />
</div>
</>
}
)}
</>
)
);
}
export default VehicleMechanics
export default VehicleMechanics;

View File

@ -1,11 +1,19 @@
import React, { useState } from "react";
import { AddIcon, RemoveIcon } from "../../../../../icons/ExportCommonIcons";
import React, { useRef, useState } from "react";
import {
AddIcon,
RemoveIcon,
ResizeHeightIcon,
} from "../../../../../icons/ExportCommonIcons";
import LabledDropdown from "../../../../../ui/inputs/LabledDropdown";
import RenameInput from "../../../../../ui/inputs/RenameInput";
import { handleResize } from "../../../../../../functions/handleResizePannel";
const Trigger: React.FC = () => {
// State to hold the list of triggers
const [triggers, setTriggers] = useState<string[]>([]);
const [triggers, setTriggers] = useState<string[]>(["Trigger 1"]);
const [selectedTrigger, setSelectedTrigger] = useState<string>("Trigger 1");
const [activeOption, setActiveOption] = useState("onComplete");
const triggersContainerRef = useRef<HTMLDivElement>(null);
// States for dropdowns
const [triggeredModel, setTriggeredModel] = useState<string[]>([]);
@ -35,70 +43,94 @@ const Trigger: React.FC = () => {
<div className="trigger-wrapper">
<div className="header">
<div className="title">Trigger</div>
<div
<button
className="add-button"
onClick={addTrigger}
style={{ cursor: "pointer" }}
>
<AddIcon /> Add
</div>
</button>
</div>
<div className="trigger-list">
{/* Map over triggers and render them */}
{triggers.map((trigger, index) => (
<div key={index} className="trigger-item">
<div className="trigger-name">
{trigger}
<div
className="lists-main-container"
ref={triggersContainerRef}
style={{ height: "120px" }}
>
<div className="list-container">
{triggers.map((trigger: any, index: number) => (
<div
className="remove-button"
onClick={() => removeTrigger(index)}
style={{ cursor: "pointer" }}
key={index}
className={`list-item ${
selectedTrigger === trigger ? "active" : ""
}`}
onClick={() => setSelectedTrigger(trigger)}
>
<RemoveIcon />
<button className="value" onClick={() => {}}>
<RenameInput value={trigger} onRename={() => {}} />
</button>
{triggers.length > 1 && (
<button
className="remove-button"
onClick={() => removeTrigger(index)}
>
<RemoveIcon />
</button>
)}
</div>
))}
</div>
<button
className="resize-icon"
id="action-resize"
onMouseDown={(e: any) => handleResize(e, triggersContainerRef)}
>
<ResizeHeightIcon />
</button>
</div>
<div className="trigger-item">
<div className="trigger-name">{selectedTrigger}</div>
<LabledDropdown
defaultOption={activeOption}
options={["onComplete", "onStart", "onStop", "delay"]}
onSelect={(option) => setActiveOption(option)}
/>
<div className="trigger-options">
<div>
<LabledDropdown
defaultOption={triggeredModel[0] || "Select Model"}
options={["Model 1", "Model 2", "Model 3"]}
onSelect={(option) => {
const newModel = [...triggeredModel];
newModel[0] = option;
setTriggeredModel(newModel);
}}
/>
</div>
<LabledDropdown
defaultOption={activeOption}
options={["onComplete", "onStart", "onStop", "delay"]}
onSelect={(option) => setActiveOption(option)}
/>
<div className="trigger-options">
<div>
<LabledDropdown
defaultOption={triggeredModel[index] || "Select Model"}
options={["Model 1", "Model 2", "Model 3"]}
onSelect={(option) => {
const newModel = [...triggeredModel];
newModel[index] = option;
setTriggeredModel(newModel);
}}
/>
</div>
<div>
<LabledDropdown
defaultOption={triggeredPoint[index] || "Select Point"}
options={["Point 1", "Point 2", "Point 3"]}
onSelect={(option) => {
const newPoint = [...triggeredPoint];
newPoint[index] = option;
setTriggeredPoint(newPoint);
}}
/>
</div>
<div>
<LabledDropdown
defaultOption={triggeredAction[index] || "Select Action"}
options={["Action 1", "Action 2", "Action 3"]}
onSelect={(option) => {
const newAction = [...triggeredAction];
newAction[index] = option;
setTriggeredAction(newAction);
}}
/>
</div>
<div>
<LabledDropdown
defaultOption={triggeredPoint[0] || "Select Point"}
options={["Point 1", "Point 2", "Point 3"]}
onSelect={(option) => {
const newPoint = [...triggeredPoint];
newPoint[0] = option;
setTriggeredPoint(newPoint);
}}
/>
</div>
<div>
<LabledDropdown
defaultOption={triggeredAction[0] || "Select Action"}
options={["Action 1", "Action 2", "Action 3"]}
onSelect={(option) => {
const newAction = [...triggeredAction];
newAction[0] = option;
setTriggeredAction(newAction);
}}
/>
</div>
</div>
))}
</div>
</div>
</div>
);

View File

@ -1,204 +1,216 @@
import React, { useEffect, useRef } from "react";
import {
AddIcon,
ArrowIcon,
RemoveIcon,
ResizeHeightIcon,
AddIcon,
ArrowIcon,
RemoveIcon,
ResizeHeightIcon,
} from "../../../icons/ExportCommonIcons";
import RenameInput from "../../../ui/inputs/RenameInput";
import { handleResize } from "../../../../functions/handleResizePannel";
import { useSelectedAsset, useSelectedProduct } from "../../../../store/simulation/useSimulationStore";
import {
useSelectedAsset,
useSelectedProduct,
} from "../../../../store/simulation/useSimulationStore";
import { useProductStore } from "../../../../store/simulation/useProductStore";
import { generateUUID } from "three/src/math/MathUtils";
import RenderOverlay from "../../../templates/Overlay";
import EditWidgetOption from "../../../ui/menu/EditWidgetOption";
import { upsertProductOrEventApi } from "../../../../services/simulation/UpsertProductOrEventApi";
import { handleAddEventToProduct } from "../../../../modules/simulation/events/points/functions/handleAddEventToProduct";
interface Event {
pathName: string;
pathName: string;
}
interface ListProps {
val: Event;
val: Event;
}
const List: React.FC<ListProps> = ({ val }) => {
return (
<div className="process-container">
<div className="value">
{val.pathName}
</div>
</div>
);
return (
<div className="process-container">
<div className="value">{val.pathName}</div>
</div>
);
};
const Simulations: React.FC = () => {
const productsContainerRef = useRef<HTMLDivElement>(null);
const { products, addProduct, removeProduct, renameProduct, addEvent, removeEvent } = useProductStore();
const { selectedProduct, setSelectedProduct } = useSelectedProduct();
const { selectedAsset, clearSelectedAsset } = useSelectedAsset();
const productsContainerRef = useRef<HTMLDivElement>(null);
const {
products,
addProduct,
removeProduct,
renameProduct,
addEvent,
removeEvent,
} = useProductStore();
const { selectedProduct, setSelectedProduct } = useSelectedProduct();
const { selectedAsset, clearSelectedAsset } = useSelectedAsset();
const handleAddProduct = () => {
addProduct(`Product ${products.length + 1}`, generateUUID());
};
const handleAddProduct = () => {
addProduct(`Product ${products.length + 1}`, generateUUID());
};
const handleRemoveProduct = (productId: string) => {
const currentIndex = products.findIndex(p => p.productId === productId);
const isSelected = selectedProduct.productId === productId;
const handleRemoveProduct = (productId: string) => {
const currentIndex = products.findIndex((p) => p.productId === productId);
const isSelected = selectedProduct.productId === productId;
const updatedProducts = products.filter(p => p.productId !== productId);
const updatedProducts = products.filter((p) => p.productId !== productId);
if (isSelected) {
if (updatedProducts.length > 0) {
let newSelectedIndex = currentIndex;
if (currentIndex >= updatedProducts.length) {
newSelectedIndex = updatedProducts.length - 1;
}
setSelectedProduct(
updatedProducts[newSelectedIndex].productId,
updatedProducts[newSelectedIndex].productName
);
} else {
setSelectedProduct('', '');
}
if (isSelected) {
if (updatedProducts.length > 0) {
let newSelectedIndex = currentIndex;
if (currentIndex >= updatedProducts.length) {
newSelectedIndex = updatedProducts.length - 1;
}
setSelectedProduct(
updatedProducts[newSelectedIndex].productId,
updatedProducts[newSelectedIndex].productName
);
} else {
setSelectedProduct("", "");
}
}
removeProduct(productId);
};
removeProduct(productId);
};
const handleRenameProduct = (productId: string, newName: string) => {
renameProduct(productId, newName);
if (selectedProduct.productId === productId) {
setSelectedProduct(productId, newName);
}
};
const handleRenameProduct = (productId: string, newName: string) => {
renameProduct(productId, newName);
if (selectedProduct.productId === productId) {
setSelectedProduct(productId, newName);
}
};
const handleAddEventToProduct = () => {
if (selectedAsset) {
addEvent(selectedProduct.productId, selectedAsset);
// upsertProductOrEventApi({
// productName: selectedProduct.productName,
// productId: selectedProduct.productId,
// eventDatas: selectedAsset
// });
clearSelectedAsset();
}
};
const handleRemoveEventFromProduct = () => {
if (selectedAsset) {
removeEvent(selectedProduct.productId, selectedAsset.modelUuid);
clearSelectedAsset();
}
};
const handleRemoveEventFromProduct = () => {
if (selectedAsset) {
removeEvent(selectedProduct.productId, selectedAsset.modelUuid);
clearSelectedAsset();
}
};
const selectedProductData = products.find(
(product) => product.productId === selectedProduct.productId
);
const selectedProductData = products.find(
(product) => product.productId === selectedProduct.productId
);
const events: Event[] = selectedProductData?.eventDatas.map((event) => ({
pathName: event.modelName,
const events: Event[] =
selectedProductData?.eventDatas.map((event) => ({
pathName: event.modelName,
})) || [];
return (
<div className="simulations-container">
<div className="header">Simulations</div>
<div className="add-product-container">
<div className="actions">
<div className="header">
<div className="header-value">Products</div>
<div className="add-button" onClick={handleAddProduct}>
<AddIcon /> Add
</div>
</div>
<div
className="lists-main-container"
ref={productsContainerRef}
style={{ height: "120px" }}
>
<div className="list-container">
{products.map((product, index) => (
<div
key={product.productId}
className={`list-item ${selectedProduct.productId === product.productId ? "active" : ""}`}
>
<div
className="value"
onClick={() => setSelectedProduct(product.productId, product.productName)}
>
<input
type="radio"
name="products"
checked={selectedProduct.productId === product.productId}
readOnly
/>
<RenameInput
value={product.productName}
onRename={(newName) => handleRenameProduct(product.productId, newName)}
/>
</div>
{products.length > 1 && (
<div
className="remove-button"
onClick={() => handleRemoveProduct(product.productId)}
>
<RemoveIcon />
</div>
)}
</div>
))}
</div>
<div
className="resize-icon"
id="action-resize"
onMouseDown={(e) => handleResize(e, productsContainerRef)}
>
<ResizeHeightIcon />
</div>
</div>
</div>
<div className="simulation-process">
<div className="collapse-header-container">
<div className="header">Events</div>
<div className="arrow-container">
<ArrowIcon />
</div>
</div>
{events.map((event, index) => (
<List key={index} val={event} />
))}
</div>
<div className="compare-simulations-container">
<div className="compare-simulations-header">
Need to Compare Layout?
</div>
<div className="content">
Click <span>'Compare'</span> to review and analyze the layout differences between them.
</div>
<div className="input">
<input type="button" value={"Compare"} className="submit" />
</div>
</div>
return (
<div className="simulations-container">
<div className="header">Simulations</div>
<div className="add-product-container">
<div className="actions">
<div className="header">
<div className="header-value">Products</div>
<div className="add-button" onClick={handleAddProduct}>
<AddIcon /> Add
</div>
{selectedAsset &&
<RenderOverlay>
<EditWidgetOption
options={['Add to Product', 'Remove from Product']}
onClick={(option) => {
if (option === 'Add to Product') {
handleAddEventToProduct();
} else {
handleRemoveEventFromProduct();
}
}}
</div>
<div
className="lists-main-container"
ref={productsContainerRef}
style={{ height: "120px" }}
>
<div className="list-container">
{products.map((product, index) => (
<div
key={product.productId}
className={`list-item ${
selectedProduct.productId === product.productId
? "active"
: ""
}`}
>
<div
className="value"
onClick={() =>
setSelectedProduct(product.productId, product.productName)
}
>
<input
type="radio"
name="products"
checked={selectedProduct.productId === product.productId}
readOnly
/>
</RenderOverlay>
}
<RenameInput
value={product.productName}
onRename={(newName) =>
handleRenameProduct(product.productId, newName)
}
/>
</div>
{products.length > 1 && (
<div
className="remove-button"
onClick={() => handleRemoveProduct(product.productId)}
>
<RemoveIcon />
</div>
)}
</div>
))}
</div>
<div
className="resize-icon"
id="action-resize"
onMouseDown={(e) => handleResize(e, productsContainerRef)}
>
<ResizeHeightIcon />
</div>
</div>
</div>
);
<div className="simulation-process">
<div className="collapse-header-container">
<div className="header">Events</div>
<div className="arrow-container">
<ArrowIcon />
</div>
</div>
{events.map((event, index) => (
<List key={index} val={event} />
))}
</div>
<div className="compare-simulations-container">
<div className="compare-simulations-header">
Need to Compare Layout?
</div>
<div className="content">
Click <span>'Compare'</span> to review and analyze the layout
differences between them.
</div>
<div className="input">
<input type="button" value={"Compare"} className="submit" />
</div>
</div>
</div>
{selectedAsset && (
<RenderOverlay>
<EditWidgetOption
options={["Add to Product", "Remove from Product"]}
onClick={(option) => {
if (option === "Add to Product") {
handleAddEventToProduct({
selectedAsset,
addEvent,
selectedProduct,
clearSelectedAsset,
});
} else {
handleRemoveEventFromProduct();
}
}}
/>
</RenderOverlay>
)}
</div>
);
};
export default Simulations;

View File

@ -0,0 +1,62 @@
import React from "react";
import { ProductionCapacityIcon } from "../../icons/analysis";
const ProductionCapacity = () => {
const totalBars = 6;
const progressPercent = 50;
const barsToFill = Math.floor((progressPercent / 100) * totalBars);
const partialFillPercent =
((progressPercent / 100) * totalBars - barsToFill) * 100;
return (
<div className="productionCapacity-container analysis-card">
<div className="productionCapacity-wrapper analysis-card-wrapper">
<div className="card-header">
<div className="header">
<div className="main-header">Throughput Summary</div>
<div className="sub-header">08:00 - 09:00 AM</div>
</div>
<div className="icon-wrapper">
<ProductionCapacityIcon />
</div>
</div>
<div className="process-container">
<div className="throughput-value">
<span className="value">128</span> Units/hour
</div>
{/* Progress Bar */}
<div className="progress-bar-wrapper">
{[...Array(totalBars)].map((_, i) => (
<div className="progress-bar" key={i}>
{i < barsToFill ? (
<div className="bar-fill full" />
) : i === barsToFill ? (
<div
className="bar-fill partial"
style={{ width: `${partialFillPercent}%` }}
/>
) : null}
</div>
))}
</div>
</div>
<div className="metrics-section">
<div className="metric">
<span className="label">Avg. Process Time</span>
<span className="value">28.4 Secs/unit</span>
</div>
<div className="metric">
<span className="label">Machine Utilization</span>
<span className="value">78%</span>
</div>
</div>
</div>
</div>
);
};
export default ProductionCapacity;

View File

@ -0,0 +1,22 @@
import React from "react";
import { ROISummaryIcon } from "../../icons/analysis";
const ROISummary = () => {
return (
<div className="analysis-card">
<div className="throughoutSummary-wrapper analysis-card-wrapper">
<div className="card-header">
<div className="header">
<div className="main-header">ROI Summary</div>
<div className="sub-header">From 24 November, 2025</div>
</div>
<div className="icon-wrapper">
<ROISummaryIcon />
</div>
</div>
</div>
</div>
);
};
export default ROISummary;

View File

@ -0,0 +1,146 @@
import React from "react";
import { Line } from "react-chartjs-2";
import {
Chart as ChartJS,
LineElement,
CategoryScale,
LinearScale,
PointElement,
} from "chart.js";
import { PowerIcon, ThroughputSummaryIcon } from "../../icons/analysis";
ChartJS.register(LineElement, CategoryScale, LinearScale, PointElement);
const ThroughputSummary = () => {
const data = {
labels: ["08:00", "08:10", "08:20", "08:30", "08:40", "08:50", "09:00"],
datasets: [
{
label: "Units/hour",
data: [100, 120, 110, 130, 125, 128, 132],
borderColor: "#B392F0",
tension: 0.4,
pointRadius: 0, // hide points
},
],
};
const options = {
responsive: true,
scales: {
x: {
grid: {
display: false,
},
ticks: {
display: false,
color: "#fff",
},
},
y: {
grid: {
display: false,
},
ticks: {
display: false,
color: "#fff",
},
},
},
plugins: {
legend: {
display: false,
},
tooltip: {
enabled: true,
},
},
};
const shiftUtilization = {
"shift 1": 25,
"shift 2": 45,
"shift 3": 15,
};
return (
<div className="throughoutSummary analysis-card">
<div className="throughoutSummary-wrapper analysis-card-wrapper">
<div className="card-header">
<div className="header">
<div className="main-header">Throughput Summary</div>
<div className="sub-header">08:00 - 09:00 AM</div>
</div>
<div className="icon-wrapper">
<ThroughputSummaryIcon />
</div>
</div>
<div className="process-container">
<div className="throughput-value">
<span className="value">1240</span> Units/hour
</div>
<div className="lineChart">
<div className="assetUsage">
<div className="key">Asset usage</div>
<div className="value">85%</div>
</div>
<Line data={data} options={options} />
</div>
</div>
<div className="footer">
<div className="energyConsumption footer-card">
<div className="header">Energy Consumption</div>
<div className="value-container">
<div className="energy-icon">
<PowerIcon />
</div>
<div className="value-wrapper">
<div className="value">456</div>
<div className="unit">KWH</div>
</div>
</div>
</div>
<div className="shiftUtilization footer-card">
<div className="header">Shift Utilization</div>
<div className="value-container">
<div className="value">85%</div>
<div className="progress-wrapper">
<div
className="progress shift-1"
style={{ width: "30%" }}
></div>
<div
className="progress shift-2"
style={{ width: "40%" }}
></div>
<div
className="progress shift-3"
style={{ width: "30%" }}
></div>
</div>
<div className="progress-indicator">
<div className="shift-wrapper">
<span className="indicator shift-1"></span>
<label>Shift 1</label>
</div>
<div className="shift-wrapper">
<span className="indicator shift-2"></span>
<label>Shift 2</label>
</div>
<div className="shift-wrapper">
<span className="indicator shift-3"></span>
<label>Shift 3</label>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
};
export default ThroughputSummary;

View File

@ -1,41 +1,72 @@
import React, { useState } from "react";
import LabledDropdown from "./LabledDropdown";
import { ArrowIcon } from "../../icons/ExportCommonIcons";
import LabledDropdown from "./LabledDropdown";
const PreviewSelectionWithUpload: React.FC = () => {
const [showPreview, setSetshowPreview] = useState(false);
interface PreviewSelectionWithUploadProps {
preview?: boolean;
upload?: boolean;
label?: string;
onSelect: (option: string) => void;
defaultOption: string;
options: string[];
}
const PreviewSelectionWithUpload: React.FC<PreviewSelectionWithUploadProps> = ({
preview = false,
upload = false,
onSelect,
label,
defaultOption,
options,
}) => {
const [showPreview, setShowPreview] = useState(false);
return (
<div className="preview-selection-with-upload-wrapper">
<div
className="input-header-container"
onClick={() => setSetshowPreview(!showPreview)}
>
<div className="input-header">Preview</div>
<div
className="arrow-container"
style={{ rotate: showPreview ? "0deg" : "90deg" }}
>
<ArrowIcon />
</div>
</div>
{showPreview && (
<div className="canvas-wrapper">
<div className="canvas-container"></div>
{preview && (
<>
<button
className="input-header-container"
onClick={() => setShowPreview(!showPreview)}
>
<div className="input-header">Preview</div>
<div
className="arrow-container"
style={{ rotate: showPreview ? "0deg" : "90deg" }}
>
<ArrowIcon />
</div>
</button>
{showPreview && (
<div className="canvas-wrapper">
<div className="canvas-container"></div>
</div>
)}
</>
)}
{upload && (
<div className="asset-selection-container">
<div className="upload-custom-asset-button">
<div className="title">Upload Product</div>
<input
type="file"
accept=".glb, .gltf"
id="simulation-product-upload"
/>
<label
className="upload-button"
htmlFor="simulation-product-upload"
>
Upload here
</label>
</div>
</div>
)}
<div className="asset-selection-container">
<div className="upload-custom-asset-button">
<div className="title">Upload Product</div>
<input
type="file"
accept=".glb, .gltf"
id="simulation-product-upload"
/>
<label className="upload-button" htmlFor="simulation-product-upload">
Upload here
</label>
</div>
</div>
<LabledDropdown
label={label}
defaultOption={defaultOption}
options={options}
onSelect={onSelect}
/>
</div>
);
};

View File

@ -10,7 +10,7 @@ import {
ArrowIcon,
EyeIcon,
LockIcon,
RmoveIcon,
RemoveIcon,
} from "../../icons/ExportCommonIcons";
import { useThree } from "@react-three/fiber";
import { useFloorItems, useZoneAssetId, useZones } from "../../../store/store";
@ -181,7 +181,7 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
</div>
{remove && (
<div className="remove option">
<RmoveIcon />
<RemoveIcon />
</div>
)}
{item.assets && item.assets.length > 0 && (
@ -218,7 +218,7 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
</div>
{remove && (
<div className="remove option">
<RmoveIcon />
<RemoveIcon />
</div>
)}
</div>

View File

@ -18,20 +18,20 @@ import Window from "../../assets/gltf-glb/window.glb";
////////// Zustand State Imports //////////
import {
useToggleView,
useDeletePointOrLine,
useMovePoint,
useActiveLayer,
useSocketStore,
useWallVisibility,
useRoofVisibility,
useShadows,
useUpdateScene,
useWalls,
useToolMode,
useRefTextUpdate,
useRenderDistance,
useLimitDistance,
useToggleView,
useDeletePointOrLine,
useMovePoint,
useActiveLayer,
useSocketStore,
useWallVisibility,
useRoofVisibility,
useShadows,
useUpdateScene,
useWalls,
useToolMode,
useRefTextUpdate,
useRenderDistance,
useLimitDistance,
} from "../../store/store";
////////// 3D Function Imports //////////
@ -56,300 +56,301 @@ import ZoneGroup from "./groups/zoneGroup";
import useModuleStore from "../../store/useModuleStore";
import MeasurementTool from "../scene/tools/measurementTool";
import NavMesh from "../simulation/vehicle/navMesh/navMesh";
import ProductionCapacity from "../../components/ui/analysis/ProductionCapacity";
export default function Builder() {
const state = useThree<Types.ThreeState>(); // Importing the state from the useThree hook, which contains the scene, camera, and other Three.js elements.
const csg = useRef(); // Reference for CSG object, used for 3D modeling.
const CSGGroup = useRef() as Types.RefMesh; // Reference to a group of CSG objects.
const scene = useRef() as Types.RefScene; // Reference to the scene.
const camera = useRef() as Types.RefCamera; // Reference to the camera object.
const controls = useRef<any>(); // Reference to the controls object.
const raycaster = useRef() as Types.RefRaycaster; // Reference for raycaster used for detecting objects being pointed at in the scene.
const dragPointControls = useRef() as Types.RefDragControl; // Reference for drag point controls, an array for drag control.
const state = useThree<Types.ThreeState>(); // Importing the state from the useThree hook, which contains the scene, camera, and other Three.js elements.
const csg = useRef(); // Reference for CSG object, used for 3D modeling.
const CSGGroup = useRef() as Types.RefMesh; // Reference to a group of CSG objects.
const scene = useRef() as Types.RefScene; // Reference to the scene.
const camera = useRef() as Types.RefCamera; // Reference to the camera object.
const controls = useRef<any>(); // Reference to the controls object.
const raycaster = useRef() as Types.RefRaycaster; // Reference for raycaster used for detecting objects being pointed at in the scene.
const dragPointControls = useRef() as Types.RefDragControl; // Reference for drag point controls, an array for drag control.
// Assigning the scene and camera from the Three.js state to the references.
// Assigning the scene and camera from the Three.js state to the references.
scene.current = state.scene;
camera.current = state.camera;
controls.current = state.controls;
raycaster.current = state.raycaster;
scene.current = state.scene;
camera.current = state.camera;
controls.current = state.controls;
raycaster.current = state.raycaster;
const plane = useRef<THREE.Mesh>(null); // Reference for a plane object for raycaster reference.
const grid = useRef() as any; // Reference for a grid object for raycaster reference.
const snappedPoint = useRef() as Types.RefVector3; // Reference for storing a snapped point at the (end = isSnapped) and (start = ispreSnapped) of the line.
const isSnapped = useRef(false) as Types.RefBoolean; // Boolean reference to indicate if an object is snapped at the (end).
const anglesnappedPoint = useRef() as Types.RefVector3; // Reference for storing an angle-snapped point when the line is in 90 degree etc...
const isAngleSnapped = useRef(false) as Types.RefBoolean; // Boolean to indicate if angle snapping is active.
const isSnappedUUID = useRef() as Types.RefString; // UUID reference to identify the snapped point.
const ispreSnapped = useRef(false) as Types.RefBoolean; // Boolean reference to indicate if an object is snapped at the (start).
const tempLoader = useRef() as Types.RefMesh; // Reference for a temporary loader for the floor items.
const isTempLoader = useRef() as Types.RefBoolean; // Reference to check if a temporary loader is active.
const Tube = useRef() as Types.RefTubeGeometry; // Reference for tubes used for reference line creation and updation.
const line = useRef([]) as Types.RefLine; // Reference for line which stores the current line that is being drawn.
const lines = useRef([]) as Types.RefLines; // Reference for lines which stores all the lines that are ever drawn.
const onlyFloorline = useRef<Types.OnlyFloorLine>([]); // Reference for floor lines which does not have walls or roof and have only floor used to store the current line that is being drawn.
const onlyFloorlines = useRef<Types.OnlyFloorLines>([]); // Reference for all the floor lines that are ever drawn.
const ReferenceLineMesh = useRef() as Types.RefMesh; // Reference for storing the mesh of the reference line for moving it during draw.
const LineCreated = useRef(false) as Types.RefBoolean; // Boolean to track whether the reference line is created or not.
const referencePole = useRef() as Types.RefMesh; // Reference for a pole that is used as the reference for the user to show where it is placed.
const itemsGroup = useRef() as Types.RefGroup; // Reference to the THREE.Group that has the floor items (Gltf).
const floorGroup = useRef() as Types.RefGroup; // Reference to the THREE.Group that has the roofs and the floors.
const AttachedObject = useRef() as Types.RefMesh; // Reference for an object that is attached using dbl click for transform controls rotation.
const floorPlanGroup = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the lines group and the points group.
const floorPlanGroupLine = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the lines that are drawn.
const floorPlanGroupPoint = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the points that are created.
const floorGroupAisle = useRef() as Types.RefGroup;
const zoneGroup = useRef() as Types.RefGroup;
const currentLayerPoint = useRef([]) as Types.RefMeshArray; // Reference for points that re in the current layer used to update the points in drag controls.
const hoveredDeletablePoint = useRef() as Types.RefMesh; // Reference for the currently hovered point that can be deleted.
const hoveredDeletableLine = useRef() as Types.RefMesh; // Reference for the currently hovered line that can be deleted.
const hoveredDeletableFloorItem = useRef() as Types.RefMesh; // Reference for the currently hovered floor item that can be deleted.
const hoveredDeletableWallItem = useRef() as Types.RefMesh; // Reference for the currently hovered wall item that can be deleted.
const hoveredDeletablePillar = useRef() as Types.RefMesh; // Reference for the currently hovered pillar that can be deleted.
const currentWallItem = useRef() as Types.RefMesh; // Reference for the currently selected wall item that can be scaled, dragged etc...
const plane = useRef<THREE.Mesh>(null); // Reference for a plane object for raycaster reference.
const grid = useRef() as any; // Reference for a grid object for raycaster reference.
const snappedPoint = useRef() as Types.RefVector3; // Reference for storing a snapped point at the (end = isSnapped) and (start = ispreSnapped) of the line.
const isSnapped = useRef(false) as Types.RefBoolean; // Boolean reference to indicate if an object is snapped at the (end).
const anglesnappedPoint = useRef() as Types.RefVector3; // Reference for storing an angle-snapped point when the line is in 90 degree etc...
const isAngleSnapped = useRef(false) as Types.RefBoolean; // Boolean to indicate if angle snapping is active.
const isSnappedUUID = useRef() as Types.RefString; // UUID reference to identify the snapped point.
const ispreSnapped = useRef(false) as Types.RefBoolean; // Boolean reference to indicate if an object is snapped at the (start).
const tempLoader = useRef() as Types.RefMesh; // Reference for a temporary loader for the floor items.
const isTempLoader = useRef() as Types.RefBoolean; // Reference to check if a temporary loader is active.
const Tube = useRef() as Types.RefTubeGeometry; // Reference for tubes used for reference line creation and updation.
const line = useRef([]) as Types.RefLine; // Reference for line which stores the current line that is being drawn.
const lines = useRef([]) as Types.RefLines; // Reference for lines which stores all the lines that are ever drawn.
const onlyFloorline = useRef<Types.OnlyFloorLine>([]); // Reference for floor lines which does not have walls or roof and have only floor used to store the current line that is being drawn.
const onlyFloorlines = useRef<Types.OnlyFloorLines>([]); // Reference for all the floor lines that are ever drawn.
const ReferenceLineMesh = useRef() as Types.RefMesh; // Reference for storing the mesh of the reference line for moving it during draw.
const LineCreated = useRef(false) as Types.RefBoolean; // Boolean to track whether the reference line is created or not.
const referencePole = useRef() as Types.RefMesh; // Reference for a pole that is used as the reference for the user to show where it is placed.
const itemsGroup = useRef() as Types.RefGroup; // Reference to the THREE.Group that has the floor items (Gltf).
const floorGroup = useRef() as Types.RefGroup; // Reference to the THREE.Group that has the roofs and the floors.
const AttachedObject = useRef() as Types.RefMesh; // Reference for an object that is attached using dbl click for transform controls rotation.
const floorPlanGroup = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the lines group and the points group.
const floorPlanGroupLine = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the lines that are drawn.
const floorPlanGroupPoint = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the points that are created.
const floorGroupAisle = useRef() as Types.RefGroup;
const zoneGroup = useRef() as Types.RefGroup;
const currentLayerPoint = useRef([]) as Types.RefMeshArray; // Reference for points that re in the current layer used to update the points in drag controls.
const hoveredDeletablePoint = useRef() as Types.RefMesh; // Reference for the currently hovered point that can be deleted.
const hoveredDeletableLine = useRef() as Types.RefMesh; // Reference for the currently hovered line that can be deleted.
const hoveredDeletableFloorItem = useRef() as Types.RefMesh; // Reference for the currently hovered floor item that can be deleted.
const hoveredDeletableWallItem = useRef() as Types.RefMesh; // Reference for the currently hovered wall item that can be deleted.
const hoveredDeletablePillar = useRef() as Types.RefMesh; // Reference for the currently hovered pillar that can be deleted.
const currentWallItem = useRef() as Types.RefMesh; // Reference for the currently selected wall item that can be scaled, dragged etc...
const cursorPosition = new THREE.Vector3(); // 3D vector for storing the cursor position.
const cursorPosition = new THREE.Vector3(); // 3D vector for storing the cursor position.
const [selectedItemsIndex, setSelectedItemsIndex] = useState<Types.Number | null>(null); // State for tracking the index of the selected item.
const { activeLayer, setActiveLayer } = useActiveLayer(); // State that changes based on which layer the user chooses in Layers.jsx.
const { toggleView, setToggleView } = useToggleView(); // State for toggling between 2D and 3D.
const { toolMode, setToolMode } = useToolMode();
const { movePoint, setMovePoint } = useMovePoint(); // State that stores a boolean which represents whether the move mode is active or not.
const { deletePointOrLine, setDeletePointOrLine } = useDeletePointOrLine();
const { socket } = useSocketStore();
const { roofVisibility, setRoofVisibility } = useRoofVisibility();
const { wallVisibility, setWallVisibility } = useWallVisibility();
const { shadows, setShadows } = useShadows();
const { renderDistance, setRenderDistance } = useRenderDistance();
const { limitDistance, setLimitDistance } = useLimitDistance();
const { updateScene, setUpdateScene } = useUpdateScene();
const { walls, setWalls } = useWalls();
const { refTextupdate, setRefTextUpdate } = useRefTextUpdate();
const { activeModule } = useModuleStore();
const [selectedItemsIndex, setSelectedItemsIndex] =
useState<Types.Number | null>(null); // State for tracking the index of the selected item.
const { activeLayer, setActiveLayer } = useActiveLayer(); // State that changes based on which layer the user chooses in Layers.jsx.
const { toggleView, setToggleView } = useToggleView(); // State for toggling between 2D and 3D.
const { toolMode, setToolMode } = useToolMode();
const { movePoint, setMovePoint } = useMovePoint(); // State that stores a boolean which represents whether the move mode is active or not.
const { deletePointOrLine, setDeletePointOrLine } = useDeletePointOrLine();
const { socket } = useSocketStore();
const { roofVisibility, setRoofVisibility } = useRoofVisibility();
const { wallVisibility, setWallVisibility } = useWallVisibility();
const { shadows, setShadows } = useShadows();
const { renderDistance, setRenderDistance } = useRenderDistance();
const { limitDistance, setLimitDistance } = useLimitDistance();
const { updateScene, setUpdateScene } = useUpdateScene();
const { walls, setWalls } = useWalls();
const { refTextupdate, setRefTextUpdate } = useRefTextUpdate();
const { activeModule } = useModuleStore();
// const loader = new GLTFLoader();
// const dracoLoader = new DRACOLoader();
// const loader = new GLTFLoader();
// const dracoLoader = new DRACOLoader();
// dracoLoader.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/');
// loader.setDRACOLoader(dracoLoader);
// dracoLoader.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/');
// loader.setDRACOLoader(dracoLoader);
////////// Assest Configuration Values //////////
////////// Assest Configuration Values //////////
const AssetConfigurations: Types.AssetConfigurations = {
arch: {
modelUrl: arch,
scale: [0.75, 0.75, 0.75],
csgscale: [2, 4, 0.5],
csgposition: [0, 2, 0],
positionY: () => 0,
type: "Fixed-Move",
},
door: {
modelUrl: door,
scale: [0.75, 0.75, 0.75],
csgscale: [2, 4, 0.5],
csgposition: [0, 2, 0],
positionY: () => 0,
type: "Fixed-Move",
},
window: {
modelUrl: Window,
scale: [0.75, 0.75, 0.75],
csgscale: [5, 3, 0.5],
csgposition: [0, 1.5, 0],
positionY: (intersectionPoint) => intersectionPoint.point.y,
type: "Free-Move",
},
};
const AssetConfigurations: Types.AssetConfigurations = {
arch: {
modelUrl: arch,
scale: [0.75, 0.75, 0.75],
csgscale: [2, 4, 0.5],
csgposition: [0, 2, 0],
positionY: () => 0,
type: "Fixed-Move",
},
door: {
modelUrl: door,
scale: [0.75, 0.75, 0.75],
csgscale: [2, 4, 0.5],
csgposition: [0, 2, 0],
positionY: () => 0,
type: "Fixed-Move",
},
window: {
modelUrl: Window,
scale: [0.75, 0.75, 0.75],
csgscale: [5, 3, 0.5],
csgposition: [0, 1.5, 0],
positionY: (intersectionPoint) => intersectionPoint.point.y,
type: "Free-Move",
},
};
////////// All Toggle's //////////
////////// All Toggle's //////////
useEffect(() => {
setRefTextUpdate((prevUpdate: number) => prevUpdate - 1);
if (dragPointControls.current) {
dragPointControls.current.enabled = false;
}
if (toggleView) {
Layer2DVisibility(
activeLayer,
floorPlanGroup,
floorPlanGroupLine,
floorPlanGroupPoint,
currentLayerPoint,
dragPointControls
);
} else {
setToolMode(null);
setDeletePointOrLine(false);
setMovePoint(false);
loadWalls(lines, setWalls);
setUpdateScene(true);
line.current = [];
}
}, [toggleView]);
useEffect(() => {
setRefTextUpdate((prevUpdate: number) => prevUpdate - 1);
if (dragPointControls.current) {
dragPointControls.current.enabled = false;
}
if (toggleView) {
Layer2DVisibility(
activeLayer,
floorPlanGroup,
floorPlanGroupLine,
floorPlanGroupPoint,
currentLayerPoint,
dragPointControls
);
} else {
setToolMode(null);
setDeletePointOrLine(false);
setMovePoint(false);
loadWalls(lines, setWalls);
setUpdateScene(true);
line.current = [];
}
}, [toggleView]);
useEffect(() => {
THREE.Cache.clear();
THREE.Cache.enabled = true;
}, []);
useEffect(() => {
THREE.Cache.clear();
THREE.Cache.enabled = true;
}, []);
useEffect(() => {
const email = localStorage.getItem("email");
const organization = email!.split("@")[1].split(".")[0];
useEffect(() => {
const email = localStorage.getItem("email");
const organization = email!.split("@")[1].split(".")[0];
async function fetchVisibility() {
const visibility = await findEnvironment(
organization,
localStorage.getItem("userId")!
);
if (visibility) {
setRoofVisibility(visibility.roofVisibility);
setWallVisibility(visibility.wallVisibility);
setShadows(visibility.shadowVisibility);
setRenderDistance(visibility.renderDistance);
setLimitDistance(visibility.limitDistance);
}
}
fetchVisibility();
}, []);
async function fetchVisibility() {
const visibility = await findEnvironment(
organization,
localStorage.getItem("userId")!
);
if (visibility) {
setRoofVisibility(visibility.roofVisibility);
setWallVisibility(visibility.wallVisibility);
setShadows(visibility.shadowVisibility);
setRenderDistance(visibility.renderDistance);
setLimitDistance(visibility.limitDistance);
}
}
fetchVisibility();
}, []);
////////// UseFrame is Here //////////
////////// UseFrame is Here //////////
useFrame(() => {
if (toolMode) {
Draw(
state,
plane,
cursorPosition,
floorPlanGroupPoint,
floorPlanGroupLine,
snappedPoint,
isSnapped,
isSnappedUUID,
line,
lines,
ispreSnapped,
floorPlanGroup,
ReferenceLineMesh,
LineCreated,
setRefTextUpdate,
Tube,
anglesnappedPoint,
isAngleSnapped,
toolMode
);
}
});
useFrame(() => {
if (toolMode) {
Draw(
state,
plane,
cursorPosition,
floorPlanGroupPoint,
floorPlanGroupLine,
snappedPoint,
isSnapped,
isSnappedUUID,
line,
lines,
ispreSnapped,
floorPlanGroup,
ReferenceLineMesh,
LineCreated,
setRefTextUpdate,
Tube,
anglesnappedPoint,
isAngleSnapped,
toolMode
);
}
});
////////// Return //////////
////////// Return //////////
return (
<>
<Ground grid={grid} plane={plane} />
return (
<>
<Ground grid={grid} plane={plane} />
<DistanceText key={toggleView} />
<DistanceText key={toggleView} />
<ReferenceDistanceText
key={refTextupdate}
line={ReferenceLineMesh.current}
/>
<ReferenceDistanceText
key={refTextupdate}
line={ReferenceLineMesh.current}
/>
<SocketResponses
floorPlanGroup={floorPlanGroup}
lines={lines}
floorGroup={floorGroup}
floorGroupAisle={floorGroupAisle}
scene={scene}
onlyFloorlines={onlyFloorlines}
AssetConfigurations={AssetConfigurations}
itemsGroup={itemsGroup}
isTempLoader={isTempLoader}
tempLoader={tempLoader}
currentLayerPoint={currentLayerPoint}
floorPlanGroupPoint={floorPlanGroupPoint}
floorPlanGroupLine={floorPlanGroupLine}
zoneGroup={zoneGroup}
dragPointControls={dragPointControls}
/>
<SocketResponses
floorPlanGroup={floorPlanGroup}
lines={lines}
floorGroup={floorGroup}
floorGroupAisle={floorGroupAisle}
scene={scene}
onlyFloorlines={onlyFloorlines}
AssetConfigurations={AssetConfigurations}
itemsGroup={itemsGroup}
isTempLoader={isTempLoader}
tempLoader={tempLoader}
currentLayerPoint={currentLayerPoint}
floorPlanGroupPoint={floorPlanGroupPoint}
floorPlanGroupLine={floorPlanGroupLine}
zoneGroup={zoneGroup}
dragPointControls={dragPointControls}
/>
<WallsAndWallItems
CSGGroup={CSGGroup}
AssetConfigurations={AssetConfigurations}
setSelectedItemsIndex={setSelectedItemsIndex}
selectedItemsIndex={selectedItemsIndex}
currentWallItem={currentWallItem}
csg={csg}
lines={lines}
hoveredDeletableWallItem={hoveredDeletableWallItem}
/>
<WallsAndWallItems
CSGGroup={CSGGroup}
AssetConfigurations={AssetConfigurations}
setSelectedItemsIndex={setSelectedItemsIndex}
selectedItemsIndex={selectedItemsIndex}
currentWallItem={currentWallItem}
csg={csg}
lines={lines}
hoveredDeletableWallItem={hoveredDeletableWallItem}
/>
<FloorItemsGroup
itemsGroup={itemsGroup}
hoveredDeletableFloorItem={hoveredDeletableFloorItem}
AttachedObject={AttachedObject}
floorGroup={floorGroup}
tempLoader={tempLoader}
isTempLoader={isTempLoader}
plane={plane}
/>
<FloorItemsGroup
itemsGroup={itemsGroup}
hoveredDeletableFloorItem={hoveredDeletableFloorItem}
AttachedObject={AttachedObject}
floorGroup={floorGroup}
tempLoader={tempLoader}
isTempLoader={isTempLoader}
plane={plane}
/>
<FloorGroup
floorGroup={floorGroup}
lines={lines}
referencePole={referencePole}
hoveredDeletablePillar={hoveredDeletablePillar}
/>
<FloorGroup
floorGroup={floorGroup}
lines={lines}
referencePole={referencePole}
hoveredDeletablePillar={hoveredDeletablePillar}
/>
<FloorPlanGroup
floorPlanGroup={floorPlanGroup}
floorPlanGroupLine={floorPlanGroupLine}
floorPlanGroupPoint={floorPlanGroupPoint}
floorGroup={floorGroup}
currentLayerPoint={currentLayerPoint}
dragPointControls={dragPointControls}
hoveredDeletablePoint={hoveredDeletablePoint}
hoveredDeletableLine={hoveredDeletableLine}
plane={plane}
line={line}
lines={lines}
onlyFloorline={onlyFloorline}
onlyFloorlines={onlyFloorlines}
ReferenceLineMesh={ReferenceLineMesh}
LineCreated={LineCreated}
isSnapped={isSnapped}
ispreSnapped={ispreSnapped}
snappedPoint={snappedPoint}
isSnappedUUID={isSnappedUUID}
isAngleSnapped={isAngleSnapped}
anglesnappedPoint={anglesnappedPoint}
/>
<FloorPlanGroup
floorPlanGroup={floorPlanGroup}
floorPlanGroupLine={floorPlanGroupLine}
floorPlanGroupPoint={floorPlanGroupPoint}
floorGroup={floorGroup}
currentLayerPoint={currentLayerPoint}
dragPointControls={dragPointControls}
hoveredDeletablePoint={hoveredDeletablePoint}
hoveredDeletableLine={hoveredDeletableLine}
plane={plane}
line={line}
lines={lines}
onlyFloorline={onlyFloorline}
onlyFloorlines={onlyFloorlines}
ReferenceLineMesh={ReferenceLineMesh}
LineCreated={LineCreated}
isSnapped={isSnapped}
ispreSnapped={ispreSnapped}
snappedPoint={snappedPoint}
isSnappedUUID={isSnappedUUID}
isAngleSnapped={isAngleSnapped}
anglesnappedPoint={anglesnappedPoint}
/>
<ZoneGroup />
<ZoneGroup />
<FloorGroupAilse
floorGroupAisle={floorGroupAisle}
plane={plane}
floorPlanGroupLine={floorPlanGroupLine}
floorPlanGroupPoint={floorPlanGroupPoint}
line={line}
lines={lines}
currentLayerPoint={currentLayerPoint}
dragPointControls={dragPointControls}
floorPlanGroup={floorPlanGroup}
ReferenceLineMesh={ReferenceLineMesh}
LineCreated={LineCreated}
isSnapped={isSnapped}
ispreSnapped={ispreSnapped}
snappedPoint={snappedPoint}
isSnappedUUID={isSnappedUUID}
isAngleSnapped={isAngleSnapped}
anglesnappedPoint={anglesnappedPoint}
/>
<FloorGroupAilse
floorGroupAisle={floorGroupAisle}
plane={plane}
floorPlanGroupLine={floorPlanGroupLine}
floorPlanGroupPoint={floorPlanGroupPoint}
line={line}
lines={lines}
currentLayerPoint={currentLayerPoint}
dragPointControls={dragPointControls}
floorPlanGroup={floorPlanGroup}
ReferenceLineMesh={ReferenceLineMesh}
LineCreated={LineCreated}
isSnapped={isSnapped}
ispreSnapped={ispreSnapped}
snappedPoint={snappedPoint}
isSnappedUUID={isSnappedUUID}
isAngleSnapped={isAngleSnapped}
anglesnappedPoint={anglesnappedPoint}
/>
<MeasurementTool />
<MeasurementTool />
<NavMesh lines={lines} />
</>
);
<NavMesh lines={lines} />
</>
);
}

View File

@ -20,24 +20,24 @@ export default function Scene() {
);
return (
<KeyboardControls map={map}>
<Canvas
eventPrefix="client"
gl={{ powerPreference: "high-performance", antialias: true }}
onContextMenu={(e) => {
e.preventDefault();
}}
>
<Setup />
<KeyboardControls map={map}>
<Canvas
eventPrefix="client"
gl={{ powerPreference: "high-performance", antialias: true }}
onContextMenu={(e) => {
e.preventDefault();
}}
>
<Setup />
<Collaboration />
<Collaboration />
<Builder />
<Builder />
<Simulation />
<Simulation />
<Visualization />
</Canvas>
</KeyboardControls>
<Visualization />
</Canvas>
</KeyboardControls>
);
}

View File

@ -5,233 +5,227 @@ import useModuleStore from "../../../../../store/useModuleStore";
import { TransformControls } from "@react-three/drei";
import { detectModifierKeys } from "../../../../../utils/shortcutkeys/detectModifierKeys";
import {
useSelectedEventSphere,
useSelectedEventData,
useSelectedEventSphere,
useSelectedEventData,
} from "../../../../../store/simulation/useSimulationStore";
function PointsCreator() {
const { events, updatePoint, getPointByUuid, getEventByModelUuid } =
useEventsStore();
const { activeModule } = useModuleStore();
const transformRef = useRef<any>(null);
const [transformMode, setTransformMode] = useState<
"translate" | "rotate" | null
>(null);
const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({});
const {
selectedEventSphere,
setSelectedEventSphere,
clearSelectedEventSphere,
} = useSelectedEventSphere();
const { setSelectedEventData, clearSelectedEventData } =
useSelectedEventData();
const { events, updatePoint, getPointByUuid, getEventByModelUuid } = useEventsStore();
const { activeModule } = useModuleStore();
const transformRef = useRef<any>(null);
const [transformMode, setTransformMode] = useState<"translate" | "rotate" | null>(null);
const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({});
const { selectedEventSphere, setSelectedEventSphere, clearSelectedEventSphere, } = useSelectedEventSphere();
const { selectedEventData, setSelectedEventData, clearSelectedEventData } = useSelectedEventData();
useEffect(() => {
if (selectedEventSphere) {
const eventData = getEventByModelUuid(
selectedEventSphere.userData.modelUuid
);
if (eventData) {
setSelectedEventData(eventData, selectedEventSphere.userData.pointUuid);
} else {
clearSelectedEventData();
}
} else {
clearSelectedEventData();
}
}, [selectedEventSphere]);
useEffect(() => {
if (selectedEventSphere) {
const eventData = getEventByModelUuid(
selectedEventSphere.userData.modelUuid
);
if (eventData) {
setSelectedEventData(eventData, selectedEventSphere.userData.pointUuid);
} else {
clearSelectedEventData();
}
} else {
clearSelectedEventData();
}
}, [selectedEventSphere]);
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
const keyCombination = detectModifierKeys(e);
if (!selectedEventSphere) return;
if (keyCombination === "G") {
setTransformMode((prev) => (prev === "translate" ? null : "translate"));
}
if (keyCombination === "R") {
setTransformMode((prev) => (prev === "rotate" ? null : "rotate"));
}
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
const keyCombination = detectModifierKeys(e);
if (!selectedEventSphere) return;
if (keyCombination === "G") {
setTransformMode((prev) => (prev === "translate" ? null : "translate"));
}
if (keyCombination === "R") {
setTransformMode((prev) => (prev === "rotate" ? null : "rotate"));
}
};
window.addEventListener("keydown", handleKeyDown);
return () => window.removeEventListener("keydown", handleKeyDown);
}, [selectedEventSphere]);
const updatePointToState = (selectedEventSphere: THREE.Mesh) => {
let point = JSON.parse(
JSON.stringify(
getPointByUuid(
selectedEventSphere.userData.modelUuid,
selectedEventSphere.userData.pointUuid
)
)
);
if (point) {
point.position = [
selectedEventSphere.position.x,
selectedEventSphere.position.y,
selectedEventSphere.position.z,
];
updatePoint(
selectedEventSphere.userData.modelUuid,
selectedEventSphere.userData.pointUuid,
point
);
}
};
window.addEventListener("keydown", handleKeyDown);
return () => window.removeEventListener("keydown", handleKeyDown);
}, [selectedEventSphere]);
const updatePointToState = (selectedEventSphere: THREE.Mesh) => {
let point = JSON.parse(
JSON.stringify(
getPointByUuid(
selectedEventSphere.userData.modelUuid,
selectedEventSphere.userData.pointUuid
)
)
);
if (point) {
point.position = [
selectedEventSphere.position.x,
selectedEventSphere.position.y,
selectedEventSphere.position.z,
];
updatePoint(
selectedEventSphere.userData.modelUuid,
selectedEventSphere.userData.pointUuid,
point
);
}
};
return (
<>
{activeModule === "simulation" && (
return (
<>
<group name="EventPointsGroup">
{events.map((event, i) => {
if (event.type === "transfer") {
return (
<group
key={i}
position={new THREE.Vector3(...event.position)}
>
{event.points.map((point, j) => (
<mesh
name="Event-Sphere"
uuid={point.uuid}
ref={(el) => (sphereRefs.current[point.uuid] = el!)}
onClick={(e) => {
e.stopPropagation();
setSelectedEventSphere(
sphereRefs.current[point.uuid]
);
}}
onPointerMissed={() => {
// clearSelectedEventSphere();
setTransformMode(null);
}}
key={`${i}-${j}`}
position={new THREE.Vector3(...point.position)}
userData={{
modelUuid: event.modelUuid,
pointUuid: point.uuid,
}}
>
<sphereGeometry args={[0.1, 16, 16]} />
<meshStandardMaterial color="orange" />
</mesh>
))}
</group>
);
} else if (event.type === "vehicle") {
return (
<group
key={i}
position={new THREE.Vector3(...event.position)}
>
<mesh
name="Event-Sphere"
uuid={event.point.uuid}
ref={(el) => (sphereRefs.current[event.point.uuid] = el!)}
onClick={(e) => {
e.stopPropagation();
setSelectedEventSphere(
sphereRefs.current[event.point.uuid]
);
}}
onPointerMissed={() => {
// clearSelectedEventSphere();
setTransformMode(null);
}}
position={new THREE.Vector3(...event.point.position)}
userData={{
modelUuid: event.modelUuid,
pointUuid: event.point.uuid,
}}
>
<sphereGeometry args={[0.1, 16, 16]} />
<meshStandardMaterial color="blue" />
</mesh>
</group>
);
} else if (event.type === "roboticArm") {
return (
<group
key={i}
position={new THREE.Vector3(...event.position)}
>
<mesh
name="Event-Sphere"
uuid={event.point.uuid}
ref={(el) => (sphereRefs.current[event.point.uuid] = el!)}
onClick={(e) => {
e.stopPropagation();
setSelectedEventSphere(
sphereRefs.current[event.point.uuid]
);
}}
onPointerMissed={() => {
// clearSelectedEventSphere();
setTransformMode(null);
}}
position={new THREE.Vector3(...event.point.position)}
userData={{
modelUuid: event.modelUuid,
pointUuid: event.point.uuid,
}}
>
<sphereGeometry args={[0.1, 16, 16]} />
<meshStandardMaterial color="green" />
</mesh>
</group>
);
} else if (event.type === "machine") {
return (
<group
key={i}
position={new THREE.Vector3(...event.position)}
>
<mesh
name="Event-Sphere"
uuid={event.point.uuid}
ref={(el) => (sphereRefs.current[event.point.uuid] = el!)}
onClick={(e) => {
e.stopPropagation();
setSelectedEventSphere(
sphereRefs.current[event.point.uuid]
);
}}
onPointerMissed={() => {
// clearSelectedEventSphere();
setTransformMode(null);
}}
position={new THREE.Vector3(...event.point.position)}
userData={{
modelUuid: event.modelUuid,
pointUuid: event.point.uuid,
}}
>
<sphereGeometry args={[0.1, 16, 16]} />
<meshStandardMaterial color="purple" />
</mesh>
</group>
);
} else {
return null;
}
})}
</group>
{selectedEventSphere && transformMode && (
<TransformControls
ref={transformRef}
object={selectedEventSphere}
mode={transformMode}
onMouseUp={(e) => {
updatePointToState(selectedEventSphere);
}}
/>
)}
{activeModule === "simulation" && (
<>
<group name="EventPointsGroup">
{events.map((event, i) => {
if (event.type === "transfer") {
return (
<group
key={i}
position={new THREE.Vector3(...event.position)}
>
{event.points.map((point, j) => (
<mesh
name="Event-Sphere"
uuid={point.uuid}
ref={(el) => (sphereRefs.current[point.uuid] = el!)}
onClick={(e) => {
e.stopPropagation();
setSelectedEventSphere(
sphereRefs.current[point.uuid]
);
}}
onPointerMissed={() => {
if (selectedEventData?.data.type !== 'vehicle') {
// clearSelectedEventSphere();
}
setTransformMode(null);
}}
key={`${i}-${j}`}
position={new THREE.Vector3(...point.position)}
userData={{
modelUuid: event.modelUuid,
pointUuid: point.uuid,
}}
>
<sphereGeometry args={[0.1, 16, 16]} />
<meshStandardMaterial color="orange" />
</mesh>
))}
</group>
);
} else if (event.type === "vehicle") {
return (
<group
key={i}
position={new THREE.Vector3(...event.position)}
>
<mesh
name="Event-Sphere"
uuid={event.point.uuid}
ref={(el) => (sphereRefs.current[event.point.uuid] = el!)}
onClick={(e) => {
e.stopPropagation();
setSelectedEventSphere(
sphereRefs.current[event.point.uuid]
);
}}
onPointerMissed={() => {
// clearSelectedEventSphere();
setTransformMode(null);
}}
position={new THREE.Vector3(...event.point.position)}
userData={{
modelUuid: event.modelUuid,
pointUuid: event.point.uuid,
}}
>
<sphereGeometry args={[0.1, 16, 16]} />
<meshStandardMaterial color="blue" />
</mesh>
</group>
);
} else if (event.type === "roboticArm") {
return (
<group
key={i}
position={new THREE.Vector3(...event.position)}
>
<mesh
name="Event-Sphere"
uuid={event.point.uuid}
ref={(el) => (sphereRefs.current[event.point.uuid] = el!)}
onClick={(e) => {
e.stopPropagation();
setSelectedEventSphere(
sphereRefs.current[event.point.uuid]
);
}}
onPointerMissed={() => {
// clearSelectedEventSphere();
setTransformMode(null);
}}
position={new THREE.Vector3(...event.point.position)}
userData={{
modelUuid: event.modelUuid,
pointUuid: event.point.uuid,
}}
>
<sphereGeometry args={[0.1, 16, 16]} />
<meshStandardMaterial color="green" />
</mesh>
</group>
);
} else if (event.type === "machine") {
return (
<group
key={i}
position={new THREE.Vector3(...event.position)}
>
<mesh
name="Event-Sphere"
uuid={event.point.uuid}
ref={(el) => (sphereRefs.current[event.point.uuid] = el!)}
onClick={(e) => {
e.stopPropagation();
setSelectedEventSphere(
sphereRefs.current[event.point.uuid]
);
}}
onPointerMissed={() => {
// clearSelectedEventSphere();
setTransformMode(null);
}}
position={new THREE.Vector3(...event.point.position)}
userData={{
modelUuid: event.modelUuid,
pointUuid: event.point.uuid,
}}
>
<sphereGeometry args={[0.1, 16, 16]} />
<meshStandardMaterial color="purple" />
</mesh>
</group>
);
} else {
return null;
}
})}
</group>
{selectedEventSphere && transformMode && (
<TransformControls
ref={transformRef}
object={selectedEventSphere}
mode={transformMode}
onMouseUp={(e) => {
updatePointToState(selectedEventSphere);
}}
/>
)}
</>
)}
</>
)}
</>
);
);
}
export default PointsCreator;

View File

@ -0,0 +1,28 @@
interface HandleAddEventToProductParams {
selectedAsset: any; // Replace `any` with specific type if you have it
addEvent: (productId: string, asset: any) => void;
selectedProduct: {
productId: string;
productName: string;
// Add other fields if needed
};
clearSelectedAsset: () => void;
}
export const handleAddEventToProduct = ({
selectedAsset,
addEvent,
selectedProduct,
clearSelectedAsset,
}: HandleAddEventToProductParams) => {
console.log('selectedProduct: ', selectedProduct);
if (selectedAsset) {
addEvent(selectedProduct.productId, selectedAsset);
// upsertProductOrEventApi({
// productName: selectedProduct.productName,
// productId: selectedProduct.productId,
// eventDatas: selectedAsset
// });
clearSelectedAsset();
}
};

View File

@ -8,7 +8,7 @@ function RoboticArmAnimator({ armUuid, HandleCallback, currentPhase, ikSolver, t
const { armBots } = useArmBotStore();
const { scene } = useThree();
const restSpeed = 0.1;
const restPosition = new THREE.Vector3(0, 2, 1.6);
const restPosition = new THREE.Vector3(0, 1, -1.6);
const initialCurveRef = useRef<THREE.CatmullRomCurve3 | null>(null);
const initialStartPositionRef = useRef<THREE.Vector3 | null>(null);
const [initialProgress, setInitialProgress] = useState(0);
@ -22,11 +22,12 @@ function RoboticArmAnimator({ armUuid, HandleCallback, currentPhase, ikSolver, t
const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]);
useEffect(() => {
setCurrentPath(path)
}, [path])
useEffect(() => {
}, [currentPath])
useFrame((_, delta) => {
@ -42,13 +43,13 @@ function RoboticArmAnimator({ armUuid, HandleCallback, currentPhase, ikSolver, t
currentPath.map(point => new THREE.Vector3(point[0], point[1], point[2]))
);
const next = initialProgressRef.current + delta * 0.5;
if (next >= 1) {
bone.position.copy(restPosition);
// bone.position.copy(restPosition);
HandleCallback(); // Call the callback when the path is completed
initialProgressRef.current = 0; // Set ref to 1 when done
} else {
const point = curve.getPoint(next); // Get the interpolated point from the curve
bone.position.copy(point); // Update the bone position along the curve
initialProgressRef.current = next; // Update progress

View File

@ -16,6 +16,7 @@ interface Process {
endPoint?: Vector3;
speed: number;
}
function RoboticArmInstance({ robot }: { robot: ArmBotStatus }) {
const { isPlaying } = usePlayButtonStore();
@ -29,7 +30,7 @@ function RoboticArmInstance({ robot }: { robot: ArmBotStatus }) {
const groupRef = useRef<any>(null);
const [processes, setProcesses] = useState<Process[]>([]);
const [armBotCurvePoints, setArmBotCurvePoints] = useState({ start: [], end: [] })
const restPosition = new THREE.Vector3(0, 2, 1.6);
const restPosition = new THREE.Vector3(0, 1, -1.6);
let armBotCurveRef = useRef<THREE.CatmullRomCurve3 | null>(null)
const [path, setPath] = useState<[number, number, number][]>([]);
@ -52,7 +53,7 @@ function RoboticArmInstance({ robot }: { robot: ArmBotStatus }) {
if (isPlaying) {
//Moving armBot from initial point to rest position.
if (!robot?.isActive && robot?.state == "idle" && currentPhase == "init") {
setArmBotActive(robot.modelUuid, true)
setArmBotState(robot.modelUuid, "running")
setCurrentPhase("init-to-rest");
@ -62,11 +63,11 @@ function RoboticArmInstance({ robot }: { robot: ArmBotStatus }) {
setPath(curve.points.map(point => [point.x, point.y, point.z]));
}
}
logStatus(robot.modelUuid, "Starting from init to rest")
logStatus(robot.modelUuid, "Moving armBot from initial point to rest position.")
}
//Waiting for trigger.
else if (robot && !robot.isActive && robot.state === "idle" && currentPhase === "rest" && !robot.currentAction) {
logStatus(robot.modelUuid, "Waiting to trigger CurrentAction")
setTimeout(() => {
addCurrentAction(robot.modelUuid, 'action-003');
}, 3000);
@ -84,7 +85,7 @@ function RoboticArmInstance({ robot }: { robot: ArmBotStatus }) {
}
}
}
logStatus(robot.modelUuid, "Starting from rest to start")
logStatus(robot.modelUuid, "Moving armBot from rest point to start position.")
}
else if (robot && !robot.isActive && robot.state === "idle" && currentPhase === "picking" && robot.currentAction) {
setArmBotActive(robot.modelUuid, true);
@ -98,10 +99,13 @@ function RoboticArmInstance({ robot }: { robot: ArmBotStatus }) {
new THREE.Vector3(endPoint[0], endPoint[1], endPoint[2])
);
if (curve) {
setPath(curve.points.map(point => [point.x, point.y, point.z]));
setTimeout(() => {
logStatus(robot.modelUuid, "picking the object");
setPath(curve.points.map(point => [point.x, point.y, point.z]));
}, 1500)
}
}
logStatus(robot.modelUuid, "Starting from start to end")
logStatus(robot.modelUuid, "Moving armBot from start point to end position.")
}
else if (robot && !robot.isActive && robot.state === "idle" && currentPhase === "dropping" && robot.currentAction) {
setArmBotActive(robot.modelUuid, true);
@ -112,10 +116,13 @@ function RoboticArmInstance({ robot }: { robot: ArmBotStatus }) {
let curve = createCurveBetweenTwoPoints(new THREE.Vector3(endPoint[0], endPoint[1], endPoint[2]), restPosition
);
if (curve) {
setPath(curve.points.map(point => [point.x, point.y, point.z]));
setTimeout(() => {
logStatus(robot.modelUuid, "dropping the object");
setPath(curve.points.map(point => [point.x, point.y, point.z]));
}, 1500)
}
}
logStatus(robot.modelUuid, "Starting from end to rest")
logStatus(robot.modelUuid, "Moving armBot from end point to rest position.")
}
}
@ -133,28 +140,28 @@ function RoboticArmInstance({ robot }: { robot: ArmBotStatus }) {
const HandleCallback = () => {
if (robot.isActive && robot.state == "running" && currentPhase == "init-to-rest") {
logStatus(robot.modelUuid, "Callback triggered: rest");
setArmBotActive(robot.modelUuid, false)
setArmBotState(robot.modelUuid, "idle")
setCurrentPhase("rest");
setPath([])
}
else if (robot.isActive && robot.state == "running" && currentPhase == "rest-to-start") {
logStatus(robot.modelUuid, "Callback triggered: pick.");
setArmBotActive(robot.modelUuid, false)
setArmBotState(robot.modelUuid, "idle")
setCurrentPhase("picking");
setPath([])
}
else if (robot.isActive && robot.state == "running" && currentPhase == "start-to-end") {
logStatus(robot.modelUuid, "Callback triggered: drop.");
setArmBotActive(robot.modelUuid, false)
setArmBotState(robot.modelUuid, "idle")
setCurrentPhase("dropping");
setPath([])
}
else if (robot.isActive && robot.state == "running" && currentPhase == "end-to-rest") {
logStatus(robot.modelUuid, "Callback triggered: rest, cycle completed.");
setArmBotActive(robot.modelUuid, false)
setArmBotState(robot.modelUuid, "idle")
setCurrentPhase("rest");
@ -163,12 +170,12 @@ function RoboticArmInstance({ robot }: { robot: ArmBotStatus }) {
}
}
const logStatus = (id: string, status: string) => {
// console.log(id + "," + status);
console.log( status);
}
return (
<>
<IKInstance modelUrl={armModel} setIkSolver={setIkSolver} ikSolver={ikSolver} robot={robot} groupRef={groupRef} processes={processes}
setArmBotCurvePoints={setArmBotCurvePoints} />
<RoboticArmAnimator armUuid={robot?.modelUuid} HandleCallback={HandleCallback}

View File

@ -15,6 +15,7 @@ type IKInstanceProps = {
setArmBotCurvePoints: any
};
function IKInstance({ modelUrl, setIkSolver, ikSolver, robot, groupRef, processes, setArmBotCurvePoints }: IKInstanceProps) {
const { scene } = useThree();
const gltf = useLoader(GLTFLoader, modelUrl, (loader) => {
const draco = new DRACOLoader();
@ -64,16 +65,16 @@ function IKInstance({ modelUrl, setIkSolver, ikSolver, robot, groupRef, processe
const solver = new CCDIKSolver(OOI.Skinned_Mesh, iks);
setIkSolver(solver);
const helper = new CCDIKHelper(OOI.Skinned_Mesh, iks, 0.05);
const helper = new CCDIKHelper(OOI.Skinned_Mesh, iks, 0.05)
// scene.add(groupRef.current)
// scene.add(helper)
}, [gltf]);
return (
<>
<group ref={groupRef} position={robot.position}>
<group ref={groupRef} position={robot.position} rotation={robot.rotation}>
<primitive
uuid={"ArmBot-X200"}
object={cloned}

View File

@ -7,18 +7,19 @@ function RoboticArm() {
const { armBots, addArmBot, removeArmBot } = useArmBotStore();
const { floorItems } = useFloorItems();
const armBotStatusSample: RoboticArmEventSchema[] = [
{
state: "idle",
modelUuid: "armbot-xyz-001",
modelUuid: "3abf5d46-b59e-4e6b-9c02-a4634b64b82d",
modelName: "ArmBot-X200",
position: [91.94347308985614, 0, 6.742905194869091],
rotation: [0, 0, 0],
position: [0.20849215906958463, 0, 0.32079278127773675],
rotation: [-1.3768690876192207e-15, 1.4883085074751308, 1.5407776675834467e-15],
type: "roboticArm",
speed: 1.5,
point: {
uuid: "point-123",
position: [0, 1.5, 0],
position: [0, 2.6, 0],
rotation: [0, 0, 0],
actions: [
{
@ -26,9 +27,21 @@ function RoboticArm() {
actionName: "Pick Component",
actionType: "pickAndPlace",
process: {
startPoint: [5.52543010919071, 1, -8.433681161200905],
endPoint: [10.52543010919071, 1, -12.433681161200905],
startPoint: [-1, 2, 1],
endPoint: [-2, 1, -1],
},
// process: {
// "startPoint": [
// 0.37114476008711866,
// 1.9999999999999998,
// 1.8418816116721384
// ],
// "endPoint": [
// -0.42197069459490777,
// 1,
// -3.159515927851809
// ]
// },
triggers: [
{
triggerUuid: "trigger-001",
@ -145,7 +158,7 @@ function RoboticArm() {
];
useEffect(() => {
removeArmBot(armBotStatusSample[0].modelUuid);
addArmBot('123', armBotStatusSample[0]);
// addArmBot('123', armBotStatusSample[1]);
@ -153,7 +166,7 @@ function RoboticArm() {
}, []);
useEffect(() => {
//
}, [armBots]);
return (

View File

@ -0,0 +1,54 @@
import React, { useRef } from "react";
import * as THREE from "three";
import { ThreeEvent } from "@react-three/fiber";
interface PickDropProps {
position: number[];
modelUuid: string;
pointUuid: string;
actionType: "pick" | "drop";
actionUuid: string;
gltfScene: THREE.Group;
selectedPoint: THREE.Mesh | null;
handlePointerDown: (e: ThreeEvent<PointerEvent>) => void;
isSelected: boolean;
}
const PickDropPoints: React.FC<PickDropProps> = ({
position,
modelUuid,
pointUuid,
actionType,
actionUuid,
gltfScene,
selectedPoint,
handlePointerDown,
isSelected,
}) => {
const groupRef = useRef<THREE.Group>(null);
return (
<group
ref={groupRef}
position={
Array.isArray(position) && position.length === 3
? new THREE.Vector3(...position)
: new THREE.Vector3(0, 0, 0)
}
onPointerDown={(e) => {
e.stopPropagation(); // Important to prevent event bubbling
if (!isSelected) return;
handlePointerDown(e);
}}
userData={{ modelUuid, pointUuid, actionType, actionUuid }}
>
<primitive
object={gltfScene.clone()}
position={[0, 0, 0]} // Ensure this stays at origin
scale={[0.5, 0.5, 0.5]}
/>
</group>
);
};
export default PickDropPoints;

View File

@ -0,0 +1,131 @@
import { useRef } from "react";
import * as THREE from "three";
import { ThreeEvent, useThree } from "@react-three/fiber";
type OnUpdateCallback = (object: THREE.Object3D) => void;
export default function useDraggableGLTF(onUpdate: OnUpdateCallback) {
const { camera, gl, controls, scene } = useThree();
const activeObjRef = useRef<THREE.Object3D | null>(null);
const planeRef = useRef<THREE.Plane>(
new THREE.Plane(new THREE.Vector3(0, 1, 0), 0)
);
const offsetRef = useRef<THREE.Vector3>(new THREE.Vector3());
const initialPositionRef = useRef<THREE.Vector3>(new THREE.Vector3());
const raycaster = new THREE.Raycaster();
const pointer = new THREE.Vector2();
const handlePointerDown = (e: ThreeEvent<PointerEvent>) => {
e.stopPropagation();
let obj: THREE.Object3D | null = e.object;
// Traverse up until we find modelUuid in userData
while (obj && !obj.userData?.modelUuid) {
obj = obj.parent;
}
if (!obj) return;
// Disable orbit controls while dragging
if (controls) (controls as any).enabled = false;
activeObjRef.current = obj;
initialPositionRef.current.copy(obj.position);
// Get world position
const objectWorldPos = new THREE.Vector3();
obj.getWorldPosition(objectWorldPos);
// Set plane at the object's Y level
planeRef.current.set(new THREE.Vector3(0, 1, 0), -objectWorldPos.y);
// Convert pointer to NDC
const rect = gl.domElement.getBoundingClientRect();
pointer.x = ((e.clientX - rect.left) / rect.width) * 2 - 1;
pointer.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;
// Raycast to intersection
raycaster.setFromCamera(pointer, camera);
const intersection = new THREE.Vector3();
raycaster.ray.intersectPlane(planeRef.current, intersection);
// Calculate offset
offsetRef.current.copy(objectWorldPos).sub(intersection);
// Start listening for drag
gl.domElement.addEventListener("pointermove", handlePointerMove);
gl.domElement.addEventListener("pointerup", handlePointerUp);
};
const handlePointerMove = (e: PointerEvent) => {
if (!activeObjRef.current) return;
// Check if Shift key is pressed
const isShiftKeyPressed = e.shiftKey;
// Get the mouse position relative to the canvas
const rect = gl.domElement.getBoundingClientRect();
pointer.x = ((e.clientX - rect.left) / rect.width) * 2 - 1;
pointer.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;
// Update raycaster to point to the mouse position
raycaster.setFromCamera(pointer, camera);
// Create a vector to store intersection point
const intersection = new THREE.Vector3();
const intersects = raycaster.ray.intersectPlane(planeRef.current, intersection);
if (!intersects) return;
// Add offset for dragging
intersection.add(offsetRef.current);
console.log('intersection: ', intersection);
// Get the parent's world matrix if exists
const parent = activeObjRef.current.parent;
const targetPosition = new THREE.Vector3();
if (isShiftKeyPressed) {
console.log('isShiftKeyPressed: ', isShiftKeyPressed);
// For Y-axis only movement, maintain original X and Z
console.log('initialPositionRef: ', initialPositionRef);
console.log('intersection.y: ', intersection);
targetPosition.set(
initialPositionRef.current.x,
intersection.y,
initialPositionRef.current.z
);
} else {
// For free movement
targetPosition.copy(intersection);
}
// Convert world position to local if object is nested inside a parent
if (parent) {
parent.worldToLocal(targetPosition);
}
// Update object position
activeObjRef.current.position.copy(targetPosition);
};
const handlePointerUp = () => {
if (controls) (controls as any).enabled = true;
if (activeObjRef.current) {
// Pass the updated position to the onUpdate callback to persist it
onUpdate(activeObjRef.current);
}
gl.domElement.removeEventListener("pointermove", handlePointerMove);
gl.domElement.removeEventListener("pointerup", handlePointerUp);
activeObjRef.current = null;
};
return { handlePointerDown };
}

View File

@ -1,302 +1,230 @@
import React, { useRef, useEffect, useState } from "react";
import React, { useEffect, useRef, useState } from 'react';
import startPoint from "../../../../assets/gltf-glb/arrow_green.glb";
import startEnd from "../../../../assets/gltf-glb/arrow_red.glb";
import { useGLTF } from "@react-three/drei";
import { useSelectedEventSphere } from "../../../../store/simulation/useSimulationStore";
import * as THREE from "three";
import { useThree } from "@react-three/fiber";
import { useVehicleStore } from "../../../../store/simulation/useVehicleStore";
import { useGLTF } from '@react-three/drei';
import { useFrame, useThree } from '@react-three/fiber';
import { useSelectedEventSphere } from '../../../../store/simulation/useSimulationStore';
import { useVehicleStore } from '../../../../store/simulation/useVehicleStore';
import * as Types from "../../../../types/world/worldTypes";
const VehicleUI = () => {
const { scene: startScene } = useGLTF(startPoint) as any;
const { scene: endScene } = useGLTF(startEnd) as any;
const startMarker = useRef<THREE.Group>(null);
const endMarker = useRef<THREE.Group>(null);
const prevMousePos = useRef({ x: 0, y: 0 });
const { selectedEventSphere } = useSelectedEventSphere();
const { vehicles, updateVehicle } = useVehicleStore();
const [startPosition, setStartPosition] = useState<[number, number, number]>([0, 0, 0]);
const [endPosition, setEndPosition] = useState<[number, number, number]>([0, 0, 0]);
const [startRotation, setStartRotation] = useState<[number, number, number]>([0, 0, 0]);
const [endRotation, setEndRotation] = useState<[number, number, number]>([0, 0, 0]);
const [isDragging, setIsDragging] = useState<"start" | "end" | null>(null);
const [isRotating, setIsRotating] = useState<"start" | "end" | null>(null);
const { raycaster } = useThree();
const plane = useRef(new THREE.Plane(new THREE.Vector3(0, 1, 0), 0));
const state: Types.ThreeState = useThree();
const controls: any = state.controls;
type VehicleUIProps = {
vehicleStatusSample: VehicleEventSchema[];
setVehicleStatusSample: React.Dispatch<
React.SetStateAction<VehicleEventSchema[]>
>;
vehicle: any
};
const VehicleUI: React.FC<VehicleUIProps> = ({
vehicleStatusSample,
setVehicleStatusSample,
vehicle
}) => {
const { scene: startScene } = useGLTF(startPoint) as any;
const { scene: endScene } = useGLTF(startEnd) as any;
const { camera, gl, controls } = useThree();
const { selectedEventSphere } = useSelectedEventSphere();
const { updateVehicle } = useVehicleStore();
const startMarker = useRef<THREE.Group>(null);
const endMarker = useRef<THREE.Group>(null);
const hasInitialized = useRef<boolean>(false);
const raycaster = useRef(new THREE.Raycaster());
const plane = useRef(new THREE.Plane(new THREE.Vector3(0, 1, 0), 0)); // Y = 0 plane
const mouse = useRef(new THREE.Vector2());
const prevMousePos = useRef({ x: 0, y: 0 });
const [draggedMarker, setDraggedMarker] = useState<"start" | "end" | null>(
null
);
const [dragOffset, setDragOffset] = useState<THREE.Vector3 | null>(null);
const [isRotating, setIsRotating] = useState<boolean>(false);
// Initialize start/end markers
useEffect(() => {
if (
selectedEventSphere &&
startMarker.current &&
endMarker.current &&
!hasInitialized.current
) {
startMarker.current.clear();
endMarker.current.clear();
const startClone = startScene.clone();
const endClone = endScene.clone();
startClone.name = "start-marker";
endClone.name = "end-marker";
startClone.traverse((child: any) => {
if (child.isMesh && child.name.toLowerCase().includes("handle")) {
child.name = "handle";
}
});
endClone.traverse((child: any) => {
if (child.isMesh && child.name.toLowerCase().includes("handle")) {
child.name = "handle";
}
});
startMarker.current.add(startClone);
endMarker.current.add(endClone);
hasInitialized.current = true;
}
}, [selectedEventSphere, startScene, endScene]);
// Position start/end markers
useEffect(() => {
if (!selectedEventSphere || !startMarker.current || !endMarker.current)
return;
const selectedVehicle = vehicleStatusSample.find(
(vehicle) => vehicle.modelUuid === selectedEventSphere.userData.modelUuid
);
if (selectedVehicle?.point?.action) {
const { pickUpPoint, unLoadPoint } = selectedVehicle.point.action;
// Update start marker position
if (pickUpPoint) {
const localPos = new THREE.Vector3(
pickUpPoint.x,
pickUpPoint.y,
pickUpPoint.z
);
localPos.y = 0; // Force y to 0
startMarker.current.position.copy(localPos);
} else {
const defaultLocal = new THREE.Vector3(0, 0, 1.5);
const defaultWorld = selectedEventSphere.localToWorld(defaultLocal);
defaultWorld.y = 0; // Force y to 0
startMarker.current.position.copy(defaultWorld);
}
// Update end marker position
if (unLoadPoint) {
const localPos = new THREE.Vector3(
unLoadPoint.x,
unLoadPoint.y,
unLoadPoint.z
useEffect(() => {
if (!selectedEventSphere) return;
const selectedVehicle = vehicles.find(
(vehicle: any) => vehicle.modelUuid === selectedEventSphere.userData.modelUuid
);
if (selectedVehicle?.point?.action) {
const { pickUpPoint, unLoadPoint } = selectedVehicle.point.action;
localPos.y = 0; // Force y to 0
endMarker.current.position.copy(localPos);
} else {
const defaultLocal = new THREE.Vector3(0, 0, -1.5);
const defaultWorld = selectedEventSphere.localToWorld(defaultLocal);
defaultWorld.y = 0; // Force y to 0
endMarker.current.position.copy(defaultWorld);
}
}
}, [selectedEventSphere, vehicleStatusSample]);
if (pickUpPoint) {
const pickupPosition = new THREE.Vector3(
pickUpPoint.position.x,
pickUpPoint.position.y,
pickUpPoint.position.z
);
const pickupRotation = new THREE.Vector3(
pickUpPoint.rotation.x,
pickUpPoint.rotation.y,
pickUpPoint.rotation.z
);
pickupPosition.y = 0; // Force y to 0
setStartPosition([pickupPosition.x, 0, pickupPosition.z]);
setStartRotation([pickupRotation.x, pickupRotation.y, pickupRotation.z]);
} else {
const defaultLocal = new THREE.Vector3(0, 0, 1.5);
const defaultWorld = selectedEventSphere.localToWorld(defaultLocal);
defaultWorld.y = 0; // Force y to 0
setStartPosition([defaultWorld.x, 0, defaultWorld.z]);
setStartRotation([0, 0, 0]);
}
// Handle dragging and rotation
const handlePointerDown = (e: any, markerType: "start" | "end") => {
if (!selectedEventSphere) return;
// Initialize end marker position and rotation
if (unLoadPoint) {
const unLoadPosition = new THREE.Vector3(
unLoadPoint.position.x,
unLoadPoint.position.y,
unLoadPoint.position.z
);
const unLoadRotation = new THREE.Vector3(
unLoadPoint.rotation.x,
unLoadPoint.rotation.y,
unLoadPoint.position.z
);
unLoadPosition.y = 0; // Force y to 0
setEndPosition([unLoadPosition.x, 0, unLoadPosition.z]);
setEndRotation([unLoadRotation.x, unLoadRotation.y, unLoadRotation.z]);
} else {
const defaultLocal = new THREE.Vector3(0, 0, -1.5);
const defaultWorld = selectedEventSphere.localToWorld(defaultLocal);
defaultWorld.y = 0; // Force y to 0
setEndPosition([defaultWorld.x, 0, defaultWorld.z]);
setEndRotation([0, 0, 0]);
}
}
}, [selectedEventSphere]);
if (e.object.name === "handle") {
setIsRotating(true);
prevMousePos.current = { x: e.clientX, y: e.clientY };
if (controls) (controls as any).enabled = false;
e.stopPropagation();
setDraggedMarker(markerType);
return;
}
useFrame(() => {
if (!isDragging) return;
const intersectPoint = new THREE.Vector3();
const intersects = raycaster.ray.intersectPlane(plane.current, intersectPoint);
setDraggedMarker(markerType);
if (controls) (controls as any).enabled = false;
if (intersects) {
intersectPoint.y = 0; // Force y to 0
if (isDragging === "start") {
setStartPosition([intersectPoint.x, 0, intersectPoint.z]);
}
if (isDragging === "end") {
setEndPosition([intersectPoint.x, 0, intersectPoint.z]);
}
}
});
const marker =
markerType === "start" ? startMarker.current : endMarker.current;
if (!marker) return;
useFrame((state) => {
if (!isRotating) return;
mouse.current.x = (e.clientX / gl.domElement.clientWidth) * 2 - 1;
mouse.current.y = -(e.clientY / gl.domElement.clientHeight) * 2 + 1;
const currentPointerX = state.pointer.x;
const deltaX = currentPointerX - prevMousePos.current.x;
prevMousePos.current.x = currentPointerX;
raycaster.current.setFromCamera(mouse.current, camera);
const marker = isRotating === "start" ? startMarker.current : endMarker.current;
const intersectPoint = new THREE.Vector3();
raycaster.current.ray.intersectPlane(plane.current, intersectPoint);
if (marker) {
const rotationSpeed = 10;
marker.rotation.y -= deltaX * rotationSpeed;
const offset = new THREE.Vector3().subVectors(
marker.position,
intersectPoint
);
setDragOffset(offset);
};
const handlePointerMove = (e: PointerEvent) => {
if (!selectedEventSphere) return;
if (isRotating) {
const deltaX = e.clientX - prevMousePos.current.x;
prevMousePos.current = { x: e.clientX, y: e.clientY };
const rotationSpeed = 0.01;
const marker =
draggedMarker === "start" ? startMarker.current : endMarker.current;
if (marker) {
marker.rotation.y -= deltaX * rotationSpeed;
}
return;
}
if (!draggedMarker || !dragOffset) return;
mouse.current.x = (e.clientX / gl.domElement.clientWidth) * 2 - 1;
mouse.current.y = -(e.clientY / gl.domElement.clientHeight) * 2 + 1;
raycaster.current.setFromCamera(mouse.current, camera);
const intersectPoint = new THREE.Vector3();
raycaster.current.ray.intersectPlane(plane.current, intersectPoint);
if (!intersectPoint) return;
const newPos = {
x: intersectPoint.x + dragOffset.x,
y: 0,
z: intersectPoint.z + dragOffset.z,
};
if (draggedMarker === "start" && startMarker.current) {
startMarker.current.position.set(newPos.x, newPos.y, newPos.z);
} else if (draggedMarker === "end" && endMarker.current) {
endMarker.current.position.set(newPos.x, newPos.y, newPos.z);
}
};
const handlePointerUp = () => {
if (isRotating) {
setIsRotating(false);
if (controls) (controls as any).enabled = true;
return;
}
if (!selectedEventSphere || !draggedMarker || !dragOffset) {
if (controls) (controls as any).enabled = true;
return;
}
if (controls) (controls as any).enabled = true;
const marker =
draggedMarker === "start" ? startMarker.current : endMarker.current;
if (!marker) return;
const worldPos = marker.position;
const updatedLocalPos = { x: worldPos.x, y: 0, z: worldPos.z };
console.log('updatedLocalPos: ', updatedLocalPos);
console.log('draggedMarker: ', draggedMarker);
// setVehicleStatusSample((prev) =>
// prev.map((vehicle) => {
// if (
// vehicle.modelUuid === selectedEventSphere.userData.modelUuid &&
// selectedEventSphere
// ) {
// const updatedVehicle = {
// ...vehicle,
// point: {
// ...vehicle.point,
// action: {
// ...vehicle.point?.action,
// ...(draggedMarker === "start"
// ? { pickUpPoint: updatedLocalPos }
// : { unLoadPoint: updatedLocalPos }),
// },
// },
// };
// return updatedVehicle;
// }
// return vehicle;
// })
// );
updateVehicle(selectedEventSphere.userData.modelUuid, {
point: {
...vehicle?.point,
action: {
...vehicle?.point?.action,
...(draggedMarker === "start"
? { pickUpPoint: updatedLocalPos }
: { unLoadPoint: updatedLocalPos }),
},
},
}
});
setDraggedMarker(null);
setDragOffset(null);
};
const handlePointerDown = (e: any, state: "start" | "end", rotation: "start" | "end") => {
useEffect(() => {
window.addEventListener("pointermove", handlePointerMove);
window.addEventListener("pointerup", handlePointerUp);
if (e.object.name === "handle") {
const normalizedX = (e.clientX / window.innerWidth) * 2 - 1;
const normalizedY = -(e.clientY / window.innerHeight) * 2 + 1;
prevMousePos.current = { x: normalizedX, y: normalizedY };
setIsRotating(rotation);
if (controls) controls.enabled = false;
setIsDragging(null);
return () => {
window.removeEventListener("pointermove", handlePointerMove);
window.removeEventListener("pointerup", handlePointerUp);
} else {
setIsDragging(state);
setIsRotating(null);
if (controls) controls.enabled = false;
}
};
}, [draggedMarker, dragOffset, isRotating]);
if (!selectedEventSphere) {
hasInitialized.current = false;
return null;
}
const handlePointerUp = () => {
console.log("nulll");
controls.enabled = true;
setIsDragging(null);
setIsRotating(null);
return (
<group>
<group
ref={startMarker}
scale={draggedMarker === "start" ? [1.1, 1.1, 1.1] : [1, 1, 1]}
onPointerDown={(e) => handlePointerDown(e, "start")}
/>
<group
ref={endMarker}
scale={draggedMarker === "end" ? [1.1, 1.1, 1.1] : [1, 1, 1]}
onPointerDown={(e) => handlePointerDown(e, "end")}
/>
</group>
);
};
if (selectedEventSphere?.userData.modelUuid) {
const updatedVehicle = vehicles.find(
(vehicle) => vehicle.modelUuid === selectedEventSphere.userData.modelUuid
);
if (updatedVehicle) {
updateVehicle(selectedEventSphere.userData.modelUuid, {
point: {
...updatedVehicle.point,
action: {
...updatedVehicle.point?.action,
pickUpPoint: {
position: {
x: startPosition[0],
y: startPosition[1],
z: startPosition[2],
},
rotation: {
x: startRotation[0],
y: startRotation[1],
z: startRotation[2],
},
},
unLoadPoint: {
position: {
x: endPosition[0],
y: endPosition[1],
z: endPosition[2],
},
rotation: {
x: endRotation[0],
y: endRotation[1],
z: endRotation[2],
},
},
},
},
});
}
}
};
return (
startPosition.length > 0 && endPosition.length > 0 ? (
<mesh>
<primitive
name="start"
object={startScene}
ref={startMarker}
position={startPosition}
onPointerDown={(e: any) => {
e.stopPropagation();
handlePointerDown(e, "start", "start");
}}
onPointerUp={() => {
handlePointerUp();
}}
onPointerMissed={() => {
console.log("start pointermissed");
handlePointerUp();
}}
/>
<primitive
name="end"
object={endScene}
ref={endMarker}
position={endPosition}
onPointerDown={(e: any) => {
e.stopPropagation();
handlePointerDown(e, "end", "end");
}}
onPointerUp={() => {
console.log("up");
handlePointerUp();
}}
onPointerMissed={() => {
console.log("end pointermissed");
handlePointerUp();
}}
/>
</mesh>
) : null
);
}
export default VehicleUI;

View File

@ -15,7 +15,6 @@ interface VehicleAnimatorProps {
}
function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetail, reset }: VehicleAnimatorProps) {
// console.log('path: ', path);
const { decrementVehicleLoad } = useVehicleStore();
const { isPaused } = usePauseButtonStore();
const { isPlaying } = usePlayButtonStore();
@ -193,7 +192,6 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai
}
}
return (
<>
{currentPath.length > 0 && (

View File

@ -51,10 +51,11 @@ function VehicleInstance({ agvDetail }: any) {
useEffect(() => {
if (isPlaying) {
if (!agvDetail.isActive && agvDetail.state === 'idle' && currentPhase === 'stationed') {
const toPickupPath = computePath(
new THREE.Vector3(agvDetail.position[0], agvDetail.position[1], agvDetail.position[2]),
agvDetail.point.action.pickUpPoint
new THREE.Vector3(agvDetail?.position[0], agvDetail?.position[1], agvDetail?.position[2]),
agvDetail?.point?.action?.pickUpPoint?.position
);
setPath(toPickupPath);
setCurrentPhase('stationed-pickup');
@ -70,8 +71,8 @@ function VehicleInstance({ agvDetail }: any) {
if (agvDetail.currentLoad === agvDetail.point.action.loadCapacity) {
const toDrop = computePath(
agvDetail.point.action.pickUpPoint,
agvDetail.point.action.unLoadPoint
agvDetail.point.action.pickUpPoint.position,
agvDetail.point.action.unLoadPoint.position
);
setPath(toDrop);
setCurrentPhase('pickup-drop');
@ -81,8 +82,8 @@ function VehicleInstance({ agvDetail }: any) {
}
} else if (!agvDetail.isActive && agvDetail.state === 'idle' && currentPhase === 'dropping' && agvDetail.currentLoad === 0) {
const dropToPickup = computePath(
agvDetail.point.action.unLoadPoint,
agvDetail.point.action.pickUpPoint
agvDetail.point.action.unLoadPoint.position,
agvDetail.point.action.pickUpPoint.position
);
setPath(dropToPickup);
setCurrentPhase('drop-pickup');

View File

@ -1,17 +1,8 @@
import React from 'react'
import VehicleInstance from './instance/vehicleInstance'
import { useVehicleStore } from '../../../../store/simulation/useVehicleStore'
import VehicleUI from '../../ui/vehicle/vehicleUI';
type VehicleUIProps = {
vehicleStatusSample: VehicleEventSchema[];
setVehicleStatusSample: React.Dispatch<
React.SetStateAction<VehicleEventSchema[]>
>;
};
const VehicleInstances: React.FC<VehicleUIProps> = ({
vehicleStatusSample,
setVehicleStatusSample,
}) => {
function VehicleInstances() {
const { vehicles } = useVehicleStore();
@ -19,14 +10,9 @@ const VehicleInstances: React.FC<VehicleUIProps> = ({
<>
{vehicles.map((val: any, i: any) =>
<>
<VehicleInstance agvDetail={val} key={i} />
<VehicleUI
setVehicleStatusSample={setVehicleStatusSample}
vehicleStatusSample={vehicleStatusSample}
vehicle={val}
/>
</>
<VehicleInstance agvDetail={val} key={i} />
)}
</>

View File

@ -2,13 +2,16 @@ import React, { useEffect, useState } from "react";
import VehicleInstances from "./instances/vehicleInstances";
import { useVehicleStore } from "../../../store/simulation/useVehicleStore";
import { useFloorItems } from "../../../store/store";
import { useSelectedEventSphere } from "../../../store/simulation/useSimulationStore";
import { useSelectedEventData, useSelectedEventSphere } from "../../../store/simulation/useSimulationStore";
import VehicleUI from "../ui/vehicle/vehicleUI";
import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
function Vehicles() {
const { vehicles, addVehicle } = useVehicleStore();
const { floorItems } = useFloorItems()
const { selectedEventSphere } = useSelectedEventSphere();
const { selectedEventData } = useSelectedEventData();
const { floorItems } = useFloorItems();
const { isPlaying } = usePlayButtonStore();
const [vehicleStatusSample, setVehicleStatusSample] = useState<
VehicleEventSchema[]
@ -31,8 +34,8 @@ function Vehicles() {
actionType: "travel",
unLoadDuration: 10,
loadCapacity: 2,
pickUpPoint: { x: 98.71483985219794, y: 0, z: 28.66321267938962 },
unLoadPoint: { x: 105.71483985219794, y: 0, z: 28.66321267938962 },
pickUpPoint: { position: { x: 98.71483985219794, y: 0, z: 28.66321267938962 }, rotation: { x: 0, y: 0, z: 0 } },
unLoadPoint: { position: { x: 105.71483985219794, y: 0, z: 28.66321267938962 }, rotation: { x: 0, y: 0, z: 0 } },
triggers: [
{
triggerUuid: "trig-001",
@ -99,49 +102,49 @@ function Vehicles() {
}
}
},
{
modelUuid: "cd7d0584-0684-42b4-b051-9e882c1914aa",
modelName: "AGV",
position: [105.90938758014703, 0, 31.584209911095215],
rotation: [0, 0, 0],
state: "idle",
type: "vehicle",
speed: 2.5,
point: {
uuid: "point-789",
position: [0, 1, 0],
rotation: [0, 0, 0],
action: {
actionUuid: "action-456",
actionName: "Deliver to Zone A",
actionType: "travel",
unLoadDuration: 10,
loadCapacity: 2,
pickUpPoint: null,
unLoadPoint: null,
triggers: [
{
triggerUuid: "trig-001",
triggerName: "Start Travel",
triggerType: "onStart",
delay: 0,
triggeredAsset: {
triggeredModel: { modelName: "ArmBot-X", modelUuid: "arm-001" },
triggeredPoint: { pointName: "Pickup Arm Point", pointUuid: "arm-point-01" },
triggeredAction: { actionName: "Grab Widget", actionUuid: "grab-001" }
}
},
{
triggerUuid: "trig-002",
triggerName: "Complete Travel",
triggerType: "onComplete",
delay: 2,
triggeredAsset: null
}
]
}
}
},
// {
// modelUuid: "cd7d0584-0684-42b4-b051-9e882c1914aa",
// modelName: "AGV",
// position: [105.90938758014703, 0, 31.584209911095215],
// rotation: [0, 0, 0],
// state: "idle",
// type: "vehicle",
// speed: 2.5,
// point: {
// uuid: "point-789",
// position: [0, 1, 0],
// rotation: [0, 0, 0],
// action: {
// actionUuid: "action-456",
// actionName: "Deliver to Zone A",
// actionType: "travel",
// unLoadDuration: 10,
// loadCapacity: 2,
// pickUpPoint: null,
// unLoadPoint: null,
// triggers: [
// {
// triggerUuid: "trig-001",
// triggerName: "Start Travel",
// triggerType: "onStart",
// delay: 0,
// triggeredAsset: {
// triggeredModel: { modelName: "ArmBot-X", modelUuid: "arm-001" },
// triggeredPoint: { pointName: "Pickup Arm Point", pointUuid: "arm-point-01" },
// triggeredAction: { actionName: "Grab Widget", actionUuid: "grab-001" }
// }
// },
// {
// triggerUuid: "trig-002",
// triggerName: "Complete Travel",
// triggerType: "onComplete",
// delay: 2,
// triggeredAsset: null
// }
// ]
// }
// }
// },
// {
// modelUuid: "e729a4f1-11d2-4778-8d6a-468f1b4f6b79",
// modelName: "forklift",
@ -189,20 +192,20 @@ function Vehicles() {
useEffect(() => {
console.log("vehicles", vehicles);
}, [vehicles])
useEffect(() => {
addVehicle("123", vehicleStatusSample[0]);
addVehicle('123', vehicleStatusSample[1]);
addVehicle('123', vehicleStatusSample[2]);
// addVehicle('123', vehicleStatusSample[2]);
}, []);
return (
<>
<VehicleInstances setVehicleStatusSample={setVehicleStatusSample}
vehicleStatusSample={vehicleStatusSample} />
{/* <VehicleUI
setVehicleStatusSample={setVehicleStatusSample}
vehicleStatusSample={vehicles}
/> */}
<VehicleInstances />
{selectedEventSphere && selectedEventData?.data.type === "vehicle" && !isPlaying &&
< VehicleUI />
}
</>
);
}

View File

@ -12,15 +12,12 @@ import {
useFloatingWidget,
} from "../../store/visualization/useDroppedObjectsStore";
import {
useAsset3dWidget,
useSocketStore,
useWidgetSubOption,
useZones,
} from "../../store/store";
import { getZone2dData } from "../../services/visulization/zone/getZoneData";
import { generateUniqueId } from "../../functions/generateUniqueId";
import { determinePosition } from "./functions/determinePosition";
import { addingFloatingWidgets } from "../../services/visulization/zone/addFloatingWidgets";
import SocketRealTimeViz from "./socket/realTimeVizSocket.dev";
import RenderOverlay from "../../components/templates/Overlay";
import ConfirmationPopup from "../../components/layout/confirmationPopup/ConfirmationPopup";
@ -68,20 +65,15 @@ const RealTimeVisulization: React.FC = () => {
const containerRef = useRef<HTMLDivElement>(null);
const { isPlaying } = usePlayButtonStore();
const { activeModule } = useModuleStore();
const [droppedObjects, setDroppedObjects] = useState<any[]>([]);
const [zonesData, setZonesData] = useState<FormattedZoneData>({});
const { selectedZone, setSelectedZone } = useSelectedZoneStore();
const { rightSelect, setRightSelect } = useRightSelected();
const { editWidgetOptions, setEditWidgetOptions } =
useEditWidgetOptionsStore();
const { setRightSelect } = useRightSelected();
const { editWidgetOptions, setEditWidgetOptions } = useEditWidgetOptionsStore();
const { rightClickSelected, setRightClickSelected } = useRightClickSelected();
const [openConfirmationPopup, setOpenConfirmationPopup] = useState(false);
// const [floatingWidgets, setFloatingWidgets] = useState<Record<string, { zoneName: string; zoneId: string; objects: any[] }>>({});
const { floatingWidget, setFloatingWidget } = useFloatingWidget();
const { widgetSelect, setWidgetSelect } = useAsset3dWidget();
const { widgetSubOption, setWidgetSubOption } = useWidgetSubOption();
const { setFloatingWidget } = useFloatingWidget();
const { widgetSubOption } = useWidgetSubOption();
const { visualizationSocket } = useSocketStore();
const { setSelectedChartId } = useWidgetStore();
@ -99,11 +91,10 @@ const RealTimeVisulization: React.FC = () => {
useEffect(() => {
async function GetZoneData() {
const email = localStorage.getItem("email") || "";
const email = localStorage.getItem("email") ?? "";
const organization = email?.split("@")[1]?.split(".")[0];
try {
const response = await getZone2dData(organization);
// console.log('response: ', response);
if (!Array.isArray(response)) {
return;
@ -125,7 +116,9 @@ const RealTimeVisulization: React.FC = () => {
{}
);
setZonesData(formattedData);
} catch (error) { }
} catch (error) {
console.log(error);
}
}
GetZoneData();
@ -151,12 +144,10 @@ const RealTimeVisulization: React.FC = () => {
});
}, [selectedZone]);
// useEffect(() => {}, [floatingWidgets]);
const handleDrop = async (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault();
try {
const email = localStorage.getItem("email") || "";
const email = localStorage.getItem("email") ?? "";
const organization = email?.split("@")[1]?.split(".")[0];
const data = event.dataTransfer.getData("text/plain");
@ -172,8 +163,8 @@ const RealTimeVisulization: React.FC = () => {
const relativeY = event.clientY - rect.top;
// Widget dimensions
const widgetWidth = droppedData.width || 125;
const widgetHeight = droppedData.height || 100;
const widgetWidth = droppedData.width ?? 125;
const widgetHeight = droppedData.height ?? 100;
// Center the widget at cursor
const centerOffsetX = widgetWidth / 2;
@ -275,7 +266,7 @@ const RealTimeVisulization: React.FC = () => {
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, [setRightClickSelected]);
}, [setRightClickSelected, setRightSelect]);
const [canvasDimensions, setCanvasDimensions] = useState({
width: 0,
@ -340,6 +331,7 @@ const RealTimeVisulization: React.FC = () => {
borderRadius:
isPlaying || activeModule !== "visualization" ? "" : "6px",
}}
role="application"
onDrop={(event) => handleDrop(event)}
onDragOver={(event) => event.preventDefault()}
>

View File

@ -3,9 +3,8 @@ import Dropped3dWidgets from './widgets/3d/Dropped3dWidget'
import ZoneCentreTarget from './zone/zoneCameraTarget'
import ZoneAssets from './zone/zoneAssets'
import MqttEvents from '../../services/factoryBuilder/mqtt/mqttEvents'
import DrieHtmlTemp from './mqttTemp/drieHtmlTemp'
const Visualization = () => {
const Visualization:React.FC = () => {
return (
<>

View File

@ -23,6 +23,9 @@ import SimulationPlayer from "../components/ui/simulation/simulationPlayer";
import RenderOverlay from "../components/templates/Overlay";
import MenuBar from "../components/ui/menu/menu";
import KeyPressListener from "../utils/shortcutkeys/handleShortcutKeys";
import ProductionCapacity from "../components/ui/analysis/ProductionCapacity";
import ThroughputSummary from "../components/ui/analysis/ThroughputSummary";
import ROISummary from "../components/ui/analysis/ROISummary";
const Project: React.FC = () => {
let navigate = useNavigate();
@ -38,7 +41,7 @@ const Project: React.FC = () => {
setFloorItems([]);
setWallItems([]);
setZones([]);
setActiveModule('builder')
setActiveModule("builder");
const email = localStorage.getItem("email");
if (email) {
const Organization = email!.split("@")[1].split(".")[0];
@ -57,6 +60,11 @@ const Project: React.FC = () => {
return (
<div className="project-main">
{/* <div className="analysis">
<ProductionCapacity />
<ThroughputSummary />
<ROISummary />
</div> */}
<KeyPressListener />
{loadingProgress && <LoadingPage progress={loadingProgress} />}
{!isPlaying && (

View File

@ -2,7 +2,11 @@ import React, { useState, FormEvent } from "react";
import { useNavigate } from "react-router-dom";
import { LogoIconLarge } from "../components/icons/Logo";
import { EyeIcon } from "../components/icons/ExportCommonIcons";
import { useLoadingProgress, useOrganization, useUserName } from "../store/store";
import {
useLoadingProgress,
useOrganization,
useUserName,
} from "../store/store";
import { signInApi } from "../services/factoryBuilder/signInSignUp/signInApi";
import { signUpApi } from "../services/factoryBuilder/signInSignUp/signUpApi";
@ -21,7 +25,7 @@ const UserAuth: React.FC = () => {
const handleLogin = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
const organization = (email.split("@")[1]).split(".")[0];
const organization = email.split("@")[1].split(".")[0];
try {
const res = await signInApi(email, password, organization);
@ -39,7 +43,7 @@ const UserAuth: React.FC = () => {
} else if (res.message === "User Not Found!!! Kindly signup...") {
setError("Account not found");
}
} catch (error) { }
} catch (error) {}
};
const handleRegister = async (e: FormEvent) => {
@ -47,7 +51,7 @@ const UserAuth: React.FC = () => {
if (email && password && userName) {
setError("");
try {
const organization = (email.split("@")[1]).split(".")[0];
const organization = email.split("@")[1].split(".")[0];
const res = await signUpApi(userName, email, password, organization);
if (res.message === "New User created") {
@ -56,123 +60,121 @@ const UserAuth: React.FC = () => {
if (res.message === "User already exists") {
setError("User already exists");
}
} catch (error) { }
} catch (error) {}
} else {
setError("Please fill all the fields!");
}
};
return (
<>
<div className="auth-container">
<div className="logo-icon">
<LogoIconLarge />
</div>
<h1>Welcome to Dwinzo</h1>
<p>
{isSignIn ? (
<>
Dont have an account?{" "}
<span
className="link"
onClick={() => setIsSignIn(false)}
style={{ cursor: "pointer" }}
>
Register here!
</span>
</>
) : (
<>
Already have an account?{" "}
<span
className="link"
onClick={() => setIsSignIn(true)}
style={{ cursor: "pointer" }}
>
Login here!
</span>
</>
)}
</p>
<div className="auth-container">
<div className="logo-icon">
<LogoIconLarge />
</div>
<h1>Welcome to Dwinzo</h1>
<p>
{isSignIn ? (
<>
Dont have an account?{" "}
<span
className="link"
onClick={() => setIsSignIn(false)}
style={{ cursor: "pointer" }}
>
Register here!
</span>
</>
) : (
<>
Already have an account?{" "}
<span
className="link"
onClick={() => setIsSignIn(true)}
style={{ cursor: "pointer" }}
>
Login here!
</span>
</>
)}
</p>
<button className="google-login">
<span className="google-icon">G</span> Continue with Google
</button>
<button className="google-login">
<span className="google-icon">G</span> Continue with Google
</button>
{error && <div className="error-message">🛈 {error}</div>}
{error && <div className="error-message">🛈 {error}</div>}
<form
onSubmit={isSignIn ? handleLogin : handleRegister}
className="auth-form"
>
{!isSignIn && (
<input
type="text"
value={userName}
placeholder="Username"
onChange={(e) => setUserName(e.target.value)}
required
/>
)}
<form
onSubmit={isSignIn ? handleLogin : handleRegister}
className="auth-form"
>
{!isSignIn && (
<input
type="email"
name="email"
value={email}
placeholder="Email"
onChange={(e) => setEmail(e.target.value)}
type="text"
value={userName}
placeholder="Username"
onChange={(e) => setUserName(e.target.value)}
required
/>
<div className="password-container">
<input
name="password"
type={showPassword ? "text" : "password"}
value={password}
placeholder="Password"
onChange={(e) => setPassword(e.target.value)}
required
/>
<button
type="button"
className="toggle-password"
onClick={() => setShowPassword(!showPassword)}
>
<EyeIcon isClosed={showPassword} />
</button>
</div>
{!isSignIn && (
<div className="policy-checkbox">
<input type="checkbox" name="" id="" required />
<div className="label">
I have read and agree to the terms of service
</div>
</div>
)}
<button type="submit" className="continue-button">
{isSignIn ? "Continue" : "Register"}
)}
<input
type="email"
name="email"
value={email}
placeholder="Email"
onChange={(e) => setEmail(e.target.value)}
required
/>
<div className="password-container">
<input
name="password"
type={showPassword ? "text" : "password"}
value={password}
placeholder="Password"
onChange={(e) => setPassword(e.target.value)}
required
/>
<button
type="button"
className="toggle-password"
onClick={() => setShowPassword(!showPassword)}
>
<EyeIcon isClosed={showPassword} />
</button>
</form>
<p className="policy">
By signing up for, or logging into, an account, you agree to our{" "}
<span
className="link"
onClick={() => navigate("/privacy")}
style={{ cursor: "pointer" }}
>
privacy policy
</span>{" "}
&{" "}
<span
className="link"
onClick={() => navigate("/terms")}
style={{ cursor: "pointer" }}
>
terms of service
</span>{" "}
whether you read them or not. You can also find these terms on our
website.
</p>
</div>
</>
</div>
{!isSignIn && (
<div className="policy-checkbox">
<input type="checkbox" name="" id="" required />
<div className="label">
I have read and agree to the terms of service
</div>
</div>
)}
<button type="submit" className="continue-button">
{isSignIn ? "Continue" : "Register"}
</button>
</form>
<p className="policy">
By signing up for, or logging into, an account, you agree to our{" "}
<span
className="link"
onClick={() => navigate("/privacy")}
style={{ cursor: "pointer" }}
>
privacy policy
</span>{" "}
&{" "}
<span
className="link"
onClick={() => navigate("/terms")}
style={{ cursor: "pointer" }}
>
terms of service
</span>{" "}
whether you read them or not. You can also find these terms on our
website.
</p>
</div>
);
};

View File

@ -1,6 +0,0 @@
// center a element
%centered {
display: flex;
justify-content: center;
align-items: center;
}

View File

@ -1,123 +1,132 @@
/* ========================================================================
Global SCSS Variables
========================================================================
This file contains the global variables used across the project for
colors, typography, spacing, shadows, and other design tokens.
======================================================================== */
@use "functions";
// ========================================================================
// Font Imports
// ========================================================================
@import url("https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=Josefin+Sans:ital,wght@0,100..700;1,100..700&family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Roboto:ital,wght@0,100..900;1,100..900&display=swap");
// ========================================================================
// Colors
// ========================================================================
// new variables
// Text colors
$text-color: #2b3344; // Primary text color
$text-disabled: #b7b7c6; // Disabled text color
$input-text-color: #595965; // Input field text color
// text colors
// ---------- light mode ----------
$text-color: #2b3344;
$text-disabled: #b7b7c6;
$input-text-color: #595965;
$highlight-text-color: #6f42c1;
$text-color-dark: #f3f3fd; // Primary text color for dark mode
$text-disabled-dark: #6f6f7a; // Disabled text color for dark mode
$input-text-color-dark: #b5b5c8; // Input field text color for dark mode
// ---------- dark mode ----------
$text-color-dark: #f3f3fd;
$text-disabled-dark: #6f6f7a;
$input-text-color-dark: #b5b5c8;
$highlight-text-color-dark: #B392F0;
// Accent colors
$accent-color: #6f42c1; // Primary accent color
$accent-color-dark: #c4abf1; // Primary accent color for dark mode
$highlight-accent-color: #e0dfff; // Highlighted accent for light mode
$highlight-accent-color-dark: #403e6a; // Highlighted accent for dark mode
// background colors
// ---------- light mode ----------
$background-color: linear-gradient(-45deg, #FCFDFDCC 0%, #FCFDFD99 100%);
$background-color-secondary: #FCFDFD4D;
$background-color-accent: #6f42c1;
$background-color-button: #6f42c1;
$background-color-drop-down: #6F42C14D;
$background-color-input: #FFFFFF4D;
$background-color-input-focus: #F2F2F7;
$background-color-drop-down-gradient: linear-gradient(-45deg, #75649366 0%, #40257266 100%);
$background-color-selected: #E0DFFF;
$background-radial-gray-gradient: radial-gradient(circle, #bfe0f8 0%, #e9ebff 46%, #e2acff 100%);
// Background colors
$background-color: #fcfdfd; // Main background color
$background-color-dark: #19191d; // Main background color for dark mode
$background-color-secondary: #e1e0ff80; // Secondary background color
$background-color-secondary-dark: #39394f99; // Secondary background color for dark mode
$background-color-gray: #f3f3f3; // Main background color
$background-color-gray-dark: #232323; // Main background color for dark mode
// ---------- dark mode ----------
$background-color-dark: linear-gradient(-45deg, #333333B3 0%, #2D2437B3 100%);
$background-color-secondary-dark: #19191D99;
$background-color-accent-dark: #6f42c1;
$background-color-button-dark: #6f42c1;
$background-color-drop-down-dark: #50505080;
$background-color-input-dark: #FFFFFF33;
$background-color-input-focus-dark: #333333;
$background-color-drop-down-gradient-dark: linear-gradient(-45deg, #8973B166 0%, #53427366 100%);
$background-color-selected-dark: #403E66;
$background-radial-gray-gradient-dark: radial-gradient(circle, #31373b 0%, #48494b 46%, #52415c 100%);
// Border colors
$border-color: #e0dfff; // Default border color
$border-color-dark: #403e6a; // Border color for dark mode
// border colors
// ---------- light mode ----------
$border-color: #E0DFFF;
$border-color-accent: #6F42C1;
// Shadow color
$shadow-color: #3c3c431a; // Shadow base color for light and dark mode
$shadow-color-dark: #8f8f8f1a; // Shadow base color for light and dark mode
// ---------- dark mode ----------
$border-color-dark: #564B69;
$border-color-accent-dark: #6F42C1;
// Gradients
$acent-gradient-dark: linear-gradient(
90deg,
#b392f0 0%,
#a676ff 100%
); // Dark mode accent gradient
$acent-gradient: linear-gradient(
90deg,
#6f42c1 0%,
#925df3 100%
); // Light mode accent gradient
// highlight colors
// ---------- light mode ----------
$highlight-accent-color: #E0DFFF;
$highlight-secondary-color: #6F42C1;
// ---------- dark mode ----------
$highlight-accent-color-dark: #403E6A;
$highlight-secondary-color-dark: #C4ABF1;
// colors
$color1: #A392CD;
$color2: #7b4cd3;
$color3: #B186FF;
$color4: #8752E8;
$color5: #C7A8FF;
// old variables
$accent-color: #6f42c1;
$accent-color-dark: #c4abf1;
$highlight-accent-color: #e0dfff;
$highlight-accent-color-dark: #403e6a;
$background-color: #fcfdfd;
$background-color-dark: #19191d;
$background-color-secondary: #e1e0ff80;
$background-color-secondary-dark: #39394f99;
$background-color-gray: #f3f3f3;
$background-color-gray-dark: #232323;
$border-color: #e0dfff;
$border-color-dark: #403e6a;
$shadow-color: #3c3c431a;
$shadow-color-dark: #8f8f8f1a;
$acent-gradient-dark: linear-gradient(90deg, #b392f0 0%, #a676ff 100%);
$acent-gradient: linear-gradient(90deg, #6f42c1 0%, #925df3 100%);
$faint-gradient: radial-gradient(circle, #bfe0f8 0%, #e9ebff 46%, #e2acff 100%);
$faint-gradient-dark: radial-gradient(circle, #31373b 0%, #48494b 46%, #52415c 100%);
// ========================================================================
// Typography
// ========================================================================
$font-inter: "Inter", sans-serif;
$font-josefin-sans: "Josefin Sans", sans-serif;
$font-poppins: "Poppins", sans-serif;
$font-roboto: "Roboto", sans-serif;
// Font Family Variables
$font-inter: "Inter", sans-serif; // Inter font
$font-josefin-sans: "Josefin Sans", sans-serif; // Josefin Sans font
$font-poppins: "Poppins", sans-serif; // Poppins font
$font-roboto: "Roboto", sans-serif; // Roboto font
$tiny: 0.625rem;
$small: 0.75rem;
$regular: 0.8rem;
$large: 1rem;
$xlarge: 1.125rem;
$xxlarge: 1.5rem;
$xxxlarge: 2rem;
// Font sizes (converted to rem using a utility function)
$tiny: 0.625rem; // Extra small text (10px)
$small: 0.75rem; // Small text (12px)
$regular: 0.8rem; // Default text size (14px)
$large: 1rem; // Large text size (16px)
$xlarge: 1.125rem; // Extra large text size (18px)
$xxlarge: 1.5rem; // Double extra large text size (24px)
$xxxlarge: 2rem; // Triple extra large text size (32px)
$thin-weight: 300;
$regular-weight: 400;
$medium-weight: 500;
$bold-weight: 600;
// Font weights
$thin-weight: 300; // Regular font weight
$regular-weight: 400; // Regular font weight
$medium-weight: 500; // Medium font weight
$bold-weight: 600; // Bold font weight
$z-index-drei-html: 1;
$z-index-default: 1;
$z-index-marketplace: 2;
$z-index-tools: 3;
$z-index-negative: -1;
$z-index-ui-base: 10;
$z-index-ui-overlay: 20;
$z-index-ui-popup: 30;
$z-index-ui-highest: 50;
// ========================================================================
// Z-Index Levels
// ========================================================================
$box-shadow-light: 0px 2px 4px $shadow-color;
$box-shadow-medium: 0px 4px 8px $shadow-color;
$box-shadow-heavy: 0px 8px 16px $shadow-color;
// Z-index variables for layering
$z-index-drei-html: 1; // For drei's Html components
$z-index-default: 1; // For drei's Html components
$z-index-marketplace: 2; // For drei's Html components
$z-index-tools: 3; // For drei's Html components
$z-index-negative: -1; // For drei's Html components
$z-index-ui-base: 10; // Base UI elements
$z-index-ui-overlay: 20; // Overlay UI elements (e.g., modals, tooltips)
$z-index-ui-popup: 30; // Popups, dialogs, or higher-priority UI elements
$z-index-ui-highest: 50; // Highest priority elements (e.g., notifications, loading screens)
// ========================================================================
// Shadows
// ========================================================================
// Box shadow variables
$box-shadow-light: 0px 2px 4px $shadow-color; // Light shadow
$box-shadow-medium: 0px 4px 8px $shadow-color; // Medium shadow
$box-shadow-heavy: 0px 8px 16px $shadow-color; // Heavy shadow
// ========================================================================
// Border Radius
// ========================================================================
// Border radius variables
$border-radius-small: 4px; // Small rounded corners
$border-radius-medium: 6px; // Medium rounded corners
$border-radius-large: 12px; // Large rounded corners
$border-radius-circle: 50%; // Fully circular
$border-radius-extra-large: 20px; // Extra-large rounded corners
$border-radius-small: 4px;
$border-radius-medium: 6px;
$border-radius-large: 12px;
$border-radius-circle: 50%;
$border-radius-extra-large: 20px;

View File

@ -0,0 +1,5 @@
section, .section{
padding: 12px;
outline: 1px solid var(--border-color);
border-radius: 16px;
}

View File

@ -12,3 +12,10 @@ 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;
}

View File

@ -0,0 +1,270 @@
.analysis {
position: absolute;
top: 0;
left: 0;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100vh;
z-index: 100000000000000000000000000000;
}
.analysis-card {
min-width: 333px;
// background: var(--primary-color);
border-radius: 20px;
padding: 8px;
.analysis-card-wrapper {
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;
height: 4px;
border-radius: 13px;
overflow: hidden;
background-color: #FBEBD7;
.bar-fill {
position: absolute;
height: 100%;
top: 0;
left: 0;
background-color: #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-color: 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-color: #F3C64D;
}
&:nth-child(2) {
background-color: #67B3F4;
}
&:nth-child(3) {
background-color: #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-color: #F3C64D;
}
}
&:nth-child(2) {
.indicator {
background-color: #67B3F4;
}
}
&:nth-child(3) {
.indicator {
background-color: #7981F5;
}
}
label {
font-size: var(--font-size-small);
position: relative;
}
.indicator {
display: inline-block;
width: 5px;
height: 5px;
border-radius: 50%;
}
}
}
}
}
}
}
}

View File

@ -366,15 +366,66 @@
min-height: 50vh;
padding-bottom: 12px;
position: relative;
display: flex;
flex-direction: column;
overflow: auto;
.sidebar-right-content-container {
border-bottom: 1px solid var(--border-color);
// flex: 1;
height: calc(100% - 36px);
position: relative;
overflow: auto;
width: 320px;
.no-event-selected {
color: #666;
padding: 1.8rem 1rem;
grid-column: 1 / -1;
.products-list {
padding-top: 1rem;
.products-list-title {
text-align: start;
color: var(--accent-color);
font-size: var(--font-size-regular);
}
ul {
li {
text-align: start;
margin: 8px 0;
padding: 2px 0;
text-decoration: none;
&::marker {
content: "";
}
button {
width: fit-content;
position: relative;
transition: all 0.2s ease;
@include flex-center;
gap: 4px;
&:before {
content: "";
position: absolute;
left: 0;
bottom: -4px;
background: var(--accent-color);
height: 1px;
width: 0%;
transition: all 0.3s ease;
}
}
&:hover {
button {
path {
stroke: var(--accent-color);
stroke-width: 1.5px;
}
color: var(--accent-color);
&:before {
width: 100%;
}
}
}
}
}
}
}
}
}
@ -675,10 +726,14 @@
color: var(--primary-color);
border-radius: #{$border-radius-small};
cursor: pointer;
outline: none;
border: none;
path {
stroke: var(--primary-color);
}
&:disabled {
background-color: var(--text-disabled);
}
}
}
@ -706,7 +761,7 @@
}
.selected-actions-list {
margin-bottom: 8px;
.eye-dropper-input-container{
.eye-dropper-input-container {
padding: 6px 12px;
.regularDropdown-container {
padding: 5px 8px;
@ -747,19 +802,21 @@
width: 100%;
margin: 2px 0;
border-radius: #{$border-radius-small};
}
.value {
display: flex;
justify-content: flex-start;
align-items: center;
min-width: 80%;
gap: 6px;
.input-value {
text-align: start;
}
.value {
display: flex;
justify-content: flex-start;
align-items: center;
min-width: 80%;
gap: 6px;
input {
width: fit-content;
outline: none;
accent-color: var(--accent-color);
input {
width: fit-content;
outline: none;
accent-color: var(--accent-color);
}
}
}
@ -797,6 +854,7 @@
@include flex-center;
padding: 4px;
cursor: grab;
width: 100%;
&:active {
cursor: grabbing;

View File

@ -1,12 +1,12 @@
// abstracts
@use 'abstracts/variables';
@use 'abstracts/mixins';
@use 'abstracts/placeholders';
@use 'abstracts/functions';
// base
@use 'base/reset';
@use 'base/typography';
@use 'base/global';
@use 'base/base';
// components
@ -25,6 +25,7 @@
@use 'components/simulation/simulation';
@use 'components/menu/menu';
@use 'components/confirmationPopUp';
@use 'components/analysis/analysis';
// layout
@use 'layout/loading';

View File

@ -776,13 +776,13 @@
border-radius: 6px;
overflow: hidden;
padding: 4px;
min-width: 150px;
.option {
padding: 4px 10px;
border-radius: #{$border-radius-small};
color: var(--text-color);
text-wrap: nowrap;
cursor: pointer;
&:hover {
@ -794,8 +794,8 @@
color: #f65648;
&:hover {
background-color: #f65648;
color: white;
background-color: #f657484d;
color: #f65648;
}
}
}

View File

@ -44,8 +44,8 @@ interface VehiclePointSchema {
actionType: "travel";
unLoadDuration: number;
loadCapacity: number;
pickUpPoint: { x: number; y: number, z: number } | null;
unLoadPoint: { x: number; y: number, z: number } | null;
pickUpPoint: { position: { x: number; y: number, z: number }, rotation: { x: number; y: number, z: number } } | null;
unLoadPoint: { position: { x: number; y: number, z: number }, rotation: { x: number; y: number, z: number } } | null;
triggers: TriggerSchema[];
};
}
@ -134,7 +134,7 @@ interface ConveyorStatus extends ConveyorEventSchema {
isActive: boolean;
idleTime: number;
activeTime: number;
}
interface MachineStatus extends MachineEventSchema {