Merge remote-tracking branch 'origin/ui' into simulation

This commit is contained in:
Jerald-Golden-B 2025-04-08 18:36:41 +05:30
commit 715e551098
12 changed files with 360 additions and 354 deletions

View File

@ -3,13 +3,10 @@ import { useDroppedObjectsStore } from "../../../../store/useDroppedObjectsStore
import useTemplateStore from "../../../../store/useTemplateStore";
import { useSelectedZoneStore } from "../../../../store/useZoneStore";
import { getTemplateData } from "../../../../services/realTimeVisulization/zoneData/getTemplate";
import { deleteTemplateApi } from "../../../../services/realTimeVisulization/zoneData/deleteTemplate";
import { loadTempleteApi } from "../../../../services/realTimeVisulization/zoneData/loadTemplate";
import { useSocketStore } from "../../../../store/store";
const Templates = () => {
const { templates, removeTemplate } = useTemplateStore();
const { setTemplates } = useTemplateStore();
const { templates, removeTemplate, setTemplates } = useTemplateStore();
const { setSelectedZone, selectedZone } = useSelectedZoneStore();
const { visualizationSocket } = useSocketStore();
@ -35,15 +32,11 @@ const Templates = () => {
let deleteTemplate = {
organization: organization,
templateID: id,
}
};
if (visualizationSocket) {
visualizationSocket.emit("v2:viz-template:deleteTemplate", deleteTemplate)
visualizationSocket.emit("v2:viz-template:deleteTemplate", deleteTemplate);
}
removeTemplate(id);
// let response = await deleteTemplateApi(id, organization);
// if (response.message === "Template deleted successfully") {
// }
} catch (error) {
console.error("Error deleting template:", error);
}
@ -60,114 +53,54 @@ const Templates = () => {
organization: organization,
zoneId: selectedZone.zoneId,
templateID: template.id,
}
console.log('template: ', template);
console.log('loadingTemplate: ', loadingTemplate);
};
if (visualizationSocket) {
visualizationSocket.emit("v2:viz-template:addToZone", loadingTemplate)
visualizationSocket.emit("v2:viz-template:addToZone", loadingTemplate);
}
// let response = await loadTempleteApi(template.id, selectedZone.zoneId, organization);
// if (response.message === "Template placed in Zone") {
setSelectedZone({
panelOrder: template.panelOrder,
activeSides: Array.from(new Set(template.panelOrder)), // No merging with previous `activeSides`
widgets: template.widgets,
setSelectedZone({
panelOrder: template.panelOrder,
activeSides: Array.from(new Set(template.panelOrder)), // No merging with previous `activeSides`
widgets: template.widgets,
});
useDroppedObjectsStore.getState().setZone(selectedZone.zoneName, selectedZone.zoneId);
if (Array.isArray(template.floatingWidget)) {
template.floatingWidget.forEach((val: any) => {
useDroppedObjectsStore.getState().addObject(selectedZone.zoneName, val);
});
useDroppedObjectsStore.getState().setZone(selectedZone.zoneName, selectedZone.zoneId);
if (Array.isArray(template.floatingWidget)) {
template.floatingWidget.forEach((val: any) => {
useDroppedObjectsStore.getState().addObject(selectedZone.zoneName, val);
});
}
// }
}
} catch (error) {
console.error("Error loading template:", error);
}
};
return (
<div
className="template-list"
style={{
display: "grid",
gridTemplateColumns: "repeat(auto-fill, minmax(180px, 1fr))",
gap: "1rem",
padding: "1rem",
}}
>
<div className="template-list">
{templates.map((template) => (
<div
key={template.id}
className="template-item"
style={{
border: "1px solid #e0e0e0",
borderRadius: "8px",
padding: "1rem",
transition: "box-shadow 0.3s ease",
}}
>
<div key={template.id} className="template-item">
{template?.snapshot && (
<div style={{ position: "relative", paddingBottom: "56.25%" }}>
{" "}
{/* 16:9 aspect ratio */}
<div className="template-image-container">
<img
src={template.snapshot} // Corrected from template.image to template.snapshot
src={template.snapshot}
alt={`${template.name} preview`}
style={{
position: "absolute",
width: "100%",
height: "100%",
objectFit: "contain",
borderRadius: "4px",
cursor: "pointer",
transition: "transform 0.3s ease",
// ':hover': {
// transform: 'scale(1.05)'
// }
}}
className="template-image"
onClick={() => handleLoadTemplate(template)}
/>
</div>
)}
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginTop: "0.5rem",
}}
>
<div className="template-details">
<div
onClick={() => handleLoadTemplate(template)}
style={{
cursor: "pointer",
fontWeight: "500",
// ':hover': {
// textDecoration: 'underline'
// }
}}
className="template-name"
>
{template.name}
</div>
<button
onClick={() => handleDeleteTemplate(template.id)}
style={{
padding: "0.25rem 0.5rem",
background: "#ff4444",
color: "white",
border: "none",
borderRadius: "4px",
cursor: "pointer",
transition: "opacity 0.3s ease",
// ':hover': {
// opacity: 0.8
// }
}}
className="delete-button"
aria-label="Delete template"
>
Delete
@ -176,14 +109,7 @@ const Templates = () => {
</div>
))}
{templates.length === 0 && (
<div
style={{
textAlign: "center",
color: "#666",
padding: "2rem",
gridColumn: "1 / -1",
}}
>
<div className="no-templates">
No saved templates yet. Create one in the visualization view!
</div>
)}
@ -192,4 +118,3 @@ const Templates = () => {
};
export default Templates;

View File

@ -46,7 +46,7 @@ const WidgetsFloating = () => {
))} */}
{/* Floating 1 */}
<SimpleCard
header={"Todays Money"}
header={"Todays Earnings"}
icon={WalletIcon}
value={"$53,000"}
per={"+55%"}

View File

@ -35,6 +35,7 @@ import {
import useToggleStore from "../../store/useUIToggleStore";
import {
use3DWidget,
useDroppedObjectsStore,
useFloatingWidget,
} from "../../store/useDroppedObjectsStore";
@ -52,8 +53,12 @@ const Tools: React.FC = () => {
const { addTemplate } = useTemplateStore();
const { selectedZone } = useSelectedZoneStore();
const { floatingWidget } = useFloatingWidget();
const { widgets3D } = use3DWidget();
const zones = useDroppedObjectsStore((state) => state.zones);
// wall options
const { toggleView, setToggleView } = useToggleView();
const { setDeleteTool } = useDeleteTool();
@ -409,10 +414,9 @@ const Tools: React.FC = () => {
widgets3D,
selectedZone,
templates,
visualizationSocket
})
}
}
visualizationSocket,
});
}}
>
<SaveTemplateIcon isActive={false} />
</div>

View File

@ -74,6 +74,7 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
const [showLeftArrow, setShowLeftArrow] = useState(false);
const [showRightArrow, setShowRightArrow] = useState(false);
const { floatingWidget, setFloatingWidget } = useFloatingWidget()
const{setSelectedChartId}=useWidgetStore()
// Function to calculate overflow state
@ -178,7 +179,7 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
zoneViewPortPosition: response.viewPortposition || {},
});
} catch (error) {
console.error(error);
}
}

View File

@ -18,6 +18,7 @@ import { deleteWidgetApi } from "../../../services/realTimeVisulization/zoneData
import { useClickOutside } from "./functions/handleWidgetsOuterClick";
import { useSocketStore } from "../../../store/store";
import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
import OuterClick from "../../../utils/outerClick";
type Side = "top" | "bottom" | "left" | "right";
@ -89,6 +90,11 @@ export const DraggableWidget = ({
const isPanelHidden = hiddenPanels.includes(widget.panel);
// OuterClick({
// contextClassName: ["chart-container", "floating", "sidebar-right-wrapper"],
// setMenuVisible: () => setSelectedChartId(null),
// });
const deleteSelectedChart = async () => {
try {
const email = localStorage.getItem("email") || "";
@ -251,36 +257,35 @@ export const DraggableWidget = ({
});
// Track canvas dimensions
// Current: Two identical useEffect hooks for canvas dimensions
// Remove the duplicate and keep only one
useEffect(() => {
const canvas = document.getElementById("real-time-vis-canvas");
if (!canvas) return;
// Current: Two identical useEffect hooks for canvas dimensions
// Remove the duplicate and keep only one
useEffect(() => {
const canvas = document.getElementById("real-time-vis-canvas");
if (!canvas) return;
const updateCanvasDimensions = () => {
const rect = canvas.getBoundingClientRect();
setCanvasDimensions({
width: rect.width,
height: rect.height,
});
};
const updateCanvasDimensions = () => {
const rect = canvas.getBoundingClientRect();
setCanvasDimensions({
width: rect.width,
height: rect.height,
});
};
updateCanvasDimensions();
const resizeObserver = new ResizeObserver(updateCanvasDimensions);
resizeObserver.observe(canvas);
updateCanvasDimensions();
const resizeObserver = new ResizeObserver(updateCanvasDimensions);
resizeObserver.observe(canvas);
return () => resizeObserver.unobserve(canvas);
}, []);
return () => resizeObserver.unobserve(canvas);
}, []);
return (
<>
<style>
{`
:root {
--realTimeViz-container-width: ${canvasDimensions.width * 0.25}px;
--realTimeViz-container-width: ${canvasDimensions.width}px;
--realTimeViz-container-height: ${canvasDimensions.height}px;
}
`}
</style>
@ -296,13 +301,12 @@ useEffect(() => {
onDragOver={handleDragOver}
onDrop={handleDrop}
style={{
// Apply styles based on panel position
width: ["top", "bottom"].includes(widget.panel)
? `calc(${canvasDimensions.width * 0.16}px - 2px)` // For top/bottom panels, set width
: undefined, // Don't set width if it's left or right
? `calc(${canvasDimensions.width}px / 6)`
: undefined,
height: ["left", "right"].includes(widget.panel)
? `calc(${canvasDimensions.height * 0.25}px - 2px)` // For left/right panels, set height
: undefined, // Don't set height if it's top or bottom
? `calc(${canvasDimensions.height - 10}px / 4)`
: undefined,
}}
ref={chartWidget}
onClick={() => setSelectedChartId(widget)}
@ -393,4 +397,4 @@ useEffect(() => {
);
};
// while resize calculate --realTimeViz-container-height pprperly

View File

@ -126,12 +126,11 @@ const DroppedObjects: React.FC = () => {
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0];
let deleteFloatingWidget = {
floatWidgetID: id,
organization: organization,
zoneId: zone.zoneId
}
zoneId: zone.zoneId,
};
if (visualizationSocket) {
visualizationSocket.emit("v2:viz-float:delete", deleteFloatingWidget);
@ -144,9 +143,7 @@ const DroppedObjects: React.FC = () => {
// if (res.message === "FloatingWidget deleted successfully") {
// deleteObject(zoneName, id, index); // Call the deleteObject method from the store
// }
} catch (error) {
}
} catch (error) {}
}
const handlePointerDown = (event: React.PointerEvent, index: number) => {
@ -461,15 +458,14 @@ const DroppedObjects: React.FC = () => {
position: boundedPosition,
},
index: draggingIndex.index,
zoneId: zone.zoneId
}
zoneId: zone.zoneId,
};
if (visualizationSocket) {
visualizationSocket.emit("v2:viz-float:add", updateFloatingWidget);
}
// if (response.message === "Widget updated successfully") {
console.log('boundedPosition: ', boundedPosition);
updateObjectPosition(zoneName, draggingIndex.index, boundedPosition);
// }
@ -503,106 +499,130 @@ const DroppedObjects: React.FC = () => {
setOpenKebabId((prevId) => (prevId === id ? null : id));
};
const containerHeight = getComputedStyle(
document.documentElement
).getPropertyValue("--realTimeViz-container-height");
const containerWidth = getComputedStyle(
document.documentElement
).getPropertyValue("--realTimeViz-container-width");
const heightMultiplier = parseFloat(containerHeight) * 0.14;
const widthMultiplier = parseFloat(containerWidth) * 0.13;
console.log("zone.objects: ", zone.objects);
return (
<div
onPointerMove={handlePointerMove}
onPointerUp={handlePointerUp}
className="floating-wrapper"
>
{zone.objects.map((obj, index) => (
<div
key={`${zoneName}-${index}`}
className={`${obj.className} ${selectedChartId?.id === obj.id && "activeChart"}`}
ref={chartWidget}
style={{
position: "absolute",
top:
typeof obj.position.top === "number"
? `calc(${obj.position.top}px + ${isPlaying && selectedZone.activeSides.includes("top")
? 90
: 0
}px)`
: "auto",
left:
typeof obj.position.left === "number"
? `calc(${obj.position.left}px + ${isPlaying && selectedZone.activeSides.includes("left")
? 90
: 0
}px)`
: "auto",
right:
typeof obj.position.right === "number"
? `calc(${obj.position.right}px + ${isPlaying && selectedZone.activeSides.includes("right")
? 90
: 0
}px)`
: "auto",
bottom:
typeof obj.position.bottom === "number"
? `calc(${obj.position.bottom}px + ${isPlaying && selectedZone.activeSides.includes("bottom")
? 90
: 0
}px)`
: "auto",
}}
onPointerDown={(event) => {
setSelectedChartId(obj);
handlePointerDown(event, index);
}}
>
{obj.className === "floating total-card" ? (
<>
<TotalCardComponent object={obj} />
</>
) : obj.className === "warehouseThroughput floating" ? (
<>
<WarehouseThroughputComponent object={obj} />
</>
) : obj.className === "fleetEfficiency floating" ? (
<>
<FleetEfficiencyComponent object={obj} />
</>
) : null}
{zone.objects.map((obj, index) => {
const topPosition =
typeof obj.position.top === "number"
? `calc(${obj.position.top}px + ${
isPlaying && selectedZone.activeSides.includes("top")
? `${heightMultiplier - 55}px`
: "0px"
})`
: "auto";
const leftPosition =
typeof obj.position.left === "number"
? `calc(${obj.position.left}px + ${
isPlaying && selectedZone.activeSides.includes("left")
? `${widthMultiplier - 100}px`
: "0px"
})`
: "auto";
const rightPosition =
typeof obj.position.right === "number"
? `calc(${obj.position.right}px + ${
isPlaying && selectedZone.activeSides.includes("right")
? `${widthMultiplier - 100}px`
: "0px"
})`
: "auto";
const bottomPosition =
typeof obj.position.bottom === "number"
? `calc(${obj.position.bottom}px + ${
isPlaying && selectedZone.activeSides.includes("bottom")
? `${heightMultiplier - 55}px`
: "0px"
})`
: "auto";
return (
<div
className="icon kebab"
ref={kebabRef}
onClick={(event) => {
event.stopPropagation();
handleKebabClick(obj.id, event);
key={`${zoneName}-${index}`}
className={`${obj.className} ${
selectedChartId?.id === obj.id && "activeChart"
}`}
ref={chartWidget}
style={{
position: "absolute",
top: topPosition,
left: leftPosition,
right: rightPosition,
bottom: bottomPosition,
}}
onPointerDown={(event) => {
setSelectedChartId(obj);
handlePointerDown(event, index);
}}
>
<KebabIcon />
</div>
{openKebabId === obj.id && (
<div className="kebab-options" ref={kebabRef}>
<div
className="dublicate btn"
onClick={(event) => {
event.stopPropagation();
handleDuplicate(zoneName, index); // Call the duplicate handler
}}
>
<div className="icon">
<DublicateIcon />
</div>
<div className="label">Duplicate</div>
</div>
<div
className="edit btn"
onClick={(event) => {
event.stopPropagation();
handleDelete(zoneName, obj.id); // Call the delete handler
}}
>
<div className="icon">
<DeleteIcon />
</div>
<div className="label">Delete</div>
</div>
{obj.className === "floating total-card" ? (
<TotalCardComponent object={obj} />
) : obj.className === "warehouseThroughput floating" ? (
<WarehouseThroughputComponent object={obj} />
) : obj.className === "fleetEfficiency floating" ? (
<FleetEfficiencyComponent object={obj} />
) : null}
<div
className="icon kebab"
ref={kebabRef}
onClick={(event) => {
event.stopPropagation();
handleKebabClick(obj.id, event);
}}
>
<KebabIcon />
</div>
)}
</div>
))}
{openKebabId === obj.id && (
<div className="kebab-options" ref={kebabRef}>
<div
className="dublicate btn"
onClick={(event) => {
event.stopPropagation();
handleDuplicate(zoneName, index); // Call the duplicate handler
}}
>
<div className="icon">
<DublicateIcon />
</div>
<div className="label">Duplicate</div>
</div>
<div
className="edit btn"
onClick={(event) => {
event.stopPropagation();
handleDelete(zoneName, obj.id); // Call the delete handler
}}
>
<div className="icon">
<DeleteIcon />
</div>
<div className="label">Delete</div>
</div>
</div>
)}
</div>
);
})}
{/* Render DistanceLines component during drag */}
{isPlaying === false &&

View File

@ -32,7 +32,6 @@ interface PanelProps {
zoneName: string;
activeSides: Side[];
panelOrder: Side[];
lockedPanels: Side[];
zoneId: string;
zoneViewPortTarget: number[];
@ -41,7 +40,7 @@ interface PanelProps {
}>
>;
hiddenPanels: string[];
setZonesData: React.Dispatch<React.SetStateAction<any>>; // Add this line
setZonesData: React.Dispatch<React.SetStateAction<any>>;
}
const generateUniqueId = () =>
@ -59,14 +58,13 @@ const Panel: React.FC<PanelProps> = ({
[side in Side]?: { width: number; height: number };
}>({});
const [openKebabId, setOpenKebabId] = useState<string | null>(null);
const { isPlaying } = usePlayButtonStore();
const { visualizationSocket } = useSocketStore();
const [canvasDimensions, setCanvasDimensions] = useState({
width: 0,
height: 0,
});
// Track canvas dimensions
useEffect(() => {
const canvas = document.getElementById("real-time-vis-canvas");
@ -80,42 +78,20 @@ const Panel: React.FC<PanelProps> = ({
});
};
// Initial measurement
updateCanvasDimensions();
// Set up ResizeObserver to track changes
const resizeObserver = new ResizeObserver(updateCanvasDimensions);
resizeObserver.observe(canvas);
return () => {
resizeObserver.unobserve(canvas);
};
return () => resizeObserver.unobserve(canvas);
}, []);
useEffect(() => {
const canvas = document.getElementById("real-time-vis-canvas");
if (!canvas) return;
const updateCanvasDimensions = () => {
const rect = canvas.getBoundingClientRect();
setCanvasDimensions({
width: rect.width,
height: rect.height,
});
};
// Initial measurement
updateCanvasDimensions();
// Set up ResizeObserver to track changes
const resizeObserver = new ResizeObserver(updateCanvasDimensions);
resizeObserver.observe(canvas);
return () => {
resizeObserver.unobserve(canvas);
};
}, []);
// Calculate panel size
const panelSize = Math.max(
Math.min(canvasDimensions.width * 0.25, canvasDimensions.height * 0.25),
170 // Min 170px
);
// Define getPanelStyle
const getPanelStyle = useMemo(
() => (side: Side) => {
const currentIndex = selectedZone.panelOrder.indexOf(side);
@ -125,123 +101,105 @@ const Panel: React.FC<PanelProps> = ({
const topActive = previousPanels.includes("top");
const bottomActive = previousPanels.includes("bottom");
// Dynamic panel sizes based on canvas width
const panelSizeWidth = isPlaying ? Math.max(canvasDimensions.width * 0.14, 200) : Math.max(canvasDimensions.width * 0.165, 200); // Ensure minimum width of 200px
const panelSizeHeight = isPlaying ? Math.max(canvasDimensions.width * 0.13, 200) : Math.max(canvasDimensions.width * 0.13, 200); // Ensure minimum height of 200px
switch (side) {
case "top":
case "bottom":
return {
// minWidth: "200px", // Minimum width constraint
width: `calc(100% - ${(leftActive ? panelSizeWidth : 0) +
(rightActive ? panelSizeWidth : 0)
}px)`,
minHeight: "200px", // Minimum height constraint
height: `${panelSizeHeight - 2}px`, // Subtracting for border or margin
left: leftActive ? `${panelSizeWidth}px` : "0",
right: rightActive ? `${panelSizeWidth}px` : "0",
minWidth: "170px",
width: `calc(100% - ${
(leftActive ? panelSize : 0) + (rightActive ? panelSize : 0)
}px)`,
minHeight: "170px",
height: `${panelSize}px`,
left: leftActive ? `${panelSize}px` : "0",
right: rightActive ? `${panelSize}px` : "0",
[side]: "0",
};
case "left":
case "right":
return {
minWidth: "200px", // Minimum width constraint
width: `${panelSizeWidth - 2}px`, // Subtracting for border or margin
// minHeight: "200px", // Minimum height constraint
height: `calc(100% - ${(topActive ? panelSizeHeight : 0) +
(bottomActive ? panelSizeHeight : 0)
}px)`,
top: topActive ? `${panelSizeHeight}px` : "0",
bottom: bottomActive ? `${panelSizeHeight}px` : "0",
minWidth: "170px",
width: `${panelSize}px`,
minHeight: "170px",
height: `calc(100% - ${
(topActive ? panelSize : 0) + (bottomActive ? panelSize : 0)
}px)`,
top: topActive ? `${panelSize}px` : "0",
bottom: bottomActive ? `${panelSize}px` : "0",
[side]: "0",
};
default:
return {};
}
},
[
selectedZone.panelOrder,
isPlaying,
canvasDimensions.width,
canvasDimensions.height,
]
[selectedZone.panelOrder, panelSize]
);
// Handle drop event
const handleDrop = (e: React.DragEvent, panel: Side) => {
e.preventDefault();
const { draggedAsset } = useWidgetStore.getState();
if (!draggedAsset) return;
if (isPanelLocked(panel)) return;
if (!draggedAsset || isPanelLocked(panel)) return;
const currentWidgetsCount = getCurrentWidgetCount(panel);
const maxCapacity = calculatePanelCapacity(panel);
if (currentWidgetsCount >= maxCapacity) return;
addWidgetToPanel(draggedAsset, panel);
if (currentWidgetsCount < maxCapacity) {
addWidgetToPanel(draggedAsset, panel);
}
};
// Check if panel is locked
const isPanelLocked = (panel: Side) =>
selectedZone.lockedPanels.includes(panel);
// Get current widget count in a panel
const getCurrentWidgetCount = (panel: Side) =>
selectedZone.widgets.filter((w) => w.panel === panel).length;
// Calculate panel capacity
const calculatePanelCapacity = (panel: Side) => {
const CHART_WIDTH = 170;
const CHART_HEIGHT = 170;
const FALLBACK_HORIZONTAL_CAPACITY = 5;
const FALLBACK_VERTICAL_CAPACITY = 3;
const CHART_WIDTH = panelSize;
const CHART_HEIGHT = panelSize;
const dimensions = panelDimensions[panel];
if (!dimensions) {
return panel === "top" || panel === "bottom"
? FALLBACK_HORIZONTAL_CAPACITY
: FALLBACK_VERTICAL_CAPACITY;
return panel === "top" || panel === "bottom" ? 5 : 3; // Fallback capacities
}
return panel === "top" || panel === "bottom"
? Math.floor(dimensions.width / CHART_WIDTH)
: Math.floor(dimensions.height / CHART_HEIGHT);
? Math.max(1, Math.floor(dimensions.width / CHART_WIDTH))
: Math.max(1, Math.floor(dimensions.height / CHART_HEIGHT));
};
// while dublicate check this and add
// Add widget to panel
const addWidgetToPanel = async (asset: any, panel: Side) => {
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0];
const newWidget = {
...asset,
id: generateUniqueId(),
panel,
};
let addWidget = {
organization: organization,
zoneId: selectedZone.zoneId,
widget: newWidget,
};
if (visualizationSocket) {
visualizationSocket.emit("v2:viz-widget:add", addWidget);
}
setSelectedZone((prev) => ({
...prev,
widgets: [...prev.widgets, newWidget],
}));
try {
// let response = await addingWidgets(selectedZone.zoneId, organization, newWidget);
// if (response.message === "Widget created successfully") {
// setSelectedZone((prev) => ({
// ...prev,
// widgets: [...prev.widgets, newWidget],
// }));
// }
} catch (error) {
console.error("Error adding widget:", error);
}
};
// Observe panel dimensions
useEffect(() => {
const observers: ResizeObserver[] = [];
const currentPanelRefs = panelRefs.current;
@ -258,6 +216,7 @@ const Panel: React.FC<PanelProps> = ({
}));
}
});
observer.observe(element);
observers.push(observer);
}
@ -268,22 +227,15 @@ const Panel: React.FC<PanelProps> = ({
};
}, [selectedZone.activeSides]);
// Handle widget reordering
const handleReorder = (fromIndex: number, toIndex: number, panel: Side) => {
if (!selectedZone) return; // Ensure selectedZone is not null
setSelectedZone((prev) => {
if (!prev) return prev; // Ensure prev is not null
// Filter widgets for the specified panel
const widgetsInPanel = prev.widgets.filter((w) => w.panel === panel);
// Reorder widgets within the same panel
const reorderedWidgets = arrayMove(widgetsInPanel, fromIndex, toIndex);
// Merge the reordered widgets back into the full list while preserving the order
const updatedWidgets = prev.widgets
.filter((widget) => widget.panel !== panel) // Keep widgets from other panels
.concat(reorderedWidgets); // Add the reordered widgets for the specified panel
.filter((widget) => widget.panel !== panel)
.concat(reorderedWidgets);
return {
...prev,
@ -292,13 +244,42 @@ const Panel: React.FC<PanelProps> = ({
});
};
// Calculate capacities and dimensions
const topWidth = getPanelStyle("top").width;
const bottomWidth = getPanelStyle("bottom").width;
const leftHeight = getPanelStyle("left").height;
const rightHeight = getPanelStyle("right").height;
const topCapacity = calculatePanelCapacity("top");
const bottomCapacity = calculatePanelCapacity("bottom");
const leftCapacity = calculatePanelCapacity("left");
const rightCapacity = calculatePanelCapacity("right");
return (
<>
<style>
{`
:root {
--topWidth: ${topWidth};
--bottomWidth: ${bottomWidth};
--leftHeight: ${leftHeight};
--rightHeight: ${rightHeight};
--topCapacity: ${topCapacity};
--bottomCapacity: ${bottomCapacity};
--leftCapacity: ${leftCapacity};
--rightCapacity: ${rightCapacity};
}
`}
</style>
{selectedZone.activeSides.map((side) => (
<div
key={side}
className={`panel ${side}-panel absolute ${isPlaying ? "" : ""} ${hiddenPanels.includes(side) ? "hidePanel" : ""
}`}
id="panel-wrapper"
className={`panel ${side}-panel absolute ${
hiddenPanels.includes(side) ? "hidePanel" : ""
}`}
style={getPanelStyle(side)}
onDrop={(e) => handleDrop(e, side)}
onDragOver={(e) => e.preventDefault()}
@ -344,5 +325,3 @@ const Panel: React.FC<PanelProps> = ({
};
export default Panel;
// canvasDimensions.width as percent

View File

@ -5,7 +5,7 @@ interface SimpleCardProps {
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>; // React component for SVG icon
value: string;
per: string; // Percentage change
position?: [number, number]
position?: [number, number];
}
const SimpleCard: React.FC<SimpleCardProps> = ({
@ -15,7 +15,6 @@ const SimpleCard: React.FC<SimpleCardProps> = ({
per,
position = [0, 0],
}) => {
const handleDragStart = (event: React.DragEvent<HTMLDivElement>) => {
const rect = event.currentTarget.getBoundingClientRect(); // Get position
const cardData = JSON.stringify({
@ -23,7 +22,7 @@ const SimpleCard: React.FC<SimpleCardProps> = ({
value,
per,
icon: Icon,
className: event.currentTarget.className,
position: [rect.top, rect.left], // ✅ Store position
});

View File

@ -64,7 +64,8 @@ export const handleSaveTemplate = async ({
floatingWidget,
widgets3D,
};
console.log('newTemplate: ', newTemplate);
// Extract organization from email
const email = localStorage.getItem("email") || "";
const organization = email.includes("@")

View File

@ -70,6 +70,67 @@
position: relative;
overflow: auto;
.template-list {
display: flex;
flex-direction: column;
gap: 1rem;
padding: 1rem;
min-height: 50vh;
max-height: 60vh;
}
.template-item {
border: 1px solid #e0e0e0;
border-radius: 8px;
padding: 1rem;
transition: box-shadow 0.3s ease;
}
.template-image-container {
position: relative;
padding-bottom: 56.25%; // 16:9 aspect ratio
}
.template-image {
position: absolute;
width: 100%;
height: 100%;
object-fit: contain;
border-radius: 4px;
cursor: pointer;
transition: transform 0.3s ease;
}
.template-details {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 0.5rem;
}
.template-name {
cursor: pointer;
font-weight: 500;
}
.delete-button {
padding: 0.25rem 0.5rem;
background: #ff4444;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
transition: opacity 0.3s ease;
}
.no-templates {
text-align: center;
color: #666;
padding: 2rem;
grid-column: 1 / -1;
}
.widget-left-sideBar {
min-height: 50vh;
max-height: 60vh;

View File

@ -24,9 +24,17 @@
}
.floating {
width: 100%;
max-width: 250px;
min-height: 83px;
width: calc(var(--realTimeViz-container-width) * 0.2);
height: calc(var(--realTimeViz-container-width) * 0.05);
min-width: 250px;
max-width: 300px;
min-height: 83px !important;
// max-height: 100px !important;
background: var(--background-color);
border: 1.23px solid var(--border-color);
box-shadow: 0px 4.91px 4.91px 0px #0000001c;
@ -60,9 +68,8 @@
display: flex;
background-color: var(--background-color);
position: absolute;
bottom: 10px;
// bottom: 10px;
left: 50%;
transform: translate(-50%, 0);
gap: 6px;
border-radius: 8px;
@ -70,6 +77,7 @@
overflow: auto;
max-width: calc(100% - 500px);
z-index: 3;
transform: translate(-50%, -100%);
&::-webkit-scrollbar {
display: none;
@ -116,8 +124,8 @@
}
.zone-wrapper.bottom {
bottom: calc(var(--realTimeViz-container-height) * 0.27);
bottom: 200px;
top: var(--bottomWidth);
// bottom: 200px;
}
.content-container {
@ -138,7 +146,7 @@
display: flex;
background-color: rgba(224, 223, 255, 0.5);
position: absolute;
bottom: 10px;
// bottom: 10px;
left: 50%;
transform: translate(-50%, 0);
gap: 6px;
@ -203,9 +211,9 @@
.chart-container {
width: 100%;
min-height: 150px;
max-height: 100%;
// border: 1px dashed var(--background-color-gray);
border: 1px dashed var(--background-color-gray);
border-radius: 8px;
box-shadow: var(--box-shadow-medium);
padding: 6px 0;
@ -343,7 +351,7 @@
.chart-container {
width: 100%;
min-height: 160px;
min-height: 150px;
max-height: 100%;
border-radius: 8px;
box-shadow: var(--box-shadow-medium);
@ -362,7 +370,7 @@
.playingFlase {
.zone-wrapper.bottom {
bottom: calc(var(--realTimeViz-container-height) * 0.25);
top: var(--bottomWidth);
// bottom: 210px;
}
}
@ -786,4 +794,4 @@
}
}
}
}
}

View File

@ -1,7 +1,7 @@
import React from "react";
interface OuterClickProps {
contextClassName: string;
contextClassName: string[]; // Make sure this is an array of strings
setMenuVisible: React.Dispatch<React.SetStateAction<boolean>>;
}
@ -11,8 +11,12 @@ export default function OuterClick({
}: OuterClickProps) {
const handleClick = (event: MouseEvent) => {
const targets = event.target as HTMLElement;
// Check if the click is outside the selectable-dropdown-wrapper
if (!targets.closest(`.${contextClassName}`)) {
// Check if the click is outside of any of the provided class names
const isOutside = contextClassName.every(
(className) => !targets.closest(`.${className}`)
);
if (isOutside) {
setMenuVisible(false); // Close the menu by updating the state
}
};
@ -23,7 +27,7 @@ export default function OuterClick({
return () => {
document.removeEventListener("click", handleClick);
};
}, []);
}, [contextClassName]); // Add contextClassName to dependency array to handle any changes
return null; // This component doesn't render anything
}