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:
5
app/.env
5
app/.env
@@ -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
10
app/package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
})}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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)}
|
||||
/>
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
@@ -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 (
|
||||
|
||||
@@ -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 */}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -119,7 +119,6 @@ const AddButtons: React.FC<ButtonsProps> = ({
|
||||
};
|
||||
|
||||
// Delete the selectedZone state
|
||||
|
||||
setSelectedZone(updatedZone);
|
||||
} else {
|
||||
const updatePanelData = async () => {
|
||||
|
||||
@@ -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 */}
|
||||
|
||||
93
app/src/components/ui/componets/DistanceLine.tsx
Normal file
93
app/src/components/ui/componets/DistanceLine.tsx
Normal 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;
|
||||
93
app/src/components/ui/componets/DistanceLines.tsx
Normal file
93
app/src/components/ui/componets/DistanceLines.tsx
Normal 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;
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
29
app/src/components/ui/componets/handleDropTemplate .tsx
Normal file
29
app/src/components/ui/componets/handleDropTemplate .tsx
Normal 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);
|
||||
// };
|
||||
@@ -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) {
|
||||
|
||||
@@ -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} />;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
@@ -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} />;
|
||||
};
|
||||
|
||||
@@ -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} />;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -17,6 +17,7 @@ interface ModelData {
|
||||
thumbnail: string;
|
||||
uploadDate: number;
|
||||
_id: string;
|
||||
price: number;
|
||||
}
|
||||
interface ModelsProps {
|
||||
models: ModelData[];
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -358,7 +358,7 @@ export default function World() {
|
||||
anglesnappedPoint={anglesnappedPoint}
|
||||
/>
|
||||
|
||||
<DrieHtmlTemp itemsGroup={itemsGroup} />
|
||||
{/* <DrieHtmlTemp itemsGroup={itemsGroup} /> */}
|
||||
|
||||
<Agv lines={lines} plane={plane} />
|
||||
</>
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}}
|
||||
|
||||
@@ -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} />
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
@@ -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) {
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -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`,
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -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;
|
||||
25
app/src/services/simulation/getAssetEventType.ts
Normal file
25
app/src/services/simulation/getAssetEventType.ts
Normal 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");
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -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 }),
|
||||
}));
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 }),
|
||||
}));
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
25
app/src/types/world/worldTypes.d.ts
vendored
25
app/src/types/world/worldTypes.d.ts
vendored
@@ -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;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user