- {Object.keys(zonesData).map((zoneName, index) => (
-
{
- console.log('zoneName: ', zoneName);
+
+ {/* Left Arrow */}
+ {showLeftArrow && (
+
+ )}
- setSelectedZone({
- zoneName,
- activeSides: zonesData[zoneName].activeSides || [],
- panelOrder: zonesData[zoneName].panelOrder || [],
- lockedPanels: zonesData[zoneName].lockedPanels || [],
- widgets: zonesData[zoneName].widgets || [],
- })
- // setSelectedZone({
- // zoneName,
- // ...zonesData[zoneName],
- // });
- console.log(selectedZone);
- }}
- >
- {zoneName}
-
- ))}
+ {/* Zones Wrapper */}
+
+ {Object.keys(zonesData).map((zoneName, index) => (
+
{
+ console.log("zoneName: ", zoneName);
+
+ setSelectedZone({
+ zoneName,
+ activeSides: zonesData[zoneName].activeSides || [],
+ panelOrder: zonesData[zoneName].panelOrder || [],
+ lockedPanels: zonesData[zoneName].lockedPanels || [],
+ widgets: zonesData[zoneName].widgets || [],
+ });
+ }}
+ >
+ {zoneName}
+
+ ))}
+
+
+ {/* Right Arrow */}
+ {showRightArrow && (
+
+ )}
);
};
diff --git a/app/src/components/ui/componets/DraggableWidget.tsx b/app/src/components/ui/componets/DraggableWidget.tsx
index 1157292..d25c8ec 100644
--- a/app/src/components/ui/componets/DraggableWidget.tsx
+++ b/app/src/components/ui/componets/DraggableWidget.tsx
@@ -3,20 +3,58 @@ import ProgressCard from "../realTimeVis/charts/ProgressCard";
import PieGraphComponent from "../realTimeVis/charts/PieGraphComponent";
import BarGraphComponent from "../realTimeVis/charts/BarGraphComponent";
import LineGraphComponent from "../realTimeVis/charts/LineGraphComponent";
-import RadarGraphComponent from "../realTimeVis/charts/RadarGraphComponent";
import DoughnutGraphComponent from "../realTimeVis/charts/DoughnutGraphComponent";
import PolarAreaGraphComponent from "../realTimeVis/charts/PolarAreaGraphComponent";
+import {
+ DeleteIcon,
+ DublicateIcon,
+ KebabIcon,
+} from "../../icons/ExportCommonIcons";
+import { useEffect, useRef } from "react";
+
+type Side = "top" | "bottom" | "left" | "right";
+
+interface Widget {
+ id: string;
+ type: string;
+ title: string;
+ panel: Side;
+ data: any;
+}
export const DraggableWidget = ({
widget,
hiddenPanels, // Add this prop to track hidden panels
- index, onReorder
+ index,
+ onReorder,
+ openKebabId,
+ setOpenKebabId,
+ selectedZone,
+ setSelectedZone,
}: {
+ selectedZone: {
+ zoneName: string;
+ activeSides: Side[];
+ panelOrder: Side[];
+ lockedPanels: Side[];
+ widgets: Widget[];
+ };
+ setSelectedZone: React.Dispatch<
+ React.SetStateAction<{
+ zoneName: string;
+ activeSides: Side[];
+ panelOrder: Side[];
+ lockedPanels: Side[];
+ widgets: Widget[];
+ }>
+ >;
widget: any;
hiddenPanels: string[]; // Array of hidden panel names
- index: number; onReorder: (fromIndex: number, toIndex: number) => void
+ index: number;
+ onReorder: (fromIndex: number, toIndex: number) => void;
+ openKebabId: string | null; // ID of the widget with an open kebab menu
+ setOpenKebabId: (id: string | null) => void; // Function to update the open kebab menu
}) => {
-
const { selectedChartId, setSelectedChartId } = useWidgetStore();
const handlePointerDown = () => {
@@ -29,7 +67,7 @@ export const DraggableWidget = ({
const isPanelHidden = hiddenPanels.includes(widget.panel);
const handleDragStart = (event: React.DragEvent
) => {
- event.dataTransfer.setData('text/plain', index.toString()); // Store the index of the dragged widget
+ event.dataTransfer.setData("text/plain", index.toString()); // Store the index of the dragged widget
};
const handleDragEnter = (event: React.DragEvent) => {
event.preventDefault(); // Allow drop
@@ -41,21 +79,89 @@ export const DraggableWidget = ({
const handleDrop = (event: React.DragEvent) => {
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
if (fromIndex !== toIndex) {
onReorder(fromIndex, toIndex); // Call the reorder function passed as a prop
}
};
+ // Handle kebab icon click to toggle kebab options
+ const handleKebabClick = (event: React.MouseEvent) => {
+ event.stopPropagation(); // Prevent click from propagating to parent elements
+ if (openKebabId === widget.id) {
+ // If the current kebab is already open, close it
+ setOpenKebabId(null);
+ } else {
+ // Open the kebab for the clicked widget and close others
+ setOpenKebabId(widget.id);
+ }
+ };
+
+ const deleteSelectedChart = () => {
+ // Filter out the widget to be deleted
+ const updatedWidgets = selectedZone.widgets.filter(
+ (w: Widget) => w.id !== widget.id
+ );
+
+ // Update the selectedZone state
+ setSelectedZone((prevZone: any) => ({
+ ...prevZone,
+ widgets: updatedWidgets, // Replace the widgets array with the updated one
+ }));
+
+ // Close the kebab menu after deletion
+ setOpenKebabId(null);
+ };
+ const widgetRef = useRef(null);
+
+ // Handle click outside to close the kebab menu
+ useEffect(() => {
+ const handleClickOutside = (event: MouseEvent) => {
+ if (
+ widgetRef.current &&
+ !widgetRef.current.contains(event.target as Node)
+ ) {
+ setOpenKebabId(null); // Close the kebab menu if the click is outside
+ }
+ };
+
+ document.addEventListener("mousedown", handleClickOutside);
+
+ // Cleanup event listener on component unmount
+ return () => {
+ document.removeEventListener("mousedown", handleClickOutside);
+ };
+ }, [setOpenKebabId]);
+ const duplicateWidget = () => {
+ // Create a copy of the current widget with a new unique ID
+ const duplicatedWidget: Widget = {
+ ...widget,
+ id: `${widget.id}-copy-${Date.now()}`, // Generate a unique ID using a timestamp
+ };
+
+ // Add the duplicated widget to the selectedZone's widgets array
+ setSelectedZone((prevZone: any) => ({
+ ...prevZone,
+ widgets: [...prevZone.widgets, duplicatedWidget],
+ }));
+
+ // Close the kebab menu after duplication
+ setOpenKebabId(null);
+
+ console.log("Duplicated widget with ID:", duplicatedWidget.id);
+ };
+
+
return (
<>
+ {/* Kebab Icon */}
+
+
+
+
+ {/* Kebab Options */}
+ {openKebabId === widget.id && (
+
+ )}
+
{/* Render charts based on widget type */}
{widget.type === "progress" && (
diff --git a/app/src/components/ui/componets/Panel.tsx b/app/src/components/ui/componets/Panel.tsx
index fc3ac97..01592c7 100644
--- a/app/src/components/ui/componets/Panel.tsx
+++ b/app/src/components/ui/componets/Panel.tsx
@@ -32,6 +32,7 @@ interface PanelProps {
}>
>;
hiddenPanels: string[];
+ setZonesData: React.Dispatch
>; // Add this line
}
const generateUniqueId = () =>
@@ -41,6 +42,7 @@ const Panel: React.FC = ({
selectedZone,
setSelectedZone,
hiddenPanels,
+ setZonesData,
}) => {
const panelRefs = useRef<{ [side in Side]?: HTMLDivElement }>({});
const [panelDimensions, setPanelDimensions] = useState<{
@@ -63,8 +65,9 @@ const Panel: React.FC = ({
case "top":
case "bottom":
return {
- width: `calc(100% - ${(leftActive ? panelSize : 0) + (rightActive ? panelSize : 0)
- }px)`,
+ width: `calc(100% - ${
+ (leftActive ? panelSize : 0) + (rightActive ? panelSize : 0)
+ }px)`,
height: `${panelSize - 2}px`,
left: leftActive ? `${panelSize}px` : "0",
right: rightActive ? `${panelSize}px` : "0",
@@ -74,8 +77,9 @@ const Panel: React.FC = ({
case "right":
return {
width: `${panelSize - 2}px`,
- height: `calc(100% - ${(topActive ? panelSize : 0) + (bottomActive ? panelSize : 0)
- }px)`,
+ height: `calc(100% - ${
+ (topActive ? panelSize : 0) + (bottomActive ? panelSize : 0)
+ }px)`,
top: topActive ? `${panelSize}px` : "0",
bottom: bottomActive ? `${panelSize}px` : "0",
[side]: "0",
@@ -98,8 +102,8 @@ const Panel: React.FC = ({
if (currentWidgetsCount >= maxCapacity) return;
- console.log('draggedAsset: ', draggedAsset);
- console.log('panel: ', panel);
+ console.log("draggedAsset: ", draggedAsset);
+ console.log("panel: ", panel);
addWidgetToPanel(draggedAsset, panel);
};
@@ -168,7 +172,7 @@ const Panel: React.FC = ({
const handleReorder = (fromIndex: number, toIndex: number, panel: Side) => {
if (!selectedZone) return; // Ensure selectedZone is not null
- console.log('selectedZone: ', selectedZone);
+ console.log("selectedZone: ", selectedZone);
setSelectedZone((prev) => {
if (!prev) return prev; // Ensure prev is not null
@@ -191,9 +195,8 @@ const Panel: React.FC = ({
});
};
+ const [openKebabId, setOpenKebabId] = useState(null);
-
-
return (
<>
{selectedZone.activeSides.map((side) => (
@@ -231,6 +234,10 @@ const Panel: React.FC = ({
onReorder={(fromIndex, toIndex) =>
handleReorder(fromIndex, toIndex, side)
}
+ openKebabId={openKebabId}
+ setOpenKebabId={setOpenKebabId}
+ selectedZone={selectedZone}
+ setSelectedZone={setSelectedZone}
/>
))}
@@ -241,5 +248,3 @@ const Panel: React.FC = ({
};
export default Panel;
-
-
diff --git a/app/src/components/ui/componets/RealTimeVisulization.tsx b/app/src/components/ui/componets/RealTimeVisulization.tsx
index 385ae5d..f80b9ae 100644
--- a/app/src/components/ui/componets/RealTimeVisulization.tsx
+++ b/app/src/components/ui/componets/RealTimeVisulization.tsx
@@ -8,7 +8,6 @@ import Scene from "../../../modules/scene/scene";
import useModuleStore from "../../../store/useModuleStore";
import { getZonesApi } from "../../../services/realTimeVisulization/zoneData/getZones";
-
type Side = "top" | "bottom" | "left" | "right";
type FormattedZoneData = Record<
@@ -41,7 +40,87 @@ const RealTimeVisulization: React.FC = () => {
const { isPlaying } = usePlayButtonStore();
const { activeModule } = useModuleStore();
- const [zonesData, setZonesData] = useState({});
+ const [zonesData, setZonesData] = useState<{
+ [key: string]: {
+ activeSides: Side[];
+ panelOrder: Side[];
+ lockedPanels: Side[];
+ widgets: Widget[];
+ };
+ }>({
+ "Manufacturing unit": {
+ activeSides: [],
+ panelOrder: [],
+ lockedPanels: [],
+ widgets: [],
+ },
+ "Assembly unit": {
+ activeSides: [],
+ panelOrder: [],
+ lockedPanels: [],
+ widgets: [],
+ },
+ "Packing unit": {
+ activeSides: [],
+ panelOrder: [],
+ lockedPanels: [],
+ widgets: [],
+ },
+ Warehouse: {
+ activeSides: [],
+ panelOrder: [],
+ lockedPanels: [],
+ widgets: [],
+ },
+ Inventory: {
+ activeSides: [],
+ panelOrder: [],
+ lockedPanels: [],
+ widgets: [],
+ },
+ "Inventory 1": {
+ activeSides: [],
+ panelOrder: [],
+ lockedPanels: [],
+ widgets: [],
+ },
+ "Inventory 2": {
+ activeSides: [],
+ panelOrder: [],
+ lockedPanels: [],
+ widgets: [],
+ },
+ "Inventory 3": {
+ activeSides: [],
+ panelOrder: [],
+ lockedPanels: [],
+ widgets: [],
+ },
+ "Inventory 4": {
+ activeSides: [],
+ panelOrder: [],
+ lockedPanels: [],
+ widgets: [],
+ },
+ "Inventory 5": {
+ activeSides: [],
+ panelOrder: [],
+ lockedPanels: [],
+ widgets: [],
+ },
+ "Inventory 6": {
+ activeSides: [],
+ panelOrder: [],
+ lockedPanels: [],
+ widgets: [],
+ },
+ "Inventory 7": {
+ activeSides: [],
+ panelOrder: [],
+ lockedPanels: [],
+ widgets: [],
+ },
+ });
const { selectedZone, setSelectedZone } = useSelectedZoneStore();
// useEffect(() => {
@@ -73,10 +152,7 @@ const RealTimeVisulization: React.FC = () => {
// GetZoneData();
// }, []);
- useEffect(() => {
-
- console.log('zonesData: ', zonesData);
- }, [zonesData]);
+ useEffect(() => {}, [zonesData]);
// useEffect(() => {
// setZonesData((prev) => {
@@ -111,7 +187,8 @@ const RealTimeVisulization: React.FC = () => {
style={{
height: "100%",
width: "100%",
- borderRadius: isPlaying || activeModule !== "visualization" ? "" : "6px",
+ borderRadius:
+ isPlaying || activeModule !== "visualization" ? "" : "6px",
}}
>
@@ -137,6 +214,7 @@ const RealTimeVisulization: React.FC = () => {
selectedZone={selectedZone}
setSelectedZone={setSelectedZone}
hiddenPanels={hiddenPanels}
+ setZonesData={setZonesData}
/>
>
)}
diff --git a/app/src/components/ui/list/DropDownList.tsx b/app/src/components/ui/list/DropDownList.tsx
index a61054b..98c357e 100644
--- a/app/src/components/ui/list/DropDownList.tsx
+++ b/app/src/components/ui/list/DropDownList.tsx
@@ -39,7 +39,7 @@ const DropDownList: React.FC = ({
useEffect(() => {
async function GetZoneData() {
const response = await getZonesApi("hexrfactory")
- console.log('response: ', response.data);
+
setZoneDataList([{ id: "1", name: "zone1" },
{ id: "2", name: "Zone 2" },])
}
diff --git a/app/src/components/ui/list/List.tsx b/app/src/components/ui/list/List.tsx
index bdf4749..1bf326f 100644
--- a/app/src/components/ui/list/List.tsx
+++ b/app/src/components/ui/list/List.tsx
@@ -8,7 +8,6 @@ interface ListProps {
}
const List: React.FC = ({ items = [] }) => {
- console.log('items: ', items);
return (
<>
{items.length > 0 ? (
diff --git a/app/src/styles/components/visualization/ui/styledWidgets.scss b/app/src/styles/components/visualization/ui/styledWidgets.scss
index 6006308..9c72fae 100644
--- a/app/src/styles/components/visualization/ui/styledWidgets.scss
+++ b/app/src/styles/components/visualization/ui/styledWidgets.scss
@@ -8,7 +8,7 @@
flex-direction: column;
gap: 6px;
width: 100%;
- min-width: 250px;
+ // min-width: 1450px;
.header {
display: flex;
justify-content: center;
diff --git a/app/src/styles/pages/realTimeViz.scss b/app/src/styles/pages/realTimeViz.scss
index f4c9ab9..29650e1 100644
--- a/app/src/styles/pages/realTimeViz.scss
+++ b/app/src/styles/pages/realTimeViz.scss
@@ -1,4 +1,5 @@
@use "../abstracts/variables.scss" as *;
+@use "../abstracts/mixins.scss" as *;
// Main Container
.realTime-viz {
@@ -43,15 +44,28 @@
left: 50%;
transform: translate(-50%, 0);
gap: 6px;
- padding: 4px;
+
border-radius: 8px;
max-width: 80%;
overflow: auto;
- max-width: calc(100% - 450px);
+ // max-width: calc(100% - 450px);
+
&::-webkit-scrollbar {
display: none;
}
+ .arrow {
+ background-color: var(--accent-color);
+ color: var(--background-color);
+ }
+
+ .zones-wrapper {
+ &::-webkit-scrollbar {
+ display: none;
+ }
+
+ }
+
.zone {
width: auto;
background-color: var(--background-color);
@@ -155,7 +169,8 @@
position: relative;
height: 100%;
padding: 10px;
- overflow: auto;
+
+
display: flex;
flex-direction: column;
gap: 10px;
@@ -176,6 +191,65 @@
box-shadow: 0px 2px 6px 0px rgba(60, 60, 67, 0.1);
padding: 6px 0;
background-color: white;
+ position: relative;
+
+ .kebab {
+ width: 30px;
+ height: 30px;
+ position: absolute;
+ top: 0px;
+ right: 0px;
+ z-index: 10;
+
+ @include flex-center;
+ }
+
+ .kebab-options {
+ position: absolute;
+ top: 12px;
+ right: -100px;
+ transform: translate(0px, 0);
+ background-color: var(--background-color);
+ z-index: 10;
+
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+ border-radius: 4px;
+
+ box-shadow: var(--box-shadow-medium);
+
+ .btn {
+ display: flex;
+ gap: 6px;
+ align-items: center;
+ padding: 5px 10px;
+ color: var(--text-color);
+
+ .label {
+ &:hover {
+ color: var(--accent-color);
+
+ }
+ }
+
+ &:hover {
+ background-color: var(--highlight-accent-color);
+ width: 100%;
+
+ svg {
+ &:first-child {
+ fill: var(--accent-color);
+ }
+
+ &:last-child {
+ fill: auto;
+ stroke: var(--accent-color);
+ }
+ }
+ }
+ }
+ }
}
.close-btn {
@@ -236,11 +310,12 @@
}
}
-.playingFlase{
- .zoon-wrapper{
+.playingFlase {
+ .zoon-wrapper {
bottom: 300px !important;
}
}
+
// Side Buttons
.side-button-container {
position: absolute;
@@ -401,3 +476,40 @@
}
}
}
+
+
+
+
+
+.arrow {
+ position: absolute;
+ top: 50%;
+ transform: translateY(-50%);
+ background-color: rgba(0, 0, 0, 0.5);
+ color: white;
+ border: none;
+ cursor: pointer;
+ font-size: 20px;
+ padding: 6px;
+ z-index: 10;
+}
+
+.left-arrow {
+ left: 0;
+}
+
+.right-arrow {
+ right: 0;
+}
+
+.zone {
+ padding: 10px;
+ border: 1px solid #ccc;
+ border-radius: 5px;
+ cursor: pointer;
+}
+
+.zone.active {
+ background-color: #007bff;
+ color: white;
+}
\ No newline at end of file