@collabdt/core 0.3.1 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/components/authentication/ForgotPassword.d.ts.map +1 -1
- package/dist/core/components/authentication/ForgotPassword.js +0 -1
- package/dist/core/components/authentication/ForgotPassword.js.map +1 -1
- package/dist/core/components/ui/Comments/CollapsibleCommentItem.d.ts +12 -2
- package/dist/core/components/ui/Comments/CollapsibleCommentItem.d.ts.map +1 -1
- package/dist/core/components/ui/Comments/CollapsibleCommentItem.js +137 -139
- package/dist/core/components/ui/Comments/CollapsibleCommentItem.js.map +1 -1
- package/dist/core/components/ui/Comments/Comment.d.ts +18 -1
- package/dist/core/components/ui/Comments/Comment.d.ts.map +1 -1
- package/dist/core/components/ui/Comments/Comment.js +94 -23
- package/dist/core/components/ui/Comments/Comment.js.map +1 -1
- package/dist/core/components/ui/Comments/CommentActionButtons.d.ts +38 -0
- package/dist/core/components/ui/Comments/CommentActionButtons.d.ts.map +1 -0
- package/dist/core/components/ui/Comments/CommentActionButtons.js +134 -0
- package/dist/core/components/ui/Comments/CommentActionButtons.js.map +1 -0
- package/dist/core/components/ui/Comments/CommentInput.d.ts.map +1 -1
- package/dist/core/components/ui/Comments/CommentInput.js +5 -2
- package/dist/core/components/ui/Comments/CommentInput.js.map +1 -1
- package/dist/core/components/ui/Comments/CommentsSection.d.ts.map +1 -1
- package/dist/core/components/ui/Comments/CommentsSection.js +86 -42
- package/dist/core/components/ui/Comments/CommentsSection.js.map +1 -1
- package/dist/core/components/ui/Comments/commentUtils.d.ts.map +1 -1
- package/dist/core/components/ui/Comments/commentUtils.js +2 -1
- package/dist/core/components/ui/Comments/commentUtils.js.map +1 -1
- package/dist/core/components/ui/FilesManager/src/useFileUploadWithProgress.d.ts.map +1 -1
- package/dist/core/components/ui/FilesManager/src/useFileUploadWithProgress.js +0 -1
- package/dist/core/components/ui/FilesManager/src/useFileUploadWithProgress.js.map +1 -1
- package/dist/core/components/ui/InfoSidebar/Container.d.ts.map +1 -1
- package/dist/core/components/ui/InfoSidebar/Container.js +16 -12
- package/dist/core/components/ui/InfoSidebar/Container.js.map +1 -1
- package/dist/core/components/ui/Sensors/CollapsibleSensorItem.d.ts +1 -2
- package/dist/core/components/ui/Sensors/CollapsibleSensorItem.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/CollapsibleSensorItem.js +66 -94
- package/dist/core/components/ui/Sensors/CollapsibleSensorItem.js.map +1 -1
- package/dist/core/components/ui/Sensors/Sensor.d.ts +16 -3
- package/dist/core/components/ui/Sensors/Sensor.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/Sensor.js +76 -37
- package/dist/core/components/ui/Sensors/Sensor.js.map +1 -1
- package/dist/core/components/ui/Sensors/SensorChart.d.ts +12 -2
- package/dist/core/components/ui/Sensors/SensorChart.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/SensorChart.js +49 -12
- package/dist/core/components/ui/Sensors/SensorChart.js.map +1 -1
- package/dist/core/components/ui/Sensors/SensorDetailDialog.d.ts +9 -0
- package/dist/core/components/ui/Sensors/SensorDetailDialog.d.ts.map +1 -0
- package/dist/core/components/ui/Sensors/SensorDetailDialog.js +128 -0
- package/dist/core/components/ui/Sensors/SensorDetailDialog.js.map +1 -0
- package/dist/core/components/ui/Sensors/SensorInput.d.ts +6 -1
- package/dist/core/components/ui/Sensors/SensorInput.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/SensorInput.js +94 -9
- package/dist/core/components/ui/Sensors/SensorInput.js.map +1 -1
- package/dist/core/components/ui/Sensors/SensorTagsSection.d.ts +3 -1
- package/dist/core/components/ui/Sensors/SensorTagsSection.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/SensorTagsSection.js +4 -1
- package/dist/core/components/ui/Sensors/SensorTagsSection.js.map +1 -1
- package/dist/core/components/ui/Sensors/SensorsSection.d.ts +1 -3
- package/dist/core/components/ui/Sensors/SensorsSection.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/SensorsSection.js +33 -10
- package/dist/core/components/ui/Sensors/SensorsSection.js.map +1 -1
- package/dist/core/components/ui/Sensors/TimeZoneSelect.d.ts +7 -0
- package/dist/core/components/ui/Sensors/TimeZoneSelect.d.ts.map +1 -0
- package/dist/core/components/ui/Sensors/TimeZoneSelect.js +56 -0
- package/dist/core/components/ui/Sensors/TimeZoneSelect.js.map +1 -0
- package/dist/core/components/ui/Sensors/sensorData.d.ts +42 -0
- package/dist/core/components/ui/Sensors/sensorData.d.ts.map +1 -0
- package/dist/core/components/ui/Sensors/sensorData.js +118 -0
- package/dist/core/components/ui/Sensors/sensorData.js.map +1 -0
- package/dist/core/components/ui/Sensors/sensorRange.d.ts +24 -0
- package/dist/core/components/ui/Sensors/sensorRange.d.ts.map +1 -0
- package/dist/core/components/ui/Sensors/sensorRange.js +34 -0
- package/dist/core/components/ui/Sensors/sensorRange.js.map +1 -0
- package/dist/core/components/ui/Sensors/useSensorSeries.d.ts +15 -0
- package/dist/core/components/ui/Sensors/useSensorSeries.d.ts.map +1 -0
- package/dist/core/components/ui/Sensors/useSensorSeries.js +63 -0
- package/dist/core/components/ui/Sensors/useSensorSeries.js.map +1 -0
- package/dist/core/components/ui/Sidebar.d.ts.map +1 -1
- package/dist/core/components/ui/Sidebar.js +27 -4
- package/dist/core/components/ui/Sidebar.js.map +1 -1
- package/dist/core/components/ui/ToolbarButton.d.ts.map +1 -1
- package/dist/core/components/ui/ToolbarButton.js +0 -1
- package/dist/core/components/ui/ToolbarButton.js.map +1 -1
- package/dist/core/components/viewers/Data/advancedFilter/FiltersDialog.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/advancedFilter/FiltersDialog.js +0 -2
- package/dist/core/components/viewers/Data/advancedFilter/FiltersDialog.js.map +1 -1
- package/dist/core/components/viewers/Data/details/EnergySourcesGrid.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/details/EnergySourcesGrid.js +0 -10
- package/dist/core/components/viewers/Data/details/EnergySourcesGrid.js.map +1 -1
- package/dist/core/components/viewers/Data/files/DatatableFileAdder.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/files/DatatableFileAdder.js +0 -1
- package/dist/core/components/viewers/Data/files/DatatableFileAdder.js.map +1 -1
- package/dist/core/components/viewers/Data/files/utils/fuzzySearch.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/files/utils/fuzzySearch.js +0 -1
- package/dist/core/components/viewers/Data/files/utils/fuzzySearch.js.map +1 -1
- package/dist/core/components/viewers/Data/userDetails/UserMoreOptions.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/userDetails/UserMoreOptions.js +0 -1
- package/dist/core/components/viewers/Data/userDetails/UserMoreOptions.js.map +1 -1
- package/dist/core/components/viewers/Data/utils/filterHelper.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/utils/filterHelper.js +0 -2
- package/dist/core/components/viewers/Data/utils/filterHelper.js.map +1 -1
- package/dist/core/components/viewers/Viewer.js +1 -1
- package/dist/core/components/viewers/Viewer.js.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/CommunicationTab/src/TopicsSection.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/CommunicationTab/src/TopicsSection.js +0 -3
- package/dist/core/components/viewers/bim/src/BimSidebar/src/CommunicationTab/src/TopicsSection.js.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/FilesSection.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/FilesSection.js +1 -7
- package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/FilesSection.js.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/ModelsSection.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/ModelsSection.js +1 -9
- package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/ModelsSection.js.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.d.ts +1 -3
- package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.js +2 -2
- package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.js.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/TabSelector.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/TabSelector.js +4 -3
- package/dist/core/components/viewers/bim/src/BimSidebar/src/TabSelector.js.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/index.js +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/index.js.map +1 -1
- package/dist/core/components/viewers/bim/src/ClippingPlane.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/ClippingPlane.js +0 -1
- package/dist/core/components/viewers/bim/src/ClippingPlane.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/index.d.ts +1 -2
- package/dist/core/components/viewers/bim/src/tools/AddToBim/index.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/index.js +25 -2
- package/dist/core/components/viewers/bim/src/tools/AddToBim/index.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimComment.d.ts +16 -10
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimComment.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimComment.js +26 -6
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimComment.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.d.ts +2 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.d.ts.map +1 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.js +5 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.js.map +1 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.d.ts +8 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.js +15 -3
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/clusterMarkersByScreenSpace.d.ts +44 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/clusterMarkersByScreenSpace.d.ts.map +1 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/clusterMarkersByScreenSpace.js +77 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/clusterMarkersByScreenSpace.js.map +1 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.d.ts +3 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.d.ts.map +1 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.js +5 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.js.map +1 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentMarkerConfig.d.ts +33 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentMarkerConfig.d.ts.map +1 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentMarkerConfig.js +90 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentMarkerConfig.js.map +1 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/renderCSS2DMarkers.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/renderCSS2DMarkers.js +2 -2
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/renderCSS2DMarkers.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.d.ts +7 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.js +50 -73
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.d.ts +7 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.js +39 -11
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/ClippingTool/ClippingTool.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/ClippingTool/ClippingTool.js +1 -5
- package/dist/core/components/viewers/bim/src/tools/ClippingTool/ClippingTool.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/ExplodeByLevelTool.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/ExplodeByLevelTool.js +0 -1
- package/dist/core/components/viewers/bim/src/tools/ExplodeByLevelTool.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/measureBimTool.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/measureBimTool.js +1 -2
- package/dist/core/components/viewers/bim/src/tools/measureBimTool.js.map +1 -1
- package/dist/core/components/viewers/map/MapViewer.d.ts +1 -2
- package/dist/core/components/viewers/map/MapViewer.d.ts.map +1 -1
- package/dist/core/components/viewers/map/MapViewer.js +2 -2
- package/dist/core/components/viewers/map/MapViewer.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/arcGISDatasets.d.ts.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/arcGISDatasets.js +11 -14
- package/dist/core/components/viewers/map/datasets/src/arcGISDatasets.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/ckanDatasets.d.ts.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/ckanDatasets.js +2 -5
- package/dist/core/components/viewers/map/datasets/src/ckanDatasets.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/localDatasets.d.ts.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/localDatasets.js +0 -3
- package/dist/core/components/viewers/map/datasets/src/localDatasets.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/opendatasoftDatasets.d.ts.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/opendatasoftDatasets.js +0 -11
- package/dist/core/components/viewers/map/datasets/src/opendatasoftDatasets.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/DatabaseBuildingPopover/src/BuildingsTools.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/DatabaseBuildingPopover/src/BuildingsTools.js +0 -1
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/DatabaseBuildingPopover/src/BuildingsTools.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/NonDatabaseBuildingPopover.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/NonDatabaseBuildingPopover.js +0 -1
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/NonDatabaseBuildingPopover.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/OpenDataFeaturePopover.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/OpenDataFeaturePopover.js +0 -1
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/OpenDataFeaturePopover.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/index.d.ts +1 -2
- package/dist/core/components/viewers/map/src/MapLayers/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/index.js +2 -2
- package/dist/core/components/viewers/map/src/MapLayers/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/CommentLayer/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/CommentLayer/index.js +174 -53
- package/dist/core/components/viewers/map/src/MapLayers/src/CommentLayer/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/FileLayer/components/MapFileManager.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/FileLayer/components/MapFileManager.js +2 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/FileLayer/components/MapFileManager.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.d.ts +1 -3
- package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.js +74 -42
- package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/TabSelector.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/TabSelector.js +4 -3
- package/dist/core/components/viewers/map/src/MapSidebar/TabSelector.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/index.js +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/FileTab/src/FilesSection.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/FileTab/src/FilesSection.js +0 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/FileTab/src/FilesSection.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/FileTab/src/ModelsSection.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/FileTab/src/ModelsSection.js +1 -5
- package/dist/core/components/viewers/map/src/MapSidebar/src/FileTab/src/ModelsSection.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.d.ts +1 -3
- package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.js +2 -2
- package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/tools/AddTools/AddFile/utils/uploadToPresignedURLS.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/tools/AddTools/AddFile/utils/uploadToPresignedURLS.js +1 -3
- package/dist/core/components/viewers/map/src/tools/AddTools/AddFile/utils/uploadToPresignedURLS.js.map +1 -1
- package/dist/core/components/viewers/map/utils/MapEventManager/MapClickManager.d.ts.map +1 -1
- package/dist/core/components/viewers/map/utils/MapEventManager/MapClickManager.js +0 -1
- package/dist/core/components/viewers/map/utils/MapEventManager/MapClickManager.js.map +1 -1
- package/dist/core/components/viewers/map/utils/flyToEach.d.ts.map +1 -1
- package/dist/core/components/viewers/map/utils/flyToEach.js +0 -1
- package/dist/core/components/viewers/map/utils/flyToEach.js.map +1 -1
- package/dist/core/components/viewers/map/utils/getOsmIdbyCoordinates.d.ts.map +1 -1
- package/dist/core/components/viewers/map/utils/getOsmIdbyCoordinates.js +0 -2
- package/dist/core/components/viewers/map/utils/getOsmIdbyCoordinates.js.map +1 -1
- package/dist/core/components/viewers/shared/markers/BimMarkerCluster.d.ts +19 -0
- package/dist/core/components/viewers/shared/markers/BimMarkerCluster.d.ts.map +1 -0
- package/dist/core/components/viewers/shared/markers/BimMarkerCluster.js +108 -0
- package/dist/core/components/viewers/shared/markers/BimMarkerCluster.js.map +1 -0
- package/dist/core/components/viewers/shared/markers/computeMarkerLookAt.d.ts +24 -0
- package/dist/core/components/viewers/shared/markers/computeMarkerLookAt.d.ts.map +1 -0
- package/dist/core/components/viewers/shared/markers/computeMarkerLookAt.js +27 -0
- package/dist/core/components/viewers/shared/markers/computeMarkerLookAt.js.map +1 -0
- package/dist/core/components/viewers/shared/markers/types.d.ts +66 -0
- package/dist/core/components/viewers/shared/markers/types.d.ts.map +1 -0
- package/dist/core/components/viewers/shared/markers/types.js +1 -0
- package/dist/core/components/viewers/shared/markers/types.js.map +1 -0
- package/dist/core/components/viewers/shared/markers/useMarkerLayerBim.d.ts +22 -0
- package/dist/core/components/viewers/shared/markers/useMarkerLayerBim.d.ts.map +1 -0
- package/dist/core/components/viewers/shared/markers/useMarkerLayerBim.js +233 -0
- package/dist/core/components/viewers/shared/markers/useMarkerLayerBim.js.map +1 -0
- package/dist/core/hooks/comments/comments.d.ts +11 -0
- package/dist/core/hooks/comments/comments.d.ts.map +1 -1
- package/dist/core/hooks/comments/comments.js +3 -1
- package/dist/core/hooks/comments/comments.js.map +1 -1
- package/dist/core/hooks/comments/createCommentHooks.d.ts +11 -0
- package/dist/core/hooks/comments/createCommentHooks.d.ts.map +1 -1
- package/dist/core/hooks/comments/createCommentHooks.js +30 -1
- package/dist/core/hooks/comments/createCommentHooks.js.map +1 -1
- package/dist/core/hooks/files/files.d.ts.map +1 -1
- package/dist/core/hooks/files/files.js +0 -1
- package/dist/core/hooks/files/files.js.map +1 -1
- package/dist/core/hooks/ui/useResizableSidebarWidth.d.ts +30 -0
- package/dist/core/hooks/ui/useResizableSidebarWidth.d.ts.map +1 -0
- package/dist/core/hooks/ui/useResizableSidebarWidth.js +75 -0
- package/dist/core/hooks/ui/useResizableSidebarWidth.js.map +1 -0
- package/dist/core/i18n/messages/en.json +10 -1
- package/dist/core/i18n/messages/es.json +11 -2
- package/dist/core/i18n/messages/fr.json +11 -2
- package/dist/core/store/AppConfig/context.d.ts.map +1 -1
- package/dist/core/store/AppConfig/context.js +4 -1
- package/dist/core/store/AppConfig/context.js.map +1 -1
- package/dist/core/store/AppConfig/reducer.d.ts +10 -0
- package/dist/core/store/AppConfig/reducer.d.ts.map +1 -1
- package/dist/core/store/AppConfig/reducer.js +10 -0
- package/dist/core/store/AppConfig/reducer.js.map +1 -1
- package/dist/core/store/Datasets/reducer.d.ts.map +1 -1
- package/dist/core/store/Datasets/reducer.js +0 -2
- package/dist/core/store/Datasets/reducer.js.map +1 -1
- package/dist/core/store/Files/reducer.js +0 -1
- package/dist/core/store/Files/reducer.js.map +1 -1
- package/dist/core/store/Menus/context.d.ts.map +1 -1
- package/dist/core/store/Menus/context.js +8 -2
- package/dist/core/store/Menus/context.js.map +1 -1
- package/dist/core/store/Menus/reducer.d.ts +65 -1
- package/dist/core/store/Menus/reducer.d.ts.map +1 -1
- package/dist/core/store/Menus/reducer.js +39 -3
- package/dist/core/store/Menus/reducer.js.map +1 -1
- package/dist/core/utils/markerUtils.d.ts +10 -0
- package/dist/core/utils/markerUtils.d.ts.map +1 -1
- package/dist/core/utils/markerUtils.js +9 -1
- package/dist/core/utils/markerUtils.js.map +1 -1
- package/dist/core/utils/memcache.d.ts.map +1 -1
- package/dist/core/utils/memcache.js +0 -7
- package/dist/core/utils/memcache.js.map +1 -1
- package/dist/core/utils/timeUtils.d.ts +24 -1
- package/dist/core/utils/timeUtils.d.ts.map +1 -1
- package/dist/core/utils/timeUtils.js +52 -3
- package/dist/core/utils/timeUtils.js.map +1 -1
- package/package.json +4 -1
|
@@ -43,7 +43,6 @@ function ToolbarButton({ tool, onClick }) {
|
|
|
43
43
|
const { currentToolId } = toolsState.tools;
|
|
44
44
|
const isActive = currentToolId === tool.id;
|
|
45
45
|
const handleToolClick = (tool2) => {
|
|
46
|
-
console.log("Tool clicked", tool2);
|
|
47
46
|
if (isActive) {
|
|
48
47
|
setTools(null);
|
|
49
48
|
setCursor("");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/core/components/ui/ToolbarButton.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// Components\r\nimport * as React from 'react'\r\n\r\nimport { MapContext, MenusContext, ToolsContext } from '../../store'\r\n\r\nimport { Button } from './Button'\r\n\r\nimport type { CursorType } from '../../types/global'\r\nimport type { ToolbarToolType, Tool } from '../../types/tools'\r\n\r\ninterface Props {\r\n tool: Tool\r\n onClick?: () => void // Rename to onClick for clarity\r\n}\r\n\r\nexport default function ToolbarButton({ tool, onClick }: Props) {\r\n const { dispatch: toolsDispatch, state: toolsState }\r\n = React.useContext(ToolsContext)\r\n const { dispatch: menusDispatch, state: menusState }\r\n = React.useContext(MenusContext)\r\n const { dispatch: mapDispatch, state: mapState }\r\n = React.useContext(MapContext)\r\n\r\n // If this tool has a custom component, render that instead\r\n if (tool.component) {\r\n const CustomComponent = tool.component\r\n return <CustomComponent tool={tool} {...(tool.extraProps ?? {})} />\r\n }\r\n\r\n const setCursor = (cursor: CursorType) => {\r\n mapDispatch({\r\n type: 'SET_CURSOR',\r\n payload: { cursor },\r\n })\r\n }\r\n\r\n const setTools = (currentTool: ToolbarToolType) => {\r\n toolsDispatch({\r\n type: 'SET-TOOL',\r\n payload: { currentToolId: currentTool },\r\n })\r\n }\r\n\r\n const { currentToolId } = toolsState.tools\r\n const isActive = currentToolId === tool.id\r\n\r\n const handleToolClick = (tool: Tool) => { // Rename the internal function\r\n
|
|
1
|
+
{"version":3,"sources":["../../../../src/core/components/ui/ToolbarButton.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// Components\r\nimport * as React from 'react'\r\n\r\nimport { MapContext, MenusContext, ToolsContext } from '../../store'\r\n\r\nimport { Button } from './Button'\r\n\r\nimport type { CursorType } from '../../types/global'\r\nimport type { ToolbarToolType, Tool } from '../../types/tools'\r\n\r\ninterface Props {\r\n tool: Tool\r\n onClick?: () => void // Rename to onClick for clarity\r\n}\r\n\r\nexport default function ToolbarButton({ tool, onClick }: Props) {\r\n const { dispatch: toolsDispatch, state: toolsState }\r\n = React.useContext(ToolsContext)\r\n const { dispatch: menusDispatch, state: menusState }\r\n = React.useContext(MenusContext)\r\n const { dispatch: mapDispatch, state: mapState }\r\n = React.useContext(MapContext)\r\n\r\n // If this tool has a custom component, render that instead\r\n if (tool.component) {\r\n const CustomComponent = tool.component\r\n return <CustomComponent tool={tool} {...(tool.extraProps ?? {})} />\r\n }\r\n\r\n const setCursor = (cursor: CursorType) => {\r\n mapDispatch({\r\n type: 'SET_CURSOR',\r\n payload: { cursor },\r\n })\r\n }\r\n\r\n const setTools = (currentTool: ToolbarToolType) => {\r\n toolsDispatch({\r\n type: 'SET-TOOL',\r\n payload: { currentToolId: currentTool },\r\n })\r\n }\r\n\r\n const { currentToolId } = toolsState.tools\r\n const isActive = currentToolId === tool.id\r\n\r\n const handleToolClick = (tool: Tool) => { // Rename the internal function\r\n if (isActive) {\r\n // Deactivate the tool\r\n setTools(null)\r\n setCursor('')\r\n }\r\n else {\r\n // Activate the tool\r\n setTools(tool.id)\r\n setCursor(tool.cursor)\r\n }\r\n }\r\n\r\n const h = tool.height ? tool.height : '8'\r\n const w = tool.width ? tool.width : '7'\r\n\r\n return (\r\n <Button\r\n size=\"icon\"\r\n variant=\"ghost\"\r\n className={`flex justify-center items-center h-${h} w-${w} pointer-events-auto text-primary-dark ${\r\n tool.stayActive && isActive ? 'bg-primary-dark/15' : ''\r\n }`}\r\n onClick={() => {\r\n handleToolClick(tool) // Call internal logic first\r\n onClick?.() // Then call the prop if provided\r\n }}\r\n title={tool.title}\r\n >\r\n <tool.icon className=\"w-5 h-5\" />\r\n </Button>\r\n )\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;AA+BW;AAzBX,YAAY,WAAW;AAEvB,SAAS,YAAY,cAAc,oBAAoB;AAEvD,SAAS,cAAc;AAUR,SAAR,cAA+B,EAAE,MAAM,QAAQ,GAAU;AApBhE;AAqBE,QAAM,EAAE,UAAU,eAAe,OAAO,WAAW,IAC/C,MAAM,WAAW,YAAY;AACjC,QAAM,EAAE,UAAU,eAAe,OAAO,WAAW,IAC/C,MAAM,WAAW,YAAY;AACjC,QAAM,EAAE,UAAU,aAAa,OAAO,SAAS,IAC3C,MAAM,WAAW,UAAU;AAG/B,MAAI,KAAK,WAAW;AAClB,UAAM,kBAAkB,KAAK;AAC7B,WAAO,oBAAC,kCAAgB,SAAiB,UAAK,eAAL,YAAmB,CAAC,EAAI;AAAA,EACnE;AAEA,QAAM,YAAY,CAAC,WAAuB;AACxC,gBAAY;AAAA,MACV,MAAM;AAAA,MACN,SAAS,EAAE,OAAO;AAAA,IACpB,CAAC;AAAA,EACH;AAEA,QAAM,WAAW,CAAC,gBAAiC;AACjD,kBAAc;AAAA,MACZ,MAAM;AAAA,MACN,SAAS,EAAE,eAAe,YAAY;AAAA,IACxC,CAAC;AAAA,EACH;AAEA,QAAM,EAAE,cAAc,IAAI,WAAW;AACrC,QAAM,WAAW,kBAAkB,KAAK;AAExC,QAAM,kBAAkB,CAACA,UAAe;AACtC,QAAI,UAAU;AAEZ,eAAS,IAAI;AACb,gBAAU,EAAE;AAAA,IACd,OACK;AAEH,eAASA,MAAK,EAAE;AAChB,gBAAUA,MAAK,MAAM;AAAA,IACvB;AAAA,EACF;AAEA,QAAM,IAAI,KAAK,SAAS,KAAK,SAAS;AACtC,QAAM,IAAI,KAAK,QAAQ,KAAK,QAAQ;AAEpC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,SAAQ;AAAA,MACR,WAAW,sCAAsC,CAAC,MAAM,CAAC,0CACvD,KAAK,cAAc,WAAW,uBAAuB,EACvD;AAAA,MACA,SAAS,MAAM;AACb,wBAAgB,IAAI;AACpB;AAAA,MACF;AAAA,MACA,OAAO,KAAK;AAAA,MAEZ,8BAAC,KAAK,MAAL,EAAU,WAAU,WAAU;AAAA;AAAA,EACjC;AAEJ;","names":["tool"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FiltersDialog.d.ts","sourceRoot":"","sources":["../../../../../../src/core/components/viewers/Data/advancedFilter/FiltersDialog.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAuB/B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAM1D,KAAK,kBAAkB,GAAG;IAExB,YAAY,CAAC,EAAE,kBAAkB,EAAE,CAAA;IACnC,OAAO,EAAE,GAAG,EAAE,CAAA;IACd,aAAa,EAAE,GAAG,EAAE,CAAA;IACpB,UAAU,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;IACvD,gBAAgB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;IAC7D,cAAc,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,KAAK,IAAI,CAAA;IACxC,IAAI,CAAC,EAAE,GAAG,EAAE,CAAA;IACZ,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB,CAAA;AAED,QAAA,MAAM,aAAa,GAAI,iIASpB,kBAAkB,
|
|
1
|
+
{"version":3,"file":"FiltersDialog.d.ts","sourceRoot":"","sources":["../../../../../../src/core/components/viewers/Data/advancedFilter/FiltersDialog.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAuB/B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAM1D,KAAK,kBAAkB,GAAG;IAExB,YAAY,CAAC,EAAE,kBAAkB,EAAE,CAAA;IACnC,OAAO,EAAE,GAAG,EAAE,CAAA;IACd,aAAa,EAAE,GAAG,EAAE,CAAA;IACpB,UAAU,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;IACvD,gBAAgB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;IAC7D,cAAc,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,KAAK,IAAI,CAAA;IACxC,IAAI,CAAC,EAAE,GAAG,EAAE,CAAA;IACZ,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB,CAAA;AAED,QAAA,MAAM,aAAa,GAAI,iIASpB,kBAAkB,sBAyTpB,CAAA;AAED,eAAe,aAAa,CAAA"}
|
|
@@ -192,9 +192,7 @@ const FiltersDialog = ({
|
|
|
192
192
|
]);
|
|
193
193
|
};
|
|
194
194
|
const handleApplyFilters = () => {
|
|
195
|
-
console.log("Applying filters", filters);
|
|
196
195
|
const validFilters = filters.filter((f) => f.field && f.operator && f.value !== null);
|
|
197
|
-
console.log("Valid filters:", validFilters);
|
|
198
196
|
setActiveFilters(validFilters);
|
|
199
197
|
setFilterDialogOpen(false);
|
|
200
198
|
if (onApplyFilters) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../src/core/components/viewers/Data/advancedFilter/FiltersDialog.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 { VisuallyHidden } from '@radix-ui/react-visually-hidden'\r\nimport {\r\n Plus,\r\n Filter,\r\n} from 'lucide-react'\r\nimport { useTranslations } from 'next-intl'\r\nimport * as React from \"react\";\r\n\r\n// Shadcn Components\r\n\r\nimport { Badge } from '../../../ui/Badge'\r\nimport { Button } from '../../../ui/Button'\r\nimport {\r\n Dialog,\r\n DialogContent,\r\n DialogHeader,\r\n DialogTitle,\r\n DialogTrigger,\r\n DialogFooter,\r\n DialogDescription,\r\n} from '../../../ui/Dialog'\r\nimport { Input } from '../../../ui/Input'\r\nimport { Separator } from '../../../ui/Separator'\r\n\r\n// Utils\r\nimport { useBuildingHeaders, useSiteHeaders } from '../utils/Headers'\r\n\r\nimport Filters from './Filters'\r\n\r\nimport type { filterFieldOptions } from '../utils/Columns'\r\n\r\n// Icons\r\n\r\n// Custom Components\r\n\r\ntype FiltersDialogProps = {\r\n // Make filterFields optional\r\n filterFields?: filterFieldOptions[]\r\n filters: any[]\r\n activeFilters: any[]\r\n setFilters: React.Dispatch<React.SetStateAction<any[]>>\r\n setActiveFilters: React.Dispatch<React.SetStateAction<any[]>>\r\n onApplyFilters: (filters: any[]) => void\r\n data?: any[]\r\n isBuilding?: boolean // Flag to determine if we're filtering buildings or sites\r\n}\r\n\r\nconst FiltersDialog = ({\r\n filters,\r\n filterFields: providedFilterFields,\r\n activeFilters,\r\n setFilters,\r\n onApplyFilters,\r\n setActiveFilters,\r\n data = [],\r\n isBuilding = true, // Default to building if not specified\r\n}: FiltersDialogProps) => {\r\n // Translations\r\n const t = useTranslations('FiltersDialog')\r\n\r\n const buildingHeaders = useBuildingHeaders()\r\n const siteHeaders = useSiteHeaders()\r\n\r\n const [filterDialogOpen, setFilterDialogOpen] = React.useState(false)\r\n const [filterSearch, setFilterSearch] = React.useState('')\r\n\r\n // Helper function to detect if data is buildings or sites\r\n const detectDataType = (data) => {\r\n if (!data || data.length === 0) return isBuilding // Use provided value if no data\r\n\r\n // Check the first item for building-specific properties\r\n const firstItem = data[0]\r\n if (firstItem?.buildingName !== undefined\r\n || firstItem?.buildingAddress !== undefined\r\n || firstItem?.buildingType !== undefined) {\r\n return true // It's a building\r\n }\r\n\r\n // Check for site-specific properties\r\n if (firstItem?.siteName !== undefined\r\n || firstItem?.siteAddress !== undefined\r\n || firstItem?.siteType !== undefined) {\r\n return false // It's a site\r\n }\r\n\r\n return isBuilding // Default to provided value if uncertain\r\n }\r\n\r\n // Use detected data type\r\n const dataIsBuilding = React.useMemo(() => detectDataType(data), [data, isBuilding])\r\n\r\n // Helper to extract fields from headers\r\n const extractFieldsFromHeaders = (headers) => {\r\n const organizedFields = []\r\n const duplicateLabelCheck = {} // Track duplicate labels\r\n\r\n // Common terms that often need context\r\n const commonLabels = [\r\n 'Additional Information',\r\n 'Notes',\r\n 'Description',\r\n 'Comments',\r\n 'Details',\r\n 'Status',\r\n 'Type',\r\n 'Date',\r\n 'Name',\r\n 'ID',\r\n ]\r\n\r\n // Define enum field mappings\r\n const enumFieldMappings = {\r\n // Building enum fields\r\n buildingCountrySubdivision: 'enum',\r\n buildingAssessmentCondition: 'enum',\r\n buildingGeometryStyle: 'enum',\r\n buildingProjectPhase: 'enum',\r\n buildingProjectType: 'enum',\r\n buildingHeatingCoolingEnergySources: 'enum',\r\n buildingHotWaterSystemsEnergySources: 'enum',\r\n buildingOccupantType: 'enum',\r\n buildingManagementType: 'enum',\r\n buildingType: 'enum',\r\n buildingElectricityServiceSize: 'enum',\r\n buildingElectricityServiceLocation: 'enum',\r\n\r\n // Site enum fields\r\n siteCountrySubdivision: 'enum',\r\n siteEnergyUsage: 'enum',\r\n siteAssessmentCondition: 'enum',\r\n siteProjectPhase: 'enum',\r\n siteProjectType: 'enum',\r\n siteLandUse: 'enum',\r\n }\r\n\r\n for (const section of headers) {\r\n // Create a group for each major section\r\n const sectionGroup = {\r\n title: section.title,\r\n fields: [],\r\n }\r\n\r\n for (const subsection of section.subsections) {\r\n for (const field of subsection.fields) {\r\n // Skip complex objects and arrays\r\n if (!field || !field.property\r\n || field.property === 'associatedBuildings'\r\n || field.property === 'siteBuildings'\r\n || Array.isArray(field.property)) {\r\n continue\r\n }\r\n\r\n // Track duplicate labels\r\n if (field.label) {\r\n duplicateLabelCheck[field.label] = (duplicateLabelCheck[field.label] || 0) + 1\r\n }\r\n\r\n // Create label with context if needed\r\n let displayLabel = field.label || field.property\r\n\r\n // Convert React nodes to strings\r\n if (typeof displayLabel === 'object' && displayLabel?.props?.children) {\r\n // Extract text from React fragments\r\n displayLabel = Array.isArray(displayLabel.props.children)\r\n ? displayLabel.props.children\r\n .map(child => typeof child === 'string' ? child : child?.props?.children || '')\r\n .join('')\r\n .trim()\r\n : displayLabel.props.children\r\n }\r\n\r\n // Check if it's a common term or already found as duplicate\r\n const needsContext\r\n = commonLabels.some((term) => {\r\n const labelText = typeof displayLabel === 'string'\r\n ? displayLabel\r\n : (typeof displayLabel?.props?.children === 'string'\r\n ? displayLabel.props.children\r\n : '')\r\n return labelText.includes(term)\r\n })\r\n || duplicateLabelCheck[displayLabel] > 1\r\n\r\n if (needsContext) {\r\n displayLabel = `${displayLabel} (${section.title})`\r\n }\r\n\r\n // Determine field type\r\n let fieldType = 'string'\r\n if (enumFieldMappings[field.property]) {\r\n fieldType = 'enum'\r\n }\r\n else if (typeof field.property === 'string') {\r\n if (field.property.toLowerCase().includes('date')) {\r\n fieldType = 'date'\r\n }\r\n else if (/num|year|cost|value|area|total|sqft|height|elevation|expenses|consumption|emissions|intensity/i.test(field.property)) {\r\n fieldType = 'number'\r\n }\r\n else if (/is[A-Z]|has[A-Z]|can[A-Z]/.test(field.property)) {\r\n fieldType = 'boolean'\r\n }\r\n }\r\n\r\n sectionGroup.fields.push({\r\n id: field.property,\r\n label: displayLabel,\r\n type: fieldType,\r\n sectionTitle: section.title,\r\n subsectionTitle: subsection.title,\r\n })\r\n }\r\n }\r\n\r\n if (sectionGroup.fields.length > 0) {\r\n organizedFields.push(sectionGroup)\r\n }\r\n }\r\n\r\n return organizedFields\r\n }\r\n\r\n const organizedFields = React.useMemo(() => {\r\n return extractFieldsFromHeaders(dataIsBuilding ? buildingHeaders : siteHeaders)\r\n }, [dataIsBuilding])\r\n\r\n // Extract all fields from organized sections\r\n const headersFields = React.useMemo(() => {\r\n const fields = []\r\n for (const section of organizedFields) {\r\n for (const field of section.fields) {\r\n fields.push(field)\r\n }\r\n }\r\n return fields\r\n }, [organizedFields])\r\n\r\n // Determine which fields to use\r\n const allFilterFields = React.useMemo(() => {\r\n if (providedFilterFields && providedFilterFields.length > 0) {\r\n return providedFilterFields\r\n }\r\n return headersFields\r\n }, [providedFilterFields, headersFields])\r\n\r\n // Apply search to filter fields\r\n const filteredFields = React.useMemo(() => {\r\n if (!filterSearch) return allFilterFields\r\n\r\n // Checks for field and field.label\r\n return allFilterFields.filter((field) => {\r\n // Check if field exists and has a label property\r\n if (!field || typeof field.label !== 'string') return false\r\n\r\n return field.label.toLowerCase().includes((filterSearch || '').toLowerCase())\r\n })\r\n }, [allFilterFields, filterSearch])\r\n\r\n // Clear filters function\r\n const clearFilters = () => {\r\n setFilters([{\r\n id: 1,\r\n field: '',\r\n fieldType: null,\r\n operator: null,\r\n value: null,\r\n secondValue: null,\r\n }])\r\n setActiveFilters([])\r\n }\r\n\r\n // New filter row\r\n const addFilter = () => {\r\n setFilters([\r\n ...filters,\r\n {\r\n id: Date.now(),\r\n field: '',\r\n fieldType: null,\r\n operator: null,\r\n value: null,\r\n secondValue: null,\r\n },\r\n ])\r\n }\r\n\r\n // Handle apply filters\r\n const handleApplyFilters = () => {\r\n console.log('Applying filters', filters)\r\n const validFilters = filters.filter(f => f.field && f.operator && f.value !== null)\r\n console.log('Valid filters:', validFilters)\r\n setActiveFilters(validFilters)\r\n setFilterDialogOpen(false)\r\n\r\n // Call the onApplyFilters prop with the valid filters\r\n if (onApplyFilters) {\r\n onApplyFilters(validFilters)\r\n }\r\n }\r\n\r\n return (\r\n <>\r\n <Dialog open={filterDialogOpen} onOpenChange={setFilterDialogOpen}>\r\n {/* Advanced Filters Button */}\r\n <DialogTrigger asChild>\r\n <Button variant=\"outline\" className=\"border-dashed\">\r\n {activeFilters.length > 0\r\n ? (\r\n <>\r\n <Badge>\r\n {activeFilters.length}\r\n </Badge>\r\n {t('advanced')}\r\n </>\r\n )\r\n : (\r\n <>\r\n <Filter />\r\n {t('advanced')}\r\n </>\r\n )}\r\n </Button>\r\n </DialogTrigger>\r\n\r\n <DialogContent className=\"w-[65%] max-w-[900px]\">\r\n <DialogHeader className=\"pb-5\">\r\n <DialogTitle>{t('header')}</DialogTitle>\r\n <VisuallyHidden>\r\n <DialogDescription>{t('description')}</DialogDescription>\r\n </VisuallyHidden>\r\n </DialogHeader>\r\n\r\n {/* Filters */}\r\n <Filters\r\n filters={filters}\r\n filterFields={filteredFields}\r\n organizedFields={organizedFields}\r\n setFilters={setFilters}\r\n onApplyFilters={handleApplyFilters}\r\n filterSearchState={filterSearch}\r\n setFilterSearchState={setFilterSearch}\r\n />\r\n\r\n <Separator />\r\n\r\n <DialogFooter>\r\n <div className=\"w-full flex justify-between\">\r\n <Button\r\n variant=\"outline\"\r\n onClick={addFilter}\r\n >\r\n <Plus />\r\n {t('add')}\r\n </Button>\r\n <div>\r\n <Button onClick={clearFilters} className=\"mr-2\">\r\n {t('clear')}\r\n </Button>\r\n <Button\r\n onClick={handleApplyFilters}\r\n disabled={!filters.some(f => f.field && f.operator && f.value !== null && f.value !== '')}\r\n >\r\n {t('apply')}\r\n </Button>\r\n </div>\r\n </div>\r\n </DialogFooter>\r\n </DialogContent>\r\n </Dialog>\r\n </>\r\n )\r\n}\r\n\r\nexport default FiltersDialog"],"mappings":"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","names":["data","organizedFields","_a"]}
|
|
1
|
+
{"version":3,"sources":["../../../../../../src/core/components/viewers/Data/advancedFilter/FiltersDialog.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 { VisuallyHidden } from '@radix-ui/react-visually-hidden'\r\nimport {\r\n Plus,\r\n Filter,\r\n} from 'lucide-react'\r\nimport { useTranslations } from 'next-intl'\r\nimport * as React from \"react\";\r\n\r\n// Shadcn Components\r\n\r\nimport { Badge } from '../../../ui/Badge'\r\nimport { Button } from '../../../ui/Button'\r\nimport {\r\n Dialog,\r\n DialogContent,\r\n DialogHeader,\r\n DialogTitle,\r\n DialogTrigger,\r\n DialogFooter,\r\n DialogDescription,\r\n} from '../../../ui/Dialog'\r\nimport { Input } from '../../../ui/Input'\r\nimport { Separator } from '../../../ui/Separator'\r\n\r\n// Utils\r\nimport { useBuildingHeaders, useSiteHeaders } from '../utils/Headers'\r\n\r\nimport Filters from './Filters'\r\n\r\nimport type { filterFieldOptions } from '../utils/Columns'\r\n\r\n// Icons\r\n\r\n// Custom Components\r\n\r\ntype FiltersDialogProps = {\r\n // Make filterFields optional\r\n filterFields?: filterFieldOptions[]\r\n filters: any[]\r\n activeFilters: any[]\r\n setFilters: React.Dispatch<React.SetStateAction<any[]>>\r\n setActiveFilters: React.Dispatch<React.SetStateAction<any[]>>\r\n onApplyFilters: (filters: any[]) => void\r\n data?: any[]\r\n isBuilding?: boolean // Flag to determine if we're filtering buildings or sites\r\n}\r\n\r\nconst FiltersDialog = ({\r\n filters,\r\n filterFields: providedFilterFields,\r\n activeFilters,\r\n setFilters,\r\n onApplyFilters,\r\n setActiveFilters,\r\n data = [],\r\n isBuilding = true, // Default to building if not specified\r\n}: FiltersDialogProps) => {\r\n // Translations\r\n const t = useTranslations('FiltersDialog')\r\n\r\n const buildingHeaders = useBuildingHeaders()\r\n const siteHeaders = useSiteHeaders()\r\n\r\n const [filterDialogOpen, setFilterDialogOpen] = React.useState(false)\r\n const [filterSearch, setFilterSearch] = React.useState('')\r\n\r\n // Helper function to detect if data is buildings or sites\r\n const detectDataType = (data) => {\r\n if (!data || data.length === 0) return isBuilding // Use provided value if no data\r\n\r\n // Check the first item for building-specific properties\r\n const firstItem = data[0]\r\n if (firstItem?.buildingName !== undefined\r\n || firstItem?.buildingAddress !== undefined\r\n || firstItem?.buildingType !== undefined) {\r\n return true // It's a building\r\n }\r\n\r\n // Check for site-specific properties\r\n if (firstItem?.siteName !== undefined\r\n || firstItem?.siteAddress !== undefined\r\n || firstItem?.siteType !== undefined) {\r\n return false // It's a site\r\n }\r\n\r\n return isBuilding // Default to provided value if uncertain\r\n }\r\n\r\n // Use detected data type\r\n const dataIsBuilding = React.useMemo(() => detectDataType(data), [data, isBuilding])\r\n\r\n // Helper to extract fields from headers\r\n const extractFieldsFromHeaders = (headers) => {\r\n const organizedFields = []\r\n const duplicateLabelCheck = {} // Track duplicate labels\r\n\r\n // Common terms that often need context\r\n const commonLabels = [\r\n 'Additional Information',\r\n 'Notes',\r\n 'Description',\r\n 'Comments',\r\n 'Details',\r\n 'Status',\r\n 'Type',\r\n 'Date',\r\n 'Name',\r\n 'ID',\r\n ]\r\n\r\n // Define enum field mappings\r\n const enumFieldMappings = {\r\n // Building enum fields\r\n buildingCountrySubdivision: 'enum',\r\n buildingAssessmentCondition: 'enum',\r\n buildingGeometryStyle: 'enum',\r\n buildingProjectPhase: 'enum',\r\n buildingProjectType: 'enum',\r\n buildingHeatingCoolingEnergySources: 'enum',\r\n buildingHotWaterSystemsEnergySources: 'enum',\r\n buildingOccupantType: 'enum',\r\n buildingManagementType: 'enum',\r\n buildingType: 'enum',\r\n buildingElectricityServiceSize: 'enum',\r\n buildingElectricityServiceLocation: 'enum',\r\n\r\n // Site enum fields\r\n siteCountrySubdivision: 'enum',\r\n siteEnergyUsage: 'enum',\r\n siteAssessmentCondition: 'enum',\r\n siteProjectPhase: 'enum',\r\n siteProjectType: 'enum',\r\n siteLandUse: 'enum',\r\n }\r\n\r\n for (const section of headers) {\r\n // Create a group for each major section\r\n const sectionGroup = {\r\n title: section.title,\r\n fields: [],\r\n }\r\n\r\n for (const subsection of section.subsections) {\r\n for (const field of subsection.fields) {\r\n // Skip complex objects and arrays\r\n if (!field || !field.property\r\n || field.property === 'associatedBuildings'\r\n || field.property === 'siteBuildings'\r\n || Array.isArray(field.property)) {\r\n continue\r\n }\r\n\r\n // Track duplicate labels\r\n if (field.label) {\r\n duplicateLabelCheck[field.label] = (duplicateLabelCheck[field.label] || 0) + 1\r\n }\r\n\r\n // Create label with context if needed\r\n let displayLabel = field.label || field.property\r\n\r\n // Convert React nodes to strings\r\n if (typeof displayLabel === 'object' && displayLabel?.props?.children) {\r\n // Extract text from React fragments\r\n displayLabel = Array.isArray(displayLabel.props.children)\r\n ? displayLabel.props.children\r\n .map(child => typeof child === 'string' ? child : child?.props?.children || '')\r\n .join('')\r\n .trim()\r\n : displayLabel.props.children\r\n }\r\n\r\n // Check if it's a common term or already found as duplicate\r\n const needsContext\r\n = commonLabels.some((term) => {\r\n const labelText = typeof displayLabel === 'string'\r\n ? displayLabel\r\n : (typeof displayLabel?.props?.children === 'string'\r\n ? displayLabel.props.children\r\n : '')\r\n return labelText.includes(term)\r\n })\r\n || duplicateLabelCheck[displayLabel] > 1\r\n\r\n if (needsContext) {\r\n displayLabel = `${displayLabel} (${section.title})`\r\n }\r\n\r\n // Determine field type\r\n let fieldType = 'string'\r\n if (enumFieldMappings[field.property]) {\r\n fieldType = 'enum'\r\n }\r\n else if (typeof field.property === 'string') {\r\n if (field.property.toLowerCase().includes('date')) {\r\n fieldType = 'date'\r\n }\r\n else if (/num|year|cost|value|area|total|sqft|height|elevation|expenses|consumption|emissions|intensity/i.test(field.property)) {\r\n fieldType = 'number'\r\n }\r\n else if (/is[A-Z]|has[A-Z]|can[A-Z]/.test(field.property)) {\r\n fieldType = 'boolean'\r\n }\r\n }\r\n\r\n sectionGroup.fields.push({\r\n id: field.property,\r\n label: displayLabel,\r\n type: fieldType,\r\n sectionTitle: section.title,\r\n subsectionTitle: subsection.title,\r\n })\r\n }\r\n }\r\n\r\n if (sectionGroup.fields.length > 0) {\r\n organizedFields.push(sectionGroup)\r\n }\r\n }\r\n\r\n return organizedFields\r\n }\r\n\r\n const organizedFields = React.useMemo(() => {\r\n return extractFieldsFromHeaders(dataIsBuilding ? buildingHeaders : siteHeaders)\r\n }, [dataIsBuilding])\r\n\r\n // Extract all fields from organized sections\r\n const headersFields = React.useMemo(() => {\r\n const fields = []\r\n for (const section of organizedFields) {\r\n for (const field of section.fields) {\r\n fields.push(field)\r\n }\r\n }\r\n return fields\r\n }, [organizedFields])\r\n\r\n // Determine which fields to use\r\n const allFilterFields = React.useMemo(() => {\r\n if (providedFilterFields && providedFilterFields.length > 0) {\r\n return providedFilterFields\r\n }\r\n return headersFields\r\n }, [providedFilterFields, headersFields])\r\n\r\n // Apply search to filter fields\r\n const filteredFields = React.useMemo(() => {\r\n if (!filterSearch) return allFilterFields\r\n\r\n // Checks for field and field.label\r\n return allFilterFields.filter((field) => {\r\n // Check if field exists and has a label property\r\n if (!field || typeof field.label !== 'string') return false\r\n\r\n return field.label.toLowerCase().includes((filterSearch || '').toLowerCase())\r\n })\r\n }, [allFilterFields, filterSearch])\r\n\r\n // Clear filters function\r\n const clearFilters = () => {\r\n setFilters([{\r\n id: 1,\r\n field: '',\r\n fieldType: null,\r\n operator: null,\r\n value: null,\r\n secondValue: null,\r\n }])\r\n setActiveFilters([])\r\n }\r\n\r\n // New filter row\r\n const addFilter = () => {\r\n setFilters([\r\n ...filters,\r\n {\r\n id: Date.now(),\r\n field: '',\r\n fieldType: null,\r\n operator: null,\r\n value: null,\r\n secondValue: null,\r\n },\r\n ])\r\n }\r\n\r\n // Handle apply filters\r\n const handleApplyFilters = () => {\r\n const validFilters = filters.filter(f => f.field && f.operator && f.value !== null)\r\n setActiveFilters(validFilters)\r\n setFilterDialogOpen(false)\r\n\r\n // Call the onApplyFilters prop with the valid filters\r\n if (onApplyFilters) {\r\n onApplyFilters(validFilters)\r\n }\r\n }\r\n\r\n return (\r\n <>\r\n <Dialog open={filterDialogOpen} onOpenChange={setFilterDialogOpen}>\r\n {/* Advanced Filters Button */}\r\n <DialogTrigger asChild>\r\n <Button variant=\"outline\" className=\"border-dashed\">\r\n {activeFilters.length > 0\r\n ? (\r\n <>\r\n <Badge>\r\n {activeFilters.length}\r\n </Badge>\r\n {t('advanced')}\r\n </>\r\n )\r\n : (\r\n <>\r\n <Filter />\r\n {t('advanced')}\r\n </>\r\n )}\r\n </Button>\r\n </DialogTrigger>\r\n\r\n <DialogContent className=\"w-[65%] max-w-[900px]\">\r\n <DialogHeader className=\"pb-5\">\r\n <DialogTitle>{t('header')}</DialogTitle>\r\n <VisuallyHidden>\r\n <DialogDescription>{t('description')}</DialogDescription>\r\n </VisuallyHidden>\r\n </DialogHeader>\r\n\r\n {/* Filters */}\r\n <Filters\r\n filters={filters}\r\n filterFields={filteredFields}\r\n organizedFields={organizedFields}\r\n setFilters={setFilters}\r\n onApplyFilters={handleApplyFilters}\r\n filterSearchState={filterSearch}\r\n setFilterSearchState={setFilterSearch}\r\n />\r\n\r\n <Separator />\r\n\r\n <DialogFooter>\r\n <div className=\"w-full flex justify-between\">\r\n <Button\r\n variant=\"outline\"\r\n onClick={addFilter}\r\n >\r\n <Plus />\r\n {t('add')}\r\n </Button>\r\n <div>\r\n <Button onClick={clearFilters} className=\"mr-2\">\r\n {t('clear')}\r\n </Button>\r\n <Button\r\n onClick={handleApplyFilters}\r\n disabled={!filters.some(f => f.field && f.operator && f.value !== null && f.value !== '')}\r\n >\r\n {t('apply')}\r\n </Button>\r\n </div>\r\n </div>\r\n </DialogFooter>\r\n </DialogContent>\r\n </Dialog>\r\n </>\r\n )\r\n}\r\n\r\nexport default FiltersDialog"],"mappings":"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","names":["data","organizedFields","_a"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EnergySourcesGrid.d.ts","sourceRoot":"","sources":["../../../../../../src/core/components/viewers/Data/details/EnergySourcesGrid.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,MAAM,OAAO,CAAA;AAQzB,UAAU,sBAAsB;IAC9B,UAAU,EAAE,GAAG,CAAA;IACf,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,GAAG,CAAA;IACxC,iBAAiB,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,IAAI,CAAA;IACzD,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,QAAA,MAAM,iBAAiB,GAAI,2DAA2D,sBAAsB,
|
|
1
|
+
{"version":3,"file":"EnergySourcesGrid.d.ts","sourceRoot":"","sources":["../../../../../../src/core/components/viewers/Data/details/EnergySourcesGrid.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,MAAM,OAAO,CAAA;AAQzB,UAAU,sBAAsB;IAC9B,UAAU,EAAE,GAAG,CAAA;IACf,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,GAAG,CAAA;IACxC,iBAAiB,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,IAAI,CAAA;IACzD,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,QAAA,MAAM,iBAAiB,GAAI,2DAA2D,sBAAsB,sBAuI3G,CAAA;AAED,eAAe,iBAAiB,CAAA"}
|
|
@@ -40,16 +40,6 @@ const EnergySourcesGrid = ({ subsection, getFieldValue, handleInputChange, editi
|
|
|
40
40
|
const selectedValues = Array.isArray(rawValue) ? rawValue : rawValue ? [] : [];
|
|
41
41
|
const metricValue = getFieldValue(row.metricProp);
|
|
42
42
|
const yearValue = getFieldValue(row.yearProp);
|
|
43
|
-
console.log("EnergySourcesGrid Debug:", {
|
|
44
|
-
label: row.label,
|
|
45
|
-
sourceProp: row.sourceProp,
|
|
46
|
-
metricProp: row.metricProp,
|
|
47
|
-
yearProp: row.yearProp,
|
|
48
|
-
rawValue,
|
|
49
|
-
selectedValues,
|
|
50
|
-
metricValue,
|
|
51
|
-
yearValue
|
|
52
|
-
});
|
|
53
43
|
return /* @__PURE__ */ jsxs(
|
|
54
44
|
"div",
|
|
55
45
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../src/core/components/viewers/Data/details/EnergySourcesGrid.tsx"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport { X } from 'lucide-react'\r\nimport React from 'react'\r\n\r\nimport { BuildingHeatingEnergySource, BuildingCoolingEnergySource, BuildingHotWaterEnergySource } from '../../../../types/dbTypes'\r\nimport { Badge } from '../../../ui/Badge'\r\nimport { Button } from '../../../ui/Button'\r\nimport { Input } from '../../../ui/Input'\r\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../../../ui/Select'\r\n\r\ninterface EnergySourcesGridProps {\r\n subsection: any\r\n getFieldValue: (property: string) => any\r\n handleInputChange: (property: string, value: any) => void\r\n editing: boolean\r\n}\r\n\r\nconst EnergySourcesGrid = ({ subsection, getFieldValue, handleInputChange, editing }: EnergySourcesGridProps) => {\r\n // Explicitly define the rows\r\n const rows = [\r\n {\r\n label: \"Heating Energy Source\",\r\n sourceProp: 'buildingHeatingEnergySources',\r\n metricProp: 'buildingHeatingMetric',\r\n yearProp: 'buildingHeatingInstallationYear',\r\n enumType: BuildingHeatingEnergySource,\r\n },\r\n {\r\n label: \"Cooling Energy Source\",\r\n sourceProp: 'buildingCoolingEnergySources',\r\n metricProp: 'buildingCoolingMetric',\r\n yearProp: 'buildingCoolingInstallationYear',\r\n enumType: BuildingCoolingEnergySource,\r\n },\r\n {\r\n label: \"Hot Water Energy Source\",\r\n sourceProp: 'buildingHotWaterSystemsEnergySources',\r\n metricProp: 'buildingHotWaterSystemsMetric',\r\n yearProp: 'buildingHotWaterSystemsInstallationYear',\r\n enumType: BuildingHotWaterEnergySource,\r\n },\r\n ]\r\n\r\n return (\r\n <div className=\"mb-6\">\r\n <div className=\"grid grid-cols-1 md:grid-cols-3 gap-4 mb-2 font-medium\">\r\n <div className=\"text-sm\">Energy Source</div>\r\n <div className=\"text-sm\">Annual Energy Use (kWh)</div>\r\n <div className=\"text-sm\">Installation Year</div>\r\n </div>\r\n <div className=\"space-y-4\">\r\n {rows.map((row, rowIdx) => {\r\n // Get the raw value and ensure it's an array\r\n const rawValue = getFieldValue(row.sourceProp)\r\n const selectedValues = Array.isArray(rawValue) ? rawValue : (rawValue ? [] : [])\r\n\r\n // Get metric and year values\r\n const metricValue = getFieldValue(row.metricProp)\r\n const yearValue = getFieldValue(row.yearProp)\r\n\r\n // DEBUG:\r\n console.log('EnergySourcesGrid Debug:', {\r\n label: row.label,\r\n sourceProp: row.sourceProp,\r\n metricProp: row.metricProp,\r\n yearProp: row.yearProp,\r\n rawValue,\r\n selectedValues,\r\n metricValue,\r\n yearValue\r\n })\r\n\r\n return (\r\n <div\r\n key={rowIdx}\r\n className=\"grid grid-cols-1 md:grid-cols-3 gap-4 items-start py-3 border-b border-border\"\r\n >\r\n {/* Source Multi-select and badges */}\r\n <div className=\"flex flex-col\">\r\n {editing ? (\r\n <>\r\n <Select\r\n value=\"\"\r\n onValueChange={val => {\r\n if (!selectedValues.includes(val)) {\r\n handleInputChange(row.sourceProp, [...selectedValues, val])\r\n }\r\n }}\r\n >\r\n <SelectTrigger>\r\n <SelectValue placeholder={`Select ${row.label.toLowerCase()}(s)`} />\r\n </SelectTrigger>\r\n <SelectContent>\r\n {Object.values(row.enumType).filter(v => !selectedValues.includes(v)).map(v => (\r\n <SelectItem key={String(v)} value={String(v)}>{(v as string).replaceAll('_', ' ')}</SelectItem>\r\n ))}\r\n </SelectContent>\r\n </Select>\r\n {selectedValues.length > 0 && (\r\n <div className=\"flex flex-wrap gap-1 mt-2\">\r\n {selectedValues.map((v: string) => (\r\n <Badge key={v} variant=\"secondary\" className=\"gap-1\">\r\n {v.replaceAll('_', ' ')}\r\n <Button\r\n variant=\"ghost\"\r\n size=\"icon\"\r\n className=\"p-0 hover:bg-transparent w-auto h-auto\"\r\n onClick={() => {\r\n const updated = selectedValues.filter(t => t !== v)\r\n handleInputChange(row.sourceProp, updated)\r\n }}\r\n >\r\n <X className=\"!w-3 !h-3\" size={8} />\r\n </Button>\r\n </Badge>\r\n ))}\r\n </div>\r\n )}\r\n </>\r\n ) : (\r\n <div className=\"flex flex-wrap gap-1 mt-1\">\r\n {selectedValues.length > 0 ? (\r\n selectedValues.map((v: string) => (\r\n <Badge key={v} variant=\"secondary\" className=\"gap-1\">\r\n {v.replaceAll('_', ' ')}\r\n </Badge>\r\n ))\r\n ) : (\r\n <span className=\"text-sm\">---</span>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n {/* Metric input */}\r\n <div className=\"flex flex-col justify-start\">\r\n {editing ? (\r\n <Input\r\n type=\"number\"\r\n value={metricValue ?? ''}\r\n onChange={e => handleInputChange(row.metricProp, e.target.value === '' ? null : Number(e.target.value))}\r\n />\r\n ) : (\r\n <span className=\"text-sm\">{metricValue ?? '---'}</span>\r\n )}\r\n </div>\r\n {/* Installation Year input */}\r\n <div className=\"flex flex-col justify-start\">\r\n {editing ? (\r\n <Input\r\n type=\"number\"\r\n value={yearValue ?? ''}\r\n onChange={e => handleInputChange(row.yearProp, e.target.value === '' ? null : Number(e.target.value))}\r\n />\r\n ) : (\r\n <span className=\"text-sm\">{yearValue ?? '---'}</span>\r\n )}\r\n </div>\r\n </div>\r\n )\r\n })}\r\n </div>\r\n </div>\r\n )\r\n}\r\n\r\nexport default EnergySourcesGrid"],"mappings":"AA+CM,SAmCY,UAlCV,KADF;AA5CN,SAAS,SAAS;AAGlB,SAAS,6BAA6B,6BAA6B,oCAAoC;AACvG,SAAS,aAAa;AACtB,SAAS,cAAc;AACvB,SAAS,aAAa;AACtB,SAAS,QAAQ,eAAe,YAAY,eAAe,mBAAmB;AAS9E,MAAM,oBAAoB,CAAC,EAAE,YAAY,eAAe,mBAAmB,QAAQ,MAA8B;AAE/G,QAAM,OAAO;AAAA,IACX;AAAA,MACE,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,UAAU;AAAA,MACV,UAAU;AAAA,IACZ;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,UAAU;AAAA,MACV,UAAU;AAAA,IACZ;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,UAAU;AAAA,MACV,UAAU;AAAA,IACZ;AAAA,EACF;AAEA,SACE,qBAAC,SAAI,WAAU,QACb;AAAA,yBAAC,SAAI,WAAU,0DACb;AAAA,0BAAC,SAAI,WAAU,WAAU,2BAAa;AAAA,MACtC,oBAAC,SAAI,WAAU,WAAU,qCAAuB;AAAA,MAChD,oBAAC,SAAI,WAAU,WAAU,+BAAiB;AAAA,OAC5C;AAAA,IACA,oBAAC,SAAI,WAAU,aACZ,eAAK,IAAI,CAAC,KAAK,WAAW;AAEzB,YAAM,WAAW,cAAc,IAAI,UAAU;AAC7C,YAAM,iBAAiB,MAAM,QAAQ,QAAQ,IAAI,WAAY,WAAW,CAAC,IAAI,CAAC;AAG9E,YAAM,cAAc,cAAc,IAAI,UAAU;AAChD,YAAM,YAAY,cAAc,IAAI,QAAQ;AAG5C,cAAQ,IAAI,4BAA4B;AAAA,QACtC,OAAO,IAAI;AAAA,QACX,YAAY,IAAI;AAAA,QAChB,YAAY,IAAI;AAAA,QAChB,UAAU,IAAI;AAAA,QACd;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAED,aACE;AAAA,QAAC;AAAA;AAAA,UAEC,WAAU;AAAA,UAGV;AAAA,gCAAC,SAAI,WAAU,iBACZ,oBACC,iCACE;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,OAAM;AAAA,kBACN,eAAe,SAAO;AACpB,wBAAI,CAAC,eAAe,SAAS,GAAG,GAAG;AACjC,wCAAkB,IAAI,YAAY,CAAC,GAAG,gBAAgB,GAAG,CAAC;AAAA,oBAC5D;AAAA,kBACF;AAAA,kBAEA;AAAA,wCAAC,iBACC,8BAAC,eAAY,aAAa,UAAU,IAAI,MAAM,YAAY,CAAC,OAAO,GACpE;AAAA,oBACA,oBAAC,iBACE,iBAAO,OAAO,IAAI,QAAQ,EAAE,OAAO,OAAK,CAAC,eAAe,SAAS,CAAC,CAAC,EAAE,IAAI,OACxE,oBAAC,cAA2B,OAAO,OAAO,CAAC,GAAK,YAAa,WAAW,KAAK,GAAG,KAA/D,OAAO,CAAC,CAAyD,CACnF,GACH;AAAA;AAAA;AAAA,cACF;AAAA,cACC,eAAe,SAAS,KACvB,oBAAC,SAAI,WAAU,6BACZ,yBAAe,IAAI,CAAC,MACnB,qBAAC,SAAc,SAAQ,aAAY,WAAU,SAC1C;AAAA,kBAAE,WAAW,KAAK,GAAG;AAAA,gBACtB;AAAA,kBAAC;AAAA;AAAA,oBACC,SAAQ;AAAA,oBACR,MAAK;AAAA,oBACL,WAAU;AAAA,oBACV,SAAS,MAAM;AACb,4BAAM,UAAU,eAAe,OAAO,OAAK,MAAM,CAAC;AAClD,wCAAkB,IAAI,YAAY,OAAO;AAAA,oBAC3C;AAAA,oBAEA,8BAAC,KAAE,WAAU,aAAY,MAAM,GAAG;AAAA;AAAA,gBACpC;AAAA,mBAZU,CAaZ,CACD,GACH;AAAA,eAEJ,IAEA,oBAAC,SAAI,WAAU,6BACZ,yBAAe,SAAS,IACvB,eAAe,IAAI,CAAC,MAClB,oBAAC,SAAc,SAAQ,aAAY,WAAU,SAC1C,YAAE,WAAW,KAAK,GAAG,KADZ,CAEZ,CACD,IAED,oBAAC,UAAK,WAAU,WAAU,iBAAG,GAEjC,GAEJ;AAAA,YAEA,oBAAC,SAAI,WAAU,+BACZ,oBACC;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,OAAO,oCAAe;AAAA,gBACtB,UAAU,OAAK,kBAAkB,IAAI,YAAY,EAAE,OAAO,UAAU,KAAK,OAAO,OAAO,EAAE,OAAO,KAAK,CAAC;AAAA;AAAA,YACxG,IAEA,oBAAC,UAAK,WAAU,WAAW,8CAAe,OAAM,GAEpD;AAAA,YAEA,oBAAC,SAAI,WAAU,+BACZ,oBACC;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,OAAO,gCAAa;AAAA,gBACpB,UAAU,OAAK,kBAAkB,IAAI,UAAU,EAAE,OAAO,UAAU,KAAK,OAAO,OAAO,EAAE,OAAO,KAAK,CAAC;AAAA;AAAA,YACtG,IAEA,oBAAC,UAAK,WAAU,WAAW,0CAAa,OAAM,GAElD;AAAA;AAAA;AAAA,QAlFK;AAAA,MAmFP;AAAA,IAEJ,CAAC,GACH;AAAA,KACF;AAEJ;AAEA,IAAO,4BAAQ;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../../../src/core/components/viewers/Data/details/EnergySourcesGrid.tsx"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport { X } from 'lucide-react'\r\nimport React from 'react'\r\n\r\nimport { BuildingHeatingEnergySource, BuildingCoolingEnergySource, BuildingHotWaterEnergySource } from '../../../../types/dbTypes'\r\nimport { Badge } from '../../../ui/Badge'\r\nimport { Button } from '../../../ui/Button'\r\nimport { Input } from '../../../ui/Input'\r\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../../../ui/Select'\r\n\r\ninterface EnergySourcesGridProps {\r\n subsection: any\r\n getFieldValue: (property: string) => any\r\n handleInputChange: (property: string, value: any) => void\r\n editing: boolean\r\n}\r\n\r\nconst EnergySourcesGrid = ({ subsection, getFieldValue, handleInputChange, editing }: EnergySourcesGridProps) => {\r\n // Explicitly define the rows\r\n const rows = [\r\n {\r\n label: \"Heating Energy Source\",\r\n sourceProp: 'buildingHeatingEnergySources',\r\n metricProp: 'buildingHeatingMetric',\r\n yearProp: 'buildingHeatingInstallationYear',\r\n enumType: BuildingHeatingEnergySource,\r\n },\r\n {\r\n label: \"Cooling Energy Source\",\r\n sourceProp: 'buildingCoolingEnergySources',\r\n metricProp: 'buildingCoolingMetric',\r\n yearProp: 'buildingCoolingInstallationYear',\r\n enumType: BuildingCoolingEnergySource,\r\n },\r\n {\r\n label: \"Hot Water Energy Source\",\r\n sourceProp: 'buildingHotWaterSystemsEnergySources',\r\n metricProp: 'buildingHotWaterSystemsMetric',\r\n yearProp: 'buildingHotWaterSystemsInstallationYear',\r\n enumType: BuildingHotWaterEnergySource,\r\n },\r\n ]\r\n\r\n return (\r\n <div className=\"mb-6\">\r\n <div className=\"grid grid-cols-1 md:grid-cols-3 gap-4 mb-2 font-medium\">\r\n <div className=\"text-sm\">Energy Source</div>\r\n <div className=\"text-sm\">Annual Energy Use (kWh)</div>\r\n <div className=\"text-sm\">Installation Year</div>\r\n </div>\r\n <div className=\"space-y-4\">\r\n {rows.map((row, rowIdx) => {\r\n // Get the raw value and ensure it's an array\r\n const rawValue = getFieldValue(row.sourceProp)\r\n const selectedValues = Array.isArray(rawValue) ? rawValue : (rawValue ? [] : [])\r\n\r\n // Get metric and year values\r\n const metricValue = getFieldValue(row.metricProp)\r\n const yearValue = getFieldValue(row.yearProp)\r\n\r\n\r\n return (\r\n <div\r\n key={rowIdx}\r\n className=\"grid grid-cols-1 md:grid-cols-3 gap-4 items-start py-3 border-b border-border\"\r\n >\r\n {/* Source Multi-select and badges */}\r\n <div className=\"flex flex-col\">\r\n {editing ? (\r\n <>\r\n <Select\r\n value=\"\"\r\n onValueChange={val => {\r\n if (!selectedValues.includes(val)) {\r\n handleInputChange(row.sourceProp, [...selectedValues, val])\r\n }\r\n }}\r\n >\r\n <SelectTrigger>\r\n <SelectValue placeholder={`Select ${row.label.toLowerCase()}(s)`} />\r\n </SelectTrigger>\r\n <SelectContent>\r\n {Object.values(row.enumType).filter(v => !selectedValues.includes(v)).map(v => (\r\n <SelectItem key={String(v)} value={String(v)}>{(v as string).replaceAll('_', ' ')}</SelectItem>\r\n ))}\r\n </SelectContent>\r\n </Select>\r\n {selectedValues.length > 0 && (\r\n <div className=\"flex flex-wrap gap-1 mt-2\">\r\n {selectedValues.map((v: string) => (\r\n <Badge key={v} variant=\"secondary\" className=\"gap-1\">\r\n {v.replaceAll('_', ' ')}\r\n <Button\r\n variant=\"ghost\"\r\n size=\"icon\"\r\n className=\"p-0 hover:bg-transparent w-auto h-auto\"\r\n onClick={() => {\r\n const updated = selectedValues.filter(t => t !== v)\r\n handleInputChange(row.sourceProp, updated)\r\n }}\r\n >\r\n <X className=\"!w-3 !h-3\" size={8} />\r\n </Button>\r\n </Badge>\r\n ))}\r\n </div>\r\n )}\r\n </>\r\n ) : (\r\n <div className=\"flex flex-wrap gap-1 mt-1\">\r\n {selectedValues.length > 0 ? (\r\n selectedValues.map((v: string) => (\r\n <Badge key={v} variant=\"secondary\" className=\"gap-1\">\r\n {v.replaceAll('_', ' ')}\r\n </Badge>\r\n ))\r\n ) : (\r\n <span className=\"text-sm\">---</span>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n {/* Metric input */}\r\n <div className=\"flex flex-col justify-start\">\r\n {editing ? (\r\n <Input\r\n type=\"number\"\r\n value={metricValue ?? ''}\r\n onChange={e => handleInputChange(row.metricProp, e.target.value === '' ? null : Number(e.target.value))}\r\n />\r\n ) : (\r\n <span className=\"text-sm\">{metricValue ?? '---'}</span>\r\n )}\r\n </div>\r\n {/* Installation Year input */}\r\n <div className=\"flex flex-col justify-start\">\r\n {editing ? (\r\n <Input\r\n type=\"number\"\r\n value={yearValue ?? ''}\r\n onChange={e => handleInputChange(row.yearProp, e.target.value === '' ? null : Number(e.target.value))}\r\n />\r\n ) : (\r\n <span className=\"text-sm\">{yearValue ?? '---'}</span>\r\n )}\r\n </div>\r\n </div>\r\n )\r\n })}\r\n </div>\r\n </div>\r\n )\r\n}\r\n\r\nexport default EnergySourcesGrid"],"mappings":"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","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DatatableFileAdder.d.ts","sourceRoot":"","sources":["../../../../../../src/core/components/viewers/Data/files/DatatableFileAdder.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAU9B,MAAM,CAAC,OAAO,UAAU,kBAAkB,
|
|
1
|
+
{"version":3,"file":"DatatableFileAdder.d.ts","sourceRoot":"","sources":["../../../../../../src/core/components/viewers/Data/files/DatatableFileAdder.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAU9B,MAAM,CAAC,OAAO,UAAU,kBAAkB,sBAsCzC"}
|
|
@@ -12,7 +12,6 @@ function DatatableFileAdder() {
|
|
|
12
12
|
const { handleAddFile, uploadState } = useFileUploadWithProgress({
|
|
13
13
|
acceptedFileTypes: "*",
|
|
14
14
|
onUploadSuccess: () => {
|
|
15
|
-
console.log("File successfully uploaded to API");
|
|
16
15
|
toast.success(t("uploadSuccess"));
|
|
17
16
|
},
|
|
18
17
|
onUploadError: (error) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../src/core/components/viewers/Data/files/DatatableFileAdder.tsx"],"sourcesContent":["\"use client\"\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport * as LR from 'lucide-react'\r\nimport { useTranslations } from 'next-intl'\r\nimport * as React from 'react'\r\nimport { toast } from 'sonner'\r\n\r\nimport { Button, LoadingSpinner } from '../../../../components/ui/'\r\nimport { useFileUploadWithProgress } from '../../../../components/ui/FilesManager'\r\nimport { usePermissions } from '../../../../store'\r\n\r\n\r\n\r\n\r\nexport default function DatatableFileAdder() {\r\n\r\n // Translations\r\n const t = useTranslations('HeadDatatableFileAddererButtons')\r\n\r\n // Permissions\r\n const { ability } = usePermissions()\r\n\r\n // Use the reusable upload hook with progress\r\n const { handleAddFile, uploadState } = useFileUploadWithProgress({\r\n acceptedFileTypes: '*',\r\n onUploadSuccess: () => {\r\n
|
|
1
|
+
{"version":3,"sources":["../../../../../../src/core/components/viewers/Data/files/DatatableFileAdder.tsx"],"sourcesContent":["\"use client\"\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport * as LR from 'lucide-react'\r\nimport { useTranslations } from 'next-intl'\r\nimport * as React from 'react'\r\nimport { toast } from 'sonner'\r\n\r\nimport { Button, LoadingSpinner } from '../../../../components/ui/'\r\nimport { useFileUploadWithProgress } from '../../../../components/ui/FilesManager'\r\nimport { usePermissions } from '../../../../store'\r\n\r\n\r\n\r\n\r\nexport default function DatatableFileAdder() {\r\n\r\n // Translations\r\n const t = useTranslations('HeadDatatableFileAddererButtons')\r\n\r\n // Permissions\r\n const { ability } = usePermissions()\r\n\r\n // Use the reusable upload hook with progress\r\n const { handleAddFile, uploadState } = useFileUploadWithProgress({\r\n acceptedFileTypes: '*',\r\n onUploadSuccess: () => {\r\n toast.success(t('uploadSuccess'))\r\n },\r\n onUploadError: (error) => {\r\n console.error('Error uploading file:', error)\r\n toast.error(error.message)\r\n }\r\n })\r\n\r\n return (\r\n <div className=\"inline-block\">\r\n <Button\r\n variant=\"default\"\r\n onClick={handleAddFile}\r\n disabled={!ability.can('create', 'File') || uploadState.uploading}\r\n >\r\n {uploadState.uploading\r\n ? (\r\n <LoadingSpinner />\r\n )\r\n : (\r\n <LR.CirclePlus className=\"mr-2\" />\r\n )}\r\n {uploadState.uploading ? `${t('uploading')} ${uploadState.progress}%` : t('addFile')}\r\n </Button>\r\n </div>\r\n )\r\n}\r\n"],"mappings":";AAuCM,SAOQ,KAPR;AAlCN,YAAY,QAAQ;AACpB,SAAS,uBAAuB;AAEhC,SAAS,aAAa;AAEtB,SAAS,QAAQ,sBAAsB;AACvC,SAAS,iCAAiC;AAC1C,SAAS,sBAAsB;AAKhB,SAAR,qBAAsC;AAG3C,QAAM,IAAI,gBAAgB,iCAAiC;AAG3D,QAAM,EAAE,QAAQ,IAAI,eAAe;AAGnC,QAAM,EAAE,eAAe,YAAY,IAAI,0BAA0B;AAAA,IAC/D,mBAAmB;AAAA,IACnB,iBAAiB,MAAM;AACrB,YAAM,QAAQ,EAAE,eAAe,CAAC;AAAA,IAClC;AAAA,IACA,eAAe,CAAC,UAAU;AACxB,cAAQ,MAAM,yBAAyB,KAAK;AAC5C,YAAM,MAAM,MAAM,OAAO;AAAA,IAC3B;AAAA,EACF,CAAC;AAED,SACE,oBAAC,SAAI,WAAU,gBACb;AAAA,IAAC;AAAA;AAAA,MACC,SAAQ;AAAA,MACR,SAAS;AAAA,MACT,UAAU,CAAC,QAAQ,IAAI,UAAU,MAAM,KAAK,YAAY;AAAA,MAEvD;AAAA,oBAAY,YAEP,oBAAC,kBAAe,IAGhB,oBAAC,GAAG,YAAH,EAAc,WAAU,QAAO;AAAA,QAErC,YAAY,YAAY,GAAG,EAAE,WAAW,CAAC,IAAI,YAAY,QAAQ,MAAM,EAAE,SAAS;AAAA;AAAA;AAAA,EACrF,GACF;AAEJ;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fuzzySearch.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/components/viewers/Data/files/utils/fuzzySearch.ts"],"names":[],"mappings":"AA0CA,eAAO,MAAM,oBAAoB,GAAI,WAAW,GAAG,EAAE,EAAE,YAAY,MAAM,
|
|
1
|
+
{"version":3,"file":"fuzzySearch.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/components/viewers/Data/files/utils/fuzzySearch.ts"],"names":[],"mappings":"AA0CA,eAAO,MAAM,oBAAoB,GAAI,WAAW,GAAG,EAAE,EAAE,YAAY,MAAM,UAsBxE,CAAA"}
|
|
@@ -47,7 +47,6 @@ const fuzzySearchBuildings = (buildings, searchTerm) => {
|
|
|
47
47
|
const addressScore = fuzzySearchScore(searchTerm, address);
|
|
48
48
|
const propertyScore = fuzzySearchScore(searchTerm, propertyName);
|
|
49
49
|
const maxScore = Math.max(addressScore, propertyScore);
|
|
50
|
-
if (building.id == 837) console.log("match");
|
|
51
50
|
return __spreadProps(__spreadValues({}, building), {
|
|
52
51
|
searchScore: maxScore,
|
|
53
52
|
matchedField: addressScore > propertyScore ? "address" : "propertyName"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../src/core/components/viewers/Data/files/utils/fuzzySearch.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\n// Fuzzy search utility functions\r\nconst fuzzySearchScore = (searchTerm: string, text: string): number => {\r\n if (!searchTerm || !text) return 0\r\n\r\n const search = searchTerm.toLowerCase()\r\n const target = text.toLowerCase()\r\n\r\n // Exact match gets highest score\r\n if (target === search) return 1000\r\n\r\n // Starts with search term gets high score\r\n if (target.startsWith(search)) return 800\r\n\r\n // Contains search term gets medium score\r\n if (target.includes(search)) return 600\r\n\r\n // Fuzzy matching - calculate similarity based on character matching\r\n let score = 0\r\n let searchIndex = 0\r\n\r\n for (let i = 0; i < target.length && searchIndex < search.length; i++) {\r\n if (target[i] === search[searchIndex]) {\r\n score += 10\r\n searchIndex++\r\n }\r\n }\r\n\r\n // Bonus for matching all characters in sequence\r\n if (searchIndex === search.length) {\r\n score += 100\r\n }\r\n\r\n // Penalize for length difference\r\n const lengthDiff = Math.abs(target.length - search.length)\r\n score -= lengthDiff * 2\r\n\r\n return Math.max(0, score)\r\n}\r\n\r\nexport const fuzzySearchBuildings = (buildings: any[], searchTerm: string) => {\r\n if (!searchTerm.trim()) return buildings.slice(0, 10)\r\n return buildings\r\n .map((building) => {\r\n const address = building.buildingAddress || building.buildingAnchorAddress || ''\r\n const propertyName = building.buildingName || ''\r\n\r\n // Calculate scores for different fields\r\n const addressScore = fuzzySearchScore(searchTerm, address)\r\n const propertyScore = fuzzySearchScore(searchTerm, propertyName)\r\n\r\n // Use the highest score\r\n const maxScore = Math.max(addressScore, propertyScore)\r\n
|
|
1
|
+
{"version":3,"sources":["../../../../../../../src/core/components/viewers/Data/files/utils/fuzzySearch.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\n// Fuzzy search utility functions\r\nconst fuzzySearchScore = (searchTerm: string, text: string): number => {\r\n if (!searchTerm || !text) return 0\r\n\r\n const search = searchTerm.toLowerCase()\r\n const target = text.toLowerCase()\r\n\r\n // Exact match gets highest score\r\n if (target === search) return 1000\r\n\r\n // Starts with search term gets high score\r\n if (target.startsWith(search)) return 800\r\n\r\n // Contains search term gets medium score\r\n if (target.includes(search)) return 600\r\n\r\n // Fuzzy matching - calculate similarity based on character matching\r\n let score = 0\r\n let searchIndex = 0\r\n\r\n for (let i = 0; i < target.length && searchIndex < search.length; i++) {\r\n if (target[i] === search[searchIndex]) {\r\n score += 10\r\n searchIndex++\r\n }\r\n }\r\n\r\n // Bonus for matching all characters in sequence\r\n if (searchIndex === search.length) {\r\n score += 100\r\n }\r\n\r\n // Penalize for length difference\r\n const lengthDiff = Math.abs(target.length - search.length)\r\n score -= lengthDiff * 2\r\n\r\n return Math.max(0, score)\r\n}\r\n\r\nexport const fuzzySearchBuildings = (buildings: any[], searchTerm: string) => {\r\n if (!searchTerm.trim()) return buildings.slice(0, 10)\r\n return buildings\r\n .map((building) => {\r\n const address = building.buildingAddress || building.buildingAnchorAddress || ''\r\n const propertyName = building.buildingName || ''\r\n\r\n // Calculate scores for different fields\r\n const addressScore = fuzzySearchScore(searchTerm, address)\r\n const propertyScore = fuzzySearchScore(searchTerm, propertyName)\r\n\r\n // Use the highest score\r\n const maxScore = Math.max(addressScore, propertyScore)\r\n return {\r\n ...building,\r\n searchScore: maxScore,\r\n matchedField: addressScore > propertyScore ? 'address' : 'propertyName',\r\n }\r\n })\r\n .filter(building => building.searchScore > 0)\r\n .sort((a, b) => b.searchScore - a.searchScore)\r\n .slice(0, 10) // Return only the top 10 results\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAIA,MAAM,mBAAmB,CAAC,YAAoB,SAAyB;AACrE,MAAI,CAAC,cAAc,CAAC,KAAM,QAAO;AAEjC,QAAM,SAAS,WAAW,YAAY;AACtC,QAAM,SAAS,KAAK,YAAY;AAGhC,MAAI,WAAW,OAAQ,QAAO;AAG9B,MAAI,OAAO,WAAW,MAAM,EAAG,QAAO;AAGtC,MAAI,OAAO,SAAS,MAAM,EAAG,QAAO;AAGpC,MAAI,QAAQ;AACZ,MAAI,cAAc;AAElB,WAAS,IAAI,GAAG,IAAI,OAAO,UAAU,cAAc,OAAO,QAAQ,KAAK;AACrE,QAAI,OAAO,CAAC,MAAM,OAAO,WAAW,GAAG;AACrC,eAAS;AACT;AAAA,IACF;AAAA,EACF;AAGA,MAAI,gBAAgB,OAAO,QAAQ;AACjC,aAAS;AAAA,EACX;AAGA,QAAM,aAAa,KAAK,IAAI,OAAO,SAAS,OAAO,MAAM;AACzD,WAAS,aAAa;AAEtB,SAAO,KAAK,IAAI,GAAG,KAAK;AAC1B;AAEO,MAAM,uBAAuB,CAAC,WAAkB,eAAuB;AAC5E,MAAI,CAAC,WAAW,KAAK,EAAG,QAAO,UAAU,MAAM,GAAG,EAAE;AACpD,SAAO,UACJ,IAAI,CAAC,aAAa;AACjB,UAAM,UAAU,SAAS,mBAAmB,SAAS,yBAAyB;AAC9E,UAAM,eAAe,SAAS,gBAAgB;AAG9C,UAAM,eAAe,iBAAiB,YAAY,OAAO;AACzD,UAAM,gBAAgB,iBAAiB,YAAY,YAAY;AAG/D,UAAM,WAAW,KAAK,IAAI,cAAc,aAAa;AACrD,WAAO,iCACF,WADE;AAAA,MAEL,aAAa;AAAA,MACb,cAAc,eAAe,gBAAgB,YAAY;AAAA,IAC3D;AAAA,EACF,CAAC,EACA,OAAO,cAAY,SAAS,cAAc,CAAC,EAC3C,KAAK,CAAC,GAAG,MAAM,EAAE,cAAc,EAAE,WAAW,EAC5C,MAAM,GAAG,EAAE;AAChB;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UserMoreOptions.d.ts","sourceRoot":"","sources":["../../../../../../src/core/components/viewers/Data/userDetails/UserMoreOptions.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAuB9B,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,2BAA2B,CAAA;AAGrD,UAAU,oBAAoB;IAC5B,KAAK,CAAC,EAAE,IAAI,EAAE,CAAA;IACd,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,CAAA;CAC1C;AAED,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,oBAAoB,
|
|
1
|
+
{"version":3,"file":"UserMoreOptions.d.ts","sourceRoot":"","sources":["../../../../../../src/core/components/viewers/Data/userDetails/UserMoreOptions.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAuB9B,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,2BAA2B,CAAA;AAGrD,UAAU,oBAAoB;IAC5B,KAAK,CAAC,EAAE,IAAI,EAAE,CAAA;IACd,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,CAAA;CAC1C;AAED,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,oBAAoB,qBA+V/E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../src/core/components/viewers/Data/userDetails/UserMoreOptions.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 {\r\n MoreHorizontal,\r\n FileOutput,\r\n FileInput,\r\n FileText,\r\n} 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\n// Shadcn Components\r\n\r\n// Icons\r\n\r\n// Types\r\n\r\n\r\nimport { mutate } from 'swr'\r\n\r\nimport { Button } from '../../../../components/ui/Button'\r\nimport {\r\n DropdownMenu,\r\n DropdownMenuContent,\r\n DropdownMenuItem,\r\n DropdownMenuTrigger,\r\n} from '../../../../components/ui/DropdownMenu'\r\nimport { useOrganizationRoles } from '../../../../hooks/organizations/organizations'\r\nimport { useCreateUser } from '../../../../hooks/users/users'\r\nimport { usePermissions } from '../../../../store'\r\n\r\nimport type { User } from '../../../../types/dbTypes'\r\n\r\n\r\ninterface UserMoreOptionsProps {\r\n users?: User[]\r\n variant?: 'ghost' | 'outline' | 'default'\r\n}\r\n\r\nexport default function UserMoreOptions({ users, variant }: UserMoreOptionsProps) {\r\n // Translations\r\n const t = useTranslations('UserMoreOptions')\r\n\r\n // Permissions\r\n const { ability } = usePermissions()\r\n\r\n const [isExporting, setIsExporting] = React.useState(false)\r\n const [isImporting, setIsImporting] = React.useState(false)\r\n const { createUser } = useCreateUser()\r\n\r\n const { user } = useSession().data\r\n const { organizationId } = user\r\n const { organizationRoles } = useOrganizationRoles(String(organizationId))\r\n\r\n // Helper function to update a user - follows the same pattern as the adapter's updateUser\r\n const updateUserById = async (userId: number, patch: Partial<User> & { role?: string }) => {\r\n const response = await fetch(`/api/users/${userId}`, {\r\n method: 'PATCH',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify(patch),\r\n })\r\n if (!response.ok) {\r\n throw new Error('Failed to update user')\r\n }\r\n return response.json()\r\n }\r\n\r\n const handleExportUsers = async () => {\r\n if (!users || users.length === 0) {\r\n toast.error(t('errors.noUsersToExport'))\r\n return\r\n }\r\n\r\n setIsExporting(true)\r\n try {\r\n // Only export fields that are in the schema (name, email, role)\r\n // Password is never exported for security reasons\r\n const headers = 'name,email,role'\r\n\r\n // Fetch roles for all users\r\n const usersWithRoles = await Promise.all(\r\n users.map(async (user) => {\r\n try {\r\n const response = await fetch(`/api/users/${user.id}/role`)\r\n if (response.ok) {\r\n const data = await response.json()\r\n const roles = data.roles || []\r\n const roleName = roles.length > 0 ? roles[0].name : ''\r\n return { ...user, role: roleName }\r\n }\r\n } catch (error) {\r\n console.error(`Error fetching role for user ${user.id}:`, error)\r\n }\r\n return { ...user, role: '' }\r\n })\r\n )\r\n\r\n // Extract only the schema fields for each user\r\n const rows = usersWithRoles.map(user => {\r\n const name = user.name || ''\r\n const email = user.email || ''\r\n const role = user.role || ''\r\n return `\"${name.replaceAll('\"', '\"\"')}\",\"${email.replaceAll('\"', '\"\"')}\",\"${role.replaceAll('\"', '\"\"')}\"`\r\n }).join('\\n')\r\n\r\n // Combine headers and data rows into complete CSV content\r\n const content = `${headers}\\n${rows}`\r\n\r\n // Generate a timestamp for the filename\r\n const timestamp = new Date().toISOString().replaceAll(/[:.]/g, '-').slice(0, 19)\r\n const filename = `users_${timestamp}.csv`\r\n\r\n // Create a blob with the CSV content\r\n const blob = new Blob([content], { type: 'text/csv' })\r\n const url = URL.createObjectURL(blob)\r\n\r\n // Create a temporary link element to trigger the download\r\n const link = document.createElement('a')\r\n link.href = url\r\n link.download = filename\r\n link.click()\r\n\r\n // Clean up the URL object\r\n URL.revokeObjectURL(url)\r\n\r\n toast.success(t('success.usersExported'))\r\n } catch (error) {\r\n console.error('Error exporting users:', error)\r\n toast.error(t('errors.exportFailed'))\r\n } finally {\r\n setIsExporting(false)\r\n }\r\n }\r\n\r\n const handleImportUsers = () => {\r\n // Create a file input element\r\n const input = document.createElement('input')\r\n input.type = 'file'\r\n input.accept = '.csv'\r\n\r\n input.onchange = async (e) => {\r\n const file = (e.target as HTMLInputElement).files?.[0]\r\n if (!file) return\r\n\r\n setIsImporting(true)\r\n try {\r\n const text = await file.text()\r\n\r\n // Parse CSV manually\r\n const lines = text.split('\\n').filter(line => line.trim())\r\n if (lines.length < 2) {\r\n toast.error(t('errors.csvEmpty'))\r\n return\r\n }\r\n\r\n // Extract headers\r\n const headers = lines[0].split(',').map(h => h.trim().replace(/^\"|\"$/g, ''))\r\n\r\n // Validate required headers\r\n const requiredHeaders = ['name', 'email', 'role', 'password']\r\n const missingHeaders = requiredHeaders.filter(h => !headers.includes(h))\r\n if (missingHeaders.length > 0) {\r\n toast.error(t('errors.missingColumns', { columns: missingHeaders.join(', ') }))\r\n return\r\n }\r\n\r\n // Parse each row\r\n const newUsers: Array<{ name: string; email: string; roleId: number; password: string }> = []\r\n const errors: string[] = []\r\n\r\n for (let i = 1; i < lines.length; i++) {\r\n const line = lines[i]\r\n // Simple CSV parsing (handles quoted fields)\r\n const values: string[] = []\r\n let current = ''\r\n let inQuotes = false\r\n\r\n for (let j = 0; j < line.length; j++) {\r\n const char = line[j]\r\n if (char === '\"') {\r\n inQuotes = !inQuotes\r\n } else if (char === ',' && !inQuotes) {\r\n values.push(current.trim())\r\n current = ''\r\n } else {\r\n current += char\r\n }\r\n }\r\n values.push(current.trim())\r\n\r\n // Create user object\r\n const userObj: Record<string, string> = {}\r\n headers.forEach((header, idx) => {\r\n userObj[header] = values[idx] || ''\r\n })\r\n\r\n // Validate user data\r\n const name = userObj.name?.trim()\r\n const email = userObj.email?.trim()\r\n const role = userObj.role?.trim()\r\n const password = userObj.password?.trim()\r\n\r\n if (!name || !email || !role || !password) {\r\n errors.push(t('errors.rowMissingFields', { row: i + 1 }))\r\n continue\r\n }\r\n\r\n // Validate email format\r\n const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\r\n if (!emailRegex.test(email)) {\r\n errors.push(t('errors.rowInvalidEmail', { row: i + 1 }))\r\n continue\r\n }\r\n\r\n // Validate password strength\r\n const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[^a-zA-Z\\d\\s])[^\\s]{12,65}$/\r\n if (!passwordRegex.test(password)) {\r\n errors.push(t('errors.rowWeakPassword', { row: i + 1 }))\r\n continue\r\n }\r\n\r\n let roleId = -1\r\n\r\n // Validate role\r\n const validRoles = organizationRoles.map(role => role.name.toLowerCase())\r\n if (!validRoles.includes(role.toLowerCase())) {\r\n errors.push(t('errors.rowInvalidRole', { row: i + 1, roles: validRoles.join(', ') }))\r\n continue\r\n }\r\n\r\n // Find the roleId for the given role name\r\n const roleObj = organizationRoles.find(r => r.name.toLowerCase() === role.toLowerCase())\r\n if (roleObj) {\r\n roleId = roleObj.id\r\n }\r\n\r\n newUsers.push({ name, email, roleId, password })\r\n }\r\n\r\n if (errors.length > 0) {\r\n toast.error(t('errors.validationErrors', { count: errors.length, first: errors[0] }))\r\n console.error('Import errors:', errors)\r\n }\r\n\r\n if (newUsers.length === 0) {\r\n toast.error(t('errors.noValidUsers'))\r\n return\r\n }\r\n\r\n // Process users: check if they exist and create/update accordingly\r\n let createdCount = 0\r\n let updatedCount = 0\r\n let failedCount = 0\r\n\r\n for (const newUser of newUsers) {\r\n try {\r\n // Check if user exists by email\r\n const existingUser = users?.find(u => u.email.toLowerCase() === newUser.email.toLowerCase())\r\n\r\n if (existingUser) {\r\n // Fetch current role\r\n const roleResponse = await fetch(`/api/users/${existingUser.id}/role`)\r\n let currentRole = -1\r\n if (roleResponse.ok) {\r\n const roleData = await roleResponse.json()\r\n const roles = roleData.roles || []\r\n currentRole = roles.length > 0 ? roles[0].name.toLowerCase() : ''\r\n }\r\n\r\n // Check for differences\r\n const nameChanged = existingUser.name !== newUser.name\r\n const roleChanged = currentRole !== newUser.roleId\r\n\r\n if (nameChanged || roleChanged) {\r\n // Update user using the same pattern as AccountSettingsPanel\r\n const updatePayload: Partial<User> & { roleId?: number } = {}\r\n if (nameChanged) updatePayload.name = newUser.name\r\n if (roleChanged) updatePayload.roleId = newUser.roleId\r\n\r\n try {\r\n await updateUserById(existingUser.id, updatePayload)\r\n updatedCount++\r\n // Trigger cache refresh for this user and their roles\r\n mutate(['user', String(existingUser.id)])\r\n if (roleChanged) {\r\n mutate(['userRoles', String(existingUser.id)])\r\n }\r\n } catch (error) {\r\n failedCount++\r\n console.error('Failed to update user:', newUser.email, error)\r\n }\r\n } else {\r\n // No changes needed\r\n console.log('User unchanged:', newUser.email)\r\n }\r\n } else {\r\n // Create new user using the hook\r\n try {\r\n await createUser({\r\n userData: {\r\n name: newUser.name,\r\n email: newUser.email,\r\n password: newUser.password,\r\n roleId: newUser.roleId,\r\n }\r\n })\r\n createdCount++\r\n } catch (error) {\r\n failedCount++\r\n console.error('Failed to create user:', newUser.email, error)\r\n }\r\n }\r\n } catch (error) {\r\n failedCount++\r\n console.error('Error processing user:', newUser.email, error)\r\n }\r\n }\r\n\r\n // Show summary\r\n const messages = []\r\n if (createdCount > 0) messages.push(t('summary.created', { count: createdCount }))\r\n if (updatedCount > 0) messages.push(t('summary.updated', { count: updatedCount }))\r\n if (failedCount > 0) messages.push(t('summary.failed', { count: failedCount }))\r\n\r\n if (createdCount > 0 || updatedCount > 0) {\r\n // Refresh the users list cache\r\n await mutate(['users'])\r\n toast.success(t('success.importComplete', { summary: messages.join(', ') }))\r\n } else if (failedCount > 0) {\r\n toast.error(t('errors.importAllFailed', { count: failedCount }))\r\n } else {\r\n toast.info(t('success.noChanges'))\r\n }\r\n } catch (error) {\r\n console.error('Error importing users:', error)\r\n toast.error(t('errors.importFailed'))\r\n } finally {\r\n setIsImporting(false)\r\n }\r\n }\r\n\r\n input.click()\r\n }\r\n\r\n const handleDownloadSchema = () => {\r\n // Create a CSV template with headers and an example row\r\n const headers = 'name,email,role,password'\r\n const example = '\"Terry Fox\",\"terry.fox@example.com\",\"User\",\"password123\"'\r\n const content = `${headers}\\n${example}`\r\n\r\n const blob = new Blob([content], { type: 'text/csv' })\r\n const url = URL.createObjectURL(blob)\r\n\r\n const link = document.createElement('a')\r\n link.href = url\r\n link.download = 'users-schema.csv'\r\n link.click()\r\n\r\n URL.revokeObjectURL(url)\r\n toast.success(t('success.schemaDownloaded'))\r\n }\r\n\r\n return (\r\n <DropdownMenu>\r\n <DropdownMenuTrigger asChild>\r\n <Button\r\n variant={variant || 'outline'}\r\n className=\"dropdown-menu-trigger\"\r\n onClick={e => e.stopPropagation()}\r\n disabled={!ability.can('read', 'Role')}\r\n >\r\n <MoreHorizontal />\r\n <span className=\"sr-only\">{t('openMenu')}</span>\r\n </Button>\r\n </DropdownMenuTrigger>\r\n <DropdownMenuContent align=\"end\">\r\n <DropdownMenuItem onClick={handleExportUsers} disabled={isExporting || isImporting || !ability.can('read', 'Role')}>\r\n <FileOutput />\r\n {isExporting ? t('actions.exporting') : t('actions.exportUsers')}\r\n </DropdownMenuItem>\r\n <DropdownMenuItem onClick={handleImportUsers} disabled={isExporting || isImporting || !ability.can('create', 'User')}>\r\n <FileInput />\r\n {isImporting ? t('actions.importing') : t('actions.importUsers')}\r\n </DropdownMenuItem>\r\n <DropdownMenuItem onClick={handleDownloadSchema} disabled={!ability.can('read', 'User')}>\r\n <FileText />\r\n {t('actions.downloadSchema')}\r\n </DropdownMenuItem>\r\n </DropdownMenuContent>\r\n </DropdownMenu>\r\n )\r\n}\r\n"],"mappings":"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","names":["user","role"]}
|
|
1
|
+
{"version":3,"sources":["../../../../../../src/core/components/viewers/Data/userDetails/UserMoreOptions.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 {\r\n MoreHorizontal,\r\n FileOutput,\r\n FileInput,\r\n FileText,\r\n} 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\n// Shadcn Components\r\n\r\n// Icons\r\n\r\n// Types\r\n\r\n\r\nimport { mutate } from 'swr'\r\n\r\nimport { Button } from '../../../../components/ui/Button'\r\nimport {\r\n DropdownMenu,\r\n DropdownMenuContent,\r\n DropdownMenuItem,\r\n DropdownMenuTrigger,\r\n} from '../../../../components/ui/DropdownMenu'\r\nimport { useOrganizationRoles } from '../../../../hooks/organizations/organizations'\r\nimport { useCreateUser } from '../../../../hooks/users/users'\r\nimport { usePermissions } from '../../../../store'\r\n\r\nimport type { User } from '../../../../types/dbTypes'\r\n\r\n\r\ninterface UserMoreOptionsProps {\r\n users?: User[]\r\n variant?: 'ghost' | 'outline' | 'default'\r\n}\r\n\r\nexport default function UserMoreOptions({ users, variant }: UserMoreOptionsProps) {\r\n // Translations\r\n const t = useTranslations('UserMoreOptions')\r\n\r\n // Permissions\r\n const { ability } = usePermissions()\r\n\r\n const [isExporting, setIsExporting] = React.useState(false)\r\n const [isImporting, setIsImporting] = React.useState(false)\r\n const { createUser } = useCreateUser()\r\n\r\n const { user } = useSession().data\r\n const { organizationId } = user\r\n const { organizationRoles } = useOrganizationRoles(String(organizationId))\r\n\r\n // Helper function to update a user - follows the same pattern as the adapter's updateUser\r\n const updateUserById = async (userId: number, patch: Partial<User> & { role?: string }) => {\r\n const response = await fetch(`/api/users/${userId}`, {\r\n method: 'PATCH',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify(patch),\r\n })\r\n if (!response.ok) {\r\n throw new Error('Failed to update user')\r\n }\r\n return response.json()\r\n }\r\n\r\n const handleExportUsers = async () => {\r\n if (!users || users.length === 0) {\r\n toast.error(t('errors.noUsersToExport'))\r\n return\r\n }\r\n\r\n setIsExporting(true)\r\n try {\r\n // Only export fields that are in the schema (name, email, role)\r\n // Password is never exported for security reasons\r\n const headers = 'name,email,role'\r\n\r\n // Fetch roles for all users\r\n const usersWithRoles = await Promise.all(\r\n users.map(async (user) => {\r\n try {\r\n const response = await fetch(`/api/users/${user.id}/role`)\r\n if (response.ok) {\r\n const data = await response.json()\r\n const roles = data.roles || []\r\n const roleName = roles.length > 0 ? roles[0].name : ''\r\n return { ...user, role: roleName }\r\n }\r\n } catch (error) {\r\n console.error(`Error fetching role for user ${user.id}:`, error)\r\n }\r\n return { ...user, role: '' }\r\n })\r\n )\r\n\r\n // Extract only the schema fields for each user\r\n const rows = usersWithRoles.map(user => {\r\n const name = user.name || ''\r\n const email = user.email || ''\r\n const role = user.role || ''\r\n return `\"${name.replaceAll('\"', '\"\"')}\",\"${email.replaceAll('\"', '\"\"')}\",\"${role.replaceAll('\"', '\"\"')}\"`\r\n }).join('\\n')\r\n\r\n // Combine headers and data rows into complete CSV content\r\n const content = `${headers}\\n${rows}`\r\n\r\n // Generate a timestamp for the filename\r\n const timestamp = new Date().toISOString().replaceAll(/[:.]/g, '-').slice(0, 19)\r\n const filename = `users_${timestamp}.csv`\r\n\r\n // Create a blob with the CSV content\r\n const blob = new Blob([content], { type: 'text/csv' })\r\n const url = URL.createObjectURL(blob)\r\n\r\n // Create a temporary link element to trigger the download\r\n const link = document.createElement('a')\r\n link.href = url\r\n link.download = filename\r\n link.click()\r\n\r\n // Clean up the URL object\r\n URL.revokeObjectURL(url)\r\n\r\n toast.success(t('success.usersExported'))\r\n } catch (error) {\r\n console.error('Error exporting users:', error)\r\n toast.error(t('errors.exportFailed'))\r\n } finally {\r\n setIsExporting(false)\r\n }\r\n }\r\n\r\n const handleImportUsers = () => {\r\n // Create a file input element\r\n const input = document.createElement('input')\r\n input.type = 'file'\r\n input.accept = '.csv'\r\n\r\n input.onchange = async (e) => {\r\n const file = (e.target as HTMLInputElement).files?.[0]\r\n if (!file) return\r\n\r\n setIsImporting(true)\r\n try {\r\n const text = await file.text()\r\n\r\n // Parse CSV manually\r\n const lines = text.split('\\n').filter(line => line.trim())\r\n if (lines.length < 2) {\r\n toast.error(t('errors.csvEmpty'))\r\n return\r\n }\r\n\r\n // Extract headers\r\n const headers = lines[0].split(',').map(h => h.trim().replace(/^\"|\"$/g, ''))\r\n\r\n // Validate required headers\r\n const requiredHeaders = ['name', 'email', 'role', 'password']\r\n const missingHeaders = requiredHeaders.filter(h => !headers.includes(h))\r\n if (missingHeaders.length > 0) {\r\n toast.error(t('errors.missingColumns', { columns: missingHeaders.join(', ') }))\r\n return\r\n }\r\n\r\n // Parse each row\r\n const newUsers: Array<{ name: string; email: string; roleId: number; password: string }> = []\r\n const errors: string[] = []\r\n\r\n for (let i = 1; i < lines.length; i++) {\r\n const line = lines[i]\r\n // Simple CSV parsing (handles quoted fields)\r\n const values: string[] = []\r\n let current = ''\r\n let inQuotes = false\r\n\r\n for (let j = 0; j < line.length; j++) {\r\n const char = line[j]\r\n if (char === '\"') {\r\n inQuotes = !inQuotes\r\n } else if (char === ',' && !inQuotes) {\r\n values.push(current.trim())\r\n current = ''\r\n } else {\r\n current += char\r\n }\r\n }\r\n values.push(current.trim())\r\n\r\n // Create user object\r\n const userObj: Record<string, string> = {}\r\n headers.forEach((header, idx) => {\r\n userObj[header] = values[idx] || ''\r\n })\r\n\r\n // Validate user data\r\n const name = userObj.name?.trim()\r\n const email = userObj.email?.trim()\r\n const role = userObj.role?.trim()\r\n const password = userObj.password?.trim()\r\n\r\n if (!name || !email || !role || !password) {\r\n errors.push(t('errors.rowMissingFields', { row: i + 1 }))\r\n continue\r\n }\r\n\r\n // Validate email format\r\n const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\r\n if (!emailRegex.test(email)) {\r\n errors.push(t('errors.rowInvalidEmail', { row: i + 1 }))\r\n continue\r\n }\r\n\r\n // Validate password strength\r\n const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[^a-zA-Z\\d\\s])[^\\s]{12,65}$/\r\n if (!passwordRegex.test(password)) {\r\n errors.push(t('errors.rowWeakPassword', { row: i + 1 }))\r\n continue\r\n }\r\n\r\n let roleId = -1\r\n\r\n // Validate role\r\n const validRoles = organizationRoles.map(role => role.name.toLowerCase())\r\n if (!validRoles.includes(role.toLowerCase())) {\r\n errors.push(t('errors.rowInvalidRole', { row: i + 1, roles: validRoles.join(', ') }))\r\n continue\r\n }\r\n\r\n // Find the roleId for the given role name\r\n const roleObj = organizationRoles.find(r => r.name.toLowerCase() === role.toLowerCase())\r\n if (roleObj) {\r\n roleId = roleObj.id\r\n }\r\n\r\n newUsers.push({ name, email, roleId, password })\r\n }\r\n\r\n if (errors.length > 0) {\r\n toast.error(t('errors.validationErrors', { count: errors.length, first: errors[0] }))\r\n console.error('Import errors:', errors)\r\n }\r\n\r\n if (newUsers.length === 0) {\r\n toast.error(t('errors.noValidUsers'))\r\n return\r\n }\r\n\r\n // Process users: check if they exist and create/update accordingly\r\n let createdCount = 0\r\n let updatedCount = 0\r\n let failedCount = 0\r\n\r\n for (const newUser of newUsers) {\r\n try {\r\n // Check if user exists by email\r\n const existingUser = users?.find(u => u.email.toLowerCase() === newUser.email.toLowerCase())\r\n\r\n if (existingUser) {\r\n // Fetch current role\r\n const roleResponse = await fetch(`/api/users/${existingUser.id}/role`)\r\n let currentRole = -1\r\n if (roleResponse.ok) {\r\n const roleData = await roleResponse.json()\r\n const roles = roleData.roles || []\r\n currentRole = roles.length > 0 ? roles[0].name.toLowerCase() : ''\r\n }\r\n\r\n // Check for differences\r\n const nameChanged = existingUser.name !== newUser.name\r\n const roleChanged = currentRole !== newUser.roleId\r\n\r\n if (nameChanged || roleChanged) {\r\n // Update user using the same pattern as AccountSettingsPanel\r\n const updatePayload: Partial<User> & { roleId?: number } = {}\r\n if (nameChanged) updatePayload.name = newUser.name\r\n if (roleChanged) updatePayload.roleId = newUser.roleId\r\n\r\n try {\r\n await updateUserById(existingUser.id, updatePayload)\r\n updatedCount++\r\n // Trigger cache refresh for this user and their roles\r\n mutate(['user', String(existingUser.id)])\r\n if (roleChanged) {\r\n mutate(['userRoles', String(existingUser.id)])\r\n }\r\n } catch (error) {\r\n failedCount++\r\n console.error('Failed to update user:', newUser.email, error)\r\n }\r\n } else {\r\n // No changes needed\r\n }\r\n } else {\r\n // Create new user using the hook\r\n try {\r\n await createUser({\r\n userData: {\r\n name: newUser.name,\r\n email: newUser.email,\r\n password: newUser.password,\r\n roleId: newUser.roleId,\r\n }\r\n })\r\n createdCount++\r\n } catch (error) {\r\n failedCount++\r\n console.error('Failed to create user:', newUser.email, error)\r\n }\r\n }\r\n } catch (error) {\r\n failedCount++\r\n console.error('Error processing user:', newUser.email, error)\r\n }\r\n }\r\n\r\n // Show summary\r\n const messages = []\r\n if (createdCount > 0) messages.push(t('summary.created', { count: createdCount }))\r\n if (updatedCount > 0) messages.push(t('summary.updated', { count: updatedCount }))\r\n if (failedCount > 0) messages.push(t('summary.failed', { count: failedCount }))\r\n\r\n if (createdCount > 0 || updatedCount > 0) {\r\n // Refresh the users list cache\r\n await mutate(['users'])\r\n toast.success(t('success.importComplete', { summary: messages.join(', ') }))\r\n } else if (failedCount > 0) {\r\n toast.error(t('errors.importAllFailed', { count: failedCount }))\r\n } else {\r\n toast.info(t('success.noChanges'))\r\n }\r\n } catch (error) {\r\n console.error('Error importing users:', error)\r\n toast.error(t('errors.importFailed'))\r\n } finally {\r\n setIsImporting(false)\r\n }\r\n }\r\n\r\n input.click()\r\n }\r\n\r\n const handleDownloadSchema = () => {\r\n // Create a CSV template with headers and an example row\r\n const headers = 'name,email,role,password'\r\n const example = '\"Terry Fox\",\"terry.fox@example.com\",\"User\",\"password123\"'\r\n const content = `${headers}\\n${example}`\r\n\r\n const blob = new Blob([content], { type: 'text/csv' })\r\n const url = URL.createObjectURL(blob)\r\n\r\n const link = document.createElement('a')\r\n link.href = url\r\n link.download = 'users-schema.csv'\r\n link.click()\r\n\r\n URL.revokeObjectURL(url)\r\n toast.success(t('success.schemaDownloaded'))\r\n }\r\n\r\n return (\r\n <DropdownMenu>\r\n <DropdownMenuTrigger asChild>\r\n <Button\r\n variant={variant || 'outline'}\r\n className=\"dropdown-menu-trigger\"\r\n onClick={e => e.stopPropagation()}\r\n disabled={!ability.can('read', 'Role')}\r\n >\r\n <MoreHorizontal />\r\n <span className=\"sr-only\">{t('openMenu')}</span>\r\n </Button>\r\n </DropdownMenuTrigger>\r\n <DropdownMenuContent align=\"end\">\r\n <DropdownMenuItem onClick={handleExportUsers} disabled={isExporting || isImporting || !ability.can('read', 'Role')}>\r\n <FileOutput />\r\n {isExporting ? t('actions.exporting') : t('actions.exportUsers')}\r\n </DropdownMenuItem>\r\n <DropdownMenuItem onClick={handleImportUsers} disabled={isExporting || isImporting || !ability.can('create', 'User')}>\r\n <FileInput />\r\n {isImporting ? t('actions.importing') : t('actions.importUsers')}\r\n </DropdownMenuItem>\r\n <DropdownMenuItem onClick={handleDownloadSchema} disabled={!ability.can('read', 'User')}>\r\n <FileText />\r\n {t('actions.downloadSchema')}\r\n </DropdownMenuItem>\r\n </DropdownMenuContent>\r\n </DropdownMenu>\r\n )\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAkXQ,SAME,KANF;AA5WR;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,kBAAkB;AAC3B,SAAS,uBAAuB;AAChC,YAAY,WAAW;AACvB,SAAS,aAAa;AAStB,SAAS,cAAc;AAEvB,SAAS,cAAc;AACvB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,4BAA4B;AACrC,SAAS,qBAAqB;AAC9B,SAAS,sBAAsB;AAUhB,SAAR,gBAAiC,EAAE,OAAO,QAAQ,GAAyB;AAEhF,QAAM,IAAI,gBAAgB,iBAAiB;AAG3C,QAAM,EAAE,QAAQ,IAAI,eAAe;AAEnC,QAAM,CAAC,aAAa,cAAc,IAAI,MAAM,SAAS,KAAK;AAC1D,QAAM,CAAC,aAAa,cAAc,IAAI,MAAM,SAAS,KAAK;AAC1D,QAAM,EAAE,WAAW,IAAI,cAAc;AAEnC,QAAM,EAAE,KAAK,IAAI,WAAW,EAAE;AAC9B,QAAM,EAAE,eAAe,IAAI;AAC3B,QAAM,EAAE,kBAAkB,IAAI,qBAAqB,OAAO,cAAc,CAAC;AAG3E,QAAM,iBAAiB,OAAO,QAAgB,UAA6C;AACzF,UAAM,WAAW,MAAM,MAAM,cAAc,MAAM,IAAI;AAAA,MACnD,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU,KAAK;AAAA,IAC5B,CAAC;AACD,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAI,MAAM,uBAAuB;AAAA,IACzC;AACA,WAAO,SAAS,KAAK;AAAA,EACvB;AAEA,QAAM,oBAAoB,YAAY;AACpC,QAAI,CAAC,SAAS,MAAM,WAAW,GAAG;AAChC,YAAM,MAAM,EAAE,wBAAwB,CAAC;AACvC;AAAA,IACF;AAEA,mBAAe,IAAI;AACnB,QAAI;AAGF,YAAM,UAAU;AAGhB,YAAM,iBAAiB,MAAM,QAAQ;AAAA,QACnC,MAAM,IAAI,OAAOA,UAAS;AACxB,cAAI;AACF,kBAAM,WAAW,MAAM,MAAM,cAAcA,MAAK,EAAE,OAAO;AACzD,gBAAI,SAAS,IAAI;AACf,oBAAM,OAAO,MAAM,SAAS,KAAK;AACjC,oBAAM,QAAQ,KAAK,SAAS,CAAC;AAC7B,oBAAM,WAAW,MAAM,SAAS,IAAI,MAAM,CAAC,EAAE,OAAO;AACpD,qBAAO,iCAAKA,QAAL,EAAW,MAAM,SAAS;AAAA,YACnC;AAAA,UACF,SAAS,OAAO;AACd,oBAAQ,MAAM,gCAAgCA,MAAK,EAAE,KAAK,KAAK;AAAA,UACjE;AACA,iBAAO,iCAAKA,QAAL,EAAW,MAAM,GAAG;AAAA,QAC7B,CAAC;AAAA,MACH;AAGA,YAAM,OAAO,eAAe,IAAI,CAAAA,UAAQ;AACtC,cAAM,OAAOA,MAAK,QAAQ;AAC1B,cAAM,QAAQA,MAAK,SAAS;AAC5B,cAAM,OAAOA,MAAK,QAAQ;AAC1B,eAAO,IAAI,KAAK,WAAW,KAAK,IAAI,CAAC,MAAM,MAAM,WAAW,KAAK,IAAI,CAAC,MAAM,KAAK,WAAW,KAAK,IAAI,CAAC;AAAA,MACxG,CAAC,EAAE,KAAK,IAAI;AAGZ,YAAM,UAAU,GAAG,OAAO;AAAA,EAAK,IAAI;AAGnC,YAAM,aAAY,oBAAI,KAAK,GAAE,YAAY,EAAE,WAAW,SAAS,GAAG,EAAE,MAAM,GAAG,EAAE;AAC/E,YAAM,WAAW,SAAS,SAAS;AAGnC,YAAM,OAAO,IAAI,KAAK,CAAC,OAAO,GAAG,EAAE,MAAM,WAAW,CAAC;AACrD,YAAM,MAAM,IAAI,gBAAgB,IAAI;AAGpC,YAAM,OAAO,SAAS,cAAc,GAAG;AACvC,WAAK,OAAO;AACZ,WAAK,WAAW;AAChB,WAAK,MAAM;AAGX,UAAI,gBAAgB,GAAG;AAEvB,YAAM,QAAQ,EAAE,uBAAuB,CAAC;AAAA,IAC1C,SAAS,OAAO;AACd,cAAQ,MAAM,0BAA0B,KAAK;AAC7C,YAAM,MAAM,EAAE,qBAAqB,CAAC;AAAA,IACtC,UAAE;AACA,qBAAe,KAAK;AAAA,IACtB;AAAA,EACF;AAEA,QAAM,oBAAoB,MAAM;AAE9B,UAAM,QAAQ,SAAS,cAAc,OAAO;AAC5C,UAAM,OAAO;AACb,UAAM,SAAS;AAEf,UAAM,WAAW,OAAO,MAAM;AAlJlC;AAmJM,YAAM,QAAQ,OAAE,OAA4B,UAA9B,mBAAsC;AACpD,UAAI,CAAC,KAAM;AAEX,qBAAe,IAAI;AACnB,UAAI;AACF,cAAM,OAAO,MAAM,KAAK,KAAK;AAG7B,cAAM,QAAQ,KAAK,MAAM,IAAI,EAAE,OAAO,UAAQ,KAAK,KAAK,CAAC;AACzD,YAAI,MAAM,SAAS,GAAG;AACpB,gBAAM,MAAM,EAAE,iBAAiB,CAAC;AAChC;AAAA,QACF;AAGA,cAAM,UAAU,MAAM,CAAC,EAAE,MAAM,GAAG,EAAE,IAAI,OAAK,EAAE,KAAK,EAAE,QAAQ,UAAU,EAAE,CAAC;AAG3E,cAAM,kBAAkB,CAAC,QAAQ,SAAS,QAAQ,UAAU;AAC5D,cAAM,iBAAiB,gBAAgB,OAAO,OAAK,CAAC,QAAQ,SAAS,CAAC,CAAC;AACvE,YAAI,eAAe,SAAS,GAAG;AAC7B,gBAAM,MAAM,EAAE,yBAAyB,EAAE,SAAS,eAAe,KAAK,IAAI,EAAE,CAAC,CAAC;AAC9E;AAAA,QACF;AAGA,cAAM,WAAqF,CAAC;AAC5F,cAAM,SAAmB,CAAC;AAE1B,iBAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,gBAAM,OAAO,MAAM,CAAC;AAEpB,gBAAM,SAAmB,CAAC;AAC1B,cAAI,UAAU;AACd,cAAI,WAAW;AAEf,mBAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,kBAAM,OAAO,KAAK,CAAC;AACnB,gBAAI,SAAS,KAAK;AAChB,yBAAW,CAAC;AAAA,YACd,WAAW,SAAS,OAAO,CAAC,UAAU;AACpC,qBAAO,KAAK,QAAQ,KAAK,CAAC;AAC1B,wBAAU;AAAA,YACZ,OAAO;AACL,yBAAW;AAAA,YACb;AAAA,UACF;AACA,iBAAO,KAAK,QAAQ,KAAK,CAAC;AAG1B,gBAAM,UAAkC,CAAC;AACzC,kBAAQ,QAAQ,CAAC,QAAQ,QAAQ;AAC/B,oBAAQ,MAAM,IAAI,OAAO,GAAG,KAAK;AAAA,UACnC,CAAC;AAGD,gBAAM,QAAO,aAAQ,SAAR,mBAAc;AAC3B,gBAAM,SAAQ,aAAQ,UAAR,mBAAe;AAC7B,gBAAM,QAAO,aAAQ,SAAR,mBAAc;AAC3B,gBAAM,YAAW,aAAQ,aAAR,mBAAkB;AAEnC,cAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,QAAQ,CAAC,UAAU;AACzC,mBAAO,KAAK,EAAE,2BAA2B,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;AACxD;AAAA,UACF;AAGA,gBAAM,aAAa;AACnB,cAAI,CAAC,WAAW,KAAK,KAAK,GAAG;AAC3B,mBAAO,KAAK,EAAE,0BAA0B,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;AACvD;AAAA,UACF;AAGA,gBAAM,gBAAgB;AACtB,cAAI,CAAC,cAAc,KAAK,QAAQ,GAAG;AACjC,mBAAO,KAAK,EAAE,0BAA0B,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;AACvD;AAAA,UACF;AAEA,cAAI,SAAS;AAGb,gBAAM,aAAa,kBAAkB,IAAI,CAAAC,UAAQA,MAAK,KAAK,YAAY,CAAC;AACxE,cAAI,CAAC,WAAW,SAAS,KAAK,YAAY,CAAC,GAAG;AAC5C,mBAAO,KAAK,EAAE,yBAAyB,EAAE,KAAK,IAAI,GAAG,OAAO,WAAW,KAAK,IAAI,EAAE,CAAC,CAAC;AACpF;AAAA,UACF;AAGA,gBAAM,UAAU,kBAAkB,KAAK,OAAK,EAAE,KAAK,YAAY,MAAM,KAAK,YAAY,CAAC;AACvF,cAAI,SAAS;AACX,qBAAS,QAAQ;AAAA,UACnB;AAEA,mBAAS,KAAK,EAAE,MAAM,OAAO,QAAQ,SAAS,CAAC;AAAA,QACjD;AAEA,YAAI,OAAO,SAAS,GAAG;AACrB,gBAAM,MAAM,EAAE,2BAA2B,EAAE,OAAO,OAAO,QAAQ,OAAO,OAAO,CAAC,EAAE,CAAC,CAAC;AACpF,kBAAQ,MAAM,kBAAkB,MAAM;AAAA,QACxC;AAEA,YAAI,SAAS,WAAW,GAAG;AACzB,gBAAM,MAAM,EAAE,qBAAqB,CAAC;AACpC;AAAA,QACF;AAGA,YAAI,eAAe;AACnB,YAAI,eAAe;AACnB,YAAI,cAAc;AAElB,mBAAW,WAAW,UAAU;AAC9B,cAAI;AAEF,kBAAM,eAAe,+BAAO,KAAK,OAAK,EAAE,MAAM,YAAY,MAAM,QAAQ,MAAM,YAAY;AAE1F,gBAAI,cAAc;AAEhB,oBAAM,eAAe,MAAM,MAAM,cAAc,aAAa,EAAE,OAAO;AACrE,kBAAI,cAAc;AAClB,kBAAI,aAAa,IAAI;AACnB,sBAAM,WAAW,MAAM,aAAa,KAAK;AACzC,sBAAM,QAAQ,SAAS,SAAS,CAAC;AACjC,8BAAc,MAAM,SAAS,IAAI,MAAM,CAAC,EAAE,KAAK,YAAY,IAAI;AAAA,cACjE;AAGA,oBAAM,cAAc,aAAa,SAAS,QAAQ;AAClD,oBAAM,cAAc,gBAAgB,QAAQ;AAE5C,kBAAI,eAAe,aAAa;AAE9B,sBAAM,gBAAqD,CAAC;AAC5D,oBAAI,YAAa,eAAc,OAAO,QAAQ;AAC9C,oBAAI,YAAa,eAAc,SAAS,QAAQ;AAEhD,oBAAI;AACF,wBAAM,eAAe,aAAa,IAAI,aAAa;AACnD;AAEA,yBAAO,CAAC,QAAQ,OAAO,aAAa,EAAE,CAAC,CAAC;AACxC,sBAAI,aAAa;AACf,2BAAO,CAAC,aAAa,OAAO,aAAa,EAAE,CAAC,CAAC;AAAA,kBAC/C;AAAA,gBACF,SAAS,OAAO;AACd;AACA,0BAAQ,MAAM,0BAA0B,QAAQ,OAAO,KAAK;AAAA,gBAC9D;AAAA,cACF,OAAO;AAAA,cAEP;AAAA,YACF,OAAO;AAEL,kBAAI;AACF,sBAAM,WAAW;AAAA,kBACf,UAAU;AAAA,oBACR,MAAM,QAAQ;AAAA,oBACd,OAAO,QAAQ;AAAA,oBACf,UAAU,QAAQ;AAAA,oBAClB,QAAQ,QAAQ;AAAA,kBAClB;AAAA,gBACF,CAAC;AACD;AAAA,cACF,SAAS,OAAO;AACd;AACA,wBAAQ,MAAM,0BAA0B,QAAQ,OAAO,KAAK;AAAA,cAC9D;AAAA,YACF;AAAA,UACF,SAAS,OAAO;AACd;AACA,oBAAQ,MAAM,0BAA0B,QAAQ,OAAO,KAAK;AAAA,UAC9D;AAAA,QACF;AAGA,cAAM,WAAW,CAAC;AAClB,YAAI,eAAe,EAAG,UAAS,KAAK,EAAE,mBAAmB,EAAE,OAAO,aAAa,CAAC,CAAC;AACjF,YAAI,eAAe,EAAG,UAAS,KAAK,EAAE,mBAAmB,EAAE,OAAO,aAAa,CAAC,CAAC;AACjF,YAAI,cAAc,EAAG,UAAS,KAAK,EAAE,kBAAkB,EAAE,OAAO,YAAY,CAAC,CAAC;AAE9E,YAAI,eAAe,KAAK,eAAe,GAAG;AAExC,gBAAM,OAAO,CAAC,OAAO,CAAC;AACtB,gBAAM,QAAQ,EAAE,0BAA0B,EAAE,SAAS,SAAS,KAAK,IAAI,EAAE,CAAC,CAAC;AAAA,QAC7E,WAAW,cAAc,GAAG;AAC1B,gBAAM,MAAM,EAAE,0BAA0B,EAAE,OAAO,YAAY,CAAC,CAAC;AAAA,QACjE,OAAO;AACL,gBAAM,KAAK,EAAE,mBAAmB,CAAC;AAAA,QACnC;AAAA,MACF,SAAS,OAAO;AACd,gBAAQ,MAAM,0BAA0B,KAAK;AAC7C,cAAM,MAAM,EAAE,qBAAqB,CAAC;AAAA,MACtC,UAAE;AACA,uBAAe,KAAK;AAAA,MACtB;AAAA,IACF;AAEA,UAAM,MAAM;AAAA,EACd;AAEA,QAAM,uBAAuB,MAAM;AAEjC,UAAM,UAAU;AAChB,UAAM,UAAU;AAChB,UAAM,UAAU,GAAG,OAAO;AAAA,EAAK,OAAO;AAEtC,UAAM,OAAO,IAAI,KAAK,CAAC,OAAO,GAAG,EAAE,MAAM,WAAW,CAAC;AACrD,UAAM,MAAM,IAAI,gBAAgB,IAAI;AAEpC,UAAM,OAAO,SAAS,cAAc,GAAG;AACvC,SAAK,OAAO;AACZ,SAAK,WAAW;AAChB,SAAK,MAAM;AAEX,QAAI,gBAAgB,GAAG;AACvB,UAAM,QAAQ,EAAE,0BAA0B,CAAC;AAAA,EAC7C;AAEA,SACE,qBAAC,gBACC;AAAA,wBAAC,uBAAoB,SAAO,MAC1B;AAAA,MAAC;AAAA;AAAA,QACC,SAAS,WAAW;AAAA,QACpB,WAAU;AAAA,QACV,SAAS,OAAK,EAAE,gBAAgB;AAAA,QAChC,UAAU,CAAC,QAAQ,IAAI,QAAQ,MAAM;AAAA,QAErC;AAAA,8BAAC,kBAAe;AAAA,UAChB,oBAAC,UAAK,WAAU,WAAW,YAAE,UAAU,GAAE;AAAA;AAAA;AAAA,IAC3C,GACF;AAAA,IACA,qBAAC,uBAAoB,OAAM,OACzB;AAAA,2BAAC,oBAAiB,SAAS,mBAAmB,UAAU,eAAe,eAAe,CAAC,QAAQ,IAAI,QAAQ,MAAM,GAC/G;AAAA,4BAAC,cAAW;AAAA,QACX,cAAc,EAAE,mBAAmB,IAAI,EAAE,qBAAqB;AAAA,SACjE;AAAA,MACA,qBAAC,oBAAiB,SAAS,mBAAmB,UAAU,eAAe,eAAe,CAAC,QAAQ,IAAI,UAAU,MAAM,GACjH;AAAA,4BAAC,aAAU;AAAA,QACV,cAAc,EAAE,mBAAmB,IAAI,EAAE,qBAAqB;AAAA,SACjE;AAAA,MACA,qBAAC,oBAAiB,SAAS,sBAAsB,UAAU,CAAC,QAAQ,IAAI,QAAQ,MAAM,GACpF;AAAA,4BAAC,YAAS;AAAA,QACT,EAAE,wBAAwB;AAAA,SAC7B;AAAA,OACF;AAAA,KACF;AAEJ;","names":["user","role"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filterHelper.d.ts","sourceRoot":"","sources":["../../../../../../src/core/components/viewers/Data/utils/filterHelper.ts"],"names":[],"mappings":"AAIA,KAAK,UAAU,GAAG;IAChB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAA;IAC5D,QAAQ,EACJ,QAAQ,GAAG,SAAS,GACpB,UAAU,GAAG,QAAQ,GAAG,YAAY,GAAG,UAAU,GACjD,aAAa,GAAG,UAAU,GAAG,SAAS,GACtC,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAA;IACjC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,EAAE,CAAA;IAClD,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAA;CAClC,CAAA;AAMD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,QAAQ,EAAE,MAAM,SAGlE;AAGD,wBAAgB,YAAY,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE,SAgF9D;AAED,eAAO,MAAM,wBAAwB,GACnC,MAAM,GAAG,EAAE,EACX,aAAa,GAAG,EAAE,EAClB,qBAAqB,GAAG,EAAE,
|
|
1
|
+
{"version":3,"file":"filterHelper.d.ts","sourceRoot":"","sources":["../../../../../../src/core/components/viewers/Data/utils/filterHelper.ts"],"names":[],"mappings":"AAIA,KAAK,UAAU,GAAG;IAChB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAA;IAC5D,QAAQ,EACJ,QAAQ,GAAG,SAAS,GACpB,UAAU,GAAG,QAAQ,GAAG,YAAY,GAAG,UAAU,GACjD,aAAa,GAAG,UAAU,GAAG,SAAS,GACtC,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAA;IACjC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,EAAE,CAAA;IAClD,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAA;CAClC,CAAA;AAMD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,QAAQ,EAAE,MAAM,SAGlE;AAGD,wBAAgB,YAAY,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE,SAgF9D;AAED,eAAO,MAAM,wBAAwB,GACnC,MAAM,GAAG,EAAE,EACX,aAAa,GAAG,EAAE,EAClB,qBAAqB,GAAG,EAAE,UA2B3B,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../src/core/components/viewers/Data/utils/filterHelper.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\n// TODO: Enforce more striclty type later\r\ntype FilterType = {\r\n id: number // id of the filter\r\n field: string // field (column name) in the data\r\n fieldType: 'boolean' | 'string' | 'number' | 'date' | 'enum' // data type of the data column\r\n operator: // operator to filter that column\r\n | 'isTrue' | 'isFalse'\r\n | 'contains' | 'equals' | 'startsWith' | 'endsWith'\r\n | 'greaterThan' | 'lessThan' | 'between'\r\n | 'before' | 'after' | 'inList'\r\n value: boolean | string | number | Date | string[] // value to filter that column\r\n secondValue: number | Date | null // second value for filtering between\r\n}\r\n\r\nfunction cleanString(value: string) {\r\n return value.toLowerCase().trim()\r\n}\r\n\r\nexport function getUniqueColumnValues(data: any[], columnId: string) {\r\n const uniqueValues = new Set(data.map(row => (row[columnId])))\r\n return [...uniqueValues]\r\n}\r\n\r\n// function that contain all the logic to filter out things\r\nexport function filterHelper(data: any[], filters: FilterType[]) {\r\n let filteredData = data\r\n\r\n for (const filter of filters) {\r\n const { id, field, fieldType, operator, value, secondValue } = filter\r\n if (fieldType == null || operator == null || value == null) continue\r\n\r\n filteredData = filteredData.filter((row) => {\r\n // console.log(row[field])\r\n switch (fieldType) {\r\n case 'boolean':\r\n if (operator == 'isTrue') return row[field] == true\r\n if (operator == 'isFalse') return row[field] == false\r\n break\r\n\r\n case 'string':\r\n if (operator == 'contains') return cleanString(String(row[field])).includes(cleanString(String(value)))\r\n if (operator == 'startsWith') return cleanString(String(row[field])).startsWith(cleanString(String(value)))\r\n if (operator == 'endsWith') return cleanString(String(row[field])).endsWith(cleanString(String(value)))\r\n if (operator == 'equals') return cleanString(String(row[field])) === cleanString(String(value))\r\n if (operator == 'inList' && Array.isArray(value) && value.length > 0) {\r\n return value.map(v => cleanString(String(v))).includes(cleanString(String(row[field])))\r\n }\r\n break\r\n\r\n case 'enum':\r\n if (operator == 'equals') return row[field] === value\r\n if (operator == 'inList' && Array.isArray(value) && value.length > 0) {\r\n // Handle both single values and arrays in the data\r\n if (Array.isArray(row[field])) {\r\n // If the item's field is an array, check if any of its values are in our filter\r\n return row[field].some(val => value.includes(val))\r\n }\r\n else {\r\n // If the item's field is a single value, check if it's in our filter array\r\n return value.includes(row[field])\r\n }\r\n }\r\n break\r\n\r\n case 'number':\r\n if (operator == 'equals') return row[field] == value\r\n if (operator == 'lessThan') return row[field] < value\r\n if (operator == 'between' && secondValue != null) return value <= row[field] && row[field] <= secondValue\r\n if (operator == 'greaterThan') return row[field] > value\r\n break\r\n\r\n case 'date':\r\n // Type guard to ensure value is appropriate for Date constructor\r\n if (typeof value === 'boolean' || Array.isArray(value)) {\r\n return false // Invalid value type for date operations\r\n }\r\n\r\n const dateValue = new Date(row[field])\r\n const filterDateValue = new Date(value)\r\n\r\n if (isNaN(dateValue.getTime()) || isNaN(filterDateValue.getTime())) return false\r\n\r\n if (operator == 'equals') return dateValue.toDateString() === filterDateValue.toDateString()\r\n if (operator == 'before') return dateValue < filterDateValue\r\n if (operator == 'after') return dateValue > filterDateValue\r\n if (operator == 'between' && secondValue != null) {\r\n // Type guard for secondValue as well\r\n if (typeof secondValue === 'boolean') {\r\n return false\r\n }\r\n const secondDateValue = new Date(secondValue)\r\n if (isNaN(secondDateValue.getTime())) return false\r\n return dateValue >= filterDateValue && dateValue <= secondDateValue\r\n }\r\n break\r\n\r\n default:\r\n return true\r\n }\r\n return true // Default case if no operator matches\r\n })\r\n }\r\n\r\n return filteredData\r\n}\r\n\r\nexport const getNarrowedFilterOptions = (\r\n data: any[],\r\n baseOptions: any[],\r\n currentFiltersState: any[],\r\n) => {\r\n const inListFilters = currentFiltersState.filter(f => f.operator === 'inList')\r\n // only start narrowing once there are at least two \"inList\" filters\r\n if (inListFilters.length <= 1) {\r\n return baseOptions\r\n }\r\n\r\n let newBaseOptions = baseOptions\r\n // use only the inList filters to narrow the data\r\n for (let i = 1; i < inListFilters.length; i++) {\r\n const filteredData = filterHelper(data, inListFilters.slice(0, i))\r\n newBaseOptions = newBaseOptions.map((opt) => {\r\n if (opt.id != inListFilters[i].field) {\r\n return opt\r\n }\r\n return (\r\n {\r\n ...opt,\r\n valueOptions: getUniqueColumnValues(filteredData, opt.id),\r\n }\r\n )\r\n },\r\n )\r\n }\r\n
|
|
1
|
+
{"version":3,"sources":["../../../../../../src/core/components/viewers/Data/utils/filterHelper.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\n// TODO: Enforce more striclty type later\r\ntype FilterType = {\r\n id: number // id of the filter\r\n field: string // field (column name) in the data\r\n fieldType: 'boolean' | 'string' | 'number' | 'date' | 'enum' // data type of the data column\r\n operator: // operator to filter that column\r\n | 'isTrue' | 'isFalse'\r\n | 'contains' | 'equals' | 'startsWith' | 'endsWith'\r\n | 'greaterThan' | 'lessThan' | 'between'\r\n | 'before' | 'after' | 'inList'\r\n value: boolean | string | number | Date | string[] // value to filter that column\r\n secondValue: number | Date | null // second value for filtering between\r\n}\r\n\r\nfunction cleanString(value: string) {\r\n return value.toLowerCase().trim()\r\n}\r\n\r\nexport function getUniqueColumnValues(data: any[], columnId: string) {\r\n const uniqueValues = new Set(data.map(row => (row[columnId])))\r\n return [...uniqueValues]\r\n}\r\n\r\n// function that contain all the logic to filter out things\r\nexport function filterHelper(data: any[], filters: FilterType[]) {\r\n let filteredData = data\r\n\r\n for (const filter of filters) {\r\n const { id, field, fieldType, operator, value, secondValue } = filter\r\n if (fieldType == null || operator == null || value == null) continue\r\n\r\n filteredData = filteredData.filter((row) => {\r\n // console.log(row[field])\r\n switch (fieldType) {\r\n case 'boolean':\r\n if (operator == 'isTrue') return row[field] == true\r\n if (operator == 'isFalse') return row[field] == false\r\n break\r\n\r\n case 'string':\r\n if (operator == 'contains') return cleanString(String(row[field])).includes(cleanString(String(value)))\r\n if (operator == 'startsWith') return cleanString(String(row[field])).startsWith(cleanString(String(value)))\r\n if (operator == 'endsWith') return cleanString(String(row[field])).endsWith(cleanString(String(value)))\r\n if (operator == 'equals') return cleanString(String(row[field])) === cleanString(String(value))\r\n if (operator == 'inList' && Array.isArray(value) && value.length > 0) {\r\n return value.map(v => cleanString(String(v))).includes(cleanString(String(row[field])))\r\n }\r\n break\r\n\r\n case 'enum':\r\n if (operator == 'equals') return row[field] === value\r\n if (operator == 'inList' && Array.isArray(value) && value.length > 0) {\r\n // Handle both single values and arrays in the data\r\n if (Array.isArray(row[field])) {\r\n // If the item's field is an array, check if any of its values are in our filter\r\n return row[field].some(val => value.includes(val))\r\n }\r\n else {\r\n // If the item's field is a single value, check if it's in our filter array\r\n return value.includes(row[field])\r\n }\r\n }\r\n break\r\n\r\n case 'number':\r\n if (operator == 'equals') return row[field] == value\r\n if (operator == 'lessThan') return row[field] < value\r\n if (operator == 'between' && secondValue != null) return value <= row[field] && row[field] <= secondValue\r\n if (operator == 'greaterThan') return row[field] > value\r\n break\r\n\r\n case 'date':\r\n // Type guard to ensure value is appropriate for Date constructor\r\n if (typeof value === 'boolean' || Array.isArray(value)) {\r\n return false // Invalid value type for date operations\r\n }\r\n\r\n const dateValue = new Date(row[field])\r\n const filterDateValue = new Date(value)\r\n\r\n if (isNaN(dateValue.getTime()) || isNaN(filterDateValue.getTime())) return false\r\n\r\n if (operator == 'equals') return dateValue.toDateString() === filterDateValue.toDateString()\r\n if (operator == 'before') return dateValue < filterDateValue\r\n if (operator == 'after') return dateValue > filterDateValue\r\n if (operator == 'between' && secondValue != null) {\r\n // Type guard for secondValue as well\r\n if (typeof secondValue === 'boolean') {\r\n return false\r\n }\r\n const secondDateValue = new Date(secondValue)\r\n if (isNaN(secondDateValue.getTime())) return false\r\n return dateValue >= filterDateValue && dateValue <= secondDateValue\r\n }\r\n break\r\n\r\n default:\r\n return true\r\n }\r\n return true // Default case if no operator matches\r\n })\r\n }\r\n\r\n return filteredData\r\n}\r\n\r\nexport const getNarrowedFilterOptions = (\r\n data: any[],\r\n baseOptions: any[],\r\n currentFiltersState: any[],\r\n) => {\r\n const inListFilters = currentFiltersState.filter(f => f.operator === 'inList')\r\n // only start narrowing once there are at least two \"inList\" filters\r\n if (inListFilters.length <= 1) {\r\n return baseOptions\r\n }\r\n\r\n let newBaseOptions = baseOptions\r\n // use only the inList filters to narrow the data\r\n for (let i = 1; i < inListFilters.length; i++) {\r\n const filteredData = filterHelper(data, inListFilters.slice(0, i))\r\n newBaseOptions = newBaseOptions.map((opt) => {\r\n if (opt.id != inListFilters[i].field) {\r\n return opt\r\n }\r\n return (\r\n {\r\n ...opt,\r\n valueOptions: getUniqueColumnValues(filteredData, opt.id),\r\n }\r\n )\r\n },\r\n )\r\n }\r\n\r\n return newBaseOptions\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAiBA,SAAS,YAAY,OAAe;AAClC,SAAO,MAAM,YAAY,EAAE,KAAK;AAClC;AAEO,SAAS,sBAAsB,MAAa,UAAkB;AACnE,QAAM,eAAe,IAAI,IAAI,KAAK,IAAI,SAAQ,IAAI,QAAQ,CAAE,CAAC;AAC7D,SAAO,CAAC,GAAG,YAAY;AACzB;AAGO,SAAS,aAAa,MAAa,SAAuB;AAC/D,MAAI,eAAe;AAEnB,aAAW,UAAU,SAAS;AAC5B,UAAM,EAAE,IAAI,OAAO,WAAW,UAAU,OAAO,YAAY,IAAI;AAC/D,QAAI,aAAa,QAAQ,YAAY,QAAQ,SAAS,KAAM;AAE5D,mBAAe,aAAa,OAAO,CAAC,QAAQ;AAE1C,cAAQ,WAAW;AAAA,QACjB,KAAK;AACH,cAAI,YAAY,SAAU,QAAO,IAAI,KAAK,KAAK;AAC/C,cAAI,YAAY,UAAW,QAAO,IAAI,KAAK,KAAK;AAChD;AAAA,QAEF,KAAK;AACH,cAAI,YAAY,WAAY,QAAO,YAAY,OAAO,IAAI,KAAK,CAAC,CAAC,EAAE,SAAS,YAAY,OAAO,KAAK,CAAC,CAAC;AACtG,cAAI,YAAY,aAAc,QAAO,YAAY,OAAO,IAAI,KAAK,CAAC,CAAC,EAAE,WAAW,YAAY,OAAO,KAAK,CAAC,CAAC;AAC1G,cAAI,YAAY,WAAY,QAAO,YAAY,OAAO,IAAI,KAAK,CAAC,CAAC,EAAE,SAAS,YAAY,OAAO,KAAK,CAAC,CAAC;AACtG,cAAI,YAAY,SAAU,QAAO,YAAY,OAAO,IAAI,KAAK,CAAC,CAAC,MAAM,YAAY,OAAO,KAAK,CAAC;AAC9F,cAAI,YAAY,YAAY,MAAM,QAAQ,KAAK,KAAK,MAAM,SAAS,GAAG;AACpE,mBAAO,MAAM,IAAI,OAAK,YAAY,OAAO,CAAC,CAAC,CAAC,EAAE,SAAS,YAAY,OAAO,IAAI,KAAK,CAAC,CAAC,CAAC;AAAA,UACxF;AACA;AAAA,QAEF,KAAK;AACH,cAAI,YAAY,SAAU,QAAO,IAAI,KAAK,MAAM;AAChD,cAAI,YAAY,YAAY,MAAM,QAAQ,KAAK,KAAK,MAAM,SAAS,GAAG;AAEpE,gBAAI,MAAM,QAAQ,IAAI,KAAK,CAAC,GAAG;AAE7B,qBAAO,IAAI,KAAK,EAAE,KAAK,SAAO,MAAM,SAAS,GAAG,CAAC;AAAA,YACnD,OACK;AAEH,qBAAO,MAAM,SAAS,IAAI,KAAK,CAAC;AAAA,YAClC;AAAA,UACF;AACA;AAAA,QAEF,KAAK;AACH,cAAI,YAAY,SAAU,QAAO,IAAI,KAAK,KAAK;AAC/C,cAAI,YAAY,WAAY,QAAO,IAAI,KAAK,IAAI;AAChD,cAAI,YAAY,aAAa,eAAe,KAAM,QAAO,SAAS,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK;AAC9F,cAAI,YAAY,cAAe,QAAO,IAAI,KAAK,IAAI;AACnD;AAAA,QAEF,KAAK;AAEH,cAAI,OAAO,UAAU,aAAa,MAAM,QAAQ,KAAK,GAAG;AACtD,mBAAO;AAAA,UACT;AAEA,gBAAM,YAAY,IAAI,KAAK,IAAI,KAAK,CAAC;AACrC,gBAAM,kBAAkB,IAAI,KAAK,KAAK;AAEtC,cAAI,MAAM,UAAU,QAAQ,CAAC,KAAK,MAAM,gBAAgB,QAAQ,CAAC,EAAG,QAAO;AAE3E,cAAI,YAAY,SAAU,QAAO,UAAU,aAAa,MAAM,gBAAgB,aAAa;AAC3F,cAAI,YAAY,SAAU,QAAO,YAAY;AAC7C,cAAI,YAAY,QAAS,QAAO,YAAY;AAC5C,cAAI,YAAY,aAAa,eAAe,MAAM;AAEhD,gBAAI,OAAO,gBAAgB,WAAW;AACpC,qBAAO;AAAA,YACT;AACA,kBAAM,kBAAkB,IAAI,KAAK,WAAW;AAC5C,gBAAI,MAAM,gBAAgB,QAAQ,CAAC,EAAG,QAAO;AAC7C,mBAAO,aAAa,mBAAmB,aAAa;AAAA,UACtD;AACA;AAAA,QAEF;AACE,iBAAO;AAAA,MACX;AACA,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAEO,MAAM,2BAA2B,CACtC,MACA,aACA,wBACG;AACH,QAAM,gBAAgB,oBAAoB,OAAO,OAAK,EAAE,aAAa,QAAQ;AAE7E,MAAI,cAAc,UAAU,GAAG;AAC7B,WAAO;AAAA,EACT;AAEA,MAAI,iBAAiB;AAErB,WAAS,IAAI,GAAG,IAAI,cAAc,QAAQ,KAAK;AAC7C,UAAM,eAAe,aAAa,MAAM,cAAc,MAAM,GAAG,CAAC,CAAC;AACjE,qBAAiB,eAAe;AAAA,MAAI,CAAC,QAAQ;AAC3C,YAAI,IAAI,MAAM,cAAc,CAAC,EAAE,OAAO;AACpC,iBAAO;AAAA,QACT;AACA,eACE,iCACK,MADL;AAAA,UAEE,cAAc,sBAAsB,cAAc,IAAI,EAAE;AAAA,QAC1D;AAAA,MAEJ;AAAA,IACA;AAAA,EACF;AAEA,SAAO;AACT;","names":[]}
|
|
@@ -98,7 +98,7 @@ function Viewer({ organization, minioBaseUrl, martinBaseUrl, pointcloudApiUrl, m
|
|
|
98
98
|
const selectedViewer = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
99
99
|
[ViewerNames.map, ViewerNames.bim, ViewerNames.pointcloud].includes(validViewer) && /* @__PURE__ */ jsx(SidebarTrigger, {}),
|
|
100
100
|
/* @__PURE__ */ jsxs("div", { style: { height: "100%", width: "100%", position: "relative" }, children: [
|
|
101
|
-
/* @__PURE__ */ jsx("div", { style: { display: validViewer === ViewerNames.map ? "block" : "none", width: "100%", height: "100%" }, children: /* @__PURE__ */ jsx(MapViewer, { organization,
|
|
101
|
+
/* @__PURE__ */ jsx("div", { style: { display: validViewer === ViewerNames.map ? "block" : "none", width: "100%", height: "100%" }, children: /* @__PURE__ */ jsx(MapViewer, { organization, maptilerKey }) }),
|
|
102
102
|
validViewer === ViewerNames.bim && /* @__PURE__ */ jsx(BimViewer, {}),
|
|
103
103
|
validViewer === ViewerNames.pointcloud && /* @__PURE__ */ jsx(PointCloudViewer, { pointcloudApiUrl }),
|
|
104
104
|
[ViewerNames.buildings, ViewerNames.sites, ViewerNames.files, ViewerNames.land, ViewerNames.infrastructure, ViewerNames.extensions, ViewerNames.users].includes(validViewer) && /* @__PURE__ */ jsx(DataMenu, { currentViewer: validViewer, organization, geocodeEarthApiKey, geocoderUrl }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/core/components/viewers/Viewer.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 { Loader2 } from 'lucide-react'\r\nimport dynamic from 'next/dynamic'\r\nimport { useSearchParams, useRouter, usePathname } from 'next/navigation'\r\nimport * as React from 'react'\r\n\r\nimport { BuildingsContext, MenusContext } from '../../store'\r\nimport { ViewerNames } from '../../types'\r\n\r\n// Deep file import (not the ui barrel): the barrel re-exports heavy modules\r\n// (InfoSidebar, useFileUploadHandler, …) that would land in the eager map-route bundle.\r\nimport { switchLanguage } from '../../utils/utils'\r\nimport { UserSettings } from '../settings'\r\nimport { Toolbar } from '../Toolbar'\r\nimport { SidebarTrigger } from '../ui/Sidebar'\r\n\r\nimport { MapViewer } from './map/MapViewer'\r\n\r\nimport type { Organization } from '../../types/dbTypes'\r\n\r\n// Code-split the heavy viewers: BimViewer pulls in ~456 KB gzipped of\r\n// @thatopen/*, PointCloudViewer the Potree loader stack — loaded only when\r\n// selected, so map-only sessions never pay their cost. MapViewer stays static\r\n// because every user lands on it.\r\nconst BimViewer = dynamic(\r\n () => import('./bim/BimViewer').then(m => ({ default: m.BimViewer })),\r\n { ssr: false, loading: () => <ViewerLoadingFallback label=\"Loading BIM viewer…\" /> },\r\n)\r\nconst PointCloudViewer = dynamic(\r\n () => import('./pointcloud/PointCloudViewer').then(m => ({ default: m.PointCloudViewer })),\r\n { ssr: false, loading: () => <ViewerLoadingFallback label=\"Loading point cloud viewer…\" /> },\r\n)\r\n\r\n// DataMenu transitively imports BimViewer (via FilePreview), so keep it lazy\r\n// too — a static import would pull @thatopen + three/webgpu back into the eager\r\n// bundle. Only rendered for the data viewers, so map-only sessions skip it.\r\nconst DataMenu = dynamic(\r\n () => import('./Data/DataMenu').then(m => ({ default: m.DataMenu })),\r\n { ssr: false, loading: () => <ViewerLoadingFallback label=\"Loading data…\" /> },\r\n)\r\n\r\n// Styled to match the in-viewer loading card (see BimLoadingState): a soft\r\n// backdrop-blurred overlay with a bordered card, spinner + label. Uses only\r\n// Tailwind tokens + one lucide icon, so it adds no meaningful weight to the\r\n// eager map-route bundle (Viewer.tsx is statically imported).\r\nfunction ViewerLoadingFallback({ label }: { label: string }) {\r\n return (\r\n <div className=\"absolute inset-0 z-10 flex items-center justify-center bg-background/50 backdrop-blur-[1px]\">\r\n <div className=\"flex items-center gap-3 rounded-md border border-border bg-card px-5 py-4 shadow-sm\">\r\n <Loader2 className=\"h-5 w-5 animate-spin text-muted-foreground\" />\r\n <span className=\"text-foreground text-base font-medium font-['Inter'] leading-7\">{label}</span>\r\n </div>\r\n </div>\r\n )\r\n}\r\n\r\ninterface ViewerProps {\r\n organization: Organization\r\n minioBaseUrl?: string\r\n martinBaseUrl?: string\r\n pointcloudApiUrl?: string\r\n maptilerKey?: string\r\n geocodeEarthApiKey?: string\r\n geocoderUrl?: string\r\n}\r\n\r\nexport function Viewer({ organization, minioBaseUrl, martinBaseUrl, pointcloudApiUrl, maptilerKey, geocodeEarthApiKey, geocoderUrl }: ViewerProps) {\r\n\r\n const searchParams = useSearchParams()\r\n const viewer = (searchParams.get('viewer') as ViewerNames) || ViewerNames.map\r\n\r\n const { dispatch: menusDispatch, state: menusState } = React.useContext(MenusContext)\r\n const { state: buildingState } = React.useContext(BuildingsContext)\r\n // const { dispatch: appConfigDispatch } = React.useContext(AppConfigContext)\r\n const { building } = buildingState.buildings\r\n const { currentViewer } = menusState.menus\r\n const router = useRouter()\r\n const pathname = usePathname()\r\n const [isMounted, setIsMounted] = React.useState(false)\r\n\r\n // Use a ref to track if we're currently updating -> prevent circular updates\r\n const isUpdatingRef = React.useRef(false)\r\n\r\n const { appContent } = organization\r\n\r\n // Check if viewer is valid based on appContent\r\n const isViewerValid = appContent.length === 0 || appContent.includes(viewer)\r\n\r\n // Use default viewer (map) if current viewer is not valid\r\n const validViewer = isViewerValid ? viewer : ViewerNames.map\r\n\r\n // Update URL if viewer was not available for the organization\r\n React.useEffect(() => {\r\n if (isMounted && !isViewerValid && viewer !== ViewerNames.map) {\r\n const newSearchParams = new URLSearchParams(searchParams.toString())\r\n newSearchParams.delete('viewer')\r\n if (building?.id) {\r\n newSearchParams.set('buildingId', String(building.id))\r\n }\r\n router.replace(`${pathname}?${newSearchParams.toString()}`, { scroll: false })\r\n }\r\n }, [isMounted, isViewerValid, viewer, searchParams, pathname, router, building?.id])\r\n\r\n // On first mount, respond to URL\r\n React.useEffect(() => {\r\n if (!isMounted) {\r\n menusDispatch({\r\n type: 'SET_VIEWER',\r\n payload: { currentViewer: validViewer },\r\n })\r\n setIsMounted(true)\r\n }\r\n }, [isMounted, validViewer, menusDispatch])\r\n\r\n React.useEffect(() => {\r\n if (!organization) return\r\n const defaultLanguage = organization.languages?.[0] || 'En'\r\n if (defaultLanguage) switchLanguage(defaultLanguage)\r\n }, [organization])\r\n\r\n // Handle context changes (from sidebar, HeaderButtons, etc.)\r\n React.useEffect(() => {\r\n if (!isMounted || !currentViewer || currentViewer === validViewer || isUpdatingRef.current) {\r\n return\r\n }\r\n\r\n isUpdatingRef.current = true\r\n\r\n // Create URLSearchParams to preserve existing search params\r\n\r\n const newSearchParams = new URLSearchParams(searchParams.toString())\r\n newSearchParams.set('viewer', currentViewer)\r\n if (building?.id) {\r\n newSearchParams.set('buildingId', String(building.id))\r\n }\r\n\r\n\r\n router.replace(`${pathname}?${newSearchParams.toString()}`, { scroll: false })\r\n\r\n // Reset the flag after a brief delay to allow the navigation to complete\r\n setTimeout(() => {\r\n isUpdatingRef.current = false\r\n }, 100)\r\n }, [currentViewer, isMounted, pathname, router, viewer, searchParams])\r\n\r\n // Handle URL changes (direct navigation)\r\n React.useEffect(() => {\r\n if (isMounted && currentViewer !== validViewer && !isUpdatingRef.current) {\r\n isUpdatingRef.current = true\r\n\r\n menusDispatch({\r\n type: 'SET_VIEWER',\r\n payload: { currentViewer: validViewer },\r\n })\r\n\r\n // Reset the flag after a brief delay\r\n setTimeout(() => {\r\n isUpdatingRef.current = false\r\n }, 100)\r\n }\r\n }, [validViewer, menusDispatch, isMounted, currentViewer])\r\n\r\n const selectedViewer = (\r\n <>\r\n {[ViewerNames.map, ViewerNames.bim, ViewerNames.pointcloud].includes(validViewer) && <SidebarTrigger />}\r\n <div style={{ height: '100%', width: '100%', position: 'relative' }}>\r\n <div style={{ display: validViewer === ViewerNames.map ? 'block' : 'none', width: '100%', height: '100%' }}>\r\n <MapViewer organization={organization} minioBaseUrl={minioBaseUrl} maptilerKey={maptilerKey} />\r\n </div>\r\n {validViewer === ViewerNames.bim && <BimViewer />}\r\n {validViewer === ViewerNames.pointcloud && <PointCloudViewer pointcloudApiUrl={pointcloudApiUrl} />}\r\n {[ViewerNames.buildings, ViewerNames.sites, ViewerNames.files, ViewerNames.land, ViewerNames.infrastructure, ViewerNames.extensions, ViewerNames.users].includes(validViewer) && (\r\n <DataMenu currentViewer={validViewer} organization={organization} geocodeEarthApiKey={geocodeEarthApiKey} geocoderUrl={geocoderUrl} />\r\n )}\r\n {[ViewerNames.settings].includes(validViewer) && (\r\n <UserSettings minioBaseUrl={minioBaseUrl} />\r\n )}\r\n </div>\r\n </>\r\n )\r\n\r\n return (\r\n <>\r\n {selectedViewer}\r\n <Toolbar viewer={validViewer} minioBaseUrl={minioBaseUrl} martinBaseUrl={martinBaseUrl} organization={organization} geocodeEarthApiKey={geocodeEarthApiKey} geocoderUrl={geocoderUrl} />\r\n </>\r\n )\r\n}"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/core/components/viewers/Viewer.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 { Loader2 } from 'lucide-react'\r\nimport dynamic from 'next/dynamic'\r\nimport { useSearchParams, useRouter, usePathname } from 'next/navigation'\r\nimport * as React from 'react'\r\n\r\nimport { BuildingsContext, MenusContext } from '../../store'\r\nimport { ViewerNames } from '../../types'\r\n\r\n// Deep file import (not the ui barrel): the barrel re-exports heavy modules\r\n// (InfoSidebar, useFileUploadHandler, …) that would land in the eager map-route bundle.\r\nimport { switchLanguage } from '../../utils/utils'\r\nimport { UserSettings } from '../settings'\r\nimport { Toolbar } from '../Toolbar'\r\nimport { SidebarTrigger } from '../ui/Sidebar'\r\n\r\nimport { MapViewer } from './map/MapViewer'\r\n\r\nimport type { Organization } from '../../types/dbTypes'\r\n\r\n// Code-split the heavy viewers: BimViewer pulls in ~456 KB gzipped of\r\n// @thatopen/*, PointCloudViewer the Potree loader stack — loaded only when\r\n// selected, so map-only sessions never pay their cost. MapViewer stays static\r\n// because every user lands on it.\r\nconst BimViewer = dynamic(\r\n () => import('./bim/BimViewer').then(m => ({ default: m.BimViewer })),\r\n { ssr: false, loading: () => <ViewerLoadingFallback label=\"Loading BIM viewer…\" /> },\r\n)\r\nconst PointCloudViewer = dynamic(\r\n () => import('./pointcloud/PointCloudViewer').then(m => ({ default: m.PointCloudViewer })),\r\n { ssr: false, loading: () => <ViewerLoadingFallback label=\"Loading point cloud viewer…\" /> },\r\n)\r\n\r\n// DataMenu transitively imports BimViewer (via FilePreview), so keep it lazy\r\n// too — a static import would pull @thatopen + three/webgpu back into the eager\r\n// bundle. Only rendered for the data viewers, so map-only sessions skip it.\r\nconst DataMenu = dynamic(\r\n () => import('./Data/DataMenu').then(m => ({ default: m.DataMenu })),\r\n { ssr: false, loading: () => <ViewerLoadingFallback label=\"Loading data…\" /> },\r\n)\r\n\r\n// Styled to match the in-viewer loading card (see BimLoadingState): a soft\r\n// backdrop-blurred overlay with a bordered card, spinner + label. Uses only\r\n// Tailwind tokens + one lucide icon, so it adds no meaningful weight to the\r\n// eager map-route bundle (Viewer.tsx is statically imported).\r\nfunction ViewerLoadingFallback({ label }: { label: string }) {\r\n return (\r\n <div className=\"absolute inset-0 z-10 flex items-center justify-center bg-background/50 backdrop-blur-[1px]\">\r\n <div className=\"flex items-center gap-3 rounded-md border border-border bg-card px-5 py-4 shadow-sm\">\r\n <Loader2 className=\"h-5 w-5 animate-spin text-muted-foreground\" />\r\n <span className=\"text-foreground text-base font-medium font-['Inter'] leading-7\">{label}</span>\r\n </div>\r\n </div>\r\n )\r\n}\r\n\r\ninterface ViewerProps {\r\n organization: Organization\r\n minioBaseUrl?: string\r\n martinBaseUrl?: string\r\n pointcloudApiUrl?: string\r\n maptilerKey?: string\r\n geocodeEarthApiKey?: string\r\n geocoderUrl?: string\r\n}\r\n\r\nexport function Viewer({ organization, minioBaseUrl, martinBaseUrl, pointcloudApiUrl, maptilerKey, geocodeEarthApiKey, geocoderUrl }: ViewerProps) {\r\n\r\n const searchParams = useSearchParams()\r\n const viewer = (searchParams.get('viewer') as ViewerNames) || ViewerNames.map\r\n\r\n const { dispatch: menusDispatch, state: menusState } = React.useContext(MenusContext)\r\n const { state: buildingState } = React.useContext(BuildingsContext)\r\n // const { dispatch: appConfigDispatch } = React.useContext(AppConfigContext)\r\n const { building } = buildingState.buildings\r\n const { currentViewer } = menusState.menus\r\n const router = useRouter()\r\n const pathname = usePathname()\r\n const [isMounted, setIsMounted] = React.useState(false)\r\n\r\n // Use a ref to track if we're currently updating -> prevent circular updates\r\n const isUpdatingRef = React.useRef(false)\r\n\r\n const { appContent } = organization\r\n\r\n // Check if viewer is valid based on appContent\r\n const isViewerValid = appContent.length === 0 || appContent.includes(viewer)\r\n\r\n // Use default viewer (map) if current viewer is not valid\r\n const validViewer = isViewerValid ? viewer : ViewerNames.map\r\n\r\n // Update URL if viewer was not available for the organization\r\n React.useEffect(() => {\r\n if (isMounted && !isViewerValid && viewer !== ViewerNames.map) {\r\n const newSearchParams = new URLSearchParams(searchParams.toString())\r\n newSearchParams.delete('viewer')\r\n if (building?.id) {\r\n newSearchParams.set('buildingId', String(building.id))\r\n }\r\n router.replace(`${pathname}?${newSearchParams.toString()}`, { scroll: false })\r\n }\r\n }, [isMounted, isViewerValid, viewer, searchParams, pathname, router, building?.id])\r\n\r\n // On first mount, respond to URL\r\n React.useEffect(() => {\r\n if (!isMounted) {\r\n menusDispatch({\r\n type: 'SET_VIEWER',\r\n payload: { currentViewer: validViewer },\r\n })\r\n setIsMounted(true)\r\n }\r\n }, [isMounted, validViewer, menusDispatch])\r\n\r\n React.useEffect(() => {\r\n if (!organization) return\r\n const defaultLanguage = organization.languages?.[0] || 'En'\r\n if (defaultLanguage) switchLanguage(defaultLanguage)\r\n }, [organization])\r\n\r\n // Handle context changes (from sidebar, HeaderButtons, etc.)\r\n React.useEffect(() => {\r\n if (!isMounted || !currentViewer || currentViewer === validViewer || isUpdatingRef.current) {\r\n return\r\n }\r\n\r\n isUpdatingRef.current = true\r\n\r\n // Create URLSearchParams to preserve existing search params\r\n\r\n const newSearchParams = new URLSearchParams(searchParams.toString())\r\n newSearchParams.set('viewer', currentViewer)\r\n if (building?.id) {\r\n newSearchParams.set('buildingId', String(building.id))\r\n }\r\n\r\n\r\n router.replace(`${pathname}?${newSearchParams.toString()}`, { scroll: false })\r\n\r\n // Reset the flag after a brief delay to allow the navigation to complete\r\n setTimeout(() => {\r\n isUpdatingRef.current = false\r\n }, 100)\r\n }, [currentViewer, isMounted, pathname, router, viewer, searchParams])\r\n\r\n // Handle URL changes (direct navigation)\r\n React.useEffect(() => {\r\n if (isMounted && currentViewer !== validViewer && !isUpdatingRef.current) {\r\n isUpdatingRef.current = true\r\n\r\n menusDispatch({\r\n type: 'SET_VIEWER',\r\n payload: { currentViewer: validViewer },\r\n })\r\n\r\n // Reset the flag after a brief delay\r\n setTimeout(() => {\r\n isUpdatingRef.current = false\r\n }, 100)\r\n }\r\n }, [validViewer, menusDispatch, isMounted, currentViewer])\r\n\r\n const selectedViewer = (\r\n <>\r\n {[ViewerNames.map, ViewerNames.bim, ViewerNames.pointcloud].includes(validViewer) && <SidebarTrigger />}\r\n <div style={{ height: '100%', width: '100%', position: 'relative' }}>\r\n <div style={{ display: validViewer === ViewerNames.map ? 'block' : 'none', width: '100%', height: '100%' }}>\r\n <MapViewer organization={organization} maptilerKey={maptilerKey} />\r\n </div>\r\n {validViewer === ViewerNames.bim && <BimViewer />}\r\n {validViewer === ViewerNames.pointcloud && <PointCloudViewer pointcloudApiUrl={pointcloudApiUrl} />}\r\n {[ViewerNames.buildings, ViewerNames.sites, ViewerNames.files, ViewerNames.land, ViewerNames.infrastructure, ViewerNames.extensions, ViewerNames.users].includes(validViewer) && (\r\n <DataMenu currentViewer={validViewer} organization={organization} geocodeEarthApiKey={geocodeEarthApiKey} geocoderUrl={geocoderUrl} />\r\n )}\r\n {[ViewerNames.settings].includes(validViewer) && (\r\n <UserSettings minioBaseUrl={minioBaseUrl} />\r\n )}\r\n </div>\r\n </>\r\n )\r\n\r\n return (\r\n <>\r\n {selectedViewer}\r\n <Toolbar viewer={validViewer} minioBaseUrl={minioBaseUrl} martinBaseUrl={martinBaseUrl} organization={organization} geocodeEarthApiKey={geocodeEarthApiKey} geocoderUrl={geocoderUrl} />\r\n </>\r\n )\r\n}"],"mappings":"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","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TopicsSection.d.ts","sourceRoot":"","sources":["../../../../../../../../../../src/core/components/viewers/bim/src/BimSidebar/src/CommunicationTab/src/TopicsSection.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAgB9B,wBAAgB,aAAa,
|
|
1
|
+
{"version":3,"file":"TopicsSection.d.ts","sourceRoot":"","sources":["../../../../../../../../../../src/core/components/viewers/bim/src/BimSidebar/src/CommunicationTab/src/TopicsSection.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAgB9B,wBAAgB,aAAa,sBAsO5B"}
|
package/dist/core/components/viewers/bim/src/BimSidebar/src/CommunicationTab/src/TopicsSection.js
CHANGED
|
@@ -90,15 +90,12 @@ function TopicsSection() {
|
|
|
90
90
|
creationAuthor: (user == null ? void 0 : user.email) || "anonymous@example.com"
|
|
91
91
|
}));
|
|
92
92
|
const topicToAdd = createdTopic || newTopic;
|
|
93
|
-
console.log("New topic to add:", topicToAdd);
|
|
94
93
|
const existingTopic = bcfTopics.find((existingTopic2) => existingTopic2.guid === topicToAdd.guid);
|
|
95
94
|
if (!existingTopic) {
|
|
96
95
|
bimDispatch({
|
|
97
96
|
type: "ADD_BCF_TOPIC",
|
|
98
97
|
payload: { bcfTopic: topicToAdd }
|
|
99
98
|
});
|
|
100
|
-
} else {
|
|
101
|
-
console.log(`BCF topic with ID ${topicToAdd.guid} already exists, skipping...`);
|
|
102
99
|
}
|
|
103
100
|
}, [bcfTopicsManager, bimDispatch, user == null ? void 0 : user.email, bcfTopics]);
|
|
104
101
|
const handleEditTopic = React.useCallback((formData) => {
|