2025-03-25 11:47:41 +05:30
import { useWidgetStore } from "../../../store/useWidgetStore" ;
2025-03-25 15:55:48 +05:30
import ProgressCard from "../realTimeVis/charts/ProgressCard" ;
import PieGraphComponent from "../realTimeVis/charts/PieGraphComponent" ;
import BarGraphComponent from "../realTimeVis/charts/BarGraphComponent" ;
import LineGraphComponent from "../realTimeVis/charts/LineGraphComponent" ;
import DoughnutGraphComponent from "../realTimeVis/charts/DoughnutGraphComponent" ;
import PolarAreaGraphComponent from "../realTimeVis/charts/PolarAreaGraphComponent" ;
2025-04-01 19:06:29 +05:30
import ProgressCard1 from "../realTimeVis/charts/ProgressCard1" ;
import ProgressCard2 from "../realTimeVis/charts/ProgressCard2" ;
2025-03-27 10:54:40 +05:30
import {
DeleteIcon ,
DublicateIcon ,
KebabIcon ,
} from "../../icons/ExportCommonIcons" ;
2025-03-27 12:16:20 +05:30
import { useEffect , useRef , useState } from "react" ;
2025-03-29 19:21:20 +05:30
import { duplicateWidgetApi } from "../../../services/realTimeVisulization/zoneData/duplicateWidget" ;
import { deleteWidgetApi } from "../../../services/realTimeVisulization/zoneData/deleteWidgetApi" ;
2025-04-02 12:29:07 +05:30
import { useClickOutside } from "./functions/handleWidgetsOuterClick" ;
2025-04-01 19:35:11 +05:30
import { useSocketStore } from "../../../store/store" ;
2025-04-03 18:15:26 +05:30
import { usePlayButtonStore } from "../../../store/usePlayButtonStore" ;
2025-04-08 18:34:21 +05:30
import OuterClick from "../../../utils/outerClick" ;
2025-03-27 10:54:40 +05:30
type Side = "top" | "bottom" | "left" | "right" ;
interface Widget {
id : string ;
type : string ;
title : string ;
panel : Side ;
data : any ;
}
2025-03-25 11:47:41 +05:30
2025-03-25 15:55:48 +05:30
export const DraggableWidget = ( {
widget ,
2025-03-27 12:16:20 +05:30
hiddenPanels ,
2025-03-27 10:54:40 +05:30
index ,
onReorder ,
openKebabId ,
setOpenKebabId ,
selectedZone ,
setSelectedZone ,
2025-03-25 15:55:48 +05:30
} : {
2025-03-27 10:54:40 +05:30
selectedZone : {
zoneName : string ;
2025-03-29 19:21:20 +05:30
zoneId : string ;
2025-03-27 10:54:40 +05:30
activeSides : Side [ ] ;
2025-04-10 17:53:28 +05:30
points : [ ] ;
2025-03-27 10:54:40 +05:30
panelOrder : Side [ ] ;
lockedPanels : Side [ ] ;
widgets : Widget [ ] ;
} ;
setSelectedZone : React.Dispatch <
React . SetStateAction < {
zoneName : string ;
activeSides : Side [ ] ;
panelOrder : Side [ ] ;
2025-04-10 17:53:28 +05:30
points : [ ] ;
2025-03-27 10:54:40 +05:30
lockedPanels : Side [ ] ;
2025-03-27 16:16:31 +05:30
zoneId : string ;
zoneViewPortTarget : number [ ] ;
zoneViewPortPosition : number [ ] ;
widgets : {
id : string ;
type : string ;
title : string ;
panel : Side ;
data : any ;
} [ ] ;
2025-03-27 10:54:40 +05:30
} >
> ;
2025-03-27 16:16:31 +05:30
2025-03-25 15:55:48 +05:30
widget : any ;
2025-03-27 12:16:20 +05:30
hiddenPanels : string [ ] ;
2025-03-27 10:54:40 +05:30
index : number ;
onReorder : ( fromIndex : number , toIndex : number ) = > void ;
2025-03-27 12:16:20 +05:30
openKebabId : string | null ;
setOpenKebabId : ( id : string | null ) = > void ;
2025-03-25 15:55:48 +05:30
} ) = > {
2025-04-01 19:35:11 +05:30
const { visualizationSocket } = useSocketStore ( ) ;
2025-03-25 11:47:41 +05:30
const { selectedChartId , setSelectedChartId } = useWidgetStore ( ) ;
2025-03-27 12:16:20 +05:30
const [ panelDimensions , setPanelDimensions ] = useState < {
[ side in Side ] ? : { width : number ; height : number } ;
} > ( { } ) ;
2025-03-25 11:47:41 +05:30
const handlePointerDown = ( ) = > {
if ( selectedChartId ? . id !== widget . id ) {
setSelectedChartId ( widget ) ;
}
} ;
2025-04-02 12:29:07 +05:30
const chartWidget = useRef < HTMLDivElement > ( null ) ;
2025-04-11 11:24:23 +05:30
2025-04-08 18:34:21 +05:30
2025-03-29 19:21:20 +05:30
const deleteSelectedChart = async ( ) = > {
try {
const email = localStorage . getItem ( "email" ) || "" ;
const organization = email ? . split ( "@" ) [ 1 ] ? . split ( "." ) [ 0 ] ;
2025-04-01 19:35:11 +05:30
let deleteWidget = {
zoneId : selectedZone.zoneId ,
widgetID : widget.id ,
2025-04-07 17:55:14 +05:30
organization : organization ,
} ;
2025-04-07 18:27:01 +05:30
2025-04-01 19:35:11 +05:30
if ( visualizationSocket ) {
2025-04-07 17:55:14 +05:30
visualizationSocket . emit ( "v2:viz-widget:delete" , deleteWidget ) ;
2025-03-29 19:21:20 +05:30
}
2025-04-01 19:35:11 +05:30
const updatedWidgets = selectedZone . widgets . filter (
( w : Widget ) = > w . id !== widget . id
) ;
2025-04-07 18:27:01 +05:30
2025-04-01 19:35:11 +05:30
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,
// }));
// }
2025-03-29 19:21:20 +05:30
} catch ( error ) {
} finally {
setOpenKebabId ( null ) ;
}
2025-03-26 12:28:22 +05:30
} ;
2025-03-27 12:16:20 +05:30
const getCurrentWidgetCount = ( panel : Side ) = >
selectedZone . widgets . filter ( ( w ) = > w . panel === panel ) . length ;
const calculatePanelCapacity = ( panel : Side ) = > {
const CHART_WIDTH = 150 ;
const CHART_HEIGHT = 150 ;
const FALLBACK_HORIZONTAL_CAPACITY = 5 ;
const FALLBACK_VERTICAL_CAPACITY = 3 ;
const dimensions = panelDimensions [ panel ] ;
if ( ! dimensions ) {
return panel === "top" || panel === "bottom"
? FALLBACK_HORIZONTAL_CAPACITY
: FALLBACK_VERTICAL_CAPACITY ;
2025-03-26 12:28:22 +05:30
}
2025-03-27 12:16:20 +05:30
return panel === "top" || panel === "bottom"
? Math . floor ( dimensions . width / CHART_WIDTH )
: Math . floor ( dimensions . height / CHART_HEIGHT ) ;
2025-03-26 12:28:22 +05:30
} ;
2025-03-27 12:16:20 +05:30
const isPanelFull = ( panel : Side ) = > {
const currentWidgetCount = getCurrentWidgetCount ( panel ) ;
const panelCapacity = calculatePanelCapacity ( panel ) ;
return currentWidgetCount >= panelCapacity ;
2025-03-27 10:54:40 +05:30
} ;
2025-03-29 19:21:20 +05:30
const duplicateWidget = async ( ) = > {
try {
const email = localStorage . getItem ( "email" ) || "" ;
const organization = email ? . split ( "@" ) [ 1 ] ? . split ( "." ) [ 0 ] ;
2025-03-27 10:54:40 +05:30
2025-03-29 19:21:20 +05:30
const duplicatedWidget : Widget = {
. . . widget ,
id : ` ${ widget . id } -copy- ${ Date . now ( ) } ` ,
} ;
2025-04-10 17:53:28 +05:30
console . log ( 'duplicatedWidget: ' , duplicatedWidget ) ;
2025-03-27 12:16:20 +05:30
2025-04-02 18:49:18 +05:30
let duplicateWidget = {
organization : organization ,
zoneId : selectedZone.zoneId ,
2025-04-07 17:55:14 +05:30
widget : duplicatedWidget ,
} ;
2025-04-02 18:49:18 +05:30
if ( visualizationSocket ) {
2025-04-07 17:55:14 +05:30
visualizationSocket . emit ( "v2:viz-widget:add" , duplicateWidget ) ;
2025-03-29 19:21:20 +05:30
}
2025-04-02 18:49:18 +05:30
setSelectedZone ( ( prevZone : any ) = > ( {
. . . prevZone ,
widgets : [ . . . prevZone . widgets , duplicatedWidget ] ,
} ) ) ;
// const response = await duplicateWidgetApi(selectedZone.zoneId, organization, duplicatedWidget);
// if (response?.message === "Widget created successfully") {
// setSelectedZone((prevZone: any) => ({
// ...prevZone,
// widgets: [...prevZone.widgets, duplicatedWidget],
// }));
// }
2025-03-29 19:21:20 +05:30
} catch ( error ) {
} finally {
setOpenKebabId ( null ) ;
}
2025-03-27 10:54:40 +05:30
} ;
2025-03-27 12:16:20 +05:30
const handleKebabClick = ( event : React.MouseEvent < HTMLDivElement > ) = > {
event . stopPropagation ( ) ;
if ( openKebabId === widget . id ) {
setOpenKebabId ( null ) ;
} else {
setOpenKebabId ( widget . id ) ;
}
} ;
2025-03-27 10:54:40 +05:30
const widgetRef = useRef < HTMLDivElement | null > ( null ) ;
2025-03-26 12:28:22 +05:30
2025-03-27 10:54:40 +05:30
useEffect ( ( ) = > {
const handleClickOutside = ( event : MouseEvent ) = > {
if (
widgetRef . current &&
! widgetRef . current . contains ( event . target as Node )
) {
2025-03-27 12:16:20 +05:30
setOpenKebabId ( null ) ;
2025-03-27 10:54:40 +05:30
}
} ;
document . addEventListener ( "mousedown" , handleClickOutside ) ;
return ( ) = > {
document . removeEventListener ( "mousedown" , handleClickOutside ) ;
} ;
} , [ setOpenKebabId ] ) ;
2025-03-27 12:24:36 +05:30
const handleDragStart = ( event : React.DragEvent < HTMLDivElement > ) = > {
event . dataTransfer . setData ( "text/plain" , index . toString ( ) ) ; // Store the index of the dragged widget
} ;
const handleDragEnter = ( event : React.DragEvent < HTMLDivElement > ) = > {
event . preventDefault ( ) ; // Allow drop
} ;
2025-03-27 10:54:40 +05:30
2025-03-27 12:24:36 +05:30
const handleDragOver = ( event : React.DragEvent < HTMLDivElement > ) = > {
event . preventDefault ( ) ; // Allow drop
} ;
const handleDrop = ( event : React.DragEvent < HTMLDivElement > ) = > {
event . preventDefault ( ) ;
const fromIndex = parseInt ( event . dataTransfer . getData ( "text/plain" ) , 10 ) ; // Get the dragged widget's index
const toIndex = index ; // The index of the widget where the drop occurred
if ( fromIndex !== toIndex ) {
onReorder ( fromIndex , toIndex ) ; // Call the reorder function passed as a prop
}
2025-03-27 10:54:40 +05:30
} ;
2025-04-02 17:51:44 +05:30
// useClickOutside(chartWidget, () => {
// setSelectedChartId(null);
// });
2025-04-03 18:15:26 +05:30
const { isPlaying } = usePlayButtonStore ( ) ;
2025-03-27 10:54:40 +05:30
2025-04-07 17:55:14 +05:30
const [ canvasDimensions , setCanvasDimensions ] = useState ( {
width : 0 ,
height : 0 ,
} ) ;
// Track canvas dimensions
2025-04-08 18:34:21 +05:30
// 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 ,
} ) ;
} ;
2025-04-07 17:55:14 +05:30
2025-04-08 18:34:21 +05:30
updateCanvasDimensions ( ) ;
const resizeObserver = new ResizeObserver ( updateCanvasDimensions ) ;
resizeObserver . observe ( canvas ) ;
2025-04-07 17:55:14 +05:30
2025-04-08 18:34:21 +05:30
return ( ) = > resizeObserver . unobserve ( canvas ) ;
} , [ ] ) ;
2025-04-07 17:55:14 +05:30
2025-03-25 11:47:41 +05:30
return (
< >
2025-04-07 17:55:14 +05:30
< style >
{ `
: root {
2025-04-08 18:34:21 +05:30
-- realTimeViz - container - width : $ { canvasDimensions . width } px ;
2025-04-07 17:55:14 +05:30
-- realTimeViz - container - height : $ { canvasDimensions . height } px ;
2025-04-08 18:34:21 +05:30
2025-04-07 17:55:14 +05:30
}
` }
< / style >
2025-03-25 11:47:41 +05:30
< div
2025-03-26 12:28:22 +05:30
draggable
2025-03-25 11:47:41 +05:30
key = { widget . id }
2025-04-07 17:55:14 +05:30
className = { ` chart-container ${
selectedChartId ? . id === widget . id && ! isPlaying && "activeChart"
} ` }
2025-03-25 11:47:41 +05:30
onPointerDown = { handlePointerDown }
2025-03-26 12:28:22 +05:30
onDragStart = { handleDragStart }
onDragEnter = { handleDragEnter }
onDragOver = { handleDragOver }
onDrop = { handleDrop }
2025-03-25 15:55:48 +05:30
style = { {
2025-04-07 17:55:14 +05:30
width : [ "top" , "bottom" ] . includes ( widget . panel )
2025-04-08 18:34:21 +05:30
? ` calc( ${ canvasDimensions . width } px / 6) `
: undefined ,
2025-04-07 17:55:14 +05:30
height : [ "left" , "right" ] . includes ( widget . panel )
2025-04-08 18:34:21 +05:30
? ` calc( ${ canvasDimensions . height - 10 } px / 4) `
: undefined ,
2025-03-25 15:55:48 +05:30
} }
2025-04-02 12:29:07 +05:30
ref = { chartWidget }
onClick = { ( ) = > setSelectedChartId ( widget ) }
2025-03-25 11:47:41 +05:30
>
2025-03-27 10:54:40 +05:30
{ /* Kebab Icon */ }
< div className = "icon kebab" onClick = { handleKebabClick } >
< KebabIcon / >
< / div >
{ /* Kebab Options */ }
{ openKebabId === widget . id && (
2025-03-27 12:16:20 +05:30
< div className = "kebab-options" ref = { widgetRef } >
< div
2025-04-07 17:55:14 +05:30
className = { ` edit btn ${
isPanelFull ( widget . panel ) ? "btn-blur" : ""
} ` }
2025-03-27 12:16:20 +05:30
onClick = { isPanelFull ( widget . panel ) ? undefined : duplicateWidget }
>
2025-03-27 10:54:40 +05:30
< div className = "icon" >
< DublicateIcon / >
< / div >
< div className = "label" > Duplicate < / div >
< / div >
< div className = "edit btn" onClick = { deleteSelectedChart } >
< div className = "icon" >
< DeleteIcon / >
< / div >
< div className = "label" > Delete < / div >
< / div >
< / div >
) }
2025-03-25 15:55:48 +05:30
{ /* Render charts based on widget type */ }
2025-04-01 19:35:11 +05:30
2025-04-01 19:06:29 +05:30
{ widget . type === "progress 1" && (
< ProgressCard1 title = { widget . title } id = { widget . id } / >
) }
{ widget . type === "progress 2" && (
< ProgressCard2 title = { widget . title } id = { widget . id } / >
2025-03-25 15:55:48 +05:30
) }
{ widget . type === "line" && (
< LineGraphComponent
2025-03-29 18:42:08 +05:30
id = { widget . id }
2025-03-25 15:55:48 +05:30
type = { widget . type }
title = { widget . title }
fontSize = { widget . fontSize }
fontWeight = { widget . fontWeight }
/ >
) }
{ widget . type === "bar" && (
< BarGraphComponent
2025-03-29 18:42:08 +05:30
id = { widget . id }
2025-03-25 15:55:48 +05:30
type = { widget . type }
title = { widget . title }
fontSize = { widget . fontSize }
fontWeight = { widget . fontWeight }
/ >
) }
{ widget . type === "pie" && (
< PieGraphComponent
2025-03-29 18:42:08 +05:30
id = { widget . id }
2025-03-25 15:55:48 +05:30
type = { widget . type }
title = { widget . title }
fontSize = { widget . fontSize }
fontWeight = { widget . fontWeight }
/ >
) }
{ widget . type === "doughnut" && (
< DoughnutGraphComponent
2025-03-31 15:02:35 +05:30
id = { widget . id }
2025-03-25 15:55:48 +05:30
type = { widget . type }
title = { widget . title }
fontSize = { widget . fontSize }
fontWeight = { widget . fontWeight }
/ >
) }
{ widget . type === "polarArea" && (
< PolarAreaGraphComponent
2025-03-31 19:22:37 +05:30
id = { widget . id }
2025-03-25 15:55:48 +05:30
type = { widget . type }
title = { widget . title }
fontSize = { widget . fontSize }
fontWeight = { widget . fontWeight }
/ >
2025-03-25 11:47:41 +05:30
) }
< / div >
< / >
) ;
} ;
2025-04-07 17:55:14 +05:30
2025-04-09 18:06:08 +05:30
// by using canvasDimensions.height canvasDimensions.width dynamically div value insted of static 6 and 4 calculate according to canvasDimensions.width canvasDimensions.height