2025-03-27 12:28:17 +05:30
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-03-27 18:04:16 +05:30
|
|
|
|
import { WalletIcon } from "../../icons/3dChartIcons";
|
|
|
|
|
|
import { useEffect, useRef, useState } from "react";
|
|
|
|
|
|
import { Line } from "react-chartjs-2";
|
|
|
|
|
|
import { useDroppedObjectsStore, Zones } from "../../../store/useDroppedObjectsStore";
|
2025-03-27 12:28:17 +05:30
|
|
|
|
|
2025-03-27 18:04:16 +05:30
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-03-27 12:28:17 +05:30
|
|
|
|
|
2025-03-27 18:04:16 +05:30
|
|
|
|
return (
|
|
|
|
|
|
<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>
|
|
|
|
|
|
) : 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>
|
|
|
|
|
|
);
|
|
|
|
|
|
};
|
2025-03-27 12:28:17 +05:30
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-03-27 18:04:16 +05:30
|
|
|
|
export default DroppedObjects;
|
2025-03-27 12:28:17 +05:30
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-03-26 18:30:33 +05:30
|
|
|
|
|
|
|
|
|
|
|
2025-03-27 12:28:17 +05:30
|
|
|
|
|
2025-03-27 18:04:16 +05:30
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 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;
|