floating widgets data added
This commit is contained in:
parent
dac7edb837
commit
2591de38da
|
@ -11,7 +11,7 @@ const CardsScene = () => {
|
||||||
<Canvas>
|
<Canvas>
|
||||||
{/* 3d-cards */}
|
{/* 3d-cards */}
|
||||||
|
|
||||||
{/* <Throughput /> */}
|
<Throughput />
|
||||||
{/* <ReturnOfInvestment /> */}
|
{/* <ReturnOfInvestment /> */}
|
||||||
{/* <ProductionCapacity /> */}
|
{/* <ProductionCapacity /> */}
|
||||||
{/* <StateWorking /> */}
|
{/* <StateWorking /> */}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import React, { useEffect, useRef } from "react";
|
import React, { useEffect, useRef } from "react";
|
||||||
import { Widget } from "../../../store/useWidgetStore";
|
import { Widget } from "../../../store/useWidgetStore";
|
||||||
|
import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore";
|
||||||
|
|
||||||
// Define the type for `Side`
|
// Define the type for `Side`
|
||||||
type Side = "top" | "bottom" | "left" | "right";
|
type Side = "top" | "bottom" | "left" | "right";
|
||||||
|
@ -64,9 +65,9 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
|
||||||
const [selectedOption, setSelectedOption] = React.useState<string | null>(
|
const [selectedOption, setSelectedOption] = React.useState<string | null>(
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
// console.log('setSelectedOption: ', setSelectedOption);
|
//
|
||||||
const [options, setOptions] = React.useState<string[]>([]);
|
const [options, setOptions] = React.useState<string[]>([]);
|
||||||
// console.log('setOptions: ', setOptions);
|
//
|
||||||
|
|
||||||
// Scroll to the selected option when it changes
|
// Scroll to the selected option when it changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -170,8 +171,7 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
|
||||||
className={`zone ${selectedZone.zoneName === zoneName ? "active" : ""
|
className={`zone ${selectedZone.zoneName === zoneName ? "active" : ""
|
||||||
}`}
|
}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
useDroppedObjectsStore.getState().setZone(zoneName, zonesData[zoneName]?.zoneId);
|
||||||
|
|
||||||
setSelectedZone({
|
setSelectedZone({
|
||||||
zoneName,
|
zoneName,
|
||||||
activeSides: zonesData[zoneName].activeSides || [],
|
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 = () => {
|
import { WalletIcon } from "../../icons/3dChartIcons";
|
||||||
// const { camera } = useThree(); // Now inside Canvas ✅
|
import { useEffect, useRef, useState } from "react";
|
||||||
// const [objects, setObjects] = useState<{ id: number; position: [number, number, number] }[]>([]);
|
import { Line } from "react-chartjs-2";
|
||||||
|
import { useDroppedObjectsStore, Zones } from "../../../store/useDroppedObjectsStore";
|
||||||
// // 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";
|
|
||||||
|
|
||||||
|
|
||||||
const DroppedObjects: React.FC = () => {
|
const DroppedObjects: React.FC = () => {
|
||||||
const objects = useDroppedObjectsStore((state) => state.objects); // Get objects from Zustand store
|
const zones = useDroppedObjectsStore((state) => state.zones);
|
||||||
console.log('objects: ', objects);
|
|
||||||
|
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 (
|
return (
|
||||||
<>
|
<div onPointerMove={handlePointerMove} onPointerUp={handlePointerUp}>
|
||||||
{objects.map((obj, index) => (
|
{zone.objects.map((obj, index) => (
|
||||||
<group key={index} position={[Math.random() * 5, Math.random() * 5, 0]}>
|
<div
|
||||||
<Html wrapperClass={obj.className}>
|
key={`${zoneName}-${index}`}
|
||||||
<div style={{ padding: "10px", background: "#fff", borderRadius: "6px" }}>
|
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="header">{obj.header}</div>
|
||||||
<div className="data-values">
|
<div className="data-values">
|
||||||
<div className="value">{obj.value}</div>
|
<div className="value">{obj.value}</div>
|
||||||
<div className="per">{obj.per}</div>
|
<div className="per">{obj.per}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Html>
|
<div className="icon">
|
||||||
</group>
|
<WalletIcon />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : 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;
|
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 DisplayZone from "./DisplayZone";
|
||||||
import Scene from "../../../modules/scene/scene";
|
import Scene from "../../../modules/scene/scene";
|
||||||
import useModuleStore from "../../../store/useModuleStore";
|
import useModuleStore from "../../../store/useModuleStore";
|
||||||
import { useDroppedObjectsStore, useZones } from "../../../store/store";
|
|
||||||
import DroppedObjects from "./DroppedFloatingWidgets";
|
import DroppedObjects from "./DroppedFloatingWidgets";
|
||||||
|
import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore";
|
||||||
|
import { useZones } from "../../../store/store";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -81,37 +83,37 @@ const RealTimeVisulization: React.FC = () => {
|
||||||
});
|
});
|
||||||
}, [selectedZone]);
|
}, [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>) => {
|
const handleDrop = (event: React.DragEvent<HTMLDivElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const data = event.dataTransfer.getData("text/plain"); // Use "text/plain" to match the drag event
|
const data = event.dataTransfer.getData("text/plain");
|
||||||
|
if (!data || !selectedZone.zoneName) return;
|
||||||
|
|
||||||
if (data) {
|
|
||||||
const droppedData = JSON.parse(data);
|
const droppedData = JSON.parse(data);
|
||||||
useDroppedObjectsStore.getState().addObject(droppedData); // Add to Zustand store
|
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 (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
|
@ -136,6 +138,7 @@ const RealTimeVisulization: React.FC = () => {
|
||||||
|
|
||||||
<Scene />
|
<Scene />
|
||||||
</div>
|
</div>
|
||||||
|
<DroppedObjects />
|
||||||
{activeModule === "visualization" && (
|
{activeModule === "visualization" && (
|
||||||
<>
|
<>
|
||||||
<DisplayZone
|
<DisplayZone
|
||||||
|
|
|
@ -47,12 +47,9 @@ export default function ZoneCentreTarget() {
|
||||||
const worldUp = new THREE.Vector3(0, 0, 1);
|
const worldUp = new THREE.Vector3(0, 0, 1);
|
||||||
const right = new THREE.Vector3().crossVectors(worldUp, direction).normalize();
|
const right = new THREE.Vector3().crossVectors(worldUp, direction).normalize();
|
||||||
const up = new THREE.Vector3().crossVectors(direction, right).normalize();
|
const up = new THREE.Vector3().crossVectors(direction, right).normalize();
|
||||||
|
|
||||||
const offsetPosition = up.clone().multiplyScalar(20);
|
const offsetPosition = up.clone().multiplyScalar(20);
|
||||||
|
|
||||||
camPosition.add(offsetPosition);
|
camPosition.add(offsetPosition);
|
||||||
|
|
||||||
|
|
||||||
const setCam = async () => {
|
const setCam = async () => {
|
||||||
controls.setLookAt(centrePoint[0], 100, centrePoint[2], ...centrePoint, true);
|
controls.setLookAt(centrePoint[0], 100, centrePoint[2], ...centrePoint, true);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|
|
@ -4,8 +4,24 @@ const FleetEfficiency = () => {
|
||||||
// Calculate the rotation angle for the progress bar
|
// Calculate the rotation angle for the progress bar
|
||||||
const rotationAngle = -90 + progress * 3.6; // Progress starts from the left (-90°)
|
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 (
|
return (
|
||||||
<div className="fleetEfficiency floating">
|
<div className="fleetEfficiency floating" draggable onDragStart={handleDragStart}>
|
||||||
<h2 className="header">Fleet Efficiency</h2>
|
<h2 className="header">Fleet Efficiency</h2>
|
||||||
|
|
||||||
<div className="progressContainer">
|
<div className="progressContainer">
|
||||||
|
|
|
@ -5,6 +5,7 @@ interface SimpleCardProps {
|
||||||
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>; // React component for SVG icon
|
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>; // React component for SVG icon
|
||||||
value: string;
|
value: string;
|
||||||
per: string; // Percentage change
|
per: string; // Percentage change
|
||||||
|
position?: [number, number]
|
||||||
}
|
}
|
||||||
|
|
||||||
const SimpleCard: React.FC<SimpleCardProps> = ({
|
const SimpleCard: React.FC<SimpleCardProps> = ({
|
||||||
|
@ -12,23 +13,30 @@ const SimpleCard: React.FC<SimpleCardProps> = ({
|
||||||
icon: Icon,
|
icon: Icon,
|
||||||
value,
|
value,
|
||||||
per,
|
per,
|
||||||
|
position = [0, 0],
|
||||||
}) => {
|
}) => {
|
||||||
|
|
||||||
const handleDragStart = (event: React.DragEvent<HTMLDivElement>) => {
|
const handleDragStart = (event: React.DragEvent<HTMLDivElement>) => {
|
||||||
|
const rect = event.currentTarget.getBoundingClientRect(); // Get position
|
||||||
const cardData = JSON.stringify({
|
const cardData = JSON.stringify({
|
||||||
header,
|
header,
|
||||||
value,
|
value,
|
||||||
per,
|
per,
|
||||||
|
icon: Icon,
|
||||||
className: event.currentTarget.className,
|
className: event.currentTarget.className,
|
||||||
|
position: [rect.top, rect.left], // ✅ Store position
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("Dragging Data:", cardData); // ✅ Debugging log
|
|
||||||
|
|
||||||
event.dataTransfer.setData("text/plain", cardData);
|
event.dataTransfer.setData("text/plain", cardData);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
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-wrapper">
|
||||||
<div className="header">{header}</div>
|
<div className="header">{header}</div>
|
||||||
<div className="data-values">
|
<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 (
|
return (
|
||||||
<div className="warehouseThroughput floating" draggable>
|
<div className="warehouseThroughput floating" draggable onDragStart={handleDragStart}>
|
||||||
<div className="header">
|
<div className="header">
|
||||||
<h2>Warehouse Throughput</h2>
|
<h2>Warehouse Throughput</h2>
|
||||||
<p>
|
<p>
|
||||||
|
|
|
@ -19,7 +19,7 @@ import ZoneCentreTarget from "../../components/ui/componets/zoneCameraTarget";
|
||||||
|
|
||||||
import { useThree } from "@react-three/fiber";
|
import { useThree } from "@react-three/fiber";
|
||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
import DroppedObjects from "../../components/ui/componets/DroppedFloatingWidgets";
|
import Throughput from "../../components/layout/3D-cards/cards/Throughput";
|
||||||
|
|
||||||
// import Simulation from "./simulationtemp/simulation";
|
// import Simulation from "./simulationtemp/simulation";
|
||||||
|
|
||||||
|
@ -47,7 +47,6 @@ export default function Scene() {
|
||||||
}}
|
}}
|
||||||
|
|
||||||
>
|
>
|
||||||
<DroppedObjects/>
|
|
||||||
<Controls />
|
<Controls />
|
||||||
<TransformControl />
|
<TransformControl />
|
||||||
<SelectionControls />
|
<SelectionControls />
|
||||||
|
|
|
@ -2,10 +2,14 @@ let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_BACKEND_URL}`;
|
||||||
|
|
||||||
type Side = "top" | "bottom" | "left" | "right";
|
type Side = "top" | "bottom" | "left" | "right";
|
||||||
|
|
||||||
export const panelData = async (organization: string, zoneID: string, panelOrder: Side[]) => {
|
export const panelData = async (
|
||||||
console.log('panelOrder: ', panelOrder);
|
organization: string,
|
||||||
console.log('zoneID: ', zoneID);
|
zoneID: string,
|
||||||
console.log('organization: ', organization);
|
panelOrder: Side[]
|
||||||
|
) => {
|
||||||
|
console.log("panelOrder: ", panelOrder);
|
||||||
|
console.log("zoneID: ", zoneID);
|
||||||
|
console.log("organization: ", organization);
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${url_Backend_dwinzo}/api/v1/panel/save`, {
|
const response = await fetch(`${url_Backend_dwinzo}/api/v1/panel/save`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
@ -29,3 +33,16 @@ export const panelData = async (organization: string, zoneID: string, panelOrder
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
// {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 * as Types from "../types/world/worldTypes";
|
||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { io } from "socket.io-client";
|
import { io } from "socket.io-client";
|
||||||
|
import { ComponentType, SVGProps } from "react";
|
||||||
|
|
||||||
export const useSocketStore = create<any>((set: any, get: any) => ({
|
export const useSocketStore = create<any>((set: any, get: any) => ({
|
||||||
socket: null,
|
socket: null,
|
||||||
|
@ -428,7 +429,6 @@ export const usezonePosition = create<any>((set: any) => ({
|
||||||
setZonePosition: (x: any) => set({ zonePosition: x }),
|
setZonePosition: (x: any) => set({ zonePosition: x }),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
interface EditPositionState {
|
interface EditPositionState {
|
||||||
Edit: boolean;
|
Edit: boolean;
|
||||||
setEdit: (value: boolean) => void;
|
setEdit: (value: boolean) => void;
|
||||||
|
@ -439,19 +439,4 @@ interface EditPositionState {
|
||||||
setEdit: (value) => set({ Edit: value }), // Properly updating the state
|
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;
|
padding: 6px;
|
||||||
|
|
||||||
.floating {
|
.floating {
|
||||||
|
|
||||||
min-height: 170px;
|
min-height: 170px;
|
||||||
background: var(--background-color);
|
background: var(--background-color);
|
||||||
border: 1.23px solid var(--border-color);
|
border: 1.23px solid var(--border-color);
|
||||||
|
|
|
@ -14,7 +14,18 @@
|
||||||
border-radius: #{$border-radius-medium};
|
border-radius: #{$border-radius-medium};
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
z-index: #{$z-index-default};
|
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 {
|
.scene-container {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
@ -194,8 +205,6 @@
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.panel-content {
|
.panel-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|
Loading…
Reference in New Issue