diff --git a/app/src/assets/gltf-glb/arrow_green.glb b/app/src/assets/gltf-glb/arrow_green.glb new file mode 100644 index 0000000..8e02527 Binary files /dev/null and b/app/src/assets/gltf-glb/arrow_green.glb differ diff --git a/app/src/assets/gltf-glb/arrow_red.glb b/app/src/assets/gltf-glb/arrow_red.glb new file mode 100644 index 0000000..02bfea4 Binary files /dev/null and b/app/src/assets/gltf-glb/arrow_red.glb differ diff --git a/app/src/components/icons/ContextMenuIcons.tsx b/app/src/components/icons/ContextMenuIcons.tsx index a1ced1b..f48cf3b 100644 --- a/app/src/components/icons/ContextMenuIcons.tsx +++ b/app/src/components/icons/ContextMenuIcons.tsx @@ -9,31 +9,31 @@ export function FlipXAxisIcon() { > @@ -42,44 +42,46 @@ export function FlipXAxisIcon() { } export function FlipYAxisIcon() { - - - - - - - ; + return ( + + + + + + + + ); } export function FlipZAxisIcon() { return ( @@ -93,31 +95,31 @@ export function FlipZAxisIcon() { ); diff --git a/app/src/components/icons/DashboardIcon.tsx b/app/src/components/icons/DashboardIcon.tsx index f1f4d7d..314a3c8 100644 --- a/app/src/components/icons/DashboardIcon.tsx +++ b/app/src/components/icons/DashboardIcon.tsx @@ -9,13 +9,13 @@ export function NotificationIcon() { > @@ -34,7 +34,7 @@ export function HomeIcon() { > ); @@ -51,7 +51,7 @@ export function ProjectsIcon() { > ); @@ -70,103 +70,103 @@ export function TutorialsIcon() { cx="8.157" cy="8.35866" r="6.17928" - stroke="var(--text-color)" + stroke="var(--icon-default-color)" strokeWidth="0.562865" /> @@ -184,12 +184,12 @@ export function DocumentationIcon() { > @@ -208,7 +208,7 @@ export function HelpIcon() { @@ -236,17 +236,17 @@ export function LogoutIcon() { > diff --git a/app/src/components/icons/ExportCommonIcons.tsx b/app/src/components/icons/ExportCommonIcons.tsx index 6e73c0e..c78261e 100644 --- a/app/src/components/icons/ExportCommonIcons.tsx +++ b/app/src/components/icons/ExportCommonIcons.tsx @@ -9,7 +9,7 @@ export function SearchIcon() { > ); @@ -24,7 +24,7 @@ export function ArrowIcon() { fill="none" xmlns="http://www.w3.org/2000/svg" > - + ); } @@ -40,14 +40,14 @@ export function FocusIcon() { > @@ -65,7 +65,7 @@ export function LockIcon({ isLocked }: { isLocked: boolean }) { > @@ -80,7 +80,7 @@ export function LockIcon({ isLocked }: { isLocked: boolean }) { > @@ -100,7 +100,7 @@ export function EyeIcon({ isClosed }: { isClosed: boolean }) { @@ -119,13 +119,13 @@ export function EyeIcon({ isClosed }: { isClosed: boolean }) { > @@ -142,9 +142,9 @@ export function KebebIcon() { fill="none" xmlns="http://www.w3.org/2000/svg" > - - - + + + ); } @@ -160,7 +160,7 @@ export function AddIcon() { > @@ -168,20 +168,6 @@ export function AddIcon() { ); } -export function RmoveIcon() { - return ( - - - - ); -} - export function CloseIcon() { return ( @@ -217,11 +203,11 @@ export function SettingsIcon() { @@ -245,7 +231,7 @@ export function HelpIcon() { @@ -269,7 +255,7 @@ export function TrashIcon() { @@ -294,32 +280,32 @@ export function FilterIcon() { > @@ -338,7 +324,7 @@ export function EyeDroperIcon({ isActive }: { isActive: boolean }) { @@ -401,7 +387,7 @@ export function UndoIcon() { fillRule="evenodd" clipRule="evenodd" d="M3.76516 1.73483C3.91161 1.88128 3.91161 2.11872 3.76516 2.26516L2.90533 3.125H7.5C9.0878 3.125 10.375 4.41218 10.375 6C10.375 7.5878 9.0878 8.875 7.5 8.875H4C3.79289 8.875 3.625 8.7071 3.625 8.5C3.625 8.2929 3.79289 8.125 4 8.125H7.5C8.6736 8.125 9.625 7.1736 9.625 6C9.625 4.82639 8.6736 3.875 7.5 3.875H2.90533L3.76516 4.73483C3.91161 4.88128 3.91161 5.1187 3.76516 5.26515C3.61872 5.4116 3.38128 5.4116 3.23483 5.26515L1.73483 3.76516C1.58839 3.61872 1.58839 3.38128 1.73483 3.23483L3.23483 1.73483C3.38128 1.58839 3.61872 1.58839 3.76516 1.73483Z" - fill="var(--text-color)" + fill="var(--icon-default-color)" /> ); @@ -420,7 +406,7 @@ export function RedoIcon() { fillRule="evenodd" clipRule="evenodd" d="M8.23484 1.73483C8.08839 1.88128 8.08839 2.11872 8.23484 2.26516L9.09467 3.125H4.5C2.9122 3.125 1.625 4.41218 1.625 6C1.625 7.5878 2.9122 8.875 4.5 8.875H8C8.20711 8.875 8.375 8.7071 8.375 8.5C8.375 8.2929 8.20711 8.125 8 8.125H4.5C3.3264 8.125 2.375 7.1736 2.375 6C2.375 4.82639 3.3264 3.875 4.5 3.875H9.09467L8.23484 4.73483C8.08839 4.88128 8.08839 5.1187 8.23484 5.26515C8.38128 5.4116 8.61872 5.4116 8.76517 5.26515L10.2652 3.76516C10.4116 3.61872 10.4116 3.38128 10.2652 3.23483L8.76517 1.73483C8.61872 1.58839 8.38128 1.58839 8.23484 1.73483Z" - fill="var(--text-color)" + fill="var(--icon-default-color)" /> ); @@ -456,7 +442,7 @@ export function RemoveIcon() { fill="none" xmlns="http://www.w3.org/2000/svg" > - + ); } @@ -472,11 +458,11 @@ export function InfoIcon() { > ); @@ -526,21 +512,21 @@ export const KebabIcon = () => { cy="1.35112" rx="1.4993" ry="1.27477" - fill="var(--text-color)" + fill="var(--icon-default-color)" /> ); @@ -559,7 +545,7 @@ export const DublicateIcon = () => { fillRule="evenodd" clipRule="evenodd" d="M14.3125 11.375C14.3125 11.7545 14.0045 12.0625 13.625 12.0625H8.125C7.7455 12.0625 7.4375 11.7545 7.4375 11.375V5.875C7.4375 5.4955 7.7455 5.1875 8.125 5.1875H13.625C14.0045 5.1875 14.3125 5.4955 14.3125 5.875V11.375ZM13.625 4.5H8.125C7.36566 4.5 6.75 5.11566 6.75 5.875V11.375C6.75 12.1343 7.36566 12.75 8.125 12.75H13.625C14.3843 12.75 15 12.1343 15 11.375V5.875C15 5.11566 14.3843 4.5 13.625 4.5ZM11.5625 14.125C11.5625 14.5045 11.2545 14.8125 10.875 14.8125H5.375C4.9955 14.8125 4.6875 14.5045 4.6875 14.125V8.625C4.6875 8.2455 4.9955 7.9375 5.375 7.9375H6.0625V7.25H5.375C4.61566 7.25 4 7.86566 4 8.625V14.125C4 14.8843 4.61566 15.5 5.375 15.5H10.875C11.6343 15.5 12.25 14.8843 12.25 14.125V13.4375H11.5625V14.125Z" - fill="var(--text-color)" + fill="var(--icon-default-color)" /> ); @@ -576,34 +562,243 @@ export const DeleteIcon = () => { > ); }; + +export const HourlySimulationIcon = () => { + return ( + + + + ); +}; + +export const DailyProductionIcon = () => { + return ( + + + + + + ); +}; + +export const MonthlyROI = () => { + return ( + + + + ); +}; + +export const ExpandIcon = () => { + return ( + + + + + + ); +}; + +export const StartIcon = () => { + return ( + + + + + + + ); +}; + +export const EndIcon = () => { + return ( + + + + + + + ); +}; + +export const SpeedIcon = () => { + return ( + + + + ); +}; +// export const DublicateIcon = () => { +// return ( +// +// +// +// ); +// }; diff --git a/app/src/components/icons/ExportModuleIcons.tsx b/app/src/components/icons/ExportModuleIcons.tsx index fb2f566..1dda323 100644 --- a/app/src/components/icons/ExportModuleIcons.tsx +++ b/app/src/components/icons/ExportModuleIcons.tsx @@ -10,8 +10,8 @@ export function BuilderIcon({ isActive }: { isActive: boolean }) { > @@ -29,41 +29,41 @@ export function SimulationIcon({ isActive }: { isActive: boolean }) { > @@ -81,19 +81,19 @@ export function VisualizationIcon({ isActive }: { isActive: boolean }) { > @@ -112,20 +112,20 @@ export function CartIcon({ isActive }: { isActive: boolean }) { > diff --git a/app/src/components/icons/ExportToolsIcons.tsx b/app/src/components/icons/ExportToolsIcons.tsx index 478113c..4dc7a5b 100644 --- a/app/src/components/icons/ExportToolsIcons.tsx +++ b/app/src/components/icons/ExportToolsIcons.tsx @@ -11,13 +11,13 @@ export function ZoneIcon({ isActive }: { isActive: boolean }) { @@ -41,27 +41,27 @@ export function AsileIcon({ isActive }: { isActive: boolean }) { > ); @@ -87,36 +87,36 @@ export function FloorIcon({ isActive }: { isActive: boolean }) { ); @@ -134,7 +134,7 @@ export function WallIcon({ isActive }: { isActive: boolean }) { @@ -286,7 +286,7 @@ export function WindowIcon({ isActive }: { isActive: boolean }) { > @@ -421,7 +421,7 @@ export function CommentIcon({ isActive }: { isActive: boolean }) { > ) : ( @@ -502,17 +502,17 @@ export function DeleteIcon({ isActive }: { isActive: boolean }) { > @@ -552,7 +552,7 @@ export function CursorIcon({ isActive }: { isActive: boolean }) { > ) : ( @@ -585,7 +585,7 @@ export function PlayIcon({ isActive }: { isActive: boolean }) { ); @@ -603,7 +603,7 @@ export function PenIcon({ isActive }: { isActive: boolean }) { @@ -623,13 +623,13 @@ export function SaveTemplateIcon({ isActive }: { isActive: boolean }) { @@ -648,19 +648,19 @@ export function MeasureToolIcon({ isActive }: { isActive: boolean }) { > ); diff --git a/app/src/components/icons/HeaderIcons.tsx b/app/src/components/icons/HeaderIcons.tsx index 81f3690..ef02f02 100644 --- a/app/src/components/icons/HeaderIcons.tsx +++ b/app/src/components/icons/HeaderIcons.tsx @@ -65,15 +65,15 @@ export function AppDockIcon() { fill="none" xmlns="http://www.w3.org/2000/svg" > - - - - - - - - - + + + + + + + + + ); } diff --git a/app/src/components/icons/Logo.tsx b/app/src/components/icons/Logo.tsx index 06ed750..822e83e 100644 --- a/app/src/components/icons/Logo.tsx +++ b/app/src/components/icons/Logo.tsx @@ -7,108 +7,108 @@ export function LogoIcon() { fill="none" xmlns="http://www.w3.org/2000/svg" > - + @@ -124,7 +124,6 @@ export function LogoIconLarge() { fill="none" xmlns="http://www.w3.org/2000/svg" > - diff --git a/app/src/components/icons/SimulationIcons.tsx b/app/src/components/icons/SimulationIcons.tsx index 4879ea4..b4aa881 100644 --- a/app/src/components/icons/SimulationIcons.tsx +++ b/app/src/components/icons/SimulationIcons.tsx @@ -9,13 +9,13 @@ export function AnalysisIcon({ isActive }: { isActive: boolean }) { > @@ -34,11 +34,11 @@ export function MechanicsIcon({ isActive }: { isActive: boolean }) { > ); @@ -55,15 +55,15 @@ export function PropertiesIcon({ isActive }: { isActive: boolean }) { > ); @@ -82,13 +82,13 @@ export function SimulationIcon({ isActive }: { isActive: boolean }) { fillRule="evenodd" clipRule="evenodd" d="M6.44104 7.04762C6.57815 6.98413 6.73614 6.98413 6.87325 7.04762L12.0161 9.42958C12.198 9.51377 12.3143 9.69589 12.3143 9.89624V15.8512C12.3143 16.0347 12.2165 16.2043 12.0577 16.2962L6.9148 19.2736C6.75547 19.3659 6.55881 19.3659 6.39949 19.2736L1.25661 16.2962C1.09779 16.2043 1 16.0347 1 15.8512V9.89624C1 9.69589 1.11635 9.51377 1.29815 9.42958L6.44104 7.04762ZM2.02857 10.7297L6.14286 12.794V17.9366L2.02857 15.5546V10.7297ZM7.17143 17.9366L11.2857 15.5546V10.7297L7.17143 12.794V17.9366ZM6.65714 11.9013L10.6163 9.91477L6.65714 8.08106L2.69798 9.91477L6.65714 11.9013Z" - fill={isActive ? "var(--primary-color)" : "var(--text-color)"} + fill={"var(--icon-default-color-active)"} /> ); @@ -107,21 +107,21 @@ export function ResetIcon() { > @@ -140,7 +140,7 @@ export function PlayStopIcon() { > @@ -159,7 +159,7 @@ export function ExitIcon() { > diff --git a/app/src/components/icons/analysis.tsx b/app/src/components/icons/analysis.tsx index 3a5542b..d081a86 100644 --- a/app/src/components/icons/analysis.tsx +++ b/app/src/components/icons/analysis.tsx @@ -9,8 +9,8 @@ export function ThroughputSummaryIcon() { > @@ -28,8 +28,8 @@ export function ProductionCapacityIcon() { > @@ -49,12 +49,12 @@ export function ROISummaryIcon() { ); diff --git a/app/src/components/icons/marketPlaceIcons.tsx b/app/src/components/icons/marketPlaceIcons.tsx index e69303f..6952ae1 100644 --- a/app/src/components/icons/marketPlaceIcons.tsx +++ b/app/src/components/icons/marketPlaceIcons.tsx @@ -29,13 +29,13 @@ export function DownloadIcon() { fillRule="evenodd" clipRule="evenodd" d="M2.5 11.875C2.84518 11.875 3.125 12.1548 3.125 12.5C3.125 13.6962 3.12633 14.5304 3.21096 15.1599C3.29317 15.7714 3.44354 16.0952 3.67418 16.3258C3.90481 16.5565 4.22863 16.7068 4.8401 16.7891C5.46956 16.8737 6.30383 16.875 7.5 16.875H12.5C13.6962 16.875 14.5304 16.8737 15.1599 16.7891C15.7714 16.7068 16.0952 16.5565 16.3258 16.3258C16.5565 16.0952 16.7068 15.7714 16.7891 15.1599C16.8737 14.5304 16.875 13.6962 16.875 12.5C16.875 12.1548 17.1548 11.875 17.5 11.875C17.8452 11.875 18.125 12.1548 18.125 12.5V12.5458C18.125 13.6854 18.125 14.604 18.0279 15.3265C17.9271 16.0766 17.7113 16.7081 17.2097 17.2097C16.7081 17.7113 16.0766 17.9271 15.3265 18.0279C14.604 18.125 13.6854 18.125 12.5458 18.125H7.45428C6.31462 18.125 5.39602 18.125 4.67354 18.0279C3.92345 17.9271 3.29189 17.7113 2.79029 17.2097C2.28869 16.7081 2.07295 16.0766 1.9721 15.3265C1.87497 14.604 1.87498 13.6854 1.875 12.5458C1.875 12.5305 1.875 12.5152 1.875 12.5C1.875 12.1548 2.15483 11.875 2.5 11.875Z" - fill="var(--primary-color)" + fill="var(--icon-default-color-active)" /> ); @@ -52,13 +52,13 @@ export function EyeIconBig() { > @@ -80,7 +80,7 @@ export function CommentsIcon() { fillRule="evenodd" clipRule="evenodd" d="M8 13C7.416 13 6.852 12.932 6.31 12.8165L3.956 14.2315L3.9875 11.912C2.183 10.827 1 9.033 1 7C1 3.6865 4.134 1 8 1C11.866 1 15 3.6865 15 7C15 10.314 11.866 13 8 13ZM8 0C3.582 0 0 3.1345 0 7C0 9.2095 1.1725 11.177 3 12.4595V16L6.5045 13.8735C6.9895 13.9535 7.4885 14 8 14C12.418 14 16 10.866 16 7C16 3.1345 12.418 0 8 0ZM11.5 5.5H4.5C4.224 5.5 4 5.724 4 6C4 6.2765 4.224 6.5 4.5 6.5H11.5C11.776 6.5 12 6.2765 12 6C12 5.724 11.776 5.5 11.5 5.5ZM10.5 8.5H5.5C5.224 8.5 5 8.7235 5 9C5 9.2765 5.224 9.5 5.5 9.5H10.5C10.776 9.5 11 9.2765 11 9C11 8.7235 10.776 8.5 10.5 8.5Z" - fill="var(--text-color)" + fill="var(--icon-default-color)" /> diff --git a/app/src/components/layout/Dashboard/MarketPlaceBanner.tsx b/app/src/components/layout/Dashboard/MarketPlaceBanner.tsx index b89bfb2..ed22a9a 100644 --- a/app/src/components/layout/Dashboard/MarketPlaceBanner.tsx +++ b/app/src/components/layout/Dashboard/MarketPlaceBanner.tsx @@ -23,16 +23,16 @@ const MarketPlaceBanner = () => { diff --git a/app/src/components/layout/Dashboard/SidePannel.tsx b/app/src/components/layout/Dashboard/SidePannel.tsx index d44c72c..5d69ffc 100644 --- a/app/src/components/layout/Dashboard/SidePannel.tsx +++ b/app/src/components/layout/Dashboard/SidePannel.tsx @@ -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 (
diff --git a/app/src/components/layout/confirmationPopup/ConfirmationPopup.tsx b/app/src/components/layout/confirmationPopup/ConfirmationPopup.tsx index 078f27d..4774bad 100644 --- a/app/src/components/layout/confirmationPopup/ConfirmationPopup.tsx +++ b/app/src/components/layout/confirmationPopup/ConfirmationPopup.tsx @@ -17,12 +17,12 @@ const ConfirmationPopup: React.FC = ({

{message}

-
+
-
+ +
+
diff --git a/app/src/components/layout/sidebarLeft/Outline.tsx b/app/src/components/layout/sidebarLeft/Outline.tsx index b7d5803..417f069 100644 --- a/app/src/components/layout/sidebarLeft/Outline.tsx +++ b/app/src/components/layout/sidebarLeft/Outline.tsx @@ -24,22 +24,26 @@ const Outline: React.FC = () => {
) : (
- - +
+ +
+
+ +
)} diff --git a/app/src/components/layout/sidebarLeft/visualization/widgets/ChartComponent.tsx b/app/src/components/layout/sidebarLeft/visualization/widgets/ChartComponent.tsx index 9d09291..0e10cf1 100644 --- a/app/src/components/layout/sidebarLeft/visualization/widgets/ChartComponent.tsx +++ b/app/src/components/layout/sidebarLeft/visualization/widgets/ChartComponent.tsx @@ -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(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", diff --git a/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets.tsx b/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets.tsx index d4a64ae..655e641 100644 --- a/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets.tsx +++ b/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets.tsx @@ -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) => { diff --git a/app/src/components/layout/sidebarLeft/visualization/widgets/WidgetsFloating.tsx b/app/src/components/layout/sidebarLeft/visualization/widgets/WidgetsFloating.tsx index b5ae0bb..4c085aa 100644 --- a/app/src/components/layout/sidebarLeft/visualization/widgets/WidgetsFloating.tsx +++ b/app/src/components/layout/sidebarLeft/visualization/widgets/WidgetsFloating.tsx @@ -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([ - // { 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, - widget: Widget - ) => { - e.dataTransfer.setData("application/json", JSON.stringify(widget)); - }; - return (
- {/* {widgets.map((widget) => ( -
handleDragStart(e, widget)} - > - {widget.name} -
- ))} */} {/* Floating 1 */} { const { activeUsers } = useActiveUsers(); - const userName = localStorage.getItem("userName") || "Anonymous"; + const userName = localStorage.getItem("userName") ?? "Anonymous"; const guestUsers: ActiveUser[] = activeUsers.filter( (user: ActiveUser) => user.userName !== userName @@ -23,14 +23,14 @@ const Header: React.FC = () => { )}
-
{ setUserManagement(true); }} > Share -
+ {/*
*/} diff --git a/app/src/components/layout/sidebarRight/SideBarRight.tsx b/app/src/components/layout/sidebarRight/SideBarRight.tsx index 38f0b18..b2f25eb 100644 --- a/app/src/components/layout/sidebarRight/SideBarRight.tsx +++ b/app/src/components/layout/sidebarRight/SideBarRight.tsx @@ -1,148 +1,164 @@ 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"); + } + } + if (activeModule !== "simulation") { + setSubModule("properties"); + } + }, [activeModule, selectedEventData, selectedEventSphere, setSubModule]); - return ( -
-
- {toggleUI && ( -
-
setSubModule("properties")} - > - -
- {activeModule === "simulation" && ( - <> -
setSubModule("mechanics")} - > - -
-
setSubModule("simulations")} - > - -
-
setSubModule("analysis")} - > - -
- - )} -
- )} - {/* process builder */} - {toggleUI && - subModule === "properties" && - activeModule !== "visualization" && - !selectedFloorItem && ( -
-
- -
-
- )} - {toggleUI && - subModule === "properties" && - activeModule !== "visualization" && - selectedFloorItem && ( -
-
- -
-
- )} - {toggleUI && - subModule === "zoneProperties" && - (activeModule === "builder" || activeModule === "simulation") && ( -
-
- -
-
- )} - {/* simulation */} - {toggleUI && activeModule === "simulation" && ( - <> - {subModule === "simulations" && ( -
-
- -
-
- )} - {subModule === "mechanics" && ( -
-
- -
-
- )} - {subModule === "analysis" && ( -
-
- -
-
- )} - - )} - {/* realtime visualization */} - {toggleUI && activeModule === "visualization" && } + return ( +
+
+ {toggleUI && ( +
+ {activeModule !== "simulation" && ( + + )} + {activeModule === "simulation" && ( + <> + + + + + )}
- ); + )} + {/* process builder */} + {toggleUI && + subModule === "properties" && + activeModule !== "visualization" && + !selectedFloorItem && ( +
+
+ +
+
+ )} + {toggleUI && + subModule === "properties" && + activeModule !== "visualization" && + selectedFloorItem && ( +
+
+ +
+
+ )} + {toggleUI && + subModule === "zoneProperties" && + (activeModule === "builder" || activeModule === "simulation") && ( +
+
+ +
+
+ )} + {/* simulation */} + {toggleUI && activeModule === "simulation" && ( + <> + {subModule === "simulations" && ( +
+
+ +
+
+ )} + {subModule === "mechanics" && ( +
+
+ +
+
+ )} + {subModule === "analysis" && ( +
+
+ +
+
+ )} + + )} + {/* realtime visualization */} + {toggleUI && activeModule === "visualization" && } +
+ ); }; -export default SideBarRight; \ No newline at end of file +export default SideBarRight; diff --git a/app/src/components/layout/sidebarRight/analysis/Analysis.tsx b/app/src/components/layout/sidebarRight/analysis/Analysis.tsx index d806740..9d2889f 100644 --- a/app/src/components/layout/sidebarRight/analysis/Analysis.tsx +++ b/app/src/components/layout/sidebarRight/analysis/Analysis.tsx @@ -100,7 +100,7 @@ const Analysis: React.FC = () => {
Generate Report
-
+
Create Analysis
diff --git a/app/src/components/layout/sidebarRight/customInput/PositionInputs.tsx b/app/src/components/layout/sidebarRight/customInput/PositionInputs.tsx index 01b4ab3..8cc47d7 100644 --- a/app/src/components/layout/sidebarRight/customInput/PositionInputs.tsx +++ b/app/src/components/layout/sidebarRight/customInput/PositionInputs.tsx @@ -22,7 +22,7 @@ const PositionInput: React.FC = ({ value2 = "number", disabled = false, // Default disabled value isEyedrop = false, // Default isEyedrop value - handleEyeDropClick = () => { }, // Default function for eye drop click + handleEyeDropClick = () => {}, // Default function for eye drop click }) => { return (
@@ -39,6 +39,7 @@ const PositionInput: React.FC = ({ disabled={disabled} // Apply disabled prop />
+
Y :
{
{/* Name */}
{selectedFloorItem.userData.name}
+
+ {}} + value1={selectedFloorItem.position.x.toFixed(5)} + value2={selectedFloorItem.position.z.toFixed(5)} + /> + {}} + value={parseFloat( + THREE.MathUtils.radToDeg(selectedFloorItem.rotation.y).toFixed(5) + )} + /> +
- {}} - value1={selectedFloorItem.position.x.toFixed(5)} - value2={selectedFloorItem.position.z.toFixed(5)} - /> - {}} - value={parseFloat( - THREE.MathUtils.radToDeg(selectedFloorItem.rotation.y).toFixed(5) - )} - /> +
+
Render settings
+ + +
-
- - - - -
User Data
- - {/* Map through userData and render InputWithDropDown for each entry */} - {userData.map((data) => ( -
- handleUserDataChange(data.id, newValue)} // Pass the change handler - /> -
handleRemoveUserData(data.id)} - > - +
+
User Data
+ {userData.map((data) => ( +
+ handleUserDataChange(data.id, newValue)} // Pass the change handler + /> +
handleRemoveUserData(data.id)} + > + +
-
- ))} + ))} - {/* Add new user data */} -
- + Add -
+ {/* Add new user data */} +
+ + Add +
+
); }; diff --git a/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx b/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx index 568783c..8b91afc 100644 --- a/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx @@ -32,9 +32,7 @@ const GlobalProperties: React.FC = () => { const { renderDistance, setRenderDistance } = useRenderDistance(); const { setPlaneValue, setGridValue, planeValue, gridValue } = useTileDistance(); - useEffect(() => { - - }, [gridValue, planeValue]); + useEffect(() => {}, [gridValue, planeValue]); const { socket } = useSocketStore(); const { limitDistance, setLimitDistance } = useLimitDistance(); const [distance, setDistance] = useState(40); @@ -224,66 +222,66 @@ const GlobalProperties: React.FC = () => { // } return (
-
Environment
-
- - Optimize -
+
+
Environment
+
+ + Optimize +
-
+
- - - - + + + + -
- {/* //visibleEdgeColor={CONSTANTS.outlineConfig.assetDeleteColor} */} - { - // setLimitDistance(!limitDistance); - // // setDistance(75); - // // setRenderDistance(75); - // }} - onClick={async () => { - await limitRenderDistance(); // Call the function here - }} - /> - updateDistance(value)} - onPointerUp={updatedDist} - key={"6"} - /> - -
+
+ {/* //visibleEdgeColor={CONSTANTS.outlineConfig.assetDeleteColor} */} + { + // setLimitDistance(!limitDistance); + // // setDistance(75); + // // setRenderDistance(75); + // }} + onClick={async () => { + await limitRenderDistance(); // Call the function here + }} + /> + updateDistance(value)} + onPointerUp={updatedDist} + key={"6"} + /> + {/*
{ max={5} onChange={(value: number) => updateGridDistance(value)} onPointerUp={updatedGrid} - /> + /> */} +
); }; diff --git a/app/src/components/layout/sidebarRight/properties/ZoneProperties.tsx b/app/src/components/layout/sidebarRight/properties/ZoneProperties.tsx index a9f8cc0..1bb9400 100644 --- a/app/src/components/layout/sidebarRight/properties/ZoneProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/ZoneProperties.tsx @@ -2,7 +2,12 @@ import React, { useEffect, useState } from "react"; import RenameInput from "../../../ui/inputs/RenameInput"; import Vector3Input from "../customInput/Vector3Input"; import { useSelectedZoneStore } from "../../../../store/visualization/useZoneStore"; -import { useEditPosition, usezonePosition, useZones, usezoneTarget } from "../../../../store/store"; +import { + useEditPosition, + usezonePosition, + useZones, + usezoneTarget, +} from "../../../../store/store"; import { zoneCameraUpdate } from "../../../../services/visulization/zone/zoneCameraUpdation"; const ZoneProperties: React.FC = () => { @@ -13,9 +18,9 @@ const ZoneProperties: React.FC = () => { const { zones, setZones } = useZones(); useEffect(() => { - setZonePosition(selectedZone.zoneViewPortPosition) - setZoneTarget(selectedZone.zoneViewPortTarget) - }, [selectedZone?.zoneViewPortPosition, selectedZone?.zoneViewPortTarget]) + setZonePosition(selectedZone.zoneViewPortPosition); + setZoneTarget(selectedZone.zoneViewPortTarget); + }, [selectedZone?.zoneViewPortPosition, selectedZone?.zoneViewPortTarget]); async function handleSetView() { try { @@ -25,7 +30,7 @@ const ZoneProperties: React.FC = () => { let zonesdata = { zoneId: selectedZone.zoneId, viewPortposition: zonePosition, - viewPortCenter: zoneTarget + viewPortCenter: zoneTarget, }; let response = await zoneCameraUpdate(zonesdata, organization); @@ -34,14 +39,11 @@ const ZoneProperties: React.FC = () => { } else { // console.log(response); } - - } catch (error) { - - } + } catch (error) {} } function handleEditView() { - setEdit(!Edit); // This will toggle the `Edit` state correctly + setEdit(!Edit); // This will toggle the `Edit` state correctly } async function handleZoneNameChange(newName: string) { @@ -49,11 +51,11 @@ const ZoneProperties: React.FC = () => { const organization = email?.split("@")[1]?.split(".")[0]; const zonesdata = { zoneId: selectedZone.zoneId, - zoneName: newName + zoneName: newName, }; // Call your API to update the zone let response = await zoneCameraUpdate(zonesdata, organization); - console.log('response: ', response); + console.log("response: ", response); if (response.message === "updated successfully") { setZones((prevZones: any[]) => prevZones.map((zone) => @@ -66,7 +68,10 @@ const ZoneProperties: React.FC = () => { // console.log(response?.message); } } - function handleVectorChange(key: "zoneViewPortTarget" | "zoneViewPortPosition", newValue: [number, number, number]) { + function handleVectorChange( + key: "zoneViewPortTarget" | "zoneViewPortPosition", + newValue: [number, number, number] + ) { setSelectedZone((prev) => ({ ...prev, [key]: newValue })); } const checkZoneNameDuplicate = (name: string) => { @@ -79,33 +84,40 @@ const ZoneProperties: React.FC = () => { return (
-
- -
- {Edit ? "Cancel" : "Edit"} +
+
+ +
+ {Edit ? "Cancel" : "Edit"} +
-
- handleVectorChange("zoneViewPortTarget", value)} - header="Viewport Target" - value={zoneTarget as [number, number, number]} - disabled={!Edit} - /> - handleVectorChange("zoneViewPortPosition", value)} - header="Viewport Position" - value={zonePosition as [number, number, number]} - disabled={!Edit} - /> + handleVectorChange("zoneViewPortTarget", value)} + header="Viewport Target" + value={zoneTarget as [number, number, number]} + disabled={!Edit} + /> + + handleVectorChange("zoneViewPortPosition", value) + } + header="Viewport Position" + value={zonePosition as [number, number, number]} + disabled={!Edit} + /> - {Edit && ( -
- Set View -
- )} + {Edit && ( +
+ Set View +
+ )} +
); }; export default ZoneProperties; - diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/EventProperties.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/EventProperties.tsx index ca32594..4fa9105 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/EventProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/EventProperties.tsx @@ -1,11 +1,18 @@ -import React, { useEffect, useRef, useState } from "react"; -import { useSelectedEventData, useSelectedProduct } from "../../../../../store/simulation/useSimulationStore"; +import React, { useEffect, useState } from "react"; +import { + 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"; +import { useEventsStore } from "../../../../../store/simulation/useEventsStore"; const EventProperties: React.FC = () => { const { selectedEventData } = useSelectedEventData(); @@ -13,6 +20,8 @@ const EventProperties: React.FC = () => { const { selectedProduct } = useSelectedProduct(); const [currentEventData, setCurrentEventData] = useState(null); const [assetType, setAssetType] = useState(null); + const { products, addEvent } = useProductStore(); + const { selectedEventSphere } = useSelectedEventSphere(); useEffect(() => { const event = getCurrentEventData(); @@ -20,45 +29,100 @@ const EventProperties: React.FC = () => { const type = determineAssetType(event); setAssetType(type); - + // eslint-disable-next-line react-hooks/exhaustive-deps }, [selectedEventData, selectedProduct]); const getCurrentEventData = () => { if (!selectedEventData?.data || !selectedProduct) return null; - return getEventByModelUuid(selectedProduct.productId, selectedEventData.data.modelUuid) || null; + 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; + case "transfer": + return "conveyor"; + case "vehicle": + return "vehicle"; + case "roboticArm": + return "roboticArm"; + case "machine": + return "machine"; + case "storageUnit": + return "storageUnit"; + default: + return null; } }; return ( - <> -
- {currentEventData && - <> -
-
{selectedEventData?.data.modelName}
+
+ {currentEventData && ( + <> +
+
+ {selectedEventData?.data.modelName}
- {assetType === 'conveyor' && } - {assetType === 'vehicle' && } - {assetType === 'roboticArm' && } - {assetType === 'machine' && } - {assetType === 'storageUnit' && } - - } -
- +
+ {assetType === "conveyor" && } + {assetType === "vehicle" && } + {assetType === "roboticArm" && } + {assetType === "machine" && } + {assetType === "storageUnit" && } + + )} + {!currentEventData && selectedEventSphere && ( +
+

+ Oops! It looks like this object doesn't have an + event assigned yet. To continue, please link it to one of the + products below. +

+ +
+

+ Here are some products you can add it to: +

+
    + {products.map((product) => ( +
  • + +
  • + ))} +
+
+
+ ) + } + {!selectedEventSphere && ( +
+

+ Oops! It looks like you haven't selected an event + point yet. Please select an event to view its properties. +

+
+ )} +
); }; -export default EventProperties; \ No newline at end of file +export default EventProperties; diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/actions/DelayAction.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/DelayAction.tsx index 2bb63d4..6c33583 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/actions/DelayAction.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/DelayAction.tsx @@ -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 = ({ value, defaultValue, min, max, onChange }) => { - return ( - <> - { }} - onChange={onChange} - /> - - ); +const DelayAction: React.FC = ({ + value, + defaultValue, + min, + max, + onChange, +}) => { + return ( + {}} + onChange={onChange} + /> + ); }; export default DelayAction; diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/actions/DespawnAction.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/DespawnAction.tsx index 0d54476..88cbd2e 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/actions/DespawnAction.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/DespawnAction.tsx @@ -1,5 +1,4 @@ import React from "react"; -import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown"; const DespawnAction: React.FC = () => { return ( diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/actions/SwapAction.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/SwapAction.tsx index 23b203f..5eaf991 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/actions/SwapAction.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/SwapAction.tsx @@ -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 = ({ onSelect, defaultOption, options }) => { - - return ( - <> - {/* */} - - - - ); +const SwapAction: React.FC = ({ + onSelect, + defaultOption, + options, +}) => { + return ( + + ); }; export default SwapAction; diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/components/ActionsList.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/components/ActionsList.tsx new file mode 100644 index 0000000..6d2c3de --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/components/ActionsList.tsx @@ -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 = ({ + setSelectedPointData, + selectedPointData, + multipleAction = false, +}) => { + const actionsContainerRef = useRef(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 ( +
+
+
+
Actions
+ + +
+
+
+ {multipleAction && + selectedPointData.actions.map((action: any) => ( +
+ + {selectedPointData.actions.length > 1 && ( + + )} +
+ ))} + {!multipleAction && selectedPointData && ( +
+ +
+ )} +
+ {multipleAction && ( + + )} +
+
+
+ ); +}; + +export default ActionsList; diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/conveyorMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/conveyorMechanics.tsx index c13bd75..6437311 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/conveyorMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/conveyorMechanics.tsx @@ -1,15 +1,16 @@ -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 { 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"); @@ -25,71 +26,59 @@ function ConveyorMechanics() { selectedEventData?.data.modelUuid, selectedEventData?.selectedPoint ) as ConveyorPointSchema | undefined; - if (point && 'action' in point) { + if (point && "action" in point) { setSelectedPointData(point); - setActiveOption(point.action.actionType as "default" | "spawn" | "swap" | "delay" | "despawn"); + setActiveOption(point.action.actionType as | "default" | "spawn" | "swap" | "delay" | "despawn"); } } - }, [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) => { if (!selectedEventData || !selectedPointData) return; - const validOption = option as "default" | "spawn" | "swap" | "delay" | "despawn"; + const validOption = option as | "default" | "spawn" | "swap" | "delay" | "despawn"; 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 handleSpawnCountChange = (value: string) => { if (!selectedPointData) return; - updateAction( - selectedPointData.action.actionUuid, - { spawnCount: value === "inherit" ? "inherit" : parseFloat(value) } - ); + updateAction(selectedPointData.action.actionUuid, { + spawnCount: value === "inherit" ? "inherit" : parseFloat(value), + }); }; const handleSpawnIntervalChange = (value: string) => { if (!selectedPointData) return; - updateAction( - selectedPointData.action.actionUuid, - { spawnInterval: value === "inherit" ? "inherit" : parseFloat(value) } - ); + updateAction(selectedPointData.action.actionUuid, { + spawnInterval: value === "inherit" ? "inherit" : parseFloat(value), + }); }; const handleMaterialSelect = (material: string) => { if (!selectedPointData) return; - updateAction( - selectedPointData.action.actionUuid, - { material } - ); + updateAction(selectedPointData.action.actionUuid, { material }); }; const handleDelayChange = (value: string) => { if (!selectedPointData) return; - updateAction( - selectedPointData.action.actionUuid, - { delay: value === "inherit" ? "inherit" : parseFloat(value) } - ); + updateAction(selectedPointData.action.actionUuid, { + delay: value === "inherit" ? "inherit" : parseFloat(value), + }); }; const availableActions = { @@ -124,89 +113,89 @@ function ConveyorMechanics() { return ( <> - {selectedEventData && - <> -
-
-
- { }} - onChange={handleSpeedChange} - /> -
-
+
+
+
+ { }} + onChange={handleSpeedChange} + />
+
+
+
+ -
-
- -
-
- +
+ +
+
+ + {activeOption === "default" && } + {activeOption === "spawn" && ( + - {activeOption === "default" && - - } - {activeOption === "spawn" && - - } - {activeOption === "swap" && - - } - {activeOption === "despawn" && - - } - {activeOption === "delay" && - - } -
+ )} + {activeOption === "swap" && ( + + )} + {activeOption === "despawn" && } + {activeOption === "delay" && ( + + )}
-
- -
- - } +
+
+ +
+
- ) + ); } -export default ConveyorMechanics \ No newline at end of file +export default ConveyorMechanics; diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/machineMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/machineMechanics.tsx index e131864..266eb39 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/machineMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/machineMechanics.tsx @@ -1,10 +1,11 @@ -import { useEffect, useState } from 'react' -import RenameInput from '../../../../../ui/inputs/RenameInput' -import LabledDropdown from '../../../../../ui/inputs/LabledDropdown' -import Trigger from '../trigger/Trigger' +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"); @@ -20,46 +21,40 @@ function MachineMechanics() { selectedEventData?.data.modelUuid, selectedEventData?.selectedPoint ) as MachinePointSchema | undefined; - if (point && 'action' in point) { + if (point && "action" in point) { setSelectedPointData(point); setActiveOption(point.action.actionType as "process"); } } - }, [selectedProduct, selectedEventData]) + }, [selectedProduct, selectedEventData, getPointByUuid]); 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 } - ); + updateAction(selectedPointData.action.actionUuid, { actionName: newName }); }; const handleProcessTimeChange = (value: string) => { if (!selectedPointData) return; - updateAction( - selectedPointData.action.actionUuid, - { processTime: parseFloat(value) } - ); + updateAction(selectedPointData.action.actionUuid, { + processTime: parseFloat(value), + }); }; const handleMaterialSelect = (material: string) => { if (!selectedPointData) return; - updateAction( - selectedPointData.action.actionUuid, - { swapMaterial: material } - ); + updateAction(selectedPointData.action.actionUuid, { + swapMaterial: material, + }); }; // Get current values from store @@ -82,8 +77,8 @@ function MachineMechanics() { return ( <> - {selectedEventData && - <> + {selectedEventData && ( +
+
- {activeOption === "process" && + {activeOption === "process" && ( - } + )}
- - } +
+ )} - ) + ); } -export default MachineMechanics \ No newline at end of file +export default MachineMechanics; diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/roboticArmMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/roboticArmMechanics.tsx index 78a32f5..faea93d 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/roboticArmMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/roboticArmMechanics.tsx @@ -1,21 +1,18 @@ -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 { 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(null); const [activeOption, setActiveOption] = useState<"default" | "pickAndPlace">("default"); const [selectedPointData, setSelectedPointData] = useState(); const { selectedEventData } = useSelectedEventData(); - const { getPointByUuid, updateEvent, updateAction, addAction, removeAction } = useProductStore(); + const { getPointByUuid, updateEvent, updateAction } = useProductStore(); const { selectedProduct } = useSelectedProduct(); const { selectedAction, setSelectedAction, clearSelectedAction } = useSelectedAction(); @@ -26,129 +23,65 @@ function RoboticArmMechanics() { selectedEventData.data.modelUuid, selectedEventData.selectedPoint ) as RoboticArmPointSchema | undefined; - if (point) { + 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); + setSelectedAction( + point.actions[0].actionUuid, + point.actions[0].actionName + ); } } } else { clearSelectedAction(); } - }, [selectedEventData, selectedProduct]); - - const handleActionSelect = (actionUuid: string, actionName: string) => { - setSelectedAction(actionUuid, actionName); - }; - - const handleAddAction = () => { - if (!selectedEventData || !selectedPointData) return; - - const newAction = { - actionUuid: THREE.MathUtils.generateUUID(), - actionName: `Action ${selectedPointData.actions.length + 1}`, - actionType: "pickAndPlace" as "pickAndPlace", - 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 => 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(); - } - } - }; + }, [clearSelectedAction, getPointByUuid, selectedAction.actionId, selectedEventData, selectedProduct, setSelectedAction,]); const handleRenameAction = (newName: string) => { if (!selectedAction.actionId) return; - updateAction( - selectedAction.actionId, - { actionName: newName } - ); + updateAction(selectedAction.actionId, { actionName: newName }); if (selectedPointData) { - const updatedActions = selectedPointData.actions.map(action => - action.actionUuid === selectedAction.actionId - ? { ...action, actionName: newName } - : action + const updatedActions = selectedPointData.actions.map((action) => + action.actionUuid === selectedAction.actionId ? { ...action, actionName: newName } : action ); setSelectedPointData({ ...selectedPointData, - actions: updatedActions + actions: updatedActions, }); } }; 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 handlePickPointChange = (value: string) => { if (!selectedAction.actionId || !selectedPointData) return; - const [x, y, z] = value.split(',').map(Number); + 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 - } - } - ); + 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); + 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] - } - } - ); + 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 = { @@ -160,7 +93,8 @@ function RoboticArmMechanics() { ? selectedEventData.data.speed.toString() : "0.5"; - const currentAction = selectedPointData?.actions.find(a => a.actionUuid === selectedAction.actionId); + 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]}` : ""; @@ -170,107 +104,60 @@ function RoboticArmMechanics() { return ( <> - {selectedEventData && selectedPointData && ( - <> -
-
-
- { }} - onChange={handleSpeedChange} - /> -
+
+
+
+ { }} + onChange={handleSpeedChange} + /> +
+
+
+
+ + + {selectedAction.actionId && currentAction && ( +
+
+ +
+
+ { }} + disabled={true} + /> + +
+
+
- -
-
-
-
Actions
-
- Add -
-
-
-
- {selectedPointData.actions.map((action) => ( -
-
handleActionSelect(action.actionUuid, action.actionName)} - > - -
- {selectedPointData.actions.length > 1 && ( -
handleDeleteAction(action.actionUuid)} - > - -
- )} -
- ))} -
-
handleResize(e, actionsContainerRef)} - > - -
-
-
-
- - {selectedAction.actionId && currentAction && ( -
-
- -
-
- { }} - disabled={true} - /> - -
-
- -
-
- )} - - )} + )} +
- ) + ); } -export default RoboticArmMechanics \ No newline at end of file +export default RoboticArmMechanics; diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx index 593dcc3..f22ec37 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx @@ -1,10 +1,11 @@ -import { useEffect, useState } from 'react' -import RenameInput from '../../../../../ui/inputs/RenameInput' -import LabledDropdown from '../../../../../ui/inputs/LabledDropdown' -import Trigger from '../trigger/Trigger' +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"); @@ -20,38 +21,33 @@ function StorageMechanics() { selectedEventData?.data.modelUuid, selectedEventData?.selectedPoint ) as StoragePointSchema | undefined; - if (point && 'action' in point) { + if (point && "action" in point) { setSelectedPointData(point); setActiveOption(point.action.actionType as "store" | "spawn"); } } - }, [selectedProduct, selectedEventData]) + }, [selectedProduct, selectedEventData, getPointByUuid]); 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 } - ); + updateAction(selectedPointData.action.actionUuid, { actionName: newName }); }; const handleCapacityChange = (value: string) => { if (!selectedPointData) return; - updateAction( - selectedPointData.action.actionUuid, - { storageCapacity: parseInt(value) } - ); + updateAction(selectedPointData.action.actionUuid, { + storageCapacity: parseInt(value), + }); }; // Get current values from store @@ -70,8 +66,12 @@ function StorageMechanics() { return ( <> - {selectedEventData && - <> + {selectedEventData && ( +
+
- {activeOption === "store" && + {activeOption === "store" && ( - } + )} {activeOption === "spawn" && (

Spawn configuration options would go here

@@ -104,10 +104,10 @@ function StorageMechanics() {
- - } +
+ )} - ) + ); } -export default StorageMechanics \ No newline at end of file +export default StorageMechanics; diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx index cc2bfa0..d8456e5 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx @@ -1,197 +1,189 @@ -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 [selectedPointData, setSelectedPointData] = useState(); - const { selectedEventData } = useSelectedEventData(); - const { getPointByUuid, updateEvent, updateAction } = useProductStore(); - const { selectedProduct } = useSelectedProduct(); + const [activeOption, setActiveOption] = useState<"default" | "travel">( + "default" + ); + const [selectedPointData, setSelectedPointData] = useState< + VehiclePointSchema | 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 VehiclePointSchema | undefined; + useEffect(() => { + if (selectedEventData) { + const point = getPointByUuid( + selectedProduct.productId, + selectedEventData.data.modelUuid, + selectedEventData.selectedPoint + ) as VehiclePointSchema | undefined; - if (point) { - setSelectedPointData(point); - setActiveOption(point.action.actionType as "travel"); - } - } - }, [selectedProduct, selectedEventData]) + if (point) { + setSelectedPointData(point); + setActiveOption(point.action.actionType as "travel"); + } + } + }, [selectedProduct, selectedEventData, getPointByUuid]); - const handleSpeedChange = (value: string) => { - if (!selectedEventData) return; - updateEvent( - selectedProduct.productId, - selectedEventData.data.modelUuid, - { speed: parseFloat(value) } - ); - }; + const handleSpeedChange = (value: string) => { + if (!selectedEventData) return; + updateEvent(selectedProduct.productId, selectedEventData.data.modelUuid, { + speed: parseFloat(value), + }); + }; - const handleActionTypeChange = (option: string) => { - if (!selectedEventData || !selectedPointData) return; - const validOption = option as "travel"; - setActiveOption(validOption); + const handleActionTypeChange = (option: string) => { + if (!selectedEventData || !selectedPointData) return; + 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 } - ); - }; + const handleRenameAction = (newName: string) => { + if (!selectedPointData) return; + updateAction(selectedPointData.action.actionUuid, { actionName: newName }); + }; - const handleLoadCapacityChange = (value: string) => { - if (!selectedPointData) return; - updateAction( - selectedPointData.action.actionUuid, - { loadCapacity: parseFloat(value) } - ); - }; + const handleLoadCapacityChange = (value: string) => { + if (!selectedPointData) return; + updateAction(selectedPointData.action.actionUuid, { + loadCapacity: parseFloat(value), + }); + }; - const handleUnloadDurationChange = (value: string) => { - if (!selectedPointData) return; - updateAction( - selectedPointData.action.actionUuid, - { unLoadDuration: parseFloat(value) } - ); - }; + const handleUnloadDurationChange = (value: string) => { + if (!selectedPointData) return; + 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 handlePickPointChange = (value: string) => { + if (!selectedPointData) return; + }; - const handleUnloadPointChange = (value: string) => { - if (!selectedPointData) return; - const [x, y, z] = value.split(',').map(Number); - updateAction( - selectedPointData.action.actionUuid, - { unLoadPoint: { x, y, z } } - ); - }; + const handleUnloadPointChange = (value: string) => { + if (!selectedPointData) return; + }; - // Get current values from store - const currentSpeed = selectedEventData?.data.type === "vehicle" - ? selectedEventData.data.speed.toString() - : "0.5"; + // Get current values from store + const currentSpeed = + selectedEventData?.data.type === "vehicle" + ? selectedEventData.data.speed.toString() + : "0.5"; - const currentActionName = selectedPointData - ? selectedPointData.action.actionName - : "Action Name"; + const currentActionName = selectedPointData + ? selectedPointData.action.actionName + : "Action Name"; - const currentLoadCapacity = selectedPointData - ? selectedPointData.action.loadCapacity.toString() - : "1"; + const currentLoadCapacity = selectedPointData + ? selectedPointData.action.loadCapacity.toString() + : "1"; - const currentUnloadDuration = selectedPointData - ? selectedPointData.action.unLoadDuration.toString() - : "1"; + const currentUnloadDuration = selectedPointData + ? 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", - options: ["travel"], - }; + const availableActions = { + defaultOption: "travel", + options: ["travel"], + }; - return ( + return ( + <> + {selectedEventData && ( <> - {selectedEventData && - <> -
-
-
- { }} - onChange={handleSpeedChange} - /> -
-
-
+
+
+
+ {}} + onChange={handleSpeedChange} + /> +
+
+
+
+ +
+
+ +
+
+ -
-
- -
-
- - - {activeOption === 'travel' && - - } -
-
-
- -
- - } + {activeOption === "travel" && ( + + )} +
+
+
+ +
+
- ) + )} + + ); } -export default VehicleMechanics \ No newline at end of file +export default VehicleMechanics; diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/trigger/Trigger.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/trigger/Trigger.tsx index f287b63..3b2549a 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/trigger/Trigger.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/trigger/Trigger.tsx @@ -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([]); + const [triggers, setTriggers] = useState(["Trigger 1"]); + const [selectedTrigger, setSelectedTrigger] = useState("Trigger 1"); const [activeOption, setActiveOption] = useState("onComplete"); + const triggersContainerRef = useRef(null); // States for dropdowns const [triggeredModel, setTriggeredModel] = useState([]); @@ -35,70 +43,94 @@ const Trigger: React.FC = () => {
Trigger
-
Add -
+
- {/* Map over triggers and render them */} - {triggers.map((trigger, index) => ( -
-
- {trigger} +
+
+ {triggers.map((trigger: any, index: number) => (
removeTrigger(index)} - style={{ cursor: "pointer" }} + key={index} + className={`list-item ${ + selectedTrigger === trigger ? "active" : "" + }`} + onClick={() => setSelectedTrigger(trigger)} > - + + {triggers.length > 1 && ( + + )}
+ ))} +
+ +
+
+
{selectedTrigger}
+ setActiveOption(option)} + /> +
+
+ { + const newModel = [...triggeredModel]; + newModel[0] = option; + setTriggeredModel(newModel); + }} + />
- setActiveOption(option)} - /> -
-
- { - const newModel = [...triggeredModel]; - newModel[index] = option; - setTriggeredModel(newModel); - }} - /> -
-
- { - const newPoint = [...triggeredPoint]; - newPoint[index] = option; - setTriggeredPoint(newPoint); - }} - /> -
-
- { - const newAction = [...triggeredAction]; - newAction[index] = option; - setTriggeredAction(newAction); - }} - /> -
+
+ { + const newPoint = [...triggeredPoint]; + newPoint[0] = option; + setTriggeredPoint(newPoint); + }} + /> +
+
+ { + const newAction = [...triggeredAction]; + newAction[0] = option; + setTriggeredAction(newAction); + }} + />
- ))} +
); diff --git a/app/src/components/layout/sidebarRight/simulation/Simulations.tsx b/app/src/components/layout/sidebarRight/simulation/Simulations.tsx index 926ce44..1280693 100644 --- a/app/src/components/layout/sidebarRight/simulation/Simulations.tsx +++ b/app/src/components/layout/sidebarRight/simulation/Simulations.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useRef } from "react"; +import React, { useEffect, useRef, useState } from "react"; import { AddIcon, ArrowIcon, @@ -7,12 +7,19 @@ import { } 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 { handleAddEventToProduct } from "../../../../modules/simulation/events/points/functions/handleAddEventToProduct"; +import { useEventsStore } from "../../../../store/simulation/useEventsStore"; +import { deleteEventDataApi } from "../../../../services/simulation/deleteEventDataApi"; import { upsertProductOrEventApi } from "../../../../services/simulation/UpsertProductOrEventApi"; +import { deleteProductApi } from "../../../../services/simulation/deleteProductApi"; interface Event { pathName: string; @@ -25,28 +32,33 @@ interface ListProps { const List: React.FC = ({ val }) => { return (
-
- {val.pathName} -
+
{val.pathName}
); }; const Simulations: React.FC = () => { const productsContainerRef = useRef(null); - const { products, addProduct, removeProduct, renameProduct, addEvent, removeEvent } = useProductStore(); + const { products, addProduct, removeProduct, renameProduct, addEvent, removeEvent, } = useProductStore(); const { selectedProduct, setSelectedProduct } = useSelectedProduct(); + const { getEventByModelUuid } = useEventsStore(); const { selectedAsset, clearSelectedAsset } = useSelectedAsset(); + const email = localStorage.getItem('email') + const organization = (email!.split("@")[1]).split(".")[0]; + const [openObjects, setOpenObjects] = useState(true); const handleAddProduct = () => { - addProduct(`Product ${products.length + 1}`, generateUUID()); + const id = generateUUID(); + const name = `Product ${products.length + 1}`; + addProduct(name, id); + upsertProductOrEventApi({ productName: name, productId: id, organization: organization }); }; const handleRemoveProduct = (productId: string) => { - const currentIndex = products.findIndex(p => p.productId === productId); + 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) { @@ -59,11 +71,12 @@ const Simulations: React.FC = () => { updatedProducts[newSelectedIndex].productName ); } else { - setSelectedProduct('', ''); + setSelectedProduct("", ""); } } removeProduct(productId); + deleteProductApi(productId, organization); }; const handleRenameProduct = (productId: string, newName: string) => { @@ -73,20 +86,15 @@ const Simulations: React.FC = () => { } }; - const handleAddEventToProduct = () => { - if (selectedAsset) { - addEvent(selectedProduct.productId, selectedAsset); - // upsertProductOrEventApi({ - // productName: selectedProduct.productName, - // productId: selectedProduct.productId, - // eventDatas: selectedAsset - // }); - clearSelectedAsset(); - } - }; - const handleRemoveEventFromProduct = () => { if (selectedAsset) { + const email = localStorage.getItem('email') + const organization = (email!.split("@")[1]).split(".")[0]; + deleteEventDataApi({ + productId: selectedProduct.productId, + modelUuid: selectedAsset.modelUuid, + organization: organization + }); removeEvent(selectedProduct.productId, selectedAsset.modelUuid); clearSelectedAsset(); } @@ -104,7 +112,7 @@ const Simulations: React.FC = () => {
Simulations
-
+
Products
@@ -120,11 +128,16 @@ const Simulations: React.FC = () => { {products.map((product, index) => (
setSelectedProduct(product.productId, product.productName)} + onClick={() => + setSelectedProduct(product.productId, product.productName) + } > { /> handleRenameProduct(product.productId, newName)} + onRename={(newName) => + handleRenameProduct(product.productId, newName) + } />
{products.length > 1 && ( @@ -158,16 +173,18 @@ const Simulations: React.FC = () => {
-
-
+
+
- {events.map((event, index) => ( - - ))} + + {openObjects && + events.map((event, index) => )}
@@ -175,7 +192,8 @@ const Simulations: React.FC = () => { Need to Compare Layout?
- Click 'Compare' to review and analyze the layout differences between them. + Click 'Compare' to review and analyze the layout + differences between them.
@@ -183,22 +201,27 @@ const Simulations: React.FC = () => {
- {selectedAsset && + {selectedAsset && ( { - if (option === 'Add to Product') { - handleAddEventToProduct(); + if (option === "Add to Product") { + handleAddEventToProduct({ + event: getEventByModelUuid(selectedAsset.modelUuid), + addEvent, + selectedProduct, + clearSelectedAsset + }); } else { handleRemoveEventFromProduct(); } }} /> - } + )}
- ); + ) }; export default Simulations; diff --git a/app/src/components/templates/FollowPerson.tsx b/app/src/components/templates/FollowPerson.tsx new file mode 100644 index 0000000..a85d0c9 --- /dev/null +++ b/app/src/components/templates/FollowPerson.tsx @@ -0,0 +1,29 @@ +import React from "react"; +import RenderOverlay from "./Overlay"; +import { useSelectedUserStore } from "../../store/useCollabStore"; +import { useCamMode } from "../../store/store"; + +const FollowPerson: React.FC = () => { + // Get the selected user from the store + const { selectedUser, clearSelectedUser } = useSelectedUserStore(); + const { setCamMode } = useCamMode(); + return ( + + {selectedUser && ( + // eslint-disable-next-line +
{ + clearSelectedUser(); + setCamMode("FirstPerson"); + }} + style={{ "--user-color": selectedUser.color } as React.CSSProperties} + > +
{selectedUser.name}
+
+ )} +
+ ); +}; + +export default FollowPerson; diff --git a/app/src/components/ui/FileMenu.tsx b/app/src/components/ui/FileMenu.tsx index a7c876d..da85e54 100644 --- a/app/src/components/ui/FileMenu.tsx +++ b/app/src/components/ui/FileMenu.tsx @@ -1,25 +1,53 @@ -import React, { useState } from "react"; +import React, { useState, useEffect, useRef } from "react"; import RenameInput from "./inputs/RenameInput"; import { ArrowIcon } from "../icons/ExportCommonIcons"; import MenuBar from "./menu/menu"; +import { ProjectIcon } from "../icons/HeaderIcons"; const FileMenu: React.FC = () => { const [openMenu, setOpenMenu] = useState(false); + const containerRef = useRef(null); + let clickTimeout: NodeJS.Timeout | null = null; + + const handleClick = () => { + if (clickTimeout) return; + setOpenMenu((prev) => !prev); + clickTimeout = setTimeout(() => { + clickTimeout = null; + }, 800); + }; + + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if ( + containerRef.current && + !containerRef.current.contains(event.target as Node) + ) { + setOpenMenu(false); + } + }; + + document.addEventListener("mousedown", handleClickOutside); + return () => document.removeEventListener("mousedown", handleClickOutside); + }, []); + return ( -
+ ); }; diff --git a/app/src/components/ui/analysis/ProductionCapacity.tsx b/app/src/components/ui/analysis/ProductionCapacity.tsx index 268ea81..192e009 100644 --- a/app/src/components/ui/analysis/ProductionCapacity.tsx +++ b/app/src/components/ui/analysis/ProductionCapacity.tsx @@ -1,10 +1,14 @@ -import React from "react"; +import React, { useState } from "react"; import { ProductionCapacityIcon } from "../../icons/analysis"; -const ProductionCapacity = () => { +const ProductionCapacity = ({ + progressPercent = 10, + avgProcessTime = "28.4 Secs/unit", + machineUtilization = "78%", + throughputValue = 128, + timeRange = { startTime: "08:00 AM", endTime: "09:00 AM" }, +}) => { const totalBars = 6; - const progressPercent = 50; - const barsToFill = Math.floor((progressPercent / 100) * totalBars); const partialFillPercent = ((progressPercent / 100) * totalBars - barsToFill) * 100; @@ -14,8 +18,10 @@ const ProductionCapacity = () => {
-
Throughput Summary
-
08:00 - 09:00 AM
+
Production Capacity
+
+ {timeRange.startTime} - {timeRange.endTime} +
@@ -24,10 +30,10 @@ const ProductionCapacity = () => {
- 128 Units/hour + {throughputValue} Units/hour
- {/* Progress Bar */} + {/* Dynamic Progress Bar */}
{[...Array(totalBars)].map((_, i) => (
@@ -47,11 +53,11 @@ const ProductionCapacity = () => {
Avg. Process Time - 28.4 Secs/unit + {avgProcessTime}
Machine Utilization - 78% + {machineUtilization}
diff --git a/app/src/components/ui/analysis/ROISummary.tsx b/app/src/components/ui/analysis/ROISummary.tsx index 331eaaf..11101fd 100644 --- a/app/src/components/ui/analysis/ROISummary.tsx +++ b/app/src/components/ui/analysis/ROISummary.tsx @@ -1,10 +1,71 @@ -import React from "react"; +import React, { useState } from "react"; import { ROISummaryIcon } from "../../icons/analysis"; +import SemiCircleProgress from "./SemiCircleProgress"; + +const ROISummary = ({ + roiSummaryData = { + productName: "Product name", + roiPercentage: 133, + paybackPeriod: 50.3, + totalCost: "₹ 1,20,000", + revenueGenerated: "₹ 2,80,000", + netProfit: "₹ 1,60,000", + costBreakdown: [ + { + item: "Raw Material A", + unitCost: "₹ 10/unit", + laborCost: "₹ 0", + totalCost: "₹ 1000", + sellingPrice: "₹ 1500", + }, + { + item: "Labor", + unitCost: "₹ 10/unit", + laborCost: "₹ 500", + totalCost: "₹ 500", + sellingPrice: "N/A", + }, + { + item: "Product 1", + unitCost: "₹ 10/unit", + laborCost: "₹ 200", + totalCost: "₹ 200", + sellingPrice: "₹ 2000", + }, + { + item: "Machine", + unitCost: "-", + laborCost: "-", + totalCost: "₹ 20,000", + sellingPrice: "N/A", + }, + { + item: "Total", + unitCost: "-", + laborCost: "-", + totalCost: "₹ 1,20,000", + sellingPrice: "-", + }, + { + item: "Net Profit", + unitCost: "-", + laborCost: "-", + totalCost: "₹ 1,60,000", + sellingPrice: "-", + }, + ], + }, +}) => { + const [isTableOpen, setIsTableOpen] = useState(false); // State to handle the table open/close + + // Function to toggle the breakdown table visibility + const toggleTable = () => { + setIsTableOpen(!isTableOpen); + }; -const ROISummary = () => { return ( -
-
+
+
ROI Summary
@@ -14,6 +75,111 @@ const ROISummary = () => {
+
+
Product :
+
{roiSummaryData.productName}
+
+
+
+
+ +{roiSummaryData.roiPercentage}% ROI with payback in + just {roiSummaryData.paybackPeriod} months +
+
+
+
+ +
+ you're on track to hit it by +
July 2029
+
+
+
+
+
+ Total Cost Incurred + {roiSummaryData.totalCost} +
+
+ Revenue Generated + + {roiSummaryData.revenueGenerated} + +
+
+
+ Net Profit + {roiSummaryData.netProfit} +
+
+
+
+
+
+ â‘  + Cost Breakdown +
+ + + {isTableOpen ? "⌵" : "⌵"} + +
+
+ + + + + + + + + + + + {roiSummaryData.costBreakdown.map((row, index) => ( + + + + + + + + ))} + +
ItemUnit CostLabor CostTotal CostSelling Price
{row.item}{row.unitCost}{row.laborCost}{row.totalCost}{row.sellingPrice}
+
+
+
+
+ 💡 + How to improve ROI? +
+
+ Increase CNC utilization by 10%{" "} + to shave 0.5 months of payback + period +
+ +
); diff --git a/app/src/components/ui/analysis/SemiCircleProgress.tsx b/app/src/components/ui/analysis/SemiCircleProgress.tsx new file mode 100644 index 0000000..084636b --- /dev/null +++ b/app/src/components/ui/analysis/SemiCircleProgress.tsx @@ -0,0 +1,27 @@ +import React from "react"; + +const SemiCircleProgress = () => { + const progress = 50; + const clampedProgress = Math.min(Math.max(progress, 0), 100); + const gradientProgress = clampedProgress * 0.5; + + return ( +
+
+
+
+
+
{clampedProgress}%
+
Years
+
+
you're on track to hit it by July 2029
+
+ ); +}; + +export default SemiCircleProgress; diff --git a/app/src/components/ui/analysis/ThroughputSummary.tsx b/app/src/components/ui/analysis/ThroughputSummary.tsx index cb4fac5..b92a82d 100644 --- a/app/src/components/ui/analysis/ThroughputSummary.tsx +++ b/app/src/components/ui/analysis/ThroughputSummary.tsx @@ -11,21 +11,57 @@ import { PowerIcon, ThroughputSummaryIcon } from "../../icons/analysis"; ChartJS.register(LineElement, CategoryScale, LinearScale, PointElement); +// Helper function to generate random colors +const getRandomColor = () => { + const letters = "0123456789ABCDEF"; + let color = "#"; + for (let i = 0; i < 6; i++) { + color += letters[Math.floor(Math.random() * 16)]; + } + return color; +}; + const ThroughputSummary = () => { - const data = { + // Define all data internally within the component + const timeRange = { + startTime: "08:00 AM", + endTime: "09:00 AM", + }; + + const throughputData = { labels: ["08:00", "08:10", "08:20", "08:30", "08:40", "08:50", "09:00"], + data: [100, 120, 110, 130, 125, 128, 132], + totalThroughput: 1240, + assetUsage: 85, + }; + + const energyConsumption = { + energyConsumed: 456, + unit: "KWH", + }; + + // Dynamic shift data + const shiftUtilization = [ + { shift: 1, percentage: 30 }, + { shift: 2, percentage: 40 }, + { shift: 3, percentage: 30 }, + ]; + + // Chart data configuration + const chartData = { + labels: throughputData.labels, datasets: [ { label: "Units/hour", - data: [100, 120, 110, 130, 125, 128, 132], + data: throughputData.data, borderColor: "#B392F0", tension: 0.4, - pointRadius: 0, // hide points + pointRadius: 0, // Hide points }, ], }; - const options = { + const chartOptions = { responsive: true, scales: { x: { @@ -57,19 +93,15 @@ const ThroughputSummary = () => { }, }; - const shiftUtilization = { - "shift 1": 25, - "shift 2": 45, - "shift 3": 15, - }; - return (
Throughput Summary
-
08:00 - 09:00 AM
+
+ {timeRange.startTime} - {timeRange.endTime} +
@@ -78,14 +110,15 @@ const ThroughputSummary = () => {
- 1240 Units/hour + {throughputData.totalThroughput}{" "} + Units/hour
Asset usage
-
85%
+
{throughputData.assetUsage}%
- +
@@ -97,43 +130,41 @@ const ThroughputSummary = () => {
-
456
-
KWH
+
{energyConsumption.energyConsumed}
+
{energyConsumption.unit}
Shift Utilization
-
85%
+
{throughputData.assetUsage}%
-
-
-
+ {/* Dynamically create progress bars based on shiftUtilization array */} + {shiftUtilization.map((shift) => ( +
+ ))}
+
-
- - -
-
- - -
-
- - -
+ {/* Dynamically create shift indicators with random colors */} + {shiftUtilization.map((shift) => ( +
+ + +
+ ))}
diff --git a/app/src/components/ui/inputs/InputToggle.tsx b/app/src/components/ui/inputs/InputToggle.tsx index 941d486..5e69291 100644 --- a/app/src/components/ui/inputs/InputToggle.tsx +++ b/app/src/components/ui/inputs/InputToggle.tsx @@ -24,11 +24,16 @@ const InputToggle: React.FC = ({ -
+
diff --git a/app/src/components/ui/inputs/PreviewSelectionWithUpload.tsx b/app/src/components/ui/inputs/PreviewSelectionWithUpload.tsx index 3e14517..53e03ce 100644 --- a/app/src/components/ui/inputs/PreviewSelectionWithUpload.tsx +++ b/app/src/components/ui/inputs/PreviewSelectionWithUpload.tsx @@ -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 = ({ + preview = false, + upload = false, + onSelect, + label, + defaultOption, + options, +}) => { + const [showPreview, setShowPreview] = useState(false); return (
-
setSetshowPreview(!showPreview)} - > -
Preview
-
- -
-
- {showPreview && ( -
-
+ {preview && ( + <> + + {showPreview && ( +
+
+
+ )} + + )} + {upload && ( +
+
+
Upload Product
+ + +
)} -
-
-
Upload Product
- - -
-
+
); }; diff --git a/app/src/components/ui/list/DropDownList.tsx b/app/src/components/ui/list/DropDownList.tsx index 0416a9e..d0b62e8 100644 --- a/app/src/components/ui/list/DropDownList.tsx +++ b/app/src/components/ui/list/DropDownList.tsx @@ -81,8 +81,8 @@ const DropDownList: React.FC = ({ return isPointInsidePolygon([x, z], polygon2D); }) .map((item: any) => ({ - id: item.modeluuid, - name: item.modelname, + id: item.modelUuid, + name: item.modelName, position: item.position, rotation: item.rotation })); diff --git a/app/src/components/ui/list/List.tsx b/app/src/components/ui/list/List.tsx index ac21c5a..cdd2a53 100644 --- a/app/src/components/ui/list/List.tsx +++ b/app/src/components/ui/list/List.tsx @@ -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"; @@ -136,8 +136,8 @@ const List: React.FC = ({ items = [], remove }) => { console.log('response: ', response); setFloorItems((prevFloorItems: any[]) => prevFloorItems.map((floorItems) => - floorItems.modeluuid === zoneAssetId.id - ? { ...floorItems, modelname: response.modelname } + floorItems.modelUuid === zoneAssetId.id + ? { ...floorItems, modelName: response.modelName } : floorItems ) ); @@ -181,7 +181,7 @@ const List: React.FC = ({ items = [], remove }) => {
{remove && (
- +
)} {item.assets && item.assets.length > 0 && ( @@ -218,7 +218,7 @@ const List: React.FC = ({ items = [], remove }) => {
{remove && (
- +
)}
diff --git a/app/src/components/ui/simulation/simulationPlayer.tsx b/app/src/components/ui/simulation/simulationPlayer.tsx index 528fbc2..a23652b 100644 --- a/app/src/components/ui/simulation/simulationPlayer.tsx +++ b/app/src/components/ui/simulation/simulationPlayer.tsx @@ -7,6 +7,15 @@ import { usePlayButtonStore, useResetButtonStore, } from "../../../store/usePlayButtonStore"; +import { + DailyProductionIcon, + EndIcon, + ExpandIcon, + HourlySimulationIcon, + MonthlyROI, + SpeedIcon, + StartIcon, +} from "../../icons/ExportCommonIcons"; const SimulationPlayer: React.FC = () => { const { speed, setSpeed } = useAnimationPlaySpeed(); @@ -21,6 +30,7 @@ const SimulationPlayer: React.FC = () => { // Button functions const handleReset = () => { setReset(true); + // setReset(!isReset); setSpeed(1); }; const handlePlayStop = () => { @@ -30,7 +40,7 @@ const SimulationPlayer: React.FC = () => { const handleExit = () => { setPlaySimulation(false); setIsPlaying(false); - setActiveTool("cursor") + setActiveTool("cursor"); }; // Slider functions starts @@ -72,70 +82,277 @@ const SimulationPlayer: React.FC = () => { }, []); // Slider function ends + // UI-Part + const hourlySimulation = 25; + const dailyProduction = 75; + const monthlyROI = 50; + + const process = [ + { name: "process 1", completed: 0 }, // 0% completed + { name: "process 2", completed: 20 }, // 20% completed + { name: "process 3", completed: 40 }, // 40% completed + { name: "process 4", completed: 60 }, // 60% completed + { name: "process 5", completed: 80 }, // 80% completed + { name: "process 6", completed: 100 }, // 100% completed + { name: "process 7", completed: 0 }, // 0% completed + { name: "process 8", completed: 50 }, // 50% completed + { name: "process 9", completed: 90 }, // 90% completed + { name: "process 10", completed: 30 }, // 30% completed + ]; + const [expand, setExpand] = useState(false); + // Move getRandomColor out of render + const getRandomColor = () => { + const letters = "0123456789ABCDEF"; + let color = "#"; + for (let i = 0; i < 6; i++) { + color += letters[Math.floor(Math.random() * 16)]; + } + return color; + }; + + // Store colors for each process item + const [processColors, setProcessColors] = useState([]); + + // Generate colors on mount or when process changes + useEffect(() => { + const generatedColors = process.map(() => getRandomColor()); + setProcessColors(generatedColors); + }, []); + + const intervals = [10, 20, 30, 40, 50, 60]; // in minutes + const totalSegments = intervals.length; + const progress = 80; // percent (example) + + const processPlayerRef = useRef(null); + const processWrapperRef = useRef(null); + const [playerPosition, setPlayerPosition] = useState(0); + + const handleProcessMouseDown = (e: React.MouseEvent) => { + if (!processWrapperRef.current) return; + + const rect = processWrapperRef.current.getBoundingClientRect(); + let x = e.clientX - rect.left; + x = Math.max(0, Math.min(x, rect.width)); + setPlayerPosition(x); + + const onMouseMove = (e: MouseEvent) => { + if (!processWrapperRef.current) return; + const newRect = processWrapperRef.current.getBoundingClientRect(); + let newX = e.clientX - newRect.left; + newX = Math.max(0, Math.min(newX, newRect.width)); + setPlayerPosition(newX); + + const progressPercent = (newX / newRect.width) * 100; + console.log(`Dragging at progress: ${progressPercent.toFixed(1)}%`); + }; + + const onMouseUp = () => { + document.removeEventListener("mousemove", onMouseMove); + document.removeEventListener("mouseup", onMouseUp); + }; + + document.addEventListener("mousemove", onMouseMove); + document.addEventListener("mouseup", onMouseUp); + }; + return (
-
+
-
{ - handleReset(); - }} - > - - Reset -
-
{ - handlePlayStop(); - }} - > - - {playSimulation ? "Play" : "Stop"} -
-
{ - handleExit(); - }} - > - - Exit -
-
-
-
0.5x
-
-
-
-
-
-
-
-
-
-
-
-
- {speed.toFixed(1)}x +
+ {/* hourlySimulation */} +
+
+
+ +
+
Hourly Simulation
- +
+
+
+
+ {/* dailyProduction */} +
+
+
+ +
+
Daily Production
+
+
+
+
+
+ {/* monthlyROI */} +
+
+
+ +
+
Monthly ROI
+
+
+
+
{" "}
-
8x
+
+
{ + handleReset(); + }} + > + + Reset +
+
{ + handlePlayStop(); + }} + > + + {playSimulation ? "Play" : "Stop"} +
+
{ + handleExit(); + }} + > + + Exit +
+
setExpand(!expand)} + > + +
+
+
+
+
+
+
+ +
+
+
23 April ,25
+
04:41 PM
+
+
+
+
+ {intervals.map((label, index) => { + const segmentProgress = (index / totalSegments) * 100; + const isFilled = progress >= segmentProgress; + return ( + +
+
{label} mins
+
+
+ {index < intervals.length - 1 && ( +
= ((index + 1) / totalSegments) * 100 + ? "filled" + : "" + }`} + >
+ )} +
+ ); + })} +
+
+ +
+
+
00:10:20
+
+
+ +
+
+
+
+
+
+ +
+ Speed +
+
+
0X
+
+
+
+
+
+
+
+
+
+
+
+ {speed.toFixed(1)}x +
+ +
+
8x
+
+
+
+
+
+
+ {process.map((item, index) => ( +
+ ))} +
+
diff --git a/app/src/functions/collabUserIcon.tsx b/app/src/functions/collabUserIcon.tsx deleted file mode 100644 index 9e6802b..0000000 --- a/app/src/functions/collabUserIcon.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import React from "react"; -import CustomAvatar from "./users/Avatar"; - -interface CollabUserIconProps { - userName: string; - userImage?: string; - color: string; -} - -const CollabUserIcon: React.FC = ({ - userImage, - userName, - color, -}) => { - return ( -
-
- {userImage ? ( - {userName} - ) : ( - - )} -
-
- {userName} -
-
- ); -}; - -export default CollabUserIcon; diff --git a/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts b/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts index c46c0e7..a351d73 100644 --- a/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts +++ b/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts @@ -72,7 +72,7 @@ async function loadInitialFloorItems( // Check Three.js Cache const cachedModel = THREE.Cache.get(item.modelfileID!); if (cachedModel) { - // console.log(`[Cache] Fetching ${item.modelname}`); + // console.log(`[Cache] Fetching ${item.modelName}`); processLoadedModel(cachedModel.scene.clone(), item, itemsGroup, setFloorItems, addEvent); modelsLoaded++; checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve); @@ -82,7 +82,7 @@ async function loadInitialFloorItems( // Check IndexedDB const indexedDBModel = await retrieveGLTF(item.modelfileID!); if (indexedDBModel) { - // console.log(`[IndexedDB] Fetching ${item.modelname}`); + // console.log(`[IndexedDB] Fetching ${item.modelName}`); const blobUrl = URL.createObjectURL(indexedDBModel); loader.load(blobUrl, (gltf) => { URL.revokeObjectURL(blobUrl); @@ -94,7 +94,7 @@ async function loadInitialFloorItems( }, undefined, (error) => { - toast.error(`[IndexedDB] Error loading ${item.modelname}:`); + toast.error(`[IndexedDB] Error loading ${item.modelName}:`); URL.revokeObjectURL(blobUrl); resolve(); } @@ -103,7 +103,7 @@ async function loadInitialFloorItems( } // Fetch from Backend - // console.log(`[Backend] Fetching ${item.modelname}`); + // console.log(`[Backend] Fetching ${item.modelName}`); const modelUrl = `${url_Backend_dwinzo}/api/v2/AssetFile/${item.modelfileID!}`; loader.load(modelUrl, async (gltf) => { const modelBlob = await fetch(modelUrl).then((res) => res.blob()); @@ -115,18 +115,18 @@ async function loadInitialFloorItems( }, undefined, (error) => { - toast.error(`[Backend] Error loading ${item.modelname}:`); + toast.error(`[Backend] Error loading ${item.modelName}:`); resolve(); } ); }); } else { - // console.log(`Item ${item.modelname} is not near`); + // console.log(`Item ${item.modelName} is not near`); setFloorItems((prevItems) => [ ...(prevItems || []), { - modeluuid: item.modeluuid, - modelname: item.modelname, + modelUuid: item.modelUuid, + modelName: item.modelName, position: item.position, rotation: item.rotation, modelfileID: item.modelfileID, @@ -154,9 +154,9 @@ function processLoadedModel( addEvent: (event: EventsSchema) => void, ) { const model = gltf.clone(); - model.uuid = item.modeluuid; + model.uuid = item.modelUuid; model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap); - model.userData = { name: item.modelname, modelId: item.modelfileID, modeluuid: item.modeluuid }; + model.userData = { name: item.modelName, modelId: item.modelfileID, modelUuid: item.modelUuid, eventData: item.eventData }; model.position.set(...item.position); model.rotation.set(item.rotation.x, item.rotation.y, item.rotation.z); @@ -170,256 +170,142 @@ function processLoadedModel( } }); - itemsGroup?.current?.add(model); - setFloorItems((prevItems) => [ - ...(prevItems || []), - { - modeluuid: item.modeluuid, - modelname: item.modelname, - position: item.position, - rotation: item.rotation, - modelfileID: item.modelfileID, - isLocked: item.isLocked, - isVisible: item.isVisible, - }, - ]); + if (item.eventData) { + setFloorItems((prevItems) => [ + ...(prevItems || []), + { + modelUuid: item.modelUuid, + modelName: item.modelName, + position: item.position, + rotation: item.rotation, + modelfileID: item.modelfileID, + isLocked: item.isLocked, + isVisible: item.isVisible, + eventData: item.eventData, + }, + ]); - if (item.modelfileID === "a1ee92554935007b10b3eb05") { - const data = PointsCalculator( - 'Vehicle', - gltf.clone(), - new THREE.Vector3(...model.rotation) - ); - - if (!data || !data.points) return; - - const vehicleEvent: VehicleEventSchema = { - modelUuid: item.modeluuid, - modelName: item.modelname, - position: item.position, - rotation: [item.rotation.x, item.rotation.y, item.rotation.z], - state: "idle", - type: "vehicle", - speed: 1, - point: { - uuid: THREE.MathUtils.generateUUID(), - position: [data.points[0].x, data.points[0].y, data.points[0].z], - rotation: [0, 0, 0], - action: { - actionUuid: THREE.MathUtils.generateUUID(), - actionName: "Vehicle Action", - actionType: "travel", - unLoadDuration: 5, - loadCapacity: 10, - pickUpPoint: null, - unLoadPoint: null, - triggers: [] - } - } - }; - addEvent(vehicleEvent); - } else if (item.modelfileID === "7dc04e36882e4debbc1a8e3d") { - const data = PointsCalculator( - 'Conveyor', - gltf.clone(), - new THREE.Vector3(...model.rotation) - ); - - if (!data || !data.points) return; - - const ConveyorEvent: ConveyorEventSchema = { - modelUuid: item.modeluuid, - modelName: item.modelname, - position: item.position, - rotation: [item.rotation.x, item.rotation.y, item.rotation.z], - state: "idle", - type: "transfer", - speed: 1, - points: data.points.map((point: THREE.Vector3, index: number) => ({ - uuid: THREE.MathUtils.generateUUID(), - position: [point.x, point.y, point.z], - rotation: [0, 0, 0], - action: { - actionUuid: THREE.MathUtils.generateUUID(), - actionName: `Action ${index + 1}`, - actionType: 'default', - material: 'Default material', - delay: 0, - spawnInterval: 5, - spawnCount: 1, - triggers: [] - } - })) - }; - addEvent(ConveyorEvent); - } else if (item.modelfileID === "7dc04e36882e4debbc1a8e3d") { - // const data = PointsCalculator( - // 'Conveyor', - // gltf.clone(), - // new THREE.Vector3(...model.rotation) - // ); - - // if (!data || !data.points) return; - - // const points: ConveyorPointSchema[] = data.points.map((point: THREE.Vector3, index: number) => { - // const actionUuid = THREE.MathUtils.generateUUID(); - // return { - // uuid: THREE.MathUtils.generateUUID(), - // position: [point.x, point.y, point.z], - // rotation: [0, 0, 0], - // action: { - // actionUuid, - // actionName: `Action ${index}`, - // actionType: 'default', - // material: 'inherit', - // delay: 0, - // spawnInterval: 5, - // spawnCount: 1, - // triggers: [] - // } - // }; - // }); - - // points.forEach((point, index) => { - // if (index < points.length - 1) { - // const nextPoint = points[index + 1]; - // point.action.triggers.push({ - // triggerUuid: THREE.MathUtils.generateUUID(), - // triggerName: `Trigger 1`, - // triggerType: "onComplete", - // delay: 0, - // triggeredAsset: { - // triggeredModel: { - // modelName: item.modelname, - // modelUuid: item.modeluuid - // }, - // triggeredPoint: { - // pointName: `Point ${index + 1}`, - // pointUuid: nextPoint.uuid - // }, - // triggeredAction: { - // actionName: nextPoint.action.actionName, - // actionUuid: nextPoint.action.actionUuid - // } - // } - // }); - // } - // }); - - // const ConveyorEvent: ConveyorEventSchema = { - // modelUuid: item.modeluuid, - // modelName: item.modelname, - // position: item.position, - // rotation: [item.rotation.x, item.rotation.y, item.rotation.z], - // state: "idle", - // type: "transfer", - // speed: 1, - // points - // }; - // addEvent(ConveyorEvent); - } else if (item.modelfileID === "7dc04e36882e4debbc1a8e3d") { - const data = PointsCalculator( - 'Conveyor', - gltf.clone(), - new THREE.Vector3(...model.rotation) - ); - - if (!data || !data.points) return; - - const ConveyorEvent: ConveyorEventSchema = { - modelUuid: item.modeluuid, - modelName: item.modelname, - position: item.position, - rotation: [item.rotation.x, item.rotation.y, item.rotation.z], - state: "idle", - type: "transfer", - speed: 1, - points: data.points.map((point: THREE.Vector3, index: number) => ({ - uuid: THREE.MathUtils.generateUUID(), - position: [point.x, point.y, point.z], - rotation: [0, 0, 0], - action: { - actionUuid: THREE.MathUtils.generateUUID(), - actionName: `Action ${index}`, - actionType: 'default', - material: 'inherit', - delay: 0, - spawnInterval: 5, - spawnCount: 1, - triggers: [] - } - })) - }; - addEvent(ConveyorEvent); - } else if (item.modelfileID === "29dee78715ad5b853f5c346d") { - const data = PointsCalculator( - 'StaticMachine', - gltf.clone(), - new THREE.Vector3(...model.rotation) - ); - - if (!data || !data.points) return; - - const machineEvent: MachineEventSchema = { - modelUuid: item.modeluuid, - modelName: item.modelname, - position: item.position, - rotation: [item.rotation.x, item.rotation.y, item.rotation.z], - state: "idle", - type: "machine", - point: { - uuid: THREE.MathUtils.generateUUID(), - position: [data.points[0].x, data.points[0].y, data.points[0].z], - rotation: [0, 0, 0], - action: { - actionUuid: THREE.MathUtils.generateUUID(), - actionName: "Process Action", - actionType: "process", - processTime: 10, - swapMaterial: "material-id", - triggers: [] - } - } - }; - addEvent(machineEvent); - } else if (item.modelfileID === "52e6681fbb743a890d96c914") { - const data = PointsCalculator( - 'ArmBot', - gltf.clone(), - new THREE.Vector3(...model.rotation) - ); - - if (!data || !data.points) return; - - const roboticArmEvent: RoboticArmEventSchema = { - modelUuid: item.modeluuid, - modelName: item.modelname, - position: item.position, - rotation: [item.rotation.x, item.rotation.y, item.rotation.z], - state: "idle", - type: "roboticArm", - speed: 1, - point: { - uuid: THREE.MathUtils.generateUUID(), - position: [data.points[0].x, data.points[0].y, data.points[0].z], - rotation: [0, 0, 0], - actions: [ - { + if (item.eventData.type === "vehicle") { + const vehicleEvent: VehicleEventSchema = { + modelUuid: item.modelUuid, + modelName: item.modelName, + position: item.position, + rotation: [item.rotation.x, item.rotation.y, item.rotation.z], + state: "idle", + type: "vehicle", + speed: 1, + point: { + uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(), + position: [item.eventData.point?.position[0] || 0, item.eventData.point?.position[1] || 0, item.eventData.point?.position[2] || 0], + rotation: [item.eventData.point?.rotation[0] || 0, item.eventData.point?.rotation[1] || 0, item.eventData.point?.rotation[2] || 0], + action: { actionUuid: THREE.MathUtils.generateUUID(), - actionName: "Pick and Place", - actionType: "pickAndPlace", - process: { - startPoint: [0, 0, 0], - endPoint: [0, 0, 0] - }, + actionName: "Vehicle Action", + actionType: "travel", + unLoadDuration: 5, + loadCapacity: 10, + steeringAngle:0, + pickUpPoint: null, + unLoadPoint: null, triggers: [] } - ] - } - }; - addEvent(roboticArmEvent); + } + }; + addEvent(vehicleEvent); + } else if (item.eventData.type === "Conveyor") { + const ConveyorEvent: ConveyorEventSchema = { + modelUuid: item.modelUuid, + modelName: item.modelName, + position: item.position, + rotation: [item.rotation.x, item.rotation.y, item.rotation.z], + state: "idle", + type: "transfer", + speed: 1, + points: item.eventData.points?.map((point: any, index: number) => ({ + uuid: point.uuid || THREE.MathUtils.generateUUID(), + position: [point.position[0], point.position[1], point.position[2]], + rotation: [point.rotation[0], point.rotation[1], point.rotation[2]], + action: { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: `Action ${index + 1}`, + actionType: 'default', + material: 'Default material', + delay: 0, + spawnInterval: 5, + spawnCount: 1, + triggers: [] + } + })) || [], + }; + addEvent(ConveyorEvent); + } else if (item.eventData.type === "StaticMachine") { + const machineEvent: MachineEventSchema = { + modelUuid: item.modelUuid, + modelName: item.modelName, + position: item.position, + rotation: [item.rotation.x, item.rotation.y, item.rotation.z], + state: "idle", + type: "machine", + point: { + uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(), + position: [item.eventData.point?.position[0] || 0, item.eventData.point?.position[1] || 0, item.eventData.point?.position[2] || 0], + rotation: [item.eventData.point?.rotation[0] || 0, item.eventData.point?.rotation[1] || 0, item.eventData.point?.rotation[2] || 0], + action: { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: "Process Action", + actionType: "process", + processTime: 10, + swapMaterial: "material-id", + triggers: [] + } + } + }; + addEvent(machineEvent); + } else if (item.eventData.type === "ArmBot") { + const roboticArmEvent: RoboticArmEventSchema = { + modelUuid: item.modelUuid, + modelName: item.modelName, + position: item.position, + rotation: [item.rotation.x, item.rotation.y, item.rotation.z], + state: "idle", + type: "roboticArm", + speed: 1, + point: { + uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(), + position: [item.eventData.point?.position[0] || 0, item.eventData.point?.position[1] || 0, item.eventData.point?.position[2] || 0], + rotation: [item.eventData.point?.rotation[0] || 0, item.eventData.point?.rotation[1] || 0, item.eventData.point?.rotation[2] || 0], + actions: [ + { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: "Pick and Place", + actionType: "pickAndPlace", + process: { + startPoint: [0, 0, 0], + endPoint: [0, 0, 0] + }, + triggers: [] + } + ] + } + }; + addEvent(roboticArmEvent); + + } + } else { + setFloorItems((prevItems) => [ + ...(prevItems || []), + { + modelUuid: item.modelUuid, + modelName: item.modelName, + position: item.position, + rotation: item.rotation, + modelfileID: item.modelfileID, + isLocked: item.isLocked, + isVisible: item.isVisible, + }, + ]); } gsap.to(model.position, { y: item.position[1], duration: 1.5, ease: 'power2.out' }); diff --git a/app/src/modules/builder/IntialLoad/loadInitialWallItems.ts b/app/src/modules/builder/IntialLoad/loadInitialWallItems.ts index 34273af..f9799a8 100644 --- a/app/src/modules/builder/IntialLoad/loadInitialWallItems.ts +++ b/app/src/modules/builder/IntialLoad/loadInitialWallItems.ts @@ -22,9 +22,9 @@ async function loadInitialWallItems( const loadedWallItems = await Promise.all(storedWallItems.map(async (item) => { const loader = new GLTFLoader(); return new Promise((resolve) => { - loader.load(AssetConfigurations[item.modelname!].modelUrl, (gltf) => { + loader.load(AssetConfigurations[item.modelName!].modelUrl, (gltf) => { const model = gltf.scene; - model.uuid = item.modeluuid!; + model.uuid = item.modelUuid!; model.children[0].children.forEach((child: any) => { if (child.name !== "CSG_REF") { @@ -36,7 +36,7 @@ async function loadInitialWallItems( resolve({ type: item.type, model: model, - modelname: item.modelname, + modelName: item.modelName, scale: item.scale, csgscale: item.csgscale, csgposition: item.csgposition, diff --git a/app/src/modules/builder/geomentries/assets/addAssetModel.ts b/app/src/modules/builder/geomentries/assets/addAssetModel.ts index d7c278c..0bc7352 100644 --- a/app/src/modules/builder/geomentries/assets/addAssetModel.ts +++ b/app/src/modules/builder/geomentries/assets/addAssetModel.ts @@ -117,7 +117,7 @@ async function handleModelLoad( socket: Socket ) { const model = gltf.scene.clone(); - model.userData = { name: selectedItem.name, modelId: selectedItem.id, modeluuid: model.uuid }; + model.userData = { name: selectedItem.name, modelId: selectedItem.id, modelUuid: model.uuid }; model.position.set(intersectPoint!.x, 3 + intersectPoint!.y, intersectPoint!.z); model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap); @@ -137,8 +137,8 @@ async function handleModelLoad( } const newFloorItem: Types.FloorItemType = { - modeluuid: model.uuid, - modelname: selectedItem.name, + modelUuid: model.uuid, + modelName: selectedItem.name, modelfileID: selectedItem.id, position: [intersectPoint!.x, intersectPoint!.y, intersectPoint!.z], rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z }, @@ -153,8 +153,8 @@ async function handleModelLoad( // await setFloorItemApi( // organization, - // newFloorItem.modeluuid, - // newFloorItem.modelname, + // newFloorItem.modelUuid, + // newFloorItem.modelName, // newFloorItem.modelfileID, // newFloorItem.position, // { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z }, @@ -164,18 +164,6 @@ async function handleModelLoad( // SOCKET - const data = { - organization, - modeluuid: newFloorItem.modeluuid, - modelname: newFloorItem.modelname, - modelfileID: newFloorItem.modelfileID, - position: newFloorItem.position, - rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z }, - isLocked: false, - isVisible: true, - socketId: socket.id - }; - if (selectedItem.type) { const data = PointsCalculator( selectedItem.type, @@ -185,10 +173,14 @@ async function handleModelLoad( if (!data || !data.points) return; + const eventData: any = { + type: selectedItem.type, + }; + if (selectedItem.type === "Conveyor") { const ConveyorEvent: ConveyorEventSchema = { - modelUuid: newFloorItem.modeluuid, - modelName: newFloorItem.modelname, + modelUuid: newFloorItem.modelUuid, + modelName: newFloorItem.modelName, position: newFloorItem.position, rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z], state: "idle", @@ -209,12 +201,18 @@ async function handleModelLoad( triggers: [] } })) - } + }; addEvent(ConveyorEvent); + eventData.points = ConveyorEvent.points.map(point => ({ + uuid: point.uuid, + position: point.position, + rotation: point.rotation + })); + } else if (selectedItem.type === "Vehicle") { const vehicleEvent: VehicleEventSchema = { - modelUuid: newFloorItem.modeluuid, - modelName: newFloorItem.modelname, + modelUuid: newFloorItem.modelUuid, + modelName: newFloorItem.modelName, position: newFloorItem.position, rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z], state: "idle", @@ -230,6 +228,7 @@ async function handleModelLoad( actionType: "travel", unLoadDuration: 5, loadCapacity: 10, + steeringAngle:0, pickUpPoint: null, unLoadPoint: null, triggers: [] @@ -237,10 +236,16 @@ async function handleModelLoad( } }; addEvent(vehicleEvent); + eventData.point = { + uuid: vehicleEvent.point.uuid, + position: vehicleEvent.point.position, + rotation: vehicleEvent.point.rotation + }; + } else if (selectedItem.type === "ArmBot") { const roboticArmEvent: RoboticArmEventSchema = { - modelUuid: newFloorItem.modeluuid, - modelName: newFloorItem.modelname, + modelUuid: newFloorItem.modelUuid, + modelName: newFloorItem.modelName, position: newFloorItem.position, rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z], state: "idle", @@ -265,10 +270,17 @@ async function handleModelLoad( } }; addEvent(roboticArmEvent); + console.log('roboticArmEvent: ', roboticArmEvent); + eventData.point = { + uuid: roboticArmEvent.point.uuid, + position: roboticArmEvent.point.position, + rotation: roboticArmEvent.point.rotation + }; + } else if (selectedItem.type === "StaticMachine") { const machineEvent: MachineEventSchema = { - modelUuid: newFloorItem.modeluuid, - modelName: newFloorItem.modelname, + modelUuid: newFloorItem.modelUuid, + modelName: newFloorItem.modelName, position: newFloorItem.position, rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z], state: "idle", @@ -288,19 +300,65 @@ async function handleModelLoad( } }; addEvent(machineEvent); + eventData.point = { + uuid: machineEvent.point.uuid, + position: machineEvent.point.position, + rotation: machineEvent.point.rotation + }; } + + const completeData = { + organization, + modelUuid: newFloorItem.modelUuid, + modelName: newFloorItem.modelName, + modelfileID: newFloorItem.modelfileID, + position: newFloorItem.position, + rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z }, + isLocked: false, + isVisible: true, + socketId: socket.id, + eventData: eventData + }; + + model.userData.eventData = eventData; + + newFloorItem.eventData = eventData; + + setFloorItems((prevItems) => { + const updatedItems = [...(prevItems || []), newFloorItem]; + localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); + return updatedItems; + }); + + socket.emit("v2:model-asset:add", completeData); + + gsap.to(model.position, { y: newFloorItem.position[1], duration: 1.5, ease: "power2.out" }); + gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 1.5, ease: "power2.out", onComplete: () => { toast.success("Model Added!"); } }); + } else { + + const data = { + organization, + modelUuid: newFloorItem.modelUuid, + modelName: newFloorItem.modelName, + modelfileID: newFloorItem.modelfileID, + position: newFloorItem.position, + rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z }, + isLocked: false, + isVisible: true, + socketId: socket.id + }; + + setFloorItems((prevItems) => { + const updatedItems = [...(prevItems || []), newFloorItem]; + localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); + return updatedItems; + }); + + socket.emit("v2:model-asset:add", data); + + gsap.to(model.position, { y: newFloorItem.position[1], duration: 1.5, ease: "power2.out" }); + gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 1.5, ease: "power2.out", onComplete: () => { toast.success("Model Added!"); } }); } - - setFloorItems((prevItems) => { - const updatedItems = [...(prevItems || []), newFloorItem]; - localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); - return updatedItems; - }); - - socket.emit("v2:model-asset:add", data); - - gsap.to(model.position, { y: newFloorItem.position[1], duration: 1.5, ease: "power2.out" }); - gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 1.5, ease: "power2.out", onComplete: () => { toast.success("Model Added!"); } }); } export default addAssetModel; diff --git a/app/src/modules/builder/geomentries/assets/assetManager.ts b/app/src/modules/builder/geomentries/assets/assetManager.ts index 5ff75a3..5f7798e 100644 --- a/app/src/modules/builder/geomentries/assets/assetManager.ts +++ b/app/src/modules/builder/geomentries/assets/assetManager.ts @@ -58,7 +58,7 @@ export default async function assetManager( // Check Three.js Cache const cachedModel = THREE.Cache.get(item.modelfileID!); if (cachedModel) { - // console.log(`[Cache] Fetching ${item.modelname}`); + // console.log(`[Cache] Fetching ${item.modelName}`); processLoadedModel(cachedModel.scene.clone(), item, itemsGroup, resolve); return; } @@ -66,7 +66,7 @@ export default async function assetManager( // Check IndexedDB const indexedDBModel = await retrieveGLTF(item.modelfileID!); if (indexedDBModel) { - // console.log(`[IndexedDB] Fetching ${item.modelname}`); + // console.log(`[IndexedDB] Fetching ${item.modelName}`); const blobUrl = URL.createObjectURL(indexedDBModel); loader.load( blobUrl, @@ -78,7 +78,7 @@ export default async function assetManager( }, undefined, (error) => { - toast.error(`[IndexedDB] Error loading ${item.modelname}:`); + toast.error(`[IndexedDB] Error loading ${item.modelName}:`); resolve(); } ); @@ -86,7 +86,7 @@ export default async function assetManager( } // Fetch from Backend - // console.log(`[Backend] Fetching ${item.modelname}`); + // console.log(`[Backend] Fetching ${item.modelName}`); loader.load( modelUrl, async (gltf) => { @@ -97,7 +97,7 @@ export default async function assetManager( }, undefined, (error) => { - toast.error(`[Backend] Error loading ${item.modelname}:`); + toast.error(`[Backend] Error loading ${item.modelName}:`); resolve(); } ); @@ -112,16 +112,16 @@ export default async function assetManager( ) { if (!activePromises.get(taskId)) return; // Stop processing if task is canceled - const existingModel = itemsGroup?.current?.getObjectByProperty("uuid", item.modeluuid); + const existingModel = itemsGroup?.current?.getObjectByProperty("uuid", item.modelUuid); if (existingModel) { - // console.log(`Model ${item.modelname} already exists in the scene.`); + // console.log(`Model ${item.modelName} already exists in the scene.`); resolve(); return; } const model = gltf; - model.uuid = item.modeluuid; - model.userData = { name: item.modelname, modelId: item.modelfileID, modeluuid: item.modeluuid }; + model.uuid = item.modelUuid; + model.userData = { name: item.modelName, modelId: item.modelfileID, modelUuid: item.modelUuid }; model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap); model.position.set(...item.position); model.rotation.set(item.rotation.x, item.rotation.y, item.rotation.z); diff --git a/app/src/modules/builder/geomentries/assets/deleteFloorItems.ts b/app/src/modules/builder/geomentries/assets/deleteFloorItems.ts index 18136b3..f13ebee 100644 --- a/app/src/modules/builder/geomentries/assets/deleteFloorItems.ts +++ b/app/src/modules/builder/geomentries/assets/deleteFloorItems.ts @@ -5,6 +5,8 @@ import * as Types from "../../../../types/world/worldTypes"; // import { deleteFloorItem } from '../../../../services/factoryBuilder/assest/floorAsset/deleteFloorItemApi'; import { Socket } from 'socket.io-client'; import { getFloorAssets } from '../../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi'; +import { useEventsStore } from "../../../../store/simulation/useEventsStore"; +import { useProductStore } from "../../../../store/simulation/useProductStore"; async function DeleteFloorItems( itemsGroup: Types.RefGroup, @@ -22,7 +24,7 @@ async function DeleteFloorItems( const items = await getFloorAssets(organization); const removedItem = items.find( - (item: { modeluuid: string }) => item.modeluuid === hoveredDeletableFloorItem.current?.uuid + (item: { modelUuid: string }) => item.modelUuid === hoveredDeletableFloorItem.current?.uuid ); if (!removedItem) { @@ -31,26 +33,29 @@ async function DeleteFloorItems( //REST - // const response = await deleteFloorItem(organization, removedItem.modeluuid, removedItem.modelname); + // const response = await deleteFloorItem(organization, removedItem.modelUuid, removedItem.modelName); //SOCKET const data = { organization: organization, - modeluuid: removedItem.modeluuid, - modelname: removedItem.modelname, + modelUuid: removedItem.modelUuid, + modelName: removedItem.modelName, socketId: socket.id } const response = socket.emit('v2:model-asset:delete', data) + useEventsStore.getState().removeEvent(removedItem.modelUuid); + useProductStore.getState().deleteEvent(removedItem.modelUuid); + if (response) { const updatedItems = items.filter( - (item: { modeluuid: string }) => item.modeluuid !== hoveredDeletableFloorItem.current?.uuid + (item: { modelUuid: string }) => item.modelUuid !== hoveredDeletableFloorItem.current?.uuid ); const storedItems = JSON.parse(localStorage.getItem("FloorItems") || '[]'); - const updatedStoredItems = storedItems.filter((item: { modeluuid: string }) => item.modeluuid !== hoveredDeletableFloorItem.current?.uuid); + const updatedStoredItems = storedItems.filter((item: { modelUuid: string }) => item.modelUuid !== hoveredDeletableFloorItem.current?.uuid); localStorage.setItem("FloorItems", JSON.stringify(updatedStoredItems)); if (hoveredDeletableFloorItem.current) { diff --git a/app/src/modules/builder/geomentries/walls/addWallItems.ts b/app/src/modules/builder/geomentries/walls/addWallItems.ts index 9c578aa..fd9eb48 100644 --- a/app/src/modules/builder/geomentries/walls/addWallItems.ts +++ b/app/src/modules/builder/geomentries/walls/addWallItems.ts @@ -43,7 +43,7 @@ async function AddWallItems( const newWallItem = { type: config.type, model: model, - modelname: selected, + modelName: selected, scale: config.scale, csgscale: config.csgscale, csgposition: config.csgposition, @@ -59,7 +59,7 @@ async function AddWallItems( // await setWallItem( // organization, // model.uuid, - // newWallItem.modelname, + // newWallItem.modelName, // newWallItem.type!, // newWallItem.csgposition!, // newWallItem.csgscale!, @@ -72,8 +72,8 @@ async function AddWallItems( const data = { organization: organization, - modeluuid: model.uuid, - modelname: newWallItem.modelname, + modelUuid: model.uuid, + modelName: newWallItem.modelName, type: newWallItem.type!, csgposition: newWallItem.csgposition!, csgscale: newWallItem.csgscale!, @@ -92,7 +92,7 @@ async function AddWallItems( const { model, ...rest } = item; return { ...rest, - modeluuid: model?.uuid, + modelUuid: model?.uuid, }; }); diff --git a/app/src/modules/builder/geomentries/walls/deleteWallItems.ts b/app/src/modules/builder/geomentries/walls/deleteWallItems.ts index abbf0c8..b5d40f4 100644 --- a/app/src/modules/builder/geomentries/walls/deleteWallItems.ts +++ b/app/src/modules/builder/geomentries/walls/deleteWallItems.ts @@ -28,14 +28,14 @@ function DeleteWallItems( //REST - // await deleteWallItem(organization, removedItem?.model?.uuid!, removedItem?.modelname!) + // await deleteWallItem(organization, removedItem?.model?.uuid!, removedItem?.modelName!) //SOCKET const data = { organization: organization, - modeluuid: removedItem?.model?.uuid!, - modelname: removedItem?.modelname!, + modelUuid: removedItem?.model?.uuid!, + modelName: removedItem?.modelName!, socketId: socket.id } @@ -45,7 +45,7 @@ function DeleteWallItems( const { model, ...rest } = item; return { ...rest, - modeluuid: model?.uuid, + modelUuid: model?.uuid, }; }); diff --git a/app/src/modules/builder/groups/wallItemsGroup.tsx b/app/src/modules/builder/groups/wallItemsGroup.tsx index 43845e7..f993754 100644 --- a/app/src/modules/builder/groups/wallItemsGroup.tsx +++ b/app/src/modules/builder/groups/wallItemsGroup.tsx @@ -91,7 +91,7 @@ const WallItemsGroup = ({ currentWallItem, AssetConfigurations, hoveredDeletable const { model, ...rest } = item; return { ...rest, - modeluuid: model?.uuid, + modelUuid: model?.uuid, }; }); @@ -110,7 +110,7 @@ const WallItemsGroup = ({ currentWallItem, AssetConfigurations, hoveredDeletable // await setWallItem( // organization, // currentItem?.model?.uuid, - // currentItem.modelname, + // currentItem.modelName, // currentItem.type!, // currentItem.csgposition!, // currentItem.csgscale!, @@ -123,8 +123,8 @@ const WallItemsGroup = ({ currentWallItem, AssetConfigurations, hoveredDeletable const data = { organization: organization, - modeluuid: currentItem.model?.uuid!, - modelname: currentItem.modelname!, + modelUuid: currentItem.model?.uuid!, + modelName: currentItem.modelName!, type: currentItem.type!, csgposition: currentItem.csgposition!, csgscale: currentItem.csgscale!, diff --git a/app/src/modules/builder/groups/zoneGroup.tsx b/app/src/modules/builder/groups/zoneGroup.tsx index d218534..1eadd24 100644 --- a/app/src/modules/builder/groups/zoneGroup.tsx +++ b/app/src/modules/builder/groups/zoneGroup.tsx @@ -69,7 +69,9 @@ const ZoneGroup: React.FC = () => { }, transparent: true, depthWrite: false, - }), []); + }), + [] + ); useEffect(() => { const fetchZones = async () => { @@ -148,6 +150,7 @@ const ZoneGroup: React.FC = () => { } }, [toolMode, toggleView]); + // eslint-disable-next-line react-hooks/exhaustive-deps const addZoneToBackend = async (zone: { zoneId: string; zoneName: string; @@ -503,6 +506,15 @@ const ZoneGroup: React.FC = () => { draggedSphere, movePoint, activeLayer, + raycaster, + pointer, + controls, + plane, + setZones, + setZonePoints, + addZoneToBackend, + handleDeleteZone, + updateZoneToBackend, ]); useFrame(() => { @@ -551,6 +563,7 @@ const ZoneGroup: React.FC = () => { key={index} position={midpoint} rotation={[0, -angle, 0]} + visible={false} > { const navigate = useNavigate(); @@ -20,13 +20,14 @@ const CamModelsGroup = () => { const { socket } = useSocketStore(); const { activeModule } = useModuleStore(); + // eslint-disable-next-line react-hooks/exhaustive-deps const loader = new GLTFLoader(); const dracoLoader = new DRACOLoader(); dracoLoader.setDecoderPath("three/examples/jsm/libs/draco/gltf/"); loader.setDRACOLoader(dracoLoader); const [cams, setCams] = useState([]); - const [models, setModels] = useState>({}); + const [models, setModels] = useState>({}); const dedupeCams = (cams: any[]) => { const seen = new Set(); @@ -102,6 +103,7 @@ const CamModelsGroup = () => { }); socket.on("cameraUpdateResponse", (data: any) => { + if ( !groupRef.current || socket.id === data.socketId || @@ -122,6 +124,11 @@ const CamModelsGroup = () => { data.data.rotation.y, data.data.rotation.z ), + target: new THREE.Vector3( + data.data.target.x, + data.data.target.y, + data.data.target.z + ), }, })); }); @@ -131,7 +138,7 @@ const CamModelsGroup = () => { socket.off("userDisConnectResponse"); socket.off("cameraUpdateResponse"); }; - }, [socket]); + }, [email, loader, navigate, setActiveUsers, socket]); useFrame(() => { if (!groupRef.current) return; @@ -217,9 +224,11 @@ const CamModelsGroup = () => { position={[-0.015, 0, 0.7]} > diff --git a/app/src/modules/collaboration/camera/collabUserIcon.tsx b/app/src/modules/collaboration/camera/collabUserIcon.tsx new file mode 100644 index 0000000..dcdb73b --- /dev/null +++ b/app/src/modules/collaboration/camera/collabUserIcon.tsx @@ -0,0 +1,56 @@ +import React from "react"; +import CustomAvatar from "../users/Avatar"; +import { useSelectedUserStore } from "../../../store/useCollabStore"; +import { useCamMode } from "../../../store/store"; + +interface CollabUserIconProps { + userName: string; + userImage?: string; + color: string; + position?: { + x: number; + y: number; + z: number; + }; + rotation?: { + x: number; + y: number; + z: number; + }; +} + +const CollabUserIcon: React.FC = ({ + userImage, + userName, + color, + position, + rotation, +}) => { + const { setSelectedUser } = useSelectedUserStore(); + const { setCamMode } = useCamMode(); + return ( +
+ +
+ {userName} +
+
+ ); +}; + +export default CollabUserIcon; diff --git a/app/src/modules/collaboration/collaboration.tsx b/app/src/modules/collaboration/collaboration.tsx index 84d3ab1..8835185 100644 --- a/app/src/modules/collaboration/collaboration.tsx +++ b/app/src/modules/collaboration/collaboration.tsx @@ -1,14 +1,32 @@ -import React from 'react' -import CamModelsGroup from './camera/collabCams' +import React, { useEffect } from "react"; +import CamModelsGroup from "./camera/collabCams"; +import { useSelectedUserStore } from "../../store/useCollabStore"; +import { useThree } from "@react-three/fiber"; +import setCameraView from "./functions/setCameraView"; +import { useCamMode } from "../../store/store"; -const Collaboration = () => { - return ( - <> +const Collaboration: React.FC = () => { + const { selectedUser } = useSelectedUserStore(); + const { camMode } = useCamMode(); + const { camera, controls } = useThree(); // Access R3F camera and controls - + useEffect(() => { + if(camMode !== "FollowPerson") return; + // If a user is selected, set the camera view to their location + // and update the camera and controls accordingly + if (selectedUser?.location) { + const { position, rotation } = selectedUser.location; + setCameraView({ + controls, + camera, + position, + rotation, + username: selectedUser.name, + }); + } + }, [selectedUser, camera, controls, camMode]); - - ) -} + return ; +}; -export default Collaboration \ No newline at end of file +export default Collaboration; diff --git a/app/src/functions/users/functions/getAvatarColor.ts b/app/src/modules/collaboration/functions/getAvatarColor.ts similarity index 98% rename from app/src/functions/users/functions/getAvatarColor.ts rename to app/src/modules/collaboration/functions/getAvatarColor.ts index d9a5d37..6d34edc 100644 --- a/app/src/functions/users/functions/getAvatarColor.ts +++ b/app/src/modules/collaboration/functions/getAvatarColor.ts @@ -27,7 +27,7 @@ export function getAvatarColor(index: number, name?: string): string { const localStorageKey = "userAvatarColors"; // Check if local storage is available if (name) { - let userColors = JSON.parse(localStorage.getItem(localStorageKey) || "{}"); + let userColors = JSON.parse(localStorage.getItem(localStorageKey) ?? "{}"); // Check if the user already has an assigned color if (userColors[name]) { diff --git a/app/src/functions/users/functions/getInitials.ts b/app/src/modules/collaboration/functions/getInitials.ts similarity index 100% rename from app/src/functions/users/functions/getInitials.ts rename to app/src/modules/collaboration/functions/getInitials.ts diff --git a/app/src/modules/collaboration/functions/setCameraView.ts b/app/src/modules/collaboration/functions/setCameraView.ts new file mode 100644 index 0000000..8bae59b --- /dev/null +++ b/app/src/modules/collaboration/functions/setCameraView.ts @@ -0,0 +1,33 @@ +import * as THREE from 'three'; + +interface SetCameraViewProps { + controls: any; + camera: THREE.Camera; + position: THREE.Vector3 | { x: number; y: number; z: number }; + rotation: THREE.Euler | { x: number; y: number; z: number }; + username?: string; + target?: THREE.Vector3 | { x: number; y: number; z: number }; +} + +export default async function setCameraView({ + controls, + camera, + position, + rotation, + username, + target +}: SetCameraViewProps) { + if (!controls || !camera) return; + + // Normalize position + const newPosition = position instanceof THREE.Vector3 + ? position + : new THREE.Vector3(position.x, position.y, position.z); + + if (controls.setTarget) { + controls?.setLookAt(...newPosition.toArray(), newPosition.x, 0, newPosition.z, true); + } + + // Optionally you can log + console.log(`Camera view updated by ${username ?? 'unknown user'}`); +} diff --git a/app/src/modules/collaboration/socket/socketResponses.dev.tsx b/app/src/modules/collaboration/socket/socketResponses.dev.tsx index 3d9cddf..86e41c2 100644 --- a/app/src/modules/collaboration/socket/socketResponses.dev.tsx +++ b/app/src/modules/collaboration/socket/socketResponses.dev.tsx @@ -99,13 +99,13 @@ export default function SocketResponses({ try { isTempLoader.current = true; - const cachedModel = THREE.Cache.get(data.data.modelname); + const cachedModel = THREE.Cache.get(data.data.modelName); let url; if (cachedModel) { - // console.log(`Getting ${data.data.modelname} from cache`); + // console.log(`Getting ${data.data.modelName} from cache`); const model = cachedModel.scene.clone(); - model.uuid = data.data.modeluuid; - model.userData = { name: data.data.modelname, modelId: data.data.modelfileID, modeluuid: data.data.modeluuid }; + model.uuid = data.data.modelUuid; + model.userData = { name: data.data.modelName, modelId: data.data.modelfileID, modelUuid: data.data.modelUuid }; model.position.set(...data.data.position as [number, number, number]); model.rotation.set(data.data.rotation.x, data.data.rotation.y, data.data.rotation.z); model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap); @@ -130,8 +130,8 @@ export default function SocketResponses({ } const newFloorItem: Types.FloorItemType = { - modeluuid: data.data.modeluuid, - modelname: data.data.modelname, + modelUuid: data.data.modelUuid, + modelName: data.data.modelName, modelfileID: data.data.modelfileID, position: [...data.data.position as [number, number, number]], rotation: { @@ -153,12 +153,12 @@ export default function SocketResponses({ gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 1.5, ease: 'power2.out', onComplete: () => { toast.success("Model Added!") } }); } else { - const indexedDBModel = await retrieveGLTF(data.data.modelname); + const indexedDBModel = await retrieveGLTF(data.data.modelName); if (indexedDBModel) { - // console.log(`Getting ${data.data.modelname} from IndexedDB`); + // console.log(`Getting ${data.data.modelName} from IndexedDB`); url = URL.createObjectURL(indexedDBModel); } else { - // console.log(`Getting ${data.data.modelname} from Backend`); + // console.log(`Getting ${data.data.modelName} from Backend`); url = `${url_Backend_dwinzo}/api/v2/AssetFile/${data.data.modelfileID}`; const modelBlob = await fetch(url).then((res) => res.blob()); await storeGLTF(data.data.modelfileID, modelBlob); @@ -178,8 +178,8 @@ export default function SocketResponses({ URL.revokeObjectURL(url); THREE.Cache.remove(url); const model = gltf.scene; - model.uuid = data.data.modeluuid; - model.userData = { name: data.data.modelname, modelId: data.data.modelfileID, modeluuid: data.data.modeluuid }; + model.uuid = data.data.modelUuid; + model.userData = { name: data.data.modelName, modelId: data.data.modelfileID, modelUuid: data.data.modelUuid }; model.position.set(...data.data.position as [number, number, number]); model.rotation.set(data.data.rotation.x, data.data.rotation.y, data.data.rotation.z); model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap); @@ -204,8 +204,8 @@ export default function SocketResponses({ } const newFloorItem: Types.FloorItemType = { - modeluuid: data.data.modeluuid, - modelname: data.data.modelname, + modelUuid: data.data.modelUuid, + modelName: data.data.modelName, modelfileID: data.data.modelfileID, position: [...data.data.position as [number, number, number]], rotation: { @@ -226,7 +226,7 @@ export default function SocketResponses({ gsap.to(model.position, { y: data.data.position[1], duration: 1.5, ease: 'power2.out' }); gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 1.5, ease: 'power2.out', onComplete: () => { toast.success("Model Added!") } }); - THREE.Cache.add(data.data.modelname, gltf); + THREE.Cache.add(data.data.modelName, gltf); }, () => { TempLoader(new THREE.Vector3(...data.data.position), isTempLoader, tempLoader, itemsGroup); }); @@ -234,7 +234,7 @@ export default function SocketResponses({ } else if (data.message === "Model updated successfully") { itemsGroup.current?.children.forEach((item: THREE.Group) => { - if (item.uuid === data.data.modeluuid) { + if (item.uuid === data.data.modelUuid) { item.position.set(...data.data.position as [number, number, number]); item.rotation.set(data.data.rotation.x, data.data.rotation.y, data.data.rotation.z); } @@ -246,7 +246,7 @@ export default function SocketResponses({ } let updatedItem: any = null; const updatedItems = prevItems.map((item) => { - if (item.modeluuid === data.data.modeluuid) { + if (item.modelUuid === data.data.modelUuid) { updatedItem = { ...item, position: [...data.data.position] as [number, number, number], @@ -269,15 +269,15 @@ export default function SocketResponses({ return } if (data.message === "Model deleted successfully") { - const deletedUUID = data.data.modeluuid; + const deletedUUID = data.data.modelUuid; let items = JSON.parse(localStorage.getItem("FloorItems")!); const updatedItems = items.filter( - (item: { modeluuid: string }) => item.modeluuid !== deletedUUID + (item: { modelUuid: string }) => item.modelUuid !== deletedUUID ); const storedItems = JSON.parse(localStorage.getItem("FloorItems") || '[]'); - const updatedStoredItems = storedItems.filter((item: { modeluuid: string }) => item.modeluuid !== deletedUUID); + const updatedStoredItems = storedItems.filter((item: { modelUuid: string }) => item.modelUuid !== deletedUUID); localStorage.setItem("FloorItems", JSON.stringify(updatedStoredItems)); itemsGroup.current.children.forEach((item: any) => { @@ -519,7 +519,7 @@ export default function SocketResponses({ return } if (data.message === "wallitem deleted") { - const deletedUUID = data.data.modeluuid; + const deletedUUID = data.data.modelUuid; let WallItemsRef = wallItems; const Items = WallItemsRef.filter((item: any) => item.model?.uuid !== deletedUUID); @@ -531,7 +531,7 @@ export default function SocketResponses({ const { model, ...rest } = item; return { ...rest, - modeluuid: model?.uuid, + modelUuid: model?.uuid, }; }); @@ -550,9 +550,9 @@ export default function SocketResponses({ } if (data.message === "wallIitem created") { const loader = new GLTFLoader(); - loader.load(AssetConfigurations[data.data.modelname].modelUrl, async (gltf) => { + loader.load(AssetConfigurations[data.data.modelName].modelUrl, async (gltf) => { const model = gltf.scene; - model.uuid = data.data.modeluuid; + model.uuid = data.data.modelUuid; model.children[0].children.forEach((child) => { if (child.name !== "CSG_REF") { child.castShadow = true; @@ -563,7 +563,7 @@ export default function SocketResponses({ const newWallItem = { type: data.data.type, model: model, - modelname: data.data.modelname, + modelName: data.data.modelName, scale: data.data.scale, csgscale: data.data.csgscale, csgposition: data.data.csgposition, @@ -578,7 +578,7 @@ export default function SocketResponses({ const { model, ...rest } = item; return { ...rest, - modeluuid: model?.uuid, + modelUuid: model?.uuid, }; }); @@ -589,7 +589,7 @@ export default function SocketResponses({ }); }); } else if (data.message === "wallIitem updated") { - const updatedUUID = data.data.modeluuid; + const updatedUUID = data.data.modelUuid; setWallItems((prevItems: any) => { const updatedItems = prevItems.map((item: any) => { @@ -610,7 +610,7 @@ export default function SocketResponses({ const { model, ...rest } = item; return { ...rest, - modeluuid: model?.uuid, + modelUuid: model?.uuid, }; }); diff --git a/app/src/functions/users/Avatar.tsx b/app/src/modules/collaboration/users/Avatar.tsx similarity index 87% rename from app/src/functions/users/Avatar.tsx rename to app/src/modules/collaboration/users/Avatar.tsx index d3e5dca..899ecb4 100644 --- a/app/src/functions/users/Avatar.tsx +++ b/app/src/modules/collaboration/users/Avatar.tsx @@ -1,6 +1,5 @@ import React, { useEffect, useState } from "react"; -import { getInitials } from "./functions/getInitials"; -import { getAvatarColor } from "./functions/getAvatarColor"; +import { getInitials } from "../functions/getInitials"; interface AvatarProps { name: string; // Name can be a full name or initials @@ -26,7 +25,7 @@ const CustomAvatar: React.FC = ({ const initials = getInitials(name); // Convert name to initials if needed // Draw background - ctx.fillStyle = color || "#323232"; // Use color prop or generate color based on index + ctx.fillStyle = color ?? "#323232"; // Use color prop or generate color based on index ctx.fillRect(0, 0, size, size); // Draw initials @@ -40,7 +39,7 @@ const CustomAvatar: React.FC = ({ const dataURL = canvas.toDataURL("image/png"); setImageSrc(dataURL); } - }, [name, size, textColor]); + }, [color, name, size, textColor]); if (!imageSrc) { return null; // Return null while the image is being generated diff --git a/app/src/modules/market/CardsContainer.tsx b/app/src/modules/market/CardsContainer.tsx index cdbce81..eabcbe4 100644 --- a/app/src/modules/market/CardsContainer.tsx +++ b/app/src/modules/market/CardsContainer.tsx @@ -42,31 +42,33 @@ const CardsContainer: React.FC = ({ models }) => { }; return ( -
-
Products You May Like
-
- {models.length > 0 && - models.map((assetDetail) => ( - +
+
Products You May Like
+
+ {models.length > 0 && + models.map((assetDetail) => ( + + ))} + {/* */} + {selectedCard && ( + - ))} - {/* */} - {selectedCard && ( - - )} - {/* */} + )} + {/* */} +
); diff --git a/app/src/modules/scene/camera/camMode.tsx b/app/src/modules/scene/camera/camMode.tsx index b4a59b0..d216ee1 100644 --- a/app/src/modules/scene/camera/camMode.tsx +++ b/app/src/modules/scene/camera/camMode.tsx @@ -1,90 +1,144 @@ -import { useFrame, useThree } from '@react-three/fiber'; -import React, { useEffect, useState } from 'react'; -import * as CONSTANTS from '../../../types/world/worldConstants'; -import { useCamMode, useToggleView } from '../../../store/store'; -import { useKeyboardControls } from '@react-three/drei'; -import switchToThirdPerson from './switchToThirdPerson'; -import switchToFirstPerson from './switchToFirstPerson'; -import { detectModifierKeys } from '../../../utils/shortcutkeys/detectModifierKeys'; +import { useFrame, useThree } from "@react-three/fiber"; +import React, { useEffect, useState } from "react"; +import * as CONSTANTS from "../../../types/world/worldConstants"; +import { useCamMode, useToggleView } from "../../../store/store"; +import { useKeyboardControls } from "@react-three/drei"; +import switchToThirdPerson from "./switchToThirdPerson"; +import switchToFirstPerson from "./switchToFirstPerson"; +import { detectModifierKeys } from "../../../utils/shortcutkeys/detectModifierKeys"; const CamMode: React.FC = () => { - const { camMode, setCamMode } = useCamMode(); - const [, get] = useKeyboardControls() - const [isTransitioning, setIsTransitioning] = useState(false); - const state: any = useThree(); - const { toggleView } = useToggleView(); + const { camMode, setCamMode } = useCamMode(); + const [, get] = useKeyboardControls(); + const [isTransitioning, setIsTransitioning] = useState(false); + const state: any = useThree(); + const { toggleView } = useToggleView(); + const [isShiftActive, setIsShiftActive] = useState(false); - useEffect(() => { - const handlePointerLockChange = async () => { - if (document.pointerLockElement && !toggleView) { - // console.log('Pointer is locked'); - } else { - // console.log('Pointer is unlocked'); - if (camMode === "FirstPerson" && !toggleView) { - setCamMode("ThirdPerson"); - await switchToThirdPerson(state.controls, state.camera); - } - } - }; - - document.addEventListener('pointerlockchange', handlePointerLockChange); - - return () => { - document.removeEventListener('pointerlockchange', handlePointerLockChange); - }; - }, [camMode, toggleView, setCamMode, state.controls, state.camera]); - - useEffect(() => { - const handleKeyPress = async (event: any) => { - if (!state.controls) return; - - const keyCombination = detectModifierKeys(event); - - if (keyCombination === "/" && !isTransitioning && !toggleView) { - setIsTransitioning(true); - state.controls.mouseButtons.left = CONSTANTS.controlsTransition.leftMouse; - state.controls.mouseButtons.right = CONSTANTS.controlsTransition.rightMouse; - state.controls.mouseButtons.wheel = CONSTANTS.controlsTransition.wheelMouse; - state.controls.mouseButtons.middle = CONSTANTS.controlsTransition.middleMouse; - - if (camMode === 'ThirdPerson') { - setCamMode("FirstPerson"); - await switchToFirstPerson(state.controls, state.camera); - } else if (camMode === "FirstPerson") { - setCamMode("ThirdPerson"); - await switchToThirdPerson(state.controls, state.camera); - } - - setIsTransitioning(false); - } - }; - - window.addEventListener("keydown", handleKeyPress); - return () => { - window.removeEventListener("keydown", handleKeyPress); - }; - }, [camMode, isTransitioning, toggleView, state.controls, state.camera, setCamMode]); - - useFrame(() => { - const { forward, backward, left, right } = get(); - if (!state.controls) return - if (!state.controls || camMode === "ThirdPerson" || !document.pointerLockElement) return; - - if (forward) { - state.controls.forward(CONSTANTS.firstPersonControls.forwardSpeed, true) + useEffect(() => { + const handlePointerLockChange = async () => { + if (document.pointerLockElement && !toggleView) { + // Pointer is locked + } else { + // Pointer is unlocked + if (camMode === "FirstPerson" && !toggleView) { + setCamMode("ThirdPerson"); + await switchToThirdPerson(state.controls, state.camera); } - if (backward) { - state.controls.forward(CONSTANTS.firstPersonControls.backwardSpeed, true) - } - if (left) { - state.controls.truck(CONSTANTS.firstPersonControls.leftSpeed, 0, true) - } - if (right) { - state.controls.truck(CONSTANTS.firstPersonControls.rightSpeed, 0, true) - } - }); + } + }; - return null; // This component does not render any UI + document.addEventListener("pointerlockchange", handlePointerLockChange); + + return () => { + document.removeEventListener( + "pointerlockchange", + handlePointerLockChange + ); + }; + }, [camMode, toggleView, setCamMode, state.controls, state.camera]); + + useEffect(() => { + const handleKeyDown = (event: KeyboardEvent) => { + if (event.key === "Shift") { + setIsShiftActive(true); + } + }; + + const handleKeyUp = (event: KeyboardEvent) => { + if (event.key === "Shift") { + setIsShiftActive(false); + } + }; + + window.addEventListener("keydown", handleKeyDown); + window.addEventListener("keyup", handleKeyUp); + + return () => { + window.removeEventListener("keydown", handleKeyDown); + window.removeEventListener("keyup", handleKeyUp); + }; + }, []); + + useEffect(() => { + const handleKeyPress = async (event: KeyboardEvent) => { + if (!state.controls) return; + + const keyCombination = detectModifierKeys(event); + + if (keyCombination === "/" && !isTransitioning && !toggleView) { + setIsTransitioning(true); + + state.controls.mouseButtons.left = + CONSTANTS.controlsTransition.leftMouse; + state.controls.mouseButtons.right = + CONSTANTS.controlsTransition.rightMouse; + state.controls.mouseButtons.wheel = + CONSTANTS.controlsTransition.wheelMouse; + state.controls.mouseButtons.middle = + CONSTANTS.controlsTransition.middleMouse; + + if (camMode === "ThirdPerson") { + setCamMode("FirstPerson"); + await switchToFirstPerson(state.controls, state.camera); + } else if (camMode === "FirstPerson") { + setCamMode("ThirdPerson"); + await switchToThirdPerson(state.controls, state.camera); + } + + setIsTransitioning(false); + } + }; + + window.addEventListener("keydown", handleKeyPress); + return () => { + window.removeEventListener("keydown", handleKeyPress); + }; + }, [ + camMode, + isTransitioning, + toggleView, + state.controls, + state.camera, + setCamMode, + ]); + + useFrame(() => { + const { forward, backward, left, right } = get(); + if (!state.controls) return; + if (camMode === "ThirdPerson" || !document.pointerLockElement) return; + + const speedMultiplier = isShiftActive ? 4 : 1; + + if (forward) { + state.controls.forward( + CONSTANTS.firstPersonControls.forwardSpeed * speedMultiplier, + true + ); + } + if (backward) { + state.controls.forward( + CONSTANTS.firstPersonControls.backwardSpeed * speedMultiplier, + true + ); + } + if (left) { + state.controls.truck( + CONSTANTS.firstPersonControls.leftSpeed * speedMultiplier, + 0, + true + ); + } + if (right) { + state.controls.truck( + CONSTANTS.firstPersonControls.rightSpeed * speedMultiplier, + 0, + true + ); + } + }); + + return null; // This component does not render any UI }; -export default CamMode; \ No newline at end of file +export default CamMode; diff --git a/app/src/modules/scene/controls/selectionControls/copyPasteControls.tsx b/app/src/modules/scene/controls/selectionControls/copyPasteControls.tsx index 9ca4dd0..613fe43 100644 --- a/app/src/modules/scene/controls/selectionControls/copyPasteControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/copyPasteControls.tsx @@ -137,14 +137,47 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas if (itemsGroupRef.current) { + let updatedEventData = null; + if (obj.userData.eventData) { + updatedEventData = JSON.parse(JSON.stringify(obj.userData.eventData)); + + updatedEventData.modelUuid = THREE.MathUtils.generateUUID(); + + if (updatedEventData.type === "Conveyor" && updatedEventData.points) { + updatedEventData.points = updatedEventData.points.map((point: any) => ({ + ...point, + uuid: THREE.MathUtils.generateUUID() + })); + } + else if (updatedEventData.type === "Vehicle" && updatedEventData.point) { + updatedEventData.point = { + ...updatedEventData.point, + uuid: THREE.MathUtils.generateUUID() + }; + } + else if (updatedEventData.type === "ArmBot" && updatedEventData.point) { + updatedEventData.point = { + ...updatedEventData.point, + uuid: THREE.MathUtils.generateUUID() + }; + } + else if (updatedEventData.type === "StaticMachine" && updatedEventData.point) { + updatedEventData.point = { + ...updatedEventData.point, + uuid: THREE.MathUtils.generateUUID() + }; + } + } + const newFloorItem: Types.FloorItemType = { - modeluuid: obj.uuid, - modelname: obj.userData.name, + modelUuid: obj.uuid, + modelName: obj.userData.name, modelfileID: obj.userData.modelId, position: [worldPosition.x, worldPosition.y, worldPosition.z], rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z, }, isLocked: false, - isVisible: true + isVisible: true, + eventData: updatedEventData }; setFloorItems((prevItems: Types.FloorItems) => { @@ -173,19 +206,26 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas const data = { organization, - modeluuid: newFloorItem.modeluuid, - modelname: newFloorItem.modelname, + modelUuid: newFloorItem.modelUuid, + modelName: newFloorItem.modelName, modelfileID: newFloorItem.modelfileID, position: newFloorItem.position, rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, isLocked: false, isVisible: true, socketId: socket.id, + eventData: updatedEventData }; socket.emit("v2:model-asset:add", data); - obj.userData.modeluuid = newFloorItem.modeluuid; + obj.userData = { + name: newFloorItem.modelName, + modelId: newFloorItem.modelfileID, + modelUuid: newFloorItem.modelUuid, + eventData: updatedEventData + }; + itemsGroupRef.current.add(obj); } }); @@ -205,7 +245,7 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas setSelectedAssets([]); } - return null; // No visible output, but the component handles copy-paste functionality + return null; }; export default CopyPasteControls; \ No newline at end of file diff --git a/app/src/modules/scene/controls/selectionControls/duplicationControls.tsx b/app/src/modules/scene/controls/selectionControls/duplicationControls.tsx index 39c3e49..733e4ac 100644 --- a/app/src/modules/scene/controls/selectionControls/duplicationControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/duplicationControls.tsx @@ -115,14 +115,47 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb if (itemsGroupRef.current) { + let updatedEventData = null; + if (obj.userData.eventData) { + updatedEventData = JSON.parse(JSON.stringify(obj.userData.eventData)); + + updatedEventData.modelUuid = THREE.MathUtils.generateUUID(); + + if (updatedEventData.type === "Conveyor" && updatedEventData.points) { + updatedEventData.points = updatedEventData.points.map((point: any) => ({ + ...point, + uuid: THREE.MathUtils.generateUUID() + })); + } + else if (updatedEventData.type === "Vehicle" && updatedEventData.point) { + updatedEventData.point = { + ...updatedEventData.point, + uuid: THREE.MathUtils.generateUUID() + }; + } + else if (updatedEventData.type === "ArmBot" && updatedEventData.point) { + updatedEventData.point = { + ...updatedEventData.point, + uuid: THREE.MathUtils.generateUUID() + }; + } + else if (updatedEventData.type === "StaticMachine" && updatedEventData.point) { + updatedEventData.point = { + ...updatedEventData.point, + uuid: THREE.MathUtils.generateUUID() + }; + } + } + const newFloorItem: Types.FloorItemType = { - modeluuid: obj.uuid, - modelname: obj.userData.name, + modelUuid: obj.uuid, + modelName: obj.userData.name, modelfileID: obj.userData.modelId, position: [worldPosition.x, worldPosition.y, worldPosition.z], rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z, }, isLocked: false, - isVisible: true + isVisible: true, + eventData: updatedEventData }; setFloorItems((prevItems: Types.FloorItems) => { @@ -151,19 +184,26 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb const data = { organization, - modeluuid: newFloorItem.modeluuid, - modelname: newFloorItem.modelname, + modelUuid: newFloorItem.modelUuid, + modelName: newFloorItem.modelName, modelfileID: newFloorItem.modelfileID, position: newFloorItem.position, rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, isLocked: false, isVisible: true, socketId: socket.id, + eventData: updatedEventData }; socket.emit("v2:model-asset:add", data); - obj.userData.modeluuid = newFloorItem.modeluuid; + obj.userData = { + name: newFloorItem.modelName, + modelId: newFloorItem.modelfileID, + modelUuid: newFloorItem.modelUuid, + eventData: updatedEventData + }; + itemsGroupRef.current.add(obj); } }); @@ -183,7 +223,7 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb setSelectedAssets([]); } - return null; // This component does not render any UI + return null; }; export default DuplicationControls; \ No newline at end of file diff --git a/app/src/modules/scene/controls/selectionControls/moveControls.tsx b/app/src/modules/scene/controls/selectionControls/moveControls.tsx index 2cde3e4..d371806 100644 --- a/app/src/modules/scene/controls/selectionControls/moveControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/moveControls.tsx @@ -1,11 +1,14 @@ import * as THREE from "three"; -import { useEffect, useMemo, useRef, useState } from "react"; +import { useEffect, useMemo, useRef } from "react"; import { useFrame, useThree } from "@react-three/fiber"; import { useFloorItems, useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/store"; // import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; import { toast } from "react-toastify"; import * as Types from "../../../../types/world/worldTypes"; import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys"; +import { useEventsStore } from "../../../../store/simulation/useEventsStore"; +import { useProductStore } from "../../../../store/simulation/useProductStore"; +import { useSelectedProduct } from "../../../../store/simulation/useSimulationStore"; function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, duplicatedObjects, setDuplicatedObjects, selectionGroup, rotatedObjects, setRotatedObjects, boundingBoxRef }: any) { const { camera, controls, gl, scene, pointer, raycaster } = useThree(); @@ -62,7 +65,7 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje if (keyCombination === "G") { if (selectedAssets.length > 0) { moveAssets(); - itemsData.current = floorItems.filter((item: { modeluuid: string }) => selectedAssets.some((asset: any) => asset.uuid === item.modeluuid)); + itemsData.current = floorItems.filter((item: { modelUuid: string }) => selectedAssets.some((asset: any) => asset.uuid === item.modelUuid)); } } if (keyCombination === "ESCAPE") { @@ -148,7 +151,7 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje const moveAssets = () => { - const updatedItems = floorItems.filter((item: { modeluuid: string }) => !selectedAssets.some((asset: any) => asset.uuid === item.modeluuid)); + const updatedItems = floorItems.filter((item: { modelUuid: string }) => !selectedAssets.some((asset: any) => asset.uuid === item.modelUuid)); setFloorItems(updatedItems); setMovedObjects(selectedAssets); selectedAssets.forEach((asset: any) => { selectionGroup.current.attach(asset); }); @@ -167,8 +170,8 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje if (itemsGroupRef.current) { const newFloorItem: Types.FloorItemType = { - modeluuid: obj.uuid, - modelname: obj.userData.name, + modelUuid: obj.uuid, + modelName: obj.userData.name, modelfileID: obj.userData.modelId, position: [worldPosition.x, worldPosition.y, worldPosition.z], rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z, }, @@ -176,6 +179,24 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje isVisible: true }; + if (obj.userData.eventData) { + const eventData = useEventsStore.getState().getEventByModelUuid(obj.userData.modelUuid); + const productData = useProductStore.getState().getEventByModelUuid(useSelectedProduct.getState().selectedProduct.productId, obj.userData.modelUuid); + + if (eventData) { + useEventsStore.getState().updateEvent(obj.userData.modelUuid, { + position: [worldPosition.x, worldPosition.y, worldPosition.z], + rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z], + }) + } + if (productData) { + useProductStore.getState().updateEvent(useSelectedProduct.getState().selectedProduct.productId, obj.userData.modelUuid, { + position: [worldPosition.x, worldPosition.y, worldPosition.z], + rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z], + }) + } + } + setFloorItems((prevItems: Types.FloorItems) => { const updatedItems = [...(prevItems || []), newFloorItem]; localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); @@ -202,8 +223,8 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje const data = { organization, - modeluuid: newFloorItem.modeluuid, - modelname: newFloorItem.modelname, + modelUuid: newFloorItem.modelUuid, + modelName: newFloorItem.modelName, modelfileID: newFloorItem.modelfileID, position: newFloorItem.position, rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, @@ -234,7 +255,7 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje setSelectedAssets([]); } - return null; // No need to return anything, as this component is used for its side effects + return null; } export default MoveControls \ No newline at end of file diff --git a/app/src/modules/scene/controls/selectionControls/rotateControls.tsx b/app/src/modules/scene/controls/selectionControls/rotateControls.tsx index 5dfaf08..08667b4 100644 --- a/app/src/modules/scene/controls/selectionControls/rotateControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/rotateControls.tsx @@ -1,11 +1,13 @@ import * as THREE from "three"; -import { useEffect, useMemo, useRef, useState } from "react"; +import { useEffect, useMemo, useRef } from "react"; import { useFrame, useThree } from "@react-three/fiber"; import { useFloorItems, useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/store"; // import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; import { toast } from "react-toastify"; import * as Types from "../../../../types/world/worldTypes"; -import { setFloorItemApi } from "../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi"; +import { useEventsStore } from "../../../../store/simulation/useEventsStore"; +import { useProductStore } from "../../../../store/simulation/useProductStore"; +import { useSelectedProduct } from "../../../../store/simulation/useSimulationStore"; function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMovedObjects, itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, duplicatedObjects, setDuplicatedObjects, selectionGroup, boundingBoxRef }: any) { const { camera, controls, gl, scene, pointer, raycaster } = useThree(); @@ -62,7 +64,7 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo if (event.key.toLowerCase() === "r") { if (selectedAssets.length > 0) { rotateAssets(); - itemsData.current = floorItems.filter((item: { modeluuid: string }) => selectedAssets.some((asset: any) => asset.uuid === item.modeluuid)); + itemsData.current = floorItems.filter((item: { modelUuid: string }) => selectedAssets.some((asset: any) => asset.uuid === item.modelUuid)); } } if (event.key.toLowerCase() === "escape") { @@ -126,7 +128,7 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo }); const rotateAssets = () => { - const updatedItems = floorItems.filter((item: { modeluuid: string }) => !selectedAssets.some((asset: any) => asset.uuid === item.modeluuid)); + const updatedItems = floorItems.filter((item: { modelUuid: string }) => !selectedAssets.some((asset: any) => asset.uuid === item.modelUuid)); setFloorItems(updatedItems); const box = new THREE.Box3(); @@ -168,8 +170,8 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo if (itemsGroupRef.current) { const newFloorItem: Types.FloorItemType = { - modeluuid: obj.uuid, - modelname: obj.userData.name, + modelUuid: obj.uuid, + modelName: obj.userData.name, modelfileID: obj.userData.modelId, position: [worldPosition.x, worldPosition.y, worldPosition.z], rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z, }, @@ -177,6 +179,24 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo isVisible: true }; + if (obj.userData.eventData) { + const eventData = useEventsStore.getState().getEventByModelUuid(obj.userData.modelUuid); + const productData = useProductStore.getState().getEventByModelUuid(useSelectedProduct.getState().selectedProduct.productId, obj.userData.modelUuid); + + if (eventData) { + useEventsStore.getState().updateEvent(obj.userData.modelUuid, { + position: [worldPosition.x, worldPosition.y, worldPosition.z], + rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z], + }) + } + if (productData) { + useProductStore.getState().updateEvent(useSelectedProduct.getState().selectedProduct.productId, obj.userData.modelUuid, { + position: [worldPosition.x, worldPosition.y, worldPosition.z], + rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z], + }) + } + } + setFloorItems((prevItems: Types.FloorItems) => { const updatedItems = [...(prevItems || []), newFloorItem]; localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); @@ -203,8 +223,8 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo const data = { organization, - modeluuid: newFloorItem.modeluuid, - modelname: newFloorItem.modelname, + modelUuid: newFloorItem.modelUuid, + modelName: newFloorItem.modelName, modelfileID: newFloorItem.modelfileID, position: newFloorItem.position, rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, @@ -235,7 +255,7 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo setSelectedAssets([]); } - return null; // No need to return anything, as this component is used for its side effects + return null; } export default RotateControls \ No newline at end of file diff --git a/app/src/modules/scene/controls/selectionControls/selectionControls.tsx b/app/src/modules/scene/controls/selectionControls/selectionControls.tsx index 49b86d2..33a35c9 100644 --- a/app/src/modules/scene/controls/selectionControls/selectionControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/selectionControls.tsx @@ -14,6 +14,8 @@ import CopyPasteControls from "./copyPasteControls"; import MoveControls from "./moveControls"; import RotateControls from "./rotateControls"; import useModuleStore from "../../../../store/useModuleStore"; +import { useEventsStore } from "../../../../store/simulation/useEventsStore"; +import { useProductStore } from "../../../../store/simulation/useProductStore"; const SelectionControls: React.FC = () => { const { camera, controls, gl, scene, pointer } = useThree(); @@ -206,7 +208,7 @@ const SelectionControls: React.FC = () => { const storedItems = JSON.parse(localStorage.getItem("FloorItems") || "[]"); const selectedUUIDs = selectedAssets.map((mesh: THREE.Object3D) => mesh.uuid); - const updatedStoredItems = storedItems.filter((item: { modeluuid: string }) => !selectedUUIDs.includes(item.modeluuid)); + const updatedStoredItems = storedItems.filter((item: { modelUuid: string }) => !selectedUUIDs.includes(item.modelUuid)); localStorage.setItem("FloorItems", JSON.stringify(updatedStoredItems)); selectedAssets.forEach((selectedMesh: THREE.Object3D) => { @@ -218,13 +220,16 @@ const SelectionControls: React.FC = () => { const data = { organization: organization, - modeluuid: selectedMesh.uuid, - modelname: selectedMesh.userData.name, + modelUuid: selectedMesh.uuid, + modelName: selectedMesh.userData.name, socketId: socket.id, }; socket.emit("v2:model-asset:delete", data); + useEventsStore.getState().removeEvent(selectedMesh.uuid); + useProductStore.getState().deleteEvent(selectedMesh.uuid); + selectedMesh.traverse((child: THREE.Object3D) => { if (child instanceof THREE.Mesh) { if (child.geometry) child.geometry.dispose(); @@ -243,7 +248,7 @@ const SelectionControls: React.FC = () => { itemsGroupRef.current?.remove(selectedMesh); }); - const updatedItems = floorItems.filter((item: { modeluuid: string }) => !selectedUUIDs.includes(item.modeluuid)); + const updatedItems = floorItems.filter((item: { modelUuid: string }) => !selectedUUIDs.includes(item.modelUuid)); setFloorItems(updatedItems); } toast.success("Selected models removed!"); diff --git a/app/src/modules/scene/scene.tsx b/app/src/modules/scene/scene.tsx index 6564032..0f2c489 100644 --- a/app/src/modules/scene/scene.tsx +++ b/app/src/modules/scene/scene.tsx @@ -9,28 +9,35 @@ import Simulation from "../simulation/simulation"; import Collaboration from "../collaboration/collaboration"; export default function Scene() { - const map = useMemo(() => [ - { name: "forward", keys: ["ArrowUp", "w", "W"] }, - { name: "backward", keys: ["ArrowDown", "s", "S"] }, - { name: "left", keys: ["ArrowLeft", "a", "A"] }, - { name: "right", keys: ["ArrowRight", "d", "D"] },], - []); + const map = useMemo( + () => [ + { name: "forward", keys: ["ArrowUp", "w", "W"] }, + { name: "backward", keys: ["ArrowDown", "s", "S"] }, + { name: "left", keys: ["ArrowLeft", "a", "A"] }, + { name: "right", keys: ["ArrowRight", "d", "D"] }, + ], + [] + ); - return ( - - { e.preventDefault(); }}> + return ( + + { + e.preventDefault(); + }} + > + - + - + - + - - - - - - - ); + + + + ); } diff --git a/app/src/modules/simulation/events/points/creator/pointsCreator.tsx b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx index 3acee3c..9d32063 100644 --- a/app/src/modules/simulation/events/points/creator/pointsCreator.tsx +++ b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx @@ -2,286 +2,210 @@ import React, { useEffect, useRef, useState } from "react"; import * as THREE from "three"; import { useEventsStore } from "../../../../../store/simulation/useEventsStore"; import useModuleStore from "../../../../../store/useModuleStore"; -import { TransformControls, useGLTF } from "@react-three/drei"; +import { TransformControls } from "@react-three/drei"; import { detectModifierKeys } from "../../../../../utils/shortcutkeys/detectModifierKeys"; import { - useSelectedEventSphere, - useSelectedEventData, + useSelectedEventSphere, + useSelectedEventData, } from "../../../../../store/simulation/useSimulationStore"; -import PickDropPoints from "../../../ui/arm/PickDropPoints"; -import armPick from "../../../../../assets/gltf-glb/arm_ui_pick.glb"; -import armDrop from "../../../../../assets/gltf-glb/arm_ui_drop.glb"; -import useDraggableGLTF from "../../../ui/arm/useDraggableGLTF"; -import { useArmBotStore } from "../../../../../store/simulation/useArmBotStore"; -import { useThree } from "@react-three/fiber"; - -interface Process { - startPoint: number[] | null; - endPoint: number[] | null; -} - -interface Action { - actionUuid: string; - actionName: string; - actionType: string; - process: Process; - triggers: any[]; -} - -interface Point { - uuid: string; - position: number[]; - rotation: number[]; - actions: Action[]; -} - -interface RoboticArmEvent { - modelUuid: string; - modelName: string; - position: number[]; - rotation: number[]; - state: string; - type: string; - speed: number; - point: Point; -} function PointsCreator() { - const { events, updatePoint, getPointByUuid, getEventByModelUuid } = - useEventsStore(); - const { armBots, updateArmBot, addArmBot, removeArmBot } = useArmBotStore() + const { events, updatePoint, getPointByUuid, getEventByModelUuid } = useEventsStore(); + const { activeModule } = useModuleStore(); + const transformRef = useRef(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(); - const armUiPick = useGLTF(armPick) as any; - const armUiDrop = useGLTF(armDrop) as any; - - const updatePointToState = (obj: THREE.Object3D) => { - const { modelUuid, pointUuid, actionType, actionUuid } = obj.userData; - - const newPosition = new THREE.Vector3(); - obj.getWorldPosition(newPosition); - const worldPositionArray = newPosition.toArray(); - - const armBot = armBots.find((a) => a.modelUuid === modelUuid); - if (!armBot) return; - - const updatedActions = armBot.point.actions.map((action: any) => { - if (action.actionUuid === actionUuid) { - const updatedProcess = { ...action.process }; - - if (actionType === "pick") { - updatedProcess.startPoint = getLocalPosition(modelUuid, worldPositionArray); + useEffect(() => { + if (selectedEventSphere) { + const eventData = getEventByModelUuid( + selectedEventSphere.userData.modelUuid + ); + if (eventData) { + setSelectedEventData(eventData, selectedEventSphere.userData.pointUuid); + } else { + clearSelectedEventData(); + } + } else { + clearSelectedEventData(); } - if (actionType === "drop") { - updatedProcess.endPoint = getLocalPosition(modelUuid, worldPositionArray); - } - return { - ...action, - process: updatedProcess, + }, [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")); + } }; - } - return action; - }); - updateArmBot(modelUuid, { - point: { - ...armBot.point, - actions: updatedActions, - }, - }); - }; + window.addEventListener("keydown", handleKeyDown); + return () => window.removeEventListener("keydown", handleKeyDown); + }, [selectedEventSphere]); - - const { handlePointerDown } = useDraggableGLTF(updatePointToState); - const { scene } = useThree(); - const { activeModule } = useModuleStore(); - const transformRef = useRef(null); - const groupRef = useRef(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(); - - 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")); - } + 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 [selectedPoint, setSelectedPoint] = useState(null); - - const getDefaultPositions = (modelUuid: string) => { - const modelData = getModelByUuid(modelUuid); - if (modelData) { - - const baseX = modelData.position?.[0] || 0; - const baseY = 2.6; - const baseZ = modelData.position?.[2] || 0; - return { - pick: [baseX, baseY, baseZ + 0.4], - drop: [baseX, baseY, baseZ - 1.2], - default: [baseX, baseY, baseZ - 1.5], - }; - } - return { - pick: [0.5, 1.5, 0], - drop: [-0.5, 1.5, 0], - default: [0, 1.5, 0], - }; - }; - - const getModelByUuid = (modelUuid: string) => { - try { - const modelsJson = localStorage.getItem("FloorItems"); - if (modelsJson) { - const models = JSON.parse(modelsJson); - return models.find((m: any) => m.modeluuid === modelUuid); - } - const storeModels = (useModuleStore.getState() as any).models || []; - return storeModels.find((m: any) => m.modelUuid === modelUuid); - } catch (error) { } - return null; - }; - - function getLocalPosition(parentUuid: string, worldPosArray: [number, number, number] | null): [number, number, number] | null { - if (worldPosArray) { - const worldPos = new THREE.Vector3(...worldPosArray); - const localPos = worldPos.clone(); - - const parentObject = scene.getObjectByProperty('uuid', parentUuid); - if (parentObject) { - parentObject.worldToLocal(localPos); - - return [localPos.x, localPos.y, localPos.z]; - } else { - - } - } - return null; - } - - useEffect(() => { - - }, [armBots]); - return ( - <> - {activeModule === "simulation" && ( + return ( <> - - {armBots.map((event, i) => { - if (event.type === "roboticArm") { - const defaultPositions = getDefaultPositions(event.modelUuid); - const isSelected = - selectedPoint?.userData?.modelUuid === event.modelUuid; - return ( - - (sphereRefs.current[event.point.uuid] = el!)} - onClick={(e) => { - e.stopPropagation(); - setSelectedEventSphere(sphereRefs.current[event.point.uuid]); - setSelectedPoint(e.object as THREE.Mesh); - }} - onPointerMissed={() => { - clearSelectedEventSphere(); - setTransformMode(null); - }} - position={new THREE.Vector3(...event.point.position)} - userData={{ modelUuid: event.modelUuid, pointUuid: event.point.uuid }} - > - - - - {event.point.actions.map((action) => { - if (action.actionType === "pickAndPlace") { - - const pickPosition = - action.process.startPoint || defaultPositions.pick; - const dropPosition = - action.process.endPoint || defaultPositions.drop; - - return ( - - {/* Pick Point */} - - - {/* Drop Point */} - - - ); - } - return null; - })} - - ); - } else { - return null; - } - })} - + {activeModule === "simulation" && ( + <> + + {events.map((event, i) => { + if (event.type === "transfer") { + return ( + + {event.points.map((point, j) => ( + (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)} + // rotation={new THREE.Euler(...point.rotation)} + userData={{ modelUuid: event.modelUuid, pointUuid: point.uuid }} + > + + + + ))} + + ); + } else if (event.type === "vehicle") { + return ( + + (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)} + // rotation={new THREE.Euler(...event.point.rotation)} + userData={{ modelUuid: event.modelUuid, pointUuid: event.point.uuid }} + > + + + + + ); + } else if (event.type === "roboticArm") { + return ( + + (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)} + // rotation={new THREE.Euler(...event.point.rotation)} + userData={{ modelUuid: event.modelUuid, pointUuid: event.point.uuid }} + > + + + + + ); + } else if (event.type === "machine") { + return ( + + (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)} + // rotation={new THREE.Euler(...event.point.rotation)} + userData={{ modelUuid: event.modelUuid, pointUuid: event.point.uuid }} + > + + + + + ); + } else { + return null; + } + })} + + {selectedEventSphere && transformMode && ( + { + updatePointToState(selectedEventSphere); + }} + /> + )} + + )} - )} - - ); + ); } export default PointsCreator; diff --git a/app/src/modules/simulation/events/points/functions/handleAddEventToProduct.ts b/app/src/modules/simulation/events/points/functions/handleAddEventToProduct.ts new file mode 100644 index 0000000..9928578 --- /dev/null +++ b/app/src/modules/simulation/events/points/functions/handleAddEventToProduct.ts @@ -0,0 +1,38 @@ +import { upsertProductOrEventApi } from "../../../../../services/simulation/UpsertProductOrEventApi"; + +interface HandleAddEventToProductParams { + event: EventsSchema | undefined; + addEvent: (productId: string, event: EventsSchema) => void; + selectedProduct: { + productId: string; + productName: string; + } + clearSelectedAsset?: () => void; +} + +export const handleAddEventToProduct = ({ + event, + addEvent, + selectedProduct, + clearSelectedAsset +}: HandleAddEventToProductParams) => { + if (event && selectedProduct.productId) { + addEvent(selectedProduct.productId, event); + + const email = localStorage.getItem('email') + const organization = (email!.split("@")[1]).split(".")[0]; + + upsertProductOrEventApi({ + productName: selectedProduct.productName, + productId: selectedProduct.productId, + organization: organization, + eventDatas: event + }).then((data) => { + // console.log(data); + }) + + if (clearSelectedAsset) { + clearSelectedAsset(); + } + } +}; diff --git a/app/src/modules/simulation/products/products.tsx b/app/src/modules/simulation/products/products.tsx index 38175e2..ee1ac42 100644 --- a/app/src/modules/simulation/products/products.tsx +++ b/app/src/modules/simulation/products/products.tsx @@ -7,28 +7,27 @@ import { upsertProductOrEventApi } from '../../../services/simulation/UpsertProd import { getAllProductsApi } from '../../../services/simulation/getallProductsApi'; function Products() { - const { products, addProduct } = useProductStore(); + const { products, addProduct, setProducts } = useProductStore(); const { setSelectedProduct } = useSelectedProduct(); useEffect(() => { - if (products.length === 0) { - const id = THREE.MathUtils.generateUUID(); - const name = 'Product 1'; - addProduct(name, id); - // upsertProductOrEventApi({ productName: name, productId: id }).then((data) => { - // console.log('data: ', data); - // }); - setSelectedProduct(id, name); - } - }, [products]) + const email = localStorage.getItem('email') + const organization = (email!.split("@")[1]).split(".")[0]; + getAllProductsApi(organization).then((data) => { + if (data.length === 0) { + const id = THREE.MathUtils.generateUUID(); + const name = 'Product 1'; + addProduct(name, id); + upsertProductOrEventApi({ productName: name, productId: id, organization: organization }) + setSelectedProduct(id, name); + } else { + setProducts(data); + setSelectedProduct(data[0].productId, data[0].productName); + } + }) + }, []) useEffect(() => { - // const email = localStorage.getItem('email') - // const organization = (email!.split("@")[1]).split(".")[0]; - // console.log(organization); - // getAllProductsApi(organization).then((data) => { - // console.log('data: ', data); - // }) }, []) return ( diff --git a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx index 5f00a45..7660ad1 100644 --- a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx +++ b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx @@ -94,12 +94,12 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { useEffect(() => { let armItems = floorItems?.filter((val: any) => - val.modeluuid === armBot.modelUuid + val.modelUuid === "3abf5d46-b59e-4e6b-9c02-a4634b64b82d" ); // Get the first matching item let armItem = armItems?.[0]; if (armItem) { - const targetMesh = scene?.getObjectByProperty("uuid", armItem.modeluuid); + const targetMesh = scene?.getObjectByProperty("uuid", armItem.modelUuid); if (targetMesh) { targetMesh.visible = activeModule !== "simulation" } diff --git a/app/src/modules/simulation/triggers/connector/triggerConnector.tsx b/app/src/modules/simulation/triggers/connector/triggerConnector.tsx index f836ea4..efefa0c 100644 --- a/app/src/modules/simulation/triggers/connector/triggerConnector.tsx +++ b/app/src/modules/simulation/triggers/connector/triggerConnector.tsx @@ -1,19 +1,85 @@ -import { useEffect } from "react"; +import { useEffect, useRef, useState } from "react"; import { useThree } from "@react-three/fiber"; +import * as THREE from "three"; import { useSubModuleStore } from "../../../../store/useModuleStore"; import { useSelectedAsset } from "../../../../store/simulation/useSimulationStore"; import { useProductStore } from "../../../../store/simulation/useProductStore"; +import { useEventsStore } from "../../../../store/simulation/useEventsStore"; import { useSelectedProduct } from "../../../../store/simulation/useSimulationStore"; +import { handleAddEventToProduct } from "../../events/points/functions/handleAddEventToProduct"; + +interface ConnectionLine { + id: string; + start: THREE.Vector3; + end: THREE.Vector3; + mid: THREE.Vector3; + trigger: TriggerSchema; +} function TriggerConnector() { const { gl, raycaster, scene } = useThree(); const { subModule } = useSubModuleStore(); - const { getPointByUuid, getIsEventInProduct } = useProductStore(); + const { products, getPointByUuid, getIsEventInProduct, getActionByUuid, addTrigger, addEvent, getEventByModelUuid } = useProductStore(); const { selectedAsset, clearSelectedAsset } = useSelectedAsset(); const { selectedProduct } = useSelectedProduct(); - useEffect(() => { + const [firstSelectedPoint, setFirstSelectedPoint] = useState<{ + productId: string; + modelUuid: string; + pointUuid: string; + actionUuid?: string; + } | null>(null); + const [connections, setConnections] = useState([]); + + useEffect(() => { + const newConnections: ConnectionLine[] = []; + + products.forEach(product => { + product.eventDatas.forEach(event => { + if ('points' in event) { + event.points.forEach(point => { + if ('action' in point && point.action?.triggers) { + point.action.triggers.forEach(trigger => { + if (trigger.triggeredAsset) { + const targetPoint = getPointByUuid( + product.productId, + trigger.triggeredAsset.triggeredModel.modelUuid, + trigger.triggeredAsset.triggeredPoint.pointUuid + ); + + if (targetPoint) { + const startPos = new THREE.Vector3(...point.position); + const endPos = new THREE.Vector3(...targetPoint.position); + const midPos = new THREE.Vector3() + .addVectors(startPos, endPos) + .multiplyScalar(0.5) + .add(new THREE.Vector3(0, 2, 0)); + + newConnections.push({ + id: `${point.uuid}-${targetPoint.uuid}-${trigger.triggerUuid}`, + start: startPos, + end: endPos, + mid: midPos, + trigger + }); + } + } + }); + } + }); + } + }); + }); + + setConnections(newConnections); + }, [products]); + + useEffect(() => { + console.log('connections: ', connections); + }, connections) + + useEffect(() => { const canvasElement = gl.domElement; let drag = false; @@ -44,37 +110,114 @@ function TriggerConnector() { const handleRightClick = (evt: MouseEvent) => { if (drag) return; evt.preventDefault(); - const canvasElement = gl.domElement; - if (!canvasElement) return; - let intersects = raycaster.intersectObjects(scene.children, true); - if (intersects.length > 0 && intersects[0]?.object?.parent?.parent?.position && intersects[0]?.object?.parent?.parent?.scale && intersects[0]?.object?.parent?.parent?.rotation) { - let currentObject = intersects[0].object; + const intersects = raycaster.intersectObjects(scene.children, true); + if (intersects.length === 0) return; - if (currentObject && currentObject.name === 'Event-Sphere') { + const currentObject = intersects[0].object; + if (!currentObject || currentObject.name !== 'Event-Sphere') return; - const isInProduct = getIsEventInProduct( - selectedProduct.productId, - currentObject.userData.modelUuid - ); + const modelUuid = currentObject.userData.modelUuid; + const pointUuid = currentObject.userData.pointUuid; - // You left Here + if (selectedProduct && getIsEventInProduct(selectedProduct.productId, modelUuid)) { - if (isInProduct) { + const point = getPointByUuid( + selectedProduct.productId, + modelUuid, + pointUuid + ); - const event = getPointByUuid( - selectedProduct.productId, - currentObject.userData.modelUuid, - currentObject.userData.pointUuid - ); - console.log('event: ', event); - } else { + if (!point) return; - } + let actionUuid: string | undefined; + if ('action' in point && point.action) { + actionUuid = point.action.actionUuid; + } else if ('actions' in point && point.actions.length === 1) { + actionUuid = point.actions[0].actionUuid; } - } else { + if (!firstSelectedPoint) { + setFirstSelectedPoint({ + productId: selectedProduct.productId, + modelUuid, + pointUuid, + actionUuid + }); + } else { + const trigger: TriggerSchema = { + triggerUuid: THREE.MathUtils.generateUUID(), + triggerName: `Trigger ${firstSelectedPoint.pointUuid.slice(0, 4)} → ${pointUuid.slice(0, 4)}`, + triggerType: "onComplete", + delay: 0, + triggeredAsset: { + triggeredModel: { + modelName: currentObject.parent?.parent?.name || 'Unknown', + modelUuid: modelUuid + }, + triggeredPoint: { + pointName: currentObject.name, + pointUuid: pointUuid + }, + triggeredAction: actionUuid ? { + actionName: getActionByUuid(selectedProduct.productId, actionUuid)?.actionName || 'Action', + actionUuid: actionUuid + } : null + } + }; + if (firstSelectedPoint.actionUuid) { + addTrigger(firstSelectedPoint.actionUuid, trigger); + } + setFirstSelectedPoint(null); + } + } else if (!getIsEventInProduct(selectedProduct.productId, modelUuid) && firstSelectedPoint) { + handleAddEventToProduct({ + event: useEventsStore.getState().getEventByModelUuid(modelUuid), + addEvent, + selectedProduct, + }) + + const point = getPointByUuid( + selectedProduct.productId, + modelUuid, + pointUuid + ); + + if (!point) return; + + let actionUuid: string | undefined; + if ('action' in point && point.action) { + actionUuid = point.action.actionUuid; + } else if ('actions' in point && point.actions.length === 1) { + actionUuid = point.actions[0].actionUuid; + } + + const trigger: TriggerSchema = { + triggerUuid: THREE.MathUtils.generateUUID(), + triggerName: `Trigger ${firstSelectedPoint.pointUuid.slice(0, 4)} → ${pointUuid.slice(0, 4)}`, + triggerType: "onComplete", + delay: 0, + triggeredAsset: { + triggeredModel: { + modelName: currentObject.parent?.parent?.name || 'Unknown', + modelUuid: modelUuid + }, + triggeredPoint: { + pointName: currentObject.name, + pointUuid: pointUuid + }, + triggeredAction: actionUuid ? { + actionName: getActionByUuid(selectedProduct.productId, actionUuid)?.actionName || 'Action', + actionUuid: actionUuid + } : null + } + }; + + if (firstSelectedPoint.actionUuid) { + addTrigger(firstSelectedPoint.actionUuid, trigger); + } + setFirstSelectedPoint(null); } }; @@ -92,12 +235,12 @@ function TriggerConnector() { canvasElement.removeEventListener('contextmenu', handleRightClick); }; - }, [gl, subModule]); + }, [gl, subModule, selectedProduct, firstSelectedPoint]); return ( <> - ) + ); } -export default TriggerConnector \ No newline at end of file +export default TriggerConnector; \ No newline at end of file diff --git a/app/src/modules/simulation/ui/vehicle/useDraggableGLTF.ts b/app/src/modules/simulation/ui/vehicle/useDraggableGLTF.ts new file mode 100644 index 0000000..b7e9272 --- /dev/null +++ b/app/src/modules/simulation/ui/vehicle/useDraggableGLTF.ts @@ -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(null); + const planeRef = useRef( + new THREE.Plane(new THREE.Vector3(0, 1, 0), 0) + ); + const offsetRef = useRef(new THREE.Vector3()); + const initialPositionRef = useRef(new THREE.Vector3()); + + const raycaster = new THREE.Raycaster(); + const pointer = new THREE.Vector2(); + + const handlePointerDown = (e: ThreeEvent) => { + 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 }; +} + + + diff --git a/app/src/modules/simulation/ui/vehicle/vehicleUI.tsx b/app/src/modules/simulation/ui/vehicle/vehicleUI.tsx new file mode 100644 index 0000000..5dec724 --- /dev/null +++ b/app/src/modules/simulation/ui/vehicle/vehicleUI.tsx @@ -0,0 +1,243 @@ +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 * as THREE from "three"; +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(null); + const endMarker = useRef(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; + + 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; + + 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; + 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; + setStartPosition([defaultWorld.x, 0, defaultWorld.z]); + setStartRotation([0, 0, 0]); + } + + 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]); + + useFrame(() => { + if (!isDragging) return; + const intersectPoint = new THREE.Vector3(); + const intersects = raycaster.ray.intersectPlane(plane.current, intersectPoint); + + 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]); + } + } + }); + + useFrame((state) => { + if (!isRotating) return; + + const currentPointerX = state.pointer.x; + const deltaX = currentPointerX - prevMousePos.current.x; + prevMousePos.current.x = currentPointerX; + + const marker = isRotating === "start" ? startMarker.current : endMarker.current; + + if (marker) { + const rotationSpeed = 10; + marker.rotation.y += deltaX * rotationSpeed; + if (isRotating === 'start') { + setStartRotation([marker.rotation.x, marker.rotation.y, marker.rotation.z]) + } else { + + setEndRotation([marker.rotation.x, marker.rotation.y, marker.rotation.z]) + } + } + }); + + + const handlePointerDown = (e: any, state: "start" | "end", rotation: "start" | "end") => { + + 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); + + } else { + setIsDragging(state); + setIsRotating(null); + if (controls) controls.enabled = false; + } + }; + + const handlePointerUp = () => { + controls.enabled = true; + setIsDragging(null); + setIsRotating(null); + + 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], + }, + }, + }, + }, + }); + } + } + }; + + useEffect(() => { + const handleGlobalPointerUp = () => { + setIsDragging(null); + setIsRotating(null); + if (controls) controls.enabled = true; + handlePointerUp(); + }; + + if (isDragging || isRotating) { + window.addEventListener("pointerup", handleGlobalPointerUp); + } + + return () => { + window.removeEventListener("pointerup", handleGlobalPointerUp); + }; + }, [isDragging, isRotating, startPosition, startRotation, endPosition, endRotation]); + + return ( + startPosition.length > 0 && endPosition.length > 0 ? ( + + { + e.stopPropagation(); + handlePointerDown(e, "start", "start"); + }} + onPointerMissed={() => { + controls.enabled = true; + setIsDragging(null); + setIsRotating(null); + }} + /> + + { + e.stopPropagation(); + handlePointerDown(e, "end", "end"); + }} + onPointerMissed={() => { + controls.enabled = true; + setIsDragging(null); + setIsRotating(null); + }} + /> + + ) : null + ); +} +export default VehicleUI; + + diff --git a/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx b/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx index fc5303e..2f0b235 100644 --- a/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx +++ b/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx @@ -1,170 +1,215 @@ import { useEffect, useRef, useState } from 'react' import { useFrame, useThree } from '@react-three/fiber'; -import { useFloorItems } from '../../../../../store/store'; import * as THREE from 'three'; import { Line } from '@react-three/drei'; -import { useAnimationPlaySpeed, usePauseButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore'; +import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore'; import { useVehicleStore } from '../../../../../store/simulation/useVehicleStore'; interface VehicleAnimatorProps { - path: [number, number, number][]; - handleCallBack: () => void; - currentPhase: string; - agvUuid: number; - agvDetail: any; + path: [number, number, number][]; + handleCallBack: () => void; + reset: () => void; + currentPhase: string; + agvUuid: number; + agvDetail: any; } -function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetail }: VehicleAnimatorProps) { - const { decrementVehicleLoad, vehicles } = useVehicleStore(); - const { isPaused } = usePauseButtonStore(); - const { speed } = useAnimationPlaySpeed(); - const { isReset } = useResetButtonStore(); - const [restRotation, setRestingRotation] = useState(true); - const [progress, setProgress] = useState(0); - const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]); - const { scene } = useThree(); - const progressRef = useRef(0); - const movingForward = useRef(true); - const completedRef = useRef(false); - let startTime: number; - let pausedTime: number; - let fixedInterval: number; - - useEffect(() => { - if (currentPhase === 'stationed-pickup' && path.length > 0) { - setCurrentPath(path); - } else if (currentPhase === 'pickup-drop' && path.length > 0) { - setCurrentPath(path); - } else if (currentPhase === 'drop-pickup' && path.length > 0) { - setCurrentPath(path); - } - }, [currentPhase, path]); - - useEffect(() => { - setProgress(0); - completedRef.current = false; - }, [currentPath]); - - useFrame((_, delta) => { - const object = scene.getObjectByProperty('uuid', agvUuid); - if (!object || currentPath.length < 2) return; - if (isPaused) return; - - let totalDistance = 0; - const distances = []; - - for (let i = 0; i < currentPath.length - 1; i++) { - const start = new THREE.Vector3(...currentPath[i]); - const end = new THREE.Vector3(...currentPath[i + 1]); - const segmentDistance = start.distanceTo(end); - distances.push(segmentDistance); - totalDistance += segmentDistance; - } - +function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetail, reset }: VehicleAnimatorProps) { + const { decrementVehicleLoad } = useVehicleStore(); + const { isPaused } = usePauseButtonStore(); + const { isPlaying } = usePlayButtonStore(); + const { speed } = useAnimationPlaySpeed(); + const { isReset, setReset } = useResetButtonStore(); + const progressRef = useRef(0); + const movingForward = useRef(true); + const completedRef = useRef(false); + const isPausedRef = useRef(false); + const pauseTimeRef = useRef(null); + const [restRotation, setRestingRotation] = useState(true); + const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]); + const [progress, setProgress] = useState(0); + const { scene } = useThree(); + let startTime: number; + let fixedInterval: number; let coveredDistance = progressRef.current; - let accumulatedDistance = 0; - let index = 0; + let objectRotation = (agvDetail.point?.action?.pickUpPoint?.rotation || { x: 0, y: 0, z: 0 }) as { x: number; y: number; z: number }; - while ( - index < distances.length && - coveredDistance > accumulatedDistance + distances[index] - ) { - accumulatedDistance += distances[index]; - index++; - } - if (index < distances.length) { - const start = new THREE.Vector3(...currentPath[index]); - const end = new THREE.Vector3(...currentPath[index + 1]); - const segmentDistance = distances[index]; - - const currentDirection = new THREE.Vector3().subVectors(end, start).normalize(); - const targetAngle = Math.atan2(currentDirection.x, currentDirection.z); - const rotationSpeed = 2.0; - const currentAngle = object.rotation.y; - - let angleDifference = targetAngle - currentAngle; - if (angleDifference > Math.PI) angleDifference -= 2 * Math.PI; - if (angleDifference < -Math.PI) angleDifference += 2 * Math.PI; - - const maxRotationStep = rotationSpeed * delta; - object.rotation.y += Math.sign(angleDifference) * Math.min(Math.abs(angleDifference), maxRotationStep); - - const isAligned = Math.abs(angleDifference) < 0.01; - - if (isAligned) { - progressRef.current += delta * (speed * agvDetail.speed); - coveredDistance = progressRef.current; - - const t = (coveredDistance - accumulatedDistance) / segmentDistance; - const position = start.clone().lerp(end, t); - object.position.copy(position); - } - } - - if (progressRef.current >= totalDistance) { - if (restRotation) { - const targetQuaternion = new THREE.Quaternion().setFromEuler(new THREE.Euler(0, 0, 0)); - object.quaternion.slerp(targetQuaternion, delta * 2); - const angleDiff = object.quaternion.angleTo(targetQuaternion); - if (angleDiff < 0.01) { - let objectRotation = agvDetail.point.rotation - object.rotation.set(objectRotation[0], objectRotation[1], objectRotation[2]); - setRestingRotation(false); + useEffect(() => { + if (currentPhase === 'stationed-pickup' && path.length > 0) { + setCurrentPath(path); + objectRotation = agvDetail.point.action?.pickUpPoint?.rotation + } else if (currentPhase === 'pickup-drop' && path.length > 0) { + objectRotation = agvDetail.point.action?.unLoadPoint?.rotation + setCurrentPath(path); + } else if (currentPhase === 'drop-pickup' && path.length > 0) { + objectRotation = agvDetail.point.action?.pickUpPoint?.rotation + setCurrentPath(path); } - return; - } + }, [currentPhase, path]); + + useEffect(() => { + setProgress(0); + completedRef.current = false; + }, [currentPath]); + + useEffect(() => { + if (isReset || !isPlaying) { + reset(); + setCurrentPath([]); + setProgress(0); + completedRef.current = false; + movingForward.current = true; + progressRef.current = 0; + startTime = 0; + coveredDistance = 0; + setReset(false); + setRestingRotation(true); + decrementVehicleLoad(agvDetail.modelUuid, 0); + const object = scene.getObjectByProperty('uuid', agvUuid); + console.log('currentPhase: ', currentPhase); + if (object) { + object.position.set(agvDetail.position[0], agvDetail.position[1], agvDetail.position[2]); + object.rotation.set(objectRotation.x, objectRotation.y, objectRotation.z); + } + } + }, [isReset, isPlaying]) + + useEffect(() => { + isPausedRef.current = isPaused; + }, [isPaused]); + + useFrame((_, delta) => { + const object = scene.getObjectByProperty('uuid', agvUuid); + if (!object || currentPath.length < 2) return; + if (isPaused) return; + + let totalDistance = 0; + const distances = []; + let accumulatedDistance = 0; + let index = 0; + + for (let i = 0; i < currentPath.length - 1; i++) { + const start = new THREE.Vector3(...currentPath[i]); + const end = new THREE.Vector3(...currentPath[i + 1]); + const segmentDistance = start.distanceTo(end); + distances.push(segmentDistance); + totalDistance += segmentDistance; + } + + while (index < distances.length && coveredDistance > accumulatedDistance + distances[index]) { + accumulatedDistance += distances[index]; + index++; + } + + if (index < distances.length) { + const start = new THREE.Vector3(...currentPath[index]); + const end = new THREE.Vector3(...currentPath[index + 1]); + const segmentDistance = distances[index]; + + const currentDirection = new THREE.Vector3().subVectors(end, start).normalize(); + const targetAngle = Math.atan2(currentDirection.x, currentDirection.z); + + const rotationSpeed = speed; + const currentAngle = object.rotation.y; + + let angleDifference = targetAngle - currentAngle; + if (angleDifference > Math.PI) angleDifference -= 2 * Math.PI; + if (angleDifference < -Math.PI) angleDifference += 2 * Math.PI; + + const maxRotationStep = rotationSpeed * delta; + object.rotation.y += Math.sign(angleDifference) * Math.min(Math.abs(angleDifference), maxRotationStep); + const isAligned = Math.abs(angleDifference) < 0.01; + + if (isAligned) { + progressRef.current += delta * (speed * agvDetail.speed); + coveredDistance = progressRef.current; + + const t = (coveredDistance - accumulatedDistance) / segmentDistance; + const position = start.clone().lerp(end, t); + object.position.copy(position); + } + } + + if (progressRef.current >= totalDistance) { + if (restRotation) { + const targetQuaternion = new THREE.Quaternion().setFromEuler(new THREE.Euler(objectRotation.x, objectRotation.y, objectRotation.z)); + object.quaternion.slerp(targetQuaternion, delta * 2); + const angleDiff = object.quaternion.angleTo(targetQuaternion); + if (angleDiff < 0.01) { + object.rotation.set(objectRotation.x, objectRotation.y, objectRotation.z); + setRestingRotation(false); + } + return; + } + } + + if (progressRef.current >= totalDistance) { + setRestingRotation(true); + progressRef.current = 0; + movingForward.current = !movingForward.current; + setCurrentPath([]); + handleCallBack(); + if (currentPhase === 'pickup-drop') { + requestAnimationFrame(firstFrame); + } + } + }); + + function firstFrame() { + const droppedMaterial = agvDetail.currentLoad; + startTime = performance.now(); + step(droppedMaterial); } - if (progressRef.current >= totalDistance) { - setRestingRotation(true); - progressRef.current = 0; - movingForward.current = !movingForward.current; - setCurrentPath([]); - handleCallBack(); - if (currentPhase === 'pickup-drop') { - requestAnimationFrame(firstFrame); - } + function step(droppedMaterial: number) { + if (isPausedRef.current) { + if (!pauseTimeRef.current) { + pauseTimeRef.current = performance.now(); + } + requestAnimationFrame(() => step(droppedMaterial)); + return; + } + + if (pauseTimeRef.current) { + const pauseDuration = performance.now() - pauseTimeRef.current; + startTime += pauseDuration; + pauseTimeRef.current = null; + } + + const elapsedTime = performance.now() - startTime; + const unLoadDuration = agvDetail.point.action.unLoadDuration; + fixedInterval = ((unLoadDuration / agvDetail.currentLoad) * (1000 / speed)); + + if (elapsedTime >= fixedInterval) { + let droppedMat = droppedMaterial - 1; + decrementVehicleLoad(agvDetail.modelUuid, 1); + if (droppedMat > 0) { + startTime = performance.now(); + requestAnimationFrame(() => step(droppedMat)); + } else { + return; + } + } else { + requestAnimationFrame(() => step(droppedMaterial)); + } } - }); - function firstFrame() { - const unLoadDuration = agvDetail.point.action.unLoadDuration; - const droppedMaterial = agvDetail.currentLoad; - fixedInterval = (unLoadDuration / droppedMaterial) * 1000; - startTime = performance.now(); - step(droppedMaterial); - } - - function step(droppedMaterial: number) { - const elapsedTime = performance.now() - startTime; - - if (elapsedTime >= fixedInterval) { - let droppedMat = droppedMaterial - 1; - decrementVehicleLoad(agvDetail.modelUuid, 1); - if (droppedMat === 0) return; - startTime = performance.now(); - requestAnimationFrame(() => step(droppedMat)); - } else { - requestAnimationFrame(() => step(droppedMaterial)); - } - } - - return ( - <> - {currentPath.length > 0 && ( + return ( <> - - {currentPath.map((point, index) => ( - - - - - ))} + {currentPath.length > 0 && ( + <> + + {currentPath.map((point, index) => ( + + + + + ))} + + )} - )} - - ); + ); } export default VehicleAnimator; \ No newline at end of file diff --git a/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx b/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx index d0691f9..6a81d3a 100644 --- a/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx +++ b/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; import VehicleAnimator from '../animator/vehicleAnimator'; import * as THREE from 'three'; import { NavMeshQuery } from '@recast-navigation/core'; @@ -7,117 +7,131 @@ import { usePlayButtonStore } from '../../../../../store/usePlayButtonStore'; import { useVehicleStore } from '../../../../../store/simulation/useVehicleStore'; function VehicleInstance({ agvDetail }: any) { - const { navMesh } = useNavMesh(); - const { isPlaying } = usePlayButtonStore(); - const { vehicles, setVehicleActive, setVehicleState, incrementVehicleLoad } = useVehicleStore(); - const [currentPhase, setCurrentPhase] = useState('stationed'); - const [path, setPath] = useState<[number, number, number][]>([]); + const { navMesh } = useNavMesh(); + const { isPlaying } = usePlayButtonStore(); + const { vehicles, setVehicleActive, setVehicleState, incrementVehicleLoad } = useVehicleStore(); + const [currentPhase, setCurrentPhase] = useState('stationed'); + const [path, setPath] = useState<[number, number, number][]>([]); + let isIncrememtable = useRef(true); - const computePath = useCallback( - (start: any, end: any) => { - try { - const navMeshQuery = new NavMeshQuery(navMesh); - const { path: segmentPath } = navMeshQuery.computePath(start, end); - return ( - segmentPath?.map(({ x, y, z }) => [x, y + 0.1, z] as [number, number, number]) || [] - ); - } catch { - return []; - } - }, - [navMesh] - ); + const computePath = useCallback( + (start: any, end: any) => { + try { + const navMeshQuery = new NavMeshQuery(navMesh); + const { path: segmentPath } = navMeshQuery.computePath(start, end); + return ( + segmentPath?.map(({ x, y, z }) => [x, 0, z] as [number, number, number]) || [] + ); + } catch { + return []; + } + }, + [navMesh] + ); - function vehicleStatus(modelid: string, status: string) { - // console.log(`AGV ${modelid}: ${status}`); - } + function vehicleStatus(modelId: string, status: string) { + // console.log(`${modelId} , ${status}); + } - 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 - ); - setPath(toPickupPath); - setVehicleActive(agvDetail.modelUuid, true); - setVehicleState(agvDetail.modelUuid, 'running'); - setCurrentPhase('stationed-pickup'); - vehicleStatus(agvDetail.modelUuid, 'Started from station, heading to pickup'); - return; - } else if ( - !agvDetail.isActive && - agvDetail.state === 'idle' && - currentPhase === 'picking' - ) { - - setTimeout(() => { - incrementVehicleLoad(agvDetail.modelUuid, 2); - }, 5000); + // Function to reset everything + function reset() { + setCurrentPhase('stationed'); + setVehicleActive(agvDetail.modelUuid, false); + setVehicleState(agvDetail.modelUuid, 'idle'); + setPath([]); + } - if (agvDetail.currentLoad === agvDetail.point.action.loadCapacity) { - const toDrop = computePath( - agvDetail.point.action.pickUpPoint, - agvDetail.point.action.unLoadPoint - ); - setPath(toDrop); - setVehicleActive(agvDetail.modelUuid, true); - setVehicleState(agvDetail.modelUuid, 'running'); - setCurrentPhase('pickup-drop'); - vehicleStatus(agvDetail.modelUuid, 'Started from pickup point, heading to drop point'); + const increment = () => { + if (isIncrememtable.current) { + + incrementVehicleLoad(agvDetail.modelUuid, 2); + isIncrememtable.current = false; } - } else if ( - !agvDetail.isActive && - agvDetail.state === 'idle' && - currentPhase === 'dropping' && - agvDetail.currentLoad === 0 - ) { - const dropToPickup = computePath( - agvDetail.point.action.unLoadPoint, - agvDetail.point.action.pickUpPoint - ); - setPath(dropToPickup); - setVehicleActive(agvDetail.modelUuid, true); - setVehicleState(agvDetail.modelUuid, 'running'); - setCurrentPhase('drop-pickup'); - vehicleStatus(agvDetail.modelUuid, 'Started from dropping point, heading to pickup point'); - } } - }, [vehicles, currentPhase, path, isPlaying]); - function handleCallBack() { - if (currentPhase === 'stationed-pickup') { - setVehicleActive(agvDetail.modelUuid, false); - setVehicleState(agvDetail.modelUuid, 'idle'); - setCurrentPhase('picking'); - vehicleStatus(agvDetail.modelUuid, 'Reached pickup point, waiting for material'); - setPath([]); - } else if (currentPhase === 'pickup-drop') { - setVehicleActive(agvDetail.modelUuid, false); - setVehicleState(agvDetail.modelUuid, 'idle'); - setCurrentPhase('dropping'); - vehicleStatus(agvDetail.modelUuid, 'Reached drop point'); - setPath([]); - } else if (currentPhase === 'drop-pickup') { - setVehicleActive(agvDetail.modelUuid, false); - setVehicleState(agvDetail.modelUuid, 'idle'); - setCurrentPhase('picking'); - setPath([]); - vehicleStatus(agvDetail.modelUuid, 'Reached pickup point again, cycle complete'); + 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?.position + ); + setPath(toPickupPath); + setCurrentPhase('stationed-pickup'); + setVehicleState(agvDetail.modelUuid, 'running'); + setVehicleActive(agvDetail.modelUuid, true); + vehicleStatus(agvDetail.modelUuid, 'Started from station, heading to pickup'); + return; + } else if (!agvDetail.isActive && agvDetail.state === 'idle' && currentPhase === 'picking') { + + setTimeout(() => { + increment(); + }, 5000); + + if (agvDetail.currentLoad === agvDetail.point.action.loadCapacity) { + const toDrop = computePath( + agvDetail.point.action.pickUpPoint.position, + agvDetail.point.action.unLoadPoint.position + ); + setPath(toDrop); + setCurrentPhase('pickup-drop'); + setVehicleState(agvDetail.modelUuid, 'running'); + setVehicleActive(agvDetail.modelUuid, true); + vehicleStatus(agvDetail.modelUuid, 'Started from pickup point, heading to drop point'); + } + } else if (!agvDetail.isActive && agvDetail.state === 'idle' && currentPhase === 'dropping' && agvDetail.currentLoad === 0) { + const dropToPickup = computePath( + agvDetail.point.action.unLoadPoint.position, + agvDetail.point.action.pickUpPoint.position + ); + setPath(dropToPickup); + setCurrentPhase('drop-pickup'); + setVehicleState(agvDetail.modelUuid, 'running'); + setVehicleActive(agvDetail.modelUuid, true); + vehicleStatus(agvDetail.modelUuid, 'Started from dropping point, heading to pickup point'); + + isIncrememtable.current = true; + } + } else { + reset() + } + }, [vehicles, currentPhase, path, isPlaying]); + + function handleCallBack() { + if (currentPhase === 'stationed-pickup') { + setCurrentPhase('picking'); + setVehicleState(agvDetail.modelUuid, 'idle'); + setVehicleActive(agvDetail.modelUuid, false); + vehicleStatus(agvDetail.modelUuid, 'Reached pickup point, waiting for material'); + setPath([]); + } else if (currentPhase === 'pickup-drop') { + setCurrentPhase('dropping'); + setVehicleState(agvDetail.modelUuid, 'idle'); + setVehicleActive(agvDetail.modelUuid, false); + vehicleStatus(agvDetail.modelUuid, 'Reached drop point'); + setPath([]); + } else if (currentPhase === 'drop-pickup') { + setCurrentPhase('picking'); + setVehicleState(agvDetail.modelUuid, 'idle'); + setVehicleActive(agvDetail.modelUuid, false); + setPath([]); + vehicleStatus(agvDetail.modelUuid, 'Reached pickup point again, cycle complete'); + } } - } - return ( - <> - - - ); + return ( + <> + + + ); } export default VehicleInstance; \ No newline at end of file diff --git a/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx b/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx index 2a0070b..91111cf 100644 --- a/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx +++ b/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx @@ -3,12 +3,16 @@ import VehicleInstance from './instance/vehicleInstance' import { useVehicleStore } from '../../../../store/simulation/useVehicleStore' function VehicleInstances() { + const { vehicles } = useVehicleStore(); + return ( <> {vehicles.map((val: any, i: any) => + + )} diff --git a/app/src/modules/simulation/vehicle/vehicles.tsx b/app/src/modules/simulation/vehicle/vehicles.tsx index 9aa89f4..7badec5 100644 --- a/app/src/modules/simulation/vehicle/vehicles.tsx +++ b/app/src/modules/simulation/vehicle/vehicles.tsx @@ -1,15 +1,21 @@ -import React, { useEffect } from 'react' -import VehicleInstances from './instances/vehicleInstances'; -import { useVehicleStore } from '../../../store/simulation/useVehicleStore'; -import { useFloorItems } from '../../../store/store'; - +import React, { useEffect, useState } from "react"; +import VehicleInstances from "./instances/vehicleInstances"; +import { useVehicleStore } from "../../../store/simulation/useVehicleStore"; +import { useFloorItems } from "../../../store/store"; +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 { selectedEventSphere } = useSelectedEventSphere(); + const { selectedEventData } = useSelectedEventData(); const { floorItems } = useFloorItems(); + const { isPlaying } = usePlayButtonStore(); - const vehicleStatusSample: VehicleEventSchema[] = [ + const [vehicleStatusSample, setVehicleStatusSample] = useState< + VehicleEventSchema[] + >([ { modelUuid: "9356f710-4727-4b50-bdb2-9c1e747ecc74", modelName: "AGV", @@ -28,8 +34,9 @@ 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 }, + steeringAngle:0, + 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", @@ -71,8 +78,9 @@ function Vehicles() { actionType: "travel", unLoadDuration: 10, loadCapacity: 2, - pickUpPoint: { x: 90, y: 0, z: 28 }, - unLoadPoint: { x: 20, y: 0, z: 10 }, + steeringAngle:0, + pickUpPoint: null, + unLoadPoint: null, triggers: [ { triggerUuid: "trig-001", @@ -95,68 +103,118 @@ function Vehicles() { ] } } - }, { - modelUuid: "e729a4f1-11d2-4778-8d6a-468f1b4f6b79", - modelName: "forklift", - position: [98.85729337188162, 0, 38.36616546567653], - 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: 15, - loadCapacity: 5, - pickUpPoint: { x: 98.71483985219794, y: 0, z: 28.66321267938962 }, - unLoadPoint: { x: 20, y: 0, z: 10 }, - 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 - } - ] - } - } - } - ]; - - - useEffect(() => { - addVehicle('123', vehicleStatusSample[0]); - // addVehicle('123', vehicleStatusSample[1]); - // addVehicle('123', vehicleStatusSample[2]); - }, []) - + }, + // { + // 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, + // steeringAngle:0, + // 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", + // position: [98.85729337188162, 0, 38.36616546567653], + // 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: 15, + // loadCapacity: 5, + // steeringAngle:0, + // 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 + // } + // ] + // } + // } + // } + ]); useEffect(() => { // console.log('vehicles: ', vehicles); }, [vehicles]) + useEffect(() => { + addVehicle("123", vehicleStatusSample[0]); + addVehicle('123', vehicleStatusSample[1]); + // addVehicle('123', vehicleStatusSample[2]); + }, []); + return ( <> + {selectedEventSphere && selectedEventData?.data.type === "vehicle" && !isPlaying && + < VehicleUI /> + } - ) + ); } -export default Vehicles; \ No newline at end of file +export default Vehicles; + + + diff --git a/app/src/modules/visualization/RealTimeVisulization.tsx b/app/src/modules/visualization/RealTimeVisulization.tsx index 30f0014..05f7371 100644 --- a/app/src/modules/visualization/RealTimeVisulization.tsx +++ b/app/src/modules/visualization/RealTimeVisulization.tsx @@ -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(null); const { isPlaying } = usePlayButtonStore(); const { activeModule } = useModuleStore(); - const [droppedObjects, setDroppedObjects] = useState([]); const [zonesData, setZonesData] = useState({}); 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>({}); - 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) => { 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()} > diff --git a/app/src/modules/visualization/visualization.tsx b/app/src/modules/visualization/visualization.tsx index e5b1692..77956f4 100644 --- a/app/src/modules/visualization/visualization.tsx +++ b/app/src/modules/visualization/visualization.tsx @@ -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 ( <> diff --git a/app/src/modules/visualization/widgets/panel/AddButtons.tsx b/app/src/modules/visualization/widgets/panel/AddButtons.tsx index d066cd4..4877824 100644 --- a/app/src/modules/visualization/widgets/panel/AddButtons.tsx +++ b/app/src/modules/visualization/widgets/panel/AddButtons.tsx @@ -304,8 +304,8 @@ const AddButtons: React.FC = ({
@@ -341,8 +341,8 @@ const AddButtons: React.FC = ({
diff --git a/app/src/pages/Project.tsx b/app/src/pages/Project.tsx index 3f6b4cc..c491583 100644 --- a/app/src/pages/Project.tsx +++ b/app/src/pages/Project.tsx @@ -1,11 +1,10 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect } from "react"; import ModuleToggle from "../components/ui/ModuleToggle"; import SideBarLeft from "../components/layout/sidebarLeft/SideBarLeft"; import SideBarRight from "../components/layout/sidebarRight/SideBarRight"; import useModuleStore, { useThreeDStore } from "../store/useModuleStore"; import RealTimeVisulization from "../modules/visualization/RealTimeVisulization"; import Tools from "../components/ui/Tools"; -// import Scene from "../modules/scene/scene"; import { useSocketStore, useFloorItems, @@ -20,12 +19,9 @@ import { usePlayButtonStore } from "../store/usePlayButtonStore"; import MarketPlace from "../modules/market/MarketPlace"; import LoadingPage from "../components/templates/LoadingPage"; 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"; +import { useSelectedUserStore } from "../store/useCollabStore"; +import FollowPerson from "../components/templates/FollowPerson"; const Project: React.FC = () => { let navigate = useNavigate(); @@ -44,7 +40,7 @@ const Project: React.FC = () => { setActiveModule("builder"); const email = localStorage.getItem("email"); if (email) { - const Organization = email!.split("@")[1].split(".")[0]; + const Organization = email.split("@")[1].split(".")[0]; useSocketStore.getState().initializeSocket(email, Organization); const name = localStorage.getItem("userName"); if (Organization && name) { @@ -55,8 +51,10 @@ const Project: React.FC = () => { navigate("/"); } }, []); + const { isPlaying } = usePlayButtonStore(); const { toggleThreeD } = useThreeDStore(); + const { selectedUser } = useSelectedUserStore(); return (
@@ -66,7 +64,7 @@ const Project: React.FC = () => {
*/} - {loadingProgress && } + {loadingProgress > 0 && } {!isPlaying && ( <> {toggleThreeD && } @@ -81,6 +79,7 @@ const Project: React.FC = () => { {activeModule !== "market" && } {isPlaying && activeModule === "simulation" && } + {selectedUser && }
); }; diff --git a/app/src/pages/UserAuth.tsx b/app/src/pages/UserAuth.tsx index a5a7c57..19393f9 100644 --- a/app/src/pages/UserAuth.tsx +++ b/app/src/pages/UserAuth.tsx @@ -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) => { 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 ( - <> -
-
- -
-

Welcome to Dwinzo

-

- {isSignIn ? ( - <> - Don’t have an account?{" "} - setIsSignIn(false)} - style={{ cursor: "pointer" }} - > - Register here! - - - ) : ( - <> - Already have an account?{" "} - setIsSignIn(true)} - style={{ cursor: "pointer" }} - > - Login here! - - - )} -

+
+
+ +
+

Welcome to Dwinzo

+

+ {isSignIn ? ( + <> + Don’t have an account?{" "} + setIsSignIn(false)} + style={{ cursor: "pointer" }} + > + Register here! + + + ) : ( + <> + Already have an account?{" "} + setIsSignIn(true)} + style={{ cursor: "pointer" }} + > + Login here! + + + )} +

- + - {error &&
🛈 {error}
} + {error &&
🛈 {error}
} -
- {!isSignIn && ( - setUserName(e.target.value)} - required - /> - )} + + {!isSignIn && ( setEmail(e.target.value)} + type="text" + value={userName} + placeholder="Username" + onChange={(e) => setUserName(e.target.value)} required /> -
- setPassword(e.target.value)} - required - /> - -
- {!isSignIn && ( -
- -
- I have read and agree to the terms of service -
-
- )} - -
-

- By signing up for, or logging into, an account, you agree to our{" "} - navigate("/privacy")} - style={{ cursor: "pointer" }} - > - privacy policy - {" "} - &{" "} - navigate("/terms")} - style={{ cursor: "pointer" }} - > - terms of service - {" "} - whether you read them or not. You can also find these terms on our - website. -

-
- +
+ {!isSignIn && ( +
+ +
+ I have read and agree to the terms of service +
+
+ )} + + +

+ By signing up for, or logging into, an account, you agree to our{" "} + navigate("/privacy")} + style={{ cursor: "pointer" }} + > + privacy policy + {" "} + &{" "} + navigate("/terms")} + style={{ cursor: "pointer" }} + > + terms of service + {" "} + whether you read them or not. You can also find these terms on our + website. +

+
); }; diff --git a/app/src/services/factoryBuilder/assest/floorAsset/deleteFloorItemApi.ts b/app/src/services/factoryBuilder/assest/floorAsset/deleteFloorItemApi.ts index fbbb042..908319b 100644 --- a/app/src/services/factoryBuilder/assest/floorAsset/deleteFloorItemApi.ts +++ b/app/src/services/factoryBuilder/assest/floorAsset/deleteFloorItemApi.ts @@ -1,13 +1,13 @@ let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; -export const deleteFloorItem = async (organization: string, modeluuid: string, modelname: string) => { +export const deleteFloorItem = async (organization: string, modelUuid: string, modelName: string) => { try { const response = await fetch(`${url_Backend_dwinzo}/api/v1/deletefloorItem`, { method: "DELETE", headers: { "Content-Type": "application/json", }, - body: JSON.stringify({ organization, modeluuid, modelname }), + body: JSON.stringify({ organization, modelUuid, modelName }), }); if (!response.ok) { diff --git a/app/src/services/factoryBuilder/assest/floorAsset/setFloorItemApi.ts b/app/src/services/factoryBuilder/assest/floorAsset/setFloorItemApi.ts index d587f06..3d7c921 100644 --- a/app/src/services/factoryBuilder/assest/floorAsset/setFloorItemApi.ts +++ b/app/src/services/factoryBuilder/assest/floorAsset/setFloorItemApi.ts @@ -1,8 +1,8 @@ let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; export const setFloorItemApi = async ( organization: string, - modeluuid?: string, - modelname?: string, + modelUuid?: string, + modelName?: string, modelfileID?: string, position?: Object, rotation?: Object, @@ -10,7 +10,7 @@ export const setFloorItemApi = async ( isVisible?: boolean, ) => { try { - const body: any = { organization, modeluuid, modelname, position, rotation, modelfileID, isLocked, isVisible }; + const body: any = { organization, modelUuid, modelName, position, rotation, modelfileID, isLocked, isVisible }; const response = await fetch(`${url_Backend_dwinzo}/api/v2/setasset`, { method: "POST", diff --git a/app/src/services/factoryBuilder/assest/wallAsset/deleteWallItemApi.ts b/app/src/services/factoryBuilder/assest/wallAsset/deleteWallItemApi.ts index 58f179d..da50167 100644 --- a/app/src/services/factoryBuilder/assest/wallAsset/deleteWallItemApi.ts +++ b/app/src/services/factoryBuilder/assest/wallAsset/deleteWallItemApi.ts @@ -1,13 +1,13 @@ let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; -export const deleteWallItem = async (organization: string, modeluuid: string, modelname: string) => { +export const deleteWallItem = async (organization: string, modelUuid: string, modelName: string) => { try { const response = await fetch(`${url_Backend_dwinzo}/api/v1/deleteWallItem`, { method: "DELETE", headers: { "Content-Type": "application/json", }, - body: JSON.stringify({ organization, modeluuid, modelname }), + body: JSON.stringify({ organization, modelUuid, modelName }), }); if (!response.ok) { diff --git a/app/src/services/factoryBuilder/assest/wallAsset/setWallItemApi.ts b/app/src/services/factoryBuilder/assest/wallAsset/setWallItemApi.ts index 79bed55..984adf5 100644 --- a/app/src/services/factoryBuilder/assest/wallAsset/setWallItemApi.ts +++ b/app/src/services/factoryBuilder/assest/wallAsset/setWallItemApi.ts @@ -2,8 +2,8 @@ let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_UR export const setWallItem = async ( organization: string, - modeluuid: string, - modelname: string, + modelUuid: string, + modelName: string, type: string, csgposition: Object, csgscale: Object, @@ -17,7 +17,7 @@ export const setWallItem = async ( headers: { "Content-Type": "application/json", }, - body: JSON.stringify({ organization, modeluuid, modelname, position, type, csgposition, csgscale, quaternion, scale }), + body: JSON.stringify({ organization, modelUuid, modelName, position, type, csgposition, csgscale, quaternion, scale }), }); if (!response.ok) { diff --git a/app/src/services/factoryBuilder/webWorkers/assetManagerWorker.js b/app/src/services/factoryBuilder/webWorkers/assetManagerWorker.js index 4ccdbf5..50dac3f 100644 --- a/app/src/services/factoryBuilder/webWorkers/assetManagerWorker.js +++ b/app/src/services/factoryBuilder/webWorkers/assetManagerWorker.js @@ -21,7 +21,7 @@ onmessage = (event) => { const itemPosition = new THREE.Vector3(...item.position); const distance = cameraPos.distanceTo(itemPosition); - if (distance <= renderDistance && !uuids.includes(item.modeluuid)) { + if (distance <= renderDistance && !uuids.includes(item.modelUuid)) { toAdd.push(item); } }); @@ -35,7 +35,7 @@ onmessage = (event) => { // Check for items to be removed uuids.forEach((uuid) => { - const floorItem = floorItems.find((item) => item.modeluuid === uuid); + const floorItem = floorItems.find((item) => item.modelUuid === uuid); if (floorItem) { const itemPosition = new THREE.Vector3(...floorItem.position); const distance = cameraPos.distanceTo(itemPosition); diff --git a/app/src/services/simulation/deleteProductDataApi.ts b/app/src/services/simulation/deleteProductApi.ts similarity index 89% rename from app/src/services/simulation/deleteProductDataApi.ts rename to app/src/services/simulation/deleteProductApi.ts index 06718f8..2987a53 100644 --- a/app/src/services/simulation/deleteProductDataApi.ts +++ b/app/src/services/simulation/deleteProductApi.ts @@ -1,6 +1,6 @@ let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; -export const deleteProductDataApi = async (productId: string, organization: string) => { +export const deleteProductApi = async (productId: string, organization: string) => { try { const response = await fetch(`${url_Backend_dwinzo}/api/v2/productDataDelete?productId=${productId}&organization=${organization}`, { method: "PATCH", diff --git a/app/src/store/simulation/useProductStore.ts b/app/src/store/simulation/useProductStore.ts index 8ec74cf..e6f6ae0 100644 --- a/app/src/store/simulation/useProductStore.ts +++ b/app/src/store/simulation/useProductStore.ts @@ -6,12 +6,14 @@ type ProductsStore = { // Product-level actions addProduct: (productName: string, productId: string) => void; + setProducts: (products: productsSchema) => void; removeProduct: (productId: string) => void; updateProduct: (productId: string, updates: Partial<{ productName: string; eventDatas: EventsSchema[] }>) => void; // Event-level actions addEvent: (productId: string, event: EventsSchema) => void; removeEvent: (productId: string, modelUuid: string) => void; + deleteEvent: (modelUuid: string) => void; updateEvent: (productId: string, modelUuid: string, updates: Partial) => void; // Point-level actions @@ -78,6 +80,12 @@ export const useProductStore = create()( }); }, + setProducts: (products) => { + set((state) => { + state.products = products; + }); + }, + removeProduct: (productId) => { set((state) => { state.products = state.products.filter(p => p.productId !== productId); @@ -112,6 +120,14 @@ export const useProductStore = create()( }); }, + deleteEvent: (modelUuid: string) => { + set((state) => { + for (const product of state.products) { + product.eventDatas = product.eventDatas.filter(e => 'modelUuid' in e && e.modelUuid !== modelUuid); + } + }); + }, + updateEvent: (productId, modelUuid, updates) => { set((state) => { const product = state.products.find(p => p.productId === productId); @@ -433,7 +449,7 @@ export const useProductStore = create()( getPointByUuid: (productId, modelUuid, pointUuid) => { const event = get().getEventByModelUuid(productId, modelUuid); if (!event) return undefined; - + if ('points' in event) { return (event as ConveyorEventSchema).points.find(p => p.uuid === pointUuid); } else if ('point' in event && (event as any).point.uuid === pointUuid) { diff --git a/app/src/store/simulation/useVehicleStore.ts b/app/src/store/simulation/useVehicleStore.ts index 449ceb7..a5ea3be 100644 --- a/app/src/store/simulation/useVehicleStore.ts +++ b/app/src/store/simulation/useVehicleStore.ts @@ -22,6 +22,7 @@ interface VehiclesStore { ) => void; setVehicleActive: (modelUuid: string, isActive: boolean) => void; + updateSteeringAngle: (modelUuid: string, steeringAngle: number) => void; incrementVehicleLoad: (modelUuid: string, incrementBy: number) => void; decrementVehicleLoad: (modelUuid: string, decrementBy: number) => void; setVehicleState: (modelUuid: string, newState: VehicleStatus['state']) => void; @@ -76,6 +77,15 @@ export const useVehicleStore = create()( }); }, + updateSteeringAngle: (modelUuid, steeringAngle) => { + set((state) => { + const vehicle = state.vehicles.find(v => v.modelUuid === modelUuid); + if (vehicle) { + vehicle.point.action.steeringAngle = steeringAngle; + } + }); + }, + incrementVehicleLoad: (modelUuid, incrementBy) => { set((state) => { const vehicle = state.vehicles.find(v => v.modelUuid === modelUuid); diff --git a/app/src/store/useCollabStore.ts b/app/src/store/useCollabStore.ts new file mode 100644 index 0000000..3fe1497 --- /dev/null +++ b/app/src/store/useCollabStore.ts @@ -0,0 +1,30 @@ +import { create } from 'zustand'; + +interface SelectedUser { + color: string; + name: string; + location?: { + position: { + x: number; + y: number; + z: number; + }; + rotation: { + x: number; + y: number; + z: number; + }; + } +} + +interface SelectedUserStore { + selectedUser: SelectedUser | null; + setSelectedUser: (user: SelectedUser) => void; + clearSelectedUser: () => void; +} + +export const useSelectedUserStore = create((set) => ({ + selectedUser: null, + setSelectedUser: (user) => set({ selectedUser: user }), + clearSelectedUser: () => set({ selectedUser: null }), +})); diff --git a/app/src/styles/abstracts/placeholders.scss b/app/src/styles/abstracts/placeholders.scss deleted file mode 100644 index 18f28f9..0000000 --- a/app/src/styles/abstracts/placeholders.scss +++ /dev/null @@ -1,6 +0,0 @@ -// center a element -%centered { - display: flex; - justify-content: center; - align-items: center; -} diff --git a/app/src/styles/abstracts/variables.scss b/app/src/styles/abstracts/variables.scss index 44e5627..2913425 100644 --- a/app/src/styles/abstracts/variables.scss +++ b/app/src/styles/abstracts/variables.scss @@ -1,123 +1,172 @@ -/* ======================================================================== - 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-button-color: #f3f3fd; -$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; +$text-button-color-dark: #f3f3fd; -// 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-solid: #fcfdfd; +$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-solid-dark: #19191d; +$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; +$input-border-color: #d5dddd80; +$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; +$input-border-color-dark: #d5dddd80; +$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; + +// icon colors +// ---------- light mode ---------- +$icon-default-color: #6f42c1; +$icon-default-color-hover: #7f4ddb; +$icon-default-color-active: #f2f2f7; + +// ---------- dark mode ---------- +$icon-default-color-dark: #6f42c1; +$icon-default-color-hover-dark: #7f4ddb; +$icon-default-color-active-dark: #f2f2f7; + +// 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%); +$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; +$border-radius-xxx: 30px; diff --git a/app/src/styles/base/base.scss b/app/src/styles/base/base.scss index e74c0ec..10fb87c 100644 --- a/app/src/styles/base/base.scss +++ b/app/src/styles/base/base.scss @@ -1,139 +1,161 @@ -@use "../abstracts/variables" as *; // abstracts/variables.scss +@use "../abstracts/variables" as *; -// Light theme styles [data-theme="light"] { - // Text and Input colors - --text-color: #{$text-color}; // Main text color for light theme - --text-disabled: #{$text-disabled}; // Disabled text color - --input-text-color: #{$input-text-color}; // Input field text color + // text colors + --text-color: #{$text-color}; + --text-disabled: #{$text-disabled}; + --text-button-color: #{$text-button-color}; + --input-text-color: #{$input-text-color}; + --highlight-text-color: #{$highlight-text-color}; - // Accent and Highlight colors - --primary-color: #{$background-color}; // Primary color for light theme - --accent-color: #{$accent-color}; // Primary accent color for light theme - --highlight-accent-color: #{$highlight-accent-color}; // Highlight color for light theme - --accent-gradient-color: #{$acent-gradient}; // Primary accent color for light theme + // background colors + --background-color: #{$background-color}; + --background-color-solid: #{$background-color-solid}; + --background-color-secondary: #{$background-color-secondary}; + --background-color-accent: #{$background-color-accent}; + --background-color-button: #{$background-color-button}; + --background-color-drop-down: #{$background-color-drop-down}; + --background-color-input: #{$background-color-input}; + --background-color-input-focus: #{$background-color-input-focus}; + --background-color-drop-down-gradient: #{$background-color-drop-down-gradient}; + --background-color-selected: #{$background-color-selected}; + --background-radial-gray-gradient: #{$background-radial-gray-gradient}; + + // border colors + --border-color: #{$border-color}; + --input-border-color: #{$input-border-color}; + --border-color-accent: #{$border-color-accent}; + + // highlight colors + --highlight-accent-color: #{$highlight-accent-color}; + --highlight-secondary-color: #{$highlight-secondary-color}; + + // icon colors + --icon-default-color: #{$icon-default-color}; + --icon-default-color-active: #{$icon-default-color-active}; + + // old colors + --accent-color: #{$accent-color}; + --highlight-accent-color: #{$highlight-accent-color}; + --accent-gradient-color: #{$acent-gradient}; --faint-gradient-color: #{$faint-gradient}; - - // Background colors - --background-color: #{$background-color}; // Main background color - --background-color-secondary: #{$background-color-secondary}; // Secondary background color - --background-color-gray: #{$background-color-gray}; // Secondary background color - - // Border colors - --border-color: #{$border-color}; // Border color for light theme - - // Shadow variables - --shadow-main-light: #{$shadow-color}; // Main shadow color - --box-shadow-light: 0px 2px 4px var(--shadow-main-light); // Light shadow - --box-shadow-medium: 0px 4px 8px var(--shadow-main-light); // Medium shadow - --box-shadow-heavy: 0px 8px 16px var(--shadow-main-light); // Heavy shadow - - // Font families - --font-inter: #{$font-inter}; // Inter font family - --font-josefin-sans: #{$font-josefin-sans}; // Josefin Sans font family - --font-poppins: #{$font-poppins}; // Poppins font family - --font-roboto: #{$font-roboto}; // Roboto font family + --background-color-gray: #{$background-color-gray}; + --border-color: #{$border-color}; + --shadow-main-light: #{$shadow-color}; + --box-shadow-light: 0px 2px 4px var(--shadow-main-light); + --box-shadow-medium: 0px 4px 8px var(--shadow-main-light); + --box-shadow-heavy: 0px 8px 16px var(--shadow-main-light); + --font-inter: #{$font-inter}; + --font-josefin-sans: #{$font-josefin-sans}; + --font-poppins: #{$font-poppins}; + --font-roboto: #{$font-roboto}; } -// Dark theme styles [data-theme="dark"] { - // Text and Input colors - --text-color: #{$text-color-dark}; // Main text color for dark theme - --text-disabled: #{$text-disabled-dark}; // Disabled text color - --input-text-color: #{$input-text-color-dark}; // Input field text color + // text colors + --text-color: #{$text-color-dark}; + --text-disabled: #{$text-disabled-dark}; + --text-button-color: #{$text-button-color-dark}; + --input-text-color: #{$input-text-color-dark}; + --highlight-text-color: #{$highlight-text-color-dark}; - // Accent and Highlight colors - --primary-color: #{$highlight-accent-color-dark}; - --accent-color: #{$accent-color-dark}; // Primary accent color for dark theme - --highlight-accent-color: #{$highlight-accent-color-dark}; // Highlight color for dark theme - --accent-gradient-color: #{$acent-gradient-dark}; // Primary accent color for light theme + // background colors + --background-color: #{$background-color-dark}; + --background-color-solid: #{$background-color-solid-dark}; + --background-color-secondary: #{$background-color-secondary-dark}; + --background-color-accent: #{$background-color-accent-dark}; + --background-color-button: #{$background-color-button-dark}; + --background-color-drop-down: #{$background-color-drop-down-dark}; + --background-color-input: #{$background-color-input-dark}; + --background-color-input-focus: #{$background-color-input-focus-dark}; + --background-color-drop-down-gradient: #{$background-color-drop-down-gradient-dark}; + --background-color-selected: #{$background-color-selected-dark}; + --background-radial-gray-gradient: #{$background-radial-gray-gradient-dark}; + + // border colors + --border-color: #{$border-color}; + --input-border-color: #{$input-border-color-dark}; + --border-color-accent: #{$border-color-accent-dark}; + + // highlight colors + --highlight-accent-color: #{$highlight-accent-color-dark}; + --highlight-secondary-color: #{$highlight-secondary-color-dark}; + + // icon colors + --icon-default-color: #{$icon-default-color-dark}; + --icon-default-color-active: #{$icon-default-color-active-dark}; + + // old colors + --accent-color: #{$accent-color-dark}; + --highlight-accent-color: #{$highlight-accent-color-dark}; + --accent-gradient-color: #{$acent-gradient-dark}; --faint-gradient-color: #{$faint-gradient-dark}; - - // Background colors - --background-color: #{$background-color-dark}; // Main background color - --background-color-secondary: #{$background-color-secondary-dark}; // Secondary background color - --background-color-gray: #{$background-color-gray-dark}; // Secondary background color - - // Border colors - --border-color: #{$border-color-dark}; // Border color for dark theme - - // Shadow variables - --shadow-main-dark: #{$shadow-color}; // Main shadow color - --box-shadow-light: 0px 2px 4px var(--shadow-main-dark); // Light shadow - --box-shadow-medium: 0px 4px 8px var(--shadow-main-dark); // Medium shadow - --box-shadow-heavy: 0px 8px 16px var(--shadow-main-dark); // Heavy shadow - - // Font families - --font-inter: #{$font-inter}; // Inter font family - --font-josefin-sans: #{$font-josefin-sans}; // Josefin Sans font family - --font-poppins: #{$font-poppins}; // Poppins font family - --font-roboto: #{$font-roboto}; // Roboto font family + --background-color-gray: #{$background-color-gray-dark}; + --border-color: #{$border-color-dark}; + --shadow-main-dark: #{$shadow-color}; + --box-shadow-light: 0px 2px 4px var(--shadow-main-dark); + --box-shadow-medium: 0px 4px 8px var(--shadow-main-dark); + --box-shadow-heavy: 0px 8px 16px var(--shadow-main-dark); + --font-inter: #{$font-inter}; + --font-josefin-sans: #{$font-josefin-sans}; + --font-poppins: #{$font-poppins}; + --font-roboto: #{$font-roboto}; } -// Root container styles #root { - height: 100vh; // Full viewport height - width: 100vw; // Full viewport width - overflow: hidden; // Prevent scrollbars - background-color: var(--background-color-gray); + height: 100vh; + width: 100vw; + overflow: hidden; + background: var(--background-color-gray); } -// Root overlay styles #root-over { - position: fixed; // Fix overlay to the viewport - top: 0; // Align to the top - left: 0; // Align to the left - z-index: 99; // Ensure high stacking order + position: fixed; + top: 0; + left: 0; + z-index: 99; } body { background: var(--background-color); + --font-size-tiny: #{$tiny}; + --font-size-small: #{$small}; + --font-size-regular: #{$regular}; + --font-size-large: #{$large}; + --font-size-xlarge: #{$xlarge}; + --font-size-xxlarge: #{$xxlarge}; + --font-size-xxxlarge: #{$xxxlarge}; + --font-weight-regular: #{$regular-weight}; + --font-weight-medium: #{$medium-weight}; + --font-weight-bold: #{$bold-weight}; - /* Font Sizes */ - --font-size-tiny: #{$tiny}; // Extra small text - --font-size-small: #{$small}; // Small text - --font-size-regular: #{$regular}; // Default text size - --font-size-large: #{$large}; // Large text size - --font-size-xlarge: #{$xlarge}; // Extra large text size - --font-size-xxlarge: #{$xxlarge}; // Double extra large text size - --font-size-xxxlarge: #{$xxxlarge}; // Triple extra large text size - - /* Font Weights */ - --font-weight-regular: #{$regular-weight}; // Regular font weight - --font-weight-medium: #{$medium-weight}; // Medium font weight - --font-weight-bold: #{$bold-weight}; // Bold font weight + // colors + --color1: #{$color1}; + --color2: #{$color2}; + --color3: #{$color3}; + --color4: #{$color4}; + --color5: #{$color5}; } -/* Apply custom scrollbar styles globally */ ::-webkit-scrollbar { - width: 8px; - /* Width of the scrollbar */ - height: 8px; - /* Height for horizontal scrollbars */ + width: 0px; + height: 0px; } ::-webkit-scrollbar-track { background: transparent; - /* Background of the scrollbar track */ border-radius: 4px; - /* Rounded corners */ } ::-webkit-scrollbar-thumb { - background: var(--accent-color); - /* Scrollbar handle color */ + background: var(--background-color-button); border-radius: 4px; - /* Rounded corners */ - border: 2px solid var(--primary-color); - /* Padding around the scrollbar handle */ } ::-webkit-scrollbar-thumb:hover { - background: var(--accent-color); - /* Handle color on hover */ + background: var(--background-color-button); } ::-webkit-scrollbar-corner { background: transparent; - /* Remove corner styling for scrollable containers */ } diff --git a/app/src/styles/base/global.scss b/app/src/styles/base/global.scss new file mode 100644 index 0000000..d90e6fb --- /dev/null +++ b/app/src/styles/base/global.scss @@ -0,0 +1,11 @@ +@use "../abstracts/variables" as *; +@use "../abstracts/mixins" as *; + +section, .section{ + padding: 4px; + outline: 1px solid var(--border-color); + outline-offset: -1px; + border-radius: #{$border-radius-large}; + background: var(--background-color); + margin: 4px 0; +} diff --git a/app/src/styles/base/reset.scss b/app/src/styles/base/reset.scss index 82d286e..ab77f9a 100644 --- a/app/src/styles/base/reset.scss +++ b/app/src/styles/base/reset.scss @@ -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; +} \ No newline at end of file diff --git a/app/src/styles/components/analysis/ROISummary.scss b/app/src/styles/components/analysis/ROISummary.scss new file mode 100644 index 0000000..c1ba7d5 --- /dev/null +++ b/app/src/styles/components/analysis/ROISummary.scss @@ -0,0 +1,318 @@ +.roiSummary-container { + .roiSummary-wrapper { + background-color: var(--background-color); + + .product-info { + display: flex; + } + + .playBack { + display: flex; + background-color: var(--background-color); + border-radius: 12px; + padding: 6px; + + .info { + span { + font-size: var(--font-size-xlarge); + + &:first-child { + color: #31C756; + } + + &:last-child { + color: var(--text-color); + } + } + } + } + + .roi-details { + display: flex; + align-items: center; + gap: 12px; + + .progress-wrapper { + width: 250px; + display: flex; + flex-direction: column; + gap: 6px; + + .content { + display: flex; + flex-direction: column; + gap: 3px; + align-items: center; + + .key { + font-size: var(--font-size-xlarge); + color: var(--accent-color); + } + } + } + + .roi-progress { + width: 100%; + } + + .metrics { + display: flex; + flex-direction: column; + gap: 6px; + + .metric-item { + width: 100%; + border-radius: 6px; + border: 1px solid #00FF56; + background: #436D51; + display: flex; + flex-direction: column; + padding: 4px 6px; + + &:last-child { + align-items: center; + } + + .metric-label { + font-size: 10px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .metric-value { + text-align: center; + line-height: 20px; + } + } + + .metric-wrapper { + display: flex; + gap: 6px; + + .metric-item { + background-color: var(--background-color); + border: 1px solid var(--Grays-Gray-6, #F2F2F7); + } + } + } + } + + .cost-breakdown { + background-color: var(--background-color); + border: 1px solid var(--text-disabled); + border-radius: 8px; + padding: 16px; + + .breakdown-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + margin-bottom: 16px; + + .section-wrapper { + display: flex; + gap: 4px; + align-items: center; + } + + .section-number { + font-size: 20px; + color: #00aaff; + } + + .section-title { + font-size: var(--font-size-regular); + color: var(--text-color); + } + + .expand-icon { + font-size: 16px; + color: var(--text-color); + cursor: pointer; + transform: rotate(90deg); + transition: transform 0.2s linear; + } + + .expand-icon.open { + transform: rotate(0deg); + + } + } + + .breakdown-table { + width: 100%; + border-collapse: collapse; + border-radius: 8px; + + th, + td { + padding: 8px; + text-align: left; + border-top: 1px solid var(--text-disabled); + border-bottom: 1px solid var(--text-disabled); + } + + th:first-child, + td:first-child { + border-left: 1px solid var(--text-disabled); + } + + th:last-child, + td:last-child { + border-right: 1px solid var(--text-disabled); + } + + th { + background-color: var(--background-color); + color: #333; + } + + .total-row, + .net-profit-row { + font-weight: bold; + color: #333; + } + } + } + + .tips-section { + background-color: var(--background-color); + border-radius: 8px; + display: flex; + flex-direction: column; + gap: 6px; + padding: 12px; + + .tip-header { + display: flex; + align-items: center; + + .tip-title { + color: var(--text-color); + font-weight: 600; + } + } + + .tip-description { + span { + font-size: var(--font-size-xlarge); + color: #34C759; + + &:first-child { + color: #34C759; + } + + &:nth-child(2) { + color: #488EF6; + } + } + } + } + + .get-tips-button { + border: none; + border-radius: 5px; + cursor: pointer; + font-size: 14px; + margin-top: 8px; + display: inline-block; + display: flex; + justify-content: flex-end; + background: none; + + .btn { + background-color: var(--accent-color); + color: var(--background-color); + padding: 4px 6px; + border-radius: 5px; + display: inline-block; + font-size: 14px; + text-align: center; + } + } + } + + .semi-circle-wrapper { + width: 100%; + height: 125px; + overflow-y: hidden; + position: relative; + } + + .semi-circle { + width: 100%; + height: 250px; + border-radius: 50%; + position: relative; + transition: background 0.5s ease; + } + + .progress-cover { + position: absolute; + width: 75%; + height: 75%; + top: 12.5%; + left: 12.5%; + background-color: var(--background-color); + border-radius: 50%; + } + + .label-wrapper { + .label { + font-size: var(--font-size-xxxlarge); + } + + position: absolute; + bottom: 0%; + left: 50%; + transform: translate(-50%, 0%); + font-weight: bold; + font-size: 1.2rem; + color: #333; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + } +} + +// Breakdown Table Open/Close Logic + +.breakdown-table-wrapper { + &.closed { + max-height: 0; + padding: 0; + } + + &.open { + max-height: 500px; + } + + + + + + .breakdown-table { + width: 100%; + border-collapse: collapse; + + th, + td { + padding: 10px; + border: 1px solid #ddd; + text-align: left; + } + + .total-row { + background-color: #f4f4f4; + font-weight: bold; + } + + .net-profit-row { + background-color: #dff0d8; + font-weight: bold; + } + } +} \ No newline at end of file diff --git a/app/src/styles/components/analysis/analysis.scss b/app/src/styles/components/analysis/analysis.scss index bc33556..0af69c2 100644 --- a/app/src/styles/components/analysis/analysis.scss +++ b/app/src/styles/components/analysis/analysis.scss @@ -1,18 +1,25 @@ .analysis { - position: absolute; + position: fixed; top: 0; left: 0; display: flex; - justify-content: center; - align-items: center; + justify-content: space-between; + align-items: start; width: 100%; height: 100vh; - z-index: 100000000000000000000000000000; + // pointer-events: none;k + z-index: 10000; + + .analysis-wrapper { + display: flex; + flex-direction: column; + gap: 12px; + } } .analysis-card { min-width: 333px; - // background: var(--primary-color); + background: var(--background-color); border-radius: 20px; padding: 8px; @@ -63,14 +70,14 @@ height: 4px; border-radius: 13px; overflow: hidden; - background-color: #FBEBD7; + background: #FBEBD7; .bar-fill { position: absolute; height: 100%; top: 0; left: 0; - background-color: #FC9D2F; + background: #FC9D2F; border-radius: 13px; } @@ -144,7 +151,7 @@ } canvas { - background-color: transparent; + background: transparent; } } } @@ -200,15 +207,15 @@ height: 5px; &:nth-child(1) { - background-color: #F3C64D; + background: #F3C64D; } &:nth-child(2) { - background-color: #67B3F4; + background: #67B3F4; } &:nth-child(3) { - background-color: #7981F5; + background: #7981F5; } } } @@ -228,21 +235,21 @@ &:nth-child(1) { .indicator { - background-color: #F3C64D; + background: #F3C64D; } } &:nth-child(2) { .indicator { - background-color: #67B3F4; + background: #67B3F4; } } &:nth-child(3) { .indicator { - background-color: #7981F5; + background: #7981F5; } } diff --git a/app/src/styles/components/confirmationPopUp.scss b/app/src/styles/components/confirmationPopUp.scss index 4bef0ae..e955a79 100644 --- a/app/src/styles/components/confirmationPopUp.scss +++ b/app/src/styles/components/confirmationPopUp.scss @@ -11,7 +11,7 @@ left: 50%; transform: translate(-50%, -50%); z-index: 5; - background-color: var(--background-color); + background: var(--background-color); padding: 14px 12px; border-radius: 6px; @@ -32,7 +32,7 @@ } &:last-child { - background-color: #ffe3e0; + background: #ffe3e0; color: #f65648; } diff --git a/app/src/styles/components/input.scss b/app/src/styles/components/input.scss index 21554e8..b4e7651 100644 --- a/app/src/styles/components/input.scss +++ b/app/src/styles/components/input.scss @@ -5,17 +5,18 @@ input { width: 100%; - padding: 2px 4px; - border-radius: #{$border-radius-small}; - outline: 1px solid var(--border-color); + padding: 4px 8px; + border-radius: #{$border-radius-large}; + outline: 1px solid var(--input-border-color); outline-offset: -1px; border: none; - background: transparent; + background: var(--background-color-input); color: var(--input-text-color); &:focus, &:active { - outline: 1px solid var(--accent-color); + outline: 1px solid var(--border-color-accent); + background: var(--background-color-input-focus); } &:-webkit-autofill, @@ -27,29 +28,34 @@ input { caret-color: var(--input-text-color); // Background styles - background-color: var(--background-color) !important; - -webkit-box-shadow: 0 0 0px 1000px var(--background-color) inset !important; + background: var(--background-color-solid) !important; + -webkit-box-shadow: 0 0 0px 1000px var(--background-color-solid) inset !important; } // File input specific style adjustments &::file-selector-button { font-size: 14px; - color: var(--accent-color); - background-color: var(--background-color-secondary); + background: var(--background-color-secondary); border: none; outline: none; border-radius: #{$border-radius-small}; padding: 2px; cursor: pointer; - - // Hover effect for the file button - &:hover { - color: var(--primary-color); - background-color: var(--accent-color); - } } } +input[type="number"] { + // Chrome, Safari, Edge, Opera + &::-webkit-outer-spin-button, + &::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; + } + + // Firefox + -moz-appearance: textfield; +} + .input-value { color: var(--input-text-color); font-size: var(--font-size-regular); @@ -67,9 +73,7 @@ input { color: var(--input-text-color); font-size: var(--font-size-regular); font-weight: var(--font-weight-regular); - border: 1px solid var(--accent-color); outline: none; - border-radius: #{$border-radius-small}; line-height: 26px; padding: 0 8px; } @@ -93,8 +97,8 @@ input { } .active { - background-color: var(--accent-color); - color: var(--primary-color); + background: var(--background-color-button); + color: var(--text-button-color); } } @@ -102,17 +106,17 @@ input { position: sticky; top: 0; padding: 8px 10px; - background: var(--background-color); + width: 100%; z-index: 1; .search-container { @include flex-center; width: 100%; - border-radius: #{$border-radius-small}; - background-color: var(--background-color); - padding: 6px 2px; + border-radius: #{$border-radius-extra-large}; + padding: 3px 2px; position: relative; border: 1px solid var(--border-color); + background: var(--background-color-input-focus); .icon-container { @include flex-center; @@ -129,7 +133,7 @@ input { font-weight: var(--font-weight-regular); border: none; outline: none; - background-color: transparent; + background: transparent; padding-left: 36px; } @@ -141,16 +145,17 @@ input { height: 24px; border: none; cursor: pointer; - background-color: transparent; + background: transparent; &:hover { - background-color: var(--highlight-accent-color); + background: var(--highlight-accent-color); } } } .active { border: 1px solid var(--accent-color); + background: var(--background-color-input-focus); } } @@ -166,7 +171,7 @@ input { position: absolute; left: 10px; top: 12px; - background-color: var(--background-color); + background: var(--background-color); border-radius: #{$border-radius-small}; box-shadow: var(--box-shadow-medium); z-index: 1; @@ -182,7 +187,7 @@ input { gap: 2px; &:hover { - background-color: var(--background-color-secondary); + background: var(--background-color-secondary); } .icon-container { @@ -197,11 +202,11 @@ input { } .selected { - background-color: var(--highlight-accent-color); + background: var(--highlight-accent-color); color: var(--accent-color); &:hover { - background-color: var(--highlight-accent-color); + background: var(--highlight-accent-color); } } @@ -226,13 +231,12 @@ input { justify-content: space-between; cursor: pointer; border-radius: 6px; - background-color: var(--background-color); } .dropdown-options { position: absolute; width: 100%; - background-color: var(--primary-color); + background: var(--background-color); border: 1px solid var(--border-color); border-radius: #{$border-radius-small}; z-index: 10; @@ -241,6 +245,7 @@ input { left: 0; top: 110%; padding: 4px; + backdrop-filter: blur(8px); .dropdown-search { margin-bottom: 4px; @@ -253,8 +258,8 @@ input { border-radius: #{$border-radius-small}; &:hover { - color: var(--accent-color); - background-color: var(--highlight-accent-color); + color: var(--highlight-text-color); + background: var(--highlight-accent-color); } } } @@ -318,7 +323,7 @@ input { .dropdown-button { width: 100%; - background-color: var(--background-color) !important; + background: var(--background-color) !important; border: 1px solid var(--border-color) !important; padding: 5px 10px; @@ -335,11 +340,11 @@ input { transition: background-color 0.3s ease; &:hover { - background-color: #333333; + background: #333333; } &.open { - background-color: #333333; + background: #333333; } } @@ -347,7 +352,7 @@ input { position: absolute; top: 110%; right: -16px; - background-color: var(--background-color); + background: var(--background-color); border: 1px solid var(--border-color); border-radius: 5px; box-shadow: #{$box-shadow-medium}; @@ -412,7 +417,7 @@ input { transition: background-color 0.3s ease; &:hover { - background-color: var(--background-color); + background: var(--background-color); } } @@ -435,12 +440,12 @@ input { } &:hover { - background-color: var(--background-color); + background: var(--background-color); } &.open { color: var(--accent-color); - background-color: var(--highlight-accent-color); + background: var(--highlight-accent-color); } .icon { @@ -472,20 +477,20 @@ input { } .check-box { - height: 22px; - width: 44px; - background: var(--background-color-secondary); + height: 24px; + width: 38px; + background: var(--background-color); border-radius: #{$border-radius-large}; position: relative; cursor: pointer; .check-box-style { position: absolute; - height: 18px; - width: 18px; + height: 20px; + width: 20px; top: 2px; left: 2px; - background: var(--accent-color); + background: var(--text-button-color); border-radius: #{$border-radius-circle}; transition: left 0.3s ease; } @@ -523,7 +528,7 @@ input { -webkit-appearance: none; width: 16px; height: 16px; - background: var(--primary-color); + background: var(--background-color-accent); border-radius: #{$border-radius-circle}; box-shadow: 0 0 2px rgba(0, 0, 0, 0.2); cursor: pointer; @@ -597,19 +602,19 @@ input { cursor: not-allowed; &::-webkit-slider-thumb { - background: var(--primary-color); + background: var(--background-color-accent); box-shadow: none; outline: 4px solid var(--text-disabled); outline: -4px; } &::-moz-range-thumb { - background: var(--primary-color); + background: var(--background-color-accent); box-shadow: none; } &::-ms-thumb { - background: var(--primary-color); + background: var(--background-color-accent); box-shadow: none; } @@ -644,14 +649,10 @@ input { padding: 2px 32px; border: none; border-radius: #{$border-radius-large}; - color: var(--text-disabled); - background: var(--accent-color); + color: var(--text-color); + background: var(--background-color-button); transition: all 0.2s; cursor: pointer; - - &:hover { - color: var(--primary-color); - } } } @@ -710,11 +711,6 @@ input { line-height: 12px; text-align: center; border-radius: #{$border-radius-small}; - - &:hover { - background: var(--accent-color); - color: var(--primary-color); - } } } } @@ -722,8 +718,8 @@ input { .invite-button { padding: 4px 12px; border-radius: #{$border-radius-large}; - background: var(--accent-color); - color: var(--primary-color); + background: var(--background-color-accent); + color: var(--text-button-color); } .multi-email-invite-input.active { @@ -740,17 +736,17 @@ input { @include flex-center; } } - .upload-custom-asset-button{ + .upload-custom-asset-button { padding: 6px 12px; @include flex-space-between; - .title{ + .title { white-space: nowrap; width: 40%; } - input{ + input { display: none; } - .upload-button{ + .upload-button { width: 60%; background: var(--highlight-accent-color); color: var(--accent-color); @@ -770,7 +766,7 @@ input { width: 100%; height: 100%; border-radius: #{$border-radius-small}; - background-color: var(--background-color-gray); + background: var(--background-color-gray); } } } diff --git a/app/src/styles/components/lists.scss b/app/src/styles/components/lists.scss index cf66d2b..3b52e69 100644 --- a/app/src/styles/components/lists.scss +++ b/app/src/styles/components/lists.scss @@ -30,8 +30,6 @@ } .list-wrapper { - - .no-item { padding: 12px; } @@ -41,7 +39,6 @@ // margin-left: 10px; overflow: hidden; - .list-item { @include flex-space-between; width: 100%; @@ -67,7 +64,7 @@ } .active { - background-color: var(--highlight-accent-color); + background: var(--highlight-accent-color); color: var(--primary-color); } } @@ -75,7 +72,6 @@ .asset-list { border-left: 2px solid var(--border-color); - margin-left: 20px + margin-left: 20px; } - -} \ No newline at end of file +} diff --git a/app/src/styles/components/marketPlace/marketPlace.scss b/app/src/styles/components/marketPlace/marketPlace.scss index c93b344..b84a252 100644 --- a/app/src/styles/components/marketPlace/marketPlace.scss +++ b/app/src/styles/components/marketPlace/marketPlace.scss @@ -5,28 +5,28 @@ height: 100vh; width: 100vw; z-index: #{$z-index-marketplace}; - background-color: var(--background-color-secondary); + background: var(--background-color-secondary); position: absolute; left: 0; top: 0; padding: 100px 50px; padding-bottom: 32px; - backdrop-filter: blur(6px); - + .marketplace-container { position: relative; padding: 20px 2px; height: 100%; - background-color: var(--background-color); + background: var(--background-color); box-shadow: #{$box-shadow-medium}; - border-radius: #{$border-radius-extra-large}; + border-radius: #{$border-radius-xxx}; + outline: 1px solid var(--border-color); + backdrop-filter: blur(16px); } .marketPlace { width: 100%; height: 100%; overflow: auto; - padding-bottom: 60px; display: flex; flex-direction: column; gap: 24px; @@ -83,12 +83,18 @@ } } } - + .cards-container-wrapper{ + position: relative; + height: calc(100% - 60px); + padding: 0px 10px; + } .cards-container-container { - padding: 0px 20px; display: flex; + padding: 0 10px; flex-direction: column; gap: 6px; + height: 100%; + overflow: auto; .header { color: var(--text-color); @@ -107,7 +113,8 @@ border-radius: 18px; padding: 12px; box-shadow: 0px 2px 10.5px 0px #0000000d; - border: 1px solid var(--background-accent-transparent, #e0dfff80); + background: var(--background-color); + border: 1px solid var(--border-color); position: relative; display: flex; flex-direction: column; @@ -124,7 +131,7 @@ height: 30px; border-radius: 10px; padding: 5px; - background-color: var(--accent-color); + background: var(--accent-color); } .image-container { @@ -193,11 +200,11 @@ .buy-now-button { width: 100%; - background-color: var(--background-color-secondary); + background: var(--background-color-button); border-radius: $border-radius-extra-large; padding: 8px 0; @include flex-center; - color: var(--accent-color); + color: var(--text-button-color); &:hover { cursor: pointer; @@ -220,7 +227,7 @@ .assetPreview { width: 100%; height: 100%; - background-color: var(--background-color); + background: var(--background-color); display: flex; gap: 12px; overflow: hidden; @@ -262,7 +269,7 @@ border-radius: 50%; font-weight: var(--font-weight-bold); color: var(--background-color); - background-color: var(--accent-color); + background: var(--accent-color); } .organization-details { @@ -327,7 +334,7 @@ display: block; width: 2px; height: 12px; - background-color: #ccc; + background: #ccc; } } @@ -363,7 +370,7 @@ } &:last-child { - background-color: var(--accent-color); + background: var(--accent-color); color: var(--background-color); } } diff --git a/app/src/styles/components/menu/menu.scss b/app/src/styles/components/menu/menu.scss index 55b16f9..be9e17f 100644 --- a/app/src/styles/components/menu/menu.scss +++ b/app/src/styles/components/menu/menu.scss @@ -6,15 +6,31 @@ align-items: center; gap: 2px; position: relative; - height: 32px; + border-radius: #{$border-radius-extra-large}; + background: var(--background-color-drop-down); + padding: 3px 8px; + width: fit-content; + max-width: 100%; .project-name { - line-height: 32px; + @include flex-center; height: 100%; + line-height: 26px; + .icon{ + @include flex-center; + height: 20px; + width: 20px; + } + .input-value{ + max-width: 120px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } } .more-options-button { @include flex-center; border-radius: #{$border-radius-small}; - height: 28px; + height: 22px; position: relative; &:hover { background: var(--highlight-accent-color); @@ -36,11 +52,12 @@ top: 32px; left: 0; z-index: 5; - background-color: var(--background-color); + background: var(--background-color); color: var(--text-color); box-shadow: var(--box-shadow-light); border-radius: 8px; border: 1px solid var(--border-color); + backdrop-filter: blur(10px); .menu-buttons { display: flex; flex-direction: column; @@ -73,7 +90,7 @@ position: absolute; top: 0; left: 100%; - background-color: var(--background-color); + background: var(--background-color); min-width: 220px; border-radius: 4px; box-shadow: var(--box-shadow-light); @@ -95,7 +112,7 @@ rotate: -90deg; } &:hover { - background-color: var(--highlight-accent-color); + background: var(--highlight-accent-color); span, .menu-item-right span { color: var(--accent-color); @@ -123,7 +140,7 @@ position: absolute; left: 100%; top: 0; - background-color: var(--background-color); + background: var(--background-color); min-width: 200px; border-radius: 0 4px 4px 4px; box-shadow: var(--box-shadow-light); @@ -140,7 +157,7 @@ white-space: nowrap; color: var(--text-color); &:hover { - background-color: var(--highlight-accent-color); + background: var(--highlight-accent-color); span { color: var(--accent-color); } @@ -154,7 +171,7 @@ } &:hover { - background-color: var(--highlight-accent-color); + background: var(--highlight-accent-color); .menu-button { color: var(--accent-color); } @@ -164,7 +181,7 @@ .split { width: 100%; height: 1px; - background-color: var(--highlight-accent-color); + background: var(--highlight-accent-color); margin: 2px 0; } } diff --git a/app/src/styles/components/moduleToggle.scss b/app/src/styles/components/moduleToggle.scss index a6a77c8..c864cbf 100644 --- a/app/src/styles/components/moduleToggle.scss +++ b/app/src/styles/components/moduleToggle.scss @@ -18,7 +18,8 @@ padding: 4px 12px; border-radius: #{$border-radius-extra-large}; box-shadow: var(--box-shadow-medium); - background-color: var(--background-color); + background: var(--background-color); + backdrop-filter: blur(8px); cursor: pointer; overflow: hidden; position: relative; @@ -37,7 +38,7 @@ left: 0; width: 0%; height: 100%; - background-color: var(--highlight-accent-color); + background: var(--highlight-accent-color); transition: width 0.2s; border-radius: #{$border-radius-extra-large}; } @@ -49,15 +50,15 @@ } .active { cursor: default; - background-color: var(--accent-color); + background: var(--background-color-button); &::after{ display: none; } &:hover { - background-color: var(--accent-color); + background: var(--background-color-button); } .module { - color: var(--highlight-accent-color); + color: var(--icon-default-color-active); } } } diff --git a/app/src/styles/components/simulation/simulation.scss b/app/src/styles/components/simulation/simulation.scss index eb109c2..9f748f8 100644 --- a/app/src/styles/components/simulation/simulation.scss +++ b/app/src/styles/components/simulation/simulation.scss @@ -3,55 +3,151 @@ .simulation-player-wrapper { position: fixed; - bottom: 32px; + bottom: 50px; left: 50%; z-index: 2; transform: translate(-50%, 0); + width: 70%; + .simulation-player-container { + background-color: var(--background-color); + padding: 7px; + border-radius: 15px; + display: flex; + flex-direction: column; + gap: 8px; + + .progresser-wrapper { + background-color: var(--highlight-accent-color); + padding: 4px 5px; + border-radius: 12px; + display: flex; + flex-direction: column; + gap: 12px; + padding-top: 30px; + transition: height 0.2s linear; + } + + + + .controls-container { @include flex-center; gap: 12px; - margin-bottom: 4px; + justify-content: space-between; + + .production-details, + .controls-wrapper { + display: flex; + gap: 6px; + } + + .production-details { + .production-wrapper { + + display: flex; + align-items: center; + flex-direction: column; + gap: 6px; + + .header { + display: flex; + flex-direction: row; + gap: 6px + } + + .progress-wrapper { + width: 164px; + height: 8px; + border-radius: 5px; + // overflow: hidden; + background-color: var(--highlight-accent-color); + + .progress { + border-radius: 5px; + height: 100%; + background-color: var(--accent-color); + } + } + } + } + .simulation-button-container { @include flex-center; gap: 2px; padding: 6px 8px; min-width: 64px; - background-color: var(--background-color); + background: var(--background-color); border-radius: #{$border-radius-small}; cursor: pointer; + &:hover { - background-color: var(--highlight-accent-color); + background: var(--highlight-accent-color); color: var(--accent-color); + path { stroke: var(--accent-color); } } } } + .speed-control-container { @include flex-center; gap: 18px; padding: 5px 16px; - background: var(--background-color); + // background: var(--background-color); border-radius: #{$border-radius-medium}; box-sizing: #{$box-shadow-medium}; + border-radius: 20px; + position: relative; + .min-value, .max-value { + display: flex; + align-items: center; font-weight: var(--font-weight-bold); } + .slider-container { - width: 580px; + width: 100%; max-width: 80vw; height: 28px; - background: var(--background-color-gray); + // background: var(--background-color-gray); border-radius: #{$border-radius-small}; position: relative; - padding: 4px 26px; + // padding: 4px 26px; + + + .speed-label { + font-size: var(--font-size-tiny); + position: absolute; + bottom: -4px; + + &:first-child { + left: 0; + } + + &:last-child { + right: 0; + } + } + + &::after { + content: ""; + background-color: #E5E5EA; + position: absolute; + top: 50%; + transform: translate(0, -50%); + width: 100%; + height: 3px; + } + .custom-slider { height: 100%; width: 100%; position: relative; + .slider-input { position: absolute; width: 100%; @@ -60,15 +156,17 @@ z-index: 3; cursor: pointer; } + .slider-handle { position: absolute; + top: 50%; width: 42px; line-height: 20px; text-align: center; background: var(--accent-color); color: var(--primary-color); border-radius: #{$border-radius-small}; - transform: translateX(-50%); + transform: translate(-50%, -50%); cursor: pointer; z-index: 2; } @@ -81,34 +179,184 @@ border-radius: 1px; top: 8px; } - .marker.marker-10{ - left: 10%; + + .marker.marker-10 { + left: 10%; } - .marker.marker-20{ - left: 20%; + + .marker.marker-20 { + left: 20%; } - .marker.marker-30{ - left: 30%; + + .marker.marker-30 { + left: 30%; } - .marker.marker-40{ - left: 40%; + + .marker.marker-40 { + left: 40%; } - .marker.marker-50{ - left: 50%; + + .marker.marker-50 { + left: 50%; } - .marker.marker-60{ - left: 60%; + + .marker.marker-60 { + left: 60%; } - .marker.marker-70{ - left: 70%; + + .marker.marker-70 { + left: 70%; } - .marker.marker-80{ - left: 80%; + + .marker.marker-80 { + left: 80%; } - .marker.marker-90{ - left: 90%; + + .marker.marker-90 { + left: 90%; } } } + + .time-displayer { + display: flex; + justify-content: space-between; + height: auto; + opacity: 1; + // overflow: hidden; + + transition: all 0.5s ease; + + .start-time-wrappper, + .end-time-wrappper { + display: flex; + align-items: center; + gap: 12px; + } + + .time-progresser { + width: 70%; + + .timeline { + padding: 16px; + // background: #f5f3fa; + background: linear-gradient(90.17deg, rgba(255, 255, 255, 0.64) 1.53%, rgba(255, 255, 255, 0.48) 98.13%); + + border-radius: 30px; + display: flex; + align-items: center; + width: 100%; + height: 33px; + + .label-dot-wrapper { + display: flex; + flex-direction: column; + align-items: center; + gap: 6px; + position: relative; + + .label { + position: absolute; + top: -200%; + transform: translate(0, -0); + font-size: 12px; + color: #666; + white-space: nowrap; + } + + .dot { + width: 14px; + height: 14px; + border-radius: 50%; + background-color: #d3d3e2; + + &.filled { + background-color: #8f5cf2; + + border: 4px solid var(--accent-color); + } + } + } + + .line { + flex-grow: 1; + height: 4px; + background-color: #d3d3e2; + margin: 0 4px; + + &.filled { + background-color: #8f5cf2; + } + } + } + } + } + + + } + + } + +.processDisplayer { + border-radius: 5px; + // overflow: hidden; + background-color: var(--highlight-accent-color); + padding: 14px 6px; + position: relative; + + .process-player { + position: absolute; + top: 50%; + transform: translate(0, -50%); + width: 3.946108102798462px; + height: 26px; + left: 86.81px; + border-radius: 14px; + border-width: 1px; + background: var(--accent-color, #6F42C1); + + } + + .process-wrapper { + display: flex; + // padding: 0px 16px; + + .process { + height: 5px; + background-color: #4caf50; + color: white; + text-align: center; + line-height: 30px; + transition: width 0.3s ease; + } + } + +} + + +.simulation-player-container.open { + + .progresser-wrapper { + padding-top: 4px; + } + + .time-displayer { + height: 0; + opacity: 0; + pointer-events: none; + display: none; + } + + .processDisplayer { + padding: 0; + background: transparent; + + .process-player { + width: 0; + display: none !important; + } + } + +} \ No newline at end of file diff --git a/app/src/styles/components/templates.scss b/app/src/styles/components/templates.scss index e69de29..a2a3da5 100644 --- a/app/src/styles/components/templates.scss +++ b/app/src/styles/components/templates.scss @@ -0,0 +1,22 @@ +.follow-person-container{ + height: 100vh; + width: 100vw; + position: fixed; + top: 0; + left: 0; + outline: 8px solid var(--user-color); + outline-offset: -3px; + border-radius: 16px; + .follower-name{ + background: var(--user-color); + color: #FFFFFF; + padding: 4px 8px; + padding-top: 16px; + text-align: center; + position: absolute; + top: -10px; + left: 50%; + transform: translate(-50%, 0); + border-radius: 8px; + } +} diff --git a/app/src/styles/components/tools.scss b/app/src/styles/components/tools.scss index 5a10518..abea736 100644 --- a/app/src/styles/components/tools.scss +++ b/app/src/styles/components/tools.scss @@ -13,14 +13,15 @@ border-radius: #{$border-radius-large}; width: fit-content; transition: width 0.2s; - background-color: var(--background-color); + background: var(--background-color); + backdrop-filter: blur(8px); z-index: #{$z-index-default}; .split { height: 20px; width: 2px; border-radius: 2px; - background: var(--highlight-accent-color); + background: var(--text-disabled); } .draw-tools, @@ -48,10 +49,10 @@ } .active { - background-color: var(--accent-color); + background: var(--background-color-accent); &:hover { - background-color: var(--accent-color); + background: var(--background-color-accent); } } } @@ -91,7 +92,7 @@ padding: 4px; &:hover { - background-color: var(--highlight-accent-color); + background: var(--highlight-accent-color); color: var(--accent-color); path { @@ -117,7 +118,7 @@ @include flex-center; padding: 3px; border-radius: #{$border-radius-small}; - background-color: var(--highlight-accent-color); + background: var(--highlight-accent-color); gap: 2px; position: relative; @@ -131,7 +132,7 @@ &::after { content: ""; position: absolute; - background-color: var(--accent-color); + background: var(--accent-color); left: 3px; top: 3px; height: 18px; @@ -161,7 +162,7 @@ width: 30px; height: 30px; border-radius: 50%; - background-color: var(--highlight-accent-color); + background: var(--highlight-accent-color); cursor: pointer; @include flex-center; position: fixed; @@ -174,7 +175,7 @@ font-weight: 700; &:hover { font-weight: 500; - background-color: var(--accent-color); + background: var(--accent-color); color: var(--highlight-accent-color); &::after{ animation: pulse 1s ease-out infinite; diff --git a/app/src/styles/components/visualization/floating/common.scss b/app/src/styles/components/visualization/floating/common.scss index d138959..0084e7a 100644 --- a/app/src/styles/components/visualization/floating/common.scss +++ b/app/src/styles/components/visualization/floating/common.scss @@ -3,7 +3,7 @@ .throughput-wrapper, .card { - background-color: var(--background-color); + background: var(--background-color); box-shadow: var(--box-shadow-heavy); @include flex-center; flex-direction: column; @@ -105,7 +105,7 @@ } .productionCapacity-wrapper { - background-color: var(--background-color); + background: var(--background-color); display: flex; flex-direction: column; gap: 6px; @@ -116,7 +116,7 @@ .headeproductionCapacityr-wrapper, .bar-chart { padding: 14px; - background-color: var(--background-color); + background: var(--background-color); display: flex; flex-direction: column; gap: 6px; @@ -276,7 +276,7 @@ .icon { width: 45px; height: 45px; - background-color: var(--accent-color); + background: var(--accent-color); display: flex; justify-content: center; align-items: center; @@ -289,7 +289,7 @@ display: flex; flex-direction: column; gap: 6px; - background-color: var(--background-color); + background: var(--background-color); padding: 14px; .header { @@ -307,7 +307,7 @@ .productivity-dashboard { width: 100%; - background-color: var(--background-color); + background: var(--background-color); color: white; padding: 20px; border-radius: 8px; @@ -324,7 +324,7 @@ } .options { - background-color: #343b47; + background: #343b47; width: 30px; height: 30px; display: flex; @@ -334,7 +334,7 @@ cursor: pointer; &:hover { - background-color: #49505a; + background: #49505a; } } } @@ -350,7 +350,7 @@ gap: 10px; .metric { - background-color: #2c3e50; + background: #2c3e50; padding: 15px; border-radius: 4px; @@ -445,7 +445,7 @@ } .scaleLabels { - background-color: var(--background-color); + background: var(--background-color); box-shadow: var(--box-shadow-heavy); display: flex; justify-content: space-between; diff --git a/app/src/styles/components/visualization/ui/styledWidgets.scss b/app/src/styles/components/visualization/ui/styledWidgets.scss index 794b756..8262bb3 100644 --- a/app/src/styles/components/visualization/ui/styledWidgets.scss +++ b/app/src/styles/components/visualization/ui/styledWidgets.scss @@ -43,7 +43,7 @@ .icon { width: 16.95305824279785px; height: 16.95305824279785px; - background-color: var(--accent-color); + background: var(--accent-color); border-radius: 50%; display: flex; justify-content: center; diff --git a/app/src/styles/layout/loading.scss b/app/src/styles/layout/loading.scss index 0a13753..59354ff 100644 --- a/app/src/styles/layout/loading.scss +++ b/app/src/styles/layout/loading.scss @@ -4,7 +4,7 @@ .loading-wrapper { height: 100vh; width: 100vw; - background: var(--background-color); + background: var(--background-color-solid); .loading-container { position: relative; height: 100%; diff --git a/app/src/styles/layout/popup.scss b/app/src/styles/layout/popup.scss index a354c10..3b31442 100644 --- a/app/src/styles/layout/popup.scss +++ b/app/src/styles/layout/popup.scss @@ -5,13 +5,13 @@ height: 100vh; width: 100vw; background: var(--background-color-secondary); - backdrop-filter: blur(2px); @include flex-center; .collaboration-popup-container { max-width: 50vw; - width: 460px; - background-color: var(--background-color); + width: 520px; + background: var(--background-color); border-radius: #{$border-radius-large}; + backdrop-filter: blur(8px); .split { width: 100%; height: 1px; @@ -88,14 +88,14 @@ .your-name { @include flex-center; gap: 6px; - color: var(--accent-color); + color: var(--text-color); .user-profile{ height: 24px; width: 24px; text-align: center; line-height: 25px; - background-color: var(--accent-color); - color: var(--primary-color); + background: var(--background-color-accent); + color: var(--text-color); border-radius: #{$border-radius-circle}; } } @@ -137,6 +137,7 @@ width: 100%; object-fit: cover; vertical-align: top; + pointer-events: none; } } .user-name{ diff --git a/app/src/styles/layout/sidebar.scss b/app/src/styles/layout/sidebar.scss index cab078e..01715ec 100644 --- a/app/src/styles/layout/sidebar.scss +++ b/app/src/styles/layout/sidebar.scss @@ -6,9 +6,10 @@ position: fixed; top: 32px; left: 8px; - background-color: var(--background-color); + background: var(--background-color); backdrop-filter: blur(150px); border-radius: #{$border-radius-extra-large}; + outline: 1px solid var(--border-color); box-shadow: #{$box-shadow-medium}; z-index: #{$z-index-tools}; @@ -43,33 +44,40 @@ width: 32px; min-height: 32px; min-width: 32px; - border-radius: #{$border-radius-small}; + border-radius: #{$border-radius-large}; &:hover { - background-color: var(--background-color-secondary); + outline: 1px solid var(--border-color); + outline-offset: -1px; + background: var(--background-color-drop-down); } } .active { - background-color: var(--background-color-secondary); - outline: 1px solid var(--accent-color); + background: var(--background-color-accent); + outline: 1px solid var(--border-color); outline-offset: -1px; + rect { + stroke: var(--icon-default-color-active); + } + circle { + fill: var(--icon-default-color-active); + } + &:hover { + background: var(--background-color-secondary); + } } } .sidebar-left-container { min-height: 50vh; - padding-bottom: 12px; + padding-bottom: 4px; position: relative; display: flex; flex-direction: column; .sidebar-left-content-container { - border-bottom: 1px solid var(--border-color); - // flex: 1; - // height: calc(100% - 36px); position: relative; - // overflow: auto; .template-list { display: flex; @@ -174,7 +182,7 @@ .stock { padding: 13px 5px; - background-color: var(--background-color-secondary); + background: var(--background-color-secondary); border-radius: 6.33px; display: flex; justify-content: space-between; @@ -231,8 +239,12 @@ .outline-content-container { position: relative; height: 100%; - overflow: auto; - max-height: 60vh; + padding: 8px; + .overflow { + height: calc(100% - 16px); + max-height: 46vh; + overflow: auto; + } } } } @@ -243,15 +255,17 @@ position: fixed; top: 32px; right: 8px; - background-color: var(--background-color); + background: var(--background-color); backdrop-filter: blur(150px); border-radius: #{$border-radius-extra-large}; + outline: 1px solid var(--border-color); box-shadow: #{$box-shadow-medium}; z-index: #{$z-index-tools}; .header-container { @include flex-space-between; padding: 10px; + padding-left: 16px; width: 100%; gap: 12px; height: 52px; @@ -262,8 +276,8 @@ .share-button { padding: 4px 12px; - color: var(--primary-color); - background-color: var(--accent-color); + color: var(--text-color); + background: var(--background-color-button); font-weight: var(--font-weight-regular); border-radius: #{$border-radius-large}; cursor: pointer; @@ -277,7 +291,7 @@ .split { height: 20px; width: 2px; - background: var(--background-color-secondary); + background: var(--text-disabled); } .users-container { @@ -320,8 +334,8 @@ display: flex; .user-profile { - background: var(--accent-color); - color: var(--primary-color); + background: var(--background-color-accent); + color: var(--text-button-color); } .user-organization { @@ -346,6 +360,8 @@ .sidebar-actions-container { position: absolute; left: -40px; + background: transparent; + overflow: visible; .sidebar-action-list { margin-bottom: 12px; @@ -353,28 +369,83 @@ height: 34px; width: 34px; border-radius: #{$border-radius-circle}; - background: var(--primary-color); + background: var(--background-color-secondary); + backdrop-filter: blur(8px); box-shadow: #{$box-shadow-medium}; + outline: 1px solid var(--border-color); + outline-offset: -1px; } .active { - background: var(--accent-color); + background: var(--background-color-accent); } } .sidebar-right-container { min-height: 50vh; - padding-bottom: 12px; + padding: 8px; 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: 304px; + padding-bottom: 10px; + + .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); + strokeWidth: 1.5px; + } + color: var(--accent-color); + &:before { + width: 100%; + } + } + } + } + } + } + } } } @@ -481,7 +552,7 @@ } button { - background-color: transparent; + background: transparent; box-shadow: none; color: #5273eb; padding: 6px; @@ -642,7 +713,7 @@ path { stroke: var(--accent-color); - stroke-width: 1.5px; + strokeWidth: 1.5px; } &:hover { @@ -659,10 +730,10 @@ .machine-mechanics-content-container, .simulations-container, .event-proprties-wrapper { + position: relative; max-height: calc(60vh - (47px - 35px)); - overflow: auto; - overflow-y: scroll; - + width: calc(100% - 4px); + overflow-x: hidden; .header { @include flex-space-between; padding: 6px 12px; @@ -675,10 +746,14 @@ color: var(--primary-color); border-radius: #{$border-radius-small}; cursor: pointer; - + outline: none; + border: none; path { stroke: var(--primary-color); } + &:disabled { + background: var(--text-disabled); + } } } @@ -706,7 +781,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 +822,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); + } } } @@ -784,7 +861,7 @@ transform: translateX(4px); &:hover { - background-color: var(--accent-color); + background: var(--accent-color); path { stroke: var(--primary-color); @@ -797,6 +874,7 @@ @include flex-center; padding: 4px; cursor: grab; + width: 100%; &:active { cursor: grabbing; @@ -841,9 +919,8 @@ .collapse-header-container { @include flex-space-between; padding-right: 12px; - margin-top: 8px; - border-top: 1px solid var(--border-color); - border-bottom: 1px solid var(--border-color); + margin: 8px 0; + width: 100%; .header { color: var(--accent-color); @@ -925,11 +1002,8 @@ .zone-properties-container { .header { @include flex-space-between; - padding: 8px 12px; - border-top: 1px solid var(--highlight-accent-color); - border-bottom: 1px solid var(--highlight-accent-color); - color: var(--accent-color); - + padding: 10px 12px; + color: var(--text-color); .input-value { color: inherit; } @@ -937,7 +1011,6 @@ .input-container { @include flex-center; - .remove-button { @include flex-center; height: 18px; @@ -946,7 +1019,7 @@ border-radius: 8px 0 0 8px; &:hover { - background-color: var(--accent-color); + background: var(--accent-color); path { stroke: var(--primary-color); @@ -959,8 +1032,8 @@ .generate-report-button, .button-save { @include flex-center; - background-color: var(--accent-color); - color: var(--primary-color); + background: var(--background-color-button); + color: var(--text-button-color); border-radius: #{$border-radius-large}; padding: 2px; gap: 4px; @@ -970,13 +1043,14 @@ margin-bottom: 8px; } - .split { - height: 1px; - background: var(--highlight-accent-color); - margin: 8px; - } - .custom-input-container { + @include flex-space-between; + .split { + height: 20px; + width: 2px; + border-radius: 2px; + background: var(--text-disabled); + } .header { @include flex-space-between; border: none; @@ -988,11 +1062,9 @@ .inputs-container { @include flex-space-between; - padding-bottom: 8px; .input-container { padding: 0 12px; - margin-top: 6px; gap: 6px; } } @@ -1010,7 +1082,6 @@ .dropdown-header-container, .dropdown-content-container { padding: 6px 12px; - border-top: 1px solid var(--highlight-accent-color); } .input-range-container { @@ -1104,14 +1175,14 @@ flex-direction: row; flex-wrap: wrap; height: 100%; - gap: 3px; + gap: 8px; padding: 10px 0; .category { - width: 117px; + width: 121px; height: 95px; border-radius: 3.59px; - background-color: var(--background-color-gray); + background: var(--background-color-gray); padding: 8px; padding-top: 12px; font-weight: $bold-weight; @@ -1131,7 +1202,7 @@ width: 60px; height: 60px; border-radius: 50%; - background-color: var(--circle-color, #000); + background: var(--circle-color, #000); position: absolute; top: 60%; right: -10px; @@ -1212,7 +1283,7 @@ width: 117px; height: 95px; border-radius: #{$border-radius-small}; - background-color: var(--background-color-gray); + background: var(--background-color-gray); font-weight: $medium-weight; position: relative; overflow: hidden; @@ -1279,7 +1350,7 @@ width: 117px; height: 95px; border-radius: 3.59px; - background-color: var(--background-color-gray); + background: var(--background-color-gray); padding: 8px; padding-top: 12px; font-weight: $medium-weight; diff --git a/app/src/styles/layout/toast.scss b/app/src/styles/layout/toast.scss index 951a213..eb51c5a 100644 --- a/app/src/styles/layout/toast.scss +++ b/app/src/styles/layout/toast.scss @@ -51,19 +51,19 @@ } .toast.success { - background-color: #4caf50; + background: #4caf50; } .toast.error { - background-color: #f44336; + background: #f44336; } .toast.info { - background-color: #2196f3; + background: #2196f3; } .toast.warning { - background-color: #ff9800; + background: #ff9800; } @keyframes fadeIn { diff --git a/app/src/styles/main.scss b/app/src/styles/main.scss index c0d2431..66a60e7 100644 --- a/app/src/styles/main.scss +++ b/app/src/styles/main.scss @@ -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 @@ -26,6 +26,7 @@ @use 'components/menu/menu'; @use 'components/confirmationPopUp'; @use 'components/analysis/analysis'; +@use 'components/analysis/ROISummary.scss'; // layout @use 'layout/loading'; diff --git a/app/src/styles/pages/dashboard.scss b/app/src/styles/pages/dashboard.scss index 3fc9a05..a41dc50 100644 --- a/app/src/styles/pages/dashboard.scss +++ b/app/src/styles/pages/dashboard.scss @@ -43,7 +43,7 @@ padding: 12px 16px; cursor: not-allowed; color: var(--accent-color); - background-color: var(--background-color-secondary); + background: var(--background-color-secondary); border-radius: #{$border-radius-large}; } .side-bar-content-container { @@ -66,9 +66,9 @@ .active { color: var(--accent-color); font-weight: var(--font-weight-medium); - background-color: var(--highlight-accent-color); + background: var(--highlight-accent-color); &:hover { - background-color: var(--highlight-accent-color); + background: var(--highlight-accent-color); } } } @@ -161,7 +161,7 @@ width: 26px; line-height: 26px; text-align: center; - background-color: var(--accent-color); + background: var(--accent-color); color: var(--primary-color); border-radius: #{$border-radius-circle}; } diff --git a/app/src/styles/pages/home.scss b/app/src/styles/pages/home.scss index 4139cc1..a2db6f7 100644 --- a/app/src/styles/pages/home.scss +++ b/app/src/styles/pages/home.scss @@ -1,38 +1,3 @@ -.App-logo { - height: 40vmin; - pointer-events: none; -} - -@media (prefers-reduced-motion: no-preference) { - .App-logo { - animation: App-logo-spin infinite 20s linear; - } -} - -.App-header { - background-color: var(--primary-color); - min-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); - color: 282c34; -} - -.App-link { - color: var(--button-action-color); -} - -@keyframes App-logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - .navbar{ position: absolute; top: 0; diff --git a/app/src/styles/pages/realTimeViz.scss b/app/src/styles/pages/realTimeViz.scss index 0d4f98d..3aa1bd5 100644 --- a/app/src/styles/pages/realTimeViz.scss +++ b/app/src/styles/pages/realTimeViz.scss @@ -3,7 +3,7 @@ // Main Container .realTime-viz { - background-color: #131313; + background: #131313; border-radius: 20px; box-shadow: $box-shadow-medium; width: calc(100% - (320px + 270px + 90px)); @@ -66,7 +66,7 @@ .zone-wrapper { display: flex; - background-color: var(--background-color); + background: var(--background-color); position: absolute; bottom: 0px; left: 50%; @@ -84,7 +84,7 @@ } .arrow { - background-color: var(--highlight-accent-color); + background: var(--highlight-accent-color); color: var(--background-color); } @@ -109,7 +109,7 @@ .zone { width: auto; - background-color: var(--background-color); + background: var(--background-color); border-radius: 6px; padding: 4px 8px; white-space: nowrap; @@ -117,7 +117,7 @@ } .active { - background-color: var(--accent-color); + background: var(--accent-color); color: var(--background-color); // color: #FCFDFD !important; } @@ -138,13 +138,13 @@ position: relative; flex: 1; height: 600px; - background-color: rgb(235, 235, 235); + background: rgb(235, 235, 235); margin: 0 30px; transition: height 0.3s ease, margin 0.3s ease; .zone-wrapper { display: flex; - background-color: rgba(224, 223, 255, 0.5); + background: rgba(224, 223, 255, 0.5); position: absolute; // bottom: 10px; left: 50%; @@ -162,7 +162,7 @@ .zone { width: auto; - background-color: $background-color; + background: $background-color; border-radius: 6px; padding: 4px 8px; white-space: nowrap; @@ -170,7 +170,7 @@ transition: background-color 0.3s ease; &.active { - background-color: var(--primary-color); + background: var(--primary-color); color: var(--accent-color); } } @@ -203,7 +203,7 @@ display: flex; flex-direction: column; gap: 6px; - background-color: var(--background-color); + background: var(--background-color); &::-webkit-scrollbar { display: none; @@ -217,7 +217,7 @@ border-radius: 8px; box-shadow: var(--box-shadow-medium); padding: 6px 0; - background-color: var(--background-color); + background: var(--background-color); position: relative; padding: 0 10px; @@ -237,7 +237,7 @@ top: 18px; right: 5px; transform: translate(0px, 0); - background-color: var(--background-color); + background: var(--background-color); z-index: 10; display: flex; @@ -261,7 +261,7 @@ } &:hover { - background-color: var(--highlight-accent-color); + background: var(--highlight-accent-color); width: 100%; svg { @@ -353,7 +353,7 @@ border-radius: 8px; box-shadow: var(--box-shadow-medium); padding: 6px 0; - background-color: var(--background-color); + background: var(--background-color); position: relative; } } @@ -377,7 +377,7 @@ .side-button-container { position: absolute; display: flex; - background-color: var(--background-color); + background: var(--background-color); padding: 2px; border-radius: 2px; transition: transform 0.3s ease; @@ -398,7 +398,7 @@ } .active { - background-color: var(--accent-color); + background: var(--accent-color); } &:hover { @@ -414,7 +414,7 @@ display: flex; justify-content: center; // align-items: center; - background-color: var(--accent-color); + background: var(--accent-color); border: none; color: var(--background-color); border-radius: 4px; @@ -494,7 +494,7 @@ padding: 12px; box-shadow: 1px -3px 4px 0px rgba(0, 0, 0, 0.11); border-radius: 8px; - background-color: white; + background: white; position: absolute; top: 20px; right: -100%; @@ -530,7 +530,7 @@ left: 1px; width: 10px; height: 10px; - background-color: var(--primary-color); + background: var(--primary-color); border-radius: 50%; } } @@ -584,7 +584,7 @@ } .zone.active { - background-color: #007bff; + background: #007bff; color: white; } @@ -592,7 +592,7 @@ .icon { // width: 25px !important; // height: 25px !important; - // background-color: transparent; + // background: transparent; } .kebab { @@ -604,7 +604,7 @@ z-index: 10; cursor: pointer; @include flex-center; - background-color: transparent !important; + background: transparent !important; } .kebab-options { @@ -612,7 +612,7 @@ top: 18px; right: 5px; transform: translate(0px, 0); - background-color: var(--background-color); + background: var(--background-color); z-index: 10; display: flex; @@ -625,7 +625,7 @@ .icon { width: 25px !important; height: 25px !important; - background-color: transparent; + background: transparent; } .btn { @@ -643,7 +643,7 @@ } &:hover { - background-color: var(--highlight-accent-color); + background: var(--highlight-accent-color); width: 100%; svg { @@ -662,6 +662,7 @@ } .distance-line { + position: absolute; border-style: dashed; border-color: var(--accent-color); @@ -675,7 +676,7 @@ /* Label styles for displaying distance values */ .distance-label { position: absolute; - background-color: var(--accent-color); + background: var(--accent-color); color: white; font-size: 12px; padding: 2px 6px; @@ -769,24 +770,25 @@ .editWidgetOptions { position: absolute; - background-color: var(--background-color); + background: var(--background-color); + backdrop-filter: blur(10px); z-index: 3; display: flex; flex-direction: column; 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 { - background-color: var(--highlight-accent-color); + background: var(--highlight-accent-color); color: var(--accent-color); } @@ -794,8 +796,8 @@ color: #f65648; &:hover { - background-color: #f65648; - color: white; + background: #f657484d; + color: #f65648; } } } diff --git a/app/src/styles/pages/userAuth.scss b/app/src/styles/pages/userAuth.scss index 6e86ac1..9d07dcf 100644 --- a/app/src/styles/pages/userAuth.scss +++ b/app/src/styles/pages/userAuth.scss @@ -9,7 +9,7 @@ padding: 20px; color: var(--text-color); height: 100vh; - background-color: var(--background-color); + background: var(--background-color); position: relative; z-index: 1; .logo-icon { diff --git a/app/src/styles/scene/scene.scss b/app/src/styles/scene/scene.scss index fc3b824..ab8f787 100644 --- a/app/src/styles/scene/scene.scss +++ b/app/src/styles/scene/scene.scss @@ -14,9 +14,8 @@ // style font-size: var(--font-size-large); padding: 2px 8px; - background: var(--primary-color); - color: var(--accent-color); - outline: 1px solid var(--accent-color); + background: var(--background-color-accent); + color: var(--text-color); border-radius: #{$border-radius-medium}; box-shadow: var(--box-shadow-light); } diff --git a/app/src/types/simulationTypes.d.ts b/app/src/types/simulationTypes.d.ts index 3293699..cb71864 100644 --- a/app/src/types/simulationTypes.d.ts +++ b/app/src/types/simulationTypes.d.ts @@ -14,7 +14,7 @@ interface TriggerSchema { triggeredAsset: { triggeredModel: { modelName: string, modelUuid: string }; triggeredPoint: { pointName: string, pointUuid: string }; - triggeredAction: { actionName: string, actionUuid: string }; + triggeredAction: { actionName: string, actionUuid: string } | null; } | null; } @@ -44,8 +44,9 @@ interface VehiclePointSchema { actionType: "travel"; unLoadDuration: number; loadCapacity: number; - pickUpPoint: { x: number; y: number, z: number } | null; - unLoadPoint: { x: number; y: number, z: number } | null; + steeringAngle: number; + 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 +135,7 @@ interface ConveyorStatus extends ConveyorEventSchema { isActive: boolean; idleTime: number; activeTime: number; - + } interface MachineStatus extends MachineEventSchema { diff --git a/app/src/types/world/worldConstants.ts b/app/src/types/world/worldConstants.ts index 924c12c..0374c8a 100644 --- a/app/src/types/world/worldConstants.ts +++ b/app/src/types/world/worldConstants.ts @@ -189,10 +189,10 @@ export const firstPersonControls: Controls = { maxDistance: 0, // Maximum distance from the target maxPolarAngle: Math.PI, // Maximum polar angle leftMouse: 1, // Mouse button for rotation (ROTATE) - forwardSpeed: 0.3, // Speed of forward movement - backwardSpeed: -0.3, // Speed of backward movement - leftSpeed: -0.3, // Speed of left movement - rightSpeed: 0.3, // Speed of right movement + forwardSpeed: 0.1, // Speed of forward movement + backwardSpeed: -0.1, // Speed of backward movement + leftSpeed: -0.1, // Speed of left movement + rightSpeed: 0.1, // Speed of right movement }; export const thirdPersonControls: ThirdPersonControls = { diff --git a/app/src/types/world/worldTypes.d.ts b/app/src/types/world/worldTypes.d.ts index fa6c129..c148038 100644 --- a/app/src/types/world/worldTypes.d.ts +++ b/app/src/types/world/worldTypes.d.ts @@ -189,13 +189,26 @@ export type RefTubeGeometry = React.MutableRefObject; // Type for individual items placed on the floor, with positioning and rotation metadata export type FloorItemType = { - modeluuid: string; - modelname: string; + modelUuid: string; + modelName: string; position: [number, number, number]; rotation: { x: number; y: number; z: number }; modelfileID: string; isLocked: boolean; isVisible: boolean; + eventData?: { + type: string; + point?: { + uuid: string; + position: [number, number, number]; + rotation: [number, number, number]; + } + points?: { + uuid: string; + position: [number, number, number]; + rotation: [number, number, number]; + }[]; + } }; // Array of floor items for managing multiple objects on the floor @@ -225,8 +238,8 @@ export type AssetConfigurations = { [key: string]: AssetConfiguration; }; interface WallItem { type: "Fixed-Move" | "Free-Move" | undefined; model?: THREE.Group; - modeluuid?: string; - modelname?: string; + modelUuid?: string; + modelName?: string; scale?: [number, number, number]; csgscale?: [number, number, number]; csgposition?: [number, number, number]; diff --git a/app/src/utils/indexDB/idbUtils.ts b/app/src/utils/indexDB/idbUtils.ts index c9228d3..24b1448 100644 --- a/app/src/utils/indexDB/idbUtils.ts +++ b/app/src/utils/indexDB/idbUtils.ts @@ -3,43 +3,43 @@ const STORE_NAME = 'models'; const DB_VERSION = 1; export function initializeDB(): Promise { - return new Promise((resolve, reject) => { - const request = indexedDB.open(DB_NAME, DB_VERSION); + return new Promise((resolve, reject) => { + const request = indexedDB.open(DB_NAME, DB_VERSION); - request.onupgradeneeded = () => { - const db = request.result; - if (!db.objectStoreNames.contains(STORE_NAME)) { - db.createObjectStore(STORE_NAME); - } - }; + request.onupgradeneeded = () => { + const db = request.result; + if (!db.objectStoreNames.contains(STORE_NAME)) { + db.createObjectStore(STORE_NAME); + } + }; - request.onsuccess = () => resolve(request.result); - request.onerror = () => reject(request.error); - }); + request.onsuccess = () => resolve(request.result); + request.onerror = () => reject(request.error); + }); } export async function storeGLTF(key: string, file: Blob): Promise { - const db = await initializeDB(); + const db = await initializeDB(); - return new Promise((resolve, reject) => { - const transaction = db.transaction(STORE_NAME, 'readwrite'); - const store = transaction.objectStore(STORE_NAME); - const request = store.put(file, key); + return new Promise((resolve, reject) => { + const transaction = db.transaction(STORE_NAME, 'readwrite'); + const store = transaction.objectStore(STORE_NAME); + const request = store.put(file, key); - request.onsuccess = () => resolve(); - request.onerror = () => reject(request.error); - }); + request.onsuccess = () => resolve(); + request.onerror = () => reject(request.error); + }); } export async function retrieveGLTF(key: string): Promise { - const db = await initializeDB(); + const db = await initializeDB(); - return new Promise((resolve, reject) => { - const transaction = db.transaction(STORE_NAME, 'readonly'); - const store = transaction.objectStore(STORE_NAME); - const request = store.get(key); + return new Promise((resolve, reject) => { + const transaction = db.transaction(STORE_NAME, 'readonly'); + const store = transaction.objectStore(STORE_NAME); + const request = store.get(key); - request.onsuccess = () => resolve(request.result as Blob | undefined); - request.onerror = () => reject(request.error); - }); + request.onsuccess = () => resolve(request.result as Blob | undefined); + request.onerror = () => reject(request.error); + }); } \ No newline at end of file diff --git a/app/src/utils/shortcutkeys/handleShortcutKeys.ts b/app/src/utils/shortcutkeys/handleShortcutKeys.ts index 43ca553..52877b9 100644 --- a/app/src/utils/shortcutkeys/handleShortcutKeys.ts +++ b/app/src/utils/shortcutkeys/handleShortcutKeys.ts @@ -190,7 +190,7 @@ const KeyPressListener: React.FC = () => { return () => { window.removeEventListener("keydown", handleKeyPress); }; - }, [activeModule, toggleUI, toggleView]); // Dependencies to reapply effect if these values change + }, [activeModule, setActiveModule, setActiveSubTool, setActiveTool, setAddAction, setDeleteTool, setIsPlaying, setSelectedWallItem, setToggleThreeD, setToggleUI, setToggleView, setToolMode, toggleUI, toggleView]); // Dependencies to reapply effect if these values change return null; // This component does not render any UI };