rtViz #16
|
@ -11,7 +11,7 @@ const CardsScene = () => {
|
|||
<Canvas>
|
||||
{/* 3d-cards */}
|
||||
|
||||
{/* <Throughput /> */}
|
||||
<Throughput />
|
||||
{/* <ReturnOfInvestment /> */}
|
||||
{/* <ProductionCapacity /> */}
|
||||
{/* <StateWorking /> */}
|
||||
|
|
|
@ -2,6 +2,7 @@ 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";
|
||||
|
||||
// Define the type for `Side`
|
||||
type Side = "top" | "bottom" | "left" | "right";
|
||||
|
@ -163,7 +164,7 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
|
|||
selectedZone.zoneName === zoneName ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
console.log("zoneName: ", zoneName);
|
||||
useDroppedObjectsStore.getState().setZone(zoneName, zonesData[zoneName]?.zoneId);
|
||||
setSelectedZone({
|
||||
zoneName,
|
||||
activeSides: zonesData[zoneName].activeSides || [],
|
||||
|
|
|
@ -1,89 +1,215 @@
|
|||
// import { useState } from "react";
|
||||
// import { useThree } from "@react-three/fiber";
|
||||
// import * as THREE from "three";
|
||||
|
||||
|
||||
|
||||
// const DroppedObjects = () => {
|
||||
// const { camera } = useThree(); // Now inside Canvas ✅
|
||||
// const [objects, setObjects] = useState<{ id: number; position: [number, number, number] }[]>([]);
|
||||
|
||||
// // Function to convert drop event into 3D position
|
||||
// const handleDrop = (event: DragEvent) => {
|
||||
// event.preventDefault();
|
||||
|
||||
// const data = event.dataTransfer?.getData("text/plain");
|
||||
// if (!data) return;
|
||||
|
||||
// try {
|
||||
// const cardData = JSON.parse(data);
|
||||
// if (!cardData.className.includes("floating total-card")) {
|
||||
// console.log("Drop rejected: Incorrect element.");
|
||||
// return;
|
||||
// }
|
||||
|
||||
// // Convert 2D drop position to 3D world coordinates
|
||||
// const x = (event.clientX / window.innerWidth) * 2 - 1;
|
||||
// const y = -(event.clientY / window.innerHeight) * 2 + 1;
|
||||
|
||||
// // Raycasting to determine the drop position in 3D
|
||||
// const raycaster = new THREE.Raycaster();
|
||||
// const mouseVector = new THREE.Vector2(x, y);
|
||||
// raycaster.setFromCamera(mouseVector, camera);
|
||||
|
||||
// // Intersect with a ground plane (assume y = 0)
|
||||
// const groundPlane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
|
||||
// const intersection = new THREE.Vector3();
|
||||
// raycaster.ray.intersectPlane(groundPlane, intersection);
|
||||
|
||||
// console.log("Spawn Object at:", intersection);
|
||||
|
||||
// // Add the dropped object to the scene state
|
||||
// setObjects((prev) => [...prev, { id: Date.now(), position: [intersection.x, intersection.y, intersection.z] }]);
|
||||
// } catch (error) {
|
||||
// console.error("Invalid data:", error);
|
||||
// }
|
||||
// };
|
||||
|
||||
// return (
|
||||
// <group>
|
||||
// {/* Render dropped objects as green boxes */}
|
||||
// {objects.map((obj) => (
|
||||
// <mesh key={obj.id} position={obj.position}>
|
||||
// <boxGeometry args={[1, 1, 1]} />
|
||||
// <meshStandardMaterial color="green" />
|
||||
// </mesh>
|
||||
// ))}
|
||||
// </group>
|
||||
// );
|
||||
// };
|
||||
|
||||
import { Html } from "@react-three/drei";
|
||||
import { useDroppedObjectsStore } from "../../../store/store";
|
||||
|
||||
import { WalletIcon } from "../../icons/3dChartIcons";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { Line } from "react-chartjs-2";
|
||||
import { useDroppedObjectsStore, Zones } from "../../../store/useDroppedObjectsStore";
|
||||
|
||||
const DroppedObjects: React.FC = () => {
|
||||
const objects = useDroppedObjectsStore((state) => state.objects); // Get objects from Zustand store
|
||||
console.log('objects: ', objects);
|
||||
const zones = useDroppedObjectsStore((state) => state.zones);
|
||||
|
||||
const updateObjectPosition = useDroppedObjectsStore((state) => state.updateObjectPosition);
|
||||
const [draggingIndex, setDraggingIndex] = useState<{ zone: string; index: number } | null>(null);
|
||||
const [offset, setOffset] = useState<[number, number] | null>(null);
|
||||
const positionRef = useRef<[number, number] | null>(null);
|
||||
const animationRef = useRef<number | null>(null);
|
||||
|
||||
// useEffect(() => {
|
||||
// const initialZones: Record<string, Zones> = {
|
||||
// "Zone 1": {
|
||||
// zoneName: "Zone 1",
|
||||
// zoneId: "2e996073-546c-470c-8323-55bd3700c6aa",
|
||||
// objects: [
|
||||
// {
|
||||
// header: "Today’s Money",
|
||||
// value: 53000, // ✅ Converted to number
|
||||
// per: "+55%",
|
||||
// className: "floating total-card",
|
||||
// position: [146, 214], // ✅ No need for 'as' here
|
||||
// },
|
||||
// {
|
||||
// header: "New Clients",
|
||||
// value: 250, // ✅ Converted to number
|
||||
// per: "+12%",
|
||||
// className: "floating total-card",
|
||||
// position: [344, 295],
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// };
|
||||
|
||||
// useDroppedObjectsStore.setState({ zones: initialZones });
|
||||
// }, []);
|
||||
|
||||
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
|
||||
|
||||
function handlePointerDown(event: React.PointerEvent, index: number) {
|
||||
const obj = zone.objects[index];
|
||||
const offsetX = event.clientX - obj.position[1];
|
||||
const offsetY = event.clientY - obj.position[0];
|
||||
|
||||
setDraggingIndex({ zone: zoneName, index });
|
||||
setOffset([offsetY, offsetX]);
|
||||
}
|
||||
|
||||
function handlePointerMove(event: React.PointerEvent) {
|
||||
if (!draggingIndex || !offset) return;
|
||||
|
||||
const container = document.getElementById("real-time-vis-canvas");
|
||||
if (!container) return;
|
||||
|
||||
const rect = container.getBoundingClientRect();
|
||||
|
||||
let newX = event.clientX - offset[1];
|
||||
let newY = event.clientY - offset[0];
|
||||
|
||||
newX = Math.max(0, Math.min(rect.width - 50, newX));
|
||||
newY = Math.max(0, Math.min(rect.height - 50, newY));
|
||||
|
||||
positionRef.current = [newY, newX];
|
||||
|
||||
if (!animationRef.current) {
|
||||
animationRef.current = requestAnimationFrame(() => {
|
||||
if (positionRef.current) {
|
||||
updateObjectPosition(zoneName, draggingIndex.index, positionRef.current);
|
||||
}
|
||||
animationRef.current = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function handlePointerUp() {
|
||||
setDraggingIndex(null);
|
||||
setOffset(null);
|
||||
if (animationRef.current) {
|
||||
cancelAnimationFrame(animationRef.current);
|
||||
animationRef.current = null;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{objects.map((obj, index) => (
|
||||
<group key={index} position={[Math.random() * 5, Math.random() * 5, 0]}>
|
||||
<Html wrapperClass={obj.className}>
|
||||
<div style={{ padding: "10px", background: "#fff", borderRadius: "6px" }}>
|
||||
<div className="header">{obj.header}</div>
|
||||
<div className="data-values">
|
||||
<div className="value">{obj.value}</div>
|
||||
<div className="per">{obj.per}</div>
|
||||
<div onPointerMove={handlePointerMove} onPointerUp={handlePointerUp}>
|
||||
{zone.objects.map((obj, index) => (
|
||||
<div
|
||||
key={`${zoneName}-${index}`}
|
||||
className={obj.className}
|
||||
style={{
|
||||
top: obj.position[0] + "px",
|
||||
left: obj.position[1] + "px",
|
||||
transition: draggingIndex?.index === index ? "none" : "transform 0.1s ease-out",
|
||||
}}
|
||||
onPointerDown={(event) => handlePointerDown(event, index)}
|
||||
>
|
||||
{obj.className === "floating total-card" ? (
|
||||
<div>
|
||||
<div className="header-wrapper">
|
||||
<div className="header">{obj.header}</div>
|
||||
<div className="data-values">
|
||||
<div className="value">{obj.value}</div>
|
||||
<div className="per">{obj.per}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="icon">
|
||||
<WalletIcon />
|
||||
</div>
|
||||
</div>
|
||||
</Html>
|
||||
</group>
|
||||
) : obj.className === "warehouseThroughput floating" ? (
|
||||
<div>
|
||||
<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>
|
||||
</div>
|
||||
) : obj.className === "fleetEfficiency floating" ? (
|
||||
<div>
|
||||
<h2 className="header">Fleet Efficiency</h2>
|
||||
<div className="progressContainer">
|
||||
<div className="progress">
|
||||
<div className="barOverflow">
|
||||
<div
|
||||
className="bar"
|
||||
style={{ transform: `rotate(${obj.value}deg)` }}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="scaleLabels">
|
||||
<span>0%</span>
|
||||
<div className="centerText">
|
||||
<div className="percentage">{obj.per}%</div>
|
||||
<div className="status">Optimal</div>
|
||||
</div>
|
||||
<span>100%</span>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export default DroppedObjects;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// import { Html } from "@react-three/drei";
|
||||
// import { useDroppedObjectsStore } from "../../../store/store";
|
||||
// import { CartIcon, DocumentIcon, GlobeIcon, WalletIcon } from "../../icons/3dChartIcons";
|
||||
// import SimpleCard from "../realTimeVis/floating/SimpleCard";
|
||||
|
||||
// const ICON_MAP: Record<string, React.ComponentType<React.SVGProps<SVGSVGElement>>> = {
|
||||
// WalletIcon,
|
||||
// GlobeIcon,
|
||||
// DocumentIcon,
|
||||
// CartIcon
|
||||
// };
|
||||
// const DroppedObjects: React.FC = () => {
|
||||
// const objects = useDroppedObjectsStore((state) => state.objects); // Get objects from Zustand store
|
||||
//
|
||||
|
||||
// return (
|
||||
// <>
|
||||
// {objects.map((obj, index) => {
|
||||
// const IconComponent = obj.Icon || WalletIcon; // Use obj.Icon directly if it exists
|
||||
// return (
|
||||
// <SimpleCard
|
||||
// key={index}
|
||||
// position={obj.position}
|
||||
// header={obj.header}
|
||||
// icon={IconComponent} // ✅ No need to look it up in ICON_MAP
|
||||
// value={obj.value}
|
||||
// per={obj.per}
|
||||
// />
|
||||
// );
|
||||
// })}
|
||||
// </>
|
||||
// );
|
||||
// };
|
||||
|
||||
// export default DroppedObjects;
|
||||
|
|
|
@ -6,8 +6,10 @@ import { useSelectedZoneStore } from "../../../store/useZoneStore";
|
|||
import DisplayZone from "./DisplayZone";
|
||||
import Scene from "../../../modules/scene/scene";
|
||||
import useModuleStore from "../../../store/useModuleStore";
|
||||
import { useDroppedObjectsStore, useZones } from "../../../store/store";
|
||||
|
||||
import DroppedObjects from "./DroppedFloatingWidgets";
|
||||
import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore";
|
||||
import { useZones } from "../../../store/store";
|
||||
|
||||
|
||||
type Side = "top" | "bottom" | "left" | "right";
|
||||
|
@ -80,36 +82,36 @@ const RealTimeVisulization: React.FC = () => {
|
|||
});
|
||||
}, [selectedZone]);
|
||||
|
||||
// const handleDrop = (event: React.DragEvent<HTMLDivElement>) => {
|
||||
// console.log("Drop event fired! ✅");
|
||||
// event.preventDefault();
|
||||
|
||||
// const data = event.dataTransfer.getData("text/plain");
|
||||
// if (!data) {
|
||||
// console.log("❌ No data received on drop!");
|
||||
// return;
|
||||
// }
|
||||
|
||||
// try {
|
||||
// const droppedData = JSON.parse(data);
|
||||
// console.log("✅ Dropped Data:", droppedData);
|
||||
|
||||
// console.log('droppedData: ', droppedData);
|
||||
// setDroppedObjects((prev) => [...prev, droppedData]); // ✅ Add to state
|
||||
// console.log(droppedObjects);
|
||||
// } catch (error) {
|
||||
// console.error("❌ Error parsing dropped data:", error);
|
||||
// }
|
||||
// };
|
||||
const handleDrop = (event: React.DragEvent<HTMLDivElement>) => {
|
||||
event.preventDefault();
|
||||
const data = event.dataTransfer.getData("text/plain"); // Use "text/plain" to match the drag event
|
||||
|
||||
if (data) {
|
||||
const droppedData = JSON.parse(data);
|
||||
useDroppedObjectsStore.getState().addObject(droppedData); // Add to Zustand store
|
||||
const data = event.dataTransfer.getData("text/plain");
|
||||
if (!data || !selectedZone.zoneName) return;
|
||||
|
||||
const droppedData = JSON.parse(data);
|
||||
const canvasElement = document.getElementById("real-time-vis-canvas");
|
||||
if (!canvasElement) return;
|
||||
|
||||
const canvasRect = canvasElement.getBoundingClientRect();
|
||||
const relativeX = event.clientX - canvasRect.left;
|
||||
const relativeY = event.clientY - canvasRect.top;
|
||||
|
||||
const newObject = {
|
||||
...droppedData,
|
||||
position: [relativeY, relativeX], // Y first because of top/left style
|
||||
};
|
||||
|
||||
console.log("newObject: ", newObject);
|
||||
|
||||
// Only set zone if it’s not already in the store (prevents overwriting objects)
|
||||
const existingZone = useDroppedObjectsStore.getState().zones[selectedZone.zoneName];
|
||||
if (!existingZone) {
|
||||
useDroppedObjectsStore.getState().setZone(selectedZone.zoneName, selectedZone.zoneId);
|
||||
}
|
||||
|
||||
// Add the dropped object to the zone
|
||||
useDroppedObjectsStore.getState().addObject(selectedZone.zoneName, newObject);
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<div
|
||||
|
@ -133,9 +135,10 @@ const RealTimeVisulization: React.FC = () => {
|
|||
onDrop={(event) => handleDrop(event)}
|
||||
onDragOver={(event) => event.preventDefault()}
|
||||
>
|
||||
|
||||
|
||||
<Scene />
|
||||
</div>
|
||||
<DroppedObjects />
|
||||
{activeModule === "visualization" && (
|
||||
<>
|
||||
<DisplayZone
|
||||
|
|
|
@ -47,12 +47,9 @@ export default function ZoneCentreTarget() {
|
|||
const worldUp = new THREE.Vector3(0, 0, 1);
|
||||
const right = new THREE.Vector3().crossVectors(worldUp, direction).normalize();
|
||||
const up = new THREE.Vector3().crossVectors(direction, right).normalize();
|
||||
|
||||
const offsetPosition = up.clone().multiplyScalar(20);
|
||||
|
||||
camPosition.add(offsetPosition);
|
||||
|
||||
|
||||
const setCam = async () => {
|
||||
controls.setLookAt(centrePoint[0], 100, centrePoint[2], ...centrePoint, true);
|
||||
setTimeout(() => {
|
||||
|
|
|
@ -4,8 +4,24 @@ const FleetEfficiency = () => {
|
|||
// Calculate the rotation angle for the progress bar
|
||||
const rotationAngle = -90 + progress * 3.6; // Progress starts from the left (-90°)
|
||||
|
||||
const handleDragStart = (event: React.DragEvent<HTMLDivElement>) => {
|
||||
const rect = event.currentTarget.getBoundingClientRect(); // Get position
|
||||
|
||||
const cardData = JSON.stringify({
|
||||
className: event.currentTarget.className,
|
||||
position: [rect.top, rect.left], // Store position
|
||||
value: rotationAngle, // Example value (you can change if dynamic)
|
||||
per: progress,
|
||||
|
||||
});
|
||||
|
||||
console.log("Dragged Data:", cardData);
|
||||
event.dataTransfer.setData("text/plain", cardData);
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<div className="fleetEfficiency floating">
|
||||
<div className="fleetEfficiency floating" draggable onDragStart={handleDragStart}>
|
||||
<h2 className="header">Fleet Efficiency</h2>
|
||||
|
||||
<div className="progressContainer">
|
||||
|
|
|
@ -5,6 +5,7 @@ interface SimpleCardProps {
|
|||
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>; // React component for SVG icon
|
||||
value: string;
|
||||
per: string; // Percentage change
|
||||
position?: [number, number]
|
||||
}
|
||||
|
||||
const SimpleCard: React.FC<SimpleCardProps> = ({
|
||||
|
@ -12,23 +13,30 @@ const SimpleCard: React.FC<SimpleCardProps> = ({
|
|||
icon: Icon,
|
||||
value,
|
||||
per,
|
||||
position = [0, 0],
|
||||
}) => {
|
||||
|
||||
const handleDragStart = (event: React.DragEvent<HTMLDivElement>) => {
|
||||
const rect = event.currentTarget.getBoundingClientRect(); // Get position
|
||||
const cardData = JSON.stringify({
|
||||
header,
|
||||
value,
|
||||
per,
|
||||
className: event.currentTarget.className,
|
||||
icon: Icon,
|
||||
className: event.currentTarget.className,
|
||||
position: [rect.top, rect.left], // ✅ Store position
|
||||
});
|
||||
|
||||
console.log("Dragging Data:", cardData); // ✅ Debugging log
|
||||
|
||||
|
||||
event.dataTransfer.setData("text/plain", cardData);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="floating total-card" draggable onDragStart={handleDragStart}>
|
||||
<div
|
||||
className="floating total-card"
|
||||
draggable
|
||||
onDragStart={handleDragStart}
|
||||
style={{ top: position[0], left: position[1] }} // No need for ?? 0 if position is guaranteed
|
||||
>
|
||||
<div className="header-wrapper">
|
||||
<div className="header">{header}</div>
|
||||
<div className="data-values">
|
||||
|
|
|
@ -111,8 +111,27 @@ const WarehouseThroughput = () => {
|
|||
},
|
||||
};
|
||||
|
||||
const handleDragStart = (event: React.DragEvent<HTMLDivElement>) => {
|
||||
const rect = event.currentTarget.getBoundingClientRect(); // Get element position
|
||||
|
||||
const cardData = JSON.stringify({
|
||||
header: "Warehouse Throughput", // Static header
|
||||
value: "+5", // Example value (you can change if dynamic)
|
||||
per: "2025", // Example percentage or date
|
||||
icon: "📊", // Placeholder for an icon (if needed)
|
||||
className: event.currentTarget.className,
|
||||
position: [rect.top, rect.left], // ✅ Store initial position
|
||||
lineGraphData, // ✅ Include chart data
|
||||
lineGraphOptions, // ✅ Include chart options
|
||||
});
|
||||
|
||||
|
||||
event.dataTransfer.setData("text/plain", cardData);
|
||||
// event.dataTransfer.effectAllowed = "move"; // Improve drag effect
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="warehouseThroughput floating" draggable>
|
||||
<div className="warehouseThroughput floating" draggable onDragStart={handleDragStart}>
|
||||
<div className="header">
|
||||
<h2>Warehouse Throughput</h2>
|
||||
<p>
|
||||
|
|
|
@ -43,7 +43,6 @@ export default function Scene() {
|
|||
}}
|
||||
|
||||
>
|
||||
<DroppedObjects/>
|
||||
<Controls />
|
||||
<TransformControl />
|
||||
<SelectionControls />
|
||||
|
|
|
@ -2,30 +2,47 @@ let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_BACKEND_URL}`;
|
|||
|
||||
type Side = "top" | "bottom" | "left" | "right";
|
||||
|
||||
export const panelData = async (organization: string, zoneID: string, panelOrder: Side[]) => {
|
||||
console.log('panelOrder: ', panelOrder);
|
||||
console.log('zoneID: ', zoneID);
|
||||
console.log('organization: ', organization);
|
||||
try {
|
||||
const response = await fetch(`${url_Backend_dwinzo}/api/v1/panel/save`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ organization, zoneID, panelOrder }),
|
||||
});
|
||||
export const panelData = async (
|
||||
organization: string,
|
||||
zoneID: string,
|
||||
panelOrder: Side[]
|
||||
) => {
|
||||
console.log("panelOrder: ", panelOrder);
|
||||
console.log("zoneID: ", zoneID);
|
||||
console.log("organization: ", organization);
|
||||
try {
|
||||
const response = await fetch(`${url_Backend_dwinzo}/api/v1/panel/save`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ organization, zoneID, panelOrder }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to add panelOrder for 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");
|
||||
}
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to add panelOrder for 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");
|
||||
}
|
||||
}
|
||||
};
|
||||
// {objects.map((obj, index) => (
|
||||
// <group key={index} position={[Math.random() * 5, Math.random() * 5, 0]}>
|
||||
// <Html wrapperClass={obj.className}>
|
||||
// <div style={{ padding: "10px", background: "#fff", borderRadius: "6px" }}>
|
||||
// <div className="header">{obj.header}</div>
|
||||
// <div className="data-values">
|
||||
// <div className="value">{obj.value}</div>
|
||||
// <div className="per">{obj.per}</div>
|
||||
// </div>
|
||||
// </div>
|
||||
// </Html>
|
||||
// </group>
|
||||
// ))}
|
||||
|
|
|
@ -2,6 +2,7 @@ import * as THREE from "three";
|
|||
import * as Types from "../types/world/worldTypes";
|
||||
import { create } from "zustand";
|
||||
import { io } from "socket.io-client";
|
||||
import { ComponentType, SVGProps } from "react";
|
||||
|
||||
export const useSocketStore = create<any>((set: any, get: any) => ({
|
||||
socket: null,
|
||||
|
@ -354,30 +355,14 @@ export const usezonePosition = create<any>((set: any) => ({
|
|||
setZonePosition: (x: any) => set({ zonePosition: x }),
|
||||
}));
|
||||
|
||||
|
||||
interface EditPositionState {
|
||||
Edit: boolean;
|
||||
setEdit: (value: boolean) => void;
|
||||
}
|
||||
|
||||
export const useEditPosition = create<EditPositionState>((set) => ({
|
||||
Edit: false,
|
||||
setEdit: (value) => set({ Edit: value }), // Properly updating the state
|
||||
}));
|
||||
Edit: boolean;
|
||||
setEdit: (value: boolean) => void;
|
||||
}
|
||||
|
||||
export const useEditPosition = create<EditPositionState>((set) => ({
|
||||
Edit: false,
|
||||
setEdit: (value) => set({ Edit: value }), // Properly updating the state
|
||||
}));
|
||||
|
||||
|
||||
interface DroppedObject {
|
||||
header: string;
|
||||
value: string;
|
||||
per: string;
|
||||
className: string;
|
||||
}
|
||||
|
||||
interface DroppedObjectsState {
|
||||
objects: DroppedObject[];
|
||||
addObject: (obj: DroppedObject) => void;
|
||||
}
|
||||
|
||||
export const useDroppedObjectsStore = create<DroppedObjectsState>((set) => ({
|
||||
objects: [],
|
||||
addObject: (obj) => set((state) => ({ objects: [...state.objects, obj] })),
|
||||
}));
|
|
@ -0,0 +1,78 @@
|
|||
import { create } from "zustand";
|
||||
|
||||
type DroppedObject = {
|
||||
className: string;
|
||||
position: [number, number];
|
||||
value?: number;
|
||||
per?: string;
|
||||
header?: string;
|
||||
};
|
||||
|
||||
type Zone = {
|
||||
zoneName: string;
|
||||
zoneId: string;
|
||||
objects: DroppedObject[];
|
||||
};
|
||||
|
||||
type DroppedObjectsState = {
|
||||
zones: Record<string, Zone>;
|
||||
setZone: (zoneName: string, zoneId: string) => void;
|
||||
addObject: (zoneName: string, newObject: DroppedObject) => void;
|
||||
updateObjectPosition: (
|
||||
zoneName: string,
|
||||
index: number,
|
||||
newPosition: [number, number]
|
||||
) => void;
|
||||
};
|
||||
|
||||
export const useDroppedObjectsStore = create<DroppedObjectsState>((set) => ({
|
||||
zones: {},
|
||||
|
||||
setZone: (zoneName: string, zoneId: string) =>
|
||||
set((state) => ({
|
||||
zones: {
|
||||
[zoneName]: state.zones[zoneName] || { zoneName, zoneId, objects: [] }, // Keep existing zone if it exists
|
||||
},
|
||||
})),
|
||||
|
||||
addObject: (zoneName: string, newObject: DroppedObject) =>
|
||||
set((state) => ({
|
||||
zones: {
|
||||
...state.zones,
|
||||
[zoneName]: {
|
||||
...state.zones[zoneName],
|
||||
objects: [...state.zones[zoneName].objects, newObject], // Append new object
|
||||
},
|
||||
},
|
||||
})),
|
||||
|
||||
updateObjectPosition: (zoneName, index, newPosition) =>
|
||||
set((state) => {
|
||||
const zone = state.zones[zoneName];
|
||||
if (!zone) return state;
|
||||
return {
|
||||
zones: {
|
||||
[zoneName]: {
|
||||
...zone,
|
||||
objects: zone.objects.map((obj, i) =>
|
||||
i === index ? { ...obj, position: newPosition } : obj
|
||||
),
|
||||
},
|
||||
},
|
||||
};
|
||||
}),
|
||||
}));
|
||||
|
||||
export interface DroppedObjects {
|
||||
header: string;
|
||||
value: string | number; // ✅ Allows both numbers and formatted strings
|
||||
per: string;
|
||||
className: string;
|
||||
position: [number, number]; // ✅ Ensures position is a tuple
|
||||
}
|
||||
|
||||
export interface Zones {
|
||||
zoneName: string;
|
||||
zoneId: string;
|
||||
objects: DroppedObject[];
|
||||
}
|
|
@ -9,7 +9,6 @@
|
|||
padding: 6px;
|
||||
|
||||
.floating {
|
||||
|
||||
min-height: 170px;
|
||||
background: var(--background-color);
|
||||
border: 1.23px solid var(--border-color);
|
||||
|
|
|
@ -15,7 +15,18 @@
|
|||
border-radius: #{$border-radius-medium};
|
||||
transition: all 0.2s;
|
||||
z-index: #{$z-index-default};
|
||||
|
||||
.floating {
|
||||
width: 100%;
|
||||
max-width: 250px;
|
||||
min-height: 83px;
|
||||
background: var(--background-color);
|
||||
border: 1.23px solid var(--border-color);
|
||||
box-shadow: 0px 4.91px 4.91px 0px #0000001c;
|
||||
border-radius: $border-radius-medium;
|
||||
padding: 18px;
|
||||
position: absolute;
|
||||
z-index: 1000;
|
||||
}
|
||||
.scene-container {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue