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 { 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;
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ const WidgetsFloating = () => {
|
|||
))} */}
|
||||
{/* Floating 1 */}
|
||||
<SimpleCard
|
||||
header={"Today’s Money"}
|
||||
header={"Today’s Earnings"}
|
||||
icon={WalletIcon}
|
||||
value={"$53,000"}
|
||||
per={"+55%"}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 &&
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
});
|
||||
|
|
|
@ -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("@")
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue