Dwinzo_dev/app/src/modules/visualization/widgets/2d/DraggableWidget.tsx

304 lines
8.5 KiB
TypeScript

import { forwardRef, useRef, useState } from "react";
import { gsap } from "gsap";
import { Draggable } from "gsap/Draggable";
import { useWidgetStore } from "../../../../store/useWidgetStore";
import { usePlayButtonStore } from "../../../../store/usePlayButtonStore";
import { useSelectedZoneStore } from "../../../../store/visualization/useZoneStore";
import PieGraphComponent from "../2d/charts/PieGraphComponent";
import BarGraphComponent from "../2d/charts/BarGraphComponent";
import LineGraphComponent from "../2d/charts/LineGraphComponent";
import DoughnutGraphComponent from "../2d/charts/DoughnutGraphComponent";
import PolarAreaGraphComponent from "../2d/charts/PolarAreaGraphComponent";
import ProgressCard1 from "../2d/charts/ProgressCard1";
import ProgressCard2 from "../2d/charts/ProgressCard2";
import {
DeleteIcon,
DublicateIcon,
KebabIcon,
} from "../../../../components/icons/ExportCommonIcons";
import { useParams } from "react-router-dom";
import { useSocketStore } from "../../../../store/builder/store";
import useChartStore from "../../../../store/visualization/useChartStore";
import { getUserData } from "../../../../functions/getUserData";
gsap.registerPlugin(Draggable);
type Side = "top" | "bottom" | "left" | "right";
interface Widget {
id: string;
type: string;
title: string;
panel: Side;
data: any;
}
type DraggableWidgetProps = {
widget: any;
index: any
openKebabId: string | null;
setOpenKebabId: (id: string | null) => void;
};
export const DraggableWidget = forwardRef<HTMLDivElement, DraggableWidgetProps>(({
widget,
index,
openKebabId,
setOpenKebabId
}, ref) => {
const { selectedZone, setSelectedZone } = useSelectedZoneStore();
const widgetRef = useRef<HTMLDivElement | null>(null);
const [panelDimensions, setPanelDimensions] = useState<{
[side in Side]?: { width: number; height: number };
}>({});
const [canvasDimensions, setCanvasDimensions] = useState({
width: 0,
height: 0,
});
const { projectId } = useParams();
const { visualizationSocket } = useSocketStore();
const { selectedChartId, setSelectedChartId } = useWidgetStore();
const { measurements, duration, name } = useChartStore();
const { userId, organization, email } = getUserData();
const handleKebabClick = (event: React.MouseEvent<HTMLDivElement>) => {
event.stopPropagation();
if (openKebabId === widget.id) {
setOpenKebabId(null);
} else {
setOpenKebabId(widget.id);
}
};
const isPanelFull = (panel: Side) => {
const currentWidgetCount = getCurrentWidgetCount(panel);
const panelCapacity = calculatePanelCapacity(panel);
return currentWidgetCount > panelCapacity;
};
const panelSize = Math.max(
Math.min(canvasDimensions.width * 0.25, canvasDimensions.height * 0.25),
170 // Min 170px
);
const getCurrentWidgetCount = (panel: Side) =>
selectedZone.widgets.filter((w) => w.panel === panel).length;
// Calculate panel capacity
const calculatePanelCapacity = (panel: Side) => {
const CHART_WIDTH = panelSize;
const CHART_HEIGHT = panelSize;
const dimensions = panelDimensions[panel];
if (!dimensions) {
return panel === "top" || panel === "bottom" ? 5 : 3; // Fallback capacities
}
return panel === "top" || panel === "bottom"
? Math.max(1, Math.floor(dimensions.width / CHART_WIDTH))
: Math.max(1, Math.floor(dimensions.height / CHART_HEIGHT));
};
const deleteSelectedChart = async () => {
try {
let deleteWidget = {
zoneUuid: selectedZone.zoneUuid,
widgetID: widget.id,
organization: organization,
projectId, userId
};
console.log('deleteWidget: ', deleteWidget);
if (visualizationSocket) {
setSelectedChartId(null);
visualizationSocket.emit("v1:viz-widget:delete", deleteWidget);
}
const updatedWidgets = selectedZone.widgets.filter(
(w: Widget) => w.id !== widget.id
);
setSelectedZone((prevZone: any) => ({
...prevZone,
widgets: updatedWidgets,
}));
setOpenKebabId(null);
// const response = await deleteWidgetApi(widget.id, organization);
// if (response?.message === "Widget deleted successfully") {
// const updatedWidgets = selectedZone.widgets.filter(
// (w: Widget) => w.id !== widget.id
// );
// setSelectedZone((prevZone: any) => ({
// ...prevZone,
// widgets: updatedWidgets,
// }));
// }
} catch (error) {
echo.error("Failued to dublicate widgeet");
} finally {
setOpenKebabId(null);
}
};
const duplicateWidget = async () => {
try {
const duplicatedWidget: Widget = {
...widget,
title: name === "" ? widget.title : name,
Data: {
duration: duration,
measurements: { ...measurements },
},
id: `${widget.id}-copy-${Date.now()}`,
};
let duplicateWidget = {
organization,
zoneUuid: selectedZone.zoneUuid,
widget: duplicatedWidget,
projectId, userId
};
if (visualizationSocket) {
visualizationSocket.emit("v1:viz-widget:add", duplicateWidget);
}
setSelectedZone((prevZone: any) => ({
...prevZone,
widgets: [...prevZone.widgets, duplicatedWidget],
}));
// const response = await duplicateWidgetApi(selectedZone.zoneUuid, organization, duplicatedWidget);
// if (response?.message === "Widget created successfully") {
// setSelectedZone((prevZone: any) => ({
// ...prevZone,
// widgets: [...prevZone.widgets, duplicatedWidget],
// }));
// }
} catch (error) {
echo.error("Failued to dublicate widgeet");
} finally {
setOpenKebabId(null);
}
};
const handlePointerDown = () => {
if (selectedChartId?.id !== widget.id) {
setSelectedChartId(widget);
}
};
const { isPlaying } = usePlayButtonStore();
return (
<div
className={`item-content ${selectedChartId?.id === widget.id && !isPlaying && "activeChart"
}`}
onClick={() => {
setSelectedChartId(widget);
}}
onPointerDown={handlePointerDown}
>
<span className="order"> {index + 1} </span>
<div className="icon kebab" onClick={handleKebabClick}>
<KebabIcon />
</div>
{openKebabId === widget.id && (
<div className="kebab-options" ref={widgetRef}>
<div
className={`edit btn ${isPanelFull(widget.panel) ? "btn-blur" : ""
}`}
onClick={duplicateWidget}
>
<div className="icon">
<DublicateIcon />
</div>
<div className="label">Duplicate</div>
</div>
<div
className="edit btn"
onClick={(e) => {
e.stopPropagation();
deleteSelectedChart();
}}
>
<div className="icon">
<DeleteIcon />
</div>
<div className="label">Delete</div>
</div>
</div>
)}
{/* Render charts based on widget type */}
{widget.type === "progress 1" && (
<ProgressCard1 title={widget.title} id={widget.id} />
)}
{widget.type === "progress 2" && (
<ProgressCard2 title={widget.title} id={widget.id} />
)}
{widget.type === "line" && (
<LineGraphComponent
id={widget.id}
type={widget.type}
title={widget.title}
fontSize={widget.fontSize}
fontWeight={widget.fontWeight}
/>
)}
{widget.type === "bar" && (
<BarGraphComponent
id={widget.id}
type={widget.type}
title={widget.title}
fontSize={widget.fontSize}
fontWeight={widget.fontWeight}
/>
)}
{widget.type === "pie" && (
<PieGraphComponent
id={widget.id}
type={widget.type}
title={widget.title}
fontSize={widget.fontSize}
fontWeight={widget.fontWeight}
/>
)}
{widget.type === "doughnut" && (
<DoughnutGraphComponent
id={widget.id}
type={widget.type}
title={widget.title}
fontSize={widget.fontSize}
fontWeight={widget.fontWeight}
/>
)}
{widget.type === "polarArea" && (
<PolarAreaGraphComponent
id={widget.id}
type={widget.type}
title={widget.title}
fontSize={widget.fontSize}
fontWeight={widget.fontWeight}
/>
)}
</div>
);
}
);