@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.
Files changed (300) hide show
  1. package/dist/core/components/authentication/ForgotPassword.d.ts.map +1 -1
  2. package/dist/core/components/authentication/ForgotPassword.js +0 -1
  3. package/dist/core/components/authentication/ForgotPassword.js.map +1 -1
  4. package/dist/core/components/ui/Comments/CollapsibleCommentItem.d.ts +12 -2
  5. package/dist/core/components/ui/Comments/CollapsibleCommentItem.d.ts.map +1 -1
  6. package/dist/core/components/ui/Comments/CollapsibleCommentItem.js +137 -139
  7. package/dist/core/components/ui/Comments/CollapsibleCommentItem.js.map +1 -1
  8. package/dist/core/components/ui/Comments/Comment.d.ts +18 -1
  9. package/dist/core/components/ui/Comments/Comment.d.ts.map +1 -1
  10. package/dist/core/components/ui/Comments/Comment.js +94 -23
  11. package/dist/core/components/ui/Comments/Comment.js.map +1 -1
  12. package/dist/core/components/ui/Comments/CommentActionButtons.d.ts +38 -0
  13. package/dist/core/components/ui/Comments/CommentActionButtons.d.ts.map +1 -0
  14. package/dist/core/components/ui/Comments/CommentActionButtons.js +134 -0
  15. package/dist/core/components/ui/Comments/CommentActionButtons.js.map +1 -0
  16. package/dist/core/components/ui/Comments/CommentInput.d.ts.map +1 -1
  17. package/dist/core/components/ui/Comments/CommentInput.js +5 -2
  18. package/dist/core/components/ui/Comments/CommentInput.js.map +1 -1
  19. package/dist/core/components/ui/Comments/CommentsSection.d.ts.map +1 -1
  20. package/dist/core/components/ui/Comments/CommentsSection.js +86 -42
  21. package/dist/core/components/ui/Comments/CommentsSection.js.map +1 -1
  22. package/dist/core/components/ui/Comments/commentUtils.d.ts.map +1 -1
  23. package/dist/core/components/ui/Comments/commentUtils.js +2 -1
  24. package/dist/core/components/ui/Comments/commentUtils.js.map +1 -1
  25. package/dist/core/components/ui/FilesManager/src/useFileUploadWithProgress.d.ts.map +1 -1
  26. package/dist/core/components/ui/FilesManager/src/useFileUploadWithProgress.js +0 -1
  27. package/dist/core/components/ui/FilesManager/src/useFileUploadWithProgress.js.map +1 -1
  28. package/dist/core/components/ui/InfoSidebar/Container.d.ts.map +1 -1
  29. package/dist/core/components/ui/InfoSidebar/Container.js +16 -12
  30. package/dist/core/components/ui/InfoSidebar/Container.js.map +1 -1
  31. package/dist/core/components/ui/Sensors/CollapsibleSensorItem.d.ts +1 -2
  32. package/dist/core/components/ui/Sensors/CollapsibleSensorItem.d.ts.map +1 -1
  33. package/dist/core/components/ui/Sensors/CollapsibleSensorItem.js +66 -94
  34. package/dist/core/components/ui/Sensors/CollapsibleSensorItem.js.map +1 -1
  35. package/dist/core/components/ui/Sensors/Sensor.d.ts +16 -3
  36. package/dist/core/components/ui/Sensors/Sensor.d.ts.map +1 -1
  37. package/dist/core/components/ui/Sensors/Sensor.js +76 -37
  38. package/dist/core/components/ui/Sensors/Sensor.js.map +1 -1
  39. package/dist/core/components/ui/Sensors/SensorChart.d.ts +12 -2
  40. package/dist/core/components/ui/Sensors/SensorChart.d.ts.map +1 -1
  41. package/dist/core/components/ui/Sensors/SensorChart.js +49 -12
  42. package/dist/core/components/ui/Sensors/SensorChart.js.map +1 -1
  43. package/dist/core/components/ui/Sensors/SensorDetailDialog.d.ts +9 -0
  44. package/dist/core/components/ui/Sensors/SensorDetailDialog.d.ts.map +1 -0
  45. package/dist/core/components/ui/Sensors/SensorDetailDialog.js +128 -0
  46. package/dist/core/components/ui/Sensors/SensorDetailDialog.js.map +1 -0
  47. package/dist/core/components/ui/Sensors/SensorInput.d.ts +6 -1
  48. package/dist/core/components/ui/Sensors/SensorInput.d.ts.map +1 -1
  49. package/dist/core/components/ui/Sensors/SensorInput.js +94 -9
  50. package/dist/core/components/ui/Sensors/SensorInput.js.map +1 -1
  51. package/dist/core/components/ui/Sensors/SensorTagsSection.d.ts +3 -1
  52. package/dist/core/components/ui/Sensors/SensorTagsSection.d.ts.map +1 -1
  53. package/dist/core/components/ui/Sensors/SensorTagsSection.js +4 -1
  54. package/dist/core/components/ui/Sensors/SensorTagsSection.js.map +1 -1
  55. package/dist/core/components/ui/Sensors/SensorsSection.d.ts +1 -3
  56. package/dist/core/components/ui/Sensors/SensorsSection.d.ts.map +1 -1
  57. package/dist/core/components/ui/Sensors/SensorsSection.js +33 -10
  58. package/dist/core/components/ui/Sensors/SensorsSection.js.map +1 -1
  59. package/dist/core/components/ui/Sensors/TimeZoneSelect.d.ts +7 -0
  60. package/dist/core/components/ui/Sensors/TimeZoneSelect.d.ts.map +1 -0
  61. package/dist/core/components/ui/Sensors/TimeZoneSelect.js +56 -0
  62. package/dist/core/components/ui/Sensors/TimeZoneSelect.js.map +1 -0
  63. package/dist/core/components/ui/Sensors/sensorData.d.ts +42 -0
  64. package/dist/core/components/ui/Sensors/sensorData.d.ts.map +1 -0
  65. package/dist/core/components/ui/Sensors/sensorData.js +118 -0
  66. package/dist/core/components/ui/Sensors/sensorData.js.map +1 -0
  67. package/dist/core/components/ui/Sensors/sensorRange.d.ts +24 -0
  68. package/dist/core/components/ui/Sensors/sensorRange.d.ts.map +1 -0
  69. package/dist/core/components/ui/Sensors/sensorRange.js +34 -0
  70. package/dist/core/components/ui/Sensors/sensorRange.js.map +1 -0
  71. package/dist/core/components/ui/Sensors/useSensorSeries.d.ts +15 -0
  72. package/dist/core/components/ui/Sensors/useSensorSeries.d.ts.map +1 -0
  73. package/dist/core/components/ui/Sensors/useSensorSeries.js +63 -0
  74. package/dist/core/components/ui/Sensors/useSensorSeries.js.map +1 -0
  75. package/dist/core/components/ui/Sidebar.d.ts.map +1 -1
  76. package/dist/core/components/ui/Sidebar.js +27 -4
  77. package/dist/core/components/ui/Sidebar.js.map +1 -1
  78. package/dist/core/components/ui/ToolbarButton.d.ts.map +1 -1
  79. package/dist/core/components/ui/ToolbarButton.js +0 -1
  80. package/dist/core/components/ui/ToolbarButton.js.map +1 -1
  81. package/dist/core/components/viewers/Data/advancedFilter/FiltersDialog.d.ts.map +1 -1
  82. package/dist/core/components/viewers/Data/advancedFilter/FiltersDialog.js +0 -2
  83. package/dist/core/components/viewers/Data/advancedFilter/FiltersDialog.js.map +1 -1
  84. package/dist/core/components/viewers/Data/details/EnergySourcesGrid.d.ts.map +1 -1
  85. package/dist/core/components/viewers/Data/details/EnergySourcesGrid.js +0 -10
  86. package/dist/core/components/viewers/Data/details/EnergySourcesGrid.js.map +1 -1
  87. package/dist/core/components/viewers/Data/files/DatatableFileAdder.d.ts.map +1 -1
  88. package/dist/core/components/viewers/Data/files/DatatableFileAdder.js +0 -1
  89. package/dist/core/components/viewers/Data/files/DatatableFileAdder.js.map +1 -1
  90. package/dist/core/components/viewers/Data/files/utils/fuzzySearch.d.ts.map +1 -1
  91. package/dist/core/components/viewers/Data/files/utils/fuzzySearch.js +0 -1
  92. package/dist/core/components/viewers/Data/files/utils/fuzzySearch.js.map +1 -1
  93. package/dist/core/components/viewers/Data/userDetails/UserMoreOptions.d.ts.map +1 -1
  94. package/dist/core/components/viewers/Data/userDetails/UserMoreOptions.js +0 -1
  95. package/dist/core/components/viewers/Data/userDetails/UserMoreOptions.js.map +1 -1
  96. package/dist/core/components/viewers/Data/utils/filterHelper.d.ts.map +1 -1
  97. package/dist/core/components/viewers/Data/utils/filterHelper.js +0 -2
  98. package/dist/core/components/viewers/Data/utils/filterHelper.js.map +1 -1
  99. package/dist/core/components/viewers/Viewer.js +1 -1
  100. package/dist/core/components/viewers/Viewer.js.map +1 -1
  101. package/dist/core/components/viewers/bim/src/BimSidebar/src/CommunicationTab/src/TopicsSection.d.ts.map +1 -1
  102. package/dist/core/components/viewers/bim/src/BimSidebar/src/CommunicationTab/src/TopicsSection.js +0 -3
  103. package/dist/core/components/viewers/bim/src/BimSidebar/src/CommunicationTab/src/TopicsSection.js.map +1 -1
  104. package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/FilesSection.d.ts.map +1 -1
  105. package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/FilesSection.js +1 -7
  106. package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/FilesSection.js.map +1 -1
  107. package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/ModelsSection.d.ts.map +1 -1
  108. package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/ModelsSection.js +1 -9
  109. package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/ModelsSection.js.map +1 -1
  110. package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.d.ts +1 -3
  111. package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.d.ts.map +1 -1
  112. package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.js +2 -2
  113. package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.js.map +1 -1
  114. package/dist/core/components/viewers/bim/src/BimSidebar/src/TabSelector.d.ts.map +1 -1
  115. package/dist/core/components/viewers/bim/src/BimSidebar/src/TabSelector.js +4 -3
  116. package/dist/core/components/viewers/bim/src/BimSidebar/src/TabSelector.js.map +1 -1
  117. package/dist/core/components/viewers/bim/src/BimSidebar/src/index.js +1 -1
  118. package/dist/core/components/viewers/bim/src/BimSidebar/src/index.js.map +1 -1
  119. package/dist/core/components/viewers/bim/src/ClippingPlane.d.ts.map +1 -1
  120. package/dist/core/components/viewers/bim/src/ClippingPlane.js +0 -1
  121. package/dist/core/components/viewers/bim/src/ClippingPlane.js.map +1 -1
  122. package/dist/core/components/viewers/bim/src/tools/AddToBim/index.d.ts +1 -2
  123. package/dist/core/components/viewers/bim/src/tools/AddToBim/index.d.ts.map +1 -1
  124. package/dist/core/components/viewers/bim/src/tools/AddToBim/index.js +25 -2
  125. package/dist/core/components/viewers/bim/src/tools/AddToBim/index.js.map +1 -1
  126. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimComment.d.ts +16 -10
  127. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimComment.d.ts.map +1 -1
  128. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimComment.js +26 -6
  129. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimComment.js.map +1 -1
  130. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.d.ts +2 -0
  131. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.d.ts.map +1 -0
  132. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.js +5 -0
  133. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.js.map +1 -0
  134. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.d.ts +8 -1
  135. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.d.ts.map +1 -1
  136. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.js +15 -3
  137. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.js.map +1 -1
  138. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/clusterMarkersByScreenSpace.d.ts +44 -0
  139. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/clusterMarkersByScreenSpace.d.ts.map +1 -0
  140. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/clusterMarkersByScreenSpace.js +77 -0
  141. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/clusterMarkersByScreenSpace.js.map +1 -0
  142. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.d.ts +3 -0
  143. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.d.ts.map +1 -0
  144. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.js +5 -0
  145. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.js.map +1 -0
  146. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentMarkerConfig.d.ts +33 -0
  147. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentMarkerConfig.d.ts.map +1 -0
  148. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentMarkerConfig.js +90 -0
  149. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentMarkerConfig.js.map +1 -0
  150. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/renderCSS2DMarkers.d.ts.map +1 -1
  151. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/renderCSS2DMarkers.js +2 -2
  152. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/renderCSS2DMarkers.js.map +1 -1
  153. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.d.ts +7 -1
  154. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.d.ts.map +1 -1
  155. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.js +50 -73
  156. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.js.map +1 -1
  157. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.d.ts +7 -1
  158. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.d.ts.map +1 -1
  159. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.js +39 -11
  160. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.js.map +1 -1
  161. package/dist/core/components/viewers/bim/src/tools/ClippingTool/ClippingTool.d.ts.map +1 -1
  162. package/dist/core/components/viewers/bim/src/tools/ClippingTool/ClippingTool.js +1 -5
  163. package/dist/core/components/viewers/bim/src/tools/ClippingTool/ClippingTool.js.map +1 -1
  164. package/dist/core/components/viewers/bim/src/tools/ExplodeByLevelTool.d.ts.map +1 -1
  165. package/dist/core/components/viewers/bim/src/tools/ExplodeByLevelTool.js +0 -1
  166. package/dist/core/components/viewers/bim/src/tools/ExplodeByLevelTool.js.map +1 -1
  167. package/dist/core/components/viewers/bim/src/tools/measureBimTool.d.ts.map +1 -1
  168. package/dist/core/components/viewers/bim/src/tools/measureBimTool.js +1 -2
  169. package/dist/core/components/viewers/bim/src/tools/measureBimTool.js.map +1 -1
  170. package/dist/core/components/viewers/map/MapViewer.d.ts +1 -2
  171. package/dist/core/components/viewers/map/MapViewer.d.ts.map +1 -1
  172. package/dist/core/components/viewers/map/MapViewer.js +2 -2
  173. package/dist/core/components/viewers/map/MapViewer.js.map +1 -1
  174. package/dist/core/components/viewers/map/datasets/src/arcGISDatasets.d.ts.map +1 -1
  175. package/dist/core/components/viewers/map/datasets/src/arcGISDatasets.js +11 -14
  176. package/dist/core/components/viewers/map/datasets/src/arcGISDatasets.js.map +1 -1
  177. package/dist/core/components/viewers/map/datasets/src/ckanDatasets.d.ts.map +1 -1
  178. package/dist/core/components/viewers/map/datasets/src/ckanDatasets.js +2 -5
  179. package/dist/core/components/viewers/map/datasets/src/ckanDatasets.js.map +1 -1
  180. package/dist/core/components/viewers/map/datasets/src/localDatasets.d.ts.map +1 -1
  181. package/dist/core/components/viewers/map/datasets/src/localDatasets.js +0 -3
  182. package/dist/core/components/viewers/map/datasets/src/localDatasets.js.map +1 -1
  183. package/dist/core/components/viewers/map/datasets/src/opendatasoftDatasets.d.ts.map +1 -1
  184. package/dist/core/components/viewers/map/datasets/src/opendatasoftDatasets.js +0 -11
  185. package/dist/core/components/viewers/map/datasets/src/opendatasoftDatasets.js.map +1 -1
  186. package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/DatabaseBuildingPopover/src/BuildingsTools.d.ts.map +1 -1
  187. package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/DatabaseBuildingPopover/src/BuildingsTools.js +0 -1
  188. package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/DatabaseBuildingPopover/src/BuildingsTools.js.map +1 -1
  189. package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/NonDatabaseBuildingPopover.d.ts.map +1 -1
  190. package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/NonDatabaseBuildingPopover.js +0 -1
  191. package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/NonDatabaseBuildingPopover.js.map +1 -1
  192. package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/OpenDataFeaturePopover.d.ts.map +1 -1
  193. package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/OpenDataFeaturePopover.js +0 -1
  194. package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/OpenDataFeaturePopover.js.map +1 -1
  195. package/dist/core/components/viewers/map/src/MapLayers/index.d.ts +1 -2
  196. package/dist/core/components/viewers/map/src/MapLayers/index.d.ts.map +1 -1
  197. package/dist/core/components/viewers/map/src/MapLayers/index.js +2 -2
  198. package/dist/core/components/viewers/map/src/MapLayers/index.js.map +1 -1
  199. package/dist/core/components/viewers/map/src/MapLayers/src/CommentLayer/index.d.ts.map +1 -1
  200. package/dist/core/components/viewers/map/src/MapLayers/src/CommentLayer/index.js +174 -53
  201. package/dist/core/components/viewers/map/src/MapLayers/src/CommentLayer/index.js.map +1 -1
  202. package/dist/core/components/viewers/map/src/MapLayers/src/FileLayer/components/MapFileManager.d.ts.map +1 -1
  203. package/dist/core/components/viewers/map/src/MapLayers/src/FileLayer/components/MapFileManager.js +2 -1
  204. package/dist/core/components/viewers/map/src/MapLayers/src/FileLayer/components/MapFileManager.js.map +1 -1
  205. package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.d.ts +1 -3
  206. package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.d.ts.map +1 -1
  207. package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.js +74 -42
  208. package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.js.map +1 -1
  209. package/dist/core/components/viewers/map/src/MapSidebar/TabSelector.d.ts.map +1 -1
  210. package/dist/core/components/viewers/map/src/MapSidebar/TabSelector.js +4 -3
  211. package/dist/core/components/viewers/map/src/MapSidebar/TabSelector.js.map +1 -1
  212. package/dist/core/components/viewers/map/src/MapSidebar/index.js +1 -1
  213. package/dist/core/components/viewers/map/src/MapSidebar/index.js.map +1 -1
  214. package/dist/core/components/viewers/map/src/MapSidebar/src/FileTab/src/FilesSection.d.ts.map +1 -1
  215. package/dist/core/components/viewers/map/src/MapSidebar/src/FileTab/src/FilesSection.js +0 -1
  216. package/dist/core/components/viewers/map/src/MapSidebar/src/FileTab/src/FilesSection.js.map +1 -1
  217. package/dist/core/components/viewers/map/src/MapSidebar/src/FileTab/src/ModelsSection.d.ts.map +1 -1
  218. package/dist/core/components/viewers/map/src/MapSidebar/src/FileTab/src/ModelsSection.js +1 -5
  219. package/dist/core/components/viewers/map/src/MapSidebar/src/FileTab/src/ModelsSection.js.map +1 -1
  220. package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.d.ts +1 -3
  221. package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.d.ts.map +1 -1
  222. package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.js +2 -2
  223. package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.js.map +1 -1
  224. package/dist/core/components/viewers/map/src/tools/AddTools/AddFile/utils/uploadToPresignedURLS.d.ts.map +1 -1
  225. package/dist/core/components/viewers/map/src/tools/AddTools/AddFile/utils/uploadToPresignedURLS.js +1 -3
  226. package/dist/core/components/viewers/map/src/tools/AddTools/AddFile/utils/uploadToPresignedURLS.js.map +1 -1
  227. package/dist/core/components/viewers/map/utils/MapEventManager/MapClickManager.d.ts.map +1 -1
  228. package/dist/core/components/viewers/map/utils/MapEventManager/MapClickManager.js +0 -1
  229. package/dist/core/components/viewers/map/utils/MapEventManager/MapClickManager.js.map +1 -1
  230. package/dist/core/components/viewers/map/utils/flyToEach.d.ts.map +1 -1
  231. package/dist/core/components/viewers/map/utils/flyToEach.js +0 -1
  232. package/dist/core/components/viewers/map/utils/flyToEach.js.map +1 -1
  233. package/dist/core/components/viewers/map/utils/getOsmIdbyCoordinates.d.ts.map +1 -1
  234. package/dist/core/components/viewers/map/utils/getOsmIdbyCoordinates.js +0 -2
  235. package/dist/core/components/viewers/map/utils/getOsmIdbyCoordinates.js.map +1 -1
  236. package/dist/core/components/viewers/shared/markers/BimMarkerCluster.d.ts +19 -0
  237. package/dist/core/components/viewers/shared/markers/BimMarkerCluster.d.ts.map +1 -0
  238. package/dist/core/components/viewers/shared/markers/BimMarkerCluster.js +108 -0
  239. package/dist/core/components/viewers/shared/markers/BimMarkerCluster.js.map +1 -0
  240. package/dist/core/components/viewers/shared/markers/computeMarkerLookAt.d.ts +24 -0
  241. package/dist/core/components/viewers/shared/markers/computeMarkerLookAt.d.ts.map +1 -0
  242. package/dist/core/components/viewers/shared/markers/computeMarkerLookAt.js +27 -0
  243. package/dist/core/components/viewers/shared/markers/computeMarkerLookAt.js.map +1 -0
  244. package/dist/core/components/viewers/shared/markers/types.d.ts +66 -0
  245. package/dist/core/components/viewers/shared/markers/types.d.ts.map +1 -0
  246. package/dist/core/components/viewers/shared/markers/types.js +1 -0
  247. package/dist/core/components/viewers/shared/markers/types.js.map +1 -0
  248. package/dist/core/components/viewers/shared/markers/useMarkerLayerBim.d.ts +22 -0
  249. package/dist/core/components/viewers/shared/markers/useMarkerLayerBim.d.ts.map +1 -0
  250. package/dist/core/components/viewers/shared/markers/useMarkerLayerBim.js +233 -0
  251. package/dist/core/components/viewers/shared/markers/useMarkerLayerBim.js.map +1 -0
  252. package/dist/core/hooks/comments/comments.d.ts +11 -0
  253. package/dist/core/hooks/comments/comments.d.ts.map +1 -1
  254. package/dist/core/hooks/comments/comments.js +3 -1
  255. package/dist/core/hooks/comments/comments.js.map +1 -1
  256. package/dist/core/hooks/comments/createCommentHooks.d.ts +11 -0
  257. package/dist/core/hooks/comments/createCommentHooks.d.ts.map +1 -1
  258. package/dist/core/hooks/comments/createCommentHooks.js +30 -1
  259. package/dist/core/hooks/comments/createCommentHooks.js.map +1 -1
  260. package/dist/core/hooks/files/files.d.ts.map +1 -1
  261. package/dist/core/hooks/files/files.js +0 -1
  262. package/dist/core/hooks/files/files.js.map +1 -1
  263. package/dist/core/hooks/ui/useResizableSidebarWidth.d.ts +30 -0
  264. package/dist/core/hooks/ui/useResizableSidebarWidth.d.ts.map +1 -0
  265. package/dist/core/hooks/ui/useResizableSidebarWidth.js +75 -0
  266. package/dist/core/hooks/ui/useResizableSidebarWidth.js.map +1 -0
  267. package/dist/core/i18n/messages/en.json +10 -1
  268. package/dist/core/i18n/messages/es.json +11 -2
  269. package/dist/core/i18n/messages/fr.json +11 -2
  270. package/dist/core/store/AppConfig/context.d.ts.map +1 -1
  271. package/dist/core/store/AppConfig/context.js +4 -1
  272. package/dist/core/store/AppConfig/context.js.map +1 -1
  273. package/dist/core/store/AppConfig/reducer.d.ts +10 -0
  274. package/dist/core/store/AppConfig/reducer.d.ts.map +1 -1
  275. package/dist/core/store/AppConfig/reducer.js +10 -0
  276. package/dist/core/store/AppConfig/reducer.js.map +1 -1
  277. package/dist/core/store/Datasets/reducer.d.ts.map +1 -1
  278. package/dist/core/store/Datasets/reducer.js +0 -2
  279. package/dist/core/store/Datasets/reducer.js.map +1 -1
  280. package/dist/core/store/Files/reducer.js +0 -1
  281. package/dist/core/store/Files/reducer.js.map +1 -1
  282. package/dist/core/store/Menus/context.d.ts.map +1 -1
  283. package/dist/core/store/Menus/context.js +8 -2
  284. package/dist/core/store/Menus/context.js.map +1 -1
  285. package/dist/core/store/Menus/reducer.d.ts +65 -1
  286. package/dist/core/store/Menus/reducer.d.ts.map +1 -1
  287. package/dist/core/store/Menus/reducer.js +39 -3
  288. package/dist/core/store/Menus/reducer.js.map +1 -1
  289. package/dist/core/utils/markerUtils.d.ts +10 -0
  290. package/dist/core/utils/markerUtils.d.ts.map +1 -1
  291. package/dist/core/utils/markerUtils.js +9 -1
  292. package/dist/core/utils/markerUtils.js.map +1 -1
  293. package/dist/core/utils/memcache.d.ts.map +1 -1
  294. package/dist/core/utils/memcache.js +0 -7
  295. package/dist/core/utils/memcache.js.map +1 -1
  296. package/dist/core/utils/timeUtils.d.ts +24 -1
  297. package/dist/core/utils/timeUtils.d.ts.map +1 -1
  298. package/dist/core/utils/timeUtils.js +52 -3
  299. package/dist/core/utils/timeUtils.js.map +1 -1
  300. package/package.json +4 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../../src/core/components/viewers/map/src/MapFeaturePopoverMenu/src/NonDatabaseBuildingPopover.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\n// Dependencies\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 { toast } from 'sonner'\r\n\r\nimport { useCreateBuilding, useBuildings, useBuilding } from '../../../../../../hooks/buildings/buildings'\r\nimport { usePermissions } from '../../../../../../store'\r\n\r\n// Shadcn Components\r\nimport { MapContext } from '../../../../../../store'\r\nimport { useMenusContext } from '../../../../../../store'\r\nimport { Button, Label } from '../../../../../ui/'\r\nimport { LoadingSpinner } from '../../../../../ui/LoadingSpinner'\r\nimport { PopoverContent } from '../../../../../ui/Popover'\r\nimport {\r\n Select,\r\n SelectContent,\r\n SelectItem,\r\n SelectTrigger,\r\n SelectValue,\r\n} from '../../../../../ui/Select'\r\n\r\n// Icons\r\n\r\n// Types / Utilities\r\nimport { getDetailedAddress } from '../../../utils/geocoder'\r\nimport { MarkerManager } from '../../../utils/MarkerManager'\r\n\r\nimport type { Building } from '../../../../../../types/dbTypes'\r\nimport type { MapGeoJSONFeature } from 'maplibre-gl'\r\n\r\n\r\n\r\n// Hooks\r\n\r\ninterface NonDatabaseBuildingPopoverProps {\r\n feature: MapGeoJSONFeature\r\n isOpen: boolean\r\n onCloseAction: () => void\r\n}\r\n\r\nexport default function NonDatabaseBuildingPopover({\r\n feature,\r\n onCloseAction,\r\n}: NonDatabaseBuildingPopoverProps) {\r\n // Translation\r\n const t = useTranslations('NonDatabaseBuildingPopover')\r\n // Permissions\r\n const { ability } = usePermissions()\r\n\r\n const { data: session } = useSession()\r\n const user = session?.user\r\n\r\n const { state: mapState } = React.useContext(MapContext)\r\n const { currentLocation, map, organizationCountry } = mapState.map\r\n\r\n const { setSelectedItem } = useMenusContext()\r\n\r\n // Use the create building hook\r\n const { createBuilding, isMutating, createError, createdData } = useCreateBuilding()\r\n\r\n // Get all buildings for searching\r\n const { buildings } = useBuildings()\r\n\r\n const featureProps = feature?.properties || {}\r\n const featureid = feature?.id\r\n const [name, setName] = React.useState('')\r\n const [address, setAddress] = React.useState('')\r\n const [matchingBuildings, setMatchingBuildings] = React.useState<Building[]>([])\r\n const [selectedMatchingBuildingId, setSelectedMatchingBuildingId] = React.useState<string>('')\r\n const [showExistingBuildings, setShowExistingBuildings] = React.useState(false)\r\n const [isAttaching, setIsAttaching] = React.useState(false)\r\n\r\n const { updateBuilding, isMutating: isUpdating } = useBuilding(selectedMatchingBuildingId ? Number(selectedMatchingBuildingId) : null)\r\n\r\n const normalizeAddress = React.useCallback((value: string) => {\r\n return value\r\n .toLowerCase()\r\n .replace(/\\./g, '')\r\n .replace(/,/g, ' ')\r\n .replace(/\\bdr\\b/g, 'drive')\r\n .replace(/\\brd\\b/g, 'road')\r\n .replace(/\\bst\\b/g, 'street')\r\n .replace(/\\bave\\b/g, 'avenue')\r\n .replace(/\\s+/g, ' ')\r\n .trim()\r\n }, [])\r\n\r\n const clearExistingBuildingMatches = React.useCallback(() => {\r\n setMatchingBuildings([])\r\n setSelectedMatchingBuildingId('')\r\n setShowExistingBuildings(false)\r\n\r\n if (didSetSelectedItemRef.current && setSelectedItem) {\r\n setSelectedItem(null)\r\n didSetSelectedItemRef.current = false\r\n }\r\n }, [setSelectedItem])\r\n\r\n // Track if we set the selected item in this component\r\n const didSetSelectedItemRef = React.useRef(false)\r\n\r\n // Cleanup on unmount\r\n React.useEffect(() => {\r\n return () => {\r\n // Clear selected item if we set it\r\n if (didSetSelectedItemRef.current && setSelectedItem) {\r\n setSelectedItem(null)\r\n didSetSelectedItemRef.current = false\r\n }\r\n }\r\n }, [setSelectedItem])\r\n\r\n // Get address and search for matching buildings\r\n React.useEffect(() => {\r\n const getAddress = async (longitude: number, latitude: number) => {\r\n try {\r\n // Reverse geocode through the shared provider (Geocode Earth / self-hosted\r\n // Pelias, falling back to public OSM) so this stays in sync with the geocoder.\r\n const result = await getDetailedAddress([longitude, latitude], organizationCountry)\r\n const geocodedAddress = result?.properties?.name as string | undefined\r\n\r\n if (!geocodedAddress) {\r\n clearExistingBuildingMatches()\r\n return\r\n }\r\n\r\n setAddress(geocodedAddress)\r\n\r\n // Search for existing buildings with this address\r\n if (buildings && buildings.length > 0) {\r\n const normalizedGeocodedAddress = normalizeAddress(geocodedAddress)\r\n\r\n const matches = buildings.filter((building) => {\r\n if (!building.buildingAddress) return false\r\n\r\n const normalizedBuildingAddress = normalizeAddress(building.buildingAddress)\r\n return (\r\n normalizedBuildingAddress === normalizedGeocodedAddress ||\r\n normalizedBuildingAddress.includes(normalizedGeocodedAddress) ||\r\n normalizedGeocodedAddress.includes(normalizedBuildingAddress)\r\n )\r\n })\r\n\r\n if (matches.length > 0) {\r\n setMatchingBuildings(matches)\r\n setSelectedMatchingBuildingId(matches[0].id.toString())\r\n setShowExistingBuildings(true)\r\n\r\n // Set the first match as the selected item for the update hook\r\n if (setSelectedItem) {\r\n setSelectedItem(matches[0])\r\n didSetSelectedItemRef.current = true\r\n }\r\n } else {\r\n clearExistingBuildingMatches()\r\n }\r\n } else {\r\n clearExistingBuildingMatches()\r\n }\r\n }\r\n catch (error) {\r\n console.error('Error fetching municipality and countrySubdivision:', error)\r\n }\r\n }\r\n\r\n if (feature.properties?.coordinates) {\r\n const longitude = feature.properties.coordinates[0]\r\n const latitude = feature.properties.coordinates[1]\r\n getAddress(longitude, latitude)\r\n }\r\n }, [feature, buildings, setSelectedItem, normalizeAddress, clearExistingBuildingMatches])\r\n\r\n React.useEffect(() => {\r\n if (!setSelectedItem || !selectedMatchingBuildingId) return\r\n\r\n const selectedBuilding = matchingBuildings.find(\r\n building => building.id.toString() === selectedMatchingBuildingId,\r\n )\r\n\r\n if (!selectedBuilding) return\r\n\r\n setSelectedItem(selectedBuilding)\r\n didSetSelectedItemRef.current = true\r\n }, [matchingBuildings, selectedMatchingBuildingId, setSelectedItem])\r\n\r\n // Fallbacks from feature or map state\r\n const buildingCountrySubdivision = featureProps.de_ao_prov || currentLocation.countrySubdivision || null\r\n const buildingMunicipality = featureProps.de_ao_city || currentLocation.municipality || null\r\n const buildingPostalCode = featureProps.de_ao_post || currentLocation.postalCode || null\r\n const site = currentLocation.site || null\r\n const buildingOsmId = featureid.toString()\r\n\r\n // Create MarkerManager instance\r\n const markerManagerRef = React.useRef<MarkerManager | null>(null)\r\n\r\n // Initialize MarkerManager\r\n React.useEffect(() => {\r\n if (!markerManagerRef.current) {\r\n markerManagerRef.current = new MarkerManager()\r\n }\r\n\r\n // Cleanup on unmount\r\n return () => {\r\n if (markerManagerRef.current) {\r\n markerManagerRef.current.destroy()\r\n markerManagerRef.current = null\r\n }\r\n }\r\n }, [])\r\n\r\n // Get coordinates data from feature\r\n const [coordinates, setCoordinates] = React.useState(featureProps.coordinates || null)\r\n\r\n // Listen for marker coordinate changes\r\n React.useEffect(() => {\r\n if (!markerManagerRef.current) return\r\n\r\n const cleanup = markerManagerRef.current.onMoved((event) => {\r\n const newCoordinates = event.detail.coordinates\r\n if (newCoordinates) {\r\n setCoordinates(newCoordinates)\r\n }\r\n })\r\n\r\n return cleanup\r\n }, [])\r\n\r\n const handleBuildingNameChange = (event: React.ChangeEvent<HTMLInputElement>) => {\r\n setName(event.target.value)\r\n }\r\n\r\n const handleAddressChange = (event: React.ChangeEvent<HTMLInputElement>) => {\r\n setAddress(event.target.value)\r\n }\r\n\r\n const selectedMatchingBuilding = matchingBuildings.find(\r\n building => building.id.toString() === selectedMatchingBuildingId,\r\n )\r\n\r\n const handleAttachExistingBuilding = async (building: Building) => {\r\n setIsAttaching(true)\r\n\r\n try {\r\n // Add OSM ID to existing building\r\n const result = await updateBuilding({\r\n buildingOsmId,\r\n // Set default storey number of 2 if building doesn't have one\r\n ...(building.buildingStoreyNum == null && { buildingStoreyNum: 2 })\r\n })\r\n\r\n toast.success(t('toastAttachSuccess'))\r\n\r\n // Clear the selected item after successful update\r\n if (setSelectedItem) {\r\n setSelectedItem(null)\r\n didSetSelectedItemRef.current = false\r\n }\r\n\r\n // Close the popover\r\n onCloseAction()\r\n\r\n } catch (error) {\r\n console.error('Error attaching building:', error)\r\n\r\n if (error?.status !== 401) {\r\n toast.error(t('toastAttachError'))\r\n }\r\n } finally {\r\n setIsAttaching(false)\r\n }\r\n }\r\n\r\n const handleCreateNewBuilding = async (event: React.FormEvent) => {\r\n event.preventDefault()\r\n\r\n // Get final values (prioritize user input, then existing data)\r\n const buildingName = name.trim() || featureProps._name || ''\r\n const finalAddress = address.trim() || address || ''\r\n\r\n if (!user?.organizationId) {\r\n toast.error(t('userNotFound'))\r\n return\r\n }\r\n\r\n // Use updated coordinates from marker if available\r\n const buildingCoordinates = coordinates || [featureProps.longitude, featureProps.latitude]\r\n\r\n const newBuilding: Partial<Building> = {\r\n buildingName,\r\n buildingAddress: finalAddress,\r\n buildingType: ['Other'],\r\n buildingOsmId,\r\n buildingLatitude: buildingCoordinates ? buildingCoordinates[1] : null,\r\n buildingLongitude: buildingCoordinates ? buildingCoordinates[0] : null,\r\n buildingCountrySubdivision,\r\n buildingMunicipality,\r\n buildingPostalCode,\r\n }\r\n\r\n try {\r\n const result = await createBuilding({\r\n buildingData: newBuilding,\r\n organizationId: user.organizationId.toString(),\r\n })\r\n\r\n // Remove marker after creating building\r\n if (markerManagerRef.current) {\r\n markerManagerRef.current.remove()\r\n }\r\n\r\n toast.success(t('toastSuccess'))\r\n\r\n // Close the popover on success\r\n onCloseAction()\r\n }\r\n catch (error) {\r\n console.error('Error creating building:', error)\r\n toast.error(t('toastError'))\r\n }\r\n }\r\n\r\n const handleLocationEdit = () => {\r\n // Create marker at current coordinates with editing enabled\r\n if (markerManagerRef.current && coordinates && map) {\r\n console.log('Creating editable marker at coordinates:', coordinates)\r\n markerManagerRef.current.create(coordinates as [number, number], map, {\r\n editing: true\r\n })\r\n }\r\n else {\r\n console.warn('Cannot create marker: missing coordinates or map instance')\r\n }\r\n }\r\n\r\n return (\r\n <PopoverContent className=\"w-64 -m-1\" side=\"top\">\r\n <form onSubmit={handleCreateNewBuilding}>\r\n {/* Header row: close button, no overlap with content below */}\r\n <div className=\"flex items-center justify-end mb-1\">\r\n <Button\r\n type=\"button\"\r\n onClick={onCloseAction}\r\n variant=\"ghost\"\r\n size=\"icon\"\r\n className=\"shrink-0 p-0 m-0 text-muted-foreground hover:text-foreground\"\r\n aria-label={t('closeAriaLabel')}\r\n >\r\n <LR.X className=\"w-4 h-4\" />\r\n </Button>\r\n </div>\r\n\r\n <div className=\"grid gap-4\">\r\n {/* Show existing building if match found */}\r\n {showExistingBuildings && matchingBuildings.length > 0 && (\r\n <div className=\"space-y-1\">\r\n <div className=\"text-xs text-muted-foreground\">\r\n {t('existingBuildingFound')}\r\n </div>\r\n <div className=\"flex flex-col justify-between gap-2 p-2 bg-muted/50 border rounded-lg\">\r\n <div className=\"grid gap-1\">\r\n <Label htmlFor=\"existing-building-select\" className=\"text-xs text-muted-foreground\">\r\n {t('selectExistingBuildingLabel')}\r\n </Label>\r\n <Select\r\n value={selectedMatchingBuildingId}\r\n onValueChange={setSelectedMatchingBuildingId}\r\n >\r\n <SelectTrigger id=\"existing-building-select\" className=\"h-9 w-full\">\r\n <SelectValue placeholder={t('selectExistingBuildingPlaceholder')} />\r\n </SelectTrigger>\r\n <SelectContent>\r\n {matchingBuildings.map((building) => (\r\n <SelectItem key={building.id} value={building.id.toString()}>\r\n {building.buildingName || building.buildingAddress || `${t('na')} (${building.id})`}\r\n </SelectItem>\r\n ))}\r\n </SelectContent>\r\n </Select>\r\n </div>\r\n <Button\r\n variant=\"outline\"\r\n size=\"sm\"\r\n className='w-full'\r\n onClick={() => selectedMatchingBuilding && handleAttachExistingBuilding(selectedMatchingBuilding)}\r\n disabled={isAttaching || isUpdating || !selectedMatchingBuilding}\r\n >\r\n {(isAttaching || isUpdating) ? (\r\n <>\r\n <LoadingSpinner />\r\n <span>{t('attaching')}</span>\r\n </>\r\n ) : (\r\n <>\r\n <LR.Link />\r\n {t('attachBuilding')}\r\n </>\r\n )}\r\n </Button>\r\n </div>\r\n </div>\r\n )}\r\n\r\n <div className=\"space-y-4\">\r\n <div className=\"grid gap-2\">\r\n <Label htmlFor=\"building-name\" className=\"text-xs text-muted-foreground\">\r\n {t('nameLabel')}\r\n </Label>\r\n <input\r\n id=\"building-name\"\r\n value={name}\r\n onChange={handleBuildingNameChange}\r\n placeholder={name || t('namePlaceholder')}\r\n className={`font-medium leading-none bg-transparent border-b border-gray-300 focus:outline-none focus:border-primary px-1 py-1 ${name ? 'placeholder:text-foreground' : 'placeholder:text-muted-foreground'}`}\r\n aria-label={t('nameInputAriaLabel')}\r\n required\r\n disabled={!ability.can('create', 'Building')}\r\n />\r\n </div>\r\n\r\n <div className=\"grid gap-2\">\r\n <Label htmlFor=\"building-address\" className=\"text-xs text-muted-foreground\">\r\n {t('addressLabel')}\r\n </Label>\r\n <input\r\n id=\"building-address\"\r\n value={address}\r\n onChange={handleAddressChange}\r\n placeholder={address || t('addressPlaceholder')}\r\n className={`font-medium leading-none bg-transparent focus:outline-none focus:border-primary px-1 py-1 ${address ? 'placeholder:text-foreground' : 'placeholder:text-muted-foreground'}`}\r\n aria-label={t('addressAriaLabel')}\r\n required\r\n disabled={!ability.can('create', 'Building')}\r\n />\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div className=\"flex items-center gap-3 justify-center mt-4\">\r\n <Button\r\n type=\"button\"\r\n variant=\"outline\"\r\n aria-label=\"Edit Location\"\r\n title=\"Edit Location\"\r\n onClick={handleLocationEdit}\r\n disabled={!ability.can('update', 'Building')}\r\n >\r\n <LR.LocationEdit className=\"w-4 h-4\" />\r\n </Button>\r\n\r\n <Button\r\n type=\"submit\"\r\n variant=\"outline\"\r\n aria-label={t('addBuilding')}\r\n title={t('addBuilding')}\r\n disabled={isMutating || !ability.can('create', 'Building')}\r\n >\r\n {isMutating\r\n ? (\r\n <>``\r\n <LoadingSpinner />\r\n <span className=\"text-sm\">{t('creating')}</span>\r\n </>\r\n )\r\n : (\r\n <>\r\n <LR.Building2 className=\"w-4 h-4\" />\r\n <span className=\"text-sm\">{t('addBuilding')}</span>\r\n </>\r\n )}\r\n </Button>\r\n </div>\r\n </form>\r\n </PopoverContent>\r\n )\r\n}"],"mappings":"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","names":["_a"]}
1
+ {"version":3,"sources":["../../../../../../../../src/core/components/viewers/map/src/MapFeaturePopoverMenu/src/NonDatabaseBuildingPopover.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\n// Dependencies\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 { toast } from 'sonner'\r\n\r\nimport { useCreateBuilding, useBuildings, useBuilding } from '../../../../../../hooks/buildings/buildings'\r\nimport { usePermissions } from '../../../../../../store'\r\n\r\n// Shadcn Components\r\nimport { MapContext } from '../../../../../../store'\r\nimport { useMenusContext } from '../../../../../../store'\r\nimport { Button, Label } from '../../../../../ui/'\r\nimport { LoadingSpinner } from '../../../../../ui/LoadingSpinner'\r\nimport { PopoverContent } from '../../../../../ui/Popover'\r\nimport {\r\n Select,\r\n SelectContent,\r\n SelectItem,\r\n SelectTrigger,\r\n SelectValue,\r\n} from '../../../../../ui/Select'\r\n\r\n// Icons\r\n\r\n// Types / Utilities\r\nimport { getDetailedAddress } from '../../../utils/geocoder'\r\nimport { MarkerManager } from '../../../utils/MarkerManager'\r\n\r\nimport type { Building } from '../../../../../../types/dbTypes'\r\nimport type { MapGeoJSONFeature } from 'maplibre-gl'\r\n\r\n\r\n\r\n// Hooks\r\n\r\ninterface NonDatabaseBuildingPopoverProps {\r\n feature: MapGeoJSONFeature\r\n isOpen: boolean\r\n onCloseAction: () => void\r\n}\r\n\r\nexport default function NonDatabaseBuildingPopover({\r\n feature,\r\n onCloseAction,\r\n}: NonDatabaseBuildingPopoverProps) {\r\n // Translation\r\n const t = useTranslations('NonDatabaseBuildingPopover')\r\n // Permissions\r\n const { ability } = usePermissions()\r\n\r\n const { data: session } = useSession()\r\n const user = session?.user\r\n\r\n const { state: mapState } = React.useContext(MapContext)\r\n const { currentLocation, map, organizationCountry } = mapState.map\r\n\r\n const { setSelectedItem } = useMenusContext()\r\n\r\n // Use the create building hook\r\n const { createBuilding, isMutating, createError, createdData } = useCreateBuilding()\r\n\r\n // Get all buildings for searching\r\n const { buildings } = useBuildings()\r\n\r\n const featureProps = feature?.properties || {}\r\n const featureid = feature?.id\r\n const [name, setName] = React.useState('')\r\n const [address, setAddress] = React.useState('')\r\n const [matchingBuildings, setMatchingBuildings] = React.useState<Building[]>([])\r\n const [selectedMatchingBuildingId, setSelectedMatchingBuildingId] = React.useState<string>('')\r\n const [showExistingBuildings, setShowExistingBuildings] = React.useState(false)\r\n const [isAttaching, setIsAttaching] = React.useState(false)\r\n\r\n const { updateBuilding, isMutating: isUpdating } = useBuilding(selectedMatchingBuildingId ? Number(selectedMatchingBuildingId) : null)\r\n\r\n const normalizeAddress = React.useCallback((value: string) => {\r\n return value\r\n .toLowerCase()\r\n .replace(/\\./g, '')\r\n .replace(/,/g, ' ')\r\n .replace(/\\bdr\\b/g, 'drive')\r\n .replace(/\\brd\\b/g, 'road')\r\n .replace(/\\bst\\b/g, 'street')\r\n .replace(/\\bave\\b/g, 'avenue')\r\n .replace(/\\s+/g, ' ')\r\n .trim()\r\n }, [])\r\n\r\n const clearExistingBuildingMatches = React.useCallback(() => {\r\n setMatchingBuildings([])\r\n setSelectedMatchingBuildingId('')\r\n setShowExistingBuildings(false)\r\n\r\n if (didSetSelectedItemRef.current && setSelectedItem) {\r\n setSelectedItem(null)\r\n didSetSelectedItemRef.current = false\r\n }\r\n }, [setSelectedItem])\r\n\r\n // Track if we set the selected item in this component\r\n const didSetSelectedItemRef = React.useRef(false)\r\n\r\n // Cleanup on unmount\r\n React.useEffect(() => {\r\n return () => {\r\n // Clear selected item if we set it\r\n if (didSetSelectedItemRef.current && setSelectedItem) {\r\n setSelectedItem(null)\r\n didSetSelectedItemRef.current = false\r\n }\r\n }\r\n }, [setSelectedItem])\r\n\r\n // Get address and search for matching buildings\r\n React.useEffect(() => {\r\n const getAddress = async (longitude: number, latitude: number) => {\r\n try {\r\n // Reverse geocode through the shared provider (Geocode Earth / self-hosted\r\n // Pelias, falling back to public OSM) so this stays in sync with the geocoder.\r\n const result = await getDetailedAddress([longitude, latitude], organizationCountry)\r\n const geocodedAddress = result?.properties?.name as string | undefined\r\n\r\n if (!geocodedAddress) {\r\n clearExistingBuildingMatches()\r\n return\r\n }\r\n\r\n setAddress(geocodedAddress)\r\n\r\n // Search for existing buildings with this address\r\n if (buildings && buildings.length > 0) {\r\n const normalizedGeocodedAddress = normalizeAddress(geocodedAddress)\r\n\r\n const matches = buildings.filter((building) => {\r\n if (!building.buildingAddress) return false\r\n\r\n const normalizedBuildingAddress = normalizeAddress(building.buildingAddress)\r\n return (\r\n normalizedBuildingAddress === normalizedGeocodedAddress ||\r\n normalizedBuildingAddress.includes(normalizedGeocodedAddress) ||\r\n normalizedGeocodedAddress.includes(normalizedBuildingAddress)\r\n )\r\n })\r\n\r\n if (matches.length > 0) {\r\n setMatchingBuildings(matches)\r\n setSelectedMatchingBuildingId(matches[0].id.toString())\r\n setShowExistingBuildings(true)\r\n\r\n // Set the first match as the selected item for the update hook\r\n if (setSelectedItem) {\r\n setSelectedItem(matches[0])\r\n didSetSelectedItemRef.current = true\r\n }\r\n } else {\r\n clearExistingBuildingMatches()\r\n }\r\n } else {\r\n clearExistingBuildingMatches()\r\n }\r\n }\r\n catch (error) {\r\n console.error('Error fetching municipality and countrySubdivision:', error)\r\n }\r\n }\r\n\r\n if (feature.properties?.coordinates) {\r\n const longitude = feature.properties.coordinates[0]\r\n const latitude = feature.properties.coordinates[1]\r\n getAddress(longitude, latitude)\r\n }\r\n }, [feature, buildings, setSelectedItem, normalizeAddress, clearExistingBuildingMatches])\r\n\r\n React.useEffect(() => {\r\n if (!setSelectedItem || !selectedMatchingBuildingId) return\r\n\r\n const selectedBuilding = matchingBuildings.find(\r\n building => building.id.toString() === selectedMatchingBuildingId,\r\n )\r\n\r\n if (!selectedBuilding) return\r\n\r\n setSelectedItem(selectedBuilding)\r\n didSetSelectedItemRef.current = true\r\n }, [matchingBuildings, selectedMatchingBuildingId, setSelectedItem])\r\n\r\n // Fallbacks from feature or map state\r\n const buildingCountrySubdivision = featureProps.de_ao_prov || currentLocation.countrySubdivision || null\r\n const buildingMunicipality = featureProps.de_ao_city || currentLocation.municipality || null\r\n const buildingPostalCode = featureProps.de_ao_post || currentLocation.postalCode || null\r\n const site = currentLocation.site || null\r\n const buildingOsmId = featureid.toString()\r\n\r\n // Create MarkerManager instance\r\n const markerManagerRef = React.useRef<MarkerManager | null>(null)\r\n\r\n // Initialize MarkerManager\r\n React.useEffect(() => {\r\n if (!markerManagerRef.current) {\r\n markerManagerRef.current = new MarkerManager()\r\n }\r\n\r\n // Cleanup on unmount\r\n return () => {\r\n if (markerManagerRef.current) {\r\n markerManagerRef.current.destroy()\r\n markerManagerRef.current = null\r\n }\r\n }\r\n }, [])\r\n\r\n // Get coordinates data from feature\r\n const [coordinates, setCoordinates] = React.useState(featureProps.coordinates || null)\r\n\r\n // Listen for marker coordinate changes\r\n React.useEffect(() => {\r\n if (!markerManagerRef.current) return\r\n\r\n const cleanup = markerManagerRef.current.onMoved((event) => {\r\n const newCoordinates = event.detail.coordinates\r\n if (newCoordinates) {\r\n setCoordinates(newCoordinates)\r\n }\r\n })\r\n\r\n return cleanup\r\n }, [])\r\n\r\n const handleBuildingNameChange = (event: React.ChangeEvent<HTMLInputElement>) => {\r\n setName(event.target.value)\r\n }\r\n\r\n const handleAddressChange = (event: React.ChangeEvent<HTMLInputElement>) => {\r\n setAddress(event.target.value)\r\n }\r\n\r\n const selectedMatchingBuilding = matchingBuildings.find(\r\n building => building.id.toString() === selectedMatchingBuildingId,\r\n )\r\n\r\n const handleAttachExistingBuilding = async (building: Building) => {\r\n setIsAttaching(true)\r\n\r\n try {\r\n // Add OSM ID to existing building\r\n const result = await updateBuilding({\r\n buildingOsmId,\r\n // Set default storey number of 2 if building doesn't have one\r\n ...(building.buildingStoreyNum == null && { buildingStoreyNum: 2 })\r\n })\r\n\r\n toast.success(t('toastAttachSuccess'))\r\n\r\n // Clear the selected item after successful update\r\n if (setSelectedItem) {\r\n setSelectedItem(null)\r\n didSetSelectedItemRef.current = false\r\n }\r\n\r\n // Close the popover\r\n onCloseAction()\r\n\r\n } catch (error) {\r\n console.error('Error attaching building:', error)\r\n\r\n if (error?.status !== 401) {\r\n toast.error(t('toastAttachError'))\r\n }\r\n } finally {\r\n setIsAttaching(false)\r\n }\r\n }\r\n\r\n const handleCreateNewBuilding = async (event: React.FormEvent) => {\r\n event.preventDefault()\r\n\r\n // Get final values (prioritize user input, then existing data)\r\n const buildingName = name.trim() || featureProps._name || ''\r\n const finalAddress = address.trim() || address || ''\r\n\r\n if (!user?.organizationId) {\r\n toast.error(t('userNotFound'))\r\n return\r\n }\r\n\r\n // Use updated coordinates from marker if available\r\n const buildingCoordinates = coordinates || [featureProps.longitude, featureProps.latitude]\r\n\r\n const newBuilding: Partial<Building> = {\r\n buildingName,\r\n buildingAddress: finalAddress,\r\n buildingType: ['Other'],\r\n buildingOsmId,\r\n buildingLatitude: buildingCoordinates ? buildingCoordinates[1] : null,\r\n buildingLongitude: buildingCoordinates ? buildingCoordinates[0] : null,\r\n buildingCountrySubdivision,\r\n buildingMunicipality,\r\n buildingPostalCode,\r\n }\r\n\r\n try {\r\n const result = await createBuilding({\r\n buildingData: newBuilding,\r\n organizationId: user.organizationId.toString(),\r\n })\r\n\r\n // Remove marker after creating building\r\n if (markerManagerRef.current) {\r\n markerManagerRef.current.remove()\r\n }\r\n\r\n toast.success(t('toastSuccess'))\r\n\r\n // Close the popover on success\r\n onCloseAction()\r\n }\r\n catch (error) {\r\n console.error('Error creating building:', error)\r\n toast.error(t('toastError'))\r\n }\r\n }\r\n\r\n const handleLocationEdit = () => {\r\n // Create marker at current coordinates with editing enabled\r\n if (markerManagerRef.current && coordinates && map) {\r\n markerManagerRef.current.create(coordinates as [number, number], map, {\r\n editing: true\r\n })\r\n }\r\n else {\r\n console.warn('Cannot create marker: missing coordinates or map instance')\r\n }\r\n }\r\n\r\n return (\r\n <PopoverContent className=\"w-64 -m-1\" side=\"top\">\r\n <form onSubmit={handleCreateNewBuilding}>\r\n {/* Header row: close button, no overlap with content below */}\r\n <div className=\"flex items-center justify-end mb-1\">\r\n <Button\r\n type=\"button\"\r\n onClick={onCloseAction}\r\n variant=\"ghost\"\r\n size=\"icon\"\r\n className=\"shrink-0 p-0 m-0 text-muted-foreground hover:text-foreground\"\r\n aria-label={t('closeAriaLabel')}\r\n >\r\n <LR.X className=\"w-4 h-4\" />\r\n </Button>\r\n </div>\r\n\r\n <div className=\"grid gap-4\">\r\n {/* Show existing building if match found */}\r\n {showExistingBuildings && matchingBuildings.length > 0 && (\r\n <div className=\"space-y-1\">\r\n <div className=\"text-xs text-muted-foreground\">\r\n {t('existingBuildingFound')}\r\n </div>\r\n <div className=\"flex flex-col justify-between gap-2 p-2 bg-muted/50 border rounded-lg\">\r\n <div className=\"grid gap-1\">\r\n <Label htmlFor=\"existing-building-select\" className=\"text-xs text-muted-foreground\">\r\n {t('selectExistingBuildingLabel')}\r\n </Label>\r\n <Select\r\n value={selectedMatchingBuildingId}\r\n onValueChange={setSelectedMatchingBuildingId}\r\n >\r\n <SelectTrigger id=\"existing-building-select\" className=\"h-9 w-full\">\r\n <SelectValue placeholder={t('selectExistingBuildingPlaceholder')} />\r\n </SelectTrigger>\r\n <SelectContent>\r\n {matchingBuildings.map((building) => (\r\n <SelectItem key={building.id} value={building.id.toString()}>\r\n {building.buildingName || building.buildingAddress || `${t('na')} (${building.id})`}\r\n </SelectItem>\r\n ))}\r\n </SelectContent>\r\n </Select>\r\n </div>\r\n <Button\r\n variant=\"outline\"\r\n size=\"sm\"\r\n className='w-full'\r\n onClick={() => selectedMatchingBuilding && handleAttachExistingBuilding(selectedMatchingBuilding)}\r\n disabled={isAttaching || isUpdating || !selectedMatchingBuilding}\r\n >\r\n {(isAttaching || isUpdating) ? (\r\n <>\r\n <LoadingSpinner />\r\n <span>{t('attaching')}</span>\r\n </>\r\n ) : (\r\n <>\r\n <LR.Link />\r\n {t('attachBuilding')}\r\n </>\r\n )}\r\n </Button>\r\n </div>\r\n </div>\r\n )}\r\n\r\n <div className=\"space-y-4\">\r\n <div className=\"grid gap-2\">\r\n <Label htmlFor=\"building-name\" className=\"text-xs text-muted-foreground\">\r\n {t('nameLabel')}\r\n </Label>\r\n <input\r\n id=\"building-name\"\r\n value={name}\r\n onChange={handleBuildingNameChange}\r\n placeholder={name || t('namePlaceholder')}\r\n className={`font-medium leading-none bg-transparent border-b border-gray-300 focus:outline-none focus:border-primary px-1 py-1 ${name ? 'placeholder:text-foreground' : 'placeholder:text-muted-foreground'}`}\r\n aria-label={t('nameInputAriaLabel')}\r\n required\r\n disabled={!ability.can('create', 'Building')}\r\n />\r\n </div>\r\n\r\n <div className=\"grid gap-2\">\r\n <Label htmlFor=\"building-address\" className=\"text-xs text-muted-foreground\">\r\n {t('addressLabel')}\r\n </Label>\r\n <input\r\n id=\"building-address\"\r\n value={address}\r\n onChange={handleAddressChange}\r\n placeholder={address || t('addressPlaceholder')}\r\n className={`font-medium leading-none bg-transparent focus:outline-none focus:border-primary px-1 py-1 ${address ? 'placeholder:text-foreground' : 'placeholder:text-muted-foreground'}`}\r\n aria-label={t('addressAriaLabel')}\r\n required\r\n disabled={!ability.can('create', 'Building')}\r\n />\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div className=\"flex items-center gap-3 justify-center mt-4\">\r\n <Button\r\n type=\"button\"\r\n variant=\"outline\"\r\n aria-label=\"Edit Location\"\r\n title=\"Edit Location\"\r\n onClick={handleLocationEdit}\r\n disabled={!ability.can('update', 'Building')}\r\n >\r\n <LR.LocationEdit className=\"w-4 h-4\" />\r\n </Button>\r\n\r\n <Button\r\n type=\"submit\"\r\n variant=\"outline\"\r\n aria-label={t('addBuilding')}\r\n title={t('addBuilding')}\r\n disabled={isMutating || !ability.can('create', 'Building')}\r\n >\r\n {isMutating\r\n ? (\r\n <>``\r\n <LoadingSpinner />\r\n <span className=\"text-sm\">{t('creating')}</span>\r\n </>\r\n )\r\n : (\r\n <>\r\n <LR.Building2 className=\"w-4 h-4\" />\r\n <span className=\"text-sm\">{t('addBuilding')}</span>\r\n </>\r\n )}\r\n </Button>\r\n </div>\r\n </form>\r\n </PopoverContent>\r\n )\r\n}"],"mappings":"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","names":["_a"]}
@@ -1 +1 @@
1
- {"version":3,"file":"OpenDataFeaturePopover.d.ts","sourceRoot":"","sources":["../../../../../../../../src/core/components/viewers/map/src/MapFeaturePopoverMenu/src/OpenDataFeaturePopover.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAqB9B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAEpD,UAAU,2BAA2B;IACnC,OAAO,EAAE,iBAAiB,CAAA;IAC1B,MAAM,EAAE,OAAO,CAAA;IACf,aAAa,EAAE,MAAM,IAAI,CAAA;CAC1B;AAED,MAAM,CAAC,OAAO,UAAU,sBAAsB,CAAC,EAC7C,OAAO,EACP,MAAM,EACN,aAAa,GACd,EAAE,2BAA2B,qBAqL7B"}
1
+ {"version":3,"file":"OpenDataFeaturePopover.d.ts","sourceRoot":"","sources":["../../../../../../../../src/core/components/viewers/map/src/MapFeaturePopoverMenu/src/OpenDataFeaturePopover.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAqB9B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAEpD,UAAU,2BAA2B;IACnC,OAAO,EAAE,iBAAiB,CAAA;IAC1B,MAAM,EAAE,OAAO,CAAA;IACf,aAAa,EAAE,MAAM,IAAI,CAAA;CAC1B;AAED,MAAM,CAAC,OAAO,UAAU,sBAAsB,CAAC,EAC7C,OAAO,EACP,MAAM,EACN,aAAa,GACd,EAAE,2BAA2B,qBAmL7B"}
@@ -82,7 +82,6 @@ function OpenDataFeaturePopover({
82
82
  const isUrl = (value) => {
83
83
  return value.startsWith("http://") || value.startsWith("https://");
84
84
  };
85
- console.log("Feature Properties:", featureProps);
86
85
  const datasetName = featureProps.datasetName ? featureProps.datasetName.replaceAll("_", " ") : t("unknownDataset");
87
86
  const dataset = addedDatasets.find((d) => d.name === featureProps.datasetName);
88
87
  const datasetColor = dataset ? dataset.layerColor.color : stringToColour(datasetName);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../../src/core/components/viewers/map/src/MapFeaturePopoverMenu/src/OpenDataFeaturePopover.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\n// Dependencies\r\nimport * as LR from 'lucide-react'\r\nimport { useTranslations } from 'next-intl'\r\nimport * as React from 'react'\r\n\r\n// Shadcn Components\r\nimport { DatasetsContext } from '../../../../../../store/Datasets/context'\r\nimport { Button } from '../../../../../ui/Button'\r\nimport ColorCircle from '../../../../../ui/ColorCircle'\r\nimport { PopoverContent } from '../../../../../ui/Popover'\r\nimport {\r\n Table,\r\n TableBody,\r\n TableCell,\r\n TableHead,\r\n TableHeader,\r\n TableRow,\r\n} from '../../../../../ui/Table'\r\n\r\n// Icons\r\n\r\n// Types / Utilities\r\nimport { stringToColour } from '../../../utils/stringToColour'\r\n\r\nimport type { MapGeoJSONFeature } from 'maplibre-gl'\r\n\r\ninterface OpenDataFeaturePopoverProps {\r\n feature: MapGeoJSONFeature\r\n isOpen: boolean\r\n onCloseAction: () => void\r\n}\r\n\r\nexport default function OpenDataFeaturePopover({\r\n feature,\r\n isOpen,\r\n onCloseAction,\r\n}: OpenDataFeaturePopoverProps) {\r\n // Translation\r\n const t = useTranslations('OpenDataFeaturePopover')\r\n\r\n const {state: datasetsState} = React.useContext(DatasetsContext)\r\n const {addedDatasets} = datasetsState.datasets\r\n\r\n const featureProps = feature?.properties || {}\r\n // Filter out properties that are null, undefined, empty strings, or coordinate-related\r\n const validProperties = Object.entries(featureProps)\r\n .filter(([key, value]) => {\r\n // Check for null, undefined, or empty string values\r\n const lowValue = String(value).toLowerCase()\r\n if (\r\n value === null\r\n || value === undefined\r\n || value === ''\r\n || lowValue === 'null'\r\n || lowValue === 'undefined'\r\n || lowValue === 'none'\r\n ) {\r\n return false\r\n }\r\n\r\n // Filter out coordinate-related properties\r\n const lowerKey = key.toLowerCase()\r\n const excludingKeys = [\r\n 'globalid',\r\n 'latitude',\r\n 'lat',\r\n 'longitude',\r\n 'lng',\r\n 'lon',\r\n 'coordinates',\r\n 'coordina',\r\n 'x',\r\n 'y',\r\n 'z',\r\n 'geometry',\r\n 'cluster',\r\n 'cluster_id',\r\n 'point_count_abbreviated',\r\n 'datasetName',\r\n 'datasetColor',\r\n ]\r\n return !excludingKeys.includes(lowerKey)\r\n })\r\n .sort(([keyA], [keyB]) => {\r\n const aHasName = keyA.toLowerCase().includes('name')\r\n const bHasName = keyB.toLowerCase().includes('name')\r\n if (aHasName && !bHasName) return -1\r\n if (!aHasName && bHasName) return 1\r\n return 0\r\n })\r\n\r\n // Function to format property names (convert snake_case to readable format)\r\n const formatPropertyName = (key: string): string => {\r\n return key\r\n .replaceAll('_', ' ')\r\n .replaceAll(/\\b\\w/g, l => l.toUpperCase())\r\n }\r\n\r\n // Function to format property values\r\n const formatPropertyValue = (value: any, key?: string): string => {\r\n if (typeof value === 'boolean') {\r\n return value ? 'Yes' : 'No'\r\n }\r\n if (typeof value === 'number') {\r\n // Don't add commas for year-like fields\r\n if (key && key.toLowerCase().includes('year')) {\r\n return value.toString()\r\n }\r\n return value.toLocaleString()\r\n }\r\n if (typeof value === 'string') {\r\n // Handle URLs\r\n if (value.startsWith('http://') || value.startsWith('https://')) {\r\n return value\r\n }\r\n return value\r\n }\r\n return String(value)\r\n }\r\n\r\n // Function to check if value is a URL\r\n const isUrl = (value: string): boolean => {\r\n return value.startsWith('http://') || value.startsWith('https://')\r\n }\r\n\r\n console.log('Feature Properties:', featureProps)\r\n\r\n const datasetName = (featureProps.datasetName ? featureProps.datasetName.replaceAll('_', ' ') : t('unknownDataset'))\r\n const dataset = addedDatasets.find(d => d.name === featureProps.datasetName)\r\n const datasetColor = dataset ? dataset.layerColor.color : stringToColour(datasetName)\r\n\r\n return (\r\n <PopoverContent className=\"w-80 -m-1\" side=\"top\">\r\n <div className=\"grid gap-4\">\r\n <Button\r\n onClick={onCloseAction}\r\n variant=\"ghost\"\r\n className=\"absolute top-1 right-2 p-1 text-muted-foreground hover:text-foreground\"\r\n aria-label=\"Close\"\r\n >\r\n <LR.X className=\"w-4 h-4\" />\r\n </Button>\r\n <div className=\"space-y-2\">\r\n <div className=\"flex items-center gap-2\">\r\n <LR.Database className=\"w-4 h-4\" />\r\n <h4 className=\"font-medium leading-none\">{t('propertiesHeader')}</h4>\r\n </div>\r\n {/* Circle with the dataset color */}\r\n <div className=\"flex items-center gap-2 pl-[2px]\">\r\n <ColorCircle color={datasetColor} size={14} />\r\n <p className=\"text-xs text-muted-foreground\">\r\n {datasetName}\r\n </p>\r\n </div>\r\n </div>\r\n\r\n {validProperties.length > 0\r\n ? (\r\n <div className=\"max-h-64 overflow-auto\">\r\n <Table>\r\n <TableHeader>\r\n <TableRow>\r\n <TableHead className=\"w-1/3 text-xs\">{t('propertyTableHead')}</TableHead>\r\n <TableHead className=\"text-xs\">{t('valueTableHead')}</TableHead>\r\n </TableRow>\r\n </TableHeader>\r\n <TableBody>\r\n {validProperties.map(([key, value]) => (\r\n <TableRow key={key}>\r\n <TableCell className=\"font-medium text-xs py-2\">\r\n {formatPropertyName(key)}\r\n </TableCell>\r\n <TableCell className=\"text-xs py-2\">\r\n {isUrl(String(value))\r\n ? (\r\n <a\r\n href={String(value)}\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\"\r\n className=\"text-blue-600 hover:text-blue-800 underline break-all\"\r\n >\r\n {formatPropertyValue(value, key)}\r\n </a>\r\n )\r\n : (\r\n <span className=\"break-words\">\r\n {formatPropertyValue(value, key)}\r\n </span>\r\n )}\r\n </TableCell>\r\n </TableRow>\r\n ))}\r\n </TableBody>\r\n </Table>\r\n </div>\r\n )\r\n : (\r\n <div className=\"text-center py-4\">\r\n <LR.FileX className=\"w-8 h-8 text-muted-foreground mx-auto mb-2\" />\r\n <p className=\"text-sm text-muted-foreground\">\r\n {t('noPropertyAvail')}\r\n </p>\r\n </div>\r\n )}\r\n\r\n {validProperties.length > 10 && (\r\n <div className=\"text-xs text-muted-foreground text-center\">\r\n {t('showing')}\r\n {' '}\r\n {validProperties.length}\r\n {' '}\r\n {t('properties')}\r\n </div>\r\n )}\r\n </div>\r\n </PopoverContent>\r\n )\r\n}\r\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../../../../../src/core/components/viewers/map/src/MapFeaturePopoverMenu/src/OpenDataFeaturePopover.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\n// Dependencies\r\nimport * as LR from 'lucide-react'\r\nimport { useTranslations } from 'next-intl'\r\nimport * as React from 'react'\r\n\r\n// Shadcn Components\r\nimport { DatasetsContext } from '../../../../../../store/Datasets/context'\r\nimport { Button } from '../../../../../ui/Button'\r\nimport ColorCircle from '../../../../../ui/ColorCircle'\r\nimport { PopoverContent } from '../../../../../ui/Popover'\r\nimport {\r\n Table,\r\n TableBody,\r\n TableCell,\r\n TableHead,\r\n TableHeader,\r\n TableRow,\r\n} from '../../../../../ui/Table'\r\n\r\n// Icons\r\n\r\n// Types / Utilities\r\nimport { stringToColour } from '../../../utils/stringToColour'\r\n\r\nimport type { MapGeoJSONFeature } from 'maplibre-gl'\r\n\r\ninterface OpenDataFeaturePopoverProps {\r\n feature: MapGeoJSONFeature\r\n isOpen: boolean\r\n onCloseAction: () => void\r\n}\r\n\r\nexport default function OpenDataFeaturePopover({\r\n feature,\r\n isOpen,\r\n onCloseAction,\r\n}: OpenDataFeaturePopoverProps) {\r\n // Translation\r\n const t = useTranslations('OpenDataFeaturePopover')\r\n\r\n const {state: datasetsState} = React.useContext(DatasetsContext)\r\n const {addedDatasets} = datasetsState.datasets\r\n\r\n const featureProps = feature?.properties || {}\r\n // Filter out properties that are null, undefined, empty strings, or coordinate-related\r\n const validProperties = Object.entries(featureProps)\r\n .filter(([key, value]) => {\r\n // Check for null, undefined, or empty string values\r\n const lowValue = String(value).toLowerCase()\r\n if (\r\n value === null\r\n || value === undefined\r\n || value === ''\r\n || lowValue === 'null'\r\n || lowValue === 'undefined'\r\n || lowValue === 'none'\r\n ) {\r\n return false\r\n }\r\n\r\n // Filter out coordinate-related properties\r\n const lowerKey = key.toLowerCase()\r\n const excludingKeys = [\r\n 'globalid',\r\n 'latitude',\r\n 'lat',\r\n 'longitude',\r\n 'lng',\r\n 'lon',\r\n 'coordinates',\r\n 'coordina',\r\n 'x',\r\n 'y',\r\n 'z',\r\n 'geometry',\r\n 'cluster',\r\n 'cluster_id',\r\n 'point_count_abbreviated',\r\n 'datasetName',\r\n 'datasetColor',\r\n ]\r\n return !excludingKeys.includes(lowerKey)\r\n })\r\n .sort(([keyA], [keyB]) => {\r\n const aHasName = keyA.toLowerCase().includes('name')\r\n const bHasName = keyB.toLowerCase().includes('name')\r\n if (aHasName && !bHasName) return -1\r\n if (!aHasName && bHasName) return 1\r\n return 0\r\n })\r\n\r\n // Function to format property names (convert snake_case to readable format)\r\n const formatPropertyName = (key: string): string => {\r\n return key\r\n .replaceAll('_', ' ')\r\n .replaceAll(/\\b\\w/g, l => l.toUpperCase())\r\n }\r\n\r\n // Function to format property values\r\n const formatPropertyValue = (value: any, key?: string): string => {\r\n if (typeof value === 'boolean') {\r\n return value ? 'Yes' : 'No'\r\n }\r\n if (typeof value === 'number') {\r\n // Don't add commas for year-like fields\r\n if (key && key.toLowerCase().includes('year')) {\r\n return value.toString()\r\n }\r\n return value.toLocaleString()\r\n }\r\n if (typeof value === 'string') {\r\n // Handle URLs\r\n if (value.startsWith('http://') || value.startsWith('https://')) {\r\n return value\r\n }\r\n return value\r\n }\r\n return String(value)\r\n }\r\n\r\n // Function to check if value is a URL\r\n const isUrl = (value: string): boolean => {\r\n return value.startsWith('http://') || value.startsWith('https://')\r\n }\r\n\r\n const datasetName = (featureProps.datasetName ? featureProps.datasetName.replaceAll('_', ' ') : t('unknownDataset'))\r\n const dataset = addedDatasets.find(d => d.name === featureProps.datasetName)\r\n const datasetColor = dataset ? dataset.layerColor.color : stringToColour(datasetName)\r\n\r\n return (\r\n <PopoverContent className=\"w-80 -m-1\" side=\"top\">\r\n <div className=\"grid gap-4\">\r\n <Button\r\n onClick={onCloseAction}\r\n variant=\"ghost\"\r\n className=\"absolute top-1 right-2 p-1 text-muted-foreground hover:text-foreground\"\r\n aria-label=\"Close\"\r\n >\r\n <LR.X className=\"w-4 h-4\" />\r\n </Button>\r\n <div className=\"space-y-2\">\r\n <div className=\"flex items-center gap-2\">\r\n <LR.Database className=\"w-4 h-4\" />\r\n <h4 className=\"font-medium leading-none\">{t('propertiesHeader')}</h4>\r\n </div>\r\n {/* Circle with the dataset color */}\r\n <div className=\"flex items-center gap-2 pl-[2px]\">\r\n <ColorCircle color={datasetColor} size={14} />\r\n <p className=\"text-xs text-muted-foreground\">\r\n {datasetName}\r\n </p>\r\n </div>\r\n </div>\r\n\r\n {validProperties.length > 0\r\n ? (\r\n <div className=\"max-h-64 overflow-auto\">\r\n <Table>\r\n <TableHeader>\r\n <TableRow>\r\n <TableHead className=\"w-1/3 text-xs\">{t('propertyTableHead')}</TableHead>\r\n <TableHead className=\"text-xs\">{t('valueTableHead')}</TableHead>\r\n </TableRow>\r\n </TableHeader>\r\n <TableBody>\r\n {validProperties.map(([key, value]) => (\r\n <TableRow key={key}>\r\n <TableCell className=\"font-medium text-xs py-2\">\r\n {formatPropertyName(key)}\r\n </TableCell>\r\n <TableCell className=\"text-xs py-2\">\r\n {isUrl(String(value))\r\n ? (\r\n <a\r\n href={String(value)}\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\"\r\n className=\"text-blue-600 hover:text-blue-800 underline break-all\"\r\n >\r\n {formatPropertyValue(value, key)}\r\n </a>\r\n )\r\n : (\r\n <span className=\"break-words\">\r\n {formatPropertyValue(value, key)}\r\n </span>\r\n )}\r\n </TableCell>\r\n </TableRow>\r\n ))}\r\n </TableBody>\r\n </Table>\r\n </div>\r\n )\r\n : (\r\n <div className=\"text-center py-4\">\r\n <LR.FileX className=\"w-8 h-8 text-muted-foreground mx-auto mb-2\" />\r\n <p className=\"text-sm text-muted-foreground\">\r\n {t('noPropertyAvail')}\r\n </p>\r\n </div>\r\n )}\r\n\r\n {validProperties.length > 10 && (\r\n <div className=\"text-xs text-muted-foreground text-center\">\r\n {t('showing')}\r\n {' '}\r\n {validProperties.length}\r\n {' '}\r\n {t('properties')}\r\n </div>\r\n )}\r\n </div>\r\n </PopoverContent>\r\n )\r\n}\r\n"],"mappings":"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","names":[]}
@@ -1,7 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import type { Organization } from '../../../../../types/dbTypes';
3
- export declare const MapLayers: ({ minioBaseUrl, organization, maptilerKey }: {
4
- minioBaseUrl?: string;
3
+ export declare const MapLayers: ({ organization, maptilerKey }: {
5
4
  organization?: Organization;
6
5
  maptilerKey?: string;
7
6
  }) => React.JSX.Element;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/components/viewers/map/src/MapLayers/index.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAa9B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAahE,eAAO,MAAM,SAAS,GAAI,6CAA6C;IAAE,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,YAAY,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,sBAalJ,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/components/viewers/map/src/MapLayers/index.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAa9B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAahE,eAAO,MAAM,SAAS,GAAI,+BAA+B;IAAE,YAAY,CAAC,EAAE,YAAY,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,sBAa7G,CAAA"}
@@ -18,14 +18,14 @@ function BimLayerGate() {
18
18
  if (bimState.bim.bimModelsAddedToMap.length === 0) return null;
19
19
  return /* @__PURE__ */ jsx(LazyBimLayer, {});
20
20
  }
21
- const MapLayers = ({ minioBaseUrl, organization, maptilerKey }) => {
21
+ const MapLayers = ({ organization, maptilerKey }) => {
22
22
  return /* @__PURE__ */ jsxs(Fragment, { children: [
23
23
  /* @__PURE__ */ jsx(SiteLayer, {}),
24
24
  /* @__PURE__ */ jsx(CountryLayer, { organization, maptilerKey }),
25
25
  /* @__PURE__ */ jsx(OpenDataLayers, {}),
26
26
  /* @__PURE__ */ jsx(BuildingLayer, {}),
27
27
  /* @__PURE__ */ jsx(CommentLayer, {}),
28
- /* @__PURE__ */ jsx(SensorLayers, { minioBaseUrl }),
28
+ /* @__PURE__ */ jsx(SensorLayers, {}),
29
29
  /* @__PURE__ */ jsx(FileLayers, {}),
30
30
  /* @__PURE__ */ jsx(BimLayerGate, {})
31
31
  ] });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../src/core/components/viewers/map/src/MapLayers/index.tsx"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\n'use client'\r\n\r\nimport dynamic from 'next/dynamic'\r\nimport * as React from 'react'\r\n\r\nimport { useBimContext } from '../../../../../store'\r\n\r\nimport { BuildingLayer } from './src/BuildingLayers'\r\nimport { CommentLayer } from './src/CommentLayer'\r\nimport { CountryLayer } from './src/CountryLayer'\r\nimport { FileLayers } from './src/FileLayer'\r\nimport { OpenDataLayers } from './src/OpenDataLayer/src'\r\nimport { SensorLayers } from './src/SensorsLayer'\r\nimport { SiteLayer } from './src/SiteLayer/SiteLayer'\r\n\r\n\r\nimport type { Organization } from '../../../../../types/dbTypes'\r\n\r\nconst LazyBimLayer = dynamic(\r\n () => import('./src/BimLayer').then(m => ({ default: m.BimLayer })),\r\n { ssr: false },\r\n)\r\n\r\nfunction BimLayerGate() {\r\n const { state: bimState } = useBimContext()\r\n if (bimState.bim.bimModelsAddedToMap.length === 0) return null\r\n return <LazyBimLayer />\r\n}\r\n\r\nexport const MapLayers = ({ minioBaseUrl, organization, maptilerKey }: { minioBaseUrl?: string; organization?: Organization; maptilerKey?: string }) => {\r\n return (\r\n <>\r\n <SiteLayer />\r\n <CountryLayer organization={organization} maptilerKey={maptilerKey} />\r\n <OpenDataLayers />\r\n <BuildingLayer />\r\n <CommentLayer />\r\n <SensorLayers minioBaseUrl={minioBaseUrl} />\r\n <FileLayers />\r\n <BimLayerGate />\r\n </>\r\n )\r\n}\r\n"],"mappings":";AA6BS,SAKL,UALK,KAKL,YALK;AAxBT,OAAO,aAAa;AAGpB,SAAS,qBAAqB;AAE9B,SAAS,qBAAqB;AAC9B,SAAS,oBAAoB;AAC7B,SAAS,oBAAoB;AAC7B,SAAS,kBAAkB;AAC3B,SAAS,sBAAsB;AAC/B,SAAS,oBAAoB;AAC7B,SAAS,iBAAiB;AAK1B,MAAM,eAAe;AAAA,EACnB,MAAM,OAAO,gBAAgB,EAAE,KAAK,QAAM,EAAE,SAAS,EAAE,SAAS,EAAE;AAAA,EAClE,EAAE,KAAK,MAAM;AACf;AAEA,SAAS,eAAe;AACtB,QAAM,EAAE,OAAO,SAAS,IAAI,cAAc;AAC1C,MAAI,SAAS,IAAI,oBAAoB,WAAW,EAAG,QAAO;AAC1D,SAAO,oBAAC,gBAAa;AACvB;AAEO,MAAM,YAAY,CAAC,EAAE,cAAc,cAAc,YAAY,MAAoF;AACtJ,SACE,iCACE;AAAA,wBAAC,aAAU;AAAA,IACX,oBAAC,gBAAa,cAA4B,aAA0B;AAAA,IACpE,oBAAC,kBAAe;AAAA,IAChB,oBAAC,iBAAc;AAAA,IACf,oBAAC,gBAAa;AAAA,IACd,oBAAC,gBAAa,cAA4B;AAAA,IAC1C,oBAAC,cAAW;AAAA,IACZ,oBAAC,gBAAa;AAAA,KAChB;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../../../../../src/core/components/viewers/map/src/MapLayers/index.tsx"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\n'use client'\r\n\r\nimport dynamic from 'next/dynamic'\r\nimport * as React from 'react'\r\n\r\nimport { useBimContext } from '../../../../../store'\r\n\r\nimport { BuildingLayer } from './src/BuildingLayers'\r\nimport { CommentLayer } from './src/CommentLayer'\r\nimport { CountryLayer } from './src/CountryLayer'\r\nimport { FileLayers } from './src/FileLayer'\r\nimport { OpenDataLayers } from './src/OpenDataLayer/src'\r\nimport { SensorLayers } from './src/SensorsLayer'\r\nimport { SiteLayer } from './src/SiteLayer/SiteLayer'\r\n\r\n\r\nimport type { Organization } from '../../../../../types/dbTypes'\r\n\r\nconst LazyBimLayer = dynamic(\r\n () => import('./src/BimLayer').then(m => ({ default: m.BimLayer })),\r\n { ssr: false },\r\n)\r\n\r\nfunction BimLayerGate() {\r\n const { state: bimState } = useBimContext()\r\n if (bimState.bim.bimModelsAddedToMap.length === 0) return null\r\n return <LazyBimLayer />\r\n}\r\n\r\nexport const MapLayers = ({ organization, maptilerKey }: { organization?: Organization; maptilerKey?: string }) => {\r\n return (\r\n <>\r\n <SiteLayer />\r\n <CountryLayer organization={organization} maptilerKey={maptilerKey} />\r\n <OpenDataLayers />\r\n <BuildingLayer />\r\n <CommentLayer />\r\n <SensorLayers />\r\n <FileLayers />\r\n <BimLayerGate />\r\n </>\r\n )\r\n}\r\n"],"mappings":";AA6BS,SAKL,UALK,KAKL,YALK;AAxBT,OAAO,aAAa;AAGpB,SAAS,qBAAqB;AAE9B,SAAS,qBAAqB;AAC9B,SAAS,oBAAoB;AAC7B,SAAS,oBAAoB;AAC7B,SAAS,kBAAkB;AAC3B,SAAS,sBAAsB;AAC/B,SAAS,oBAAoB;AAC7B,SAAS,iBAAiB;AAK1B,MAAM,eAAe;AAAA,EACnB,MAAM,OAAO,gBAAgB,EAAE,KAAK,QAAM,EAAE,SAAS,EAAE,SAAS,EAAE;AAAA,EAClE,EAAE,KAAK,MAAM;AACf;AAEA,SAAS,eAAe;AACtB,QAAM,EAAE,OAAO,SAAS,IAAI,cAAc;AAC1C,MAAI,SAAS,IAAI,oBAAoB,WAAW,EAAG,QAAO;AAC1D,SAAO,oBAAC,gBAAa;AACvB;AAEO,MAAM,YAAY,CAAC,EAAE,cAAc,YAAY,MAA6D;AACjH,SACE,iCACE;AAAA,wBAAC,aAAU;AAAA,IACX,oBAAC,gBAAa,cAA4B,aAA0B;AAAA,IACpE,oBAAC,kBAAe;AAAA,IAChB,oBAAC,iBAAc;AAAA,IACf,oBAAC,gBAAa;AAAA,IACd,oBAAC,gBAAa;AAAA,IACd,oBAAC,cAAW;AAAA,IACZ,oBAAC,gBAAa;AAAA,KAChB;AAEJ;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/CommentLayer/index.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AA8D9B,eAAO,MAAM,YAAY,yBAmRxB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/CommentLayer/index.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAqE9B,eAAO,MAAM,YAAY,yBA6YxB,CAAA"}
@@ -24,39 +24,45 @@ import { useTranslations } from "next-intl";
24
24
  import * as React from "react";
25
25
  import { Source, Layer, Popup, Marker } from "react-map-gl/maplibre";
26
26
  import { toast } from "sonner";
27
- import { useComment, useComments } from "../../../../../../../hooks/comments/comments";
27
+ import { useComments, useDeleteComments } from "../../../../../../../hooks/comments/comments";
28
28
  import { useUser, useUsers } from "../../../../../../../hooks/users/users";
29
29
  import { MapContext, MenusContext } from "../../../../../../../store";
30
30
  import { ViewerNames } from "../../../../../../../types/dbTypes";
31
+ import { commentRingShadow } from "../../../../../../../utils/markerUtils";
31
32
  import { Avatar } from "../../../../../../ui/Avatar";
32
33
  import Comment from "../../../../../../ui/Comments/Comment";
33
34
  import { UserAvatar } from "../../../../../../ui/UserAvatar";
34
35
  import { extractCoordinatesFromFeature } from "../../../../utils/extractCoordinates";
35
36
  import { MapLayerClickPriority } from "../../../../utils/MapEventManager/MapClickManager";
36
37
  import { createClusterLayer, createClusterCountLayer, createUnclusteredPointLayer } from "../mapLayersUtils";
37
- const CommentAvatarMarker = ({ feature, isHighlighted, onMouseEnter, onMouseLeave }) => {
38
+ const CommentAvatarMarker = ({ feature, isHighlighted, isFocused, onMouseEnter, onMouseLeave, onClick, onDoubleClick, center, offset }) => {
38
39
  var _a, _b, _c, _d, _e, _f, _g;
39
40
  const authorId = (_a = feature.properties) == null ? void 0 : _a.authorId;
40
41
  const { user: author } = useUser(authorId != null ? String(authorId) : "");
41
42
  const userName = (_b = feature.properties) == null ? void 0 : _b.authorName;
42
43
  const userImageFileId = (_c = feature.properties) == null ? void 0 : _c.imageFileId;
43
44
  const resolvedImageFileId = (_e = (_d = author == null ? void 0 : author.imageFileId) != null ? _d : userImageFileId) != null ? _e : null;
44
- const coords = extractCoordinatesFromFeature(feature);
45
+ const coords = center ? { lng: center[0], lat: center[1] } : extractCoordinatesFromFeature(feature);
45
46
  if (!coords) return null;
46
- return /* @__PURE__ */ jsx(Marker, { longitude: coords.lng, latitude: coords.lat, anchor: "center", children: /* @__PURE__ */ jsx(
47
+ const scale = isFocused ? 1.25 : isHighlighted ? 1.2 : 1;
48
+ return /* @__PURE__ */ jsx(Marker, { longitude: coords.lng, latitude: coords.lat, anchor: "center", offset, children: /* @__PURE__ */ jsx(
47
49
  "div",
48
50
  {
51
+ title: onDoubleClick ? "Double click to zoom" : void 0,
49
52
  style: {
50
53
  width: "36px",
51
54
  height: "36px",
52
55
  borderRadius: "50%",
53
- boxShadow: isHighlighted ? "0 0 0 3px #73cee2, 0 0 8px rgba(115, 206, 226, 0.5)" : "0 0 0 2px white ",
56
+ boxShadow: commentRingShadow({ highlight: isHighlighted, focused: isFocused }),
54
57
  transition: "all 0.2s ease-in-out",
55
- transform: isHighlighted ? "scale(1.2)" : "scale(1)",
56
- overflow: "hidden"
58
+ transform: `scale(${scale})`,
59
+ overflow: "hidden",
60
+ cursor: onClick || onDoubleClick ? "pointer" : void 0
57
61
  },
58
62
  onMouseEnter,
59
63
  onMouseLeave,
64
+ onClick,
65
+ onDoubleClick,
60
66
  children: /* @__PURE__ */ jsx(Avatar, { className: "h-full w-full", children: /* @__PURE__ */ jsx(
61
67
  UserAvatar,
62
68
  {
@@ -69,7 +75,7 @@ const CommentAvatarMarker = ({ feature, isHighlighted, onMouseEnter, onMouseLeav
69
75
  ) }, String((_g = (_f = feature.properties) == null ? void 0 : _f.id) != null ? _g : `${coords.lng},${coords.lat}`));
70
76
  };
71
77
  const CommentLayer = () => {
72
- var _a, _b;
78
+ var _a;
73
79
  const [hoveredCommentId, setHoveredCommentId] = React.useState(null);
74
80
  const clusterLayer = createClusterLayer("comments");
75
81
  const clusterCountLayer = createClusterCountLayer("comments");
@@ -82,22 +88,87 @@ const CommentLayer = () => {
82
88
  const user = (_a = useSession().data) == null ? void 0 : _a.user;
83
89
  const [popupInfo, setPopUpInfo] = React.useState(null);
84
90
  const { user: popupAuthor } = useUser((popupInfo == null ? void 0 : popupInfo.authorId) != null ? String(popupInfo.authorId) : "");
85
- const { deleteComment } = useComment((_b = popupInfo == null ? void 0 : popupInfo.id) != null ? _b : null);
86
- const { state: menusState } = React.useContext(MenusContext);
87
- const { commentsVisibleInViewer, currentCommentId } = menusState.menus;
91
+ const { deleteComments } = useDeleteComments();
92
+ const { state: menusState, dispatch: menusDispatch, setIsSidebarOpen } = React.useContext(MenusContext);
93
+ const { commentsVisibleInViewer, currentCommentId, focusedCommentId, focusRequestId } = menusState.menus;
94
+ const focusComment = React.useCallback((commentId) => {
95
+ if (commentId == null) return;
96
+ menusDispatch({ type: "SET_CURRENT_COMMENT_ID", payload: { commentId } });
97
+ menusDispatch({ type: "SET_FOCUSED_COMMENT_ID", payload: { commentId } });
98
+ }, [menusDispatch]);
99
+ const requestSidebarAction = React.useCallback((commentId, action) => {
100
+ if (commentId == null) return;
101
+ setIsSidebarOpen(true);
102
+ menusDispatch({ type: "SET_SIDEBAR_SELECTED_TAB", payload: { selectedTab: "communication" } });
103
+ menusDispatch({ type: "SET_CURRENT_COMMENT_ID", payload: { commentId } });
104
+ menusDispatch({ type: "REQUEST_COMMENT_ACTION", payload: { commentId, action } });
105
+ }, [menusDispatch, setIsSidebarOpen]);
106
+ const [spider, setSpider] = React.useState(null);
107
+ const spiderCloseTimer = React.useRef(null);
108
+ const cancelSpiderClose = React.useCallback(() => {
109
+ if (spiderCloseTimer.current) {
110
+ clearTimeout(spiderCloseTimer.current);
111
+ spiderCloseTimer.current = null;
112
+ }
113
+ }, []);
114
+ const scheduleSpiderClose = React.useCallback(() => {
115
+ cancelSpiderClose();
116
+ spiderCloseTimer.current = setTimeout(() => setSpider(null), 150);
117
+ }, [cancelSpiderClose]);
118
+ const openPopupFromFeature = React.useCallback((feature) => {
119
+ var _a2, _b;
120
+ if (!feature || feature.properties.point_count) return;
121
+ if (feature.geometry.type !== "Point") return;
122
+ const [longitude, latitude] = feature.geometry.coordinates;
123
+ const p = feature.properties;
124
+ setPopUpInfo({
125
+ id: Number(p.id),
126
+ authorId: Number(p.authorId),
127
+ organizationId: (_a2 = feature.properties) == null ? void 0 : _a2.organizationId,
128
+ visible: (_b = feature.properties) == null ? void 0 : _b.visible,
129
+ longitude,
130
+ latitude,
131
+ text: p.text,
132
+ createdAt: p.createdAt,
133
+ authorName: p.authorName,
134
+ imageFileId: p.imageFileId,
135
+ viewer: ViewerNames.map
136
+ });
137
+ }, []);
88
138
  const eligibleComments = comments.filter((comment) => comment.viewer === ViewerNames.map).map((comment) => {
89
- var _a2, _b2;
139
+ var _a2, _b;
90
140
  const user2 = users.find((u) => u.id === comment.authorId);
91
141
  return __spreadProps(__spreadValues({}, comment), {
92
142
  authorName: (_a2 = user2 == null ? void 0 : user2.name) != null ? _a2 : "Unknown User",
93
- imageFileId: (_b2 = user2 == null ? void 0 : user2.imageFileId) != null ? _b2 : null
143
+ imageFileId: (_b = user2 == null ? void 0 : user2.imageFileId) != null ? _b : null
94
144
  });
95
145
  });
146
+ const eligibleCommentsRef = React.useRef(eligibleComments);
147
+ eligibleCommentsRef.current = eligibleComments;
96
148
  React.useEffect(() => {
97
149
  if (popupInfo && !comments.find((c) => c.id === popupInfo.id)) {
98
150
  setPopUpInfo(null);
99
151
  }
100
152
  }, [comments, popupInfo]);
153
+ React.useEffect(() => {
154
+ if (focusedCommentId == null || !map) return;
155
+ const target = eligibleCommentsRef.current.find((c) => c.id === focusedCommentId);
156
+ if (!target || target.longitude == null || target.latitude == null) return;
157
+ map.flyTo({ center: [target.longitude, target.latitude], zoom: Math.max(map.getZoom(), 17), duration: 800 });
158
+ setPopUpInfo({
159
+ id: target.id,
160
+ authorId: target.authorId,
161
+ organizationId: target.organizationId,
162
+ visible: target.visible,
163
+ longitude: target.longitude,
164
+ latitude: target.latitude,
165
+ text: target.text,
166
+ createdAt: target.createdAt,
167
+ authorName: target.authorName,
168
+ imageFileId: target.imageFileId,
169
+ viewer: ViewerNames.map
170
+ });
171
+ }, [focusRequestId, map]);
101
172
  const geojsonCommentData = React.useMemo(() => {
102
173
  const convertDataToGeojson = (commentData) => {
103
174
  const commentFeatures = commentData.map((comment) => {
@@ -134,26 +205,7 @@ const CommentLayer = () => {
134
205
  React.useEffect(() => {
135
206
  if (!map) return;
136
207
  const showCommentPopUp = (e, features) => {
137
- var _a2, _b2;
138
- const feature = features == null ? void 0 : features[0];
139
- if (!feature || feature.properties.point_count) return;
140
- if (feature.geometry.type != "Point") return;
141
- const [longitude, latitude] = feature.geometry.coordinates;
142
- const featureProperties = feature.properties;
143
- const { id, authorId, text, createdAt, authorName, imageFileId } = featureProperties;
144
- setPopUpInfo({
145
- id: Number(id),
146
- authorId: Number(authorId),
147
- organizationId: (_a2 = feature.properties) == null ? void 0 : _a2.organizationId,
148
- visible: (_b2 = feature.properties) == null ? void 0 : _b2.visible,
149
- longitude,
150
- latitude,
151
- text,
152
- createdAt,
153
- authorName,
154
- imageFileId,
155
- viewer: ViewerNames.map
156
- });
208
+ openPopupFromFeature(features == null ? void 0 : features[0]);
157
209
  };
158
210
  const mouseEnterChangeCursor = () => {
159
211
  map.getCanvas().style.cursor = "pointer";
@@ -169,12 +221,25 @@ const CommentLayer = () => {
169
221
  map.off("mouseenter", "comments-unclustered-points", mouseEnterChangeCursor);
170
222
  map.off("mouseleave", "comments-unclustered-points", mouseLeaveChangeCursor);
171
223
  };
172
- }, [map, mapClickManager]);
224
+ }, [map, mapClickManager, openPopupFromFeature]);
173
225
  const handleRemoveComment = () => {
226
+ if ((popupInfo == null ? void 0 : popupInfo.id) == null) return;
174
227
  toast.success(t("commentDeleted"));
175
- deleteComment();
228
+ const id = popupInfo.id;
229
+ const replyIds = comments.filter((c) => c.replyToId === id).map((c) => c.id);
230
+ void deleteComments({ ids: [id, ...replyIds] });
176
231
  setPopUpInfo(null);
177
232
  };
233
+ React.useEffect(() => {
234
+ if (!map) return;
235
+ const close = () => setSpider(null);
236
+ map.on("movestart", close);
237
+ map.on("zoomstart", close);
238
+ return () => {
239
+ map.off("movestart", close);
240
+ map.off("zoomstart", close);
241
+ };
242
+ }, [map]);
178
243
  React.useEffect(() => {
179
244
  if (!map) return;
180
245
  const zoomInToDecluster = (e) => {
@@ -185,34 +250,45 @@ const CommentLayer = () => {
185
250
  if (!("coordinates" in feature.geometry)) return;
186
251
  const [lng, lat] = feature.geometry.coordinates;
187
252
  const source = map.getSource("comments");
188
- console.log(source);
189
- console.log(clusterId);
190
253
  source.getClusterExpansionZoom(clusterId).then(
191
254
  (zoom) => {
192
- console.log("easing to");
193
255
  map.easeTo({ center: [lng, lat], zoom });
194
256
  }
195
257
  ).catch((error) => {
196
258
  console.error(error);
197
259
  });
198
260
  };
199
- const mouseEnterChangeCursor = () => {
261
+ const expandClusterOnHover = (e) => {
262
+ var _a2;
200
263
  map.getCanvas().style.cursor = "pointer";
264
+ const feature = (_a2 = e.features) == null ? void 0 : _a2[0];
265
+ if (!feature || !feature.properties.cluster_id) return;
266
+ if (!("coordinates" in feature.geometry)) return;
267
+ const center = feature.geometry.coordinates;
268
+ const clusterId = feature.properties.cluster_id;
269
+ const source = map.getSource("comments");
270
+ source.getClusterLeaves(clusterId, Infinity, 0).then((leaves) => {
271
+ cancelSpiderClose();
272
+ setSpider({ center, features: leaves });
273
+ }).catch((error) => {
274
+ console.error(error);
275
+ });
201
276
  };
202
- const mouseLeaveChangeCursor = () => {
277
+ const mouseLeaveCluster = () => {
203
278
  map.getCanvas().style.cursor = "";
279
+ scheduleSpiderClose();
204
280
  };
205
281
  map.on("click", "comments-clusters", zoomInToDecluster);
206
- map.on("mouseenter", "comments-clusters", mouseEnterChangeCursor);
207
- map.on("mouseleave", "comments-clusters", mouseLeaveChangeCursor);
282
+ map.on("mouseenter", "comments-clusters", expandClusterOnHover);
283
+ map.on("mouseleave", "comments-clusters", mouseLeaveCluster);
208
284
  return () => {
209
285
  map.off("click", "comments-clusters", zoomInToDecluster);
210
- map.off("mouseenter", "comments-clusters", mouseEnterChangeCursor);
211
- map.off("mouseleave", "comments-clusters", mouseLeaveChangeCursor);
286
+ map.off("mouseenter", "comments-clusters", expandClusterOnHover);
287
+ map.off("mouseleave", "comments-clusters", mouseLeaveCluster);
212
288
  };
213
- }, [map]);
289
+ }, [map, cancelSpiderClose, scheduleSpiderClose]);
214
290
  const renderPopup = () => {
215
- var _a2, _b2, _c, _d;
291
+ var _a2, _b, _c, _d;
216
292
  if (!popupInfo) return null;
217
293
  return /* @__PURE__ */ jsxs(
218
294
  Popup,
@@ -230,11 +306,16 @@ const CommentLayer = () => {
230
306
  Comment,
231
307
  {
232
308
  userName: popupInfo.authorName || "",
233
- userImage: (_b2 = (_a2 = popupAuthor == null ? void 0 : popupAuthor.imageFileId) != null ? _a2 : popupInfo.imageFileId) != null ? _b2 : null,
309
+ userImage: (_b = (_a2 = popupAuthor == null ? void 0 : popupAuthor.imageFileId) != null ? _a2 : popupInfo.imageFileId) != null ? _b : null,
234
310
  userImageFileId: (_d = (_c = popupAuthor == null ? void 0 : popupAuthor.imageFileId) != null ? _c : popupInfo.imageFileId) != null ? _d : null,
235
311
  text: popupInfo.text,
236
312
  createdAt: popupInfo.createdAt,
237
- onRemove: user.id === String(popupInfo.authorId) ? handleRemoveComment : null,
313
+ showActions: true,
314
+ canEdit: user.id === String(popupInfo.authorId),
315
+ canDelete: user.id === String(popupInfo.authorId),
316
+ onEdit: user.id === String(popupInfo.authorId) ? () => requestSidebarAction(popupInfo.id, "edit") : void 0,
317
+ onReply: () => requestSidebarAction(popupInfo.id, "reply"),
318
+ onRemove: user.id === String(popupInfo.authorId) ? handleRemoveComment : void 0,
238
319
  onClose: () => setPopUpInfo(null)
239
320
  }
240
321
  ),
@@ -259,8 +340,8 @@ const CommentLayer = () => {
259
340
  const allFeatures = map.querySourceFeatures("comments");
260
341
  const unclusteredOnly = allFeatures.filter(
261
342
  (f) => {
262
- var _a2, _b2;
263
- return !((_a2 = f.properties) == null ? void 0 : _a2.cluster) && !((_b2 = f.properties) == null ? void 0 : _b2.point_count);
343
+ var _a2, _b;
344
+ return !((_a2 = f.properties) == null ? void 0 : _a2.cluster) && !((_b = f.properties) == null ? void 0 : _b.point_count);
264
345
  }
265
346
  );
266
347
  const uniqueFeatures = /* @__PURE__ */ new Map();
@@ -298,20 +379,60 @@ const CommentLayer = () => {
298
379
  var _a2;
299
380
  return ((_a2 = feature.properties) == null ? void 0 : _a2.id) !== (popupInfo == null ? void 0 : popupInfo.id);
300
381
  }).map((feature) => {
301
- var _a2, _b2, _c;
382
+ var _a2, _b, _c, _d;
302
383
  return /* @__PURE__ */ jsx(
303
384
  CommentAvatarMarker,
304
385
  {
305
386
  feature,
306
- isHighlighted: currentCommentId === ((_b2 = feature.properties) == null ? void 0 : _b2.id) || hoveredCommentId === ((_c = feature.properties) == null ? void 0 : _c.id),
387
+ isHighlighted: currentCommentId === ((_b = feature.properties) == null ? void 0 : _b.id) || hoveredCommentId === ((_c = feature.properties) == null ? void 0 : _c.id),
388
+ isFocused: focusedCommentId === ((_d = feature.properties) == null ? void 0 : _d.id),
307
389
  onMouseEnter: () => {
308
390
  var _a3;
309
391
  return setHoveredCommentId((_a3 = feature.properties) == null ? void 0 : _a3.id);
310
392
  },
311
- onMouseLeave: () => setHoveredCommentId(null)
393
+ onMouseLeave: () => setHoveredCommentId(null),
394
+ onDoubleClick: () => {
395
+ var _a3;
396
+ return focusComment((_a3 = feature.properties) == null ? void 0 : _a3.id);
397
+ }
312
398
  },
313
399
  String((_a2 = feature.properties) == null ? void 0 : _a2.id)
314
400
  );
401
+ }),
402
+ spider && spider.features.map((feature, i) => {
403
+ var _a2;
404
+ const n = spider.features.length;
405
+ const angle = 2 * Math.PI * i / n - Math.PI / 2;
406
+ const radius = Math.min(60, 24 + n * 4);
407
+ const offset = [Math.cos(angle) * radius, Math.sin(angle) * radius];
408
+ const id = (_a2 = feature.properties) == null ? void 0 : _a2.id;
409
+ return /* @__PURE__ */ jsx(
410
+ CommentAvatarMarker,
411
+ {
412
+ feature,
413
+ center: spider.center,
414
+ offset,
415
+ isHighlighted: currentCommentId === id || hoveredCommentId === id,
416
+ isFocused: focusedCommentId === id,
417
+ onMouseEnter: () => {
418
+ cancelSpiderClose();
419
+ setHoveredCommentId(id);
420
+ },
421
+ onMouseLeave: () => {
422
+ scheduleSpiderClose();
423
+ setHoveredCommentId(null);
424
+ },
425
+ onClick: () => {
426
+ openPopupFromFeature(feature);
427
+ setSpider(null);
428
+ },
429
+ onDoubleClick: () => {
430
+ focusComment(id);
431
+ setSpider(null);
432
+ }
433
+ },
434
+ `spider-${String(id)}`
435
+ );
315
436
  })
316
437
  ]
317
438
  }