Merge pull request 'simulation' (#30) from simulation into main

Reviewed-on: http://185.100.212.76:7776/Dwinzo-Beta/Dwinzo_dev/pulls/30
This commit was merged in pull request #30.
This commit is contained in:
2025-03-31 16:37:16 +00:00
84 changed files with 3813 additions and 1831 deletions

View File

@@ -7,8 +7,11 @@ REACT_APP_SERVER_SOCKET_API_BASE_URL=185.100.212.76:8000
# Base URL for the server REST API, used for HTTP requests to the backend server.
REACT_APP_SERVER_REST_API_BASE_URL=185.100.212.76:5000
REACT_APP_SERVER_REST_API_LOCAL_BASE_URL=192.168.0.102:5000
# Base URL for the server marketplace API.
REACT_APP_SERVER_MARKETPLACE_URL=185.100.212.76:50011
# REACT_APP_SERVER_MARKETPLACE_URL=185.100.212.76:50011
REACT_APP_SERVER_MARKETPLACE_URL=192.168.0.111:3501
# base url for IoT socket server
REACT_APP_IOT_SOCKET_SERVER_URL =185.100.212.76:5010

10
app/package-lock.json generated
View File

@@ -26,6 +26,7 @@
"@types/react-dom": "^18.3.0",
"@use-gesture/react": "^10.3.1",
"chart.js": "^4.4.8",
"chartjs-plugin-annotation": "^3.1.0",
"glob": "^11.0.0",
"gsap": "^3.12.5",
"leva": "^0.10.0",
@@ -8499,6 +8500,15 @@
"pnpm": ">=8"
}
},
"node_modules/chartjs-plugin-annotation": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/chartjs-plugin-annotation/-/chartjs-plugin-annotation-3.1.0.tgz",
"integrity": "sha512-EkAed6/ycXD/7n0ShrlT1T2Hm3acnbFhgkIEJLa0X+M6S16x0zwj1Fv4suv/2bwayCT3jGPdAtI9uLcAMToaQQ==",
"license": "MIT",
"peerDependencies": {
"chart.js": ">=4.0.0"
}
},
"node_modules/check-more-types": {
"version": "2.24.0",
"resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz",

View File

@@ -21,6 +21,7 @@
"@types/react-dom": "^18.3.0",
"@use-gesture/react": "^10.3.1",
"chart.js": "^4.4.8",
"chartjs-plugin-annotation": "^3.1.0",
"glob": "^11.0.0",
"gsap": "^3.12.5",
"leva": "^0.10.0",

View File

@@ -1,20 +1,41 @@
import React, { useState } from "react";
import React, { useEffect, useMemo, useState } from "react";
import Search from "../../ui/inputs/Search";
import vehicle from "../../../assets/image/vehicles.png";
import workStation from "../../../assets/image/workStation.png";
import machines from "../../../assets/image/machines.png";
import feneration from "../../../assets/image/feneration.png";
import worker from "../../../assets/image/worker.png";
import { getCategoryAsset } from "../../../services/factoryBuilder/assest/assets/getCategoryAsset";
import arch from "../../../assets/gltf-glb/arch.glb";
import door from "../../../assets/gltf-glb/door.glb";
import window from "../../../assets/gltf-glb/window.glb";
import { useSelectedItem } from "../../../store/store";
interface AssetProp {
filename: string;
thumbnail?: string;
category: string;
description?: string;
tags?: string;
url?: String;
uploadDate?: number;
isArchieve?: boolean;
animated?: boolean;
price?: number;
CreatedBy?: String;
}
const Assets: React.FC = () => {
const { setSelectedItem } = useSelectedItem();
const [searchValue, setSearchValue] = useState<string>("");
const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
const [filteredAsset, setFilteredAsset] = useState<AssetProp[]>([]);
const handleSearchChange = (value: string) => {
setSearchValue(value);
setSelectedCategory(null); // Reset selected category when search changes
};
const categoryList = [
const categoryList = useMemo(
() => [
{
assetName: "Doors",
assetImage: "",
@@ -33,73 +54,44 @@ const Assets: React.FC = () => {
category: "Feneration",
categoryImage: feneration,
},
{
assetName: "Forklift",
assetImage: "",
category: "Vehicles",
categoryImage: vehicle,
},
{
assetName: "AGV",
assetImage: "",
category: "Vehicles",
categoryImage: vehicle,
},
{
assetName: "Pallet",
assetImage: "",
category: "Workstation",
categoryImage: workStation,
},
{
assetName: "Controller",
assetImage: "",
category: "Workstation",
categoryImage: workStation,
},
{
assetName: "Conveyor",
assetImage: "",
category: "Workstation",
categoryImage: workStation,
},
{
assetName: "VMC",
assetImage: "",
category: "Machines",
categoryImage: machines,
},
{
assetName: "CMC",
assetImage: "",
category: "Machines",
categoryImage: machines,
},
{
assetName: "Male Worker",
assetImage: "",
category: "Workers",
categoryImage: worker,
},
{
assetName: "Female Worker",
assetImage: "",
category: "Workers",
categoryImage: worker,
},
];
// Get unique categories
const uniqueCategories = Array.from(
new Set(categoryList.map((asset) => asset.category))
{ category: "Vehicles", categoryImage: vehicle },
{ category: "Workstation", categoryImage: workStation },
{ category: "Machines", categoryImage: machines },
{ category: "Workers", categoryImage: worker },
],
[]
);
// Filter assets based on the selected category
const filteredAssets =
selectedCategory !== null
? categoryList.filter((asset) => asset.category === selectedCategory)
: [];
const fetchCategoryAssets = async (asset: any) => {
setSelectedCategory(asset);
if (asset === "Feneration") {
const localAssets: AssetProp[] = [
{
filename: "arch",
category: "Feneration",
url: arch,
},
{
filename: "door",
category: "Feneration",
url: door,
},
{
filename: "window",
category: "Feneration",
url: window,
},
];
setFilteredAsset(localAssets);
} else {
try {
const res = await getCategoryAsset(asset);
setFilteredAsset(res || []); // Ensure it's always an array
} catch (error) { }
}
};
useEffect(() => { }, [filteredAsset]);
return (
<div className="assets-container">
<Search onChange={handleSearchChange} />
@@ -112,20 +104,35 @@ const Assets: React.FC = () => {
{/* Back Button */}
<div
className="back-button"
onClick={() => setSelectedCategory(null)}
onClick={() => {
setSelectedCategory(null);
setFilteredAsset([]);
}}
>
Back
</div>
<h2>{selectedCategory}</h2>
<div className="assets-container">
{filteredAssets.map((asset, index) => (
{filteredAsset &&
filteredAsset?.map((asset: any, index: number) => (
<div key={index} className="assets">
{asset?.thumbnail && (
<img
src={asset.assetImage}
alt={asset.assetName}
src={asset?.thumbnail}
alt={asset.filename}
className="asset-image"
onPointerDown={() => setSelectedItem({ name: asset.filename, id: asset.modelfileID })}
/>
<div className="asset-name">{asset.assetName}</div>
)}
<div className="asset-name">
{asset.filename
.split("_")
.map(
(word: any) =>
word.charAt(0).toUpperCase() + word.slice(1)
)
.join(" ")}
</div>
</div>
))}
</div>
@@ -134,7 +141,9 @@ const Assets: React.FC = () => {
<div className="assets-wrapper">
<h2>Categories</h2>
<div className="categories-container">
{uniqueCategories.map((category, index) => {
{Array.from(
new Set(categoryList.map((asset) => asset.category))
).map((category, index) => {
const categoryInfo = categoryList.find(
(asset) => asset.category === category
);
@@ -142,7 +151,7 @@ const Assets: React.FC = () => {
<div
key={index}
className="category"
onClick={() => setSelectedCategory(category)}
onClick={() => fetchCategoryAssets(category)}
>
<img
src={categoryInfo?.categoryImage || ""}
@@ -161,4 +170,3 @@ const Assets: React.FC = () => {
};
export default Assets;

View File

@@ -1,25 +1,76 @@
import { useEffect } from "react";
import { useDroppedObjectsStore } from "../../../../store/useDroppedObjectsStore";
import useTemplateStore from "../../../../store/useTemplateStore";
import { useSelectedZoneStore } from "../../../../store/useZoneStore";
import { getTemplateData } from "../../../../services/realTimeVisulization/zoneData/getTemplate";
import { deleteTemplateApi } from "../../../../services/realTimeVisulization/zoneData/deleteTemplate";
import { loadTempleteApi } from "../../../../services/realTimeVisulization/zoneData/loadTemplate";
const Templates = () => {
const { templates, removeTemplate } = useTemplateStore();
const { setSelectedZone } = useSelectedZoneStore();
const { setTemplates } = useTemplateStore();
const { setSelectedZone, selectedZone } = useSelectedZoneStore();
const handleDeleteTemplate = (id: string) => {
useEffect(() => {
async function templateData() {
try {
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0];
let response = await getTemplateData(organization);
setTemplates(response);
} catch (error) {
console.error("Error fetching template data:", error);
}
}
templateData();
}, []);
const handleDeleteTemplate = async (id: string) => {
try {
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0];
let response = await deleteTemplateApi(id, organization);
if (response.message === "Template deleted successfully") {
removeTemplate(id);
}
} catch (error) {
console.error("Error deleting template:", error);
}
};
const handleLoadTemplate = (template: any) => {
setSelectedZone((prev) => ({
...prev,
const handleLoadTemplate = async (template: any) => {
try {
if (selectedZone.zoneName === "") return;
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0];
let response = await loadTempleteApi(template.id, selectedZone.zoneId, organization);
if (response.message === "Template placed in Zone") {
setSelectedZone({
panelOrder: template.panelOrder,
activeSides: Array.from(
new Set([...prev.activeSides, ...template.panelOrder])
),
activeSides: Array.from(new Set(template.panelOrder)), // No merging with previous `activeSides`
widgets: template.widgets,
}));
});
useDroppedObjectsStore.getState().setZone(selectedZone.zoneName, selectedZone.zoneId);
if (Array.isArray(template.floatingWidget)) {
template.floatingWidget.forEach((val: any) => {
useDroppedObjectsStore.getState().addObject(selectedZone.zoneName, val);
});
}
}
} catch (error) {
console.error("Error loading template:", error);
}
};
return (
<div
className="template-list"
@@ -41,7 +92,7 @@ const Templates = () => {
transition: "box-shadow 0.3s ease",
}}
>
{template.snapshot && (
{template?.snapshot && (
<div style={{ position: "relative", paddingBottom: "56.25%" }}>
{" "}
{/* 16:9 aspect ratio */}
@@ -122,3 +173,4 @@ const Templates = () => {
};
export default Templates;

View File

@@ -27,7 +27,14 @@ const RenderAnalysisInputs: React.FC<InputRendererProps> = ({
);
}
if (preset.type === "range") {
return <InputRange key={index} label={preset.inputs.label} />;
return (
<InputRange
key={index}
label={preset.inputs.label}
min={0}
max={0}
/>
);
}
return null;
})}

View File

@@ -1,23 +1,32 @@
import React from "react";
import { EyeDroperIcon } from "../../../icons/ExportCommonIcons";
interface PositionInputProps {
label?: string; // Optional label for the input
onChange: (value: string) => void; // Callback for value change
placeholder?: string; // Optional placeholder
type?: string; // Input type (e.g., text, number, email)
value1?: number;
value2?: number;
disabled?: boolean; // Optional disabled property
isEyedrop?: boolean; // Optional eyedrop property
handleEyeDropClick?: () => void; // Optional function for eye drop click
}
const PositionInput: React.FC<PositionInputProps> = ({
onChange,
label = "Position", // Default label
placeholder = "Enter value", // Default placeholder
type = "number", // Default type
value1 = "number",
value2 = "number",
disabled = false, // Default disabled value
isEyedrop = false, // Default isEyedrop value
handleEyeDropClick = () => { }, // Default function for eye drop click
}) => {
return (
<div className="custom-input-container">
<div className="header">Position</div>
<div className="header">{label}</div>
<div className="inputs-container">
<div className="input-container">
<div className="custom-input-label">X : </div>
@@ -26,7 +35,8 @@ const PositionInput: React.FC<PositionInputProps> = ({
type={type}
onChange={(e) => onChange(e.target.value)}
placeholder={placeholder}
value={value2}
value={value1}
disabled={disabled} // Apply disabled prop
/>
</div>
<div className="input-container">
@@ -36,10 +46,16 @@ const PositionInput: React.FC<PositionInputProps> = ({
type={type}
onChange={(e) => onChange(e.target.value)}
placeholder={placeholder}
value={value1}
value={value2}
disabled={disabled} // Apply disabled prop
/>
</div>
</div>
{isEyedrop && (
<div className="eye-picker-button" onClick={handleEyeDropClick}>
<EyeDroperIcon isActive={false} />
</div>
)}
</div>
);
};

View File

@@ -1,13 +1,16 @@
import React, { useRef, useMemo } from "react";
import { InfoIcon } from "../../../icons/ExportCommonIcons";
import InputWithDropDown from "../../../ui/inputs/InputWithDropDown";
import { useSelectedActionSphere, useSimulationPaths } from "../../../../store/store";
import { useEditingPoint, useEyeDropMode, usePreviewPosition, useSelectedActionSphere, useSimulationPaths } from "../../../../store/store";
import * as Types from '../../../../types/world/worldTypes';
import LabledDropdown from "../../../ui/inputs/LabledDropdown";
import PositionInput from "../customInput/PositionInputs";
const VehicleMechanics: React.FC = () => {
const { selectedActionSphere } = useSelectedActionSphere();
const { simulationPaths, setSimulationPaths } = useSimulationPaths();
const { eyeDropMode, setEyeDropMode } = useEyeDropMode();
const { editingPoint, setEditingPoint } = useEditingPoint();
const { previewPosition, setPreviewPosition } = usePreviewPosition();
const propertiesContainerRef = useRef<HTMLDivElement>(null);
@@ -59,12 +62,10 @@ const VehicleMechanics: React.FC = () => {
setSimulationPaths(updatedPaths);
}, [selectedActionSphere?.point?.uuid, simulationPaths, setSimulationPaths]);
const handleStartPointChange = React.useCallback((uuid: string) => {
handleActionUpdate({ start: uuid });
const handleStartPointChange = React.useCallback((position: { x: number, y: number }) => {
}, [handleActionUpdate]);
const handleEndPointChange = React.useCallback((uuid: string) => {
handleActionUpdate({ end: uuid });
const handleEndPointChange = React.useCallback((position: { x: number, y: number }) => {
}, [handleActionUpdate]);
const handleHitCountChange = React.useCallback((hitCount: number) => {
@@ -94,6 +95,16 @@ const VehicleMechanics: React.FC = () => {
setSimulationPaths(updatedPaths);
}, [selectedActionSphere?.point?.uuid, simulationPaths, setSimulationPaths]);
const handleStartEyeDropClick = () => {
setEditingPoint('start');
setEyeDropMode(true);
};
const handleEndEyeDropClick = () => {
setEditingPoint('end');
setEyeDropMode(true);
};
return (
<div className="machine-mechanics-container" key={selectedPoint?.uuid}>
<div className="machine-mechanics-header">
@@ -106,20 +117,49 @@ const VehicleMechanics: React.FC = () => {
{selectedPoint && (
<>
<LabledDropdown
key={`start-${selectedPoint.uuid}`}
<PositionInput
label="Start Point"
defaultOption={selectedPoint.actions.start || "Select start point"}
options={connectedPointUuids}
onSelect={handleStartPointChange}
onChange={() => { }}
disabled={true}
value1={
editingPoint === 'start' && previewPosition
? parseFloat(previewPosition.x.toFixed(4))
: selectedPoint.actions.start && 'x' in selectedPoint.actions.start
? parseFloat(selectedPoint.actions.start.x.toFixed(4))
: 0
}
value2={
editingPoint === 'start' && previewPosition
? parseFloat(previewPosition.y.toFixed(4))
: selectedPoint.actions.start && 'y' in selectedPoint.actions.start
? parseFloat(selectedPoint.actions.start.y.toFixed(4))
: 0
}
isEyedrop={true}
handleEyeDropClick={handleStartEyeDropClick}
/>
<LabledDropdown
key={`end-${selectedPoint.uuid}`}
<PositionInput
label="End Point"
defaultOption={selectedPoint.actions.end || "Select end point"}
options={connectedPointUuids}
onSelect={handleEndPointChange}
onChange={() => { }}
disabled={true}
value1={
editingPoint === 'end' && previewPosition
? parseFloat(previewPosition.x.toFixed(4))
: selectedPoint.actions.end && 'x' in selectedPoint.actions.end
? parseFloat(selectedPoint.actions.end.x.toFixed(4))
: 0
}
value2={
editingPoint === 'end' && previewPosition
? parseFloat(previewPosition.y.toFixed(4))
: selectedPoint.actions.end && 'y' in selectedPoint.actions.end
? parseFloat(selectedPoint.actions.end.y.toFixed(4))
: 0
}
isEyedrop={true}
handleEyeDropClick={handleEndEyeDropClick}
/>
<InputWithDropDown

View File

@@ -17,12 +17,6 @@ const AssetProperties: React.FC = () => {
const [userData, setUserData] = useState<UserData[]>([]); // State to track user data
const [nextId, setNextId] = useState(1); // Unique ID for new entries
const { selectedFloorItem } = useselectedFloorItem();
let xValue = selectedFloorItem.position.x;
let zValue = selectedFloorItem.position.z;
let rotationRad = selectedFloorItem.rotation.y;
let rotationDeg = THREE.MathUtils.radToDeg(rotationRad);
// useEffect(() => {}, [selectedFloorItem]);
// Function to handle adding new user data
const handleAddUserData = () => {
const newUserData: UserData = {
@@ -55,14 +49,17 @@ const AssetProperties: React.FC = () => {
{/* Name */}
<div className="header">{selectedFloorItem.userData.name}</div>
<div className="split"></div>
<PositionInput
onChange={() => {}}
value1={xValue.toFixed(5)}
value2={zValue.toFixed(5)}
value1={selectedFloorItem.position.x.toFixed(5)}
value2={selectedFloorItem.position.z.toFixed(5)}
/>
<RotationInput
onChange={() => {}}
value={parseFloat(
THREE.MathUtils.radToDeg(selectedFloorItem.rotation.y).toFixed(5)
)}
/>
<RotationInput onChange={() => {}} value={rotationDeg} />
<div className="split"></div>

View File

@@ -16,7 +16,7 @@ import {
useWallVisibility,
} from "../../../../store/store";
import { setEnvironment } from "../../../../services/factoryBuilder/environment/setEnvironment";
import * as CONSTANTS from "../../../../types/world/worldConstants";
const GlobalProperties: React.FC = () => {
const { toggleView, setToggleView } = useToggleView();
const { selectedWallItem, setSelectedWallItem } = useSelectedWallItem();
@@ -29,18 +29,20 @@ const GlobalProperties: React.FC = () => {
const { renderDistance, setRenderDistance } = useRenderDistance();
const { socket } = useSocketStore();
const [limitDistance, setLimitDistance] = useState(false);
const [distance, setDistance] = useState<number>(5);
const [distance, setDistance] = useState<number>(30);
const [limitGridDistance, setLimitGridDistance] = useState(false);
const [gridDistance, setGridDistance] = useState<number>(5);
function optimizeScene() {
setLimitDistance(true);
setDistance(5);
setDistance(30);
}
function updateDistance(value: number) {
console.log("value: ", value);
setDistance(value);
setRenderDistance(value);
}
function updateGridDistance(value: number) {
@@ -134,6 +136,15 @@ const GlobalProperties: React.FC = () => {
}
};
// function changeRenderDistance(e: any) {
// if (parseInt(e.target.value) < 20) {
// setRenderDistance(20);
// } else if (parseInt(e.target.value) > 75) {
// setRenderDistance(75);
// } else {
// setRenderDistance(parseInt(e.target.value));
// }
// }
return (
<div className="global-properties-container">
<div className="header">Environment</div>
@@ -169,7 +180,7 @@ const GlobalProperties: React.FC = () => {
/>
<div className="split"></div>
{/* //visibleEdgeColor={CONSTANTS.outlineConfig.assetDeleteColor} */}
<InputToggle
inputKey="4"
label="Limit Render Distance"
@@ -183,6 +194,8 @@ const GlobalProperties: React.FC = () => {
disabled={!limitDistance}
value={distance}
key={"5"}
min={CONSTANTS.distanceConfig.minDistance}
max={CONSTANTS.distanceConfig.maxDistance}
onChange={(value: number) => updateDistance(value)}
/>

View File

@@ -30,7 +30,6 @@ const ZoneProperties: React.FC = () => {
let response = await zoneCameraUpdate(zonesdata, organization);
console.log('response: ', response);
setEdit(false);
} catch (error) {
console.error("Error in handleSetView:", error);

View File

@@ -0,0 +1,177 @@
import React, { useEffect, useState } from "react";
import MultiLevelDropdown from "../../../../ui/inputs/MultiLevelDropDown";
import { AddIcon } from "../../../../icons/ExportCommonIcons";
import RegularDropDown from "../../../../ui/inputs/RegularDropDown";
import useChartStore from "../../../../../store/useChartStore";
import { useSelectedZoneStore } from "../../../../../store/useZoneStore";
import { useWidgetStore } from "../../../../../store/useWidgetStore";
import axios from "axios";
import RenameInput from "../../../../ui/inputs/RenameInput";
type Props = {};
const FlotingWidgetInput = (props: Props) => {
const [widgetName, setWidgetName] = useState('Widget');
const { setMeasurements, updateDuration, updateName } = useChartStore();
const [duration, setDuration] = useState('1h')
const [dropDowndata, setDropDownData] = useState({});
const [selections, setSelections] = useState<Record<string, { name: string; fields: string }>>({});
const { selectedZone } = useSelectedZoneStore();
const { selectedChartId } = useWidgetStore();
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]
useEffect(() => {
const fetchZoneData = async () => {
try {
const response = await axios.get(`http://${iotApiUrl}/getinput`);
if (response.status === 200) {
// console.log("dropdown data:", response.data);
setDropDownData(response.data);
} else {
console.log("Unexpected response:", response);
}
} catch (error) {
console.error("There was an error!", error);
}
};
fetchZoneData();
}, []);
useEffect(() => {
const fetchSavedInputes = async () => {
if (selectedChartId.id !== "") {
try {
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${selectedChartId.id}/${organization}`);
if (response.status === 200) {
setSelections(response.data.Data.measurements)
setDuration(response.data.Data.duration)
setWidgetName(response.data.widgetName)
} else {
console.log("Unexpected response:", response);
}
} catch (error) {
console.error("There was an error!", error);
}
}
}
fetchSavedInputes();
}, [selectedChartId.id]);
// Sync Zustand state when component mounts
useEffect(() => {
setMeasurements(selections);
updateDuration(duration);
updateName(widgetName);
}, [selections, duration, widgetName]);
const sendInputes = async (inputMeasurement: any, inputDuration: any, inputName: any) => {
try {
const response = await axios.post(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/floatwidget/save`, {
organization: organization,
zoneId: selectedZone.zoneId,
widget: {
id: selectedChartId.id,
panel: selectedChartId.panel,
widgetName: inputName,
Data: {
measurements: inputMeasurement,
duration: inputDuration
}
}
} as any);
if (response.status === 200) {
return true
} else {
console.log("Unexpected response:", response);
return false
}
} catch (error) {
console.error("There was an error!", error);
return false
}
}
const handleSelect = async (inputKey: string, selectedData: { name: string; fields: string } | null) => {
// async() => {
const newSelections = { ...selections };
if (selectedData === null) {
delete newSelections[inputKey];
} else {
newSelections[inputKey] = selectedData;
}
// setMeasurements(newSelections); // Update Zustand store
console.log(newSelections);
if (await sendInputes(newSelections, duration, widgetName)) {
setSelections(newSelections);
}
// sendInputes(newSelections, duration); // Send data to server
// return newSelections;
// };
};
const handleSelectDuration = async (option: string) => {
if (await sendInputes(selections, option, widgetName)) {
setDuration(option);
}
// setDuration(option);
};
const handleNameChange = async (name:any) => {
console.log('name change requested',name);
if (await sendInputes(selections, duration, name)) {
setWidgetName(name);
}
}
return (
<>
<div className="inputs-wrapper">
<div className="datas">
<div className="datas__label">Title</div>
<RenameInput value={selectedChartId?.title || "untited"} onRename={handleNameChange}/>
</div>
{[...Array(6)].map((_, index) => {
const inputKey = `input${index + 1}`;
return (
<div key={index} className="datas">
<div className="datas__label">Input {index + 1}</div>
<div className="datas__class">
<MultiLevelDropdown
data={dropDowndata}
onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
onUnselect={() => handleSelect(inputKey, null)}
selectedValue={selections[inputKey]} // Load from Zustand
/>
<div className="icon">
<AddIcon />
</div>
</div>
</div>
);
})}
</div>
<div>
<div className="datas">
<div className="datas__label">Duration</div>
<div className="datas__class">
<RegularDropDown
header={duration}
options={["1h", "2h", "12h"]}
onSelect={handleSelectDuration}
search={false}
/>
</div>
</div>
</div>
</>
);
};
export default FlotingWidgetInput;

View File

@@ -125,11 +125,13 @@ import useChartStore from "../../../../../store/useChartStore";
import { useSelectedZoneStore } from "../../../../../store/useZoneStore";
import { useWidgetStore } from "../../../../../store/useWidgetStore";
import axios from "axios";
import RenameInput from "../../../../ui/inputs/RenameInput";
type Props = {};
const LineGrapInput = (props: Props) => {
const { setMeasurements, updateDuration } = useChartStore();
const [widgetName, setWidgetName] = useState('Widget');
const { setMeasurements, updateDuration, updateName } = useChartStore();
const [duration, setDuration] = useState('1h')
const [dropDowndata, setDropDownData] = useState({});
const [selections, setSelections] = useState<Record<string, { name: string; fields: string }>>({});
@@ -157,13 +159,14 @@ const LineGrapInput = (props: Props) => {
}, []);
useEffect(() => {
const fetchSavedInputes = async() => {
const fetchSavedInputes = async () => {
if (selectedChartId.id !== "") {
try {
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${selectedChartId.id}/${organization}`);
if (response.status === 200) {
setSelections(response.data.Data.measurements)
setDuration(response.data.Data.duration)
setWidgetName(response.data.widgetName)
} else {
console.log("Unexpected response:", response);
}
@@ -181,17 +184,19 @@ const LineGrapInput = (props: Props) => {
useEffect(() => {
setMeasurements(selections);
updateDuration(duration);
}, [selections, duration]);
updateName(widgetName);
}, [selections, duration, widgetName]);
const sendInputes = async(inputMeasurement: any, inputDuration: any) => {
const sendInputes = async (inputMeasurement: any, inputDuration: any, inputName: any) => {
try {
const response = await axios.post(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget/save`,{
const response = await axios.post(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget/save`, {
organization: organization,
zoneId: selectedZone.zoneId,
widget:{
widget: {
id: selectedChartId.id,
panel: selectedChartId.panel,
widgetName: inputName,
Data: {
measurements: inputMeasurement,
duration: inputDuration
@@ -210,7 +215,7 @@ const LineGrapInput = (props: Props) => {
}
}
const handleSelect = async(inputKey: string, selectedData: { name: string; fields: string } | null) => {
const handleSelect = async (inputKey: string, selectedData: { name: string; fields: string } | null) => {
// async() => {
const newSelections = { ...selections };
@@ -221,7 +226,7 @@ const LineGrapInput = (props: Props) => {
}
// setMeasurements(newSelections); // Update Zustand store
console.log(newSelections);
if ( await sendInputes(newSelections, duration)) {
if (await sendInputes(newSelections, duration, widgetName)) {
setSelections(newSelections);
}
// sendInputes(newSelections, duration); // Send data to server
@@ -229,16 +234,28 @@ const LineGrapInput = (props: Props) => {
// };
};
const handleSelectDuration = async(option: string) => {
if ( await sendInputes(selections, option)) {
const handleSelectDuration = async (option: string) => {
if (await sendInputes(selections, option, widgetName)) {
setDuration(option);
}
// setDuration(option);
};
const handleNameChange = async (name:any) => {
console.log('name change requested',name);
if (await sendInputes(selections, duration, name)) {
setWidgetName(name);
}
}
return (
<>
<div className="inputs-wrapper">
<div className="datas">
<div className="datas__label">Title</div>
<RenameInput value={selectedChartId?.title || "untited"} onRename={handleNameChange}/>
</div>
{[...Array(6)].map((_, index) => {
const inputKey = `input${index + 1}`;
return (

View File

@@ -3,6 +3,7 @@ import { useWidgetStore } from "../../../../../store/useWidgetStore";
import { AddIcon, RemoveIcon } from "../../../../icons/ExportCommonIcons";
import MultiLevelDropDown from "../../../../ui/inputs/MultiLevelDropDown";
import LineGrapInput from "../IotInputCards/LineGrapInput";
import RenameInput from "../../../../ui/inputs/RenameInput";
// Define the data structure for demonstration purposes
const DATA_STRUCTURE = {
@@ -117,15 +118,24 @@ const Data = () => {
};
});
};
console.log("selectedChartId", selectedChartId);
return (
<div className="dataSideBar">
{selectedChartId?.title && (
{/* {selectedChartId?.title && (
<div className="sideBarHeader">{selectedChartId?.title}</div>
)}
)} */}
{/* <RenameInput value={selectedChartId?.title || "untited"} /> */}
{/* Render groups dynamically */}
{
chartDataGroups[selectedChartId?.id] && <LineGrapInput />
chartDataGroups[selectedChartId?.id] &&
<>
<div className="sideBarHeader">2D Widget Input</div>
<LineGrapInput />
</>
}
{/* Info Box */}

View File

@@ -32,6 +32,7 @@ import {
useTransformMode,
} from "../../store/store";
import useToggleStore from "../../store/useUIToggleStore";
import { use3DWidget, useFloatingWidget } from "../../store/useDroppedObjectsStore";
const Tools: React.FC = () => {
const { templates } = useTemplateStore();
@@ -46,6 +47,8 @@ const Tools: React.FC = () => {
const { isPlaying, setIsPlaying } = usePlayButtonStore();
const { addTemplate } = useTemplateStore();
const { selectedZone } = useSelectedZoneStore();
const { floatingWidget } = useFloatingWidget()
const { widgets3D } = use3DWidget()
// wall options
const { toggleView, setToggleView } = useToggleView();
@@ -68,7 +71,7 @@ const Tools: React.FC = () => {
: true
);
}, []);
useEffect(() => { }, [activeModule]);
useEffect(() => {
setActiveTool(activeSubTool);
setActiveSubTool(activeSubTool);
@@ -210,8 +213,7 @@ const Tools: React.FC = () => {
<div className="activeDropicon">
{activeSubTool == "cursor" && (
<div
className={`tool-button ${
activeTool === "cursor" ? "active" : ""
className={`tool-button ${activeTool === "cursor" ? "active" : ""
}`}
onClick={() => {
setActiveTool("cursor");
@@ -222,8 +224,7 @@ const Tools: React.FC = () => {
)}
{activeSubTool == "free-hand" && (
<div
className={`tool-button ${
activeTool === "free-hand" ? "active" : ""
className={`tool-button ${activeTool === "free-hand" ? "active" : ""
}`}
onClick={() => {
setActiveTool("free-hand");
@@ -234,8 +235,7 @@ const Tools: React.FC = () => {
)}
{activeSubTool == "delete" && (
<div
className={`tool-button ${
activeTool === "delete" ? "active" : ""
className={`tool-button ${activeTool === "delete" ? "active" : ""
}`}
onClick={() => {
setActiveTool("delete");
@@ -308,8 +308,7 @@ const Tools: React.FC = () => {
<div className="split"></div>
<div className="draw-tools">
<div
className={`tool-button ${
activeTool === "draw-wall" ? "active" : ""
className={`tool-button ${activeTool === "draw-wall" ? "active" : ""
}`}
onClick={() => {
setActiveTool("draw-wall");
@@ -319,8 +318,7 @@ const Tools: React.FC = () => {
<WallIcon isActive={activeTool === "draw-wall"} />
</div>
<div
className={`tool-button ${
activeTool === "draw-zone" ? "active" : ""
className={`tool-button ${activeTool === "draw-zone" ? "active" : ""
}`}
onClick={() => {
setActiveTool("draw-zone");
@@ -330,8 +328,7 @@ const Tools: React.FC = () => {
<ZoneIcon isActive={activeTool === "draw-zone"} />
</div>
<div
className={`tool-button ${
activeTool === "draw-aisle" ? "active" : ""
className={`tool-button ${activeTool === "draw-aisle" ? "active" : ""
}`}
onClick={() => {
setActiveTool("draw-aisle");
@@ -341,8 +338,7 @@ const Tools: React.FC = () => {
<AsileIcon isActive={activeTool === "draw-aisle"} />
</div>
<div
className={`tool-button ${
activeTool === "draw-floor" ? "active" : ""
className={`tool-button ${activeTool === "draw-floor" ? "active" : ""
}`}
onClick={() => {
setActiveTool("draw-floor");
@@ -359,8 +355,7 @@ const Tools: React.FC = () => {
<div className="split"></div>
<div className="draw-tools">
<div
className={`tool-button ${
activeTool === "measure" ? "active" : ""
className={`tool-button ${activeTool === "measure" ? "active" : ""
}`}
onClick={() => {
setActiveTool("measure");
@@ -377,8 +372,7 @@ const Tools: React.FC = () => {
<div className="split"></div>
<div className="draw-tools">
<div
className={`tool-button ${
activeTool === "pen" ? "active" : ""
className={`tool-button ${activeTool === "pen" ? "active" : ""
}`}
onClick={() => {
setActiveTool("pen");
@@ -395,13 +389,17 @@ const Tools: React.FC = () => {
<div className="draw-tools">
<div
className={`tool-button`}
onClick={() =>
onClick={() => {
handleSaveTemplate({
addTemplate,
floatingWidget,
widgets3D,
selectedZone,
templates,
})
}
}
>
<SaveTemplateIcon isActive={false} />
</div>
@@ -411,8 +409,7 @@ const Tools: React.FC = () => {
<div className="split"></div>
<div className="general-options">
<div
className={`tool-button ${
activeTool === "comment" ? "active" : ""
className={`tool-button ${activeTool === "comment" ? "active" : ""
}`}
onClick={() => {
setActiveTool("comment");
@@ -422,8 +419,7 @@ const Tools: React.FC = () => {
</div>
{toggleThreeD && (
<div
className={`tool-button ${
activeTool === "play" ? "active" : ""
className={`tool-button ${activeTool === "play" ? "active" : ""
}`}
onClick={() => {
setIsPlaying(!isPlaying);
@@ -437,19 +433,14 @@ const Tools: React.FC = () => {
<>
<div className="split"></div>
<div
className={`toggle-threed-button${
toggleThreeD ? " toggled" : ""
className={`toggle-threed-button${toggleThreeD ? " toggled" : ""
}`}
onClick={toggleSwitch}
>
<div
className={`toggle-option${!toggleThreeD ? " active" : ""}`}
>
<div className={`toggle-option${!toggleThreeD ? " active" : ""}`}>
2d
</div>
<div
className={`toggle-option${toggleThreeD ? " active" : ""}`}
>
<div className={`toggle-option${toggleThreeD ? " active" : ""}`}>
3d
</div>
</div>

View File

@@ -119,7 +119,6 @@ const AddButtons: React.FC<ButtonsProps> = ({
};
// Delete the selectedZone state
setSelectedZone(updatedZone);
} else {
const updatePanelData = async () => {

View File

@@ -2,9 +2,10 @@ import React, { useEffect, useRef, useState, useCallback } from "react";
import { Widget } from "../../../store/useWidgetStore";
import { MoveArrowLeft, MoveArrowRight } from "../../icons/SimulationIcons";
import { InfoIcon } from "../../icons/ExportCommonIcons";
import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore";
import { useDroppedObjectsStore, useFloatingWidget } from "../../../store/useDroppedObjectsStore";
import { getSelect2dZoneData } from "../../../services/realTimeVisulization/zoneData/getSelect2dZoneData";
import { getFloatingZoneData } from "../../../services/realTimeVisulization/zoneData/getFloatingData";
import { get3dWidgetZoneData } from "../../../services/realTimeVisulization/zoneData/get3dWidgetData";
// Define the type for `Side`
type Side = "top" | "bottom" | "left" | "right";
@@ -72,6 +73,7 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
// State to track overflow visibility
const [showLeftArrow, setShowLeftArrow] = useState(false);
const [showRightArrow, setShowRightArrow] = useState(false);
const { floatingWidget, setFloatingWidget } = useFloatingWidget()
// Function to calculate overflow state
const updateOverflowState = useCallback(() => {
@@ -150,14 +152,16 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
async function handleSelect2dZoneData(zoneId: string, zoneName: string) {
try {
if (selectedZone?.zoneId === zoneId) {
console.log("Zone is already selected:", zoneName);
return;
}
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0];
// Fetch data from backend
let response = await getSelect2dZoneData(zoneId, organization);
console.log('response: ', response);
let res = await getFloatingZoneData(zoneId, organization);
setFloatingWidget(res)
// Set the selected zone in the store
useDroppedObjectsStore.getState().setZone(zoneName, zoneId);
if (Array.isArray(res)) {
@@ -177,7 +181,7 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
zoneViewPortPosition: response.viewPortposition || {},
});
} catch (error) {
console.log('error: ', error);
}
}
@@ -186,8 +190,7 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
return (
<div
ref={containerRef}
className={`zone-wrapper ${
selectedZone?.activeSides?.includes("bottom") && "bottom"
className={`zone-wrapper ${selectedZone?.activeSides?.includes("bottom") && "bottom"
}`}
>
{/* Left Arrow */}

View File

@@ -0,0 +1,93 @@
import React from "react";
interface DistanceLinesProps {
obj: {
position: {
top?: number | "auto";
left?: number | "auto";
right?: number | "auto";
bottom?: number | "auto";
};
};
activeEdges: {
vertical: "top" | "bottom";
horizontal: "left" | "right";
} | null;
}
const DistanceLines: React.FC<DistanceLinesProps> = ({ obj, activeEdges }) => {
if (!activeEdges) return null;
return (
<>
{activeEdges.vertical === "top" && typeof obj.position.top === "number" && (
<div
className="distance-line top"
style={{
top: 0,
left:
activeEdges.horizontal === "left"
? `${(obj.position.left as number) + 125}px`
: `calc(100% - ${(obj.position.right as number) + 125}px)`,
height: `${obj.position.top}px`,
}}
>
<span className="distance-label">{obj.position.top.toFixed()}px</span>
</div>
)}
{activeEdges.vertical === "bottom" &&
typeof obj.position.bottom === "number" && (
<div
className="distance-line bottom"
style={{
bottom: 0,
left:
activeEdges.horizontal === "left"
? `${(obj.position.left as number) + 125}px`
: `calc(100% - ${(obj.position.right as number) + 125}px)`,
height: `${obj.position.bottom}px`,
}}
>
<span className="distance-label">{obj.position.bottom.toFixed()}px</span>
</div>
)}
{activeEdges.horizontal === "left" &&
typeof obj.position.left === "number" && (
<div
className="distance-line left"
style={{
left: 0,
top:
activeEdges.vertical === "top"
? `${(obj.position.top as number) + 41.5}px`
: `calc(100% - ${(obj.position.bottom as number) + 41.5}px)`,
width: `${obj.position.left}px`,
}}
>
<span className="distance-label">{obj.position.left.toFixed()}px</span>
</div>
)}
{activeEdges.horizontal === "right" &&
typeof obj.position.right === "number" && (
<div
className="distance-line right"
style={{
right: 0,
top:
activeEdges.vertical === "top"
? `${(obj.position.top as number) + 41.5}px`
: `calc(100% - ${(obj.position.bottom as number) + 41.5}px)`,
width: `${obj.position.right}px`,
}}
>
<span className="distance-label">{obj.position.right.toFixed()}px</span>
</div>
)}
</>
);
};
export default DistanceLines;

View File

@@ -0,0 +1,93 @@
import React from "react";
interface DistanceLinesProps {
obj: {
position: {
top?: number | "auto";
left?: number | "auto";
right?: number | "auto";
bottom?: number | "auto";
};
};
activeEdges: {
vertical: "top" | "bottom";
horizontal: "left" | "right";
} | null;
}
const DistanceLines: React.FC<DistanceLinesProps> = ({ obj, activeEdges }) => {
if (!activeEdges) return null;
return (
<>
{activeEdges.vertical === "top" && typeof obj.position.top === "number" && (
<div
className="distance-line top"
style={{
top: 0,
left:
activeEdges.horizontal === "left"
? `${(obj.position.left as number) + 125}px`
: `calc(100% - ${(obj.position.right as number) + 125}px)`,
height: `${obj.position.top}px`,
}}
>
<span className="distance-label">{obj.position.top}px</span>
</div>
)}
{activeEdges.vertical === "bottom" &&
typeof obj.position.bottom === "number" && (
<div
className="distance-line bottom"
style={{
bottom: 0,
left:
activeEdges.horizontal === "left"
? `${(obj.position.left as number) + 125}px`
: `calc(100% - ${(obj.position.right as number) + 125}px)`,
height: `${obj.position.bottom}px`,
}}
>
<span className="distance-label">{obj.position.bottom}px</span>
</div>
)}
{activeEdges.horizontal === "left" &&
typeof obj.position.left === "number" && (
<div
className="distance-line left"
style={{
left: 0,
top:
activeEdges.vertical === "top"
? `${(obj.position.top as number) + 41.5}px`
: `calc(100% - ${(obj.position.bottom as number) + 41.5}px)`,
width: `${obj.position.left}px`,
}}
>
<span className="distance-label">{obj.position.left}px</span>
</div>
)}
{activeEdges.horizontal === "right" &&
typeof obj.position.right === "number" && (
<div
className="distance-line right"
style={{
right: 0,
top:
activeEdges.vertical === "top"
? `${(obj.position.top as number) + 41.5}px`
: `calc(100% - ${(obj.position.bottom as number) + 41.5}px)`,
width: `${obj.position.right}px`,
}}
>
<span className="distance-label">{obj.position.right}px</span>
</div>
)}
</>
);
};
export default DistanceLines;

View File

@@ -306,34 +306,20 @@ export const DraggableWidget = ({
)}
{widget.type === "doughnut" && (
<DoughnutGraphComponent
id={widget.id}
type={widget.type}
title={widget.title}
fontSize={widget.fontSize}
fontWeight={widget.fontWeight}
data={{
measurements: [
{ name: "testDevice", fields: "powerConsumption" },
{ name: "furnace", fields: "powerConsumption" },
],
interval: 1000,
duration: "1h",
}}
/>
)}
{widget.type === "polarArea" && (
<PolarAreaGraphComponent
id={widget.id}
type={widget.type}
title={widget.title}
fontSize={widget.fontSize}
fontWeight={widget.fontWeight}
data={{
measurements: [
{ name: "testDevice", fields: "powerConsumption" },
{ name: "furnace", fields: "powerConsumption" },
],
interval: 1000,
duration: "1h",
}}
/>
)}
</div>

View File

@@ -10,34 +10,79 @@ import ProductionCapacity from "../../layout/3D-cards/cards/ProductionCapacity";
import ReturnOfInvestment from "../../layout/3D-cards/cards/ReturnOfInvestment";
import StateWorking from "../../layout/3D-cards/cards/StateWorking";
import { useSelectedZoneStore } from "../../../store/useZoneStore";
import { generateUniqueId } from "../../../functions/generateUniqueId";
import { adding3dWidgets } from "../../../services/realTimeVisulization/zoneData/add3dWidget";
import { get3dWidgetZoneData } from "../../../services/realTimeVisulization/zoneData/get3dWidgetData";
import { use3DWidget } from "../../../store/useDroppedObjectsStore";
export default function Dropped3dWidgets() {
const { widgetSelect, setWidgetSelect } = useAsset3dWidget();
const { widgetSelect } = useAsset3dWidget();
const { activeModule } = useModuleStore();
const { raycaster, gl, scene }: ThreeState = useThree();
const { selectedZone } = useSelectedZoneStore(); // Get currently selected zone
const { widgetSubOption, setWidgetSubOption } = useWidgetSubOption()
// 🔥 Store widget positions per zone
const [zoneWidgets, setZoneWidgets] = useState<Record<
string, // Zone ID
Record<string, [number, number, number][]> // Widget type -> Positions array
>>({});
const { widgetSubOption, setWidgetSubOption } = useWidgetSubOption();
const { selectedZone } = useSelectedZoneStore(); // Get the currently active zone
// 🔥 Store widget data (id, type, position) based on the selected zone
const [zoneWidgetData, setZoneWidgetData] = useState<
Record<string, { id: string; type: string; position: [number, number, number] }[]>
>({});
const { setWidgets3D } = use3DWidget()
useEffect(() => {
if (activeModule !== "visualization") return
if (selectedZone.zoneName === "") return;
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0];
async function get3dWidgetData() {
let result = await get3dWidgetZoneData(selectedZone.zoneId, organization);
setWidgets3D(result)
// Ensure the extracted data has id, type, and position correctly mapped
const formattedWidgets = result.map((widget: any) => ({
id: widget.id,
type: widget.type,
position: widget.position,
}));
setZoneWidgetData((prev) => ({
...prev,
[selectedZone.zoneId]: formattedWidgets,
}));
}
get3dWidgetData();
}, [selectedZone.zoneId,activeModule]);
// useEffect(() => {
// // ✅ Set data only for the selected zone, keeping existing state structure
// setZoneWidgetData((prev) => ({
// ...prev,
// [selectedZone.zoneId]: [
// {
// "id": "1743322674626-50mucpb1c",
// "type": "ui-Widget 1",
// "position": [120.94655021768133, 4.142360029666558, 124.39283546121099]
// },
// {
// "id": "1743322682086-je2h9x33v",
// "type": "ui-Widget 2",
// "position": [131.28751045879255, 0.009999999999970264, 133.92059801984362]
// }
// ]
// }));
// }, [selectedZone.zoneId]); // ✅ Only update when the zone changes
useEffect(() => {
if (widgetSubOption === "Floating") return
// if (activeModule !== "visualization") return;
const canvasElement = gl.domElement;
const onDrop = (event: DragEvent) => {
event.preventDefault(); // Prevent default browser behavior
if (widgetSubOption === "3D") {
if (activeModule !== "visualization") return;
if (widgetSubOption === "Floating") return;
if (selectedZone.zoneName === "") return
if (!widgetSelect?.startsWith("ui")) return;
const canvasElement = gl.domElement;
const onDrop = async (event: DragEvent) => {
event.preventDefault(); // Prevent default browser behavior
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0];
if (!widgetSelect.startsWith("ui")) return;
const group1 = scene.getObjectByName("itemsGroup");
if (!group1) return;
const Assets = group1.children
.map((val) => scene.getObjectByProperty("uuid", val.uuid))
.filter(Boolean) as THREE.Object3D[];
const intersects = raycaster.intersectObjects(scene.children, true).filter(
(intersect) =>
!intersect.object.name.includes("Roof") &&
@@ -49,40 +94,54 @@ export default function Dropped3dWidgets() {
if (intersects.length > 0) {
const { x, y, z } = intersects[0].point;
setZoneWidgets((prev) => ({
// ✅ Explicitly define position as a tuple
const newWidget: { id: string; type: string; position: [number, number, number] } = {
id: generateUniqueId(),
type: widgetSelect,
position: [x, y, z], // Ensures TypeScript recognizes it as a tuple
};
let response = await adding3dWidgets(selectedZone.zoneId, organization, newWidget)
// ✅ Store widgets uniquely for each zone
setZoneWidgetData((prev) => ({
...prev,
[selectedZone.zoneId]: {
...(prev[selectedZone.zoneId] || {}),
[widgetSelect]: [...(prev[selectedZone.zoneId]?.[widgetSelect] || []), [x, y, z]],
},
[selectedZone.zoneId]: [...(prev[selectedZone.zoneId] || []), newWidget],
}));
}
}
};
canvasElement.addEventListener("drop", onDrop);
return () => {
canvasElement.removeEventListener("drop", onDrop)
// setWidgetSelect()
canvasElement.removeEventListener("drop", onDrop);
};
}, [widgetSelect, activeModule, widgetSubOption]);
}, [widgetSelect, activeModule, selectedZone.zoneId, widgetSubOption]);
// Get widgets for the currently active zone
const activeZoneWidgets = zoneWidgetData[selectedZone.zoneId] || [];
return (
<>
{zoneWidgets[selectedZone.zoneId]?.["ui-Widget 1"]?.map((pos, index) => (
<ProductionCapacity key={`Widget1-${index}`} position={pos} />
))}
{zoneWidgets[selectedZone.zoneId]?.["ui-Widget 2"]?.map((pos, index) => (
<ReturnOfInvestment key={`Widget2-${index}`} position={pos} />
))}
{zoneWidgets[selectedZone.zoneId]?.["ui-Widget 3"]?.map((pos, index) => (
<StateWorking key={`Widget3-${index}`} position={pos} />
))}
{zoneWidgets[selectedZone.zoneId]?.["ui-Widget 4"]?.map((pos, index) => (
<Throughput key={`Widget4-${index}`} position={pos} />
))}
{activeZoneWidgets.map(({ id, type, position }) => {
switch (type) {
case "ui-Widget 1":
return <ProductionCapacity key={id} position={position} />;
case "ui-Widget 2":
return <ReturnOfInvestment key={id} position={position} />;
case "ui-Widget 3":
return <StateWorking key={id} position={position} />;
case "ui-Widget 4":
return <Throughput key={id} position={position} />;
default:
return null;
}
})}
</>
);
}

View File

@@ -1,6 +1,6 @@
import { WalletIcon } from "../../icons/3dChartIcons";
import { useEffect, useRef, useState } from "react";
import { Line } from "react-chartjs-2";
import {
useDroppedObjectsStore,
Zones,
@@ -9,29 +9,102 @@ import useModuleStore from "../../../store/useModuleStore";
import { determinePosition } from "./functions/determinePosition";
import { getActiveProperties } from "./functions/getActiveProperties";
import { addingFloatingWidgets } from "../../../services/realTimeVisulization/zoneData/addFloatingWidgets";
import {
DublicateIcon,
KebabIcon,
DeleteIcon,
} from "../../icons/ExportCommonIcons";
import DistanceLines from "./DistanceLines"; // Import the DistanceLines component
import { deleteFloatingWidgetApi } from "../../../services/realTimeVisulization/zoneData/deleteFloatingWidget";
import TotalCardComponent from "../realTimeVis/floating/TotalCardComponent";
import WarehouseThroughputComponent from "../realTimeVis/floating/WarehouseThroughputComponent";
import FleetEfficiencyComponent from "../realTimeVis/floating/FleetEfficiencyComponent";
import { useWidgetStore } from "../../../store/useWidgetStore";
interface DraggingState {
zone: string;
index: number;
initialPosition: {
top: number | "auto";
left: number | "auto";
right: number | "auto";
bottom: number | "auto";
};
}
interface DraggingState {
zone: string;
index: number;
initialPosition: {
top: number | "auto";
left: number | "auto";
right: number | "auto";
bottom: number | "auto";
};
}
const DroppedObjects: React.FC = () => {
const zones = useDroppedObjectsStore((state) => state.zones);
const [openKebabId, setOpenKebabId] = useState<string | null>(null);
const updateObjectPosition = useDroppedObjectsStore(
(state) => state.updateObjectPosition
);
const [draggingIndex, setDraggingIndex] = useState<{
zone: string;
index: number;
} | null>(null);
const deleteObject = useDroppedObjectsStore((state) => state.deleteObject);
const duplicateObject = useDroppedObjectsStore((state) => state.duplicateObject);
const [draggingIndex, setDraggingIndex] = useState<DraggingState | null>(
null
);
const [offset, setOffset] = useState<[number, number] | null>(null);
const positionRef = useRef<[number, number] | null>(null);
const { setSelectedChartId } = useWidgetStore();
const [activeEdges, setActiveEdges] = useState<{
vertical: "top" | "bottom";
horizontal: "left" | "right";
} | null>(null); // State to track active edges for distance lines
const [currentPosition, setCurrentPosition] = useState<{
top: number | "auto";
left: number | "auto";
right: number | "auto";
bottom: number | "auto";
} | null>(null); // State to track the current position during drag
const animationRef = useRef<number | null>(null);
const { activeModule } = useModuleStore();
// Get the first zone and its objects
const zoneEntries = Object.entries(zones);
if (zoneEntries.length === 0) return null; // No zone, nothing to render
const [zoneName, zone] = zoneEntries[0]; // Only render the first zone
// Clean up animation frame on unmount
useEffect(() => {
return () => {
if (animationRef.current) {
cancelAnimationFrame(animationRef.current);
}
};
}, []);
// Handle pointer down event
function handlePointerDown(event: React.PointerEvent, index: number) {
const zoneEntries = Object.entries(zones);
if (zoneEntries.length === 0) return null;
const [zoneName, zone] = zoneEntries[0];
function handleDuplicate(zoneName: string, index: number) {
setOpenKebabId(null)
duplicateObject(zoneName, index); // Call the duplicateObject method from the store
}
async function handleDelete(zoneName: string, id: string, index: number) {
try {
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0];
let res = await deleteFloatingWidgetApi(id, organization);
console.log('res: ', res);
if (res.message === "FloatingWidget deleted successfully") {
deleteObject(zoneName, index); // Call the deleteObject method from the store
}
} catch (error) {
console.error("Error deleting floating widget:", error);
}
}
const handlePointerDown = (event: React.PointerEvent, index: number) => {
const obj = zone.objects[index];
const container = document.getElementById("real-time-vis-canvas");
if (!container) return;
@@ -40,41 +113,41 @@ const DroppedObjects: React.FC = () => {
const relativeX = event.clientX - rect.left;
const relativeY = event.clientY - rect.top;
// Determine which properties are active for this object
// Determine active properties for the initial position
const [activeProp1, activeProp2] = getActiveProperties(obj.position);
// Calculate the offset based on the active properties
// Set active edges for distance lines
const vertical = activeProp1 === "top" ? "top" : "bottom";
const horizontal = activeProp2 === "left" ? "left" : "right";
setActiveEdges({ vertical, horizontal });
// Store the initial position strategy and active edges
setDraggingIndex({
zone: zoneName,
index,
initialPosition: { ...obj.position },
});
// Calculate offset from mouse to object edges
let offsetX = 0;
let offsetY = 0;
if (activeProp1 === "top") {
offsetY =
relativeY -
(typeof obj.position.top === "number" ? obj.position.top : 0);
} else if (activeProp1 === "bottom") {
offsetY =
rect.height -
relativeY -
(typeof obj.position.bottom === "number" ? obj.position.bottom : 0);
offsetY = relativeY - (obj.position.top as number);
} else {
offsetY = rect.height - relativeY - (obj.position.bottom as number);
}
if (activeProp2 === "left") {
offsetX =
relativeX -
(typeof obj.position.left === "number" ? obj.position.left : 0);
} else if (activeProp2 === "right") {
offsetX =
rect.width -
relativeX -
(typeof obj.position.right === "number" ? obj.position.right : 0);
offsetX = relativeX - (obj.position.left as number);
} else {
offsetX = rect.width - relativeX - (obj.position.right as number);
}
setDraggingIndex({ zone: zoneName, index });
setOffset([offsetY, offsetX]);
}
};
// Handle pointer move event
function handlePointerMove(event: React.PointerEvent) {
const handlePointerMove = (event: React.PointerEvent) => {
if (!draggingIndex || !offset) return;
const container = document.getElementById("real-time-vis-canvas");
@@ -84,118 +157,132 @@ const DroppedObjects: React.FC = () => {
const relativeX = event.clientX - rect.left;
const relativeY = event.clientY - rect.top;
// Determine which properties are active for the dragged object
const obj = zone.objects[draggingIndex.index];
const [activeProp1, activeProp2] = getActiveProperties(obj.position);
// Dynamically determine the current position strategy
const newPositionStrategy = determinePosition(rect, relativeX, relativeY);
const [activeProp1, activeProp2] = getActiveProperties(newPositionStrategy);
// Calculate the new position based on the active properties
let newX = 0;
// Update active edges for distance lines
const vertical = activeProp1 === "top" ? "top" : "bottom";
const horizontal = activeProp2 === "left" ? "left" : "right";
setActiveEdges({ vertical, horizontal });
// Calculate new position based on the active properties
let newY = 0;
if (activeProp2 === "left") {
newX = relativeX - offset[1];
} else if (activeProp2 === "right") {
newX = rect.width - (relativeX + offset[1]);
}
let newX = 0;
if (activeProp1 === "top") {
newY = relativeY - offset[0];
} else if (activeProp1 === "bottom") {
} else {
newY = rect.height - (relativeY + offset[0]);
}
// Ensure the object stays within the canvas boundaries
if (activeProp2 === "left") {
newX = relativeX - offset[1];
} else {
newX = rect.width - (relativeX + offset[1]);
}
// Apply boundaries
newX = Math.max(0, Math.min(rect.width - 50, newX));
newY = Math.max(0, Math.min(rect.height - 50, newY));
// Update the position reference
positionRef.current = [newY, newX];
// Create new position object
const newPosition = {
...newPositionStrategy,
[activeProp1]: newY,
[activeProp2]: newX,
// Clear opposite properties
[activeProp1 === "top" ? "bottom" : "top"]: "auto",
[activeProp2 === "left" ? "right" : "left"]: "auto",
};
// Update the current position state for DistanceLines
setCurrentPosition(newPosition);
// Update the object's position using requestAnimationFrame for smoother animations
if (!animationRef.current) {
animationRef.current = requestAnimationFrame(() => {
if (positionRef.current) {
updateObjectPosition(zoneName, draggingIndex.index, {
...obj.position,
[activeProp1]: positionRef.current[0],
[activeProp2]: positionRef.current[1],
});
}
updateObjectPosition(zoneName, draggingIndex.index, newPosition);
animationRef.current = null;
});
}
}
};
// Handle pointer up event
async function handlePointerUp(event: React.MouseEvent<HTMLDivElement>) {
const handlePointerUp = async (event: React.PointerEvent<HTMLDivElement>) => {
try {
if (!draggingIndex || !offset) return;
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0];
const container = document.getElementById("real-time-vis-canvas");
if (!container) throw new Error("Canvas container not found");
if (!container) return;
const rect = container.getBoundingClientRect();
const relativeX = event.clientX - rect.left;
const relativeY = event.clientY - rect.top;
// Recalculate the position using determinePosition
const newPosition = determinePosition(rect, relativeX, relativeY);
// Only now determine the final position strategy
const finalPosition = determinePosition(rect, relativeX, relativeY);
const [activeProp1, activeProp2] = getActiveProperties(finalPosition);
// Validate the dragging index and get the object
if (!zone.objects[draggingIndex.index]) {
throw new Error("Dragged object not found in the zone");
// Calculate final position using the new strategy
let finalY = 0;
let finalX = 0;
if (activeProp1 === "top") {
finalY = relativeY - offset[0];
} else {
finalY = rect.height - (relativeY + offset[0]);
}
const obj = { ...zone.objects[draggingIndex.index], position: newPosition };
let response = await addingFloatingWidgets(zone.zoneId, organization, obj);
if (activeProp2 === "left") {
finalX = relativeX - offset[1];
} else {
finalX = rect.width - (relativeX + offset[1]);
}
// Apply boundaries
finalX = Math.max(0, Math.min(rect.width - 50, finalX));
finalY = Math.max(0, Math.min(rect.height - 50, finalY));
const boundedPosition = {
...finalPosition,
[activeProp1]: finalY,
[activeProp2]: finalX,
};
// Save to backend
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0];
const response = await addingFloatingWidgets(zone.zoneId, organization, {
...zone.objects[draggingIndex.index],
position: boundedPosition,
});
if (response.message === "Widget updated successfully") {
updateObjectPosition(zoneName, draggingIndex.index, newPosition);
updateObjectPosition(zoneName, draggingIndex.index, boundedPosition);
}
// Reset states
// Clean up
setDraggingIndex(null);
setOffset(null);
setActiveEdges(null); // Clear active edges
setCurrentPosition(null); // Reset current position
if (animationRef.current) {
cancelAnimationFrame(animationRef.current);
animationRef.current = null;
}
} catch (error) {
}
console.error("Error in handlePointerUp:", error);
}
};
const handleKebabClick = (id: string, event: React.MouseEvent) => {
event.stopPropagation();
setOpenKebabId((prevId) => (prevId === id ? null : id));
};
const renderObjectContent = (obj: any) => {
switch (obj.className) {
case "floating total-card":
return (
<div onPointerMove={handlePointerMove} onPointerUp={handlePointerUp}>
{zone.objects.map((obj, index) => (
<div
key={`${zoneName}-${index}`}
className={obj.className}
style={{
position: "absolute",
top:
typeof obj.position.top !== "string"
? `${obj.position.top}px`
: "auto",
left:
typeof obj.position.left !== "string"
? `${obj.position.left}px`
: "auto",
right:
typeof obj.position.right !== "string"
? `${obj.position.right}px`
: "auto",
bottom:
typeof obj.position.bottom !== "string"
? `${obj.position.bottom}px`
: "auto",
// transition: draggingIndex?.index === index ? "none" : "transform 0.1s ease-out",
}}
onPointerDown={(event) => handlePointerDown(event, index)}
>
{obj.className === "floating total-card" ? (
<>
<div className="header-wrapper">
<div className="header">{obj.header}</div>
@@ -208,7 +295,9 @@ const DroppedObjects: React.FC = () => {
<WalletIcon />
</div>
</>
) : obj.className === "warehouseThroughput floating" ? (
);
case "warehouseThroughput floating":
return (
<>
<div className="header">
<h2>Warehouse Throughput</h2>
@@ -220,7 +309,9 @@ const DroppedObjects: React.FC = () => {
{/* <Line data={lineGraphData} options={lineGraphOptions} /> */}
</div>
</>
) : obj.className === "fleetEfficiency floating" ? (
);
case "fleetEfficiency floating":
return (
<>
<h2 className="header">Fleet Efficiency</h2>
<div className="progressContainer">
@@ -242,11 +333,123 @@ const DroppedObjects: React.FC = () => {
<span>100%</span>
</div>
</>
);
default:
return null;
}
};
return (
<div
onPointerMove={handlePointerMove}
onPointerUp={handlePointerUp}
className="floating-wrapper"
>
{zone.objects.map((obj, index) => (
<div
key={`${zoneName}-${index}`}
className={obj.className}
style={{
position: "absolute",
top:
typeof obj.position.top === "number"
? `${obj.position.top}px`
: "auto",
left:
typeof obj.position.left === "number"
? `${obj.position.left}px`
: "auto",
right:
typeof obj.position.right === "number"
? `${obj.position.right}px`
: "auto",
bottom:
typeof obj.position.bottom === "number"
? `${obj.position.bottom}px`
: "auto",
}}
onPointerDown={(event) => handlePointerDown(event, index)}
onClick={() => {
setSelectedChartId(obj)
}}
>
{obj.className === "floating total-card" ? (
<>
<TotalCardComponent object={obj} />
</>
) : obj.className === "warehouseThroughput floating" ? (
<>
<WarehouseThroughputComponent />
</>
) : obj.className === "fleetEfficiency floating" ? (
<>
<FleetEfficiencyComponent object={obj} />
</>
) : null}
{renderObjectContent(obj)}
<div
className="icon kebab"
onClick={(event) => handleKebabClick(obj.id, event)}
>
<KebabIcon />
</div>
{openKebabId === obj.id && (
<div className="kebab-options">
<div className="dublicate btn" onClick={(event) => {
event.stopPropagation();
handleDuplicate(zoneName, index); // Call the duplicate handler
}}>
<div className="icon">
<DublicateIcon />
</div>
<div className="label">Duplicate</div>
</div>
<div className="edit btn" onClick={(event) => {
event.stopPropagation();
handleDelete(zoneName, obj.id, index); // Call the delete handler
}}>
<div className="icon">
<DeleteIcon />
</div>
<div className="label">Delete</div>
</div>
</div>
)}
</div>
))}
{/* Render DistanceLines component during drag */}
{draggingIndex !== null &&
activeEdges !== null &&
currentPosition !== null && (
<DistanceLines
obj={{
position: {
top:
currentPosition.top !== "auto"
? currentPosition.top
: undefined,
bottom:
currentPosition.bottom !== "auto"
? currentPosition.bottom
: undefined,
left:
currentPosition.left !== "auto"
? currentPosition.left
: undefined,
right:
currentPosition.right !== "auto"
? currentPosition.right
: undefined,
},
}}
activeEdges={activeEdges}
/>
)}
</div>
);
};
export default DroppedObjects;

View File

@@ -63,6 +63,8 @@ const RealTimeVisulization: React.FC = () => {
const organization = email?.split("@")[1]?.split(".")[0];
try {
const response = await getZone2dData(organization);
console.log('response: ', response);
if (!Array.isArray(response)) {
return;
}
@@ -83,7 +85,7 @@ const RealTimeVisulization: React.FC = () => {
);
setZonesData(formattedData);
} catch (error) {
console.log("error: ", error);
}
}
@@ -109,7 +111,7 @@ const RealTimeVisulization: React.FC = () => {
});
}, [selectedZone]);
useEffect(() => {}, [floatingWidgets]);
// useEffect(() => {}, [floatingWidgets]);
const handleDrop = async (event: React.DragEvent<HTMLDivElement>) => {
try {
@@ -135,6 +137,7 @@ const RealTimeVisulization: React.FC = () => {
id: generateUniqueId(),
position: determinePosition(canvasRect, relativeX, relativeY),
};
console.log('newObject: ', newObject);
let response = await addingFloatingWidgets(
selectedZone.zoneId,
@@ -171,7 +174,7 @@ const RealTimeVisulization: React.FC = () => {
],
},
}));
} catch (error) {}
} catch (error) { }
};
return (
@@ -198,7 +201,8 @@ const RealTimeVisulization: React.FC = () => {
>
<Scene />
</div>
<DroppedObjects />
{activeModule === "visualization" && selectedZone.zoneName !== "" && <DroppedObjects />}
{/* <DroppedObjects /> */}
{activeModule === "visualization" && (
<>
<DisplayZone

View File

@@ -1,3 +1,70 @@
// export function determinePosition(
// canvasRect: DOMRect,
// relativeX: number,
// relativeY: number
// ): {
// top: number | "auto";
// left: number | "auto";
// right: number | "auto";
// bottom: number | "auto";
// } {
// // Calculate the midpoints of the canvas
// const centerX = canvasRect.width / 2;
// const centerY = canvasRect.height / 2;
// // Initialize position with default values
// let position: {
// top: number | "auto";
// left: number | "auto";
// right: number | "auto";
// bottom: number | "auto";
// };
// if (relativeY < centerY) {
// // Top half
// if (relativeX < centerX) {
// // Left side
// position = {
// top: relativeY,
// left: relativeX,
// right: "auto",
// bottom: "auto",
// };
// } else {
// // Right side
// position = {
// top: relativeY,
// right: canvasRect.width - relativeX,
// left: "auto",
// bottom: "auto",
// };
// }
// } else {
// // Bottom half
// if (relativeX < centerX) {
// // Left side
// position = {
// bottom: canvasRect.height - relativeY,
// left: relativeX,
// right: "auto",
// top: "auto",
// };
// } else {
// // Right side
// position = {
// bottom: canvasRect.height - relativeY,
// right: canvasRect.width - relativeX,
// left: "auto",
// top: "auto",
// };
// }
// }
// return position;
// }
export function determinePosition(
canvasRect: DOMRect,
relativeX: number,
@@ -8,11 +75,9 @@ export function determinePosition(
right: number | "auto";
bottom: number | "auto";
} {
// Calculate the midpoints of the canvas
const centerX = canvasRect.width / 2;
const centerY = canvasRect.height / 2;
// Initialize position with default values
let position: {
top: number | "auto";
left: number | "auto";
@@ -21,9 +86,8 @@ export function determinePosition(
};
if (relativeY < centerY) {
// Top half
if (relativeX < centerX) {
// Left side
console.log("Top-left");
position = {
top: relativeY,
left: relativeX,
@@ -31,7 +95,7 @@ export function determinePosition(
bottom: "auto",
};
} else {
// Right side
console.log("Top-right");
position = {
top: relativeY,
right: canvasRect.width - relativeX,
@@ -40,9 +104,8 @@ export function determinePosition(
};
}
} else {
// Bottom half
if (relativeX < centerX) {
// Left side
console.log("Bottom-left");
position = {
bottom: canvasRect.height - relativeY,
left: relativeX,
@@ -50,7 +113,7 @@ export function determinePosition(
top: "auto",
};
} else {
// Right side
console.log("Bottom-right");
position = {
bottom: canvasRect.height - relativeY,
right: canvasRect.width - relativeX,

View File

@@ -1,17 +1,11 @@
export const getActiveProperties = (position: {
top: number | "auto";
left: number | "auto";
right: number | "auto";
bottom: number | "auto";
}) => {
let activeProps: ["top", "left"] | ["bottom", "right"] = ["top", "left"]; // Default to top-left
if (
typeof position.bottom !== "string" &&
typeof position.right !== "string"
) {
activeProps = ["bottom", "right"];
export function getActiveProperties(position: any): [string, string] {
if (position.top !== "auto" && position.left !== "auto") {
return ["top", "left"]; // Top-left
} else if (position.top !== "auto" && position.right !== "auto") {
return ["top", "right"]; // Top-right
} else if (position.bottom !== "auto" && position.left !== "auto") {
return ["bottom", "left"]; // Bottom-left
} else {
return ["bottom", "right"]; // Bottom-right
}
return activeProps;
};
}

View File

@@ -0,0 +1,29 @@
// import { useSelectedZoneStore } from "../../../store/useZoneStore";
// type HandleDropTemplateProps = {
// templateId: string;
// };
// export const handleDropTemplate = ({ templateId }: HandleDropTemplateProps): void => {
// const { getTemplate } = useTemplateStore.getState();
// const { setSelectedZone } = useSelectedZoneStore.getState();
// // Find the template by ID
// const template: Template | undefined = getTemplate(templateId);
// if (!template) {
// console.error("Template not found!");
// return;
// }
// // Update the selected zone with the template data
// setSelectedZone((prev) => ({
// ...prev,
// panelOrder: template.panelOrder,
// activeSides: Array.from(new Set([...prev.activeSides, ...template.panelOrder])),
// widgets: template.widgets, // Keep widget structure the same
// }));
// console.log("Dropped template applied:", template);
// };

View File

@@ -1,5 +1,5 @@
import React, { useEffect, useState } from "react";
import * as CONSTANTS from "../../../types/world/worldConstants";
interface InputToggleProps {
label: string; // Represents the toggle state (on/off)
min?: number;
@@ -14,8 +14,8 @@ const InputRange: React.FC<InputToggleProps> = ({
label,
onClick,
onChange,
min = 0,
max = 10,
min,
max,
disabled,
value = 5,
}) => {
@@ -23,6 +23,7 @@ const InputRange: React.FC<InputToggleProps> = ({
function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
const newValue = parseInt(e.target.value); // Parse the value to an integer
setRangeValue(newValue); // Update the local state
if (onChange) {

View File

@@ -211,9 +211,10 @@ const BarGraphComponent = ({
fontWeight = "Regular",
}: ChartComponentProps) => {
const { themeColor } = useThemeStore();
const { measurements: chartMeasurements, duration: chartDuration } = useChartStore();
const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore();
const [measurements, setmeasurements] = useState<any>({});
const [duration, setDuration] = useState("1h")
const [name, setName] = useState("Widget")
const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({
labels: [],
datasets: [],
@@ -236,6 +237,10 @@ const BarGraphComponent = ({
],
};
useEffect(() => {
console.log("titleeeeeeeeeeeeeeeeeee",title);
},[])
// Memoize Theme Colors
const buttonActionColor = useMemo(() => themeColor[0] || "#5c87df", [themeColor]);
const buttonAbortColor = useMemo(() => themeColor[1] || "#ffffff", [themeColor]);
@@ -270,7 +275,7 @@ const BarGraphComponent = ({
plugins: {
title: {
display: true,
text: title,
text: name,
font: chartFontStyle,
},
legend: {
@@ -285,7 +290,7 @@ const BarGraphComponent = ({
},
},
}),
[title, chartFontStyle]
[title, chartFontStyle, name]
);
// useEffect(() => {console.log(measurements);
@@ -304,15 +309,12 @@ const BarGraphComponent = ({
const startStream = () => {
console.log("inputtttttttttt",inputData);
socket.emit("lineInput", inputData);
};
socket.on("connect", startStream);
socket.on("lineOutput", (response) => {
console.log("responce dataaaaaaaaa",response.data);
const responseData = response.data;
// Extract timestamps and values
@@ -347,6 +349,7 @@ const BarGraphComponent = ({
if (response.status === 200) {
setmeasurements(response.data.Data.measurements)
setDuration(response.data.Data.duration)
setName(response.data.widgetName)
} else {
console.log("Unexpected response:", response);
}
@@ -365,7 +368,7 @@ const BarGraphComponent = ({
fetchSavedInputes();
}
}
,[chartMeasurements, chartDuration])
,[chartMeasurements, chartDuration, widgetName])
return <Bar data={Object.keys(measurements).length > 0 ? chartData : defaultData} options={options} />;
};

View File

@@ -1,22 +1,64 @@
import { useMemo } from "react";
import { Doughnut, Line } from "react-chartjs-2";
import React, { useEffect, useMemo, useState } from "react";
import { Doughnut } from "react-chartjs-2";
import io from "socket.io-client";
import { useThemeStore } from "../../../../store/useThemeStore";
import useChartStore from "../../../../store/useChartStore";
import { useWidgetStore } from "../../../../store/useWidgetStore";
import axios from "axios";
interface ChartComponentProps {
id: string;
type: any;
title: string;
fontFamily?: string;
fontSize?: string;
fontWeight?: "Light" | "Regular" | "Bold";
data: any;
}
const DoughnutGraphComponent = ({
id,
type,
title,
fontFamily,
fontSize,
fontWeight = "Regular",
}: ChartComponentProps) => {
// Memoize Font Weight Mapping
const { themeColor } = useThemeStore();
const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore();
const [measurements, setmeasurements] = useState<any>({});
const [duration, setDuration] = useState("1h")
const [name, setName] = useState("Widget")
const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({
labels: [],
datasets: [],
});
const { selectedChartId } = useWidgetStore();
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]
const defaultData = {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [
{
label: "Dataset",
data: [12, 19, 3, 5, 2, 3],
backgroundColor: ["#6f42c1"],
borderColor: "#b392f0",
borderWidth: 1,
},
],
};
useEffect(() => {
console.log("titleeeeeeeeeeeeeeeeeee",title);
},[])
// Memoize Theme Colors
const buttonActionColor = useMemo(() => themeColor[0] || "#5c87df", [themeColor]);
const buttonAbortColor = useMemo(() => themeColor[1] || "#ffffff", [themeColor]);
// Memoize Font Styling
const chartFontWeightMap = useMemo(
() => ({
Light: "lighter" as const,
@@ -26,19 +68,9 @@ const DoughnutGraphComponent = ({
[]
);
// Parse and Memoize Font Size
const fontSizeValue = useMemo(
() => (fontSize ? parseInt(fontSize) : 12),
[fontSize]
);
const fontSizeValue = useMemo(() => (fontSize ? parseInt(fontSize) : 12), [fontSize]);
const fontWeightValue = useMemo(() => chartFontWeightMap[fontWeight], [fontWeight, chartFontWeightMap]);
// Determine and Memoize Font Weight
const fontWeightValue = useMemo(
() => chartFontWeightMap[fontWeight],
[fontWeight, chartFontWeightMap]
);
// Memoize Chart Font Style
const chartFontStyle = useMemo(
() => ({
family: fontFamily || "Arial",
@@ -48,6 +80,7 @@ const DoughnutGraphComponent = ({
[fontFamily, fontSizeValue, fontWeightValue]
);
// Memoize Chart Options
const options = useMemo(
() => ({
responsive: true,
@@ -55,7 +88,7 @@ const DoughnutGraphComponent = ({
plugins: {
title: {
display: true,
text: title,
text: name,
font: chartFontStyle,
},
legend: {
@@ -63,31 +96,94 @@ const DoughnutGraphComponent = ({
},
},
scales: {
x: {
ticks: {
display: false, // This hides the x-axis labels
},
},
// x: {
// ticks: {
// display: true, // This hides the x-axis labels
// },
// },
},
}),
[title, chartFontStyle]
[title, chartFontStyle, name]
);
const chartData = {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [
{
label: "My First Dataset",
data: [65, 59, 80, 81, 56, 55, 40],
backgroundColor: "#6f42c1", // Updated to #6f42c1 (Purple)
borderColor: "#ffffff", // Keeping border color white
borderWidth: 2,
fill: false,
},
],
// useEffect(() => {console.log(measurements);
// },[measurements])
useEffect(() => {
if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return;
const socket = io(`http://${iotApiUrl}`);
const inputData = {
measurements,
duration,
interval: 1000,
};
return <Doughnut data={chartData} options={options} />;
const startStream = () => {
socket.emit("lineInput", inputData);
};
socket.on("connect", startStream);
socket.on("lineOutput", (response) => {
const responseData = response.data;
// Extract timestamps and values
const labels = responseData.time;
const datasets = Object.keys(measurements).map((key) => {
const measurement = measurements[key];
const datasetKey = `${measurement.name}.${measurement.fields}`;
return {
label: datasetKey,
data: responseData[datasetKey]?.values ?? [],
backgroundColor: "#6f42c1",
borderColor: "#b392f0",
borderWidth: 1,
};
});
setChartData({ labels, datasets });
});
return () => {
socket.off("lineOutput");
socket.emit("stop_stream"); // Stop streaming when component unmounts
socket.disconnect();
};
}, [measurements, duration, iotApiUrl]);
const fetchSavedInputes = async() => {
if (id !== "") {
try {
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${id}/${organization}`);
if (response.status === 200) {
setmeasurements(response.data.Data.measurements)
setDuration(response.data.Data.duration)
setName(response.data.widgetName)
} else {
console.log("Unexpected response:", response);
}
} catch (error) {
console.error("There was an error!", error);
}
}
}
useEffect(() => {
fetchSavedInputes();
}, []);
useEffect(() => {
if (selectedChartId?.id === id) {
fetchSavedInputes();
}
}
,[chartMeasurements, chartDuration, widgetName])
return <Doughnut data={Object.keys(measurements).length > 0 ? chartData : defaultData} options={options} />;
};
export default DoughnutGraphComponent;

View File

@@ -24,9 +24,10 @@ const LineGraphComponent = ({
fontWeight = "Regular",
}: ChartComponentProps) => {
const { themeColor } = useThemeStore();
const { measurements: chartMeasurements, duration: chartDuration } = useChartStore();
const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore();
const [measurements, setmeasurements] = useState<any>({});
const [duration, setDuration] = useState("1h")
const [name, setName] = useState("Widget")
const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({
labels: [],
datasets: [],
@@ -49,6 +50,10 @@ const LineGraphComponent = ({
],
};
useEffect(() => {
console.log("titleeeeeeeeeeeeeeeeeee",title);
},[])
// Memoize Theme Colors
const buttonActionColor = useMemo(() => themeColor[0] || "#5c87df", [themeColor]);
const buttonAbortColor = useMemo(() => themeColor[1] || "#ffffff", [themeColor]);
@@ -83,7 +88,7 @@ const LineGraphComponent = ({
plugins: {
title: {
display: true,
text: title,
text: name,
font: chartFontStyle,
},
legend: {
@@ -98,7 +103,7 @@ const LineGraphComponent = ({
},
},
}),
[title, chartFontStyle]
[title, chartFontStyle, name]
);
// useEffect(() => {console.log(measurements);
@@ -157,6 +162,7 @@ const LineGraphComponent = ({
if (response.status === 200) {
setmeasurements(response.data.Data.measurements)
setDuration(response.data.Data.duration)
setName(response.data.widgetName)
} else {
console.log("Unexpected response:", response);
}
@@ -175,7 +181,7 @@ const LineGraphComponent = ({
fetchSavedInputes();
}
}
,[chartMeasurements, chartDuration])
,[chartMeasurements, chartDuration, widgetName])
return <Line data={Object.keys(measurements).length > 0 ? chartData : defaultData} options={options} />;
};

View File

@@ -210,9 +210,10 @@ const PieChartComponent = ({
fontWeight = "Regular",
}: ChartComponentProps) => {
const { themeColor } = useThemeStore();
const { measurements: chartMeasurements, duration: chartDuration } = useChartStore();
const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore();
const [measurements, setmeasurements] = useState<any>({});
const [duration, setDuration] = useState("1h")
const [name, setName] = useState("Widget")
const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({
labels: [],
datasets: [],
@@ -235,6 +236,10 @@ const PieChartComponent = ({
],
};
useEffect(() => {
console.log("titleeeeeeeeeeeeeeeeeee",title);
},[])
// Memoize Theme Colors
const buttonActionColor = useMemo(() => themeColor[0] || "#5c87df", [themeColor]);
const buttonAbortColor = useMemo(() => themeColor[1] || "#ffffff", [themeColor]);
@@ -269,7 +274,7 @@ const PieChartComponent = ({
plugins: {
title: {
display: true,
text: title,
text: name,
font: chartFontStyle,
},
legend: {
@@ -284,7 +289,7 @@ const PieChartComponent = ({
// },
},
}),
[title, chartFontStyle]
[title, chartFontStyle, name]
);
// useEffect(() => {console.log(measurements);
@@ -343,6 +348,7 @@ const PieChartComponent = ({
if (response.status === 200) {
setmeasurements(response.data.Data.measurements)
setDuration(response.data.Data.duration)
setName(response.data.widgetName)
} else {
console.log("Unexpected response:", response);
}
@@ -361,7 +367,7 @@ const PieChartComponent = ({
fetchSavedInputes();
}
}
,[chartMeasurements, chartDuration])
,[chartMeasurements, chartDuration, widgetName])
return <Pie data={Object.keys(measurements).length > 0 ? chartData : defaultData} options={options} />;
};

View File

@@ -1,22 +1,64 @@
import { useMemo } from "react";
import { Line, PolarArea } from "react-chartjs-2";
import React, { useEffect, useMemo, useState } from "react";
import { PolarArea } from "react-chartjs-2";
import io from "socket.io-client";
import { useThemeStore } from "../../../../store/useThemeStore";
import useChartStore from "../../../../store/useChartStore";
import { useWidgetStore } from "../../../../store/useWidgetStore";
import axios from "axios";
interface ChartComponentProps {
id: string;
type: any;
title: string;
fontFamily?: string;
fontSize?: string;
fontWeight?: "Light" | "Regular" | "Bold";
data: any;
}
const PolarAreaGraphComponent = ({
id,
type,
title,
fontFamily,
fontSize,
fontWeight = "Regular",
}: ChartComponentProps) => {
// Memoize Font Weight Mapping
const { themeColor } = useThemeStore();
const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore();
const [measurements, setmeasurements] = useState<any>({});
const [duration, setDuration] = useState("1h")
const [name, setName] = useState("Widget")
const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({
labels: [],
datasets: [],
});
const { selectedChartId } = useWidgetStore();
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]
const defaultData = {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [
{
label: "Dataset",
data: [12, 19, 3, 5, 2, 3],
backgroundColor: ["#6f42c1"],
borderColor: "#b392f0",
borderWidth: 1,
},
],
};
useEffect(() => {
console.log("titleeeeeeeeeeeeeeeeeee",title);
},[])
// Memoize Theme Colors
const buttonActionColor = useMemo(() => themeColor[0] || "#5c87df", [themeColor]);
const buttonAbortColor = useMemo(() => themeColor[1] || "#ffffff", [themeColor]);
// Memoize Font Styling
const chartFontWeightMap = useMemo(
() => ({
Light: "lighter" as const,
@@ -26,19 +68,9 @@ const PolarAreaGraphComponent = ({
[]
);
// Parse and Memoize Font Size
const fontSizeValue = useMemo(
() => (fontSize ? parseInt(fontSize) : 12),
[fontSize]
);
const fontSizeValue = useMemo(() => (fontSize ? parseInt(fontSize) : 12), [fontSize]);
const fontWeightValue = useMemo(() => chartFontWeightMap[fontWeight], [fontWeight, chartFontWeightMap]);
// Determine and Memoize Font Weight
const fontWeightValue = useMemo(
() => chartFontWeightMap[fontWeight],
[fontWeight, chartFontWeightMap]
);
// Memoize Chart Font Style
const chartFontStyle = useMemo(
() => ({
family: fontFamily || "Arial",
@@ -48,6 +80,7 @@ const PolarAreaGraphComponent = ({
[fontFamily, fontSizeValue, fontWeightValue]
);
// Memoize Chart Options
const options = useMemo(
() => ({
responsive: true,
@@ -55,7 +88,7 @@ const PolarAreaGraphComponent = ({
plugins: {
title: {
display: true,
text: title,
text: name,
font: chartFontStyle,
},
legend: {
@@ -63,31 +96,94 @@ const PolarAreaGraphComponent = ({
},
},
scales: {
x: {
ticks: {
display: false, // This hides the x-axis labels
},
},
// x: {
// ticks: {
// display: true, // This hides the x-axis labels
// },
// },
},
}),
[title, chartFontStyle]
[title, chartFontStyle, name]
);
const chartData = {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [
{
label: "My First Dataset",
data: [65, 59, 80, 81, 56, 55, 40],
backgroundColor: "#6f42c1", // Updated to #6f42c1 (Purple)
borderColor: "#ffffff", // Keeping border color white
borderWidth: 2,
fill: false,
},
],
// useEffect(() => {console.log(measurements);
// },[measurements])
useEffect(() => {
if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return;
const socket = io(`http://${iotApiUrl}`);
const inputData = {
measurements,
duration,
interval: 1000,
};
return <PolarArea data={chartData} options={options} />;
const startStream = () => {
socket.emit("lineInput", inputData);
};
socket.on("connect", startStream);
socket.on("lineOutput", (response) => {
const responseData = response.data;
// Extract timestamps and values
const labels = responseData.time;
const datasets = Object.keys(measurements).map((key) => {
const measurement = measurements[key];
const datasetKey = `${measurement.name}.${measurement.fields}`;
return {
label: datasetKey,
data: responseData[datasetKey]?.values ?? [],
backgroundColor: "#6f42c1",
borderColor: "#b392f0",
borderWidth: 1,
};
});
setChartData({ labels, datasets });
});
return () => {
socket.off("lineOutput");
socket.emit("stop_stream"); // Stop streaming when component unmounts
socket.disconnect();
};
}, [measurements, duration, iotApiUrl]);
const fetchSavedInputes = async() => {
if (id !== "") {
try {
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${id}/${organization}`);
if (response.status === 200) {
setmeasurements(response.data.Data.measurements)
setDuration(response.data.Data.duration)
setName(response.data.widgetName)
} else {
console.log("Unexpected response:", response);
}
} catch (error) {
console.error("There was an error!", error);
}
}
}
useEffect(() => {
fetchSavedInputes();
}, []);
useEffect(() => {
if (selectedChartId?.id === id) {
fetchSavedInputes();
}
}
,[chartMeasurements, chartDuration, widgetName])
return <PolarArea data={Object.keys(measurements).length > 0 ? chartData : defaultData} options={options} />;
};
export default PolarAreaGraphComponent;

View File

@@ -0,0 +1,33 @@
import React from 'react'
type Props = {}
const FleetEfficiencyComponent = ({
object
}: any) => {
return (
<>
<h2 className="header">Fleet Efficiency</h2>
<div className="progressContainer">
<div className="progress">
<div className="barOverflow">
<div
className="bar"
style={{ transform: `rotate(${object.value}deg)` }}
></div>
</div>
</div>
</div>
<div className="scaleLabels">
<span>0%</span>
<div className="centerText">
<div className="percentage">{object.per}%</div>
<div className="status">Optimal</div>
</div>
<span>100%</span>
</div>
</>
)
}
export default FleetEfficiencyComponent

View File

@@ -0,0 +1,29 @@
import React from 'react'
import { WalletIcon } from '../../../icons/3dChartIcons'
import { useWidgetStore } from '../../../../store/useWidgetStore';
const TotalCardComponent = ({
object
}: any) => {
const { setSelectedChartId } =
useWidgetStore();
return (
<>
<div className="header-wrapper" onClick={() => {
setSelectedChartId(object.id)
}}>
<div className="header">{object.header}</div>
<div className="data-values">
<div className="value">{object.value}</div>
<div className="per">{object.per}</div>
</div>
</div>
<div className="icon">
<WalletIcon />
</div>
</>
)
}
export default TotalCardComponent

View File

@@ -0,0 +1,132 @@
import React, { useState } from 'react'
import { Line } from 'react-chartjs-2'
import axios from 'axios';
const WarehouseThroughputComponent = ({
object
}: any) => {
const [measurements, setmeasurements] = useState<any>({});
const [duration, setDuration] = useState("1h")
const lineGraphData = {
labels: [
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
], // Months of the year
datasets: [
{
label: "Throughput (units/month)",
data: [500, 400, 300, 450, 350, 250, 200, 300, 250, 150, 100, 150], // Example monthly data
borderColor: "#6f42c1", // Use the desired color for the line (purple)
backgroundColor: "rgba(111, 66, 193, 0.2)", // Use a semi-transparent purple for the fill
borderWidth: 2, // Line thickness
fill: true, // Enable fill for this dataset
pointRadius: 0, // Remove dots at each data point
tension: 0.5, // Smooth interpolation for the line
},
],
};
// Line graph options
const lineGraphOptions = {
responsive: true,
maintainAspectRatio: false, // Allow custom height/width adjustments
plugins: {
legend: {
display: false, // Hide legend
},
title: {
display: false, // No chart title needed
},
tooltip: {
callbacks: {
label: (context: any) => {
const value = context.parsed.y;
return `${value} units`; // Customize tooltip to display "units"
},
},
},
},
scales: {
x: {
grid: {
display: false, // Hide x-axis grid lines
},
ticks: {
maxRotation: 0, // Prevent label rotation
autoSkip: false, // Display all months
font: {
size: 8, // Adjust font size for readability
color: "#ffffff", // Light text color for labels
},
},
},
y: {
display: true, // Show y-axis
grid: {
drawBorder: false, // Remove border line
color: "rgba(255, 255, 255, 0.2)", // Light gray color for grid lines
borderDash: [5, 5], // Dotted line style (array defines dash and gap lengths)
},
ticks: {
font: {
size: 8, // Adjust font size for readability
color: "#ffffff", // Light text color for ticks
},
},
},
},
elements: {
line: {
tension: 0.5, // Smooth interpolation for the line
},
},
};
// const fetchSavedInputes = async() => {
// if (object.id !== "") {
// try {
// const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_LOCAL_BASE_URL}/api/v2/WidgetData/${id}/${organization}`);
// if (response.status === 200) {
// setmeasurements(response.data.Data.measurements)
// setDuration(response.data.Data.duration)
// setName(response.data.widgetName)
// } else {
// console.log("Unexpected response:", response);
// }
// } catch (error) {
// console.error("There was an error!", error);
// }
// }
// }
return (
<>
<div className="header">
<h2>Warehouse Throughput</h2>
<p>
<span>(+5) more</span> in 2025
</p>
</div>
<div className="lineGraph" style={{ height: "100%" }}>
<Line data={lineGraphData} options={lineGraphOptions} />
</div>
</>
)
}
export default WarehouseThroughputComponent

View File

@@ -9,6 +9,8 @@ import { retrieveGLTF, storeGLTF } from '../../../../utils/indexDB/idbUtils';
// import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi';
import { Socket } from 'socket.io-client';
import * as CONSTANTS from '../../../../types/world/worldConstants';
import { getAssetEventType } from '../../../../services/simulation/getAssetEventType';
import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi';
async function addAssetModel(
raycaster: THREE.Raycaster,
@@ -58,6 +60,7 @@ async function addAssetModel(
if (intersectPoint.y < 0) {
intersectPoint = new THREE.Vector3(intersectPoint.x, 0, intersectPoint.z);
}
console.log('selectedItem: ', selectedItem);
const cachedModel = THREE.Cache.get(selectedItem.id);
if (cachedModel) {
// console.log(`[Cache] Fetching ${selectedItem.name}`);
@@ -136,34 +139,69 @@ async function handleModelLoad(
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, },
rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z },
isLocked: false,
isVisible: true
};
const email = localStorage.getItem("email");
const organization = email ? email.split("@")[1].split(".")[0] : "default";
getAssetEventType(selectedItem.id, organization).then(async (res) => {
console.log('res: ', res);
if (res.type === "Conveyor") {
const pointUUIDs = res.points.map(() => THREE.MathUtils.generateUUID());
const eventData: Extract<Types.FloorItemType['eventData'], { type: 'Conveyor' }> = {
type: 'Conveyor',
points: res.points.map((point: any, index: number) => ({
uuid: pointUUIDs[index],
position: point.position as [number, number, number],
rotation: point.rotation as [number, number, number],
actions: [{
uuid: THREE.MathUtils.generateUUID(),
name: 'Action 1',
type: 'Inherit',
material: 'Inherit',
delay: 'Inherit',
spawnInterval: 'Inherit',
isUsed: false
}],
triggers: [],
connections: {
source: { pathUUID: model.uuid, pointUUID: pointUUIDs[index] },
targets: []
}
})),
speed: 'Inherit'
};
console.log('eventData: ', eventData);
newFloorItem.eventData = eventData;
}
setFloorItems((prevItems) => {
const updatedItems = [...(prevItems || []), newFloorItem];
localStorage.setItem("FloorItems", JSON.stringify(updatedItems));
return updatedItems;
});
const email = localStorage.getItem("email");
const organization = email ? email.split("@")[1].split(".")[0] : "default";
//REST
// API
// await setFloorItemApi(
// organization,
// newFloorItem.modeluuid,
// newFloorItem.modelname,
// newFloorItem.modelfileID,
// newFloorItem.position,
// { "x": model.rotation.x, "y": model.rotation.y, "z": model.rotation.z },
// newFloorItem.modelfileID!,
// { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z },
// false,
// true,
// newFloorItem.eventData
// );
//SOCKET
// SOCKET
const data = {
organization,
@@ -174,13 +212,15 @@ async function handleModelLoad(
rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z },
isLocked: false,
isVisible: true,
socketId: socket.id,
eventData: newFloorItem.eventData,
socketId: socket.id
};
socket.emit("v1:FloorItems:set", data);
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;

View File

@@ -60,6 +60,7 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject
};
getFloorItems(organization).then((data) => {
console.log('data: ', data);
const uniqueItems = (data as Types.FloorItems).filter((item, index, self) =>
index === self.findIndex((t) => t.modelfileID === item.modelfileID)
);
@@ -305,7 +306,10 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject
};
}, [deleteModels, transformMode, controls, selectedItem, state.camera, state.pointer, activeTool, activeModule]);
useEffect(() => {
console.log('floorItems: ', floorItems);
}, [floorItems])
useFrame(() => {
if (controls)

View File

@@ -81,7 +81,8 @@ export default function SocketResponses({
// console.log('data: ', data);
})
socket.on('FloorItemsUpdateResponse', async (data: any) => {
socket.on('model-asset:response:updates', async (data: any) => {
console.log('data: ', data);
if (socket.id === data.socketId) {
return
}

View File

@@ -94,7 +94,7 @@ const AssetPreview: React.FC<AssetPreviewProps> = ({
<div className="asset-details">
<div className="asset-name">{selectedCard.assetName}</div>
<div className="asset-description">
{`${selectedCard.assetName} is used in factories to improve efficiency and production speed It is designed to handle heavy workloads and perform repetitive tasks with precision. Many industries rely on this machine to manufacture products quickly and accurately. It reduces human effort and minimizes errors in the production process. Regular maintenance is required to keep the machine in good working condition.With advanced technology, this machine continues to enhance industrial operations and increase productivity.`}
{`${selectedCard.description}`}
</div>
<div className="asset-review">
<div className="asset-rating">

View File

@@ -14,6 +14,7 @@ interface ModelData {
thumbnail: string;
uploadDate: number;
_id: string;
price: number;
}
interface ModelsProps {
models: ModelData[];
@@ -50,7 +51,7 @@ const CardsContainer: React.FC<ModelsProps> = ({ models }) => {
key={assetDetail._id}
assetName={assetDetail?.filename}
uploadedOn={assetDetail.uploadDate}
price={36500}
price={assetDetail?.price}
rating={4.5}
views={800}
onSelectCard={handleCardSelect}

View File

@@ -17,6 +17,7 @@ interface ModelData {
thumbnail: string;
uploadDate: number;
_id: string;
price: number;
}
interface ModelsProps {
models: ModelData[];

View File

@@ -15,6 +15,7 @@ interface ModelData {
thumbnail: string;
uploadDate: number;
_id: string;
price: number;
}
const MarketPlace = () => {
const [models, setModels] = useState<ModelData[]>([]);
@@ -24,6 +25,7 @@ const MarketPlace = () => {
const filteredAssets = async () => {
try {
const filt = await getAssetImages("67d934ad0f42a1fdadb19aa6");
setModels(filt.items);
setFilteredModels(filt.items);
} catch {}

View File

@@ -42,6 +42,8 @@ const SelectionControls: React.FC = () => {
itemsGroupRef.current = itemsGroup;
let isSelecting = false;
let isRightClick = false;
let rightClickMoved = false;
let isCtrlSelecting = false;
const helper = new SelectionHelper(gl);
@@ -52,16 +54,23 @@ const SelectionControls: React.FC = () => {
}
const onPointerDown = (event: PointerEvent) => {
if (event.button !== 0) return
if (event.button === 2) {
isRightClick = true;
rightClickMoved = false;
} else if (event.button === 0) {
isSelecting = false;
isCtrlSelecting = event.ctrlKey;
if (event.ctrlKey && duplicatedObjects.length === 0) {
if (controls) (controls as any).enabled = false;
selectionBox.startPoint.set(pointer.x, pointer.y, 0);
}
}
};
const onPointerMove = (event: PointerEvent) => {
if (isRightClick) {
rightClickMoved = true;
}
isSelecting = true;
if (helper.isDown && event.ctrlKey && duplicatedObjects.length === 0 && isCtrlSelecting) {
selectionBox.endPoint.set(pointer.x, pointer.y, 0);
@@ -69,6 +78,14 @@ const SelectionControls: React.FC = () => {
};
const onPointerUp = (event: PointerEvent) => {
if (event.button === 2) {
isRightClick = false;
if (!rightClickMoved) {
clearSelection();
}
return;
}
if (isSelecting && isCtrlSelecting) {
isCtrlSelecting = false;
isSelecting = false;
@@ -94,10 +111,13 @@ const SelectionControls: React.FC = () => {
}
};
const onContextMenu = (event: MouseEvent) => {
event.preventDefault();
if (!rightClickMoved) {
clearSelection();
}
};
if (!toggleView && activeModule === "builder") {
helper.enabled = true;

View File

@@ -1,9 +1,16 @@
import * as THREE from 'three'
import { EffectComposer, N8AO, Outline } from '@react-three/postprocessing'
import { BlendFunction } from 'postprocessing'
import { useDeletableFloorItem, useSelectedActionSphere, useSelectedPath, useSelectedWallItem, useselectedFloorItem } from '../../../store/store';
import * as Types from '../../../types/world/worldTypes'
import * as CONSTANTS from '../../../types/world/worldConstants';
import * as THREE from "three";
import { EffectComposer, N8AO, Outline } from "@react-three/postprocessing";
import { BlendFunction } from "postprocessing";
import {
useDeletableFloorItem,
useSelectedActionSphere,
useSelectedPath,
useSelectedWallItem,
useselectedFloorItem,
} from "../../../store/store";
import * as Types from "../../../types/world/worldTypes";
import * as CONSTANTS from "../../../types/world/worldConstants";
import { useEffect } from "react";
export default function PostProcessing() {
const { deletableFloorItem, setDeletableFloorItem } = useDeletableFloorItem();
@@ -14,7 +21,7 @@ export default function PostProcessing() {
function flattenChildren(children: any[]) {
const allChildren: any[] = [];
children.forEach(child => {
children.forEach((child) => {
allChildren.push(child);
if (child.children && child.children.length > 0) {
allChildren.push(...flattenChildren(child.children));
@@ -26,8 +33,16 @@ export default function PostProcessing() {
return (
<>
<EffectComposer autoClear={false}>
<N8AO color="black" aoRadius={20} intensity={7} distanceFalloff={4} aoSamples={32} denoiseRadius={6} denoiseSamples={16} />
{deletableFloorItem &&
<N8AO
color="black"
aoRadius={20}
intensity={7}
distanceFalloff={4}
aoSamples={32}
denoiseRadius={6}
denoiseSamples={16}
/>
{deletableFloorItem && (
<Outline
selection={flattenChildren(deletableFloorItem.children)}
selectionLayer={10}
@@ -41,14 +56,12 @@ export default function PostProcessing() {
blur={true}
xRay={true}
/>
}
{selectedWallItem &&
)}
{selectedWallItem && (
<Outline
selection={
selectedWallItem.children[1].children[0].children.filter(
selection={selectedWallItem.children[1].children[0].children.filter(
(child: Types.Mesh) => child.name !== "CSG_REF"
)
}
)}
selectionLayer={10}
width={3000}
blendFunction={BlendFunction.ALPHA}
@@ -59,8 +72,9 @@ export default function PostProcessing() {
hiddenEdgeColor={CONSTANTS.outlineConfig.assetSelectColor}
blur={true}
xRay={true}
/>}
{selectedFloorItem &&
/>
)}
{selectedFloorItem && (
<Outline
selection={flattenChildren(selectedFloorItem.children)}
selectionLayer={10}
@@ -74,8 +88,8 @@ export default function PostProcessing() {
blur={true}
xRay={true}
/>
}
{selectedActionSphere &&
)}
{selectedActionSphere && (
<Outline
selection={[selectedActionSphere.point]}
selectionLayer={10}
@@ -89,8 +103,8 @@ export default function PostProcessing() {
blur={true}
xRay={true}
/>
}
{selectedPath &&
)}
{selectedPath && (
<Outline
selection={flattenChildren(selectedPath.group.children)}
selectionLayer={10}
@@ -104,8 +118,8 @@ export default function PostProcessing() {
blur={true}
xRay={true}
/>
}
)}
</EffectComposer>
</>
)
);
}

View File

@@ -358,7 +358,7 @@ export default function World() {
anglesnappedPoint={anglesnappedPoint}
/>
<DrieHtmlTemp itemsGroup={itemsGroup} />
{/* <DrieHtmlTemp itemsGroup={itemsGroup} /> */}
<Agv lines={lines} plane={plane} />
</>

View File

@@ -67,7 +67,7 @@ function Behaviour() {
point: {
uuid: pointUUID,
position: [pointPosition.x, pointPosition.y, pointPosition.z],
actions: { uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: 'Start', start: '', hitCount: 1, end: '', buffer: 0 },
actions: { uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: 'Start', start: {}, hitCount: 1, end: {}, buffer: 0 },
connections: { source: { pathUUID: item.modeluuid, pointUUID: pointUUID }, targets: [] },
speed: 2,
},

View File

@@ -96,24 +96,16 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
};
const existingTargets = path.point.connections.targets || [];
// Check if we're trying to add a connection to a Conveyor
// Check if target is a Conveyor
const toPath = simulationPaths.find(p => p.modeluuid === toPathUUID);
const isConnectingToConveyor = toPath?.type === 'Conveyor';
// Count existing connections
if (existingTargets.length >= 2) {
console.log("Vehicle can have maximum 2 connections");
if (toPath?.type !== 'Conveyor') {
console.log("Vehicle can only connect to Conveyors");
return path;
}
// Check if we already have a Conveyor connection and trying to add another
const hasConveyorConnection = existingTargets.some(target => {
const targetPath = simulationPaths.find(p => p.modeluuid === target.pathUUID);
return targetPath?.type === 'Conveyor';
});
if (hasConveyorConnection && isConnectingToConveyor) {
console.log("Vehicle can only have one connection to a Conveyor");
// Check if already has a connection
if (existingTargets.length >= 1) {
console.log("Vehicle can have only one connection");
return path;
}
@@ -141,24 +133,16 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
};
const existingTargets = path.point.connections.targets || [];
// Check if we're receiving a connection from a Conveyor
// Check if source is a Conveyor
const fromPath = simulationPaths.find(p => p.modeluuid === fromPathUUID);
const isConnectingFromConveyor = fromPath?.type === 'Conveyor';
// Count existing connections
if (existingTargets.length >= 2) {
console.log("Vehicle can have maximum 2 connections");
if (fromPath?.type !== 'Conveyor') {
console.log("Vehicle can only connect to Conveyors");
return path;
}
// Check if we already have a Conveyor connection and trying to add another
const hasConveyorConnection = existingTargets.some(target => {
const targetPath = simulationPaths.find(p => p.modeluuid === target.pathUUID);
return targetPath?.type === 'Conveyor';
});
if (hasConveyorConnection && isConnectingFromConveyor) {
console.log("Vehicle can only have one connection to a Conveyor");
// Check if already has a connection
if (existingTargets.length >= 1) {
console.log("Vehicle can have only one connection");
return path;
}
@@ -212,6 +196,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
drag = true;
}
};
const onContextMenu = (evt: MouseEvent) => {
evt.preventDefault();
if (drag || evt.button === 0) return;
@@ -282,7 +267,16 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
return;
}
// For Vehicles, skip the "already connected" check since they can have multiple connections
// For Vehicles, check if they're already connected to anything
if (intersected.userData.path.type === 'Vehicle') {
const vehicleConnections = intersected.userData.path.point.connections.targets.length;
if (vehicleConnections >= 1) {
console.log("Vehicle can only have one connection");
return;
}
}
// For non-Vehicle paths, check if already connected
if (intersected.userData.path.type !== 'Vehicle') {
const isAlreadyConnected = simulationPaths.some(path => {
if (path.type === 'Conveyor') {
@@ -300,48 +294,14 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
}
}
// Check vehicle connection limits
const checkVehicleConnections = (pathUUID: string) => {
const path = simulationPaths.find(p => p.modeluuid === pathUUID);
if (path?.type === 'Vehicle') {
return path.point.connections.targets.length >= 2;
}
return false;
};
if (firstSelected) {
// Check if either selected point is from a Vehicle with max connections
if (checkVehicleConnections(firstSelected.pathUUID) ||
checkVehicleConnections(pathUUID)) {
console.log("Vehicle already has maximum connections");
// Check if trying to connect Vehicle to non-Conveyor
if ((firstPath?.type === 'Vehicle' && secondPath?.type !== 'Conveyor') ||
(secondPath?.type === 'Vehicle' && firstPath?.type !== 'Conveyor')) {
console.log("Vehicle can only connect to Conveyors");
return;
}
// Check if we're trying to add a second Conveyor connection to a Vehicle
if (firstPath?.type === 'Vehicle' && secondPath?.type === 'Conveyor') {
const hasConveyorConnection = firstPath.point.connections.targets.some(target => {
const targetPath = simulationPaths.find(p => p.modeluuid === target.pathUUID);
return targetPath?.type === 'Conveyor';
});
if (hasConveyorConnection) {
console.log("Vehicle can only have one connection to a Conveyor");
return;
}
}
if (secondPath?.type === 'Vehicle' && firstPath?.type === 'Conveyor') {
const hasConveyorConnection = secondPath.point.connections.targets.some(target => {
const targetPath = simulationPaths.find(p => p.modeluuid === target.pathUUID);
return targetPath?.type === 'Conveyor';
});
if (hasConveyorConnection) {
console.log("Vehicle can only have one connection to a Conveyor");
return;
}
}
// Prevent same-path connections
if (firstSelected.pathUUID === pathUUID) {
console.log("Cannot connect spheres on the same path.");
@@ -478,28 +438,19 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
return false;
});
// Check vehicle connection limits
// Check vehicle connection rules
const isVehicleAtMaxConnections = pathData.type === 'Vehicle' &&
pathData.point.connections.targets.length >= 2;
const isVehicleConveyorConflict =
(firstPath?.type === 'Vehicle' && secondPath?.type === 'Conveyor' &&
firstPath.point.connections.targets.some(t => {
const targetPath = simulationPaths.find(p => p.modeluuid === t.pathUUID);
return targetPath?.type === 'Conveyor';
})) ||
(secondPath?.type === 'Vehicle' && firstPath?.type === 'Conveyor' &&
secondPath.point.connections.targets.some(t => {
const targetPath = simulationPaths.find(p => p.modeluuid === t.pathUUID);
return targetPath?.type === 'Conveyor';
}));
pathData.point.connections.targets.length >= 1;
const isVehicleConnectingToNonConveyor =
(firstPath?.type === 'Vehicle' && secondPath?.type !== 'Conveyor') ||
(secondPath?.type === 'Vehicle' && firstPath?.type !== 'Conveyor');
if (
!isDuplicateConnection &&
!isVehicleToVehicle &&
!isNonVehicleAlreadyConnected &&
!isVehicleAtMaxConnections &&
!isVehicleConveyorConflict &&
!isVehicleConnectingToNonConveyor &&
firstSelected.sphereUUID !== sphereUUID &&
firstSelected.pathUUID !== pathUUID &&
(firstSelected.isCorner || isConnectable)

View File

@@ -1,8 +1,8 @@
import * as THREE from 'three';
import * as Types from '../../../types/world/worldTypes';
import { useRef, useState, useEffect } from 'react';
import { useRef, useState, useEffect, useMemo } from 'react';
import { Sphere, TransformControls } from '@react-three/drei';
import { useIsConnecting, useRenderDistance, useSelectedActionSphere, useSelectedPath, useSimulationPaths } from '../../../store/store';
import { useEditingPoint, useEyeDropMode, useIsConnecting, usePreviewPosition, useRenderDistance, useSelectedActionSphere, useSelectedPath, useSimulationPaths } from '../../../store/store';
import { useFrame, useThree } from '@react-three/fiber';
import { useSubModuleStore } from '../../../store/useModuleStore';
@@ -10,13 +10,18 @@ function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject
const { renderDistance } = useRenderDistance();
const { setSubModule } = useSubModuleStore();
const { setSelectedActionSphere, selectedActionSphere } = useSelectedActionSphere();
const { eyeDropMode, setEyeDropMode } = useEyeDropMode();
const { editingPoint, setEditingPoint } = useEditingPoint();
const { previewPosition, setPreviewPosition } = usePreviewPosition();
const { raycaster, camera, pointer, gl } = useThree();
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
const { setSelectedPath } = useSelectedPath();
const { simulationPaths, setSimulationPaths } = useSimulationPaths();
const { isConnecting } = useIsConnecting();
const { camera } = useThree();
const groupRefs = useRef<{ [key: string]: THREE.Group }>({});
const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({});
const isMovingRef = useRef(false);
const transformRef = useRef<any>(null);
const [transformMode, setTransformMode] = useState<'translate' | 'rotate' | null>(null);
@@ -77,6 +82,83 @@ function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject
setSimulationPaths(updatedPaths);
};
useFrame(() => {
if (eyeDropMode) {
raycaster.setFromCamera(pointer, camera);
const intersectionPoint = new THREE.Vector3();
const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
if (point) {
setPreviewPosition({ x: point.x, y: point.z });
}
} else {
setPreviewPosition(null);
}
});
useEffect(() => {
if (!camera) return;
const canvasElement = gl.domElement;
canvasElement.tabIndex = 0;
const onPointerDown = () => {
isMovingRef.current = false;
};
const onPointerMove = () => {
isMovingRef.current = true;
};
const onPointerUp = (event: PointerEvent) => {
if (!isMovingRef.current && eyeDropMode && event.button === 0 && previewPosition) {
event.preventDefault();
if (editingPoint) {
handlePointUpdate(editingPoint, previewPosition.x, previewPosition.y);
setEditingPoint(null);
setEyeDropMode(false);
}
}
};
if (eyeDropMode) {
canvasElement.addEventListener("pointerdown", onPointerDown);
canvasElement.addEventListener("pointermove", onPointerMove);
canvasElement.addEventListener("pointerup", onPointerUp);
}
return () => {
canvasElement.removeEventListener("pointerdown", onPointerDown);
canvasElement.removeEventListener("pointermove", onPointerMove);
canvasElement.removeEventListener("pointerup", onPointerUp);
};
}, [eyeDropMode, editingPoint, previewPosition]);
const handlePointUpdate = (pointType: 'start' | 'end', x: number, z: number) => {
if (!selectedActionSphere?.point?.uuid) return;
const updatedPaths = simulationPaths.map((path) => {
if (path.type === "Vehicle" && path.point.uuid === selectedActionSphere.point.uuid) {
return {
...path,
point: {
...path.point,
actions: {
...path.point.actions,
[pointType]: {
...path.point.actions[pointType],
x: x,
y: z
}
}
}
};
}
return path;
});
setSimulationPaths(updatedPaths);
};
return (
<group name='simulation-simulationPaths-group' ref={pathsGroupRef}>
@@ -92,7 +174,7 @@ function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject
position={path.assetPosition}
rotation={path.assetRotation}
onClick={(e) => {
if (isConnecting) return;
if (isConnecting || eyeDropMode) return;
e.stopPropagation();
setSelectedPath({ path, group: groupRefs.current[path.modeluuid] });
setSelectedActionSphere(null);
@@ -100,6 +182,7 @@ function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject
setSubModule('mechanics');
}}
onPointerMissed={() => {
if (eyeDropMode) return;
setSelectedPath(null);
setSubModule('properties');
}}
@@ -113,7 +196,7 @@ function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject
name='events-sphere'
ref={el => (sphereRefs.current[point.uuid] = el!)}
onClick={(e) => {
if (isConnecting) return;
if (isConnecting || eyeDropMode) return;
e.stopPropagation();
setSelectedActionSphere({
path,
@@ -124,6 +207,7 @@ function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject
}}
userData={{ point, path }}
onPointerMissed={() => {
if (eyeDropMode) return;
setSubModule('properties');
setSelectedActionSphere(null);
}}
@@ -155,7 +239,7 @@ function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject
ref={el => (groupRefs.current[path.modeluuid] = el!)}
position={path.assetPosition}
onClick={(e) => {
if (isConnecting) return;
if (isConnecting || eyeDropMode) return;
e.stopPropagation();
setSelectedPath({ path, group: groupRefs.current[path.modeluuid] });
setSelectedActionSphere(null);
@@ -163,6 +247,7 @@ function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject
setSubModule('mechanics');
}}
onPointerMissed={() => {
if (eyeDropMode) return;
setSelectedPath(null);
setSubModule('properties');
}}
@@ -175,7 +260,7 @@ function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject
name='events-sphere'
ref={el => (sphereRefs.current[path.point.uuid] = el!)}
onClick={(e) => {
if (isConnecting) return;
if (isConnecting || eyeDropMode) return;
e.stopPropagation();
setSelectedActionSphere({
path,
@@ -186,6 +271,7 @@ function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject
}}
userData={{ point: path.point, path }}
onPointerMissed={() => {
if (eyeDropMode) return;
setSubModule('properties');
setSelectedActionSphere(null);
}}

View File

@@ -1,6 +1,5 @@
import { useState, useEffect, useRef } from 'react';
import { useState, useEffect, useRef, useMemo } from 'react';
import { useSelectedActionSphere, useSelectedPath, useSimulationPaths } from '../../store/store';
import { useThree } from '@react-three/fiber';
import * as THREE from 'three';
import Behaviour from './behaviour/behaviour';
import PathCreation from './path/pathCreation';
@@ -14,7 +13,6 @@ function Simulation() {
const [processes, setProcesses] = useState([]);
useEffect(() => {
console.log('simulationPaths: ', simulationPaths);
}, [simulationPaths]);
// useEffect(() => {
@@ -29,9 +27,10 @@ function Simulation() {
// }
// }, [selectedPath]);
return (
<>
<Behaviour/>
<Behaviour />
{activeModule === 'simulation' && (
<>
<PathCreation pathsGroupRef={pathsGroupRef} />

View File

@@ -1,74 +1,89 @@
import { saveTemplateApi } from "../../services/realTimeVisulization/zoneData/saveTempleteApi";
import { Template } from "../../store/useTemplateStore";
import { captureVisualization } from "./captureVisualization";
type HandleSaveTemplateProps = {
addTemplate: (template: Template) => void;
floatingWidget: []; // Updated type from `[]` to `any[]` for clarity
widgets3D: []; // Updated type from `[]` to `any[]` for clarity
selectedZone: {
panelOrder: string[]; // Adjust the type based on actual data structure
widgets: any[]; // Replace `any` with the actual widget type
panelOrder: string[];
widgets: any[];
};
templates?: Template[];
};
// Generate a unique ID (placeholder function)
// Generate a unique ID
const generateUniqueId = (): string => {
return `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
};
// Refactored function
export const handleSaveTemplate = async ({
addTemplate,
floatingWidget,
widgets3D,
selectedZone,
templates = [],
}: HandleSaveTemplateProps): Promise<void> => {
try {
// Check if the selected zone has any widgets
if (!selectedZone.widgets || selectedZone.widgets.length === 0) {
console.warn("Cannot save an empty template.");
console.warn("No widgets found in the selected zone.");
return;
}
// Check if the template already exists
const isDuplicate = templates.some((template) => {
const isSamePanelOrder =
const isDuplicate = templates.some(
(template) =>
JSON.stringify(template.panelOrder) ===
JSON.stringify(selectedZone.panelOrder);
const isSameWidgets =
JSON.stringify(selectedZone.panelOrder) &&
JSON.stringify(template.widgets) ===
JSON.stringify(selectedZone.widgets);
return isSamePanelOrder && isSameWidgets;
});
JSON.stringify(selectedZone.widgets)
);
if (isDuplicate) {
console.warn("This template already exists.");
return;
}
// Capture visualization snapshot
const snapshot = await captureVisualization();
if (!snapshot) {
console.error("Failed to capture visualization snapshot.");
return;
}
console.log("snapshot: ", snapshot);
// if (!snapshot) {
// return;
// }
// Create a new template
const newTemplate: Template = {
id: generateUniqueId(),
name: `Template ${Date.now()}`,
name: `Template ${new Date().toISOString()}`, // Better name formatting
panelOrder: selectedZone.panelOrder,
widgets: selectedZone.widgets,
snapshot,
floatingWidget,
widgets3D,
};
console.log("Saving template:", newTemplate);
// Extract organization from email
const email = localStorage.getItem("email") || "";
const organization = email.includes("@")
? email.split("@")[1]?.split(".")[0]
: "";
if (!organization) {
console.error("Organization could not be determined from email.");
return;
}
// Save the template
try {
const response = await saveTemplateApi(organization, newTemplate);
console.log("Save API Response:", response);
// Add template only if API call succeeds
addTemplate(newTemplate);
} catch (error) {
console.error("Failed to add template:", error);
} catch (apiError) {
console.error("Error saving template to API:", apiError);
}
} catch (error) {
console.error("Failed to save template:", error);
console.error("Error in handleSaveTemplate:", error);
}
};

View File

@@ -37,8 +37,8 @@ const Project: React.FC = () => {
setZones([]);
const email = localStorage.getItem("email");
if (email) {
useSocketStore.getState().initializeSocket(email);
const Organization = email!.split("@")[1].split(".")[0];
useSocketStore.getState().initializeSocket(email, Organization);
const name = localStorage.getItem("userName");
if (Organization && name) {
setOrganization(Organization);

View File

@@ -0,0 +1,19 @@
let BackEnd_url = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
export const getCategoryAsset = async (categoryName: any) => {
try {
const response = await fetch(
`${BackEnd_url}/api/v2/getCatagoryAssets/${categoryName}`,
{
method: "GET",
headers: {
"Content-Type": "application/json",
},
}
);
const result = await response.json();
return result;
} catch (error: any) {
throw new Error(error.message);
}
};

View File

@@ -1,13 +1,28 @@
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
let url_Backend_dwinzo = `http://${process.env.REACT_APP_TEST}`;
export const setFloorItemApi = async (organization: string, modeluuid: string, modelname: string, position: Object, rotation: Object, modelfileID: string, isLocked: boolean, isVisible: boolean) => {
export const setFloorItemApi = async (
organization: string,
modeluuid: string,
modelname: string,
modelfileID: string,
position: Object,
rotation: Object,
isLocked: boolean,
isVisible: boolean,
eventData?: any
) => {
try {
const response = await fetch(`${url_Backend_dwinzo}/api/v1/setFloorItems`, {
const body: any = { organization, modeluuid, modelname, position, rotation, modelfileID, isLocked, isVisible };
if (eventData) {
body.eventData = eventData;
}
const response = await fetch(`${url_Backend_dwinzo}/api/v2/setasset`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ organization, modeluuid, modelname, position, rotation, modelfileID, isLocked, isVisible }),
body: JSON.stringify(body),
});
if (!response.ok) {
@@ -15,6 +30,7 @@ export const setFloorItemApi = async (organization: string, modeluuid: string, m
}
const result = await response.json();
console.log('result: ', result);
return result;
} catch (error) {
if (error instanceof Error) {

View File

@@ -0,0 +1,36 @@
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
// let url_Backend_dwinzo = `http://192.168.0.102:5000`;
export const adding3dWidgets = async (
zoneId: string,
organization: string,
widget: {}
) => {
console.log('widget: ', widget);
console.log('organization: ', organization);
console.log('zoneId: ', zoneId);
try {
const response = await fetch(
`${url_Backend_dwinzo}/api/v2/3dwidget/save`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ organization, zoneId, widget }),
}
);
if (!response.ok) {
throw new Error("Failed to add 3dwidget in the zone");
}
const result = await response.json();
return result;
} catch (error) {
if (error instanceof Error) {
throw new Error(error.message);
} else {
throw new Error("An unknown error occurred");
}
}
};

View File

@@ -1,10 +1,14 @@
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
// let url_Backend_dwinzo = `http://192.168.0.102:5000`;
export const addingFloatingWidgets = async (
zoneId: string,
organization: string,
widget: {}
) => {
console.log('organization: ', organization);
console.log('widget: ', widget);
console.log('zoneId: ', zoneId);
try {
const response = await fetch(
`${url_Backend_dwinzo}/api/v2/floatwidget/save`,

View File

@@ -0,0 +1,35 @@
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
// let url_Backend_dwinzo = `http://192.168.0.102:5000`;
export const deleteFloatingWidgetApi = async (
floatWidgetID: string,
organization: string
) => {
console.log('organization: ', organization);
console.log('floatWidgetID: ', floatWidgetID);
try {
const response = await fetch(
`${url_Backend_dwinzo}/api/v2/floatwidget/delete`,
{
method: "PATCH",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ organization, floatWidgetID }),
}
);
if (!response.ok) {
throw new Error("Failed to delete floating widget in the zone");
}
const result = await response.json();
return result;
} catch (error) {
if (error instanceof Error) {
throw new Error(error.message);
} else {
throw new Error("An unknown error occurred");
}
}
};

View File

@@ -0,0 +1,32 @@
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
// let url_Backend_dwinzo = `http://192.168.0.102:5000`;
export const deleteTemplateApi = async (
templateID: string,
organization?: string
) => {
try {
const response = await fetch(
`${url_Backend_dwinzo}/api/v2/TemplateDelete/${templateID}/${organization}`,
{
method: "PATCH",
headers: {
"Content-Type": "application/json",
},
}
);
if (!response.ok) {
throw new Error("Failed to delete template ");
}
const result = await response.json();
return result;
} catch (error) {
if (error instanceof Error) {
throw new Error(error.message);
} else {
throw new Error("An unknown error occurred");
}
}
};

View File

@@ -0,0 +1,25 @@
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
// let url_Backend_dwinzo = `http://192.168.0.102:5000`;
export const get3dWidgetZoneData = async (
ZoneId?: string,
organization?: string
) => {
try {
const response = await fetch(
`${url_Backend_dwinzo}/api/v2/3dwidgetData/${ZoneId}/${organization}`,
{
method: "GET",
headers: {
"Content-Type": "application/json",
},
}
);
if (!response.ok) {
throw new Error("Failed to fetch Zone3dWidgetData");
}
return await response.json();
} catch (error: any) {
throw new Error(error.message);
}
};

View File

@@ -1,4 +1,5 @@
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
// let url_Backend_dwinzo = `http://192.168.0.102:5000`;
export const getSelect2dZoneData = async (
ZoneId?: string,

View File

@@ -0,0 +1,23 @@
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
// let url_Backend_dwinzo = `http://192.168.0.102:5000`;
export const getTemplateData = async (organization?: string) => {
try {
const response = await fetch(
`${url_Backend_dwinzo}/api/v2/templateData/${organization}`,
{
method: "GET",
headers: {
"Content-Type": "application/json",
},
}
);
if (!response.ok) {
throw new Error("Failed to fetch ZoneFloatingData");
}
return await response.json();
} catch (error: any) {
throw new Error(error.message);
}
};

View File

@@ -1,4 +1,5 @@
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
// let url_Backend_dwinzo = `http://192.168.0.102:5000`;
export const getZoneData = async (zoneId: string, organization: string) => {
console.log("organization: ", organization);
@@ -14,7 +15,6 @@ export const getZoneData = async (zoneId: string, organization: string) => {
}
);
if (!response.ok) {
throw new Error("Failed to fetch zoneData");
}

View File

@@ -0,0 +1,33 @@
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
// let url_Backend_dwinzo = `http://192.168.0.102:5000`;
export const loadTempleteApi = async (
templateID: string,
zoneId: string,
organization: string
) => {
try {
const response = await fetch(
`${url_Backend_dwinzo}/api/v2/TemplatetoZone`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ organization, zoneId, templateID }),
}
);
if (!response.ok) {
throw new Error("Failed to add 3dwidget in the zone");
}
const result = await response.json();
return result;
} catch (error) {
if (error instanceof Error) {
throw new Error(error.message);
} else {
throw new Error("An unknown error occurred");
}
}
};

View File

@@ -0,0 +1,28 @@
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
// let url_Backend_dwinzo = `http://192.168.0.102:5000`;
export const saveTemplateApi = async (organization: string, template: {}) => {
console.log('template: ', template);
try {
const response = await fetch(`${url_Backend_dwinzo}/api/v2/template/save`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ organization, template }),
});
if (!response.ok) {
throw new Error("Failed to save template zone");
}
const result = await response.json();
console.log('result: ', result);
return result;
} catch (error) {
if (error instanceof Error) {
throw new Error(error.message);
} else {
throw new Error("An unknown error occurred");
}
}
};

View File

@@ -0,0 +1,8 @@
import { create } from "zustand";
const useFloatingDataStore = create((set) => ({
floatingdata: [], // Initial state
setfloatingadata: (newData: []) => set({ floatingdata: newData }), // Setter function
}));
export default useFloatingDataStore;

View File

@@ -0,0 +1,25 @@
let url_Backend_dwinzo = `http://${process.env.REACT_APP_TEST}`;
export const getAssetEventType = async (modelId: string, organization: string) => {
try {
const response = await fetch(`${url_Backend_dwinzo}/api/v2/pointData/${modelId}/${organization}`, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
if (!response.ok) {
throw new Error("Failed to fetch model event type");
}
const result = await response.json();
return result;
} catch (error) {
if (error instanceof Error) {
throw new Error(error.message);
} else {
throw new Error("An unknown error occurred");
}
}
};

View File

@@ -5,19 +5,16 @@ import { io } from "socket.io-client";
export const useSocketStore = create<any>((set: any, get: any) => ({
socket: null,
initializeSocket: (email: any) => {
initializeSocket: (email: string, organization: string) => {
const existingSocket = get().socket;
if (existingSocket) {
return;
}
const socket = io(
`http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/`,
{
const socket = io(`http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Builder`, {
reconnection: false,
auth: { email },
}
);
auth: { email, organization },
});
set({ socket });
},
@@ -348,10 +345,30 @@ export const useStartSimulation = create<any>((set: any) => ({
startSimulation: false,
setStartSimulation: (x: any) => set({ startSimulation: x }),
}));
export const useEyeDropMode = create<any>((set: any) => ({
eyeDropMode: false,
setEyeDropMode: (x: any) => set({ eyeDropMode: x }),
}));
export const useEditingPoint = create<any>((set: any) => ({
editingPoint: false,
setEditingPoint: (x: any) => set({ editingPoint: x }),
}));
export const usePreviewPosition = create<{
previewPosition: { x: number; y: number } | null;
setPreviewPosition: (position: { x: number; y: number } | null) => void;
}>((set) => ({
previewPosition: null,
setPreviewPosition: (position) => set({ previewPosition: position }),
}));
export const usezoneTarget = create<any>((set: any) => ({
zoneTarget: [],
setZoneTarget: (x: any) => set({ zoneTarget: x }),
}));
export const usezonePosition = create<any>((set: any) => ({
zonePosition: [],
setZonePosition: (x: any) => set({ zonePosition: x }),
@@ -371,7 +388,9 @@ export const useAsset3dWidget = create<any>((set: any) => ({
widgetSelect: "",
setWidgetSelect: (x: any) => set({ widgetSelect: x }),
}));
export const useWidgetSubOption = create<any>((set: any) => ({
widgetSubOption: "2D",
setWidgetSubOption: (x: any) => set({ widgetSubOption: x }),
}));

View File

@@ -9,20 +9,26 @@ interface MeasurementStore {
measurements: Record<string, Measurement>; // Change array to Record<string, Measurement>
interval: number;
duration: string;
name: string;
setMeasurements: (newMeasurements: Record<string, Measurement>) => void;
updateDuration: (newDuration: string) => void;
updateName: (newName: string) => void;
}
const useChartStore = create<MeasurementStore>((set) => ({
measurements: {}, // Initialize as an empty object
interval: 1000,
duration: "1h",
name:'',
setMeasurements: (newMeasurements) =>
set(() => ({ measurements: newMeasurements })),
updateDuration: (newDuration) =>
set(() => ({ duration: newDuration })),
updateName: (newName) =>
set(() => ({ duration: newName })),
}));
export default useChartStore;

View File

@@ -1,4 +1,5 @@
import { create } from "zustand";
import { addingFloatingWidgets } from "../services/realTimeVisulization/zoneData/addFloatingWidgets";
type DroppedObject = {
className: string;
@@ -35,6 +36,8 @@ type DroppedObjectsState = {
bottom: number | "auto";
}
) => void;
deleteObject: (zoneName: string, index: number) => void; // Add this line
duplicateObject: (zoneName: string, index: number) => void; // Add this line
};
export const useDroppedObjectsStore = create<DroppedObjectsState>((set) => ({
@@ -73,6 +76,72 @@ export const useDroppedObjectsStore = create<DroppedObjectsState>((set) => ({
},
};
}),
deleteObject: (zoneName: string, index: number) =>
set((state) => {
const zone = state.zones[zoneName];
if (!zone) return state;
return {
zones: {
[zoneName]: {
...zone,
objects: zone.objects.filter((_, i) => i !== index), // Remove object at the given index
},
},
};
}),
duplicateObject: async (zoneName: string, index: number) => {
const state = useDroppedObjectsStore.getState(); // Get the current state
const zone = state.zones[zoneName];
if (!zone) return;
const originalObject = zone.objects[index];
if (!originalObject) return;
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0];
// Create a shallow copy of the object with a unique ID and slightly adjusted position
const duplicatedObject: DroppedObject = {
...originalObject,
id: `${originalObject.id}-copy-${Date.now()}`, // Unique ID
position: {
...originalObject.position,
top:
typeof originalObject.position.top === "number"
? originalObject.position.top + 20 // Offset vertically
: originalObject.position.top,
left:
typeof originalObject.position.left === "number"
? originalObject.position.left + 20 // Offset horizontally
: originalObject.position.left,
},
};
console.log("zone: ", zone.zoneId);
console.log("duplicatedObject: ", duplicatedObject);
// Make async API call outside of Zustand set function
// let response = await addingFloatingWidgets(
// zone.zoneId,
// organization,
// duplicatedObject
// );
// if (response.message === "FloatWidget created successfully") {
// Update the state inside `set`
useDroppedObjectsStore.setState((state) => ({
zones: {
...state.zones,
[zoneName]: {
...state.zones[zoneName],
objects: [...state.zones[zoneName].objects, duplicatedObject], // Append duplicated object
},
},
}));
// }
},
}));
export interface DroppedObjects {
@@ -90,3 +159,13 @@ export interface Zones {
zoneId: string;
objects: DroppedObject[];
}
export const use3DWidget = create<any>((set: any) => ({
widgets3D: [],
setWidgets3D: (x: any) => set({ widgets3D: x }),
}));
export const useFloatingWidget = create<any>((set: any) => ({
floatingWidget: [],
setFloatingWidget: (x: any) => set({ floatingWidget: x }),
}));

View File

@@ -1,7 +1,5 @@
import { create } from "zustand";
// type Side = "top" | "bottom" | "left" | "right";
export interface Widget {
id: string;
type: string;
@@ -15,21 +13,34 @@ export interface Template {
name: string;
panelOrder: string[];
widgets: Widget[];
snapshot?: string | null; // Add an optional image property (base64)
floatingWidget: any[]; // Fixed empty array type
widgets3D: any[]; // Fixed empty array type
snapshot?: string | null;
}
interface TemplateStore {
templates: Template[];
addTemplate: (template: Template) => void;
setTemplates: (templates: Template[]) => void; // Changed from `setTemplate`
removeTemplate: (id: string) => void;
}
export const useTemplateStore = create<TemplateStore>((set) => ({
templates: [],
// Add a new template to the list
addTemplate: (template) =>
set((state) => ({
templates: [...state.templates, template],
})),
// Set (replace) the templates list with a new array
setTemplates: (templates) =>
set(() => ({
templates, // Ensures no duplication
})),
// Remove a template by ID
removeTemplate: (id) =>
set((state) => ({
templates: state.templates.filter((t) => t.id !== id),
@@ -37,3 +48,4 @@ export const useTemplateStore = create<TemplateStore>((set) => ({
}));
export default useTemplateStore;

View File

@@ -33,7 +33,7 @@ interface WidgetStore {
setDraggedAsset: (asset: Widget | null) => void; // Setter for draggedAsset
addWidget: (widget: Widget) => void; // Add a new widget
setWidgets: (widgets: Widget[]) => void; // Replace the entire widgets array
setSelectedChartId: (widget: Widget | null) => void; // Set the selected chart/widget
setSelectedChartId: (widget: any | null) => void; // Set the selected chart/widget
}
// Create the store with Zustand

View File

@@ -318,6 +318,25 @@
.sidebar-right-content-container {
.dataSideBar {
.inputs-wrapper {
.datas {
.input-value {
padding: 5px 10px;
}
.input-value,
.rename-input {
margin-right: 24px;
width: 170px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
width: 100%;
height: 100%;
display: flex;
@@ -991,11 +1010,9 @@
top: 50%;
right: -10px;
transform: translate(0, -50%);
background: linear-gradient(
144.19deg,
background: linear-gradient(144.19deg,
#f1e7cd 16.62%,
#fffaef 85.81%
);
#fffaef 85.81%);
}
.category-image {
@@ -1037,10 +1054,14 @@
}
.asset-image {
height: 100%;
width: 100%;
position: absolute;
top: 50%;
right: 5px;
transform: translate(0, -50%);
// top: 50%;
// right: 5px;
// transform: translate(0, -50%);
top: 0;
left: 0;
z-index: 2;
}
}

View File

@@ -543,3 +543,144 @@
background-color: #007bff;
color: white;
}
.floating-wrapper {
.icon {
width: 25px !important;
height: 25px !important;
background-color: transparent;
}
.kebab {
width: 30px;
height: 30px;
position: absolute !important;
top: 0px;
right: 0px;
z-index: 10;
cursor: pointer;
@include flex-center;
}
.kebab-options {
position: absolute;
top: 18px;
right: 5px;
transform: translate(0px, 0);
background-color: var(--background-color);
z-index: 10;
display: flex;
flex-direction: column;
gap: 6px;
border-radius: 4px;
box-shadow: var(--box-shadow-medium);
.btn {
display: flex;
gap: 6px;
align-items: center;
padding: 5px 10px;
color: var(--text-color);
cursor: pointer;
&:hover {
.label {
color: var(--accent-color);
}
}
&:hover {
background-color: var(--highlight-accent-color);
width: 100%;
svg {
&:first-child {
fill: var(--accent-color);
}
&:last-child {
fill: auto;
stroke: var(--accent-color);
}
}
}
}
.dublicate {
cursor: not-allowed;
}
}
}
.distance-line {
position: absolute;
border-style: dashed;
border-color: var(--accent-color); /* Green color for visibility */
border-width: 1px;
pointer-events: none; /* Ensure lins don't interfere with dragging */
z-index: 10000;
}
/* Label styles for displaying distance values */
.distance-label {
position: absolute;
background-color: var(--accent-color);
color: white;
font-size: 12px;
padding: 2px 6px;
border-radius: 3px;
white-space: nowrap;
transform: translate(-50%, -50%); /* Center the label */
}
/* Specific styles for each type of line */
/* Top distance line */
.distance-line.top {
border-bottom: none; /* Remove bottom border for a single line */
width: 2px; /* Thin vertical line */
}
.distance-line.top .distance-label {
top: -10px; /* Position label above the line */
left: 50%; /* Center horizontally */
}
/* Bottom distance line */
.distance-line.bottom {
border-top: none; /* Remove top border for a single line */
width: 2px; /* Thin vertical line */
}
.distance-line.bottom .distance-label {
bottom: -10px; /* Position label below the line */
left: 50%; /* Center horizontally */
}
/* Left distance line */
.distance-line.left {
border-right: none; /* Remove right border for a single line */
height: 2px; /* Thin horizontal line */
}
.distance-line.left .distance-label {
left: -10px; /* Position label to the left of the line */
top: 50%; /* Center vertically */
}
/* Right distance line */
.distance-line.right {
border-left: none; /* Remove left border for a single line */
height: 2px; /* Thin horizontal line */
}
.distance-line.right .distance-label {
right: -10px; /* Position label to the right of the line */
top: 50%; /* Center vertically */
}

View File

@@ -58,7 +58,6 @@ export type ThreeDimension = {
rightMouse: number;
};
export type GridConfig = {
size: number;
divisions: number;
@@ -102,12 +101,12 @@ export type SkyConfig = {
mieCoefficient: number;
mieDirectionalG: number;
skyDistance: number;
}
};
export type AssetConfig = {
defaultScaleBeforeGsap: [number, number, number];
defaultScaleAfterGsap: [number, number, number];
}
};
export type PointConfig = {
defaultInnerColor: string;
@@ -143,7 +142,7 @@ export type WallConfig = {
defaultColor: string;
height: number;
width: number;
}
};
export type FloorConfig = {
defaultColor: string;
@@ -154,7 +153,7 @@ export type FloorConfig = {
export type RoofConfig = {
defaultColor: string;
height: number;
}
};
export type AisleConfig = {
width: number;
@@ -170,15 +169,17 @@ export type ZoneConfig = {
export type ColumnConfig = {
defaultColor: string;
}
};
export type OutlineConfig = {
assetSelectColor: number;
assetDeleteColor: number;
}
};
export type DistanceConfig = {
minDistance: number;
maxDistance: number;
};
export const firstPersonControls: Controls = {
azimuthRotateSpeed: 0.3, // Speed of rotation around the azimuth axis
@@ -187,9 +188,7 @@ export const firstPersonControls: Controls = {
minDistance: 0, // Minimum distance from the target
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
@@ -264,7 +263,6 @@ export const planeConfig: PlaneConfig = {
export const shadowConfig: ShadowConfig = {
shadowOffset: 50, // Offset of the shadow
shadowmapSizewidth: 1024, // Width of the shadow map
shadowmapSizeheight: 1024, // Height of the shadow map
// shadowmapSizewidth: 8192, // Width of the shadow map
@@ -277,12 +275,10 @@ export const shadowConfig: ShadowConfig = {
shadowcameraright: 30, // Right plane of the shadow camera
shadowbias: -0.001, // Bias of the shadow
shadownormalBias: 0.02, // Normal bias of the shadow
shadowMaterialPosition: [0, 0.01, 0], // Position of the shadow material
shadowMaterialRotation: [-Math.PI / 2, 0, 0], // Rotation of the shadow material
shadowMaterialOpacity: 0.1 // Opacity of the shadow material
}
shadowMaterialOpacity: 0.1, // Opacity of the shadow material
};
export const skyConfig: SkyConfig = {
defaultTurbidity: 10.0, // Default turbidity of the sky
@@ -291,72 +287,66 @@ export const skyConfig: SkyConfig = {
defaultRayleigh: 1.9, // Default Rayleigh scattering coefficient
mieCoefficient: 0.1, // Mie scattering coefficient
mieDirectionalG: 1.0, // Mie directional G
skyDistance: 2000 // Distance of the sky
}
skyDistance: 2000, // Distance of the sky
};
export const assetConfig: AssetConfig = {
defaultScaleBeforeGsap: [0.1, 0.1, 0.1], // Default scale of the assets
defaultScaleAfterGsap: [1, 1, 1] // Default scale of the assets
}
defaultScaleAfterGsap: [1, 1, 1], // Default scale of the assets
};
export const pointConfig: PointConfig = {
defaultInnerColor: "#ffffff", // Default inner color of the points
defaultOuterColor: "#ffffff", // Default outer color of the points
deleteColor: "#ff0000", // Color of the points when deleting
boxScale: [0.5, 0.5, 0.5], // Scale of the points
wallOuterColor: "#C7C7C7", // Outer color of the wall points
floorOuterColor: "#808080", // Outer color of the floor points
aisleOuterColor: "#FBBC05", // Outer color of the aisle points
zoneOuterColor: "#007BFF", // Outer color of the zone points
snappingThreshold: 1, // Threshold for snapping
}
};
export const lineConfig: LineConfig = {
tubularSegments: 64, // Number of tubular segments
radius: 0.15, // Radius of the lines
radialSegments: 8, // Number of radial segments
wallName: "WallLine", // Name of the wall lines
floorName: "FloorLine", // Name of the floor lines
aisleName: "AisleLine", // Name of the aisle lines
zoneName: "ZoneLine", // Name of the zone lines
referenceName: "ReferenceLine", // Name of the reference lines
lineIntersectionPoints: 300, // Number of intersection points
defaultColor: "#000000", // Default color of the lines
wallColor: "#C7C7C7", // Color of the wall lines
floorColor: "#808080", // Color of the floor lines
aisleColor: "#FBBC05", // Color of the aisle lines
zoneColor: "#007BFF", // Color of the zone lines
helperColor: "#C164FF" // Color of the helper lines
}
helperColor: "#C164FF", // Color of the helper lines
};
export const wallConfig: WallConfig = {
defaultColor: "white", // Default color of the walls
height: 7, // Height of the walls
width: 0.05, // Width of the walls
}
};
export const floorConfig: FloorConfig = {
defaultColor: "grey", // Default color of the floors
height: 0.1, // Height of the floors
textureScale: 0.1, // Scale of the floor texture
}
};
export const roofConfig: RoofConfig = {
defaultColor: "grey", // Default color of the roofs
height: 0.1 // Height of the roofs
}
height: 0.1, // Height of the roofs
};
export const aisleConfig: AisleConfig = {
width: 0.1, // Width of the aisles
height: 0.01, // Height of the aisles
defaultColor: 0xffff00 // Default color of the aisles
}
defaultColor: 0xffff00, // Default color of the aisles
};
export const zoneConfig: ZoneConfig = {
defaultColor: "black", // Default color of the zones
@@ -366,9 +356,14 @@ export const zoneConfig: ZoneConfig = {
export const columnConfig: ColumnConfig = {
defaultColor: "White", // Default color of the columns
}
};
export const outlineConfig: OutlineConfig = {
assetSelectColor: 0x0054fE, // Color of the selected assets
assetDeleteColor: 0xFF0000 // Color of the deleted assets
}
assetSelectColor: 0x0054fe, // Color of the selected assets
assetDeleteColor: 0xff0000, // Color of the deleted assets
};
export const distanceConfig: DistanceConfig = {
minDistance: 20,
maxDistance: 75,
};

View File

@@ -198,9 +198,30 @@ export type FloorItemType = {
modelname: string;
position: [number, number, number];
rotation: { x: number; y: number; z: number };
modelfileID?: string;
modelfileID: string;
isLocked: boolean;
isVisible: boolean;
eventData?: {
type: 'Conveyor';
points: {
uuid: string;
position: [number, number, number];
rotation: [number, number, number];
actions: { uuid: string; name: string; type: string; material: string; delay: number | string; spawnInterval: number | string; isUsed: boolean }[] | [];
triggers: { uuid: string; name: string; type: string; isUsed: boolean; bufferTime: number }[] | [];
connections: { source: { pathUUID: string; pointUUID: string }; targets: { pathUUID: string; pointUUID: string }[] };
}[];
speed: number | string;
} | {
type: 'Vehicle';
point: {
uuid: string;
position: [number, number, number];
actions: { uuid: string; name: string; type: string; start: { x: number, y: number } | {}, hitCount: number, end: { x: number, y: number } | {}, buffer: number };
connections: { source: { pathUUID: string; pointUUID: string }; targets: { pathUUID: string; pointUUID: string }[] };
speed: number;
};
};
};
// Array of floor items for managing multiple objects on the floor
@@ -310,7 +331,7 @@ interface VehicleEventsSchema {
point: {
uuid: string;
position: [number, number, number];
actions: { uuid: string; name: string; type: string; start: string, hitCount: number, end: string, buffer: number };
actions: { uuid: string; name: string; type: string; start: { x: number, y: number } | {}, hitCount: number, end: { x: number, y: number } | {}, buffer: number };
connections: { source: { pathUUID: string; pointUUID: string }; targets: { pathUUID: string; pointUUID: string }[] };
speed: number;
};