@collabdt/core 0.3.1 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/components/authentication/ForgotPassword.d.ts.map +1 -1
- package/dist/core/components/authentication/ForgotPassword.js +0 -1
- package/dist/core/components/authentication/ForgotPassword.js.map +1 -1
- package/dist/core/components/ui/Comments/CollapsibleCommentItem.d.ts +12 -2
- package/dist/core/components/ui/Comments/CollapsibleCommentItem.d.ts.map +1 -1
- package/dist/core/components/ui/Comments/CollapsibleCommentItem.js +137 -139
- package/dist/core/components/ui/Comments/CollapsibleCommentItem.js.map +1 -1
- package/dist/core/components/ui/Comments/Comment.d.ts +18 -1
- package/dist/core/components/ui/Comments/Comment.d.ts.map +1 -1
- package/dist/core/components/ui/Comments/Comment.js +94 -23
- package/dist/core/components/ui/Comments/Comment.js.map +1 -1
- package/dist/core/components/ui/Comments/CommentActionButtons.d.ts +38 -0
- package/dist/core/components/ui/Comments/CommentActionButtons.d.ts.map +1 -0
- package/dist/core/components/ui/Comments/CommentActionButtons.js +134 -0
- package/dist/core/components/ui/Comments/CommentActionButtons.js.map +1 -0
- package/dist/core/components/ui/Comments/CommentInput.d.ts.map +1 -1
- package/dist/core/components/ui/Comments/CommentInput.js +5 -2
- package/dist/core/components/ui/Comments/CommentInput.js.map +1 -1
- package/dist/core/components/ui/Comments/CommentsSection.d.ts.map +1 -1
- package/dist/core/components/ui/Comments/CommentsSection.js +86 -42
- package/dist/core/components/ui/Comments/CommentsSection.js.map +1 -1
- package/dist/core/components/ui/Comments/commentUtils.d.ts.map +1 -1
- package/dist/core/components/ui/Comments/commentUtils.js +2 -1
- package/dist/core/components/ui/Comments/commentUtils.js.map +1 -1
- package/dist/core/components/ui/FilesManager/src/useFileUploadWithProgress.d.ts.map +1 -1
- package/dist/core/components/ui/FilesManager/src/useFileUploadWithProgress.js +0 -1
- package/dist/core/components/ui/FilesManager/src/useFileUploadWithProgress.js.map +1 -1
- package/dist/core/components/ui/InfoSidebar/Container.d.ts.map +1 -1
- package/dist/core/components/ui/InfoSidebar/Container.js +16 -12
- package/dist/core/components/ui/InfoSidebar/Container.js.map +1 -1
- package/dist/core/components/ui/Sensors/CollapsibleSensorItem.d.ts +1 -2
- package/dist/core/components/ui/Sensors/CollapsibleSensorItem.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/CollapsibleSensorItem.js +66 -94
- package/dist/core/components/ui/Sensors/CollapsibleSensorItem.js.map +1 -1
- package/dist/core/components/ui/Sensors/Sensor.d.ts +16 -3
- package/dist/core/components/ui/Sensors/Sensor.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/Sensor.js +76 -37
- package/dist/core/components/ui/Sensors/Sensor.js.map +1 -1
- package/dist/core/components/ui/Sensors/SensorChart.d.ts +12 -2
- package/dist/core/components/ui/Sensors/SensorChart.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/SensorChart.js +49 -12
- package/dist/core/components/ui/Sensors/SensorChart.js.map +1 -1
- package/dist/core/components/ui/Sensors/SensorDetailDialog.d.ts +9 -0
- package/dist/core/components/ui/Sensors/SensorDetailDialog.d.ts.map +1 -0
- package/dist/core/components/ui/Sensors/SensorDetailDialog.js +128 -0
- package/dist/core/components/ui/Sensors/SensorDetailDialog.js.map +1 -0
- package/dist/core/components/ui/Sensors/SensorInput.d.ts +6 -1
- package/dist/core/components/ui/Sensors/SensorInput.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/SensorInput.js +94 -9
- package/dist/core/components/ui/Sensors/SensorInput.js.map +1 -1
- package/dist/core/components/ui/Sensors/SensorTagsSection.d.ts +3 -1
- package/dist/core/components/ui/Sensors/SensorTagsSection.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/SensorTagsSection.js +4 -1
- package/dist/core/components/ui/Sensors/SensorTagsSection.js.map +1 -1
- package/dist/core/components/ui/Sensors/SensorsSection.d.ts +1 -3
- package/dist/core/components/ui/Sensors/SensorsSection.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/SensorsSection.js +33 -10
- package/dist/core/components/ui/Sensors/SensorsSection.js.map +1 -1
- package/dist/core/components/ui/Sensors/TimeZoneSelect.d.ts +7 -0
- package/dist/core/components/ui/Sensors/TimeZoneSelect.d.ts.map +1 -0
- package/dist/core/components/ui/Sensors/TimeZoneSelect.js +56 -0
- package/dist/core/components/ui/Sensors/TimeZoneSelect.js.map +1 -0
- package/dist/core/components/ui/Sensors/sensorData.d.ts +42 -0
- package/dist/core/components/ui/Sensors/sensorData.d.ts.map +1 -0
- package/dist/core/components/ui/Sensors/sensorData.js +118 -0
- package/dist/core/components/ui/Sensors/sensorData.js.map +1 -0
- package/dist/core/components/ui/Sensors/sensorRange.d.ts +24 -0
- package/dist/core/components/ui/Sensors/sensorRange.d.ts.map +1 -0
- package/dist/core/components/ui/Sensors/sensorRange.js +34 -0
- package/dist/core/components/ui/Sensors/sensorRange.js.map +1 -0
- package/dist/core/components/ui/Sensors/useSensorSeries.d.ts +15 -0
- package/dist/core/components/ui/Sensors/useSensorSeries.d.ts.map +1 -0
- package/dist/core/components/ui/Sensors/useSensorSeries.js +63 -0
- package/dist/core/components/ui/Sensors/useSensorSeries.js.map +1 -0
- package/dist/core/components/ui/Sidebar.d.ts.map +1 -1
- package/dist/core/components/ui/Sidebar.js +27 -4
- package/dist/core/components/ui/Sidebar.js.map +1 -1
- package/dist/core/components/ui/ToolbarButton.d.ts.map +1 -1
- package/dist/core/components/ui/ToolbarButton.js +0 -1
- package/dist/core/components/ui/ToolbarButton.js.map +1 -1
- package/dist/core/components/viewers/Data/advancedFilter/FiltersDialog.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/advancedFilter/FiltersDialog.js +0 -2
- package/dist/core/components/viewers/Data/advancedFilter/FiltersDialog.js.map +1 -1
- package/dist/core/components/viewers/Data/details/EnergySourcesGrid.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/details/EnergySourcesGrid.js +0 -10
- package/dist/core/components/viewers/Data/details/EnergySourcesGrid.js.map +1 -1
- package/dist/core/components/viewers/Data/files/DatatableFileAdder.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/files/DatatableFileAdder.js +0 -1
- package/dist/core/components/viewers/Data/files/DatatableFileAdder.js.map +1 -1
- package/dist/core/components/viewers/Data/files/utils/fuzzySearch.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/files/utils/fuzzySearch.js +0 -1
- package/dist/core/components/viewers/Data/files/utils/fuzzySearch.js.map +1 -1
- package/dist/core/components/viewers/Data/userDetails/UserMoreOptions.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/userDetails/UserMoreOptions.js +0 -1
- package/dist/core/components/viewers/Data/userDetails/UserMoreOptions.js.map +1 -1
- package/dist/core/components/viewers/Data/utils/filterHelper.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/utils/filterHelper.js +0 -2
- package/dist/core/components/viewers/Data/utils/filterHelper.js.map +1 -1
- package/dist/core/components/viewers/Viewer.js +1 -1
- package/dist/core/components/viewers/Viewer.js.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/CommunicationTab/src/TopicsSection.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/CommunicationTab/src/TopicsSection.js +0 -3
- package/dist/core/components/viewers/bim/src/BimSidebar/src/CommunicationTab/src/TopicsSection.js.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/FilesSection.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/FilesSection.js +1 -7
- package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/FilesSection.js.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/ModelsSection.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/ModelsSection.js +1 -9
- package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/ModelsSection.js.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.d.ts +1 -3
- package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.js +2 -2
- package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.js.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/TabSelector.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/TabSelector.js +4 -3
- package/dist/core/components/viewers/bim/src/BimSidebar/src/TabSelector.js.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/index.js +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/index.js.map +1 -1
- package/dist/core/components/viewers/bim/src/ClippingPlane.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/ClippingPlane.js +0 -1
- package/dist/core/components/viewers/bim/src/ClippingPlane.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/index.d.ts +1 -2
- package/dist/core/components/viewers/bim/src/tools/AddToBim/index.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/index.js +25 -2
- package/dist/core/components/viewers/bim/src/tools/AddToBim/index.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimComment.d.ts +16 -10
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimComment.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimComment.js +26 -6
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimComment.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.d.ts +2 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.d.ts.map +1 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.js +5 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.js.map +1 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.d.ts +8 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.js +15 -3
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/clusterMarkersByScreenSpace.d.ts +44 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/clusterMarkersByScreenSpace.d.ts.map +1 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/clusterMarkersByScreenSpace.js +77 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/clusterMarkersByScreenSpace.js.map +1 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.d.ts +3 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.d.ts.map +1 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.js +5 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.js.map +1 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentMarkerConfig.d.ts +33 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentMarkerConfig.d.ts.map +1 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentMarkerConfig.js +90 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentMarkerConfig.js.map +1 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/renderCSS2DMarkers.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/renderCSS2DMarkers.js +2 -2
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/renderCSS2DMarkers.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.d.ts +7 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.js +50 -73
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.d.ts +7 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.js +39 -11
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/ClippingTool/ClippingTool.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/ClippingTool/ClippingTool.js +1 -5
- package/dist/core/components/viewers/bim/src/tools/ClippingTool/ClippingTool.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/ExplodeByLevelTool.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/ExplodeByLevelTool.js +0 -1
- package/dist/core/components/viewers/bim/src/tools/ExplodeByLevelTool.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/measureBimTool.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/measureBimTool.js +1 -2
- package/dist/core/components/viewers/bim/src/tools/measureBimTool.js.map +1 -1
- package/dist/core/components/viewers/map/MapViewer.d.ts +1 -2
- package/dist/core/components/viewers/map/MapViewer.d.ts.map +1 -1
- package/dist/core/components/viewers/map/MapViewer.js +2 -2
- package/dist/core/components/viewers/map/MapViewer.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/arcGISDatasets.d.ts.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/arcGISDatasets.js +11 -14
- package/dist/core/components/viewers/map/datasets/src/arcGISDatasets.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/ckanDatasets.d.ts.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/ckanDatasets.js +2 -5
- package/dist/core/components/viewers/map/datasets/src/ckanDatasets.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/localDatasets.d.ts.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/localDatasets.js +0 -3
- package/dist/core/components/viewers/map/datasets/src/localDatasets.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/opendatasoftDatasets.d.ts.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/opendatasoftDatasets.js +0 -11
- package/dist/core/components/viewers/map/datasets/src/opendatasoftDatasets.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/DatabaseBuildingPopover/src/BuildingsTools.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/DatabaseBuildingPopover/src/BuildingsTools.js +0 -1
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/DatabaseBuildingPopover/src/BuildingsTools.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/NonDatabaseBuildingPopover.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/NonDatabaseBuildingPopover.js +0 -1
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/NonDatabaseBuildingPopover.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/OpenDataFeaturePopover.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/OpenDataFeaturePopover.js +0 -1
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/OpenDataFeaturePopover.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/index.d.ts +1 -2
- package/dist/core/components/viewers/map/src/MapLayers/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/index.js +2 -2
- package/dist/core/components/viewers/map/src/MapLayers/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/CommentLayer/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/CommentLayer/index.js +174 -53
- package/dist/core/components/viewers/map/src/MapLayers/src/CommentLayer/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/FileLayer/components/MapFileManager.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/FileLayer/components/MapFileManager.js +2 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/FileLayer/components/MapFileManager.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.d.ts +1 -3
- package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.js +74 -42
- package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/TabSelector.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/TabSelector.js +4 -3
- package/dist/core/components/viewers/map/src/MapSidebar/TabSelector.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/index.js +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/FileTab/src/FilesSection.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/FileTab/src/FilesSection.js +0 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/FileTab/src/FilesSection.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/FileTab/src/ModelsSection.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/FileTab/src/ModelsSection.js +1 -5
- package/dist/core/components/viewers/map/src/MapSidebar/src/FileTab/src/ModelsSection.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.d.ts +1 -3
- package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.js +2 -2
- package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/tools/AddTools/AddFile/utils/uploadToPresignedURLS.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/tools/AddTools/AddFile/utils/uploadToPresignedURLS.js +1 -3
- package/dist/core/components/viewers/map/src/tools/AddTools/AddFile/utils/uploadToPresignedURLS.js.map +1 -1
- package/dist/core/components/viewers/map/utils/MapEventManager/MapClickManager.d.ts.map +1 -1
- package/dist/core/components/viewers/map/utils/MapEventManager/MapClickManager.js +0 -1
- package/dist/core/components/viewers/map/utils/MapEventManager/MapClickManager.js.map +1 -1
- package/dist/core/components/viewers/map/utils/flyToEach.d.ts.map +1 -1
- package/dist/core/components/viewers/map/utils/flyToEach.js +0 -1
- package/dist/core/components/viewers/map/utils/flyToEach.js.map +1 -1
- package/dist/core/components/viewers/map/utils/getOsmIdbyCoordinates.d.ts.map +1 -1
- package/dist/core/components/viewers/map/utils/getOsmIdbyCoordinates.js +0 -2
- package/dist/core/components/viewers/map/utils/getOsmIdbyCoordinates.js.map +1 -1
- package/dist/core/components/viewers/shared/markers/BimMarkerCluster.d.ts +19 -0
- package/dist/core/components/viewers/shared/markers/BimMarkerCluster.d.ts.map +1 -0
- package/dist/core/components/viewers/shared/markers/BimMarkerCluster.js +108 -0
- package/dist/core/components/viewers/shared/markers/BimMarkerCluster.js.map +1 -0
- package/dist/core/components/viewers/shared/markers/computeMarkerLookAt.d.ts +24 -0
- package/dist/core/components/viewers/shared/markers/computeMarkerLookAt.d.ts.map +1 -0
- package/dist/core/components/viewers/shared/markers/computeMarkerLookAt.js +27 -0
- package/dist/core/components/viewers/shared/markers/computeMarkerLookAt.js.map +1 -0
- package/dist/core/components/viewers/shared/markers/types.d.ts +66 -0
- package/dist/core/components/viewers/shared/markers/types.d.ts.map +1 -0
- package/dist/core/components/viewers/shared/markers/types.js +1 -0
- package/dist/core/components/viewers/shared/markers/types.js.map +1 -0
- package/dist/core/components/viewers/shared/markers/useMarkerLayerBim.d.ts +22 -0
- package/dist/core/components/viewers/shared/markers/useMarkerLayerBim.d.ts.map +1 -0
- package/dist/core/components/viewers/shared/markers/useMarkerLayerBim.js +233 -0
- package/dist/core/components/viewers/shared/markers/useMarkerLayerBim.js.map +1 -0
- package/dist/core/hooks/comments/comments.d.ts +11 -0
- package/dist/core/hooks/comments/comments.d.ts.map +1 -1
- package/dist/core/hooks/comments/comments.js +3 -1
- package/dist/core/hooks/comments/comments.js.map +1 -1
- package/dist/core/hooks/comments/createCommentHooks.d.ts +11 -0
- package/dist/core/hooks/comments/createCommentHooks.d.ts.map +1 -1
- package/dist/core/hooks/comments/createCommentHooks.js +30 -1
- package/dist/core/hooks/comments/createCommentHooks.js.map +1 -1
- package/dist/core/hooks/files/files.d.ts.map +1 -1
- package/dist/core/hooks/files/files.js +0 -1
- package/dist/core/hooks/files/files.js.map +1 -1
- package/dist/core/hooks/ui/useResizableSidebarWidth.d.ts +30 -0
- package/dist/core/hooks/ui/useResizableSidebarWidth.d.ts.map +1 -0
- package/dist/core/hooks/ui/useResizableSidebarWidth.js +75 -0
- package/dist/core/hooks/ui/useResizableSidebarWidth.js.map +1 -0
- package/dist/core/i18n/messages/en.json +10 -1
- package/dist/core/i18n/messages/es.json +11 -2
- package/dist/core/i18n/messages/fr.json +11 -2
- package/dist/core/store/AppConfig/context.d.ts.map +1 -1
- package/dist/core/store/AppConfig/context.js +4 -1
- package/dist/core/store/AppConfig/context.js.map +1 -1
- package/dist/core/store/AppConfig/reducer.d.ts +10 -0
- package/dist/core/store/AppConfig/reducer.d.ts.map +1 -1
- package/dist/core/store/AppConfig/reducer.js +10 -0
- package/dist/core/store/AppConfig/reducer.js.map +1 -1
- package/dist/core/store/Datasets/reducer.d.ts.map +1 -1
- package/dist/core/store/Datasets/reducer.js +0 -2
- package/dist/core/store/Datasets/reducer.js.map +1 -1
- package/dist/core/store/Files/reducer.js +0 -1
- package/dist/core/store/Files/reducer.js.map +1 -1
- package/dist/core/store/Menus/context.d.ts.map +1 -1
- package/dist/core/store/Menus/context.js +8 -2
- package/dist/core/store/Menus/context.js.map +1 -1
- package/dist/core/store/Menus/reducer.d.ts +65 -1
- package/dist/core/store/Menus/reducer.d.ts.map +1 -1
- package/dist/core/store/Menus/reducer.js +39 -3
- package/dist/core/store/Menus/reducer.js.map +1 -1
- package/dist/core/utils/markerUtils.d.ts +10 -0
- package/dist/core/utils/markerUtils.d.ts.map +1 -1
- package/dist/core/utils/markerUtils.js +9 -1
- package/dist/core/utils/markerUtils.js.map +1 -1
- package/dist/core/utils/memcache.d.ts.map +1 -1
- package/dist/core/utils/memcache.js +0 -7
- package/dist/core/utils/memcache.js.map +1 -1
- package/dist/core/utils/timeUtils.d.ts +24 -1
- package/dist/core/utils/timeUtils.d.ts.map +1 -1
- package/dist/core/utils/timeUtils.js +52 -3
- package/dist/core/utils/timeUtils.js.map +1 -1
- package/package.json +4 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/CommentLayer/index.tsx"],"sourcesContent":["'use client'\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport { useSession } from 'next-auth/react'\r\nimport { useTranslations } from 'next-intl'\r\nimport * as React from 'react'\r\nimport { Source, Layer, Popup, Marker } from 'react-map-gl/maplibre'\r\nimport { toast } from 'sonner'\r\n\r\nimport { useComment, useComments } from '../../../../../../../hooks/comments/comments'\r\nimport { useUser, useUsers } from '../../../../../../../hooks/users/users'\r\nimport { MapContext, MenusContext } from '../../../../../../../store'\r\nimport { ViewerNames, type Comment as IComment } from '../../../../../../../types/dbTypes'\r\nimport { Avatar } from '../../../../../../ui/Avatar'\r\nimport Comment from '../../../../../../ui/Comments/Comment'\r\nimport { UserAvatar } from '../../../../../../ui/UserAvatar'\r\nimport { extractCoordinatesFromFeature } from '../../../../utils/extractCoordinates'\r\nimport { MapLayerClickPriority } from '../../../../utils/MapEventManager/MapClickManager'\r\nimport { createClusterLayer, createClusterCountLayer, createUnclusteredPointLayer } from '../mapLayersUtils'\r\n\r\nimport type { ClickCallback } from '../../../../utils/MapEventManager/MapClickManager';\r\nimport type { MapGeoJSONFeature, MapLayerMouseEvent } from 'maplibre-gl'\r\n\r\ntype MapComment = IComment & {\r\n authorName?: string\r\n imageFileId?: number | null\r\n}\r\n\r\nconst CommentAvatarMarker = ({ feature, isHighlighted, onMouseEnter, onMouseLeave }: { feature: MapGeoJSONFeature; isHighlighted?: boolean; onMouseEnter?: () => void; onMouseLeave?: () => void }) => {\r\n const authorId = feature.properties?.authorId\r\n const { user: author } = useUser(authorId != null ? String(authorId) : '')\r\n const userName = feature.properties?.authorName\r\n const userImageFileId = feature.properties?.imageFileId\r\n const resolvedImageFileId = author?.imageFileId ?? userImageFileId ?? null\r\n\r\n const coords = extractCoordinatesFromFeature(feature)\r\n if (!coords) return null\r\n\r\n return (\r\n <Marker key={String(feature.properties?.id ?? `${coords.lng},${coords.lat}`)} longitude={coords.lng} latitude={coords.lat} anchor=\"center\">\r\n <div\r\n style={{\r\n width: '36px',\r\n height: '36px',\r\n borderRadius: '50%',\r\n boxShadow: isHighlighted\r\n ? '0 0 0 3px #73cee2, 0 0 8px rgba(115, 206, 226, 0.5)'\r\n : '0 0 0 2px white ',\r\n transition: 'all 0.2s ease-in-out',\r\n transform: isHighlighted ? 'scale(1.2)' : 'scale(1)',\r\n overflow: 'hidden',\r\n }}\r\n onMouseEnter={onMouseEnter}\r\n onMouseLeave={onMouseLeave}\r\n >\r\n <Avatar className=\"h-full w-full\">\r\n <UserAvatar\r\n imageFileId={resolvedImageFileId}\r\n name={userName}\r\n className=\"h-full w-full rounded-full object-cover\"\r\n />\r\n </Avatar>\r\n </div>\r\n </Marker>\r\n )\r\n}\r\n\r\nexport const CommentLayer = () => {\r\n const [hoveredCommentId, setHoveredCommentId] = React.useState<number | null>(null)\r\n const clusterLayer = createClusterLayer('comments')\r\n const clusterCountLayer = createClusterCountLayer('comments')\r\n const unclusteredPointLayer = createUnclusteredPointLayer('comments')\r\n\r\n // global map state\r\n\r\n const t = useTranslations('CommentLayers')\r\n\r\n const { state: mapState } = React.useContext(MapContext)\r\n const { map, mapClickManager } = mapState.map\r\n const { comments } = useComments()\r\n const { users } = useUsers()\r\n const user = useSession().data?.user\r\n\r\n const [popupInfo, setPopUpInfo] = React.useState<(Partial<IComment> & { authorName?: string; imageFileId?: number | null }) | null>(null)\r\n const { user: popupAuthor } = useUser(popupInfo?.authorId != null ? String(popupInfo.authorId) : '')\r\n const { deleteComment } = useComment(popupInfo?.id ?? null)\r\n const { state: menusState } = React.useContext(MenusContext)\r\n const { commentsVisibleInViewer, currentCommentId } = menusState.menus\r\n\r\n const eligibleComments = comments\r\n .filter((comment) => comment.viewer === ViewerNames.map)\r\n .map((comment) => {\r\n const user = users.find(u => u.id === comment.authorId)\r\n return {\r\n ...comment,\r\n authorName: user?.name ?? 'Unknown User',\r\n imageFileId: user?.imageFileId ?? null,\r\n }\r\n })\r\n\r\n // Close popup if the comment was deleted\r\n React.useEffect(() => {\r\n if (popupInfo && !comments.find((c) => c.id === popupInfo.id)) {\r\n setPopUpInfo(null)\r\n }\r\n }, [comments, popupInfo])\r\n\r\n const geojsonCommentData = React.useMemo(() => {\r\n const convertDataToGeojson = (commentData: MapComment[]): GeoJSON.FeatureCollection<GeoJSON.Point, { [key: string]: any }> => {\r\n const commentFeatures: GeoJSON.Feature<GeoJSON.Point, { [key: string]: any }>[] = commentData\r\n .map((comment: MapComment) => {\r\n const { longitude, latitude, id, text, createdAt, authorId, organizationId, visible, authorName, imageFileId } = comment\r\n\r\n return {\r\n type: 'Feature',\r\n geometry: {\r\n type: 'Point',\r\n coordinates: [longitude, latitude],\r\n },\r\n properties: {\r\n id: Number(id),\r\n organizationId,\r\n visible,\r\n longitude,\r\n latitude,\r\n authorId: Number(authorId),\r\n authorName,\r\n imageFileId,\r\n text,\r\n createdAt,\r\n viewer: ViewerNames.map,\r\n },\r\n }\r\n })\r\n\r\n const commentFC: GeoJSON.FeatureCollection<GeoJSON.Point, { [key: string]: any }> = {\r\n type: 'FeatureCollection',\r\n features: commentFeatures,\r\n }\r\n return commentFC\r\n }\r\n return convertDataToGeojson(eligibleComments)\r\n }, [eligibleComments])\r\n\r\n // event listeners for comment unclustered points\r\n React.useEffect(() => {\r\n if (!map) return\r\n const showCommentPopUp: ClickCallback = (e: MapLayerMouseEvent, features: MapGeoJSONFeature[]) => {\r\n const feature = features?.[0]\r\n\r\n if (!feature || feature.properties.point_count) return\r\n\r\n if (feature.geometry.type != 'Point') return\r\n const [longitude, latitude] = feature.geometry.coordinates\r\n const featureProperties = feature.properties as MapComment\r\n const { id, authorId, text, createdAt, authorName, imageFileId } = featureProperties\r\n setPopUpInfo({\r\n id: Number(id),\r\n authorId: Number(authorId),\r\n organizationId: feature.properties?.organizationId,\r\n visible: feature.properties?.visible,\r\n longitude,\r\n latitude,\r\n text,\r\n createdAt,\r\n authorName,\r\n imageFileId,\r\n viewer: ViewerNames.map,\r\n })\r\n }\r\n\r\n const mouseEnterChangeCursor = () => {\r\n map.getCanvas().style.cursor = 'pointer'\r\n }\r\n const mouseLeaveChangeCursor = () => {\r\n map.getCanvas().style.cursor = ''\r\n }\r\n\r\n // event listener for clicking on single point to show comment, hover to change cursor\r\n mapClickManager.register('comments-unclustered-points', MapLayerClickPriority.CommentLayersClickPriority, showCommentPopUp)\r\n\r\n map.on('mouseenter', 'comments-unclustered-points', mouseEnterChangeCursor)\r\n map.on('mouseleave', 'comments-unclustered-points', mouseLeaveChangeCursor)\r\n return () => {\r\n mapClickManager.unregister('comments-unclustered-points')\r\n map.off('mouseenter', 'comments-unclustered-points', mouseEnterChangeCursor)\r\n map.off('mouseleave', 'comments-unclustered-points', mouseLeaveChangeCursor)\r\n }\r\n }, [map, mapClickManager])\r\n\r\n const handleRemoveComment = () => {\r\n toast.success(t('commentDeleted'))\r\n deleteComment()\r\n setPopUpInfo(null)\r\n }\r\n\r\n // event listeners for clustered points\r\n React.useEffect(() => {\r\n if (!map) return\r\n\r\n const zoomInToDecluster = (e: MapLayerMouseEvent) => {\r\n const feature = e.features?.[0] // only proceed if this is actually a cluster\r\n if (!feature || !feature.properties.cluster_id) return\r\n\r\n const clusterId = feature.properties.cluster_id as number\r\n\r\n // Check if geometry has coordinates (not a GeometryCollection)\r\n if (!('coordinates' in feature.geometry)) return\r\n const [lng, lat] = feature.geometry.coordinates as [number, number]\r\n\r\n const source = map.getSource('comments') as any\r\n console.log(source)\r\n console.log(clusterId)\r\n source.getClusterExpansionZoom(clusterId).then(\r\n (zoom: number) => {\r\n console.log('easing to')\r\n map.easeTo({ center: [lng, lat], zoom })\r\n },\r\n )\r\n .catch((error) => {\r\n console.error(error)\r\n })\r\n }\r\n\r\n const mouseEnterChangeCursor = () => { map.getCanvas().style.cursor = 'pointer' }\r\n const mouseLeaveChangeCursor = () => { map.getCanvas().style.cursor = '' }\r\n\r\n // event listener for clicking on single point to zoom in and decluster, hover to change cursor\r\n map.on('click', 'comments-clusters', zoomInToDecluster)\r\n map.on('mouseenter', 'comments-clusters', mouseEnterChangeCursor)\r\n map.on('mouseleave', 'comments-clusters', mouseLeaveChangeCursor)\r\n return () => {\r\n map.off('click', 'comments-clusters', zoomInToDecluster)\r\n map.off('mouseenter', 'comments-clusters', mouseEnterChangeCursor)\r\n map.off('mouseleave', 'comments-clusters', mouseLeaveChangeCursor)\r\n }\r\n }, [map])\r\n\r\n const renderPopup = () => {\r\n if (!popupInfo) return null\r\n return (\r\n <Popup\r\n className=\"noBorderPopup\"\r\n longitude={popupInfo.longitude}\r\n latitude={popupInfo.latitude}\r\n closeOnClick={false}\r\n onClose={() => setPopUpInfo(null)}\r\n anchor=\"bottom\"\r\n style={{ height: '50px', border: 'none', boxShadow: 'none' }}\r\n offset={[0, 10]}\r\n >\r\n <Comment\r\n userName={popupInfo.authorName || ''}\r\n userImage={popupAuthor?.imageFileId ?? popupInfo.imageFileId ?? null}\r\n userImageFileId={popupAuthor?.imageFileId ?? popupInfo.imageFileId ?? null}\r\n text={popupInfo.text}\r\n createdAt={popupInfo.createdAt}\r\n onRemove={user.id === String(popupInfo.authorId) ? handleRemoveComment : null}\r\n onClose={() => setPopUpInfo(null)}\r\n />\r\n {/* inline styles to override MapLibre’s Pop up CSS */}\r\n <style>\r\n {`\r\n .noBorderPopup .maplibregl-popup-content {\r\n border: none !important;\r\n box-shadow: none !important;\r\n background: transparent !important;\r\n }\r\n .noBorderPopup .maplibregl-popup-tip {\r\n display: none !important;\r\n }\r\n `}\r\n </style>\r\n </Popup>\r\n )\r\n }\r\n\r\n // Track unclustered comments to display avatars\r\n const [unclusteredFeatures, setUnclusteredFeatures] = React.useState<MapGeoJSONFeature[]>([])\r\n\r\n React.useEffect(() => {\r\n if (!map) return\r\n\r\n const updateUnclusteredFeatures = () => {\r\n const allFeatures = map.querySourceFeatures('comments')\r\n const unclusteredOnly = allFeatures.filter(\r\n (f) => !f.properties?.cluster && !f.properties?.point_count\r\n )\r\n\r\n // Deduplicate by id\r\n const uniqueFeatures = new Map()\r\n unclusteredOnly.forEach((f) => uniqueFeatures.set(f.properties.id, f))\r\n setUnclusteredFeatures(Array.from(uniqueFeatures.values()))\r\n }\r\n\r\n const onSourceData = (e: any) => {\r\n if (e?.sourceId === 'comments') updateUnclusteredFeatures()\r\n }\r\n\r\n map.on('sourcedata', onSourceData)\r\n map.on('moveend', updateUnclusteredFeatures)\r\n map.on('zoomend', updateUnclusteredFeatures)\r\n\r\n updateUnclusteredFeatures()\r\n\r\n return () => {\r\n map.off('sourcedata', onSourceData)\r\n map.off('moveend', updateUnclusteredFeatures)\r\n map.off('zoomend', updateUnclusteredFeatures)\r\n }\r\n }, [map])\r\n\r\n return (\r\n <>\r\n {commentsVisibleInViewer.includes(ViewerNames.map) &&\r\n <Source\r\n id=\"comments\"\r\n type=\"geojson\"\r\n data={geojsonCommentData}\r\n cluster={true}\r\n clusterMaxZoom={14}\r\n clusterRadius={40}\r\n >\r\n <Layer {...clusterLayer} />\r\n <Layer {...clusterCountLayer} />\r\n <Layer {...unclusteredPointLayer} />\r\n\r\n {renderPopup()}\r\n {unclusteredFeatures\r\n .filter((feature) => feature.properties?.id !== popupInfo?.id)\r\n .map((feature) => (\r\n <CommentAvatarMarker\r\n key={String(feature.properties?.id)}\r\n feature={feature}\r\n isHighlighted={currentCommentId === feature.properties?.id || hoveredCommentId === feature.properties?.id}\r\n onMouseEnter={() => setHoveredCommentId(feature.properties?.id)}\r\n onMouseLeave={() => setHoveredCommentId(null)}\r\n />\r\n ))}\r\n </Source>}\r\n </>\r\n )\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA0DU,SAiQN,UAjQM,KAyLJ,YAzLI;AArDV,SAAS,kBAAkB;AAC3B,SAAS,uBAAuB;AAChC,YAAY,WAAW;AACvB,SAAS,QAAQ,OAAO,OAAO,cAAc;AAC7C,SAAS,aAAa;AAEtB,SAAS,YAAY,mBAAmB;AACxC,SAAS,SAAS,gBAAgB;AAClC,SAAS,YAAY,oBAAoB;AACzC,SAAS,mBAA6C;AACtD,SAAS,cAAc;AACvB,OAAO,aAAa;AACpB,SAAS,kBAAkB;AAC3B,SAAS,qCAAqC;AAC9C,SAAS,6BAA6B;AACtC,SAAS,oBAAoB,yBAAyB,mCAAmC;AAUzF,MAAM,sBAAsB,CAAC,EAAE,SAAS,eAAe,cAAc,aAAa,MAAqH;AA9BvM;AA+BE,QAAM,YAAW,aAAQ,eAAR,mBAAoB;AACrC,QAAM,EAAE,MAAM,OAAO,IAAI,QAAQ,YAAY,OAAO,OAAO,QAAQ,IAAI,EAAE;AACzE,QAAM,YAAW,aAAQ,eAAR,mBAAoB;AACrC,QAAM,mBAAkB,aAAQ,eAAR,mBAAoB;AAC5C,QAAM,uBAAsB,4CAAQ,gBAAR,YAAuB,oBAAvB,YAA0C;AAEtE,QAAM,SAAS,8BAA8B,OAAO;AACpD,MAAI,CAAC,OAAQ,QAAO;AAEpB,SACE,oBAAC,UAA6E,WAAW,OAAO,KAAK,UAAU,OAAO,KAAK,QAAO,UAChI;AAAA,IAAC;AAAA;AAAA,MACC,OAAO;AAAA,QACL,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,cAAc;AAAA,QACd,WAAW,gBACP,wDACA;AAAA,QACJ,YAAY;AAAA,QACZ,WAAW,gBAAgB,eAAe;AAAA,QAC1C,UAAU;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA,MAEA,8BAAC,UAAO,WAAU,iBAChB;AAAA,QAAC;AAAA;AAAA,UACC,aAAa;AAAA,UACb,MAAM;AAAA,UACN,WAAU;AAAA;AAAA,MACZ,GACF;AAAA;AAAA,EACF,KAvBW,QAAO,mBAAQ,eAAR,mBAAoB,OAApB,YAA0B,GAAG,OAAO,GAAG,IAAI,OAAO,GAAG,EAAE,CAwB3E;AAEJ;AAEO,MAAM,eAAe,MAAM;AArElC;AAsEI,QAAM,CAAC,kBAAkB,mBAAmB,IAAI,MAAM,SAAwB,IAAI;AACpF,QAAM,eAAe,mBAAmB,UAAU;AAClD,QAAM,oBAAoB,wBAAwB,UAAU;AAC5D,QAAM,wBAAwB,4BAA4B,UAAU;AAIpE,QAAM,IAAI,gBAAgB,eAAe;AAEzC,QAAM,EAAE,OAAO,SAAS,IAAI,MAAM,WAAW,UAAU;AACvD,QAAM,EAAE,KAAK,gBAAgB,IAAI,SAAS;AAC1C,QAAM,EAAE,SAAS,IAAI,YAAY;AACjC,QAAM,EAAE,MAAM,IAAI,SAAS;AAC3B,QAAM,QAAO,gBAAW,EAAE,SAAb,mBAAmB;AAEhC,QAAM,CAAC,WAAW,YAAY,IAAI,MAAM,SAA4F,IAAI;AACxI,QAAM,EAAE,MAAM,YAAY,IAAI,SAAQ,uCAAW,aAAY,OAAO,OAAO,UAAU,QAAQ,IAAI,EAAE;AACnG,QAAM,EAAE,cAAc,IAAI,YAAW,4CAAW,OAAX,YAAiB,IAAI;AAC1D,QAAM,EAAE,OAAO,WAAW,IAAI,MAAM,WAAW,YAAY;AAC3D,QAAM,EAAE,yBAAyB,iBAAiB,IAAI,WAAW;AAEjE,QAAM,mBAAmB,SACtB,OAAO,CAAC,YAAY,QAAQ,WAAW,YAAY,GAAG,EACtD,IAAI,CAAC,YAAY;AA7FtB,QAAAA,KAAAC;AA8FM,UAAMC,QAAO,MAAM,KAAK,OAAK,EAAE,OAAO,QAAQ,QAAQ;AACtD,WAAO,iCACF,UADE;AAAA,MAEL,aAAYF,MAAAE,SAAA,gBAAAA,MAAM,SAAN,OAAAF,MAAc;AAAA,MAC1B,cAAaC,MAAAC,SAAA,gBAAAA,MAAM,gBAAN,OAAAD,MAAqB;AAAA,IACpC;AAAA,EACF,CAAC;AAGH,QAAM,UAAU,MAAM;AACpB,QAAI,aAAa,CAAC,SAAS,KAAK,CAAC,MAAM,EAAE,OAAO,UAAU,EAAE,GAAG;AAC7D,mBAAa,IAAI;AAAA,IACnB;AAAA,EACF,GAAG,CAAC,UAAU,SAAS,CAAC;AAExB,QAAM,qBAAqB,MAAM,QAAQ,MAAM;AAC7C,UAAM,uBAAuB,CAAC,gBAAgG;AAC5H,YAAM,kBAA4E,YAC/E,IAAI,CAAC,YAAwB;AAC5B,cAAM,EAAE,WAAW,UAAU,IAAI,MAAM,WAAW,UAAU,gBAAgB,SAAS,YAAY,YAAY,IAAI;AAEjH,eAAO;AAAA,UACL,MAAM;AAAA,UACN,UAAU;AAAA,YACR,MAAM;AAAA,YACN,aAAa,CAAC,WAAW,QAAQ;AAAA,UACnC;AAAA,UACA,YAAY;AAAA,YACV,IAAI,OAAO,EAAE;AAAA,YACb;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA,UAAU,OAAO,QAAQ;AAAA,YACzB;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA,QAAQ,YAAY;AAAA,UACtB;AAAA,QACF;AAAA,MACF,CAAC;AAEH,YAAM,YAA8E;AAAA,QAClF,MAAM;AAAA,QACN,UAAU;AAAA,MACZ;AACA,aAAO;AAAA,IACT;AACA,WAAO,qBAAqB,gBAAgB;AAAA,EAC9C,GAAG,CAAC,gBAAgB,CAAC;AAGrB,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,IAAK;AACV,UAAM,mBAAkC,CAAC,GAAuB,aAAkC;AArJtG,UAAAD,KAAAC;AAsJM,YAAM,UAAU,qCAAW;AAE3B,UAAI,CAAC,WAAW,QAAQ,WAAW,YAAa;AAEhD,UAAI,QAAQ,SAAS,QAAQ,QAAS;AACtC,YAAM,CAAC,WAAW,QAAQ,IAAI,QAAQ,SAAS;AAC/C,YAAM,oBAAoB,QAAQ;AAClC,YAAM,EAAE,IAAI,UAAU,MAAM,WAAW,YAAY,YAAY,IAAI;AACnE,mBAAa;AAAA,QACX,IAAI,OAAO,EAAE;AAAA,QACb,UAAU,OAAO,QAAQ;AAAA,QACzB,iBAAgBD,MAAA,QAAQ,eAAR,gBAAAA,IAAoB;AAAA,QACpC,UAASC,MAAA,QAAQ,eAAR,gBAAAA,IAAoB;AAAA,QAC7B;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,QAAQ,YAAY;AAAA,MACtB,CAAC;AAAA,IACH;AAEA,UAAM,yBAAyB,MAAM;AACnC,UAAI,UAAU,EAAE,MAAM,SAAS;AAAA,IACjC;AACA,UAAM,yBAAyB,MAAM;AACnC,UAAI,UAAU,EAAE,MAAM,SAAS;AAAA,IACjC;AAGA,oBAAgB,SAAS,+BAA+B,sBAAsB,4BAA4B,gBAAgB;AAE1H,QAAI,GAAG,cAAc,+BAA+B,sBAAsB;AAC1E,QAAI,GAAG,cAAc,+BAA+B,sBAAsB;AAC1E,WAAO,MAAM;AACX,sBAAgB,WAAW,6BAA6B;AACxD,UAAI,IAAI,cAAc,+BAA+B,sBAAsB;AAC3E,UAAI,IAAI,cAAc,+BAA+B,sBAAsB;AAAA,IAC7E;AAAA,EACF,GAAG,CAAC,KAAK,eAAe,CAAC;AAEzB,QAAM,sBAAsB,MAAM;AAChC,UAAM,QAAQ,EAAE,gBAAgB,CAAC;AACjC,kBAAc;AACd,iBAAa,IAAI;AAAA,EACnB;AAGA,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,IAAK;AAEV,UAAM,oBAAoB,CAAC,MAA0B;AA1MzD,UAAAD;AA2MM,YAAM,WAAUA,MAAA,EAAE,aAAF,gBAAAA,IAAa;AAC7B,UAAI,CAAC,WAAW,CAAC,QAAQ,WAAW,WAAY;AAEhD,YAAM,YAAY,QAAQ,WAAW;AAGrC,UAAI,EAAE,iBAAiB,QAAQ,UAAW;AAC1C,YAAM,CAAC,KAAK,GAAG,IAAI,QAAQ,SAAS;AAEpC,YAAM,SAAS,IAAI,UAAU,UAAU;AACvC,cAAQ,IAAI,MAAM;AAClB,cAAQ,IAAI,SAAS;AACrB,aAAO,wBAAwB,SAAS,EAAE;AAAA,QACxC,CAAC,SAAiB;AAChB,kBAAQ,IAAI,WAAW;AACvB,cAAI,OAAO,EAAE,QAAQ,CAAC,KAAK,GAAG,GAAG,KAAK,CAAC;AAAA,QACzC;AAAA,MACF,EACG,MAAM,CAAC,UAAU;AAChB,gBAAQ,MAAM,KAAK;AAAA,MACrB,CAAC;AAAA,IACL;AAEA,UAAM,yBAAyB,MAAM;AAAE,UAAI,UAAU,EAAE,MAAM,SAAS;AAAA,IAAU;AAChF,UAAM,yBAAyB,MAAM;AAAE,UAAI,UAAU,EAAE,MAAM,SAAS;AAAA,IAAG;AAGzE,QAAI,GAAG,SAAS,qBAAqB,iBAAiB;AACtD,QAAI,GAAG,cAAc,qBAAqB,sBAAsB;AAChE,QAAI,GAAG,cAAc,qBAAqB,sBAAsB;AAChE,WAAO,MAAM;AACX,UAAI,IAAI,SAAS,qBAAqB,iBAAiB;AACvD,UAAI,IAAI,cAAc,qBAAqB,sBAAsB;AACjE,UAAI,IAAI,cAAc,qBAAqB,sBAAsB;AAAA,IACnE;AAAA,EACF,GAAG,CAAC,GAAG,CAAC;AAER,QAAM,cAAc,MAAM;AAhP5B,QAAAA,KAAAC,KAAA;AAiPI,QAAI,CAAC,UAAW,QAAO;AACvB,WACE;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,WAAW,UAAU;AAAA,QACrB,UAAU,UAAU;AAAA,QACpB,cAAc;AAAA,QACd,SAAS,MAAM,aAAa,IAAI;AAAA,QAChC,QAAO;AAAA,QACP,OAAO,EAAE,QAAQ,QAAQ,QAAQ,QAAQ,WAAW,OAAO;AAAA,QAC3D,QAAQ,CAAC,GAAG,EAAE;AAAA,QAEd;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,UAAU,UAAU,cAAc;AAAA,cAClC,YAAWA,OAAAD,MAAA,2CAAa,gBAAb,OAAAA,MAA4B,UAAU,gBAAtC,OAAAC,MAAqD;AAAA,cAChE,kBAAiB,sDAAa,gBAAb,YAA4B,UAAU,gBAAtC,YAAqD;AAAA,cACtE,MAAM,UAAU;AAAA,cAChB,WAAW,UAAU;AAAA,cACrB,UAAU,KAAK,OAAO,OAAO,UAAU,QAAQ,IAAI,sBAAsB;AAAA,cACzE,SAAS,MAAM,aAAa,IAAI;AAAA;AAAA,UAClC;AAAA,UAEA,oBAAC,WACE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,eAUH;AAAA;AAAA;AAAA,IACF;AAAA,EAEJ;AAGA,QAAM,CAAC,qBAAqB,sBAAsB,IAAI,MAAM,SAA8B,CAAC,CAAC;AAE5F,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,IAAK;AAEV,UAAM,4BAA4B,MAAM;AACtC,YAAM,cAAc,IAAI,oBAAoB,UAAU;AACtD,YAAM,kBAAkB,YAAY;AAAA,QAClC,CAAC,MAAG;AAhSZ,cAAAD,KAAAC;AAgSe,oBAACD,MAAA,EAAE,eAAF,gBAAAA,IAAc,YAAW,GAACC,MAAA,EAAE,eAAF,gBAAAA,IAAc;AAAA;AAAA,MAClD;AAGA,YAAM,iBAAiB,oBAAI,IAAI;AAC/B,sBAAgB,QAAQ,CAAC,MAAM,eAAe,IAAI,EAAE,WAAW,IAAI,CAAC,CAAC;AACrE,6BAAuB,MAAM,KAAK,eAAe,OAAO,CAAC,CAAC;AAAA,IAC5D;AAEA,UAAM,eAAe,CAAC,MAAW;AAC/B,WAAI,uBAAG,cAAa,WAAY,2BAA0B;AAAA,IAC5D;AAEA,QAAI,GAAG,cAAc,YAAY;AACjC,QAAI,GAAG,WAAW,yBAAyB;AAC3C,QAAI,GAAG,WAAW,yBAAyB;AAE3C,8BAA0B;AAE1B,WAAO,MAAM;AACX,UAAI,IAAI,cAAc,YAAY;AAClC,UAAI,IAAI,WAAW,yBAAyB;AAC5C,UAAI,IAAI,WAAW,yBAAyB;AAAA,IAC9C;AAAA,EACF,GAAG,CAAC,GAAG,CAAC;AAER,SACE,gCACD,kCAAwB,SAAS,YAAY,GAAG,KAC/C;AAAA,IAAC;AAAA;AAAA,MACC,IAAG;AAAA,MACH,MAAK;AAAA,MACL,MAAM;AAAA,MACN,SAAS;AAAA,MACT,gBAAgB;AAAA,MAChB,eAAe;AAAA,MAEf;AAAA,4BAAC,0BAAU,aAAc;AAAA,QACzB,oBAAC,0BAAU,kBAAmB;AAAA,QAC9B,oBAAC,0BAAU,sBAAuB;AAAA,QAEjC,YAAY;AAAA,QACZ,oBACE,OAAO,CAAC,YAAS;AA3U1B,cAAAD;AA2U6B,mBAAAA,MAAA,QAAQ,eAAR,gBAAAA,IAAoB,SAAO,uCAAW;AAAA,SAAE,EAC5D,IAAI,CAAC,YAAS;AA5UvB,cAAAA,KAAAC,KAAA;AA6UU;AAAA,YAAC;AAAA;AAAA,cAEC;AAAA,cACA,eAAe,uBAAqBA,MAAA,QAAQ,eAAR,gBAAAA,IAAoB,OAAM,uBAAqB,aAAQ,eAAR,mBAAoB;AAAA,cACvG,cAAc,MAAG;AAjV7B,oBAAAD;AAiVgC,4CAAoBA,MAAA,QAAQ,eAAR,gBAAAA,IAAoB,EAAE;AAAA;AAAA,cAC9D,cAAc,MAAM,oBAAoB,IAAI;AAAA;AAAA,YAJvC,QAAOA,MAAA,QAAQ,eAAR,gBAAAA,IAAoB,EAAE;AAAA,UAKpC;AAAA,SACD;AAAA;AAAA;AAAA,EACL,GACA;AAEJ;","names":["_a","_b","user"]}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/CommentLayer/index.tsx"],"sourcesContent":["'use client'\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport { useSession } from 'next-auth/react'\r\nimport { useTranslations } from 'next-intl'\r\nimport * as React from 'react'\r\nimport { Source, Layer, Popup, Marker } from 'react-map-gl/maplibre'\r\nimport { toast } from 'sonner'\r\n\r\nimport { useComments, useDeleteComments } from '../../../../../../../hooks/comments/comments'\r\nimport { useUser, useUsers } from '../../../../../../../hooks/users/users'\r\nimport { MapContext, MenusContext } from '../../../../../../../store'\r\nimport { ViewerNames, type Comment as IComment } from '../../../../../../../types/dbTypes'\r\nimport { commentRingShadow } from '../../../../../../../utils/markerUtils'\r\nimport { Avatar } from '../../../../../../ui/Avatar'\r\nimport Comment from '../../../../../../ui/Comments/Comment'\r\nimport { UserAvatar } from '../../../../../../ui/UserAvatar'\r\nimport { extractCoordinatesFromFeature } from '../../../../utils/extractCoordinates'\r\nimport { MapLayerClickPriority } from '../../../../utils/MapEventManager/MapClickManager'\r\nimport { createClusterLayer, createClusterCountLayer, createUnclusteredPointLayer } from '../mapLayersUtils'\r\n\r\nimport type { ClickCallback } from '../../../../utils/MapEventManager/MapClickManager';\r\nimport type { MapGeoJSONFeature, MapLayerMouseEvent } from 'maplibre-gl'\r\n\r\ntype MapComment = IComment & {\r\n authorName?: string\r\n imageFileId?: number | null\r\n}\r\n\r\nconst CommentAvatarMarker = ({ feature, isHighlighted, isFocused, onMouseEnter, onMouseLeave, onClick, onDoubleClick, center, offset }: { feature: MapGeoJSONFeature; isHighlighted?: boolean; isFocused?: boolean; onMouseEnter?: () => void; onMouseLeave?: () => void; onClick?: () => void; onDoubleClick?: () => void; center?: [number, number]; offset?: [number, number] }) => {\r\n const authorId = feature.properties?.authorId\r\n const { user: author } = useUser(authorId != null ? String(authorId) : '')\r\n const userName = feature.properties?.authorName\r\n const userImageFileId = feature.properties?.imageFileId\r\n const resolvedImageFileId = author?.imageFileId ?? userImageFileId ?? null\r\n\r\n // When part of an expanded (spiderfied) cluster, position at the cluster\r\n // center and fan out with a pixel offset instead of the feature's own coords.\r\n const coords = center ? { lng: center[0], lat: center[1] } : extractCoordinatesFromFeature(feature)\r\n if (!coords) return null\r\n\r\n const scale = isFocused ? 1.25 : isHighlighted ? 1.2 : 1\r\n\r\n return (\r\n <Marker key={String(feature.properties?.id ?? `${coords.lng},${coords.lat}`)} longitude={coords.lng} latitude={coords.lat} anchor=\"center\" offset={offset}>\r\n <div\r\n title={onDoubleClick ? 'Double click to zoom' : undefined}\r\n style={{\r\n width: '36px',\r\n height: '36px',\r\n borderRadius: '50%',\r\n boxShadow: commentRingShadow({ highlight: isHighlighted, focused: isFocused }),\r\n transition: 'all 0.2s ease-in-out',\r\n transform: `scale(${scale})`,\r\n overflow: 'hidden',\r\n cursor: onClick || onDoubleClick ? 'pointer' : undefined,\r\n }}\r\n onMouseEnter={onMouseEnter}\r\n onMouseLeave={onMouseLeave}\r\n onClick={onClick}\r\n onDoubleClick={onDoubleClick}\r\n >\r\n <Avatar className=\"h-full w-full\">\r\n <UserAvatar\r\n imageFileId={resolvedImageFileId}\r\n name={userName}\r\n className=\"h-full w-full rounded-full object-cover\"\r\n />\r\n </Avatar>\r\n </div>\r\n </Marker>\r\n )\r\n}\r\n\r\nexport const CommentLayer = () => {\r\n const [hoveredCommentId, setHoveredCommentId] = React.useState<number | null>(null)\r\n const clusterLayer = createClusterLayer('comments')\r\n const clusterCountLayer = createClusterCountLayer('comments')\r\n const unclusteredPointLayer = createUnclusteredPointLayer('comments')\r\n\r\n // global map state\r\n\r\n const t = useTranslations('CommentLayers')\r\n\r\n const { state: mapState } = React.useContext(MapContext)\r\n const { map, mapClickManager } = mapState.map\r\n const { comments } = useComments()\r\n const { users } = useUsers()\r\n const user = useSession().data?.user\r\n\r\n const [popupInfo, setPopUpInfo] = React.useState<(Partial<IComment> & { authorName?: string; imageFileId?: number | null }) | null>(null)\r\n const { user: popupAuthor } = useUser(popupInfo?.authorId != null ? String(popupInfo.authorId) : '')\r\n const { deleteComments } = useDeleteComments()\r\n const { state: menusState, dispatch: menusDispatch, setIsSidebarOpen } = React.useContext(MenusContext)\r\n const { commentsVisibleInViewer, currentCommentId, focusedCommentId, focusRequestId } = menusState.menus\r\n\r\n const focusComment = React.useCallback((commentId?: number) => {\r\n if (commentId == null) return\r\n menusDispatch({ type: 'SET_CURRENT_COMMENT_ID', payload: { commentId } })\r\n menusDispatch({ type: 'SET_FOCUSED_COMMENT_ID', payload: { commentId } })\r\n }, [menusDispatch])\r\n\r\n // Edit/reply from the popup card open the comment's editor/reply box in the sidebar,\r\n // matching the BIM comment card behaviour.\r\n const requestSidebarAction = React.useCallback((commentId: number | undefined, action: 'edit' | 'reply') => {\r\n if (commentId == null) return\r\n setIsSidebarOpen(true)\r\n menusDispatch({ type: 'SET_SIDEBAR_SELECTED_TAB', payload: { selectedTab: 'communication' } })\r\n menusDispatch({ type: 'SET_CURRENT_COMMENT_ID', payload: { commentId } })\r\n menusDispatch({ type: 'REQUEST_COMMENT_ACTION', payload: { commentId, action } })\r\n }, [menusDispatch, setIsSidebarOpen])\r\n\r\n // Hover-to-expand (spiderfy) state for clusters\r\n const [spider, setSpider] = React.useState<{ center: [number, number]; features: MapGeoJSONFeature[] } | null>(null)\r\n const spiderCloseTimer = React.useRef<ReturnType<typeof setTimeout> | null>(null)\r\n\r\n const cancelSpiderClose = React.useCallback(() => {\r\n if (spiderCloseTimer.current) {\r\n clearTimeout(spiderCloseTimer.current)\r\n spiderCloseTimer.current = null\r\n }\r\n }, [])\r\n\r\n const scheduleSpiderClose = React.useCallback(() => {\r\n cancelSpiderClose()\r\n spiderCloseTimer.current = setTimeout(() => setSpider(null), 150)\r\n }, [cancelSpiderClose])\r\n\r\n const openPopupFromFeature = React.useCallback((feature?: MapGeoJSONFeature) => {\r\n if (!feature || feature.properties.point_count) return\r\n if (feature.geometry.type !== 'Point') return\r\n const [longitude, latitude] = feature.geometry.coordinates\r\n const p = feature.properties as MapComment\r\n setPopUpInfo({\r\n id: Number(p.id),\r\n authorId: Number(p.authorId),\r\n organizationId: feature.properties?.organizationId,\r\n visible: feature.properties?.visible,\r\n longitude,\r\n latitude,\r\n text: p.text,\r\n createdAt: p.createdAt,\r\n authorName: p.authorName,\r\n imageFileId: p.imageFileId,\r\n viewer: ViewerNames.map,\r\n })\r\n }, [])\r\n\r\n const eligibleComments = comments\r\n .filter((comment) => comment.viewer === ViewerNames.map)\r\n .map((comment) => {\r\n const user = users.find(u => u.id === comment.authorId)\r\n return {\r\n ...comment,\r\n authorName: user?.name ?? 'Unknown User',\r\n imageFileId: user?.imageFileId ?? null,\r\n }\r\n })\r\n\r\n // Keep the latest comments available to the focus effect without re-running it every render.\r\n const eligibleCommentsRef = React.useRef(eligibleComments)\r\n eligibleCommentsRef.current = eligibleComments\r\n\r\n // Close popup if the comment was deleted\r\n React.useEffect(() => {\r\n if (popupInfo && !comments.find((c) => c.id === popupInfo.id)) {\r\n setPopUpInfo(null)\r\n }\r\n }, [comments, popupInfo])\r\n\r\n // Fly to a comment when it is focused (double-clicked here or in the sidebar) and open its card.\r\n React.useEffect(() => {\r\n if (focusedCommentId == null || !map) return\r\n const target = eligibleCommentsRef.current.find((c) => c.id === focusedCommentId)\r\n if (!target || target.longitude == null || target.latitude == null) return\r\n map.flyTo({ center: [target.longitude, target.latitude], zoom: Math.max(map.getZoom(), 17), duration: 800 })\r\n setPopUpInfo({\r\n id: target.id,\r\n authorId: target.authorId,\r\n organizationId: target.organizationId,\r\n visible: target.visible,\r\n longitude: target.longitude,\r\n latitude: target.latitude,\r\n text: target.text,\r\n createdAt: target.createdAt,\r\n authorName: target.authorName,\r\n imageFileId: target.imageFileId,\r\n viewer: ViewerNames.map,\r\n })\r\n }, [focusRequestId, map])\r\n\r\n const geojsonCommentData = React.useMemo(() => {\r\n const convertDataToGeojson = (commentData: MapComment[]): GeoJSON.FeatureCollection<GeoJSON.Point, { [key: string]: any }> => {\r\n const commentFeatures: GeoJSON.Feature<GeoJSON.Point, { [key: string]: any }>[] = commentData\r\n .map((comment: MapComment) => {\r\n const { longitude, latitude, id, text, createdAt, authorId, organizationId, visible, authorName, imageFileId } = comment\r\n\r\n return {\r\n type: 'Feature',\r\n geometry: {\r\n type: 'Point',\r\n coordinates: [longitude, latitude],\r\n },\r\n properties: {\r\n id: Number(id),\r\n organizationId,\r\n visible,\r\n longitude,\r\n latitude,\r\n authorId: Number(authorId),\r\n authorName,\r\n imageFileId,\r\n text,\r\n createdAt,\r\n viewer: ViewerNames.map,\r\n },\r\n }\r\n })\r\n\r\n const commentFC: GeoJSON.FeatureCollection<GeoJSON.Point, { [key: string]: any }> = {\r\n type: 'FeatureCollection',\r\n features: commentFeatures,\r\n }\r\n return commentFC\r\n }\r\n return convertDataToGeojson(eligibleComments)\r\n }, [eligibleComments])\r\n\r\n // event listeners for comment unclustered points\r\n React.useEffect(() => {\r\n if (!map) return\r\n const showCommentPopUp: ClickCallback = (e: MapLayerMouseEvent, features: MapGeoJSONFeature[]) => {\r\n openPopupFromFeature(features?.[0])\r\n }\r\n\r\n const mouseEnterChangeCursor = () => {\r\n map.getCanvas().style.cursor = 'pointer'\r\n }\r\n const mouseLeaveChangeCursor = () => {\r\n map.getCanvas().style.cursor = ''\r\n }\r\n\r\n // event listener for clicking on single point to show comment, hover to change cursor\r\n mapClickManager.register('comments-unclustered-points', MapLayerClickPriority.CommentLayersClickPriority, showCommentPopUp)\r\n\r\n map.on('mouseenter', 'comments-unclustered-points', mouseEnterChangeCursor)\r\n map.on('mouseleave', 'comments-unclustered-points', mouseLeaveChangeCursor)\r\n return () => {\r\n mapClickManager.unregister('comments-unclustered-points')\r\n map.off('mouseenter', 'comments-unclustered-points', mouseEnterChangeCursor)\r\n map.off('mouseleave', 'comments-unclustered-points', mouseLeaveChangeCursor)\r\n }\r\n }, [map, mapClickManager, openPopupFromFeature])\r\n\r\n const handleRemoveComment = () => {\r\n if (popupInfo?.id == null) return\r\n toast.success(t('commentDeleted'))\r\n // Cascade: delete the comment together with its replies (the DB does not cascade).\r\n const id = popupInfo.id\r\n const replyIds = comments.filter((c) => c.replyToId === id).map((c) => c.id)\r\n void deleteComments({ ids: [id, ...replyIds] })\r\n setPopUpInfo(null)\r\n }\r\n\r\n // Close the spiderfied cluster when the map moves (positions would be stale)\r\n React.useEffect(() => {\r\n if (!map) return\r\n const close = () => setSpider(null)\r\n map.on('movestart', close)\r\n map.on('zoomstart', close)\r\n return () => {\r\n map.off('movestart', close)\r\n map.off('zoomstart', close)\r\n }\r\n }, [map])\r\n\r\n // event listeners for clustered points\r\n React.useEffect(() => {\r\n if (!map) return\r\n\r\n const zoomInToDecluster = (e: MapLayerMouseEvent) => {\r\n const feature = e.features?.[0] // only proceed if this is actually a cluster\r\n if (!feature || !feature.properties.cluster_id) return\r\n\r\n const clusterId = feature.properties.cluster_id as number\r\n\r\n // Check if geometry has coordinates (not a GeometryCollection)\r\n if (!('coordinates' in feature.geometry)) return\r\n const [lng, lat] = feature.geometry.coordinates as [number, number]\r\n\r\n const source = map.getSource('comments') as any\r\n source.getClusterExpansionZoom(clusterId).then(\r\n (zoom: number) => {\r\n map.easeTo({ center: [lng, lat], zoom })\r\n },\r\n )\r\n .catch((error) => {\r\n console.error(error)\r\n })\r\n }\r\n\r\n // Hover a cluster to expand (spiderfy) its members\r\n const expandClusterOnHover = (e: MapLayerMouseEvent) => {\r\n map.getCanvas().style.cursor = 'pointer'\r\n const feature = e.features?.[0]\r\n if (!feature || !feature.properties.cluster_id) return\r\n if (!('coordinates' in feature.geometry)) return\r\n const center = feature.geometry.coordinates as [number, number]\r\n const clusterId = feature.properties.cluster_id as number\r\n\r\n const source = map.getSource('comments') as any\r\n source.getClusterLeaves(clusterId, Infinity, 0)\r\n .then((leaves: MapGeoJSONFeature[]) => {\r\n cancelSpiderClose()\r\n setSpider({ center, features: leaves })\r\n })\r\n .catch((error: unknown) => {\r\n console.error(error)\r\n })\r\n }\r\n\r\n const mouseLeaveCluster = () => {\r\n map.getCanvas().style.cursor = ''\r\n scheduleSpiderClose()\r\n }\r\n\r\n // Hover expands the cluster; click still zooms in to decluster as a fallback\r\n map.on('click', 'comments-clusters', zoomInToDecluster)\r\n map.on('mouseenter', 'comments-clusters', expandClusterOnHover)\r\n map.on('mouseleave', 'comments-clusters', mouseLeaveCluster)\r\n return () => {\r\n map.off('click', 'comments-clusters', zoomInToDecluster)\r\n map.off('mouseenter', 'comments-clusters', expandClusterOnHover)\r\n map.off('mouseleave', 'comments-clusters', mouseLeaveCluster)\r\n }\r\n }, [map, cancelSpiderClose, scheduleSpiderClose])\r\n\r\n const renderPopup = () => {\r\n if (!popupInfo) return null\r\n return (\r\n <Popup\r\n className=\"noBorderPopup\"\r\n longitude={popupInfo.longitude}\r\n latitude={popupInfo.latitude}\r\n closeOnClick={false}\r\n onClose={() => setPopUpInfo(null)}\r\n anchor=\"bottom\"\r\n style={{ height: '50px', border: 'none', boxShadow: 'none' }}\r\n offset={[0, 10]}\r\n >\r\n <Comment\r\n userName={popupInfo.authorName || ''}\r\n userImage={popupAuthor?.imageFileId ?? popupInfo.imageFileId ?? null}\r\n userImageFileId={popupAuthor?.imageFileId ?? popupInfo.imageFileId ?? null}\r\n text={popupInfo.text}\r\n createdAt={popupInfo.createdAt}\r\n showActions\r\n canEdit={user.id === String(popupInfo.authorId)}\r\n canDelete={user.id === String(popupInfo.authorId)}\r\n onEdit={user.id === String(popupInfo.authorId) ? () => requestSidebarAction(popupInfo.id, 'edit') : undefined}\r\n onReply={() => requestSidebarAction(popupInfo.id, 'reply')}\r\n onRemove={user.id === String(popupInfo.authorId) ? handleRemoveComment : undefined}\r\n onClose={() => setPopUpInfo(null)}\r\n />\r\n {/* inline styles to override MapLibre’s Pop up CSS */}\r\n <style>\r\n {`\r\n .noBorderPopup .maplibregl-popup-content {\r\n border: none !important;\r\n box-shadow: none !important;\r\n background: transparent !important;\r\n }\r\n .noBorderPopup .maplibregl-popup-tip {\r\n display: none !important;\r\n }\r\n `}\r\n </style>\r\n </Popup>\r\n )\r\n }\r\n\r\n // Track unclustered comments to display avatars\r\n const [unclusteredFeatures, setUnclusteredFeatures] = React.useState<MapGeoJSONFeature[]>([])\r\n\r\n React.useEffect(() => {\r\n if (!map) return\r\n\r\n const updateUnclusteredFeatures = () => {\r\n const allFeatures = map.querySourceFeatures('comments')\r\n const unclusteredOnly = allFeatures.filter(\r\n (f) => !f.properties?.cluster && !f.properties?.point_count\r\n )\r\n\r\n // Deduplicate by id\r\n const uniqueFeatures = new Map()\r\n unclusteredOnly.forEach((f) => uniqueFeatures.set(f.properties.id, f))\r\n setUnclusteredFeatures(Array.from(uniqueFeatures.values()))\r\n }\r\n\r\n const onSourceData = (e: any) => {\r\n if (e?.sourceId === 'comments') updateUnclusteredFeatures()\r\n }\r\n\r\n map.on('sourcedata', onSourceData)\r\n map.on('moveend', updateUnclusteredFeatures)\r\n map.on('zoomend', updateUnclusteredFeatures)\r\n\r\n updateUnclusteredFeatures()\r\n\r\n return () => {\r\n map.off('sourcedata', onSourceData)\r\n map.off('moveend', updateUnclusteredFeatures)\r\n map.off('zoomend', updateUnclusteredFeatures)\r\n }\r\n }, [map])\r\n\r\n return (\r\n <>\r\n {commentsVisibleInViewer.includes(ViewerNames.map) &&\r\n <Source\r\n id=\"comments\"\r\n type=\"geojson\"\r\n data={geojsonCommentData}\r\n cluster={true}\r\n clusterMaxZoom={14}\r\n clusterRadius={40}\r\n >\r\n <Layer {...clusterLayer} />\r\n <Layer {...clusterCountLayer} />\r\n <Layer {...unclusteredPointLayer} />\r\n\r\n {renderPopup()}\r\n {unclusteredFeatures\r\n .filter((feature) => feature.properties?.id !== popupInfo?.id)\r\n .map((feature) => (\r\n <CommentAvatarMarker\r\n key={String(feature.properties?.id)}\r\n feature={feature}\r\n isHighlighted={currentCommentId === feature.properties?.id || hoveredCommentId === feature.properties?.id}\r\n isFocused={focusedCommentId === feature.properties?.id}\r\n onMouseEnter={() => setHoveredCommentId(feature.properties?.id)}\r\n onMouseLeave={() => setHoveredCommentId(null)}\r\n onDoubleClick={() => focusComment(feature.properties?.id)}\r\n />\r\n ))}\r\n\r\n {/* Spiderfied cluster members shown on hover */}\r\n {spider && spider.features.map((feature, i) => {\r\n const n = spider.features.length\r\n const angle = (2 * Math.PI * i) / n - Math.PI / 2\r\n const radius = Math.min(60, 24 + n * 4)\r\n const offset: [number, number] = [Math.cos(angle) * radius, Math.sin(angle) * radius]\r\n const id = feature.properties?.id\r\n return (\r\n <CommentAvatarMarker\r\n key={`spider-${String(id)}`}\r\n feature={feature}\r\n center={spider.center}\r\n offset={offset}\r\n isHighlighted={currentCommentId === id || hoveredCommentId === id}\r\n isFocused={focusedCommentId === id}\r\n onMouseEnter={() => { cancelSpiderClose(); setHoveredCommentId(id) }}\r\n onMouseLeave={() => { scheduleSpiderClose(); setHoveredCommentId(null) }}\r\n onClick={() => { openPopupFromFeature(feature); setSpider(null) }}\r\n onDoubleClick={() => { focusComment(id); setSpider(null) }}\r\n />\r\n )\r\n })}\r\n </Source>}\r\n </>\r\n )\r\n}\r\n"],"mappings":"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","names":["_a","user"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MapFileManager.d.ts","sourceRoot":"","sources":["../../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/FileLayer/components/MapFileManager.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AA4B9B,KAAK,eAAe,GAAG;IACrB,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAA;CAC9B,CAAA;AAED,MAAM,CAAC,OAAO,UAAU,cAAc,CAAC,EAAE,gBAAgB,EAAE,EAAE,eAAe,
|
|
1
|
+
{"version":3,"file":"MapFileManager.d.ts","sourceRoot":"","sources":["../../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/FileLayer/components/MapFileManager.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AA4B9B,KAAK,eAAe,GAAG;IACrB,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAA;CAC9B,CAAA;AAED,MAAM,CAAC,OAAO,UAAU,cAAc,CAAC,EAAE,gBAAgB,EAAE,EAAE,eAAe,qBA4F3E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/FileLayer/components/MapFileManager.tsx"],"sourcesContent":["\"use client\"\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport * as LR from 'lucide-react'\r\nimport * as React from 'react'\r\nimport { mutate } from 'swr'\r\n\r\nimport { Button } from '../../../../../../../../components/ui/Button'\r\nimport { Card, CardContent, CardHeader, CardTitle } from '../../../../../../../../components/ui/Card'\r\nimport { FilesContext } from '../../../../../../../../store'\r\n\r\n\r\nimport { FileManagerRow } from './FileManagerRow'\r\n\r\nimport type { DbFile } from '../../../../../../../../types/dbTypes'\r\n\r\nconst is3DModelFile = (extension?: string | null): boolean => {\r\n if (!extension) return false\r\n return ['glb', 'gltf', 'fbx', 'obj', 'collada'].includes(extension.toLowerCase())\r\n}\r\n\r\nconst isHiddenFileType = (extension?: string | null): boolean => {\r\n if (!extension) return false\r\n const ext = extension.toLowerCase()\r\n return ext === 'frag' || ext === 'ifc'\r\n}\r\n\r\nconst shouldExcludeByTag = (tag?: string | null): boolean => {\r\n if (!tag) return false\r\n return tag === 'user' || tag === 'bim-file' || tag === 'fragment-file' || tag === 'bimModel'\r\n}\r\n\r\ntype FileManagerProp = {\r\n closeFileManager?: () => void\r\n}\r\n\r\nexport default function MapFileManager({ closeFileManager }: FileManagerProp) {\r\n const { state: fileState, dispatch: fileDispatch } = React.useContext(FilesContext)\r\n const { files, mapFileIds } = fileState.files\r\n\r\n const mapFiles: DbFile[] = files.filter(file =>\r\n !is3DModelFile(file.extension) && !isHiddenFileType(file.extension) && !shouldExcludeByTag(file.tag)\r\n )\r\n\r\n const allOnMap = mapFiles.length > 0 && mapFiles.every(file => mapFileIds.includes(file.id))\r\n\r\n const toggleAll = () => {\r\n if (allOnMap) {\r\n mapFiles.forEach(file => fileDispatch({ type: 'REMOVE_FROM_MAP', payload: { id: file.id } }))\r\n }\r\n else {\r\n fileDispatch({ type: 'ADD_ALL_TO_MAP', payload: { ids: mapFiles.map(f => f.id) } })\r\n }\r\n }\r\n\r\n const handleDeleteFile = async (file: DbFile) => {\r\n try {\r\n const response = await fetch(`/api/files/${file.id}`, { method: 'DELETE' })\r\n if (response.ok) {\r\n fileDispatch({ type: 'REMOVE_FILE', payload: { id: file.id } })\r\n }\r\n mutate(['files'])\r\n }\r\n catch (error) {\r\n console.error('Error when deleting a file', error)\r\n }\r\n }\r\n\r\n return (\r\n <Card className=\"fixed bottom-12 right-[50%] translate-x-[50%] bg-background round-sm shadow-md z-10 pointer-events-auto max-w-[410px] max-h-[60vh]\">\r\n <CardHeader className=\"flex flex-row justify-between items-center pointer-events-auto py-0\">\r\n <CardTitle className=\"text-sm\">\r\n Added Files (\r\n {mapFiles.length}\r\n )\r\n </CardTitle>\r\n <div>\r\n <Button\r\n variant=\"ghost\"\r\n size=\"icon\"\r\n onClick={toggleAll}\r\n className=\"opacity-70 hover:opacity-100 transition-opacity duration-200 hover:bg-transparent\"\r\n >\r\n {allOnMap ? <LR.Eye size={18} /> : <LR.EyeOff size={18} />}\r\n </Button>\r\n <Button\r\n variant=\"ghost\"\r\n size=\"icon\"\r\n onClick={() => closeFileManager()}\r\n className=\"opacity-70 hover:opacity-100 transition-opacity duration-200 hover:bg-transparent\"\r\n >\r\n <span className=\"sr-only\">Clear</span>\r\n <LR.X size={18} />\r\n </Button>\r\n </div>\r\n </CardHeader>\r\n <CardContent>\r\n {mapFiles.length === 0\r\n ? (\r\n <div className=\"py-4 text-center text-sm text-muted-foreground\">\r\n No files added to map\r\n </div>\r\n )\r\n : (\r\n <div className=\"space-y-2 max-h-40 overflow-y-auto\">\r\n {mapFiles.map(file => (\r\n <FileManagerRow\r\n key={file.id}\r\n file={file}\r\n isOnMap={mapFileIds.includes(file.id)}\r\n onVisibilityToggle={() => {\r\n const isOnMap = mapFileIds.includes(file.id)\r\n fileDispatch({\r\n type: isOnMap ? 'REMOVE_FROM_MAP' : 'ADD_TO_MAP',\r\n payload: { id: file.id },\r\n })\r\n }}\r\n onRemoveFile={() => handleDeleteFile(file)}\r\n onEditFile={() =>
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/FileLayer/components/MapFileManager.tsx"],"sourcesContent":["\"use client\"\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport * as LR from 'lucide-react'\r\nimport * as React from 'react'\r\nimport { mutate } from 'swr'\r\n\r\nimport { Button } from '../../../../../../../../components/ui/Button'\r\nimport { Card, CardContent, CardHeader, CardTitle } from '../../../../../../../../components/ui/Card'\r\nimport { FilesContext } from '../../../../../../../../store'\r\n\r\n\r\nimport { FileManagerRow } from './FileManagerRow'\r\n\r\nimport type { DbFile } from '../../../../../../../../types/dbTypes'\r\n\r\nconst is3DModelFile = (extension?: string | null): boolean => {\r\n if (!extension) return false\r\n return ['glb', 'gltf', 'fbx', 'obj', 'collada'].includes(extension.toLowerCase())\r\n}\r\n\r\nconst isHiddenFileType = (extension?: string | null): boolean => {\r\n if (!extension) return false\r\n const ext = extension.toLowerCase()\r\n return ext === 'frag' || ext === 'ifc'\r\n}\r\n\r\nconst shouldExcludeByTag = (tag?: string | null): boolean => {\r\n if (!tag) return false\r\n return tag === 'user' || tag === 'bim-file' || tag === 'fragment-file' || tag === 'bimModel'\r\n}\r\n\r\ntype FileManagerProp = {\r\n closeFileManager?: () => void\r\n}\r\n\r\nexport default function MapFileManager({ closeFileManager }: FileManagerProp) {\r\n const { state: fileState, dispatch: fileDispatch } = React.useContext(FilesContext)\r\n const { files, mapFileIds } = fileState.files\r\n\r\n const mapFiles: DbFile[] = files.filter(file =>\r\n !is3DModelFile(file.extension) && !isHiddenFileType(file.extension) && !shouldExcludeByTag(file.tag)\r\n )\r\n\r\n const allOnMap = mapFiles.length > 0 && mapFiles.every(file => mapFileIds.includes(file.id))\r\n\r\n const toggleAll = () => {\r\n if (allOnMap) {\r\n mapFiles.forEach(file => fileDispatch({ type: 'REMOVE_FROM_MAP', payload: { id: file.id } }))\r\n }\r\n else {\r\n fileDispatch({ type: 'ADD_ALL_TO_MAP', payload: { ids: mapFiles.map(f => f.id) } })\r\n }\r\n }\r\n\r\n const handleDeleteFile = async (file: DbFile) => {\r\n try {\r\n const response = await fetch(`/api/files/${file.id}`, { method: 'DELETE' })\r\n if (response.ok) {\r\n fileDispatch({ type: 'REMOVE_FILE', payload: { id: file.id } })\r\n }\r\n mutate(['files'])\r\n }\r\n catch (error) {\r\n console.error('Error when deleting a file', error)\r\n }\r\n }\r\n\r\n return (\r\n <Card className=\"fixed bottom-12 right-[50%] translate-x-[50%] bg-background round-sm shadow-md z-10 pointer-events-auto max-w-[410px] max-h-[60vh]\">\r\n <CardHeader className=\"flex flex-row justify-between items-center pointer-events-auto py-0\">\r\n <CardTitle className=\"text-sm\">\r\n Added Files (\r\n {mapFiles.length}\r\n )\r\n </CardTitle>\r\n <div>\r\n <Button\r\n variant=\"ghost\"\r\n size=\"icon\"\r\n onClick={toggleAll}\r\n className=\"opacity-70 hover:opacity-100 transition-opacity duration-200 hover:bg-transparent\"\r\n >\r\n {allOnMap ? <LR.Eye size={18} /> : <LR.EyeOff size={18} />}\r\n </Button>\r\n <Button\r\n variant=\"ghost\"\r\n size=\"icon\"\r\n onClick={() => closeFileManager()}\r\n className=\"opacity-70 hover:opacity-100 transition-opacity duration-200 hover:bg-transparent\"\r\n >\r\n <span className=\"sr-only\">Clear</span>\r\n <LR.X size={18} />\r\n </Button>\r\n </div>\r\n </CardHeader>\r\n <CardContent>\r\n {mapFiles.length === 0\r\n ? (\r\n <div className=\"py-4 text-center text-sm text-muted-foreground\">\r\n No files added to map\r\n </div>\r\n )\r\n : (\r\n <div className=\"space-y-2 max-h-40 overflow-y-auto\">\r\n {mapFiles.map(file => (\r\n <FileManagerRow\r\n key={file.id}\r\n file={file}\r\n isOnMap={mapFileIds.includes(file.id)}\r\n onVisibilityToggle={() => {\r\n const isOnMap = mapFileIds.includes(file.id)\r\n fileDispatch({\r\n type: isOnMap ? 'REMOVE_FROM_MAP' : 'ADD_TO_MAP',\r\n payload: { id: file.id },\r\n })\r\n }}\r\n onRemoveFile={() => handleDeleteFile(file)}\r\n onEditFile={() => {\r\n // ⚠️ Edit-file action not implemented\r\n }}\r\n />\r\n ))}\r\n </div>\r\n )}\r\n </CardContent>\r\n </Card>\r\n )\r\n}\r\n"],"mappings":"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","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAuD9B,eAAO,MAAM,YAAY,yBAmVxB,CAAA"}
|
|
@@ -28,9 +28,11 @@ import { toast } from "sonner";
|
|
|
28
28
|
import { useSensor, useSensors } from "../../../../../../../hooks/sensors/sensors";
|
|
29
29
|
import { useSensorTypes } from "../../../../../../../hooks/sensorTypes/sensorTypes";
|
|
30
30
|
import { useUsers } from "../../../../../../../hooks/users/users";
|
|
31
|
-
import { MapContext, MenusContext } from "../../../../../../../store";
|
|
31
|
+
import { AppConfigContext, MapContext, MenusContext } from "../../../../../../../store";
|
|
32
32
|
import { ViewerNames } from "../../../../../../../types/dbTypes";
|
|
33
33
|
import Sensor from "../../../../../../ui/Sensors/Sensor";
|
|
34
|
+
import { SensorDetailDialog } from "../../../../../../ui/Sensors/SensorDetailDialog";
|
|
35
|
+
import { SensorInput } from "../../../../../../ui/Sensors/SensorInput";
|
|
34
36
|
import { UNTAGGED_TAG } from "../../../../../../ui/Sensors/SensorsSection";
|
|
35
37
|
import { extractCoordinatesFromFeature } from "../../../../utils/extractCoordinates";
|
|
36
38
|
import { MapLayerClickPriority } from "../../../../utils/MapEventManager/MapClickManager";
|
|
@@ -67,7 +69,7 @@ const SensorIconMarker = ({ feature, isHighlighted, onMouseEnter, onMouseLeave,
|
|
|
67
69
|
}
|
|
68
70
|
) }, String((_b = (_a = feature.properties) == null ? void 0 : _a.id) != null ? _b : `${coords.lng},${coords.lat}`));
|
|
69
71
|
};
|
|
70
|
-
const SensorLayers = (
|
|
72
|
+
const SensorLayers = () => {
|
|
71
73
|
var _a, _b;
|
|
72
74
|
const [hoveredSensorId, setHoveredSensorId] = React.useState(null);
|
|
73
75
|
const clusterLayer = createClusterLayer("sensors");
|
|
@@ -87,6 +89,10 @@ const SensorLayers = ({ minioBaseUrl }) => {
|
|
|
87
89
|
const typesVisible = (visibleSensorTypes == null ? void 0 : visibleSensorTypes[ViewerNames.map]) || [];
|
|
88
90
|
const tagsVisible = (visibleSensorTags == null ? void 0 : visibleSensorTags[ViewerNames.map]) || [];
|
|
89
91
|
const { sensorTypes } = useSensorTypes();
|
|
92
|
+
const { state: appConfigState } = React.useContext(AppConfigContext);
|
|
93
|
+
const timeZone = appConfigState.appConfig.displayTimeZone;
|
|
94
|
+
const [detailSensor, setDetailSensor] = React.useState(null);
|
|
95
|
+
const [editSensor, setEditSensor] = React.useState(null);
|
|
90
96
|
const eligibleSensors = sensors.filter((sensor) => sensor.viewer === ViewerNames.map).filter((sensor) => {
|
|
91
97
|
var _a2, _b2, _c;
|
|
92
98
|
const matchesType = typesVisible.includes(sensor.typeId);
|
|
@@ -201,11 +207,8 @@ const SensorLayers = ({ minioBaseUrl }) => {
|
|
|
201
207
|
if (!("coordinates" in feature.geometry)) return;
|
|
202
208
|
const [lng, lat] = feature.geometry.coordinates;
|
|
203
209
|
const source = map.getSource("sensors");
|
|
204
|
-
console.log(source);
|
|
205
|
-
console.log(clusterId);
|
|
206
210
|
source.getClusterExpansionZoom(clusterId).then(
|
|
207
211
|
(zoom) => {
|
|
208
|
-
console.log("easing to");
|
|
209
212
|
map.easeTo({ center: [lng, lat], zoom });
|
|
210
213
|
}
|
|
211
214
|
).catch((error) => {
|
|
@@ -232,7 +235,7 @@ const SensorLayers = ({ minioBaseUrl }) => {
|
|
|
232
235
|
if (!popupInfo) return null;
|
|
233
236
|
const sensorType = sensorTypes.find((t2) => t2.id === popupInfo.typeId);
|
|
234
237
|
const liveSensor = sensors.find((s) => s.id === popupInfo.id);
|
|
235
|
-
const dataUrl = popupInfo.url
|
|
238
|
+
const dataUrl = popupInfo.url || popupInfo.data || "";
|
|
236
239
|
return /* @__PURE__ */ jsxs(
|
|
237
240
|
Popup,
|
|
238
241
|
{
|
|
@@ -240,6 +243,7 @@ const SensorLayers = ({ minioBaseUrl }) => {
|
|
|
240
243
|
longitude: popupInfo.longitude,
|
|
241
244
|
latitude: popupInfo.latitude,
|
|
242
245
|
closeOnClick: false,
|
|
246
|
+
closeButton: false,
|
|
243
247
|
onClose: () => setPopUpInfo(null),
|
|
244
248
|
anchor: "bottom",
|
|
245
249
|
style: { height: "50px", border: "none", boxShadow: "none" },
|
|
@@ -273,6 +277,12 @@ const SensorLayers = ({ minioBaseUrl }) => {
|
|
|
273
277
|
createdAt: popupInfo.createdAt,
|
|
274
278
|
onRemove: user.id === String(popupInfo.authorId) ? handleRemoveSensor : null,
|
|
275
279
|
onClose: () => setPopUpInfo(null),
|
|
280
|
+
onExpand: () => {
|
|
281
|
+
if (liveSensor) setDetailSensor(liveSensor);
|
|
282
|
+
},
|
|
283
|
+
onEdit: user.id === String(popupInfo.authorId) && liveSensor ? () => setEditSensor(liveSensor) : void 0,
|
|
284
|
+
showActions: true,
|
|
285
|
+
timeZone,
|
|
276
286
|
size: "sm"
|
|
277
287
|
}
|
|
278
288
|
),
|
|
@@ -318,43 +328,65 @@ const SensorLayers = ({ minioBaseUrl }) => {
|
|
|
318
328
|
map.off("zoomend", updateUnclusteredFeatures);
|
|
319
329
|
};
|
|
320
330
|
}, [map]);
|
|
321
|
-
return /* @__PURE__ */
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
331
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
332
|
+
detailSensor && /* @__PURE__ */ jsx(
|
|
333
|
+
SensorDetailDialog,
|
|
334
|
+
{
|
|
335
|
+
open: !!detailSensor,
|
|
336
|
+
onOpenChange: (o) => !o && setDetailSensor(null),
|
|
337
|
+
sensor: detailSensor,
|
|
338
|
+
sensorType: sensorTypes.find((t2) => t2.id === detailSensor.typeId)
|
|
339
|
+
}
|
|
340
|
+
),
|
|
341
|
+
editSensor && /* @__PURE__ */ jsx(
|
|
342
|
+
SensorInput,
|
|
343
|
+
{
|
|
344
|
+
viewer: ViewerNames.map,
|
|
345
|
+
layout: "dialog",
|
|
346
|
+
isOpen: !!editSensor,
|
|
347
|
+
editSensor: editSensor != null ? editSensor : void 0,
|
|
348
|
+
onCancel: () => setEditSensor(null),
|
|
349
|
+
onSaved: () => setEditSensor(null)
|
|
350
|
+
}
|
|
351
|
+
),
|
|
352
|
+
(typesVisible.length > 0 || tagsVisible.length > 0) && /* @__PURE__ */ jsxs(
|
|
353
|
+
Source,
|
|
354
|
+
{
|
|
355
|
+
id: "sensors",
|
|
356
|
+
type: "geojson",
|
|
357
|
+
data: geojsonSensorData,
|
|
358
|
+
cluster: true,
|
|
359
|
+
clusterMaxZoom: 14,
|
|
360
|
+
clusterRadius: 40,
|
|
361
|
+
children: [
|
|
362
|
+
/* @__PURE__ */ jsx(Layer, __spreadValues({}, clusterLayer)),
|
|
363
|
+
/* @__PURE__ */ jsx(Layer, __spreadValues({}, clusterCountLayer)),
|
|
364
|
+
/* @__PURE__ */ jsx(Layer, __spreadValues({}, unclusteredPointLayer)),
|
|
365
|
+
renderPopup(),
|
|
366
|
+
unclusteredFeatures.filter((feature) => {
|
|
367
|
+
var _a2;
|
|
368
|
+
return ((_a2 = feature.properties) == null ? void 0 : _a2.id) !== (popupInfo == null ? void 0 : popupInfo.id);
|
|
369
|
+
}).map((feature) => {
|
|
370
|
+
var _a2, _b2, _c;
|
|
371
|
+
return /* @__PURE__ */ jsx(
|
|
372
|
+
SensorIconMarker,
|
|
373
|
+
{
|
|
374
|
+
feature,
|
|
375
|
+
isHighlighted: currentSensorId === ((_b2 = feature.properties) == null ? void 0 : _b2.id) || hoveredSensorId === ((_c = feature.properties) == null ? void 0 : _c.id),
|
|
376
|
+
onMouseEnter: () => {
|
|
377
|
+
var _a3;
|
|
378
|
+
return setHoveredSensorId((_a3 = feature.properties) == null ? void 0 : _a3.id);
|
|
379
|
+
},
|
|
380
|
+
onMouseLeave: () => setHoveredSensorId(null),
|
|
381
|
+
sensorTypes
|
|
348
382
|
},
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
}
|
|
357
|
-
) });
|
|
383
|
+
String((_a2 = feature.properties) == null ? void 0 : _a2.id)
|
|
384
|
+
);
|
|
385
|
+
})
|
|
386
|
+
]
|
|
387
|
+
}
|
|
388
|
+
)
|
|
389
|
+
] });
|
|
358
390
|
};
|
|
359
391
|
export {
|
|
360
392
|
SensorLayers
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.tsx"],"sourcesContent":["'use client'\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport * as LR from 'lucide-react'\r\nimport { useSession } from 'next-auth/react'\r\nimport { useTranslations } from 'next-intl'\r\nimport * as React from 'react'\r\nimport { Source, Layer, Popup, Marker } from 'react-map-gl/maplibre'\r\nimport { toast } from 'sonner'\r\n\r\nimport { useSensor, useSensors } from '../../../../../../../hooks/sensors/sensors'\r\nimport { useSensorTypes } from '../../../../../../../hooks/sensorTypes/sensorTypes'\r\nimport { useUsers } from '../../../../../../../hooks/users/users'\r\nimport { MapContext, MenusContext } from '../../../../../../../store'\r\nimport { ViewerNames, type Sensor as ISensor} from '../../../../../../../types/dbTypes'\r\nimport Sensor from '../../../../../../ui/Sensors/Sensor'\r\nimport { UNTAGGED_TAG } from '../../../../../../ui/Sensors/SensorsSection'\r\nimport { extractCoordinatesFromFeature } from '../../../../utils/extractCoordinates'\r\nimport { MapLayerClickPriority } from '../../../../utils/MapEventManager/MapClickManager'\r\nimport { createClusterLayer, createClusterCountLayer, createUnclusteredPointLayer } from '../mapLayersUtils'\r\n\r\nimport type { SensorType} from '../../../../../../../types/dbTypes';\r\nimport type { ClickCallback } from '../../../../utils/MapEventManager/MapClickManager';\r\nimport type { MapGeoJSONFeature, MapLayerMouseEvent } from 'maplibre-gl'\r\n\r\nconst SensorIconMarker = ({ feature, isHighlighted, onMouseEnter, onMouseLeave, sensorTypes }: { feature: MapGeoJSONFeature; isHighlighted?: boolean; onMouseEnter?: () => void; onMouseLeave?: () => void; sensorTypes: SensorType[] }) => {\r\n\r\n const coords = extractCoordinatesFromFeature(feature)\r\n if (!coords) return null\r\n\r\n const sensorType = sensorTypes.find(t => t.id === feature.properties?.typeId)\r\n const icon = sensorType?.icon || 'Radio'\r\n const SensorIcon = LR[icon] || LR.Radio\r\n\r\n return (\r\n <Marker key={String(feature.properties?.id ?? `${coords.lng},${coords.lat}`)} longitude={coords.lng} latitude={coords.lat} anchor=\"center\">\r\n <div\r\n style={{\r\n width: '36px',\r\n height: '36px',\r\n borderRadius: '50%',\r\n border: isHighlighted ? '3px solid #73cee2' : '2px solid white',\r\n backgroundColor: 'black',\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n boxShadow: isHighlighted ? '0 0 12px rgba(115, 206, 226, 0.5)' : 'none',\r\n transition: 'all 0.2s ease-in-out',\r\n transform: isHighlighted ? 'scale(1.2)' : 'scale(1)',\r\n }}\r\n onMouseEnter={onMouseEnter}\r\n onMouseLeave={onMouseLeave}\r\n >\r\n <SensorIcon style={{ width: '20px', height: '20px', color: 'hsl(var(--primary-foreground))' }} />\r\n </div>\r\n </Marker>\r\n )\r\n}\r\n\r\nexport const SensorLayers = ({ minioBaseUrl }: { minioBaseUrl?: string }) => {\r\n const [hoveredSensorId, setHoveredSensorId] = React.useState<number | null>(null)\r\n const clusterLayer = createClusterLayer('sensors')\r\n const clusterCountLayer = createClusterCountLayer('sensors')\r\n const unclusteredPointLayer = createUnclusteredPointLayer('sensors')\r\n\r\n // global map state\r\n\r\n const t = useTranslations('SensorLayers')\r\n const tSensors = useTranslations('SensorsSection')\r\n\r\n const { state: mapState } = React.useContext(MapContext)\r\n const { map, mapClickManager } = mapState.map\r\n const { sensors } = useSensors()\r\n const { users } = useUsers()\r\n const user = useSession().data?.user\r\n\r\n const [popupInfo, setPopUpInfo] = React.useState<Partial<ISensor & { sensorType: SensorType }> | null>(null)\r\n const { deleteSensor, updateSensor } = useSensor(popupInfo?.id ?? null)\r\n const { state: menusState } = React.useContext(MenusContext)\r\n const { visibleSensorTypes, visibleSensorTags, currentSensorId } = menusState.menus\r\n const typesVisible = visibleSensorTypes?.[ViewerNames.map] || []\r\n const tagsVisible = visibleSensorTags?.[ViewerNames.map] || []\r\n\r\n const {sensorTypes} = useSensorTypes()\r\n\r\n const eligibleSensors: Array<ISensor & { authorName: string } & { sensorType: SensorType }> = sensors\r\n .filter((sensor) => sensor.viewer === ViewerNames.map)\r\n .filter((sensor) => {\r\n const matchesType = typesVisible.includes(sensor.typeId)\r\n const hasNoTags = !sensor.tags?.length\r\n const matchesTag = hasNoTags\r\n ? tagsVisible.includes(UNTAGGED_TAG)\r\n : sensor.tags?.some(tag => tagsVisible.includes(tag)) ?? false\r\n return matchesType || matchesTag\r\n })\r\n .map((sensor) => {\r\n const user = users.find(u => u.id === sensor.authorId)\r\n const sensorType = sensorTypes.find(t => t.id === sensor.typeId)\r\n return {\r\n ...sensor,\r\n authorName: user?.name ?? 'Unknown User',\r\n sensorType,\r\n }\r\n })\r\n\r\n // Close popup if the sensor was deleted\r\n React.useEffect(() => {\r\n if (popupInfo && !sensors.find((s) => s.id === popupInfo.id)) {\r\n setPopUpInfo(null)\r\n }\r\n }, [sensors, popupInfo])\r\n\r\n const geojsonSensorData = React.useMemo(() => {\r\n const convertDataToGeojson = (sensorData: typeof eligibleSensors): GeoJSON.FeatureCollection<GeoJSON.Point, { [key: string]: any }> => {\r\n const sensorFeatures: GeoJSON.Feature<GeoJSON.Point, { [key: string]: any }>[] = sensorData\r\n .map((sensor) => {\r\n const { longitude, latitude, id, name, typeId, sensorType, data, dataFormat, updateFrequency, createdAt, authorId, organizationId, visible, url, authorName } = sensor\r\n\r\n return {\r\n type: 'Feature',\r\n geometry: {\r\n type: 'Point',\r\n coordinates: [longitude, latitude],\r\n },\r\n properties: {\r\n id: Number(id),\r\n organizationId,\r\n visible,\r\n longitude,\r\n latitude,\r\n authorId: Number(authorId),\r\n authorName,\r\n name,\r\n typeId: Number(typeId),\r\n typeIcon: sensorType?.icon,\r\n data,\r\n dataFormat,\r\n updateFrequency,\r\n url,\r\n createdAt,\r\n viewer: ViewerNames.map,\r\n },\r\n }\r\n })\r\n\r\n\r\n\r\n const sensorFC: GeoJSON.FeatureCollection<GeoJSON.Point, { [key: string]: any }> = {\r\n type: 'FeatureCollection',\r\n features: sensorFeatures,\r\n }\r\n return sensorFC\r\n }\r\n return convertDataToGeojson(eligibleSensors)\r\n }, [eligibleSensors])\r\n\r\n // event listeners for sensor unclustered points\r\n React.useEffect(() => {\r\n if (!map) return\r\n const showSensorPopUp: ClickCallback = (e: MapLayerMouseEvent, features: MapGeoJSONFeature[]) => {\r\n const feature = features?.[0]\r\n\r\n if (!feature || feature.properties.point_count) return\r\n\r\n if (feature.geometry.type != 'Point') return\r\n const [longitude, latitude] = feature.geometry.coordinates\r\n const { id, authorId, name, typeId, data, dataFormat, updateFrequency, createdAt, url } = feature.properties\r\n setPopUpInfo({\r\n id: Number(id),\r\n authorId: Number(authorId),\r\n organizationId: feature.properties?.organizationId,\r\n visible: feature.properties?.visible,\r\n longitude,\r\n latitude,\r\n name,\r\n typeId: Number(typeId),\r\n data,\r\n dataFormat,\r\n updateFrequency,\r\n url,\r\n createdAt,\r\n viewer: ViewerNames.map,\r\n })\r\n }\r\n\r\n const mouseEnterChangeCursor = () => {\r\n map.getCanvas().style.cursor = 'pointer'\r\n }\r\n const mouseLeaveChangeCursor = () => {\r\n map.getCanvas().style.cursor = ''\r\n }\r\n\r\n // event listener for clicking on single point to show sensor, hover to change cursor\r\n mapClickManager.register('sensors-unclustered-points', MapLayerClickPriority.CommentLayersClickPriority, showSensorPopUp)\r\n\r\n map.on('mouseenter', 'sensors-unclustered-points', mouseEnterChangeCursor)\r\n map.on('mouseleave', 'sensors-unclustered-points', mouseLeaveChangeCursor)\r\n return () => {\r\n mapClickManager.unregister('sensors-unclustered-points')\r\n map.off('mouseenter', 'sensors-unclustered-points', mouseEnterChangeCursor)\r\n map.off('mouseleave', 'sensors-unclustered-points', mouseLeaveChangeCursor)\r\n }\r\n }, [map, mapClickManager])\r\n\r\n const handleRemoveSensor = () => {\r\n toast.success(t('sensorDeleted'))\r\n deleteSensor()\r\n setPopUpInfo(null)\r\n }\r\n\r\n // event listeners for clustered points\r\n React.useEffect(() => {\r\n if (!map) return\r\n\r\n const zoomInToDecluster = (e: MapLayerMouseEvent) => {\r\n const feature = e.features?.[0] // only proceed if this is actually a cluster\r\n if (!feature || !feature.properties.cluster_id) return\r\n\r\n const clusterId = feature.properties.cluster_id as number\r\n\r\n // Check if geometry has coordinates (not a GeometryCollection)\r\n if (!('coordinates' in feature.geometry)) return\r\n const [lng, lat] = feature.geometry.coordinates as [number, number]\r\n\r\n const source = map.getSource('sensors') as any\r\n console.log(source)\r\n console.log(clusterId)\r\n source.getClusterExpansionZoom(clusterId).then(\r\n (zoom: number) => {\r\n console.log('easing to')\r\n map.easeTo({ center: [lng, lat], zoom })\r\n },\r\n )\r\n .catch((error) => {\r\n console.error(error)\r\n })\r\n }\r\n\r\n const mouseEnterChangeCursor = () => { map.getCanvas().style.cursor = 'pointer' }\r\n const mouseLeaveChangeCursor = () => { map.getCanvas().style.cursor = '' }\r\n\r\n // event listener for clicking on single point to zoom in and decluster, hover to change cursor\r\n map.on('click', 'sensors-clusters', zoomInToDecluster)\r\n map.on('mouseenter', 'sensors-clusters', mouseEnterChangeCursor)\r\n map.on('mouseleave', 'sensors-clusters', mouseLeaveChangeCursor)\r\n return () => {\r\n map.off('click', 'sensors-clusters', zoomInToDecluster)\r\n map.off('mouseenter', 'sensors-clusters', mouseEnterChangeCursor)\r\n map.off('mouseleave', 'sensors-clusters', mouseLeaveChangeCursor)\r\n }\r\n }, [map])\r\n\r\n const renderPopup = () => {\r\n if (!popupInfo) return null\r\n const sensorType = sensorTypes.find(t => t.id === popupInfo.typeId)\r\n const liveSensor = sensors.find(s => s.id === popupInfo.id)\r\n const dataUrl = popupInfo.url\r\n ? `${minioBaseUrl ?? ''}/sensors/${popupInfo.url}`\r\n : popupInfo.data || ''\r\n\r\n return (\r\n <Popup\r\n className=\"noBorderPopup\"\r\n longitude={popupInfo.longitude}\r\n latitude={popupInfo.latitude}\r\n closeOnClick={false}\r\n onClose={() => setPopUpInfo(null)}\r\n anchor=\"bottom\"\r\n style={{ height: '50px', border: 'none', boxShadow: 'none' }}\r\n offset={[0, 10]}\r\n >\r\n <Sensor\r\n sensorName={popupInfo.name || ''}\r\n sensorType={sensorType}\r\n sensorId={popupInfo.id}\r\n tags={liveSensor?.tags ?? []}\r\n onAddTag={async (tag) => { await updateSensor({ tags: [...(liveSensor?.tags ?? []), tag] }) }}\r\n onDeleteTag={async (tag) => { await updateSensor({ tags: (liveSensor?.tags ?? []).filter(t => t !== tag) }) }}\r\n tagsTranslations={{\r\n addTag: tSensors('addTag'),\r\n removeTag: tSensors('removeTag'),\r\n cancel: tSensors('cancel'),\r\n newTagPlaceholder: tSensors('newTagPlaceholder'),\r\n }}\r\n dataUrl={dataUrl}\r\n dataFormat={popupInfo.dataFormat}\r\n updateFrequency={popupInfo.updateFrequency}\r\n buildingId={popupInfo.buildingId}\r\n createdAt={popupInfo.createdAt}\r\n onRemove={user.id === String(popupInfo.authorId) ? handleRemoveSensor : null}\r\n onClose={() => setPopUpInfo(null)}\r\n size=\"sm\"\r\n />\r\n {/* inline styles to override MapLibre's Pop up CSS */}\r\n <style>\r\n {`\r\n .noBorderPopup .maplibregl-popup-content {\r\n border: none !important;\r\n box-shadow: none !important;\r\n background: transparent !important;\r\n }\r\n .noBorderPopup .maplibregl-popup-tip {\r\n display: none !important;\r\n }\r\n `}\r\n </style>\r\n </Popup>\r\n )\r\n }\r\n\r\n // Track unclustered sensors to display icons\r\n const [unclusteredFeatures, setUnclusteredFeatures] = React.useState<MapGeoJSONFeature[]>([])\r\n\r\n React.useEffect(() => {\r\n if (!map) return\r\n\r\n const updateUnclusteredFeatures = () => {\r\n const allFeatures = map.querySourceFeatures('sensors')\r\n const unclusteredOnly = allFeatures.filter(\r\n (f) => !f.properties?.cluster && !f.properties?.point_count\r\n )\r\n // Deduplicate by id\r\n const uniqueFeatures = new Map()\r\n unclusteredOnly.forEach((f) => uniqueFeatures.set(f.properties.id, f))\r\n setUnclusteredFeatures(Array.from(uniqueFeatures.values()))\r\n }\r\n\r\n const onSourceData = (e: any) => {\r\n if (e?.sourceId === 'sensors') updateUnclusteredFeatures()\r\n }\r\n\r\n map.on('sourcedata', onSourceData)\r\n map.on('moveend', updateUnclusteredFeatures)\r\n map.on('zoomend', updateUnclusteredFeatures)\r\n\r\n updateUnclusteredFeatures()\r\n\r\n return () => {\r\n map.off('sourcedata', onSourceData)\r\n map.off('moveend', updateUnclusteredFeatures)\r\n map.off('zoomend', updateUnclusteredFeatures)\r\n }\r\n }, [map])\r\n\r\n return (\r\n <>\r\n {(typesVisible.length > 0 || tagsVisible.length > 0) &&\r\n <Source\r\n id=\"sensors\"\r\n type=\"geojson\"\r\n data={geojsonSensorData}\r\n cluster={true}\r\n clusterMaxZoom={14}\r\n clusterRadius={40}\r\n >\r\n <Layer {...clusterLayer} />\r\n <Layer {...clusterCountLayer} />\r\n <Layer {...unclusteredPointLayer} />\r\n\r\n {renderPopup()}\r\n {unclusteredFeatures\r\n .filter((feature) => feature.properties?.id !== popupInfo?.id)\r\n .map((feature) => (\r\n <SensorIconMarker\r\n key={String(feature.properties?.id)}\r\n feature={feature}\r\n isHighlighted={currentSensorId === feature.properties?.id || hoveredSensorId === feature.properties?.id}\r\n onMouseEnter={() => setHoveredSensorId(feature.properties?.id)}\r\n onMouseLeave={() => setHoveredSensorId(null)}\r\n sensorTypes={sensorTypes}\r\n />\r\n ))}\r\n </Source>}\r\n </>\r\n )\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAuDQ,SAoSJ,UApSI,KAgNF,YAhNE;AAlDR,YAAY,QAAQ;AACpB,SAAS,kBAAkB;AAC3B,SAAS,uBAAuB;AAChC,YAAY,WAAW;AACvB,SAAS,QAAQ,OAAO,OAAO,cAAc;AAC7C,SAAS,aAAa;AAEtB,SAAS,WAAW,kBAAkB;AACtC,SAAS,sBAAsB;AAC/B,SAAS,gBAAgB;AACzB,SAAS,YAAY,oBAAoB;AACzC,SAAS,mBAA0C;AACnD,OAAO,YAAY;AACnB,SAAS,oBAAoB;AAC7B,SAAS,qCAAqC;AAC9C,SAAS,6BAA6B;AACtC,SAAS,oBAAoB,yBAAyB,mCAAmC;AAMzF,MAAM,mBAAmB,CAAC,EAAE,SAAS,eAAe,cAAc,cAAc,YAAY,MAAgJ;AA3B5O;AA6BE,QAAM,SAAS,8BAA8B,OAAO;AACpD,MAAI,CAAC,OAAQ,QAAO;AAEpB,QAAM,aAAa,YAAY,KAAK,OAAE;AAhCxC,QAAAA;AAgC2C,aAAE,SAAOA,MAAA,QAAQ,eAAR,gBAAAA,IAAoB;AAAA,GAAM;AAC5E,QAAM,QAAO,yCAAY,SAAQ;AACjC,QAAM,aAAa,GAAG,IAAI,KAAK,GAAG;AAElC,SACE,oBAAC,UAA6E,WAAW,OAAO,KAAK,UAAU,OAAO,KAAK,QAAO,UAChI;AAAA,IAAC;AAAA;AAAA,MACC,OAAO;AAAA,QACL,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,cAAc;AAAA,QACd,QAAQ,gBAAgB,sBAAsB;AAAA,QAC9C,iBAAiB;AAAA,QACjB,SAAS;AAAA,QACT,YAAY;AAAA,QACZ,gBAAgB;AAAA,QAChB,WAAW,gBAAgB,sCAAsC;AAAA,QACjE,YAAY;AAAA,QACZ,WAAW,gBAAgB,eAAe;AAAA,MAC5C;AAAA,MACA;AAAA,MACA;AAAA,MAEA,8BAAC,cAAW,OAAO,EAAE,OAAO,QAAQ,QAAQ,QAAQ,OAAO,iCAAiC,GAAG;AAAA;AAAA,EACjG,KAnBW,QAAO,mBAAQ,eAAR,mBAAoB,OAApB,YAA0B,GAAG,OAAO,GAAG,IAAI,OAAO,GAAG,EAAE,CAoB3E;AAEJ;AAEO,MAAM,eAAe,CAAC,EAAE,aAAa,MAAiC;AA7D7E;AA8DI,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,MAAM,SAAwB,IAAI;AAClF,QAAM,eAAe,mBAAmB,SAAS;AACjD,QAAM,oBAAoB,wBAAwB,SAAS;AAC3D,QAAM,wBAAwB,4BAA4B,SAAS;AAInE,QAAM,IAAI,gBAAgB,cAAc;AACxC,QAAM,WAAW,gBAAgB,gBAAgB;AAEjD,QAAM,EAAE,OAAO,SAAS,IAAI,MAAM,WAAW,UAAU;AACvD,QAAM,EAAE,KAAK,gBAAgB,IAAI,SAAS;AAC1C,QAAM,EAAE,QAAQ,IAAI,WAAW;AAC/B,QAAM,EAAE,MAAM,IAAI,SAAS;AAC3B,QAAM,QAAO,gBAAW,EAAE,SAAb,mBAAmB;AAEhC,QAAM,CAAC,WAAW,YAAY,IAAI,MAAM,SAA+D,IAAI;AAC3G,QAAM,EAAE,cAAc,aAAa,IAAI,WAAU,4CAAW,OAAX,YAAiB,IAAI;AACtE,QAAM,EAAE,OAAO,WAAW,IAAI,MAAM,WAAW,YAAY;AAC3D,QAAM,EAAE,oBAAoB,mBAAmB,gBAAgB,IAAI,WAAW;AAC9E,QAAM,gBAAe,yDAAqB,YAAY,SAAQ,CAAC;AAC/D,QAAM,eAAc,uDAAoB,YAAY,SAAQ,CAAC;AAE7D,QAAM,EAAC,YAAW,IAAI,eAAe;AAErC,QAAM,kBAAwF,QAC3F,OAAO,CAAC,WAAW,OAAO,WAAW,YAAY,GAAG,EACpD,OAAO,CAAC,WAAW;AAzFxB,QAAAA,KAAAC,KAAA;AA0FM,UAAM,cAAc,aAAa,SAAS,OAAO,MAAM;AACvD,UAAM,YAAY,GAACD,MAAA,OAAO,SAAP,gBAAAA,IAAa;AAChC,UAAM,aAAa,YACf,YAAY,SAAS,YAAY,KACjC,MAAAC,MAAA,OAAO,SAAP,gBAAAA,IAAa,KAAK,SAAO,YAAY,SAAS,GAAG,OAAjD,YAAuD;AAC3D,WAAO,eAAe;AAAA,EACxB,CAAC,EACA,IAAI,CAAC,WAAW;AAjGrB,QAAAD;AAkGM,UAAME,QAAO,MAAM,KAAK,OAAK,EAAE,OAAO,OAAO,QAAQ;AACrD,UAAM,aAAa,YAAY,KAAK,CAAAC,OAAKA,GAAE,OAAO,OAAO,MAAM;AAC/D,WAAO,iCACF,SADE;AAAA,MAEL,aAAYH,MAAAE,SAAA,gBAAAA,MAAM,SAAN,OAAAF,MAAc;AAAA,MAC1B;AAAA,IACF;AAAA,EACF,CAAC;AAGH,QAAM,UAAU,MAAM;AACpB,QAAI,aAAa,CAAC,QAAQ,KAAK,CAAC,MAAM,EAAE,OAAO,UAAU,EAAE,GAAG;AAC5D,mBAAa,IAAI;AAAA,IACnB;AAAA,EACF,GAAG,CAAC,SAAS,SAAS,CAAC;AAEvB,QAAM,oBAAoB,MAAM,QAAQ,MAAM;AAC5C,UAAM,uBAAuB,CAAC,eAAyG;AACrI,YAAM,iBAA2E,WAC9E,IAAI,CAAC,WAAW;AACf,cAAM,EAAE,WAAW,UAAU,IAAI,MAAM,QAAQ,YAAY,MAAM,YAAY,iBAAiB,WAAW,UAAU,gBAAgB,SAAS,KAAK,WAAW,IAAI;AAEhK,eAAO;AAAA,UACL,MAAM;AAAA,UACN,UAAU;AAAA,YACR,MAAM;AAAA,YACN,aAAa,CAAC,WAAW,QAAQ;AAAA,UACnC;AAAA,UACA,YAAY;AAAA,YACV,IAAI,OAAO,EAAE;AAAA,YACb;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA,UAAU,OAAO,QAAQ;AAAA,YACzB;AAAA,YACA;AAAA,YACA,QAAQ,OAAO,MAAM;AAAA,YACrB,UAAU,yCAAY;AAAA,YACtB;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA,QAAQ,YAAY;AAAA,UACtB;AAAA,QACF;AAAA,MACF,CAAC;AAIH,YAAM,WAA6E;AAAA,QACjF,MAAM;AAAA,QACN,UAAU;AAAA,MACZ;AACA,aAAO;AAAA,IACT;AACA,WAAO,qBAAqB,eAAe;AAAA,EAC7C,GAAG,CAAC,eAAe,CAAC;AAGpB,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,IAAK;AACV,UAAM,kBAAiC,CAAC,GAAuB,aAAkC;AAjKrG,UAAAA,KAAAC;AAkKM,YAAM,UAAU,qCAAW;AAE3B,UAAI,CAAC,WAAW,QAAQ,WAAW,YAAa;AAEhD,UAAI,QAAQ,SAAS,QAAQ,QAAS;AACtC,YAAM,CAAC,WAAW,QAAQ,IAAI,QAAQ,SAAS;AAC/C,YAAM,EAAE,IAAI,UAAU,MAAM,QAAQ,MAAM,YAAY,iBAAiB,WAAW,IAAI,IAAI,QAAQ;AAClG,mBAAa;AAAA,QACX,IAAI,OAAO,EAAE;AAAA,QACb,UAAU,OAAO,QAAQ;AAAA,QACzB,iBAAgBD,MAAA,QAAQ,eAAR,gBAAAA,IAAoB;AAAA,QACpC,UAASC,MAAA,QAAQ,eAAR,gBAAAA,IAAoB;AAAA,QAC7B;AAAA,QACA;AAAA,QACA;AAAA,QACA,QAAQ,OAAO,MAAM;AAAA,QACrB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,QAAQ,YAAY;AAAA,MACtB,CAAC;AAAA,IACH;AAEA,UAAM,yBAAyB,MAAM;AACnC,UAAI,UAAU,EAAE,MAAM,SAAS;AAAA,IACjC;AACA,UAAM,yBAAyB,MAAM;AACnC,UAAI,UAAU,EAAE,MAAM,SAAS;AAAA,IACjC;AAGA,oBAAgB,SAAS,8BAA8B,sBAAsB,4BAA4B,eAAe;AAExH,QAAI,GAAG,cAAc,8BAA8B,sBAAsB;AACzE,QAAI,GAAG,cAAc,8BAA8B,sBAAsB;AACzE,WAAO,MAAM;AACX,sBAAgB,WAAW,4BAA4B;AACvD,UAAI,IAAI,cAAc,8BAA8B,sBAAsB;AAC1E,UAAI,IAAI,cAAc,8BAA8B,sBAAsB;AAAA,IAC5E;AAAA,EACF,GAAG,CAAC,KAAK,eAAe,CAAC;AAEzB,QAAM,qBAAqB,MAAM;AAC/B,UAAM,QAAQ,EAAE,eAAe,CAAC;AAChC,iBAAa;AACb,iBAAa,IAAI;AAAA,EACnB;AAGA,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,IAAK;AAEV,UAAM,oBAAoB,CAAC,MAA0B;AAxNzD,UAAAD;AAyNM,YAAM,WAAUA,MAAA,EAAE,aAAF,gBAAAA,IAAa;AAC7B,UAAI,CAAC,WAAW,CAAC,QAAQ,WAAW,WAAY;AAEhD,YAAM,YAAY,QAAQ,WAAW;AAGrC,UAAI,EAAE,iBAAiB,QAAQ,UAAW;AAC1C,YAAM,CAAC,KAAK,GAAG,IAAI,QAAQ,SAAS;AAEpC,YAAM,SAAS,IAAI,UAAU,SAAS;AACtC,cAAQ,IAAI,MAAM;AAClB,cAAQ,IAAI,SAAS;AACrB,aAAO,wBAAwB,SAAS,EAAE;AAAA,QACxC,CAAC,SAAiB;AAChB,kBAAQ,IAAI,WAAW;AACvB,cAAI,OAAO,EAAE,QAAQ,CAAC,KAAK,GAAG,GAAG,KAAK,CAAC;AAAA,QACzC;AAAA,MACF,EACG,MAAM,CAAC,UAAU;AAChB,gBAAQ,MAAM,KAAK;AAAA,MACrB,CAAC;AAAA,IACL;AAEA,UAAM,yBAAyB,MAAM;AAAE,UAAI,UAAU,EAAE,MAAM,SAAS;AAAA,IAAU;AAChF,UAAM,yBAAyB,MAAM;AAAE,UAAI,UAAU,EAAE,MAAM,SAAS;AAAA,IAAG;AAGzE,QAAI,GAAG,SAAS,oBAAoB,iBAAiB;AACrD,QAAI,GAAG,cAAc,oBAAoB,sBAAsB;AAC/D,QAAI,GAAG,cAAc,oBAAoB,sBAAsB;AAC/D,WAAO,MAAM;AACX,UAAI,IAAI,SAAS,oBAAoB,iBAAiB;AACtD,UAAI,IAAI,cAAc,oBAAoB,sBAAsB;AAChE,UAAI,IAAI,cAAc,oBAAoB,sBAAsB;AAAA,IAClE;AAAA,EACF,GAAG,CAAC,GAAG,CAAC;AAER,QAAM,cAAc,MAAM;AA9P5B,QAAAA;AA+PI,QAAI,CAAC,UAAW,QAAO;AACvB,UAAM,aAAa,YAAY,KAAK,CAAAG,OAAKA,GAAE,OAAO,UAAU,MAAM;AAClE,UAAM,aAAa,QAAQ,KAAK,OAAK,EAAE,OAAO,UAAU,EAAE;AAC1D,UAAM,UAAU,UAAU,MACtB,GAAG,sCAAgB,EAAE,YAAY,UAAU,GAAG,KAC9C,UAAU,QAAQ;AAEtB,WACE;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,WAAW,UAAU;AAAA,QACrB,UAAU,UAAU;AAAA,QACpB,cAAc;AAAA,QACd,SAAS,MAAM,aAAa,IAAI;AAAA,QAChC,QAAO;AAAA,QACP,OAAO,EAAE,QAAQ,QAAQ,QAAQ,QAAQ,WAAW,OAAO;AAAA,QAC3D,QAAQ,CAAC,GAAG,EAAE;AAAA,QAEd;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,YAAY,UAAU,QAAQ;AAAA,cAC9B;AAAA,cACA,UAAU,UAAU;AAAA,cACpB,OAAMH,MAAA,yCAAY,SAAZ,OAAAA,MAAoB,CAAC;AAAA,cAC3B,UAAU,OAAO,QAAQ;AAtRnC,oBAAAA;AAsRqC,sBAAM,aAAa,EAAE,MAAM,CAAC,IAAIA,MAAA,yCAAY,SAAZ,OAAAA,MAAoB,CAAC,GAAI,GAAG,EAAE,CAAC;AAAA,cAAE;AAAA,cAC5F,aAAa,OAAO,QAAQ;AAvRtC,oBAAAA;AAuRwC,sBAAM,aAAa,EAAE,QAAOA,MAAA,yCAAY,SAAZ,OAAAA,MAAoB,CAAC,GAAG,OAAO,CAAAG,OAAKA,OAAM,GAAG,EAAE,CAAC;AAAA,cAAE;AAAA,cAC5G,kBAAkB;AAAA,gBAChB,QAAQ,SAAS,QAAQ;AAAA,gBACzB,WAAW,SAAS,WAAW;AAAA,gBAC/B,QAAQ,SAAS,QAAQ;AAAA,gBACzB,mBAAmB,SAAS,mBAAmB;AAAA,cACjD;AAAA,cACA;AAAA,cACA,YAAY,UAAU;AAAA,cACtB,iBAAiB,UAAU;AAAA,cAC3B,YAAY,UAAU;AAAA,cACtB,WAAW,UAAU;AAAA,cACrB,UAAU,KAAK,OAAO,OAAO,UAAU,QAAQ,IAAI,qBAAqB;AAAA,cACxE,SAAS,MAAM,aAAa,IAAI;AAAA,cAChC,MAAK;AAAA;AAAA,UACP;AAAA,UAEA,oBAAC,WACE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,eAUH;AAAA;AAAA;AAAA,IACF;AAAA,EAEJ;AAGA,QAAM,CAAC,qBAAqB,sBAAsB,IAAI,MAAM,SAA8B,CAAC,CAAC;AAE5F,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,IAAK;AAEV,UAAM,4BAA4B,MAAM;AACtC,YAAM,cAAc,IAAI,oBAAoB,SAAS;AACrD,YAAM,kBAAkB,YAAY;AAAA,QAClC,CAAC,MAAG;AAjUZ,cAAAH,KAAAC;AAiUe,oBAACD,MAAA,EAAE,eAAF,gBAAAA,IAAc,YAAW,GAACC,MAAA,EAAE,eAAF,gBAAAA,IAAc;AAAA;AAAA,MAClD;AAEA,YAAM,iBAAiB,oBAAI,IAAI;AAC/B,sBAAgB,QAAQ,CAAC,MAAM,eAAe,IAAI,EAAE,WAAW,IAAI,CAAC,CAAC;AACrE,6BAAuB,MAAM,KAAK,eAAe,OAAO,CAAC,CAAC;AAAA,IAC5D;AAEA,UAAM,eAAe,CAAC,MAAW;AAC/B,WAAI,uBAAG,cAAa,UAAW,2BAA0B;AAAA,IAC3D;AAEA,QAAI,GAAG,cAAc,YAAY;AACjC,QAAI,GAAG,WAAW,yBAAyB;AAC3C,QAAI,GAAG,WAAW,yBAAyB;AAE3C,8BAA0B;AAE1B,WAAO,MAAM;AACX,UAAI,IAAI,cAAc,YAAY;AAClC,UAAI,IAAI,WAAW,yBAAyB;AAC5C,UAAI,IAAI,WAAW,yBAAyB;AAAA,IAC9C;AAAA,EACF,GAAG,CAAC,GAAG,CAAC;AAER,SACE,gCACA,wBAAa,SAAS,KAAK,YAAY,SAAS,MAChD;AAAA,IAAC;AAAA;AAAA,MACC,IAAG;AAAA,MACH,MAAK;AAAA,MACL,MAAM;AAAA,MACN,SAAS;AAAA,MACT,gBAAgB;AAAA,MAChB,eAAe;AAAA,MAEf;AAAA,4BAAC,0BAAU,aAAc;AAAA,QACzB,oBAAC,0BAAU,kBAAmB;AAAA,QAC9B,oBAAC,0BAAU,sBAAuB;AAAA,QAEjC,YAAY;AAAA,QACZ,oBACE,OAAO,CAAC,YAAS;AA3W1B,cAAAD;AA2W6B,mBAAAA,MAAA,QAAQ,eAAR,gBAAAA,IAAoB,SAAO,uCAAW;AAAA,SAAE,EAC5D,IAAI,CAAC,YAAS;AA5WvB,cAAAA,KAAAC,KAAA;AA6WU;AAAA,YAAC;AAAA;AAAA,cAEC;AAAA,cACA,eAAe,sBAAoBA,MAAA,QAAQ,eAAR,gBAAAA,IAAoB,OAAM,sBAAoB,aAAQ,eAAR,mBAAoB;AAAA,cACrG,cAAc,MAAG;AAjX7B,oBAAAD;AAiXgC,2CAAmBA,MAAA,QAAQ,eAAR,gBAAAA,IAAoB,EAAE;AAAA;AAAA,cAC7D,cAAc,MAAM,mBAAmB,IAAI;AAAA,cAC3C;AAAA;AAAA,YALK,QAAOA,MAAA,QAAQ,eAAR,gBAAAA,IAAoB,EAAE;AAAA,UAMpC;AAAA,SACD;AAAA;AAAA;AAAA,EACL,GACA;AAEJ;","names":["_a","_b","user","t"]}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.tsx"],"sourcesContent":["'use client'\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport * as LR from 'lucide-react'\r\nimport { useSession } from 'next-auth/react'\r\nimport { useTranslations } from 'next-intl'\r\nimport * as React from 'react'\r\nimport { Source, Layer, Popup, Marker } from 'react-map-gl/maplibre'\r\nimport { toast } from 'sonner'\r\n\r\nimport { useSensor, useSensors } from '../../../../../../../hooks/sensors/sensors'\r\nimport { useSensorTypes } from '../../../../../../../hooks/sensorTypes/sensorTypes'\r\nimport { useUsers } from '../../../../../../../hooks/users/users'\r\nimport { AppConfigContext, MapContext, MenusContext } from '../../../../../../../store'\r\nimport { ViewerNames, type Sensor as ISensor} from '../../../../../../../types/dbTypes'\r\nimport Sensor from '../../../../../../ui/Sensors/Sensor'\r\nimport { SensorDetailDialog } from '../../../../../../ui/Sensors/SensorDetailDialog'\r\nimport { SensorInput } from '../../../../../../ui/Sensors/SensorInput'\r\nimport { UNTAGGED_TAG } from '../../../../../../ui/Sensors/SensorsSection'\r\nimport { extractCoordinatesFromFeature } from '../../../../utils/extractCoordinates'\r\nimport { MapLayerClickPriority } from '../../../../utils/MapEventManager/MapClickManager'\r\nimport { createClusterLayer, createClusterCountLayer, createUnclusteredPointLayer } from '../mapLayersUtils'\r\n\r\nimport type { SensorType} from '../../../../../../../types/dbTypes';\r\nimport type { ClickCallback } from '../../../../utils/MapEventManager/MapClickManager';\r\nimport type { MapGeoJSONFeature, MapLayerMouseEvent } from 'maplibre-gl'\r\n\r\nconst SensorIconMarker = ({ feature, isHighlighted, onMouseEnter, onMouseLeave, sensorTypes }: { feature: MapGeoJSONFeature; isHighlighted?: boolean; onMouseEnter?: () => void; onMouseLeave?: () => void; sensorTypes: SensorType[] }) => {\r\n\r\n const coords = extractCoordinatesFromFeature(feature)\r\n if (!coords) return null\r\n\r\n const sensorType = sensorTypes.find(t => t.id === feature.properties?.typeId)\r\n const icon = sensorType?.icon || 'Radio'\r\n const SensorIcon = LR[icon] || LR.Radio\r\n\r\n return (\r\n <Marker key={String(feature.properties?.id ?? `${coords.lng},${coords.lat}`)} longitude={coords.lng} latitude={coords.lat} anchor=\"center\">\r\n <div\r\n style={{\r\n width: '36px',\r\n height: '36px',\r\n borderRadius: '50%',\r\n border: isHighlighted ? '3px solid #73cee2' : '2px solid white',\r\n backgroundColor: 'black',\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n boxShadow: isHighlighted ? '0 0 12px rgba(115, 206, 226, 0.5)' : 'none',\r\n transition: 'all 0.2s ease-in-out',\r\n transform: isHighlighted ? 'scale(1.2)' : 'scale(1)',\r\n }}\r\n onMouseEnter={onMouseEnter}\r\n onMouseLeave={onMouseLeave}\r\n >\r\n <SensorIcon style={{ width: '20px', height: '20px', color: 'hsl(var(--primary-foreground))' }} />\r\n </div>\r\n </Marker>\r\n )\r\n}\r\n\r\nexport const SensorLayers = () => {\r\n const [hoveredSensorId, setHoveredSensorId] = React.useState<number | null>(null)\r\n const clusterLayer = createClusterLayer('sensors')\r\n const clusterCountLayer = createClusterCountLayer('sensors')\r\n const unclusteredPointLayer = createUnclusteredPointLayer('sensors')\r\n\r\n // global map state\r\n\r\n const t = useTranslations('SensorLayers')\r\n const tSensors = useTranslations('SensorsSection')\r\n\r\n const { state: mapState } = React.useContext(MapContext)\r\n const { map, mapClickManager } = mapState.map\r\n const { sensors } = useSensors()\r\n const { users } = useUsers()\r\n const user = useSession().data?.user\r\n\r\n const [popupInfo, setPopUpInfo] = React.useState<Partial<ISensor & { sensorType: SensorType }> | null>(null)\r\n const { deleteSensor, updateSensor } = useSensor(popupInfo?.id ?? null)\r\n const { state: menusState } = React.useContext(MenusContext)\r\n const { visibleSensorTypes, visibleSensorTags, currentSensorId } = menusState.menus\r\n const typesVisible = visibleSensorTypes?.[ViewerNames.map] || []\r\n const tagsVisible = visibleSensorTags?.[ViewerNames.map] || []\r\n\r\n const {sensorTypes} = useSensorTypes()\r\n\r\n const { state: appConfigState } = React.useContext(AppConfigContext)\r\n const timeZone = appConfigState.appConfig.displayTimeZone\r\n const [detailSensor, setDetailSensor] = React.useState<ISensor | null>(null)\r\n const [editSensor, setEditSensor] = React.useState<ISensor | null>(null)\r\n\r\n const eligibleSensors: Array<ISensor & { authorName: string } & { sensorType: SensorType }> = sensors\r\n .filter((sensor) => sensor.viewer === ViewerNames.map)\r\n .filter((sensor) => {\r\n const matchesType = typesVisible.includes(sensor.typeId)\r\n const hasNoTags = !sensor.tags?.length\r\n const matchesTag = hasNoTags\r\n ? tagsVisible.includes(UNTAGGED_TAG)\r\n : sensor.tags?.some(tag => tagsVisible.includes(tag)) ?? false\r\n return matchesType || matchesTag\r\n })\r\n .map((sensor) => {\r\n const user = users.find(u => u.id === sensor.authorId)\r\n const sensorType = sensorTypes.find(t => t.id === sensor.typeId)\r\n return {\r\n ...sensor,\r\n authorName: user?.name ?? 'Unknown User',\r\n sensorType,\r\n }\r\n })\r\n\r\n // Close popup if the sensor was deleted\r\n React.useEffect(() => {\r\n if (popupInfo && !sensors.find((s) => s.id === popupInfo.id)) {\r\n setPopUpInfo(null)\r\n }\r\n }, [sensors, popupInfo])\r\n\r\n const geojsonSensorData = React.useMemo(() => {\r\n const convertDataToGeojson = (sensorData: typeof eligibleSensors): GeoJSON.FeatureCollection<GeoJSON.Point, { [key: string]: any }> => {\r\n const sensorFeatures: GeoJSON.Feature<GeoJSON.Point, { [key: string]: any }>[] = sensorData\r\n .map((sensor) => {\r\n const { longitude, latitude, id, name, typeId, sensorType, data, dataFormat, updateFrequency, createdAt, authorId, organizationId, visible, url, authorName } = sensor\r\n\r\n return {\r\n type: 'Feature',\r\n geometry: {\r\n type: 'Point',\r\n coordinates: [longitude, latitude],\r\n },\r\n properties: {\r\n id: Number(id),\r\n organizationId,\r\n visible,\r\n longitude,\r\n latitude,\r\n authorId: Number(authorId),\r\n authorName,\r\n name,\r\n typeId: Number(typeId),\r\n typeIcon: sensorType?.icon,\r\n data,\r\n dataFormat,\r\n updateFrequency,\r\n url,\r\n createdAt,\r\n viewer: ViewerNames.map,\r\n },\r\n }\r\n })\r\n\r\n\r\n\r\n const sensorFC: GeoJSON.FeatureCollection<GeoJSON.Point, { [key: string]: any }> = {\r\n type: 'FeatureCollection',\r\n features: sensorFeatures,\r\n }\r\n return sensorFC\r\n }\r\n return convertDataToGeojson(eligibleSensors)\r\n }, [eligibleSensors])\r\n\r\n // event listeners for sensor unclustered points\r\n React.useEffect(() => {\r\n if (!map) return\r\n const showSensorPopUp: ClickCallback = (e: MapLayerMouseEvent, features: MapGeoJSONFeature[]) => {\r\n const feature = features?.[0]\r\n\r\n if (!feature || feature.properties.point_count) return\r\n\r\n if (feature.geometry.type != 'Point') return\r\n const [longitude, latitude] = feature.geometry.coordinates\r\n const { id, authorId, name, typeId, data, dataFormat, updateFrequency, createdAt, url } = feature.properties\r\n setPopUpInfo({\r\n id: Number(id),\r\n authorId: Number(authorId),\r\n organizationId: feature.properties?.organizationId,\r\n visible: feature.properties?.visible,\r\n longitude,\r\n latitude,\r\n name,\r\n typeId: Number(typeId),\r\n data,\r\n dataFormat,\r\n updateFrequency,\r\n url,\r\n createdAt,\r\n viewer: ViewerNames.map,\r\n })\r\n }\r\n\r\n const mouseEnterChangeCursor = () => {\r\n map.getCanvas().style.cursor = 'pointer'\r\n }\r\n const mouseLeaveChangeCursor = () => {\r\n map.getCanvas().style.cursor = ''\r\n }\r\n\r\n // event listener for clicking on single point to show sensor, hover to change cursor\r\n mapClickManager.register('sensors-unclustered-points', MapLayerClickPriority.CommentLayersClickPriority, showSensorPopUp)\r\n\r\n map.on('mouseenter', 'sensors-unclustered-points', mouseEnterChangeCursor)\r\n map.on('mouseleave', 'sensors-unclustered-points', mouseLeaveChangeCursor)\r\n return () => {\r\n mapClickManager.unregister('sensors-unclustered-points')\r\n map.off('mouseenter', 'sensors-unclustered-points', mouseEnterChangeCursor)\r\n map.off('mouseleave', 'sensors-unclustered-points', mouseLeaveChangeCursor)\r\n }\r\n }, [map, mapClickManager])\r\n\r\n const handleRemoveSensor = () => {\r\n toast.success(t('sensorDeleted'))\r\n deleteSensor()\r\n setPopUpInfo(null)\r\n }\r\n\r\n // event listeners for clustered points\r\n React.useEffect(() => {\r\n if (!map) return\r\n\r\n const zoomInToDecluster = (e: MapLayerMouseEvent) => {\r\n const feature = e.features?.[0] // only proceed if this is actually a cluster\r\n if (!feature || !feature.properties.cluster_id) return\r\n\r\n const clusterId = feature.properties.cluster_id as number\r\n\r\n // Check if geometry has coordinates (not a GeometryCollection)\r\n if (!('coordinates' in feature.geometry)) return\r\n const [lng, lat] = feature.geometry.coordinates as [number, number]\r\n\r\n const source = map.getSource('sensors') as any\r\n source.getClusterExpansionZoom(clusterId).then(\r\n (zoom: number) => {\r\n map.easeTo({ center: [lng, lat], zoom })\r\n },\r\n )\r\n .catch((error) => {\r\n console.error(error)\r\n })\r\n }\r\n\r\n const mouseEnterChangeCursor = () => { map.getCanvas().style.cursor = 'pointer' }\r\n const mouseLeaveChangeCursor = () => { map.getCanvas().style.cursor = '' }\r\n\r\n // event listener for clicking on single point to zoom in and decluster, hover to change cursor\r\n map.on('click', 'sensors-clusters', zoomInToDecluster)\r\n map.on('mouseenter', 'sensors-clusters', mouseEnterChangeCursor)\r\n map.on('mouseleave', 'sensors-clusters', mouseLeaveChangeCursor)\r\n return () => {\r\n map.off('click', 'sensors-clusters', zoomInToDecluster)\r\n map.off('mouseenter', 'sensors-clusters', mouseEnterChangeCursor)\r\n map.off('mouseleave', 'sensors-clusters', mouseLeaveChangeCursor)\r\n }\r\n }, [map])\r\n\r\n const renderPopup = () => {\r\n if (!popupInfo) return null\r\n const sensorType = sensorTypes.find(t => t.id === popupInfo.typeId)\r\n const liveSensor = sensors.find(s => s.id === popupInfo.id)\r\n const dataUrl = popupInfo.url || popupInfo.data || ''\r\n\r\n return (\r\n <Popup\r\n className=\"noBorderPopup\"\r\n longitude={popupInfo.longitude}\r\n latitude={popupInfo.latitude}\r\n closeOnClick={false}\r\n closeButton={false}\r\n onClose={() => setPopUpInfo(null)}\r\n anchor=\"bottom\"\r\n style={{ height: '50px', border: 'none', boxShadow: 'none' }}\r\n offset={[0, 10]}\r\n >\r\n <Sensor\r\n sensorName={popupInfo.name || ''}\r\n sensorType={sensorType}\r\n sensorId={popupInfo.id}\r\n tags={liveSensor?.tags ?? []}\r\n onAddTag={async (tag) => { await updateSensor({ tags: [...(liveSensor?.tags ?? []), tag] }) }}\r\n onDeleteTag={async (tag) => { await updateSensor({ tags: (liveSensor?.tags ?? []).filter(t => t !== tag) }) }}\r\n tagsTranslations={{\r\n addTag: tSensors('addTag'),\r\n removeTag: tSensors('removeTag'),\r\n cancel: tSensors('cancel'),\r\n newTagPlaceholder: tSensors('newTagPlaceholder'),\r\n }}\r\n dataUrl={dataUrl}\r\n dataFormat={popupInfo.dataFormat}\r\n updateFrequency={popupInfo.updateFrequency}\r\n buildingId={popupInfo.buildingId}\r\n createdAt={popupInfo.createdAt}\r\n onRemove={user.id === String(popupInfo.authorId) ? handleRemoveSensor : null}\r\n onClose={() => setPopUpInfo(null)}\r\n onExpand={() => { if (liveSensor) setDetailSensor(liveSensor) }}\r\n onEdit={user.id === String(popupInfo.authorId) && liveSensor ? () => setEditSensor(liveSensor) : undefined}\r\n showActions\r\n timeZone={timeZone}\r\n size=\"sm\"\r\n />\r\n {/* inline styles to override MapLibre's Pop up CSS */}\r\n <style>\r\n {`\r\n .noBorderPopup .maplibregl-popup-content {\r\n border: none !important;\r\n box-shadow: none !important;\r\n background: transparent !important;\r\n }\r\n .noBorderPopup .maplibregl-popup-tip {\r\n display: none !important;\r\n }\r\n `}\r\n </style>\r\n </Popup>\r\n )\r\n }\r\n\r\n // Track unclustered sensors to display icons\r\n const [unclusteredFeatures, setUnclusteredFeatures] = React.useState<MapGeoJSONFeature[]>([])\r\n\r\n React.useEffect(() => {\r\n if (!map) return\r\n\r\n const updateUnclusteredFeatures = () => {\r\n const allFeatures = map.querySourceFeatures('sensors')\r\n const unclusteredOnly = allFeatures.filter(\r\n (f) => !f.properties?.cluster && !f.properties?.point_count\r\n )\r\n // Deduplicate by id\r\n const uniqueFeatures = new Map()\r\n unclusteredOnly.forEach((f) => uniqueFeatures.set(f.properties.id, f))\r\n setUnclusteredFeatures(Array.from(uniqueFeatures.values()))\r\n }\r\n\r\n const onSourceData = (e: any) => {\r\n if (e?.sourceId === 'sensors') updateUnclusteredFeatures()\r\n }\r\n\r\n map.on('sourcedata', onSourceData)\r\n map.on('moveend', updateUnclusteredFeatures)\r\n map.on('zoomend', updateUnclusteredFeatures)\r\n\r\n updateUnclusteredFeatures()\r\n\r\n return () => {\r\n map.off('sourcedata', onSourceData)\r\n map.off('moveend', updateUnclusteredFeatures)\r\n map.off('zoomend', updateUnclusteredFeatures)\r\n }\r\n }, [map])\r\n\r\n return (\r\n <>\r\n {detailSensor && (\r\n <SensorDetailDialog\r\n open={!!detailSensor}\r\n onOpenChange={(o) => !o && setDetailSensor(null)}\r\n sensor={detailSensor}\r\n sensorType={sensorTypes.find(t => t.id === detailSensor.typeId)}\r\n />\r\n )}\r\n {editSensor && (\r\n <SensorInput\r\n viewer={ViewerNames.map}\r\n layout=\"dialog\"\r\n isOpen={!!editSensor}\r\n editSensor={editSensor ?? undefined}\r\n onCancel={() => setEditSensor(null)}\r\n onSaved={() => setEditSensor(null)}\r\n />\r\n )}\r\n {(typesVisible.length > 0 || tagsVisible.length > 0) &&\r\n <Source\r\n id=\"sensors\"\r\n type=\"geojson\"\r\n data={geojsonSensorData}\r\n cluster={true}\r\n clusterMaxZoom={14}\r\n clusterRadius={40}\r\n >\r\n <Layer {...clusterLayer} />\r\n <Layer {...clusterCountLayer} />\r\n <Layer {...unclusteredPointLayer} />\r\n\r\n {renderPopup()}\r\n {unclusteredFeatures\r\n .filter((feature) => feature.properties?.id !== popupInfo?.id)\r\n .map((feature) => (\r\n <SensorIconMarker\r\n key={String(feature.properties?.id)}\r\n feature={feature}\r\n isHighlighted={currentSensorId === feature.properties?.id || hoveredSensorId === feature.properties?.id}\r\n onMouseEnter={() => setHoveredSensorId(feature.properties?.id)}\r\n onMouseLeave={() => setHoveredSensorId(null)}\r\n sensorTypes={sensorTypes}\r\n />\r\n ))}\r\n </Source>}\r\n </>\r\n )\r\n}\r\n"],"mappings":"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","names":["_a","_b","user","t"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TabSelector.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/components/viewers/map/src/MapSidebar/TabSelector.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAA;AAExE,UAAU,gBAAgB;IACxB,SAAS,EAAE,cAAc,CAAA;IACzB,iBAAiB,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,CAAA;CACjD;AAED,wBAAgB,WAAW,CAAC,EAAE,SAAS,EAAE,iBAAiB,EAAE,EAAE,gBAAgB,
|
|
1
|
+
{"version":3,"file":"TabSelector.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/components/viewers/map/src/MapSidebar/TabSelector.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAA;AAExE,UAAU,gBAAgB;IACxB,SAAS,EAAE,cAAc,CAAA;IACzB,iBAAiB,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,CAAA;CACjD;AAED,wBAAgB,WAAW,CAAC,EAAE,SAAS,EAAE,iBAAiB,EAAE,EAAE,gBAAgB,qBAsC7E"}
|
|
@@ -13,15 +13,16 @@ function TabSelector({ activeTab, onTabChangeAction }) {
|
|
|
13
13
|
];
|
|
14
14
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
15
15
|
/* @__PURE__ */ jsx(Separator, {}),
|
|
16
|
-
/* @__PURE__ */ jsx("div", { className: "px-4 py-3", children: /* @__PURE__ */ jsx("div", { className: "flex items-center
|
|
16
|
+
/* @__PURE__ */ jsx("div", { className: "px-4 py-3", children: /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1 w-full", children: tabs.map((tab) => /* @__PURE__ */ jsx(
|
|
17
17
|
"div",
|
|
18
18
|
{
|
|
19
|
-
|
|
19
|
+
title: tab.label,
|
|
20
|
+
className: `flex-1 min-w-0 truncate text-center px-2 py-2 rounded-md cursor-pointer transition-colors text-xs font-medium ${activeTab === tab.id ? "bg-accent text-accent-foreground" : "text-muted-foreground hover:text-foreground hover:bg-accent/50"}`,
|
|
20
21
|
onClick: () => onTabChangeAction(tab.id),
|
|
21
22
|
children: tab.label
|
|
22
23
|
},
|
|
23
24
|
tab.id
|
|
24
|
-
)) }) })
|
|
25
|
+
)) }) }),
|
|
25
26
|
/* @__PURE__ */ jsx(Separator, {})
|
|
26
27
|
] });
|
|
27
28
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../src/core/components/viewers/map/src/MapSidebar/TabSelector.tsx"],"sourcesContent":["'use client'\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport { useTranslations } from 'next-intl'\r\nimport * as React from 'react'\r\n\r\nimport { Separator } from '../../../../ui/Separator'\r\n\r\nimport type { SidebarTabType } from '../../../../../store/Menus/reducer'\r\n\r\ninterface TabSelectorProps {\r\n activeTab: SidebarTabType\r\n onTabChangeAction: (tab: SidebarTabType) => void\r\n}\r\n\r\nexport function TabSelector({ activeTab, onTabChangeAction }: TabSelectorProps) {\r\n // Translation\r\n const t = useTranslations('TabSelector')\r\n\r\n const tabs = [\r\n { id: 'file', label: t('fileLabel') },\r\n { id: 'layers', label: t('layersTitle') },\r\n { id: 'communication', label: t('communicationTitle') },\r\n { id: 'sensors', label: t('sensorsTitle') },\r\n { id: 'settings', label: t('settingsTitle') },\r\n ]\r\n\r\n return (\r\n <div>\r\n <Separator />\r\n\r\n <div className=\"px-4 py-3\">\r\n <div className=\"flex items-center
|
|
1
|
+
{"version":3,"sources":["../../../../../../../src/core/components/viewers/map/src/MapSidebar/TabSelector.tsx"],"sourcesContent":["'use client'\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport { useTranslations } from 'next-intl'\r\nimport * as React from 'react'\r\n\r\nimport { Separator } from '../../../../ui/Separator'\r\n\r\nimport type { SidebarTabType } from '../../../../../store/Menus/reducer'\r\n\r\ninterface TabSelectorProps {\r\n activeTab: SidebarTabType\r\n onTabChangeAction: (tab: SidebarTabType) => void\r\n}\r\n\r\nexport function TabSelector({ activeTab, onTabChangeAction }: TabSelectorProps) {\r\n // Translation\r\n const t = useTranslations('TabSelector')\r\n\r\n const tabs = [\r\n { id: 'file', label: t('fileLabel') },\r\n { id: 'layers', label: t('layersTitle') },\r\n { id: 'communication', label: t('communicationTitle') },\r\n { id: 'sensors', label: t('sensorsTitle') },\r\n { id: 'settings', label: t('settingsTitle') },\r\n ]\r\n\r\n return (\r\n <div>\r\n <Separator />\r\n\r\n <div className=\"px-4 py-3\">\r\n <div className=\"flex items-center gap-1 w-full\">\r\n {tabs.map(tab => (\r\n <div\r\n key={tab.id}\r\n title={tab.label}\r\n className={`flex-1 min-w-0 truncate text-center px-2 py-2 rounded-md cursor-pointer transition-colors text-xs font-medium ${\r\n activeTab === tab.id\r\n ? 'bg-accent text-accent-foreground'\r\n : 'text-muted-foreground hover:text-foreground hover:bg-accent/50'\r\n }`}\r\n onClick={() => onTabChangeAction(tab.id as SidebarTabType)}\r\n >\r\n {tab.label}\r\n </div>\r\n ))}\r\n </div>\r\n </div>\r\n\r\n <Separator />\r\n </div>\r\n )\r\n}\r\n"],"mappings":";AA8BI,SACE,KADF;AAzBJ,SAAS,uBAAuB;AAGhC,SAAS,iBAAiB;AASnB,SAAS,YAAY,EAAE,WAAW,kBAAkB,GAAqB;AAE9E,QAAM,IAAI,gBAAgB,aAAa;AAEvC,QAAM,OAAO;AAAA,IACX,EAAE,IAAI,QAAQ,OAAO,EAAE,WAAW,EAAE;AAAA,IACpC,EAAE,IAAI,UAAU,OAAO,EAAE,aAAa,EAAE;AAAA,IACxC,EAAE,IAAI,iBAAiB,OAAO,EAAE,oBAAoB,EAAE;AAAA,IACtD,EAAE,IAAI,WAAW,OAAO,EAAE,cAAc,EAAE;AAAA,IAC1C,EAAE,IAAI,YAAY,OAAO,EAAE,eAAe,EAAE;AAAA,EAC9C;AAEA,SACE,qBAAC,SACC;AAAA,wBAAC,aAAU;AAAA,IAEX,oBAAC,SAAI,WAAU,aACb,8BAAC,SAAI,WAAU,kCACZ,eAAK,IAAI,SACR;AAAA,MAAC;AAAA;AAAA,QAEC,OAAO,IAAI;AAAA,QACX,WAAW,iHACT,cAAc,IAAI,KACd,qCACA,gEACN;AAAA,QACA,SAAS,MAAM,kBAAkB,IAAI,EAAoB;AAAA,QAExD,cAAI;AAAA;AAAA,MATA,IAAI;AAAA,IAUX,CACD,GACH,GACF;AAAA,IAEA,oBAAC,aAAU;AAAA,KACb;AAEJ;","names":[]}
|
|
@@ -31,7 +31,7 @@ function MapSidebar({ minioBaseUrl, martinBaseUrl, organization }) {
|
|
|
31
31
|
selectedTab === "file" && ability.can("read", "File") && /* @__PURE__ */ jsx(FileTab, {}),
|
|
32
32
|
selectedTab === "layers" && ability.can("read", "File") && /* @__PURE__ */ jsx(LayersTab, { martinBaseUrl, organization }),
|
|
33
33
|
selectedTab === "communication" && ability.can("read", "Comment") && /* @__PURE__ */ jsx(CommunicationTab, {}),
|
|
34
|
-
selectedTab === "sensors" && ability.can("read", "Sensor") && /* @__PURE__ */ jsx(SensorsTab, {
|
|
34
|
+
selectedTab === "sensors" && ability.can("read", "Sensor") && /* @__PURE__ */ jsx(SensorsTab, {}),
|
|
35
35
|
selectedTab === "settings" && /* @__PURE__ */ jsx(SettingsTab, { countryCode: organization == null ? void 0 : organization.country })
|
|
36
36
|
]
|
|
37
37
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../src/core/components/viewers/map/src/MapSidebar/index.tsx"],"sourcesContent":["'use client'\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport * as React from 'react'\r\n\r\n\r\nimport { InfoSidebarContainer } from '../../../../../components/ui/InfoSidebar/Container'\r\nimport { MenusContext, usePermissions } from '../../../../../store'\r\n\r\nimport { CommunicationTab } from './src/CommunicationTab'\r\nimport { FileTab } from './src/FileTab'\r\nimport { LayersTab } from './src/LayersTab'\r\nimport { SensorsTab } from './src/SensorsTab'\r\nimport { SettingsTab } from './src/SettingsTab'\r\nimport { TabSelector } from './TabSelector'\r\n\r\nimport type { SidebarTabType } from '../../../../../store/Menus/reducer'\r\nimport type { Organization } from '../../../../../types/dbTypes'\r\n\r\nexport function MapSidebar({ minioBaseUrl, martinBaseUrl, organization }: { minioBaseUrl?: string; martinBaseUrl?: string; organization?: Organization }) {\r\n // Permissions\r\n const { ability } = usePermissions()\r\n\r\n const { state: menusState, dispatch: menusDispatch } = React.useContext(MenusContext)\r\n const { selectedTab } = menusState.menus\r\n\r\n const handleTabChange = (selectedTab: SidebarTabType) => {\r\n menusDispatch({ type: 'SET_SIDEBAR_SELECTED_TAB', payload: { selectedTab } })\r\n }\r\n\r\n return (\r\n <InfoSidebarContainer\r\n organization={organization}\r\n tabSelector={\r\n <TabSelector\r\n activeTab={selectedTab}\r\n onTabChangeAction={handleTabChange}\r\n />\r\n }\r\n >\r\n {selectedTab === 'file' && ability.can('read', 'File') && <FileTab />}\r\n {selectedTab === 'layers' && ability.can('read', 'File') && <LayersTab martinBaseUrl={martinBaseUrl} organization={organization} />}\r\n {selectedTab === 'communication' && ability.can('read', 'Comment') && <CommunicationTab />}\r\n {selectedTab === 'sensors' && ability.can('read', 'Sensor') && <SensorsTab
|
|
1
|
+
{"version":3,"sources":["../../../../../../../src/core/components/viewers/map/src/MapSidebar/index.tsx"],"sourcesContent":["'use client'\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport * as React from 'react'\r\n\r\n\r\nimport { InfoSidebarContainer } from '../../../../../components/ui/InfoSidebar/Container'\r\nimport { MenusContext, usePermissions } from '../../../../../store'\r\n\r\nimport { CommunicationTab } from './src/CommunicationTab'\r\nimport { FileTab } from './src/FileTab'\r\nimport { LayersTab } from './src/LayersTab'\r\nimport { SensorsTab } from './src/SensorsTab'\r\nimport { SettingsTab } from './src/SettingsTab'\r\nimport { TabSelector } from './TabSelector'\r\n\r\nimport type { SidebarTabType } from '../../../../../store/Menus/reducer'\r\nimport type { Organization } from '../../../../../types/dbTypes'\r\n\r\nexport function MapSidebar({ minioBaseUrl, martinBaseUrl, organization }: { minioBaseUrl?: string; martinBaseUrl?: string; organization?: Organization }) {\r\n // Permissions\r\n const { ability } = usePermissions()\r\n\r\n const { state: menusState, dispatch: menusDispatch } = React.useContext(MenusContext)\r\n const { selectedTab } = menusState.menus\r\n\r\n const handleTabChange = (selectedTab: SidebarTabType) => {\r\n menusDispatch({ type: 'SET_SIDEBAR_SELECTED_TAB', payload: { selectedTab } })\r\n }\r\n\r\n return (\r\n <InfoSidebarContainer\r\n organization={organization}\r\n tabSelector={\r\n <TabSelector\r\n activeTab={selectedTab}\r\n onTabChangeAction={handleTabChange}\r\n />\r\n }\r\n >\r\n {selectedTab === 'file' && ability.can('read', 'File') && <FileTab />}\r\n {selectedTab === 'layers' && ability.can('read', 'File') && <LayersTab martinBaseUrl={martinBaseUrl} organization={organization} />}\r\n {selectedTab === 'communication' && ability.can('read', 'Comment') && <CommunicationTab />}\r\n {selectedTab === 'sensors' && ability.can('read', 'Sensor') && <SensorsTab />}\r\n {selectedTab === 'settings' && <SettingsTab countryCode={organization?.country} />}\r\n </InfoSidebarContainer>\r\n )\r\n}"],"mappings":";AAiCI,SAGI,KAHJ;AA5BJ,YAAY,WAAW;AAGvB,SAAS,4BAA4B;AACrC,SAAS,cAAc,sBAAsB;AAE7C,SAAS,wBAAwB;AACjC,SAAS,eAAe;AACxB,SAAS,iBAAiB;AAC1B,SAAS,kBAAkB;AAC3B,SAAS,mBAAmB;AAC5B,SAAS,mBAAmB;AAKrB,SAAS,WAAW,EAAE,cAAc,eAAe,aAAa,GAAmF;AAExJ,QAAM,EAAE,QAAQ,IAAI,eAAe;AAEnC,QAAM,EAAE,OAAO,YAAY,UAAU,cAAc,IAAI,MAAM,WAAW,YAAY;AACpF,QAAM,EAAE,YAAY,IAAI,WAAW;AAEnC,QAAM,kBAAkB,CAACA,iBAAgC;AACvD,kBAAc,EAAE,MAAM,4BAA4B,SAAS,EAAE,aAAAA,aAAY,EAAE,CAAC;AAAA,EAC9E;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,aACE;AAAA,QAAC;AAAA;AAAA,UACC,WAAW;AAAA,UACX,mBAAmB;AAAA;AAAA,MACrB;AAAA,MAGD;AAAA,wBAAgB,UAAU,QAAQ,IAAI,QAAQ,MAAM,KAAK,oBAAC,WAAQ;AAAA,QAClE,gBAAgB,YAAY,QAAQ,IAAI,QAAQ,MAAM,KAAK,oBAAC,aAAU,eAA8B,cAA4B;AAAA,QAChI,gBAAgB,mBAAmB,QAAQ,IAAI,QAAQ,SAAS,KAAK,oBAAC,oBAAiB;AAAA,QACvF,gBAAgB,aAAa,QAAQ,IAAI,QAAQ,QAAQ,KAAK,oBAAC,cAAW;AAAA,QAC1E,gBAAgB,cAAc,oBAAC,eAAY,aAAa,6CAAc,SAAS;AAAA;AAAA;AAAA,EAClF;AAEJ;","names":["selectedTab"]}
|
package/dist/core/components/viewers/map/src/MapSidebar/src/FileTab/src/FilesSection.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FilesSection.d.ts","sourceRoot":"","sources":["../../../../../../../../../../src/core/components/viewers/map/src/MapSidebar/src/FileTab/src/FilesSection.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAU9B,OAAO,KAAK,EAAE,MAAM,IAAI,KAAK,EAAE,MAAM,uCAAuC,CAAA;AAmB5E,UAAU,iBAAiB;IACzB,KAAK,EAAE,KAAK,EAAE,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,wBAAgB,YAAY,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,KAAU,EAAE,EAAE,iBAAiB,
|
|
1
|
+
{"version":3,"file":"FilesSection.d.ts","sourceRoot":"","sources":["../../../../../../../../../../src/core/components/viewers/map/src/MapSidebar/src/FileTab/src/FilesSection.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAU9B,OAAO,KAAK,EAAE,MAAM,IAAI,KAAK,EAAE,MAAM,uCAAuC,CAAA;AAmB5E,UAAU,iBAAiB;IACzB,KAAK,EAAE,KAAK,EAAE,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,wBAAgB,YAAY,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,KAAU,EAAE,EAAE,iBAAiB,qBA4IhF"}
|
|
@@ -59,7 +59,6 @@ function FilesSection({ files: _filesProp, query = "" }) {
|
|
|
59
59
|
const { handleAddFile, uploadState } = useFileUploadWithProgress({
|
|
60
60
|
acceptedFileTypes: "*",
|
|
61
61
|
onUploadSuccess: () => {
|
|
62
|
-
console.log("File uploaded successfully");
|
|
63
62
|
mutate(`/api/files`);
|
|
64
63
|
},
|
|
65
64
|
onUploadError: (error) => console.error("Error uploading file:", error)
|