2025-03-25 06:17:41 +00:00
import { useWidgetStore } from "../../../store/useWidgetStore" ;
2025-03-25 10:25:48 +00:00
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 13:36:29 +00:00
import ProgressCard1 from "../realTimeVis/charts/ProgressCard1" ;
import ProgressCard2 from "../realTimeVis/charts/ProgressCard2" ;
2025-03-27 05:24:40 +00:00
import {
DeleteIcon ,
DublicateIcon ,
KebabIcon ,
} from "../../icons/ExportCommonIcons" ;
2025-03-27 06:46:20 +00:00
import { useEffect , useRef , useState } from "react" ;
2025-03-29 13:51:20 +00:00
import { duplicateWidgetApi } from "../../../services/realTimeVisulization/zoneData/duplicateWidget" ;
import { deleteWidgetApi } from "../../../services/realTimeVisulization/zoneData/deleteWidgetApi" ;
2025-04-02 06:59:07 +00:00
import { useClickOutside } from "./functions/handleWidgetsOuterClick" ;
2025-04-01 14:05:11 +00:00
import { useSocketStore } from "../../../store/store" ;
2025-04-03 12:45:26 +00:00
import { usePlayButtonStore } from "../../../store/usePlayButtonStore" ;
2025-04-08 13:04:21 +00:00
import OuterClick from "../../../utils/outerClick" ;
2025-03-27 05:24:40 +00:00
type Side = "top" | "bottom" | "left" | "right" ;
interface Widget {
id : string ;
type : string ;
title : string ;
panel : Side ;
data : any ;
}
2025-03-25 06:17:41 +00:00
2025-03-25 10:25:48 +00:00
export const DraggableWidget = ( {
widget ,
2025-03-27 06:46:20 +00:00
hiddenPanels ,
2025-03-27 05:24:40 +00:00
index ,
onReorder ,
openKebabId ,
setOpenKebabId ,
selectedZone ,
setSelectedZone ,
2025-03-25 10:25:48 +00:00
} : {
2025-03-27 05:24:40 +00:00
selectedZone : {
zoneName : string ;
2025-03-29 13:51:20 +00:00
zoneId : string ;
2025-03-27 05:24:40 +00:00
activeSides : Side [ ] ;
panelOrder : Side [ ] ;
lockedPanels : Side [ ] ;
widgets : Widget [ ] ;
} ;
setSelectedZone : React.Dispatch <
React . SetStateAction < {
zoneName : string ;
activeSides : Side [ ] ;
panelOrder : Side [ ] ;
2025-03-28 13:43:20 +00:00
2025-03-27 05:24:40 +00:00
lockedPanels : Side [ ] ;
2025-03-27 10:46:31 +00:00
zoneId : string ;
zoneViewPortTarget : number [ ] ;
zoneViewPortPosition : number [ ] ;
widgets : {
id : string ;
type : string ;
title : string ;
panel : Side ;
data : any ;
} [ ] ;
2025-03-27 05:24:40 +00:00
} >
> ;
2025-03-27 10:46:31 +00:00
2025-03-25 10:25:48 +00:00
widget : any ;
2025-03-27 06:46:20 +00:00
hiddenPanels : string [ ] ;
2025-03-27 05:24:40 +00:00
index : number ;
onReorder : ( fromIndex : number , toIndex : number ) = > void ;
2025-03-27 06:46:20 +00:00
openKebabId : string | null ;
setOpenKebabId : ( id : string | null ) = > void ;
2025-03-25 10:25:48 +00:00
} ) = > {
2025-04-01 14:05:11 +00:00
const { visualizationSocket } = useSocketStore ( ) ;
2025-03-25 06:17:41 +00:00
const { selectedChartId , setSelectedChartId } = useWidgetStore ( ) ;
2025-03-27 06:46:20 +00:00
const [ panelDimensions , setPanelDimensions ] = useState < {
[ side in Side ] ? : { width : number ; height : number } ;
} > ( { } ) ;
2025-03-25 06:17:41 +00:00
const handlePointerDown = ( ) = > {
if ( selectedChartId ? . id !== widget . id ) {
setSelectedChartId ( widget ) ;
}
} ;
2025-04-02 06:59:07 +00:00
const chartWidget = useRef < HTMLDivElement > ( null ) ;
2025-04-09 12:36:08 +00:00
OuterClick ( {
contextClassName : [
"chart-container" ,
"floating" ,
"sidebar-right-wrapper" ,
"card" ,
2025-04-10 04:36:53 +00:00
"dropdown-menu" ,
2025-04-09 12:36:08 +00:00
] ,
setMenuVisible : ( ) = > setSelectedChartId ( null ) ,
} ) ;
2025-04-08 13:04:21 +00:00
2025-03-29 13:51:20 +00:00
const deleteSelectedChart = async ( ) = > {
try {
const email = localStorage . getItem ( "email" ) || "" ;
const organization = email ? . split ( "@" ) [ 1 ] ? . split ( "." ) [ 0 ] ;
2025-04-01 14:05:11 +00:00
let deleteWidget = {
zoneId : selectedZone.zoneId ,
widgetID : widget.id ,
2025-04-07 12:25:14 +00:00
organization : organization ,
} ;
2025-04-07 12:57:01 +00:00
2025-04-01 14:05:11 +00:00
if ( visualizationSocket ) {
2025-04-07 12:25:14 +00:00
visualizationSocket . emit ( "v2:viz-widget:delete" , deleteWidget ) ;
2025-03-29 13:51:20 +00:00
}
2025-04-01 14:05:11 +00:00
const updatedWidgets = selectedZone . widgets . filter (
( w : Widget ) = > w . id !== widget . id
) ;
2025-04-07 12:57:01 +00:00
2025-04-01 14:05:11 +00:00
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 13:51:20 +00:00
} catch ( error ) {
} finally {
setOpenKebabId ( null ) ;
}
2025-03-26 06:58:22 +00:00
} ;
2025-03-27 06:46:20 +00:00
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 06:58:22 +00:00
}
2025-03-27 06:46:20 +00:00
return panel === "top" || panel === "bottom"
? Math . floor ( dimensions . width / CHART_WIDTH )
: Math . floor ( dimensions . height / CHART_HEIGHT ) ;
2025-03-26 06:58:22 +00:00
} ;
2025-03-27 06:46:20 +00:00
const isPanelFull = ( panel : Side ) = > {
const currentWidgetCount = getCurrentWidgetCount ( panel ) ;
const panelCapacity = calculatePanelCapacity ( panel ) ;
return currentWidgetCount >= panelCapacity ;
2025-03-27 05:24:40 +00:00
} ;
2025-03-29 13:51:20 +00:00
const duplicateWidget = async ( ) = > {
try {
const email = localStorage . getItem ( "email" ) || "" ;
const organization = email ? . split ( "@" ) [ 1 ] ? . split ( "." ) [ 0 ] ;
2025-03-27 05:24:40 +00:00
2025-03-29 13:51:20 +00:00
const duplicatedWidget : Widget = {
. . . widget ,
id : ` ${ widget . id } -copy- ${ Date . now ( ) } ` ,
} ;
2025-03-27 06:46:20 +00:00
2025-04-02 13:19:18 +00:00
let duplicateWidget = {
organization : organization ,
zoneId : selectedZone.zoneId ,
2025-04-07 12:25:14 +00:00
widget : duplicatedWidget ,
} ;
2025-04-02 13:19:18 +00:00
if ( visualizationSocket ) {
2025-04-07 12:25:14 +00:00
visualizationSocket . emit ( "v2:viz-widget:add" , duplicateWidget ) ;
2025-03-29 13:51:20 +00:00
}
2025-04-02 13:19:18 +00:00
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 13:51:20 +00:00
} catch ( error ) {
} finally {
setOpenKebabId ( null ) ;
}
2025-03-27 05:24:40 +00:00
} ;
2025-03-27 06:46:20 +00:00
const handleKebabClick = ( event : React.MouseEvent < HTMLDivElement > ) = > {
event . stopPropagation ( ) ;
if ( openKebabId === widget . id ) {
setOpenKebabId ( null ) ;
} else {
setOpenKebabId ( widget . id ) ;
}
} ;
2025-03-27 05:24:40 +00:00
const widgetRef = useRef < HTMLDivElement | null > ( null ) ;
2025-03-26 06:58:22 +00:00
2025-03-27 05:24:40 +00:00
useEffect ( ( ) = > {
const handleClickOutside = ( event : MouseEvent ) = > {
if (
widgetRef . current &&
! widgetRef . current . contains ( event . target as Node )
) {
2025-03-27 06:46:20 +00:00
setOpenKebabId ( null ) ;
2025-03-27 05:24:40 +00:00
}
} ;
document . addEventListener ( "mousedown" , handleClickOutside ) ;
return ( ) = > {
document . removeEventListener ( "mousedown" , handleClickOutside ) ;
} ;
} , [ setOpenKebabId ] ) ;
2025-03-27 06:54:36 +00:00
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 05:24:40 +00:00
2025-03-27 06:54:36 +00:00
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 05:24:40 +00:00
} ;
2025-04-02 12:21:44 +00:00
// useClickOutside(chartWidget, () => {
// setSelectedChartId(null);
// });
2025-04-03 12:45:26 +00:00
const { isPlaying } = usePlayButtonStore ( ) ;
2025-03-27 05:24:40 +00:00
2025-04-07 12:25:14 +00:00
const [ canvasDimensions , setCanvasDimensions ] = useState ( {
width : 0 ,
height : 0 ,
} ) ;
// Track canvas dimensions
2025-04-08 13:04:21 +00:00
// 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 12:25:14 +00:00
2025-04-08 13:04:21 +00:00
updateCanvasDimensions ( ) ;
const resizeObserver = new ResizeObserver ( updateCanvasDimensions ) ;
resizeObserver . observe ( canvas ) ;
2025-04-07 12:25:14 +00:00
2025-04-08 13:04:21 +00:00
return ( ) = > resizeObserver . unobserve ( canvas ) ;
} , [ ] ) ;
2025-04-07 12:25:14 +00:00
2025-03-25 06:17:41 +00:00
return (
< >
2025-04-07 12:25:14 +00:00
< style >
{ `
: root {
2025-04-08 13:04:21 +00:00
-- realTimeViz - container - width : $ { canvasDimensions . width } px ;
2025-04-07 12:25:14 +00:00
-- realTimeViz - container - height : $ { canvasDimensions . height } px ;
2025-04-08 13:04:21 +00:00
2025-04-07 12:25:14 +00:00
}
` }
< / style >
2025-03-25 06:17:41 +00:00
< div
2025-03-26 06:58:22 +00:00
draggable
2025-03-25 06:17:41 +00:00
key = { widget . id }
2025-04-07 12:25:14 +00:00
className = { ` chart-container ${
selectedChartId ? . id === widget . id && ! isPlaying && "activeChart"
} ` }
2025-03-25 06:17:41 +00:00
onPointerDown = { handlePointerDown }
2025-03-26 06:58:22 +00:00
onDragStart = { handleDragStart }
onDragEnter = { handleDragEnter }
onDragOver = { handleDragOver }
onDrop = { handleDrop }
2025-03-25 10:25:48 +00:00
style = { {
2025-04-07 12:25:14 +00:00
width : [ "top" , "bottom" ] . includes ( widget . panel )
2025-04-08 13:04:21 +00:00
? ` calc( ${ canvasDimensions . width } px / 6) `
: undefined ,
2025-04-07 12:25:14 +00:00
height : [ "left" , "right" ] . includes ( widget . panel )
2025-04-08 13:04:21 +00:00
? ` calc( ${ canvasDimensions . height - 10 } px / 4) `
: undefined ,
2025-03-25 10:25:48 +00:00
} }
2025-04-02 06:59:07 +00:00
ref = { chartWidget }
onClick = { ( ) = > setSelectedChartId ( widget ) }
2025-03-25 06:17:41 +00:00
>
2025-03-27 05:24:40 +00:00
{ /* Kebab Icon */ }
< div className = "icon kebab" onClick = { handleKebabClick } >
< KebabIcon / >
< / div >
{ /* Kebab Options */ }
{ openKebabId === widget . id && (
2025-03-27 06:46:20 +00:00
< div className = "kebab-options" ref = { widgetRef } >
< div
2025-04-07 12:25:14 +00:00
className = { ` edit btn ${
isPanelFull ( widget . panel ) ? "btn-blur" : ""
} ` }
2025-03-27 06:46:20 +00:00
onClick = { isPanelFull ( widget . panel ) ? undefined : duplicateWidget }
>
2025-03-27 05:24:40 +00:00
< 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 10:25:48 +00:00
{ /* Render charts based on widget type */ }
2025-04-01 14:05:11 +00:00
2025-04-01 13:36:29 +00:00
{ 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 10:25:48 +00:00
) }
{ widget . type === "line" && (
< LineGraphComponent
2025-03-29 13:12:08 +00:00
id = { widget . id }
2025-03-25 10:25:48 +00:00
type = { widget . type }
title = { widget . title }
fontSize = { widget . fontSize }
fontWeight = { widget . fontWeight }
/ >
) }
{ widget . type === "bar" && (
< BarGraphComponent
2025-03-29 13:12:08 +00:00
id = { widget . id }
2025-03-25 10:25:48 +00:00
type = { widget . type }
title = { widget . title }
fontSize = { widget . fontSize }
fontWeight = { widget . fontWeight }
/ >
) }
{ widget . type === "pie" && (
< PieGraphComponent
2025-03-29 13:12:08 +00:00
id = { widget . id }
2025-03-25 10:25:48 +00:00
type = { widget . type }
title = { widget . title }
fontSize = { widget . fontSize }
fontWeight = { widget . fontWeight }
/ >
) }
{ widget . type === "doughnut" && (
< DoughnutGraphComponent
2025-03-31 09:32:35 +00:00
id = { widget . id }
2025-03-25 10:25:48 +00:00
type = { widget . type }
title = { widget . title }
fontSize = { widget . fontSize }
fontWeight = { widget . fontWeight }
/ >
) }
{ widget . type === "polarArea" && (
< PolarAreaGraphComponent
2025-03-31 13:52:37 +00:00
id = { widget . id }
2025-03-25 10:25:48 +00:00
type = { widget . type }
title = { widget . title }
fontSize = { widget . fontSize }
fontWeight = { widget . fontWeight }
/ >
2025-03-25 06:17:41 +00:00
) }
< / div >
< / >
) ;
} ;
2025-04-07 12:25:14 +00:00
2025-04-09 12:36:08 +00:00
// by using canvasDimensions.height canvasDimensions.width dynamically div value insted of static 6 and 4 calculate according to canvasDimensions.width canvasDimensions.height