Merge pull request '3dWidget dnd and 2d widgets backend api updated' (#21) from rtViz into main

Reviewed-on: http://185.100.212.76:7776/Dwinzo-Beta/Dwinzo_dev/pulls/21
This commit is contained in:
Vishnu 2025-03-28 13:57:58 +00:00
commit 01588cf6c1
27 changed files with 400 additions and 133 deletions

View File

@ -11,10 +11,10 @@ const CardsScene = () => {
<Canvas> <Canvas>
{/* 3d-cards */} {/* 3d-cards */}
<Throughput />
{/* <ReturnOfInvestment /> */}
{/* <ProductionCapacity /> */} {/* <ProductionCapacity /> */}
{/* <ReturnOfInvestment /> */}
{/* <StateWorking /> */} {/* <StateWorking /> */}
{/* <Throughput /> */}
<OrbitControls /> <OrbitControls />
</Canvas> </Canvas>

View File

@ -21,8 +21,11 @@ ChartJS.register(
Tooltip, Tooltip,
Legend Legend
); );
interface ProductionCapacityProps {
position: [number, number, number];
}
const ProductionCapacity = () => { const ProductionCapacity : React.FC<ProductionCapacityProps> = ({ position }) => {
// Chart data for a week // Chart data for a week
const chartData = { const chartData = {
labels: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], // Days of the week labels: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], // Days of the week
@ -76,7 +79,10 @@ const ProductionCapacity = () => {
}; };
return ( return (
<Html position={[0, 0, 0]} transform occlude> <Html position={[position[0], position[1], position[2]]}
scale={[0.5, 0.5, 0.5]}
transform
sprite>
<div className="productionCapacity-wrapper card"> <div className="productionCapacity-wrapper card">
<div className="headeproductionCapacityr-wrapper"> <div className="headeproductionCapacityr-wrapper">
<div className="header">Production Capacity</div> <div className="header">Production Capacity</div>

View File

@ -35,8 +35,10 @@ const SmoothLineGraphComponent: React.FC<SmoothLineGraphProps> = ({
}) => { }) => {
return <Line data={data} options={options} />; return <Line data={data} options={options} />;
}; };
interface ReturnOfInvestmentProps {
const ReturnOfInvestment = () => { position: [number, number, number];
}
const ReturnOfInvestment: React.FC<ReturnOfInvestmentProps> = ({ position }) => {
// Improved sample data for the smooth curve graph (single day) // Improved sample data for the smooth curve graph (single day)
const graphData: ChartData<"line"> = { const graphData: ChartData<"line"> = {
labels: [ labels: [
@ -106,7 +108,10 @@ const ReturnOfInvestment = () => {
}; };
return ( return (
<Html position={[0, 0, 0]} transform occlude> <Html position={[position[0], position[1], position[2]]}
scale={[0.5, 0.5, 0.5]}
transform
sprite>
<div className="returnOfInvestment card"> <div className="returnOfInvestment card">
<div className="header">Return of Investment</div> <div className="header">Return of Investment</div>
<div className="lineGraph charts"> <div className="lineGraph charts">

View File

@ -1,6 +1,9 @@
import { Html } from "@react-three/drei"; import { Html } from "@react-three/drei";
import image from "../../../../assets/image/temp/image.png"; // import image from "../../../../assets/image/temp/image.png";
const StateWorking = () => { interface StateWorkingProps {
position: [number, number, number];
}
const StateWorking: React.FC<StateWorkingProps> = ({ position }) => {
const datas = [ const datas = [
{ key: "Oil Tank:", value: "24/341" }, { key: "Oil Tank:", value: "24/341" },
{ key: "Oil Refin:", value: 36.023 }, { key: "Oil Refin:", value: 36.023 },
@ -10,7 +13,10 @@ const StateWorking = () => {
{ key: "Time:", value: 13 - 9 - 2023 }, { key: "Time:", value: 13 - 9 - 2023 },
]; ];
return ( return (
<Html position={[0, 0, 0]} transform occlude> <Html position={[position[0], position[1], position[2]]}
scale={[0.5, 0.5, 0.5]}
transform
sprite>
<div className="stateWorking-wrapper card"> <div className="stateWorking-wrapper card">
<div className="header-wrapper"> <div className="header-wrapper">
<div className="header"> <div className="header">
@ -20,7 +26,7 @@ const StateWorking = () => {
</span> </span>
</div> </div>
<div className="img"> <div className="img">
<img src={image} alt="" /> {/* <img src={image} alt="" /> */}
</div> </div>
</div> </div>
{/* Data */} {/* Data */}

View File

@ -37,7 +37,12 @@ const LineGraphComponent: React.FC<LineGraphProps> = ({ data, options }) => {
return <Line data={data} options={options} />; return <Line data={data} options={options} />;
}; };
const Throughput = () => { interface ThroughputProps {
position: [number, number, number];
}
const Throughput: React.FC<ThroughputProps> = ({ position }) => {
// Sample data for the line graph // Sample data for the line graph
const graphData: ChartData<"line"> = { const graphData: ChartData<"line"> = {
labels: ["Jan", "Feb", "Mar", "Apr", "May", "Jun"], labels: ["Jan", "Feb", "Mar", "Apr", "May", "Jun"],
@ -86,7 +91,10 @@ const Throughput = () => {
}; };
return ( return (
<Html position={[0, 0, 0]} transform occlude> <Html position={[position[0], position[1], position[2]]}
scale={[0.5, 0.5, 0.5]}
transform
sprite>
<div className="throughput-wrapper"> <div className="throughput-wrapper">
<div className="header">Throughput</div> <div className="header">Throughput</div>
<div className="display-value"> <div className="display-value">

View File

@ -3,6 +3,7 @@ import { useWidgetStore } from "../../../../../store/useWidgetStore";
import { ChartType } from "chart.js/auto"; import { ChartType } from "chart.js/auto";
import ChartComponent from "./ChartComponent"; import ChartComponent from "./ChartComponent";
import { StockIncreseIcon } from "../../../../icons/RealTimeVisulationIcons"; import { StockIncreseIcon } from "../../../../icons/RealTimeVisulationIcons";
import { generateUniqueId } from "../../../../../functions/generateUniqueId";
const chartTypes: ChartType[] = [ const chartTypes: ChartType[] = [
"bar", "bar",
@ -40,7 +41,8 @@ const ChartWidget: React.FC<WidgetProps> = ({ type, index, title }) => {
onDragStart={() => { onDragStart={() => {
setDraggedAsset({ setDraggedAsset({
type, type,
id: `widget-${index + 1}`, id: generateUniqueId(
),
title, title,
panel: "top", panel: "top",
data: sampleData, data: sampleData,

View File

@ -2,6 +2,7 @@ import widget1 from "../../../../../assets/image/3D/ProductionCapacity.png";
import widget2 from "../../../../../assets/image/3D/ReturnOfInvestment.png"; import widget2 from "../../../../../assets/image/3D/ReturnOfInvestment.png";
import widget3 from "../../../../../assets/image/3D/StateWorking.png"; import widget3 from "../../../../../assets/image/3D/StateWorking.png";
import widget4 from "../../../../../assets/image/3D/Throughput.png"; import widget4 from "../../../../../assets/image/3D/Throughput.png";
import { useAsset3dWidget } from "../../../../../store/store";
const Widgets3D = () => { const Widgets3D = () => {
const widgets = [ const widgets = [
{ name: "Widget 1", img: widget1 }, { name: "Widget 1", img: widget1 },
@ -9,17 +10,38 @@ const Widgets3D = () => {
{ name: "Widget 3", img: widget3 }, { name: "Widget 3", img: widget3 },
{ name: "Widget 4", img: widget4 }, { name: "Widget 4", img: widget4 },
]; ];
const { widgetSelect, setWidgetSelect } = useAsset3dWidget();
return ( return (
<div className="widgets-container widget3D"> <div className="widgets-container widget3D">
{widgets?.map((widget, index) => ( {widgets?.map((widget, index) => (
<div key={index} className="widget-item" draggable> <div
<div className="widget-name">{widget.name}</div> key={index}
className="widget-item"
draggable
onDragStart={(e) => {
let crt = e.target
if (crt instanceof HTMLElement) {
const widget = crt.cloneNode(true) as HTMLElement;
console.log('widget: ', widget);
e.dataTransfer.setDragImage(widget,0,0)
e.dataTransfer.effectAllowed="move"
}
}}
onPointerDown={() => {
setWidgetSelect("ui-" + widget.name);
}}
onPointerUp={() => {
setWidgetSelect(""); // Clearing selection correctly
}}
>
{/* <div className="widget-name">{widget.name}</div> */}
<img <img
className="widget-image" className="widget-image"
src={widget.img} src={widget.img}
alt={widget.name} alt={widget.name}
draggable={false} // draggable={false}
/> />
</div> </div>
))} ))}

View File

@ -16,6 +16,7 @@ interface ButtonsProps {
zoneName: string; zoneName: string;
activeSides: Side[]; activeSides: Side[];
panelOrder: Side[]; panelOrder: Side[];
lockedPanels: Side[]; lockedPanels: Side[];
zoneId: string; zoneId: string;
zoneViewPortTarget: number[]; zoneViewPortTarget: number[];
@ -33,6 +34,7 @@ interface ButtonsProps {
zoneName: string; zoneName: string;
activeSides: Side[]; activeSides: Side[];
panelOrder: Side[]; panelOrder: Side[];
lockedPanels: Side[]; lockedPanels: Side[];
zoneId: string; zoneId: string;
zoneViewPortTarget: number[]; zoneViewPortTarget: number[];
@ -120,23 +122,39 @@ const AddButtons: React.FC<ButtonsProps> = ({
console.log("updatedZone: ", updatedZone); console.log("updatedZone: ", updatedZone);
setSelectedZone(updatedZone); setSelectedZone(updatedZone);
} else { } else {
// If the panel is not active, activate it const updatePanelData = async () => {
const newActiveSides = [...selectedZone.activeSides, side]; try {
// Get email and organization safely
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0] || "defaultOrg"; // Fallback value
const updatedZone = { // Prevent duplicate side entries
...selectedZone, const newActiveSides = selectedZone.activeSides.includes(side)
activeSides: newActiveSides, ? [...selectedZone.activeSides]
panelOrder: newActiveSides, : [...selectedZone.activeSides, side];
const updatedZone = {
...selectedZone,
activeSides: newActiveSides,
panelOrder: newActiveSides,
};
// API call
const response = await panelData(organization, selectedZone.zoneId, newActiveSides);
console.log("response: ", response);
// Update state
console.log("updatedZone: ", updatedZone);
setSelectedZone(updatedZone);
} catch (error) {
console.error("Error updating panel data:", error);
}
}; };
const email = localStorage.getItem("email");
const organization = email!.split("@")[1].split(".")[0];
// let response = panelData(organization, selectedZone.zoneId, newActiveSides)
// console.log('response: ', response);
// Update the selectedZone state updatePanelData(); // Call the async function
console.log("updatedZone: ", updatedZone);
setSelectedZone(updatedZone);
} }
}; };
return ( return (
@ -146,9 +164,8 @@ const AddButtons: React.FC<ButtonsProps> = ({
<div key={side} className={`side-button-container ${side}`}> <div key={side} className={`side-button-container ${side}`}>
{/* "+" Button */} {/* "+" Button */}
<button <button
className={`side-button ${side}${ className={`side-button ${side}${selectedZone.activeSides.includes(side) ? " active" : ""
selectedZone.activeSides.includes(side) ? " active" : "" }`}
}`}
onClick={() => handlePlusButtonClick(side)} onClick={() => handlePlusButtonClick(side)}
title={ title={
selectedZone.activeSides.includes(side) selectedZone.activeSides.includes(side)
@ -166,9 +183,8 @@ const AddButtons: React.FC<ButtonsProps> = ({
<div className="extra-Bs"> <div className="extra-Bs">
{/* Hide Panel */} {/* Hide Panel */}
<div <div
className={`icon ${ className={`icon ${hiddenPanels.includes(side) ? "active" : ""
hiddenPanels.includes(side) ? "active" : "" }`}
}`}
title={ title={
hiddenPanels.includes(side) ? "Show Panel" : "Hide Panel" hiddenPanels.includes(side) ? "Show Panel" : "Hide Panel"
} }
@ -190,9 +206,8 @@ const AddButtons: React.FC<ButtonsProps> = ({
{/* Lock/Unlock Panel */} {/* Lock/Unlock Panel */}
<div <div
className={`icon ${ className={`icon ${selectedZone.lockedPanels.includes(side) ? "active" : ""
selectedZone.lockedPanels.includes(side) ? "active" : "" }`}
}`}
title={ title={
selectedZone.lockedPanels.includes(side) selectedZone.lockedPanels.includes(side)
? "Unlock Panel" ? "Unlock Panel"

View File

@ -3,6 +3,7 @@ import { Widget } from "../../../store/useWidgetStore";
import { MoveArrowLeft, MoveArrowRight } from "../../icons/SimulationIcons"; import { MoveArrowLeft, MoveArrowRight } from "../../icons/SimulationIcons";
import { InfoIcon } from "../../icons/ExportCommonIcons"; import { InfoIcon } from "../../icons/ExportCommonIcons";
import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore"; import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore";
import { getSelect2dZoneData } from "../../../services/realTimeVisulization/zoneData/getSelect2dZoneData";
// Define the type for `Side` // Define the type for `Side`
type Side = "top" | "bottom" | "left" | "right"; type Side = "top" | "bottom" | "left" | "right";
@ -12,6 +13,7 @@ interface DisplayZoneProps {
[key: string]: { [key: string]: {
activeSides: Side[]; activeSides: Side[];
panelOrder: Side[]; panelOrder: Side[];
lockedPanels: Side[]; lockedPanels: Side[];
widgets: Widget[]; widgets: Widget[];
zoneId: string; zoneId: string;
@ -23,6 +25,7 @@ interface DisplayZoneProps {
zoneName: string; zoneName: string;
activeSides: Side[]; activeSides: Side[];
panelOrder: Side[]; panelOrder: Side[];
lockedPanels: Side[]; lockedPanels: Side[];
zoneId: string; zoneId: string;
zoneViewPortTarget: number[]; zoneViewPortTarget: number[];
@ -40,6 +43,7 @@ interface DisplayZoneProps {
zoneName: string; zoneName: string;
activeSides: Side[]; activeSides: Side[];
panelOrder: Side[]; panelOrder: Side[];
lockedPanels: Side[]; lockedPanels: Side[];
zoneId: string; zoneId: string;
zoneViewPortTarget: number[]; zoneViewPortTarget: number[];
@ -60,6 +64,7 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
selectedZone, selectedZone,
setSelectedZone, setSelectedZone,
}) => { }) => {
// Ref for the container element // Ref for the container element
const containerRef = useRef<HTMLDivElement | null>(null); const containerRef = useRef<HTMLDivElement | null>(null);
@ -141,11 +146,28 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
} }
}; };
async function handleSelect2dZoneData(zoneId: string, zoneName: string) {
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]
let response = await getSelect2dZoneData(zoneId, organization)
setSelectedZone({
zoneName,
activeSides: response.activeSides,
panelOrder: response.panelOrder,
lockedPanels: response.lockedPanels,
widgets: response.widgets,
zoneId: zoneId,
zoneViewPortTarget:
response.viewPortCenter,
zoneViewPortPosition:
response.viewPortposition,
});
}
return ( return (
<div <div
className={`zone-wrapper ${ className={`zone-wrapper ${selectedZone?.activeSides?.includes("bottom") && "bottom"
selectedZone?.activeSides?.includes("bottom") && "bottom" }`}
}`}
> >
{/* Left Arrow */} {/* Left Arrow */}
{showLeftArrow && ( {showLeftArrow && (
@ -160,23 +182,11 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
{Object.keys(zonesData).map((zoneName, index) => ( {Object.keys(zonesData).map((zoneName, index) => (
<div <div
key={index} key={index}
className={`zone ${ className={`zone ${selectedZone.zoneName === zoneName ? "active" : ""
selectedZone.zoneName === zoneName ? "active" : "" }`}
}`}
onClick={() => { onClick={() => {
useDroppedObjectsStore.getState().setZone(zoneName, zonesData[zoneName]?.zoneId); useDroppedObjectsStore.getState().setZone(zoneName, zonesData[zoneName]?.zoneId);
setSelectedZone({ handleSelect2dZoneData(zonesData[zoneName]?.zoneId, zoneName)
zoneName,
activeSides: zonesData[zoneName].activeSides || [],
panelOrder: zonesData[zoneName].panelOrder || [],
lockedPanels: zonesData[zoneName].lockedPanels || [],
widgets: zonesData[zoneName].widgets || [],
zoneId: zonesData[zoneName]?.zoneId || "",
zoneViewPortTarget:
zonesData[zoneName].zoneViewPortTarget || [],
zoneViewPortPosition:
zonesData[zoneName].zoneViewPortPosition || [],
});
}} }}
> >
{zoneName} {zoneName}

View File

@ -36,6 +36,7 @@ export const DraggableWidget = ({
zoneName: string; zoneName: string;
activeSides: Side[]; activeSides: Side[];
panelOrder: Side[]; panelOrder: Side[];
lockedPanels: Side[]; lockedPanels: Side[];
widgets: Widget[]; widgets: Widget[];
}; };
@ -44,6 +45,7 @@ export const DraggableWidget = ({
zoneName: string; zoneName: string;
activeSides: Side[]; activeSides: Side[];
panelOrder: Side[]; panelOrder: Side[];
lockedPanels: Side[]; lockedPanels: Side[];
zoneId: string; zoneId: string;
zoneViewPortTarget: number[]; zoneViewPortTarget: number[];
@ -78,9 +80,11 @@ export const DraggableWidget = ({
const isPanelHidden = hiddenPanels.includes(widget.panel); const isPanelHidden = hiddenPanels.includes(widget.panel);
const deleteSelectedChart = () => { const deleteSelectedChart = () => {
console.log('widget.id: ', widget.id);
const updatedWidgets = selectedZone.widgets.filter( const updatedWidgets = selectedZone.widgets.filter(
(w: Widget) => w.id !== widget.id (w: Widget) => w.id !== widget.id
); );
console.log('updatedWidgets: ', updatedWidgets);
setSelectedZone((prevZone: any) => ({ setSelectedZone((prevZone: any) => ({
...prevZone, ...prevZone,
@ -122,7 +126,6 @@ export const DraggableWidget = ({
...widget, ...widget,
id: `${widget.id}-copy-${Date.now()}`, id: `${widget.id}-copy-${Date.now()}`,
}; };
setSelectedZone((prevZone: any) => ({ setSelectedZone((prevZone: any) => ({
...prevZone, ...prevZone,
widgets: [...prevZone.widgets, duplicatedWidget], widgets: [...prevZone.widgets, duplicatedWidget],
@ -130,7 +133,7 @@ export const DraggableWidget = ({
setOpenKebabId(null); setOpenKebabId(null);
console.log("Duplicated widget with ID:", duplicatedWidget.id);
}; };
const handleKebabClick = (event: React.MouseEvent<HTMLDivElement>) => { const handleKebabClick = (event: React.MouseEvent<HTMLDivElement>) => {
@ -173,6 +176,7 @@ export const DraggableWidget = ({
}; };
const handleDrop = (event: React.DragEvent<HTMLDivElement>) => { const handleDrop = (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault(); event.preventDefault();
const fromIndex = parseInt(event.dataTransfer.getData("text/plain"), 10); // Get the dragged widget's index const fromIndex = parseInt(event.dataTransfer.getData("text/plain"), 10); // Get the dragged widget's index
const toIndex = index; // The index of the widget where the drop occurred const toIndex = index; // The index of the widget where the drop occurred
@ -186,9 +190,8 @@ export const DraggableWidget = ({
<div <div
draggable draggable
key={widget.id} key={widget.id}
className={`chart-container ${ className={`chart-container ${selectedChartId?.id === widget.id && "activeChart"
selectedChartId?.id === widget.id && "activeChart" }`}
}`}
onPointerDown={handlePointerDown} onPointerDown={handlePointerDown}
onDragStart={handleDragStart} onDragStart={handleDragStart}
onDragEnter={handleDragEnter} onDragEnter={handleDragEnter}
@ -208,9 +211,8 @@ export const DraggableWidget = ({
{openKebabId === widget.id && ( {openKebabId === widget.id && (
<div className="kebab-options" ref={widgetRef}> <div className="kebab-options" ref={widgetRef}>
<div <div
className={`edit btn ${ className={`edit btn ${isPanelFull(widget.panel) ? "btn-blur" : ""
isPanelFull(widget.panel) ? "btn-blur" : "" }`}
}`}
onClick={isPanelFull(widget.panel) ? undefined : duplicateWidget} onClick={isPanelFull(widget.panel) ? undefined : duplicateWidget}
> >
<div className="icon"> <div className="icon">

View File

@ -0,0 +1,79 @@
import { useThree } from "@react-three/fiber";
import React, { useState, useEffect } from "react";
import { useAsset3dWidget } from "../../../store/store";
import useModuleStore from "../../../store/useModuleStore";
import { ThreeState } from "../../../types/world/worldTypes";
import * as THREE from "three";
import Throughput from "../../layout/3D-cards/cards/Throughput";
import ProductionCapacity from "../../layout/3D-cards/cards/ProductionCapacity";
import ReturnOfInvestment from "../../layout/3D-cards/cards/ReturnOfInvestment";
import StateWorking from "../../layout/3D-cards/cards/StateWorking";
export default function Dropped3dWidgets() {
const { widgetSelect } = useAsset3dWidget();
const { activeModule } = useModuleStore();
const { raycaster, gl, scene }: ThreeState = useThree();
// 🔥 Store multiple instances per widget type
const [widgetPositions, setWidgetPositions] = useState<Record<string, [number, number, number][]>>({});
useEffect(() => {
if (activeModule !== "visualization") return;
const canvasElement = gl.domElement;
const onDrop = (event: DragEvent) => {
event.preventDefault(); // Prevent default browser behavior
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(Assets);
if (intersects.length > 0) {
const { x, y, z } = intersects[0].point;
// ✅ Allow multiple instances by storing positions in an array
setWidgetPositions((prev) => ({
...prev,
[widgetSelect]: [...(prev[widgetSelect] || []), [x, y, z]],
}));
}
};
canvasElement.addEventListener("drop", onDrop);
return () => {
canvasElement.removeEventListener("drop", onDrop);
};
}, [widgetSelect, activeModule]);
return (
<>
{widgetPositions["ui-Widget 1"]?.map((pos, index) => (
<ProductionCapacity key={`Widget1-${index}`} position={pos} />
))}
{widgetPositions["ui-Widget 2"]?.map((pos, index) => (
<ReturnOfInvestment key={`Widget2-${index}`} position={pos} />
))}
{widgetPositions["ui-Widget 3"]?.map((pos, index) => (
<StateWorking key={`Widget3-${index}`} position={pos} />
))}
{widgetPositions["ui-Widget 4"]?.map((pos, index) => (
<Throughput key={`Widget4-${index}`} position={pos} />
))}
</>
);
}

View File

@ -8,7 +8,6 @@ import { useDroppedObjectsStore, Zones } from "../../../store/useDroppedObjectsS
const DroppedObjects: React.FC = () => { const DroppedObjects: React.FC = () => {
const zones = useDroppedObjectsStore((state) => state.zones); const zones = useDroppedObjectsStore((state) => state.zones);
const updateObjectPosition = useDroppedObjectsStore((state) => state.updateObjectPosition); const updateObjectPosition = useDroppedObjectsStore((state) => state.updateObjectPosition);
const [draggingIndex, setDraggingIndex] = useState<{ zone: string; index: number } | null>(null); const [draggingIndex, setDraggingIndex] = useState<{ zone: string; index: number } | null>(null);
const [offset, setOffset] = useState<[number, number] | null>(null); const [offset, setOffset] = useState<[number, number] | null>(null);

View File

@ -3,6 +3,7 @@ import { useWidgetStore } from "../../../store/useWidgetStore";
import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
import { DraggableWidget } from "./DraggableWidget"; import { DraggableWidget } from "./DraggableWidget";
import { arrayMove } from "@dnd-kit/sortable"; import { arrayMove } from "@dnd-kit/sortable";
import { addingWidgets } from "../../../services/realTimeVisulization/zoneData/addWidgets";
type Side = "top" | "bottom" | "left" | "right"; type Side = "top" | "bottom" | "left" | "right";
@ -19,6 +20,7 @@ interface PanelProps {
zoneName: string; zoneName: string;
activeSides: Side[]; activeSides: Side[];
panelOrder: Side[]; panelOrder: Side[];
lockedPanels: Side[]; lockedPanels: Side[];
zoneId: string; zoneId: string;
zoneViewPortTarget: number[]; zoneViewPortTarget: number[];
@ -30,6 +32,7 @@ interface PanelProps {
zoneName: string; zoneName: string;
activeSides: Side[]; activeSides: Side[];
panelOrder: Side[]; panelOrder: Side[];
lockedPanels: Side[]; lockedPanels: Side[];
zoneId: string; zoneId: string;
zoneViewPortTarget: number[]; zoneViewPortTarget: number[];
@ -72,9 +75,8 @@ const Panel: React.FC<PanelProps> = ({
case "top": case "top":
case "bottom": case "bottom":
return { return {
width: `calc(100% - ${ width: `calc(100% - ${(leftActive ? panelSize : 0) + (rightActive ? panelSize : 0)
(leftActive ? panelSize : 0) + (rightActive ? panelSize : 0) }px)`,
}px)`,
height: `${panelSize - 2}px`, height: `${panelSize - 2}px`,
left: leftActive ? `${panelSize}px` : "0", left: leftActive ? `${panelSize}px` : "0",
right: rightActive ? `${panelSize}px` : "0", right: rightActive ? `${panelSize}px` : "0",
@ -84,9 +86,8 @@ const Panel: React.FC<PanelProps> = ({
case "right": case "right":
return { return {
width: `${panelSize - 2}px`, width: `${panelSize - 2}px`,
height: `calc(100% - ${ height: `calc(100% - ${(topActive ? panelSize : 0) + (bottomActive ? panelSize : 0)
(topActive ? panelSize : 0) + (bottomActive ? panelSize : 0) }px)`,
}px)`,
top: topActive ? `${panelSize}px` : "0", top: topActive ? `${panelSize}px` : "0",
bottom: bottomActive ? `${panelSize}px` : "0", bottom: bottomActive ? `${panelSize}px` : "0",
[side]: "0", [side]: "0",
@ -99,6 +100,7 @@ const Panel: React.FC<PanelProps> = ({
); );
const handleDrop = (e: React.DragEvent, panel: Side) => { const handleDrop = (e: React.DragEvent, panel: Side) => {
e.preventDefault(); e.preventDefault();
const { draggedAsset } = useWidgetStore.getState(); const { draggedAsset } = useWidgetStore.getState();
if (!draggedAsset) return; if (!draggedAsset) return;
@ -109,8 +111,6 @@ const Panel: React.FC<PanelProps> = ({
if (currentWidgetsCount >= maxCapacity) return; if (currentWidgetsCount >= maxCapacity) return;
console.log("draggedAsset: ", draggedAsset);
console.log("panel: ", panel);
addWidgetToPanel(draggedAsset, panel); addWidgetToPanel(draggedAsset, panel);
}; };
@ -139,17 +139,27 @@ const Panel: React.FC<PanelProps> = ({
}; };
// while dublicate check this and add // while dublicate check this and add
const addWidgetToPanel = (asset: any, panel: Side) => { const addWidgetToPanel = async (asset: any, panel: Side) => {
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]
const newWidget = { const newWidget = {
...asset, ...asset,
id: generateUniqueId(), id: generateUniqueId(),
panel, panel,
}; };
try {
let response = await addingWidgets(selectedZone.zoneId, organization, newWidget);
console.log("response: ", response);
if (response.message === "Widget created successfully") {
setSelectedZone((prev) => ({
...prev,
widgets: [...prev.widgets, newWidget],
}));
}
} catch (error) {
console.error("Error adding widget:", error);
}
setSelectedZone((prev) => ({
...prev,
widgets: [...prev.widgets, newWidget],
}));
}; };
useEffect(() => { useEffect(() => {
@ -180,7 +190,7 @@ const Panel: React.FC<PanelProps> = ({
const handleReorder = (fromIndex: number, toIndex: number, panel: Side) => { const handleReorder = (fromIndex: number, toIndex: number, panel: Side) => {
if (!selectedZone) return; // Ensure selectedZone is not null if (!selectedZone) return; // Ensure selectedZone is not null
console.log("selectedZone: ", selectedZone);
setSelectedZone((prev) => { setSelectedZone((prev) => {
if (!prev) return prev; // Ensure prev is not null if (!prev) return prev; // Ensure prev is not null

View File

@ -9,7 +9,9 @@ import useModuleStore from "../../../store/useModuleStore";
import DroppedObjects from "./DroppedFloatingWidgets"; import DroppedObjects from "./DroppedFloatingWidgets";
import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore"; import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore";
import { useZones } from "../../../store/store"; import { useAsset3dWidget, useZones } from "../../../store/store";
import { getZoneData } from "../../../services/realTimeVisulization/zoneData/getZones";
import { getZone2dData } from "../../../services/realTimeVisulization/zoneData/getZoneData";
type Side = "top" | "bottom" | "left" | "right"; type Side = "top" | "bottom" | "left" | "right";
@ -19,6 +21,7 @@ type FormattedZoneData = Record<
{ {
activeSides: Side[]; activeSides: Side[];
panelOrder: Side[]; panelOrder: Side[];
lockedPanels: Side[]; lockedPanels: Side[];
zoneId: string; zoneId: string;
zoneViewPortTarget: number[]; zoneViewPortTarget: number[];
@ -43,25 +46,38 @@ const RealTimeVisulization: React.FC = () => {
const [zonesData, setZonesData] = useState<FormattedZoneData>({}); const [zonesData, setZonesData] = useState<FormattedZoneData>({});
const { selectedZone, setSelectedZone } = useSelectedZoneStore(); const { selectedZone, setSelectedZone } = useSelectedZoneStore();
const { zones } = useZones() const { zones } = useZones()
const [floatingWidgets, setFloatingWidgets] = useState<Record<string, { zoneName: string; zoneId: string; objects: any[] }>>({});
const { widgetSelect, setWidgetSelect } = useAsset3dWidget();
useEffect(() => { useEffect(() => {
const data = Array.isArray(zones) ? zones : []; async function GetZoneData() {
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0];
try {
const response = await getZone2dData(organization);
if (!Array.isArray(response)) {
return;
}
const formattedData = response.reduce<FormattedZoneData>((acc, zone) => {
acc[zone.zoneName] = {
activeSides: [],
panelOrder: [],
lockedPanels: [],
zoneId: zone.zoneId,
zoneViewPortTarget: zone.viewPortCenter,
zoneViewPortPosition: zone.viewPortposition,
widgets: [],
};
return acc;
}, {});
setZonesData(formattedData);
} catch (error) {
console.log('error: ', error);
}
}
const formattedData = data.reduce<FormattedZoneData>((acc, zone) => { GetZoneData();
acc[zone.zoneName] = { }, []); // Removed `zones` from dependencies
activeSides: [],
panelOrder: [],
lockedPanels: [],
zoneId: zone.zoneId,
zoneViewPortTarget: zone.viewPortCenter,
zoneViewPortPosition: zone.viewPortposition,
widgets: [],
};
return acc;
}, {});
setZonesData(formattedData);
}, [zones]);
useEffect(() => { useEffect(() => {
setZonesData((prev) => { setZonesData((prev) => {
@ -81,37 +97,47 @@ const RealTimeVisulization: React.FC = () => {
}; };
}); });
}, [selectedZone]); }, [selectedZone]);
useEffect(() => {
}, [floatingWidgets])
const handleDrop = (event: React.DragEvent<HTMLDivElement>) => { const handleDrop = (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault(); event.preventDefault();
const data = event.dataTransfer.getData("text/plain"); const data = event.dataTransfer.getData("text/plain");
if (!data || !selectedZone.zoneName) return; if (widgetSelect !== "") return;
if (!data || selectedZone.zoneName === "") return;
const droppedData = JSON.parse(data); const droppedData = JSON.parse(data);
const canvasElement = document.getElementById("real-time-vis-canvas"); const canvasElement = document.getElementById("real-time-vis-canvas");
if (!canvasElement) return; if (!canvasElement) return;
const canvasRect = canvasElement.getBoundingClientRect(); const canvasRect = canvasElement.getBoundingClientRect();
const relativeX = event.clientX - canvasRect.left; const relativeX = event.clientX - canvasRect.left;
const relativeY = event.clientY - canvasRect.top; const relativeY = event.clientY - canvasRect.top;
const newObject = { const newObject = {
...droppedData, ...droppedData,
position: [relativeY, relativeX], // Y first because of top/left style position: [relativeY, relativeX], // Y first because of top/left style
}; };
console.log("newObject: ", newObject);
// Only set zone if its not already in the store (prevents overwriting objects) // Only set zone if its not already in the store (prevents overwriting objects)
const existingZone = useDroppedObjectsStore.getState().zones[selectedZone.zoneName]; const existingZone = useDroppedObjectsStore.getState().zones[selectedZone.zoneName];
if (!existingZone) { if (!existingZone) {
useDroppedObjectsStore.getState().setZone(selectedZone.zoneName, selectedZone.zoneId); useDroppedObjectsStore.getState().setZone(selectedZone.zoneName, selectedZone.zoneId);
} }
// Add the dropped object to the zone // Add the dropped object to the zone
useDroppedObjectsStore.getState().addObject(selectedZone.zoneName, newObject); useDroppedObjectsStore.getState().addObject(selectedZone.zoneName, newObject);
setFloatingWidgets((prevWidgets) => ({
...prevWidgets,
[selectedZone.zoneName]: {
...prevWidgets[selectedZone.zoneName],
zoneName: selectedZone.zoneName,
zoneId: selectedZone.zoneId,
objects: [...(prevWidgets[selectedZone.zoneName]?.objects || []), newObject],
},
}));
}; };
return ( return (
<div <div
@ -123,6 +149,7 @@ const RealTimeVisulization: React.FC = () => {
width: isPlaying || activeModule !== "visualization" ? "100vw" : "", width: isPlaying || activeModule !== "visualization" ? "100vw" : "",
left: isPlaying || activeModule !== "visualization" ? "0%" : "", left: isPlaying || activeModule !== "visualization" ? "0%" : "",
}} }}
> >
<div <div
className="scene-container" className="scene-container"

View File

@ -9,7 +9,7 @@ const ProgressCard = ({
}) => ( }) => (
<div className="chart progressBar"> <div className="chart progressBar">
<div className="header">{title}</div> <div className="header">{title}</div>
{data?.stocks.map((stock, index) => ( {data?.stocks?.map((stock, index) => (
<div key={index} className="stock"> <div key={index} className="stock">
<span className="stock-item"> <span className="stock-item">
<span className="stockValues"> <span className="stockValues">

View File

@ -1,5 +1,5 @@
import { useFrame, useThree } from "@react-three/fiber"; import { useFrame, useThree } from "@react-three/fiber";
import { useActiveTool, useCamMode, useDeletableFloorItem, useDeleteModels, useFloorItems, useLoadingProgress, useRenderDistance, useselectedFloorItem, useSelectedItem, useSocketStore, useToggleView, useTransformMode } from "../../../store/store"; import { useActiveTool, useAsset3dWidget, useCamMode, useDeletableFloorItem, useDeleteModels, useFloorItems, useLoadingProgress, useRenderDistance, useselectedFloorItem, useSelectedItem, useSocketStore, useToggleView, useTransformMode } from "../../../store/store";
import assetVisibility from "../geomentries/assets/assetVisibility"; import assetVisibility from "../geomentries/assets/assetVisibility";
import { useEffect } from "react"; import { useEffect } from "react";
import * as THREE from "three"; import * as THREE from "three";
@ -33,7 +33,6 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject
const { setLoadingProgress } = useLoadingProgress(); const { setLoadingProgress } = useLoadingProgress();
const { activeModule } = useModuleStore(); const { activeModule } = useModuleStore();
const { socket } = useSocketStore(); const { socket } = useSocketStore();
const loader = new GLTFLoader(); const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader(); const dracoLoader = new DRACOLoader();
@ -306,6 +305,8 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject
}; };
}, [deleteModels, transformMode, controls, selectedItem, state.camera, state.pointer, activeTool, activeModule]); }, [deleteModels, transformMode, controls, selectedItem, state.camera, state.pointer, activeTool, activeModule]);
useFrame(() => { useFrame(() => {
if (controls) if (controls)
assetVisibility(itemsGroup, state.camera.position, renderDistance); assetVisibility(itemsGroup, state.camera.position, renderDistance);

View File

@ -224,6 +224,7 @@ const WallItemsGroup = ({ currentWallItem, AssetConfigurations, hoveredDeletable
const onDrop = (event: any) => { const onDrop = (event: any) => {
if (!event.dataTransfer?.files[0]) return if (!event.dataTransfer?.files[0]) return
pointer.x = (event.clientX / window.innerWidth) * 2 - 1; pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; pointer.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(pointer, camera); raycaster.setFromCamera(pointer, camera);

View File

@ -9,7 +9,7 @@ import * as THREE from "three";
// Define the shape of the selected card // Define the shape of the selected card
interface SelectedCard { interface SelectedCard {
assetName: string; assetName: string;
uploadedOn: number; uploadedOn: string;
price: number; price: number;
rating: number; rating: number;
views: number; views: number;

View File

@ -19,6 +19,8 @@ import DroppedObjects from "../../components/ui/componets/DroppedFloatingWidgets
// import Simulation from "./simulationtemp/simulation"; // import Simulation from "./simulationtemp/simulation";
import ZoneCentreTarget from "../../components/ui/componets/zoneCameraTarget"; import ZoneCentreTarget from "../../components/ui/componets/zoneCameraTarget";
import ProductionCapacity from "../../components/layout/3D-cards/cards/ProductionCapacity";
import Dropped3dWidgets from "../../components/ui/componets/Dropped3dWidget";
export default function Scene() { export default function Scene() {
@ -43,6 +45,7 @@ export default function Scene() {
}} }}
> >
<Dropped3dWidgets/>
<Controls /> <Controls />
<TransformControl /> <TransformControl />
<SelectionControls /> <SelectionControls />

View File

@ -14,7 +14,7 @@ function Simulation() {
const [processes, setProcesses] = useState([]); const [processes, setProcesses] = useState([]);
useEffect(() => { useEffect(() => {
console.log('simulationPaths: ', simulationPaths);
}, [simulationPaths]); }, [simulationPaths]);
// useEffect(() => { // useEffect(() => {

View File

@ -0,0 +1,30 @@
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
export const addingWidgets = async (
zoneId: string,
organization: string,
widget: {}
) => {
try {
const response = await fetch(`${url_Backend_dwinzo}/api/v2/widget/save`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ organization, zoneId, widget }),
});
if (!response.ok) {
throw new Error("Failed to add 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");
}
}
};

View File

@ -0,0 +1,27 @@
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
console.log("url_Backend_dwinzo: ", url_Backend_dwinzo);
export const getSelect2dZoneData = async (
ZoneId?: string,
organization?: string
) => {
try {
const response = await fetch(
`${url_Backend_dwinzo}/api/v2/ZoneVisualization/${ZoneId}?organization=${organization}`,
{
method: "GET",
headers: {
"Content-Type": "application/json",
},
}
);
if (!response.ok) {
throw new Error("Failed to fetch zoneDatas");
}
return await response.json();
} catch (error: any) {
throw new Error(error.message);
}
};

View File

@ -0,0 +1,23 @@
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
export const getZone2dData = async (organization?: string) => {
try {
const response = await fetch(
`${url_Backend_dwinzo}/api/v2/pageZodeData?organization=${organization}`,
{
method: "GET",
headers: {
"Content-Type": "application/json",
},
}
);
if (!response.ok) {
throw new Error("Failed to fetch zoneDatas");
}
return await response.json();
} catch (error: any) {
throw new Error(error.message);
}
};

View File

@ -1,22 +1,19 @@
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_BACKEND_URL}`; let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
type Side = "top" | "bottom" | "left" | "right"; type Side = "top" | "bottom" | "left" | "right";
export const panelData = async ( export const panelData = async (
organization: string, organization: string,
zoneID: string, zoneId: string,
panelOrder: Side[] 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/v2/panel/save`, {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify({ organization, zoneID, panelOrder }), body: JSON.stringify({ organization, zoneId, panelOrder }),
}); });
if (!response.ok) { if (!response.ok) {
@ -33,16 +30,3 @@ export const panelData = async (
} }
} }
}; };
// {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>
// ))}

View File

@ -348,4 +348,7 @@ export const useEditPosition = create<EditPositionState>((set) => ({
setEdit: (value) => set({ Edit: value }), // Properly updating the state setEdit: (value) => set({ Edit: value }), // Properly updating the state
})); }));
export const useAsset3dWidget = create<any>((set: any) => ({
widgetSelect: "",
setWidgetSelect: (x: any) => set({ widgetSelect: x }),
}));

View File

@ -14,6 +14,7 @@ interface SelectedZoneState {
zoneName: string; zoneName: string;
activeSides: Side[]; activeSides: Side[];
panelOrder: Side[]; panelOrder: Side[];
lockedPanels: Side[]; lockedPanels: Side[];
zoneId: string; zoneId: string;
zoneViewPortTarget: number[]; zoneViewPortTarget: number[];

View File

@ -109,7 +109,7 @@
.productionCapacity-wrapper { .productionCapacity-wrapper {
background: var(--background-color); background-color: var(--background-color);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 6px; gap: 6px;
@ -170,6 +170,9 @@
} }
} }
} }
.bar-chart{
padding:14px 0;
}
} }