304 lines
8.5 KiB
TypeScript
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>
|
|
);
|
|
}
|
|
);
|
|
|