253 lines
8.6 KiB
TypeScript
253 lines
8.6 KiB
TypeScript
import { WalletIcon } from "../../icons/3dChartIcons";
|
|
import { useEffect, useRef, useState } from "react";
|
|
import { Line } from "react-chartjs-2";
|
|
import {
|
|
useDroppedObjectsStore,
|
|
Zones,
|
|
} from "../../../store/useDroppedObjectsStore";
|
|
import useModuleStore from "../../../store/useModuleStore";
|
|
import { determinePosition } from "./functions/determinePosition";
|
|
import { getActiveProperties } from "./functions/getActiveProperties";
|
|
import { addingFloatingWidgets } from "../../../services/realTimeVisulization/zoneData/addFloatingWidgets";
|
|
|
|
const DroppedObjects: React.FC = () => {
|
|
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);
|
|
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
|
|
|
|
// Handle pointer down event
|
|
function handlePointerDown(event: React.PointerEvent, index: number) {
|
|
const obj = zone.objects[index];
|
|
const container = document.getElementById("real-time-vis-canvas");
|
|
if (!container) return;
|
|
|
|
const rect = container.getBoundingClientRect();
|
|
const relativeX = event.clientX - rect.left;
|
|
const relativeY = event.clientY - rect.top;
|
|
|
|
// Determine which properties are active for this object
|
|
const [activeProp1, activeProp2] = getActiveProperties(obj.position);
|
|
|
|
// Calculate the offset based on the active properties
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
setDraggingIndex({ zone: zoneName, index });
|
|
setOffset([offsetY, offsetX]);
|
|
}
|
|
|
|
// Handle pointer move event
|
|
function handlePointerMove(event: React.PointerEvent) {
|
|
if (!draggingIndex || !offset) return;
|
|
|
|
const container = document.getElementById("real-time-vis-canvas");
|
|
if (!container) return;
|
|
|
|
const rect = container.getBoundingClientRect();
|
|
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);
|
|
|
|
// Calculate the new position based on the active properties
|
|
let newX = 0;
|
|
let newY = 0;
|
|
|
|
if (activeProp2 === "left") {
|
|
newX = relativeX - offset[1];
|
|
} else if (activeProp2 === "right") {
|
|
newX = rect.width - (relativeX + offset[1]);
|
|
}
|
|
|
|
if (activeProp1 === "top") {
|
|
newY = relativeY - offset[0];
|
|
} else if (activeProp1 === "bottom") {
|
|
newY = rect.height - (relativeY + offset[0]);
|
|
}
|
|
|
|
// Ensure the object stays within the canvas 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];
|
|
|
|
// 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],
|
|
});
|
|
}
|
|
animationRef.current = null;
|
|
});
|
|
}
|
|
}
|
|
|
|
// Handle pointer up event
|
|
async function handlePointerUp(event: React.MouseEvent<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");
|
|
|
|
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);
|
|
|
|
// Validate the dragging index and get the object
|
|
if (!zone.objects[draggingIndex.index]) {
|
|
throw new Error("Dragged object not found in the zone");
|
|
}
|
|
const obj = { ...zone.objects[draggingIndex.index], position: newPosition };
|
|
let response = await addingFloatingWidgets(zone.zoneId, organization, obj);
|
|
if (response.message === "Widget updated successfully") {
|
|
updateObjectPosition(zoneName, draggingIndex.index, newPosition);
|
|
}
|
|
|
|
// Reset states
|
|
setDraggingIndex(null);
|
|
setOffset(null);
|
|
|
|
if (animationRef.current) {
|
|
cancelAnimationFrame(animationRef.current);
|
|
animationRef.current = null;
|
|
}
|
|
} catch (error) {
|
|
|
|
}
|
|
}
|
|
|
|
|
|
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>
|
|
<div className="data-values">
|
|
<div className="value">{obj.value}</div>
|
|
<div className="per">{obj.per}</div>
|
|
</div>
|
|
</div>
|
|
<div className="icon">
|
|
<WalletIcon />
|
|
</div>
|
|
</>
|
|
) : obj.className === "warehouseThroughput floating" ? (
|
|
<>
|
|
<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>
|
|
</>
|
|
) : obj.className === "fleetEfficiency floating" ? (
|
|
<>
|
|
<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>
|
|
</>
|
|
) : null}
|
|
</div>
|
|
))}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default DroppedObjects;
|