Merge remote-tracking branch 'origin/ui' into simulation
This commit is contained in:
commit
715e551098
|
@ -3,13 +3,10 @@ import { useDroppedObjectsStore } from "../../../../store/useDroppedObjectsStore
|
||||||
import useTemplateStore from "../../../../store/useTemplateStore";
|
import useTemplateStore from "../../../../store/useTemplateStore";
|
||||||
import { useSelectedZoneStore } from "../../../../store/useZoneStore";
|
import { useSelectedZoneStore } from "../../../../store/useZoneStore";
|
||||||
import { getTemplateData } from "../../../../services/realTimeVisulization/zoneData/getTemplate";
|
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";
|
import { useSocketStore } from "../../../../store/store";
|
||||||
|
|
||||||
const Templates = () => {
|
const Templates = () => {
|
||||||
const { templates, removeTemplate } = useTemplateStore();
|
const { templates, removeTemplate, setTemplates } = useTemplateStore();
|
||||||
const { setTemplates } = useTemplateStore();
|
|
||||||
const { setSelectedZone, selectedZone } = useSelectedZoneStore();
|
const { setSelectedZone, selectedZone } = useSelectedZoneStore();
|
||||||
const { visualizationSocket } = useSocketStore();
|
const { visualizationSocket } = useSocketStore();
|
||||||
|
|
||||||
|
@ -35,15 +32,11 @@ const Templates = () => {
|
||||||
let deleteTemplate = {
|
let deleteTemplate = {
|
||||||
organization: organization,
|
organization: organization,
|
||||||
templateID: id,
|
templateID: id,
|
||||||
}
|
};
|
||||||
if (visualizationSocket) {
|
if (visualizationSocket) {
|
||||||
visualizationSocket.emit("v2:viz-template:deleteTemplate", deleteTemplate)
|
visualizationSocket.emit("v2:viz-template:deleteTemplate", deleteTemplate);
|
||||||
}
|
}
|
||||||
removeTemplate(id);
|
removeTemplate(id);
|
||||||
// let response = await deleteTemplateApi(id, organization);
|
|
||||||
|
|
||||||
// if (response.message === "Template deleted successfully") {
|
|
||||||
// }
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error deleting template:", error);
|
console.error("Error deleting template:", error);
|
||||||
}
|
}
|
||||||
|
@ -60,114 +53,54 @@ const Templates = () => {
|
||||||
organization: organization,
|
organization: organization,
|
||||||
zoneId: selectedZone.zoneId,
|
zoneId: selectedZone.zoneId,
|
||||||
templateID: template.id,
|
templateID: template.id,
|
||||||
}
|
};
|
||||||
console.log('template: ', template);
|
|
||||||
console.log('loadingTemplate: ', loadingTemplate);
|
|
||||||
|
|
||||||
if (visualizationSocket) {
|
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({
|
||||||
setSelectedZone({
|
panelOrder: template.panelOrder,
|
||||||
panelOrder: template.panelOrder,
|
activeSides: Array.from(new Set(template.panelOrder)), // No merging with previous `activeSides`
|
||||||
activeSides: Array.from(new Set(template.panelOrder)), // No merging with previous `activeSides`
|
widgets: template.widgets,
|
||||||
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) {
|
} catch (error) {
|
||||||
console.error("Error loading template:", error);
|
console.error("Error loading template:", error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className="template-list">
|
||||||
className="template-list"
|
|
||||||
style={{
|
|
||||||
display: "grid",
|
|
||||||
gridTemplateColumns: "repeat(auto-fill, minmax(180px, 1fr))",
|
|
||||||
gap: "1rem",
|
|
||||||
padding: "1rem",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{templates.map((template) => (
|
{templates.map((template) => (
|
||||||
<div
|
<div key={template.id} className="template-item">
|
||||||
key={template.id}
|
|
||||||
className="template-item"
|
|
||||||
style={{
|
|
||||||
border: "1px solid #e0e0e0",
|
|
||||||
borderRadius: "8px",
|
|
||||||
padding: "1rem",
|
|
||||||
transition: "box-shadow 0.3s ease",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{template?.snapshot && (
|
{template?.snapshot && (
|
||||||
<div style={{ position: "relative", paddingBottom: "56.25%" }}>
|
<div className="template-image-container">
|
||||||
{" "}
|
|
||||||
{/* 16:9 aspect ratio */}
|
|
||||||
<img
|
<img
|
||||||
src={template.snapshot} // Corrected from template.image to template.snapshot
|
src={template.snapshot}
|
||||||
alt={`${template.name} preview`}
|
alt={`${template.name} preview`}
|
||||||
style={{
|
className="template-image"
|
||||||
position: "absolute",
|
|
||||||
width: "100%",
|
|
||||||
height: "100%",
|
|
||||||
objectFit: "contain",
|
|
||||||
borderRadius: "4px",
|
|
||||||
cursor: "pointer",
|
|
||||||
transition: "transform 0.3s ease",
|
|
||||||
// ':hover': {
|
|
||||||
// transform: 'scale(1.05)'
|
|
||||||
// }
|
|
||||||
}}
|
|
||||||
onClick={() => handleLoadTemplate(template)}
|
onClick={() => handleLoadTemplate(template)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div
|
<div className="template-details">
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
alignItems: "center",
|
|
||||||
marginTop: "0.5rem",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
onClick={() => handleLoadTemplate(template)}
|
onClick={() => handleLoadTemplate(template)}
|
||||||
style={{
|
className="template-name"
|
||||||
cursor: "pointer",
|
|
||||||
fontWeight: "500",
|
|
||||||
// ':hover': {
|
|
||||||
// textDecoration: 'underline'
|
|
||||||
// }
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{template.name}
|
{template.name}
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleDeleteTemplate(template.id)}
|
onClick={() => handleDeleteTemplate(template.id)}
|
||||||
style={{
|
className="delete-button"
|
||||||
padding: "0.25rem 0.5rem",
|
|
||||||
background: "#ff4444",
|
|
||||||
color: "white",
|
|
||||||
border: "none",
|
|
||||||
borderRadius: "4px",
|
|
||||||
cursor: "pointer",
|
|
||||||
transition: "opacity 0.3s ease",
|
|
||||||
// ':hover': {
|
|
||||||
// opacity: 0.8
|
|
||||||
// }
|
|
||||||
}}
|
|
||||||
aria-label="Delete template"
|
aria-label="Delete template"
|
||||||
>
|
>
|
||||||
Delete
|
Delete
|
||||||
|
@ -176,14 +109,7 @@ const Templates = () => {
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{templates.length === 0 && (
|
{templates.length === 0 && (
|
||||||
<div
|
<div className="no-templates">
|
||||||
style={{
|
|
||||||
textAlign: "center",
|
|
||||||
color: "#666",
|
|
||||||
padding: "2rem",
|
|
||||||
gridColumn: "1 / -1",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
No saved templates yet. Create one in the visualization view!
|
No saved templates yet. Create one in the visualization view!
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -192,4 +118,3 @@ const Templates = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Templates;
|
export default Templates;
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ const WidgetsFloating = () => {
|
||||||
))} */}
|
))} */}
|
||||||
{/* Floating 1 */}
|
{/* Floating 1 */}
|
||||||
<SimpleCard
|
<SimpleCard
|
||||||
header={"Today’s Money"}
|
header={"Today’s Earnings"}
|
||||||
icon={WalletIcon}
|
icon={WalletIcon}
|
||||||
value={"$53,000"}
|
value={"$53,000"}
|
||||||
per={"+55%"}
|
per={"+55%"}
|
||||||
|
|
|
@ -35,6 +35,7 @@ import {
|
||||||
import useToggleStore from "../../store/useUIToggleStore";
|
import useToggleStore from "../../store/useUIToggleStore";
|
||||||
import {
|
import {
|
||||||
use3DWidget,
|
use3DWidget,
|
||||||
|
useDroppedObjectsStore,
|
||||||
useFloatingWidget,
|
useFloatingWidget,
|
||||||
} from "../../store/useDroppedObjectsStore";
|
} from "../../store/useDroppedObjectsStore";
|
||||||
|
|
||||||
|
@ -52,8 +53,12 @@ const Tools: React.FC = () => {
|
||||||
const { addTemplate } = useTemplateStore();
|
const { addTemplate } = useTemplateStore();
|
||||||
const { selectedZone } = useSelectedZoneStore();
|
const { selectedZone } = useSelectedZoneStore();
|
||||||
const { floatingWidget } = useFloatingWidget();
|
const { floatingWidget } = useFloatingWidget();
|
||||||
|
|
||||||
const { widgets3D } = use3DWidget();
|
const { widgets3D } = use3DWidget();
|
||||||
|
|
||||||
|
const zones = useDroppedObjectsStore((state) => state.zones);
|
||||||
|
|
||||||
|
|
||||||
// wall options
|
// wall options
|
||||||
const { toggleView, setToggleView } = useToggleView();
|
const { toggleView, setToggleView } = useToggleView();
|
||||||
const { setDeleteTool } = useDeleteTool();
|
const { setDeleteTool } = useDeleteTool();
|
||||||
|
@ -409,10 +414,9 @@ const Tools: React.FC = () => {
|
||||||
widgets3D,
|
widgets3D,
|
||||||
selectedZone,
|
selectedZone,
|
||||||
templates,
|
templates,
|
||||||
visualizationSocket
|
visualizationSocket,
|
||||||
})
|
});
|
||||||
}
|
}}
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<SaveTemplateIcon isActive={false} />
|
<SaveTemplateIcon isActive={false} />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -74,6 +74,7 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
|
||||||
const [showLeftArrow, setShowLeftArrow] = useState(false);
|
const [showLeftArrow, setShowLeftArrow] = useState(false);
|
||||||
const [showRightArrow, setShowRightArrow] = useState(false);
|
const [showRightArrow, setShowRightArrow] = useState(false);
|
||||||
const { floatingWidget, setFloatingWidget } = useFloatingWidget()
|
const { floatingWidget, setFloatingWidget } = useFloatingWidget()
|
||||||
|
|
||||||
const{setSelectedChartId}=useWidgetStore()
|
const{setSelectedChartId}=useWidgetStore()
|
||||||
|
|
||||||
// Function to calculate overflow state
|
// Function to calculate overflow state
|
||||||
|
@ -178,7 +179,7 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
|
||||||
zoneViewPortPosition: response.viewPortposition || {},
|
zoneViewPortPosition: response.viewPortposition || {},
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ import { deleteWidgetApi } from "../../../services/realTimeVisulization/zoneData
|
||||||
import { useClickOutside } from "./functions/handleWidgetsOuterClick";
|
import { useClickOutside } from "./functions/handleWidgetsOuterClick";
|
||||||
import { useSocketStore } from "../../../store/store";
|
import { useSocketStore } from "../../../store/store";
|
||||||
import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
|
import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
|
||||||
|
import OuterClick from "../../../utils/outerClick";
|
||||||
|
|
||||||
type Side = "top" | "bottom" | "left" | "right";
|
type Side = "top" | "bottom" | "left" | "right";
|
||||||
|
|
||||||
|
@ -89,6 +90,11 @@ export const DraggableWidget = ({
|
||||||
|
|
||||||
const isPanelHidden = hiddenPanels.includes(widget.panel);
|
const isPanelHidden = hiddenPanels.includes(widget.panel);
|
||||||
|
|
||||||
|
// OuterClick({
|
||||||
|
// contextClassName: ["chart-container", "floating", "sidebar-right-wrapper"],
|
||||||
|
// setMenuVisible: () => setSelectedChartId(null),
|
||||||
|
// });
|
||||||
|
|
||||||
const deleteSelectedChart = async () => {
|
const deleteSelectedChart = async () => {
|
||||||
try {
|
try {
|
||||||
const email = localStorage.getItem("email") || "";
|
const email = localStorage.getItem("email") || "";
|
||||||
|
@ -251,36 +257,35 @@ export const DraggableWidget = ({
|
||||||
});
|
});
|
||||||
// Track canvas dimensions
|
// 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
|
const updateCanvasDimensions = () => {
|
||||||
// Remove the duplicate and keep only one
|
const rect = canvas.getBoundingClientRect();
|
||||||
useEffect(() => {
|
setCanvasDimensions({
|
||||||
const canvas = document.getElementById("real-time-vis-canvas");
|
width: rect.width,
|
||||||
if (!canvas) return;
|
height: rect.height,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const updateCanvasDimensions = () => {
|
updateCanvasDimensions();
|
||||||
const rect = canvas.getBoundingClientRect();
|
const resizeObserver = new ResizeObserver(updateCanvasDimensions);
|
||||||
setCanvasDimensions({
|
resizeObserver.observe(canvas);
|
||||||
width: rect.width,
|
|
||||||
height: rect.height,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
updateCanvasDimensions();
|
return () => resizeObserver.unobserve(canvas);
|
||||||
const resizeObserver = new ResizeObserver(updateCanvasDimensions);
|
}, []);
|
||||||
resizeObserver.observe(canvas);
|
|
||||||
|
|
||||||
return () => resizeObserver.unobserve(canvas);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<style>
|
<style>
|
||||||
{`
|
{`
|
||||||
:root {
|
:root {
|
||||||
--realTimeViz-container-width: ${canvasDimensions.width * 0.25}px;
|
--realTimeViz-container-width: ${canvasDimensions.width}px;
|
||||||
--realTimeViz-container-height: ${canvasDimensions.height}px;
|
--realTimeViz-container-height: ${canvasDimensions.height}px;
|
||||||
|
|
||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
</style>
|
</style>
|
||||||
|
@ -296,13 +301,12 @@ useEffect(() => {
|
||||||
onDragOver={handleDragOver}
|
onDragOver={handleDragOver}
|
||||||
onDrop={handleDrop}
|
onDrop={handleDrop}
|
||||||
style={{
|
style={{
|
||||||
// Apply styles based on panel position
|
|
||||||
width: ["top", "bottom"].includes(widget.panel)
|
width: ["top", "bottom"].includes(widget.panel)
|
||||||
? `calc(${canvasDimensions.width * 0.16}px - 2px)` // For top/bottom panels, set width
|
? `calc(${canvasDimensions.width}px / 6)`
|
||||||
: undefined, // Don't set width if it's left or right
|
: undefined,
|
||||||
height: ["left", "right"].includes(widget.panel)
|
height: ["left", "right"].includes(widget.panel)
|
||||||
? `calc(${canvasDimensions.height * 0.25}px - 2px)` // For left/right panels, set height
|
? `calc(${canvasDimensions.height - 10}px / 4)`
|
||||||
: undefined, // Don't set height if it's top or bottom
|
: undefined,
|
||||||
}}
|
}}
|
||||||
ref={chartWidget}
|
ref={chartWidget}
|
||||||
onClick={() => setSelectedChartId(widget)}
|
onClick={() => setSelectedChartId(widget)}
|
||||||
|
@ -393,4 +397,4 @@ useEffect(() => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// while resize calculate --realTimeViz-container-height pprperly
|
|
||||||
|
|
|
@ -126,12 +126,11 @@ const DroppedObjects: React.FC = () => {
|
||||||
const email = localStorage.getItem("email") || "";
|
const email = localStorage.getItem("email") || "";
|
||||||
const organization = email?.split("@")[1]?.split(".")[0];
|
const organization = email?.split("@")[1]?.split(".")[0];
|
||||||
|
|
||||||
|
|
||||||
let deleteFloatingWidget = {
|
let deleteFloatingWidget = {
|
||||||
floatWidgetID: id,
|
floatWidgetID: id,
|
||||||
organization: organization,
|
organization: organization,
|
||||||
zoneId: zone.zoneId
|
zoneId: zone.zoneId,
|
||||||
}
|
};
|
||||||
|
|
||||||
if (visualizationSocket) {
|
if (visualizationSocket) {
|
||||||
visualizationSocket.emit("v2:viz-float:delete", deleteFloatingWidget);
|
visualizationSocket.emit("v2:viz-float:delete", deleteFloatingWidget);
|
||||||
|
@ -144,9 +143,7 @@ const DroppedObjects: React.FC = () => {
|
||||||
// if (res.message === "FloatingWidget deleted successfully") {
|
// if (res.message === "FloatingWidget deleted successfully") {
|
||||||
// deleteObject(zoneName, id, index); // Call the deleteObject method from the store
|
// deleteObject(zoneName, id, index); // Call the deleteObject method from the store
|
||||||
// }
|
// }
|
||||||
} catch (error) {
|
} catch (error) {}
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handlePointerDown = (event: React.PointerEvent, index: number) => {
|
const handlePointerDown = (event: React.PointerEvent, index: number) => {
|
||||||
|
@ -461,15 +458,14 @@ const DroppedObjects: React.FC = () => {
|
||||||
position: boundedPosition,
|
position: boundedPosition,
|
||||||
},
|
},
|
||||||
index: draggingIndex.index,
|
index: draggingIndex.index,
|
||||||
zoneId: zone.zoneId
|
zoneId: zone.zoneId,
|
||||||
}
|
};
|
||||||
if (visualizationSocket) {
|
if (visualizationSocket) {
|
||||||
visualizationSocket.emit("v2:viz-float:add", updateFloatingWidget);
|
visualizationSocket.emit("v2:viz-float:add", updateFloatingWidget);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (response.message === "Widget updated successfully") {
|
// if (response.message === "Widget updated successfully") {
|
||||||
|
|
||||||
console.log('boundedPosition: ', boundedPosition);
|
|
||||||
updateObjectPosition(zoneName, draggingIndex.index, boundedPosition);
|
updateObjectPosition(zoneName, draggingIndex.index, boundedPosition);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
@ -503,106 +499,130 @@ const DroppedObjects: React.FC = () => {
|
||||||
setOpenKebabId((prevId) => (prevId === id ? null : id));
|
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 (
|
return (
|
||||||
<div
|
<div
|
||||||
onPointerMove={handlePointerMove}
|
onPointerMove={handlePointerMove}
|
||||||
onPointerUp={handlePointerUp}
|
onPointerUp={handlePointerUp}
|
||||||
className="floating-wrapper"
|
className="floating-wrapper"
|
||||||
>
|
>
|
||||||
{zone.objects.map((obj, index) => (
|
{zone.objects.map((obj, index) => {
|
||||||
<div
|
const topPosition =
|
||||||
key={`${zoneName}-${index}`}
|
typeof obj.position.top === "number"
|
||||||
className={`${obj.className} ${selectedChartId?.id === obj.id && "activeChart"}`}
|
? `calc(${obj.position.top}px + ${
|
||||||
ref={chartWidget}
|
isPlaying && selectedZone.activeSides.includes("top")
|
||||||
style={{
|
? `${heightMultiplier - 55}px`
|
||||||
position: "absolute",
|
: "0px"
|
||||||
top:
|
})`
|
||||||
typeof obj.position.top === "number"
|
: "auto";
|
||||||
? `calc(${obj.position.top}px + ${isPlaying && selectedZone.activeSides.includes("top")
|
|
||||||
? 90
|
const leftPosition =
|
||||||
: 0
|
typeof obj.position.left === "number"
|
||||||
}px)`
|
? `calc(${obj.position.left}px + ${
|
||||||
: "auto",
|
isPlaying && selectedZone.activeSides.includes("left")
|
||||||
left:
|
? `${widthMultiplier - 100}px`
|
||||||
typeof obj.position.left === "number"
|
: "0px"
|
||||||
? `calc(${obj.position.left}px + ${isPlaying && selectedZone.activeSides.includes("left")
|
})`
|
||||||
? 90
|
: "auto";
|
||||||
: 0
|
|
||||||
}px)`
|
const rightPosition =
|
||||||
: "auto",
|
typeof obj.position.right === "number"
|
||||||
right:
|
? `calc(${obj.position.right}px + ${
|
||||||
typeof obj.position.right === "number"
|
isPlaying && selectedZone.activeSides.includes("right")
|
||||||
? `calc(${obj.position.right}px + ${isPlaying && selectedZone.activeSides.includes("right")
|
? `${widthMultiplier - 100}px`
|
||||||
? 90
|
: "0px"
|
||||||
: 0
|
})`
|
||||||
}px)`
|
: "auto";
|
||||||
: "auto",
|
|
||||||
bottom:
|
const bottomPosition =
|
||||||
typeof obj.position.bottom === "number"
|
typeof obj.position.bottom === "number"
|
||||||
? `calc(${obj.position.bottom}px + ${isPlaying && selectedZone.activeSides.includes("bottom")
|
? `calc(${obj.position.bottom}px + ${
|
||||||
? 90
|
isPlaying && selectedZone.activeSides.includes("bottom")
|
||||||
: 0
|
? `${heightMultiplier - 55}px`
|
||||||
}px)`
|
: "0px"
|
||||||
: "auto",
|
})`
|
||||||
}}
|
: "auto";
|
||||||
onPointerDown={(event) => {
|
|
||||||
setSelectedChartId(obj);
|
return (
|
||||||
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}
|
|
||||||
<div
|
<div
|
||||||
className="icon kebab"
|
key={`${zoneName}-${index}`}
|
||||||
ref={kebabRef}
|
className={`${obj.className} ${
|
||||||
onClick={(event) => {
|
selectedChartId?.id === obj.id && "activeChart"
|
||||||
event.stopPropagation();
|
}`}
|
||||||
handleKebabClick(obj.id, event);
|
ref={chartWidget}
|
||||||
|
style={{
|
||||||
|
position: "absolute",
|
||||||
|
top: topPosition,
|
||||||
|
left: leftPosition,
|
||||||
|
right: rightPosition,
|
||||||
|
bottom: bottomPosition,
|
||||||
|
}}
|
||||||
|
onPointerDown={(event) => {
|
||||||
|
setSelectedChartId(obj);
|
||||||
|
handlePointerDown(event, index);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<KebabIcon />
|
{obj.className === "floating total-card" ? (
|
||||||
</div>
|
<TotalCardComponent object={obj} />
|
||||||
{openKebabId === obj.id && (
|
) : obj.className === "warehouseThroughput floating" ? (
|
||||||
<div className="kebab-options" ref={kebabRef}>
|
<WarehouseThroughputComponent object={obj} />
|
||||||
<div
|
) : obj.className === "fleetEfficiency floating" ? (
|
||||||
className="dublicate btn"
|
<FleetEfficiencyComponent object={obj} />
|
||||||
onClick={(event) => {
|
) : null}
|
||||||
event.stopPropagation();
|
|
||||||
handleDuplicate(zoneName, index); // Call the duplicate handler
|
<div
|
||||||
}}
|
className="icon kebab"
|
||||||
>
|
ref={kebabRef}
|
||||||
<div className="icon">
|
onClick={(event) => {
|
||||||
<DublicateIcon />
|
event.stopPropagation();
|
||||||
</div>
|
handleKebabClick(obj.id, event);
|
||||||
<div className="label">Duplicate</div>
|
}}
|
||||||
</div>
|
>
|
||||||
<div
|
<KebabIcon />
|
||||||
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>
|
||||||
)}
|
|
||||||
</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 */}
|
{/* Render DistanceLines component during drag */}
|
||||||
{isPlaying === false &&
|
{isPlaying === false &&
|
||||||
|
|
|
@ -32,7 +32,6 @@ 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[];
|
||||||
|
@ -41,7 +40,7 @@ interface PanelProps {
|
||||||
}>
|
}>
|
||||||
>;
|
>;
|
||||||
hiddenPanels: string[];
|
hiddenPanels: string[];
|
||||||
setZonesData: React.Dispatch<React.SetStateAction<any>>; // Add this line
|
setZonesData: React.Dispatch<React.SetStateAction<any>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const generateUniqueId = () =>
|
const generateUniqueId = () =>
|
||||||
|
@ -59,14 +58,13 @@ const Panel: React.FC<PanelProps> = ({
|
||||||
[side in Side]?: { width: number; height: number };
|
[side in Side]?: { width: number; height: number };
|
||||||
}>({});
|
}>({});
|
||||||
const [openKebabId, setOpenKebabId] = useState<string | null>(null);
|
const [openKebabId, setOpenKebabId] = useState<string | null>(null);
|
||||||
|
|
||||||
const { isPlaying } = usePlayButtonStore();
|
const { isPlaying } = usePlayButtonStore();
|
||||||
const { visualizationSocket } = useSocketStore();
|
const { visualizationSocket } = useSocketStore();
|
||||||
|
|
||||||
const [canvasDimensions, setCanvasDimensions] = useState({
|
const [canvasDimensions, setCanvasDimensions] = useState({
|
||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Track canvas dimensions
|
// Track canvas dimensions
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const canvas = document.getElementById("real-time-vis-canvas");
|
const canvas = document.getElementById("real-time-vis-canvas");
|
||||||
|
@ -80,42 +78,20 @@ const Panel: React.FC<PanelProps> = ({
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Initial measurement
|
|
||||||
updateCanvasDimensions();
|
updateCanvasDimensions();
|
||||||
|
|
||||||
// Set up ResizeObserver to track changes
|
|
||||||
const resizeObserver = new ResizeObserver(updateCanvasDimensions);
|
const resizeObserver = new ResizeObserver(updateCanvasDimensions);
|
||||||
resizeObserver.observe(canvas);
|
resizeObserver.observe(canvas);
|
||||||
|
|
||||||
return () => {
|
return () => resizeObserver.unobserve(canvas);
|
||||||
resizeObserver.unobserve(canvas);
|
|
||||||
};
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
// Calculate panel size
|
||||||
const canvas = document.getElementById("real-time-vis-canvas");
|
const panelSize = Math.max(
|
||||||
if (!canvas) return;
|
Math.min(canvasDimensions.width * 0.25, canvasDimensions.height * 0.25),
|
||||||
|
170 // Min 170px
|
||||||
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);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
|
// Define getPanelStyle
|
||||||
const getPanelStyle = useMemo(
|
const getPanelStyle = useMemo(
|
||||||
() => (side: Side) => {
|
() => (side: Side) => {
|
||||||
const currentIndex = selectedZone.panelOrder.indexOf(side);
|
const currentIndex = selectedZone.panelOrder.indexOf(side);
|
||||||
|
@ -125,123 +101,105 @@ const Panel: React.FC<PanelProps> = ({
|
||||||
const topActive = previousPanels.includes("top");
|
const topActive = previousPanels.includes("top");
|
||||||
const bottomActive = previousPanels.includes("bottom");
|
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) {
|
switch (side) {
|
||||||
case "top":
|
case "top":
|
||||||
case "bottom":
|
case "bottom":
|
||||||
return {
|
return {
|
||||||
// minWidth: "200px", // Minimum width constraint
|
minWidth: "170px",
|
||||||
width: `calc(100% - ${(leftActive ? panelSizeWidth : 0) +
|
width: `calc(100% - ${
|
||||||
(rightActive ? panelSizeWidth : 0)
|
(leftActive ? panelSize : 0) + (rightActive ? panelSize : 0)
|
||||||
}px)`,
|
}px)`,
|
||||||
minHeight: "200px", // Minimum height constraint
|
minHeight: "170px",
|
||||||
height: `${panelSizeHeight - 2}px`, // Subtracting for border or margin
|
height: `${panelSize}px`,
|
||||||
left: leftActive ? `${panelSizeWidth}px` : "0",
|
left: leftActive ? `${panelSize}px` : "0",
|
||||||
right: rightActive ? `${panelSizeWidth}px` : "0",
|
right: rightActive ? `${panelSize}px` : "0",
|
||||||
[side]: "0",
|
[side]: "0",
|
||||||
};
|
};
|
||||||
|
|
||||||
case "left":
|
case "left":
|
||||||
case "right":
|
case "right":
|
||||||
return {
|
return {
|
||||||
minWidth: "200px", // Minimum width constraint
|
minWidth: "170px",
|
||||||
width: `${panelSizeWidth - 2}px`, // Subtracting for border or margin
|
width: `${panelSize}px`,
|
||||||
// minHeight: "200px", // Minimum height constraint
|
minHeight: "170px",
|
||||||
height: `calc(100% - ${(topActive ? panelSizeHeight : 0) +
|
height: `calc(100% - ${
|
||||||
(bottomActive ? panelSizeHeight : 0)
|
(topActive ? panelSize : 0) + (bottomActive ? panelSize : 0)
|
||||||
}px)`,
|
}px)`,
|
||||||
top: topActive ? `${panelSizeHeight}px` : "0",
|
top: topActive ? `${panelSize}px` : "0",
|
||||||
bottom: bottomActive ? `${panelSizeHeight}px` : "0",
|
bottom: bottomActive ? `${panelSize}px` : "0",
|
||||||
[side]: "0",
|
[side]: "0",
|
||||||
};
|
};
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[
|
[selectedZone.panelOrder, panelSize]
|
||||||
selectedZone.panelOrder,
|
|
||||||
isPlaying,
|
|
||||||
canvasDimensions.width,
|
|
||||||
canvasDimensions.height,
|
|
||||||
]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Handle drop event
|
||||||
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 || isPanelLocked(panel)) return;
|
||||||
if (isPanelLocked(panel)) return;
|
|
||||||
|
|
||||||
const currentWidgetsCount = getCurrentWidgetCount(panel);
|
const currentWidgetsCount = getCurrentWidgetCount(panel);
|
||||||
const maxCapacity = calculatePanelCapacity(panel);
|
const maxCapacity = calculatePanelCapacity(panel);
|
||||||
|
|
||||||
if (currentWidgetsCount >= maxCapacity) return;
|
if (currentWidgetsCount < maxCapacity) {
|
||||||
addWidgetToPanel(draggedAsset, panel);
|
addWidgetToPanel(draggedAsset, panel);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Check if panel is locked
|
||||||
const isPanelLocked = (panel: Side) =>
|
const isPanelLocked = (panel: Side) =>
|
||||||
selectedZone.lockedPanels.includes(panel);
|
selectedZone.lockedPanels.includes(panel);
|
||||||
|
|
||||||
|
// Get current widget count in a panel
|
||||||
const getCurrentWidgetCount = (panel: Side) =>
|
const getCurrentWidgetCount = (panel: Side) =>
|
||||||
selectedZone.widgets.filter((w) => w.panel === panel).length;
|
selectedZone.widgets.filter((w) => w.panel === panel).length;
|
||||||
|
|
||||||
|
// Calculate panel capacity
|
||||||
const calculatePanelCapacity = (panel: Side) => {
|
const calculatePanelCapacity = (panel: Side) => {
|
||||||
const CHART_WIDTH = 170;
|
const CHART_WIDTH = panelSize;
|
||||||
const CHART_HEIGHT = 170;
|
const CHART_HEIGHT = panelSize;
|
||||||
const FALLBACK_HORIZONTAL_CAPACITY = 5;
|
|
||||||
const FALLBACK_VERTICAL_CAPACITY = 3;
|
|
||||||
|
|
||||||
const dimensions = panelDimensions[panel];
|
const dimensions = panelDimensions[panel];
|
||||||
if (!dimensions) {
|
if (!dimensions) {
|
||||||
return panel === "top" || panel === "bottom"
|
return panel === "top" || panel === "bottom" ? 5 : 3; // Fallback capacities
|
||||||
? FALLBACK_HORIZONTAL_CAPACITY
|
|
||||||
: FALLBACK_VERTICAL_CAPACITY;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return panel === "top" || panel === "bottom"
|
return panel === "top" || panel === "bottom"
|
||||||
? Math.floor(dimensions.width / CHART_WIDTH)
|
? Math.max(1, Math.floor(dimensions.width / CHART_WIDTH))
|
||||||
: Math.floor(dimensions.height / CHART_HEIGHT);
|
: 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 addWidgetToPanel = async (asset: any, panel: Side) => {
|
||||||
const email = localStorage.getItem("email") || "";
|
const email = localStorage.getItem("email") || "";
|
||||||
const organization = email?.split("@")[1]?.split(".")[0];
|
const organization = email?.split("@")[1]?.split(".")[0];
|
||||||
|
|
||||||
const newWidget = {
|
const newWidget = {
|
||||||
...asset,
|
...asset,
|
||||||
id: generateUniqueId(),
|
id: generateUniqueId(),
|
||||||
panel,
|
panel,
|
||||||
};
|
};
|
||||||
|
|
||||||
let addWidget = {
|
let addWidget = {
|
||||||
organization: organization,
|
organization: organization,
|
||||||
zoneId: selectedZone.zoneId,
|
zoneId: selectedZone.zoneId,
|
||||||
widget: newWidget,
|
widget: newWidget,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (visualizationSocket) {
|
if (visualizationSocket) {
|
||||||
visualizationSocket.emit("v2:viz-widget:add", addWidget);
|
visualizationSocket.emit("v2:viz-widget:add", addWidget);
|
||||||
}
|
}
|
||||||
|
|
||||||
setSelectedZone((prev) => ({
|
setSelectedZone((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
widgets: [...prev.widgets, newWidget],
|
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(() => {
|
useEffect(() => {
|
||||||
const observers: ResizeObserver[] = [];
|
const observers: ResizeObserver[] = [];
|
||||||
const currentPanelRefs = panelRefs.current;
|
const currentPanelRefs = panelRefs.current;
|
||||||
|
@ -258,6 +216,7 @@ const Panel: React.FC<PanelProps> = ({
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
observer.observe(element);
|
observer.observe(element);
|
||||||
observers.push(observer);
|
observers.push(observer);
|
||||||
}
|
}
|
||||||
|
@ -268,22 +227,15 @@ const Panel: React.FC<PanelProps> = ({
|
||||||
};
|
};
|
||||||
}, [selectedZone.activeSides]);
|
}, [selectedZone.activeSides]);
|
||||||
|
|
||||||
|
// Handle widget reordering
|
||||||
const handleReorder = (fromIndex: number, toIndex: number, panel: Side) => {
|
const handleReorder = (fromIndex: number, toIndex: number, panel: Side) => {
|
||||||
if (!selectedZone) return; // Ensure selectedZone is not null
|
|
||||||
|
|
||||||
setSelectedZone((prev) => {
|
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);
|
const widgetsInPanel = prev.widgets.filter((w) => w.panel === panel);
|
||||||
|
|
||||||
// Reorder widgets within the same panel
|
|
||||||
const reorderedWidgets = arrayMove(widgetsInPanel, fromIndex, toIndex);
|
const reorderedWidgets = arrayMove(widgetsInPanel, fromIndex, toIndex);
|
||||||
|
|
||||||
// Merge the reordered widgets back into the full list while preserving the order
|
|
||||||
const updatedWidgets = prev.widgets
|
const updatedWidgets = prev.widgets
|
||||||
.filter((widget) => widget.panel !== panel) // Keep widgets from other panels
|
.filter((widget) => widget.panel !== panel)
|
||||||
.concat(reorderedWidgets); // Add the reordered widgets for the specified panel
|
.concat(reorderedWidgets);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...prev,
|
...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 (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<style>
|
||||||
|
{`
|
||||||
|
:root {
|
||||||
|
--topWidth: ${topWidth};
|
||||||
|
--bottomWidth: ${bottomWidth};
|
||||||
|
--leftHeight: ${leftHeight};
|
||||||
|
--rightHeight: ${rightHeight};
|
||||||
|
|
||||||
|
--topCapacity: ${topCapacity};
|
||||||
|
--bottomCapacity: ${bottomCapacity};
|
||||||
|
--leftCapacity: ${leftCapacity};
|
||||||
|
--rightCapacity: ${rightCapacity};
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
</style>
|
||||||
|
|
||||||
{selectedZone.activeSides.map((side) => (
|
{selectedZone.activeSides.map((side) => (
|
||||||
<div
|
<div
|
||||||
key={side}
|
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)}
|
style={getPanelStyle(side)}
|
||||||
onDrop={(e) => handleDrop(e, side)}
|
onDrop={(e) => handleDrop(e, side)}
|
||||||
onDragOver={(e) => e.preventDefault()}
|
onDragOver={(e) => e.preventDefault()}
|
||||||
|
@ -344,5 +325,3 @@ const Panel: React.FC<PanelProps> = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Panel;
|
export default Panel;
|
||||||
|
|
||||||
// canvasDimensions.width as percent
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ interface SimpleCardProps {
|
||||||
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>; // React component for SVG icon
|
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>; // React component for SVG icon
|
||||||
value: string;
|
value: string;
|
||||||
per: string; // Percentage change
|
per: string; // Percentage change
|
||||||
position?: [number, number]
|
position?: [number, number];
|
||||||
}
|
}
|
||||||
|
|
||||||
const SimpleCard: React.FC<SimpleCardProps> = ({
|
const SimpleCard: React.FC<SimpleCardProps> = ({
|
||||||
|
@ -15,7 +15,6 @@ const SimpleCard: React.FC<SimpleCardProps> = ({
|
||||||
per,
|
per,
|
||||||
position = [0, 0],
|
position = [0, 0],
|
||||||
}) => {
|
}) => {
|
||||||
|
|
||||||
const handleDragStart = (event: React.DragEvent<HTMLDivElement>) => {
|
const handleDragStart = (event: React.DragEvent<HTMLDivElement>) => {
|
||||||
const rect = event.currentTarget.getBoundingClientRect(); // Get position
|
const rect = event.currentTarget.getBoundingClientRect(); // Get position
|
||||||
const cardData = JSON.stringify({
|
const cardData = JSON.stringify({
|
||||||
|
@ -23,7 +22,7 @@ const SimpleCard: React.FC<SimpleCardProps> = ({
|
||||||
value,
|
value,
|
||||||
per,
|
per,
|
||||||
icon: Icon,
|
icon: Icon,
|
||||||
|
|
||||||
className: event.currentTarget.className,
|
className: event.currentTarget.className,
|
||||||
position: [rect.top, rect.left], // ✅ Store position
|
position: [rect.top, rect.left], // ✅ Store position
|
||||||
});
|
});
|
||||||
|
|
|
@ -64,7 +64,8 @@ export const handleSaveTemplate = async ({
|
||||||
floatingWidget,
|
floatingWidget,
|
||||||
widgets3D,
|
widgets3D,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
console.log('newTemplate: ', newTemplate);
|
||||||
// Extract organization from email
|
// Extract organization from email
|
||||||
const email = localStorage.getItem("email") || "";
|
const email = localStorage.getItem("email") || "";
|
||||||
const organization = email.includes("@")
|
const organization = email.includes("@")
|
||||||
|
|
|
@ -70,6 +70,67 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: auto;
|
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 {
|
.widget-left-sideBar {
|
||||||
min-height: 50vh;
|
min-height: 50vh;
|
||||||
max-height: 60vh;
|
max-height: 60vh;
|
||||||
|
|
|
@ -24,9 +24,17 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.floating {
|
.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);
|
background: var(--background-color);
|
||||||
border: 1.23px solid var(--border-color);
|
border: 1.23px solid var(--border-color);
|
||||||
box-shadow: 0px 4.91px 4.91px 0px #0000001c;
|
box-shadow: 0px 4.91px 4.91px 0px #0000001c;
|
||||||
|
@ -60,9 +68,8 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
background-color: var(--background-color);
|
background-color: var(--background-color);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 10px;
|
// bottom: 10px;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%, 0);
|
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
|
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
@ -70,6 +77,7 @@
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
max-width: calc(100% - 500px);
|
max-width: calc(100% - 500px);
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
|
transform: translate(-50%, -100%);
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
&::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
|
@ -116,8 +124,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.zone-wrapper.bottom {
|
.zone-wrapper.bottom {
|
||||||
bottom: calc(var(--realTimeViz-container-height) * 0.27);
|
top: var(--bottomWidth);
|
||||||
bottom: 200px;
|
// bottom: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-container {
|
.content-container {
|
||||||
|
@ -138,7 +146,7 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
background-color: rgba(224, 223, 255, 0.5);
|
background-color: rgba(224, 223, 255, 0.5);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 10px;
|
// bottom: 10px;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%, 0);
|
transform: translate(-50%, 0);
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
|
@ -203,9 +211,9 @@
|
||||||
|
|
||||||
.chart-container {
|
.chart-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 150px;
|
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
// border: 1px dashed var(--background-color-gray);
|
border: 1px dashed var(--background-color-gray);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
box-shadow: var(--box-shadow-medium);
|
box-shadow: var(--box-shadow-medium);
|
||||||
padding: 6px 0;
|
padding: 6px 0;
|
||||||
|
@ -343,7 +351,7 @@
|
||||||
|
|
||||||
.chart-container {
|
.chart-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 160px;
|
min-height: 150px;
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
box-shadow: var(--box-shadow-medium);
|
box-shadow: var(--box-shadow-medium);
|
||||||
|
@ -362,7 +370,7 @@
|
||||||
|
|
||||||
.playingFlase {
|
.playingFlase {
|
||||||
.zone-wrapper.bottom {
|
.zone-wrapper.bottom {
|
||||||
bottom: calc(var(--realTimeViz-container-height) * 0.25);
|
top: var(--bottomWidth);
|
||||||
// bottom: 210px;
|
// bottom: 210px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -786,4 +794,4 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
interface OuterClickProps {
|
interface OuterClickProps {
|
||||||
contextClassName: string;
|
contextClassName: string[]; // Make sure this is an array of strings
|
||||||
setMenuVisible: React.Dispatch<React.SetStateAction<boolean>>;
|
setMenuVisible: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,8 +11,12 @@ export default function OuterClick({
|
||||||
}: OuterClickProps) {
|
}: OuterClickProps) {
|
||||||
const handleClick = (event: MouseEvent) => {
|
const handleClick = (event: MouseEvent) => {
|
||||||
const targets = event.target as HTMLElement;
|
const targets = event.target as HTMLElement;
|
||||||
// Check if the click is outside the selectable-dropdown-wrapper
|
// Check if the click is outside of any of the provided class names
|
||||||
if (!targets.closest(`.${contextClassName}`)) {
|
const isOutside = contextClassName.every(
|
||||||
|
(className) => !targets.closest(`.${className}`)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isOutside) {
|
||||||
setMenuVisible(false); // Close the menu by updating the state
|
setMenuVisible(false); // Close the menu by updating the state
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -23,7 +27,7 @@ export default function OuterClick({
|
||||||
return () => {
|
return () => {
|
||||||
document.removeEventListener("click", handleClick);
|
document.removeEventListener("click", handleClick);
|
||||||
};
|
};
|
||||||
}, []);
|
}, [contextClassName]); // Add contextClassName to dependency array to handle any changes
|
||||||
|
|
||||||
return null; // This component doesn't render anything
|
return null; // This component doesn't render anything
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue